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 void focus_startup(gboolean reconfig
)
47 focus_cycle_popup
= icon_popup_new(TRUE
);
50 /* start with nothing focused */
51 focus_set_client(NULL
);
54 void focus_shutdown(gboolean reconfig
)
58 icon_popup_free(focus_cycle_popup
);
61 for (i
= 0; i
< screen_num_desktops
; ++i
)
62 g_list_free(focus_order
[i
]);
65 /* reset focus to root */
66 XSetInputFocus(ob_display
, PointerRoot
, RevertToPointerRoot
,
71 static void push_to_top(ObClient
*client
)
75 desktop
= client
->desktop
;
76 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
77 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
], client
);
78 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
], client
);
81 void focus_set_client(ObClient
*client
)
87 ob_debug("focus_set_client 0x%lx\n", client
? client
->window
: 0);
90 /* uninstall the old colormap, and install the new one */
91 screen_install_colormap(focus_client
, FALSE
);
92 screen_install_colormap(client
, TRUE
);
95 /* when nothing will be focused, send focus to the backup target */
96 XSetInputFocus(ob_display
, screen_support_win
, RevertToPointerRoot
,
98 XSync(ob_display
, FALSE
);
101 /* in the middle of cycling..? kill it. */
102 if (focus_cycle_target
)
103 focus_cycle(TRUE
, TRUE
, TRUE
, TRUE
, TRUE
);
106 focus_client
= client
;
108 /* move to the top of the list */
112 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
113 if (ob_state() != OB_STATE_EXITING
) {
114 active
= client
? client
->window
: None
;
115 PROP_SET32(RootWindow(ob_display
, ob_screen
),
116 net_active_window
, window
, active
);
120 static gboolean
focus_under_pointer()
124 if ((c
= client_under_pointer()))
125 return client_normal(c
) && client_focus(c
);
129 /* finds the first transient that isn't 'skip' and ensure's that client_normal
131 static ObClient
*find_transient_recursive(ObClient
*c
, ObClient
*top
, ObClient
*skip
)
136 for (it
= c
->transients
; it
; it
= it
->next
) {
137 if (it
->data
== top
) return NULL
;
138 ret
= find_transient_recursive(it
->data
, top
, skip
);
139 if (ret
&& ret
!= skip
&& client_normal(ret
)) return ret
;
140 if (it
->data
!= skip
&& client_normal(it
->data
)) return it
->data
;
145 static gboolean
focus_fallback_transient(ObClient
*top
, ObClient
*old
)
147 ObClient
*target
= find_transient_recursive(top
, top
, old
);
149 /* make sure client_normal is true always */
150 if (!client_normal(top
))
152 target
= top
; /* no transient, keep the top */
154 return client_focus(target
);
157 void focus_fallback(ObFocusFallbackType type
)
160 ObClient
*old
= NULL
;
164 /* unfocus any focused clients.. they can be focused by Pointer events
165 and such, and then when I try focus them, I won't get a FocusIn event
168 focus_set_client(NULL
);
170 if (!config_focus_last
&& config_focus_follow
)
171 if (focus_under_pointer())
174 if (type
== OB_FOCUS_FALLBACK_UNFOCUSING
&& old
) {
175 /* try for transient relations */
176 if (old
->transient_for
) {
177 if (old
->transient_for
== OB_TRAN_GROUP
) {
178 for (it
= focus_order
[screen_desktop
]; it
; it
= it
->next
) {
181 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
182 if (sit
->data
== it
->data
)
183 if (focus_fallback_transient(sit
->data
, old
))
187 if (focus_fallback_transient(old
->transient_for
, old
))
193 /* try for group relations */
197 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
198 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
199 if (sit
->data
== it
->data
)
200 if (sit
->data
!= old
&& client_normal(sit
->data
))
201 if (client_can_focus(sit
->data
)) {
202 gboolean r
= client_focus(sit
->data
);
210 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
211 if (type
!= OB_FOCUS_FALLBACK_UNFOCUSING
|| it
->data
!= old
)
212 if (client_normal(it
->data
) &&
213 /* dont fall back to 'anonymous' fullscreen windows. theres no
214 checks for this is in transient/group fallbacks, so they can
215 be fallback targets there. */
216 !((ObClient
*)it
->data
)->fullscreen
&&
217 client_can_focus(it
->data
)) {
218 gboolean r
= client_focus(it
->data
);
223 /* nothing to focus, and already set it to none above */
226 static void popup_cycle(ObClient
*c
, gboolean show
)
229 icon_popup_hide(focus_cycle_popup
);
235 a
= screen_physical_area_monitor(0);
236 icon_popup_position(focus_cycle_popup
, CenterGravity
,
237 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
238 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
239 icon_popup_show(focus_cycle_popup, c->title,
240 client_icon(c, a->height/16, a->height/16));
242 /* XXX the size and the font extents need to be related on some level
244 icon_popup_size(focus_cycle_popup
, POPUP_WIDTH
, POPUP_HEIGHT
);
246 /* use the transient's parent's title/icon */
247 while (p
->transient_for
&& p
->transient_for
!= OB_TRAN_GROUP
)
248 p
= p
->transient_for
;
253 title
= g_strconcat((c
->iconic
? c
->icon_title
: c
->title
),
255 (p
->iconic
? p
->icon_title
: p
->title
),
258 icon_popup_show(focus_cycle_popup
,
260 (c
->iconic
? c
->icon_title
: c
->title
)),
261 client_icon(p
, 48, 48));
266 static gboolean
valid_focus_target(ObClient
*ft
)
268 /* we don't use client_can_focus here, because that doesn't let you
269 focus an iconic window, but we want to be able to, so we just check
270 if the focus flags on the window allow it, and its on the current
272 return (ft
->transients
== NULL
&& client_normal(ft
) &&
273 ((ft
->can_focus
|| ft
->focus_notify
) &&
275 (ft
->desktop
== screen_desktop
|| ft
->desktop
== DESKTOP_ALL
)));
278 void focus_cycle(gboolean forward
, gboolean linear
,
279 gboolean dialog
, gboolean done
, gboolean cancel
)
281 static ObClient
*first
= NULL
;
282 static ObClient
*t
= NULL
;
283 static GList
*order
= NULL
;
284 GList
*it
, *start
, *list
;
288 if (focus_cycle_target
)
289 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
291 frame_adjust_focus(focus_client
->frame
, TRUE
);
292 focus_cycle_target
= NULL
;
294 } else if (done
&& dialog
) {
298 if (!focus_order
[screen_desktop
])
301 if (!first
) first
= focus_client
;
302 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
304 if (linear
) list
= client_list
;
305 else list
= focus_order
[screen_desktop
];
307 start
= it
= g_list_find(list
, focus_cycle_target
);
308 if (!start
) /* switched desktops or something? */
309 start
= it
= forward
? g_list_last(list
) : g_list_first(list
);
310 if (!start
) goto done_cycle
;
315 if (it
== NULL
) it
= g_list_first(list
);
318 if (it
== NULL
) it
= g_list_last(list
);
320 /*ft = client_focus_target(it->data);*/
322 if (valid_focus_target(ft
)) {
323 if (ft
!= focus_cycle_target
) { /* prevents flicker */
324 if (focus_cycle_target
)
325 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
326 focus_cycle_target
= ft
;
327 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
329 popup_cycle(ft
, dialog
);
332 } while (it
!= start
);
335 if (done
&& focus_cycle_target
)
336 client_activate(focus_cycle_target
, FALSE
);
340 focus_cycle_target
= NULL
;
344 popup_cycle(ft
, FALSE
);
349 void focus_directional_cycle(ObDirection dir
,
350 gboolean dialog
, gboolean done
, gboolean cancel
)
352 static ObClient
*first
= NULL
;
356 if (focus_cycle_target
)
357 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
359 frame_adjust_focus(focus_client
->frame
, TRUE
);
360 focus_cycle_target
= NULL
;
362 } else if (done
&& dialog
) {
366 if (!focus_order
[screen_desktop
])
369 if (!first
) first
= focus_client
;
370 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
372 if (focus_cycle_target
)
373 ft
= client_find_directional(focus_cycle_target
, dir
);
377 for (it
= focus_order
[screen_desktop
]; it
; it
= g_list_next(it
))
378 if (valid_focus_target(it
->data
))
383 if (ft
!= focus_cycle_target
) {/* prevents flicker */
384 if (focus_cycle_target
)
385 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
386 focus_cycle_target
= ft
;
387 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
390 if (focus_cycle_target
) {
391 popup_cycle(focus_cycle_target
, dialog
);
398 if (done
&& focus_cycle_target
)
399 client_activate(focus_cycle_target
, FALSE
);
402 focus_cycle_target
= NULL
;
404 popup_cycle(ft
, FALSE
);
409 void focus_order_add_new(ObClient
*c
)
414 focus_order_to_top(c
);
417 if (d
== DESKTOP_ALL
) {
418 for (i
= 0; i
< screen_num_desktops
; ++i
) {
419 if (focus_order
[i
] && ((ObClient
*)focus_order
[i
]->data
)->iconic
)
420 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 0);
422 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 1);
425 if (focus_order
[d
] && ((ObClient
*)focus_order
[d
]->data
)->iconic
)
426 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 0);
428 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 1);
432 void focus_order_remove(ObClient
*c
)
437 if (d
== DESKTOP_ALL
) {
438 for (i
= 0; i
< screen_num_desktops
; ++i
)
439 focus_order
[i
] = g_list_remove(focus_order
[i
], c
);
441 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
444 static void to_top(ObClient
*c
, guint d
)
446 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
448 focus_order
[d
] = g_list_prepend(focus_order
[d
], c
);
452 /* insert before first iconic window */
453 for (it
= focus_order
[d
];
454 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
455 focus_order
[d
] = g_list_insert_before(focus_order
[d
], it
, c
);
459 void focus_order_to_top(ObClient
*c
)
464 if (d
== DESKTOP_ALL
) {
465 for (i
= 0; i
< screen_num_desktops
; ++i
)
471 static void to_bottom(ObClient
*c
, guint d
)
473 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
475 focus_order
[d
] = g_list_append(focus_order
[d
], c
);
479 /* insert before first iconic window */
480 for (it
= focus_order
[d
];
481 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
482 g_list_insert_before(focus_order
[d
], it
, c
);
486 void focus_order_to_bottom(ObClient
*c
)
491 if (d
== DESKTOP_ALL
) {
492 for (i
= 0; i
< screen_num_desktops
; ++i
)