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