Skip to content

Commit

Permalink
Add XFixes support and CusorBarrier command.
Browse files Browse the repository at this point in the history
CursorBarrier is a command to create a PointerBarrier using
the XFixes extension. This barrier can be used to confine mouse
movement to any rectangle on the desktop. This first adds XFixes
as an optional dependency, and if XFixes is included, the
CursorBarrier option can be used to create PointerBarriers.

CursorBarrier can be use to both create and destroy barriers.
Current `MAX_BARRIERS` is set at 16, so only up to 16 barriers
can be created at a single time. The use of CursorBarrier is
documented in the manual page.

A default key binding ctrl+shift+D is setup to destroy all
barriers to give users a way to escape any jails their cursor
is stuck in.
  • Loading branch information
somiaj authored and ThomasAdam committed Oct 31, 2024
1 parent a528d2b commit 56b8a74
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 0 deletions.
47 changes: 47 additions & 0 deletions doc/fvwm3_manpage_source.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4329,6 +4329,53 @@ MoveToDesk 0 n

=== Focus & Mouse Movement

*CursorBarrier* [destroy] [options] [_left_ _top_ _right_ _bottom_]::
A cursor barrier is a box that the cursor cannot be moved outside of
(unless warped with _CursorMove_ or _WarpToWindow_). The _left_, _top_,
_right_, and _bottom_ values give the percent distance the box is placed
from each of the corresponding edges of the desktop. If the values end
with a _p_, they are interpreted as pixel amounts instead. If the option
_coords_ is given, the values are interpreted as the left/top and
right/bottom coordinates of the box's corners. If the option
_screen RandRname_ is given, the positions are computed relative to the
specified monitor. Use _screen c_ to place the barrier on the current
monitor.
+
Multiple barriers can be created by calling _CursorBarrier_ multiple times.
This allows creating multiple regions to confine the cursor to (such as each
monitor in a multi-monitor setup), then use _CursorMove_ or _WarpToWindow_
to move the cursor between the barriers. If the command _destroy_ is included,
all barriers are destroyed allowing free movement of the mouse again. If
_destroy_ is followed by an integer _N_, only that barrier is destroyed
(barriers are numbered in the order they are created starting at 0). If the
integer is negative, this counts backwards, such as "-1" is the most recent
created barrier, "-2" is the second most recent, and so on. When a barrier
is destroyed, this renumbers all barriers after it. In practice this is
best used with _destroy -1_ to destroy the most recent barrier without
affecting any previously created barriers.
+
By default the key binding ctrl+shift+D will destroy all cursor barriers
by calling _CursorBarrier destroy_.
+
Here are some examples:
+
....
# A barrier 15% from left/right and 10% from top/bottom.
CursorBarrier 15 10 15 10
# A barrier to confine the mouse to the current monitor.
CursorBarrier screen c
# A barrier to confine the mouse to a selected window
Pick CursorBarrier coords $[w.x]p $[w.y]p \
$[math.+.$[w.x],$[w.width]]p $[math.+.$[w.y],$[w.height]]p
# Destroy all barriers
CursorBarrier destroy
....
+
_CursorBarrier_ only works if fvwm is complied with the XFixes extension.

*CursorMove* _horizontal_[p] _vertical_[p]::
Moves the mouse pointer by _horizontal_ pages in the X direction and
_vertical_ pages in the Y direction. Either or both entries may be
Expand Down
2 changes: 2 additions & 0 deletions fvwm/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ enum
F_CONFIG_LIST,
F_COPY_MENU_STYLE,
F_CURRENT,
F_CURSOR_BARRIER,
F_CURSOR_STYLE,
F_DESCHEDULE,
F_DESKTOP_CONFIGURATION,
Expand Down Expand Up @@ -225,6 +226,7 @@ void CMD_ColormapFocus(F_CMD_ARGS);
void CMD_Colorset(F_CMD_ARGS);
void CMD_CopyMenuStyle(F_CMD_ARGS);
void CMD_Current(F_CMD_ARGS);
void CMD_CursorBarrier(F_CMD_ARGS);
void CMD_CursorMove(F_CMD_ARGS);
void CMD_CursorStyle(F_CMD_ARGS);
void CMD_DefaultFont(F_CMD_ARGS);
Expand Down
159 changes: 159 additions & 0 deletions fvwm/cursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,81 @@
#include "cursor.h"
#include "menus.h"

#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#define MAX_BARRIERS 16
PointerBarrier barriers_l[MAX_BARRIERS];
PointerBarrier barriers_r[MAX_BARRIERS];
PointerBarrier barriers_t[MAX_BARRIERS];
PointerBarrier barriers_b[MAX_BARRIERS];
int num_barriers = 0;

void create_barrier(int x1, int y1, int x2, int y2)
{
if (num_barriers >= MAX_BARRIERS) {
fvwm_debug(__func__, "Too many barriers. Aborting.");
return;
}

barriers_l[num_barriers] = XFixesCreatePointerBarrier(
dpy, Scr.Root, x1, y1, x1, y2, 0, 0, NULL);
barriers_r[num_barriers] = XFixesCreatePointerBarrier(
dpy, Scr.Root, x2, y1, x2, y2, 0, 0, NULL);
barriers_t[num_barriers] = XFixesCreatePointerBarrier(
dpy, Scr.Root, x1, y1, x2, y1, 0, 0, NULL);
barriers_b[num_barriers] = XFixesCreatePointerBarrier(
dpy, Scr.Root, x1, y2, x2, y2, 0, 0, NULL);
num_barriers++;
}

void destroy_barrier(int n)
{
XFixesDestroyPointerBarrier(dpy, barriers_l[n]);
XFixesDestroyPointerBarrier(dpy, barriers_r[n]);
XFixesDestroyPointerBarrier(dpy, barriers_t[n]);
XFixesDestroyPointerBarrier(dpy, barriers_b[n]);
}

void destroy_all_barriers(void)
{
int i;

for (i = 0; i < num_barriers; i++) {
destroy_barrier(i);
}

num_barriers = 0;
}

void destroy_barrier_n(int n)
{
int i;

if (num_barriers == 0)
return;

if (n < 0)
n += num_barriers;
if (n < 0 || n >= num_barriers) {
fvwm_debug(__func__, "Invalid barrier number: %d", n);
return;
}

destroy_barrier(n);
num_barriers--;
for (i = n; i < num_barriers; i++) {
barriers_l[i] = barriers_l[i+1];
barriers_r[i] = barriers_r[i+1];
barriers_t[i] = barriers_t[i+1];
barriers_b[i] = barriers_b[i+1];
}
}

#else
#define create_barrier(a, b, c, d)
#define destroy_all_barriers()
#define destroy_barrier_n(a)
#endif
/* ---------------------------- local definitions -------------------------- */

/* ---------------------------- local macros ------------------------------- */
Expand Down Expand Up @@ -544,3 +619,87 @@ void CMD_BusyCursor(F_CMD_ARGS)

return;
}

void CMD_CursorBarrier(F_CMD_ARGS)
{
int val[4] = {0, 0, 0, 0};
int x1, y1, x2, y2;
rectangle r =
{0, 0, monitor_get_all_widths(), monitor_get_all_heights()};
bool is_coords = false;
char *option;
struct monitor *m = NULL;

#if !defined(HAVE_XFIXES)
SUPPRESS_UNUSED_VAR_WARNING(x1);
SUPPRESS_UNUSED_VAR_WARNING(y1);
SUPPRESS_UNUSED_VAR_WARNING(x2);
SUPPRESS_UNUSED_VAR_WARNING(y2);
return;
#endif

/* Note, if option is matched, the matched option must be skipped
* with PeekToken(action, &action) to stop infinite loop.
*/
while ((option = PeekToken(action, NULL)) != NULL) {
if (StrEquals(option, "screen")) {
option = PeekToken(action, &action); /* Skip */
option = PeekToken(action, &action);
if ((m = monitor_resolve_name(option)) == NULL) {
fvwm_debug(__func__, "Invalid screen: %s",
option);
return;
}
r.x = m->si->x;
r.y = m->si->y;
r.width = m->si->w;
r.height = m->si->h;
} else if (StrEquals(option, "destroy")) {
int n;

option = PeekToken(action, &action); /* Skip */
option = PeekToken(action, &action);
if (option != NULL && sscanf(option, "%d", &n) == 1) {
destroy_barrier_n(n);
} else {
destroy_all_barriers();
}
XSync(dpy, False);
return;
} else if (StrEquals(option, "coords")) {
option = PeekToken(action, &action); /* Skip */
is_coords = true;
} else {
int i;
int unit[4] = {r.width, r.height, r.width, r.height};

for (i = 0; i < 4; i++) {
option = PeekToken(action, &action);
if (GetOnePercentArgument(
option, &val[i], &unit[i]) == 0
|| val[i] < 0) {
fvwm_debug(__func__,
"Invalid coordinates.");
return;
}
val[i] = val[i] * unit[i] / 100;
}
break;
}
}

if (is_coords) {
x1 = r.x + val[0];
y1 = r.y + val[1];
x2 = r.x + val[2];
y2 = r.y + val[3];
} else {
x1 = r.x + val[0];
y1 = r.y + val[1];
x2 = r.x + r.width - val[2];
y2 = r.y + r.height - val[3];
}

create_barrier(x1, y1, x2, y2);
XSync(dpy, False);
}
3 changes: 3 additions & 0 deletions fvwm/functable.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ const func_t func_table[] =
CMD_ENT("current", CMD_Current, F_CURRENT, 0, 0),
/* - Operate on the currently focused window */

CMD_ENT("cursorbarrier", CMD_CursorBarrier, F_CURSOR_BARRIER, 0, 0),
/* - Manage barriers the cursor cannot moved out of unless warped */

CMD_ENT("cursormove", CMD_CursorMove, F_MOVECURSOR, 0, 0),
/* - Move the cursor pointer non interactively */

Expand Down
5 changes: 5 additions & 0 deletions fvwm/fvwm3.c
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,9 @@ static void setVersionInfo(void)
#ifdef HAVE_XFT
strlcat(support_str, " XFT,", sizeof(support_str));
#endif
#ifdef HAVE_XFIXES
strlcat(support_str, " XFixes,", sizeof(support_str));
#endif
#ifdef HAVE_NLS
strlcat(support_str, " NLS,", sizeof(support_str));
#endif
Expand Down Expand Up @@ -1320,6 +1323,8 @@ static void SetRCDefaults(void)
{ "Key Up M A MenuMoveCursor -1", "", "" },
{ "Key Down M A MenuMoveCursor 1", "", "" },
{ "Mouse 1 MI A MenuSelectItem", "", "" },
/* Default escape from CusorBarriers */
{ "Key D A CS CursorBarrier destroy", "", "" },
/* don't add anything below */
{ RC_DEFAULTS_COMPLETE, "", "" },
{ "Read "FVWM_DATADIR"/ConfigFvwmDefaults", "", "" },
Expand Down
7 changes: 7 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,12 @@ if xcursor.found()
conf.set10('HAVE_XCURSOR', true)
endif

xfixes = dependency('xfixes', required: get_option('xfixes'))
if xfixes.found()
all_found_deps += xfixes
conf.set10('HAVE_XFIXES', true)
endif

xkbcommon = dependency('xkbcommon', required: get_option('xkbcommon'))
if xkbcommon.found()
all_found_deps += xkbcommon
Expand Down Expand Up @@ -568,6 +574,7 @@ featurevals = {
'Shaped Windows': xext.found() ? xext : false,
'SVG support': librsvg.found() ? librsvg : false,
'Xcursor': xcursor.found() ? xcursor : false,
'XFixes': xfixes.found() ? xfixes : false,
'xkbcommon': xkbcommon.found() ? xkbcommon : false,
'XPM support': xpm.found() ? xpm : false,
'XRender': xrender.found() ? xrender : false,
Expand Down
6 changes: 6 additions & 0 deletions meson.options
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ option(
value: 'auto',
description: 'Enable Xcursor support',
)
option(
'xfixes',
type: 'feature',
value: 'auto',
description: 'Enable XFixes support',
)
option(
'xkbcommon',
type: 'feature',
Expand Down

0 comments on commit 56b8a74

Please sign in to comment.