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
*c
)
47 /* end cycling if the target disappears */
48 if (focus_cycle_target
== c
)
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((GDestroyNotify
) focus_cycle_destructor
);
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((GDestroyNotify
) 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
, RevertToPointerRoot
,
83 static void push_to_top(ObClient
*client
)
87 desktop
= client
->desktop
;
88 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
89 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
], client
);
90 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
], client
);
93 void focus_set_client(ObClient
*client
)
99 ob_debug("focus_set_client 0x%lx\n", client
? client
->window
: 0);
102 /* uninstall the old colormap, and install the new one */
103 screen_install_colormap(focus_client
, FALSE
);
104 screen_install_colormap(client
, TRUE
);
106 if (client
== NULL
) {
107 /* when nothing will be focused, send focus to the backup target */
108 XSetInputFocus(ob_display
, screen_support_win
, RevertToPointerRoot
,
110 XSync(ob_display
, FALSE
);
113 /* in the middle of cycling..? kill it. */
114 if (focus_cycle_target
)
115 focus_cycle(TRUE
, TRUE
, TRUE
, TRUE
, TRUE
);
118 focus_client
= client
;
120 /* move to the top of the list */
124 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
125 if (ob_state() != OB_STATE_EXITING
) {
126 active
= client
? client
->window
: None
;
127 PROP_SET32(RootWindow(ob_display
, ob_screen
),
128 net_active_window
, window
, active
);
132 static gboolean
focus_under_pointer()
136 if ((c
= client_under_pointer()))
137 return client_normal(c
) && client_focus(c
);
141 /* finds the first transient that isn't 'skip' and ensure's that client_normal
143 static ObClient
*find_transient_recursive(ObClient
*c
, ObClient
*top
, ObClient
*skip
)
148 for (it
= c
->transients
; it
; it
= it
->next
) {
149 if (it
->data
== top
) return NULL
;
150 ret
= find_transient_recursive(it
->data
, top
, skip
);
151 if (ret
&& ret
!= skip
&& client_normal(ret
)) return ret
;
152 if (it
->data
!= skip
&& client_normal(it
->data
)) return it
->data
;
157 static gboolean
focus_fallback_transient(ObClient
*top
, ObClient
*old
)
159 ObClient
*target
= find_transient_recursive(top
, top
, old
);
161 /* make sure client_normal is true always */
162 if (!client_normal(top
))
164 target
= top
; /* no transient, keep the top */
166 return client_focus(target
);
169 void focus_fallback(ObFocusFallbackType type
)
172 ObClient
*old
= NULL
;
176 /* unfocus any focused clients.. they can be focused by Pointer events
177 and such, and then when I try focus them, I won't get a FocusIn event
180 focus_set_client(NULL
);
182 if (type
== OB_FOCUS_FALLBACK_UNFOCUSING
&& old
) {
183 if (old
->transient_for
) {
184 gboolean trans
= FALSE
;
186 if (config_focus_last
|| !config_focus_follow
)
191 if ((c
= client_under_pointer()) &&
192 client_search_transient(client_search_top_transient(c
),
197 /* try for transient relations */
199 if (old
->transient_for
== OB_TRAN_GROUP
) {
200 for (it
= focus_order
[screen_desktop
]; it
; it
= it
->next
) {
203 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
204 if (sit
->data
== it
->data
)
205 if (focus_fallback_transient(sit
->data
, old
))
209 if (focus_fallback_transient(old
->transient_for
, old
))
216 if (!config_focus_last
&& config_focus_follow
)
217 if (focus_under_pointer())
221 /* try for group relations */
225 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
226 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
227 if (sit
->data
== it
->data
)
228 if (sit
->data
!= old
&& client_normal(sit
->data
))
229 if (client_can_focus(sit
->data
)) {
230 gboolean r
= client_focus(sit
->data
);
237 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
238 if (type
!= OB_FOCUS_FALLBACK_UNFOCUSING
|| it
->data
!= old
)
239 if (client_normal(it
->data
) && client_can_focus(it
->data
)) {
240 gboolean r
= client_focus(it
->data
);
245 /* nothing to focus, and already set it to none above */
248 static void popup_cycle(ObClient
*c
, gboolean show
)
251 icon_popup_hide(focus_cycle_popup
);
257 a
= screen_physical_area_monitor(0);
258 icon_popup_position(focus_cycle_popup
, CenterGravity
,
259 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
260 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
261 icon_popup_show(focus_cycle_popup, c->title,
262 client_icon(c, a->height/16, a->height/16));
264 /* XXX the size and the font extents need to be related on some level
266 icon_popup_size(focus_cycle_popup
, POPUP_WIDTH
, POPUP_HEIGHT
);
268 /* use the transient's parent's title/icon */
269 while (p
->transient_for
&& p
->transient_for
!= OB_TRAN_GROUP
)
270 p
= p
->transient_for
;
275 title
= g_strconcat((c
->iconic
? c
->icon_title
: c
->title
),
277 (p
->iconic
? p
->icon_title
: p
->title
),
280 icon_popup_show(focus_cycle_popup
,
282 (c
->iconic
? c
->icon_title
: c
->title
)),
283 client_icon(p
, 48, 48));
288 static gboolean
valid_focus_target(ObClient
*ft
)
290 /* we don't use client_can_focus here, because that doesn't let you
291 focus an iconic window, but we want to be able to, so we just check
292 if the focus flags on the window allow it, and its on the current
294 return (ft
== client_focus_target(ft
) && client_normal(ft
) &&
295 ((ft
->can_focus
|| ft
->focus_notify
) &&
297 (ft
->desktop
== screen_desktop
|| ft
->desktop
== DESKTOP_ALL
)));
300 void focus_cycle(gboolean forward
, gboolean linear
,
301 gboolean dialog
, gboolean done
, gboolean cancel
)
303 static ObClient
*first
= NULL
;
304 static ObClient
*t
= NULL
;
305 static GList
*order
= NULL
;
306 GList
*it
, *start
, *list
;
310 if (focus_cycle_target
)
311 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
313 frame_adjust_focus(focus_client
->frame
, TRUE
);
314 focus_cycle_target
= NULL
;
316 } else if (done
&& dialog
) {
320 if (!focus_order
[screen_desktop
])
323 if (!first
) first
= focus_client
;
324 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
326 if (linear
) list
= client_list
;
327 else list
= focus_order
[screen_desktop
];
329 start
= it
= g_list_find(list
, focus_cycle_target
);
330 if (!start
) /* switched desktops or something? */
331 start
= it
= forward
? g_list_last(list
) : g_list_first(list
);
332 if (!start
) goto done_cycle
;
337 if (it
== NULL
) it
= g_list_first(list
);
340 if (it
== NULL
) it
= g_list_last(list
);
343 if (valid_focus_target(ft
)) {
344 if (ft
!= focus_cycle_target
) { /* prevents flicker */
345 if (focus_cycle_target
)
346 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
347 focus_cycle_target
= ft
;
348 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
350 popup_cycle(ft
, dialog
);
353 } while (it
!= start
);
356 if (done
&& focus_cycle_target
)
357 client_activate(focus_cycle_target
, FALSE
);
361 focus_cycle_target
= NULL
;
365 popup_cycle(ft
, FALSE
);
370 void focus_directional_cycle(ObDirection dir
,
371 gboolean dialog
, gboolean done
, gboolean cancel
)
373 static ObClient
*first
= NULL
;
377 if (focus_cycle_target
)
378 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
380 frame_adjust_focus(focus_client
->frame
, TRUE
);
381 focus_cycle_target
= NULL
;
383 } else if (done
&& dialog
) {
387 if (!focus_order
[screen_desktop
])
390 if (!first
) first
= focus_client
;
391 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
393 if (focus_cycle_target
)
394 ft
= client_find_directional(focus_cycle_target
, dir
);
398 for (it
= focus_order
[screen_desktop
]; it
; it
= g_list_next(it
))
399 if (valid_focus_target(it
->data
))
404 if (ft
!= focus_cycle_target
) {/* prevents flicker */
405 if (focus_cycle_target
)
406 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
407 focus_cycle_target
= ft
;
408 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
411 if (focus_cycle_target
) {
412 popup_cycle(focus_cycle_target
, dialog
);
419 if (done
&& focus_cycle_target
)
420 client_activate(focus_cycle_target
, FALSE
);
423 focus_cycle_target
= NULL
;
425 popup_cycle(ft
, FALSE
);
430 void focus_order_add_new(ObClient
*c
)
435 focus_order_to_top(c
);
438 if (d
== DESKTOP_ALL
) {
439 for (i
= 0; i
< screen_num_desktops
; ++i
) {
440 if (focus_order
[i
] && ((ObClient
*)focus_order
[i
]->data
)->iconic
)
441 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 0);
443 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 1);
446 if (focus_order
[d
] && ((ObClient
*)focus_order
[d
]->data
)->iconic
)
447 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 0);
449 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 1);
453 void focus_order_remove(ObClient
*c
)
458 if (d
== DESKTOP_ALL
) {
459 for (i
= 0; i
< screen_num_desktops
; ++i
)
460 focus_order
[i
] = g_list_remove(focus_order
[i
], c
);
462 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
465 static void to_top(ObClient
*c
, guint d
)
467 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
469 focus_order
[d
] = g_list_prepend(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 focus_order
[d
] = g_list_insert_before(focus_order
[d
], it
, c
);
480 void focus_order_to_top(ObClient
*c
)
485 if (d
== DESKTOP_ALL
) {
486 for (i
= 0; i
< screen_num_desktops
; ++i
)
492 static void to_bottom(ObClient
*c
, guint d
)
494 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
496 focus_order
[d
] = g_list_append(focus_order
[d
], c
);
500 /* insert before first iconic window */
501 for (it
= focus_order
[d
];
502 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
503 g_list_insert_before(focus_order
[d
], it
, c
);
507 void focus_order_to_bottom(ObClient
*c
)
512 if (d
== DESKTOP_ALL
) {
513 for (i
= 0; i
< screen_num_desktops
; ++i
)