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_follow
&& focus_under_pointer())
173 if (type
== OB_FOCUS_FALLBACK_UNFOCUSING
&& old
) {
174 /* try for transient relations */
175 if (old
->transient_for
) {
176 if (old
->transient_for
== OB_TRAN_GROUP
) {
177 for (it
= focus_order
[screen_desktop
]; it
; it
= it
->next
) {
180 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
181 if (sit
->data
== it
->data
)
182 if (focus_fallback_transient(sit
->data
, old
))
186 if (focus_fallback_transient(old
->transient_for
, old
))
192 /* try for group relations */
196 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
197 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
198 if (sit
->data
== it
->data
)
199 if (sit
->data
!= old
&& client_normal(sit
->data
))
200 if (client_can_focus(sit
->data
)) {
201 gboolean r
= client_focus(sit
->data
);
209 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
210 if (type
!= OB_FOCUS_FALLBACK_UNFOCUSING
|| it
->data
!= old
)
211 if (client_normal(it
->data
) &&
212 /* dont fall back to 'anonymous' fullscreen windows. theres no
213 checks for this is in transient/group fallbacks, so they can
214 be fallback targets there. */
215 !((ObClient
*)it
->data
)->fullscreen
&&
216 client_can_focus(it
->data
)) {
217 gboolean r
= client_focus(it
->data
);
222 /* nothing to focus, and already set it to none above */
225 static void popup_cycle(ObClient
*c
, gboolean show
)
228 icon_popup_hide(focus_cycle_popup
);
234 a
= screen_physical_area_monitor(0);
235 icon_popup_position(focus_cycle_popup
, CenterGravity
,
236 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
237 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
238 icon_popup_show(focus_cycle_popup, c->title,
239 client_icon(c, a->height/16, a->height/16));
241 /* XXX the size and the font extents need to be related on some level
243 icon_popup_size(focus_cycle_popup
, POPUP_WIDTH
, POPUP_HEIGHT
);
245 /* use the transient's parent's title/icon */
246 while (p
->transient_for
&& p
->transient_for
!= OB_TRAN_GROUP
)
247 p
= p
->transient_for
;
252 title
= g_strconcat((c
->iconic
? c
->icon_title
: c
->title
),
254 (p
->iconic
? p
->icon_title
: p
->title
),
257 icon_popup_show(focus_cycle_popup
,
259 (c
->iconic
? c
->icon_title
: c
->title
)),
260 client_icon(p
, 48, 48));
265 static gboolean
valid_focus_target(ObClient
*ft
)
267 /* we don't use client_can_focus here, because that doesn't let you
268 focus an iconic window, but we want to be able to, so we just check
269 if the focus flags on the window allow it, and its on the current
271 return (ft
->transients
== NULL
&& client_normal(ft
) &&
272 ((ft
->can_focus
|| ft
->focus_notify
) &&
274 (ft
->desktop
== screen_desktop
|| ft
->desktop
== DESKTOP_ALL
)));
277 void focus_cycle(gboolean forward
, gboolean linear
,
278 gboolean dialog
, gboolean done
, gboolean cancel
)
280 static ObClient
*first
= NULL
;
281 static ObClient
*t
= NULL
;
282 static GList
*order
= NULL
;
283 GList
*it
, *start
, *list
;
287 if (focus_cycle_target
)
288 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
290 frame_adjust_focus(focus_client
->frame
, TRUE
);
291 focus_cycle_target
= NULL
;
293 } else if (done
&& dialog
) {
297 if (!focus_order
[screen_desktop
])
300 if (!first
) first
= focus_client
;
301 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
303 if (linear
) list
= client_list
;
304 else list
= focus_order
[screen_desktop
];
306 start
= it
= g_list_find(list
, focus_cycle_target
);
307 if (!start
) /* switched desktops or something? */
308 start
= it
= forward
? g_list_last(list
) : g_list_first(list
);
309 if (!start
) goto done_cycle
;
314 if (it
== NULL
) it
= g_list_first(list
);
317 if (it
== NULL
) it
= g_list_last(list
);
319 /*ft = client_focus_target(it->data);*/
321 if (valid_focus_target(ft
)) {
322 if (ft
!= focus_cycle_target
) { /* prevents flicker */
323 if (focus_cycle_target
)
324 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
325 focus_cycle_target
= ft
;
326 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
328 popup_cycle(ft
, dialog
);
331 } while (it
!= start
);
334 if (done
&& focus_cycle_target
)
335 client_activate(focus_cycle_target
, FALSE
);
339 focus_cycle_target
= NULL
;
343 popup_cycle(ft
, FALSE
);
348 void focus_directional_cycle(ObDirection dir
,
349 gboolean dialog
, gboolean done
, gboolean cancel
)
351 static ObClient
*first
= NULL
;
355 if (focus_cycle_target
)
356 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
358 frame_adjust_focus(focus_client
->frame
, TRUE
);
359 focus_cycle_target
= NULL
;
361 } else if (done
&& dialog
) {
365 if (!focus_order
[screen_desktop
])
368 if (!first
) first
= focus_client
;
369 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
371 if (focus_cycle_target
)
372 ft
= client_find_directional(focus_cycle_target
, dir
);
376 for (it
= focus_order
[screen_desktop
]; it
; it
= g_list_next(it
))
377 if (valid_focus_target(it
->data
))
382 if (ft
!= focus_cycle_target
) {/* prevents flicker */
383 if (focus_cycle_target
)
384 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
385 focus_cycle_target
= ft
;
386 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
389 if (focus_cycle_target
) {
390 popup_cycle(focus_cycle_target
, dialog
);
397 if (done
&& focus_cycle_target
)
398 client_activate(focus_cycle_target
, FALSE
);
401 focus_cycle_target
= NULL
;
403 popup_cycle(ft
, FALSE
);
408 void focus_order_add_new(ObClient
*c
)
413 focus_order_to_top(c
);
416 if (d
== DESKTOP_ALL
) {
417 for (i
= 0; i
< screen_num_desktops
; ++i
) {
418 if (focus_order
[i
] && ((ObClient
*)focus_order
[i
]->data
)->iconic
)
419 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 0);
421 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 1);
424 if (focus_order
[d
] && ((ObClient
*)focus_order
[d
]->data
)->iconic
)
425 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 0);
427 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 1);
431 void focus_order_remove(ObClient
*c
)
436 if (d
== DESKTOP_ALL
) {
437 for (i
= 0; i
< screen_num_desktops
; ++i
)
438 focus_order
[i
] = g_list_remove(focus_order
[i
], c
);
440 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
443 static void to_top(ObClient
*c
, guint d
)
445 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
447 focus_order
[d
] = g_list_prepend(focus_order
[d
], c
);
451 /* insert before first iconic window */
452 for (it
= focus_order
[d
];
453 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
454 focus_order
[d
] = g_list_insert_before(focus_order
[d
], it
, c
);
458 void focus_order_to_top(ObClient
*c
)
463 if (d
== DESKTOP_ALL
) {
464 for (i
= 0; i
< screen_num_desktops
; ++i
)
470 static void to_bottom(ObClient
*c
, guint d
)
472 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
474 focus_order
[d
] = g_list_append(focus_order
[d
], c
);
478 /* insert before first iconic window */
479 for (it
= focus_order
[d
];
480 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
481 g_list_insert_before(focus_order
[d
], it
, c
);
485 void focus_order_to_bottom(ObClient
*c
)
490 if (d
== DESKTOP_ALL
) {
491 for (i
= 0; i
< screen_num_desktops
; ++i
)