summaryrefslogtreecommitdiffstats
path: root/compton.c
diff options
context:
space:
mode:
Diffstat (limited to 'compton.c')
-rw-r--r--compton.c176
1 files changed, 135 insertions, 41 deletions
diff --git a/compton.c b/compton.c
index cdf321d54..c1016769f 100644
--- a/compton.c
+++ b/compton.c
@@ -847,45 +847,6 @@ determine_evmask(session_t *ps, Window wid, win_evmode_t mode) {
}
/**
- * Find a window from window id in window linked list of the session.
- */
-static win *
-find_win(session_t *ps, Window id) {
- if (!id)
- return NULL;
-
- win *w;
-
- for (w = ps->list; w; w = w->next) {
- if (w->id == id && !w->destroyed)
- return w;
- }
-
- return 0;
-}
-
-/**
- * Find out the WM frame of a client window using existing data.
- *
- * @param w window ID
- * @return struct _win object of the found window, NULL if not found
- */
-static win *
-find_toplevel(session_t *ps, Window id) {
- if (!id)
- return NULL;
-
- win *w;
-
- for (w = ps->list; w; w = w->next) {
- if (w->client_win == id && !w->destroyed)
- return w;
- }
-
- return NULL;
-}
-
-/**
* Find out the WM frame of a client window by querying X.
*
* @param ps current session
@@ -1820,7 +1781,6 @@ map_win(session_t *ps, Window id) {
focused_real = true;
win_set_focused(ps, w, focused_real);
-
// Call XSelectInput() before reading properties so that no property
// changes are lost
XSelectInput(ps->dpy, id, determine_evmask(ps, id, WIN_EVMODE_FRAME));
@@ -2238,6 +2198,10 @@ win_mark_client(session_t *ps, win *w, Window client) {
w->window_type = WINTYPE_DIALOG;
}
+ // Get window group
+ if (ps->o.track_leader)
+ win_update_leader(ps, w);
+
win_update_focused(ps, w);
}
@@ -2382,6 +2346,7 @@ add_win(session_t *ps, Window id, Window prev) {
new->focused = false;
new->focused_real = false;
new->leader = None;
+ new->cache_leader = None;
new->destroyed = false;
new->need_configure = false;
new->window_type = WINTYPE_UNKNOWN;
@@ -2740,6 +2705,91 @@ wid_get_prop_window(session_t *ps, Window wid, Atom aprop) {
}
/**
+ * Update leader of a window.
+ */
+static void
+win_update_leader(session_t *ps, win *w) {
+ Window leader = None;
+
+ // Read the leader properties
+ if (ps->o.detect_transient && !leader)
+ leader = wid_get_prop_window(ps, w->client_win, ps->atom_transient);
+
+ if (ps->o.detect_client_leader && !leader)
+ leader = wid_get_prop_window(ps, w->client_win, ps->atom_client_leader);
+
+ win_set_leader(ps, w, leader);
+}
+
+/**
+ * Set leader of a window.
+ */
+static void
+win_set_leader(session_t *ps, win *w, Window nleader) {
+ // If the leader changes
+ if (w->leader != nleader) {
+ Window cache_leader_old = win_get_leader(ps, w);
+
+ w->leader = nleader;
+
+ // Forcefully do this to deal with the case when a child window
+ // gets mapped before parent, or when the window is a waypoint
+ clear_cache_win_leaders(ps);
+
+ // Update the old and new window group and active_leader if the window
+ // could affect their state.
+ Window cache_leader = win_get_leader(ps, w);
+ if (w->focused_real && cache_leader_old != cache_leader) {
+ ps->active_leader = cache_leader;
+
+ group_update_focused(ps, cache_leader_old);
+ group_update_focused(ps, cache_leader);
+ }
+ // Otherwise, at most the window itself is affected
+ else {
+ win_update_focused(ps, w);
+ }
+ }
+}
+
+/**
+ * Get the leader of a window.
+ *
+ * This function updates w->cache_leader if necessary.
+ */
+static Window
+win_get_leader(session_t *ps, win *w) {
+ return win_get_leader_raw(ps, w, 0);
+}
+
+/**
+ * Internal function of win_get_leader().
+ */
+static Window
+win_get_leader_raw(session_t *ps, win *w, int recursions) {
+ // Rebuild the cache if needed
+ if (!w->cache_leader && (w->client_win || w->leader)) {
+ // Leader defaults to client window
+ if (!(w->cache_leader = w->leader))
+ w->cache_leader = w->client_win;
+
+ // If the leader of this window isn't itself, look for its ancestors
+ if (w->cache_leader && w->cache_leader != w->client_win) {
+ win *wp = find_toplevel(ps, w->cache_leader);
+ if (wp) {
+ // Dead loop?
+ if (recursions > WIN_GET_LEADER_MAX_RECURSION)
+ return None;
+
+ w->cache_leader = win_get_leader_raw(ps, wp, recursions + 1);
+ }
+ }
+ }
+
+ return w->cache_leader;
+}
+
+/**
* Get the value of a text property of a window.
*/
static bool
@@ -3173,10 +3223,10 @@ update_ewmh_active_win(session_t *ps) {
// Mark the window focused
if (w) {
- win_set_focused(ps, w, true);
if (ps->active_win && w != ps->active_win)
win_set_focused(ps, ps->active_win, false);
ps->active_win = w;
+ win_set_focused(ps, w, true);
}
}
@@ -3280,6 +3330,16 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) {
if (w)
win_update_attr_shadow(ps, w);
}
+
+ // If a leader property changes
+ if ((ps->o.detect_transient && ps->atom_transient == ev->atom)
+ || (ps->o.detect_client_leader && ps->atom_client_leader == ev->atom)) {
+ win *w = find_toplevel(ps, ev->window);
+ if (w) {
+ win_update_leader(ps, w);
+ }
+ }
+
}
inline static void
@@ -3558,6 +3618,13 @@ usage(void) {
" considered focused.\n"
"--inactive-dim-fixed\n"
" Use fixed inactive dim value.\n"
+ "--detect-transient\n"
+ " Use WM_TRANSIENT_FOR to group windows, and consider windows in\n"
+ " the same group focused at the same time.\n"
+ "--detect-client-leader\n"
+ " Use WM_CLIENT_LEADER to group windows, and consider windows in\n"
+ " the same group focused at the same time. WM_TRANSIENT_FOR has\n"
+ " higher priority if --detect-transient is enabled, too.\n"
"\n"
"Format of a condition:\n"
"\n"
@@ -3955,6 +4022,11 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) {
&ps->o.unredir_if_possible);
// --inactive-dim-fixed
lcfg_lookup_bool(&cfg, "inactive-dim-fixed", &ps->o.inactive_dim_fixed);
+ // --detect-transient
+ lcfg_lookup_bool(&cfg, "detect-transient", &ps->o.detect_transient);
+ // --detect-client-leader
+ lcfg_lookup_bool(&cfg, "detect-client-leader",
+ &ps->o.detect_client_leader);
// --shadow-exclude
parse_cfg_condlst(&cfg, &ps->o.shadow_blacklist, "shadow-exclude");
// --focus-exclude
@@ -4016,6 +4088,8 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
{ "unredir-if-possible", no_argument, NULL, 278 },
{ "focus-exclude", required_argument, NULL, 279 },
{ "inactive-dim-fixed", no_argument, NULL, 280 },
+ { "detect-transient", no_argument, NULL, 281 },
+ { "detect-client-leader", no_argument, NULL, 282 },
// Must terminate with a NULL entry
{ NULL, 0, NULL, 0 },
};
@@ -4225,6 +4299,14 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
// --inactive-dim-fixed
ps->o.inactive_dim_fixed = true;
break;
+ case 281:
+ // --detect-transient
+ ps->o.detect_transient = true;
+ break;
+ case 282:
+ // --detect-client-leader
+ ps->o.detect_client_leader = true;
+ break;
default:
usage();
}
@@ -4274,6 +4356,12 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
if (ps->o.shadow_blacklist || ps->o.fade_blacklist
|| ps->o.focus_blacklist)
ps->o.track_wdata = true;
+
+ // Determine whether we track window grouping
+ if (ps->o.detect_transient || ps->o.detect_client_leader) {
+ ps->o.track_leader = true;
+ }
+
}
/**
@@ -4289,6 +4377,7 @@ init_atoms(session_t *ps) {
ps->atom_class = XA_WM_CLASS;
ps->atom_role = XInternAtom(ps->dpy, "WM_WINDOW_ROLE", False);
ps->atom_transient = XA_WM_TRANSIENT_FOR;
+ ps->atom_client_leader = XInternAtom(ps->dpy, "WM_CLIENT_LEADER", False);
ps->atom_ewmh_active_win = XInternAtom(ps->dpy, "_NET_ACTIVE_WINDOW", False);
ps->atom_compton_shadow = XInternAtom(ps->dpy, "_COMPTON_SHADOW", False);
@@ -4759,9 +4848,12 @@ session_init(session_t *ps_old, int argc, char **argv) {
.wintype_focus = { false },
.use_ewmh_active_win = false,
.focus_blacklist = NULL,
+ .detect_transient = false,
+ .detect_client_leader = false,
.track_focus = false,
.track_wdata = false,
+ .track_leader = false,
},
.all_damage = None,
@@ -4782,6 +4874,8 @@ session_init(session_t *ps_old, int argc, char **argv) {
.list = NULL,
.active_win = NULL,
+ .active_leader = None,
+
.black_picture = None,
.cshadow_picture = None,
.gaussian_map = NULL,