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"
35 #include "render/render.h"
41 #define FOCUS_INDICATOR_WIDTH 6
43 ObClient
*focus_client
= NULL
;
44 GList
*focus_order
= NULL
;
45 ObClient
*focus_cycle_target
= NULL
;
51 InternalWindow bottom
;
54 RrAppearance
*a_focus_indicator
;
57 static ObIconPopup
*focus_cycle_popup
;
59 static gboolean
valid_focus_target(ObClient
*ft
,
60 gboolean all_desktops
,
61 gboolean dock_windows
,
62 gboolean desktop_windows
);
63 static void focus_cycle_destroy_notify(ObClient
*client
, gpointer data
);
65 static Window
createWindow(Window parent
, gulong mask
,
66 XSetWindowAttributes
*attrib
)
68 return XCreateWindow(ob_display
, parent
, 0, 0, 1, 1, 0,
69 RrDepth(ob_rr_inst
), InputOutput
,
70 RrVisual(ob_rr_inst
), mask
, attrib
);
74 void focus_startup(gboolean reconfig
)
76 focus_cycle_popup
= icon_popup_new(TRUE
);
79 XSetWindowAttributes attr
;
81 client_add_destroy_notify(focus_cycle_destroy_notify
, NULL
);
83 /* start with nothing focused */
86 focus_indicator
.top
.obwin
.type
= Window_Internal
;
87 focus_indicator
.left
.obwin
.type
= Window_Internal
;
88 focus_indicator
.right
.obwin
.type
= Window_Internal
;
89 focus_indicator
.bottom
.obwin
.type
= Window_Internal
;
91 attr
.override_redirect
= True
;
92 attr
.background_pixel
= BlackPixel(ob_display
, ob_screen
);
93 focus_indicator
.top
.win
=
94 createWindow(RootWindow(ob_display
, ob_screen
),
95 CWOverrideRedirect
| CWBackPixel
, &attr
);
96 focus_indicator
.left
.win
=
97 createWindow(RootWindow(ob_display
, ob_screen
),
98 CWOverrideRedirect
| CWBackPixel
, &attr
);
99 focus_indicator
.right
.win
=
100 createWindow(RootWindow(ob_display
, ob_screen
),
101 CWOverrideRedirect
| CWBackPixel
, &attr
);
102 focus_indicator
.bottom
.win
=
103 createWindow(RootWindow(ob_display
, ob_screen
),
104 CWOverrideRedirect
| CWBackPixel
, &attr
);
106 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator
.top
));
107 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator
.left
));
108 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator
.right
));
109 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator
.bottom
));
111 color_white
= RrColorNew(ob_rr_inst
, 0xff, 0xff, 0xff);
113 a_focus_indicator
= RrAppearanceNew(ob_rr_inst
, 4);
114 a_focus_indicator
->surface
.grad
= RR_SURFACE_SOLID
;
115 a_focus_indicator
->surface
.relief
= RR_RELIEF_FLAT
;
116 a_focus_indicator
->surface
.primary
= RrColorNew(ob_rr_inst
,
118 a_focus_indicator
->texture
[0].type
= RR_TEXTURE_LINE_ART
;
119 a_focus_indicator
->texture
[0].data
.lineart
.color
= color_white
;
120 a_focus_indicator
->texture
[1].type
= RR_TEXTURE_LINE_ART
;
121 a_focus_indicator
->texture
[1].data
.lineart
.color
= color_white
;
122 a_focus_indicator
->texture
[2].type
= RR_TEXTURE_LINE_ART
;
123 a_focus_indicator
->texture
[2].data
.lineart
.color
= color_white
;
124 a_focus_indicator
->texture
[3].type
= RR_TEXTURE_LINE_ART
;
125 a_focus_indicator
->texture
[3].data
.lineart
.color
= color_white
;
129 void focus_shutdown(gboolean reconfig
)
131 icon_popup_free(focus_cycle_popup
);
134 client_remove_destroy_notify(focus_cycle_destroy_notify
);
136 /* reset focus to root */
137 XSetInputFocus(ob_display
, PointerRoot
, RevertToNone
, CurrentTime
);
139 RrColorFree(color_white
);
141 RrAppearanceFree(a_focus_indicator
);
143 XDestroyWindow(ob_display
, focus_indicator
.top
.win
);
144 XDestroyWindow(ob_display
, focus_indicator
.left
.win
);
145 XDestroyWindow(ob_display
, focus_indicator
.right
.win
);
146 XDestroyWindow(ob_display
, focus_indicator
.bottom
.win
);
150 static void push_to_top(ObClient
*client
)
152 focus_order
= g_list_remove(focus_order
, client
);
153 focus_order
= g_list_prepend(focus_order
, client
);
156 void focus_set_client(ObClient
*client
)
160 ob_debug_type(OB_DEBUG_FOCUS
,
161 "focus_set_client 0x%lx\n", client
? client
->window
: 0);
163 /* uninstall the old colormap, and install the new one */
164 screen_install_colormap(focus_client
, FALSE
);
165 screen_install_colormap(client
, TRUE
);
167 /* in the middle of cycling..? kill it. CurrentTime is fine, time won't
170 if (focus_cycle_target
)
171 focus_cycle(TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
);
173 focus_client
= client
;
175 if (client
!= NULL
) {
176 /* move to the top of the list */
178 /* remove hiliting from the window when it gets focused */
179 client_hilite(client
, FALSE
);
182 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
183 if (ob_state() != OB_STATE_EXITING
) {
184 active
= client
? client
->window
: None
;
185 PROP_SET32(RootWindow(ob_display
, ob_screen
),
186 net_active_window
, window
, active
);
190 static ObClient
* focus_fallback_target(gboolean allow_refocus
, ObClient
*old
)
193 ObClient
*target
= NULL
;
194 ObClient
*desktop
= NULL
;
196 ob_debug_type(OB_DEBUG_FOCUS
, "trying pointer stuff\n");
197 if (config_focus_follow
&& !config_focus_last
)
199 if ((target
= client_under_pointer()))
200 if (allow_refocus
|| target
!= old
)
201 if (client_normal(target
) && client_can_focus(target
)) {
202 ob_debug_type(OB_DEBUG_FOCUS
, "found in pointer stuff\n");
208 /* try for group relations */
212 for (it
= focus_order
[screen_desktop
]; it
; it
= g_list_next(it
))
213 for (sit
= old
->group
->members
; sit
; sit
= g_slist_next(sit
))
214 if (sit
->data
== it
->data
)
215 if (sit
->data
!= old
&& client_normal(sit
->data
))
216 if (client_can_focus(sit
->data
))
221 ob_debug_type(OB_DEBUG_FOCUS
, "trying omnipresentness\n");
222 if (allow_refocus
&& old
&& old
->desktop
== DESKTOP_ALL
&&
229 ob_debug_type(OB_DEBUG_FOCUS
, "trying the focus order\n");
230 for (it
= focus_order
; it
; it
= g_list_next(it
))
231 if (allow_refocus
|| it
->data
!= old
) {
232 ObClient
*c
= it
->data
;
233 /* fallback focus to a window if:
234 1. it is actually focusable, cuz if it's not then we're sending
235 focus off to nothing. this includes if it is visible right now
236 2. it is on the current desktop. this ignores omnipresent
237 windows, which are problematic in their own rite.
238 3. it is a normal type window, don't fall back onto a dock or
239 a splashscreen or a desktop window (save the desktop as a
240 backup fallback though)
242 if (client_can_focus(c
))
244 if (c
->desktop
== screen_desktop
&& client_normal(c
)) {
245 ob_debug_type(OB_DEBUG_FOCUS
, "found in focus order\n");
247 } else if (c
->type
== OB_CLIENT_TYPE_DESKTOP
&&
253 /* as a last resort fallback to the desktop window if there is one.
254 (if there's more than one, then the one most recently focused.)
256 ob_debug_type(OB_DEBUG_FOCUS
, "found desktop: \n", !!desktop
);
260 ObClient
* focus_fallback(gboolean allow_refocus
)
266 new = focus_fallback_target(allow_refocus
, focus_client
);
268 /* unfocus any focused clients.. they can be focused by Pointer events
269 and such, and then when we try focus them, we won't get a FocusIn
270 event at all for them. */
281 /* Install our own colormap */
282 if (focus_client
!= NULL
) {
283 screen_install_colormap(focus_client
, FALSE
);
284 screen_install_colormap(NULL
, TRUE
);
287 /* Don't set focus_client to NULL here. It will be set to NULL when the
288 FocusOut event comes. Otherwise, if we focus nothing and then focus the
289 same window again, The focus code says nothing changed, but focus_client
290 ends up being NULL anyways.
294 /* if there is a grab going on, then we need to cancel it. if we move
295 focus during the grab, applications will get NotifyWhileGrabbed events
298 actions should not rely on being able to move focus during an
301 if (keyboard_interactively_grabbed())
302 keyboard_interactive_cancel();
304 /* when nothing will be focused, send focus to the backup target */
305 XSetInputFocus(ob_display
, screen_support_win
, RevertToPointerRoot
,
309 static gchar
*popup_get_name(ObClient
*c
, ObClient
**nametarget
)
313 const gchar
*desk
= NULL
;
316 /* find our highest direct parent, including non-normal windows */
317 for (p
= c
; p
->transient_for
&& p
->transient_for
!= OB_TRAN_GROUP
;
318 p
= p
->transient_for
);
320 if (c
->desktop
!= DESKTOP_ALL
&& c
->desktop
!= screen_desktop
)
321 desk
= screen_desktop_names
[c
->desktop
];
323 /* use the transient's parent's title/icon if we don't have one */
324 if (p
!= c
&& !strcmp("", (c
->iconic
? c
->icon_title
: c
->title
)))
325 title
= g_strdup(p
->iconic
? p
->icon_title
: p
->title
);
328 title
= g_strdup(c
->iconic
? c
->icon_title
: c
->title
);
331 ret
= g_strdup_printf("%s [%s]", title
, desk
);
338 /* set this only if we're returning true and they asked for it */
339 if (ret
&& nametarget
) *nametarget
= p
;
343 static void popup_cycle(ObClient
*c
, gboolean show
,
344 gboolean all_desktops
, gboolean dock_windows
,
345 gboolean desktop_windows
)
347 gchar
*showtext
= NULL
;
348 ObClient
*showtarget
;
351 icon_popup_hide(focus_cycle_popup
);
355 /* do this stuff only when the dialog is first showing */
356 if (!focus_cycle_popup
->popup
->mapped
&&
357 !focus_cycle_popup
->popup
->delay_mapped
)
361 GList
*targets
= NULL
, *it
;
364 /* position the popup */
365 a
= screen_physical_area_monitor(0);
366 icon_popup_position(focus_cycle_popup
, CenterGravity
,
367 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
368 icon_popup_height(focus_cycle_popup
, POPUP_HEIGHT
);
369 icon_popup_min_width(focus_cycle_popup
, POPUP_WIDTH
);
370 icon_popup_max_width(focus_cycle_popup
,
371 MAX(a
->width
/3, POPUP_WIDTH
));
374 /* make its width to be the width of all the possible titles */
376 /* build a list of all the valid focus targets */
377 for (it
= focus_order
; it
; it
= g_list_next(it
)) {
378 ObClient
*ft
= it
->data
;
379 if (valid_focus_target(ft
, all_desktops
, dock_windows
382 targets
= g_list_prepend(targets
, ft
);
386 /* make it null terminated so we can use g_strfreev */
387 names
= g_new(char*, n
+1);
388 for (it
= targets
, i
= 0; it
; it
= g_list_next(it
), ++i
) {
389 ObClient
*ft
= it
->data
, *t
;
390 names
[i
] = popup_get_name(ft
, &t
);
392 /* little optimization.. save this text and client, so we dont
393 have to get it again */
395 showtext
= g_strdup(names
[i
]);
401 icon_popup_text_width_to_strings(focus_cycle_popup
, names
, n
);
406 if (!showtext
) showtext
= popup_get_name(c
, &showtarget
);
407 icon_popup_show(focus_cycle_popup
, showtext
,
408 client_icon(showtarget
, 48, 48));
412 static void focus_cycle_destroy_notify(ObClient
*client
, gpointer data
)
414 /* end cycling if the target disappears. CurrentTime is fine, time won't
417 if (focus_cycle_target
== client
)
418 focus_cycle(TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
);
421 void focus_cycle_draw_indicator()
423 if (!focus_cycle_target
) {
424 XUnmapWindow(ob_display
, focus_indicator
.top
.win
);
425 XUnmapWindow(ob_display
, focus_indicator
.left
.win
);
426 XUnmapWindow(ob_display
, focus_indicator
.right
.win
);
427 XUnmapWindow(ob_display
, focus_indicator
.bottom
.win
);
429 /* kill enter events cause by this unmapping */
430 event_ignore_queued_enters();
433 if (focus_cycle_target)
434 frame_adjust_focus(focus_cycle_target->frame, FALSE);
435 frame_adjust_focus(focus_cycle_target->frame, TRUE);
440 wt
= wl
= wr
= wb
= FOCUS_INDICATOR_WIDTH
;
442 x
= focus_cycle_target
->frame
->area
.x
;
443 y
= focus_cycle_target
->frame
->area
.y
;
444 w
= focus_cycle_target
->frame
->area
.width
;
447 XMoveResizeWindow(ob_display
, focus_indicator
.top
.win
,
449 a_focus_indicator
->texture
[0].data
.lineart
.x1
= 0;
450 a_focus_indicator
->texture
[0].data
.lineart
.y1
= h
-1;
451 a_focus_indicator
->texture
[0].data
.lineart
.x2
= 0;
452 a_focus_indicator
->texture
[0].data
.lineart
.y2
= 0;
453 a_focus_indicator
->texture
[1].data
.lineart
.x1
= 0;
454 a_focus_indicator
->texture
[1].data
.lineart
.y1
= 0;
455 a_focus_indicator
->texture
[1].data
.lineart
.x2
= w
-1;
456 a_focus_indicator
->texture
[1].data
.lineart
.y2
= 0;
457 a_focus_indicator
->texture
[2].data
.lineart
.x1
= w
-1;
458 a_focus_indicator
->texture
[2].data
.lineart
.y1
= 0;
459 a_focus_indicator
->texture
[2].data
.lineart
.x2
= w
-1;
460 a_focus_indicator
->texture
[2].data
.lineart
.y2
= h
-1;
461 a_focus_indicator
->texture
[3].data
.lineart
.x1
= (wl
-1);
462 a_focus_indicator
->texture
[3].data
.lineart
.y1
= h
-1;
463 a_focus_indicator
->texture
[3].data
.lineart
.x2
= w
- wr
;
464 a_focus_indicator
->texture
[3].data
.lineart
.y2
= h
-1;
465 RrPaint(a_focus_indicator
, focus_indicator
.top
.win
,
468 x
= focus_cycle_target
->frame
->area
.x
;
469 y
= focus_cycle_target
->frame
->area
.y
;
471 h
= focus_cycle_target
->frame
->area
.height
;
473 XMoveResizeWindow(ob_display
, focus_indicator
.left
.win
,
475 a_focus_indicator
->texture
[0].data
.lineart
.x1
= w
-1;
476 a_focus_indicator
->texture
[0].data
.lineart
.y1
= 0;
477 a_focus_indicator
->texture
[0].data
.lineart
.x2
= 0;
478 a_focus_indicator
->texture
[0].data
.lineart
.y2
= 0;
479 a_focus_indicator
->texture
[1].data
.lineart
.x1
= 0;
480 a_focus_indicator
->texture
[1].data
.lineart
.y1
= 0;
481 a_focus_indicator
->texture
[1].data
.lineart
.x2
= 0;
482 a_focus_indicator
->texture
[1].data
.lineart
.y2
= h
-1;
483 a_focus_indicator
->texture
[2].data
.lineart
.x1
= 0;
484 a_focus_indicator
->texture
[2].data
.lineart
.y1
= h
-1;
485 a_focus_indicator
->texture
[2].data
.lineart
.x2
= w
-1;
486 a_focus_indicator
->texture
[2].data
.lineart
.y2
= h
-1;
487 a_focus_indicator
->texture
[3].data
.lineart
.x1
= w
-1;
488 a_focus_indicator
->texture
[3].data
.lineart
.y1
= wt
-1;
489 a_focus_indicator
->texture
[3].data
.lineart
.x2
= w
-1;
490 a_focus_indicator
->texture
[3].data
.lineart
.y2
= h
- wb
;
491 RrPaint(a_focus_indicator
, focus_indicator
.left
.win
,
494 x
= focus_cycle_target
->frame
->area
.x
+
495 focus_cycle_target
->frame
->area
.width
- wr
;
496 y
= focus_cycle_target
->frame
->area
.y
;
498 h
= focus_cycle_target
->frame
->area
.height
;
500 XMoveResizeWindow(ob_display
, focus_indicator
.right
.win
,
502 a_focus_indicator
->texture
[0].data
.lineart
.x1
= 0;
503 a_focus_indicator
->texture
[0].data
.lineart
.y1
= 0;
504 a_focus_indicator
->texture
[0].data
.lineart
.x2
= w
-1;
505 a_focus_indicator
->texture
[0].data
.lineart
.y2
= 0;
506 a_focus_indicator
->texture
[1].data
.lineart
.x1
= w
-1;
507 a_focus_indicator
->texture
[1].data
.lineart
.y1
= 0;
508 a_focus_indicator
->texture
[1].data
.lineart
.x2
= w
-1;
509 a_focus_indicator
->texture
[1].data
.lineart
.y2
= h
-1;
510 a_focus_indicator
->texture
[2].data
.lineart
.x1
= w
-1;
511 a_focus_indicator
->texture
[2].data
.lineart
.y1
= h
-1;
512 a_focus_indicator
->texture
[2].data
.lineart
.x2
= 0;
513 a_focus_indicator
->texture
[2].data
.lineart
.y2
= h
-1;
514 a_focus_indicator
->texture
[3].data
.lineart
.x1
= 0;
515 a_focus_indicator
->texture
[3].data
.lineart
.y1
= wt
-1;
516 a_focus_indicator
->texture
[3].data
.lineart
.x2
= 0;
517 a_focus_indicator
->texture
[3].data
.lineart
.y2
= h
- wb
;
518 RrPaint(a_focus_indicator
, focus_indicator
.right
.win
,
521 x
= focus_cycle_target
->frame
->area
.x
;
522 y
= focus_cycle_target
->frame
->area
.y
+
523 focus_cycle_target
->frame
->area
.height
- wb
;
524 w
= focus_cycle_target
->frame
->area
.width
;
527 XMoveResizeWindow(ob_display
, focus_indicator
.bottom
.win
,
529 a_focus_indicator
->texture
[0].data
.lineart
.x1
= 0;
530 a_focus_indicator
->texture
[0].data
.lineart
.y1
= 0;
531 a_focus_indicator
->texture
[0].data
.lineart
.x2
= 0;
532 a_focus_indicator
->texture
[0].data
.lineart
.y2
= h
-1;
533 a_focus_indicator
->texture
[1].data
.lineart
.x1
= 0;
534 a_focus_indicator
->texture
[1].data
.lineart
.y1
= h
-1;
535 a_focus_indicator
->texture
[1].data
.lineart
.x2
= w
-1;
536 a_focus_indicator
->texture
[1].data
.lineart
.y2
= h
-1;
537 a_focus_indicator
->texture
[2].data
.lineart
.x1
= w
-1;
538 a_focus_indicator
->texture
[2].data
.lineart
.y1
= h
-1;
539 a_focus_indicator
->texture
[2].data
.lineart
.x2
= w
-1;
540 a_focus_indicator
->texture
[2].data
.lineart
.y2
= 0;
541 a_focus_indicator
->texture
[3].data
.lineart
.x1
= wl
-1;
542 a_focus_indicator
->texture
[3].data
.lineart
.y1
= 0;
543 a_focus_indicator
->texture
[3].data
.lineart
.x2
= w
- wr
;
544 a_focus_indicator
->texture
[3].data
.lineart
.y2
= 0;
545 RrPaint(a_focus_indicator
, focus_indicator
.bottom
.win
,
548 XMapWindow(ob_display
, focus_indicator
.top
.win
);
549 XMapWindow(ob_display
, focus_indicator
.left
.win
);
550 XMapWindow(ob_display
, focus_indicator
.right
.win
);
551 XMapWindow(ob_display
, focus_indicator
.bottom
.win
);
555 static gboolean
has_valid_group_siblings_on_desktop(ObClient
*ft
,
556 gboolean all_desktops
)
561 if (!ft
->group
) return FALSE
;
563 for (it
= ft
->group
->members
; it
; it
= g_slist_next(it
)) {
564 ObClient
*c
= it
->data
;
565 /* check that it's not a helper window to avoid infinite recursion */
566 if (c
!= ft
&& !client_helper(c
) &&
567 valid_focus_target(c
, all_desktops
, FALSE
, FALSE
))
575 /*! @param allow_helpers This is used for calling itself recursively while
576 checking helper windows. */
577 static gboolean
valid_focus_target(ObClient
*ft
,
578 gboolean all_desktops
,
579 gboolean dock_windows
,
580 gboolean desktop_windows
)
584 /* it's on this desktop unless you want all desktops.
586 do this check first because it will usually filter out the most
588 ok
= (all_desktops
|| ft
->desktop
== screen_desktop
||
589 ft
->desktop
== DESKTOP_ALL
);
591 /* the window can receive focus somehow */
592 ok
= ok
&& (ft
->can_focus
|| ft
->focus_notify
);
594 /* it's the right type of window */
595 if (dock_windows
|| desktop_windows
)
596 ok
= ok
&& ((dock_windows
&& ft
->type
== OB_CLIENT_TYPE_DOCK
) ||
597 (desktop_windows
&& ft
->type
== OB_CLIENT_TYPE_DESKTOP
));
599 /* normal non-helper windows are valid targets */
601 ((client_normal(ft
) && !client_helper(ft
))
603 /* helper windows are valid targets it... */
604 (client_helper(ft
) &&
605 /* ...a window in its group already has focus ... */
606 ((focus_client
&& ft
->group
== focus_client
->group
) ||
607 /* ... or if there are no other windows in its group
608 that can be cycled to instead */
609 !has_valid_group_siblings_on_desktop(ft
, all_desktops
))));
611 /* it's not set to skip the taskbar (unless it is a type that would be
612 expected to set this hint */
613 ok
= ok
&& ((ft
->type
== OB_CLIENT_TYPE_DOCK
||
614 ft
->type
== OB_CLIENT_TYPE_DESKTOP
||
615 ft
->type
== OB_CLIENT_TYPE_TOOLBAR
||
616 ft
->type
== OB_CLIENT_TYPE_MENU
||
617 ft
->type
== OB_CLIENT_TYPE_UTILITY
) ||
620 /* it's not going to just send fous off somewhere else (modal window) */
621 ok
= ok
&& ft
== client_focus_target(ft
);
626 void focus_cycle(gboolean forward
, gboolean all_desktops
,
627 gboolean dock_windows
, gboolean desktop_windows
,
628 gboolean linear
, gboolean interactive
,
629 gboolean dialog
, gboolean done
, gboolean cancel
)
631 static ObClient
*first
= NULL
;
632 static ObClient
*t
= NULL
;
633 static GList
*order
= NULL
;
634 GList
*it
, *start
, *list
;
639 focus_cycle_target
= NULL
;
647 if (!first
) first
= focus_client
;
649 if (linear
) list
= client_list
;
650 else list
= focus_order
;
656 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
658 start
= it
= g_list_find(list
, focus_cycle_target
);
659 if (!start
) /* switched desktops or something? */
660 start
= it
= forward
? g_list_last(list
) : g_list_first(list
);
661 if (!start
) goto done_cycle
;
666 if (it
== NULL
) it
= g_list_first(list
);
669 if (it
== NULL
) it
= g_list_last(list
);
672 if (valid_focus_target(ft
, all_desktops
, dock_windows
,
676 if (ft
!= focus_cycle_target
) { /* prevents flicker */
677 focus_cycle_target
= ft
;
678 focus_cycle_draw_indicator();
680 /* same arguments as valid_focus_target */
681 popup_cycle(ft
, dialog
, all_desktops
, dock_windows
,
684 } else if (ft
!= focus_cycle_target
) {
685 focus_cycle_target
= ft
;
690 } while (it
!= start
);
693 if (done
&& focus_cycle_target
)
694 client_activate(focus_cycle_target
, FALSE
, TRUE
);
698 focus_cycle_target
= NULL
;
703 focus_cycle_draw_indicator();
704 popup_cycle(ft
, FALSE
, FALSE
, FALSE
, FALSE
);
710 /* this be mostly ripped from fvwm */
711 static ObClient
*focus_find_directional(ObClient
*c
, ObDirection dir
,
712 gboolean dock_windows
,
713 gboolean desktop_windows
)
715 gint my_cx
, my_cy
, his_cx
, his_cy
;
718 gint score
, best_score
;
719 ObClient
*best_client
, *cur
;
725 /* first, find the centre coords of the currently focused window */
726 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
727 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
732 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
735 /* the currently selected window isn't interesting */
738 if (cur
->type
== OB_CLIENT_TYPE_DOCK
&& !dock_windows
)
740 if (cur
->type
== OB_CLIENT_TYPE_DESKTOP
&& !desktop_windows
)
742 if (!client_normal(cur
) &&
743 cur
->type
!= OB_CLIENT_TYPE_DOCK
&&
744 cur
->type
!= OB_CLIENT_TYPE_DESKTOP
)
746 /* using c->desktop instead of screen_desktop doesn't work if the
747 * current window was omnipresent, hope this doesn't have any other
749 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
753 if(!(client_focus_target(cur
) == cur
&&
754 client_can_focus(cur
)))
757 /* find the centre coords of this window, from the
758 * currently focused window's point of view */
759 his_cx
= (cur
->frame
->area
.x
- my_cx
)
760 + cur
->frame
->area
.width
/ 2;
761 his_cy
= (cur
->frame
->area
.y
- my_cy
)
762 + cur
->frame
->area
.height
/ 2;
764 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
765 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
767 /* Rotate the diagonals 45 degrees counterclockwise.
768 * To do this, multiply the matrix /+h +h\ with the
769 * vector (x y). \-h +h/
770 * h = sqrt(0.5). We can set h := 1 since absolute
771 * distance doesn't matter here. */
772 tx
= his_cx
+ his_cy
;
773 his_cy
= -his_cx
+ his_cy
;
778 case OB_DIRECTION_NORTH
:
779 case OB_DIRECTION_SOUTH
:
780 case OB_DIRECTION_NORTHEAST
:
781 case OB_DIRECTION_SOUTHWEST
:
782 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
783 distance
= ((dir
== OB_DIRECTION_NORTH
||
784 dir
== OB_DIRECTION_NORTHEAST
) ?
787 case OB_DIRECTION_EAST
:
788 case OB_DIRECTION_WEST
:
789 case OB_DIRECTION_SOUTHEAST
:
790 case OB_DIRECTION_NORTHWEST
:
791 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
792 distance
= ((dir
== OB_DIRECTION_WEST
||
793 dir
== OB_DIRECTION_NORTHWEST
) ?
798 /* the target must be in the requested direction */
802 /* Calculate score for this window. The smaller the better. */
803 score
= distance
+ offset
;
805 /* windows more than 45 degrees off the direction are
806 * heavily penalized and will only be chosen if nothing
807 * else within a million pixels */
808 if(offset
> distance
)
811 if(best_score
== -1 || score
< best_score
)
819 void focus_directional_cycle(ObDirection dir
, gboolean dock_windows
,
820 gboolean desktop_windows
, gboolean interactive
,
821 gboolean dialog
, gboolean done
, gboolean cancel
)
823 static ObClient
*first
= NULL
;
830 focus_cycle_target
= NULL
;
838 if (!first
) first
= focus_client
;
839 if (!focus_cycle_target
) focus_cycle_target
= focus_client
;
841 if (focus_cycle_target
)
842 ft
= focus_find_directional(focus_cycle_target
, dir
, dock_windows
,
847 for (it
= focus_order
; it
; it
= g_list_next(it
))
848 if (valid_focus_target(it
->data
, FALSE
, dock_windows
,
854 if (ft
!= focus_cycle_target
) {/* prevents flicker */
855 focus_cycle_target
= ft
;
856 focus_cycle_draw_indicator();
859 if (focus_cycle_target
) {
860 /* same arguments as valid_focus_target */
861 popup_cycle(focus_cycle_target
, dialog
, FALSE
, dock_windows
,
869 if (done
&& focus_cycle_target
)
870 client_activate(focus_cycle_target
, FALSE
, TRUE
);
873 focus_cycle_target
= NULL
;
875 focus_cycle_draw_indicator();
876 popup_cycle(ft
, FALSE
, FALSE
, FALSE
, FALSE
);
881 void focus_order_add_new(ObClient
*c
)
884 focus_order_to_top(c
);
886 g_assert(!g_list_find(focus_order
, c
));
887 /* if there are any iconic windows, put this above them in the order,
888 but if there are not, then put it under the currently focused one */
889 if (focus_order
&& ((ObClient
*)focus_order
->data
)->iconic
)
890 focus_order
= g_list_insert(focus_order
, c
, 0);
892 focus_order
= g_list_insert(focus_order
, c
, 1);
896 void focus_order_remove(ObClient
*c
)
898 focus_order
= g_list_remove(focus_order
, c
);
901 void focus_order_to_top(ObClient
*c
)
903 focus_order
= g_list_remove(focus_order
, c
);
905 focus_order
= g_list_prepend(focus_order
, c
);
909 /* insert before first iconic window */
910 for (it
= focus_order
;
911 it
&& !((ObClient
*)it
->data
)->iconic
; it
= g_list_next(it
));
912 focus_order
= g_list_insert_before(focus_order
, it
, c
);
916 void focus_order_to_bottom(ObClient
*c
)
918 focus_order
= g_list_remove(focus_order
, c
);
920 focus_order
= g_list_append(focus_order
, c
);
924 /* insert before first iconic window */
925 for (it
= focus_order
;
926 it
&& !((ObClient
*)it
->data
)->iconic
; it
= g_list_next(it
));
927 focus_order
= g_list_insert_before(focus_order
, it
, c
);
931 ObClient
*focus_order_find_first(guint desktop
)
934 for (it
= focus_order
; it
; it
= g_list_next(it
)) {
935 ObClient
*c
= it
->data
;
936 if (c
->desktop
== desktop
|| c
->desktop
== DESKTOP_ALL
)