]> icculus.org git repositories - dana/openbox.git/blob - openbox/focus.c
scary commit..but here goes.
[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                                 ob_rr_theme->handle_height +
395                                 ob_rr_theme->fbwidth * 2);
396
397         x = focus_cycle_target->frame->area.x;
398         y = focus_cycle_target->frame->area.y;
399         w = focus_cycle_target->frame->area.width;
400         h = wt;
401
402         XMoveResizeWindow(ob_display, focus_indicator.top.win,
403                           x, y, w, h);
404         a_focus_indicator->texture[0].data.lineart.x1 = 0;
405         a_focus_indicator->texture[0].data.lineart.y1 = h-1;
406         a_focus_indicator->texture[0].data.lineart.x2 = 0;
407         a_focus_indicator->texture[0].data.lineart.y2 = 0;
408         a_focus_indicator->texture[1].data.lineart.x1 = 0;
409         a_focus_indicator->texture[1].data.lineart.y1 = 0;
410         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
411         a_focus_indicator->texture[1].data.lineart.y2 = 0;
412         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
413         a_focus_indicator->texture[2].data.lineart.y1 = 0;
414         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
415         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
416         a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
417         a_focus_indicator->texture[3].data.lineart.y1 = h-1;
418         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
419         a_focus_indicator->texture[3].data.lineart.y2 = h-1;
420         RrPaint(a_focus_indicator, focus_indicator.top.win,
421                 w, h);
422
423         x = focus_cycle_target->frame->area.x;
424         y = focus_cycle_target->frame->area.y;
425         w = wl;
426         h = focus_cycle_target->frame->area.height;
427
428         XMoveResizeWindow(ob_display, focus_indicator.left.win,
429                           x, y, w, h);
430         a_focus_indicator->texture[0].data.lineart.x1 = w-1;
431         a_focus_indicator->texture[0].data.lineart.y1 = 0;
432         a_focus_indicator->texture[0].data.lineart.x2 = 0;
433         a_focus_indicator->texture[0].data.lineart.y2 = 0;
434         a_focus_indicator->texture[1].data.lineart.x1 = 0;
435         a_focus_indicator->texture[1].data.lineart.y1 = 0;
436         a_focus_indicator->texture[1].data.lineart.x2 = 0;
437         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
438         a_focus_indicator->texture[2].data.lineart.x1 = 0;
439         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
440         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
441         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
442         a_focus_indicator->texture[3].data.lineart.x1 = w-1;
443         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
444         a_focus_indicator->texture[3].data.lineart.x2 = w-1;
445         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
446         RrPaint(a_focus_indicator, focus_indicator.left.win,
447                 w, h);
448
449         x = focus_cycle_target->frame->area.x +
450             focus_cycle_target->frame->area.width - wr;
451         y = focus_cycle_target->frame->area.y;
452         w = wr;
453         h = focus_cycle_target->frame->area.height ;
454
455         XMoveResizeWindow(ob_display, focus_indicator.right.win,
456                           x, y, w, h);
457         a_focus_indicator->texture[0].data.lineart.x1 = 0;
458         a_focus_indicator->texture[0].data.lineart.y1 = 0;
459         a_focus_indicator->texture[0].data.lineart.x2 = w-1;
460         a_focus_indicator->texture[0].data.lineart.y2 = 0;
461         a_focus_indicator->texture[1].data.lineart.x1 = w-1;
462         a_focus_indicator->texture[1].data.lineart.y1 = 0;
463         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
464         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
465         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
466         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
467         a_focus_indicator->texture[2].data.lineart.x2 = 0;
468         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
469         a_focus_indicator->texture[3].data.lineart.x1 = 0;
470         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
471         a_focus_indicator->texture[3].data.lineart.x2 = 0;
472         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
473         RrPaint(a_focus_indicator, focus_indicator.right.win,
474                 w, h);
475
476         x = focus_cycle_target->frame->area.x;
477         y = focus_cycle_target->frame->area.y +
478             focus_cycle_target->frame->area.height - wb;
479         w = focus_cycle_target->frame->area.width;
480         h = wb;
481
482         XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
483                           x, y, w, h);
484         a_focus_indicator->texture[0].data.lineart.x1 = 0;
485         a_focus_indicator->texture[0].data.lineart.y1 = 0;
486         a_focus_indicator->texture[0].data.lineart.x2 = 0;
487         a_focus_indicator->texture[0].data.lineart.y2 = h-1;
488         a_focus_indicator->texture[1].data.lineart.x1 = 0;
489         a_focus_indicator->texture[1].data.lineart.y1 = h-1;
490         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
491         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
492         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
493         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
494         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
495         a_focus_indicator->texture[2].data.lineart.y2 = 0;
496         a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
497         a_focus_indicator->texture[3].data.lineart.y1 = 0;
498         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
499         a_focus_indicator->texture[3].data.lineart.y2 = 0;
500         RrPaint(a_focus_indicator, focus_indicator.bottom.win,
501                 w, h);
502
503         XMapWindow(ob_display, focus_indicator.top.win);
504         XMapWindow(ob_display, focus_indicator.left.win);
505         XMapWindow(ob_display, focus_indicator.right.win);
506         XMapWindow(ob_display, focus_indicator.bottom.win);
507     }
508 }
509
510 static gboolean valid_focus_target(ObClient *ft)
511 {
512     /* we don't use client_can_focus here, because that doesn't let you
513        focus an iconic window, but we want to be able to, so we just check
514        if the focus flags on the window allow it, and its on the current
515        desktop */
516     if ((ft->type == OB_CLIENT_TYPE_NORMAL ||
517          ft->type == OB_CLIENT_TYPE_DIALOG ||
518          (!client_has_group_siblings(ft) &&
519           (ft->type == OB_CLIENT_TYPE_TOOLBAR ||
520            ft->type == OB_CLIENT_TYPE_MENU ||
521            ft->type == OB_CLIENT_TYPE_UTILITY))) &&
522         ((ft->can_focus || ft->focus_notify) &&
523          !ft->skip_taskbar &&
524          (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)) &&
525         ft == client_focus_target(ft))
526         return TRUE;
527 /*
528     {
529         GSList *it;
530
531         for (it = ft->transients; it; it = g_slist_next(it)) {
532             ObClient *c = it->data;
533
534             if (c->frame->visible)
535                 return FALSE;
536         }
537         return TRUE;
538     }
539 */
540
541     return FALSE;
542 }
543
544 void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
545                  gboolean dialog, gboolean done, gboolean cancel)
546 {
547     static ObClient *first = NULL;
548     static ObClient *t = NULL;
549     static GList *order = NULL;
550     GList *it, *start, *list;
551     ObClient *ft = NULL;
552
553     if (interactive) {
554         if (cancel) {
555             focus_cycle_target = NULL;
556             goto done_cycle;
557         } else if (done)
558             goto done_cycle;
559
560         if (!focus_order[screen_desktop])
561             goto done_cycle;
562
563         if (!first) first = focus_client;
564
565         if (linear) list = client_list;
566         else        list = focus_order[screen_desktop];
567     } else {
568         if (!focus_order[screen_desktop])
569             goto done_cycle;
570         list = client_list;
571     }
572     if (!focus_cycle_target) focus_cycle_target = focus_client;
573
574     start = it = g_list_find(list, focus_cycle_target);
575     if (!start) /* switched desktops or something? */
576         start = it = forward ? g_list_last(list) : g_list_first(list);
577     if (!start) goto done_cycle;
578
579     do {
580         if (forward) {
581             it = it->next;
582             if (it == NULL) it = g_list_first(list);
583         } else {
584             it = it->prev;
585             if (it == NULL) it = g_list_last(list);
586         }
587         ft = it->data;
588         if (valid_focus_target(ft)) {
589             if (interactive) {
590                 if (ft != focus_cycle_target) { /* prevents flicker */
591                     focus_cycle_target = ft;
592                     focus_cycle_draw_indicator();
593                 }
594                 popup_cycle(ft, dialog);
595                 return;
596             } else if (ft != focus_cycle_target) {
597                 focus_cycle_target = ft;
598                 done = TRUE;
599                 break;
600             }
601         }
602     } while (it != start);
603
604 done_cycle:
605     if (done && focus_cycle_target)
606         client_activate(focus_cycle_target, FALSE, TRUE);
607
608     t = NULL;
609     first = NULL;
610     focus_cycle_target = NULL;
611     g_list_free(order);
612     order = NULL;
613
614     if (interactive) {
615         focus_cycle_draw_indicator();
616         popup_cycle(ft, FALSE);
617     }
618
619     return;
620 }
621
622 void focus_directional_cycle(ObDirection dir, gboolean interactive,
623                              gboolean dialog, gboolean done, gboolean cancel)
624 {
625     static ObClient *first = NULL;
626     ObClient *ft = NULL;
627
628     if (!interactive)
629         return;
630
631     if (cancel) {
632         focus_cycle_target = NULL;
633         goto done_cycle;
634     } else if (done)
635         goto done_cycle;
636
637     if (!focus_order[screen_desktop])
638         goto done_cycle;
639
640     if (!first) first = focus_client;
641     if (!focus_cycle_target) focus_cycle_target = focus_client;
642
643     if (focus_cycle_target)
644         ft = client_find_directional(focus_cycle_target, dir);
645     else {
646         GList *it;
647
648         for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
649             if (valid_focus_target(it->data))
650                 ft = it->data;
651     }
652         
653     if (ft) {
654         if (ft != focus_cycle_target) {/* prevents flicker */
655             focus_cycle_target = ft;
656             focus_cycle_draw_indicator();
657         }
658     }
659     if (focus_cycle_target) {
660         popup_cycle(focus_cycle_target, dialog);
661         if (dialog)
662             return;
663     }
664
665
666 done_cycle:
667     if (done && focus_cycle_target)
668         client_activate(focus_cycle_target, FALSE, TRUE);
669
670     first = NULL;
671     focus_cycle_target = NULL;
672
673     focus_cycle_draw_indicator();
674     popup_cycle(ft, FALSE);
675
676     return;
677 }
678
679 void focus_order_add_new(ObClient *c)
680 {
681     guint d, i;
682
683     if (c->iconic)
684         focus_order_to_top(c);
685     else {
686         d = c->desktop;
687         if (d == DESKTOP_ALL) {
688             for (i = 0; i < screen_num_desktops; ++i) {
689                 g_assert(!g_list_find(focus_order[i], c));
690                 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
691                     focus_order[i] = g_list_insert(focus_order[i], c, 0);
692                 else
693                     focus_order[i] = g_list_insert(focus_order[i], c, 1);
694             }
695         } else {
696             g_assert(!g_list_find(focus_order[d], c));
697             if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
698                 focus_order[d] = g_list_insert(focus_order[d], c, 0);
699             else
700                 focus_order[d] = g_list_insert(focus_order[d], c, 1);
701         }
702     }
703 }
704
705 void focus_order_remove(ObClient *c)
706 {
707     guint d, i;
708
709     d = c->desktop;
710     if (d == DESKTOP_ALL) {
711         for (i = 0; i < screen_num_desktops; ++i)
712             focus_order[i] = g_list_remove(focus_order[i], c);
713     } else
714         focus_order[d] = g_list_remove(focus_order[d], c);
715 }
716
717 static void to_top(ObClient *c, guint d)
718 {
719     focus_order[d] = g_list_remove(focus_order[d], c);
720     if (!c->iconic) {
721         focus_order[d] = g_list_prepend(focus_order[d], c);
722     } else {
723         GList *it;
724
725         /* insert before first iconic window */
726         for (it = focus_order[d];
727              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
728         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
729     }
730 }
731
732 void focus_order_to_top(ObClient *c)
733 {
734     guint d, i;
735
736     d = c->desktop;
737     if (d == DESKTOP_ALL) {
738         for (i = 0; i < screen_num_desktops; ++i)
739             to_top(c, i);
740     } else
741         to_top(c, d);
742 }
743
744 static void to_bottom(ObClient *c, guint d)
745 {
746     focus_order[d] = g_list_remove(focus_order[d], c);
747     if (c->iconic) {
748         focus_order[d] = g_list_append(focus_order[d], c);
749     } else {
750         GList *it;
751
752         /* insert before first iconic window */
753         for (it = focus_order[d];
754              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
755         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
756     }
757 }
758
759 void focus_order_to_bottom(ObClient *c)
760 {
761     guint d, i;
762
763     d = c->desktop;
764     if (d == DESKTOP_ALL) {
765         for (i = 0; i < screen_num_desktops; ++i)
766             to_bottom(c, i);
767     } else
768         to_bottom(c, d);
769 }