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