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