]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
move ob_pointer_pos to screen_pointer_pos
[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(RootWindow(ob_display, ob_screen),
100 net_active_window, window, active);
101 }
102
103 if (focus_client != NULL)
104 dispatch_client(Event_Client_Focus, focus_client, 0, 0);
105 if (old != NULL)
106 dispatch_client(Event_Client_Unfocus, old, 0, 0);
107 }
108
109 static gboolean focus_under_pointer()
110 {
111 int x, y;
112 GList *it;
113
114 if (screen_pointer_pos(&x, &y)) {
115 for (it = stacking_list; it != NULL; it = it->next) {
116 if (WINDOW_IS_CLIENT(it->data)) {
117 ObClient *c = WINDOW_AS_CLIENT(it->data);
118 if (c->desktop == screen_desktop &&
119 RECT_CONTAINS(c->frame->area, x, y))
120 break;
121 }
122 }
123 if (it != NULL) {
124 g_assert(WINDOW_IS_CLIENT(it->data));
125 return client_normal(it->data) && client_focus(it->data);
126 }
127 }
128 return FALSE;
129 }
130
131 /* finds the first transient that isn't 'skip' and ensure's that client_normal
132 is true for it */
133 static ObClient *find_transient_recursive(ObClient *c, ObClient *top, ObClient *skip)
134 {
135 GSList *it;
136 ObClient *ret;
137
138 for (it = c->transients; it; it = it->next) {
139 if (it->data == top) return NULL;
140 ret = find_transient_recursive(it->data, top, skip);
141 if (ret && ret != skip && client_normal(ret)) return ret;
142 if (it->data != skip && client_normal(it->data)) return it->data;
143 }
144 return NULL;
145 }
146
147 static gboolean focus_fallback_transient(ObClient *top, ObClient *old)
148 {
149 ObClient *target = find_transient_recursive(top, top, old);
150 if (!target) {
151 /* make sure client_normal is true always */
152 if (!client_normal(top))
153 return FALSE;
154 target = top; /* no transient, keep the top */
155 }
156 return client_focus(target);
157 }
158
159 void focus_fallback(ObFocusFallbackType type)
160 {
161 GList *it;
162 ObClient *old = NULL;
163
164 old = focus_client;
165
166 /* unfocus any focused clients.. they can be focused by Pointer events
167 and such, and then when I try focus them, I won't get a FocusIn event
168 at all for them.
169 */
170 focus_set_client(NULL);
171
172 if (!(type == OB_FOCUS_FALLBACK_DESKTOP ?
173 config_focus_last_on_desktop : config_focus_last)) {
174 if (config_focus_follow) focus_under_pointer();
175 return;
176 }
177
178 if (type == OB_FOCUS_FALLBACK_UNFOCUSING && old) {
179 /* try for transient relations */
180 if (old->transient_for) {
181 if (old->transient_for == OB_TRAN_GROUP) {
182 for (it = focus_order[screen_desktop]; it; it = it->next) {
183 GSList *sit;
184
185 for (sit = old->group->members; sit; sit = sit->next)
186 if (sit->data == it->data)
187 if (focus_fallback_transient(sit->data, old))
188 return;
189 }
190 } else {
191 if (focus_fallback_transient(old->transient_for, old))
192 return;
193 }
194 }
195
196 /* try for group relations */
197 if (old->group) {
198 GSList *sit;
199
200 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
201 for (sit = old->group->members; sit; sit = sit->next)
202 if (sit->data == it->data)
203 if (sit->data != old && client_normal(sit->data))
204 if (client_can_focus(sit->data)) {
205 gboolean r = client_focus(sit->data);
206 assert(r);
207 return;
208 }
209 }
210 }
211
212 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
213 if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
214 if (client_normal(it->data) &&
215 /* dont fall back to 'anonymous' fullscreen windows. theres no
216 checks for this is in transient/group fallbacks, so they can
217 be fallback targets there. */
218 !((ObClient*)it->data)->fullscreen &&
219 client_can_focus(it->data)) {
220 gboolean r = client_focus(it->data);
221 assert(r);
222 return;
223 }
224
225 /* nothing to focus, and already set it to none above */
226 }
227
228 static void popup_cycle(ObClient *c, gboolean show)
229 {
230 if (!show) {
231 popup_hide(focus_cycle_popup);
232 } else {
233 Rect *a;
234 ObClient *p = c;
235 char *title;
236
237 a = screen_physical_area_monitor(0);
238 popup_position(focus_cycle_popup, CenterGravity,
239 a->x + a->width / 2, a->y + a->height / 2);
240 /* popup_size(focus_cycle_popup, a->height/2, a->height/16);
241 popup_show(focus_cycle_popup, c->title,
242 client_icon(c, a->height/16, a->height/16));
243 */
244 /* XXX the size and the font extents need to be related on some level
245 */
246 popup_size(focus_cycle_popup, 320, 48);
247
248 /* use the transient's parent's title/icon */
249 while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
250 p = p->transient_for;
251
252 if (p == c)
253 title = NULL;
254 else
255 title = g_strconcat((c->iconic ? c->icon_title : c->title),
256 " - ",
257 (p->iconic ? p->icon_title : p->title),
258 NULL);
259
260 popup_show(focus_cycle_popup,
261 (title ? title : (c->iconic ? c->icon_title : c->title)),
262 client_icon(p, 48, 48));
263 g_free(title);
264 }
265 }
266
267 ObClient *focus_cycle(gboolean forward, gboolean linear, gboolean done,
268 gboolean cancel)
269 {
270 static ObClient *first = NULL;
271 static ObClient *t = NULL;
272 static GList *order = NULL;
273 GList *it, *start, *list;
274 ObClient *ft;
275
276 if (cancel) {
277 if (focus_cycle_target)
278 frame_adjust_focus(focus_cycle_target->frame, FALSE);
279 if (focus_client)
280 frame_adjust_focus(focus_client->frame, TRUE);
281 goto done_cycle;
282 } else if (done) {
283 if (focus_cycle_target)
284 client_activate(focus_cycle_target);
285 goto done_cycle;
286 }
287 if (!first)
288 grab_pointer(TRUE, None);
289
290 if (!first) first = focus_client;
291 if (!focus_cycle_target) focus_cycle_target = focus_client;
292
293 if (linear) list = client_list;
294 else list = focus_order[screen_desktop];
295
296 start = it = g_list_find(list, focus_cycle_target);
297 if (!start) /* switched desktops or something? */
298 start = it = forward ? g_list_last(list) : g_list_first(list);
299 if (!start) goto done_cycle;
300
301 do {
302 if (forward) {
303 it = it->next;
304 if (it == NULL) it = g_list_first(list);
305 } else {
306 it = it->prev;
307 if (it == NULL) it = g_list_last(list);
308 }
309 /*ft = client_focus_target(it->data);*/
310 ft = it->data;
311 /* we don't use client_can_focus here, because that doesn't let you
312 focus an iconic window, but we want to be able to, so we just check
313 if the focus flags on the window allow it, and its on the current
314 desktop */
315 if (ft->transients == NULL && client_normal(ft) &&
316 ((ft->can_focus || ft->focus_notify) &&
317 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL))) {
318 if (ft != focus_cycle_target) { /* prevents flicker */
319 if (focus_cycle_target)
320 frame_adjust_focus(focus_cycle_target->frame, FALSE);
321 focus_cycle_target = ft;
322 frame_adjust_focus(focus_cycle_target->frame, TRUE);
323 }
324 popup_cycle(ft, config_focus_popup);
325 return ft;
326 }
327 } while (it != start);
328
329 done_cycle:
330 t = NULL;
331 first = NULL;
332 focus_cycle_target = NULL;
333 g_list_free(order);
334 order = NULL;
335
336 popup_cycle(ft, FALSE);
337 grab_pointer(FALSE, None);
338
339 return NULL;
340 }
341
342 void focus_order_add_new(ObClient *c)
343 {
344 guint d, i;
345
346 if (c->iconic)
347 focus_order_to_top(c);
348 else {
349 d = c->desktop;
350 if (d == DESKTOP_ALL) {
351 for (i = 0; i < screen_num_desktops; ++i) {
352 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
353 focus_order[i] = g_list_insert(focus_order[i], c, 0);
354 else
355 focus_order[i] = g_list_insert(focus_order[i], c, 1);
356 }
357 } else
358 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
359 focus_order[d] = g_list_insert(focus_order[d], c, 0);
360 else
361 focus_order[d] = g_list_insert(focus_order[d], c, 1);
362 }
363 }
364
365 void focus_order_remove(ObClient *c)
366 {
367 guint d, i;
368
369 d = c->desktop;
370 if (d == DESKTOP_ALL) {
371 for (i = 0; i < screen_num_desktops; ++i)
372 focus_order[i] = g_list_remove(focus_order[i], c);
373 } else
374 focus_order[d] = g_list_remove(focus_order[d], c);
375 }
376
377 static void to_top(ObClient *c, guint d)
378 {
379 focus_order[d] = g_list_remove(focus_order[d], c);
380 if (!c->iconic) {
381 focus_order[d] = g_list_prepend(focus_order[d], c);
382 } else {
383 GList *it;
384
385 /* insert before first iconic window */
386 for (it = focus_order[d];
387 it && !((ObClient*)it->data)->iconic; it = it->next);
388 g_list_insert_before(focus_order[d], it, c);
389 }
390 }
391
392 void focus_order_to_top(ObClient *c)
393 {
394 guint d, i;
395
396 d = c->desktop;
397 if (d == DESKTOP_ALL) {
398 for (i = 0; i < screen_num_desktops; ++i)
399 to_top(c, i);
400 } else
401 to_top(c, d);
402 }
403
404 static void to_bottom(ObClient *c, guint d)
405 {
406 focus_order[d] = g_list_remove(focus_order[d], c);
407 if (c->iconic) {
408 focus_order[d] = g_list_append(focus_order[d], c);
409 } else {
410 GList *it;
411
412 /* insert before first iconic window */
413 for (it = focus_order[d];
414 it && !((ObClient*)it->data)->iconic; it = it->next);
415 g_list_insert_before(focus_order[d], it, c);
416 }
417 }
418
419 void focus_order_to_bottom(ObClient *c)
420 {
421 guint d, i;
422
423 d = c->desktop;
424 if (d == DESKTOP_ALL) {
425 for (i = 0; i < screen_num_desktops; ++i)
426 to_bottom(c, i);
427 } else
428 to_bottom(c, d);
429 }
This page took 0.054555 seconds and 4 git commands to generate.