1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 focus.c for the Openbox window manager
4 Copyright (c) 2003 Ben Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
23 #include "framerender.h"
38 ObClient *focus_client;
39 GList **focus_order; /* these lists are created when screen_startup
40 sets the number of desktops */
41 ObClient *focus_cycle_target;
43 static ObIconPopup *focus_cycle_popup;
45 static void focus_cycle_destructor(ObClient *client, gpointer data)
47 /* end cycling if the target disappears */
48 if (focus_cycle_target == client)
49 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
52 void focus_startup(gboolean reconfig)
54 focus_cycle_popup = icon_popup_new(TRUE);
57 client_add_destructor(focus_cycle_destructor, NULL);
59 /* start with nothing focused */
60 focus_set_client(NULL);
64 void focus_shutdown(gboolean reconfig)
68 icon_popup_free(focus_cycle_popup);
71 client_remove_destructor(focus_cycle_destructor);
73 for (i = 0; i < screen_num_desktops; ++i)
74 g_list_free(focus_order[i]);
77 /* reset focus to root */
78 XSetInputFocus(ob_display, PointerRoot, RevertToNone, event_lasttime);
82 static void push_to_top(ObClient *client)
86 desktop = client->desktop;
87 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
88 focus_order[desktop] = g_list_remove(focus_order[desktop], client);
89 focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
92 void focus_set_client(ObClient *client)
98 ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
101 /* uninstall the old colormap, and install the new one */
102 screen_install_colormap(focus_client, FALSE);
103 screen_install_colormap(client, TRUE);
105 if (client == NULL) {
107 ob_debug("actively focusing NONWINDOW\n");
109 /* when nothing will be focused, send focus to the backup target */
110 XSetInputFocus(ob_display, screen_support_win, RevertToNone,
112 XSync(ob_display, FALSE);
115 /* in the middle of cycling..? kill it. */
116 if (focus_cycle_target)
117 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
120 focus_client = client;
122 /* move to the top of the list */
126 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
127 if (ob_state() != OB_STATE_EXITING) {
128 active = client ? client->window : None;
129 PROP_SET32(RootWindow(ob_display, ob_screen),
130 net_active_window, window, active);
134 static gboolean focus_under_pointer()
138 if ((c = client_under_pointer()))
139 return client_normal(c) && client_focus(c);
143 /* finds the first transient that isn't 'skip' and ensure's that client_normal
145 static ObClient *find_transient_recursive(ObClient *c, ObClient *top, ObClient *skip)
150 for (it = c->transients; it; it = it->next) {
151 if (it->data == top) return NULL;
152 ret = find_transient_recursive(it->data, top, skip);
153 if (ret && ret != skip && client_normal(ret)) return ret;
154 if (it->data != skip && client_normal(it->data)) return it->data;
159 static gboolean focus_fallback_transient(ObClient *top, ObClient *old)
161 ObClient *target = find_transient_recursive(top, top, old);
163 /* make sure client_normal is true always */
164 if (!client_normal(top))
166 target = top; /* no transient, keep the top */
168 return client_focus(target);
171 void focus_fallback(ObFocusFallbackType type)
174 ObClient *old = NULL;
178 /* unfocus any focused clients.. they can be focused by Pointer events
179 and such, and then when I try focus them, I won't get a FocusIn event
182 focus_set_client(NULL);
184 if (type == OB_FOCUS_FALLBACK_UNFOCUSING && old) {
185 if (old->transient_for) {
186 gboolean trans = FALSE;
188 if (!config_focus_follow)
193 if ((c = client_under_pointer()) &&
194 client_search_transient(client_search_top_transient(c),
199 g_message("trans %d", trans);
201 /* try for transient relations */
203 if (old->transient_for == OB_TRAN_GROUP) {
204 for (it = focus_order[screen_desktop]; it; it = it->next) {
207 for (sit = old->group->members; sit; sit = sit->next)
208 if (sit->data == it->data)
209 if (focus_fallback_transient(sit->data, old))
213 if (focus_fallback_transient(old->transient_for, old))
220 if (config_focus_follow)
221 if (focus_under_pointer())
225 /* try for group relations */
229 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
230 for (sit = old->group->members; sit; sit = sit->next)
231 if (sit->data == it->data)
232 if (sit->data != old && client_normal(sit->data))
233 if (client_can_focus(sit->data)) {
234 gboolean r = client_focus(sit->data);
241 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
242 if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
243 if (client_normal(it->data) && client_can_focus(it->data)) {
244 gboolean r = client_focus(it->data);
249 /* nothing to focus, and already set it to none above */
252 static void popup_cycle(ObClient *c, gboolean show)
255 icon_popup_hide(focus_cycle_popup);
261 a = screen_physical_area_monitor(0);
262 icon_popup_position(focus_cycle_popup, CenterGravity,
263 a->x + a->width / 2, a->y + a->height / 2);
264 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
265 icon_popup_show(focus_cycle_popup, c->title,
266 client_icon(c, a->height/16, a->height/16));
268 /* XXX the size and the font extents need to be related on some level
270 icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
272 /* use the transient's parent's title/icon */
273 while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
274 p = p->transient_for;
279 title = g_strconcat((c->iconic ? c->icon_title : c->title),
281 (p->iconic ? p->icon_title : p->title),
284 icon_popup_show(focus_cycle_popup,
286 (c->iconic ? c->icon_title : c->title)),
287 client_icon(p, 48, 48));
292 static gboolean valid_focus_target(ObClient *ft)
294 /* we don't use client_can_focus here, because that doesn't let you
295 focus an iconic window, but we want to be able to, so we just check
296 if the focus flags on the window allow it, and its on the current
298 return (!ft->transients && client_normal(ft) &&
299 ((ft->can_focus || ft->focus_notify) &&
301 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)));
304 void focus_cycle(gboolean forward, gboolean linear,
305 gboolean dialog, gboolean done, gboolean cancel)
307 static ObClient *first = NULL;
308 static ObClient *t = NULL;
309 static GList *order = NULL;
310 GList *it, *start, *list;
314 if (focus_cycle_target)
315 frame_adjust_focus(focus_cycle_target->frame, FALSE);
317 frame_adjust_focus(focus_client->frame, TRUE);
318 focus_cycle_target = NULL;
320 } else if (done && dialog) {
324 if (!focus_order[screen_desktop])
327 if (!first) first = focus_client;
328 if (!focus_cycle_target) focus_cycle_target = focus_client;
330 if (linear) list = client_list;
331 else list = focus_order[screen_desktop];
333 start = it = g_list_find(list, focus_cycle_target);
334 if (!start) /* switched desktops or something? */
335 start = it = forward ? g_list_last(list) : g_list_first(list);
336 if (!start) goto done_cycle;
341 if (it == NULL) it = g_list_first(list);
344 if (it == NULL) it = g_list_last(list);
347 if (valid_focus_target(ft)) {
348 if (ft != focus_cycle_target) { /* prevents flicker */
349 if (focus_cycle_target)
350 frame_adjust_focus(focus_cycle_target->frame, FALSE);
351 focus_cycle_target = ft;
352 frame_adjust_focus(focus_cycle_target->frame, TRUE);
354 popup_cycle(ft, dialog);
357 } while (it != start);
360 if (done && focus_cycle_target)
361 client_activate(focus_cycle_target, FALSE);
365 focus_cycle_target = NULL;
369 popup_cycle(ft, FALSE);
374 void focus_directional_cycle(ObDirection dir,
375 gboolean dialog, gboolean done, gboolean cancel)
377 static ObClient *first = NULL;
381 if (focus_cycle_target)
382 frame_adjust_focus(focus_cycle_target->frame, FALSE);
384 frame_adjust_focus(focus_client->frame, TRUE);
385 focus_cycle_target = NULL;
387 } else if (done && dialog) {
391 if (!focus_order[screen_desktop])
394 if (!first) first = focus_client;
395 if (!focus_cycle_target) focus_cycle_target = focus_client;
397 if (focus_cycle_target)
398 ft = client_find_directional(focus_cycle_target, dir);
402 for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
403 if (valid_focus_target(it->data))
408 if (ft != focus_cycle_target) {/* prevents flicker */
409 if (focus_cycle_target)
410 frame_adjust_focus(focus_cycle_target->frame, FALSE);
411 focus_cycle_target = ft;
412 frame_adjust_focus(focus_cycle_target->frame, TRUE);
415 if (focus_cycle_target) {
416 popup_cycle(focus_cycle_target, dialog);
423 if (done && focus_cycle_target)
424 client_activate(focus_cycle_target, FALSE);
427 focus_cycle_target = NULL;
429 popup_cycle(ft, FALSE);
434 void focus_order_add_new(ObClient *c)
439 focus_order_to_top(c);
442 if (d == DESKTOP_ALL) {
443 for (i = 0; i < screen_num_desktops; ++i) {
444 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
445 focus_order[i] = g_list_insert(focus_order[i], c, 0);
447 focus_order[i] = g_list_insert(focus_order[i], c, 1);
450 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
451 focus_order[d] = g_list_insert(focus_order[d], c, 0);
453 focus_order[d] = g_list_insert(focus_order[d], c, 1);
457 void focus_order_remove(ObClient *c)
462 if (d == DESKTOP_ALL) {
463 for (i = 0; i < screen_num_desktops; ++i)
464 focus_order[i] = g_list_remove(focus_order[i], c);
466 focus_order[d] = g_list_remove(focus_order[d], c);
469 static void to_top(ObClient *c, guint d)
471 focus_order[d] = g_list_remove(focus_order[d], c);
473 focus_order[d] = g_list_prepend(focus_order[d], c);
477 /* insert before first iconic window */
478 for (it = focus_order[d];
479 it && !((ObClient*)it->data)->iconic; it = it->next);
480 focus_order[d] = g_list_insert_before(focus_order[d], it, c);
484 void focus_order_to_top(ObClient *c)
489 if (d == DESKTOP_ALL) {
490 for (i = 0; i < screen_num_desktops; ++i)
496 static void to_bottom(ObClient *c, guint d)
498 focus_order[d] = g_list_remove(focus_order[d], c);
500 focus_order[d] = g_list_append(focus_order[d], c);
504 /* insert before first iconic window */
505 for (it = focus_order[d];
506 it && !((ObClient*)it->data)->iconic; it = it->next);
507 g_list_insert_before(focus_order[d], it, c);
511 void focus_order_to_bottom(ObClient *c)
516 if (d == DESKTOP_ALL) {
517 for (i = 0; i < screen_num_desktops; ++i)