]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
xinerama support
[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, and already set it to none above */
239 }
240
241 static void popup_cycle(Client *c, gboolean show)
242 {
243 if (!show) {
244 popup_hide(focus_cycle_popup);
245 } else {
246 Rect *a;
247 Client *p = c;
248 char *title;
249
250 a = screen_physical_area_xinerama(0);
251 popup_position(focus_cycle_popup, CenterGravity,
252 a->x + a->width / 2, a->y + a->height / 2);
253 /* popup_size(focus_cycle_popup, a->height/2, a->height/16);
254 popup_show(focus_cycle_popup, c->title,
255 client_icon(c, a->height/16, a->height/16));
256 */
257 /* XXX the size and the font extents need to be related on some level
258 */
259 popup_size(focus_cycle_popup, 320, 48);
260
261 /* use the transient's parent's title/icon */
262 while (p->transient_for && p->transient_for != TRAN_GROUP)
263 p = p->transient_for;
264
265 if (p == c)
266 title = NULL;
267 else
268 title = g_strconcat((p->iconic ? p->icon_title : p->title),
269 " - ",
270 (c->iconic ? c->icon_title : c->title),
271 NULL);
272
273 popup_show(focus_cycle_popup,
274 (title ? title : (c->iconic ? c->icon_title : c->title)),
275 client_icon(p, 48, 48));
276 g_free(title);
277 }
278 }
279
280 Client *focus_cycle(gboolean forward, gboolean linear, gboolean done,
281 gboolean cancel)
282 {
283 static Client *first = NULL;
284 static Client *t = NULL;
285 static GList *order = NULL;
286 GList *it, *start, *list;
287 Client *ft;
288
289 if (cancel) {
290 if (focus_cycle_target)
291 frame_adjust_focus(focus_cycle_target->frame, FALSE);
292 if (focus_client)
293 frame_adjust_focus(focus_client->frame, TRUE);
294 goto done_cycle;
295 } else if (done) {
296 if (focus_cycle_target)
297 client_activate(focus_cycle_target);
298 goto done_cycle;
299 }
300 if (!first)
301 grab_pointer(TRUE, None);
302
303 if (!first) first = focus_client;
304 if (!focus_cycle_target) focus_cycle_target = focus_client;
305
306 if (linear) list = client_list;
307 else list = focus_order[screen_desktop];
308
309 start = it = g_list_find(list, focus_cycle_target);
310 if (!start) /* switched desktops or something? */
311 start = it = forward ? g_list_last(list) : g_list_first(list);
312 if (!start) goto done_cycle;
313
314 do {
315 if (forward) {
316 it = it->next;
317 if (it == NULL) it = g_list_first(list);
318 } else {
319 it = it->prev;
320 if (it == NULL) it = g_list_last(list);
321 }
322 /*ft = client_focus_target(it->data);*/
323 ft = it->data;
324 /* we don't use client_can_focus here, because that doesn't let you
325 focus an iconic window, but we want to be able to, so we just check
326 if the focus flags on the window allow it, and its on the current
327 desktop */
328 if (ft->transients == NULL && client_normal(ft) &&
329 ((ft->can_focus || ft->focus_notify) &&
330 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL))) {
331 if (ft != focus_cycle_target) { /* prevents flicker */
332 if (focus_cycle_target)
333 frame_adjust_focus(focus_cycle_target->frame, FALSE);
334 focus_cycle_target = ft;
335 frame_adjust_focus(focus_cycle_target->frame, TRUE);
336 }
337 popup_cycle(ft, config_focus_popup);
338 return ft;
339 }
340 } while (it != start);
341
342 done_cycle:
343 t = NULL;
344 first = NULL;
345 focus_cycle_target = NULL;
346 g_list_free(order);
347 order = NULL;
348
349 popup_cycle(ft, FALSE);
350 grab_pointer(FALSE, None);
351
352 return NULL;
353 }
354
355 void focus_order_add_new(Client *c)
356 {
357 guint d, i;
358
359 if (c->iconic)
360 focus_order_to_top(c);
361 else {
362 d = c->desktop;
363 if (d == DESKTOP_ALL) {
364 for (i = 0; i < screen_num_desktops; ++i) {
365 if (focus_order[i] && ((Client*)focus_order[i]->data)->iconic)
366 focus_order[i] = g_list_insert(focus_order[i], c, 0);
367 else
368 focus_order[i] = g_list_insert(focus_order[i], c, 1);
369 }
370 } else
371 if (focus_order[d] && ((Client*)focus_order[d]->data)->iconic)
372 focus_order[d] = g_list_insert(focus_order[d], c, 0);
373 else
374 focus_order[d] = g_list_insert(focus_order[d], c, 1);
375 }
376 }
377
378 void focus_order_remove(Client *c)
379 {
380 guint d, i;
381
382 d = c->desktop;
383 if (d == DESKTOP_ALL) {
384 for (i = 0; i < screen_num_desktops; ++i)
385 focus_order[i] = g_list_remove(focus_order[i], c);
386 } else
387 focus_order[d] = g_list_remove(focus_order[d], c);
388 }
389
390 static void to_top(Client *c, guint d)
391 {
392 focus_order[d] = g_list_remove(focus_order[d], c);
393 if (!c->iconic) {
394 focus_order[d] = g_list_prepend(focus_order[d], c);
395 } else {
396 GList *it;
397
398 /* insert before first iconic window */
399 for (it = focus_order[d];
400 it && !((Client*)it->data)->iconic; it = it->next);
401 g_list_insert_before(focus_order[d], it, c);
402 }
403 }
404
405 void focus_order_to_top(Client *c)
406 {
407 guint d, i;
408
409 d = c->desktop;
410 if (d == DESKTOP_ALL) {
411 for (i = 0; i < screen_num_desktops; ++i)
412 to_top(c, i);
413 } else
414 to_top(c, d);
415 }
416
417 static void to_bottom(Client *c, guint d)
418 {
419 focus_order[d] = g_list_remove(focus_order[d], c);
420 if (c->iconic) {
421 focus_order[d] = g_list_append(focus_order[d], c);
422 } else {
423 GList *it;
424
425 /* insert before first iconic window */
426 for (it = focus_order[d];
427 it && !((Client*)it->data)->iconic; it = it->next);
428 g_list_insert_before(focus_order[d], it, c);
429 }
430 }
431
432 void focus_order_to_bottom(Client *c)
433 {
434 guint d, i;
435
436 d = c->desktop;
437 if (d == DESKTOP_ALL) {
438 for (i = 0; i < screen_num_desktops; ++i)
439 to_bottom(c, i);
440 } else
441 to_bottom(c, d);
442 }
This page took 0.055994 seconds and 4 git commands to generate.