]> icculus.org git repositories - dana/openbox.git/blob - openbox/focus.c
>:| I put old code back somehow.. thanks mika. hooray peer review :)
[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.override_redirect = True;
91         attr.background_pixel = BlackPixel(ob_display, ob_screen);
92         focus_indicator.top.win =
93             createWindow(RootWindow(ob_display, ob_screen),
94                          CWOverrideRedirect | CWBackPixel, &attr);
95         focus_indicator.left.win =
96             createWindow(RootWindow(ob_display, ob_screen),
97                          CWOverrideRedirect | CWBackPixel, &attr);
98         focus_indicator.right.win =
99             createWindow(RootWindow(ob_display, ob_screen),
100                          CWOverrideRedirect | CWBackPixel, &attr);
101         focus_indicator.bottom.win =
102             createWindow(RootWindow(ob_display, ob_screen),
103                          CWOverrideRedirect | CWBackPixel, &attr);
104
105         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.top));
106         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left));
107         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right));
108         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom));
109
110         color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff);
111
112         a_focus_indicator = RrAppearanceNew(ob_rr_inst, 4);
113         a_focus_indicator->surface.grad = RR_SURFACE_SOLID;
114         a_focus_indicator->surface.relief = RR_RELIEF_FLAT;
115         a_focus_indicator->surface.primary = RrColorNew(ob_rr_inst,
116                                                         0, 0, 0);
117         a_focus_indicator->texture[0].type = RR_TEXTURE_LINE_ART;
118         a_focus_indicator->texture[0].data.lineart.color = color_white;
119         a_focus_indicator->texture[1].type = RR_TEXTURE_LINE_ART;
120         a_focus_indicator->texture[1].data.lineart.color = color_white;
121         a_focus_indicator->texture[2].type = RR_TEXTURE_LINE_ART;
122         a_focus_indicator->texture[2].data.lineart.color = color_white;
123         a_focus_indicator->texture[3].type = RR_TEXTURE_LINE_ART;
124         a_focus_indicator->texture[3].data.lineart.color = color_white;
125     }
126 }
127
128 void focus_shutdown(gboolean reconfig)
129 {
130     guint i;
131
132     icon_popup_free(focus_cycle_popup);
133
134     if (!reconfig) {
135         client_remove_destructor(focus_cycle_destructor);
136
137         for (i = 0; i < screen_num_desktops; ++i)
138             g_list_free(focus_order[i]);
139         g_free(focus_order);
140
141         /* reset focus to root */
142         XSetInputFocus(ob_display, PointerRoot, RevertToNone, CurrentTime);
143
144         RrColorFree(color_white);
145
146         RrAppearanceFree(a_focus_indicator);
147
148         XDestroyWindow(ob_display, focus_indicator.top.win);
149         XDestroyWindow(ob_display, focus_indicator.left.win);
150         XDestroyWindow(ob_display, focus_indicator.right.win);
151         XDestroyWindow(ob_display, focus_indicator.bottom.win);
152     }
153 }
154
155 static void push_to_top(ObClient *client)
156 {
157     guint desktop;
158
159     desktop = client->desktop;
160     if (desktop == DESKTOP_ALL) desktop = screen_desktop;
161     focus_order[desktop] = g_list_remove(focus_order[desktop], client);
162     focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
163 }
164
165 void focus_set_client(ObClient *client)
166 {
167     Window active;
168     ObClient *old;
169
170 #ifdef DEBUG_FOCUS
171     ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
172 #endif
173
174     /* uninstall the old colormap, and install the new one */
175     screen_install_colormap(focus_client, FALSE);
176     screen_install_colormap(client, TRUE);
177
178     if (client == NULL) {
179 #ifdef DEBUG_FOCUS
180         ob_debug("actively focusing NONWINDOW\n");
181 #endif
182         /* when nothing will be focused, send focus to the backup target */
183         XSetInputFocus(ob_display, screen_support_win, RevertToNone,
184                        event_curtime);
185         XSync(ob_display, FALSE);
186     }
187
188     /* in the middle of cycling..? kill it. */
189     if (focus_cycle_target)
190         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
191
192     old = focus_client;
193     focus_client = client;
194
195     /* move to the top of the list */
196     if (client != NULL)
197         push_to_top(client);
198
199     /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
200     if (ob_state() != OB_STATE_EXITING) {
201         active = client ? client->window : None;
202         PROP_SET32(RootWindow(ob_display, ob_screen),
203                    net_active_window, window, active);
204
205         /* remove hiliting from the window when it gets focused */
206         if (client != NULL)
207             client_hilite(client, FALSE);
208     }
209 }
210
211 /* finds the first transient that isn't 'skip' and ensure's that client_normal
212  is true for it */
213 static ObClient *find_transient_recursive(ObClient *c, ObClient *top,
214                                           ObClient *skip)
215 {
216     GSList *it;
217     ObClient *ret;
218
219     for (it = c->transients; it; it = g_slist_next(it)) {
220         if (it->data == top) return NULL;
221         ret = find_transient_recursive(it->data, top, skip);
222         if (ret && ret != skip && client_normal(ret)) return ret;
223         if (it->data != skip && client_normal(it->data)) return it->data;
224     }
225     return NULL;
226 }
227
228 static ObClient* focus_fallback_transient(ObClient *top, ObClient *old)
229 {
230     ObClient *target = find_transient_recursive(top, top, old);
231     if (!target) {
232         /* make sure client_normal is true always */
233         if (!client_normal(top))
234             return NULL;
235         target = top; /* no transient, keep the top */
236     }
237     if (client_can_focus(target))
238         return target;
239     else
240         return NULL;
241 }
242
243 ObClient* focus_fallback_target(ObFocusFallbackType type)
244 {
245     GList *it;
246     ObClient *old = NULL;
247     ObClient *target = NULL;
248
249     old = focus_client;
250
251     if ((type == OB_FOCUS_FALLBACK_UNFOCUSING
252          || type == OB_FOCUS_FALLBACK_CLOSED) && old) {
253         if (old->transient_for) {
254             gboolean trans = FALSE;
255
256             if (!config_focus_follow || config_focus_last)
257                 trans = TRUE;
258             else {
259                 if ((target = client_under_pointer()) &&
260                     client_search_transient
261                     (client_search_top_transient(target), old))
262                 {
263                     trans = TRUE;
264                 }
265             }
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                                     return target;
282                         }
283                     }
284                 } else {
285                     if ((target =
286                          focus_fallback_transient(old->transient_for, old)))
287                         return target;
288                 }
289             }
290         }
291     }
292
293     if (config_focus_follow &&
294         (type == OB_FOCUS_FALLBACK_UNFOCUSING || !config_focus_last))
295     {
296         if ((target = client_under_pointer()))
297             if (client_normal(target) && client_can_focus(target))
298                 return target;
299     }
300
301 #if 0
302         /* try for group relations */
303         if (old->group) {
304             GSList *sit;
305
306             for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
307                 for (sit = old->group->members; sit; sit = g_slist_next(sit))
308                     if (sit->data == it->data)
309                         if (sit->data != old && client_normal(sit->data))
310                             if (client_can_focus(sit->data))
311                                 return sit->data;
312         }
313 #endif
314
315     for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
316         if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
317             if (client_normal(it->data) && client_can_focus(it->data))
318                 return it->data;
319
320     /* XXX fallback to the "desktop window" if one exists ?
321        could store it while going through all the windows in the loop right
322        above this..
323     */
324
325     return NULL;
326 }
327
328 void focus_fallback(ObFocusFallbackType type)
329 {
330     ObClient *new;
331
332     /* unfocus any focused clients.. they can be focused by Pointer events
333        and such, and then when I try focus them, I won't get a FocusIn event
334        at all for them.
335     */
336     focus_set_client(NULL);
337
338     if ((new = focus_fallback_target(type)))
339         client_focus(new);
340 }
341
342 static void popup_cycle(ObClient *c, gboolean show)
343 {
344     if (!show) {
345         icon_popup_hide(focus_cycle_popup);
346     } else {
347         Rect *a;
348         ObClient *p = c;
349         gchar *title = NULL;
350
351         a = screen_physical_area_monitor(0);
352         icon_popup_position(focus_cycle_popup, CenterGravity,
353                             a->x + a->width / 2, a->y + a->height / 2);
354 /*        icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
355         icon_popup_show(focus_cycle_popup, c->title,
356                         client_icon(c, a->height/16, a->height/16));
357 */
358         /* XXX the size and the font extents need to be related on some level
359          */
360         icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
361
362         /* use the transient's parent's title/icon */
363         while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
364             p = p->transient_for;
365
366         if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
367             title = g_strdup(p->iconic ? p->icon_title : p->title);
368             /*title = g_strconcat((c->iconic ? c->icon_title : c->title),
369                                 " - ",
370                                 (p->iconic ? p->icon_title : p->title),
371                                 NULL);
372             */
373         icon_popup_show(focus_cycle_popup,
374                         (title ? title :
375                          (c->iconic ? c->icon_title : c->title)),
376                         client_icon(p, 48, 48));
377         g_free(title);
378     }
379 }
380
381 void focus_cycle_draw_indicator()
382 {
383     if (!focus_cycle_target) {
384         XUnmapWindow(ob_display, focus_indicator.top.win);
385         XUnmapWindow(ob_display, focus_indicator.left.win);
386         XUnmapWindow(ob_display, focus_indicator.right.win);
387         XUnmapWindow(ob_display, focus_indicator.bottom.win);
388     } else {
389         /*
390           if (focus_cycle_target)
391               frame_adjust_focus(focus_cycle_target->frame, FALSE);
392           frame_adjust_focus(focus_cycle_target->frame, TRUE);
393         */
394         gint x, y, w, h;
395         gint wt, wl, wr, wb;
396
397         wt = wl = wr = wb = MAX(3,
398                                 MAX(1, MAX(ob_rr_theme->paddingx,
399                                            ob_rr_theme->paddingy)) * 2 +
400                                 ob_rr_theme->fbwidth * 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_pager &&
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 }