e16/src/focus.c

1250 lines
32 KiB
C

/*
* Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
* Copyright (C) 2004-2022 Kim Woelders
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of the Software, its documentation and marketing & publicity
* materials, and acknowledgment shall be given in the documentation, materials
* and software packages that this Software was used.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <X11/Xlib.h>
#include "E.h"
#include "cursors.h"
#include "desktops.h" /* FIXME - Should not be here */
#include "dialog.h"
#include "emodule.h"
#include "ewins.h"
#include "focus.h"
#include "grabs.h"
#include "hints.h"
#include "icons.h"
#include "settings.h"
#include "timers.h"
#include "xwin.h"
#define EwinListFocusRaise(ewin) EobjListFocusRaise(EoObj(ewin))
static const char *const focus_why[] = {
"NOP", "INIT", "SET", "NONE", "ENTER", "LEAVE", "EWIN_NEW", "EWIN_UNMAP",
"DESK_ENTER", "DESK_LEAVE", "NEXT", "PREV", "CLICK",
};
static char focus_inhibit = 1;
static char focus_is_set = 0;
static char click_pending_update_grabs = 0;
static int focus_pending_why = 0;
static EWin *focus_pending_ewin = NULL;
static EWin *focus_pending_new = NULL;
static EWin *focus_pending_raise = NULL;
static Timer *focus_timer_autoraise = NULL;
static int focus_request = 0;
void
FocusEnable(int on)
{
if (on)
{
if (focus_inhibit > 0)
focus_inhibit--;
}
else
{
focus_inhibit++;
}
if (EDebug(EDBUG_TYPE_FOCUS))
Eprintf("%s: inhibit=%d\n", __func__, focus_inhibit);
}
/*
* Return !0 if it is OK to focus ewin.
*/
static int
FocusEwinValid(EWin * ewin, int want_on_screen, int click, int want_visible)
{
int ok;
if (!ewin)
return 0;
if (ewin->state.inhibit_focus)
ok = 0;
else if (!EoIsMapped(ewin) || !EoIsShown(ewin) ||
ewin->state.state != EWIN_STATE_MAPPED)
ok = 0;
else if (ewin->state.sliding)
ok = 0;
else if (ewin->props.focusclick && !click)
ok = 0;
else if (want_visible && ewin->state.visibility == VisibilityFullyObscured)
ok = 0;
else if (want_on_screen && !EwinIsOnScreen(ewin))
ok = 0;
else
ok = 1;
#if 0
Eprintf
("%s: %#x %s: st=%d sh=%d inh=%d/%d ons=%d(%d) vis=%d(%d) cl=%d(%d): Ok=%d\n",
__func__, EwinGetClientXwin(ewin), EwinGetTitle(ewin),
ewin->state.state, EoIsShown(ewin),
ewin->state.inhibit_focus, ewin->state.sliding,
EwinIsOnScreen(ewin), want_on_screen,
ewin->state.visibility, want_visible, ok, click, ewin->props.focusclick);
#endif
return ok;
}
/*
* Return !0 if new ewin should be focused.
*/
static int
FocusEwinValidNew(EWin * ewin)
{
int ok = 0;
if (Conf.focus.all_new_windows_get_focus)
ok = 1;
else if (Mode.place.doing_manual == ewin)
ok = 1;
else if (Conf.focus.new_windows_get_focus_if_group_focused &&
Mode.focuswin &&
EwinGetWindowGroup(ewin) == EwinGetWindowGroup(Mode.focuswin))
ok = 1;
else if (EwinIsTransient(ewin))
{
if (Conf.focus.new_transients_get_focus)
{
ok = 1;
}
else if (Conf.focus.new_transients_get_focus_if_group_focused &&
Mode.focuswin)
{
if ((EwinGetTransientFor(ewin) ==
EwinGetClientXwin(Mode.focuswin)) ||
(EwinGetWindowGroup(ewin) ==
EwinGetWindowGroup(Mode.focuswin)))
ok = 1;
}
}
#if 0
Eprintf("%s: %#x %s: Ok=%d\n", __func__,
EwinGetClientXwin(ewin), EwinGetTitle(ewin), ok);
#endif
return ok;
}
/*
* Return the ewin to focus after entering area or losing focused window.
*/
static EWin *
FocusEwinSelect(void)
{
EWin *const *lst, *ewin;
int num, i;
switch (Conf.focus.mode)
{
default:
case MODE_FOCUS_POINTER:
ewin = GetEwinPointerInClient();
if (ewin && !FocusEwinValid(ewin, 1, 0, 0))
ewin = NULL;
break;
case MODE_FOCUS_SLOPPY:
ewin = GetEwinPointerInClient();
if (ewin && FocusEwinValid(ewin, 1, 0, 0))
break;
goto do_select;
case MODE_FOCUS_CLICK:
goto do_select;
do_select:
ewin = NULL;
lst = EwinListFocusGet(&num);
for (i = 0; i < num; i++)
{
if (!FocusEwinValid(lst[i], 1, 0, 0) ||
lst[i]->props.skip_focuslist)
continue;
ewin = lst[i];
break;
}
break;
}
return ewin;
}
static int
AutoraiseTimeout(void *data)
{
EWin *ewin = (EWin *) data;
if (Conf.focus.mode == MODE_FOCUS_CLICK)
goto done;
if (EwinFindByPtr(ewin)) /* May be gone */
EwinRaise(ewin);
done:
focus_timer_autoraise = NULL;
return 0;
}
static void
FocusRaisePending(void)
{
EWin *ewin = focus_pending_raise;
unsigned int mask;
/* The focusing cycle ends when no more modifiers are depressed */
mask = 0;
EQueryPointer(NULL, NULL, NULL, NULL, &mask);
if ((mask & Mode.masks.mod_key_mask) != 0)
return;
if (EwinFindByPtr(ewin)) /* May be gone */
EwinListFocusRaise(ewin);
GrabKeyboardRelease();
focus_pending_raise = NULL;
}
/*
* dir > 0: Focus previously focused window
* else : Focus least recently focused window
*/
static void
FocusCycleEwin(int dir)
{
EWin *const *lst;
EWin *ewin;
int i, j, num;
lst = EwinListFocusGet(&num);
if (num <= 1)
return;
dir = (dir > 0) ? 1 : -1;
for (j = 0; j < num; j++)
{
if (lst[j] == Mode.focuswin)
break;
}
for (i = 1; i < num; i++)
{
ewin = lst[(j + i * dir + num) % num];
if (!FocusEwinValid(ewin, 1, 0, 0) || ewin->props.skip_focuslist)
continue;
FocusToEWin(ewin, FOCUS_PREV);
break;
}
}
static void
ClickGrabsSet(EWin * ewin)
{
int set = 0;
unsigned int raise_button = AnyButton;
if (Conf.focus.clickraises == 2)
raise_button = Button1;
if ((Conf.focus.clickraises && !EwinListStackIsRaised(ewin)) ||
(!ewin->state.active && !ewin->state.inhibit_focus))
set = 1;
if (set)
{
if (ewin->state.click_grab_isset &&
ewin->state.click_grab_button != raise_button)
{
GrabButtonRelease(ewin->state.click_grab_button, AnyModifier,
EwinGetClientConWin(ewin));
ewin->state.click_grab_isset = 0;
}
if (!ewin->state.click_grab_isset)
{
GrabButtonSet(raise_button, AnyModifier, EwinGetClientConWin(ewin),
ButtonPressMask, ECSR_PGRAB, 1);
if (EDebug(EDBUG_TYPE_GRABS))
Eprintf("%s: %#x set %s\n", __func__,
EwinGetClientXwin(ewin), EwinGetTitle(ewin));
ewin->state.click_grab_isset = 1;
ewin->state.click_grab_button = raise_button;
}
}
else
{
if (ewin->state.click_grab_isset)
{
GrabButtonRelease(ewin->state.click_grab_button, AnyModifier,
EwinGetClientConWin(ewin));
if (EDebug(EDBUG_TYPE_GRABS))
Eprintf("%s: %#x unset %s\n", __func__,
EwinGetClientXwin(ewin), EwinGetTitle(ewin));
ewin->state.click_grab_isset = 0;
}
}
}
static void
FocusEwinSetActive(EWin * ewin, int active)
{
if (ewin->state.active == (unsigned)active)
return;
ewin->state.active = active;
EwinBorderUpdateState(ewin);
EwinUpdateOpacity(ewin);
if (active && ewin->state.attention)
ewin->state.attention = 0;
HintsSetWindowState(ewin);
}
static void
doClickGrabsUpdate(void)
{
EWin *const *lst, *ewin;
int i, num;
lst = EwinListGetAll(&num);
for (i = 0; i < num; i++)
{
ewin = lst[i];
ClickGrabsSet(ewin);
}
click_pending_update_grabs = 0;
}
void
ClickGrabsUpdate(void)
{
click_pending_update_grabs = 1;
}
static void
doFocusToEwin(EWin * ewin, int why)
{
int do_raise = 0, do_warp = 0;
if (focus_inhibit)
return;
if (EDebug(EDBUG_TYPE_FOCUS))
Eprintf("%s: %#x %s why=%s\n", __func__,
(ewin) ? EwinGetClientXwin(ewin) : 0,
(ewin) ? EwinGetTitle(ewin) : "None", focus_why[why]);
switch (why)
{
case FOCUS_NEXT:
case FOCUS_PREV:
if (Conf.focus.raise_on_next)
do_raise = 1;
if (Conf.focus.warp_on_next)
do_warp = 1;
/* Fall thru */
default:
case FOCUS_SET:
case FOCUS_ENTER:
case FOCUS_LEAVE: /* Unused */
case FOCUS_CLICK:
if (ewin && ewin == Mode.focuswin)
return;
if (!ewin) /* Unfocus */
break;
if (!FocusEwinValid(ewin, 1, why == FOCUS_CLICK, 0))
return;
break;
case FOCUS_INIT:
case FOCUS_DESK_ENTER:
ewin = FocusEwinSelect();
break;
case FOCUS_DESK_LEAVE:
focus_is_set = 0;
/* FALLTHROUGH */
case FOCUS_NONE:
ewin = NULL;
if (ewin == Mode.focuswin)
return;
break;
case FOCUS_EWIN_UNMAP:
if (Mode.focuswin)
return;
ewin = FocusEwinSelect();
if (ewin == Mode.focuswin)
ewin = NULL;
break;
case FOCUS_EWIN_NEW:
if (!ewin)
return;
if (ewin->props.focus_when_mapped)
goto check_focus_new;
if (FocusEwinValidNew(ewin))
{
if (EwinIsTransient(ewin))
DeskGotoByEwin(ewin, 0);
goto check_focus_new;
}
if (Conf.focus.mode != MODE_FOCUS_CLICK && ewin == Mode.mouse_over_ewin)
goto check_focus_new;
return;
check_focus_new:
if (!FocusEwinValid(ewin, 1, 0, 0))
return;
break;
}
if (ewin == Mode.focuswin && focus_is_set)
return;
/* Check if ewin is a valid focus window target */
if (!ewin)
goto done;
/* NB! ewin != NULL */
if (why != FOCUS_CLICK && ewin->props.focusclick)
return;
if (Conf.autoraise.enable)
{
TIMER_DEL(focus_timer_autoraise);
if (Conf.focus.mode != MODE_FOCUS_CLICK)
TIMER_ADD(focus_timer_autoraise, Conf.autoraise.delay,
AutoraiseTimeout, ewin);
}
if (do_raise)
EwinRaise(ewin);
if (Conf.focus.warp_always)
do_warp = 1;
if (do_warp)
EwinWarpTo(ewin, 0);
switch (why)
{
case FOCUS_PREV:
case FOCUS_NEXT:
GrabKeyboardSet(VROOT); /* Causes idler to be called on KeyRelease */
focus_pending_raise = ewin;
break;
case FOCUS_DESK_ENTER:
if (Conf.focus.mode == MODE_FOCUS_CLICK)
break;
/* FALLTHROUGH */
default:
case FOCUS_INIT:
EwinListFocusRaise(ewin);
break;
}
SoundPlay(SOUND_FOCUS_SET);
done:
ClickGrabsUpdate();
/* Unset old focus window (if any) highlighting */
if (Mode.focuswin)
FocusEwinSetActive(Mode.focuswin, 0);
ICCCM_Cmap(ewin);
/* Quit if pointer is not on our screen */
if (!Mode.events.on_screen)
{
Mode.focuswin = NULL;
return;
}
/* Set new focus window (if any) highlighting */
Mode.focuswin = ewin;
if (Mode.focuswin)
FocusEwinSetActive(Mode.focuswin, 1);
if (why == FOCUS_DESK_LEAVE)
return;
ICCCM_Focus(ewin);
focus_is_set = 1;
}
void
FocusToEWin(EWin * ewin, int why)
{
if (EDebug(EDBUG_TYPE_FOCUS))
Eprintf("%s(%d) %#x %s why=%s\n", __func__, focus_inhibit,
(ewin) ? EwinGetClientXwin(ewin) : 0,
(ewin) ? EwinGetTitle(ewin) : "None", focus_why[why]);
switch (why)
{
case FOCUS_EWIN_NEW:
if (!FocusEwinValidNew(ewin))
break;
if (!FocusEwinValid(ewin, 0, 0, 0))
break;
focus_pending_new = ewin;
focus_pending_why = why;
focus_pending_ewin = ewin;
focus_request = (int)NextRequest(disp) - 1;
break;
default:
if (ewin && !FocusEwinValid(ewin, 0, why == FOCUS_CLICK, 0))
break;
focus_pending_why = why;
focus_pending_ewin = ewin;
focus_request = (int)NextRequest(disp) - 1;
break;
case FOCUS_EWIN_UNMAP:
focus_pending_why = why;
focus_pending_ewin = NULL;
if (ewin == Mode.focuswin)
{
Mode.focuswin = NULL;
focus_is_set = 0;
if (!EoIsGone(ewin))
FocusEwinSetActive(ewin, 0);
}
if (ewin == focus_pending_new)
focus_pending_new = NULL;
break;
}
}
static void
FocusSet(void)
{
if (focus_pending_new && Conf.focus.all_new_windows_get_focus)
doFocusToEwin(focus_pending_new, FOCUS_EWIN_NEW);
else
doFocusToEwin(focus_pending_ewin, focus_pending_why);
focus_pending_why = 0;
focus_pending_ewin = focus_pending_new = NULL;
}
void
FocusNewDeskBegin(void)
{
/* Freeze keyboard */
GrabKeyboardFreeze(VROOT);
focus_pending_new = NULL;
doFocusToEwin(NULL, FOCUS_DESK_LEAVE);
FocusEnable(0);
}
void
FocusNewDesk(void)
{
FocusEnable(1);
FocusToEWin(NULL, FOCUS_DESK_ENTER);
/* Unfreeze keyboard */
GrabKeyboardRelease();
}
static void
_FocusScreenSendEvent(EX_Window xwin, int x, int y, EX_Time t, int enter)
{
XEvent xe;
xe.type = (enter) ? EnterNotify : LeaveNotify;
xe.xcrossing.window = xwin;
xe.xcrossing.root = xwin;
xe.xcrossing.subwindow = 0;
xe.xcrossing.time = t;
xe.xcrossing.x = xe.xcrossing.x_root = x;
xe.xcrossing.y = xe.xcrossing.y_root = y;
xe.xcrossing.mode = NotifyNormal;
xe.xcrossing.detail = NotifyNonlinear;
xe.xcrossing.same_screen = (enter) ? True : False;
xe.xcrossing.focus = (enter) ? False : True;
xe.xcrossing.state = 0;
XSendEvent(disp, xwin, False, EnterWindowMask | LeaveWindowMask, &xe);
}
void
FocusScreen(int scr)
{
EX_Window xwin;
EX_Time t;
int x, y;
if (scr < 0 || scr >= ScreenCount(disp))
return;
/* IIRC warping to a different screen once did cause
* LeaveNotify's on the current root window. This does not
* happen in xorg 1.5.3 (and probably other versions).
* So, send LeaveNotify to current root and EnterNotify
* to new root. */
t = EGetTimestamp();
/* Report LeaveNotify on current root window */
xwin = WinGetXwin(VROOT);
EXQueryPointer(xwin, &x, &y, NULL, NULL);
_FocusScreenSendEvent(xwin, x, y, t, 0);
/* Do warp and report EnterNotify on new root window */
xwin = RootWindow(disp, scr);
x = DisplayWidth(disp, scr) / 2;
y = DisplayHeight(disp, scr) / 2;
EXWarpPointer(xwin, x, y);
_FocusScreenSendEvent(xwin, x, y, t, 1);
}
static void
FocusInit(void)
{
/* Start focusing windows */
FocusEnable(1);
FocusToEWin(NULL, FOCUS_INIT);
FocusSet();
/* Enable window placement features */
Mode.place.enable_features++;
}
static void
FocusExit(void)
{
}
/*
* Focus event handlers
*/
void
FocusHandleEnter(EWin * ewin, XEvent * ev)
{
Mode.mouse_over_ewin = ewin;
#if 0 /* FIXME - Remove? */
if (ev->xcrossing.mode == NotifyUngrab &&
ev->xcrossing.detail == NotifyNonlinearVirtual)
{
if (EDebug(1))
Eprintf("%s: Previously ignored: focused: %s, enter: %s\n", __func__,
EoGetNameSafe(Mode.focuswin), EoGetNameSafe(ewin));
}
#endif
if ((int)ev->xcrossing.serial - focus_request < 0)
{
/* This event was caused by a request older than the latest
* focus assignment request - ignore */
if (EDebug(EDBUG_TYPE_FOCUS))
Eprintf("%s: Ignore serial < %#x\n", __func__, focus_request);
return;
}
if (!ewin)
{
/* Entering root may mean entering this screen */
FocusToEWin(NULL, FOCUS_DESK_ENTER);
return;
}
switch (Conf.focus.mode)
{
default:
case MODE_FOCUS_CLICK:
break;
case MODE_FOCUS_SLOPPY:
if (FocusEwinValid(ewin, 1, 0, 0))
FocusToEWin(ewin, FOCUS_ENTER);
break;
case MODE_FOCUS_POINTER:
if (FocusEwinValid(ewin, 1, 0, 0))
FocusToEWin(ewin, FOCUS_ENTER);
else
FocusToEWin(NULL, FOCUS_NONE);
break;
}
}
void
FocusHandleLeave(EWin * ewin, XEvent * ev)
{
if ((int)ev->xcrossing.serial - focus_request < 0)
{
/* This event was caused by a request older than the latest
* focus assignment request - ignore */
if (EDebug(EDBUG_TYPE_FOCUS))
Eprintf("%s: Ignore serial < %#x\n", __func__, focus_request);
return;
}
/* Leaving root may mean entering other screen */
if (!ewin)
{
if (ev->xcrossing.mode == NotifyNormal &&
ev->xcrossing.detail != NotifyInferior)
FocusToEWin(NULL, FOCUS_DESK_LEAVE);
}
}
void
FocusHandleChange(EWin * ewin __UNUSED__, XEvent * ev __UNUSED__)
{
#if 0 /* Debug */
if (ewin == Mode.focuswin && ev->type == FocusOut)
Eprintf("%s: ??? Lost focus: %s\n", __func__, EwinGetTitle(ewin));
#endif
}
void
FocusHandleClick(EWin * ewin, Win win)
{
if (Conf.focus.clickraises)
EwinRaise(ewin);
FocusToEWin(ewin, FOCUS_CLICK);
/* Allow click to pass thorugh */
if (EDebug(EDBUG_TYPE_GRABS))
Eprintf("%s: %#x %#x\n", __func__, WinGetXwin(win),
EwinGetContainerXwin(ewin));
if (win == EwinGetClientConWin(ewin))
{
ESync(ESYNC_FOCUS);
GrabPointerThaw();
ESync(ESYNC_FOCUS);
}
}
#if ENABLE_DIALOGS
/*
* Configuration dialog
*/
typedef struct {
struct {
int mode;
int clickalways;
char new_focus;
char new_focus_if_group;
char popup_focus;
char popup_focus_if_group;
char raise_focus;
char warp_focus;
char warp_always;
} focus;
struct {
char enable;
int time;
} autoraise;
struct {
char enable;
char warp_after_focus;
char raise_after_focus;
char showsticky;
char showshaded;
char showiconified;
char showalldesks;
char warpfocused;
char show_shape;
int icon_mode;
} focuslist;
} FocusDlgData;
static void
_DlgApplyFocus(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
{
FocusDlgData *dd = DLG_DATA_GET(d, FocusDlgData);
Conf.focus.mode = dd->focus.mode;
Conf.focus.clickraises = dd->focus.clickalways;
Conf.focus.all_new_windows_get_focus = dd->focus.new_focus;
Conf.focus.new_windows_get_focus_if_group_focused =
dd->focus.new_focus_if_group;
Conf.focus.new_transients_get_focus = dd->focus.popup_focus;
Conf.focus.new_transients_get_focus_if_group_focused =
dd->focus.popup_focus_if_group;
Conf.focus.raise_on_next = dd->focus.raise_focus;
Conf.focus.warp_on_next = dd->focus.warp_focus;
Conf.focus.warp_always = dd->focus.warp_always;
Conf.autoraise.enable = dd->autoraise.enable;
Conf.autoraise.delay = 10 * dd->autoraise.time;
Conf.warplist.enable = dd->focuslist.enable;
Conf.warplist.warp_on_select = dd->focuslist.warp_after_focus;
Conf.warplist.raise_on_select = dd->focuslist.raise_after_focus;
Conf.warplist.showsticky = dd->focuslist.showsticky;
Conf.warplist.showshaded = dd->focuslist.showshaded;
Conf.warplist.showiconified = dd->focuslist.showiconified;
Conf.warplist.showalldesks = dd->focuslist.showalldesks;
Conf.warplist.warpfocused = dd->focuslist.warpfocused;
Conf.warplist.show_shape = dd->focuslist.show_shape;
Conf.warplist.icon_mode = dd->focuslist.icon_mode;
ClickGrabsUpdate();
autosave();
}
static void
_DlgFillFocus(Dialog * d, DItem * table, void *data __UNUSED__)
{
FocusDlgData *dd = DLG_DATA_GET(d, FocusDlgData);
DItem *di, *radio;
dd->focus.mode = Conf.focus.mode;
dd->focus.clickalways = Conf.focus.clickraises;
dd->focus.new_focus = Conf.focus.all_new_windows_get_focus;
dd->focus.new_focus_if_group =
Conf.focus.new_windows_get_focus_if_group_focused;
dd->focus.popup_focus = Conf.focus.new_transients_get_focus;
dd->focus.popup_focus_if_group =
Conf.focus.new_transients_get_focus_if_group_focused;
dd->focus.raise_focus = Conf.focus.raise_on_next;
dd->focus.warp_focus = Conf.focus.warp_on_next;
dd->focus.warp_always = Conf.focus.warp_always;
dd->autoraise.enable = Conf.autoraise.enable;
dd->autoraise.time = Conf.autoraise.delay / 10;
dd->focuslist.enable = Conf.warplist.enable;
dd->focuslist.raise_after_focus = Conf.warplist.raise_on_select;
dd->focuslist.warp_after_focus = Conf.warplist.warp_on_select;
dd->focuslist.showsticky = Conf.warplist.showsticky;
dd->focuslist.showshaded = Conf.warplist.showshaded;
dd->focuslist.showiconified = Conf.warplist.showiconified;
dd->focuslist.showalldesks = Conf.warplist.showalldesks;
dd->focuslist.warpfocused = Conf.warplist.warpfocused;
dd->focuslist.show_shape = Conf.warplist.show_shape;
dd->focuslist.icon_mode = Conf.warplist.icon_mode;
DialogItemTableSetOptions(table, 2, 0, 0, 0);
radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Focus follows pointer"));
DialogItemRadioButtonSetFirst(di, radio);
DialogItemRadioButtonGroupSetVal(di, 0);
di = DialogAddItem(table, DITEM_RADIOBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Focus follows pointer sloppily"));
DialogItemRadioButtonSetFirst(di, radio);
DialogItemRadioButtonGroupSetVal(di, 1);
di = DialogAddItem(table, DITEM_RADIOBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Focus follows mouse clicks"));
DialogItemRadioButtonSetFirst(di, radio);
DialogItemRadioButtonGroupSetVal(di, 2);
DialogItemRadioButtonGroupSetValPtr(radio, &dd->focus.mode);
di = DialogAddItem(table, DITEM_SEPARATOR);
DialogItemSetColSpan(di, 2);
radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Clicking in a window does not raise it"));
DialogItemRadioButtonSetFirst(di, radio);
DialogItemRadioButtonGroupSetVal(di, 0);
di = DialogAddItem(table, DITEM_RADIOBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Clicking in a window always raises it"));
DialogItemRadioButtonSetFirst(di, radio);
DialogItemRadioButtonGroupSetVal(di, 1);
di = DialogAddItem(table, DITEM_RADIOBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Only primary mouse button can raise window"));
DialogItemRadioButtonSetFirst(di, radio);
DialogItemRadioButtonGroupSetVal(di, 2);
DialogItemRadioButtonGroupSetValPtr(radio, &dd->focus.clickalways);
di = DialogAddItem(table, DITEM_SEPARATOR);
DialogItemSetColSpan(di, 2);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("All new windows first get the focus"));
DialogItemCheckButtonSetPtr(di, &dd->focus.new_focus);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di,
_
("New windows get the focus if their window group is focused"));
DialogItemCheckButtonSetPtr(di, &dd->focus.new_focus_if_group);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Only new dialog windows get the focus"));
DialogItemCheckButtonSetPtr(di, &dd->focus.popup_focus);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di,
_
("Only new dialogs whose owner is focused get the focus"));
DialogItemCheckButtonSetPtr(di, &dd->focus.popup_focus_if_group);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Raise windows while switching focus"));
DialogItemCheckButtonSetPtr(di, &dd->focus.raise_focus);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di,
_("Send mouse pointer to window while switching focus"));
DialogItemCheckButtonSetPtr(di, &dd->focus.warp_focus);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di,
_("Always send mouse pointer to window on focus switch"));
DialogItemCheckButtonSetPtr(di, &dd->focus.warp_always);
di = DialogAddItem(table, DITEM_SEPARATOR);
DialogItemSetColSpan(di, 2);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Raise windows automatically"));
DialogItemCheckButtonSetPtr(di, &dd->autoraise.enable);
di = DialogAddItem(table, DITEM_TEXT);
DialogItemSetText(di, _("Autoraise delay:"));
di = DialogAddItem(table, DITEM_SLIDER);
DialogItemSliderSetBounds(di, 0, 300);
DialogItemSliderSetUnits(di, 10);
DialogItemSliderSetJump(di, 25);
DialogItemSliderSetValPtr(di, &dd->autoraise.time);
di = DialogAddItem(table, DITEM_SEPARATOR);
DialogItemSetColSpan(di, 2);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Display and use focus list"));
DialogItemCheckButtonSetPtr(di, &dd->focuslist.enable);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Include sticky windows in focus list"));
DialogItemCheckButtonSetPtr(di, &dd->focuslist.showsticky);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Include shaded windows in focus list"));
DialogItemCheckButtonSetPtr(di, &dd->focuslist.showshaded);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Include iconified windows in focus list"));
DialogItemCheckButtonSetPtr(di, &dd->focuslist.showiconified);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Include windows on other desks in focus list"));
DialogItemCheckButtonSetPtr(di, &dd->focuslist.showalldesks);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Focus windows while switching"));
DialogItemCheckButtonSetPtr(di, &dd->focuslist.warpfocused);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Outline windows while switching"));
DialogItemCheckButtonSetPtr(di, &dd->focuslist.show_shape);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Raise windows after focus switch"));
DialogItemCheckButtonSetPtr(di, &dd->focuslist.raise_after_focus);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("Send mouse pointer to window after focus switch"));
DialogItemCheckButtonSetPtr(di, &dd->focuslist.warp_after_focus);
di = DialogAddItem(table, DITEM_SEPARATOR);
DialogItemSetColSpan(di, 2);
di = DialogAddItem(table, DITEM_TEXT);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di,
_
("Focuslist image display policy (if one operation fails, try the next):"));
radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("First E Icon, then App Icon"));
DialogItemRadioButtonSetFirst(di, radio);
DialogItemRadioButtonGroupSetVal(di, EWIN_ICON_MODE_IMG_APP);
di = DialogAddItem(table, DITEM_RADIOBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("First App Icon, then E Icon"));
DialogItemRadioButtonSetFirst(di, radio);
DialogItemRadioButtonGroupSetVal(di, EWIN_ICON_MODE_APP_IMG);
di = DialogAddItem(table, DITEM_RADIOBUTTON);
DialogItemSetColSpan(di, 2);
DialogItemSetText(di, _("None"));
DialogItemRadioButtonSetFirst(di, radio);
DialogItemRadioButtonGroupSetVal(di, EWIN_ICON_MODE_NONE);
DialogItemRadioButtonGroupSetValPtr(radio, &dd->focuslist.icon_mode);
}
const DialogDef DlgFocus = {
"CONFIGURE_FOCUS",
N_("Focus"), N_("Focus Settings"),
sizeof(FocusDlgData),
SOUND_SETTINGS_FOCUS,
"pix/focus.png",
N_("Enlightenment Focus\n" "Settings Dialog"),
_DlgFillFocus,
DLG_OAC, _DlgApplyFocus, NULL
};
#endif /* ENABLE_DIALOGS */
/*
* Focus Module
*/
static int
FocusInitTimeout(void *data __UNUSED__)
{
FocusInit();
return 0;
}
static void
_FocusIdler(void *data __UNUSED__)
{
if (focus_inhibit)
return;
if (focus_pending_why)
FocusSet();
if (click_pending_update_grabs)
doClickGrabsUpdate();
if (focus_pending_raise)
FocusRaisePending();
}
static void
FocusSighan(int sig, void *prm __UNUSED__)
{
switch (sig)
{
case ESIGNAL_START:
/* Delay focusing a bit to allow things to settle down */
IdlerAdd(_FocusIdler, NULL);
TIMER_ADD_NP(500, FocusInitTimeout, NULL);
break;
case ESIGNAL_EXIT:
FocusExit();
break;
}
}
static void
FocusIpc(const char *params)
{
const char *p;
char cmd[128], prm[4096];
int len;
cmd[0] = prm[0] = '\0';
p = params;
if (p)
{
len = 0;
sscanf(p, "%100s %4000s %n", cmd, prm, &len);
p += len;
}
if (!p || cmd[0] == '?')
{
EWin *ewin;
ewin = GetFocusEwin();
if (ewin)
IpcPrintf("Focused: %#x\n", EwinGetClientXwin(ewin));
else
IpcPrintf("Focused: none\n");
}
else if (!strncmp(cmd, "mode", 2))
{
int mode = Conf.focus.mode;
if (!strcmp(prm, "click"))
{
mode = MODE_FOCUS_CLICK;
Mode.grabs.pointer_grab_active = 1;
}
else if (!strcmp(prm, "clicknograb"))
{
mode = MODE_FOCUS_CLICK;
Mode.grabs.pointer_grab_active = 0;
}
else if (!strcmp(prm, "pointer"))
{
mode = MODE_FOCUS_POINTER;
}
else if (!strcmp(prm, "sloppy"))
{
mode = MODE_FOCUS_SLOPPY;
}
else if (!strcmp(prm, "?"))
{
if (Conf.focus.mode == MODE_FOCUS_CLICK)
{
if (Mode.grabs.pointer_grab_active)
p = "click";
else
p = "clicknograb";
}
else if (Conf.focus.mode == MODE_FOCUS_SLOPPY)
p = "sloppy";
else if (Conf.focus.mode == MODE_FOCUS_POINTER)
p = "pointer";
else
p = "unknown";
IpcPrintf("Focus Mode: %s\n", p);
}
else
{
IpcPrintf("Error: unknown focus type\n");
}
if (Conf.focus.mode != mode)
{
Conf.focus.mode = mode;
ClickGrabsUpdate();
autosave();
}
}
else if (!strncmp(cmd, "next", 2))
{
/* Focus previously focused window */
if (Conf.warplist.enable)
WarpFocus(1);
else
FocusCycleEwin(1);
}
else if (!strncmp(cmd, "prev", 2))
{
/* Focus least recently focused window */
if (Conf.warplist.enable)
WarpFocus(-1);
else
FocusCycleEwin(-1);
}
}
static const IpcItem FocusIpcArray[] = {
{
FocusIpc,
"focus", "sf",
"Focus functions",
" focus ? Show focused window id\n"
" focus mode MODE Set focus mode. MODEs:\n"
" click The traditional click-to-focus mode\n"
" clicknograb A similar focus mode, but without the grabbing of the click\n"
" (you cannot click anywhere in a window to focus it)\n"
" pointer The focus will follow the mouse pointer\n"
" sloppy The focus follows the mouse, but when over the desktop\n"
" background the last window does not lose the focus\n"
" ? Show currrent focus mode\n"
" focus next Focus previously focused window\n"
" focus prev Focus least recently focused window\n"}
,
};
static const CfgItem FocusCfgItems[] = {
CFG_ITEM_INT(Conf.focus, mode, MODE_FOCUS_SLOPPY),
CFG_ITEM_INT(Conf.focus, clickraises, 1),
CFG_ITEM_BOOL(Conf.focus, transientsfollowleader, 1),
CFG_ITEM_BOOL(Conf.focus, switchfortransientmap, 1),
CFG_ITEM_BOOL(Conf.focus, all_new_windows_get_focus, 0),
CFG_ITEM_BOOL(Conf.focus, new_windows_get_focus_if_group_focused, 1),
CFG_ITEM_BOOL(Conf.focus, new_transients_get_focus, 0),
CFG_ITEM_BOOL(Conf.focus, new_transients_get_focus_if_group_focused, 1),
CFG_ITEM_BOOL(Conf.focus, raise_on_next, 1),
CFG_ITEM_BOOL(Conf.focus, warp_on_next, 0),
CFG_ITEM_BOOL(Conf.focus, warp_always, 0),
CFG_ITEM_BOOL(Conf, autoraise.enable, 0),
CFG_ITEM_INT(Conf, autoraise.delay, 500),
};
/*
* Module descriptor
*/
extern const EModule ModFocus;
const EModule ModFocus = {
"focus", NULL,
FocusSighan,
MOD_ITEMS(FocusIpcArray),
MOD_ITEMS(FocusCfgItems)
};