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