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