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