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