chiark / gitweb /
Imported Debian patch 1.0.0-5
[e16] / src / focus.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 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 "E.h"
25 #include "cursors.h"
26 #include "desktops.h"           /* FIXME - Should not be here */
27 #include "dialog.h"
28 #include "emodule.h"
29 #include "ewins.h"
30 #include "focus.h"
31 #include "grabs.h"
32 #include "hints.h"
33 #include "settings.h"
34 #include "timers.h"
35 #include "xwin.h"
36
37 #define EwinListFocusRaise(ewin) EobjListFocusRaise(EoObj(ewin))
38
39 static char         focus_inhibit = 1;
40 static char         focus_is_set = 0;
41 static char         click_pending_update_grabs = 0;
42 static int          focus_pending_why = 0;
43 static EWin        *focus_pending_ewin = NULL;
44 static EWin        *focus_pending_new = NULL;
45 static EWin        *focus_pending_raise = NULL;
46 static Timer       *focus_timer_autoraise = NULL;
47
48 void
49 FocusEnable(int on)
50 {
51    if (on)
52      {
53         if (focus_inhibit > 0)
54            focus_inhibit--;
55      }
56    else
57      {
58         focus_inhibit++;
59      }
60
61    if (EDebug(EDBUG_TYPE_FOCUS))
62       Eprintf("FocusEnable inhibit=%d\n", focus_inhibit);
63 }
64
65 /*
66  * Return !0 if it is OK to focus ewin.
67  */
68 static int
69 FocusEwinValid(EWin * ewin, int want_on_screen, int click, int want_visible)
70 {
71    if (!ewin)
72       return 0;
73
74 #if 0
75    Eprintf("FocusEwinValid %#lx %s: st=%d sh=%d inh=%d cl=%d(%d) vis=%d(%d)\n",
76            EwinGetClientXwin(ewin), EwinGetTitle(ewin),
77            ewin->state.state, EoIsShown(ewin), ewin->state.inhibit_focus,
78            click, ewin->props.focusclick, want_visible, ewin->state.visibility);
79 #endif
80
81    if (ewin->state.inhibit_focus)
82       return 0;
83
84    if (!EwinIsMapped(ewin) || !EoIsShown(ewin))
85       return 0;
86
87    if (ewin->props.focusclick && !click)
88       return 0;
89
90    if (want_visible && ewin->state.visibility == VisibilityFullyObscured)
91       return 0;
92
93    return !want_on_screen || EwinIsOnScreen(ewin);
94 }
95
96 /*
97  * Return the ewin to focus after entering area or losing focused window.
98  */
99 static EWin        *
100 FocusEwinSelect(void)
101 {
102    EWin               *const *lst, *ewin;
103    int                 num, i;
104
105    switch (Conf.focus.mode)
106      {
107      default:
108      case MODE_FOCUS_POINTER:
109         ewin = GetEwinPointerInClient();
110         if (ewin && !FocusEwinValid(ewin, 1, 0, 0))
111            ewin = NULL;
112         break;
113
114      case MODE_FOCUS_SLOPPY:
115         ewin = GetEwinPointerInClient();
116         if (ewin && FocusEwinValid(ewin, 1, 0, 0))
117            break;
118         goto do_select;
119
120      case MODE_FOCUS_CLICK:
121         goto do_select;
122
123       do_select:
124         ewin = NULL;
125         lst = EwinListFocusGet(&num);
126         for (i = 0; i < num; i++)
127           {
128              if (!FocusEwinValid(lst[i], 1, 0, 0) ||
129                  lst[i]->props.skip_focuslist)
130                 continue;
131              ewin = lst[i];
132              break;
133           }
134         break;
135      }
136
137    return ewin;
138 }
139
140 static int
141 AutoraiseTimeout(void *data)
142 {
143    EWin               *ewin = (EWin *) data;
144
145    if (Conf.focus.mode == MODE_FOCUS_CLICK)
146       goto done;
147
148    if (EwinFindByPtr(ewin))     /* May be gone */
149       EwinRaise(ewin);
150
151  done:
152    focus_timer_autoraise = NULL;
153    return 0;
154 }
155
156 static void
157 FocusRaisePending(void)
158 {
159    EWin               *ewin = focus_pending_raise;
160    unsigned int        mask;
161
162    /* The focusing cycle ends when no more modifiers are depressed */
163    mask = 0;
164    EQueryPointer(NULL, NULL, NULL, NULL, &mask);
165    if ((mask & Mode.masks.mod_key_mask) != 0)
166       return;
167
168    if (EwinFindByPtr(ewin))     /* May be gone */
169       EwinListFocusRaise(ewin);
170
171    GrabKeyboardRelease();
172
173    focus_pending_raise = NULL;
174 }
175
176 /*
177  * dir > 0: Focus previously focused window
178  * else   : Focus least recently focused window
179  */
180 static void
181 FocusCycleEwin(int dir)
182 {
183    EWin               *const *lst;
184    EWin               *ewin;
185    int                 i, j, num;
186
187    lst = EwinListFocusGet(&num);
188    if (num <= 1)
189       return;
190
191    dir = (dir > 0) ? 1 : -1;
192
193    for (j = 0; j < num; j++)
194      {
195         if (lst[j] == Mode.focuswin)
196            break;
197      }
198    for (i = 1; i < num; i++)
199      {
200         ewin = lst[(j + i * dir + num) % num];
201         if (!FocusEwinValid(ewin, 1, 0, 0) || ewin->props.skip_focuslist)
202            continue;
203         FocusToEWin(ewin, FOCUS_PREV);
204         break;
205      }
206 }
207
208 static void
209 ClickGrabsSet(EWin * ewin)
210 {
211    int                 set = 0;
212
213    if ((Conf.focus.clickraises && !EwinListStackIsRaised(ewin)) ||
214        (!ewin->state.active && !ewin->state.inhibit_focus))
215       set = 1;
216
217    if (set)
218      {
219         if (!ewin->state.click_grab_isset)
220           {
221              GrabButtonSet(AnyButton, AnyModifier, EwinGetContainerWin(ewin),
222                            ButtonPressMask, ECSR_PGRAB, 1);
223              if (EDebug(EDBUG_TYPE_GRABS))
224                 Eprintf("ClickGrabsSet: %#lx set %s\n",
225                         EwinGetClientXwin(ewin), EwinGetTitle(ewin));
226              ewin->state.click_grab_isset = 1;
227           }
228      }
229    else
230      {
231         if (ewin->state.click_grab_isset)
232           {
233              GrabButtonRelease(AnyButton, AnyModifier,
234                                EwinGetContainerWin(ewin));
235              if (EDebug(EDBUG_TYPE_GRABS))
236                 Eprintf("ClickGrabsSet: %#lx unset %s\n",
237                         EwinGetClientXwin(ewin), EwinGetTitle(ewin));
238              ewin->state.click_grab_isset = 0;
239           }
240      }
241 }
242
243 static void
244 FocusEwinSetActive(EWin * ewin, int active)
245 {
246    if (ewin->state.active == (unsigned)active)
247       return;
248
249    ewin->state.active = active;
250    EwinBorderUpdateState(ewin);
251    EwinUpdateOpacity(ewin);
252
253    if (active && ewin->state.attention)
254      {
255         ewin->state.attention = 0;
256         HintsSetWindowState(ewin);
257      }
258 }
259
260 static void
261 doClickGrabsUpdate(void)
262 {
263    EWin               *const *lst, *ewin;
264    int                 i, num;
265
266    lst = EwinListGetAll(&num);
267    for (i = 0; i < num; i++)
268      {
269         ewin = lst[i];
270         ClickGrabsSet(ewin);
271      }
272    click_pending_update_grabs = 0;
273 }
274
275 void
276 ClickGrabsUpdate(void)
277 {
278    click_pending_update_grabs = 1;
279 }
280
281 static void
282 doFocusToEwin(EWin * ewin, int why)
283 {
284    int                 do_focus = 0;
285    int                 do_raise = 0, do_warp = 0;
286
287    if (focus_inhibit)
288       return;
289
290    if (EDebug(EDBUG_TYPE_FOCUS))
291       Eprintf("doFocusToEWin %#lx %s why=%d\n",
292               (ewin) ? EwinGetClientXwin(ewin) : 0,
293               (ewin) ? EwinGetTitle(ewin) : "None", why);
294
295    switch (why)
296      {
297      case FOCUS_NEXT:
298      case FOCUS_PREV:
299         if (Conf.focus.raise_on_next)
300            do_raise = 1;
301         if (Conf.focus.warp_on_next)
302            do_warp = 1;
303         /* Fall thru */
304      default:
305      case FOCUS_SET:
306      case FOCUS_ENTER:
307      case FOCUS_LEAVE:          /* Unused */
308      case FOCUS_CLICK:
309         if (ewin && ewin == Mode.focuswin)
310            return;
311         if (ewin == NULL)       /* Unfocus */
312            break;
313         if (!FocusEwinValid(ewin, 1, why == FOCUS_CLICK, 0))
314            return;
315         break;
316
317      case FOCUS_INIT:
318      case FOCUS_DESK_ENTER:
319         ewin = FocusEwinSelect();
320         break;
321
322      case FOCUS_DESK_LEAVE:
323         focus_is_set = 0;
324      case FOCUS_NONE:
325         ewin = NULL;
326         if (ewin == Mode.focuswin)
327            return;
328         break;
329
330      case FOCUS_EWIN_UNMAP:
331         if (Mode.focuswin)
332            return;
333         ewin = FocusEwinSelect();
334         if (ewin == Mode.focuswin)
335            ewin = NULL;
336         break;
337
338      case FOCUS_EWIN_NEW:
339         if (Conf.focus.all_new_windows_get_focus)
340            do_focus = 1;
341         else if (Mode.place.doing_manual)
342            do_focus = 1;
343
344         if (ewin->props.focus_when_mapped)
345            do_focus = 2;
346
347         if (EwinIsTransient(ewin))
348           {
349              if (Conf.focus.new_transients_get_focus)
350                {
351                   do_focus = 2;
352                }
353              else if (Conf.focus.new_transients_get_focus_if_group_focused)
354                {
355                   EWin               *ewin2;
356
357                   ewin2 = EwinFindByClient(EwinGetTransientFor(ewin));
358                   if ((ewin2) && (Mode.focuswin == ewin2))
359                      do_focus = 2;
360                }
361
362              if (do_focus == 2)
363                 DeskGotoByEwin(ewin);
364           }
365
366         if (!do_focus)
367            return;
368         if (!FocusEwinValid(ewin, 1, 0, 0))
369            return;
370         break;
371      }
372
373    if (ewin == Mode.focuswin && focus_is_set)
374       return;
375
376    /* Check if ewin is a valid focus window target */
377
378    if (!ewin)
379       goto done;
380
381    /* NB! ewin != NULL */
382
383    if (why != FOCUS_CLICK && ewin->props.focusclick)
384       return;
385
386    if (Conf.autoraise.enable)
387      {
388         TIMER_DEL(focus_timer_autoraise);
389
390         if (Conf.focus.mode != MODE_FOCUS_CLICK)
391            TIMER_ADD(focus_timer_autoraise, 0.001 * Conf.autoraise.delay,
392                      AutoraiseTimeout, ewin);
393      }
394
395    if (do_raise)
396       EwinRaise(ewin);
397
398    if (Conf.focus.warp_always)
399       do_warp = 1;
400    if (do_warp)
401       EwinWarpTo(ewin);
402
403    switch (why)
404      {
405      case FOCUS_PREV:
406      case FOCUS_NEXT:
407         GrabKeyboardSet(VROOT); /* Causes idler to be called on KeyRelease */
408         focus_pending_raise = ewin;
409         break;
410      case FOCUS_DESK_ENTER:
411         if (Conf.focus.mode == MODE_FOCUS_CLICK)
412            break;
413      default:
414      case FOCUS_INIT:
415         EwinListFocusRaise(ewin);
416         break;
417      }
418
419    SoundPlay(SOUND_FOCUS_SET);
420  done:
421
422    ClickGrabsUpdate();
423
424    /* Unset old focus window (if any) highlighting */
425    if (Mode.focuswin)
426       FocusEwinSetActive(Mode.focuswin, 0);
427    ICCCM_Cmap(ewin);
428
429    /* Quit if pointer is not on our screen */
430    if (!EQueryPointer(NULL, NULL, NULL, NULL, NULL))
431      {
432         Mode.focuswin = NULL;
433         return;
434      }
435
436    /* Set new focus window (if any) highlighting */
437    Mode.focuswin = ewin;
438    if (Mode.focuswin)
439       FocusEwinSetActive(Mode.focuswin, 1);
440
441    if (why == FOCUS_DESK_LEAVE)
442       return;
443
444    ICCCM_Focus(ewin);
445    focus_is_set = 1;
446 }
447
448 void
449 FocusToEWin(EWin * ewin, int why)
450 {
451    if (EDebug(EDBUG_TYPE_FOCUS))
452       Eprintf("FocusToEWin(%d) %#lx %s why=%d\n", focus_inhibit,
453               (ewin) ? EwinGetClientXwin(ewin) : 0,
454               (ewin) ? EwinGetTitle(ewin) : "None", why);
455
456    switch (why)
457      {
458      case FOCUS_EWIN_NEW:
459         if (!FocusEwinValid(ewin, 0, 0, 0))
460            break;
461         focus_pending_new = ewin;
462         focus_pending_why = why;
463         focus_pending_ewin = ewin;
464         break;
465
466      default:
467         if (ewin && !FocusEwinValid(ewin, 0, why == FOCUS_CLICK, 0))
468            break;
469         focus_pending_why = why;
470         focus_pending_ewin = ewin;
471         break;
472
473      case FOCUS_EWIN_UNMAP:
474         focus_pending_why = why;
475         focus_pending_ewin = NULL;
476         if (ewin == Mode.focuswin)
477           {
478              Mode.focuswin = NULL;
479              focus_is_set = 0;
480              if (!EoIsGone(ewin))
481                 FocusEwinSetActive(ewin, 0);
482           }
483         if (ewin == focus_pending_new)
484            focus_pending_new = NULL;
485         break;
486      }
487 }
488
489 static void
490 FocusSet(void)
491 {
492    if (focus_pending_new && Conf.focus.all_new_windows_get_focus)
493       doFocusToEwin(focus_pending_new, FOCUS_EWIN_NEW);
494    else
495       doFocusToEwin(focus_pending_ewin, focus_pending_why);
496    focus_pending_why = 0;
497    focus_pending_ewin = focus_pending_new = NULL;
498 }
499
500 void
501 FocusNewDeskBegin(void)
502 {
503    /* Freeze keyboard */
504    XGrabKeyboard(disp, WinGetXwin(VROOT), False, GrabModeAsync,
505                  GrabModeSync, CurrentTime);
506
507    focus_pending_new = NULL;
508    doFocusToEwin(NULL, FOCUS_DESK_LEAVE);
509 }
510
511 void
512 FocusNewDesk(void)
513 {
514    FocusToEWin(NULL, FOCUS_DESK_ENTER);
515
516    /* Unfreeze keyboard */
517    XUngrabKeyboard(disp, CurrentTime);
518 }
519
520 void
521 FocusCheckScreen(void)
522 {
523    if (EQueryPointer(NULL, NULL, NULL, NULL, NULL))
524       return;                   /* On screen */
525
526    FocusToEWin(NULL, FOCUS_DESK_LEAVE);
527 }
528
529 static void
530 FocusInit(void)
531 {
532    /* Start focusing windows */
533    FocusEnable(1);
534
535    FocusToEWin(NULL, FOCUS_INIT);
536    FocusSet();
537
538    /* Enable window placement features */
539    Mode.place.enable_features++;
540 }
541
542 static void
543 FocusExit(void)
544 {
545 }
546
547 /*
548  * Focus event handlers
549  */
550
551 void
552 FocusHandleEnter(EWin * ewin, XEvent * ev)
553 {
554    Window              win = ev->xcrossing.window;
555
556    Mode.mouse_over_ewin = ewin;
557
558    if (!ewin)
559      {
560         /* Entering root may mean entering this screen */
561         if (win == WinGetXwin(VROOT) &&
562             (ev->xcrossing.mode == NotifyNormal &&
563              ev->xcrossing.detail != NotifyInferior))
564           {
565              FocusToEWin(NULL, FOCUS_DESK_ENTER);
566              return;
567           }
568      }
569
570    if (ev->xcrossing.mode == NotifyUngrab &&
571        ev->xcrossing.detail == NotifyNonlinearVirtual)
572       return;
573
574    switch (Conf.focus.mode)
575      {
576      default:
577      case MODE_FOCUS_CLICK:
578         break;
579      case MODE_FOCUS_SLOPPY:
580         if (FocusEwinValid(ewin, 1, 0, 0))
581            FocusToEWin(ewin, FOCUS_ENTER);
582         break;
583      case MODE_FOCUS_POINTER:
584         if (!ewin || FocusEwinValid(ewin, 1, 0, 0))
585            FocusToEWin(ewin, FOCUS_ENTER);
586         break;
587      }
588 }
589
590 void
591 FocusHandleLeave(EWin * ewin __UNUSED__, XEvent * ev)
592 {
593    Window              win = ev->xcrossing.window;
594
595    /* Leaving root may mean entering other screen */
596    if (win == WinGetXwin(VROOT) &&
597        (ev->xcrossing.mode == NotifyNormal &&
598         ev->xcrossing.detail != NotifyInferior))
599       FocusToEWin(NULL, FOCUS_DESK_LEAVE);
600 }
601
602 void
603 FocusHandleChange(EWin * ewin __UNUSED__, XEvent * ev __UNUSED__)
604 {
605 #if 0                           /* Debug */
606    if (ewin == Mode.focuswin && ev->type == FocusOut)
607       Eprintf("??? Lost focus: %s\n", EwinGetTitle(ewin));
608 #endif
609 }
610
611 void
612 FocusHandleClick(EWin * ewin, Win win)
613 {
614    if (Conf.focus.clickraises)
615       EwinRaise(ewin);
616
617    FocusToEWin(ewin, FOCUS_CLICK);
618
619    /* Allow click to pass thorugh */
620    if (EDebug(EDBUG_TYPE_GRABS))
621       Eprintf("FocusHandleClick %#lx %#lx\n", WinGetXwin(win),
622               EwinGetContainerXwin(ewin));
623    if (win == EwinGetContainerWin(ewin))
624      {
625         ESync(ESYNC_FOCUS);
626         XAllowEvents(disp, ReplayPointer, CurrentTime);
627         ESync(ESYNC_FOCUS);
628      }
629 }
630
631 #if ENABLE_DIALOGS
632 /*      
633  * Configuration dialog
634  */
635 static int          tmp_focus;
636 static char         tmp_clickalways;
637 static char         tmp_new_focus;
638 static char         tmp_popup_focus;
639 static char         tmp_owner_popup_focus;
640 static char         tmp_raise_focus;
641 static char         tmp_warp_focus;
642 static char         tmp_warp_always;
643
644 static char         tmp_display_warp;
645 static char         tmp_warp_after_focus;
646 static char         tmp_raise_after_focus;
647 static char         tmp_showsticky;
648 static char         tmp_showshaded;
649 static char         tmp_showiconified;
650 static char         tmp_showalldesks;
651 static char         tmp_warpfocused;
652 static int          tmp_warp_icon_mode;
653
654 static void
655 CB_ConfigureFocus(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
656 {
657    if (val < 2)
658      {
659         Conf.focus.mode = tmp_focus;
660         Conf.focus.all_new_windows_get_focus = tmp_new_focus;
661         Conf.focus.new_transients_get_focus = tmp_popup_focus;
662         Conf.focus.new_transients_get_focus_if_group_focused =
663            tmp_owner_popup_focus;
664         Conf.focus.raise_on_next = tmp_raise_focus;
665         Conf.focus.warp_on_next = tmp_warp_focus;
666         Conf.focus.warp_always = tmp_warp_always;
667
668         Conf.warplist.enable = tmp_display_warp;
669         Conf.warplist.warp_on_select = tmp_warp_after_focus;
670         Conf.warplist.raise_on_select = tmp_raise_after_focus;
671         Conf.warplist.showsticky = tmp_showsticky;
672         Conf.warplist.showshaded = tmp_showshaded;
673         Conf.warplist.showiconified = tmp_showiconified;
674         Conf.warplist.showalldesks = tmp_showalldesks;
675         Conf.warplist.warpfocused = tmp_warpfocused;
676         Conf.warplist.icon_mode = tmp_warp_icon_mode;
677
678         Conf.focus.clickraises = tmp_clickalways;
679         ClickGrabsUpdate();
680      }
681    autosave();
682 }
683
684 static void
685 _DlgFillFocus(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
686 {
687    DItem              *di, *radio, *radio2;
688
689    tmp_focus = Conf.focus.mode;
690    tmp_new_focus = Conf.focus.all_new_windows_get_focus;
691    tmp_popup_focus = Conf.focus.new_transients_get_focus;
692    tmp_owner_popup_focus = Conf.focus.new_transients_get_focus_if_group_focused;
693    tmp_raise_focus = Conf.focus.raise_on_next;
694    tmp_warp_focus = Conf.focus.warp_on_next;
695    tmp_warp_always = Conf.focus.warp_always;
696
697    tmp_raise_after_focus = Conf.warplist.raise_on_select;
698    tmp_warp_after_focus = Conf.warplist.warp_on_select;
699    tmp_display_warp = Conf.warplist.enable;
700    tmp_showsticky = Conf.warplist.showsticky;
701    tmp_showshaded = Conf.warplist.showshaded;
702    tmp_showiconified = Conf.warplist.showiconified;
703    tmp_showalldesks = Conf.warplist.showalldesks;
704    tmp_warpfocused = Conf.warplist.warpfocused;
705    tmp_warp_icon_mode = Conf.warplist.icon_mode;
706
707    tmp_clickalways = Conf.focus.clickraises;
708
709    DialogItemTableSetOptions(table, 2, 0, 0, 0);
710
711    radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
712    DialogItemSetColSpan(di, 2);
713    DialogItemSetText(di, _("Focus follows pointer"));
714    DialogItemRadioButtonSetFirst(di, radio);
715    DialogItemRadioButtonGroupSetVal(di, 0);
716
717    di = DialogAddItem(table, DITEM_RADIOBUTTON);
718    DialogItemSetColSpan(di, 2);
719    DialogItemSetText(di, _("Focus follows pointer sloppily"));
720    DialogItemRadioButtonSetFirst(di, radio);
721    DialogItemRadioButtonGroupSetVal(di, 1);
722
723    di = DialogAddItem(table, DITEM_RADIOBUTTON);
724    DialogItemSetColSpan(di, 2);
725    DialogItemSetText(di, _("Focus follows mouse clicks"));
726    DialogItemRadioButtonSetFirst(di, radio);
727    DialogItemRadioButtonGroupSetVal(di, 2);
728    DialogItemRadioButtonGroupSetValPtr(radio, &tmp_focus);
729
730    di = DialogAddItem(table, DITEM_SEPARATOR);
731    DialogItemSetColSpan(di, 2);
732
733    di = DialogAddItem(table, DITEM_CHECKBUTTON);
734    DialogItemSetColSpan(di, 2);
735    DialogItemSetText(di, _("Clicking in a window always raises it"));
736    DialogItemCheckButtonSetPtr(di, &tmp_clickalways);
737
738    di = DialogAddItem(table, DITEM_SEPARATOR);
739    DialogItemSetColSpan(di, 2);
740
741    di = DialogAddItem(table, DITEM_CHECKBUTTON);
742    DialogItemSetColSpan(di, 2);
743    DialogItemSetText(di, _("All new windows first get the focus"));
744    DialogItemCheckButtonSetPtr(di, &tmp_new_focus);
745
746    di = DialogAddItem(table, DITEM_CHECKBUTTON);
747    DialogItemSetColSpan(di, 2);
748    DialogItemSetText(di, _("Only new dialog windows get the focus"));
749    DialogItemCheckButtonSetPtr(di, &tmp_popup_focus);
750
751    di = DialogAddItem(table, DITEM_CHECKBUTTON);
752    DialogItemSetColSpan(di, 2);
753    DialogItemSetText(di,
754                      _
755                      ("Only new dialogs whose owner is focused get the focus"));
756    DialogItemCheckButtonSetPtr(di, &tmp_owner_popup_focus);
757
758    di = DialogAddItem(table, DITEM_CHECKBUTTON);
759    DialogItemSetColSpan(di, 2);
760    DialogItemSetText(di, _("Raise windows while switching focus"));
761    DialogItemCheckButtonSetPtr(di, &tmp_raise_focus);
762
763    di = DialogAddItem(table, DITEM_CHECKBUTTON);
764    DialogItemSetColSpan(di, 2);
765    DialogItemSetText(di,
766                      _("Send mouse pointer to window while switching focus"));
767    DialogItemCheckButtonSetPtr(di, &tmp_warp_focus);
768
769    di = DialogAddItem(table, DITEM_CHECKBUTTON);
770    DialogItemSetColSpan(di, 2);
771    DialogItemSetText(di,
772                      _("Always send mouse pointer to window on focus switch"));
773    DialogItemCheckButtonSetPtr(di, &tmp_warp_always);
774
775    di = DialogAddItem(table, DITEM_SEPARATOR);
776    DialogItemSetColSpan(di, 2);
777
778    di = DialogAddItem(table, DITEM_CHECKBUTTON);
779    DialogItemSetColSpan(di, 2);
780    DialogItemSetText(di, _("Display and use focus list"));
781    DialogItemCheckButtonSetPtr(di, &tmp_display_warp);
782
783    di = DialogAddItem(table, DITEM_CHECKBUTTON);
784    DialogItemSetColSpan(di, 2);
785    DialogItemSetText(di, _("Include sticky windows in focus list"));
786    DialogItemCheckButtonSetPtr(di, &tmp_showsticky);
787
788    di = DialogAddItem(table, DITEM_CHECKBUTTON);
789    DialogItemSetColSpan(di, 2);
790    DialogItemSetText(di, _("Include shaded windows in focus list"));
791    DialogItemCheckButtonSetPtr(di, &tmp_showshaded);
792
793    di = DialogAddItem(table, DITEM_CHECKBUTTON);
794    DialogItemSetColSpan(di, 2);
795    DialogItemSetText(di, _("Include iconified windows in focus list"));
796    DialogItemCheckButtonSetPtr(di, &tmp_showiconified);
797
798    di = DialogAddItem(table, DITEM_CHECKBUTTON);
799    DialogItemSetColSpan(di, 2);
800    DialogItemSetText(di, _("Include windows on other desks in focus list"));
801    DialogItemCheckButtonSetPtr(di, &tmp_showalldesks);
802
803    di = DialogAddItem(table, DITEM_CHECKBUTTON);
804    DialogItemSetColSpan(di, 2);
805    DialogItemSetText(di, _("Focus windows while switching"));
806    DialogItemCheckButtonSetPtr(di, &tmp_warpfocused);
807
808    di = DialogAddItem(table, DITEM_CHECKBUTTON);
809    DialogItemSetColSpan(di, 2);
810    DialogItemSetText(di, _("Raise windows after focus switch"));
811    DialogItemCheckButtonSetPtr(di, &tmp_raise_after_focus);
812
813    di = DialogAddItem(table, DITEM_CHECKBUTTON);
814    DialogItemSetColSpan(di, 2);
815    DialogItemSetText(di, _("Send mouse pointer to window after focus switch"));
816    DialogItemCheckButtonSetPtr(di, &tmp_warp_after_focus);
817
818    di = DialogAddItem(table, DITEM_SEPARATOR);
819    DialogItemSetColSpan(di, 2);
820
821    di = DialogAddItem(table, DITEM_TEXT);
822    DialogItemSetColSpan(di, 2);
823    DialogItemSetText(di,
824                      _
825                      ("Focuslist image display policy (if one operation fails, try the next):"));
826
827    radio2 = di = DialogAddItem(table, DITEM_RADIOBUTTON);
828    DialogItemSetColSpan(di, 2);
829    DialogItemSetText(di, _("First E Icon, then App Icon"));
830    DialogItemRadioButtonSetFirst(di, radio2);
831    DialogItemRadioButtonGroupSetVal(di, 3);
832
833    di = DialogAddItem(table, DITEM_RADIOBUTTON);
834    DialogItemSetColSpan(di, 2);
835    DialogItemSetText(di, _("First App Icon, then E Icon"));
836    DialogItemRadioButtonSetFirst(di, radio2);
837    DialogItemRadioButtonGroupSetVal(di, 4);
838
839    di = DialogAddItem(table, DITEM_RADIOBUTTON);
840    DialogItemSetColSpan(di, 2);
841    DialogItemSetText(di, _("None"));
842    DialogItemRadioButtonSetFirst(di, radio2);
843    DialogItemRadioButtonGroupSetVal(di, 0);
844    DialogItemRadioButtonGroupSetValPtr(radio2, &tmp_warp_icon_mode);
845 }
846
847 const DialogDef     DlgFocus = {
848    "CONFIGURE_FOCUS",
849    N_("Focus"),
850    N_("Focus Settings"),
851    SOUND_SETTINGS_FOCUS,
852    "pix/focus.png",
853    N_("Enlightenment Focus\n" "Settings Dialog\n"),
854    _DlgFillFocus,
855    DLG_OAC, CB_ConfigureFocus,
856 };
857 #endif /* ENABLE_DIALOGS */
858
859 /*
860  * Focus Module
861  */
862
863 static int
864 FocusInitTimeout(void *data __UNUSED__)
865 {
866    FocusInit();
867    return 0;
868 }
869
870 static void
871 _FocusIdler(void *data __UNUSED__)
872 {
873    if (!focus_inhibit && focus_pending_why)
874       FocusSet();
875    if (click_pending_update_grabs)
876       doClickGrabsUpdate();
877    if (focus_pending_raise)
878       FocusRaisePending();
879 }
880
881 static void
882 FocusSighan(int sig, void *prm __UNUSED__)
883 {
884    Timer              *focus_init_timer;
885
886    switch (sig)
887      {
888      case ESIGNAL_START:
889         /* Delay focusing a bit to allow things to settle down */
890         IdlerAdd(_FocusIdler, NULL);
891         TIMER_ADD(focus_init_timer, 0.5, FocusInitTimeout, NULL);
892         break;
893
894      case ESIGNAL_EXIT:
895         FocusExit();
896         break;
897      }
898 }
899
900 static void
901 FocusIpc(const char *params)
902 {
903    const char         *p;
904    char                cmd[128], prm[4096];
905    int                 len;
906
907    cmd[0] = prm[0] = '\0';
908    p = params;
909    if (p)
910      {
911         len = 0;
912         sscanf(p, "%100s %4000s %n", cmd, prm, &len);
913         p += len;
914      }
915
916    if (!p || cmd[0] == '?')
917      {
918         EWin               *ewin;
919
920         ewin = GetFocusEwin();
921         if (ewin)
922            IpcPrintf("Focused: %#lx\n", EwinGetClientXwin(ewin));
923         else
924            IpcPrintf("Focused: none\n");
925      }
926    else if (!strncmp(cmd, "mode", 2))
927      {
928         int                 mode = Conf.focus.mode;
929
930         if (!strcmp(prm, "click"))
931           {
932              mode = MODE_FOCUS_CLICK;
933              Mode.grabs.pointer_grab_active = 1;
934           }
935         else if (!strcmp(prm, "clicknograb"))
936           {
937              mode = MODE_FOCUS_CLICK;
938              Mode.grabs.pointer_grab_active = 0;
939           }
940         else if (!strcmp(prm, "pointer"))
941           {
942              mode = MODE_FOCUS_POINTER;
943           }
944         else if (!strcmp(prm, "sloppy"))
945           {
946              mode = MODE_FOCUS_SLOPPY;
947           }
948         else if (!strcmp(prm, "?"))
949           {
950              if (Conf.focus.mode == MODE_FOCUS_CLICK)
951                {
952                   if (Mode.grabs.pointer_grab_active)
953                      p = "click";
954                   else
955                      p = "clicknograb";
956                }
957              else if (Conf.focus.mode == MODE_FOCUS_SLOPPY)
958                 p = "sloppy";
959              else if (Conf.focus.mode == MODE_FOCUS_POINTER)
960                 p = "pointer";
961              else
962                 p = "unknown";
963              IpcPrintf("Focus Mode: %s\n", p);
964           }
965         else
966           {
967              IpcPrintf("Error: unknown focus type\n");
968           }
969         if (Conf.focus.mode != mode)
970           {
971              Conf.focus.mode = mode;
972              ClickGrabsUpdate();
973              autosave();
974           }
975      }
976    else if (!strncmp(cmd, "next", 2))
977      {
978         /* Focus previously focused window */
979         if (Conf.warplist.enable)
980            WarpFocus(1);
981         else
982            FocusCycleEwin(1);
983      }
984    else if (!strncmp(cmd, "prev", 2))
985      {
986         /* Focus least recently focused window */
987         if (Conf.warplist.enable)
988            WarpFocus(-1);
989         else
990            FocusCycleEwin(-1);
991      }
992 }
993
994 static const IpcItem FocusIpcArray[] = {
995    {
996     FocusIpc,
997     "focus", "sf",
998     "Focus functions",
999     "  focus ?                     Show focus info\n"
1000     "  focus mode                  Set focus mode. Modes:\n"
1001     "    click:       The traditional click-to-focus mode.\n"
1002     "    clicknograb: A similar focus mode, but without the grabbing of the click\n"
1003     "      (you cannot click anywhere in a window to focus it)\n"
1004     "    pointer:     The focus will follow the mouse pointer\n"
1005     "    sloppy:      The focus follows the mouse, but when over the desktop background\n"
1006     "                 the last window does not lose the focus\n"}
1007    ,
1008 };
1009 #define N_IPC_FUNCS (sizeof(FocusIpcArray)/sizeof(IpcItem))
1010
1011 static const CfgItem FocusCfgItems[] = {
1012    CFG_ITEM_INT(Conf.focus, mode, MODE_FOCUS_SLOPPY),
1013    CFG_ITEM_BOOL(Conf.focus, clickraises, 1),
1014    CFG_ITEM_BOOL(Conf.focus, transientsfollowleader, 1),
1015    CFG_ITEM_BOOL(Conf.focus, switchfortransientmap, 1),
1016    CFG_ITEM_BOOL(Conf.focus, all_new_windows_get_focus, 0),
1017    CFG_ITEM_BOOL(Conf.focus, new_transients_get_focus, 0),
1018    CFG_ITEM_BOOL(Conf.focus, new_transients_get_focus_if_group_focused, 1),
1019    CFG_ITEM_BOOL(Conf.focus, raise_on_next, 1),
1020    CFG_ITEM_BOOL(Conf.focus, warp_on_next, 0),
1021    CFG_ITEM_BOOL(Conf.focus, warp_always, 0),
1022
1023    CFG_ITEM_BOOL(Conf, autoraise.enable, 0),
1024    CFG_ITEM_INT(Conf, autoraise.delay, 500),
1025 };
1026 #define N_CFG_ITEMS (sizeof(FocusCfgItems)/sizeof(CfgItem))
1027
1028 /*
1029  * Module descriptor
1030  */
1031 extern const EModule ModFocus;
1032 const EModule       ModFocus = {
1033    "focus", NULL,
1034    FocusSighan,
1035    {N_IPC_FUNCS, FocusIpcArray},
1036    {N_CFG_ITEMS, FocusCfgItems}
1037 };