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