]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
Merge branch 'backport' into work
[chaz/openbox] / openbox / focus.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 focus.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "event.h"
22 #include "openbox.h"
23 #include "grab.h"
24 #include "client.h"
25 #include "config.h"
26 #include "group.h"
27 #include "focus_cycle.h"
28 #include "screen.h"
29 #include "keyboard.h"
30 #include "hooks.h"
31 #include "focus.h"
32 #include "stacking.h"
33 #include "obt/prop.h"
34
35 #include <X11/Xlib.h>
36 #include <glib.h>
37
38 #define FOCUS_INDICATOR_WIDTH 6
39
40 ObClient *focus_client = NULL;
41 GList *focus_order = NULL;
42
43 void focus_startup(gboolean reconfig)
44 {
45 if (reconfig) return;
46
47 /* start with nothing focused */
48 focus_nothing();
49 }
50
51 void focus_shutdown(gboolean reconfig)
52 {
53 if (reconfig) return;
54
55 /* reset focus to root */
56 XSetInputFocus(obt_display, PointerRoot, RevertToNone, CurrentTime);
57 }
58
59 static void push_to_top(ObClient *client)
60 {
61 ObClient *p;
62
63 /* if it is modal for a single window, then put that window at the top
64 of the focus order first, so it will be right after ours. the same is
65 done with stacking */
66 if (client->modal && (p = client_direct_parent(client)))
67 push_to_top(p);
68
69 focus_order = g_list_remove(focus_order, client);
70 focus_order = g_list_prepend(focus_order, client);
71 }
72
73 void focus_set_client(ObClient *client)
74 {
75 Window active;
76 ObClient *old;
77
78 ob_debug_type(OB_DEBUG_FOCUS,
79 "focus_set_client 0x%lx", client ? client->window : 0);
80
81 if (focus_client == client)
82 return;
83
84 /* uninstall the old colormap, and install the new one */
85 screen_install_colormap(focus_client, FALSE);
86 screen_install_colormap(client, TRUE);
87
88 /* in the middle of cycling..? kill it. */
89 focus_cycle_stop(focus_client);
90 focus_cycle_stop(client);
91
92 old = focus_client;
93 focus_client = client;
94
95 if (client != NULL) {
96 /* move to the top of the list */
97 push_to_top(client);
98 /* remove hiliting from the window when it gets focused */
99 client_hilite(client, FALSE);
100 }
101
102 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
103 if (ob_state() != OB_STATE_EXITING) {
104 active = client ? client->window : None;
105 OBT_PROP_SET32(obt_root(ob_screen), NET_ACTIVE_WINDOW, WINDOW, active);
106 }
107
108 hooks_queue(OB_HOOK_WIN_UNFOCUS, old);
109 hooks_queue(OB_HOOK_WIN_FOCUS, client);
110 }
111
112 static ObClient* focus_fallback_target(gboolean allow_refocus,
113 gboolean allow_pointer,
114 gboolean allow_omnipresent,
115 ObClient *old)
116 {
117 GList *it;
118 ObClient *c;
119
120 ob_debug_type(OB_DEBUG_FOCUS, "trying pointer stuff");
121 if (allow_pointer && config_focus_follow)
122 if ((c = client_under_pointer()) &&
123 (allow_refocus || client_focus_target(c) != old) &&
124 (client_normal(c) &&
125 client_focus(c)))
126 {
127 ob_debug_type(OB_DEBUG_FOCUS, "found in pointer stuff");
128 return c;
129 }
130
131 ob_debug_type(OB_DEBUG_FOCUS, "trying the focus order");
132 for (it = focus_order; it; it = g_list_next(it)) {
133 c = it->data;
134 /* fallback focus to a window if:
135 1. it is on the current desktop. this ignores omnipresent
136 windows, which are problematic in their own rite, unless they are
137 specifically allowed
138 2. it is a valid auto-focus target
139 3. it is not shaded
140 */
141 if ((allow_omnipresent || c->desktop == screen_desktop) &&
142 focus_valid_target(c, TRUE, FALSE, FALSE, FALSE, FALSE) &&
143 !c->shaded &&
144 (allow_refocus || client_focus_target(c) != old) &&
145 client_focus(c))
146 {
147 ob_debug_type(OB_DEBUG_FOCUS, "found in focus order");
148 return c;
149 }
150 }
151
152 ob_debug_type(OB_DEBUG_FOCUS, "trying a desktop window");
153 for (it = focus_order; it; it = g_list_next(it)) {
154 c = it->data;
155 /* fallback focus to a window if:
156 1. it is on the current desktop. this ignores omnipresent
157 windows, which are problematic in their own rite.
158 2. it is a normal type window, don't fall back onto a dock or
159 a splashscreen or a desktop window (save the desktop as a
160 backup fallback though)
161 */
162 if (focus_valid_target(c, TRUE, FALSE, FALSE, FALSE, TRUE) &&
163 (allow_refocus || client_focus_target(c) != old) &&
164 client_focus(c))
165 {
166 ob_debug_type(OB_DEBUG_FOCUS, "found a desktop window");
167 return c;
168 }
169 }
170
171 return NULL;
172 }
173
174 ObClient* focus_fallback(gboolean allow_refocus, gboolean allow_pointer,
175 gboolean allow_omnipresent, gboolean focus_lost)
176 {
177 ObClient *new;
178 ObClient *old = focus_client;
179
180 /* unfocus any focused clients.. they can be focused by Pointer events
181 and such, and then when we try focus them, we won't get a FocusIn
182 event at all for them. */
183 if (focus_lost)
184 focus_nothing();
185
186 new = focus_fallback_target(allow_refocus, allow_pointer,
187 allow_omnipresent, old);
188 /* get what was really focused */
189 if (new) new = client_focus_target(new);
190
191 return new;
192 }
193
194 void focus_nothing(void)
195 {
196 /* Install our own colormap */
197 if (focus_client != NULL) {
198 screen_install_colormap(focus_client, FALSE);
199 screen_install_colormap(NULL, TRUE);
200 }
201
202 /* nothing is focused, update the colormap and _the root property_ */
203 focus_set_client(NULL);
204
205 event_cancel_all_key_grabs();
206
207 /* when nothing will be focused, send focus to the backup target */
208 XSetInputFocus(obt_display, screen_support_win, RevertToPointerRoot,
209 event_curtime);
210 }
211
212 void focus_order_add_new(ObClient *c)
213 {
214 if (c->iconic)
215 focus_order_to_top(c);
216 else {
217 g_assert(!g_list_find(focus_order, c));
218 /* if there are any iconic windows, put this above them in the order,
219 but if there are not, then put it under the currently focused one */
220 if (focus_order && ((ObClient*)focus_order->data)->iconic)
221 focus_order = g_list_insert(focus_order, c, 0);
222 else
223 focus_order = g_list_insert(focus_order, c, 1);
224 }
225
226 /* in the middle of cycling..? kill it. */
227 focus_cycle_stop(c);
228 }
229
230 void focus_order_remove(ObClient *c)
231 {
232 focus_order = g_list_remove(focus_order, c);
233
234 /* in the middle of cycling..? kill it. */
235 focus_cycle_stop(c);
236 }
237
238 void focus_order_to_top(ObClient *c)
239 {
240 focus_order = g_list_remove(focus_order, c);
241 if (!c->iconic) {
242 focus_order = g_list_prepend(focus_order, c);
243 } else {
244 GList *it;
245
246 /* insert before first iconic window */
247 for (it = focus_order;
248 it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
249 focus_order = g_list_insert_before(focus_order, it, c);
250 }
251 }
252
253 void focus_order_to_bottom(ObClient *c)
254 {
255 focus_order = g_list_remove(focus_order, c);
256 if (c->iconic) {
257 focus_order = g_list_append(focus_order, c);
258 } else {
259 GList *it;
260
261 /* insert before first iconic window */
262 for (it = focus_order;
263 it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
264 focus_order = g_list_insert_before(focus_order, it, c);
265 }
266 }
267
268 ObClient *focus_order_find_first(guint desktop)
269 {
270 GList *it;
271 for (it = focus_order; it; it = g_list_next(it)) {
272 ObClient *c = it->data;
273 if (c->desktop == desktop || c->desktop == DESKTOP_ALL)
274 return c;
275 }
276 return NULL;
277 }
278
279 /*! Returns if a focus target has valid group siblings that can be cycled
280 to in its place */
281 static gboolean focus_target_has_siblings(ObClient *ft,
282 gboolean iconic_windows,
283 gboolean all_desktops)
284
285 {
286 GSList *it;
287
288 if (!ft->group) return FALSE;
289
290 for (it = ft->group->members; it; it = g_slist_next(it)) {
291 ObClient *c = it->data;
292 /* check that it's not a helper window to avoid infinite recursion */
293 if (c != ft && c->type == OB_CLIENT_TYPE_NORMAL &&
294 focus_valid_target(c, TRUE, iconic_windows, all_desktops,
295 FALSE, FALSE))
296 {
297 return TRUE;
298 }
299 }
300 return FALSE;
301 }
302
303 gboolean focus_valid_target(ObClient *ft,
304 gboolean helper_windows,
305 gboolean iconic_windows,
306 gboolean all_desktops,
307 gboolean dock_windows,
308 gboolean desktop_windows)
309 {
310 gboolean ok = FALSE;
311
312 /* it's on this desktop unless you want all desktops.
313
314 do this check first because it will usually filter out the most
315 windows */
316 ok = (all_desktops || ft->desktop == screen_desktop ||
317 ft->desktop == DESKTOP_ALL);
318
319 /* the window can receive focus somehow */
320 ok = ok && (ft->can_focus || ft->focus_notify);
321
322 /* the window is not iconic, or we're allowed to go to iconic ones */
323 ok = ok && (iconic_windows || !ft->iconic);
324
325 /* it's the right type of window */
326 if (dock_windows || desktop_windows)
327 ok = ok && ((dock_windows && ft->type == OB_CLIENT_TYPE_DOCK) ||
328 (desktop_windows && ft->type == OB_CLIENT_TYPE_DESKTOP));
329 /* modal windows are important and can always get focus if they are
330 visible and stuff, so don't change 'ok' based on their type */
331 else if (!ft->modal)
332 /* normal non-helper windows are valid targets */
333 ok = ok &&
334 ((client_normal(ft) && !client_helper(ft))
335 ||
336 /* helper windows are valid targets if... */
337 (client_helper(ft) &&
338 /* ...a window in its group already has focus and we want to
339 include helper windows ... */
340 ((focus_client && ft->group == focus_client->group &&
341 helper_windows) ||
342 /* ... or if there are no other windows in its group
343 that can be focused instead */
344 !focus_target_has_siblings(ft, iconic_windows, all_desktops))));
345
346 /* it's not set to skip the taskbar (but this only applies to normal typed
347 windows, and is overridden if the window is modal) */
348 ok = ok && (ft->type != OB_CLIENT_TYPE_NORMAL ||
349 ft->modal ||
350 !ft->skip_taskbar);
351
352 /* it's not going to just send focus off somewhere else (modal window),
353 unless that modal window is not one of our valid targets, then let
354 you choose this window and bring the modal one here */
355 {
356 ObClient *cft = client_focus_target(ft);
357 ok = ok && (ft == cft || !focus_valid_target(cft,
358 TRUE,
359 iconic_windows,
360 all_desktops,
361 dock_windows,
362 desktop_windows));
363 }
364
365 return ok;
366 }
367
This page took 0.050615 seconds and 5 git commands to generate.