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