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