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