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