]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
hover theme elements for window titlebar buttons
[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 = NULL; /* these lists are created when screen_startup
23 sets the number of desktops */
24
25 static ObClient *focus_cycle_target = NULL;
26 static Popup *focus_cycle_popup = NULL;
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, 320, 48);
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);
282 goto done_cycle;
283 }
284 if (!first)
285 grab_pointer(TRUE, None);
286
287 if (!first) first = focus_client;
288 if (!focus_cycle_target) focus_cycle_target = focus_client;
289
290 if (linear) list = client_list;
291 else list = focus_order[screen_desktop];
292
293 start = it = g_list_find(list, focus_cycle_target);
294 if (!start) /* switched desktops or something? */
295 start = it = forward ? g_list_last(list) : g_list_first(list);
296 if (!start) goto done_cycle;
297
298 do {
299 if (forward) {
300 it = it->next;
301 if (it == NULL) it = g_list_first(list);
302 } else {
303 it = it->prev;
304 if (it == NULL) it = g_list_last(list);
305 }
306 /*ft = client_focus_target(it->data);*/
307 ft = it->data;
308 /* we don't use client_can_focus here, because that doesn't let you
309 focus an iconic window, but we want to be able to, so we just check
310 if the focus flags on the window allow it, and its on the current
311 desktop */
312 if (ft->transients == NULL && client_normal(ft) &&
313 ((ft->can_focus || ft->focus_notify) &&
314 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL))) {
315 if (ft != focus_cycle_target) { /* prevents flicker */
316 if (focus_cycle_target)
317 frame_adjust_focus(focus_cycle_target->frame, FALSE);
318 focus_cycle_target = ft;
319 frame_adjust_focus(focus_cycle_target->frame, TRUE);
320 }
321 popup_cycle(ft, config_focus_popup);
322 return ft;
323 }
324 } while (it != start);
325
326 done_cycle:
327 t = NULL;
328 first = NULL;
329 focus_cycle_target = NULL;
330 g_list_free(order);
331 order = NULL;
332
333 popup_cycle(ft, FALSE);
334 grab_pointer(FALSE, None);
335
336 return NULL;
337 }
338
339 void focus_order_add_new(ObClient *c)
340 {
341 guint d, i;
342
343 if (c->iconic)
344 focus_order_to_top(c);
345 else {
346 d = c->desktop;
347 if (d == DESKTOP_ALL) {
348 for (i = 0; i < screen_num_desktops; ++i) {
349 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
350 focus_order[i] = g_list_insert(focus_order[i], c, 0);
351 else
352 focus_order[i] = g_list_insert(focus_order[i], c, 1);
353 }
354 } else
355 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
356 focus_order[d] = g_list_insert(focus_order[d], c, 0);
357 else
358 focus_order[d] = g_list_insert(focus_order[d], c, 1);
359 }
360 }
361
362 void focus_order_remove(ObClient *c)
363 {
364 guint d, i;
365
366 d = c->desktop;
367 if (d == DESKTOP_ALL) {
368 for (i = 0; i < screen_num_desktops; ++i)
369 focus_order[i] = g_list_remove(focus_order[i], c);
370 } else
371 focus_order[d] = g_list_remove(focus_order[d], c);
372 }
373
374 static void to_top(ObClient *c, guint d)
375 {
376 focus_order[d] = g_list_remove(focus_order[d], c);
377 if (!c->iconic) {
378 focus_order[d] = g_list_prepend(focus_order[d], c);
379 } else {
380 GList *it;
381
382 /* insert before first iconic window */
383 for (it = focus_order[d];
384 it && !((ObClient*)it->data)->iconic; it = it->next);
385 g_list_insert_before(focus_order[d], it, c);
386 }
387 }
388
389 void focus_order_to_top(ObClient *c)
390 {
391 guint d, i;
392
393 d = c->desktop;
394 if (d == DESKTOP_ALL) {
395 for (i = 0; i < screen_num_desktops; ++i)
396 to_top(c, i);
397 } else
398 to_top(c, d);
399 }
400
401 static void to_bottom(ObClient *c, guint d)
402 {
403 focus_order[d] = g_list_remove(focus_order[d], c);
404 if (c->iconic) {
405 focus_order[d] = g_list_append(focus_order[d], c);
406 } else {
407 GList *it;
408
409 /* insert before first iconic window */
410 for (it = focus_order[d];
411 it && !((ObClient*)it->data)->iconic; it = it->next);
412 g_list_insert_before(focus_order[d], it, c);
413 }
414 }
415
416 void focus_order_to_bottom(ObClient *c)
417 {
418 guint d, i;
419
420 d = c->desktop;
421 if (d == DESKTOP_ALL) {
422 for (i = 0; i < screen_num_desktops; ++i)
423 to_bottom(c, i);
424 } else
425 to_bottom(c, d);
426 }
This page took 0.055835 seconds and 4 git commands to generate.