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 /* finds the first transient that isn't 'skip' and ensure's that client_normal
136 static ObClient
*find_transient_recursive(ObClient
*c
, ObClient
*top
, ObClient
*skip
)
141 for (it
= c
->transients
; it
; it
= it
->next
) {
142 if (it
->data
== top
) return NULL
;
143 ret
= find_transient_recursive(it
->data
, top
, skip
);
144 if (ret
&& ret
!= skip
&& client_normal(ret
)) return ret
;
145 if (it
->data
!= skip
&& client_normal(it
->data
)) return it
->data
;
150 static ObClient
* focus_fallback_transient(ObClient
*top
, ObClient
*old
)
152 ObClient
*target
= find_transient_recursive(top
, top
, old
);
154 /* make sure client_normal is true always */
155 if (!client_normal(top
))
157 target
= top
; /* no transient, keep the top */
159 if (client_can_focus(target
))
165 ObClient
* focus_fallback_target(ObFocusFallbackType type
)
168 ObClient
*old
= NULL
;
169 ObClient
*target
= NULL
;
173 if (type
== OB_FOCUS_FALLBACK_UNFOCUSING
&& old
) {
174 if (old
->transient_for
) {
175 gboolean trans
= FALSE
;
177 if (!config_focus_follow
)
180 if ((target
= client_under_pointer()) &&
181 client_search_transient
182 (client_search_top_transient(target
), old
))
188 /* try for transient relations */
190 if (old
->transient_for
== OB_TRAN_GROUP
) {
191 for (it
= focus_order
[screen_desktop
]; it
; it
= it
->next
) {
194 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
195 if (sit
->data
== it
->data
)
197 focus_fallback_transient(sit
->data
, old
)))
202 focus_fallback_transient(old
->transient_for
, old
)))
209 if (config_focus_follow
) {
210 if ((target
= client_under_pointer()))
211 if (client_normal(target
) && client_can_focus(target
))
216 /* try for group relations */
220 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
221 for (sit
= old
->group
->members
; sit
; sit
= sit
->next
)
222 if (sit
->data
== it
->data
)
223 if (sit
->data
!= old
&& client_normal(sit
->data
))
224 if (client_can_focus(sit
->data
))
229 for (it
= focus_order
[screen_desktop
]; it
!= NULL
; it
= it
->next
)
230 if (type
!= OB_FOCUS_FALLBACK_UNFOCUSING
|| it
->data
!= old
)
231 if (client_normal(it
->data
) && client_can_focus(it
->data
))
237 void focus_fallback(ObFocusFallbackType type
)
241 /* unfocus any focused clients.. they can be focused by Pointer events
242 and such, and then when I try focus them, I won't get a FocusIn event
245 focus_set_client(NULL
);
247 if ((new = focus_fallback_target(type
)))
251 static void popup_cycle(ObClient
*c
, gboolean show
)
254 icon_popup_hide(focus_cycle_popup
);
260 a
= screen_physical_area_monitor(0);
261 icon_popup_position(focus_cycle_popup
, CenterGravity
,
262 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
263 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
264 icon_popup_show(focus_cycle_popup, c->title,
265 client_icon(c, a->height/16, a->height/16));
267 /* XXX the size and the font extents need to be related on some level
269 icon_popup_size(focus_cycle_popup
, POPUP_WIDTH
, POPUP_HEIGHT
);
271 /* use the transient's parent's title/icon */
272 while (p
->transient_for
&& p
->transient_for
!= OB_TRAN_GROUP
)
273 p
= p
->transient_for
;
278 title
= g_strconcat((c
->iconic
? c
->icon_title
: c
->title
),
280 (p
->iconic
? p
->icon_title
: p
->title
),
283 icon_popup_show(focus_cycle_popup
,
285 (c
->iconic
? c
->icon_title
: c
->title
)),
286 client_icon(p
, 48, 48));
291 static gboolean
valid_focus_target(ObClient
*ft
)
293 /* we don't use client_can_focus here, because that doesn't let you
294 focus an iconic window, but we want to be able to, so we just check
295 if the focus flags on the window allow it, and its on the current
297 return (!ft
->transients
&& client_normal(ft
) &&
298 ((ft
->can_focus
|| ft
->focus_notify
) &&
300 (ft
->desktop
== screen_desktop
|| ft
->desktop
== DESKTOP_ALL
)));
303 void focus_cycle(gboolean forward
, gboolean linear
,
304 gboolean dialog
, gboolean done
, gboolean cancel
)
306 static ObClient
*first
= NULL
;
307 static ObClient
*t
= NULL
;
308 static GList
*order
= NULL
;
309 GList
*it
, *start
, *list
;
313 if (focus_cycle_target
)
314 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
316 frame_adjust_focus(focus_client
->frame
, TRUE
);
317 focus_cycle_target
= NULL
;
319 } else if (done
&& dialog
) {
323 if (!focus_order
[screen_desktop
])
326 if (!first
) first
= focus_client
;
327 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
329 if (linear
) list
= client_list
;
330 else list
= focus_order
[screen_desktop
];
332 start
= it
= g_list_find(list
, focus_cycle_target
);
333 if (!start
) /* switched desktops or something? */
334 start
= it
= forward
? g_list_last(list
) : g_list_first(list
);
335 if (!start
) goto done_cycle
;
340 if (it
== NULL
) it
= g_list_first(list
);
343 if (it
== NULL
) it
= g_list_last(list
);
346 if (valid_focus_target(ft
)) {
347 if (ft
!= focus_cycle_target
) { /* prevents flicker */
348 if (focus_cycle_target
)
349 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
350 focus_cycle_target
= ft
;
351 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
353 popup_cycle(ft
, dialog
);
356 } while (it
!= start
);
359 if (done
&& focus_cycle_target
)
360 client_activate(focus_cycle_target
, FALSE
);
364 focus_cycle_target
= NULL
;
368 popup_cycle(ft
, FALSE
);
373 void focus_directional_cycle(ObDirection dir
,
374 gboolean dialog
, gboolean done
, gboolean cancel
)
376 static ObClient
*first
= NULL
;
380 if (focus_cycle_target
)
381 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
383 frame_adjust_focus(focus_client
->frame
, TRUE
);
384 focus_cycle_target
= NULL
;
386 } else if (done
&& dialog
) {
390 if (!focus_order
[screen_desktop
])
393 if (!first
) first
= focus_client
;
394 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
396 if (focus_cycle_target
)
397 ft
= client_find_directional(focus_cycle_target
, dir
);
401 for (it
= focus_order
[screen_desktop
]; it
; it
= g_list_next(it
))
402 if (valid_focus_target(it
->data
))
407 if (ft
!= focus_cycle_target
) {/* prevents flicker */
408 if (focus_cycle_target
)
409 frame_adjust_focus(focus_cycle_target
->frame
, FALSE
);
410 focus_cycle_target
= ft
;
411 frame_adjust_focus(focus_cycle_target
->frame
, TRUE
);
414 if (focus_cycle_target
) {
415 popup_cycle(focus_cycle_target
, dialog
);
422 if (done
&& focus_cycle_target
)
423 client_activate(focus_cycle_target
, FALSE
);
426 focus_cycle_target
= NULL
;
428 popup_cycle(ft
, FALSE
);
433 void focus_order_add_new(ObClient
*c
)
438 focus_order_to_top(c
);
441 if (d
== DESKTOP_ALL
) {
442 for (i
= 0; i
< screen_num_desktops
; ++i
) {
443 if (focus_order
[i
] && ((ObClient
*)focus_order
[i
]->data
)->iconic
)
444 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 0);
446 focus_order
[i
] = g_list_insert(focus_order
[i
], c
, 1);
449 if (focus_order
[d
] && ((ObClient
*)focus_order
[d
]->data
)->iconic
)
450 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 0);
452 focus_order
[d
] = g_list_insert(focus_order
[d
], c
, 1);
456 void focus_order_remove(ObClient
*c
)
461 if (d
== DESKTOP_ALL
) {
462 for (i
= 0; i
< screen_num_desktops
; ++i
)
463 focus_order
[i
] = g_list_remove(focus_order
[i
], c
);
465 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
468 static void to_top(ObClient
*c
, guint d
)
470 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
472 focus_order
[d
] = g_list_prepend(focus_order
[d
], c
);
476 /* insert before first iconic window */
477 for (it
= focus_order
[d
];
478 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
479 focus_order
[d
] = g_list_insert_before(focus_order
[d
], it
, c
);
483 void focus_order_to_top(ObClient
*c
)
488 if (d
== DESKTOP_ALL
) {
489 for (i
= 0; i
< screen_num_desktops
; ++i
)
495 static void to_bottom(ObClient
*c
, guint d
)
497 focus_order
[d
] = g_list_remove(focus_order
[d
], c
);
499 focus_order
[d
] = g_list_append(focus_order
[d
], c
);
503 /* insert before first iconic window */
504 for (it
= focus_order
[d
];
505 it
&& !((ObClient
*)it
->data
)->iconic
; it
= it
->next
);
506 g_list_insert_before(focus_order
[d
], it
, c
);
510 void focus_order_to_bottom(ObClient
*c
)
515 if (d
== DESKTOP_ALL
) {
516 for (i
= 0; i
< screen_num_desktops
; ++i
)