]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/focus.c
add an allDesktops option to Next/PreviousWindow to alt-tab across all desktops.
[mikachu/openbox.git] / openbox / focus.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    focus.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
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.
11
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.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "event.h"
22 #include "openbox.h"
23 #include "grab.h"
24 #include "framerender.h"
25 #include "client.h"
26 #include "config.h"
27 #include "frame.h"
28 #include "screen.h"
29 #include "group.h"
30 #include "prop.h"
31 #include "focus.h"
32 #include "stacking.h"
33 #include "popup.h"
34 #include "render/render.h"
35
36 #include <X11/Xlib.h>
37 #include <glib.h>
38 #include <assert.h>
39
40 #define FOCUS_INDICATOR_WIDTH 5
41
42 ObClient *focus_client = NULL;
43 GList *focus_order = NULL;
44 ObClient *focus_cycle_target = NULL;
45
46 struct {
47     InternalWindow top;
48     InternalWindow left;
49     InternalWindow right;
50     InternalWindow bottom;
51 } focus_indicator;
52
53 RrAppearance *a_focus_indicator;
54 RrColor *color_white;
55
56 static ObIconPopup *focus_cycle_popup;
57
58 static void focus_cycle_destructor(ObClient *client, gpointer data)
59 {
60     /* end cycling if the target disappears. CurrentTime is fine, time won't
61        be used
62     */
63     if (focus_cycle_target == client)
64         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
65 }
66
67 static Window createWindow(Window parent, gulong mask,
68                            XSetWindowAttributes *attrib)
69 {
70     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
71                          RrDepth(ob_rr_inst), InputOutput,
72                          RrVisual(ob_rr_inst), mask, attrib);
73                        
74 }
75
76 void focus_startup(gboolean reconfig)
77 {
78     focus_cycle_popup = icon_popup_new(TRUE);
79
80     if (!reconfig) {
81         XSetWindowAttributes attr;
82
83         client_add_destructor(focus_cycle_destructor, NULL);
84
85         /* start with nothing focused */
86         focus_nothing();
87
88         focus_indicator.top.obwin.type = Window_Internal;
89         focus_indicator.left.obwin.type = Window_Internal;
90         focus_indicator.right.obwin.type = Window_Internal;
91         focus_indicator.bottom.obwin.type = Window_Internal;
92
93         attr.override_redirect = True;
94         attr.background_pixel = BlackPixel(ob_display, ob_screen);
95         focus_indicator.top.win =
96             createWindow(RootWindow(ob_display, ob_screen),
97                          CWOverrideRedirect | CWBackPixel, &attr);
98         focus_indicator.left.win =
99             createWindow(RootWindow(ob_display, ob_screen),
100                          CWOverrideRedirect | CWBackPixel, &attr);
101         focus_indicator.right.win =
102             createWindow(RootWindow(ob_display, ob_screen),
103                          CWOverrideRedirect | CWBackPixel, &attr);
104         focus_indicator.bottom.win =
105             createWindow(RootWindow(ob_display, ob_screen),
106                          CWOverrideRedirect | CWBackPixel, &attr);
107
108         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.top));
109         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left));
110         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right));
111         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom));
112
113         color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff);
114
115         a_focus_indicator = RrAppearanceNew(ob_rr_inst, 4);
116         a_focus_indicator->surface.grad = RR_SURFACE_SOLID;
117         a_focus_indicator->surface.relief = RR_RELIEF_FLAT;
118         a_focus_indicator->surface.primary = RrColorNew(ob_rr_inst,
119                                                         0, 0, 0);
120         a_focus_indicator->texture[0].type = RR_TEXTURE_LINE_ART;
121         a_focus_indicator->texture[0].data.lineart.color = color_white;
122         a_focus_indicator->texture[1].type = RR_TEXTURE_LINE_ART;
123         a_focus_indicator->texture[1].data.lineart.color = color_white;
124         a_focus_indicator->texture[2].type = RR_TEXTURE_LINE_ART;
125         a_focus_indicator->texture[2].data.lineart.color = color_white;
126         a_focus_indicator->texture[3].type = RR_TEXTURE_LINE_ART;
127         a_focus_indicator->texture[3].data.lineart.color = color_white;
128     }
129 }
130
131 void focus_shutdown(gboolean reconfig)
132 {
133     icon_popup_free(focus_cycle_popup);
134
135     if (!reconfig) {
136         client_remove_destructor(focus_cycle_destructor);
137
138         /* reset focus to root */
139         XSetInputFocus(ob_display, PointerRoot, RevertToNone, CurrentTime);
140
141         RrColorFree(color_white);
142
143         RrAppearanceFree(a_focus_indicator);
144
145         XDestroyWindow(ob_display, focus_indicator.top.win);
146         XDestroyWindow(ob_display, focus_indicator.left.win);
147         XDestroyWindow(ob_display, focus_indicator.right.win);
148         XDestroyWindow(ob_display, focus_indicator.bottom.win);
149     }
150 }
151
152 static void push_to_top(ObClient *client)
153 {
154     focus_order = g_list_remove(focus_order, client);
155     focus_order = g_list_prepend(focus_order, client);
156 }
157
158 void focus_set_client(ObClient *client)
159 {
160     Window active;
161
162     ob_debug_type(OB_DEBUG_FOCUS,
163                   "focus_set_client 0x%lx\n", client ? client->window : 0);
164
165     /* uninstall the old colormap, and install the new one */
166     screen_install_colormap(focus_client, FALSE);
167     screen_install_colormap(client, TRUE);
168
169     /* in the middle of cycling..? kill it. CurrentTime is fine, time won't
170        be used.
171     */
172     if (focus_cycle_target)
173         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
174
175     focus_client = client;
176
177     if (client != NULL) {
178         /* move to the top of the list */
179         push_to_top(client);
180         /* remove hiliting from the window when it gets focused */
181         client_hilite(client, FALSE);
182     }
183
184     /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
185     if (ob_state() != OB_STATE_EXITING) {
186         active = client ? client->window : None;
187         PROP_SET32(RootWindow(ob_display, ob_screen),
188                    net_active_window, window, active);
189     }
190 }
191
192 ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
193 {
194     GList *it;
195     ObClient *target = NULL;
196     ObClient *desktop = NULL;
197
198     ob_debug_type(OB_DEBUG_FOCUS, "trying pointer stuff\n");
199     if (config_focus_follow && !config_focus_last)
200     {
201         if ((target = client_under_pointer()))
202             if (allow_refocus || target != old)
203                 if (client_normal(target) && client_can_focus(target)) {
204                     ob_debug_type(OB_DEBUG_FOCUS, "found in pointer stuff\n");
205                     return target;
206                 }
207     }
208
209 #if 0
210         /* try for group relations */
211         if (old->group) {
212             GSList *sit;
213
214             for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
215                 for (sit = old->group->members; sit; sit = g_slist_next(sit))
216                     if (sit->data == it->data)
217                         if (sit->data != old && client_normal(sit->data))
218                             if (client_can_focus(sit->data))
219                                 return sit->data;
220         }
221 #endif
222
223     ob_debug_type(OB_DEBUG_FOCUS, "trying omnipresentness\n");
224     if (allow_refocus && old && old->desktop == DESKTOP_ALL &&
225         client_normal(old))
226     {
227         return old;
228     }
229
230
231     ob_debug_type(OB_DEBUG_FOCUS, "trying  the focus order\n");
232     for (it = focus_order; it; it = g_list_next(it))
233         if (allow_refocus || it->data != old) {
234             ObClient *c = it->data;
235             /* fallback focus to a window if:
236                1. it is actually focusable, cuz if it's not then we're sending
237                focus off to nothing. this includes if it is visible right now
238                2. it is on the current desktop. this ignores omnipresent
239                windows, which are problematic in their own rite.
240                3. it is a normal type window, don't fall back onto a dock or
241                a splashscreen or a desktop window (save the desktop as a
242                backup fallback though)
243             */
244             if (client_can_focus(c))
245             {
246                 if (c->desktop == screen_desktop && client_normal(c)) {
247                     ob_debug_type(OB_DEBUG_FOCUS, "found in focus order\n");
248                     return it->data;
249                 } else if (c->type == OB_CLIENT_TYPE_DESKTOP && 
250                            desktop == NULL)
251                     desktop = c;
252             }
253         }
254
255     /* as a last resort fallback to the desktop window if there is one.
256        (if there's more than one, then the one most recently focused.)
257     */
258     ob_debug_type(OB_DEBUG_FOCUS, "found desktop: \n", !!desktop);
259     return desktop;   
260 }
261
262 void focus_fallback(gboolean allow_refocus)
263 {
264     ObClient *new;
265     ObClient *old = focus_client;
266
267     /* unfocus any focused clients.. they can be focused by Pointer events
268        and such, and then when I try focus them, I won't get a FocusIn event
269        at all for them.
270     */
271     focus_nothing();
272
273     if ((new = focus_fallback_target(allow_refocus, old)))
274         client_focus(new);
275 }
276
277 void focus_nothing()
278 {
279     /* Install our own colormap */
280     if (focus_client != NULL) {
281         screen_install_colormap(focus_client, FALSE);
282         screen_install_colormap(NULL, TRUE);
283     }
284
285     focus_client = NULL;
286
287     /* when nothing will be focused, send focus to the backup target */
288     XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
289                    event_curtime);
290 }
291
292 static void popup_cycle(ObClient *c, gboolean show)
293 {
294     if (!show) {
295         icon_popup_hide(focus_cycle_popup);
296     } else {
297         Rect *a;
298         ObClient *p = c;
299         gchar *title = NULL;
300
301         a = screen_physical_area_monitor(0);
302         icon_popup_position(focus_cycle_popup, CenterGravity,
303                             a->x + a->width / 2, a->y + a->height / 2);
304         icon_popup_width(focus_cycle_popup, MAX(a->width/3, POPUP_WIDTH));
305         icon_popup_height(focus_cycle_popup, POPUP_HEIGHT);
306
307         /* use the transient's parent's title/icon */
308         while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
309             p = p->transient_for;
310
311         if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
312             title = g_strdup(p->iconic ? p->icon_title : p->title);
313             /*title = g_strconcat((c->iconic ? c->icon_title : c->title),
314                                 " - ",
315                                 (p->iconic ? p->icon_title : p->title),
316                                 NULL);
317             */
318         icon_popup_show(focus_cycle_popup,
319                         (title ? title :
320                          (c->iconic ? c->icon_title : c->title)),
321                         client_icon(p, 48, 48));
322         g_free(title);
323     }
324 }
325
326 void focus_cycle_draw_indicator()
327 {
328     if (!focus_cycle_target) {
329         XUnmapWindow(ob_display, focus_indicator.top.win);
330         XUnmapWindow(ob_display, focus_indicator.left.win);
331         XUnmapWindow(ob_display, focus_indicator.right.win);
332         XUnmapWindow(ob_display, focus_indicator.bottom.win);
333
334         /* kill enter events cause by this unmapping */
335         event_ignore_queued_enters();
336     } else {
337         /*
338           if (focus_cycle_target)
339               frame_adjust_focus(focus_cycle_target->frame, FALSE);
340           frame_adjust_focus(focus_cycle_target->frame, TRUE);
341         */
342         gint x, y, w, h;
343         gint wt, wl, wr, wb;
344
345         wt = wl = wr = wb = FOCUS_INDICATOR_WIDTH;
346
347         x = focus_cycle_target->frame->area.x;
348         y = focus_cycle_target->frame->area.y;
349         w = focus_cycle_target->frame->area.width;
350         h = wt;
351
352         XMoveResizeWindow(ob_display, focus_indicator.top.win,
353                           x, y, w, h);
354         a_focus_indicator->texture[0].data.lineart.x1 = 0;
355         a_focus_indicator->texture[0].data.lineart.y1 = h-1;
356         a_focus_indicator->texture[0].data.lineart.x2 = 0;
357         a_focus_indicator->texture[0].data.lineart.y2 = 0;
358         a_focus_indicator->texture[1].data.lineart.x1 = 0;
359         a_focus_indicator->texture[1].data.lineart.y1 = 0;
360         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
361         a_focus_indicator->texture[1].data.lineart.y2 = 0;
362         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
363         a_focus_indicator->texture[2].data.lineart.y1 = 0;
364         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
365         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
366         a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
367         a_focus_indicator->texture[3].data.lineart.y1 = h-1;
368         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
369         a_focus_indicator->texture[3].data.lineart.y2 = h-1;
370         RrPaint(a_focus_indicator, focus_indicator.top.win,
371                 w, h);
372
373         x = focus_cycle_target->frame->area.x;
374         y = focus_cycle_target->frame->area.y;
375         w = wl;
376         h = focus_cycle_target->frame->area.height;
377
378         XMoveResizeWindow(ob_display, focus_indicator.left.win,
379                           x, y, w, h);
380         a_focus_indicator->texture[0].data.lineart.x1 = w-1;
381         a_focus_indicator->texture[0].data.lineart.y1 = 0;
382         a_focus_indicator->texture[0].data.lineart.x2 = 0;
383         a_focus_indicator->texture[0].data.lineart.y2 = 0;
384         a_focus_indicator->texture[1].data.lineart.x1 = 0;
385         a_focus_indicator->texture[1].data.lineart.y1 = 0;
386         a_focus_indicator->texture[1].data.lineart.x2 = 0;
387         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
388         a_focus_indicator->texture[2].data.lineart.x1 = 0;
389         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
390         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
391         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
392         a_focus_indicator->texture[3].data.lineart.x1 = w-1;
393         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
394         a_focus_indicator->texture[3].data.lineart.x2 = w-1;
395         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
396         RrPaint(a_focus_indicator, focus_indicator.left.win,
397                 w, h);
398
399         x = focus_cycle_target->frame->area.x +
400             focus_cycle_target->frame->area.width - wr;
401         y = focus_cycle_target->frame->area.y;
402         w = wr;
403         h = focus_cycle_target->frame->area.height ;
404
405         XMoveResizeWindow(ob_display, focus_indicator.right.win,
406                           x, y, w, h);
407         a_focus_indicator->texture[0].data.lineart.x1 = 0;
408         a_focus_indicator->texture[0].data.lineart.y1 = 0;
409         a_focus_indicator->texture[0].data.lineart.x2 = w-1;
410         a_focus_indicator->texture[0].data.lineart.y2 = 0;
411         a_focus_indicator->texture[1].data.lineart.x1 = w-1;
412         a_focus_indicator->texture[1].data.lineart.y1 = 0;
413         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
414         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
415         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
416         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
417         a_focus_indicator->texture[2].data.lineart.x2 = 0;
418         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
419         a_focus_indicator->texture[3].data.lineart.x1 = 0;
420         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
421         a_focus_indicator->texture[3].data.lineart.x2 = 0;
422         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
423         RrPaint(a_focus_indicator, focus_indicator.right.win,
424                 w, h);
425
426         x = focus_cycle_target->frame->area.x;
427         y = focus_cycle_target->frame->area.y +
428             focus_cycle_target->frame->area.height - wb;
429         w = focus_cycle_target->frame->area.width;
430         h = wb;
431
432         XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
433                           x, y, w, h);
434         a_focus_indicator->texture[0].data.lineart.x1 = 0;
435         a_focus_indicator->texture[0].data.lineart.y1 = 0;
436         a_focus_indicator->texture[0].data.lineart.x2 = 0;
437         a_focus_indicator->texture[0].data.lineart.y2 = h-1;
438         a_focus_indicator->texture[1].data.lineart.x1 = 0;
439         a_focus_indicator->texture[1].data.lineart.y1 = h-1;
440         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
441         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
442         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
443         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
444         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
445         a_focus_indicator->texture[2].data.lineart.y2 = 0;
446         a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
447         a_focus_indicator->texture[3].data.lineart.y1 = 0;
448         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
449         a_focus_indicator->texture[3].data.lineart.y2 = 0;
450         RrPaint(a_focus_indicator, focus_indicator.bottom.win,
451                 w, h);
452
453         XMapWindow(ob_display, focus_indicator.top.win);
454         XMapWindow(ob_display, focus_indicator.left.win);
455         XMapWindow(ob_display, focus_indicator.right.win);
456         XMapWindow(ob_display, focus_indicator.bottom.win);
457     }
458 }
459
460 static gboolean valid_focus_target(ObClient *ft,
461                                    gboolean all_desktops,
462                                    gboolean dock_windows)
463 {
464     gboolean ok = FALSE;
465     /* we don't use client_can_focus here, because that doesn't let you
466        focus an iconic window, but we want to be able to, so we just check
467        if the focus flags on the window allow it, and its on the current
468        desktop */
469     if (dock_windows)
470         ok = ft->type == OB_CLIENT_TYPE_DOCK;
471     else
472         ok = (ft->type == OB_CLIENT_TYPE_NORMAL ||
473               ft->type == OB_CLIENT_TYPE_DIALOG ||
474               ((ft->type == OB_CLIENT_TYPE_TOOLBAR ||
475                 ft->type == OB_CLIENT_TYPE_MENU ||
476                 ft->type == OB_CLIENT_TYPE_UTILITY) &&
477                /* let alt-tab go to these windows when a window in its group
478                   already has focus ... */
479                ((focus_client && ft->group == focus_client->group) ||
480                 /* ... or if there are no application windows in its group */
481                 !client_has_application_group_siblings(ft))));
482     ok = ok && (ft->can_focus || ft->focus_notify);
483     if (!dock_windows && /* use dock windows that skip taskbar too */
484         !(ft->type == OB_CLIENT_TYPE_TOOLBAR || /* also, if we actually are */
485           ft->type == OB_CLIENT_TYPE_MENU ||    /* being allowed to target */
486           ft->type == OB_CLIENT_TYPE_UTILITY))  /* one of these, don't let */
487         ok = ok && !ft->skip_taskbar;           /*  skip taskbar stop us */
488     if (!all_desktops)
489         ok = ok && (ft->desktop == screen_desktop ||
490                     ft->desktop == DESKTOP_ALL);
491     ok = ok && ft == client_focus_target(ft);
492     return ok;
493 /*
494     {
495         GSList *it;
496
497         for (it = ft->transients; it; it = g_slist_next(it)) {
498             ObClient *c = it->data;
499
500             if (frame_visible(c->frame))
501                 return FALSE;
502         }
503         return TRUE;
504     }
505 */
506 }
507
508 void focus_cycle(gboolean forward, gboolean all_desktops,
509                  gboolean dock_windows,
510                  gboolean linear, gboolean interactive,
511                  gboolean dialog, gboolean done, gboolean cancel)
512 {
513     static ObClient *first = NULL;
514     static ObClient *t = NULL;
515     static GList *order = NULL;
516     GList *it, *start, *list;
517     ObClient *ft = NULL;
518
519     if (interactive) {
520         if (cancel) {
521             focus_cycle_target = NULL;
522             goto done_cycle;
523         } else if (done)
524             goto done_cycle;
525
526         if (!focus_order)
527             goto done_cycle;
528
529         if (!first) first = focus_client;
530
531         if (linear) list = client_list;
532         else        list = focus_order;
533     } else {
534         if (!focus_order)
535             goto done_cycle;
536         list = client_list;
537     }
538     if (!focus_cycle_target) focus_cycle_target = focus_client;
539
540     start = it = g_list_find(list, focus_cycle_target);
541     if (!start) /* switched desktops or something? */
542         start = it = forward ? g_list_last(list) : g_list_first(list);
543     if (!start) goto done_cycle;
544
545     do {
546         if (forward) {
547             it = it->next;
548             if (it == NULL) it = g_list_first(list);
549         } else {
550             it = it->prev;
551             if (it == NULL) it = g_list_last(list);
552         }
553         ft = it->data;
554         if (valid_focus_target(ft, all_desktops, dock_windows)) {
555             if (interactive) {
556                 if (ft != focus_cycle_target) { /* prevents flicker */
557                     focus_cycle_target = ft;
558                     focus_cycle_draw_indicator();
559                 }
560                 popup_cycle(ft, dialog);
561                 return;
562             } else if (ft != focus_cycle_target) {
563                 focus_cycle_target = ft;
564                 done = TRUE;
565                 break;
566             }
567         }
568     } while (it != start);
569
570 done_cycle:
571     if (done && focus_cycle_target)
572         client_activate(focus_cycle_target, FALSE, TRUE);
573
574     t = NULL;
575     first = NULL;
576     focus_cycle_target = NULL;
577     g_list_free(order);
578     order = NULL;
579
580     if (interactive) {
581         focus_cycle_draw_indicator();
582         popup_cycle(ft, FALSE);
583     }
584
585     return;
586 }
587
588 /* this be mostly ripped from fvwm */
589 ObClient *focus_find_directional(ObClient *c, ObDirection dir,
590                                  gboolean dock_windows) 
591 {
592     gint my_cx, my_cy, his_cx, his_cy;
593     gint offset = 0;
594     gint distance = 0;
595     gint score, best_score;
596     ObClient *best_client, *cur;
597     GList *it;
598
599     if(!client_list)
600         return NULL;
601
602     /* first, find the centre coords of the currently focused window */
603     my_cx = c->frame->area.x + c->frame->area.width / 2;
604     my_cy = c->frame->area.y + c->frame->area.height / 2;
605
606     best_score = -1;
607     best_client = NULL;
608
609     for(it = g_list_first(client_list); it; it = g_list_next(it)) {
610         cur = it->data;
611
612         /* the currently selected window isn't interesting */
613         if(cur == c)
614             continue;
615         if (!dock_windows && !client_normal(cur))
616             continue;
617         if (dock_windows && cur->type != OB_CLIENT_TYPE_DOCK)
618             continue;
619         /* using c->desktop instead of screen_desktop doesn't work if the
620          * current window was omnipresent, hope this doesn't have any other
621          * side effects */
622         if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
623             continue;
624         if(cur->iconic)
625             continue;
626         if(!(client_focus_target(cur) == cur &&
627              client_can_focus(cur)))
628             continue;
629
630         /* find the centre coords of this window, from the
631          * currently focused window's point of view */
632         his_cx = (cur->frame->area.x - my_cx)
633             + cur->frame->area.width / 2;
634         his_cy = (cur->frame->area.y - my_cy)
635             + cur->frame->area.height / 2;
636
637         if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
638            dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
639             gint tx;
640             /* Rotate the diagonals 45 degrees counterclockwise.
641              * To do this, multiply the matrix /+h +h\ with the
642              * vector (x y).                   \-h +h/
643              * h = sqrt(0.5). We can set h := 1 since absolute
644              * distance doesn't matter here. */
645             tx = his_cx + his_cy;
646             his_cy = -his_cx + his_cy;
647             his_cx = tx;
648         }
649
650         switch(dir) {
651         case OB_DIRECTION_NORTH:
652         case OB_DIRECTION_SOUTH:
653         case OB_DIRECTION_NORTHEAST:
654         case OB_DIRECTION_SOUTHWEST:
655             offset = (his_cx < 0) ? -his_cx : his_cx;
656             distance = ((dir == OB_DIRECTION_NORTH ||
657                          dir == OB_DIRECTION_NORTHEAST) ?
658                         -his_cy : his_cy);
659             break;
660         case OB_DIRECTION_EAST:
661         case OB_DIRECTION_WEST:
662         case OB_DIRECTION_SOUTHEAST:
663         case OB_DIRECTION_NORTHWEST:
664             offset = (his_cy < 0) ? -his_cy : his_cy;
665             distance = ((dir == OB_DIRECTION_WEST ||
666                          dir == OB_DIRECTION_NORTHWEST) ?
667                         -his_cx : his_cx);
668             break;
669         }
670
671         /* the target must be in the requested direction */
672         if(distance <= 0)
673             continue;
674
675         /* Calculate score for this window.  The smaller the better. */
676         score = distance + offset;
677
678         /* windows more than 45 degrees off the direction are
679          * heavily penalized and will only be chosen if nothing
680          * else within a million pixels */
681         if(offset > distance)
682             score += 1000000;
683
684         if(best_score == -1 || score < best_score)
685             best_client = cur,
686                 best_score = score;
687     }
688
689     return best_client;
690 }
691
692 void focus_directional_cycle(ObDirection dir, gboolean dock_windows,
693                              gboolean interactive,
694                              gboolean dialog, gboolean done, gboolean cancel)
695 {
696     static ObClient *first = NULL;
697     ObClient *ft = NULL;
698
699     if (!interactive)
700         return;
701
702     if (cancel) {
703         focus_cycle_target = NULL;
704         goto done_cycle;
705     } else if (done)
706         goto done_cycle;
707
708     if (!focus_order)
709         goto done_cycle;
710
711     if (!first) first = focus_client;
712     if (!focus_cycle_target) focus_cycle_target = focus_client;
713
714     if (focus_cycle_target)
715         ft = focus_find_directional(focus_cycle_target, dir, dock_windows);
716     else {
717         GList *it;
718
719         for (it = focus_order; it; it = g_list_next(it))
720             if (valid_focus_target(it->data, FALSE, dock_windows))
721                 ft = it->data;
722     }
723         
724     if (ft) {
725         if (ft != focus_cycle_target) {/* prevents flicker */
726             focus_cycle_target = ft;
727             focus_cycle_draw_indicator();
728         }
729     }
730     if (focus_cycle_target) {
731         popup_cycle(focus_cycle_target, dialog);
732         if (dialog)
733             return;
734     }
735
736
737 done_cycle:
738     if (done && focus_cycle_target)
739         client_activate(focus_cycle_target, FALSE, TRUE);
740
741     first = NULL;
742     focus_cycle_target = NULL;
743
744     focus_cycle_draw_indicator();
745     popup_cycle(ft, FALSE);
746
747     return;
748 }
749
750 void focus_order_add_new(ObClient *c)
751 {
752     if (c->iconic)
753         focus_order_to_top(c);
754     else {
755         g_assert(!g_list_find(focus_order, c));
756         /* if there are any iconic windows, put this above them in the order,
757            but if there are not, then put it under the currently focused one */
758         if (focus_order && ((ObClient*)focus_order->data)->iconic)
759             focus_order = g_list_insert(focus_order, c, 0);
760         else
761             focus_order = g_list_insert(focus_order, c, 1);
762     }
763 }
764
765 void focus_order_remove(ObClient *c)
766 {
767     focus_order = g_list_remove(focus_order, c);
768 }
769
770 void focus_order_to_top(ObClient *c)
771 {
772     focus_order = g_list_remove(focus_order, c);
773     if (!c->iconic) {
774         focus_order = g_list_prepend(focus_order, c);
775     } else {
776         GList *it;
777
778         /* insert before first iconic window */
779         for (it = focus_order;
780              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
781         focus_order = g_list_insert_before(focus_order, it, c);
782     }
783 }
784
785 void focus_order_to_bottom(ObClient *c)
786 {
787     focus_order = g_list_remove(focus_order, c);
788     if (c->iconic) {
789         focus_order = g_list_append(focus_order, c);
790     } else {
791         GList *it;
792
793         /* insert before first iconic window */
794         for (it = focus_order;
795              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
796         focus_order = g_list_insert_before(focus_order, it, c);
797     }
798 }
799
800 ObClient *focus_order_find_first(guint desktop)
801 {
802     GList *it;
803     for (it = focus_order; it; it = g_list_next(it)) {
804         ObClient *c = it->data;
805         if (c->desktop == desktop || c->desktop == DESKTOP_ALL)
806             return c;
807     }
808     return NULL;
809 }