]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
add stacking_below which moves a window to immediately below another window. use...
[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 /* try for group relations */
194 if (old->group) {
195 GSList *sit;
196
197 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
198 for (sit = old->group->members; sit; sit = sit->next)
199 if (sit->data == it->data)
200 if (sit->data != old && client_normal(sit->data))
201 if (client_can_focus(sit->data)) {
202 gboolean r = client_focus(sit->data);
203 assert(r);
204 return;
205 }
206 }
207 }
208
209 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
210 if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
211 if (client_normal(it->data) &&
212 /* dont fall back to 'anonymous' fullscreen windows. theres no
213 checks for this is in transient/group fallbacks, so they can
214 be fallback targets there. */
215 !((ObClient*)it->data)->fullscreen &&
216 client_can_focus(it->data)) {
217 gboolean r = client_focus(it->data);
218 assert(r);
219 return;
220 }
221
222 /* nothing to focus, and already set it to none above */
223 }
224
225 static void popup_cycle(ObClient *c, gboolean show)
226 {
227 if (!show) {
228 popup_hide(focus_cycle_popup);
229 } else {
230 Rect *a;
231 ObClient *p = c;
232 char *title;
233
234 a = screen_physical_area_monitor(0);
235 popup_position(focus_cycle_popup, CenterGravity,
236 a->x + a->width / 2, a->y + a->height / 2);
237 /* popup_size(focus_cycle_popup, a->height/2, a->height/16);
238 popup_show(focus_cycle_popup, c->title,
239 client_icon(c, a->height/16, a->height/16));
240 */
241 /* XXX the size and the font extents need to be related on some level
242 */
243 popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
244
245 /* use the transient's parent's title/icon */
246 while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
247 p = p->transient_for;
248
249 if (p == c)
250 title = NULL;
251 else
252 title = g_strconcat((c->iconic ? c->icon_title : c->title),
253 " - ",
254 (p->iconic ? p->icon_title : p->title),
255 NULL);
256
257 popup_show(focus_cycle_popup,
258 (title ? title : (c->iconic ? c->icon_title : c->title)),
259 client_icon(p, 48, 48));
260 g_free(title);
261 }
262 }
263
264 ObClient *focus_cycle(gboolean forward, gboolean linear, gboolean done,
265 gboolean cancel)
266 {
267 static ObClient *first = NULL;
268 static ObClient *t = NULL;
269 static GList *order = NULL;
270 GList *it, *start, *list;
271 ObClient *ft;
272
273 if (cancel) {
274 if (focus_cycle_target)
275 frame_adjust_focus(focus_cycle_target->frame, FALSE);
276 if (focus_client)
277 frame_adjust_focus(focus_client->frame, TRUE);
278 goto done_cycle;
279 } else if (done) {
280 if (focus_cycle_target)
281 client_activate(focus_cycle_target, FALSE);
282 goto done_cycle;
283 }
284
285 if (!first) first = focus_client;
286 if (!focus_cycle_target) focus_cycle_target = focus_client;
287
288 if (linear) list = client_list;
289 else list = focus_order[screen_desktop];
290
291 start = it = g_list_find(list, focus_cycle_target);
292 if (!start) /* switched desktops or something? */
293 start = it = forward ? g_list_last(list) : g_list_first(list);
294 if (!start) goto done_cycle;
295
296 do {
297 if (forward) {
298 it = it->next;
299 if (it == NULL) it = g_list_first(list);
300 } else {
301 it = it->prev;
302 if (it == NULL) it = g_list_last(list);
303 }
304 /*ft = client_focus_target(it->data);*/
305 ft = it->data;
306 /* we don't use client_can_focus here, because that doesn't let you
307 focus an iconic window, but we want to be able to, so we just check
308 if the focus flags on the window allow it, and its on the current
309 desktop */
310 if (ft->transients == NULL && client_normal(ft) &&
311 ((ft->can_focus || ft->focus_notify) &&
312 !ft->skip_taskbar &&
313 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL))) {
314 if (ft != focus_cycle_target) { /* prevents flicker */
315 if (focus_cycle_target)
316 frame_adjust_focus(focus_cycle_target->frame, FALSE);
317 focus_cycle_target = ft;
318 frame_adjust_focus(focus_cycle_target->frame, TRUE);
319 }
320 popup_cycle(ft, config_focus_popup);
321 return ft;
322 }
323 } while (it != start);
324
325 done_cycle:
326 t = NULL;
327 first = NULL;
328 focus_cycle_target = NULL;
329 g_list_free(order);
330 order = NULL;
331
332 popup_cycle(ft, FALSE);
333
334 return NULL;
335 }
336
337 void focus_order_add_new(ObClient *c)
338 {
339 guint d, i;
340
341 if (c->iconic)
342 focus_order_to_top(c);
343 else {
344 d = c->desktop;
345 if (d == DESKTOP_ALL) {
346 for (i = 0; i < screen_num_desktops; ++i) {
347 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
348 focus_order[i] = g_list_insert(focus_order[i], c, 0);
349 else
350 focus_order[i] = g_list_insert(focus_order[i], c, 1);
351 }
352 } else
353 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
354 focus_order[d] = g_list_insert(focus_order[d], c, 0);
355 else
356 focus_order[d] = g_list_insert(focus_order[d], c, 1);
357 }
358 }
359
360 void focus_order_remove(ObClient *c)
361 {
362 guint d, i;
363
364 d = c->desktop;
365 if (d == DESKTOP_ALL) {
366 for (i = 0; i < screen_num_desktops; ++i)
367 focus_order[i] = g_list_remove(focus_order[i], c);
368 } else
369 focus_order[d] = g_list_remove(focus_order[d], c);
370 }
371
372 static void to_top(ObClient *c, guint d)
373 {
374 focus_order[d] = g_list_remove(focus_order[d], c);
375 if (!c->iconic) {
376 focus_order[d] = g_list_prepend(focus_order[d], c);
377 } else {
378 GList *it;
379
380 /* insert before first iconic window */
381 for (it = focus_order[d];
382 it && !((ObClient*)it->data)->iconic; it = it->next);
383 g_list_insert_before(focus_order[d], it, c);
384 }
385 }
386
387 void focus_order_to_top(ObClient *c)
388 {
389 guint d, i;
390
391 d = c->desktop;
392 if (d == DESKTOP_ALL) {
393 for (i = 0; i < screen_num_desktops; ++i)
394 to_top(c, i);
395 } else
396 to_top(c, d);
397 }
398
399 static void to_bottom(ObClient *c, guint d)
400 {
401 focus_order[d] = g_list_remove(focus_order[d], c);
402 if (c->iconic) {
403 focus_order[d] = g_list_append(focus_order[d], c);
404 } else {
405 GList *it;
406
407 /* insert before first iconic window */
408 for (it = focus_order[d];
409 it && !((ObClient*)it->data)->iconic; it = it->next);
410 g_list_insert_before(focus_order[d], it, c);
411 }
412 }
413
414 void focus_order_to_bottom(ObClient *c)
415 {
416 guint d, i;
417
418 d = c->desktop;
419 if (d == DESKTOP_ALL) {
420 for (i = 0; i < screen_num_desktops; ++i)
421 to_bottom(c, i);
422 } else
423 to_bottom(c, d);
424 }
This page took 0.051704 seconds and 4 git commands to generate.