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