1 /*
2 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3 * Copyright (C) 2004-2021 Kim Woelders
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies of the Software, its documentation and marketing & publicity
14 * materials, and acknowledgment shall be given in the documentation, materials
15 * and software packages that this Software was used.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24 #include "config.h"
25
26 #include <X11/Xlib.h>
27
28 #include "E.h"
29 #include "cursors.h"
30 #include "desktops.h" /* FIXME - Should not be here */
31 #include "dialog.h"
32 #include "emodule.h"
33 #include "ewins.h"
34 #include "focus.h"
35 #include "grabs.h"
36 #include "hints.h"
37 #include "icons.h"
38 #include "settings.h"
39 #include "timers.h"
40 #include "xwin.h"
41
42 #define EwinListFocusRaise(ewin) EobjListFocusRaise(EoObj(ewin))
43
44 static const char *const focus_why[] = {
45 "NOP", "INIT", "SET", "NONE", "ENTER", "LEAVE", "EWIN_NEW", "EWIN_UNMAP",
46 "DESK_ENTER", "DESK_LEAVE", "NEXT", "PREV", "CLICK",
47 };
48
49 static char focus_inhibit = 1;
50 static char focus_is_set = 0;
51 static char click_pending_update_grabs = 0;
52 static int focus_pending_why = 0;
53 static EWin *focus_pending_ewin = NULL;
54 static EWin *focus_pending_new = NULL;
55 static EWin *focus_pending_raise = NULL;
56 static Timer *focus_timer_autoraise = NULL;
57 static int focus_request = 0;
58
59 void
FocusEnable(int on)60 FocusEnable(int on)
61 {
62 if (on)
63 {
64 if (focus_inhibit > 0)
65 focus_inhibit--;
66 }
67 else
68 {
69 focus_inhibit++;
70 }
71
72 if (EDebug(EDBUG_TYPE_FOCUS))
73 Eprintf("%s: inhibit=%d\n", __func__, focus_inhibit);
74 }
75
76 /*
77 * Return !0 if it is OK to focus ewin.
78 */
79 static int
FocusEwinValid(EWin * ewin,int want_on_screen,int click,int want_visible)80 FocusEwinValid(EWin * ewin, int want_on_screen, int click, int want_visible)
81 {
82 int ok;
83
84 if (!ewin)
85 return 0;
86
87 if (ewin->state.inhibit_focus)
88 ok = 0;
89
90 else if (!EoIsMapped(ewin) || !EoIsShown(ewin) ||
91 ewin->state.state != EWIN_STATE_MAPPED)
92 ok = 0;
93
94 else if (ewin->state.sliding)
95 ok = 0;
96
97 else if (ewin->props.focusclick && !click)
98 ok = 0;
99
100 else if (want_visible && ewin->state.visibility == VisibilityFullyObscured)
101 ok = 0;
102
103 else if (want_on_screen && !EwinIsOnScreen(ewin))
104 ok = 0;
105
106 else
107 ok = 1;
108
109 #if 0
110 Eprintf
111 ("%s: %#x %s: st=%d sh=%d inh=%d/%d ons=%d(%d) vis=%d(%d) cl=%d(%d): Ok=%d\n",
112 __func__, EwinGetClientXwin(ewin), EwinGetTitle(ewin),
113 ewin->state.state, EoIsShown(ewin),
114 ewin->state.inhibit_focus, ewin->state.sliding,
115 EwinIsOnScreen(ewin), want_on_screen,
116 ewin->state.visibility, want_visible, ok, click, ewin->props.focusclick);
117 #endif
118 return ok;
119 }
120
121 /*
122 * Return !0 if new ewin should be focused.
123 */
124 static int
FocusEwinValidNew(EWin * ewin)125 FocusEwinValidNew(EWin * ewin)
126 {
127 int ok = 0;
128
129 if (Conf.focus.all_new_windows_get_focus)
130 ok = 1;
131
132 else if (Conf.focus.new_windows_get_focus_if_group_focused &&
133 Mode.focuswin &&
134 EwinGetWindowGroup(ewin) == EwinGetWindowGroup(Mode.focuswin))
135 ok = 1;
136
137 else if (EwinIsTransient(ewin))
138 {
139 if (Conf.focus.new_transients_get_focus)
140 {
141 ok = 1;
142 }
143 else if (Conf.focus.new_transients_get_focus_if_group_focused &&
144 Mode.focuswin)
145 {
146 if ((EwinGetTransientFor(ewin) ==
147 EwinGetClientXwin(Mode.focuswin)) ||
148 (EwinGetWindowGroup(ewin) ==
149 EwinGetWindowGroup(Mode.focuswin)))
150 ok = 1;
151 }
152 }
153
154 #if 0
155 Eprintf("%s: %#x %s: Ok=%d\n", __func__,
156 EwinGetClientXwin(ewin), EwinGetTitle(ewin), ok);
157 #endif
158
159 return ok;
160 }
161
162 /*
163 * Return the ewin to focus after entering area or losing focused window.
164 */
165 static EWin *
FocusEwinSelect(void)166 FocusEwinSelect(void)
167 {
168 EWin *const *lst, *ewin;
169 int num, i;
170
171 switch (Conf.focus.mode)
172 {
173 default:
174 case MODE_FOCUS_POINTER:
175 ewin = GetEwinPointerInClient();
176 if (ewin && !FocusEwinValid(ewin, 1, 0, 0))
177 ewin = NULL;
178 break;
179
180 case MODE_FOCUS_SLOPPY:
181 ewin = GetEwinPointerInClient();
182 if (ewin && FocusEwinValid(ewin, 1, 0, 0))
183 break;
184 goto do_select;
185
186 case MODE_FOCUS_CLICK:
187 goto do_select;
188
189 do_select:
190 ewin = NULL;
191 lst = EwinListFocusGet(&num);
192 for (i = 0; i < num; i++)
193 {
194 if (!FocusEwinValid(lst[i], 1, 0, 0) ||
195 lst[i]->props.skip_focuslist)
196 continue;
197 ewin = lst[i];
198 break;
199 }
200 break;
201 }
202
203 return ewin;
204 }
205
206 static int
AutoraiseTimeout(void * data)207 AutoraiseTimeout(void *data)
208 {
209 EWin *ewin = (EWin *) data;
210
211 if (Conf.focus.mode == MODE_FOCUS_CLICK)
212 goto done;
213
214 if (EwinFindByPtr(ewin)) /* May be gone */
215 EwinRaise(ewin);
216
217 done:
218 focus_timer_autoraise = NULL;
219 return 0;
220 }
221
222 static void
FocusRaisePending(void)223 FocusRaisePending(void)
224 {
225 EWin *ewin = focus_pending_raise;
226 unsigned int mask;
227
228 /* The focusing cycle ends when no more modifiers are depressed */
229 mask = 0;
230 EQueryPointer(NULL, NULL, NULL, NULL, &mask);
231 if ((mask & Mode.masks.mod_key_mask) != 0)
232 return;
233
234 if (EwinFindByPtr(ewin)) /* May be gone */
235 EwinListFocusRaise(ewin);
236
237 GrabKeyboardRelease();
238
239 focus_pending_raise = NULL;
240 }
241
242 /*
243 * dir > 0: Focus previously focused window
244 * else : Focus least recently focused window
245 */
246 static void
FocusCycleEwin(int dir)247 FocusCycleEwin(int dir)
248 {
249 EWin *const *lst;
250 EWin *ewin;
251 int i, j, num;
252
253 lst = EwinListFocusGet(&num);
254 if (num <= 1)
255 return;
256
257 dir = (dir > 0) ? 1 : -1;
258
259 for (j = 0; j < num; j++)
260 {
261 if (lst[j] == Mode.focuswin)
262 break;
263 }
264 for (i = 1; i < num; i++)
265 {
266 ewin = lst[(j + i * dir + num) % num];
267 if (!FocusEwinValid(ewin, 1, 0, 0) || ewin->props.skip_focuslist)
268 continue;
269 FocusToEWin(ewin, FOCUS_PREV);
270 break;
271 }
272 }
273
274 static void
ClickGrabsSet(EWin * ewin)275 ClickGrabsSet(EWin * ewin)
276 {
277 int set = 0;
278 unsigned int raise_button = AnyButton;
279
280 if (Conf.focus.clickraises == 2)
281 raise_button = Button1;
282
283 if ((Conf.focus.clickraises && !EwinListStackIsRaised(ewin)) ||
284 (!ewin->state.active && !ewin->state.inhibit_focus))
285 set = 1;
286
287 if (set)
288 {
289 if (ewin->state.click_grab_isset &&
290 ewin->state.click_grab_button != raise_button)
291 {
292 GrabButtonRelease(ewin->state.click_grab_button, AnyModifier,
293 EwinGetClientConWin(ewin));
294 ewin->state.click_grab_isset = 0;
295 }
296 if (!ewin->state.click_grab_isset)
297 {
298 GrabButtonSet(raise_button, AnyModifier, EwinGetClientConWin(ewin),
299 ButtonPressMask, ECSR_PGRAB, 1);
300 if (EDebug(EDBUG_TYPE_GRABS))
301 Eprintf("%s: %#x set %s\n", __func__,
302 EwinGetClientXwin(ewin), EwinGetTitle(ewin));
303 ewin->state.click_grab_isset = 1;
304 ewin->state.click_grab_button = raise_button;
305 }
306 }
307 else
308 {
309 if (ewin->state.click_grab_isset)
310 {
311 GrabButtonRelease(ewin->state.click_grab_button, AnyModifier,
312 EwinGetClientConWin(ewin));
313 if (EDebug(EDBUG_TYPE_GRABS))
314 Eprintf("%s: %#x unset %s\n", __func__,
315 EwinGetClientXwin(ewin), EwinGetTitle(ewin));
316 ewin->state.click_grab_isset = 0;
317 }
318 }
319 }
320
321 static void
FocusEwinSetActive(EWin * ewin,int active)322 FocusEwinSetActive(EWin * ewin, int active)
323 {
324 if (ewin->state.active == (unsigned)active)
325 return;
326
327 ewin->state.active = active;
328 EwinBorderUpdateState(ewin);
329 EwinUpdateOpacity(ewin);
330
331 if (active && ewin->state.attention)
332 ewin->state.attention = 0;
333
334 HintsSetWindowState(ewin);
335 }
336
337 static void
doClickGrabsUpdate(void)338 doClickGrabsUpdate(void)
339 {
340 EWin *const *lst, *ewin;
341 int i, num;
342
343 lst = EwinListGetAll(&num);
344 for (i = 0; i < num; i++)
345 {
346 ewin = lst[i];
347 ClickGrabsSet(ewin);
348 }
349 click_pending_update_grabs = 0;
350 }
351
352 void
ClickGrabsUpdate(void)353 ClickGrabsUpdate(void)
354 {
355 click_pending_update_grabs = 1;
356 }
357
358 static void
doFocusToEwin(EWin * ewin,int why)359 doFocusToEwin(EWin * ewin, int why)
360 {
361 int do_raise = 0, do_warp = 0;
362
363 if (focus_inhibit)
364 return;
365
366 if (EDebug(EDBUG_TYPE_FOCUS))
367 Eprintf("%s: %#x %s why=%s\n", __func__,
368 (ewin) ? EwinGetClientXwin(ewin) : 0,
369 (ewin) ? EwinGetTitle(ewin) : "None", focus_why[why]);
370
371 switch (why)
372 {
373 case FOCUS_NEXT:
374 case FOCUS_PREV:
375 if (Conf.focus.raise_on_next)
376 do_raise = 1;
377 if (Conf.focus.warp_on_next)
378 do_warp = 1;
379 /* Fall thru */
380 default:
381 case FOCUS_SET:
382 case FOCUS_ENTER:
383 case FOCUS_LEAVE: /* Unused */
384 case FOCUS_CLICK:
385 if (ewin && ewin == Mode.focuswin)
386 return;
387 if (!ewin) /* Unfocus */
388 break;
389 if (!FocusEwinValid(ewin, 1, why == FOCUS_CLICK, 0))
390 return;
391 break;
392
393 case FOCUS_INIT:
394 case FOCUS_DESK_ENTER:
395 ewin = FocusEwinSelect();
396 break;
397
398 case FOCUS_DESK_LEAVE:
399 focus_is_set = 0;
400 /* FALLTHROUGH */
401 case FOCUS_NONE:
402 ewin = NULL;
403 if (ewin == Mode.focuswin)
404 return;
405 break;
406
407 case FOCUS_EWIN_UNMAP:
408 if (Mode.focuswin)
409 return;
410 ewin = FocusEwinSelect();
411 if (ewin == Mode.focuswin)
412 ewin = NULL;
413 break;
414
415 case FOCUS_EWIN_NEW:
416 if (!ewin)
417 return;
418
419 if (Mode.place.doing_manual)
420 goto check_focus_new;
421
422 if (ewin->props.focus_when_mapped)
423 goto check_focus_new;
424
425 if (FocusEwinValidNew(ewin))
426 {
427 if (EwinIsTransient(ewin))
428 DeskGotoByEwin(ewin, 0);
429 goto check_focus_new;
430 }
431
432 if (Conf.focus.mode != MODE_FOCUS_CLICK && ewin == Mode.mouse_over_ewin)
433 goto check_focus_new;
434
435 return;
436
437 check_focus_new:
438 if (!FocusEwinValid(ewin, 1, 0, 0))
439 return;
440 break;
441 }
442
443 if (ewin == Mode.focuswin && focus_is_set)
444 return;
445
446 /* Check if ewin is a valid focus window target */
447
448 if (!ewin)
449 goto done;
450
451 /* NB! ewin != NULL */
452
453 if (why != FOCUS_CLICK && ewin->props.focusclick)
454 return;
455
456 if (Conf.autoraise.enable)
457 {
458 TIMER_DEL(focus_timer_autoraise);
459
460 if (Conf.focus.mode != MODE_FOCUS_CLICK)
461 TIMER_ADD(focus_timer_autoraise, Conf.autoraise.delay,
462 AutoraiseTimeout, ewin);
463 }
464
465 if (do_raise)
466 EwinRaise(ewin);
467
468 if (Conf.focus.warp_always)
469 do_warp = 1;
470 if (do_warp)
471 EwinWarpTo(ewin, 0);
472
473 switch (why)
474 {
475 case FOCUS_PREV:
476 case FOCUS_NEXT:
477 GrabKeyboardSet(VROOT); /* Causes idler to be called on KeyRelease */
478 focus_pending_raise = ewin;
479 break;
480 case FOCUS_DESK_ENTER:
481 if (Conf.focus.mode == MODE_FOCUS_CLICK)
482 break;
483 /* FALLTHROUGH */
484 default:
485 case FOCUS_INIT:
486 EwinListFocusRaise(ewin);
487 break;
488 }
489
490 SoundPlay(SOUND_FOCUS_SET);
491 done:
492
493 ClickGrabsUpdate();
494
495 /* Unset old focus window (if any) highlighting */
496 if (Mode.focuswin)
497 FocusEwinSetActive(Mode.focuswin, 0);
498 ICCCM_Cmap(ewin);
499
500 /* Quit if pointer is not on our screen */
501
502 if (!Mode.events.on_screen)
503 {
504 Mode.focuswin = NULL;
505 return;
506 }
507
508 /* Set new focus window (if any) highlighting */
509 Mode.focuswin = ewin;
510 if (Mode.focuswin)
511 FocusEwinSetActive(Mode.focuswin, 1);
512
513 if (why == FOCUS_DESK_LEAVE)
514 return;
515
516 ICCCM_Focus(ewin);
517 focus_is_set = 1;
518 }
519
520 void
FocusToEWin(EWin * ewin,int why)521 FocusToEWin(EWin * ewin, int why)
522 {
523 if (EDebug(EDBUG_TYPE_FOCUS))
524 Eprintf("%s(%d) %#x %s why=%s\n", __func__, focus_inhibit,
525 (ewin) ? EwinGetClientXwin(ewin) : 0,
526 (ewin) ? EwinGetTitle(ewin) : "None", focus_why[why]);
527
528 switch (why)
529 {
530 case FOCUS_EWIN_NEW:
531 if (!FocusEwinValidNew(ewin))
532 break;
533 if (!FocusEwinValid(ewin, 0, 0, 0))
534 break;
535 focus_pending_new = ewin;
536 focus_pending_why = why;
537 focus_pending_ewin = ewin;
538 focus_request = (int)NextRequest(disp) - 1;
539 break;
540
541 default:
542 if (ewin && !FocusEwinValid(ewin, 0, why == FOCUS_CLICK, 0))
543 break;
544 focus_pending_why = why;
545 focus_pending_ewin = ewin;
546 focus_request = (int)NextRequest(disp) - 1;
547 break;
548
549 case FOCUS_EWIN_UNMAP:
550 focus_pending_why = why;
551 focus_pending_ewin = NULL;
552 if (ewin == Mode.focuswin)
553 {
554 Mode.focuswin = NULL;
555 focus_is_set = 0;
556 if (!EoIsGone(ewin))
557 FocusEwinSetActive(ewin, 0);
558 }
559 if (ewin == focus_pending_new)
560 focus_pending_new = NULL;
561 break;
562 }
563 }
564
565 static void
FocusSet(void)566 FocusSet(void)
567 {
568 if (focus_pending_new && Conf.focus.all_new_windows_get_focus)
569 doFocusToEwin(focus_pending_new, FOCUS_EWIN_NEW);
570 else
571 doFocusToEwin(focus_pending_ewin, focus_pending_why);
572 focus_pending_why = 0;
573 focus_pending_ewin = focus_pending_new = NULL;
574 }
575
576 void
FocusNewDeskBegin(void)577 FocusNewDeskBegin(void)
578 {
579 /* Freeze keyboard */
580 GrabKeyboardFreeze(VROOT);
581
582 focus_pending_new = NULL;
583 doFocusToEwin(NULL, FOCUS_DESK_LEAVE);
584 FocusEnable(0);
585 }
586
587 void
FocusNewDesk(void)588 FocusNewDesk(void)
589 {
590 FocusEnable(1);
591 FocusToEWin(NULL, FOCUS_DESK_ENTER);
592
593 /* Unfreeze keyboard */
594 GrabKeyboardRelease();
595 }
596
597 static void
_FocusScreenSendEvent(EX_Window xwin,int x,int y,EX_Time t,int enter)598 _FocusScreenSendEvent(EX_Window xwin, int x, int y, EX_Time t, int enter)
599 {
600 XEvent xe;
601
602 xe.type = (enter) ? EnterNotify : LeaveNotify;
603 xe.xcrossing.window = xwin;
604 xe.xcrossing.root = xwin;
605 xe.xcrossing.subwindow = 0;
606 xe.xcrossing.time = t;
607 xe.xcrossing.x = xe.xcrossing.x_root = x;
608 xe.xcrossing.y = xe.xcrossing.y_root = y;
609 xe.xcrossing.mode = NotifyNormal;
610 xe.xcrossing.detail = NotifyNonlinear;
611 xe.xcrossing.same_screen = (enter) ? True : False;
612 xe.xcrossing.focus = (enter) ? False : True;
613 xe.xcrossing.state = 0;
614
615 XSendEvent(disp, xwin, False, EnterWindowMask | LeaveWindowMask, &xe);
616 }
617
618 void
FocusScreen(int scr)619 FocusScreen(int scr)
620 {
621 EX_Window xwin;
622 EX_Time t;
623 int x, y;
624
625 if (scr < 0 || scr >= ScreenCount(disp))
626 return;
627
628 /* IIRC warping to a different screen once did cause
629 * LeaveNotify's on the current root window. This does not
630 * happen in xorg 1.5.3 (and probably other versions).
631 * So, send LeaveNotify to current root and EnterNotify
632 * to new root. */
633
634 t = EGetTimestamp();
635
636 /* Report LeaveNotify on current root window */
637 xwin = WinGetXwin(VROOT);
638 EXQueryPointer(xwin, &x, &y, NULL, NULL);
639 _FocusScreenSendEvent(xwin, x, y, t, 0);
640
641 /* Do warp and report EnterNotify on new root window */
642 xwin = RootWindow(disp, scr);
643 x = DisplayWidth(disp, scr) / 2;
644 y = DisplayHeight(disp, scr) / 2;
645 EXWarpPointer(xwin, x, y);
646 _FocusScreenSendEvent(xwin, x, y, t, 1);
647 }
648
649 static void
FocusInit(void)650 FocusInit(void)
651 {
652 /* Start focusing windows */
653 FocusEnable(1);
654
655 FocusToEWin(NULL, FOCUS_INIT);
656 FocusSet();
657
658 /* Enable window placement features */
659 Mode.place.enable_features++;
660 }
661
662 static void
FocusExit(void)663 FocusExit(void)
664 {
665 }
666
667 /*
668 * Focus event handlers
669 */
670
671 void
FocusHandleEnter(EWin * ewin,XEvent * ev)672 FocusHandleEnter(EWin * ewin, XEvent * ev)
673 {
674 Mode.mouse_over_ewin = ewin;
675
676 #if 0 /* FIXME - Remove? */
677 if (ev->xcrossing.mode == NotifyUngrab &&
678 ev->xcrossing.detail == NotifyNonlinearVirtual)
679 {
680 if (EDebug(1))
681 Eprintf("%s: Previously ignored: focused: %s, enter: %s\n", __func__,
682 EoGetNameSafe(Mode.focuswin), EoGetNameSafe(ewin));
683 }
684 #endif
685
686 if ((int)ev->xcrossing.serial - focus_request < 0)
687 {
688 /* This event was caused by a request older than the latest
689 * focus assignment request - ignore */
690 if (EDebug(EDBUG_TYPE_FOCUS))
691 Eprintf("%s: Ignore serial < %#x\n", __func__, focus_request);
692 return;
693 }
694
695 if (!ewin)
696 {
697 /* Entering root may mean entering this screen */
698 FocusToEWin(NULL, FOCUS_DESK_ENTER);
699 return;
700 }
701
702 switch (Conf.focus.mode)
703 {
704 default:
705 case MODE_FOCUS_CLICK:
706 break;
707 case MODE_FOCUS_SLOPPY:
708 if (FocusEwinValid(ewin, 1, 0, 0))
709 FocusToEWin(ewin, FOCUS_ENTER);
710 break;
711 case MODE_FOCUS_POINTER:
712 if (FocusEwinValid(ewin, 1, 0, 0))
713 FocusToEWin(ewin, FOCUS_ENTER);
714 else
715 FocusToEWin(NULL, FOCUS_NONE);
716 break;
717 }
718 }
719
720 void
FocusHandleLeave(EWin * ewin,XEvent * ev)721 FocusHandleLeave(EWin * ewin, XEvent * ev)
722 {
723 if ((int)ev->xcrossing.serial - focus_request < 0)
724 {
725 /* This event was caused by a request older than the latest
726 * focus assignment request - ignore */
727 if (EDebug(EDBUG_TYPE_FOCUS))
728 Eprintf("%s: Ignore serial < %#x\n", __func__, focus_request);
729 return;
730 }
731
732 /* Leaving root may mean entering other screen */
733 if (!ewin)
734 {
735 if (ev->xcrossing.mode == NotifyNormal &&
736 ev->xcrossing.detail != NotifyInferior)
737 FocusToEWin(NULL, FOCUS_DESK_LEAVE);
738 }
739 }
740
741 void
FocusHandleChange(EWin * ewin __UNUSED__,XEvent * ev __UNUSED__)742 FocusHandleChange(EWin * ewin __UNUSED__, XEvent * ev __UNUSED__)
743 {
744 #if 0 /* Debug */
745 if (ewin == Mode.focuswin && ev->type == FocusOut)
746 Eprintf("%s: ??? Lost focus: %s\n", __func__, EwinGetTitle(ewin));
747 #endif
748 }
749
750 void
FocusHandleClick(EWin * ewin,Win win)751 FocusHandleClick(EWin * ewin, Win win)
752 {
753 if (Conf.focus.clickraises)
754 EwinRaise(ewin);
755
756 FocusToEWin(ewin, FOCUS_CLICK);
757
758 /* Allow click to pass thorugh */
759 if (EDebug(EDBUG_TYPE_GRABS))
760 Eprintf("%s: %#x %#x\n", __func__, WinGetXwin(win),
761 EwinGetContainerXwin(ewin));
762 if (win == EwinGetClientConWin(ewin))
763 {
764 ESync(ESYNC_FOCUS);
765 GrabPointerThaw();
766 ESync(ESYNC_FOCUS);
767 }
768 }
769
770 #if ENABLE_DIALOGS
771 /*
772 * Configuration dialog
773 */
774
775 typedef struct {
776 struct {
777 int mode;
778 int clickalways;
779 char new_focus;
780 char new_focus_if_group;
781 char popup_focus;
782 char popup_focus_if_group;
783 char raise_focus;
784 char warp_focus;
785 char warp_always;
786 } focus;
787
788 struct {
789 char enable;
790 int time;
791 } autoraise;
792
793 struct {
794 char enable;
795 char warp_after_focus;
796 char raise_after_focus;
797 char showsticky;
798 char showshaded;
799 char showiconified;
800 char showalldesks;
801 char warpfocused;
802 char show_shape;
803 int icon_mode;
804 } focuslist;
805 } FocusDlgData;
806
807 static void
_DlgApplyFocus(Dialog * d,int val __UNUSED__,void * data __UNUSED__)808 _DlgApplyFocus(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
809 {
810 FocusDlgData *dd = DLG_DATA_GET(d, FocusDlgData);
811
812 Conf.focus.mode = dd->focus.mode;
813 Conf.focus.clickraises = dd->focus.clickalways;
814 Conf.focus.all_new_windows_get_focus = dd->focus.new_focus;
815 Conf.focus.new_windows_get_focus_if_group_focused =
816 dd->focus.new_focus_if_group;
817 Conf.focus.new_transients_get_focus = dd->focus.popup_focus;
818 Conf.focus.new_transients_get_focus_if_group_focused =
819 dd->focus.popup_focus_if_group;
820 Conf.focus.raise_on_next = dd->focus.raise_focus;
821 Conf.focus.warp_on_next = dd->focus.warp_focus;
822 Conf.focus.warp_always = dd->focus.warp_always;
823
824 Conf.autoraise.enable = dd->autoraise.enable;
825 Conf.autoraise.delay = 10 * dd->autoraise.time;
826
827 Conf.warplist.enable = dd->focuslist.enable;
828 Conf.warplist.warp_on_select = dd->focuslist.warp_after_focus;
829 Conf.warplist.raise_on_select = dd->focuslist.raise_after_focus;
830 Conf.warplist.showsticky = dd->focuslist.showsticky;
831 Conf.warplist.showshaded = dd->focuslist.showshaded;
832 Conf.warplist.showiconified = dd->focuslist.showiconified;
833 Conf.warplist.showalldesks = dd->focuslist.showalldesks;
834 Conf.warplist.warpfocused = dd->focuslist.warpfocused;
835 Conf.warplist.show_shape = dd->focuslist.show_shape;
836 Conf.warplist.icon_mode = dd->focuslist.icon_mode;
837
838 ClickGrabsUpdate();
839
840 autosave();
841 }
842
843 static void
_DlgFillFocus(Dialog * d,DItem * table,void * data __UNUSED__)844 _DlgFillFocus(Dialog * d, DItem * table, void *data __UNUSED__)
845 {
846 FocusDlgData *dd = DLG_DATA_GET(d, FocusDlgData);
847 DItem *di, *radio;
848
849 dd->focus.mode = Conf.focus.mode;
850 dd->focus.clickalways = Conf.focus.clickraises;
851 dd->focus.new_focus = Conf.focus.all_new_windows_get_focus;
852 dd->focus.new_focus_if_group =
853 Conf.focus.new_windows_get_focus_if_group_focused;
854 dd->focus.popup_focus = Conf.focus.new_transients_get_focus;
855 dd->focus.popup_focus_if_group =
856 Conf.focus.new_transients_get_focus_if_group_focused;
857 dd->focus.raise_focus = Conf.focus.raise_on_next;
858 dd->focus.warp_focus = Conf.focus.warp_on_next;
859 dd->focus.warp_always = Conf.focus.warp_always;
860
861 dd->autoraise.enable = Conf.autoraise.enable;
862 dd->autoraise.time = Conf.autoraise.delay / 10;
863
864 dd->focuslist.enable = Conf.warplist.enable;
865 dd->focuslist.raise_after_focus = Conf.warplist.raise_on_select;
866 dd->focuslist.warp_after_focus = Conf.warplist.warp_on_select;
867 dd->focuslist.showsticky = Conf.warplist.showsticky;
868 dd->focuslist.showshaded = Conf.warplist.showshaded;
869 dd->focuslist.showiconified = Conf.warplist.showiconified;
870 dd->focuslist.showalldesks = Conf.warplist.showalldesks;
871 dd->focuslist.warpfocused = Conf.warplist.warpfocused;
872 dd->focuslist.show_shape = Conf.warplist.show_shape;
873 dd->focuslist.icon_mode = Conf.warplist.icon_mode;
874
875 DialogItemTableSetOptions(table, 2, 0, 0, 0);
876
877 radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
878 DialogItemSetColSpan(di, 2);
879 DialogItemSetText(di, _("Focus follows pointer"));
880 DialogItemRadioButtonSetFirst(di, radio);
881 DialogItemRadioButtonGroupSetVal(di, 0);
882
883 di = DialogAddItem(table, DITEM_RADIOBUTTON);
884 DialogItemSetColSpan(di, 2);
885 DialogItemSetText(di, _("Focus follows pointer sloppily"));
886 DialogItemRadioButtonSetFirst(di, radio);
887 DialogItemRadioButtonGroupSetVal(di, 1);
888
889 di = DialogAddItem(table, DITEM_RADIOBUTTON);
890 DialogItemSetColSpan(di, 2);
891 DialogItemSetText(di, _("Focus follows mouse clicks"));
892 DialogItemRadioButtonSetFirst(di, radio);
893 DialogItemRadioButtonGroupSetVal(di, 2);
894 DialogItemRadioButtonGroupSetValPtr(radio, &dd->focus.mode);
895
896 di = DialogAddItem(table, DITEM_SEPARATOR);
897 DialogItemSetColSpan(di, 2);
898
899 radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
900 DialogItemSetColSpan(di, 2);
901 DialogItemSetText(di, _("Clicking in a window does not raise it"));
902 DialogItemRadioButtonSetFirst(di, radio);
903 DialogItemRadioButtonGroupSetVal(di, 0);
904
905 di = DialogAddItem(table, DITEM_RADIOBUTTON);
906 DialogItemSetColSpan(di, 2);
907 DialogItemSetText(di, _("Clicking in a window always raises it"));
908 DialogItemRadioButtonSetFirst(di, radio);
909 DialogItemRadioButtonGroupSetVal(di, 1);
910
911 di = DialogAddItem(table, DITEM_RADIOBUTTON);
912 DialogItemSetColSpan(di, 2);
913 DialogItemSetText(di, _("Only primary mouse button can raise window"));
914 DialogItemRadioButtonSetFirst(di, radio);
915 DialogItemRadioButtonGroupSetVal(di, 2);
916 DialogItemRadioButtonGroupSetValPtr(radio, &dd->focus.clickalways);
917
918 di = DialogAddItem(table, DITEM_SEPARATOR);
919 DialogItemSetColSpan(di, 2);
920
921 di = DialogAddItem(table, DITEM_CHECKBUTTON);
922 DialogItemSetColSpan(di, 2);
923 DialogItemSetText(di, _("All new windows first get the focus"));
924 DialogItemCheckButtonSetPtr(di, &dd->focus.new_focus);
925
926 di = DialogAddItem(table, DITEM_CHECKBUTTON);
927 DialogItemSetColSpan(di, 2);
928 DialogItemSetText(di,
929 _
930 ("New windows get the focus if their window group is focused"));
931 DialogItemCheckButtonSetPtr(di, &dd->focus.new_focus_if_group);
932
933 di = DialogAddItem(table, DITEM_CHECKBUTTON);
934 DialogItemSetColSpan(di, 2);
935 DialogItemSetText(di, _("Only new dialog windows get the focus"));
936 DialogItemCheckButtonSetPtr(di, &dd->focus.popup_focus);
937
938 di = DialogAddItem(table, DITEM_CHECKBUTTON);
939 DialogItemSetColSpan(di, 2);
940 DialogItemSetText(di,
941 _
942 ("Only new dialogs whose owner is focused get the focus"));
943 DialogItemCheckButtonSetPtr(di, &dd->focus.popup_focus_if_group);
944
945 di = DialogAddItem(table, DITEM_CHECKBUTTON);
946 DialogItemSetColSpan(di, 2);
947 DialogItemSetText(di, _("Raise windows while switching focus"));
948 DialogItemCheckButtonSetPtr(di, &dd->focus.raise_focus);
949
950 di = DialogAddItem(table, DITEM_CHECKBUTTON);
951 DialogItemSetColSpan(di, 2);
952 DialogItemSetText(di,
953 _("Send mouse pointer to window while switching focus"));
954 DialogItemCheckButtonSetPtr(di, &dd->focus.warp_focus);
955
956 di = DialogAddItem(table, DITEM_CHECKBUTTON);
957 DialogItemSetColSpan(di, 2);
958 DialogItemSetText(di,
959 _("Always send mouse pointer to window on focus switch"));
960 DialogItemCheckButtonSetPtr(di, &dd->focus.warp_always);
961
962 di = DialogAddItem(table, DITEM_SEPARATOR);
963 DialogItemSetColSpan(di, 2);
964
965 di = DialogAddItem(table, DITEM_CHECKBUTTON);
966 DialogItemSetColSpan(di, 2);
967 DialogItemSetText(di, _("Raise windows automatically"));
968 DialogItemCheckButtonSetPtr(di, &dd->autoraise.enable);
969
970 di = DialogAddItem(table, DITEM_TEXT);
971 DialogItemSetFill(di, 0, 0);
972 DialogItemSetAlign(di, 0, 512);
973 DialogItemSetText(di, _("Autoraise delay:"));
974
975 di = DialogAddItem(table, DITEM_SLIDER);
976 DialogItemSliderSetBounds(di, 0, 300);
977 DialogItemSliderSetUnits(di, 10);
978 DialogItemSliderSetJump(di, 25);
979 DialogItemSliderSetValPtr(di, &dd->autoraise.time);
980
981 di = DialogAddItem(table, DITEM_SEPARATOR);
982 DialogItemSetColSpan(di, 2);
983
984 di = DialogAddItem(table, DITEM_CHECKBUTTON);
985 DialogItemSetColSpan(di, 2);
986 DialogItemSetText(di, _("Display and use focus list"));
987 DialogItemCheckButtonSetPtr(di, &dd->focuslist.enable);
988
989 di = DialogAddItem(table, DITEM_CHECKBUTTON);
990 DialogItemSetColSpan(di, 2);
991 DialogItemSetText(di, _("Include sticky windows in focus list"));
992 DialogItemCheckButtonSetPtr(di, &dd->focuslist.showsticky);
993
994 di = DialogAddItem(table, DITEM_CHECKBUTTON);
995 DialogItemSetColSpan(di, 2);
996 DialogItemSetText(di, _("Include shaded windows in focus list"));
997 DialogItemCheckButtonSetPtr(di, &dd->focuslist.showshaded);
998
999 di = DialogAddItem(table, DITEM_CHECKBUTTON);
1000 DialogItemSetColSpan(di, 2);
1001 DialogItemSetText(di, _("Include iconified windows in focus list"));
1002 DialogItemCheckButtonSetPtr(di, &dd->focuslist.showiconified);
1003
1004 di = DialogAddItem(table, DITEM_CHECKBUTTON);
1005 DialogItemSetColSpan(di, 2);
1006 DialogItemSetText(di, _("Include windows on other desks in focus list"));
1007 DialogItemCheckButtonSetPtr(di, &dd->focuslist.showalldesks);
1008
1009 di = DialogAddItem(table, DITEM_CHECKBUTTON);
1010 DialogItemSetColSpan(di, 2);
1011 DialogItemSetText(di, _("Focus windows while switching"));
1012 DialogItemCheckButtonSetPtr(di, &dd->focuslist.warpfocused);
1013
1014 di = DialogAddItem(table, DITEM_CHECKBUTTON);
1015 DialogItemSetColSpan(di, 2);
1016 DialogItemSetText(di, _("Outline windows while switching"));
1017 DialogItemCheckButtonSetPtr(di, &dd->focuslist.show_shape);
1018
1019 di = DialogAddItem(table, DITEM_CHECKBUTTON);
1020 DialogItemSetColSpan(di, 2);
1021 DialogItemSetText(di, _("Raise windows after focus switch"));
1022 DialogItemCheckButtonSetPtr(di, &dd->focuslist.raise_after_focus);
1023
1024 di = DialogAddItem(table, DITEM_CHECKBUTTON);
1025 DialogItemSetColSpan(di, 2);
1026 DialogItemSetText(di, _("Send mouse pointer to window after focus switch"));
1027 DialogItemCheckButtonSetPtr(di, &dd->focuslist.warp_after_focus);
1028
1029 di = DialogAddItem(table, DITEM_SEPARATOR);
1030 DialogItemSetColSpan(di, 2);
1031
1032 di = DialogAddItem(table, DITEM_TEXT);
1033 DialogItemSetColSpan(di, 2);
1034 DialogItemSetText(di,
1035 _
1036 ("Focuslist image display policy (if one operation fails, try the next):"));
1037
1038 radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
1039 DialogItemSetColSpan(di, 2);
1040 DialogItemSetText(di, _("First E Icon, then App Icon"));
1041 DialogItemRadioButtonSetFirst(di, radio);
1042 DialogItemRadioButtonGroupSetVal(di, EWIN_ICON_MODE_IMG_APP);
1043
1044 di = DialogAddItem(table, DITEM_RADIOBUTTON);
1045 DialogItemSetColSpan(di, 2);
1046 DialogItemSetText(di, _("First App Icon, then E Icon"));
1047 DialogItemRadioButtonSetFirst(di, radio);
1048 DialogItemRadioButtonGroupSetVal(di, EWIN_ICON_MODE_APP_IMG);
1049
1050 di = DialogAddItem(table, DITEM_RADIOBUTTON);
1051 DialogItemSetColSpan(di, 2);
1052 DialogItemSetText(di, _("None"));
1053 DialogItemRadioButtonSetFirst(di, radio);
1054 DialogItemRadioButtonGroupSetVal(di, EWIN_ICON_MODE_NONE);
1055 DialogItemRadioButtonGroupSetValPtr(radio, &dd->focuslist.icon_mode);
1056 }
1057
1058 const DialogDef DlgFocus = {
1059 "CONFIGURE_FOCUS",
1060 N_("Focus"), N_("Focus Settings"),
1061 sizeof(FocusDlgData),
1062 SOUND_SETTINGS_FOCUS,
1063 "pix/focus.png",
1064 N_("Enlightenment Focus\n" "Settings Dialog"),
1065 _DlgFillFocus,
1066 DLG_OAC, _DlgApplyFocus, NULL
1067 };
1068 #endif /* ENABLE_DIALOGS */
1069
1070 /*
1071 * Focus Module
1072 */
1073
1074 static int
FocusInitTimeout(void * data __UNUSED__)1075 FocusInitTimeout(void *data __UNUSED__)
1076 {
1077 FocusInit();
1078 return 0;
1079 }
1080
1081 static void
_FocusIdler(void * data __UNUSED__)1082 _FocusIdler(void *data __UNUSED__)
1083 {
1084 if (focus_inhibit)
1085 return;
1086 if (focus_pending_why)
1087 FocusSet();
1088 if (click_pending_update_grabs)
1089 doClickGrabsUpdate();
1090 if (focus_pending_raise)
1091 FocusRaisePending();
1092 }
1093
1094 static void
FocusSighan(int sig,void * prm __UNUSED__)1095 FocusSighan(int sig, void *prm __UNUSED__)
1096 {
1097 switch (sig)
1098 {
1099 case ESIGNAL_START:
1100 /* Delay focusing a bit to allow things to settle down */
1101 IdlerAdd(_FocusIdler, NULL);
1102 TIMER_ADD_NP(500, FocusInitTimeout, NULL);
1103 break;
1104
1105 case ESIGNAL_EXIT:
1106 FocusExit();
1107 break;
1108 }
1109 }
1110
1111 static void
FocusIpc(const char * params)1112 FocusIpc(const char *params)
1113 {
1114 const char *p;
1115 char cmd[128], prm[4096];
1116 int len;
1117
1118 cmd[0] = prm[0] = '\0';
1119 p = params;
1120 if (p)
1121 {
1122 len = 0;
1123 sscanf(p, "%100s %4000s %n", cmd, prm, &len);
1124 p += len;
1125 }
1126
1127 if (!p || cmd[0] == '?')
1128 {
1129 EWin *ewin;
1130
1131 ewin = GetFocusEwin();
1132 if (ewin)
1133 IpcPrintf("Focused: %#x\n", EwinGetClientXwin(ewin));
1134 else
1135 IpcPrintf("Focused: none\n");
1136 }
1137 else if (!strncmp(cmd, "mode", 2))
1138 {
1139 int mode = Conf.focus.mode;
1140
1141 if (!strcmp(prm, "click"))
1142 {
1143 mode = MODE_FOCUS_CLICK;
1144 Mode.grabs.pointer_grab_active = 1;
1145 }
1146 else if (!strcmp(prm, "clicknograb"))
1147 {
1148 mode = MODE_FOCUS_CLICK;
1149 Mode.grabs.pointer_grab_active = 0;
1150 }
1151 else if (!strcmp(prm, "pointer"))
1152 {
1153 mode = MODE_FOCUS_POINTER;
1154 }
1155 else if (!strcmp(prm, "sloppy"))
1156 {
1157 mode = MODE_FOCUS_SLOPPY;
1158 }
1159 else if (!strcmp(prm, "?"))
1160 {
1161 if (Conf.focus.mode == MODE_FOCUS_CLICK)
1162 {
1163 if (Mode.grabs.pointer_grab_active)
1164 p = "click";
1165 else
1166 p = "clicknograb";
1167 }
1168 else if (Conf.focus.mode == MODE_FOCUS_SLOPPY)
1169 p = "sloppy";
1170 else if (Conf.focus.mode == MODE_FOCUS_POINTER)
1171 p = "pointer";
1172 else
1173 p = "unknown";
1174 IpcPrintf("Focus Mode: %s\n", p);
1175 }
1176 else
1177 {
1178 IpcPrintf("Error: unknown focus type\n");
1179 }
1180 if (Conf.focus.mode != mode)
1181 {
1182 Conf.focus.mode = mode;
1183 ClickGrabsUpdate();
1184 autosave();
1185 }
1186 }
1187 else if (!strncmp(cmd, "next", 2))
1188 {
1189 /* Focus previously focused window */
1190 if (Conf.warplist.enable)
1191 WarpFocus(1);
1192 else
1193 FocusCycleEwin(1);
1194 }
1195 else if (!strncmp(cmd, "prev", 2))
1196 {
1197 /* Focus least recently focused window */
1198 if (Conf.warplist.enable)
1199 WarpFocus(-1);
1200 else
1201 FocusCycleEwin(-1);
1202 }
1203 }
1204
1205 static const IpcItem FocusIpcArray[] = {
1206 {
1207 FocusIpc,
1208 "focus", "sf",
1209 "Focus functions",
1210 " focus ? Show focus info\n"
1211 " focus mode Set focus mode. Modes:\n"
1212 " click: The traditional click-to-focus mode.\n"
1213 " clicknograb: A similar focus mode, but without the grabbing of the click\n"
1214 " (you cannot click anywhere in a window to focus it)\n"
1215 " pointer: The focus will follow the mouse pointer\n"
1216 " sloppy: The focus follows the mouse, but when over the desktop background\n"
1217 " the last window does not lose the focus\n"}
1218 ,
1219 };
1220
1221 static const CfgItem FocusCfgItems[] = {
1222 CFG_ITEM_INT(Conf.focus, mode, MODE_FOCUS_SLOPPY),
1223 CFG_ITEM_INT(Conf.focus, clickraises, 1),
1224 CFG_ITEM_BOOL(Conf.focus, transientsfollowleader, 1),
1225 CFG_ITEM_BOOL(Conf.focus, switchfortransientmap, 1),
1226 CFG_ITEM_BOOL(Conf.focus, all_new_windows_get_focus, 0),
1227 CFG_ITEM_BOOL(Conf.focus, new_windows_get_focus_if_group_focused, 1),
1228 CFG_ITEM_BOOL(Conf.focus, new_transients_get_focus, 0),
1229 CFG_ITEM_BOOL(Conf.focus, new_transients_get_focus_if_group_focused, 1),
1230 CFG_ITEM_BOOL(Conf.focus, raise_on_next, 1),
1231 CFG_ITEM_BOOL(Conf.focus, warp_on_next, 0),
1232 CFG_ITEM_BOOL(Conf.focus, warp_always, 0),
1233
1234 CFG_ITEM_BOOL(Conf, autoraise.enable, 0),
1235 CFG_ITEM_INT(Conf, autoraise.delay, 500),
1236 };
1237
1238 /*
1239 * Module descriptor
1240 */
1241 extern const EModule ModFocus;
1242
1243 const EModule ModFocus = {
1244 "focus", NULL,
1245 FocusSighan,
1246 MOD_ITEMS(FocusIpcArray),
1247 MOD_ITEMS(FocusCfgItems)
1248 };
1249