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