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