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 /* try for transient relations */
184 if (old
->transient_for
) {
185 if (old
->transient_for
== OB_TRAN_GROUP
) {
186 for (it
= focus_order
[screen_desktop
]; it
; it
= it
->next
) {
189 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
190 if (sit
->data
== it
->data
)
191 if (focus_fallback_transient(sit
->data
, old
))
195 if (focus_fallback_transient(old
->transient_for
, old
))
200 if (!config_focus_last
&& config_focus_follow
)
201 if (focus_under_pointer())
205 /* try for group relations */
209 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
210 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
211 if (sit
->data
== it
->data
)
212 if (sit
->data
!= old
&& client_normal(sit
->data
))
213 if (client_can_focus(sit
->data
)) {
214 gboolean r
= client_focus(sit
->data
);
222 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
223 if (type
!= OB_FOCUS_FALLBACK_UNFOCUSING
|| it
->data
!= old
)
224 if (client_normal(it
->data
) && client_can_focus(it
->data
)) {
225 gboolean r
= client_focus(it
->data
);
230 /* nothing to focus, and already set it to none above */
233 static void popup_cycle(ObClient
*c
, gboolean show
)
236 icon_popup_hide(focus_cycle_popup
);
242 a
= screen_physical_area_monitor(0);
243 icon_popup_position(focus_cycle_popup
, CenterGravity
,
244 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
245 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
246 icon_popup_show(focus_cycle_popup, c->title,
247 client_icon(c, a->height/16, a->height/16));
249 /* XXX the size and the font extents need to be related on some level
251 icon_popup_size(focus_cycle_popup
, POPUP_WIDTH
, POPUP_HEIGHT
);
253 /* use the transient's parent's title/icon */
254 while (p
->transient_for
&& p
->transient_for
!= OB_TRAN_GROUP
)
255 p
= p
->transient_for
;
260 title
= g_strconcat((c
->iconic
? c
->icon_title
: c
->title
),
262 (p
->iconic
? p
->icon_title
: p
->title
),
265 icon_popup_show(focus_cycle_popup
,
267 (c
->iconic
? c
->icon_title
: c
->title
)),
268 client_icon(p
, 48, 48));
273 static gboolean
valid_focus_target(ObClient
*ft
)
275 /* we don't use client_can_focus here, because that doesn't let you
276 focus an iconic window, but we want to be able to, so we just check
277 if the focus flags on the window allow it, and its on the current
279 return (ft
== client_focus_target(ft
) && client_normal(ft
) &&
280 ((ft
->can_focus
|| ft
->focus_notify
) &&
282 (ft
->desktop
== screen_desktop
|| ft
->desktop
== DESKTOP_ALL
)));
285 void focus_cycle(gboolean forward
, gboolean linear
,
286 gboolean dialog
, gboolean done
, gboolean cancel
)
288 static ObClient
*first
= NULL
;
289 static ObClient
*t
= NULL
;
290 static GList
*order
= NULL
;
291 GList
*it
, *start
, *list
;
295 if (focus_cycle_target
)
296 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
298 frame_adjust_focus(focus_client
->frame
, TRUE
);
299 focus_cycle_target
= NULL
;
301 } else if (done
&& dialog
) {
305 if (!focus_order
[screen_desktop
])
308 if (!first
) first
= focus_client
;
309 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
311 if (linear
) list
= client_list
;
312 else list
= focus_order
[screen_desktop
];
314 start
= it
= g_list_find(list
, focus_cycle_target
);
315 if (!start
) /* switched desktops or something? */
316 start
= it
= forward
? g_list_last(list
) : g_list_first(list
);
317 if (!start
) goto done_cycle
;
322 if (it
== NULL
) it
= g_list_first(list
);
325 if (it
== NULL
) it
= g_list_last(list
);
328 if (valid_focus_target(ft
)) {
329 if (ft
!= focus_cycle_target
) { /* prevents flicker */
330 if (focus_cycle_target
)
331 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
332 focus_cycle_target
= ft
;
333 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
335 popup_cycle(ft
, dialog
);
338 } while (it
!= start
);
341 if (done
&& focus_cycle_target
)
342 client_activate(focus_cycle_target
, FALSE
);
346 focus_cycle_target
= NULL
;
350 popup_cycle(ft
, FALSE
);
355 void focus_directional_cycle(ObDirection dir
,
356 gboolean dialog
, gboolean done
, gboolean cancel
)
358 static ObClient
*first
= NULL
;
362 if (focus_cycle_target
)
363 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
365 frame_adjust_focus(focus_client
->frame
, TRUE
);
366 focus_cycle_target
= NULL
;
368 } else if (done
&& dialog
) {
372 if (!focus_order
[screen_desktop
])
375 if (!first
) first
= focus_client
;
376 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
378 if (focus_cycle_target
)
379 ft
= client_find_directional(focus_cycle_target
, dir
);
383 for (it
= focus_order
[screen_desktop
]; it
; it
= g_list_next(it
))
384 if (valid_focus_target(it
->data
))
389 if (ft
!= focus_cycle_target
) {/* prevents flicker */
390 if (focus_cycle_target
)
391 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
392 focus_cycle_target
= ft
;
393 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
396 if (focus_cycle_target
) {
397 popup_cycle(focus_cycle_target
, dialog
);
404 if (done
&& focus_cycle_target
)
405 client_activate(focus_cycle_target
, FALSE
);
408 focus_cycle_target
= NULL
;
410 popup_cycle(ft
, FALSE
);
415 void focus_order_add_new(ObClient
*c
)
420 focus_order_to_top(c
);
423 if (d
== DESKTOP_ALL
) {
424 for (i
= 0; i
< screen_num_desktops
; ++i
) {
425 if (focus_order
[i
] && ((ObClient
*)focus_order
[i
]->data
)->iconic
)
426 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 0);
428 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 1);
431 if (focus_order
[d
] && ((ObClient
*)focus_order
[d
]->data
)->iconic
)
432 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 0);
434 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 1);
438 void focus_order_remove(ObClient
*c
)
443 if (d
== DESKTOP_ALL
) {
444 for (i
= 0; i
< screen_num_desktops
; ++i
)
445 focus_order
[i
] = g_list_remove(focus_order
[i
], c
);
447 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
450 static void to_top(ObClient
*c
, guint d
)
452 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
454 focus_order
[d
] = g_list_prepend(focus_order
[d
], c
);
458 /* insert before first iconic window */
459 for (it
= focus_order
[d
];
460 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
461 focus_order
[d
] = g_list_insert_before(focus_order
[d
], it
, c
);
465 void focus_order_to_top(ObClient
*c
)
470 if (d
== DESKTOP_ALL
) {
471 for (i
= 0; i
< screen_num_desktops
; ++i
)
477 static void to_bottom(ObClient
*c
, guint d
)
479 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
481 focus_order
[d
] = g_list_append(focus_order
[d
], c
);
485 /* insert before first iconic window */
486 for (it
= focus_order
[d
];
487 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
488 g_list_insert_before(focus_order
[d
], it
, c
);
492 void focus_order_to_bottom(ObClient
*c
)
497 if (d
== DESKTOP_ALL
) {
498 for (i
= 0; i
< screen_num_desktops
; ++i
)