]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
allow focus fallbacks to fullscreen windows
[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) 2003 Ben Jansens
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "debug.h"
20 #include "event.h"
21 #include "openbox.h"
22 #include "grab.h"
23 #include "framerender.h"
24 #include "client.h"
25 #include "config.h"
26 #include "frame.h"
27 #include "screen.h"
28 #include "group.h"
29 #include "prop.h"
30 #include "focus.h"
31 #include "stacking.h"
32 #include "popup.h"
33
34 #include <X11/Xlib.h>
35 #include <glib.h>
36 #include <assert.h>
37
38 ObClient *focus_client;
39 GList **focus_order; /* these lists are created when screen_startup
40 sets the number of desktops */
41 ObClient *focus_cycle_target;
42
43 static ObIconPopup *focus_cycle_popup;
44
45 static void focus_cycle_destructor(ObClient *c)
46 {
47 /* end cycling if the target disappears */
48 if (focus_cycle_target == c)
49 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
50 }
51
52 void focus_startup(gboolean reconfig)
53 {
54 focus_cycle_popup = icon_popup_new(TRUE);
55
56 if (!reconfig) {
57 client_add_destructor((GDestroyNotify) focus_cycle_destructor);
58
59 /* start with nothing focused */
60 focus_set_client(NULL);
61 }
62 }
63
64 void focus_shutdown(gboolean reconfig)
65 {
66 guint i;
67
68 icon_popup_free(focus_cycle_popup);
69
70 if (!reconfig) {
71 client_remove_destructor((GDestroyNotify) focus_cycle_destructor);
72
73 for (i = 0; i < screen_num_desktops; ++i)
74 g_list_free(focus_order[i]);
75 g_free(focus_order);
76
77 /* reset focus to root */
78 XSetInputFocus(ob_display, PointerRoot, RevertToPointerRoot,
79 event_lasttime);
80 }
81 }
82
83 static void push_to_top(ObClient *client)
84 {
85 guint desktop;
86
87 desktop = client->desktop;
88 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
89 focus_order[desktop] = g_list_remove(focus_order[desktop], client);
90 focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
91 }
92
93 void focus_set_client(ObClient *client)
94 {
95 Window active;
96 ObClient *old;
97
98 #ifdef DEBUG_FOCUS
99 ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
100 #endif
101
102 /* uninstall the old colormap, and install the new one */
103 screen_install_colormap(focus_client, FALSE);
104 screen_install_colormap(client, TRUE);
105
106 if (client == NULL) {
107 /* when nothing will be focused, send focus to the backup target */
108 XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
109 event_lasttime);
110 XSync(ob_display, FALSE);
111 }
112
113 /* in the middle of cycling..? kill it. */
114 if (focus_cycle_target)
115 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
116
117 old = focus_client;
118 focus_client = client;
119
120 /* move to the top of the list */
121 if (client != NULL)
122 push_to_top(client);
123
124 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
125 if (ob_state() != OB_STATE_EXITING) {
126 active = client ? client->window : None;
127 PROP_SET32(RootWindow(ob_display, ob_screen),
128 net_active_window, window, active);
129 }
130 }
131
132 static gboolean focus_under_pointer()
133 {
134 ObClient *c;
135
136 if ((c = client_under_pointer()))
137 return client_normal(c) && client_focus(c);
138 return FALSE;
139 }
140
141 /* finds the first transient that isn't 'skip' and ensure's that client_normal
142 is true for it */
143 static ObClient *find_transient_recursive(ObClient *c, ObClient *top, ObClient *skip)
144 {
145 GSList *it;
146 ObClient *ret;
147
148 for (it = c->transients; it; it = it->next) {
149 if (it->data == top) return NULL;
150 ret = find_transient_recursive(it->data, top, skip);
151 if (ret && ret != skip && client_normal(ret)) return ret;
152 if (it->data != skip && client_normal(it->data)) return it->data;
153 }
154 return NULL;
155 }
156
157 static gboolean focus_fallback_transient(ObClient *top, ObClient *old)
158 {
159 ObClient *target = find_transient_recursive(top, top, old);
160 if (!target) {
161 /* make sure client_normal is true always */
162 if (!client_normal(top))
163 return FALSE;
164 target = top; /* no transient, keep the top */
165 }
166 return client_focus(target);
167 }
168
169 void focus_fallback(ObFocusFallbackType type)
170 {
171 GList *it;
172 ObClient *old = NULL;
173
174 old = focus_client;
175
176 /* unfocus any focused clients.. they can be focused by Pointer events
177 and such, and then when I try focus them, I won't get a FocusIn event
178 at all for them.
179 */
180 focus_set_client(NULL);
181
182 if (!config_focus_last && config_focus_follow)
183 if (focus_under_pointer())
184 return;
185
186 if (type == OB_FOCUS_FALLBACK_UNFOCUSING && old) {
187 /* try for transient relations */
188 if (old->transient_for) {
189 if (old->transient_for == OB_TRAN_GROUP) {
190 for (it = focus_order[screen_desktop]; it; it = it->next) {
191 GSList *sit;
192
193 for (sit = old->group->members; sit; sit = sit->next)
194 if (sit->data == it->data)
195 if (focus_fallback_transient(sit->data, old))
196 return;
197 }
198 } else {
199 if (focus_fallback_transient(old->transient_for, old))
200 return;
201 }
202 }
203
204 #if 0
205 /* try for group relations */
206 if (old->group) {
207 GSList *sit;
208
209 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
210 for (sit = old->group->members; sit; sit = sit->next)
211 if (sit->data == it->data)
212 if (sit->data != old && client_normal(sit->data))
213 if (client_can_focus(sit->data)) {
214 gboolean r = client_focus(sit->data);
215 assert(r);
216 return;
217 }
218 }
219 #endif
220 }
221
222 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
223 if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
224 if (client_normal(it->data) && client_can_focus(it->data)) {
225 gboolean r = client_focus(it->data);
226 assert(r);
227 return;
228 }
229
230 /* nothing to focus, and already set it to none above */
231 }
232
233 static void popup_cycle(ObClient *c, gboolean show)
234 {
235 if (!show || !config_dialog_focus) {
236 icon_popup_hide(focus_cycle_popup);
237 } else {
238 Rect *a;
239 ObClient *p = c;
240 char *title;
241
242 a = screen_physical_area_monitor(0);
243 icon_popup_position(focus_cycle_popup, CenterGravity,
244 a->x + a->width / 2, a->y + a->height / 2);
245 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
246 icon_popup_show(focus_cycle_popup, c->title,
247 client_icon(c, a->height/16, a->height/16));
248 */
249 /* XXX the size and the font extents need to be related on some level
250 */
251 icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
252
253 /* use the transient's parent's title/icon */
254 while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
255 p = p->transient_for;
256
257 if (p == c)
258 title = NULL;
259 else
260 title = g_strconcat((c->iconic ? c->icon_title : c->title),
261 " - ",
262 (p->iconic ? p->icon_title : p->title),
263 NULL);
264
265 icon_popup_show(focus_cycle_popup,
266 (title ? title :
267 (c->iconic ? c->icon_title : c->title)),
268 client_icon(p, 48, 48));
269 g_free(title);
270 }
271 }
272
273 static gboolean valid_focus_target(ObClient *ft)
274 {
275 /* we don't use client_can_focus here, because that doesn't let you
276 focus an iconic window, but we want to be able to, so we just check
277 if the focus flags on the window allow it, and its on the current
278 desktop */
279 return (ft->transients == NULL && client_normal(ft) &&
280 ((ft->can_focus || ft->focus_notify) &&
281 !ft->skip_taskbar &&
282 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)));
283 }
284
285 void focus_cycle(gboolean forward, gboolean linear,
286 gboolean dialog, gboolean done, gboolean cancel)
287 {
288 static ObClient *first = NULL;
289 static ObClient *t = NULL;
290 static GList *order = NULL;
291 GList *it, *start, *list;
292 ObClient *ft = NULL;
293
294 if (cancel) {
295 if (focus_cycle_target)
296 frame_adjust_focus(focus_cycle_target->frame, FALSE);
297 if (focus_client)
298 frame_adjust_focus(focus_client->frame, TRUE);
299 focus_cycle_target = NULL;
300 goto done_cycle;
301 } else if (done && dialog) {
302 goto done_cycle;
303 }
304
305 if (!focus_order[screen_desktop])
306 goto done_cycle;
307
308 if (!first) first = focus_client;
309 if (!focus_cycle_target) focus_cycle_target = focus_client;
310
311 if (linear) list = client_list;
312 else list = focus_order[screen_desktop];
313
314 start = it = g_list_find(list, focus_cycle_target);
315 if (!start) /* switched desktops or something? */
316 start = it = forward ? g_list_last(list) : g_list_first(list);
317 if (!start) goto done_cycle;
318
319 do {
320 if (forward) {
321 it = it->next;
322 if (it == NULL) it = g_list_first(list);
323 } else {
324 it = it->prev;
325 if (it == NULL) it = g_list_last(list);
326 }
327 /*ft = client_focus_target(it->data);*/
328 ft = it->data;
329 if (valid_focus_target(ft)) {
330 if (ft != focus_cycle_target) { /* prevents flicker */
331 if (focus_cycle_target)
332 frame_adjust_focus(focus_cycle_target->frame, FALSE);
333 focus_cycle_target = ft;
334 frame_adjust_focus(focus_cycle_target->frame, TRUE);
335 }
336 popup_cycle(ft, dialog);
337 return;
338 }
339 } while (it != start);
340
341 done_cycle:
342 if (done && focus_cycle_target)
343 client_activate(focus_cycle_target, FALSE);
344
345 t = NULL;
346 first = NULL;
347 focus_cycle_target = NULL;
348 g_list_free(order);
349 order = NULL;
350
351 popup_cycle(ft, FALSE);
352
353 return;
354 }
355
356 void focus_directional_cycle(ObDirection dir,
357 gboolean dialog, gboolean done, gboolean cancel)
358 {
359 static ObClient *first = NULL;
360 ObClient *ft = NULL;
361
362 if (cancel) {
363 if (focus_cycle_target)
364 frame_adjust_focus(focus_cycle_target->frame, FALSE);
365 if (focus_client)
366 frame_adjust_focus(focus_client->frame, TRUE);
367 focus_cycle_target = NULL;
368 goto done_cycle;
369 } else if (done && dialog) {
370 goto done_cycle;
371 }
372
373 if (!focus_order[screen_desktop])
374 goto done_cycle;
375
376 if (!first) first = focus_client;
377 if (!focus_cycle_target) focus_cycle_target = focus_client;
378
379 if (focus_cycle_target)
380 ft = client_find_directional(focus_cycle_target, dir);
381 else {
382 GList *it;
383
384 for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
385 if (valid_focus_target(it->data))
386 ft = it->data;
387 }
388
389 if (ft) {
390 if (ft != focus_cycle_target) {/* prevents flicker */
391 if (focus_cycle_target)
392 frame_adjust_focus(focus_cycle_target->frame, FALSE);
393 focus_cycle_target = ft;
394 frame_adjust_focus(focus_cycle_target->frame, TRUE);
395 }
396 }
397 if (focus_cycle_target) {
398 popup_cycle(focus_cycle_target, dialog);
399 if (dialog)
400 return;
401 }
402
403
404 done_cycle:
405 if (done && focus_cycle_target)
406 client_activate(focus_cycle_target, FALSE);
407
408 first = NULL;
409 focus_cycle_target = NULL;
410
411 popup_cycle(ft, FALSE);
412
413 return;
414 }
415
416 void focus_order_add_new(ObClient *c)
417 {
418 guint d, i;
419
420 if (c->iconic)
421 focus_order_to_top(c);
422 else {
423 d = c->desktop;
424 if (d == DESKTOP_ALL) {
425 for (i = 0; i < screen_num_desktops; ++i) {
426 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
427 focus_order[i] = g_list_insert(focus_order[i], c, 0);
428 else
429 focus_order[i] = g_list_insert(focus_order[i], c, 1);
430 }
431 } else
432 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
433 focus_order[d] = g_list_insert(focus_order[d], c, 0);
434 else
435 focus_order[d] = g_list_insert(focus_order[d], c, 1);
436 }
437 }
438
439 void focus_order_remove(ObClient *c)
440 {
441 guint d, i;
442
443 d = c->desktop;
444 if (d == DESKTOP_ALL) {
445 for (i = 0; i < screen_num_desktops; ++i)
446 focus_order[i] = g_list_remove(focus_order[i], c);
447 } else
448 focus_order[d] = g_list_remove(focus_order[d], c);
449 }
450
451 static void to_top(ObClient *c, guint d)
452 {
453 focus_order[d] = g_list_remove(focus_order[d], c);
454 if (!c->iconic) {
455 focus_order[d] = g_list_prepend(focus_order[d], c);
456 } else {
457 GList *it;
458
459 /* insert before first iconic window */
460 for (it = focus_order[d];
461 it && !((ObClient*)it->data)->iconic; it = it->next);
462 focus_order[d] = g_list_insert_before(focus_order[d], it, c);
463 }
464 }
465
466 void focus_order_to_top(ObClient *c)
467 {
468 guint d, i;
469
470 d = c->desktop;
471 if (d == DESKTOP_ALL) {
472 for (i = 0; i < screen_num_desktops; ++i)
473 to_top(c, i);
474 } else
475 to_top(c, d);
476 }
477
478 static void to_bottom(ObClient *c, guint d)
479 {
480 focus_order[d] = g_list_remove(focus_order[d], c);
481 if (c->iconic) {
482 focus_order[d] = g_list_append(focus_order[d], c);
483 } else {
484 GList *it;
485
486 /* insert before first iconic window */
487 for (it = focus_order[d];
488 it && !((ObClient*)it->data)->iconic; it = it->next);
489 g_list_insert_before(focus_order[d], it, c);
490 }
491 }
492
493 void focus_order_to_bottom(ObClient *c)
494 {
495 guint d, i;
496
497 d = c->desktop;
498 if (d == DESKTOP_ALL) {
499 for (i = 0; i < screen_num_desktops; ++i)
500 to_bottom(c, i);
501 } else
502 to_bottom(c, d);
503 }
This page took 0.055721 seconds and 5 git commands to generate.