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