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