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