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