]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
expose focus_cycle_client
[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 Popup *focus_cycle_popup;
26
27 void focus_startup(gboolean reconfig)
28 {
29 focus_cycle_popup = 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 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 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 popup_position(focus_cycle_popup, CenterGravity,
234 a->x + a->width / 2, a->y + a->height / 2);
235 /* popup_size(focus_cycle_popup, a->height/2, a->height/16);
236 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 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 popup_show(focus_cycle_popup,
256 (title ? title : (c->iconic ? c->icon_title : c->title)),
257 client_icon(p, 48, 48));
258 g_free(title);
259 }
260 }
261
262 void focus_cycle(gboolean forward, gboolean linear,
263 gboolean dialog, gboolean done, gboolean cancel)
264 {
265 static ObClient *first = NULL;
266 static ObClient *t = NULL;
267 static GList *order = NULL;
268 GList *it, *start, *list;
269 ObClient *ft;
270
271 if (cancel) {
272 if (focus_cycle_target)
273 frame_adjust_focus(focus_cycle_target->frame, FALSE);
274 if (focus_client)
275 frame_adjust_focus(focus_client->frame, TRUE);
276 focus_cycle_target = NULL;
277 goto done_cycle;
278 } else if (done && dialog) {
279 goto done_cycle;
280 }
281
282 if (!first) first = focus_client;
283 if (!focus_cycle_target) focus_cycle_target = focus_client;
284
285 if (linear) list = client_list;
286 else list = focus_order[screen_desktop];
287
288 start = it = g_list_find(list, focus_cycle_target);
289 if (!start) /* switched desktops or something? */
290 start = it = forward ? g_list_last(list) : g_list_first(list);
291 if (!start) goto done_cycle;
292
293 do {
294 if (forward) {
295 it = it->next;
296 if (it == NULL) it = g_list_first(list);
297 } else {
298 it = it->prev;
299 if (it == NULL) it = g_list_last(list);
300 }
301 /*ft = client_focus_target(it->data);*/
302 ft = it->data;
303 /* we don't use client_can_focus here, because that doesn't let you
304 focus an iconic window, but we want to be able to, so we just check
305 if the focus flags on the window allow it, and its on the current
306 desktop */
307 if (ft->transients == NULL && client_normal(ft) &&
308 ((ft->can_focus || ft->focus_notify) &&
309 !ft->skip_taskbar &&
310 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL))) {
311 if (ft != focus_cycle_target) { /* prevents flicker */
312 if (focus_cycle_target)
313 frame_adjust_focus(focus_cycle_target->frame, FALSE);
314 focus_cycle_target = ft;
315 frame_adjust_focus(focus_cycle_target->frame, TRUE);
316 }
317 popup_cycle(ft, dialog);
318 return;
319 }
320 } while (it != start);
321
322 done_cycle:
323 if (done && focus_cycle_target)
324 client_activate(focus_cycle_target, FALSE);
325
326 t = NULL;
327 first = NULL;
328 focus_cycle_target = NULL;
329 g_list_free(order);
330 order = NULL;
331
332 popup_cycle(ft, FALSE);
333
334 return;
335 }
336
337 void focus_directional_cycle(ObDirection dir,
338 gboolean dialog, gboolean done, gboolean cancel)
339 {
340 static ObClient *first = NULL;
341 ObClient *ft;
342
343 if (cancel) {
344 if (focus_cycle_target)
345 frame_adjust_focus(focus_cycle_target->frame, FALSE);
346 if (focus_client)
347 frame_adjust_focus(focus_client->frame, TRUE);
348 focus_cycle_target = NULL;
349 goto done_cycle;
350 } else if (done && dialog) {
351 goto done_cycle;
352 }
353
354 if (!first) first = focus_client;
355 if (!focus_cycle_target) focus_cycle_target = focus_client;
356
357 if ((ft = client_find_directional(focus_cycle_target, dir))) {
358 if (ft != focus_cycle_target) {/* prevents flicker */
359 if (focus_cycle_target)
360 frame_adjust_focus(focus_cycle_target->frame, FALSE);
361 focus_cycle_target = ft;
362 frame_adjust_focus(focus_cycle_target->frame, TRUE);
363 }
364 popup_cycle(ft, dialog);
365 }
366 if (dialog)
367 return;
368
369 done_cycle:
370 if (done && focus_cycle_target)
371 client_activate(focus_cycle_target, FALSE);
372
373 first = NULL;
374 focus_cycle_target = NULL;
375
376 popup_cycle(ft, FALSE);
377
378 return;
379 }
380
381 void focus_order_add_new(ObClient *c)
382 {
383 guint d, i;
384
385 if (c->iconic)
386 focus_order_to_top(c);
387 else {
388 d = c->desktop;
389 if (d == DESKTOP_ALL) {
390 for (i = 0; i < screen_num_desktops; ++i) {
391 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
392 focus_order[i] = g_list_insert(focus_order[i], c, 0);
393 else
394 focus_order[i] = g_list_insert(focus_order[i], c, 1);
395 }
396 } else
397 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
398 focus_order[d] = g_list_insert(focus_order[d], c, 0);
399 else
400 focus_order[d] = g_list_insert(focus_order[d], c, 1);
401 }
402 }
403
404 void focus_order_remove(ObClient *c)
405 {
406 guint d, i;
407
408 d = c->desktop;
409 if (d == DESKTOP_ALL) {
410 for (i = 0; i < screen_num_desktops; ++i)
411 focus_order[i] = g_list_remove(focus_order[i], c);
412 } else
413 focus_order[d] = g_list_remove(focus_order[d], c);
414 }
415
416 static void to_top(ObClient *c, guint d)
417 {
418 focus_order[d] = g_list_remove(focus_order[d], c);
419 if (!c->iconic) {
420 focus_order[d] = g_list_prepend(focus_order[d], c);
421 } else {
422 GList *it;
423
424 /* insert before first iconic window */
425 for (it = focus_order[d];
426 it && !((ObClient*)it->data)->iconic; it = it->next);
427 g_list_insert_before(focus_order[d], it, c);
428 }
429 }
430
431 void focus_order_to_top(ObClient *c)
432 {
433 guint d, i;
434
435 d = c->desktop;
436 if (d == DESKTOP_ALL) {
437 for (i = 0; i < screen_num_desktops; ++i)
438 to_top(c, i);
439 } else
440 to_top(c, d);
441 }
442
443 static void to_bottom(ObClient *c, guint d)
444 {
445 focus_order[d] = g_list_remove(focus_order[d], c);
446 if (c->iconic) {
447 focus_order[d] = g_list_append(focus_order[d], c);
448 } else {
449 GList *it;
450
451 /* insert before first iconic window */
452 for (it = focus_order[d];
453 it && !((ObClient*)it->data)->iconic; it = it->next);
454 g_list_insert_before(focus_order[d], it, c);
455 }
456 }
457
458 void focus_order_to_bottom(ObClient *c)
459 {
460 guint d, i;
461
462 d = c->desktop;
463 if (d == DESKTOP_ALL) {
464 for (i = 0; i < screen_num_desktops; ++i)
465 to_bottom(c, i);
466 } else
467 to_bottom(c, d);
468 }
This page took 0.05761 seconds and 4 git commands to generate.