]> icculus.org git repositories - dana/openbox.git/blob - openbox/focus.c
don't fall back focus to omnipresent windows
[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-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;
42 ObClient *focus_cycle_target;
43
44 struct {
45     InternalWindow top;
46     InternalWindow left;
47     InternalWindow right;
48     InternalWindow bottom;
49 } focus_indicator;
50
51 RrAppearance *a_focus_indicator;
52 RrColor *color_white;
53
54 static ObIconPopup *focus_cycle_popup;
55
56 static void focus_cycle_destructor(ObClient *client, gpointer data)
57 {
58     /* end cycling if the target disappears. CurrentTime is fine, time won't
59        be used
60     */
61     if (focus_cycle_target == client)
62         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, CurrentTime);
63 }
64
65 static Window createWindow(Window parent, gulong mask,
66                            XSetWindowAttributes *attrib)
67 {
68     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
69                          RrDepth(ob_rr_inst), InputOutput,
70                          RrVisual(ob_rr_inst), mask, attrib);
71                        
72 }
73
74 void focus_startup(gboolean reconfig)
75 {
76     focus_cycle_popup = icon_popup_new(TRUE);
77
78     if (!reconfig) {
79         XSetWindowAttributes attr;
80
81         client_add_destructor(focus_cycle_destructor, NULL);
82
83         /* start with nothing focused */
84         focus_set_client(NULL);
85
86         focus_indicator.top.obwin.type = Window_Internal;
87         focus_indicator.left.obwin.type = Window_Internal;
88         focus_indicator.right.obwin.type = Window_Internal;
89         focus_indicator.bottom.obwin.type = Window_Internal;
90
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, &attr);
96         focus_indicator.left.win =
97             createWindow(RootWindow(ob_display, ob_screen),
98                          CWOverrideRedirect | CWBackPixel, &attr);
99         focus_indicator.right.win =
100             createWindow(RootWindow(ob_display, ob_screen),
101                          CWOverrideRedirect | CWBackPixel, &attr);
102         focus_indicator.bottom.win =
103             createWindow(RootWindow(ob_display, ob_screen),
104                          CWOverrideRedirect | CWBackPixel, &attr);
105
106         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.top));
107         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left));
108         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right));
109         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom));
110
111         color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff);
112
113         a_focus_indicator = RrAppearanceNew(ob_rr_inst, 4);
114         a_focus_indicator->surface.grad = RR_SURFACE_SOLID;
115         a_focus_indicator->surface.relief = RR_RELIEF_FLAT;
116         a_focus_indicator->surface.primary = RrColorNew(ob_rr_inst,
117                                                         0, 0, 0);
118         a_focus_indicator->texture[0].type = RR_TEXTURE_LINE_ART;
119         a_focus_indicator->texture[0].data.lineart.color = color_white;
120         a_focus_indicator->texture[1].type = RR_TEXTURE_LINE_ART;
121         a_focus_indicator->texture[1].data.lineart.color = color_white;
122         a_focus_indicator->texture[2].type = RR_TEXTURE_LINE_ART;
123         a_focus_indicator->texture[2].data.lineart.color = color_white;
124         a_focus_indicator->texture[3].type = RR_TEXTURE_LINE_ART;
125         a_focus_indicator->texture[3].data.lineart.color = color_white;
126     }
127 }
128
129 void focus_shutdown(gboolean reconfig)
130 {
131     icon_popup_free(focus_cycle_popup);
132
133     if (!reconfig) {
134         client_remove_destructor(focus_cycle_destructor);
135
136         /* reset focus to root */
137         XSetInputFocus(ob_display, PointerRoot, RevertToNone, CurrentTime);
138
139         RrColorFree(color_white);
140
141         RrAppearanceFree(a_focus_indicator);
142
143         XDestroyWindow(ob_display, focus_indicator.top.win);
144         XDestroyWindow(ob_display, focus_indicator.left.win);
145         XDestroyWindow(ob_display, focus_indicator.right.win);
146         XDestroyWindow(ob_display, focus_indicator.bottom.win);
147     }
148 }
149
150 static void push_to_top(ObClient *client)
151 {
152     focus_order = g_list_remove(focus_order, client);
153     focus_order = g_list_prepend(focus_order, client);
154 }
155
156 void focus_set_client(ObClient *client)
157 {
158     Window active;
159     ObClient *old;
160
161 #ifdef DEBUG_FOCUS
162     ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
163 #endif
164
165     /* uninstall the old colormap, and install the new one */
166     screen_install_colormap(focus_client, FALSE);
167     screen_install_colormap(client, TRUE);
168
169     if (client == NULL) {
170 #ifdef DEBUG_FOCUS
171         ob_debug("actively focusing NONWINDOW\n");
172 #endif
173         /* when nothing will be focused, send focus to the backup target */
174         XSetInputFocus(ob_display, screen_support_win, RevertToNone,
175                        event_curtime);
176         XSync(ob_display, FALSE);
177     }
178
179     /* in the middle of cycling..? kill it. CurrentTime is fine, time won't
180        be used.
181     */
182     if (focus_cycle_target)
183         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, CurrentTime);
184
185     old = focus_client;
186     focus_client = client;
187
188     /* move to the top of the list */
189     if (client != NULL)
190         push_to_top(client);
191
192     /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
193     if (ob_state() != OB_STATE_EXITING) {
194         active = client ? client->window : None;
195         PROP_SET32(RootWindow(ob_display, ob_screen),
196                    net_active_window, window, active);
197
198         /* remove hiliting from the window when it gets focused */
199         if (client != NULL)
200             client_hilite(client, FALSE);
201     }
202 }
203
204 ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
205 {
206     GList *it;
207     ObClient *target = NULL;
208     ObClient *desktop = NULL;
209
210     ob_debug("trying pointer stuff\n");
211     if (config_focus_follow && !config_focus_last)
212     {
213         if ((target = client_under_pointer()))
214             if (allow_refocus || target != old)
215                 if (client_normal(target) && client_can_focus(target) &&
216                     client_validate(target)) {
217                     ob_debug("found in pointer stuff\n");
218                     return target;
219                 }
220     }
221
222 #if 0
223         /* try for group relations */
224         if (old->group) {
225             GSList *sit;
226
227             for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
228                 for (sit = old->group->members; sit; sit = g_slist_next(sit))
229                     if (sit->data == it->data)
230                         if (sit->data != old && client_normal(sit->data))
231                             if (client_can_focus(sit->data))
232                                 return sit->data;
233         }
234 #endif
235
236     ob_debug("trying  the focus order\n");
237     for (it = focus_order; it; it = g_list_next(it))
238         if (allow_refocus || it->data != old) {
239             ObClient *c = it->data;
240             /* fallback focus to a window if:
241                1. it is actually focusable, cuz if it's not then we're sending
242                focus off to nothing
243                2. it is validated. if the window is about to disappear, then
244                don't try focus it.
245                3. it is visible on the current desktop. this ignores
246                omnipresent windows, which are problematic in their own rite.
247                4. it's not iconic
248                5. it is a normal type window, don't fall back onto a dock or
249                a splashscreen or a desktop window (save the desktop as a
250                backup fallback though)
251             */
252             if (client_can_focus(c) && client_validate(c) &&
253                 c->desktop == screen_desktop && !c->iconic)
254             {
255                 if (client_normal(c)) {
256                     ob_debug("found in focus order\n");
257                     return it->data;
258                 } else if (c->type == OB_CLIENT_TYPE_DESKTOP && !desktop)
259                     desktop = c;
260             }
261         }
262
263     /* as a last resort fallback to the desktop window if there is one.
264        (if there's more than one, then the one last focused.)
265     */
266     return desktop;   
267 }
268
269 void focus_fallback(gboolean allow_refocus)
270 {
271     ObClient *new;
272     ObClient *old;
273
274     /* save this before moving focus away to nothing */
275     old = focus_client;
276
277     /* unfocus any focused clients.. they can be focused by Pointer events
278        and such, and then when I try focus them, I won't get a FocusIn event
279        at all for them.
280     */
281     focus_set_client(NULL);
282
283     if ((new = focus_fallback_target(allow_refocus, old)))
284         client_focus(new);
285 }
286
287 static void popup_cycle(ObClient *c, gboolean show)
288 {
289     if (!show) {
290         icon_popup_hide(focus_cycle_popup);
291     } else {
292         Rect *a;
293         ObClient *p = c;
294         gchar *title = NULL;
295
296         a = screen_physical_area_monitor(0);
297         icon_popup_position(focus_cycle_popup, CenterGravity,
298                             a->x + a->width / 2, a->y + a->height / 2);
299 /*        icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
300         icon_popup_show(focus_cycle_popup, c->title,
301                         client_icon(c, a->height/16, a->height/16));
302 */
303         /* XXX the size and the font extents need to be related on some level
304          */
305         icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
306
307         /* use the transient's parent's title/icon */
308         while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
309             p = p->transient_for;
310
311         if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
312             title = g_strdup(p->iconic ? p->icon_title : p->title);
313             /*title = g_strconcat((c->iconic ? c->icon_title : c->title),
314                                 " - ",
315                                 (p->iconic ? p->icon_title : p->title),
316                                 NULL);
317             */
318         icon_popup_show(focus_cycle_popup,
319                         (title ? title :
320                          (c->iconic ? c->icon_title : c->title)),
321                         client_icon(p, 48, 48));
322         g_free(title);
323     }
324 }
325
326 void focus_cycle_draw_indicator()
327 {
328     if (!focus_cycle_target) {
329         XUnmapWindow(ob_display, focus_indicator.top.win);
330         XUnmapWindow(ob_display, focus_indicator.left.win);
331         XUnmapWindow(ob_display, focus_indicator.right.win);
332         XUnmapWindow(ob_display, focus_indicator.bottom.win);
333     } else {
334         /*
335           if (focus_cycle_target)
336               frame_adjust_focus(focus_cycle_target->frame, FALSE);
337           frame_adjust_focus(focus_cycle_target->frame, TRUE);
338         */
339         gint x, y, w, h;
340         gint wt, wl, wr, wb;
341
342         wt = wl = wr = wb = MAX(3,
343                                 MAX(1, MAX(ob_rr_theme->paddingx,
344                                            ob_rr_theme->paddingy)) * 2 +
345                                 ob_rr_theme->fbwidth * 2);
346
347         x = focus_cycle_target->frame->area.x;
348         y = focus_cycle_target->frame->area.y;
349         w = focus_cycle_target->frame->area.width;
350         h = wt;
351
352         XMoveResizeWindow(ob_display, focus_indicator.top.win,
353                           x, y, w, h);
354         a_focus_indicator->texture[0].data.lineart.x1 = 0;
355         a_focus_indicator->texture[0].data.lineart.y1 = h-1;
356         a_focus_indicator->texture[0].data.lineart.x2 = 0;
357         a_focus_indicator->texture[0].data.lineart.y2 = 0;
358         a_focus_indicator->texture[1].data.lineart.x1 = 0;
359         a_focus_indicator->texture[1].data.lineart.y1 = 0;
360         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
361         a_focus_indicator->texture[1].data.lineart.y2 = 0;
362         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
363         a_focus_indicator->texture[2].data.lineart.y1 = 0;
364         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
365         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
366         a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
367         a_focus_indicator->texture[3].data.lineart.y1 = h-1;
368         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
369         a_focus_indicator->texture[3].data.lineart.y2 = h-1;
370         RrPaint(a_focus_indicator, focus_indicator.top.win,
371                 w, h);
372
373         x = focus_cycle_target->frame->area.x;
374         y = focus_cycle_target->frame->area.y;
375         w = wl;
376         h = focus_cycle_target->frame->area.height;
377
378         XMoveResizeWindow(ob_display, focus_indicator.left.win,
379                           x, y, w, h);
380         a_focus_indicator->texture[0].data.lineart.x1 = w-1;
381         a_focus_indicator->texture[0].data.lineart.y1 = 0;
382         a_focus_indicator->texture[0].data.lineart.x2 = 0;
383         a_focus_indicator->texture[0].data.lineart.y2 = 0;
384         a_focus_indicator->texture[1].data.lineart.x1 = 0;
385         a_focus_indicator->texture[1].data.lineart.y1 = 0;
386         a_focus_indicator->texture[1].data.lineart.x2 = 0;
387         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
388         a_focus_indicator->texture[2].data.lineart.x1 = 0;
389         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
390         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
391         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
392         a_focus_indicator->texture[3].data.lineart.x1 = w-1;
393         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
394         a_focus_indicator->texture[3].data.lineart.x2 = w-1;
395         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
396         RrPaint(a_focus_indicator, focus_indicator.left.win,
397                 w, h);
398
399         x = focus_cycle_target->frame->area.x +
400             focus_cycle_target->frame->area.width - wr;
401         y = focus_cycle_target->frame->area.y;
402         w = wr;
403         h = focus_cycle_target->frame->area.height ;
404
405         XMoveResizeWindow(ob_display, focus_indicator.right.win,
406                           x, y, w, h);
407         a_focus_indicator->texture[0].data.lineart.x1 = 0;
408         a_focus_indicator->texture[0].data.lineart.y1 = 0;
409         a_focus_indicator->texture[0].data.lineart.x2 = w-1;
410         a_focus_indicator->texture[0].data.lineart.y2 = 0;
411         a_focus_indicator->texture[1].data.lineart.x1 = w-1;
412         a_focus_indicator->texture[1].data.lineart.y1 = 0;
413         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
414         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
415         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
416         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
417         a_focus_indicator->texture[2].data.lineart.x2 = 0;
418         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
419         a_focus_indicator->texture[3].data.lineart.x1 = 0;
420         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
421         a_focus_indicator->texture[3].data.lineart.x2 = 0;
422         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
423         RrPaint(a_focus_indicator, focus_indicator.right.win,
424                 w, h);
425
426         x = focus_cycle_target->frame->area.x;
427         y = focus_cycle_target->frame->area.y +
428             focus_cycle_target->frame->area.height - wb;
429         w = focus_cycle_target->frame->area.width;
430         h = wb;
431
432         XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
433                           x, y, w, h);
434         a_focus_indicator->texture[0].data.lineart.x1 = 0;
435         a_focus_indicator->texture[0].data.lineart.y1 = 0;
436         a_focus_indicator->texture[0].data.lineart.x2 = 0;
437         a_focus_indicator->texture[0].data.lineart.y2 = h-1;
438         a_focus_indicator->texture[1].data.lineart.x1 = 0;
439         a_focus_indicator->texture[1].data.lineart.y1 = h-1;
440         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
441         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
442         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
443         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
444         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
445         a_focus_indicator->texture[2].data.lineart.y2 = 0;
446         a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
447         a_focus_indicator->texture[3].data.lineart.y1 = 0;
448         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
449         a_focus_indicator->texture[3].data.lineart.y2 = 0;
450         RrPaint(a_focus_indicator, focus_indicator.bottom.win,
451                 w, h);
452
453         XMapWindow(ob_display, focus_indicator.top.win);
454         XMapWindow(ob_display, focus_indicator.left.win);
455         XMapWindow(ob_display, focus_indicator.right.win);
456         XMapWindow(ob_display, focus_indicator.bottom.win);
457     }
458 }
459
460 static gboolean valid_focus_target(ObClient *ft)
461 {
462     /* we don't use client_can_focus here, because that doesn't let you
463        focus an iconic window, but we want to be able to, so we just check
464        if the focus flags on the window allow it, and its on the current
465        desktop */
466     if ((ft->type == OB_CLIENT_TYPE_NORMAL ||
467          ft->type == OB_CLIENT_TYPE_DIALOG ||
468          (!client_has_group_siblings(ft) &&
469           (ft->type == OB_CLIENT_TYPE_TOOLBAR ||
470            ft->type == OB_CLIENT_TYPE_MENU ||
471            ft->type == OB_CLIENT_TYPE_UTILITY))) &&
472         ((ft->can_focus || ft->focus_notify) &&
473          !ft->skip_pager &&
474          (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)) &&
475         ft == client_focus_target(ft))
476         return TRUE;
477 /*
478     {
479         GSList *it;
480
481         for (it = ft->transients; it; it = g_slist_next(it)) {
482             ObClient *c = it->data;
483
484             if (c->frame->visible)
485                 return FALSE;
486         }
487         return TRUE;
488     }
489 */
490
491     return FALSE;
492 }
493
494 void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
495                  gboolean dialog, gboolean done, gboolean cancel, Time time)
496 {
497     static ObClient *first = NULL;
498     static ObClient *t = NULL;
499     static GList *order = NULL;
500     GList *it, *start, *list;
501     ObClient *ft = NULL;
502
503     if (interactive) {
504         if (cancel) {
505             focus_cycle_target = NULL;
506             goto done_cycle;
507         } else if (done)
508             goto done_cycle;
509
510         if (!focus_order)
511             goto done_cycle;
512
513         if (!first) first = focus_client;
514
515         if (linear) list = client_list;
516         else        list = focus_order;
517     } else {
518         if (!focus_order)
519             goto done_cycle;
520         list = client_list;
521     }
522     if (!focus_cycle_target) focus_cycle_target = focus_client;
523
524     start = it = g_list_find(list, focus_cycle_target);
525     if (!start) /* switched desktops or something? */
526         start = it = forward ? g_list_last(list) : g_list_first(list);
527     if (!start) goto done_cycle;
528
529     do {
530         if (forward) {
531             it = it->next;
532             if (it == NULL) it = g_list_first(list);
533         } else {
534             it = it->prev;
535             if (it == NULL) it = g_list_last(list);
536         }
537         ft = it->data;
538         if (valid_focus_target(ft)) {
539             if (interactive) {
540                 if (ft != focus_cycle_target) { /* prevents flicker */
541                     focus_cycle_target = ft;
542                     focus_cycle_draw_indicator();
543                 }
544                 popup_cycle(ft, dialog);
545                 return;
546             } else if (ft != focus_cycle_target) {
547                 focus_cycle_target = ft;
548                 done = TRUE;
549                 break;
550             }
551         }
552     } while (it != start);
553
554 done_cycle:
555     if (done && focus_cycle_target)
556         client_activate(focus_cycle_target, FALSE, TRUE, time);
557
558     t = NULL;
559     first = NULL;
560     focus_cycle_target = NULL;
561     g_list_free(order);
562     order = NULL;
563
564     if (interactive) {
565         focus_cycle_draw_indicator();
566         popup_cycle(ft, FALSE);
567     }
568
569     return;
570 }
571
572 void focus_directional_cycle(ObDirection dir, gboolean interactive,
573                              gboolean dialog, gboolean done, gboolean cancel,
574                              Time time)
575 {
576     static ObClient *first = NULL;
577     ObClient *ft = NULL;
578
579     if (!interactive)
580         return;
581
582     if (cancel) {
583         focus_cycle_target = NULL;
584         goto done_cycle;
585     } else if (done)
586         goto done_cycle;
587
588     if (!focus_order)
589         goto done_cycle;
590
591     if (!first) first = focus_client;
592     if (!focus_cycle_target) focus_cycle_target = focus_client;
593
594     if (focus_cycle_target)
595         ft = client_find_directional(focus_cycle_target, dir);
596     else {
597         GList *it;
598
599         for (it = focus_order; it; it = g_list_next(it))
600             if (valid_focus_target(it->data))
601                 ft = it->data;
602     }
603         
604     if (ft) {
605         if (ft != focus_cycle_target) {/* prevents flicker */
606             focus_cycle_target = ft;
607             focus_cycle_draw_indicator();
608         }
609     }
610     if (focus_cycle_target) {
611         popup_cycle(focus_cycle_target, dialog);
612         if (dialog)
613             return;
614     }
615
616
617 done_cycle:
618     if (done && focus_cycle_target)
619         client_activate(focus_cycle_target, FALSE, TRUE, time);
620
621     first = NULL;
622     focus_cycle_target = NULL;
623
624     focus_cycle_draw_indicator();
625     popup_cycle(ft, FALSE);
626
627     return;
628 }
629
630 void focus_order_add_new(ObClient *c)
631 {
632     if (c->iconic)
633         focus_order_to_top(c);
634     else {
635         g_assert(!g_list_find(focus_order, c));
636         /* if there are any iconic windows, put this above them in the order,
637            but if there are not, then put it under the currently focused one */
638         if (focus_order && ((ObClient*)focus_order->data)->iconic)
639             focus_order = g_list_insert(focus_order, c, 0);
640         else
641             focus_order = g_list_insert(focus_order, c, 1);
642     }
643 }
644
645 void focus_order_remove(ObClient *c)
646 {
647     focus_order = g_list_remove(focus_order, c);
648 }
649
650 void focus_order_to_top(ObClient *c)
651 {
652     focus_order = g_list_remove(focus_order, c);
653     if (!c->iconic) {
654         focus_order = g_list_prepend(focus_order, c);
655     } else {
656         GList *it;
657
658         /* insert before first iconic window */
659         for (it = focus_order;
660              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
661         focus_order = g_list_insert_before(focus_order, it, c);
662     }
663 }
664
665 void focus_order_to_bottom(ObClient *c)
666 {
667     focus_order = g_list_remove(focus_order, c);
668     if (c->iconic) {
669         focus_order = g_list_append(focus_order, c);
670     } else {
671         GList *it;
672
673         /* insert before first iconic window */
674         for (it = focus_order;
675              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
676         focus_order = g_list_insert_before(focus_order, it, c);
677     }
678 }
679
680 ObClient *focus_order_find_first(guint desktop)
681 {
682     GList *it;
683     for (it = focus_order; it; it = g_list_next(it)) {
684         ObClient *c = it->data;
685         if (c->desktop == desktop || c->desktop == DESKTOP_ALL)
686             return c;
687     }
688     return NULL;
689 }