]> icculus.org git repositories - dana/openbox.git/blob - openbox/focus.c
show the desktop in the focus popup if its not on the current desktop
[dana/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;
299         gchar *text;
300         gchar *title = NULL;
301         const gchar *desk = NULL;
302
303         a = screen_physical_area_monitor(0);
304         icon_popup_position(focus_cycle_popup, CenterGravity,
305                             a->x + a->width / 2, a->y + a->height / 2);
306         icon_popup_width(focus_cycle_popup, MAX(a->width/3, POPUP_WIDTH));
307         icon_popup_height(focus_cycle_popup, POPUP_HEIGHT);
308
309         /* use the transient's parent's title/icon */
310         p = client_search_top_parent(c);
311
312         if (c->desktop != DESKTOP_ALL && c->desktop != screen_desktop)
313             desk = screen_desktop_names[c->desktop];
314
315         if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
316             title = g_strdup(p->iconic ? p->icon_title : p->title);
317             /*ptitle = g_strconcat((c->iconic ? c->icon_title : c->title),
318                                 " - ",
319                                 (p->iconic ? p->icon_title : p->title),
320                                 NULL);
321             */
322         if (title == NULL)
323             title = g_strdup(c->iconic ? c->icon_title : c->title);
324         if (desk)
325             text = g_strdup_printf("%s [%s]", title, desk);
326         else
327             text = g_strdup(title);
328
329         icon_popup_show(focus_cycle_popup, text, client_icon(p, 48, 48));
330         g_free(text);
331         g_free(title);
332     }
333 }
334
335 void focus_cycle_draw_indicator()
336 {
337     if (!focus_cycle_target) {
338         XUnmapWindow(ob_display, focus_indicator.top.win);
339         XUnmapWindow(ob_display, focus_indicator.left.win);
340         XUnmapWindow(ob_display, focus_indicator.right.win);
341         XUnmapWindow(ob_display, focus_indicator.bottom.win);
342
343         /* kill enter events cause by this unmapping */
344         event_ignore_queued_enters();
345     } else {
346         /*
347           if (focus_cycle_target)
348               frame_adjust_focus(focus_cycle_target->frame, FALSE);
349           frame_adjust_focus(focus_cycle_target->frame, TRUE);
350         */
351         gint x, y, w, h;
352         gint wt, wl, wr, wb;
353
354         wt = wl = wr = wb = FOCUS_INDICATOR_WIDTH;
355
356         x = focus_cycle_target->frame->area.x;
357         y = focus_cycle_target->frame->area.y;
358         w = focus_cycle_target->frame->area.width;
359         h = wt;
360
361         XMoveResizeWindow(ob_display, focus_indicator.top.win,
362                           x, y, w, h);
363         a_focus_indicator->texture[0].data.lineart.x1 = 0;
364         a_focus_indicator->texture[0].data.lineart.y1 = h-1;
365         a_focus_indicator->texture[0].data.lineart.x2 = 0;
366         a_focus_indicator->texture[0].data.lineart.y2 = 0;
367         a_focus_indicator->texture[1].data.lineart.x1 = 0;
368         a_focus_indicator->texture[1].data.lineart.y1 = 0;
369         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
370         a_focus_indicator->texture[1].data.lineart.y2 = 0;
371         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
372         a_focus_indicator->texture[2].data.lineart.y1 = 0;
373         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
374         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
375         a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
376         a_focus_indicator->texture[3].data.lineart.y1 = h-1;
377         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
378         a_focus_indicator->texture[3].data.lineart.y2 = h-1;
379         RrPaint(a_focus_indicator, focus_indicator.top.win,
380                 w, h);
381
382         x = focus_cycle_target->frame->area.x;
383         y = focus_cycle_target->frame->area.y;
384         w = wl;
385         h = focus_cycle_target->frame->area.height;
386
387         XMoveResizeWindow(ob_display, focus_indicator.left.win,
388                           x, y, w, h);
389         a_focus_indicator->texture[0].data.lineart.x1 = w-1;
390         a_focus_indicator->texture[0].data.lineart.y1 = 0;
391         a_focus_indicator->texture[0].data.lineart.x2 = 0;
392         a_focus_indicator->texture[0].data.lineart.y2 = 0;
393         a_focus_indicator->texture[1].data.lineart.x1 = 0;
394         a_focus_indicator->texture[1].data.lineart.y1 = 0;
395         a_focus_indicator->texture[1].data.lineart.x2 = 0;
396         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
397         a_focus_indicator->texture[2].data.lineart.x1 = 0;
398         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
399         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
400         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
401         a_focus_indicator->texture[3].data.lineart.x1 = w-1;
402         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
403         a_focus_indicator->texture[3].data.lineart.x2 = w-1;
404         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
405         RrPaint(a_focus_indicator, focus_indicator.left.win,
406                 w, h);
407
408         x = focus_cycle_target->frame->area.x +
409             focus_cycle_target->frame->area.width - wr;
410         y = focus_cycle_target->frame->area.y;
411         w = wr;
412         h = focus_cycle_target->frame->area.height ;
413
414         XMoveResizeWindow(ob_display, focus_indicator.right.win,
415                           x, y, w, h);
416         a_focus_indicator->texture[0].data.lineart.x1 = 0;
417         a_focus_indicator->texture[0].data.lineart.y1 = 0;
418         a_focus_indicator->texture[0].data.lineart.x2 = w-1;
419         a_focus_indicator->texture[0].data.lineart.y2 = 0;
420         a_focus_indicator->texture[1].data.lineart.x1 = w-1;
421         a_focus_indicator->texture[1].data.lineart.y1 = 0;
422         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
423         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
424         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
425         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
426         a_focus_indicator->texture[2].data.lineart.x2 = 0;
427         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
428         a_focus_indicator->texture[3].data.lineart.x1 = 0;
429         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
430         a_focus_indicator->texture[3].data.lineart.x2 = 0;
431         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
432         RrPaint(a_focus_indicator, focus_indicator.right.win,
433                 w, h);
434
435         x = focus_cycle_target->frame->area.x;
436         y = focus_cycle_target->frame->area.y +
437             focus_cycle_target->frame->area.height - wb;
438         w = focus_cycle_target->frame->area.width;
439         h = wb;
440
441         XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
442                           x, y, w, h);
443         a_focus_indicator->texture[0].data.lineart.x1 = 0;
444         a_focus_indicator->texture[0].data.lineart.y1 = 0;
445         a_focus_indicator->texture[0].data.lineart.x2 = 0;
446         a_focus_indicator->texture[0].data.lineart.y2 = h-1;
447         a_focus_indicator->texture[1].data.lineart.x1 = 0;
448         a_focus_indicator->texture[1].data.lineart.y1 = h-1;
449         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
450         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
451         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
452         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
453         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
454         a_focus_indicator->texture[2].data.lineart.y2 = 0;
455         a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
456         a_focus_indicator->texture[3].data.lineart.y1 = 0;
457         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
458         a_focus_indicator->texture[3].data.lineart.y2 = 0;
459         RrPaint(a_focus_indicator, focus_indicator.bottom.win,
460                 w, h);
461
462         XMapWindow(ob_display, focus_indicator.top.win);
463         XMapWindow(ob_display, focus_indicator.left.win);
464         XMapWindow(ob_display, focus_indicator.right.win);
465         XMapWindow(ob_display, focus_indicator.bottom.win);
466     }
467 }
468
469 static gboolean valid_focus_target(ObClient *ft,
470                                    gboolean all_desktops,
471                                    gboolean dock_windows)
472 {
473     gboolean ok = FALSE;
474     /* we don't use client_can_focus here, because that doesn't let you
475        focus an iconic window, but we want to be able to, so we just check
476        if the focus flags on the window allow it, and its on the current
477        desktop */
478     if (dock_windows)
479         ok = ft->type == OB_CLIENT_TYPE_DOCK;
480     else
481         ok = (ft->type == OB_CLIENT_TYPE_NORMAL ||
482               ft->type == OB_CLIENT_TYPE_DIALOG ||
483               ((ft->type == OB_CLIENT_TYPE_TOOLBAR ||
484                 ft->type == OB_CLIENT_TYPE_MENU ||
485                 ft->type == OB_CLIENT_TYPE_UTILITY) &&
486                /* let alt-tab go to these windows when a window in its group
487                   already has focus ... */
488                ((focus_client && ft->group == focus_client->group) ||
489                 /* ... or if there are no application windows in its group */
490                 !client_has_application_group_siblings(ft))));
491     ok = ok && (ft->can_focus || ft->focus_notify);
492     if (!dock_windows && /* use dock windows that skip taskbar too */
493         !(ft->type == OB_CLIENT_TYPE_TOOLBAR || /* also, if we actually are */
494           ft->type == OB_CLIENT_TYPE_MENU ||    /* being allowed to target */
495           ft->type == OB_CLIENT_TYPE_UTILITY))  /* one of these, don't let */
496         ok = ok && !ft->skip_taskbar;           /*  skip taskbar stop us */
497     if (!all_desktops)
498         ok = ok && (ft->desktop == screen_desktop ||
499                     ft->desktop == DESKTOP_ALL);
500     ok = ok && ft == client_focus_target(ft);
501     return ok;
502 /*
503     {
504         GSList *it;
505
506         for (it = ft->transients; it; it = g_slist_next(it)) {
507             ObClient *c = it->data;
508
509             if (frame_visible(c->frame))
510                 return FALSE;
511         }
512         return TRUE;
513     }
514 */
515 }
516
517 void focus_cycle(gboolean forward, gboolean all_desktops,
518                  gboolean dock_windows,
519                  gboolean linear, gboolean interactive,
520                  gboolean dialog, gboolean done, gboolean cancel)
521 {
522     static ObClient *first = NULL;
523     static ObClient *t = NULL;
524     static GList *order = NULL;
525     GList *it, *start, *list;
526     ObClient *ft = NULL;
527
528     if (interactive) {
529         if (cancel) {
530             focus_cycle_target = NULL;
531             goto done_cycle;
532         } else if (done)
533             goto done_cycle;
534
535         if (!focus_order)
536             goto done_cycle;
537
538         if (!first) first = focus_client;
539
540         if (linear) list = client_list;
541         else        list = focus_order;
542     } else {
543         if (!focus_order)
544             goto done_cycle;
545         list = client_list;
546     }
547     if (!focus_cycle_target) focus_cycle_target = focus_client;
548
549     start = it = g_list_find(list, focus_cycle_target);
550     if (!start) /* switched desktops or something? */
551         start = it = forward ? g_list_last(list) : g_list_first(list);
552     if (!start) goto done_cycle;
553
554     do {
555         if (forward) {
556             it = it->next;
557             if (it == NULL) it = g_list_first(list);
558         } else {
559             it = it->prev;
560             if (it == NULL) it = g_list_last(list);
561         }
562         ft = it->data;
563         if (valid_focus_target(ft, all_desktops, dock_windows)) {
564             if (interactive) {
565                 if (ft != focus_cycle_target) { /* prevents flicker */
566                     focus_cycle_target = ft;
567                     focus_cycle_draw_indicator();
568                 }
569                 popup_cycle(ft, dialog);
570                 return;
571             } else if (ft != focus_cycle_target) {
572                 focus_cycle_target = ft;
573                 done = TRUE;
574                 break;
575             }
576         }
577     } while (it != start);
578
579 done_cycle:
580     if (done && focus_cycle_target)
581         client_activate(focus_cycle_target, FALSE, TRUE);
582
583     t = NULL;
584     first = NULL;
585     focus_cycle_target = NULL;
586     g_list_free(order);
587     order = NULL;
588
589     if (interactive) {
590         focus_cycle_draw_indicator();
591         popup_cycle(ft, FALSE);
592     }
593
594     return;
595 }
596
597 /* this be mostly ripped from fvwm */
598 ObClient *focus_find_directional(ObClient *c, ObDirection dir,
599                                  gboolean dock_windows) 
600 {
601     gint my_cx, my_cy, his_cx, his_cy;
602     gint offset = 0;
603     gint distance = 0;
604     gint score, best_score;
605     ObClient *best_client, *cur;
606     GList *it;
607
608     if(!client_list)
609         return NULL;
610
611     /* first, find the centre coords of the currently focused window */
612     my_cx = c->frame->area.x + c->frame->area.width / 2;
613     my_cy = c->frame->area.y + c->frame->area.height / 2;
614
615     best_score = -1;
616     best_client = NULL;
617
618     for(it = g_list_first(client_list); it; it = g_list_next(it)) {
619         cur = it->data;
620
621         /* the currently selected window isn't interesting */
622         if(cur == c)
623             continue;
624         if (!dock_windows && !client_normal(cur))
625             continue;
626         if (dock_windows && cur->type != OB_CLIENT_TYPE_DOCK)
627             continue;
628         /* using c->desktop instead of screen_desktop doesn't work if the
629          * current window was omnipresent, hope this doesn't have any other
630          * side effects */
631         if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
632             continue;
633         if(cur->iconic)
634             continue;
635         if(!(client_focus_target(cur) == cur &&
636              client_can_focus(cur)))
637             continue;
638
639         /* find the centre coords of this window, from the
640          * currently focused window's point of view */
641         his_cx = (cur->frame->area.x - my_cx)
642             + cur->frame->area.width / 2;
643         his_cy = (cur->frame->area.y - my_cy)
644             + cur->frame->area.height / 2;
645
646         if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
647            dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
648             gint tx;
649             /* Rotate the diagonals 45 degrees counterclockwise.
650              * To do this, multiply the matrix /+h +h\ with the
651              * vector (x y).                   \-h +h/
652              * h = sqrt(0.5). We can set h := 1 since absolute
653              * distance doesn't matter here. */
654             tx = his_cx + his_cy;
655             his_cy = -his_cx + his_cy;
656             his_cx = tx;
657         }
658
659         switch(dir) {
660         case OB_DIRECTION_NORTH:
661         case OB_DIRECTION_SOUTH:
662         case OB_DIRECTION_NORTHEAST:
663         case OB_DIRECTION_SOUTHWEST:
664             offset = (his_cx < 0) ? -his_cx : his_cx;
665             distance = ((dir == OB_DIRECTION_NORTH ||
666                          dir == OB_DIRECTION_NORTHEAST) ?
667                         -his_cy : his_cy);
668             break;
669         case OB_DIRECTION_EAST:
670         case OB_DIRECTION_WEST:
671         case OB_DIRECTION_SOUTHEAST:
672         case OB_DIRECTION_NORTHWEST:
673             offset = (his_cy < 0) ? -his_cy : his_cy;
674             distance = ((dir == OB_DIRECTION_WEST ||
675                          dir == OB_DIRECTION_NORTHWEST) ?
676                         -his_cx : his_cx);
677             break;
678         }
679
680         /* the target must be in the requested direction */
681         if(distance <= 0)
682             continue;
683
684         /* Calculate score for this window.  The smaller the better. */
685         score = distance + offset;
686
687         /* windows more than 45 degrees off the direction are
688          * heavily penalized and will only be chosen if nothing
689          * else within a million pixels */
690         if(offset > distance)
691             score += 1000000;
692
693         if(best_score == -1 || score < best_score)
694             best_client = cur,
695                 best_score = score;
696     }
697
698     return best_client;
699 }
700
701 void focus_directional_cycle(ObDirection dir, gboolean dock_windows,
702                              gboolean interactive,
703                              gboolean dialog, gboolean done, gboolean cancel)
704 {
705     static ObClient *first = NULL;
706     ObClient *ft = NULL;
707
708     if (!interactive)
709         return;
710
711     if (cancel) {
712         focus_cycle_target = NULL;
713         goto done_cycle;
714     } else if (done)
715         goto done_cycle;
716
717     if (!focus_order)
718         goto done_cycle;
719
720     if (!first) first = focus_client;
721     if (!focus_cycle_target) focus_cycle_target = focus_client;
722
723     if (focus_cycle_target)
724         ft = focus_find_directional(focus_cycle_target, dir, dock_windows);
725     else {
726         GList *it;
727
728         for (it = focus_order; it; it = g_list_next(it))
729             if (valid_focus_target(it->data, FALSE, dock_windows))
730                 ft = it->data;
731     }
732         
733     if (ft) {
734         if (ft != focus_cycle_target) {/* prevents flicker */
735             focus_cycle_target = ft;
736             focus_cycle_draw_indicator();
737         }
738     }
739     if (focus_cycle_target) {
740         popup_cycle(focus_cycle_target, dialog);
741         if (dialog)
742             return;
743     }
744
745
746 done_cycle:
747     if (done && focus_cycle_target)
748         client_activate(focus_cycle_target, FALSE, TRUE);
749
750     first = NULL;
751     focus_cycle_target = NULL;
752
753     focus_cycle_draw_indicator();
754     popup_cycle(ft, FALSE);
755
756     return;
757 }
758
759 void focus_order_add_new(ObClient *c)
760 {
761     if (c->iconic)
762         focus_order_to_top(c);
763     else {
764         g_assert(!g_list_find(focus_order, c));
765         /* if there are any iconic windows, put this above them in the order,
766            but if there are not, then put it under the currently focused one */
767         if (focus_order && ((ObClient*)focus_order->data)->iconic)
768             focus_order = g_list_insert(focus_order, c, 0);
769         else
770             focus_order = g_list_insert(focus_order, c, 1);
771     }
772 }
773
774 void focus_order_remove(ObClient *c)
775 {
776     focus_order = g_list_remove(focus_order, c);
777 }
778
779 void focus_order_to_top(ObClient *c)
780 {
781     focus_order = g_list_remove(focus_order, c);
782     if (!c->iconic) {
783         focus_order = g_list_prepend(focus_order, c);
784     } else {
785         GList *it;
786
787         /* insert before first iconic window */
788         for (it = focus_order;
789              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
790         focus_order = g_list_insert_before(focus_order, it, c);
791     }
792 }
793
794 void focus_order_to_bottom(ObClient *c)
795 {
796     focus_order = g_list_remove(focus_order, c);
797     if (c->iconic) {
798         focus_order = g_list_append(focus_order, c);
799     } else {
800         GList *it;
801
802         /* insert before first iconic window */
803         for (it = focus_order;
804              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
805         focus_order = g_list_insert_before(focus_order, it, c);
806     }
807 }
808
809 ObClient *focus_order_find_first(guint desktop)
810 {
811     GList *it;
812     for (it = focus_order; it; it = g_list_next(it)) {
813         ObClient *c = it->data;
814         if (c->desktop == desktop || c->desktop == DESKTOP_ALL)
815             return c;
816     }
817     return NULL;
818 }