]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
replace the focus_backup with the screen_support_win, we dont need 2 offscreen windows.
[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 ObClient *focus_client = NULL;
21 GList **focus_order = NULL; /* these lists are created when screen_startup
22 sets the number of desktops */
23
24 static ObClient *focus_cycle_target = NULL;
25 static Popup *focus_cycle_popup = NULL;
26
27 void focus_startup()
28 {
29
30 focus_client = NULL;
31
32 focus_cycle_popup = popup_new(TRUE);
33
34 /* start with nothing focused */
35 focus_set_client(NULL);
36 }
37
38 void focus_shutdown()
39 {
40 guint i;
41
42 for (i = 0; i < screen_num_desktops; ++i)
43 g_list_free(focus_order[i]);
44 g_free(focus_order);
45 focus_order = NULL;
46
47 popup_free(focus_cycle_popup);
48 focus_cycle_popup = NULL;
49
50 /* reset focus to root */
51 XSetInputFocus(ob_display, PointerRoot, RevertToPointerRoot,
52 event_lasttime);
53 }
54
55 static void push_to_top(ObClient *client)
56 {
57 guint desktop;
58
59 desktop = client->desktop;
60 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
61 focus_order[desktop] = g_list_remove(focus_order[desktop], client);
62 focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
63 }
64
65 void focus_set_client(ObClient *client)
66 {
67 Window active;
68 ObClient *old;
69
70 #ifdef DEBUG_FOCUS
71 g_message("focus_set_client 0x%lx", client ? client->window : 0);
72 #endif
73
74 /* uninstall the old colormap, and install the new one */
75 screen_install_colormap(focus_client, FALSE);
76 screen_install_colormap(client, TRUE);
77
78 if (client == NULL) {
79 /* when nothing will be focused, send focus to the backup target */
80 XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
81 event_lasttime);
82 XSync(ob_display, FALSE);
83 }
84
85 /* in the middle of cycling..? kill it. */
86 if (focus_cycle_target)
87 focus_cycle(TRUE, TRUE, TRUE, TRUE);
88
89 old = focus_client;
90 focus_client = client;
91
92 /* move to the top of the list */
93 if (client != NULL)
94 push_to_top(client);
95
96 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
97 if (ob_state != OB_STATE_EXITING) {
98 active = client ? client->window : None;
99 PROP_SET32(ob_root, net_active_window, window, active);
100 }
101
102 if (focus_client != NULL)
103 dispatch_client(Event_Client_Focus, focus_client, 0, 0);
104 if (old != NULL)
105 dispatch_client(Event_Client_Unfocus, old, 0, 0);
106 }
107
108 static gboolean focus_under_pointer()
109 {
110 int x, y;
111 GList *it;
112
113 if (ob_pointer_pos(&x, &y)) {
114 for (it = stacking_list; it != NULL; it = it->next) {
115 if (WINDOW_IS_CLIENT(it->data)) {
116 ObClient *c = WINDOW_AS_CLIENT(it->data);
117 if (c->desktop == screen_desktop &&
118 RECT_CONTAINS(c->frame->area, x, y))
119 break;
120 }
121 }
122 if (it != NULL) {
123 g_assert(WINDOW_IS_CLIENT(it->data));
124 return client_normal(it->data) && client_focus(it->data);
125 }
126 }
127 return FALSE;
128 }
129
130 /* finds the first transient that isn't 'skip' and ensure's that client_normal
131 is true for it */
132 static ObClient *find_transient_recursive(ObClient *c, ObClient *top, ObClient *skip)
133 {
134 GSList *it;
135 ObClient *ret;
136
137 for (it = c->transients; it; it = it->next) {
138 if (it->data == top) return NULL;
139 ret = find_transient_recursive(it->data, top, skip);
140 if (ret && ret != skip && client_normal(ret)) return ret;
141 if (it->data != skip && client_normal(it->data)) return it->data;
142 }
143 return NULL;
144 }
145
146 static gboolean focus_fallback_transient(ObClient *top, ObClient *old)
147 {
148 ObClient *target = find_transient_recursive(top, top, old);
149 if (!target) {
150 /* make sure client_normal is true always */
151 if (!client_normal(top))
152 return FALSE;
153 target = top; /* no transient, keep the top */
154 }
155 return client_focus(target);
156 }
157
158 void focus_fallback(ObFocusFallbackType type)
159 {
160 GList *it;
161 ObClient *old = NULL;
162
163 old = focus_client;
164
165 /* unfocus any focused clients.. they can be focused by Pointer events
166 and such, and then when I try focus them, I won't get a FocusIn event
167 at all for them.
168 */
169 focus_set_client(NULL);
170
171 if (!(type == OB_FOCUS_FALLBACK_DESKTOP ?
172 config_focus_last_on_desktop : config_focus_last)) {
173 if (config_focus_follow) focus_under_pointer();
174 return;
175 }
176
177 if (type == OB_FOCUS_FALLBACK_UNFOCUSING && old) {
178 /* try for transient relations */
179 if (old->transient_for) {
180 if (old->transient_for == OB_TRAN_GROUP) {
181 for (it = focus_order[screen_desktop]; it; it = it->next) {
182 GSList *sit;
183
184 for (sit = old->group->members; sit; sit = sit->next)
185 if (sit->data == it->data)
186 if (focus_fallback_transient(sit->data, old))
187 return;
188 }
189 } else {
190 if (focus_fallback_transient(old->transient_for, old))
191 return;
192 }
193 }
194
195 /* try for group relations */
196 if (old->group) {
197 GSList *sit;
198
199 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
200 for (sit = old->group->members; sit; sit = sit->next)
201 if (sit->data == it->data)
202 if (sit->data != old && client_normal(sit->data))
203 if (client_can_focus(sit->data)) {
204 gboolean r = client_focus(sit->data);
205 assert(r);
206 return;
207 }
208 }
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, 320, 48);
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);
284 goto done_cycle;
285 }
286 if (!first)
287 grab_pointer(TRUE, None);
288
289 if (!first) first = focus_client;
290 if (!focus_cycle_target) focus_cycle_target = focus_client;
291
292 if (linear) list = client_list;
293 else list = focus_order[screen_desktop];
294
295 start = it = g_list_find(list, focus_cycle_target);
296 if (!start) /* switched desktops or something? */
297 start = it = forward ? g_list_last(list) : g_list_first(list);
298 if (!start) goto done_cycle;
299
300 do {
301 if (forward) {
302 it = it->next;
303 if (it == NULL) it = g_list_first(list);
304 } else {
305 it = it->prev;
306 if (it == NULL) it = g_list_last(list);
307 }
308 /*ft = client_focus_target(it->data);*/
309 ft = it->data;
310 /* we don't use client_can_focus here, because that doesn't let you
311 focus an iconic window, but we want to be able to, so we just check
312 if the focus flags on the window allow it, and its on the current
313 desktop */
314 if (ft->transients == NULL && client_normal(ft) &&
315 ((ft->can_focus || ft->focus_notify) &&
316 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL))) {
317 if (ft != focus_cycle_target) { /* prevents flicker */
318 if (focus_cycle_target)
319 frame_adjust_focus(focus_cycle_target->frame, FALSE);
320 focus_cycle_target = ft;
321 frame_adjust_focus(focus_cycle_target->frame, TRUE);
322 }
323 popup_cycle(ft, config_focus_popup);
324 return ft;
325 }
326 } while (it != start);
327
328 done_cycle:
329 t = NULL;
330 first = NULL;
331 focus_cycle_target = NULL;
332 g_list_free(order);
333 order = NULL;
334
335 popup_cycle(ft, FALSE);
336 grab_pointer(FALSE, None);
337
338 return NULL;
339 }
340
341 void focus_order_add_new(ObClient *c)
342 {
343 guint d, i;
344
345 if (c->iconic)
346 focus_order_to_top(c);
347 else {
348 d = c->desktop;
349 if (d == DESKTOP_ALL) {
350 for (i = 0; i < screen_num_desktops; ++i) {
351 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
352 focus_order[i] = g_list_insert(focus_order[i], c, 0);
353 else
354 focus_order[i] = g_list_insert(focus_order[i], c, 1);
355 }
356 } else
357 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
358 focus_order[d] = g_list_insert(focus_order[d], c, 0);
359 else
360 focus_order[d] = g_list_insert(focus_order[d], c, 1);
361 }
362 }
363
364 void focus_order_remove(ObClient *c)
365 {
366 guint d, i;
367
368 d = c->desktop;
369 if (d == DESKTOP_ALL) {
370 for (i = 0; i < screen_num_desktops; ++i)
371 focus_order[i] = g_list_remove(focus_order[i], c);
372 } else
373 focus_order[d] = g_list_remove(focus_order[d], c);
374 }
375
376 static void to_top(ObClient *c, guint d)
377 {
378 focus_order[d] = g_list_remove(focus_order[d], c);
379 if (!c->iconic) {
380 focus_order[d] = g_list_prepend(focus_order[d], c);
381 } else {
382 GList *it;
383
384 /* insert before first iconic window */
385 for (it = focus_order[d];
386 it && !((ObClient*)it->data)->iconic; it = it->next);
387 g_list_insert_before(focus_order[d], it, c);
388 }
389 }
390
391 void focus_order_to_top(ObClient *c)
392 {
393 guint d, i;
394
395 d = c->desktop;
396 if (d == DESKTOP_ALL) {
397 for (i = 0; i < screen_num_desktops; ++i)
398 to_top(c, i);
399 } else
400 to_top(c, d);
401 }
402
403 static void to_bottom(ObClient *c, guint d)
404 {
405 focus_order[d] = g_list_remove(focus_order[d], c);
406 if (c->iconic) {
407 focus_order[d] = g_list_append(focus_order[d], c);
408 } else {
409 GList *it;
410
411 /* insert before first iconic window */
412 for (it = focus_order[d];
413 it && !((ObClient*)it->data)->iconic; it = it->next);
414 g_list_insert_before(focus_order[d], it, c);
415 }
416 }
417
418 void focus_order_to_bottom(ObClient *c)
419 {
420 guint d, i;
421
422 d = c->desktop;
423 if (d == DESKTOP_ALL) {
424 for (i = 0; i < screen_num_desktops; ++i)
425 to_bottom(c, i);
426 } else
427 to_bottom(c, d);
428 }
This page took 0.051306 seconds and 4 git commands to generate.