i rewrote handling of focus events. this is pretty much based on blackbox's current...
[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, *focus_hilite;
41 GList **focus_order; /* these lists are created when screen_startup
42                         sets the number of desktops */
43 ObClient *focus_cycle_target;
44
45 struct {
46     InternalWindow top;
47     InternalWindow left;
48     InternalWindow right;
49     InternalWindow bottom;
50 } focus_indicator;
51
52 RrAppearance *a_focus_indicator;
53 RrColor *color_white;
54
55 static ObIconPopup *focus_cycle_popup;
56
57 static void focus_cycle_destructor(ObClient *client, gpointer data)
58 {
59     /* end cycling if the target disappears. CurrentTime is fine, time won't
60        be used
61     */
62     if (focus_cycle_target == client)
63         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, CurrentTime);
64 }
65
66 static Window createWindow(Window parent, gulong mask,
67                            XSetWindowAttributes *attrib)
68 {
69     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
70                          RrDepth(ob_rr_inst), InputOutput,
71                          RrVisual(ob_rr_inst), mask, attrib);
72                        
73 }
74
75 void focus_startup(gboolean reconfig)
76 {
77     focus_cycle_popup = icon_popup_new(TRUE);
78
79     if (!reconfig) {
80         XSetWindowAttributes attr;
81
82         client_add_destructor(focus_cycle_destructor, NULL);
83
84         /* start with nothing focused */
85         focus_set_client(NULL);
86
87         focus_indicator.top.obwin.type = Window_Internal;
88         focus_indicator.left.obwin.type = Window_Internal;
89         focus_indicator.right.obwin.type = Window_Internal;
90         focus_indicator.bottom.obwin.type = Window_Internal;
91
92         attr.override_redirect = True;
93         attr.background_pixel = BlackPixel(ob_display, ob_screen);
94         focus_indicator.top.win =
95             createWindow(RootWindow(ob_display, ob_screen),
96                          CWOverrideRedirect | CWBackPixel, &attr);
97         focus_indicator.left.win =
98             createWindow(RootWindow(ob_display, ob_screen),
99                          CWOverrideRedirect | CWBackPixel, &attr);
100         focus_indicator.right.win =
101             createWindow(RootWindow(ob_display, ob_screen),
102                          CWOverrideRedirect | CWBackPixel, &attr);
103         focus_indicator.bottom.win =
104             createWindow(RootWindow(ob_display, ob_screen),
105                          CWOverrideRedirect | CWBackPixel, &attr);
106
107         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.top));
108         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left));
109         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right));
110         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom));
111
112         color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff);
113
114         a_focus_indicator = RrAppearanceNew(ob_rr_inst, 4);
115         a_focus_indicator->surface.grad = RR_SURFACE_SOLID;
116         a_focus_indicator->surface.relief = RR_RELIEF_FLAT;
117         a_focus_indicator->surface.primary = RrColorNew(ob_rr_inst,
118                                                         0, 0, 0);
119         a_focus_indicator->texture[0].type = RR_TEXTURE_LINE_ART;
120         a_focus_indicator->texture[0].data.lineart.color = color_white;
121         a_focus_indicator->texture[1].type = RR_TEXTURE_LINE_ART;
122         a_focus_indicator->texture[1].data.lineart.color = color_white;
123         a_focus_indicator->texture[2].type = RR_TEXTURE_LINE_ART;
124         a_focus_indicator->texture[2].data.lineart.color = color_white;
125         a_focus_indicator->texture[3].type = RR_TEXTURE_LINE_ART;
126         a_focus_indicator->texture[3].data.lineart.color = color_white;
127     }
128 }
129
130 void focus_shutdown(gboolean reconfig)
131 {
132     guint i;
133
134     icon_popup_free(focus_cycle_popup);
135
136     if (!reconfig) {
137         client_remove_destructor(focus_cycle_destructor);
138
139         for (i = 0; i < screen_num_desktops; ++i)
140             g_list_free(focus_order[i]);
141         g_free(focus_order);
142
143         /* reset focus to root */
144         XSetInputFocus(ob_display, PointerRoot, RevertToNone, CurrentTime);
145
146         RrColorFree(color_white);
147
148         RrAppearanceFree(a_focus_indicator);
149
150         XDestroyWindow(ob_display, focus_indicator.top.win);
151         XDestroyWindow(ob_display, focus_indicator.left.win);
152         XDestroyWindow(ob_display, focus_indicator.right.win);
153         XDestroyWindow(ob_display, focus_indicator.bottom.win);
154     }
155 }
156
157 static void push_to_top(ObClient *client)
158 {
159     guint desktop;
160
161     desktop = client->desktop;
162     if (desktop == DESKTOP_ALL) desktop = screen_desktop;
163     focus_order[desktop] = g_list_remove(focus_order[desktop], client);
164     focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
165 }
166
167 void focus_set_client(ObClient *client)
168 {
169     Window active;
170     ObClient *old;
171
172 #ifdef DEBUG_FOCUS
173     ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
174 #endif
175
176     /* uninstall the old colormap, and install the new one */
177     screen_install_colormap(focus_client, FALSE);
178     screen_install_colormap(client, TRUE);
179
180     if (client == NULL) {
181 #ifdef DEBUG_FOCUS
182         ob_debug("actively focusing NONWINDOW\n");
183 #endif
184         /* when nothing will be focused, send focus to the backup target */
185         XSetInputFocus(ob_display, screen_support_win, RevertToNone,
186                        event_curtime);
187         XSync(ob_display, FALSE);
188     }
189
190     /* in the middle of cycling..? kill it. CurrentTime is fine, time won't
191        be used.
192     */
193     if (focus_cycle_target)
194         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, CurrentTime);
195
196     old = focus_client;
197     focus_client = client;
198
199     /* move to the top of the list */
200     if (client != NULL)
201         push_to_top(client);
202
203     /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
204     if (ob_state() != OB_STATE_EXITING) {
205         active = client ? client->window : None;
206         PROP_SET32(RootWindow(ob_display, ob_screen),
207                    net_active_window, window, active);
208
209         /* remove hiliting from the window when it gets focused */
210         if (client != NULL)
211             client_hilite(client, FALSE);
212     }
213 }
214
215 /* finds the first transient that isn't 'skip' and ensure's that client_normal
216  is true for it */
217 static ObClient *find_transient_recursive(ObClient *c, ObClient *top,
218                                           ObClient *skip)
219 {
220     GSList *it;
221     ObClient *ret;
222
223     for (it = c->transients; it; it = g_slist_next(it)) {
224         if (it->data == top) return NULL;
225         ret = find_transient_recursive(it->data, top, skip);
226         if (ret && ret != skip && client_normal(ret)) return ret;
227         if (it->data != skip && client_normal(it->data)) return it->data;
228     }
229     return NULL;
230 }
231
232 static ObClient* focus_fallback_transient(ObClient *top, ObClient *old)
233 {
234     ObClient *target = find_transient_recursive(top, top, old);
235     if (!target) {
236         /* make sure client_normal is true always */
237         if (!client_normal(top))
238             return NULL;
239         target = top; /* no transient, keep the top */
240     }
241     if (client_can_focus(target))
242         return target;
243     else
244         return NULL;
245 }
246
247 ObClient* focus_fallback_target(ObFocusFallbackType type)
248 {
249     GList *it;
250     ObClient *old;
251     ObClient *target = NULL;
252
253     old = focus_client;
254
255     if ((type == OB_FOCUS_FALLBACK_UNFOCUSING
256          || type == OB_FOCUS_FALLBACK_CLOSED) && old) {
257         if (old->transient_for) {
258             gboolean trans = FALSE;
259
260             if (!config_focus_follow || config_focus_last)
261                 trans = TRUE;
262             else if ((target = client_under_pointer()) &&
263                      client_search_transient
264                      (client_search_top_parent(target), old))
265                 trans = TRUE;
266
267             /* try for transient relations */
268             if (trans) {
269                 if (old->transient_for == OB_TRAN_GROUP) {
270                     for (it = focus_order[screen_desktop]; it;
271                          it = g_list_next(it))
272                     {
273                         GSList *sit;
274
275                         for (sit = old->group->members; sit;
276                              sit = g_slist_next(sit))
277                         {
278                             if (sit->data == it->data)
279                                 if ((target =
280                                      focus_fallback_transient(sit->data, old)))
281                                 {
282                                     ob_debug("found in transient #1\n");
283                                     return target;
284                                 }
285                         }
286                     }
287                 } else {
288                     if ((target =
289                          focus_fallback_transient(old->transient_for, old)))
290                     {
291                         ob_debug("found in transient #2\n");
292                         return target;
293                     }
294                 }
295             }
296         }
297     }
298
299     ob_debug("trying pointer stuff\n");
300     if (config_focus_follow &&
301         (type == OB_FOCUS_FALLBACK_UNFOCUSING || !config_focus_last))
302     {
303         if ((target = client_under_pointer()))
304             if (client_normal(target) && client_can_focus(target)) {
305                 ob_debug("found in pointer stuff\n");
306                 return target;
307             }
308     }
309
310 #if 0
311         /* try for group relations */
312         if (old->group) {
313             GSList *sit;
314
315             for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
316                 for (sit = old->group->members; sit; sit = g_slist_next(sit))
317                     if (sit->data == it->data)
318                         if (sit->data != old && client_normal(sit->data))
319                             if (client_can_focus(sit->data))
320                                 return sit->data;
321         }
322 #endif
323
324     ob_debug("trying  the focus order\n");
325     for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
326         if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
327             if (client_normal(it->data) && client_can_focus(it->data)) {
328                 ob_debug("found in focus order\n");
329                 return it->data;
330             }
331
332     /* XXX fallback to the "desktop window" if one exists ?
333        could store it while going through all the windows in the loop right
334        above this..
335     */
336
337     return NULL;
338 }
339
340 void focus_fallback(ObFocusFallbackType type)
341 {
342     ObClient *new;
343
344     /* unfocus any focused clients.. they can be focused by Pointer events
345        and such, and then when I try focus them, I won't get a FocusIn event
346        at all for them.
347     */
348     focus_set_client(NULL);
349
350     if ((new = focus_fallback_target(type)))
351         client_focus(new);
352 }
353
354 static void popup_cycle(ObClient *c, gboolean show)
355 {
356     if (!show) {
357         icon_popup_hide(focus_cycle_popup);
358     } else {
359         Rect *a;
360         ObClient *p = c;
361         gchar *title = NULL;
362
363         a = screen_physical_area_monitor(0);
364         icon_popup_position(focus_cycle_popup, CenterGravity,
365                             a->x + a->width / 2, a->y + a->height / 2);
366 /*        icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
367         icon_popup_show(focus_cycle_popup, c->title,
368                         client_icon(c, a->height/16, a->height/16));
369 */
370         /* XXX the size and the font extents need to be related on some level
371          */
372         icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
373
374         /* use the transient's parent's title/icon */
375         while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
376             p = p->transient_for;
377
378         if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
379             title = g_strdup(p->iconic ? p->icon_title : p->title);
380             /*title = g_strconcat((c->iconic ? c->icon_title : c->title),
381                                 " - ",
382                                 (p->iconic ? p->icon_title : p->title),
383                                 NULL);
384             */
385         icon_popup_show(focus_cycle_popup,
386                         (title ? title :
387                          (c->iconic ? c->icon_title : c->title)),
388                         client_icon(p, 48, 48));
389         g_free(title);
390     }
391 }
392
393 void focus_cycle_draw_indicator()
394 {
395     if (!focus_cycle_target) {
396         XUnmapWindow(ob_display, focus_indicator.top.win);
397         XUnmapWindow(ob_display, focus_indicator.left.win);
398         XUnmapWindow(ob_display, focus_indicator.right.win);
399         XUnmapWindow(ob_display, focus_indicator.bottom.win);
400     } else {
401         /*
402           if (focus_cycle_target)
403               frame_adjust_focus(focus_cycle_target->frame, FALSE);
404           frame_adjust_focus(focus_cycle_target->frame, TRUE);
405         */
406         gint x, y, w, h;
407         gint wt, wl, wr, wb;
408
409         wt = wl = wr = wb = MAX(3,
410                                 MAX(1, MAX(ob_rr_theme->paddingx,
411                                            ob_rr_theme->paddingy)) * 2 +
412                                 ob_rr_theme->fbwidth * 2);
413
414         x = focus_cycle_target->frame->area.x;
415         y = focus_cycle_target->frame->area.y;
416         w = focus_cycle_target->frame->area.width;
417         h = wt;
418
419         XMoveResizeWindow(ob_display, focus_indicator.top.win,
420                           x, y, w, h);
421         a_focus_indicator->texture[0].data.lineart.x1 = 0;
422         a_focus_indicator->texture[0].data.lineart.y1 = h-1;
423         a_focus_indicator->texture[0].data.lineart.x2 = 0;
424         a_focus_indicator->texture[0].data.lineart.y2 = 0;
425         a_focus_indicator->texture[1].data.lineart.x1 = 0;
426         a_focus_indicator->texture[1].data.lineart.y1 = 0;
427         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
428         a_focus_indicator->texture[1].data.lineart.y2 = 0;
429         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
430         a_focus_indicator->texture[2].data.lineart.y1 = 0;
431         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
432         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
433         a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
434         a_focus_indicator->texture[3].data.lineart.y1 = h-1;
435         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
436         a_focus_indicator->texture[3].data.lineart.y2 = h-1;
437         RrPaint(a_focus_indicator, focus_indicator.top.win,
438                 w, h);
439
440         x = focus_cycle_target->frame->area.x;
441         y = focus_cycle_target->frame->area.y;
442         w = wl;
443         h = focus_cycle_target->frame->area.height;
444
445         XMoveResizeWindow(ob_display, focus_indicator.left.win,
446                           x, y, w, h);
447         a_focus_indicator->texture[0].data.lineart.x1 = w-1;
448         a_focus_indicator->texture[0].data.lineart.y1 = 0;
449         a_focus_indicator->texture[0].data.lineart.x2 = 0;
450         a_focus_indicator->texture[0].data.lineart.y2 = 0;
451         a_focus_indicator->texture[1].data.lineart.x1 = 0;
452         a_focus_indicator->texture[1].data.lineart.y1 = 0;
453         a_focus_indicator->texture[1].data.lineart.x2 = 0;
454         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
455         a_focus_indicator->texture[2].data.lineart.x1 = 0;
456         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
457         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
458         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
459         a_focus_indicator->texture[3].data.lineart.x1 = w-1;
460         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
461         a_focus_indicator->texture[3].data.lineart.x2 = w-1;
462         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
463         RrPaint(a_focus_indicator, focus_indicator.left.win,
464                 w, h);
465
466         x = focus_cycle_target->frame->area.x +
467             focus_cycle_target->frame->area.width - wr;
468         y = focus_cycle_target->frame->area.y;
469         w = wr;
470         h = focus_cycle_target->frame->area.height ;
471
472         XMoveResizeWindow(ob_display, focus_indicator.right.win,
473                           x, y, w, h);
474         a_focus_indicator->texture[0].data.lineart.x1 = 0;
475         a_focus_indicator->texture[0].data.lineart.y1 = 0;
476         a_focus_indicator->texture[0].data.lineart.x2 = w-1;
477         a_focus_indicator->texture[0].data.lineart.y2 = 0;
478         a_focus_indicator->texture[1].data.lineart.x1 = w-1;
479         a_focus_indicator->texture[1].data.lineart.y1 = 0;
480         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
481         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
482         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
483         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
484         a_focus_indicator->texture[2].data.lineart.x2 = 0;
485         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
486         a_focus_indicator->texture[3].data.lineart.x1 = 0;
487         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
488         a_focus_indicator->texture[3].data.lineart.x2 = 0;
489         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
490         RrPaint(a_focus_indicator, focus_indicator.right.win,
491                 w, h);
492
493         x = focus_cycle_target->frame->area.x;
494         y = focus_cycle_target->frame->area.y +
495             focus_cycle_target->frame->area.height - wb;
496         w = focus_cycle_target->frame->area.width;
497         h = wb;
498
499         XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
500                           x, y, w, h);
501         a_focus_indicator->texture[0].data.lineart.x1 = 0;
502         a_focus_indicator->texture[0].data.lineart.y1 = 0;
503         a_focus_indicator->texture[0].data.lineart.x2 = 0;
504         a_focus_indicator->texture[0].data.lineart.y2 = h-1;
505         a_focus_indicator->texture[1].data.lineart.x1 = 0;
506         a_focus_indicator->texture[1].data.lineart.y1 = h-1;
507         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
508         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
509         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
510         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
511         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
512         a_focus_indicator->texture[2].data.lineart.y2 = 0;
513         a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
514         a_focus_indicator->texture[3].data.lineart.y1 = 0;
515         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
516         a_focus_indicator->texture[3].data.lineart.y2 = 0;
517         RrPaint(a_focus_indicator, focus_indicator.bottom.win,
518                 w, h);
519
520         XMapWindow(ob_display, focus_indicator.top.win);
521         XMapWindow(ob_display, focus_indicator.left.win);
522         XMapWindow(ob_display, focus_indicator.right.win);
523         XMapWindow(ob_display, focus_indicator.bottom.win);
524     }
525 }
526
527 static gboolean valid_focus_target(ObClient *ft)
528 {
529     /* we don't use client_can_focus here, because that doesn't let you
530        focus an iconic window, but we want to be able to, so we just check
531        if the focus flags on the window allow it, and its on the current
532        desktop */
533     if ((ft->type == OB_CLIENT_TYPE_NORMAL ||
534          ft->type == OB_CLIENT_TYPE_DIALOG ||
535          (!client_has_group_siblings(ft) &&
536           (ft->type == OB_CLIENT_TYPE_TOOLBAR ||
537            ft->type == OB_CLIENT_TYPE_MENU ||
538            ft->type == OB_CLIENT_TYPE_UTILITY))) &&
539         ((ft->can_focus || ft->focus_notify) &&
540          !ft->skip_pager &&
541          (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)) &&
542         ft == client_focus_target(ft))
543         return TRUE;
544 /*
545     {
546         GSList *it;
547
548         for (it = ft->transients; it; it = g_slist_next(it)) {
549             ObClient *c = it->data;
550
551             if (c->frame->visible)
552                 return FALSE;
553         }
554         return TRUE;
555     }
556 */
557
558     return FALSE;
559 }
560
561 void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
562                  gboolean dialog, gboolean done, gboolean cancel, Time time)
563 {
564     static ObClient *first = NULL;
565     static ObClient *t = NULL;
566     static GList *order = NULL;
567     GList *it, *start, *list;
568     ObClient *ft = NULL;
569
570     if (interactive) {
571         if (cancel) {
572             focus_cycle_target = NULL;
573             goto done_cycle;
574         } else if (done)
575             goto done_cycle;
576
577         if (!focus_order[screen_desktop])
578             goto done_cycle;
579
580         if (!first) first = focus_client;
581
582         if (linear) list = client_list;
583         else        list = focus_order[screen_desktop];
584     } else {
585         if (!focus_order[screen_desktop])
586             goto done_cycle;
587         list = client_list;
588     }
589     if (!focus_cycle_target) focus_cycle_target = focus_client;
590
591     start = it = g_list_find(list, focus_cycle_target);
592     if (!start) /* switched desktops or something? */
593         start = it = forward ? g_list_last(list) : g_list_first(list);
594     if (!start) goto done_cycle;
595
596     do {
597         if (forward) {
598             it = it->next;
599             if (it == NULL) it = g_list_first(list);
600         } else {
601             it = it->prev;
602             if (it == NULL) it = g_list_last(list);
603         }
604         ft = it->data;
605         if (valid_focus_target(ft)) {
606             if (interactive) {
607                 if (ft != focus_cycle_target) { /* prevents flicker */
608                     focus_cycle_target = ft;
609                     focus_cycle_draw_indicator();
610                 }
611                 popup_cycle(ft, dialog);
612                 return;
613             } else if (ft != focus_cycle_target) {
614                 focus_cycle_target = ft;
615                 done = TRUE;
616                 break;
617             }
618         }
619     } while (it != start);
620
621 done_cycle:
622     if (done && focus_cycle_target)
623         client_activate(focus_cycle_target, FALSE, TRUE, time);
624
625     t = NULL;
626     first = NULL;
627     focus_cycle_target = NULL;
628     g_list_free(order);
629     order = NULL;
630
631     if (interactive) {
632         focus_cycle_draw_indicator();
633         popup_cycle(ft, FALSE);
634     }
635
636     return;
637 }
638
639 void focus_directional_cycle(ObDirection dir, gboolean interactive,
640                              gboolean dialog, gboolean done, gboolean cancel,
641                              Time time)
642 {
643     static ObClient *first = NULL;
644     ObClient *ft = NULL;
645
646     if (!interactive)
647         return;
648
649     if (cancel) {
650         focus_cycle_target = NULL;
651         goto done_cycle;
652     } else if (done)
653         goto done_cycle;
654
655     if (!focus_order[screen_desktop])
656         goto done_cycle;
657
658     if (!first) first = focus_client;
659     if (!focus_cycle_target) focus_cycle_target = focus_client;
660
661     if (focus_cycle_target)
662         ft = client_find_directional(focus_cycle_target, dir);
663     else {
664         GList *it;
665
666         for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
667             if (valid_focus_target(it->data))
668                 ft = it->data;
669     }
670         
671     if (ft) {
672         if (ft != focus_cycle_target) {/* prevents flicker */
673             focus_cycle_target = ft;
674             focus_cycle_draw_indicator();
675         }
676     }
677     if (focus_cycle_target) {
678         popup_cycle(focus_cycle_target, dialog);
679         if (dialog)
680             return;
681     }
682
683
684 done_cycle:
685     if (done && focus_cycle_target)
686         client_activate(focus_cycle_target, FALSE, TRUE, time);
687
688     first = NULL;
689     focus_cycle_target = NULL;
690
691     focus_cycle_draw_indicator();
692     popup_cycle(ft, FALSE);
693
694     return;
695 }
696
697 void focus_order_add_new(ObClient *c)
698 {
699     guint d, i;
700
701     if (c->iconic)
702         focus_order_to_top(c);
703     else {
704         d = c->desktop;
705         if (d == DESKTOP_ALL) {
706             for (i = 0; i < screen_num_desktops; ++i) {
707                 g_assert(!g_list_find(focus_order[i], c));
708                 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
709                     focus_order[i] = g_list_insert(focus_order[i], c, 0);
710                 else
711                     focus_order[i] = g_list_insert(focus_order[i], c, 1);
712             }
713         } else {
714             g_assert(!g_list_find(focus_order[d], c));
715             if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
716                 focus_order[d] = g_list_insert(focus_order[d], c, 0);
717             else
718                 focus_order[d] = g_list_insert(focus_order[d], c, 1);
719         }
720     }
721 }
722
723 void focus_order_remove(ObClient *c)
724 {
725     guint d, i;
726
727     d = c->desktop;
728     if (d == DESKTOP_ALL) {
729         for (i = 0; i < screen_num_desktops; ++i)
730             focus_order[i] = g_list_remove(focus_order[i], c);
731     } else
732         focus_order[d] = g_list_remove(focus_order[d], c);
733 }
734
735 static void to_top(ObClient *c, guint d)
736 {
737     focus_order[d] = g_list_remove(focus_order[d], c);
738     if (!c->iconic) {
739         focus_order[d] = g_list_prepend(focus_order[d], c);
740     } else {
741         GList *it;
742
743         /* insert before first iconic window */
744         for (it = focus_order[d];
745              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
746         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
747     }
748 }
749
750 void focus_order_to_top(ObClient *c)
751 {
752     guint d, i;
753
754     d = c->desktop;
755     if (d == DESKTOP_ALL) {
756         for (i = 0; i < screen_num_desktops; ++i)
757             to_top(c, i);
758     } else
759         to_top(c, d);
760 }
761
762 static void to_bottom(ObClient *c, guint d)
763 {
764     focus_order[d] = g_list_remove(focus_order[d], c);
765     if (c->iconic) {
766         focus_order[d] = g_list_append(focus_order[d], c);
767     } else {
768         GList *it;
769
770         /* insert before first iconic window */
771         for (it = focus_order[d];
772              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
773         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
774     }
775 }
776
777 void focus_order_to_bottom(ObClient *c)
778 {
779     guint d, i;
780
781     d = c->desktop;
782     if (d == DESKTOP_ALL) {
783         for (i = 0; i < screen_num_desktops; ++i)
784             to_bottom(c, i);
785     } else
786         to_bottom(c, d);
787 }