]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/focus.c
fallback to transient parents properly
[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        Ben 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, ObClient *old)
248 {
249     GList *it;
250     ObClient *target = NULL;
251
252     if ((type == OB_FOCUS_FALLBACK_UNFOCUSING
253          || type == OB_FOCUS_FALLBACK_CLOSED) && old) {
254         if (old->transient_for) {
255             gboolean trans = FALSE;
256
257             if (!config_focus_follow || config_focus_last)
258                 trans = TRUE;
259             else {
260                 if ((target = client_under_pointer())) {
261                     GSList *sit;
262
263                     sit = client_search_top_transients(target);
264                     for (; sit; sit = g_slist_next(sit))
265                         if (client_search_transient(sit->data, old)) {
266                             trans = TRUE;
267                             break;
268                         }
269                 }
270             }
271
272             /* try for transient relations */
273             if (trans) {
274                 if (old->transient_for == OB_TRAN_GROUP) {
275                     for (it = focus_order[screen_desktop]; it;
276                          it = g_list_next(it))
277                     {
278                         GSList *sit;
279
280                         for (sit = old->group->members; sit;
281                              sit = g_slist_next(sit))
282                         {
283                             if (sit->data == it->data)
284                                 if ((target =
285                                      focus_fallback_transient(sit->data, old)))
286                                     return target;
287                         }
288                     }
289                 } else {
290                     if ((target =
291                          focus_fallback_transient(old->transient_for, old)))
292                         return target;
293                 }
294             }
295         }
296     }
297
298     if (config_focus_follow &&
299         (type == OB_FOCUS_FALLBACK_UNFOCUSING || !config_focus_last))
300     {
301         if ((target = client_under_pointer()))
302             if (client_normal(target) && client_can_focus(target))
303                 return target;
304     }
305
306 #if 0
307         /* try for group relations */
308         if (old->group) {
309             GSList *sit;
310
311             for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
312                 for (sit = old->group->members; sit; sit = g_slist_next(sit))
313                     if (sit->data == it->data)
314                         if (sit->data != old && client_normal(sit->data))
315                             if (client_can_focus(sit->data))
316                                 return sit->data;
317         }
318 #endif
319
320     for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
321         if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
322             if (client_normal(it->data) && client_can_focus(it->data))
323                 return it->data;
324
325     /* XXX fallback to the "desktop window" if one exists ?
326        could store it while going through all the windows in the loop right
327        above this..
328     */
329
330     return NULL;
331 }
332
333 void focus_fallback(ObFocusFallbackType type)
334 {
335     ObClient *new;
336     ObClient *old = focus_client;
337
338     /* unfocus any focused clients.. they can be focused by Pointer events
339        and such, and then when I try focus them, I won't get a FocusIn event
340        at all for them.
341     */
342     focus_set_client(NULL);
343
344     if ((new = focus_fallback_target(type, old)))
345         client_focus(new);
346 }
347
348 static void popup_cycle(ObClient *c, gboolean show)
349 {
350     if (!show) {
351         icon_popup_hide(focus_cycle_popup);
352     } else {
353         Rect *a;
354         ObClient *p = c;
355         gchar *title = NULL;
356
357         a = screen_physical_area_monitor(0);
358         icon_popup_position(focus_cycle_popup, CenterGravity,
359                             a->x + a->width / 2, a->y + a->height / 2);
360 /*        icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
361         icon_popup_show(focus_cycle_popup, c->title,
362                         client_icon(c, a->height/16, a->height/16));
363 */
364         /* XXX the size and the font extents need to be related on some level
365          */
366         icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
367
368         /* use the transient's parent's title/icon */
369         while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
370             p = p->transient_for;
371
372         if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
373             title = g_strdup(p->iconic ? p->icon_title : p->title);
374             /*title = g_strconcat((c->iconic ? c->icon_title : c->title),
375                                 " - ",
376                                 (p->iconic ? p->icon_title : p->title),
377                                 NULL);
378             */
379         icon_popup_show(focus_cycle_popup,
380                         (title ? title :
381                          (c->iconic ? c->icon_title : c->title)),
382                         client_icon(p, 48, 48));
383         g_free(title);
384     }
385 }
386
387 void focus_cycle_draw_indicator()
388 {
389     if (!focus_cycle_target) {
390         XUnmapWindow(ob_display, focus_indicator.top.win);
391         XUnmapWindow(ob_display, focus_indicator.left.win);
392         XUnmapWindow(ob_display, focus_indicator.right.win);
393         XUnmapWindow(ob_display, focus_indicator.bottom.win);
394     } else {
395         /*
396           if (focus_cycle_target)
397               frame_adjust_focus(focus_cycle_target->frame, FALSE);
398           frame_adjust_focus(focus_cycle_target->frame, TRUE);
399         */
400         gint x, y, w, h;
401         gint wt, wl, wr, wb;
402
403         wt = wl = wr = wb = MAX(3,
404                                 MAX(1, MAX(ob_rr_theme->paddingx,
405                                            ob_rr_theme->paddingy)) * 2 +
406                                 ob_rr_theme->fbwidth * 2);
407
408         x = focus_cycle_target->frame->area.x;
409         y = focus_cycle_target->frame->area.y;
410         w = focus_cycle_target->frame->area.width;
411         h = wt;
412
413         XMoveResizeWindow(ob_display, focus_indicator.top.win,
414                           x, y, w, h);
415         a_focus_indicator->texture[0].data.lineart.x1 = 0;
416         a_focus_indicator->texture[0].data.lineart.y1 = h-1;
417         a_focus_indicator->texture[0].data.lineart.x2 = 0;
418         a_focus_indicator->texture[0].data.lineart.y2 = 0;
419         a_focus_indicator->texture[1].data.lineart.x1 = 0;
420         a_focus_indicator->texture[1].data.lineart.y1 = 0;
421         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
422         a_focus_indicator->texture[1].data.lineart.y2 = 0;
423         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
424         a_focus_indicator->texture[2].data.lineart.y1 = 0;
425         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
426         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
427         a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
428         a_focus_indicator->texture[3].data.lineart.y1 = h-1;
429         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
430         a_focus_indicator->texture[3].data.lineart.y2 = h-1;
431         RrPaint(a_focus_indicator, focus_indicator.top.win,
432                 w, h);
433
434         x = focus_cycle_target->frame->area.x;
435         y = focus_cycle_target->frame->area.y;
436         w = wl;
437         h = focus_cycle_target->frame->area.height;
438
439         XMoveResizeWindow(ob_display, focus_indicator.left.win,
440                           x, y, w, h);
441         a_focus_indicator->texture[0].data.lineart.x1 = w-1;
442         a_focus_indicator->texture[0].data.lineart.y1 = 0;
443         a_focus_indicator->texture[0].data.lineart.x2 = 0;
444         a_focus_indicator->texture[0].data.lineart.y2 = 0;
445         a_focus_indicator->texture[1].data.lineart.x1 = 0;
446         a_focus_indicator->texture[1].data.lineart.y1 = 0;
447         a_focus_indicator->texture[1].data.lineart.x2 = 0;
448         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
449         a_focus_indicator->texture[2].data.lineart.x1 = 0;
450         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
451         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
452         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
453         a_focus_indicator->texture[3].data.lineart.x1 = w-1;
454         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
455         a_focus_indicator->texture[3].data.lineart.x2 = w-1;
456         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
457         RrPaint(a_focus_indicator, focus_indicator.left.win,
458                 w, h);
459
460         x = focus_cycle_target->frame->area.x +
461             focus_cycle_target->frame->area.width - wr;
462         y = focus_cycle_target->frame->area.y;
463         w = wr;
464         h = focus_cycle_target->frame->area.height ;
465
466         XMoveResizeWindow(ob_display, focus_indicator.right.win,
467                           x, y, w, h);
468         a_focus_indicator->texture[0].data.lineart.x1 = 0;
469         a_focus_indicator->texture[0].data.lineart.y1 = 0;
470         a_focus_indicator->texture[0].data.lineart.x2 = w-1;
471         a_focus_indicator->texture[0].data.lineart.y2 = 0;
472         a_focus_indicator->texture[1].data.lineart.x1 = w-1;
473         a_focus_indicator->texture[1].data.lineart.y1 = 0;
474         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
475         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
476         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
477         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
478         a_focus_indicator->texture[2].data.lineart.x2 = 0;
479         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
480         a_focus_indicator->texture[3].data.lineart.x1 = 0;
481         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
482         a_focus_indicator->texture[3].data.lineart.x2 = 0;
483         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
484         RrPaint(a_focus_indicator, focus_indicator.right.win,
485                 w, h);
486
487         x = focus_cycle_target->frame->area.x;
488         y = focus_cycle_target->frame->area.y +
489             focus_cycle_target->frame->area.height - wb;
490         w = focus_cycle_target->frame->area.width;
491         h = wb;
492
493         XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
494                           x, y, w, h);
495         a_focus_indicator->texture[0].data.lineart.x1 = 0;
496         a_focus_indicator->texture[0].data.lineart.y1 = 0;
497         a_focus_indicator->texture[0].data.lineart.x2 = 0;
498         a_focus_indicator->texture[0].data.lineart.y2 = h-1;
499         a_focus_indicator->texture[1].data.lineart.x1 = 0;
500         a_focus_indicator->texture[1].data.lineart.y1 = h-1;
501         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
502         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
503         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
504         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
505         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
506         a_focus_indicator->texture[2].data.lineart.y2 = 0;
507         a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
508         a_focus_indicator->texture[3].data.lineart.y1 = 0;
509         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
510         a_focus_indicator->texture[3].data.lineart.y2 = 0;
511         RrPaint(a_focus_indicator, focus_indicator.bottom.win,
512                 w, h);
513
514         XMapWindow(ob_display, focus_indicator.top.win);
515         XMapWindow(ob_display, focus_indicator.left.win);
516         XMapWindow(ob_display, focus_indicator.right.win);
517         XMapWindow(ob_display, focus_indicator.bottom.win);
518     }
519 }
520
521 static gboolean valid_focus_target(ObClient *ft)
522 {
523     /* we don't use client_can_focus here, because that doesn't let you
524        focus an iconic window, but we want to be able to, so we just check
525        if the focus flags on the window allow it, and its on the current
526        desktop */
527     if ((ft->type == OB_CLIENT_TYPE_NORMAL ||
528          ft->type == OB_CLIENT_TYPE_DIALOG ||
529          (!client_has_group_siblings(ft) &&
530           (ft->type == OB_CLIENT_TYPE_TOOLBAR ||
531            ft->type == OB_CLIENT_TYPE_MENU ||
532            ft->type == OB_CLIENT_TYPE_UTILITY))) &&
533         ((ft->can_focus || ft->focus_notify) &&
534          !ft->skip_pager &&
535          (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)) &&
536         ft == client_focus_target(ft))
537         return TRUE;
538 /*
539     {
540         GSList *it;
541
542         for (it = ft->transients; it; it = g_slist_next(it)) {
543             ObClient *c = it->data;
544
545             if (c->frame->visible)
546                 return FALSE;
547         }
548         return TRUE;
549     }
550 */
551
552     return FALSE;
553 }
554
555 void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
556                  gboolean dialog, gboolean done, gboolean cancel, Time time)
557 {
558     static ObClient *first = NULL;
559     static ObClient *t = NULL;
560     static GList *order = NULL;
561     GList *it, *start, *list;
562     ObClient *ft = NULL;
563
564     if (interactive) {
565         if (cancel) {
566             focus_cycle_target = NULL;
567             goto done_cycle;
568         } else if (done)
569             goto done_cycle;
570
571         if (!focus_order[screen_desktop])
572             goto done_cycle;
573
574         if (!first) first = focus_client;
575
576         if (linear) list = client_list;
577         else        list = focus_order[screen_desktop];
578     } else {
579         if (!focus_order[screen_desktop])
580             goto done_cycle;
581         list = client_list;
582     }
583     if (!focus_cycle_target) focus_cycle_target = focus_client;
584
585     start = it = g_list_find(list, focus_cycle_target);
586     if (!start) /* switched desktops or something? */
587         start = it = forward ? g_list_last(list) : g_list_first(list);
588     if (!start) goto done_cycle;
589
590     do {
591         if (forward) {
592             it = it->next;
593             if (it == NULL) it = g_list_first(list);
594         } else {
595             it = it->prev;
596             if (it == NULL) it = g_list_last(list);
597         }
598         ft = it->data;
599         if (valid_focus_target(ft)) {
600             if (interactive) {
601                 if (ft != focus_cycle_target) { /* prevents flicker */
602                     focus_cycle_target = ft;
603                     focus_cycle_draw_indicator();
604                 }
605                 popup_cycle(ft, dialog);
606                 return;
607             } else if (ft != focus_cycle_target) {
608                 focus_cycle_target = ft;
609                 done = TRUE;
610                 break;
611             }
612         }
613     } while (it != start);
614
615 done_cycle:
616     if (done && focus_cycle_target)
617         client_activate(focus_cycle_target, FALSE, TRUE, time);
618
619     t = NULL;
620     first = NULL;
621     focus_cycle_target = NULL;
622     g_list_free(order);
623     order = NULL;
624
625     if (interactive) {
626         focus_cycle_draw_indicator();
627         popup_cycle(ft, FALSE);
628     }
629
630     return;
631 }
632
633 void focus_directional_cycle(ObDirection dir, gboolean interactive,
634                              gboolean dialog, gboolean done, gboolean cancel,
635                              Time time)
636 {
637     static ObClient *first = NULL;
638     ObClient *ft = NULL;
639
640     if (!interactive)
641         return;
642
643     if (cancel) {
644         focus_cycle_target = NULL;
645         goto done_cycle;
646     } else if (done)
647         goto done_cycle;
648
649     if (!focus_order[screen_desktop])
650         goto done_cycle;
651
652     if (!first) first = focus_client;
653     if (!focus_cycle_target) focus_cycle_target = focus_client;
654
655     if (focus_cycle_target)
656         ft = client_find_directional(focus_cycle_target, dir);
657     else {
658         GList *it;
659
660         for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
661             if (valid_focus_target(it->data))
662                 ft = it->data;
663     }
664         
665     if (ft) {
666         if (ft != focus_cycle_target) {/* prevents flicker */
667             focus_cycle_target = ft;
668             focus_cycle_draw_indicator();
669         }
670     }
671     if (focus_cycle_target) {
672         popup_cycle(focus_cycle_target, dialog);
673         if (dialog)
674             return;
675     }
676
677
678 done_cycle:
679     if (done && focus_cycle_target)
680         client_activate(focus_cycle_target, FALSE, TRUE, time);
681
682     first = NULL;
683     focus_cycle_target = NULL;
684
685     focus_cycle_draw_indicator();
686     popup_cycle(ft, FALSE);
687
688     return;
689 }
690
691 void focus_order_add_new(ObClient *c)
692 {
693     guint d, i;
694
695     if (c->iconic)
696         focus_order_to_top(c);
697     else {
698         d = c->desktop;
699         if (d == DESKTOP_ALL) {
700             for (i = 0; i < screen_num_desktops; ++i) {
701                 g_assert(!g_list_find(focus_order[i], c));
702                 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
703                     focus_order[i] = g_list_insert(focus_order[i], c, 0);
704                 else
705                     focus_order[i] = g_list_insert(focus_order[i], c, 1);
706             }
707         } else {
708             g_assert(!g_list_find(focus_order[d], c));
709             if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
710                 focus_order[d] = g_list_insert(focus_order[d], c, 0);
711             else
712                 focus_order[d] = g_list_insert(focus_order[d], c, 1);
713         }
714     }
715 }
716
717 void focus_order_remove(ObClient *c)
718 {
719     guint d, i;
720
721     d = c->desktop;
722     if (d == DESKTOP_ALL) {
723         for (i = 0; i < screen_num_desktops; ++i)
724             focus_order[i] = g_list_remove(focus_order[i], c);
725     } else
726         focus_order[d] = g_list_remove(focus_order[d], c);
727 }
728
729 static void to_top(ObClient *c, guint d)
730 {
731     focus_order[d] = g_list_remove(focus_order[d], c);
732     if (!c->iconic) {
733         focus_order[d] = g_list_prepend(focus_order[d], c);
734     } else {
735         GList *it;
736
737         /* insert before first iconic window */
738         for (it = focus_order[d];
739              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
740         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
741     }
742 }
743
744 void focus_order_to_top(ObClient *c)
745 {
746     guint d, i;
747
748     d = c->desktop;
749     if (d == DESKTOP_ALL) {
750         for (i = 0; i < screen_num_desktops; ++i)
751             to_top(c, i);
752     } else
753         to_top(c, d);
754 }
755
756 static void to_bottom(ObClient *c, guint d)
757 {
758     focus_order[d] = g_list_remove(focus_order[d], c);
759     if (c->iconic) {
760         focus_order[d] = g_list_append(focus_order[d], c);
761     } else {
762         GList *it;
763
764         /* insert before first iconic window */
765         for (it = focus_order[d];
766              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
767         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
768     }
769 }
770
771 void focus_order_to_bottom(ObClient *c)
772 {
773     guint d, i;
774
775     d = c->desktop;
776     if (d == DESKTOP_ALL) {
777         for (i = 0; i < screen_num_desktops; ++i)
778             to_bottom(c, i);
779     } else
780         to_bottom(c, d);
781 }