]> icculus.org git repositories - dana/openbox.git/blob - openbox/focus.c
back out r5812. you do want that there
[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 = 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     ObClient *old = focus_client;
263
264     /* unfocus any focused clients.. they can be focused by Pointer events
265        and such, and then when I try focus them, I won't get a FocusIn event
266        at all for them.
267     */
268     focus_nothing();
269
270     if ((new = focus_fallback_target(allow_refocus, old)))
271         client_focus(new);
272 }
273
274 void focus_nothing()
275 {
276     /* Install our own colormap */
277     if (focus_client != NULL) {
278         screen_install_colormap(focus_client, FALSE);
279         screen_install_colormap(NULL, TRUE);
280     }
281
282     focus_client = NULL;
283
284     /* when nothing will be focused, send focus to the backup target */
285     XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
286                    event_curtime);
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         XEvent e;
332
333         XUnmapWindow(ob_display, focus_indicator.top.win);
334         XUnmapWindow(ob_display, focus_indicator.left.win);
335         XUnmapWindow(ob_display, focus_indicator.right.win);
336         XUnmapWindow(ob_display, focus_indicator.bottom.win);
337
338         /* kill enter events cause by this unmapping */
339         event_ignore_queued_enters();
340     } else {
341         /*
342           if (focus_cycle_target)
343               frame_adjust_focus(focus_cycle_target->frame, FALSE);
344           frame_adjust_focus(focus_cycle_target->frame, TRUE);
345         */
346         gint x, y, w, h;
347         gint wt, wl, wr, wb;
348
349         wt = wl = wr = wb = MAX(3,
350                                 MAX(1, MAX(ob_rr_theme->paddingx,
351                                            ob_rr_theme->paddingy)) * 2 +
352                                 ob_rr_theme->fbwidth * 2);
353
354         x = focus_cycle_target->frame->area.x;
355         y = focus_cycle_target->frame->area.y;
356         w = focus_cycle_target->frame->area.width;
357         h = wt;
358
359         XMoveResizeWindow(ob_display, focus_indicator.top.win,
360                           x, y, w, h);
361         a_focus_indicator->texture[0].data.lineart.x1 = 0;
362         a_focus_indicator->texture[0].data.lineart.y1 = h-1;
363         a_focus_indicator->texture[0].data.lineart.x2 = 0;
364         a_focus_indicator->texture[0].data.lineart.y2 = 0;
365         a_focus_indicator->texture[1].data.lineart.x1 = 0;
366         a_focus_indicator->texture[1].data.lineart.y1 = 0;
367         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
368         a_focus_indicator->texture[1].data.lineart.y2 = 0;
369         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
370         a_focus_indicator->texture[2].data.lineart.y1 = 0;
371         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
372         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
373         a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
374         a_focus_indicator->texture[3].data.lineart.y1 = h-1;
375         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
376         a_focus_indicator->texture[3].data.lineart.y2 = h-1;
377         RrPaint(a_focus_indicator, focus_indicator.top.win,
378                 w, h);
379
380         x = focus_cycle_target->frame->area.x;
381         y = focus_cycle_target->frame->area.y;
382         w = wl;
383         h = focus_cycle_target->frame->area.height;
384
385         XMoveResizeWindow(ob_display, focus_indicator.left.win,
386                           x, y, w, h);
387         a_focus_indicator->texture[0].data.lineart.x1 = w-1;
388         a_focus_indicator->texture[0].data.lineart.y1 = 0;
389         a_focus_indicator->texture[0].data.lineart.x2 = 0;
390         a_focus_indicator->texture[0].data.lineart.y2 = 0;
391         a_focus_indicator->texture[1].data.lineart.x1 = 0;
392         a_focus_indicator->texture[1].data.lineart.y1 = 0;
393         a_focus_indicator->texture[1].data.lineart.x2 = 0;
394         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
395         a_focus_indicator->texture[2].data.lineart.x1 = 0;
396         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
397         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
398         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
399         a_focus_indicator->texture[3].data.lineart.x1 = w-1;
400         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
401         a_focus_indicator->texture[3].data.lineart.x2 = w-1;
402         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
403         RrPaint(a_focus_indicator, focus_indicator.left.win,
404                 w, h);
405
406         x = focus_cycle_target->frame->area.x +
407             focus_cycle_target->frame->area.width - wr;
408         y = focus_cycle_target->frame->area.y;
409         w = wr;
410         h = focus_cycle_target->frame->area.height ;
411
412         XMoveResizeWindow(ob_display, focus_indicator.right.win,
413                           x, y, w, h);
414         a_focus_indicator->texture[0].data.lineart.x1 = 0;
415         a_focus_indicator->texture[0].data.lineart.y1 = 0;
416         a_focus_indicator->texture[0].data.lineart.x2 = w-1;
417         a_focus_indicator->texture[0].data.lineart.y2 = 0;
418         a_focus_indicator->texture[1].data.lineart.x1 = w-1;
419         a_focus_indicator->texture[1].data.lineart.y1 = 0;
420         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
421         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
422         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
423         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
424         a_focus_indicator->texture[2].data.lineart.x2 = 0;
425         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
426         a_focus_indicator->texture[3].data.lineart.x1 = 0;
427         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
428         a_focus_indicator->texture[3].data.lineart.x2 = 0;
429         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
430         RrPaint(a_focus_indicator, focus_indicator.right.win,
431                 w, h);
432
433         x = focus_cycle_target->frame->area.x;
434         y = focus_cycle_target->frame->area.y +
435             focus_cycle_target->frame->area.height - wb;
436         w = focus_cycle_target->frame->area.width;
437         h = wb;
438
439         XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
440                           x, y, w, h);
441         a_focus_indicator->texture[0].data.lineart.x1 = 0;
442         a_focus_indicator->texture[0].data.lineart.y1 = 0;
443         a_focus_indicator->texture[0].data.lineart.x2 = 0;
444         a_focus_indicator->texture[0].data.lineart.y2 = h-1;
445         a_focus_indicator->texture[1].data.lineart.x1 = 0;
446         a_focus_indicator->texture[1].data.lineart.y1 = h-1;
447         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
448         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
449         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
450         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
451         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
452         a_focus_indicator->texture[2].data.lineart.y2 = 0;
453         a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
454         a_focus_indicator->texture[3].data.lineart.y1 = 0;
455         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
456         a_focus_indicator->texture[3].data.lineart.y2 = 0;
457         RrPaint(a_focus_indicator, focus_indicator.bottom.win,
458                 w, h);
459
460         XMapWindow(ob_display, focus_indicator.top.win);
461         XMapWindow(ob_display, focus_indicator.left.win);
462         XMapWindow(ob_display, focus_indicator.right.win);
463         XMapWindow(ob_display, focus_indicator.bottom.win);
464     }
465 }
466
467 static gboolean valid_focus_target(ObClient *ft)
468 {
469     /* we don't use client_can_focus here, because that doesn't let you
470        focus an iconic window, but we want to be able to, so we just check
471        if the focus flags on the window allow it, and its on the current
472        desktop */
473     if ((ft->type == OB_CLIENT_TYPE_NORMAL ||
474          ft->type == OB_CLIENT_TYPE_DIALOG ||
475          (!client_has_group_siblings(ft) &&
476           (ft->type == OB_CLIENT_TYPE_TOOLBAR ||
477            ft->type == OB_CLIENT_TYPE_MENU ||
478            ft->type == OB_CLIENT_TYPE_UTILITY))) &&
479         ((ft->can_focus || ft->focus_notify) &&
480          !ft->skip_pager &&
481          (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)) &&
482         ft == client_focus_target(ft))
483         return TRUE;
484 /*
485     {
486         GSList *it;
487
488         for (it = ft->transients; it; it = g_slist_next(it)) {
489             ObClient *c = it->data;
490
491             if (c->frame->visible)
492                 return FALSE;
493         }
494         return TRUE;
495     }
496 */
497
498     return FALSE;
499 }
500
501 void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
502                  gboolean dialog, gboolean done, gboolean cancel)
503 {
504     static ObClient *first = NULL;
505     static ObClient *t = NULL;
506     static GList *order = NULL;
507     GList *it, *start, *list;
508     ObClient *ft = NULL;
509
510     if (interactive) {
511         if (cancel) {
512             focus_cycle_target = NULL;
513             goto done_cycle;
514         } else if (done)
515             goto done_cycle;
516
517         if (!focus_order)
518             goto done_cycle;
519
520         if (!first) first = focus_client;
521
522         if (linear) list = client_list;
523         else        list = focus_order;
524     } else {
525         if (!focus_order)
526             goto done_cycle;
527         list = client_list;
528     }
529     if (!focus_cycle_target) focus_cycle_target = focus_client;
530
531     start = it = g_list_find(list, focus_cycle_target);
532     if (!start) /* switched desktops or something? */
533         start = it = forward ? g_list_last(list) : g_list_first(list);
534     if (!start) goto done_cycle;
535
536     do {
537         if (forward) {
538             it = it->next;
539             if (it == NULL) it = g_list_first(list);
540         } else {
541             it = it->prev;
542             if (it == NULL) it = g_list_last(list);
543         }
544         ft = it->data;
545         if (valid_focus_target(ft)) {
546             if (interactive) {
547                 if (ft != focus_cycle_target) { /* prevents flicker */
548                     focus_cycle_target = ft;
549                     focus_cycle_draw_indicator();
550                 }
551                 popup_cycle(ft, dialog);
552                 return;
553             } else if (ft != focus_cycle_target) {
554                 focus_cycle_target = ft;
555                 done = TRUE;
556                 break;
557             }
558         }
559     } while (it != start);
560
561 done_cycle:
562     if (done && focus_cycle_target)
563         client_activate(focus_cycle_target, FALSE, TRUE);
564
565     t = NULL;
566     first = NULL;
567     focus_cycle_target = NULL;
568     g_list_free(order);
569     order = NULL;
570
571     if (interactive) {
572         focus_cycle_draw_indicator();
573         popup_cycle(ft, FALSE);
574     }
575
576     return;
577 }
578
579 void focus_directional_cycle(ObDirection dir, gboolean interactive,
580                              gboolean dialog, gboolean done, gboolean cancel)
581 {
582     static ObClient *first = NULL;
583     ObClient *ft = NULL;
584
585     if (!interactive)
586         return;
587
588     if (cancel) {
589         focus_cycle_target = NULL;
590         goto done_cycle;
591     } else if (done)
592         goto done_cycle;
593
594     if (!focus_order)
595         goto done_cycle;
596
597     if (!first) first = focus_client;
598     if (!focus_cycle_target) focus_cycle_target = focus_client;
599
600     if (focus_cycle_target)
601         ft = client_find_directional(focus_cycle_target, dir);
602     else {
603         GList *it;
604
605         for (it = focus_order; it; it = g_list_next(it))
606             if (valid_focus_target(it->data))
607                 ft = it->data;
608     }
609         
610     if (ft) {
611         if (ft != focus_cycle_target) {/* prevents flicker */
612             focus_cycle_target = ft;
613             focus_cycle_draw_indicator();
614         }
615     }
616     if (focus_cycle_target) {
617         popup_cycle(focus_cycle_target, dialog);
618         if (dialog)
619             return;
620     }
621
622
623 done_cycle:
624     if (done && focus_cycle_target)
625         client_activate(focus_cycle_target, FALSE, TRUE);
626
627     first = NULL;
628     focus_cycle_target = NULL;
629
630     focus_cycle_draw_indicator();
631     popup_cycle(ft, FALSE);
632
633     return;
634 }
635
636 void focus_order_add_new(ObClient *c)
637 {
638     if (c->iconic)
639         focus_order_to_top(c);
640     else {
641         g_assert(!g_list_find(focus_order, c));
642         /* if there are any iconic windows, put this above them in the order,
643            but if there are not, then put it under the currently focused one */
644         if (focus_order && ((ObClient*)focus_order->data)->iconic)
645             focus_order = g_list_insert(focus_order, c, 0);
646         else
647             focus_order = g_list_insert(focus_order, c, 1);
648     }
649 }
650
651 void focus_order_remove(ObClient *c)
652 {
653     focus_order = g_list_remove(focus_order, c);
654 }
655
656 void focus_order_to_top(ObClient *c)
657 {
658     focus_order = g_list_remove(focus_order, c);
659     if (!c->iconic) {
660         focus_order = g_list_prepend(focus_order, c);
661     } else {
662         GList *it;
663
664         /* insert before first iconic window */
665         for (it = focus_order;
666              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
667         focus_order = g_list_insert_before(focus_order, it, c);
668     }
669 }
670
671 void focus_order_to_bottom(ObClient *c)
672 {
673     focus_order = g_list_remove(focus_order, c);
674     if (c->iconic) {
675         focus_order = g_list_append(focus_order, c);
676     } else {
677         GList *it;
678
679         /* insert before first iconic window */
680         for (it = focus_order;
681              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
682         focus_order = g_list_insert_before(focus_order, it, c);
683     }
684 }
685
686 ObClient *focus_order_find_first(guint desktop)
687 {
688     GList *it;
689     for (it = focus_order; it; it = g_list_next(it)) {
690         ObClient *c = it->data;
691         if (c->desktop == desktop || c->desktop == DESKTOP_ALL)
692             return c;
693     }
694     return NULL;
695 }