]> icculus.org git repositories - dana/openbox.git/blob - openbox/focus.c
remove the dialog options
[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) 2003        Ben Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "debug.h"
20 #include "event.h"
21 #include "openbox.h"
22 #include "grab.h"
23 #include "framerender.h"
24 #include "client.h"
25 #include "config.h"
26 #include "frame.h"
27 #include "screen.h"
28 #include "group.h"
29 #include "prop.h"
30 #include "focus.h"
31 #include "stacking.h"
32 #include "popup.h"
33
34 #include <X11/Xlib.h>
35 #include <glib.h>
36 #include <assert.h>
37
38 ObClient *focus_client;
39 GList **focus_order; /* these lists are created when screen_startup
40                         sets the number of desktops */
41 ObClient *focus_cycle_target;
42
43 static ObIconPopup *focus_cycle_popup;
44
45 static void focus_cycle_destructor(ObClient *c)
46 {
47     /* end cycling if the target disappears */
48     if (focus_cycle_target == c)
49         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
50 }
51
52 void focus_startup(gboolean reconfig)
53 {
54     focus_cycle_popup = icon_popup_new(TRUE);
55
56     if (!reconfig) {
57         client_add_destructor((GDestroyNotify) focus_cycle_destructor);
58
59         /* start with nothing focused */
60         focus_set_client(NULL);
61     }
62 }
63
64 void focus_shutdown(gboolean reconfig)
65 {
66     guint i;
67
68     icon_popup_free(focus_cycle_popup);
69
70     if (!reconfig) {
71         client_remove_destructor((GDestroyNotify) focus_cycle_destructor);
72
73         for (i = 0; i < screen_num_desktops; ++i)
74             g_list_free(focus_order[i]);
75         g_free(focus_order);
76
77         /* reset focus to root */
78         XSetInputFocus(ob_display, PointerRoot, RevertToPointerRoot,
79                        event_lasttime);
80     }
81 }
82
83 static void push_to_top(ObClient *client)
84 {
85     guint desktop;
86
87     desktop = client->desktop;
88     if (desktop == DESKTOP_ALL) desktop = screen_desktop;
89     focus_order[desktop] = g_list_remove(focus_order[desktop], client);
90     focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
91 }
92
93 void focus_set_client(ObClient *client)
94 {
95     Window active;
96     ObClient *old;
97
98 #ifdef DEBUG_FOCUS
99     ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
100 #endif
101
102     /* uninstall the old colormap, and install the new one */
103     screen_install_colormap(focus_client, FALSE);
104     screen_install_colormap(client, TRUE);
105
106     if (client == NULL) {
107         /* when nothing will be focused, send focus to the backup target */
108         XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
109                        event_lasttime);
110         XSync(ob_display, FALSE);
111     }
112
113     /* in the middle of cycling..? kill it. */
114     if (focus_cycle_target)
115         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
116
117     old = focus_client;
118     focus_client = client;
119
120     /* move to the top of the list */
121     if (client != NULL)
122         push_to_top(client);
123
124     /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
125     if (ob_state() != OB_STATE_EXITING) {
126         active = client ? client->window : None;
127         PROP_SET32(RootWindow(ob_display, ob_screen),
128                    net_active_window, window, active);
129     }
130 }
131
132 static gboolean focus_under_pointer()
133 {
134     ObClient *c;
135
136     if ((c = client_under_pointer()))
137         return client_normal(c) && client_focus(c);
138     return FALSE;
139 }
140
141 /* finds the first transient that isn't 'skip' and ensure's that client_normal
142  is true for it */
143 static ObClient *find_transient_recursive(ObClient *c, ObClient *top, ObClient *skip)
144 {
145     GSList *it;
146     ObClient *ret;
147
148     for (it = c->transients; it; it = it->next) {
149         if (it->data == top) return NULL;
150         ret = find_transient_recursive(it->data, top, skip);
151         if (ret && ret != skip && client_normal(ret)) return ret;
152         if (it->data != skip && client_normal(it->data)) return it->data;
153     }
154     return NULL;
155 }
156
157 static gboolean focus_fallback_transient(ObClient *top, ObClient *old)
158 {
159     ObClient *target = find_transient_recursive(top, top, old);
160     if (!target) {
161         /* make sure client_normal is true always */
162         if (!client_normal(top))
163             return FALSE;
164         target = top; /* no transient, keep the top */
165     }
166     return client_focus(target);
167 }
168
169 void focus_fallback(ObFocusFallbackType type)
170 {
171     GList *it;
172     ObClient *old = NULL;
173
174     old = focus_client;
175
176     /* unfocus any focused clients.. they can be focused by Pointer events
177        and such, and then when I try focus them, I won't get a FocusIn event
178        at all for them.
179     */
180     focus_set_client(NULL);
181
182     if (type == OB_FOCUS_FALLBACK_UNFOCUSING && old) {
183         /* try for transient relations */
184         if (old->transient_for) {
185             if (old->transient_for == OB_TRAN_GROUP) {
186                 for (it = focus_order[screen_desktop]; it; it = it->next) {
187                     GSList *sit;
188
189                     for (sit = old->group->members; sit; sit = sit->next)
190                         if (sit->data == it->data)
191                             if (focus_fallback_transient(sit->data, old))
192                                 return;
193                 }
194             } else {
195                 if (focus_fallback_transient(old->transient_for, old))
196                     return;
197             }
198         }
199
200     if (!config_focus_last && config_focus_follow)
201         if (focus_under_pointer())
202             return;
203
204 #if 0
205         /* try for group relations */
206         if (old->group) {
207             GSList *sit;
208
209             for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
210                 for (sit = old->group->members; sit; sit = sit->next)
211                     if (sit->data == it->data)
212                         if (sit->data != old && client_normal(sit->data))
213                             if (client_can_focus(sit->data)) {
214                                 gboolean r = client_focus(sit->data);
215                                 assert(r);
216                                 return;
217                             }
218         }
219 #endif
220     }
221
222     for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
223         if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
224             if (client_normal(it->data) && client_can_focus(it->data)) {
225                 gboolean r = client_focus(it->data);
226                 assert(r);
227                 return;
228             }
229
230     /* nothing to focus, and already set it to none above */
231 }
232
233 static void popup_cycle(ObClient *c, gboolean show)
234 {
235     if (!show) {
236         icon_popup_hide(focus_cycle_popup);
237     } else {
238         Rect *a;
239         ObClient *p = c;
240         char *title;
241
242         a = screen_physical_area_monitor(0);
243         icon_popup_position(focus_cycle_popup, CenterGravity,
244                             a->x + a->width / 2, a->y + a->height / 2);
245 /*        icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
246         icon_popup_show(focus_cycle_popup, c->title,
247                         client_icon(c, a->height/16, a->height/16));
248 */
249         /* XXX the size and the font extents need to be related on some level
250          */
251         icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
252
253         /* use the transient's parent's title/icon */
254         while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
255             p = p->transient_for;
256
257         if (p == c)
258             title = NULL;
259         else
260             title = g_strconcat((c->iconic ? c->icon_title : c->title),
261                                 " - ",
262                                 (p->iconic ? p->icon_title : p->title),
263                                 NULL);
264
265         icon_popup_show(focus_cycle_popup,
266                         (title ? title :
267                          (c->iconic ? c->icon_title : c->title)),
268                         client_icon(p, 48, 48));
269         g_free(title);
270     }
271 }
272
273 static gboolean valid_focus_target(ObClient *ft)
274 {
275     /* we don't use client_can_focus here, because that doesn't let you
276        focus an iconic window, but we want to be able to, so we just check
277        if the focus flags on the window allow it, and its on the current
278        desktop */
279     return (ft == client_focus_target(ft) && client_normal(ft) &&
280             ((ft->can_focus || ft->focus_notify) &&
281              !ft->skip_taskbar &&
282              (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)));
283 }
284
285 void focus_cycle(gboolean forward, gboolean linear,
286                  gboolean dialog, gboolean done, gboolean cancel)
287 {
288     static ObClient *first = NULL;
289     static ObClient *t = NULL;
290     static GList *order = NULL;
291     GList *it, *start, *list;
292     ObClient *ft = NULL;
293
294     if (cancel) {
295         if (focus_cycle_target)
296             frame_adjust_focus(focus_cycle_target->frame, FALSE);
297         if (focus_client)
298             frame_adjust_focus(focus_client->frame, TRUE);
299         focus_cycle_target = NULL;
300         goto done_cycle;
301     } else if (done && dialog) {
302         goto done_cycle;
303     }
304
305     if (!focus_order[screen_desktop])
306         goto done_cycle;
307
308     if (!first) first = focus_client;
309     if (!focus_cycle_target) focus_cycle_target = focus_client;
310
311     if (linear) list = client_list;
312     else        list = focus_order[screen_desktop];
313
314     start = it = g_list_find(list, focus_cycle_target);
315     if (!start) /* switched desktops or something? */
316         start = it = forward ? g_list_last(list) : g_list_first(list);
317     if (!start) goto done_cycle;
318
319     do {
320         if (forward) {
321             it = it->next;
322             if (it == NULL) it = g_list_first(list);
323         } else {
324             it = it->prev;
325             if (it == NULL) it = g_list_last(list);
326         }
327         ft = it->data;
328         if (valid_focus_target(ft)) {
329             if (ft != focus_cycle_target) { /* prevents flicker */
330                 if (focus_cycle_target)
331                     frame_adjust_focus(focus_cycle_target->frame, FALSE);
332                 focus_cycle_target = ft;
333                 frame_adjust_focus(focus_cycle_target->frame, TRUE);
334             }
335             popup_cycle(ft, dialog);
336             return;
337         }
338     } while (it != start);
339
340 done_cycle:
341     if (done && focus_cycle_target)
342         client_activate(focus_cycle_target, FALSE);
343
344     t = NULL;
345     first = NULL;
346     focus_cycle_target = NULL;
347     g_list_free(order);
348     order = NULL;
349
350     popup_cycle(ft, FALSE);
351
352     return;
353 }
354
355 void focus_directional_cycle(ObDirection dir,
356                              gboolean dialog, gboolean done, gboolean cancel)
357 {
358     static ObClient *first = NULL;
359     ObClient *ft = NULL;
360
361     if (cancel) {
362         if (focus_cycle_target)
363             frame_adjust_focus(focus_cycle_target->frame, FALSE);
364         if (focus_client)
365             frame_adjust_focus(focus_client->frame, TRUE);
366         focus_cycle_target = NULL;
367         goto done_cycle;
368     } else if (done && dialog) {
369         goto done_cycle;
370     }
371
372     if (!focus_order[screen_desktop])
373         goto done_cycle;
374
375     if (!first) first = focus_client;
376     if (!focus_cycle_target) focus_cycle_target = focus_client;
377
378     if (focus_cycle_target)
379         ft = client_find_directional(focus_cycle_target, dir);
380     else {
381         GList *it;
382
383         for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
384             if (valid_focus_target(it->data))
385                 ft = it->data;
386     }
387         
388     if (ft) {
389         if (ft != focus_cycle_target) {/* prevents flicker */
390             if (focus_cycle_target)
391                 frame_adjust_focus(focus_cycle_target->frame, FALSE);
392             focus_cycle_target = ft;
393             frame_adjust_focus(focus_cycle_target->frame, TRUE);
394         }
395     }
396     if (focus_cycle_target) {
397         popup_cycle(focus_cycle_target, dialog);
398         if (dialog)
399             return;
400     }
401
402
403 done_cycle:
404     if (done && focus_cycle_target)
405         client_activate(focus_cycle_target, FALSE);
406
407     first = NULL;
408     focus_cycle_target = NULL;
409
410     popup_cycle(ft, FALSE);
411
412     return;
413 }
414
415 void focus_order_add_new(ObClient *c)
416 {
417     guint d, i;
418
419     if (c->iconic)
420         focus_order_to_top(c);
421     else {
422         d = c->desktop;
423         if (d == DESKTOP_ALL) {
424             for (i = 0; i < screen_num_desktops; ++i) {
425                 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
426                     focus_order[i] = g_list_insert(focus_order[i], c, 0);
427                 else
428                     focus_order[i] = g_list_insert(focus_order[i], c, 1);
429             }
430         } else
431              if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
432                 focus_order[d] = g_list_insert(focus_order[d], c, 0);
433             else
434                 focus_order[d] = g_list_insert(focus_order[d], c, 1);
435     }
436 }
437
438 void focus_order_remove(ObClient *c)
439 {
440     guint d, i;
441
442     d = c->desktop;
443     if (d == DESKTOP_ALL) {
444         for (i = 0; i < screen_num_desktops; ++i)
445             focus_order[i] = g_list_remove(focus_order[i], c);
446     } else
447         focus_order[d] = g_list_remove(focus_order[d], c);
448 }
449
450 static void to_top(ObClient *c, guint d)
451 {
452     focus_order[d] = g_list_remove(focus_order[d], c);
453     if (!c->iconic) {
454         focus_order[d] = g_list_prepend(focus_order[d], c);
455     } else {
456         GList *it;
457
458         /* insert before first iconic window */
459         for (it = focus_order[d];
460              it && !((ObClient*)it->data)->iconic; it = it->next);
461         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
462     }
463 }
464
465 void focus_order_to_top(ObClient *c)
466 {
467     guint d, i;
468
469     d = c->desktop;
470     if (d == DESKTOP_ALL) {
471         for (i = 0; i < screen_num_desktops; ++i)
472             to_top(c, i);
473     } else
474         to_top(c, d);
475 }
476
477 static void to_bottom(ObClient *c, guint d)
478 {
479     focus_order[d] = g_list_remove(focus_order[d], c);
480     if (c->iconic) {
481         focus_order[d] = g_list_append(focus_order[d], c);
482     } else {
483         GList *it;
484
485         /* insert before first iconic window */
486         for (it = focus_order[d];
487              it && !((ObClient*)it->data)->iconic; it = it->next);
488         g_list_insert_before(focus_order[d], it, c);
489     }
490 }
491
492 void focus_order_to_bottom(ObClient *c)
493 {
494     guint d, i;
495
496     d = c->desktop;
497     if (d == DESKTOP_ALL) {
498         for (i = 0; i < screen_num_desktops; ++i)
499             to_bottom(c, i);
500     } else
501         to_bottom(c, d);
502 }