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
*client
, gpointer data
)
47 /* end cycling if the target disappears */
48 if (focus_cycle_target
== client
)
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(focus_cycle_destructor
, NULL
);
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(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
, RevertToNone
, event_lasttime
);
82 static void push_to_top(ObClient
*client
)
86 desktop
= client
->desktop
;
87 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
88 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
], client
);
89 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
], client
);
92 void focus_set_client(ObClient
*client
)
98 ob_debug("focus_set_client 0x%lx\n", client
? client
->window
: 0);
101 /* uninstall the old colormap, and install the new one */
102 screen_install_colormap(focus_client
, FALSE
);
103 screen_install_colormap(client
, TRUE
);
105 if (client
== NULL
) {
107 ob_debug("actively focusing NONWINDOW\n");
109 /* when nothing will be focused, send focus to the backup target */
110 XSetInputFocus(ob_display
, screen_support_win
, RevertToNone
,
112 XSync(ob_display
, FALSE
);
115 /* in the middle of cycling..? kill it. */
116 if (focus_cycle_target
)
117 focus_cycle(TRUE
, TRUE
, TRUE
, TRUE
, TRUE
);
120 focus_client
= client
;
122 /* move to the top of the list */
126 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
127 if (ob_state() != OB_STATE_EXITING
) {
128 active
= client
? client
->window
: None
;
129 PROP_SET32(RootWindow(ob_display
, ob_screen
),
130 net_active_window
, window
, active
);
134 static gboolean
focus_under_pointer()
138 if ((c
= client_under_pointer()))
139 return client_normal(c
) && client_focus(c
);
143 /* finds the first transient that isn't 'skip' and ensure's that client_normal
145 static ObClient
*find_transient_recursive(ObClient
*c
, ObClient
*top
, ObClient
*skip
)
150 for (it
= c
->transients
; it
; it
= it
->next
) {
151 if (it
->data
== top
) return NULL
;
152 ret
= find_transient_recursive(it
->data
, top
, skip
);
153 if (ret
&& ret
!= skip
&& client_normal(ret
)) return ret
;
154 if (it
->data
!= skip
&& client_normal(it
->data
)) return it
->data
;
159 static gboolean
focus_fallback_transient(ObClient
*top
, ObClient
*old
)
161 ObClient
*target
= find_transient_recursive(top
, top
, old
);
163 /* make sure client_normal is true always */
164 if (!client_normal(top
))
166 target
= top
; /* no transient, keep the top */
168 return client_focus(target
);
171 void focus_fallback(ObFocusFallbackType type
)
174 ObClient
*old
= NULL
;
178 /* unfocus any focused clients.. they can be focused by Pointer events
179 and such, and then when I try focus them, I won't get a FocusIn event
182 focus_set_client(NULL
);
184 if (type
== OB_FOCUS_FALLBACK_UNFOCUSING
&& old
) {
185 if (old
->transient_for
) {
186 gboolean trans
= FALSE
;
188 if (!config_focus_follow
)
193 if ((c
= client_under_pointer()) &&
194 client_search_transient(client_search_top_transient(c
),
199 /* try for transient relations */
201 if (old
->transient_for
== OB_TRAN_GROUP
) {
202 for (it
= focus_order
[screen_desktop
]; it
; it
= it
->next
) {
205 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
206 if (sit
->data
== it
->data
)
207 if (focus_fallback_transient(sit
->data
, old
))
211 if (focus_fallback_transient(old
->transient_for
, old
))
218 if (config_focus_follow
)
219 if (focus_under_pointer())
223 /* try for group relations */
227 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
228 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
229 if (sit
->data
== it
->data
)
230 if (sit
->data
!= old
&& client_normal(sit
->data
))
231 if (client_can_focus(sit
->data
)) {
232 gboolean r
= client_focus(sit
->data
);
239 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
240 if (type
!= OB_FOCUS_FALLBACK_UNFOCUSING
|| it
->data
!= old
)
241 if (client_normal(it
->data
) && client_can_focus(it
->data
)) {
242 gboolean r
= client_focus(it
->data
);
247 /* nothing to focus, and already set it to none above */
250 static void popup_cycle(ObClient
*c
, gboolean show
)
253 icon_popup_hide(focus_cycle_popup
);
259 a
= screen_physical_area_monitor(0);
260 icon_popup_position(focus_cycle_popup
, CenterGravity
,
261 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
262 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
263 icon_popup_show(focus_cycle_popup, c->title,
264 client_icon(c, a->height/16, a->height/16));
266 /* XXX the size and the font extents need to be related on some level
268 icon_popup_size(focus_cycle_popup
, POPUP_WIDTH
, POPUP_HEIGHT
);
270 /* use the transient's parent's title/icon */
271 while (p
->transient_for
&& p
->transient_for
!= OB_TRAN_GROUP
)
272 p
= p
->transient_for
;
277 title
= g_strconcat((c
->iconic
? c
->icon_title
: c
->title
),
279 (p
->iconic
? p
->icon_title
: p
->title
),
282 icon_popup_show(focus_cycle_popup
,
284 (c
->iconic
? c
->icon_title
: c
->title
)),
285 client_icon(p
, 48, 48));
290 static gboolean
valid_focus_target(ObClient
*ft
)
292 /* we don't use client_can_focus here, because that doesn't let you
293 focus an iconic window, but we want to be able to, so we just check
294 if the focus flags on the window allow it, and its on the current
296 return (!ft
->transients
&& client_normal(ft
) &&
297 ((ft
->can_focus
|| ft
->focus_notify
) &&
299 (ft
->desktop
== screen_desktop
|| ft
->desktop
== DESKTOP_ALL
)));
302 void focus_cycle(gboolean forward
, gboolean linear
,
303 gboolean dialog
, gboolean done
, gboolean cancel
)
305 static ObClient
*first
= NULL
;
306 static ObClient
*t
= NULL
;
307 static GList
*order
= NULL
;
308 GList
*it
, *start
, *list
;
312 if (focus_cycle_target
)
313 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
315 frame_adjust_focus(focus_client
->frame
, TRUE
);
316 focus_cycle_target
= NULL
;
318 } else if (done
&& dialog
) {
322 if (!focus_order
[screen_desktop
])
325 if (!first
) first
= focus_client
;
326 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
328 if (linear
) list
= client_list
;
329 else list
= focus_order
[screen_desktop
];
331 start
= it
= g_list_find(list
, focus_cycle_target
);
332 if (!start
) /* switched desktops or something? */
333 start
= it
= forward
? g_list_last(list
) : g_list_first(list
);
334 if (!start
) goto done_cycle
;
339 if (it
== NULL
) it
= g_list_first(list
);
342 if (it
== NULL
) it
= g_list_last(list
);
345 if (valid_focus_target(ft
)) {
346 if (ft
!= focus_cycle_target
) { /* prevents flicker */
347 if (focus_cycle_target
)
348 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
349 focus_cycle_target
= ft
;
350 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
352 popup_cycle(ft
, dialog
);
355 } while (it
!= start
);
358 if (done
&& focus_cycle_target
)
359 client_activate(focus_cycle_target
, FALSE
);
363 focus_cycle_target
= NULL
;
367 popup_cycle(ft
, FALSE
);
372 void focus_directional_cycle(ObDirection dir
,
373 gboolean dialog
, gboolean done
, gboolean cancel
)
375 static ObClient
*first
= NULL
;
379 if (focus_cycle_target
)
380 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
382 frame_adjust_focus(focus_client
->frame
, TRUE
);
383 focus_cycle_target
= NULL
;
385 } else if (done
&& dialog
) {
389 if (!focus_order
[screen_desktop
])
392 if (!first
) first
= focus_client
;
393 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
395 if (focus_cycle_target
)
396 ft
= client_find_directional(focus_cycle_target
, dir
);
400 for (it
= focus_order
[screen_desktop
]; it
; it
= g_list_next(it
))
401 if (valid_focus_target(it
->data
))
406 if (ft
!= focus_cycle_target
) {/* prevents flicker */
407 if (focus_cycle_target
)
408 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
409 focus_cycle_target
= ft
;
410 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
413 if (focus_cycle_target
) {
414 popup_cycle(focus_cycle_target
, dialog
);
421 if (done
&& focus_cycle_target
)
422 client_activate(focus_cycle_target
, FALSE
);
425 focus_cycle_target
= NULL
;
427 popup_cycle(ft
, FALSE
);
432 void focus_order_add_new(ObClient
*c
)
437 focus_order_to_top(c
);
440 if (d
== DESKTOP_ALL
) {
441 for (i
= 0; i
< screen_num_desktops
; ++i
) {
442 if (focus_order
[i
] && ((ObClient
*)focus_order
[i
]->data
)->iconic
)
443 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 0);
445 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 1);
448 if (focus_order
[d
] && ((ObClient
*)focus_order
[d
]->data
)->iconic
)
449 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 0);
451 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 1);
455 void focus_order_remove(ObClient
*c
)
460 if (d
== DESKTOP_ALL
) {
461 for (i
= 0; i
< screen_num_desktops
; ++i
)
462 focus_order
[i
] = g_list_remove(focus_order
[i
], c
);
464 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
467 static void to_top(ObClient
*c
, guint d
)
469 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
471 focus_order
[d
] = g_list_prepend(focus_order
[d
], c
);
475 /* insert before first iconic window */
476 for (it
= focus_order
[d
];
477 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
478 focus_order
[d
] = g_list_insert_before(focus_order
[d
], it
, c
);
482 void focus_order_to_top(ObClient
*c
)
487 if (d
== DESKTOP_ALL
) {
488 for (i
= 0; i
< screen_num_desktops
; ++i
)
494 static void to_bottom(ObClient
*c
, guint d
)
496 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
498 focus_order
[d
] = g_list_append(focus_order
[d
], c
);
502 /* insert before first iconic window */
503 for (it
= focus_order
[d
];
504 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
505 g_list_insert_before(focus_order
[d
], it
, c
);
509 void focus_order_to_bottom(ObClient
*c
)
514 if (d
== DESKTOP_ALL
) {
515 for (i
= 0; i
< screen_num_desktops
; ++i
)