Skip to content

Commit 6d5e47a

Browse files
committed
Merge pull request godotengine#100532 from bruvzg/win_size_drag
Implement `DisplayServer.window_start_resize`.
2 parents 084da58 + 7f0b4e5 commit 6d5e47a

14 files changed

+340
-2
lines changed

doc/classes/DisplayServer.xml

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1836,8 +1836,17 @@
18361836
<return type="void" />
18371837
<param index="0" name="window_id" type="int" default="0" />
18381838
<description>
1839-
Starts a drag operation on the window with the given [param window_id], using the current mouse position. Call this method when handling a mouse button being pressed to simulate a pressed event on the window's title bar. Using this method allows the window to participate in space switching, tiling, and other system features.
1840-
[b]Note:[/b] This method is implemented on Linux(X11/Wayland), macOS, and Windows.
1839+
Starts an interactive drag operation on the window with the given [param window_id], using the current mouse position. Call this method when handling a mouse button being pressed to simulate a pressed event on the window's title bar. Using this method allows the window to participate in space switching, tiling, and other system features.
1840+
[b]Note:[/b] This method is implemented on Linux (X11/Wayland), macOS, and Windows.
1841+
</description>
1842+
</method>
1843+
<method name="window_start_resize">
1844+
<return type="void" />
1845+
<param index="0" name="edge" type="int" enum="DisplayServer.WindowResizeEdge" />
1846+
<param index="1" name="window_id" type="int" default="0" />
1847+
<description>
1848+
Starts an interactive resize operation on the window with the given [param window_id], using the current mouse position. Call this method when handling a mouse button being pressed to simulate a pressed event on the window's edge.
1849+
[b]Note:[/b] This method is implemented on Linux (X11/Wayland), macOS, and Windows.
18411850
</description>
18421851
</method>
18431852
</methods>
@@ -2183,6 +2192,33 @@
21832192
Sent when the window title bar decoration is changed (e.g. [constant WINDOW_FLAG_EXTEND_TO_TITLE] is set or window entered/exited full screen mode).
21842193
[b]Note:[/b] This flag is implemented only on macOS.
21852194
</constant>
2195+
<constant name="WINDOW_EDGE_TOP_LEFT" value="0" enum="WindowResizeEdge">
2196+
Top-left edge of a window.
2197+
</constant>
2198+
<constant name="WINDOW_EDGE_TOP" value="1" enum="WindowResizeEdge">
2199+
Top edge of a window.
2200+
</constant>
2201+
<constant name="WINDOW_EDGE_TOP_RIGHT" value="2" enum="WindowResizeEdge">
2202+
Top-right edge of a window.
2203+
</constant>
2204+
<constant name="WINDOW_EDGE_LEFT" value="3" enum="WindowResizeEdge">
2205+
Left edge of a window.
2206+
</constant>
2207+
<constant name="WINDOW_EDGE_RIGHT" value="4" enum="WindowResizeEdge">
2208+
Right edge of a window.
2209+
</constant>
2210+
<constant name="WINDOW_EDGE_BOTTOM_LEFT" value="5" enum="WindowResizeEdge">
2211+
Bottom-left edge of a window.
2212+
</constant>
2213+
<constant name="WINDOW_EDGE_BOTTOM" value="6" enum="WindowResizeEdge">
2214+
Bottom edge of a window.
2215+
</constant>
2216+
<constant name="WINDOW_EDGE_BOTTOM_RIGHT" value="7" enum="WindowResizeEdge">
2217+
Bottom-right edge of a window.
2218+
</constant>
2219+
<constant name="WINDOW_EDGE_MAX" value="8" enum="WindowResizeEdge">
2220+
Represents the size of the [enum WindowResizeEdge] enum.
2221+
</constant>
21862222
<constant name="VSYNC_DISABLED" value="0" enum="VSyncMode">
21872223
No vertical synchronization, which means the engine will display frames as fast as possible (tearing may be visible). Framerate is unlimited (regardless of [member Engine.max_fps]).
21882224
</constant>

platform/linuxbsd/wayland/display_server_wayland.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,13 @@ void DisplayServerWayland::window_start_drag(WindowID p_window) {
993993
wayland_thread.window_start_drag(p_window);
994994
}
995995

996+
void DisplayServerWayland::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {
997+
MutexLock mutex_lock(wayland_thread.mutex);
998+
999+
ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);
1000+
wayland_thread.window_start_resize(p_edge, p_window);
1001+
}
1002+
9961003
void DisplayServerWayland::cursor_set_shape(CursorShape p_shape) {
9971004
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
9981005

platform/linuxbsd/wayland/display_server_wayland.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ class DisplayServerWayland : public DisplayServer {
273273
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_window_id) const override;
274274

275275
virtual void window_start_drag(WindowID p_window = MAIN_WINDOW_ID) override;
276+
virtual void window_start_resize(WindowResizeEdge p_edge, WindowID p_window) override;
276277

277278
virtual void cursor_set_shape(CursorShape p_shape) override;
278279
virtual CursorShape cursor_get_shape() const override;

platform/linuxbsd/wayland/wayland_thread.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3354,6 +3354,80 @@ void WaylandThread::window_start_drag(DisplayServer::WindowID p_window_id) {
33543354
#endif
33553355
}
33563356

3357+
void WaylandThread::window_start_resize(DisplayServer::WindowResizeEdge p_edge, DisplayServer::WindowID p_window) {
3358+
// TODO: Use window IDs for multiwindow support.
3359+
WindowState &ws = main_window;
3360+
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
3361+
3362+
if (ss && ws.xdg_toplevel) {
3363+
xdg_toplevel_resize_edge edge = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
3364+
switch (p_edge) {
3365+
case DisplayServer::WINDOW_EDGE_TOP_LEFT: {
3366+
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
3367+
} break;
3368+
case DisplayServer::WINDOW_EDGE_TOP: {
3369+
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
3370+
} break;
3371+
case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {
3372+
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
3373+
} break;
3374+
case DisplayServer::WINDOW_EDGE_LEFT: {
3375+
edge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
3376+
} break;
3377+
case DisplayServer::WINDOW_EDGE_RIGHT: {
3378+
edge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
3379+
} break;
3380+
case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {
3381+
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
3382+
} break;
3383+
case DisplayServer::WINDOW_EDGE_BOTTOM: {
3384+
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
3385+
} break;
3386+
case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {
3387+
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
3388+
} break;
3389+
default:
3390+
break;
3391+
}
3392+
xdg_toplevel_resize(ws.xdg_toplevel, ss->wl_seat, ss->pointer_data.button_serial, edge);
3393+
}
3394+
3395+
#ifdef LIBDECOR_ENABLED
3396+
if (ws.libdecor_frame) {
3397+
libdecor_resize_edge edge = LIBDECOR_RESIZE_EDGE_NONE;
3398+
switch (p_edge) {
3399+
case DisplayServer::WINDOW_EDGE_TOP_LEFT: {
3400+
edge = LIBDECOR_RESIZE_EDGE_TOP_LEFT;
3401+
} break;
3402+
case DisplayServer::WINDOW_EDGE_TOP: {
3403+
edge = LIBDECOR_RESIZE_EDGE_TOP;
3404+
} break;
3405+
case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {
3406+
edge = LIBDECOR_RESIZE_EDGE_TOP_RIGHT;
3407+
} break;
3408+
case DisplayServer::WINDOW_EDGE_LEFT: {
3409+
edge = LIBDECOR_RESIZE_EDGE_LEFT;
3410+
} break;
3411+
case DisplayServer::WINDOW_EDGE_RIGHT: {
3412+
edge = LIBDECOR_RESIZE_EDGE_RIGHT;
3413+
} break;
3414+
case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {
3415+
edge = LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT;
3416+
} break;
3417+
case DisplayServer::WINDOW_EDGE_BOTTOM: {
3418+
edge = LIBDECOR_RESIZE_EDGE_BOTTOM;
3419+
} break;
3420+
case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {
3421+
edge = LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT;
3422+
} break;
3423+
default:
3424+
break;
3425+
}
3426+
libdecor_frame_resize(ws.libdecor_frame, ss->wl_seat, ss->pointer_data.button_serial, edge);
3427+
}
3428+
#endif
3429+
}
3430+
33573431
void WaylandThread::window_set_max_size(DisplayServer::WindowID p_window_id, const Size2i &p_size) {
33583432
// TODO: Use window IDs for multiwindow support.
33593433
WindowState &ws = main_window;

platform/linuxbsd/wayland/wayland_thread.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,8 @@ class WaylandThread {
955955

956956
struct wl_surface *window_get_wl_surface(DisplayServer::WindowID p_window_id) const;
957957

958+
void window_start_resize(DisplayServer::WindowResizeEdge p_edge, DisplayServer::WindowID p_window);
959+
958960
void window_set_max_size(DisplayServer::WindowID p_window_id, const Size2i &p_size);
959961
void window_set_min_size(DisplayServer::WindowID p_window_id, const Size2i &p_size);
960962

platform/linuxbsd/x11/display_server_x11.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@
6969
#define _NET_WM_STATE_REMOVE 0L // remove/unset property
7070
#define _NET_WM_STATE_ADD 1L // add/set property
7171

72+
#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0L
73+
#define _NET_WM_MOVERESIZE_SIZE_TOP 1L
74+
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2L
75+
#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3L
76+
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4L
77+
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5L
78+
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6L
79+
#define _NET_WM_MOVERESIZE_SIZE_LEFT 7L
7280
#define _NET_WM_MOVERESIZE_MOVE 8L
7381

7482
// 2.2 is the first release with multitouch
@@ -5516,6 +5524,70 @@ void DisplayServerX11::window_start_drag(WindowID p_window) {
55165524
XSync(x11_display, 0);
55175525
}
55185526

5527+
void DisplayServerX11::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {
5528+
_THREAD_SAFE_METHOD_
5529+
5530+
ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);
5531+
5532+
ERR_FAIL_COND(!windows.has(p_window));
5533+
WindowData &wd = windows[p_window];
5534+
5535+
XClientMessageEvent m;
5536+
memset(&m, 0, sizeof(m));
5537+
5538+
XUngrabPointer(x11_display, CurrentTime);
5539+
5540+
Window root_return, child_return;
5541+
int root_x, root_y, win_x, win_y;
5542+
unsigned int mask_return;
5543+
5544+
Bool xquerypointer_result = XQueryPointer(x11_display, wd.x11_window, &root_return, &child_return, &root_x, &root_y, &win_x, &win_y, &mask_return);
5545+
5546+
m.type = ClientMessage;
5547+
m.window = wd.x11_window;
5548+
m.message_type = XInternAtom(x11_display, "_NET_WM_MOVERESIZE", True);
5549+
m.format = 32;
5550+
if (xquerypointer_result) {
5551+
m.data.l[0] = root_x;
5552+
m.data.l[1] = root_y;
5553+
m.data.l[3] = Button1;
5554+
}
5555+
5556+
switch (p_edge) {
5557+
case DisplayServer::WINDOW_EDGE_TOP_LEFT: {
5558+
m.data.l[2] = _NET_WM_MOVERESIZE_SIZE_TOPLEFT;
5559+
} break;
5560+
case DisplayServer::WINDOW_EDGE_TOP: {
5561+
m.data.l[2] = _NET_WM_MOVERESIZE_SIZE_TOP;
5562+
} break;
5563+
case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {
5564+
m.data.l[2] = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
5565+
} break;
5566+
case DisplayServer::WINDOW_EDGE_LEFT: {
5567+
m.data.l[2] = _NET_WM_MOVERESIZE_SIZE_LEFT;
5568+
} break;
5569+
case DisplayServer::WINDOW_EDGE_RIGHT: {
5570+
m.data.l[2] = _NET_WM_MOVERESIZE_SIZE_RIGHT;
5571+
} break;
5572+
case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {
5573+
m.data.l[2] = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
5574+
} break;
5575+
case DisplayServer::WINDOW_EDGE_BOTTOM: {
5576+
m.data.l[2] = _NET_WM_MOVERESIZE_SIZE_BOTTOM;
5577+
} break;
5578+
case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {
5579+
m.data.l[2] = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
5580+
} break;
5581+
default:
5582+
break;
5583+
}
5584+
m.data.l[4] = 1; // Source - normal application.
5585+
5586+
XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&m);
5587+
5588+
XSync(x11_display, 0);
5589+
}
5590+
55195591
pid_t get_window_pid(Display *p_display, Window p_window) {
55205592
Atom atom = XInternAtom(p_display, "_NET_WM_PID", False);
55215593
Atom actualType;

platform/linuxbsd/x11/display_server_x11.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ class DisplayServerX11 : public DisplayServer {
527527
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
528528

529529
virtual void window_start_drag(WindowID p_window = MAIN_WINDOW_ID) override;
530+
virtual void window_start_resize(WindowResizeEdge p_edge, WindowID p_window) override;
530531

531532
virtual Error embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) override;
532533
virtual Error remove_embedded_process(OS::ProcessID p_pid) override;

platform/macos/display_server_macos.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class DisplayServerMacOS : public DisplayServer {
9292
Vector<Vector2> mpath;
9393

9494
Point2i mouse_pos;
95+
WindowResizeEdge edge = WINDOW_EDGE_MAX;
9596

9697
Size2i min_size;
9798
Size2i max_size;
@@ -409,6 +410,7 @@ class DisplayServerMacOS : public DisplayServer {
409410
virtual bool window_minimize_on_title_dbl_click() const override;
410411

411412
virtual void window_start_drag(WindowID p_window = MAIN_WINDOW_ID) override;
413+
virtual void window_start_resize(WindowResizeEdge p_edge, WindowID p_window = MAIN_WINDOW_ID) override;
412414

413415
virtual void window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window = MAIN_WINDOW_ID) override;
414416
virtual Vector3i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const override;

platform/macos/display_server_macos.mm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,16 @@
24402440
[wd.window_object performWindowDragWithEvent:event];
24412441
}
24422442

2443+
void DisplayServerMacOS::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {
2444+
_THREAD_SAFE_METHOD_
2445+
2446+
ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);
2447+
ERR_FAIL_COND(!windows.has(p_window));
2448+
WindowData &wd = windows[p_window];
2449+
2450+
wd.edge = p_edge;
2451+
}
2452+
24432453
void DisplayServerMacOS::window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window) {
24442454
_THREAD_SAFE_METHOD_
24452455

platform/macos/godot_content_view.mm

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,11 @@ - (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index pressed:(boo
394394
}
395395

396396
- (void)mouseDown:(NSEvent *)event {
397+
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
398+
if (ds && ds->has_window(window_id)) {
399+
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
400+
wd.edge = DisplayServer::WINDOW_EDGE_MAX;
401+
}
397402
if (([event modifierFlags] & NSEventModifierFlagControl)) {
398403
mouse_down_control = true;
399404
[self processMouseEvent:event index:MouseButton::RIGHT pressed:true outofstream:false];
@@ -404,10 +409,65 @@ - (void)mouseDown:(NSEvent *)event {
404409
}
405410

406411
- (void)mouseDragged:(NSEvent *)event {
412+
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
413+
if (ds && ds->has_window(window_id)) {
414+
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
415+
if (wd.edge != DisplayServer::WINDOW_EDGE_MAX) {
416+
Size2i max_size = wd.max_size / ds->screen_get_max_scale();
417+
Size2i min_size = wd.min_size / ds->screen_get_max_scale();
418+
NSRect frame = [wd.window_object frame];
419+
switch (wd.edge) {
420+
case DisplayServer::WINDOW_EDGE_TOP_LEFT: {
421+
int clamped_dx = CLAMP(frame.size.width - event.deltaX, min_size.x, max_size.x) - frame.size.width;
422+
int clamped_dy = CLAMP(frame.size.height - event.deltaY, min_size.y, max_size.y) - frame.size.height;
423+
[wd.window_object setFrame:NSMakeRect(frame.origin.x - clamped_dx, frame.origin.y, frame.size.width + clamped_dx, frame.size.height + clamped_dy) display:YES];
424+
} break;
425+
case DisplayServer::WINDOW_EDGE_TOP: {
426+
int clamped_dy = CLAMP(frame.size.height - event.deltaY, min_size.y, max_size.y) - frame.size.height;
427+
[wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height + clamped_dy) display:YES];
428+
} break;
429+
case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {
430+
int clamped_dx = CLAMP(frame.size.width + event.deltaX, min_size.x, max_size.x) - frame.size.width;
431+
int clamped_dy = CLAMP(frame.size.height - event.deltaY, min_size.y, max_size.y) - frame.size.height;
432+
[wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width + clamped_dx, frame.size.height + clamped_dy) display:YES];
433+
} break;
434+
case DisplayServer::WINDOW_EDGE_LEFT: {
435+
int clamped_dx = CLAMP(frame.size.width - event.deltaX, min_size.x, max_size.x) - frame.size.width;
436+
[wd.window_object setFrame:NSMakeRect(frame.origin.x - clamped_dx, frame.origin.y, frame.size.width + clamped_dx, frame.size.height) display:YES];
437+
} break;
438+
case DisplayServer::WINDOW_EDGE_RIGHT: {
439+
int clamped_dx = CLAMP(frame.size.width + event.deltaX, min_size.x, max_size.x) - frame.size.width;
440+
[wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width + clamped_dx, frame.size.height) display:YES];
441+
} break;
442+
case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {
443+
int clamped_dx = CLAMP(frame.size.width - event.deltaX, min_size.x, max_size.x) - frame.size.width;
444+
int clamped_dy = CLAMP(frame.size.height + event.deltaY, min_size.y, max_size.y) - frame.size.height;
445+
[wd.window_object setFrame:NSMakeRect(frame.origin.x - clamped_dx, frame.origin.y - clamped_dy, frame.size.width + clamped_dx, frame.size.height + clamped_dy) display:YES];
446+
} break;
447+
case DisplayServer::WINDOW_EDGE_BOTTOM: {
448+
int clamped_dy = CLAMP(frame.size.height + event.deltaY, min_size.y, max_size.y) - frame.size.height;
449+
[wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y - clamped_dy, frame.size.width, frame.size.height + clamped_dy) display:YES];
450+
} break;
451+
case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {
452+
int clamped_dx = CLAMP(frame.size.width + event.deltaX, min_size.x, max_size.x) - frame.size.width;
453+
int clamped_dy = CLAMP(frame.size.height + event.deltaY, min_size.y, max_size.y) - frame.size.height;
454+
[wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y - clamped_dy, frame.size.width + clamped_dx, frame.size.height + clamped_dy) display:YES];
455+
} break;
456+
default:
457+
break;
458+
}
459+
return;
460+
}
461+
}
407462
[self mouseMoved:event];
408463
}
409464

410465
- (void)mouseUp:(NSEvent *)event {
466+
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
467+
if (ds && ds->has_window(window_id)) {
468+
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
469+
wd.edge = DisplayServer::WINDOW_EDGE_MAX;
470+
}
411471
if (mouse_down_control) {
412472
[self processMouseEvent:event index:MouseButton::RIGHT pressed:false outofstream:false];
413473
} else {

0 commit comments

Comments
 (0)