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