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