1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 focus.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
24 #include "framerender.h"
34 #include "render/render.h"
40 #define FOCUS_INDICATOR_WIDTH 6
42 ObClient
*focus_client
= NULL
;
43 GList
*focus_order
= NULL
;
44 ObClient
*focus_cycle_target
= NULL
;
50 InternalWindow bottom
;
53 RrAppearance
*a_focus_indicator
;
56 static ObIconPopup
*focus_cycle_popup
;
58 static gboolean
valid_focus_target(ObClient
*ft
,
59 gboolean all_desktops
,
60 gboolean dock_windows
,
61 gboolean desktop_windows
);
62 static void focus_cycle_destructor(ObClient
*client
, gpointer data
);
64 static Window
createWindow(Window parent
, gulong mask
,
65 XSetWindowAttributes
*attrib
)
67 return XCreateWindow(ob_display
, parent
, 0, 0, 1, 1, 0,
68 RrDepth(ob_rr_inst
), InputOutput
,
69 RrVisual(ob_rr_inst
), mask
, attrib
);
73 void focus_startup(gboolean reconfig
)
75 focus_cycle_popup
= icon_popup_new(TRUE
);
78 XSetWindowAttributes attr
;
80 client_add_destructor(focus_cycle_destructor
, NULL
);
82 /* start with nothing focused */
85 focus_indicator
.top
.obwin
.type
= Window_Internal
;
86 focus_indicator
.left
.obwin
.type
= Window_Internal
;
87 focus_indicator
.right
.obwin
.type
= Window_Internal
;
88 focus_indicator
.bottom
.obwin
.type
= Window_Internal
;
90 attr
.override_redirect
= True
;
91 attr
.background_pixel
= BlackPixel(ob_display
, ob_screen
);
92 focus_indicator
.top
.win
=
93 createWindow(RootWindow(ob_display
, ob_screen
),
94 CWOverrideRedirect
| CWBackPixel
, &attr
);
95 focus_indicator
.left
.win
=
96 createWindow(RootWindow(ob_display
, ob_screen
),
97 CWOverrideRedirect
| CWBackPixel
, &attr
);
98 focus_indicator
.right
.win
=
99 createWindow(RootWindow(ob_display
, ob_screen
),
100 CWOverrideRedirect
| CWBackPixel
, &attr
);
101 focus_indicator
.bottom
.win
=
102 createWindow(RootWindow(ob_display
, ob_screen
),
103 CWOverrideRedirect
| CWBackPixel
, &attr
);
105 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator
.top
));
106 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator
.left
));
107 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator
.right
));
108 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator
.bottom
));
110 color_white
= RrColorNew(ob_rr_inst
, 0xff, 0xff, 0xff);
112 a_focus_indicator
= RrAppearanceNew(ob_rr_inst
, 4);
113 a_focus_indicator
->surface
.grad
= RR_SURFACE_SOLID
;
114 a_focus_indicator
->surface
.relief
= RR_RELIEF_FLAT
;
115 a_focus_indicator
->surface
.primary
= RrColorNew(ob_rr_inst
,
117 a_focus_indicator
->texture
[0].type
= RR_TEXTURE_LINE_ART
;
118 a_focus_indicator
->texture
[0].data
.lineart
.color
= color_white
;
119 a_focus_indicator
->texture
[1].type
= RR_TEXTURE_LINE_ART
;
120 a_focus_indicator
->texture
[1].data
.lineart
.color
= color_white
;
121 a_focus_indicator
->texture
[2].type
= RR_TEXTURE_LINE_ART
;
122 a_focus_indicator
->texture
[2].data
.lineart
.color
= color_white
;
123 a_focus_indicator
->texture
[3].type
= RR_TEXTURE_LINE_ART
;
124 a_focus_indicator
->texture
[3].data
.lineart
.color
= color_white
;
128 void focus_shutdown(gboolean reconfig
)
130 icon_popup_free(focus_cycle_popup
);
133 client_remove_destructor(focus_cycle_destructor
);
135 /* reset focus to root */
136 XSetInputFocus(ob_display
, PointerRoot
, RevertToNone
, CurrentTime
);
138 RrColorFree(color_white
);
140 RrAppearanceFree(a_focus_indicator
);
142 XDestroyWindow(ob_display
, focus_indicator
.top
.win
);
143 XDestroyWindow(ob_display
, focus_indicator
.left
.win
);
144 XDestroyWindow(ob_display
, focus_indicator
.right
.win
);
145 XDestroyWindow(ob_display
, focus_indicator
.bottom
.win
);
149 static void push_to_top(ObClient
*client
)
151 focus_order
= g_list_remove(focus_order
, client
);
152 focus_order
= g_list_prepend(focus_order
, client
);
155 void focus_set_client(ObClient
*client
)
159 ob_debug_type(OB_DEBUG_FOCUS
,
160 "focus_set_client 0x%lx\n", client
? client
->window
: 0);
162 /* uninstall the old colormap, and install the new one */
163 screen_install_colormap(focus_client
, FALSE
);
164 screen_install_colormap(client
, TRUE
);
166 /* in the middle of cycling..? kill it. CurrentTime is fine, time won't
169 if (focus_cycle_target
)
170 focus_cycle(TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
);
172 focus_client
= client
;
174 if (client
!= NULL
) {
175 /* move to the top of the list */
177 /* remove hiliting from the window when it gets focused */
178 client_hilite(client
, FALSE
);
181 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
182 if (ob_state() != OB_STATE_EXITING
) {
183 active
= client
? client
->window
: None
;
184 PROP_SET32(RootWindow(ob_display
, ob_screen
),
185 net_active_window
, window
, active
);
189 ObClient
* focus_fallback_target(gboolean allow_refocus
, ObClient
*old
)
192 ObClient
*target
= NULL
;
193 ObClient
*desktop
= NULL
;
195 ob_debug_type(OB_DEBUG_FOCUS
, "trying pointer stuff\n");
196 if (config_focus_follow
&& !config_focus_last
)
198 if ((target
= client_under_pointer()))
199 if (allow_refocus
|| target
!= old
)
200 if (client_normal(target
) && client_can_focus(target
)) {
201 ob_debug_type(OB_DEBUG_FOCUS
, "found in pointer stuff\n");
207 /* try for group relations */
211 for (it
= focus_order
[screen_desktop
]; it
; it
= g_list_next(it
))
212 for (sit
= old
->group
->members
; sit
; sit
= g_slist_next(sit
))
213 if (sit
->data
== it
->data
)
214 if (sit
->data
!= old
&& client_normal(sit
->data
))
215 if (client_can_focus(sit
->data
))
220 ob_debug_type(OB_DEBUG_FOCUS
, "trying omnipresentness\n");
221 if (allow_refocus
&& old
&& old
->desktop
== DESKTOP_ALL
&&
228 ob_debug_type(OB_DEBUG_FOCUS
, "trying the focus order\n");
229 for (it
= focus_order
; it
; it
= g_list_next(it
))
230 if (allow_refocus
|| it
->data
!= old
) {
231 ObClient
*c
= it
->data
;
232 /* fallback focus to a window if:
233 1. it is actually focusable, cuz if it's not then we're sending
234 focus off to nothing. this includes if it is visible right now
235 2. it is on the current desktop. this ignores omnipresent
236 windows, which are problematic in their own rite.
237 3. it is a normal type window, don't fall back onto a dock or
238 a splashscreen or a desktop window (save the desktop as a
239 backup fallback though)
241 if (client_can_focus(c
))
243 if (c
->desktop
== screen_desktop
&& client_normal(c
)) {
244 ob_debug_type(OB_DEBUG_FOCUS
, "found in focus order\n");
246 } else if (c
->type
== OB_CLIENT_TYPE_DESKTOP
&&
252 /* as a last resort fallback to the desktop window if there is one.
253 (if there's more than one, then the one most recently focused.)
255 ob_debug_type(OB_DEBUG_FOCUS
, "found desktop: \n", !!desktop
);
259 ObClient
* focus_fallback(gboolean allow_refocus
)
262 ObClient
*old
= focus_client
;
264 /* unfocus any focused clients.. they can be focused by Pointer events
265 and such, and then when I try focus them, I won't get a FocusIn event
270 if ((new = focus_fallback_target(allow_refocus
, old
))) {
279 /* Install our own colormap */
280 if (focus_client
!= NULL
) {
281 screen_install_colormap(focus_client
, FALSE
);
282 screen_install_colormap(NULL
, TRUE
);
287 /* when nothing will be focused, send focus to the backup target */
288 XSetInputFocus(ob_display
, screen_support_win
, RevertToPointerRoot
,
292 static gchar
*popup_get_name(ObClient
*c
, ObClient
**nametarget
)
296 const gchar
*desk
= NULL
;
299 /* find our highest direct parent, including non-normal windows */
300 for (p
= c
; p
->transient_for
&& p
->transient_for
!= OB_TRAN_GROUP
;
301 p
= p
->transient_for
);
303 if (c
->desktop
!= DESKTOP_ALL
&& c
->desktop
!= screen_desktop
)
304 desk
= screen_desktop_names
[c
->desktop
];
306 /* use the transient's parent's title/icon if we don't have one */
307 if (p
!= c
&& !strcmp("", (c
->iconic
? c
->icon_title
: c
->title
)))
308 title
= g_strdup(p
->iconic
? p
->icon_title
: p
->title
);
311 title
= g_strdup(c
->iconic
? c
->icon_title
: c
->title
);
314 ret
= g_strdup_printf("%s [%s]", title
, desk
);
321 /* set this only if we're returning true and they asked for it */
322 if (ret
&& nametarget
) *nametarget
= p
;
326 static void popup_cycle(ObClient
*c
, gboolean show
,
327 gboolean all_desktops
, gboolean dock_windows
,
328 gboolean desktop_windows
)
330 gchar
*showtext
= NULL
;
331 ObClient
*showtarget
;
334 icon_popup_hide(focus_cycle_popup
);
338 /* do this stuff only when the dialog is first showing */
339 if (!focus_cycle_popup
->popup
->mapped
&&
340 !focus_cycle_popup
->popup
->delay_mapped
)
344 GList
*targets
= NULL
, *it
;
347 /* position the popup */
348 a
= screen_physical_area_monitor(0);
349 icon_popup_position(focus_cycle_popup
, CenterGravity
,
350 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
351 icon_popup_height(focus_cycle_popup
, POPUP_HEIGHT
);
352 icon_popup_min_width(focus_cycle_popup
, POPUP_WIDTH
);
353 icon_popup_max_width(focus_cycle_popup
,
354 MAX(a
->width
/3, POPUP_WIDTH
));
357 /* make its width to be the width of all the possible titles */
359 /* build a list of all the valid focus targets */
360 for (it
= focus_order
; it
; it
= g_list_next(it
)) {
361 ObClient
*ft
= it
->data
;
362 if (valid_focus_target(ft
, all_desktops
, dock_windows
365 targets
= g_list_prepend(targets
, ft
);
369 /* make it null terminated so we can use g_strfreev */
370 names
= g_new(char*, n
+1);
371 for (it
= targets
, i
= 0; it
; it
= g_list_next(it
), ++i
) {
372 ObClient
*ft
= it
->data
, *t
;
373 names
[i
] = popup_get_name(ft
, &t
);
375 /* little optimization.. save this text and client, so we dont
376 have to get it again */
378 showtext
= g_strdup(names
[i
]);
384 icon_popup_text_width_to_strings(focus_cycle_popup
, names
, n
);
389 if (!showtext
) showtext
= popup_get_name(c
, &showtarget
);
390 icon_popup_show(focus_cycle_popup
, showtext
,
391 client_icon(showtarget
, 48, 48));
395 static void focus_cycle_destructor(ObClient
*client
, gpointer data
)
397 /* end cycling if the target disappears. CurrentTime is fine, time won't
400 if (focus_cycle_target
== client
)
401 focus_cycle(TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
);
404 void focus_cycle_draw_indicator()
406 if (!focus_cycle_target
) {
407 XUnmapWindow(ob_display
, focus_indicator
.top
.win
);
408 XUnmapWindow(ob_display
, focus_indicator
.left
.win
);
409 XUnmapWindow(ob_display
, focus_indicator
.right
.win
);
410 XUnmapWindow(ob_display
, focus_indicator
.bottom
.win
);
412 /* kill enter events cause by this unmapping */
413 event_ignore_queued_enters();
416 if (focus_cycle_target)
417 frame_adjust_focus(focus_cycle_target->frame, FALSE);
418 frame_adjust_focus(focus_cycle_target->frame, TRUE);
423 wt
= wl
= wr
= wb
= FOCUS_INDICATOR_WIDTH
;
425 x
= focus_cycle_target
->frame
->area
.x
;
426 y
= focus_cycle_target
->frame
->area
.y
;
427 w
= focus_cycle_target
->frame
->area
.width
;
430 XMoveResizeWindow(ob_display
, focus_indicator
.top
.win
,
432 a_focus_indicator
->texture
[0].data
.lineart
.x1
= 0;
433 a_focus_indicator
->texture
[0].data
.lineart
.y1
= h
-1;
434 a_focus_indicator
->texture
[0].data
.lineart
.x2
= 0;
435 a_focus_indicator
->texture
[0].data
.lineart
.y2
= 0;
436 a_focus_indicator
->texture
[1].data
.lineart
.x1
= 0;
437 a_focus_indicator
->texture
[1].data
.lineart
.y1
= 0;
438 a_focus_indicator
->texture
[1].data
.lineart
.x2
= w
-1;
439 a_focus_indicator
->texture
[1].data
.lineart
.y2
= 0;
440 a_focus_indicator
->texture
[2].data
.lineart
.x1
= w
-1;
441 a_focus_indicator
->texture
[2].data
.lineart
.y1
= 0;
442 a_focus_indicator
->texture
[2].data
.lineart
.x2
= w
-1;
443 a_focus_indicator
->texture
[2].data
.lineart
.y2
= h
-1;
444 a_focus_indicator
->texture
[3].data
.lineart
.x1
= (wl
-1);
445 a_focus_indicator
->texture
[3].data
.lineart
.y1
= h
-1;
446 a_focus_indicator
->texture
[3].data
.lineart
.x2
= w
- wr
;
447 a_focus_indicator
->texture
[3].data
.lineart
.y2
= h
-1;
448 RrPaint(a_focus_indicator
, focus_indicator
.top
.win
,
451 x
= focus_cycle_target
->frame
->area
.x
;
452 y
= focus_cycle_target
->frame
->area
.y
;
454 h
= focus_cycle_target
->frame
->area
.height
;
456 XMoveResizeWindow(ob_display
, focus_indicator
.left
.win
,
458 a_focus_indicator
->texture
[0].data
.lineart
.x1
= w
-1;
459 a_focus_indicator
->texture
[0].data
.lineart
.y1
= 0;
460 a_focus_indicator
->texture
[0].data
.lineart
.x2
= 0;
461 a_focus_indicator
->texture
[0].data
.lineart
.y2
= 0;
462 a_focus_indicator
->texture
[1].data
.lineart
.x1
= 0;
463 a_focus_indicator
->texture
[1].data
.lineart
.y1
= 0;
464 a_focus_indicator
->texture
[1].data
.lineart
.x2
= 0;
465 a_focus_indicator
->texture
[1].data
.lineart
.y2
= h
-1;
466 a_focus_indicator
->texture
[2].data
.lineart
.x1
= 0;
467 a_focus_indicator
->texture
[2].data
.lineart
.y1
= h
-1;
468 a_focus_indicator
->texture
[2].data
.lineart
.x2
= w
-1;
469 a_focus_indicator
->texture
[2].data
.lineart
.y2
= h
-1;
470 a_focus_indicator
->texture
[3].data
.lineart
.x1
= w
-1;
471 a_focus_indicator
->texture
[3].data
.lineart
.y1
= wt
-1;
472 a_focus_indicator
->texture
[3].data
.lineart
.x2
= w
-1;
473 a_focus_indicator
->texture
[3].data
.lineart
.y2
= h
- wb
;
474 RrPaint(a_focus_indicator
, focus_indicator
.left
.win
,
477 x
= focus_cycle_target
->frame
->area
.x
+
478 focus_cycle_target
->frame
->area
.width
- wr
;
479 y
= focus_cycle_target
->frame
->area
.y
;
481 h
= focus_cycle_target
->frame
->area
.height
;
483 XMoveResizeWindow(ob_display
, focus_indicator
.right
.win
,
485 a_focus_indicator
->texture
[0].data
.lineart
.x1
= 0;
486 a_focus_indicator
->texture
[0].data
.lineart
.y1
= 0;
487 a_focus_indicator
->texture
[0].data
.lineart
.x2
= w
-1;
488 a_focus_indicator
->texture
[0].data
.lineart
.y2
= 0;
489 a_focus_indicator
->texture
[1].data
.lineart
.x1
= w
-1;
490 a_focus_indicator
->texture
[1].data
.lineart
.y1
= 0;
491 a_focus_indicator
->texture
[1].data
.lineart
.x2
= w
-1;
492 a_focus_indicator
->texture
[1].data
.lineart
.y2
= h
-1;
493 a_focus_indicator
->texture
[2].data
.lineart
.x1
= w
-1;
494 a_focus_indicator
->texture
[2].data
.lineart
.y1
= h
-1;
495 a_focus_indicator
->texture
[2].data
.lineart
.x2
= 0;
496 a_focus_indicator
->texture
[2].data
.lineart
.y2
= h
-1;
497 a_focus_indicator
->texture
[3].data
.lineart
.x1
= 0;
498 a_focus_indicator
->texture
[3].data
.lineart
.y1
= wt
-1;
499 a_focus_indicator
->texture
[3].data
.lineart
.x2
= 0;
500 a_focus_indicator
->texture
[3].data
.lineart
.y2
= h
- wb
;
501 RrPaint(a_focus_indicator
, focus_indicator
.right
.win
,
504 x
= focus_cycle_target
->frame
->area
.x
;
505 y
= focus_cycle_target
->frame
->area
.y
+
506 focus_cycle_target
->frame
->area
.height
- wb
;
507 w
= focus_cycle_target
->frame
->area
.width
;
510 XMoveResizeWindow(ob_display
, focus_indicator
.bottom
.win
,
512 a_focus_indicator
->texture
[0].data
.lineart
.x1
= 0;
513 a_focus_indicator
->texture
[0].data
.lineart
.y1
= 0;
514 a_focus_indicator
->texture
[0].data
.lineart
.x2
= 0;
515 a_focus_indicator
->texture
[0].data
.lineart
.y2
= h
-1;
516 a_focus_indicator
->texture
[1].data
.lineart
.x1
= 0;
517 a_focus_indicator
->texture
[1].data
.lineart
.y1
= h
-1;
518 a_focus_indicator
->texture
[1].data
.lineart
.x2
= w
-1;
519 a_focus_indicator
->texture
[1].data
.lineart
.y2
= h
-1;
520 a_focus_indicator
->texture
[2].data
.lineart
.x1
= w
-1;
521 a_focus_indicator
->texture
[2].data
.lineart
.y1
= h
-1;
522 a_focus_indicator
->texture
[2].data
.lineart
.x2
= w
-1;
523 a_focus_indicator
->texture
[2].data
.lineart
.y2
= 0;
524 a_focus_indicator
->texture
[3].data
.lineart
.x1
= wl
-1;
525 a_focus_indicator
->texture
[3].data
.lineart
.y1
= 0;
526 a_focus_indicator
->texture
[3].data
.lineart
.x2
= w
- wr
;
527 a_focus_indicator
->texture
[3].data
.lineart
.y2
= 0;
528 RrPaint(a_focus_indicator
, focus_indicator
.bottom
.win
,
531 XMapWindow(ob_display
, focus_indicator
.top
.win
);
532 XMapWindow(ob_display
, focus_indicator
.left
.win
);
533 XMapWindow(ob_display
, focus_indicator
.right
.win
);
534 XMapWindow(ob_display
, focus_indicator
.bottom
.win
);
538 static gboolean
has_valid_group_siblings_on_desktop(ObClient
*ft
,
539 gboolean all_desktops
)
544 if (!ft
->group
) return FALSE
;
546 for (it
= ft
->group
->members
; it
; it
= g_slist_next(it
)) {
547 ObClient
*c
= it
->data
;
548 /* check that it's not a helper window to avoid infinite recursion */
549 if (c
!= ft
&& !client_helper(c
) &&
550 valid_focus_target(c
, all_desktops
, FALSE
, FALSE
))
558 /*! @param allow_helpers This is used for calling itself recursively while
559 checking helper windows. */
560 static gboolean
valid_focus_target(ObClient
*ft
,
561 gboolean all_desktops
,
562 gboolean dock_windows
,
563 gboolean desktop_windows
)
567 /* it's on this desktop unless you want all desktops.
569 do this check first because it will usually filter out the most
571 ok
= (all_desktops
|| ft
->desktop
== screen_desktop
||
572 ft
->desktop
== DESKTOP_ALL
);
574 /* the window can receive focus somehow */
575 ok
= ok
&& (ft
->can_focus
|| ft
->focus_notify
);
577 /* it's the right type of window */
578 if (dock_windows
|| desktop_windows
)
579 ok
= ok
&& ((dock_windows
&& ft
->type
== OB_CLIENT_TYPE_DOCK
) ||
580 (desktop_windows
&& ft
->type
== OB_CLIENT_TYPE_DESKTOP
));
582 /* normal non-helper windows are valid targets */
584 ((client_normal(ft
) && !client_helper(ft
))
586 /* helper windows are valid targets it... */
587 (client_helper(ft
) &&
588 /* ...a window in its group already has focus ... */
589 ((focus_client
&& ft
->group
== focus_client
->group
) ||
590 /* ... or if there are no other windows in its group
591 that can be cycled to instead */
592 !has_valid_group_siblings_on_desktop(ft
, all_desktops
))));
594 /* it's not set to skip the taskbar (unless it is a type that would be
595 expected to set this hint */
596 ok
= ok
&& ((ft
->type
== OB_CLIENT_TYPE_DOCK
||
597 ft
->type
== OB_CLIENT_TYPE_TOOLBAR
||
598 ft
->type
== OB_CLIENT_TYPE_MENU
||
599 ft
->type
== OB_CLIENT_TYPE_UTILITY
) ||
602 /* it's not going to just send fous off somewhere else (modal window) */
603 ok
= ok
&& ft
== client_focus_target(ft
);
608 void focus_cycle(gboolean forward
, gboolean all_desktops
,
609 gboolean dock_windows
, gboolean desktop_windows
,
610 gboolean linear
, gboolean interactive
,
611 gboolean dialog
, gboolean done
, gboolean cancel
)
613 static ObClient
*first
= NULL
;
614 static ObClient
*t
= NULL
;
615 static GList
*order
= NULL
;
616 GList
*it
, *start
, *list
;
621 focus_cycle_target
= NULL
;
629 if (!first
) first
= focus_client
;
631 if (linear
) list
= client_list
;
632 else list
= focus_order
;
638 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
640 start
= it
= g_list_find(list
, focus_cycle_target
);
641 if (!start
) /* switched desktops or something? */
642 start
= it
= forward
? g_list_last(list
) : g_list_first(list
);
643 if (!start
) goto done_cycle
;
648 if (it
== NULL
) it
= g_list_first(list
);
651 if (it
== NULL
) it
= g_list_last(list
);
654 if (valid_focus_target(ft
, all_desktops
, dock_windows
,
658 if (ft
!= focus_cycle_target
) { /* prevents flicker */
659 focus_cycle_target
= ft
;
660 focus_cycle_draw_indicator();
662 /* same arguments as valid_focus_target */
663 popup_cycle(ft
, dialog
, all_desktops
, dock_windows
,
666 } else if (ft
!= focus_cycle_target
) {
667 focus_cycle_target
= ft
;
672 } while (it
!= start
);
675 if (done
&& focus_cycle_target
)
676 client_activate(focus_cycle_target
, FALSE
, TRUE
);
680 focus_cycle_target
= NULL
;
685 focus_cycle_draw_indicator();
686 popup_cycle(ft
, FALSE
, FALSE
, FALSE
, FALSE
);
692 /* this be mostly ripped from fvwm */
693 static ObClient
*focus_find_directional(ObClient
*c
, ObDirection dir
,
694 gboolean dock_windows
,
695 gboolean desktop_windows
)
697 gint my_cx
, my_cy
, his_cx
, his_cy
;
700 gint score
, best_score
;
701 ObClient
*best_client
, *cur
;
707 /* first, find the centre coords of the currently focused window */
708 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
709 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
714 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
717 /* the currently selected window isn't interesting */
720 if (!dock_windows
&& !desktop_windows
&& !client_normal(cur
))
722 if (!(dock_windows
&& cur
->type
== OB_CLIENT_TYPE_DOCK
) ||
723 (desktop_windows
&& cur
->type
== OB_CLIENT_TYPE_DESKTOP
))
725 /* using c->desktop instead of screen_desktop doesn't work if the
726 * current window was omnipresent, hope this doesn't have any other
728 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
732 if(!(client_focus_target(cur
) == cur
&&
733 client_can_focus(cur
)))
736 /* find the centre coords of this window, from the
737 * currently focused window's point of view */
738 his_cx
= (cur
->frame
->area
.x
- my_cx
)
739 + cur
->frame
->area
.width
/ 2;
740 his_cy
= (cur
->frame
->area
.y
- my_cy
)
741 + cur
->frame
->area
.height
/ 2;
743 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
744 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
746 /* Rotate the diagonals 45 degrees counterclockwise.
747 * To do this, multiply the matrix /+h +h\ with the
748 * vector (x y). \-h +h/
749 * h = sqrt(0.5). We can set h := 1 since absolute
750 * distance doesn't matter here. */
751 tx
= his_cx
+ his_cy
;
752 his_cy
= -his_cx
+ his_cy
;
757 case OB_DIRECTION_NORTH
:
758 case OB_DIRECTION_SOUTH
:
759 case OB_DIRECTION_NORTHEAST
:
760 case OB_DIRECTION_SOUTHWEST
:
761 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
762 distance
= ((dir
== OB_DIRECTION_NORTH
||
763 dir
== OB_DIRECTION_NORTHEAST
) ?
766 case OB_DIRECTION_EAST
:
767 case OB_DIRECTION_WEST
:
768 case OB_DIRECTION_SOUTHEAST
:
769 case OB_DIRECTION_NORTHWEST
:
770 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
771 distance
= ((dir
== OB_DIRECTION_WEST
||
772 dir
== OB_DIRECTION_NORTHWEST
) ?
777 /* the target must be in the requested direction */
781 /* Calculate score for this window. The smaller the better. */
782 score
= distance
+ offset
;
784 /* windows more than 45 degrees off the direction are
785 * heavily penalized and will only be chosen if nothing
786 * else within a million pixels */
787 if(offset
> distance
)
790 if(best_score
== -1 || score
< best_score
)
798 void focus_directional_cycle(ObDirection dir
, gboolean dock_windows
,
799 gboolean desktop_windows
, gboolean interactive
,
800 gboolean dialog
, gboolean done
, gboolean cancel
)
802 static ObClient
*first
= NULL
;
809 focus_cycle_target
= NULL
;
817 if (!first
) first
= focus_client
;
818 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
820 if (focus_cycle_target
)
821 ft
= focus_find_directional(focus_cycle_target
, dir
, dock_windows
,
826 for (it
= focus_order
; it
; it
= g_list_next(it
))
827 if (valid_focus_target(it
->data
, FALSE
, dock_windows
,
833 if (ft
!= focus_cycle_target
) {/* prevents flicker */
834 focus_cycle_target
= ft
;
835 focus_cycle_draw_indicator();
838 if (focus_cycle_target
) {
839 /* same arguments as valid_focus_target */
840 popup_cycle(focus_cycle_target
, dialog
, FALSE
, dock_windows
,
848 if (done
&& focus_cycle_target
)
849 client_activate(focus_cycle_target
, FALSE
, TRUE
);
852 focus_cycle_target
= NULL
;
854 focus_cycle_draw_indicator();
855 popup_cycle(ft
, FALSE
, FALSE
, FALSE
, FALSE
);
860 void focus_order_add_new(ObClient
*c
)
863 focus_order_to_top(c
);
865 g_assert(!g_list_find(focus_order
, c
));
866 /* if there are any iconic windows, put this above them in the order,
867 but if there are not, then put it under the currently focused one */
868 if (focus_order
&& ((ObClient
*)focus_order
->data
)->iconic
)
869 focus_order
= g_list_insert(focus_order
, c
, 0);
871 focus_order
= g_list_insert(focus_order
, c
, 1);
875 void focus_order_remove(ObClient
*c
)
877 focus_order
= g_list_remove(focus_order
, c
);
880 void focus_order_to_top(ObClient
*c
)
882 focus_order
= g_list_remove(focus_order
, c
);
884 focus_order
= g_list_prepend(focus_order
, c
);
888 /* insert before first iconic window */
889 for (it
= focus_order
;
890 it
&& !((ObClient
*)it
->data
)->iconic
; it
= g_list_next(it
));
891 focus_order
= g_list_insert_before(focus_order
, it
, c
);
895 void focus_order_to_bottom(ObClient
*c
)
897 focus_order
= g_list_remove(focus_order
, c
);
899 focus_order
= g_list_append(focus_order
, c
);
903 /* insert before first iconic window */
904 for (it
= focus_order
;
905 it
&& !((ObClient
*)it
->data
)->iconic
; it
= g_list_next(it
));
906 focus_order
= g_list_insert_before(focus_order
, it
, c
);
910 ObClient
*focus_order_find_first(guint desktop
)
913 for (it
= focus_order
; it
; it
= g_list_next(it
)) {
914 ObClient
*c
= it
->data
;
915 if (c
->desktop
== desktop
|| c
->desktop
== DESKTOP_ALL
)