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