]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
add <dialog><focus/><desktop/> to allow disabling popups
[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) &&
225 /* dont fall back to 'anonymous' fullscreen windows. theres no
226 checks for this is in transient/group fallbacks, so they can
227 be fallback targets there. */
228 !((ObClient*)it->data)->fullscreen &&
229 client_can_focus(it->data)) {
230 gboolean r = client_focus(it->data);
231 assert(r);
232 return;
233 }
234
235 /* nothing to focus, and already set it to none above */
236 }
237
238 static void popup_cycle(ObClient *c, gboolean show)
239 {
240 if (!show || !config_dialog_focus) {
241 icon_popup_hide(focus_cycle_popup);
242 } else {
243 Rect *a;
244 ObClient *p = c;
245 char *title;
246
247 a = screen_physical_area_monitor(0);
248 icon_popup_position(focus_cycle_popup, CenterGravity,
249 a->x + a->width / 2, a->y + a->height / 2);
250 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
251 icon_popup_show(focus_cycle_popup, c->title,
252 client_icon(c, a->height/16, a->height/16));
253 */
254 /* XXX the size and the font extents need to be related on some level
255 */
256 icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
257
258 /* use the transient's parent's title/icon */
259 while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
260 p = p->transient_for;
261
262 if (p == c)
263 title = NULL;
264 else
265 title = g_strconcat((c->iconic ? c->icon_title : c->title),
266 " - ",
267 (p->iconic ? p->icon_title : p->title),
268 NULL);
269
270 icon_popup_show(focus_cycle_popup,
271 (title ? title :
272 (c->iconic ? c->icon_title : c->title)),
273 client_icon(p, 48, 48));
274 g_free(title);
275 }
276 }
277
278 static gboolean valid_focus_target(ObClient *ft)
279 {
280 /* we don't use client_can_focus here, because that doesn't let you
281 focus an iconic window, but we want to be able to, so we just check
282 if the focus flags on the window allow it, and its on the current
283 desktop */
284 return (ft->transients == NULL && client_normal(ft) &&
285 ((ft->can_focus || ft->focus_notify) &&
286 !ft->skip_taskbar &&
287 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)));
288 }
289
290 void focus_cycle(gboolean forward, gboolean linear,
291 gboolean dialog, gboolean done, gboolean cancel)
292 {
293 static ObClient *first = NULL;
294 static ObClient *t = NULL;
295 static GList *order = NULL;
296 GList *it, *start, *list;
297 ObClient *ft = NULL;
298
299 if (cancel) {
300 if (focus_cycle_target)
301 frame_adjust_focus(focus_cycle_target->frame, FALSE);
302 if (focus_client)
303 frame_adjust_focus(focus_client->frame, TRUE);
304 focus_cycle_target = NULL;
305 goto done_cycle;
306 } else if (done && dialog) {
307 goto done_cycle;
308 }
309
310 if (!focus_order[screen_desktop])
311 goto done_cycle;
312
313 if (!first) first = focus_client;
314 if (!focus_cycle_target) focus_cycle_target = focus_client;
315
316 if (linear) list = client_list;
317 else list = focus_order[screen_desktop];
318
319 start = it = g_list_find(list, focus_cycle_target);
320 if (!start) /* switched desktops or something? */
321 start = it = forward ? g_list_last(list) : g_list_first(list);
322 if (!start) goto done_cycle;
323
324 do {
325 if (forward) {
326 it = it->next;
327 if (it == NULL) it = g_list_first(list);
328 } else {
329 it = it->prev;
330 if (it == NULL) it = g_list_last(list);
331 }
332 /*ft = client_focus_target(it->data);*/
333 ft = it->data;
334 if (valid_focus_target(ft)) {
335 if (ft != focus_cycle_target) { /* prevents flicker */
336 if (focus_cycle_target)
337 frame_adjust_focus(focus_cycle_target->frame, FALSE);
338 focus_cycle_target = ft;
339 frame_adjust_focus(focus_cycle_target->frame, TRUE);
340 }
341 popup_cycle(ft, dialog);
342 return;
343 }
344 } while (it != start);
345
346 done_cycle:
347 if (done && focus_cycle_target)
348 client_activate(focus_cycle_target, FALSE);
349
350 t = NULL;
351 first = NULL;
352 focus_cycle_target = NULL;
353 g_list_free(order);
354 order = NULL;
355
356 popup_cycle(ft, FALSE);
357
358 return;
359 }
360
361 void focus_directional_cycle(ObDirection dir,
362 gboolean dialog, gboolean done, gboolean cancel)
363 {
364 static ObClient *first = NULL;
365 ObClient *ft = NULL;
366
367 if (cancel) {
368 if (focus_cycle_target)
369 frame_adjust_focus(focus_cycle_target->frame, FALSE);
370 if (focus_client)
371 frame_adjust_focus(focus_client->frame, TRUE);
372 focus_cycle_target = NULL;
373 goto done_cycle;
374 } else if (done && dialog) {
375 goto done_cycle;
376 }
377
378 if (!focus_order[screen_desktop])
379 goto done_cycle;
380
381 if (!first) first = focus_client;
382 if (!focus_cycle_target) focus_cycle_target = focus_client;
383
384 if (focus_cycle_target)
385 ft = client_find_directional(focus_cycle_target, dir);
386 else {
387 GList *it;
388
389 for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
390 if (valid_focus_target(it->data))
391 ft = it->data;
392 }
393
394 if (ft) {
395 if (ft != focus_cycle_target) {/* prevents flicker */
396 if (focus_cycle_target)
397 frame_adjust_focus(focus_cycle_target->frame, FALSE);
398 focus_cycle_target = ft;
399 frame_adjust_focus(focus_cycle_target->frame, TRUE);
400 }
401 }
402 if (focus_cycle_target) {
403 popup_cycle(focus_cycle_target, dialog);
404 if (dialog)
405 return;
406 }
407
408
409 done_cycle:
410 if (done && focus_cycle_target)
411 client_activate(focus_cycle_target, FALSE);
412
413 first = NULL;
414 focus_cycle_target = NULL;
415
416 popup_cycle(ft, FALSE);
417
418 return;
419 }
420
421 void focus_order_add_new(ObClient *c)
422 {
423 guint d, i;
424
425 if (c->iconic)
426 focus_order_to_top(c);
427 else {
428 d = c->desktop;
429 if (d == DESKTOP_ALL) {
430 for (i = 0; i < screen_num_desktops; ++i) {
431 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
432 focus_order[i] = g_list_insert(focus_order[i], c, 0);
433 else
434 focus_order[i] = g_list_insert(focus_order[i], c, 1);
435 }
436 } else
437 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
438 focus_order[d] = g_list_insert(focus_order[d], c, 0);
439 else
440 focus_order[d] = g_list_insert(focus_order[d], c, 1);
441 }
442 }
443
444 void focus_order_remove(ObClient *c)
445 {
446 guint d, i;
447
448 d = c->desktop;
449 if (d == DESKTOP_ALL) {
450 for (i = 0; i < screen_num_desktops; ++i)
451 focus_order[i] = g_list_remove(focus_order[i], c);
452 } else
453 focus_order[d] = g_list_remove(focus_order[d], c);
454 }
455
456 static void to_top(ObClient *c, guint d)
457 {
458 focus_order[d] = g_list_remove(focus_order[d], c);
459 if (!c->iconic) {
460 focus_order[d] = g_list_prepend(focus_order[d], c);
461 } else {
462 GList *it;
463
464 /* insert before first iconic window */
465 for (it = focus_order[d];
466 it && !((ObClient*)it->data)->iconic; it = it->next);
467 focus_order[d] = g_list_insert_before(focus_order[d], it, c);
468 }
469 }
470
471 void focus_order_to_top(ObClient *c)
472 {
473 guint d, i;
474
475 d = c->desktop;
476 if (d == DESKTOP_ALL) {
477 for (i = 0; i < screen_num_desktops; ++i)
478 to_top(c, i);
479 } else
480 to_top(c, d);
481 }
482
483 static void to_bottom(ObClient *c, guint d)
484 {
485 focus_order[d] = g_list_remove(focus_order[d], c);
486 if (c->iconic) {
487 focus_order[d] = g_list_append(focus_order[d], c);
488 } else {
489 GList *it;
490
491 /* insert before first iconic window */
492 for (it = focus_order[d];
493 it && !((ObClient*)it->data)->iconic; it = it->next);
494 g_list_insert_before(focus_order[d], it, c);
495 }
496 }
497
498 void focus_order_to_bottom(ObClient *c)
499 {
500 guint d, i;
501
502 d = c->desktop;
503 if (d == DESKTOP_ALL) {
504 for (i = 0; i < screen_num_desktops; ++i)
505 to_bottom(c, i);
506 } else
507 to_bottom(c, d);
508 }
This page took 0.052577 seconds and 4 git commands to generate.