]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
missing a header
[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) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "event.h"
22 #include "openbox.h"
23 #include "grab.h"
24 #include "framerender.h"
25 #include "client.h"
26 #include "config.h"
27 #include "frame.h"
28 #include "screen.h"
29 #include "group.h"
30 #include "prop.h"
31 #include "keyboard.h"
32 #include "focus.h"
33 #include "stacking.h"
34 #include "popup.h"
35 #include "render/render.h"
36
37 #include <X11/Xlib.h>
38 #include <glib.h>
39 #include <assert.h>
40
41 #define FOCUS_INDICATOR_WIDTH 6
42
43 ObClient *focus_client = NULL;
44 GList *focus_order = NULL;
45 ObClient *focus_cycle_target = NULL;
46
47 /*! This variable is used for focus fallback. If we fallback to a window, we
48 set this to the window. And when focus goes somewhere after that, it will
49 be set to NULL. If between falling back to that window and something
50 getting focused, the window gets unmanaged, then if there are no incoming
51 FocusIn events, we fallback again because focus has just gotten itself lost.
52 */
53 static ObClient *focus_tried = NULL;
54
55 struct {
56 InternalWindow top;
57 InternalWindow left;
58 InternalWindow right;
59 InternalWindow bottom;
60 } focus_indicator;
61
62 RrAppearance *a_focus_indicator;
63 RrColor *color_white;
64
65 static ObIconPopup *focus_cycle_popup;
66
67 static gboolean valid_focus_target(ObClient *ft,
68 gboolean all_desktops,
69 gboolean dock_windows,
70 gboolean desktop_windows);
71 static void focus_cycle_destroy_notify(ObClient *client, gpointer data);
72 static void focus_tried_hide_notify(ObClient *client, gpointer data);
73
74 static Window createWindow(Window parent, gulong mask,
75 XSetWindowAttributes *attrib)
76 {
77 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
78 RrDepth(ob_rr_inst), InputOutput,
79 RrVisual(ob_rr_inst), mask, attrib);
80
81 }
82
83 void focus_startup(gboolean reconfig)
84 {
85 focus_cycle_popup = icon_popup_new(TRUE);
86
87 if (!reconfig) {
88 XSetWindowAttributes attr;
89
90 client_add_destroy_notify(focus_cycle_destroy_notify, NULL);
91 client_add_destroy_notify(focus_tried_hide_notify, NULL);
92 client_add_hide_notify(focus_tried_hide_notify, NULL);
93
94 /* start with nothing focused */
95 focus_nothing();
96
97 focus_indicator.top.obwin.type = Window_Internal;
98 focus_indicator.left.obwin.type = Window_Internal;
99 focus_indicator.right.obwin.type = Window_Internal;
100 focus_indicator.bottom.obwin.type = Window_Internal;
101
102 attr.override_redirect = True;
103 attr.background_pixel = BlackPixel(ob_display, ob_screen);
104 focus_indicator.top.win =
105 createWindow(RootWindow(ob_display, ob_screen),
106 CWOverrideRedirect | CWBackPixel, &attr);
107 focus_indicator.left.win =
108 createWindow(RootWindow(ob_display, ob_screen),
109 CWOverrideRedirect | CWBackPixel, &attr);
110 focus_indicator.right.win =
111 createWindow(RootWindow(ob_display, ob_screen),
112 CWOverrideRedirect | CWBackPixel, &attr);
113 focus_indicator.bottom.win =
114 createWindow(RootWindow(ob_display, ob_screen),
115 CWOverrideRedirect | CWBackPixel, &attr);
116
117 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.top));
118 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left));
119 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right));
120 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom));
121
122 color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff);
123
124 a_focus_indicator = RrAppearanceNew(ob_rr_inst, 4);
125 a_focus_indicator->surface.grad = RR_SURFACE_SOLID;
126 a_focus_indicator->surface.relief = RR_RELIEF_FLAT;
127 a_focus_indicator->surface.primary = RrColorNew(ob_rr_inst,
128 0, 0, 0);
129 a_focus_indicator->texture[0].type = RR_TEXTURE_LINE_ART;
130 a_focus_indicator->texture[0].data.lineart.color = color_white;
131 a_focus_indicator->texture[1].type = RR_TEXTURE_LINE_ART;
132 a_focus_indicator->texture[1].data.lineart.color = color_white;
133 a_focus_indicator->texture[2].type = RR_TEXTURE_LINE_ART;
134 a_focus_indicator->texture[2].data.lineart.color = color_white;
135 a_focus_indicator->texture[3].type = RR_TEXTURE_LINE_ART;
136 a_focus_indicator->texture[3].data.lineart.color = color_white;
137 }
138 }
139
140 void focus_shutdown(gboolean reconfig)
141 {
142 icon_popup_free(focus_cycle_popup);
143
144 if (!reconfig) {
145 client_remove_destroy_notify(focus_cycle_destroy_notify);
146 client_remove_destroy_notify(focus_tried_hide_notify);
147 client_remove_hide_notify(focus_tried_hide_notify);
148
149 /* reset focus to root */
150 XSetInputFocus(ob_display, PointerRoot, RevertToNone, CurrentTime);
151
152 RrColorFree(color_white);
153
154 RrAppearanceFree(a_focus_indicator);
155
156 XDestroyWindow(ob_display, focus_indicator.top.win);
157 XDestroyWindow(ob_display, focus_indicator.left.win);
158 XDestroyWindow(ob_display, focus_indicator.right.win);
159 XDestroyWindow(ob_display, focus_indicator.bottom.win);
160 }
161 }
162
163 static void push_to_top(ObClient *client)
164 {
165 focus_order = g_list_remove(focus_order, client);
166 focus_order = g_list_prepend(focus_order, client);
167 }
168
169 void focus_set_client(ObClient *client)
170 {
171 Window active;
172
173 ob_debug_type(OB_DEBUG_FOCUS,
174 "focus_set_client 0x%lx\n", client ? client->window : 0);
175
176 /* uninstall the old colormap, and install the new one */
177 screen_install_colormap(focus_client, FALSE);
178 screen_install_colormap(client, TRUE);
179
180 /* in the middle of cycling..? kill it. CurrentTime is fine, time won't
181 be used.
182 */
183 if (focus_cycle_target)
184 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
185
186 focus_client = client;
187
188 if (client != NULL) {
189 /* move to the top of the list */
190 push_to_top(client);
191 /* remove hiliting from the window when it gets focused */
192 client_hilite(client, FALSE);
193 }
194
195 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
196 if (ob_state() != OB_STATE_EXITING) {
197 active = client ? client->window : None;
198 PROP_SET32(RootWindow(ob_display, ob_screen),
199 net_active_window, window, active);
200 }
201
202
203 focus_tried = NULL; /* focus isn't "trying" to go anywhere now */
204 }
205
206 static ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
207 {
208 GList *it;
209 ObClient *target = NULL;
210 ObClient *desktop = NULL;
211
212 ob_debug_type(OB_DEBUG_FOCUS, "trying pointer stuff\n");
213 if (config_focus_follow && !config_focus_last)
214 {
215 if ((target = client_under_pointer()))
216 if (allow_refocus || target != old)
217 if (client_normal(target) && client_can_focus(target)) {
218 ob_debug_type(OB_DEBUG_FOCUS, "found in pointer stuff\n");
219 return target;
220 }
221 }
222
223 #if 0
224 /* try for group relations */
225 if (old->group) {
226 GSList *sit;
227
228 for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
229 for (sit = old->group->members; sit; sit = g_slist_next(sit))
230 if (sit->data == it->data)
231 if (sit->data != old && client_normal(sit->data))
232 if (client_can_focus(sit->data))
233 return sit->data;
234 }
235 #endif
236
237 ob_debug_type(OB_DEBUG_FOCUS, "trying omnipresentness\n");
238 if (allow_refocus && old && old->desktop == DESKTOP_ALL &&
239 client_normal(old))
240 {
241 return old;
242 }
243
244
245 ob_debug_type(OB_DEBUG_FOCUS, "trying the focus order\n");
246 for (it = focus_order; it; it = g_list_next(it))
247 if (allow_refocus || it->data != old) {
248 ObClient *c = it->data;
249 /* fallback focus to a window if:
250 1. it is actually focusable, cuz if it's not then we're sending
251 focus off to nothing. this includes if it is visible right now
252 2. it is on the current desktop. this ignores omnipresent
253 windows, which are problematic in their own rite.
254 3. it is a normal type window, don't fall back onto a dock or
255 a splashscreen or a desktop window (save the desktop as a
256 backup fallback though)
257 */
258 if (client_can_focus(c))
259 {
260 if (c->desktop == screen_desktop && client_normal(c)) {
261 ob_debug_type(OB_DEBUG_FOCUS, "found in focus order\n");
262 return it->data;
263 } else if (c->type == OB_CLIENT_TYPE_DESKTOP &&
264 desktop == NULL)
265 desktop = c;
266 }
267 }
268
269 /* as a last resort fallback to the desktop window if there is one.
270 (if there's more than one, then the one most recently focused.)
271 */
272 ob_debug_type(OB_DEBUG_FOCUS, "found desktop: \n", !!desktop);
273 return desktop;
274 }
275
276 ObClient* focus_fallback(gboolean allow_refocus)
277 {
278 ObClient *new;
279 ObClient *old;
280
281 old = focus_client;
282 new = focus_fallback_target(allow_refocus, focus_client);
283
284 /* unfocus any focused clients.. they can be focused by Pointer events
285 and such, and then when we try focus them, we won't get a FocusIn
286 event at all for them. */
287 focus_nothing();
288
289 if (new) {
290 client_focus(new);
291 /* remember that we tried to send focus here */
292 focus_tried = new;
293 }
294
295 return new;
296 }
297
298 void focus_nothing()
299 {
300 /* Install our own colormap */
301 if (focus_client != NULL) {
302 screen_install_colormap(focus_client, FALSE);
303 screen_install_colormap(NULL, TRUE);
304 }
305
306 /* Don't set focus_client to NULL here. It will be set to NULL when the
307 FocusOut event comes. Otherwise, if we focus nothing and then focus the
308 same window again, The focus code says nothing changed, but focus_client
309 ends up being NULL anyways.
310 focus_client = NULL;
311 */
312
313 focus_tried = NULL; /* focus isn't "trying" to go anywhere now */
314
315 /* if there is a grab going on, then we need to cancel it. if we move
316 focus during the grab, applications will get NotifyWhileGrabbed events
317 and ignore them !
318
319 actions should not rely on being able to move focus during an
320 interactive grab.
321 */
322 if (keyboard_interactively_grabbed())
323 keyboard_interactive_cancel();
324
325 /* when nothing will be focused, send focus to the backup target */
326 XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
327 event_curtime);
328 }
329
330 static gchar *popup_get_name(ObClient *c, ObClient **nametarget)
331 {
332 ObClient *p;
333 gchar *title = NULL;
334 const gchar *desk = NULL;
335 gchar *ret;
336
337 /* find our highest direct parent, including non-normal windows */
338 for (p = c; p->transient_for && p->transient_for != OB_TRAN_GROUP;
339 p = p->transient_for);
340
341 if (c->desktop != DESKTOP_ALL && c->desktop != screen_desktop)
342 desk = screen_desktop_names[c->desktop];
343
344 /* use the transient's parent's title/icon if we don't have one */
345 if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
346 title = g_strdup(p->iconic ? p->icon_title : p->title);
347
348 if (title == NULL)
349 title = g_strdup(c->iconic ? c->icon_title : c->title);
350
351 if (desk)
352 ret = g_strdup_printf("%s [%s]", title, desk);
353 else {
354 ret = title;
355 title = NULL;
356 }
357 g_free(title);
358
359 /* set this only if we're returning true and they asked for it */
360 if (ret && nametarget) *nametarget = p;
361 return ret;
362 }
363
364 static void popup_cycle(ObClient *c, gboolean show,
365 gboolean all_desktops, gboolean dock_windows,
366 gboolean desktop_windows)
367 {
368 gchar *showtext = NULL;
369 ObClient *showtarget;
370
371 if (!show) {
372 icon_popup_hide(focus_cycle_popup);
373 return;
374 }
375
376 /* do this stuff only when the dialog is first showing */
377 if (!focus_cycle_popup->popup->mapped &&
378 !focus_cycle_popup->popup->delay_mapped)
379 {
380 Rect *a;
381 gchar **names;
382 GList *targets = NULL, *it;
383 gint n = 0, i;
384
385 /* position the popup */
386 a = screen_physical_area_monitor(0);
387 icon_popup_position(focus_cycle_popup, CenterGravity,
388 a->x + a->width / 2, a->y + a->height / 2);
389 icon_popup_height(focus_cycle_popup, POPUP_HEIGHT);
390 icon_popup_min_width(focus_cycle_popup, POPUP_WIDTH);
391 icon_popup_max_width(focus_cycle_popup,
392 MAX(a->width/3, POPUP_WIDTH));
393
394
395 /* make its width to be the width of all the possible titles */
396
397 /* build a list of all the valid focus targets */
398 for (it = focus_order; it; it = g_list_next(it)) {
399 ObClient *ft = it->data;
400 if (valid_focus_target(ft, all_desktops, dock_windows
401 , desktop_windows))
402 {
403 targets = g_list_prepend(targets, ft);
404 ++n;
405 }
406 }
407 /* make it null terminated so we can use g_strfreev */
408 names = g_new(char*, n+1);
409 for (it = targets, i = 0; it; it = g_list_next(it), ++i) {
410 ObClient *ft = it->data, *t;
411 names[i] = popup_get_name(ft, &t);
412
413 /* little optimization.. save this text and client, so we dont
414 have to get it again */
415 if (ft == c) {
416 showtext = g_strdup(names[i]);
417 showtarget = t;
418 }
419 }
420 names[n] = NULL;
421
422 icon_popup_text_width_to_strings(focus_cycle_popup, names, n);
423 g_strfreev(names);
424 }
425
426
427 if (!showtext) showtext = popup_get_name(c, &showtarget);
428 icon_popup_show(focus_cycle_popup, showtext,
429 client_icon(showtarget, 48, 48));
430 g_free(showtext);
431 }
432
433 static void focus_cycle_destroy_notify(ObClient *client, gpointer data)
434 {
435 /* end cycling if the target disappears. CurrentTime is fine, time won't
436 be used
437 */
438 if (focus_cycle_target == client)
439 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
440 }
441
442 void focus_cycle_draw_indicator()
443 {
444 if (!focus_cycle_target) {
445 XUnmapWindow(ob_display, focus_indicator.top.win);
446 XUnmapWindow(ob_display, focus_indicator.left.win);
447 XUnmapWindow(ob_display, focus_indicator.right.win);
448 XUnmapWindow(ob_display, focus_indicator.bottom.win);
449
450 /* kill enter events cause by this unmapping */
451 event_ignore_queued_enters();
452 } else {
453 /*
454 if (focus_cycle_target)
455 frame_adjust_focus(focus_cycle_target->frame, FALSE);
456 frame_adjust_focus(focus_cycle_target->frame, TRUE);
457 */
458 gint x, y, w, h;
459 gint wt, wl, wr, wb;
460
461 wt = wl = wr = wb = FOCUS_INDICATOR_WIDTH;
462
463 x = focus_cycle_target->frame->area.x;
464 y = focus_cycle_target->frame->area.y;
465 w = focus_cycle_target->frame->area.width;
466 h = wt;
467
468 XMoveResizeWindow(ob_display, focus_indicator.top.win,
469 x, y, w, h);
470 a_focus_indicator->texture[0].data.lineart.x1 = 0;
471 a_focus_indicator->texture[0].data.lineart.y1 = h-1;
472 a_focus_indicator->texture[0].data.lineart.x2 = 0;
473 a_focus_indicator->texture[0].data.lineart.y2 = 0;
474 a_focus_indicator->texture[1].data.lineart.x1 = 0;
475 a_focus_indicator->texture[1].data.lineart.y1 = 0;
476 a_focus_indicator->texture[1].data.lineart.x2 = w-1;
477 a_focus_indicator->texture[1].data.lineart.y2 = 0;
478 a_focus_indicator->texture[2].data.lineart.x1 = w-1;
479 a_focus_indicator->texture[2].data.lineart.y1 = 0;
480 a_focus_indicator->texture[2].data.lineart.x2 = w-1;
481 a_focus_indicator->texture[2].data.lineart.y2 = h-1;
482 a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
483 a_focus_indicator->texture[3].data.lineart.y1 = h-1;
484 a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
485 a_focus_indicator->texture[3].data.lineart.y2 = h-1;
486 RrPaint(a_focus_indicator, focus_indicator.top.win,
487 w, h);
488
489 x = focus_cycle_target->frame->area.x;
490 y = focus_cycle_target->frame->area.y;
491 w = wl;
492 h = focus_cycle_target->frame->area.height;
493
494 XMoveResizeWindow(ob_display, focus_indicator.left.win,
495 x, y, w, h);
496 a_focus_indicator->texture[0].data.lineart.x1 = w-1;
497 a_focus_indicator->texture[0].data.lineart.y1 = 0;
498 a_focus_indicator->texture[0].data.lineart.x2 = 0;
499 a_focus_indicator->texture[0].data.lineart.y2 = 0;
500 a_focus_indicator->texture[1].data.lineart.x1 = 0;
501 a_focus_indicator->texture[1].data.lineart.y1 = 0;
502 a_focus_indicator->texture[1].data.lineart.x2 = 0;
503 a_focus_indicator->texture[1].data.lineart.y2 = h-1;
504 a_focus_indicator->texture[2].data.lineart.x1 = 0;
505 a_focus_indicator->texture[2].data.lineart.y1 = h-1;
506 a_focus_indicator->texture[2].data.lineart.x2 = w-1;
507 a_focus_indicator->texture[2].data.lineart.y2 = h-1;
508 a_focus_indicator->texture[3].data.lineart.x1 = w-1;
509 a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
510 a_focus_indicator->texture[3].data.lineart.x2 = w-1;
511 a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
512 RrPaint(a_focus_indicator, focus_indicator.left.win,
513 w, h);
514
515 x = focus_cycle_target->frame->area.x +
516 focus_cycle_target->frame->area.width - wr;
517 y = focus_cycle_target->frame->area.y;
518 w = wr;
519 h = focus_cycle_target->frame->area.height ;
520
521 XMoveResizeWindow(ob_display, focus_indicator.right.win,
522 x, y, w, h);
523 a_focus_indicator->texture[0].data.lineart.x1 = 0;
524 a_focus_indicator->texture[0].data.lineart.y1 = 0;
525 a_focus_indicator->texture[0].data.lineart.x2 = w-1;
526 a_focus_indicator->texture[0].data.lineart.y2 = 0;
527 a_focus_indicator->texture[1].data.lineart.x1 = w-1;
528 a_focus_indicator->texture[1].data.lineart.y1 = 0;
529 a_focus_indicator->texture[1].data.lineart.x2 = w-1;
530 a_focus_indicator->texture[1].data.lineart.y2 = h-1;
531 a_focus_indicator->texture[2].data.lineart.x1 = w-1;
532 a_focus_indicator->texture[2].data.lineart.y1 = h-1;
533 a_focus_indicator->texture[2].data.lineart.x2 = 0;
534 a_focus_indicator->texture[2].data.lineart.y2 = h-1;
535 a_focus_indicator->texture[3].data.lineart.x1 = 0;
536 a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
537 a_focus_indicator->texture[3].data.lineart.x2 = 0;
538 a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
539 RrPaint(a_focus_indicator, focus_indicator.right.win,
540 w, h);
541
542 x = focus_cycle_target->frame->area.x;
543 y = focus_cycle_target->frame->area.y +
544 focus_cycle_target->frame->area.height - wb;
545 w = focus_cycle_target->frame->area.width;
546 h = wb;
547
548 XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
549 x, y, w, h);
550 a_focus_indicator->texture[0].data.lineart.x1 = 0;
551 a_focus_indicator->texture[0].data.lineart.y1 = 0;
552 a_focus_indicator->texture[0].data.lineart.x2 = 0;
553 a_focus_indicator->texture[0].data.lineart.y2 = h-1;
554 a_focus_indicator->texture[1].data.lineart.x1 = 0;
555 a_focus_indicator->texture[1].data.lineart.y1 = h-1;
556 a_focus_indicator->texture[1].data.lineart.x2 = w-1;
557 a_focus_indicator->texture[1].data.lineart.y2 = h-1;
558 a_focus_indicator->texture[2].data.lineart.x1 = w-1;
559 a_focus_indicator->texture[2].data.lineart.y1 = h-1;
560 a_focus_indicator->texture[2].data.lineart.x2 = w-1;
561 a_focus_indicator->texture[2].data.lineart.y2 = 0;
562 a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
563 a_focus_indicator->texture[3].data.lineart.y1 = 0;
564 a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
565 a_focus_indicator->texture[3].data.lineart.y2 = 0;
566 RrPaint(a_focus_indicator, focus_indicator.bottom.win,
567 w, h);
568
569 XMapWindow(ob_display, focus_indicator.top.win);
570 XMapWindow(ob_display, focus_indicator.left.win);
571 XMapWindow(ob_display, focus_indicator.right.win);
572 XMapWindow(ob_display, focus_indicator.bottom.win);
573 }
574 }
575
576 static gboolean has_valid_group_siblings_on_desktop(ObClient *ft,
577 gboolean all_desktops)
578
579 {
580 GSList *it;
581
582 if (!ft->group) return FALSE;
583
584 for (it = ft->group->members; it; it = g_slist_next(it)) {
585 ObClient *c = it->data;
586 /* check that it's not a helper window to avoid infinite recursion */
587 if (c != ft && !client_helper(c) &&
588 valid_focus_target(c, all_desktops, FALSE, FALSE))
589 {
590 return TRUE;
591 }
592 }
593 return FALSE;
594 }
595
596 /*! @param allow_helpers This is used for calling itself recursively while
597 checking helper windows. */
598 static gboolean valid_focus_target(ObClient *ft,
599 gboolean all_desktops,
600 gboolean dock_windows,
601 gboolean desktop_windows)
602 {
603 gboolean ok = FALSE;
604
605 /* it's on this desktop unless you want all desktops.
606
607 do this check first because it will usually filter out the most
608 windows */
609 ok = (all_desktops || ft->desktop == screen_desktop ||
610 ft->desktop == DESKTOP_ALL);
611
612 /* the window can receive focus somehow */
613 ok = ok && (ft->can_focus || ft->focus_notify);
614
615 /* it's the right type of window */
616 if (dock_windows || desktop_windows)
617 ok = ok && ((dock_windows && ft->type == OB_CLIENT_TYPE_DOCK) ||
618 (desktop_windows && ft->type == OB_CLIENT_TYPE_DESKTOP));
619 else
620 /* normal non-helper windows are valid targets */
621 ok = ok &&
622 ((client_normal(ft) && !client_helper(ft))
623 ||
624 /* helper windows are valid targets it... */
625 (client_helper(ft) &&
626 /* ...a window in its group already has focus ... */
627 ((focus_client && ft->group == focus_client->group) ||
628 /* ... or if there are no other windows in its group
629 that can be cycled to instead */
630 !has_valid_group_siblings_on_desktop(ft, all_desktops))));
631
632 /* it's not set to skip the taskbar (unless it is a type that would be
633 expected to set this hint */
634 ok = ok && ((ft->type == OB_CLIENT_TYPE_DOCK ||
635 ft->type == OB_CLIENT_TYPE_DESKTOP ||
636 ft->type == OB_CLIENT_TYPE_TOOLBAR ||
637 ft->type == OB_CLIENT_TYPE_MENU ||
638 ft->type == OB_CLIENT_TYPE_UTILITY) ||
639 !ft->skip_taskbar);
640
641 /* it's not going to just send fous off somewhere else (modal window) */
642 ok = ok && ft == client_focus_target(ft);
643
644 return ok;
645 }
646
647 void focus_cycle(gboolean forward, gboolean all_desktops,
648 gboolean dock_windows, gboolean desktop_windows,
649 gboolean linear, gboolean interactive,
650 gboolean dialog, gboolean done, gboolean cancel)
651 {
652 static ObClient *first = NULL;
653 static ObClient *t = NULL;
654 static GList *order = NULL;
655 GList *it, *start, *list;
656 ObClient *ft = NULL;
657
658 if (interactive) {
659 if (cancel) {
660 focus_cycle_target = NULL;
661 goto done_cycle;
662 } else if (done)
663 goto done_cycle;
664
665 if (!focus_order)
666 goto done_cycle;
667
668 if (!first) first = focus_client;
669
670 if (linear) list = client_list;
671 else list = focus_order;
672 } else {
673 if (!focus_order)
674 goto done_cycle;
675 list = client_list;
676 }
677 if (!focus_cycle_target) focus_cycle_target = focus_client;
678
679 start = it = g_list_find(list, focus_cycle_target);
680 if (!start) /* switched desktops or something? */
681 start = it = forward ? g_list_last(list) : g_list_first(list);
682 if (!start) goto done_cycle;
683
684 do {
685 if (forward) {
686 it = it->next;
687 if (it == NULL) it = g_list_first(list);
688 } else {
689 it = it->prev;
690 if (it == NULL) it = g_list_last(list);
691 }
692 ft = it->data;
693 if (valid_focus_target(ft, all_desktops, dock_windows,
694 desktop_windows))
695 {
696 if (interactive) {
697 if (ft != focus_cycle_target) { /* prevents flicker */
698 focus_cycle_target = ft;
699 focus_cycle_draw_indicator();
700 }
701 /* same arguments as valid_focus_target */
702 popup_cycle(ft, dialog, all_desktops, dock_windows,
703 desktop_windows);
704 return;
705 } else if (ft != focus_cycle_target) {
706 focus_cycle_target = ft;
707 done = TRUE;
708 break;
709 }
710 }
711 } while (it != start);
712
713 done_cycle:
714 if (done && focus_cycle_target)
715 client_activate(focus_cycle_target, FALSE, TRUE);
716
717 t = NULL;
718 first = NULL;
719 focus_cycle_target = NULL;
720 g_list_free(order);
721 order = NULL;
722
723 if (interactive) {
724 focus_cycle_draw_indicator();
725 popup_cycle(ft, FALSE, FALSE, FALSE, FALSE);
726 }
727
728 return;
729 }
730
731 /* this be mostly ripped from fvwm */
732 static ObClient *focus_find_directional(ObClient *c, ObDirection dir,
733 gboolean dock_windows,
734 gboolean desktop_windows)
735 {
736 gint my_cx, my_cy, his_cx, his_cy;
737 gint offset = 0;
738 gint distance = 0;
739 gint score, best_score;
740 ObClient *best_client, *cur;
741 GList *it;
742
743 if(!client_list)
744 return NULL;
745
746 /* first, find the centre coords of the currently focused window */
747 my_cx = c->frame->area.x + c->frame->area.width / 2;
748 my_cy = c->frame->area.y + c->frame->area.height / 2;
749
750 best_score = -1;
751 best_client = NULL;
752
753 for(it = g_list_first(client_list); it; it = g_list_next(it)) {
754 cur = it->data;
755
756 /* the currently selected window isn't interesting */
757 if(cur == c)
758 continue;
759 if (cur->type == OB_CLIENT_TYPE_DOCK && !dock_windows)
760 continue;
761 if (cur->type == OB_CLIENT_TYPE_DESKTOP && !desktop_windows)
762 continue;
763 if (!client_normal(cur) &&
764 cur->type != OB_CLIENT_TYPE_DOCK &&
765 cur->type != OB_CLIENT_TYPE_DESKTOP)
766 continue;
767 /* using c->desktop instead of screen_desktop doesn't work if the
768 * current window was omnipresent, hope this doesn't have any other
769 * side effects */
770 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
771 continue;
772 if(cur->iconic)
773 continue;
774 if(!(client_focus_target(cur) == cur &&
775 client_can_focus(cur)))
776 continue;
777
778 /* find the centre coords of this window, from the
779 * currently focused window's point of view */
780 his_cx = (cur->frame->area.x - my_cx)
781 + cur->frame->area.width / 2;
782 his_cy = (cur->frame->area.y - my_cy)
783 + cur->frame->area.height / 2;
784
785 if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
786 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
787 gint tx;
788 /* Rotate the diagonals 45 degrees counterclockwise.
789 * To do this, multiply the matrix /+h +h\ with the
790 * vector (x y). \-h +h/
791 * h = sqrt(0.5). We can set h := 1 since absolute
792 * distance doesn't matter here. */
793 tx = his_cx + his_cy;
794 his_cy = -his_cx + his_cy;
795 his_cx = tx;
796 }
797
798 switch(dir) {
799 case OB_DIRECTION_NORTH:
800 case OB_DIRECTION_SOUTH:
801 case OB_DIRECTION_NORTHEAST:
802 case OB_DIRECTION_SOUTHWEST:
803 offset = (his_cx < 0) ? -his_cx : his_cx;
804 distance = ((dir == OB_DIRECTION_NORTH ||
805 dir == OB_DIRECTION_NORTHEAST) ?
806 -his_cy : his_cy);
807 break;
808 case OB_DIRECTION_EAST:
809 case OB_DIRECTION_WEST:
810 case OB_DIRECTION_SOUTHEAST:
811 case OB_DIRECTION_NORTHWEST:
812 offset = (his_cy < 0) ? -his_cy : his_cy;
813 distance = ((dir == OB_DIRECTION_WEST ||
814 dir == OB_DIRECTION_NORTHWEST) ?
815 -his_cx : his_cx);
816 break;
817 }
818
819 /* the target must be in the requested direction */
820 if(distance <= 0)
821 continue;
822
823 /* Calculate score for this window. The smaller the better. */
824 score = distance + offset;
825
826 /* windows more than 45 degrees off the direction are
827 * heavily penalized and will only be chosen if nothing
828 * else within a million pixels */
829 if(offset > distance)
830 score += 1000000;
831
832 if(best_score == -1 || score < best_score)
833 best_client = cur,
834 best_score = score;
835 }
836
837 return best_client;
838 }
839
840 void focus_directional_cycle(ObDirection dir, gboolean dock_windows,
841 gboolean desktop_windows, gboolean interactive,
842 gboolean dialog, gboolean done, gboolean cancel)
843 {
844 static ObClient *first = NULL;
845 ObClient *ft = NULL;
846
847 if (!interactive)
848 return;
849
850 if (cancel) {
851 focus_cycle_target = NULL;
852 goto done_cycle;
853 } else if (done)
854 goto done_cycle;
855
856 if (!focus_order)
857 goto done_cycle;
858
859 if (!first) first = focus_client;
860 if (!focus_cycle_target) focus_cycle_target = focus_client;
861
862 if (focus_cycle_target)
863 ft = focus_find_directional(focus_cycle_target, dir, dock_windows,
864 desktop_windows);
865 else {
866 GList *it;
867
868 for (it = focus_order; it; it = g_list_next(it))
869 if (valid_focus_target(it->data, FALSE, dock_windows,
870 desktop_windows))
871 ft = it->data;
872 }
873
874 if (ft) {
875 if (ft != focus_cycle_target) {/* prevents flicker */
876 focus_cycle_target = ft;
877 focus_cycle_draw_indicator();
878 }
879 }
880 if (focus_cycle_target) {
881 /* same arguments as valid_focus_target */
882 popup_cycle(focus_cycle_target, dialog, FALSE, dock_windows,
883 desktop_windows);
884 if (dialog)
885 return;
886 }
887
888
889 done_cycle:
890 if (done && focus_cycle_target)
891 client_activate(focus_cycle_target, FALSE, TRUE);
892
893 first = NULL;
894 focus_cycle_target = NULL;
895
896 focus_cycle_draw_indicator();
897 popup_cycle(ft, FALSE, FALSE, FALSE, FALSE);
898
899 return;
900 }
901
902 void focus_order_add_new(ObClient *c)
903 {
904 if (c->iconic)
905 focus_order_to_top(c);
906 else {
907 g_assert(!g_list_find(focus_order, c));
908 /* if there are any iconic windows, put this above them in the order,
909 but if there are not, then put it under the currently focused one */
910 if (focus_order && ((ObClient*)focus_order->data)->iconic)
911 focus_order = g_list_insert(focus_order, c, 0);
912 else
913 focus_order = g_list_insert(focus_order, c, 1);
914 }
915 }
916
917 void focus_order_remove(ObClient *c)
918 {
919 focus_order = g_list_remove(focus_order, c);
920 }
921
922 void focus_order_to_top(ObClient *c)
923 {
924 focus_order = g_list_remove(focus_order, c);
925 if (!c->iconic) {
926 focus_order = g_list_prepend(focus_order, c);
927 } else {
928 GList *it;
929
930 /* insert before first iconic window */
931 for (it = focus_order;
932 it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
933 focus_order = g_list_insert_before(focus_order, it, c);
934 }
935 }
936
937 void focus_order_to_bottom(ObClient *c)
938 {
939 focus_order = g_list_remove(focus_order, c);
940 if (c->iconic) {
941 focus_order = g_list_append(focus_order, c);
942 } else {
943 GList *it;
944
945 /* insert before first iconic window */
946 for (it = focus_order;
947 it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
948 focus_order = g_list_insert_before(focus_order, it, c);
949 }
950 }
951
952 ObClient *focus_order_find_first(guint desktop)
953 {
954 GList *it;
955 for (it = focus_order; it; it = g_list_next(it)) {
956 ObClient *c = it->data;
957 if (c->desktop == desktop || c->desktop == DESKTOP_ALL)
958 return c;
959 }
960 return NULL;
961 }
962
963 static void focus_tried_hide_notify(ObClient *client, gpointer data)
964 {
965 XEvent ce;
966
967 if (client == focus_tried) {
968 /* we were trying to focus this window but it's gone */
969
970 focus_tried = NULL;
971
972 ob_debug_type(OB_DEBUG_FOCUS, "Tried to focus window 0x%x and it "
973 "is being unmanaged:\n");
974 if (XCheckIfEvent(ob_display, &ce, event_look_for_focusin_client,NULL))
975 {
976 XPutBackEvent(ob_display, &ce);
977 ob_debug_type(OB_DEBUG_FOCUS, " but another FocusIn is coming\n");
978 } else {
979 ob_debug_type(OB_DEBUG_FOCUS, " so falling back focus again.\n");
980 focus_fallback(TRUE);
981 }
982 }
983 }
This page took 0.077049 seconds and 5 git commands to generate.