5 #include "framerender.h"
20 ObClient *focus_client;
21 GList **focus_order; /* these lists are created when screen_startup
22 sets the number of desktops */
23 ObClient *focus_cycle_target;
25 static ObIconPopup *focus_cycle_popup;
27 void focus_startup(gboolean reconfig)
29 focus_cycle_popup = icon_popup_new(TRUE);
32 /* start with nothing focused */
33 focus_set_client(NULL);
36 void focus_shutdown(gboolean reconfig)
40 icon_popup_free(focus_cycle_popup);
43 for (i = 0; i < screen_num_desktops; ++i)
44 g_list_free(focus_order[i]);
47 /* reset focus to root */
48 XSetInputFocus(ob_display, PointerRoot, RevertToPointerRoot,
53 static void push_to_top(ObClient *client)
57 desktop = client->desktop;
58 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
59 focus_order[desktop] = g_list_remove(focus_order[desktop], client);
60 focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
63 void focus_set_client(ObClient *client)
69 ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
72 /* uninstall the old colormap, and install the new one */
73 screen_install_colormap(focus_client, FALSE);
74 screen_install_colormap(client, TRUE);
77 /* when nothing will be focused, send focus to the backup target */
78 XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
80 XSync(ob_display, FALSE);
83 /* in the middle of cycling..? kill it. */
84 if (focus_cycle_target)
85 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
88 focus_client = client;
90 /* move to the top of the list */
94 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
95 if (ob_state() != OB_STATE_EXITING) {
96 active = client ? client->window : None;
97 PROP_SET32(RootWindow(ob_display, ob_screen),
98 net_active_window, window, active);
102 static gboolean focus_under_pointer()
107 if (screen_pointer_pos(&x, &y)) {
108 for (it = stacking_list; it != NULL; it = it->next) {
109 if (WINDOW_IS_CLIENT(it->data)) {
110 ObClient *c = WINDOW_AS_CLIENT(it->data);
111 if (c->desktop == screen_desktop &&
112 RECT_CONTAINS(c->frame->area, x, y))
117 g_assert(WINDOW_IS_CLIENT(it->data));
118 return client_normal(it->data) && client_focus(it->data);
124 /* finds the first transient that isn't 'skip' and ensure's that client_normal
126 static ObClient *find_transient_recursive(ObClient *c, ObClient *top, ObClient *skip)
131 for (it = c->transients; it; it = it->next) {
132 if (it->data == top) return NULL;
133 ret = find_transient_recursive(it->data, top, skip);
134 if (ret && ret != skip && client_normal(ret)) return ret;
135 if (it->data != skip && client_normal(it->data)) return it->data;
140 static gboolean focus_fallback_transient(ObClient *top, ObClient *old)
142 ObClient *target = find_transient_recursive(top, top, old);
144 /* make sure client_normal is true always */
145 if (!client_normal(top))
147 target = top; /* no transient, keep the top */
149 return client_focus(target);
152 void focus_fallback(ObFocusFallbackType type)
155 ObClient *old = NULL;
159 /* unfocus any focused clients.. they can be focused by Pointer events
160 and such, and then when I try focus them, I won't get a FocusIn event
163 focus_set_client(NULL);
165 if (!(type == OB_FOCUS_FALLBACK_DESKTOP ?
166 config_focus_last_on_desktop : config_focus_last)) {
167 if (config_focus_follow) focus_under_pointer();
171 if (type == OB_FOCUS_FALLBACK_UNFOCUSING && old) {
172 /* try for transient relations */
173 if (old->transient_for) {
174 if (old->transient_for == OB_TRAN_GROUP) {
175 for (it = focus_order[screen_desktop]; it; it = it->next) {
178 for (sit = old->group->members; sit; sit = sit->next)
179 if (sit->data == it->data)
180 if (focus_fallback_transient(sit->data, old))
184 if (focus_fallback_transient(old->transient_for, old))
190 /* try for group relations */
194 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
195 for (sit = old->group->members; sit; sit = sit->next)
196 if (sit->data == it->data)
197 if (sit->data != old && client_normal(sit->data))
198 if (client_can_focus(sit->data)) {
199 gboolean r = client_focus(sit->data);
207 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
208 if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
209 if (client_normal(it->data) &&
210 /* dont fall back to 'anonymous' fullscreen windows. theres no
211 checks for this is in transient/group fallbacks, so they can
212 be fallback targets there. */
213 !((ObClient*)it->data)->fullscreen &&
214 client_can_focus(it->data)) {
215 gboolean r = client_focus(it->data);
220 /* nothing to focus, and already set it to none above */
223 static void popup_cycle(ObClient *c, gboolean show)
226 icon_popup_hide(focus_cycle_popup);
232 a = screen_physical_area_monitor(0);
233 icon_popup_position(focus_cycle_popup, CenterGravity,
234 a->x + a->width / 2, a->y + a->height / 2);
235 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
236 icon_popup_show(focus_cycle_popup, c->title,
237 client_icon(c, a->height/16, a->height/16));
239 /* XXX the size and the font extents need to be related on some level
241 icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
243 /* use the transient's parent's title/icon */
244 while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
245 p = p->transient_for;
250 title = g_strconcat((c->iconic ? c->icon_title : c->title),
252 (p->iconic ? p->icon_title : p->title),
255 icon_popup_show(focus_cycle_popup,
257 (c->iconic ? c->icon_title : c->title)),
258 client_icon(p, 48, 48));
263 static gboolean valid_focus_target(ObClient *ft)
265 /* we don't use client_can_focus here, because that doesn't let you
266 focus an iconic window, but we want to be able to, so we just check
267 if the focus flags on the window allow it, and its on the current
269 return (ft->transients == NULL && client_normal(ft) &&
270 ((ft->can_focus || ft->focus_notify) &&
272 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)));
275 void focus_cycle(gboolean forward, gboolean linear,
276 gboolean dialog, gboolean done, gboolean cancel)
278 static ObClient *first = NULL;
279 static ObClient *t = NULL;
280 static GList *order = NULL;
281 GList *it, *start, *list;
285 if (focus_cycle_target)
286 frame_adjust_focus(focus_cycle_target->frame, FALSE);
288 frame_adjust_focus(focus_client->frame, TRUE);
289 focus_cycle_target = NULL;
291 } else if (done && dialog) {
295 if (!focus_order[screen_desktop])
298 if (!first) first = focus_client;
299 if (!focus_cycle_target) focus_cycle_target = focus_client;
301 if (linear) list = client_list;
302 else list = focus_order[screen_desktop];
304 start = it = g_list_find(list, focus_cycle_target);
305 if (!start) /* switched desktops or something? */
306 start = it = forward ? g_list_last(list) : g_list_first(list);
307 if (!start) goto done_cycle;
312 if (it == NULL) it = g_list_first(list);
315 if (it == NULL) it = g_list_last(list);
317 /*ft = client_focus_target(it->data);*/
319 if (valid_focus_target(ft)) {
320 if (ft != focus_cycle_target) { /* prevents flicker */
321 if (focus_cycle_target)
322 frame_adjust_focus(focus_cycle_target->frame, FALSE);
323 focus_cycle_target = ft;
324 frame_adjust_focus(focus_cycle_target->frame, TRUE);
326 popup_cycle(ft, dialog);
329 } while (it != start);
332 if (done && focus_cycle_target)
333 client_activate(focus_cycle_target, FALSE);
337 focus_cycle_target = NULL;
341 popup_cycle(ft, FALSE);
346 void focus_directional_cycle(ObDirection dir,
347 gboolean dialog, gboolean done, gboolean cancel)
349 static ObClient *first = NULL;
353 if (focus_cycle_target)
354 frame_adjust_focus(focus_cycle_target->frame, FALSE);
356 frame_adjust_focus(focus_client->frame, TRUE);
357 focus_cycle_target = NULL;
359 } else if (done && dialog) {
363 if (!focus_order[screen_desktop])
366 if (!first) first = focus_client;
367 if (!focus_cycle_target) focus_cycle_target = focus_client;
369 if (focus_cycle_target)
370 ft = client_find_directional(focus_cycle_target, dir);
374 for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
375 if (valid_focus_target(it->data))
380 if (ft != focus_cycle_target) {/* prevents flicker */
381 if (focus_cycle_target)
382 frame_adjust_focus(focus_cycle_target->frame, FALSE);
383 focus_cycle_target = ft;
384 frame_adjust_focus(focus_cycle_target->frame, TRUE);
386 popup_cycle(ft, dialog);
392 if (done && focus_cycle_target)
393 client_activate(focus_cycle_target, FALSE);
396 focus_cycle_target = NULL;
398 popup_cycle(ft, FALSE);
403 void focus_order_add_new(ObClient *c)
408 focus_order_to_top(c);
411 if (d == DESKTOP_ALL) {
412 for (i = 0; i < screen_num_desktops; ++i) {
413 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
414 focus_order[i] = g_list_insert(focus_order[i], c, 0);
416 focus_order[i] = g_list_insert(focus_order[i], c, 1);
419 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
420 focus_order[d] = g_list_insert(focus_order[d], c, 0);
422 focus_order[d] = g_list_insert(focus_order[d], c, 1);
426 void focus_order_remove(ObClient *c)
431 if (d == DESKTOP_ALL) {
432 for (i = 0; i < screen_num_desktops; ++i)
433 focus_order[i] = g_list_remove(focus_order[i], c);
435 focus_order[d] = g_list_remove(focus_order[d], c);
438 static void to_top(ObClient *c, guint d)
440 focus_order[d] = g_list_remove(focus_order[d], c);
442 focus_order[d] = g_list_prepend(focus_order[d], c);
446 /* insert before first iconic window */
447 for (it = focus_order[d];
448 it && !((ObClient*)it->data)->iconic; it = it->next);
449 focus_order[d] = g_list_insert_before(focus_order[d], it, c);
453 void focus_order_to_top(ObClient *c)
458 if (d == DESKTOP_ALL) {
459 for (i = 0; i < screen_num_desktops; ++i)
465 static void to_bottom(ObClient *c, guint d)
467 focus_order[d] = g_list_remove(focus_order[d], c);
469 focus_order[d] = g_list_append(focus_order[d], c);
473 /* insert before first iconic window */
474 for (it = focus_order[d];
475 it && !((ObClient*)it->data)->iconic; it = it->next);
476 g_list_insert_before(focus_order[d], it, c);
480 void focus_order_to_bottom(ObClient *c)
485 if (d == DESKTOP_ALL) {
486 for (i = 0; i < screen_num_desktops; ++i)