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 (!config_focus_last
&& config_focus_follow
)
183 if (focus_under_pointer())
186 if (type
== OB_FOCUS_FALLBACK_UNFOCUSING
&& old
) {
187 /* try for transient relations */
188 if (old
->transient_for
) {
189 if (old
->transient_for
== OB_TRAN_GROUP
) {
190 for (it
= focus_order
[screen_desktop
]; it
; it
= it
->next
) {
193 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
194 if (sit
->data
== it
->data
)
195 if (focus_fallback_transient(sit
->data
, old
))
199 if (focus_fallback_transient(old
->transient_for
, old
))
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
) &&
225 /* dont fall back to 'anonymous' fullscreen windows. theres no
226 checks for this is in transient/group fallbacks, so they can
227 be fallback targets there. */
228 !((ObClient
*)it
->data
)->fullscreen
&&
229 client_can_focus(it
->data
)) {
230 gboolean r
= client_focus(it
->data
);
235 /* nothing to focus, and already set it to none above */
238 static void popup_cycle(ObClient
*c
, gboolean show
)
241 icon_popup_hide(focus_cycle_popup
);
247 a
= screen_physical_area_monitor(0);
248 icon_popup_position(focus_cycle_popup
, CenterGravity
,
249 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
250 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
251 icon_popup_show(focus_cycle_popup, c->title,
252 client_icon(c, a->height/16, a->height/16));
254 /* XXX the size and the font extents need to be related on some level
256 icon_popup_size(focus_cycle_popup
, POPUP_WIDTH
, POPUP_HEIGHT
);
258 /* use the transient's parent's title/icon */
259 while (p
->transient_for
&& p
->transient_for
!= OB_TRAN_GROUP
)
260 p
= p
->transient_for
;
265 title
= g_strconcat((c
->iconic
? c
->icon_title
: c
->title
),
267 (p
->iconic
? p
->icon_title
: p
->title
),
270 icon_popup_show(focus_cycle_popup
,
272 (c
->iconic
? c
->icon_title
: c
->title
)),
273 client_icon(p
, 48, 48));
278 static gboolean
valid_focus_target(ObClient
*ft
)
280 /* we don't use client_can_focus here, because that doesn't let you
281 focus an iconic window, but we want to be able to, so we just check
282 if the focus flags on the window allow it, and its on the current
284 return (ft
->transients
== NULL
&& client_normal(ft
) &&
285 ((ft
->can_focus
|| ft
->focus_notify
) &&
287 (ft
->desktop
== screen_desktop
|| ft
->desktop
== DESKTOP_ALL
)));
290 void focus_cycle(gboolean forward
, gboolean linear
,
291 gboolean dialog
, gboolean done
, gboolean cancel
)
293 static ObClient
*first
= NULL
;
294 static ObClient
*t
= NULL
;
295 static GList
*order
= NULL
;
296 GList
*it
, *start
, *list
;
300 if (focus_cycle_target
)
301 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
303 frame_adjust_focus(focus_client
->frame
, TRUE
);
304 focus_cycle_target
= NULL
;
306 } else if (done
&& dialog
) {
310 if (!focus_order
[screen_desktop
])
313 if (!first
) first
= focus_client
;
314 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
316 if (linear
) list
= client_list
;
317 else list
= focus_order
[screen_desktop
];
319 start
= it
= g_list_find(list
, focus_cycle_target
);
320 if (!start
) /* switched desktops or something? */
321 start
= it
= forward
? g_list_last(list
) : g_list_first(list
);
322 if (!start
) goto done_cycle
;
327 if (it
== NULL
) it
= g_list_first(list
);
330 if (it
== NULL
) it
= g_list_last(list
);
332 /*ft = client_focus_target(it->data);*/
334 if (valid_focus_target(ft
)) {
335 if (ft
!= focus_cycle_target
) { /* prevents flicker */
336 if (focus_cycle_target
)
337 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
338 focus_cycle_target
= ft
;
339 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
341 popup_cycle(ft
, dialog
);
344 } while (it
!= start
);
347 if (done
&& focus_cycle_target
)
348 client_activate(focus_cycle_target
, FALSE
);
352 focus_cycle_target
= NULL
;
356 popup_cycle(ft
, FALSE
);
361 void focus_directional_cycle(ObDirection dir
,
362 gboolean dialog
, gboolean done
, gboolean cancel
)
364 static ObClient
*first
= NULL
;
368 if (focus_cycle_target
)
369 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
371 frame_adjust_focus(focus_client
->frame
, TRUE
);
372 focus_cycle_target
= NULL
;
374 } else if (done
&& dialog
) {
378 if (!focus_order
[screen_desktop
])
381 if (!first
) first
= focus_client
;
382 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
384 if (focus_cycle_target
)
385 ft
= client_find_directional(focus_cycle_target
, dir
);
389 for (it
= focus_order
[screen_desktop
]; it
; it
= g_list_next(it
))
390 if (valid_focus_target(it
->data
))
395 if (ft
!= focus_cycle_target
) {/* prevents flicker */
396 if (focus_cycle_target
)
397 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
398 focus_cycle_target
= ft
;
399 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
402 if (focus_cycle_target
) {
403 popup_cycle(focus_cycle_target
, dialog
);
410 if (done
&& focus_cycle_target
)
411 client_activate(focus_cycle_target
, FALSE
);
414 focus_cycle_target
= NULL
;
416 popup_cycle(ft
, FALSE
);
421 void focus_order_add_new(ObClient
*c
)
426 focus_order_to_top(c
);
429 if (d
== DESKTOP_ALL
) {
430 for (i
= 0; i
< screen_num_desktops
; ++i
) {
431 if (focus_order
[i
] && ((ObClient
*)focus_order
[i
]->data
)->iconic
)
432 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 0);
434 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 1);
437 if (focus_order
[d
] && ((ObClient
*)focus_order
[d
]->data
)->iconic
)
438 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 0);
440 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 1);
444 void focus_order_remove(ObClient
*c
)
449 if (d
== DESKTOP_ALL
) {
450 for (i
= 0; i
< screen_num_desktops
; ++i
)
451 focus_order
[i
] = g_list_remove(focus_order
[i
], c
);
453 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
456 static void to_top(ObClient
*c
, guint d
)
458 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
460 focus_order
[d
] = g_list_prepend(focus_order
[d
], c
);
464 /* insert before first iconic window */
465 for (it
= focus_order
[d
];
466 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
467 focus_order
[d
] = g_list_insert_before(focus_order
[d
], it
, c
);
471 void focus_order_to_top(ObClient
*c
)
476 if (d
== DESKTOP_ALL
) {
477 for (i
= 0; i
< screen_num_desktops
; ++i
)
483 static void to_bottom(ObClient
*c
, guint d
)
485 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
487 focus_order
[d
] = g_list_append(focus_order
[d
], c
);
491 /* insert before first iconic window */
492 for (it
= focus_order
[d
];
493 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
494 g_list_insert_before(focus_order
[d
], it
, c
);
498 void focus_order_to_bottom(ObClient
*c
)
503 if (d
== DESKTOP_ALL
) {
504 for (i
= 0; i
< screen_num_desktops
; ++i
)