From 7a9b1e64c02e2b9cf4d26be68039e9cb1239e459 Mon Sep 17 00:00:00 2001 From: Joshua Bemenderfer Date: Fri, 9 Feb 2018 18:07:59 +0800 Subject: [PATCH] Define, implement, and document OS.set_drag_mode(int). OS.set_drag_mode() tells the window manager how to handle drag events on your app. For example, setting DRAG_MODE_MOVE will tell the window manager to start moving your window on drag. Setting DRAG_MODE_RESIZE_BOTTOMRIGHT will tell the window manger to start resizing your window from the bottom right on drag. Support for these features is critical for borderless apps which need to support normal window manager interactions. --- core/bind/core_bind.cpp | 24 +++++++ core/bind/core_bind.h | 18 ++++++ core/os/os.cpp | 5 ++ core/os/os.h | 16 +++++ doc/classes/OS.xml | 52 +++++++++++++++ platform/osx/os_osx.h | 5 ++ platform/osx/os_osx.mm | 92 ++++++++++++++++++++++++-- platform/windows/os_windows.cpp | 30 ++++++++- platform/windows/os_windows.h | 4 ++ platform/x11/os_x11.cpp | 110 ++++++++++++++++++++++++++++---- platform/x11/os_x11.h | 5 ++ 11 files changed, 343 insertions(+), 18 deletions(-) diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 0032c4317985b..e2d4fbcd37e97 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -204,6 +204,16 @@ int _OS::get_mouse_button_state() const { return OS::get_singleton()->get_mouse_button_state(); } +void _OS::set_drag_mode(DragMode p_drag_mode) { + + OS::get_singleton()->set_drag_mode(OS::DragMode(p_drag_mode)); +} + +_OS::DragMode _OS::get_drag_mode() const { + + return DragMode(OS::get_singleton()->get_drag_mode()); +} + String _OS::get_unique_id() const { return OS::get_singleton()->get_unique_id(); } @@ -1145,6 +1155,8 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("has_touchscreen_ui_hint"), &_OS::has_touchscreen_ui_hint); ClassDB::bind_method(D_METHOD("set_window_title", "title"), &_OS::set_window_title); + ClassDB::bind_method(D_METHOD("set_drag_mode", "drag_mode"), &_OS::set_drag_mode); + ClassDB::bind_method(D_METHOD("get_drag_mode"), &_OS::get_drag_mode); ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode", "enable"), &_OS::set_low_processor_usage_mode); ClassDB::bind_method(D_METHOD("is_in_low_processor_usage_mode"), &_OS::is_in_low_processor_usage_mode); @@ -1251,6 +1263,7 @@ void _OS::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_screen_on"), "set_keep_screen_on", "is_keep_screen_on"); ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor"), "set_screen_orientation", "get_screen_orientation"); ADD_GROUP("Window", "window_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_mode", PROPERTY_HINT_ENUM, "None,Move,Resize Top,Resize Left,Resize Bottom,Resize Right,Resize Top Left,Resize Top Right,Resize Bottom Right,Resize Bottom Left"), "set_drag_mode", "get_drag_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_borderless"), "set_borderless_window", "get_borderless_window"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_per_pixel_transparency_enabled"), "set_window_per_pixel_transparency_enabled", "get_window_per_pixel_transparency_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_fullscreen"), "set_window_fullscreen", "is_window_fullscreen"); @@ -1281,6 +1294,17 @@ void _OS::_bind_methods() { BIND_ENUM_CONSTANT(MONTH_NOVEMBER); BIND_ENUM_CONSTANT(MONTH_DECEMBER); + BIND_ENUM_CONSTANT(DRAG_MODE_NONE); + BIND_ENUM_CONSTANT(DRAG_MODE_MOVE); + BIND_ENUM_CONSTANT(DRAG_MODE_RESIZE_TOP); + BIND_ENUM_CONSTANT(DRAG_MODE_RESIZE_RIGHT); + BIND_ENUM_CONSTANT(DRAG_MODE_RESIZE_BOTTOM); + BIND_ENUM_CONSTANT(DRAG_MODE_RESIZE_LEFT); + BIND_ENUM_CONSTANT(DRAG_MODE_RESIZE_TOPLEFT); + BIND_ENUM_CONSTANT(DRAG_MODE_RESIZE_TOPRIGHT); + BIND_ENUM_CONSTANT(DRAG_MODE_RESIZE_BOTTOMRIGHT); + BIND_ENUM_CONSTANT(DRAG_MODE_RESIZE_BOTTOMLEFT); + BIND_ENUM_CONSTANT(SCREEN_ORIENTATION_LANDSCAPE); BIND_ENUM_CONSTANT(SCREEN_ORIENTATION_PORTRAIT); BIND_ENUM_CONSTANT(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 720b14bf564c1..9362453663875 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -142,6 +142,23 @@ class _OS : public Object { void set_window_title(const String &p_title); int get_mouse_button_state() const; + enum DragMode { + + DRAG_MODE_NONE, + DRAG_MODE_MOVE, + DRAG_MODE_RESIZE_TOP, + DRAG_MODE_RESIZE_RIGHT, + DRAG_MODE_RESIZE_BOTTOM, + DRAG_MODE_RESIZE_LEFT, + DRAG_MODE_RESIZE_TOPLEFT, + DRAG_MODE_RESIZE_TOPRIGHT, + DRAG_MODE_RESIZE_BOTTOMRIGHT, + DRAG_MODE_RESIZE_BOTTOMLEFT + }; + + void set_drag_mode(DragMode p_drag_mode); + DragMode get_drag_mode() const; + void set_clipboard(const String &p_text); String get_clipboard() const; @@ -357,6 +374,7 @@ VARIANT_ENUM_CAST(_OS::Weekday); VARIANT_ENUM_CAST(_OS::Month); VARIANT_ENUM_CAST(_OS::SystemDir); VARIANT_ENUM_CAST(_OS::ScreenOrientation); +VARIANT_ENUM_CAST(_OS::DragMode); class _Geometry : public Object { diff --git a/core/os/os.cpp b/core/os/os.cpp index 7547b6a04255a..491c08782428c 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -510,6 +510,9 @@ void OS::native_video_stop(){ void OS::set_mouse_mode(MouseMode p_mode) { } +void OS::set_drag_mode(DragMode p_drag_mode) { +} + bool OS::can_use_threads() const { #ifdef NO_THREADS @@ -524,6 +527,8 @@ OS::MouseMode OS::get_mouse_mode() const { return MOUSE_MODE_VISIBLE; } +OS::DragMode OS::get_drag_mode() const { return DRAG_MODE_NONE; } + OS::LatinKeyboardVariant OS::get_latin_keyboard_variant() const { return LATIN_KEYBOARD_QWERTY; diff --git a/core/os/os.h b/core/os/os.h index 7786ffb26eb2e..ee7dd50b01216 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -172,6 +172,22 @@ class OS { virtual void set_mouse_mode(MouseMode p_mode); virtual MouseMode get_mouse_mode() const; + enum DragMode { + DRAG_MODE_NONE, + DRAG_MODE_MOVE, + DRAG_MODE_RESIZE_TOP, + DRAG_MODE_RESIZE_RIGHT, + DRAG_MODE_RESIZE_BOTTOM, + DRAG_MODE_RESIZE_LEFT, + DRAG_MODE_RESIZE_TOPLEFT, + DRAG_MODE_RESIZE_TOPRIGHT, + DRAG_MODE_RESIZE_BOTTOMRIGHT, + DRAG_MODE_RESIZE_BOTTOMLEFT + }; + + virtual void set_drag_mode(DragMode p_drag_mode); + virtual DragMode get_drag_mode() const; + virtual void warp_mouse_position(const Point2 &p_to) {} virtual Point2 get_mouse_position() const = 0; virtual int get_mouse_button_state() const = 0; diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index e218949757f35..ac6cd4ecdebf6 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -260,6 +260,13 @@ Returns the current state of the device regarding battery and power. See [code]POWERSTATE_*[/code] constants. + + + + + Returns the current window manager drag mode. (What action the window manager will take when the left mouse button is pressed and dragged in the window. Only valid on the [code]X11[/code] and [code]OSX[/code] platforms at the moment. See [code]DRAG_MODE_*[/code] constants. + + @@ -663,6 +670,15 @@ Sets the game's icon. + + + + + + + Sets the window manager drag mode. (What action the window manager will take when the left mouse button is pressed and dragged in the window.) Useful for implementing custom window dragging and resize handles. Only valid on the [code]X11[/code] and [code]OSX[/code] platforms at the moment. Valid arguments are the [code]DRAG_MODE_*[/code] constants. + + @@ -773,6 +789,9 @@ The size of the window (without counting window manager decorations). + + The window manager drag mode. (What action the window manager will take when the left mouse button is pressed and dragged in the window.) Used for implementing custom window dragging and resize handles. Only valid on the [code]X11[/code] and [code]OSX[/code] platforms at the moment. Valid arguments are the [code]DRAG_MODE_*[/code] constants. + @@ -892,5 +911,38 @@ Plugged in, battery fully charged. + + Tells the window manager to ignore the click / drag and passes it to your game (default). + While any state other than [code]DRAG_MODE_NONE[/code] is active, mouse events will not be passed to your game. + [code]DRAG_MODE_NONE[/code] is the only valid mode when the mouse is constrained or grabbed by your window. + Once the window manager has completed an action other than [code]DRAG_MODE_NONE[/code] the mode will be automatically reset to [code]DRAG_MODE_NONE[/code]. + + + Tells the window manger to start moving your window. + + + Tells the window manger to resize your window from the top. + + + Tells the window manger to resize your window from the right. + + + Tells the window manger to resize your window from the bottom. + + + Tells the window manger to resize your window from the left. + + + Tells the window manger to resize your window from the top-left corner. + + + Tells the window manger to resize your window from the top-right corner. + + + Tells the window manger to resize your window from the bottom-right corner. + + + Tells the window manger to resize your window from the bottom-left corner. + diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 546c88e74abc2..8641a0f13014e 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -110,6 +110,7 @@ class OS_OSX : public OS_Unix { CursorShape cursor_shape; NSCursor *cursors[CURSOR_MAX]; MouseMode mouse_mode; + DragMode drag_mode; String title; bool minimized; @@ -263,6 +264,10 @@ class OS_OSX : public OS_Unix { void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; + void set_drag_mode(DragMode p_drag_mode); + OS::DragMode get_drag_mode() const; + bool process_drag_mode(NSEvent *event); + void disable_crash_handler(); bool is_disable_crash_handler() const; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index e7b3e35381a86..36a4ccd3d1c93 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -556,6 +556,10 @@ - (BOOL)canBecomeKeyView { return YES; } +- (BOOL)mouseDownCanMoveWindow { + return YES; +} + - (BOOL)acceptsFirstResponder { return YES; } @@ -591,18 +595,75 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { - (void)mouseDown:(NSEvent *)event { if (([event modifierFlags] & NSEventModifierFlagControl)) { mouse_down_control = true; + _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true); } else { mouse_down_control = false; - _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, true); + if (OS_OSX::singleton->drag_mode != OS::DRAG_MODE_MOVE) { + _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, true); + } } } - (void)mouseDragged:(NSEvent *)event { - [self mouseMoved:event]; + // Prevent from being both movable and recieving drag events. + if ([[event window] isMovableByWindowBackground]) { + return; + } + + NSRect frame = [[event window] frame]; + switch (OS_OSX::singleton->drag_mode) { + case OS::DRAG_MODE_RESIZE_TOP: + [[event window] setFrame:NSMakeRect( + frame.origin.x, frame.origin.y, frame.size.width, frame.size.height - event.deltaY) + display:YES]; + break; + case OS::DRAG_MODE_RESIZE_BOTTOM: + [[event window] setFrame:NSMakeRect( + frame.origin.x, frame.origin.y - event.deltaY, frame.size.width, frame.size.height + event.deltaY) + display:YES]; + break; + case OS::DRAG_MODE_RESIZE_LEFT: + [[event window] setFrame:NSMakeRect( + frame.origin.x + event.deltaX, frame.origin.y, frame.size.width - event.deltaX, frame.size.height) + display:YES]; + break; + case OS::DRAG_MODE_RESIZE_RIGHT: + [[event window] setFrame:NSMakeRect( + frame.origin.x, frame.origin.y, frame.size.width + event.deltaX, frame.size.height) + display:YES]; + break; + case OS::DRAG_MODE_RESIZE_TOPLEFT: + [[event window] setFrame:NSMakeRect( + frame.origin.x + event.deltaX, frame.origin.y, frame.size.width - event.deltaX, frame.size.height - event.deltaY) + display:YES]; + break; + case OS::DRAG_MODE_RESIZE_BOTTOMLEFT: + [[event window] setFrame:NSMakeRect( + frame.origin.x + event.deltaX, frame.origin.y - event.deltaY, frame.size.width - event.deltaX, frame.size.height + event.deltaY) + display:YES]; + break; + case OS::DRAG_MODE_RESIZE_TOPRIGHT: + [[event window] setFrame:NSMakeRect( + frame.origin.x, frame.origin.y, frame.size.width + event.deltaX, frame.size.height - event.deltaY) + display:YES]; + break; + case OS::DRAG_MODE_RESIZE_BOTTOMRIGHT: + [[event window] setFrame:NSMakeRect( + frame.origin.x, frame.origin.y - event.deltaY, frame.size.width + event.deltaX, frame.size.height + event.deltaY) + display:YES]; + break; + default: + [self mouseMoved:event]; + } } - (void)mouseUp:(NSEvent *)event { + if (OS_OSX::singleton->drag_mode == OS::DRAG_MODE_MOVE && [event clickCount] == 2) { + OS_OSX::singleton->set_window_maximized(!OS_OSX::singleton->is_window_maximized()); + return; + } + if (mouse_down_control) { _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false); } else { @@ -623,8 +684,8 @@ - (void)mouseMoved:(NSEvent *)event { mm->set_position(pos); mm->set_global_position(pos); Vector2 relativeMotion = Vector2(); - relativeMotion.x = [event deltaX] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor); - relativeMotion.y = [event deltaY] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor); + relativeMotion.x = [event deltaX] * OS_OSX::singleton->_mouse_scale(backingScaleFactor); + relativeMotion.y = [event deltaY] * OS_OSX::singleton->_mouse_scale(backingScaleFactor); mm->set_relative(relativeMotion); get_key_modifier_state([event modifierFlags], mm); @@ -1134,6 +1195,10 @@ - (BOOL)canBecomeKeyWindow { return YES; } +- (BOOL)mouseDownCanMoveWindow { + return YES; +} + @end void OS_OSX::set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp) { @@ -1273,6 +1338,9 @@ static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplay [window_object center]; [window_object setRestorable:NO]; + if (p_desired.borderless_window) { + [window_object setHasShadow:YES]; + } unsigned int attributeCount = 0; @@ -2321,6 +2389,7 @@ static int get_screen_index(NSScreen *screen) { if (p_borderless) { [window_object setStyleMask:NSWindowStyleMaskBorderless]; + [window_object setHasShadow:YES]; } else { if (layered_window) set_window_per_pixel_transparency_enabled(false); @@ -2580,6 +2649,20 @@ static int get_screen_index(NSScreen *screen) { return mouse_mode; } +void OS_OSX::set_drag_mode(DragMode p_drag_mode) { + if (p_drag_mode == DRAG_MODE_MOVE) { + [window_object setMovableByWindowBackground:YES]; + } else { + [window_object setMovableByWindowBackground:NO]; + } + + drag_mode = p_drag_mode; +} + +OS::DragMode OS_OSX::get_drag_mode() const { + return drag_mode; +} + String OS_OSX::get_joy_guid(int p_device) const { return input->get_joy_guid_remapped(p_device); } @@ -2633,6 +2716,7 @@ static int get_screen_index(NSScreen *screen) { memset(cursors, 0, sizeof(cursors)); key_event_pos = 0; mouse_mode = OS::MOUSE_MODE_VISIBLE; + drag_mode = OS::DRAG_MODE_NONE; main_loop = NULL; singleton = this; im_active = false; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index bdf459fd83707..937121dd37857 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -915,7 +915,25 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } } break; - + case WM_NCHITTEST: { + // Only handle the hit test if the left mouse button is pressed. + bool buttons_swapped = GetSystemMetrics(SM_SWAPBUTTON); + if (!GetAsyncKeyState(VK_LBUTTON) && !buttons_swapped || !GetAsyncKeyState(VK_RBUTTON) && buttons_swapped) break; + + switch (drag_mode) { + case DRAG_MODE_MOVE: return HTCAPTION; + case DRAG_MODE_RESIZE_TOP: return HTTOP; + case DRAG_MODE_RESIZE_RIGHT: return HTRIGHT; + case DRAG_MODE_RESIZE_BOTTOM: return HTBOTTOM; + case DRAG_MODE_RESIZE_LEFT: return HTLEFT; + case DRAG_MODE_RESIZE_TOPLEFT: return HTTOPLEFT; + case DRAG_MODE_RESIZE_TOPRIGHT: return HTTOPRIGHT; + case DRAG_MODE_RESIZE_BOTTOMRIGHT: return HTBOTTOMRIGHT; + case DRAG_MODE_RESIZE_BOTTOMLEFT: return HTBOTTOMLEFT; + case DRAG_MODE_NONE: return HTCLIENT; + default: return HTCLIENT; + } + } break; default: { if (user_proc) { @@ -1574,6 +1592,16 @@ int OS_Windows::get_mouse_button_state() const { return last_button_state; } +void OS_Windows::set_drag_mode(DragMode p_drag_mode) { + + drag_mode = p_drag_mode; +} + +OS::DragMode OS_Windows::get_drag_mode() const { + + return drag_mode; +} + void OS_Windows::set_window_title(const String &p_title) { SetWindowTextW(hWnd, p_title.c_str()); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 6aadd6994cd3a..f5201c04ecbac 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -117,6 +117,8 @@ class OS_Windows : public OS { Vector2 im_position; MouseMode mouse_mode; + DragMode drag_mode; + bool alt_mem; bool gr_mem; bool shift_mem; @@ -196,6 +198,8 @@ class OS_Windows : public OS { void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; + virtual void set_drag_mode(DragMode p_drag_mode); + virtual OS::DragMode get_drag_mode() const; virtual void warp_mouse_position(const Point2 &p_to); virtual Point2 get_mouse_position() const; diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 04854e93b689a..846ad0951044f 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -57,6 +57,15 @@ #define _NET_WM_STATE_REMOVE 0L // remove/unset property #define _NET_WM_STATE_ADD 1L // add/set property #define _NET_WM_STATE_TOGGLE 2L // toggle property +#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 #include "main/main.h" @@ -398,18 +407,18 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a XSetWindowAttributes new_attr; new_attr.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | - ButtonReleaseMask | EnterWindowMask | - LeaveWindowMask | PointerMotionMask | - Button1MotionMask | - Button2MotionMask | Button3MotionMask | - Button4MotionMask | Button5MotionMask | - ButtonMotionMask | KeymapStateMask | - ExposureMask | VisibilityChangeMask | - StructureNotifyMask | - SubstructureNotifyMask | SubstructureRedirectMask | - FocusChangeMask | PropertyChangeMask | - ColormapChangeMask | OwnerGrabButtonMask | - im_event_mask; + ButtonReleaseMask | EnterWindowMask | + LeaveWindowMask | PointerMotionMask | + Button1MotionMask | + Button2MotionMask | Button3MotionMask | + Button4MotionMask | Button5MotionMask | + ButtonMotionMask | KeymapStateMask | + ExposureMask | VisibilityChangeMask | + StructureNotifyMask | + SubstructureNotifyMask | SubstructureRedirectMask | + FocusChangeMask | PropertyChangeMask | + ColormapChangeMask | OwnerGrabButtonMask | + im_event_mask; XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr); @@ -810,6 +819,13 @@ void OS_X11::set_window_per_pixel_transparency_enabled(bool p_enabled) { } } } +void OS_X11::set_drag_mode(DragMode p_drag_mode) { + drag_mode = p_drag_mode; +} + +OS::DragMode OS_X11::get_drag_mode() const { + return drag_mode; +} void OS_X11::set_window_title(const String &p_title) { XStoreName(x11_display, x11_window, p_title.utf8().get_data()); @@ -1953,7 +1969,14 @@ void OS_X11::process_xevents() { case ConfigureNotify: _window_changed(&event); break; - case ButtonPress: + case ButtonPress: { + if (event.xbutton.button == 1) { + // If the WM has taken over drag control, don't send any events to the + // game. + if (process_drag_mode(&event)) + break; + } + } case ButtonRelease: { /* exit in case of a mouse button press */ @@ -2276,6 +2299,67 @@ void OS_X11::process_xevents() { } } +// Handle native window manager drag modes. (Move, Resize, etc.) +bool OS_X11::process_drag_mode(XEvent *event) { + + if (drag_mode == DRAG_MODE_NONE || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) + return false; + + int resize_direction = -1; + + switch (drag_mode) { + case DRAG_MODE_MOVE: + resize_direction = _NET_WM_MOVERESIZE_MOVE; + break; + case DRAG_MODE_RESIZE_TOP: + resize_direction = _NET_WM_MOVERESIZE_SIZE_TOP; + break; + case DRAG_MODE_RESIZE_RIGHT: + resize_direction = _NET_WM_MOVERESIZE_SIZE_RIGHT; + break; + case DRAG_MODE_RESIZE_BOTTOM: + resize_direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM; + break; + case DRAG_MODE_RESIZE_LEFT: + resize_direction = _NET_WM_MOVERESIZE_SIZE_LEFT; + break; + case DRAG_MODE_RESIZE_TOPLEFT: + resize_direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; + break; + case DRAG_MODE_RESIZE_TOPRIGHT: + resize_direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; + break; + case DRAG_MODE_RESIZE_BOTTOMRIGHT: + resize_direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; + break; + case DRAG_MODE_RESIZE_BOTTOMLEFT: + resize_direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; + break; + default: + return false; + } + + XEvent evt; + XUngrabPointer(x11_display, 0L); + XFlush(x11_display); + + evt.xclient.type = ClientMessage; + evt.xclient.window = x11_window; + evt.xclient.message_type = XInternAtom(x11_display, "_NET_WM_MOVERESIZE", True); + evt.xclient.format = 32; + evt.xclient.data.l[0] = event->xbutton.x_root; + evt.xclient.data.l[1] = event->xbutton.y_root; + evt.xclient.data.l[2] = resize_direction; + evt.xclient.data.l[3] = Button1; + evt.xclient.data.l[4] = 0; + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); + + XSync(x11_display, 0); + + set_drag_mode(DRAG_MODE_NONE); + return true; +} + MainLoop *OS_X11::get_main_loop() const { return main_loop; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index bb8411e213af2..e6e088e090d85 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -140,6 +140,7 @@ class OS_X11 : public OS_Unix { void get_key_modifier_state(unsigned int p_x11_state, Ref state); MouseMode mouse_mode; + DragMode drag_mode; Point2i center; void handle_key_event(XKeyEvent *p_event, bool p_echo = false); @@ -217,6 +218,10 @@ class OS_X11 : public OS_Unix { void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; + virtual void set_drag_mode(DragMode p_drag_mode); + virtual OS::DragMode get_drag_mode() const; + bool process_drag_mode(XEvent *event); + virtual void warp_mouse_position(const Point2 &p_to); virtual Point2 get_mouse_position() const; virtual int get_mouse_button_state() const;