]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
halfway through client changes but...
[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, 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 if (old->transient_for) {
184 gboolean trans = FALSE;
185
186 if (config_focus_last || !config_focus_follow)
187 trans = TRUE;
188 else {
189 ObClient *c;
190
191 if ((c = client_under_pointer()) &&
192 client_search_transient(client_search_top_transient(c),
193 old))
194 trans = TRUE;
195 }
196
197 g_message("trans %d", trans);
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_last && 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.060453 seconds and 5 git commands to generate.