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