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