]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
indenting
[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 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 }
This page took 0.064277 seconds and 4 git commands to generate.