Closed LaylBongers closed 3 years ago
I think a more flexible solution would be to let the application start an interactive move. Wayland clients can do this.
Unfortunately it looks like Windows does only support defining an area where drag and drop is possible.
On X11 it seems like this is done with the wm-spec (also called ewmh) protocol, would it be possible to use it somehow?
Do you just need an EWMH hint, or do you really mean a protocol?
I see it's only a spec and not a protocol, so excuse my error. For allowing window movement this message is probably needed. So I think it's a hint.
Please note that I just searched for this and have no prior experience in this regard. But I want to learn more about things like these and work with them.
Do you think I could work on this a little? I do not think it's only relevant for Windows and since it has low priority there would not be too much pressure.
It's a client message sent to the root window; a hint is just a window property. I can't say whether or not this qualifies as a protocol without getting needlessly pedantic, but my main reason for mentioning it was because protocols can be a big pain. That's not an issue here, though.
Do you think I could work on this a little?
Feel free! Reviewing/advising other people's PRs is more fun than making my own, to be honest.
Fortunately, winit's X11 util
module (authored by yours truly) contains a bunch of convenience/safety wrappers for various Xlib functions. send_client_msg
is the one you want for this.
I'd say winit itself is pretty good reference material for learning how to program X11 clients, since I've generally tried to do things in maintainable ways. You can also just ask me things, since what you'll discover is that almost nobody in the past decade and a half has directly touched this stuff, and thus learning resources are super scarce.
Wow, i got great consistency with my initial implementation!
On KWin the window jumps once (not continuous) but as soon as you cancel the move it jumps back.
On Gnome X11 it doesn't do anything at all.
On Gnome via XWayland it works very well except that I don't get release events for secondary mouse buttons.
This is so strange. I guess this message is used for e.g. GTK CSD so it has to work somehow.
It's hard for me to say much without seeing the code. You can also use hint_is_supported
to check if the WM advertises support for that atom (I know the function contains the word "hint", and that I said this isn't a hint, but that doesn't really matter).
I guess this message is used for e.g. GTK CSD so it has to work somehow.
If you ctrl+F for _NET_WM_MOVERESIZE
in this file, you'll find some results: https://github.com/GNOME/gtk/blob/d13843ee2a5e2afabe6418cc7e9e744258a3fc5d/gdk/x11/gdksurface-x11.c
Yes, I have looked at some other implementations and can't really spot a relevant difference.
This is what I implemented. I'm calling this function every time I want to start or end a move (e.g. on button and cursor events). I've tried different values for button and mask but haven't got satisfying results.
pub fn start_move(&self, logical_position: LogicalPosition, stop: bool){
self.grab_cursor(false);
let (x, y): (i32, i32) = logical_position.to_physical(self.get_hidpi_factor()).into();
let (root_x, root_y): (i32, i32) = {
let pos = self.get_inner_position_physical().unwrap();
(pos.0 + x, pos.1 + y)
};
let move_resize_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_MOVERESIZE\0") };
let button = 0; //ffi::Button1;
let test= self.xconn.send_client_msg(
self.xwindow,
self.root,
move_resize_atom,
Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask),
[
root_x as c_long,
root_y as c_long,
if stop {11} else {8} as c_long, // _NET_WM_MOVERESIZE_MOVE or _NET_WM_MOVERESIZE_CANCEL
button as c_long,
0,
],
).flush();
}
I got it to grab once (though not in a way that was good), but since then, I don't remember what example code I used to do it, and nothing else I've tried has worked... I suspect the problem is in the usage of the messages rather than the messages themselves.
Reviving this - I'm okay in principle with this getting added, but I'd like to see an API design and/or PR that works across platforms.
In principle I am open to continue my initial attempt at getting this to work on Linux (X11). Unfortunately I am stuck. I would appreciate if anyone can tell me what I am doing wrong.
After your comment, I wanted to look at it again. I looked at this spec page and my implementation again and tried a few things on KWin.
Apparently moving the window with the keyboard works fine after sending the according message. Moving with the mouse produces strange results. I suspect I am supplying the wrong mouse position or button but it should be correct. Maybe someone else can take a look at it.
Resizing from the respective window edges is analogous and should be very easy to implement after the error is found.
I started working on this. To explain the issue I found in @Shookbelf's code, self.grab_cursor(false);
doesn't execute XUngrabPointer
unless the cursor has been grabbed before, but _NET_WM_MOVERESIZE
requires the use of XUngrabPointer
.
So replacing self.grab_cursor(false);
with:
unsafe {
(self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
}
self.xconn.flush_requests().unwrap();
fixes this.
@Osspial I would really love to make a PR for this, I'm working on implementing this for Wayland, MacOS and Windows too, but I can't find a way to expose a neat cross-platform API.
The following summary is for an API that only supports moving the window (no resizing) with holding down a mouse button (no keyboard input), and only tested on X11 (testing on Windows incoming, MacOS and Wayland testing has to be done by others):
Wayland
Uses wl_shell_surface::move
.
X11
Uses _NET_WM_MOVERESIZE_MOVE
to tell X11 that the last mouse button pressed should start moving the window until it is released again. _NET_WM_MOVERESIZE_CANCEL
should be moved to cancel it, because some buttons don't cancel automatically.
Window::set_cursor_grab
, we could potentially track it and reset it when dragging is doneMacOS
Uses NSWindow::performWindowDragWithEvent
.
Windows
Uses WM_NCHITTEST
or WM_NCLBUTTONDOWN
.
Limitations | Feature | Wayland | X11 | MacOS | Windows |
---|---|---|---|---|---|
only left button | ✘ | ✘ | ✘ | ✔ | |
only immediately after button press | ✔ | ✘ | ✔ | ✔ | |
only stops when released | ✔ | ✘ | ✔ | ✔ | |
may prevent button release event | ✘ | ✘ | ✔ | ✘ | |
undos cursor grab | ✘ | ✔ | ✘ | ✘ |
So my current approach is to just document all these caveats and let users deal with it, as I don't see an easy way to force this to be used correctly.
PR incoming.
There is one thing here that #1840 doesn't address, and that's user-defined resizing.
Users could arguably implement something like it by abusing set_cursor_icon
and set_outer_position
, but I doubt it would be as smooth as a solution which leverages the WM.
This is most likely low priority but a nice-to-have for making custom styled windows using borderless windows, and still allowing the user to drag a custom rendered title bar. On windows this is implemented using
WM_NCHITTEST
, which allows the application to define custom areas that can do these operations.