]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
indenting, focus fallback to None not PointerRoot
[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 *client, gpointer data)
46 {
47 /* end cycling if the target disappears */
48 if (focus_cycle_target == client)
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(focus_cycle_destructor, NULL);
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(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, RevertToNone, event_lasttime);
79 }
80 }
81
82 static void push_to_top(ObClient *client)
83 {
84 guint desktop;
85
86 desktop = client->desktop;
87 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
88 focus_order[desktop] = g_list_remove(focus_order[desktop], client);
89 focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
90 }
91
92 void focus_set_client(ObClient *client)
93 {
94 Window active;
95 ObClient *old;
96
97 #ifdef DEBUG_FOCUS
98 ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
99 #endif
100
101 /* uninstall the old colormap, and install the new one */
102 screen_install_colormap(focus_client, FALSE);
103 screen_install_colormap(client, TRUE);
104
105 if (client == NULL) {
106 #ifdef DEBUG_FOCUS
107 ob_debug("actively focusing NONWINDOW\n");
108 #endif
109 /* when nothing will be focused, send focus to the backup target */
110 XSetInputFocus(ob_display, screen_support_win, RevertToNone,
111 event_lasttime);
112 XSync(ob_display, FALSE);
113 }
114
115 /* in the middle of cycling..? kill it. */
116 if (focus_cycle_target)
117 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
118
119 old = focus_client;
120 focus_client = client;
121
122 /* move to the top of the list */
123 if (client != NULL)
124 push_to_top(client);
125
126 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
127 if (ob_state() != OB_STATE_EXITING) {
128 active = client ? client->window : None;
129 PROP_SET32(RootWindow(ob_display, ob_screen),
130 net_active_window, window, active);
131 }
132 }
133
134 static gboolean focus_under_pointer()
135 {
136 ObClient *c;
137
138 if ((c = client_under_pointer()))
139 return client_normal(c) && client_focus(c);
140 return FALSE;
141 }
142
143 /* finds the first transient that isn't 'skip' and ensure's that client_normal
144 is true for it */
145 static ObClient *find_transient_recursive(ObClient *c, ObClient *top, ObClient *skip)
146 {
147 GSList *it;
148 ObClient *ret;
149
150 for (it = c->transients; it; it = it->next) {
151 if (it->data == top) return NULL;
152 ret = find_transient_recursive(it->data, top, skip);
153 if (ret && ret != skip && client_normal(ret)) return ret;
154 if (it->data != skip && client_normal(it->data)) return it->data;
155 }
156 return NULL;
157 }
158
159 static gboolean focus_fallback_transient(ObClient *top, ObClient *old)
160 {
161 ObClient *target = find_transient_recursive(top, top, old);
162 if (!target) {
163 /* make sure client_normal is true always */
164 if (!client_normal(top))
165 return FALSE;
166 target = top; /* no transient, keep the top */
167 }
168 return client_focus(target);
169 }
170
171 void focus_fallback(ObFocusFallbackType type)
172 {
173 GList *it;
174 ObClient *old = NULL;
175
176 old = focus_client;
177
178 /* unfocus any focused clients.. they can be focused by Pointer events
179 and such, and then when I try focus them, I won't get a FocusIn event
180 at all for them.
181 */
182 focus_set_client(NULL);
183
184 if (type == OB_FOCUS_FALLBACK_UNFOCUSING && old) {
185 if (old->transient_for) {
186 gboolean trans = FALSE;
187
188 if (!config_focus_follow)
189 trans = TRUE;
190 else {
191 ObClient *c;
192
193 if ((c = client_under_pointer()) &&
194 client_search_transient(client_search_top_transient(c),
195 old))
196 trans = TRUE;
197 }
198
199 g_message("trans %d", trans);
200
201 /* try for transient relations */
202 if (trans) {
203 if (old->transient_for == OB_TRAN_GROUP) {
204 for (it = focus_order[screen_desktop]; it; it = it->next) {
205 GSList *sit;
206
207 for (sit = old->group->members; sit; sit = sit->next)
208 if (sit->data == it->data)
209 if (focus_fallback_transient(sit->data, old))
210 return;
211 }
212 } else {
213 if (focus_fallback_transient(old->transient_for, old))
214 return;
215 }
216 }
217 }
218 }
219
220 if (config_focus_follow)
221 if (focus_under_pointer())
222 return;
223
224 #if 0
225 /* try for group relations */
226 if (old->group) {
227 GSList *sit;
228
229 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
230 for (sit = old->group->members; sit; sit = sit->next)
231 if (sit->data == it->data)
232 if (sit->data != old && client_normal(sit->data))
233 if (client_can_focus(sit->data)) {
234 gboolean r = client_focus(sit->data);
235 assert(r);
236 return;
237 }
238 }
239 #endif
240
241 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
242 if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
243 if (client_normal(it->data) && client_can_focus(it->data)) {
244 gboolean r = client_focus(it->data);
245 assert(r);
246 return;
247 }
248
249 /* nothing to focus, and already set it to none above */
250 }
251
252 static void popup_cycle(ObClient *c, gboolean show)
253 {
254 if (!show) {
255 icon_popup_hide(focus_cycle_popup);
256 } else {
257 Rect *a;
258 ObClient *p = c;
259 char *title;
260
261 a = screen_physical_area_monitor(0);
262 icon_popup_position(focus_cycle_popup, CenterGravity,
263 a->x + a->width / 2, a->y + a->height / 2);
264 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
265 icon_popup_show(focus_cycle_popup, c->title,
266 client_icon(c, a->height/16, a->height/16));
267 */
268 /* XXX the size and the font extents need to be related on some level
269 */
270 icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
271
272 /* use the transient's parent's title/icon */
273 while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
274 p = p->transient_for;
275
276 if (p == c)
277 title = NULL;
278 else
279 title = g_strconcat((c->iconic ? c->icon_title : c->title),
280 " - ",
281 (p->iconic ? p->icon_title : p->title),
282 NULL);
283
284 icon_popup_show(focus_cycle_popup,
285 (title ? title :
286 (c->iconic ? c->icon_title : c->title)),
287 client_icon(p, 48, 48));
288 g_free(title);
289 }
290 }
291
292 static gboolean valid_focus_target(ObClient *ft)
293 {
294 /* we don't use client_can_focus here, because that doesn't let you
295 focus an iconic window, but we want to be able to, so we just check
296 if the focus flags on the window allow it, and its on the current
297 desktop */
298 return (!ft->transients && client_normal(ft) &&
299 ((ft->can_focus || ft->focus_notify) &&
300 !ft->skip_taskbar &&
301 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)));
302 }
303
304 void focus_cycle(gboolean forward, gboolean linear,
305 gboolean dialog, gboolean done, gboolean cancel)
306 {
307 static ObClient *first = NULL;
308 static ObClient *t = NULL;
309 static GList *order = NULL;
310 GList *it, *start, *list;
311 ObClient *ft = NULL;
312
313 if (cancel) {
314 if (focus_cycle_target)
315 frame_adjust_focus(focus_cycle_target->frame, FALSE);
316 if (focus_client)
317 frame_adjust_focus(focus_client->frame, TRUE);
318 focus_cycle_target = NULL;
319 goto done_cycle;
320 } else if (done && dialog) {
321 goto done_cycle;
322 }
323
324 if (!focus_order[screen_desktop])
325 goto done_cycle;
326
327 if (!first) first = focus_client;
328 if (!focus_cycle_target) focus_cycle_target = focus_client;
329
330 if (linear) list = client_list;
331 else list = focus_order[screen_desktop];
332
333 start = it = g_list_find(list, focus_cycle_target);
334 if (!start) /* switched desktops or something? */
335 start = it = forward ? g_list_last(list) : g_list_first(list);
336 if (!start) goto done_cycle;
337
338 do {
339 if (forward) {
340 it = it->next;
341 if (it == NULL) it = g_list_first(list);
342 } else {
343 it = it->prev;
344 if (it == NULL) it = g_list_last(list);
345 }
346 ft = it->data;
347 if (valid_focus_target(ft)) {
348 if (ft != focus_cycle_target) { /* prevents flicker */
349 if (focus_cycle_target)
350 frame_adjust_focus(focus_cycle_target->frame, FALSE);
351 focus_cycle_target = ft;
352 frame_adjust_focus(focus_cycle_target->frame, TRUE);
353 }
354 popup_cycle(ft, dialog);
355 return;
356 }
357 } while (it != start);
358
359 done_cycle:
360 if (done && focus_cycle_target)
361 client_activate(focus_cycle_target, FALSE);
362
363 t = NULL;
364 first = NULL;
365 focus_cycle_target = NULL;
366 g_list_free(order);
367 order = NULL;
368
369 popup_cycle(ft, FALSE);
370
371 return;
372 }
373
374 void focus_directional_cycle(ObDirection dir,
375 gboolean dialog, gboolean done, gboolean cancel)
376 {
377 static ObClient *first = NULL;
378 ObClient *ft = NULL;
379
380 if (cancel) {
381 if (focus_cycle_target)
382 frame_adjust_focus(focus_cycle_target->frame, FALSE);
383 if (focus_client)
384 frame_adjust_focus(focus_client->frame, TRUE);
385 focus_cycle_target = NULL;
386 goto done_cycle;
387 } else if (done && dialog) {
388 goto done_cycle;
389 }
390
391 if (!focus_order[screen_desktop])
392 goto done_cycle;
393
394 if (!first) first = focus_client;
395 if (!focus_cycle_target) focus_cycle_target = focus_client;
396
397 if (focus_cycle_target)
398 ft = client_find_directional(focus_cycle_target, dir);
399 else {
400 GList *it;
401
402 for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
403 if (valid_focus_target(it->data))
404 ft = it->data;
405 }
406
407 if (ft) {
408 if (ft != focus_cycle_target) {/* prevents flicker */
409 if (focus_cycle_target)
410 frame_adjust_focus(focus_cycle_target->frame, FALSE);
411 focus_cycle_target = ft;
412 frame_adjust_focus(focus_cycle_target->frame, TRUE);
413 }
414 }
415 if (focus_cycle_target) {
416 popup_cycle(focus_cycle_target, dialog);
417 if (dialog)
418 return;
419 }
420
421
422 done_cycle:
423 if (done && focus_cycle_target)
424 client_activate(focus_cycle_target, FALSE);
425
426 first = NULL;
427 focus_cycle_target = NULL;
428
429 popup_cycle(ft, FALSE);
430
431 return;
432 }
433
434 void focus_order_add_new(ObClient *c)
435 {
436 guint d, i;
437
438 if (c->iconic)
439 focus_order_to_top(c);
440 else {
441 d = c->desktop;
442 if (d == DESKTOP_ALL) {
443 for (i = 0; i < screen_num_desktops; ++i) {
444 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
445 focus_order[i] = g_list_insert(focus_order[i], c, 0);
446 else
447 focus_order[i] = g_list_insert(focus_order[i], c, 1);
448 }
449 } else
450 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
451 focus_order[d] = g_list_insert(focus_order[d], c, 0);
452 else
453 focus_order[d] = g_list_insert(focus_order[d], c, 1);
454 }
455 }
456
457 void focus_order_remove(ObClient *c)
458 {
459 guint d, i;
460
461 d = c->desktop;
462 if (d == DESKTOP_ALL) {
463 for (i = 0; i < screen_num_desktops; ++i)
464 focus_order[i] = g_list_remove(focus_order[i], c);
465 } else
466 focus_order[d] = g_list_remove(focus_order[d], c);
467 }
468
469 static void to_top(ObClient *c, guint d)
470 {
471 focus_order[d] = g_list_remove(focus_order[d], c);
472 if (!c->iconic) {
473 focus_order[d] = g_list_prepend(focus_order[d], c);
474 } else {
475 GList *it;
476
477 /* insert before first iconic window */
478 for (it = focus_order[d];
479 it && !((ObClient*)it->data)->iconic; it = it->next);
480 focus_order[d] = g_list_insert_before(focus_order[d], it, c);
481 }
482 }
483
484 void focus_order_to_top(ObClient *c)
485 {
486 guint d, i;
487
488 d = c->desktop;
489 if (d == DESKTOP_ALL) {
490 for (i = 0; i < screen_num_desktops; ++i)
491 to_top(c, i);
492 } else
493 to_top(c, d);
494 }
495
496 static void to_bottom(ObClient *c, guint d)
497 {
498 focus_order[d] = g_list_remove(focus_order[d], c);
499 if (c->iconic) {
500 focus_order[d] = g_list_append(focus_order[d], c);
501 } else {
502 GList *it;
503
504 /* insert before first iconic window */
505 for (it = focus_order[d];
506 it && !((ObClient*)it->data)->iconic; it = it->next);
507 g_list_insert_before(focus_order[d], it, c);
508 }
509 }
510
511 void focus_order_to_bottom(ObClient *c)
512 {
513 guint d, i;
514
515 d = c->desktop;
516 if (d == DESKTOP_ALL) {
517 for (i = 0; i < screen_num_desktops; ++i)
518 to_bottom(c, i);
519 } else
520 to_bottom(c, d);
521 }
This page took 0.060001 seconds and 5 git commands to generate.