[PATCH 5/5] wmaker: add support for _NET_WM_MOVERESIZE

61 views
Skip to first unread message

david.m...@gmail.com

unread,
Jan 25, 2026, 4:11:44 PM (8 days ago) Jan 25
to Window Maker Development
This patch adds support for _NET_WM_MOVERESIZE hint as defined in EWMH
which let a window without decorations to manage itself (moving/resizing).
Tested with VS Code and Google Chrome.
Specs available at https://specifications.freedesktop.org/wm/1.5/ar01s04.html#id-1.5.4
---
 src/event.c  | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/screen.h | 12 ++++++++
 src/wmspec.c | 78 ++++++++++++++++++++++++++++-------------------
 src/wmspec.h | 20 ++++++++++++
 4 files changed, 162 insertions(+), 34 deletions(-)

diff --git a/src/event.c b/src/event.c
index 67d30098..4c45228a 100644
--- a/src/event.c
+++ b/src/event.c
@@ -3,7 +3,7 @@
  *  Window Maker window manager
  *
  *  Copyright (c) 1997-2003 Alfredo K. Kojima
- *  Copyright (c) 2014-2023 Window Maker Team
+ *  Copyright (c) 2014-2026 Window Maker Team
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -91,6 +91,7 @@ static void handleMapNotify(XEvent *event);
 static void handleUnmapNotify(XEvent *event);
 static void handleButtonPress(XEvent *event);
 static void handleExpose(XEvent *event);
+static void handleButtonRelease(XEvent *event);
 static void handleDestroyNotify(XEvent *event);
 static void handleConfigureRequest(XEvent *event);
 static void handleMapRequest(XEvent *event);
@@ -233,6 +234,10 @@ void DispatchEvent(XEvent * event)
  handleButtonPress(event);
  break;
 
+ case ButtonRelease:
+ handleButtonRelease(event);
+ break;
+
  case Expose:
  handleExpose(event);
  break;
@@ -886,6 +891,17 @@ static void handleButtonPress(XEvent * event)
  }
 }
 
+static void handleButtonRelease(XEvent * event)
+{
+ WScreen *scr = wScreenForRootWindow(event->xbutton.root);
+
+ if (scr && scr->moveresize.active && event->xbutton.button == Button1) {
+ XUngrabPointer(dpy, CurrentTime);
+ scr->moveresize.active = 0;
+ scr->moveresize.window = NULL;
+ }
+}
+
 static void handleMapNotify(XEvent * event)
 {
  WWindow *wwin;
@@ -1966,8 +1982,74 @@ static void hotCornerDelay(void *data)
 
 static void handleMotionNotify(XEvent *event)
 {
+ WScreen *scr = wScreenForRootWindow(event->xmotion.root);
+
+ /* Handle move/resize for _NET_WM_MOVERESIZE */
+ if (scr && scr->moveresize.active && scr->moveresize.window) {
+ WWindow *wwin = scr->moveresize.window;
+ int dx = event->xmotion.x_root - scr->moveresize.start_x;
+ int dy = event->xmotion.y_root - scr->moveresize.start_y;
+
+ if (scr->moveresize.is_move) {
+ int new_x = scr->moveresize.orig_x + dx;
+ int new_y = scr->moveresize.orig_y + dy;
+ wWindowConfigure(wwin, new_x, new_y, wwin->client.width, wwin->client.height);
+ } else {
+ int new_x = scr->moveresize.orig_x;
+ int new_y = scr->moveresize.orig_y;
+ int new_w = scr->moveresize.orig_w;
+ int new_h = scr->moveresize.orig_h;
+
+ switch (scr->moveresize.resize_edge) {
+ case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
+ new_x += dx;
+ new_y += dy;
+ new_w -= dx;
+ new_h -= dy;
+ break;
+ case _NET_WM_MOVERESIZE_SIZE_TOP:
+ new_y += dy;
+ new_h -= dy;
+ break;
+ case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
+ new_y += dy;
+ new_w += dx;
+ new_h -= dy;
+ break;
+ case _NET_WM_MOVERESIZE_SIZE_RIGHT:
+ new_w += dx;
+ break;
+ case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
+ new_w += dx;
+ new_h += dy;
+ break;
+ case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
+ new_h += dy;
+ break;
+ case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
+ new_x += dx;
+ new_w -= dx;
+ new_h += dy;
+ break;
+ case _NET_WM_MOVERESIZE_SIZE_LEFT:
+ new_x += dx;
+ new_w -= dx;
+ break;
+ }
+
+ if (new_w < 20)
+ new_w = 20;
+ if (new_h < 20)
+ new_h = 20;
+
+ wWindowConfigure(wwin, new_x, new_y,
+ new_w - (wwin->frame->core->width - wwin->client.width),
+ new_h - (wwin->frame->core->height - wwin->client.height));
+ }
+ return;
+ }
+
  if (wPreferences.scrollable_menus || wPreferences.hot_corners) {
- WScreen *scr = wScreenForRootWindow(event->xmotion.root);
  WMPoint p = wmkpoint(event->xmotion.x_root, event->xmotion.y_root);
  WMRect rect = wGetRectForHead(scr, wGetHeadForPoint(scr, p));
 
diff --git a/src/screen.h b/src/screen.h
index dac0be56..18f26488 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -2,6 +2,7 @@
  *  Window Maker window manager
  *
  *  Copyright (c) 1997-2003 Alfredo K. Kojima
+ *  Copyright (c) 2026 Window Maker Team
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -328,6 +329,17 @@ typedef struct _WScreen {
         unsigned int ignore_focus_events:1;
         unsigned int in_hot_corner:3;
     } flags;
+
+    /* move/resize state for _NET_WM_MOVERESIZE support */
+    struct {
+        int active;                    /* 1 if move/resize in progress */
+        int is_move;                   /* 1 for move, 0 for resize */
+        struct WWindow *window;        /* window being moved/resized */
+        int start_x, start_y;          /* pointer position when started */
+        int orig_x, orig_y;            /* original window position */
+        int orig_w, orig_h;            /* original window size */
+        int resize_edge;               /* which edge for resize (0-7) */
+    } moveresize;
 } WScreen;
 
 
diff --git a/src/wmspec.c b/src/wmspec.c
index fe313410..9b50a87d 100644
--- a/src/wmspec.c
+++ b/src/wmspec.c
@@ -3,6 +3,7 @@
  *  Window Maker window manager
  *
  *  Copyright (c) 1998-2003 Alfredo K. Kojima
+ *  Copyright (c) 2026 Window Maker Team
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -71,7 +72,7 @@ static Atom net_showing_desktop;
 /* Other Root Window Messages */
 static Atom net_close_window;
 static Atom net_moveresize_window; /* TODO */
-static Atom net_wm_moveresize; /* TODO */
+static Atom net_wm_moveresize;
 
 /* Application Window Properties */
 static Atom net_wm_name;
@@ -226,35 +227,6 @@ static atomitem_t atomNames[] = {
 #define _NET_WM_STATE_ADD 1
 #define _NET_WM_STATE_TOGGLE 2
 
-#if 0
-/*
- * These constant provide information on the kind of window move/resize when
- * it is initiated by the application instead of by WindowMaker. They are
- * parameter for the client message _NET_WM_MOVERESIZE, as defined by the
- * FreeDesktop wm-spec standard:
- *   http://standards.freedesktop.org/wm-spec/1.5/ar01s04.html
- *
- * Today, WindowMaker does not support this at all (the corresponding Atom
- * is not added to the list in setSupportedHints), probably because there is
- * nothing it needs to do about it, the application is assumed to know what
- * it is doing, and WindowMaker won't get in the way.
- *
- * The definition of the constants (taken from the standard) are disabled to
- * avoid a spurious warning (-Wunused-macros).
- */
-#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
-#define _NET_WM_MOVERESIZE_SIZE_TOP          1
-#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
-#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
-#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
-#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
-#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
-#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
-#define _NET_WM_MOVERESIZE_MOVE              8 /* movement only */
-#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9 /* size via keyboard */
-#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10 /* move via keyboard */
-#endif
-
 static void observer(void *self, WMNotification *notif);
 static void wsobserver(void *self, WMNotification *notif);
 
@@ -294,9 +266,7 @@ static void setSupportedHints(WScreen *scr)
  atom[i++] = net_workarea;
  atom[i++] = net_supporting_wm_check;
  atom[i++] = net_showing_desktop;
-#if 0
  atom[i++] = net_wm_moveresize;
-#endif
  atom[i++] = net_wm_desktop;
 #ifdef USE_XINERAMA
  atom[i++] = net_wm_fullscreen_monitors;
@@ -1740,6 +1710,50 @@ Bool wNETWMProcessClientMessage(XClientMessageEvent *event)
  } else if (event->message_type == net_desktop_names) {
  handleDesktopNames(scr);
  return True;
+
+ } else if (event->message_type == net_wm_moveresize) {
+ wwin = wWindowFor(event->window);
+ if (!wwin || !wwin->frame)
+ return False;
+
+ int direction = event->data.l[2];
+ int x_root = event->data.l[0];
+ int y_root = event->data.l[1];
+
+ /* Check if already in progress */
+ if (scr->moveresize.active)
+ return True;
+
+ /* Check if operation is allowed */
+ if (direction == _NET_WM_MOVERESIZE_MOVE) {
+ if (WFLAGP(wwin, no_movable))
+ return True;
+ } else if (direction <= _NET_WM_MOVERESIZE_SIZE_LEFT) {
+ if (WFLAGP(wwin, no_resizable))
+ return True;
+ } else {
+ return True;
+ }
+
+ if(XGrabPointer(dpy, scr->root_win, False,
+  ButtonReleaseMask | PointerMotionMask | ButtonPressMask,
+  GrabModeAsync, GrabModeAsync,
+  None, None, CurrentTime) != GrabSuccess)
+ return True;
+
+ /* Set up the move/resize state */
+ scr->moveresize.active = 1;
+ scr->moveresize.is_move = (direction == _NET_WM_MOVERESIZE_MOVE);
+ scr->moveresize.window = wwin;
+ scr->moveresize.start_x = x_root;
+ scr->moveresize.start_y = y_root;
+ scr->moveresize.orig_x = wwin->frame_x;
+ scr->moveresize.orig_y = wwin->frame_y;
+ scr->moveresize.orig_w = wwin->frame->core->width;
+ scr->moveresize.orig_h = wwin->frame->core->height;
+ scr->moveresize.resize_edge = direction;
+
+ return True;
  }
  }
 
diff --git a/src/wmspec.h b/src/wmspec.h
index 6e171145..b0713ef0 100644
--- a/src/wmspec.h
+++ b/src/wmspec.h
@@ -28,6 +28,26 @@
 #include "window.h"
 #include <X11/Xlib.h>
 
+/*
+ * These constant provide information on the kind of window move/resize when
+ * it is initiated by the application instead of by WindowMaker. They are
+ * parameter for the client message _NET_WM_MOVERESIZE, as defined by the
+ * FreeDesktop wm-spec standard:
+ *   http://standards.freedesktop.org/wm-spec/1.5/ar01s04.html
+ */
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
+#define _NET_WM_MOVERESIZE_SIZE_TOP          1
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
+#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
+#define _NET_WM_MOVERESIZE_MOVE              8 /* movement only */
+#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9 /* size via keyboard */
+#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10 /* move via keyboard */
+#define _NET_WM_MOVERESIZE_CANCEL           11 /* cancel operation */
+
 void wNETWMInitStuff(WScreen *scr);
 void wNETWMCleanup(WScreen *scr);
 void wNETWMUpdateWorkarea(WScreen *scr);
--
2.43.0
0005-wmaker-add-support-for-_NET_WM_MOVERESIZE.patch

Kostas Michalopoulos

unread,
Jan 25, 2026, 11:28:18 PM (8 days ago) Jan 25
to wmake...@googlegroups.com
On 1/25/26 11:11 PM, david.m...@gmail.com wrote:
> This patch adds support for _NET_WM_MOVERESIZE hint as defined in EWMH
> which let a window without decorations to manage itself (moving/resizing).
> Tested with VS Code and Google Chrome.


I tested it with Steam and it seems to *kinda* work but i can often get
Steam to stop responding in moves/resizes - usually by opening popup
menus then using the wheel to move to another workspace and back, though
it isn't reliable - only to randomly (i.e. can't reliably reproduce)
have the Steam window be moved by the mouse as if i was dragging it but
without me keeping any button down.

> +static void handleButtonRelease(XEvent * event)
> +{
> +WScreen *scr = wScreenForRootWindow(event->xbutton.root);
> +
> +if (scr && scr->moveresize.active && event->xbutton.button == Button1) {


(sorry for the formatting, Thunderbird ate the spaces)

I think it should store and use the button that initiated the
move/resize (passed as l[3]) instead of hardcoding Button1. However it
might be an even better idea to end the interaction regardless of button
- at least other WMs (e.g. IceWM) seem to do that.

>  static void handleMapNotify(XEvent * event)
>  {
> WWindow *wwin;
> @@ -1966,8 +1982,74 @@ static void hotCornerDelay(void *data)
>
>  static void handleMotionNotify(XEvent *event)
>  {
> +WScreen *scr = wScreenForRootWindow(event->xmotion.root);
> +
> +/* Handle move/resize for _NET_WM_MOVERESIZE */


Shouldn't this code use the existing Window Maker logic for resizes? For
example i can resize Steam's window via its own grip (that uses the
_NET_WM_MOVERESIZE message) down to 20x20 or so but if i use Window
Maker's resize bar (by forcing it by the Ignore Decoration Changes from
the Advanced Options group in the window attributes) or resize it using
Mod+Drag, then it snaps/limits its size to 1010x600 which seems to be
the minimum size set by Steam for its window.

> +/* Check if already in progress */
> +if (scr->moveresize.active)
> +return True;

I think this should change to something like this to also handle
_NET_WM_MOVERESIZE_CANCEL:

---
/* Check if already in progress */
if (scr->moveresize.active) {
if (direction == _NET_WM_MOVERESIZE_CANCEL) {
XUngrabPointer(dpy, CurrentTime);
scr->moveresize.active = 0;
scr->moveresize.window = NULL;
}
return True;
}
---

Though a better idea is to move the "end/cancel moveresize" code in its
own function because it should be ended/cancelled when a window is
unmapped/destroyed too. Similarly for "start moveresize" too (to be
consistent, even if it is called by a single place).

All that said, I modified the code a bit to add the above and also
remove the button check but didn't seem to make much of a different wrt
Steam. Not sure if cancelling it during unmapping/destroying would help
(AFAICT IceWM does seem to do that though and IIRC Steam works fine
under IceWM).

Kostas

david.m...@gmail.com

unread,
Jan 26, 2026, 8:07:56 AM (8 days ago) Jan 26
to Window Maker Development
Thanks Kostas for the feedback, I did not try it on Steam, I will try.

Can you confirm at least it's working for you with VS Code and Google Chrome ?
I have been using the patch for 1 week with those 2 apps and did not see any issue
so I am wondering if it's cause of the different setup.
 

Kostas Michalopoulos

unread,
Jan 26, 2026, 2:01:33 PM (7 days ago) Jan 26
to wmake...@googlegroups.com
On 1/26/26 3:07 PM, david.m...@gmail.com wrote:
> Thanks Kostas for the feedback, I did not try it on Steam, I will try.
>
> Can you confirm at least it's working for you with VS Code and Google
> Chrome ?
> I have been using the patch for 1 week with those 2 apps and did not see
> any issue
> so I am wondering if it's cause of the different setup.

I tried Chromium and seems to work ok, though i mainly use Firefox so i
only tied it a little. AFAICT VS Code works fine, though again i only
tested it a little since i do not use it :-P.

Kostas

david.m...@gmail.com

unread,
Jan 26, 2026, 4:25:13 PM (7 days ago) Jan 26
to Window Maker Development
I tried Steam, but I am not using the mouse wheel for actions that's maybe why I did not see anything.
I am thinking there could be a potential kind of race condition if you are in the middle of moving the window
with this new hint and also trying at the same time to use mouse/keyboard actions to move it the old normal way.
 

Kostas Michalopoulos

unread,
Jan 27, 2026, 9:08:51 PM (6 days ago) Jan 27
to wmake...@googlegroups.com
I used the mouse wheel on the desktop/background to switch workspaces in
that test, so it was always outside the move mode, though what you
describe can also happen if you switch workspace with the keyboard while
moving a window.

Kostas

david.m...@gmail.com

unread,
Jan 28, 2026, 5:29:43 PM (5 days ago) Jan 28
to Window Maker Development
Indeed, that's weird if the mouse is on the root screen it should not trigger the net_wm_moveresize hint.
Anyway, I will do some more tests and send a v2 version.

david.m...@gmail.com

unread,
Jan 29, 2026, 5:28:11 PM (4 days ago) Jan 29
to Window Maker Development
I found the issue with Steam, it's just easy with Steam to run into that race condition which is mentioned also at https://github.com/awesomeWM/awesome/pull/3859

I moved the code from the screen to the window itself and reused the existing resize/move window functions.
That's simplifying the code.

Please have a try.
 
0001-wmaker-add-support-for-_NET_WM_MOVERESIZE.patch

david.m...@gmail.com

unread,
Jan 30, 2026, 7:47:21 AM (4 days ago) Jan 30
to Window Maker Development
Forgot to mention this patch is linked to https://github.com/window-maker/wmaker/issues/20
Reply all
Reply to author
Forward
0 new messages