]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
6ca589b1efe0d1391b0a3a34d1bb934c3c1eee9e
[chaz/openbox] / openbox / focus.c
1 /* -*- indent-tabs-mode: t; 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 void focus_startup(gboolean reconfig)
46 {
47 focus_cycle_popup = icon_popup_new(TRUE);
48
49 if (!reconfig)
50 /* start with nothing focused */
51 focus_set_client(NULL);
52 }
53
54 void focus_shutdown(gboolean reconfig)
55 {
56 guint i;
57
58 icon_popup_free(focus_cycle_popup);
59
60 if (!reconfig) {
61 for (i = 0; i < screen_num_desktops; ++i)
62 g_list_free(focus_order[i]);
63 g_free(focus_order);
64
65 /* reset focus to root */
66 XSetInputFocus(ob_display, PointerRoot, RevertToPointerRoot,
67 event_lasttime);
68 }
69 }
70
71 static void push_to_top(ObClient *client)
72 {
73 guint desktop;
74
75 desktop = client->desktop;
76 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
77 focus_order[desktop] = g_list_remove(focus_order[desktop], client);
78 focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
79 }
80
81 void focus_set_client(ObClient *client)
82 {
83 Window active;
84 ObClient *old;
85
86 #ifdef DEBUG_FOCUS
87 ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
88 #endif
89
90 /* uninstall the old colormap, and install the new one */
91 screen_install_colormap(focus_client, FALSE);
92 screen_install_colormap(client, TRUE);
93
94 if (client == NULL) {
95 /* when nothing will be focused, send focus to the backup target */
96 XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
97 event_lasttime);
98 XSync(ob_display, FALSE);
99 }
100
101 /* in the middle of cycling..? kill it. */
102 if (focus_cycle_target)
103 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
104
105 old = focus_client;
106 focus_client = client;
107
108 /* move to the top of the list */
109 if (client != NULL)
110 push_to_top(client);
111
112 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
113 if (ob_state() != OB_STATE_EXITING) {
114 active = client ? client->window : None;
115 PROP_SET32(RootWindow(ob_display, ob_screen),
116 net_active_window, window, active);
117 }
118 }
119
120 static gboolean focus_under_pointer()
121 {
122 int x, y;
123 GList *it;
124
125 if (screen_pointer_pos(&x, &y)) {
126 for (it = stacking_list; it != NULL; it = it->next) {
127 if (WINDOW_IS_CLIENT(it->data)) {
128 ObClient *c = WINDOW_AS_CLIENT(it->data);
129 if (c->desktop == screen_desktop &&
130 RECT_CONTAINS(c->frame->area, x, y))
131 break;
132 }
133 }
134 if (it != NULL) {
135 g_assert(WINDOW_IS_CLIENT(it->data));
136 return client_normal(it->data) && client_focus(it->data);
137 }
138 }
139 return FALSE;
140 }
141
142 /* finds the first transient that isn't 'skip' and ensure's that client_normal
143 is true for it */
144 static ObClient *find_transient_recursive(ObClient *c, ObClient *top, ObClient *skip)
145 {
146 GSList *it;
147 ObClient *ret;
148
149 for (it = c->transients; it; it = it->next) {
150 if (it->data == top) return NULL;
151 ret = find_transient_recursive(it->data, top, skip);
152 if (ret && ret != skip && client_normal(ret)) return ret;
153 if (it->data != skip && client_normal(it->data)) return it->data;
154 }
155 return NULL;
156 }
157
158 static gboolean focus_fallback_transient(ObClient *top, ObClient *old)
159 {
160 ObClient *target = find_transient_recursive(top, top, old);
161 if (!target) {
162 /* make sure client_normal is true always */
163 if (!client_normal(top))
164 return FALSE;
165 target = top; /* no transient, keep the top */
166 }
167 return client_focus(target);
168 }
169
170 void focus_fallback(ObFocusFallbackType type)
171 {
172 GList *it;
173 ObClient *old = NULL;
174
175 old = focus_client;
176
177 /* unfocus any focused clients.. they can be focused by Pointer events
178 and such, and then when I try focus them, I won't get a FocusIn event
179 at all for them.
180 */
181 focus_set_client(NULL);
182
183 if (config_focus_follow && 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) {
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;
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 popup_cycle(ft, dialog);
402 if (dialog)
403 return;
404 }
405
406 done_cycle:
407 if (done && focus_cycle_target)
408 client_activate(focus_cycle_target, FALSE);
409
410 first = NULL;
411 focus_cycle_target = NULL;
412
413 popup_cycle(ft, FALSE);
414
415 return;
416 }
417
418 void focus_order_add_new(ObClient *c)
419 {
420 guint d, i;
421
422 if (c->iconic)
423 focus_order_to_top(c);
424 else {
425 d = c->desktop;
426 if (d == DESKTOP_ALL) {
427 for (i = 0; i < screen_num_desktops; ++i) {
428 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
429 focus_order[i] = g_list_insert(focus_order[i], c, 0);
430 else
431 focus_order[i] = g_list_insert(focus_order[i], c, 1);
432 }
433 } else
434 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
435 focus_order[d] = g_list_insert(focus_order[d], c, 0);
436 else
437 focus_order[d] = g_list_insert(focus_order[d], c, 1);
438 }
439 }
440
441 void focus_order_remove(ObClient *c)
442 {
443 guint d, i;
444
445 d = c->desktop;
446 if (d == DESKTOP_ALL) {
447 for (i = 0; i < screen_num_desktops; ++i)
448 focus_order[i] = g_list_remove(focus_order[i], c);
449 } else
450 focus_order[d] = g_list_remove(focus_order[d], c);
451 }
452
453 static void to_top(ObClient *c, guint d)
454 {
455 focus_order[d] = g_list_remove(focus_order[d], c);
456 if (!c->iconic) {
457 focus_order[d] = g_list_prepend(focus_order[d], c);
458 } else {
459 GList *it;
460
461 /* insert before first iconic window */
462 for (it = focus_order[d];
463 it && !((ObClient*)it->data)->iconic; it = it->next);
464 focus_order[d] = g_list_insert_before(focus_order[d], it, c);
465 }
466 }
467
468 void focus_order_to_top(ObClient *c)
469 {
470 guint d, i;
471
472 d = c->desktop;
473 if (d == DESKTOP_ALL) {
474 for (i = 0; i < screen_num_desktops; ++i)
475 to_top(c, i);
476 } else
477 to_top(c, d);
478 }
479
480 static void to_bottom(ObClient *c, guint d)
481 {
482 focus_order[d] = g_list_remove(focus_order[d], c);
483 if (c->iconic) {
484 focus_order[d] = g_list_append(focus_order[d], c);
485 } else {
486 GList *it;
487
488 /* insert before first iconic window */
489 for (it = focus_order[d];
490 it && !((ObClient*)it->data)->iconic; it = it->next);
491 g_list_insert_before(focus_order[d], it, c);
492 }
493 }
494
495 void focus_order_to_bottom(ObClient *c)
496 {
497 guint d, i;
498
499 d = c->desktop;
500 if (d == DESKTOP_ALL) {
501 for (i = 0; i < screen_num_desktops; ++i)
502 to_bottom(c, i);
503 } else
504 to_bottom(c, d);
505 }
This page took 0.061834 seconds and 3 git commands to generate.