diff options
Diffstat (limited to 'intern/ghost')
83 files changed, 5485 insertions, 3452 deletions
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 5b06b5d98e6..aa23618ca39 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -3,13 +3,14 @@ set(INC . - ../glew-mx + ../clog + ../../source/blender/blenlib ../../source/blender/imbuf ../../source/blender/makesdna ) set(INC_SYS - ${GLEW_INCLUDE_PATH} + ${Epoxy_INCLUDE_DIRS} ) set(SRC @@ -24,6 +25,7 @@ set(SRC intern/GHOST_ISystemPaths.cpp intern/GHOST_ModifierKeys.cpp intern/GHOST_Path-api.cpp + intern/GHOST_PathUtils.cpp intern/GHOST_Rect.cpp intern/GHOST_System.cpp intern/GHOST_TimerManager.cpp @@ -58,6 +60,7 @@ set(SRC intern/GHOST_EventTrackpad.h intern/GHOST_EventWheel.h intern/GHOST_ModifierKeys.h + intern/GHOST_PathUtils.h intern/GHOST_System.h intern/GHOST_SystemPaths.h intern/GHOST_TimerManager.h @@ -65,11 +68,12 @@ set(SRC intern/GHOST_Util.h intern/GHOST_Window.h intern/GHOST_WindowManager.h + intern/GHOST_utildefines.h + intern/GHOST_utildefines_variadic.h ) set(LIB - bf_intern_glew_mx - ${GLEW_LIBRARY} + ${Epoxy_LIBRARIES} ) if(WITH_GHOST_DEBUG) @@ -99,42 +103,39 @@ if(WITH_INPUT_NDOF) ) endif() -if(WITH_HEADLESS OR WITH_GHOST_SDL) - if(WITH_HEADLESS) - list(APPEND SRC - intern/GHOST_DisplayManagerNULL.h - intern/GHOST_SystemNULL.h - intern/GHOST_WindowNULL.h +list(APPEND SRC + intern/GHOST_DisplayManagerNULL.h + intern/GHOST_SystemHeadless.h + intern/GHOST_WindowNULL.h +) + +if(WITH_HEADLESS) + add_definitions(-DWITH_HEADLESS) +elseif (WITH_GHOST_SDL) + list(APPEND SRC + intern/GHOST_ContextSDL.cpp + intern/GHOST_DisplayManagerSDL.cpp + intern/GHOST_SystemSDL.cpp + intern/GHOST_WindowSDL.cpp + + intern/GHOST_ContextSDL.h + intern/GHOST_DisplayManagerSDL.h + intern/GHOST_SystemSDL.h + intern/GHOST_WindowSDL.h + ) + add_definitions(-DWITH_GHOST_SDL) + + list(APPEND INC_SYS + ${SDL_INCLUDE_DIR} + ) + if(WITH_SDL_DYNLOAD) + list(APPEND LIB + extern_sdlew ) - add_definitions(-DWITH_HEADLESS) else() - list(APPEND SRC - intern/GHOST_ContextSDL.cpp - intern/GHOST_DisplayManagerSDL.cpp - intern/GHOST_SystemSDL.cpp - intern/GHOST_WindowSDL.cpp - - intern/GHOST_ContextSDL.h - intern/GHOST_DisplayManagerSDL.h - intern/GHOST_SystemSDL.h - intern/GHOST_WindowSDL.h - ) - add_definitions(-DWITH_GHOST_SDL) - endif() - - if(NOT WITH_HEADLESS) - list(APPEND INC_SYS - ${SDL_INCLUDE_DIR} + list(APPEND LIB + ${SDL_LIBRARY} ) - if(WITH_SDL_DYNLOAD) - list(APPEND LIB - extern_sdlew - ) - else() - list(APPEND LIB - ${SDL_LIBRARY} - ) - endif() endif() elseif(APPLE AND NOT WITH_GHOST_X11) @@ -152,13 +153,11 @@ elseif(APPLE AND NOT WITH_GHOST_X11) intern/GHOST_WindowViewCocoa.h ) - if(NOT WITH_GL_EGL) - list(APPEND SRC - intern/GHOST_ContextCGL.mm + list(APPEND SRC + intern/GHOST_ContextCGL.mm - intern/GHOST_ContextCGL.h - ) - endif() + intern/GHOST_ContextCGL.h + ) if(WITH_INPUT_NDOF) list(APPEND SRC @@ -192,13 +191,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) intern/GHOST_WindowX11.h ) - if(NOT WITH_GL_EGL) - list(APPEND SRC - intern/GHOST_ContextGLX.cpp + list(APPEND SRC + intern/GHOST_ContextGLX.cpp - intern/GHOST_ContextGLX.h - ) - endif() + intern/GHOST_ContextGLX.h + ) if(WITH_GHOST_XDND) add_definitions(-DWITH_XDND) @@ -245,10 +242,6 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) ) endif() - if(WITH_X11_ALPHA) - add_definitions(-DWITH_X11_ALPHA) - endif() - if(WITH_X11_XINPUT) add_definitions(-DWITH_X11_XINPUT) list(APPEND INC_SYS @@ -268,12 +261,34 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) ${wayland-egl_INCLUDE_DIRS} ${xkbcommon_INCLUDE_DIRS} ${wayland-cursor_INCLUDE_DIRS} - ${dbus_INCLUDE_DIRS} ) + if(WITH_GHOST_WAYLAND_DYNLOAD) + list(APPEND INC_SYS + ../../intern/wayland_dynload/extern + ) + list(APPEND LIB + bf_intern_wayland_dynload + ) + add_definitions(-DWITH_GHOST_WAYLAND_DYNLOAD) + endif() + + if(WITH_GHOST_WAYLAND_DBUS) + list(APPEND INC_SYS + ${dbus_INCLUDE_DIRS} + ) + endif() + + if(WITH_GHOST_WAYLAND_LIBDECOR) + list(APPEND INC_SYS + ${libdecor_INCLUDE_DIRS} + ) + endif() + include(CheckSymbolExists) set(CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") check_symbol_exists(memfd_create "sys/mman.h" HAVE_MEMFD_CREATE) + unset(CMAKE_REQUIRED_DEFINITIONS) if(HAVE_MEMFD_CREATE) add_definitions(-DHAVE_MEMFD_CREATE) endif() @@ -284,78 +299,70 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) intern/GHOST_SystemWayland.h intern/GHOST_WaylandCursorSettings.h + intern/GHOST_WaylandUtils.h intern/GHOST_WindowWayland.h ) - pkg_get_variable(WAYLAND_SCANNER wayland-scanner wayland_scanner) - - pkg_check_modules(wayland-protocols wayland-protocols>=1.15) - if(${wayland-protocols_FOUND}) - pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) - else() - find_path(WAYLAND_PROTOCOLS_DIR - NAMES unstable/xdg-decoration/xdg-decoration-unstable-v1.xml - PATH_SUFFIXES share/wayland-protocols - ) - endif() - - if(NOT EXISTS ${WAYLAND_PROTOCOLS_DIR}) - message(FATAL_ERROR "path to wayland-protocols not found") - endif() + set(INC_DST ${CMAKE_CURRENT_BINARY_DIR}/libwayland) # Generate protocols bindings. - macro(generate_protocol_bindings NAME PROT_DEF) + macro(generate_protocol_bindings PROT_DEF) + # File name without directory or extension (use for header name). + get_filename_component(_name ${PROT_DEF} NAME_WLE) add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}-client-protocol.h - COMMAND ${WAYLAND_SCANNER} client-header ${PROT_DEF} ${NAME}-client-protocol.h + OUTPUT ${INC_DST}/${_name}-client-protocol.h + COMMAND ${CMAKE_COMMAND} -E make_directory ${INC_DST} + COMMAND ${WAYLAND_SCANNER} client-header ${PROT_DEF} ${INC_DST}/${_name}-client-protocol.h ) add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}-client-protocol.c - COMMAND ${WAYLAND_SCANNER} private-code ${PROT_DEF} ${NAME}-client-protocol.c - DEPENDS ${NAME}-client-protocol.h + OUTPUT ${INC_DST}/${_name}-client-protocol.c + COMMAND ${CMAKE_COMMAND} -E make_directory ${INC_DST} + COMMAND ${WAYLAND_SCANNER} private-code ${PROT_DEF} ${INC_DST}/${_name}-client-protocol.c + DEPENDS ${INC_DST}/${_name}-client-protocol.h ) list(APPEND SRC - ${CMAKE_CURRENT_BINARY_DIR}/${NAME}-client-protocol.c - ${CMAKE_CURRENT_BINARY_DIR}/${NAME}-client-protocol.h + ${INC_DST}/${_name}-client-protocol.c + ${INC_DST}/${_name}-client-protocol.h ) + unset(_name) endmacro() + list(APPEND INC_SYS - ${CMAKE_CURRENT_BINARY_DIR} + ${INC_DST} ) - # `xdg-shell`. - generate_protocol_bindings( - xdg-shell - "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml" - ) - # `xdg-decoration`. - generate_protocol_bindings( - xdg-decoration - "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" - ) + if(NOT WITH_GHOST_WAYLAND_LIBDECOR) + # `xdg-shell`. + generate_protocol_bindings( + "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml" + ) + # `xdg-decoration`. + generate_protocol_bindings( + "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + ) + endif() + # `xdg-output`. generate_protocol_bindings( - xdg-output "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-output/xdg-output-unstable-v1.xml" ) # Pointer-constraints. generate_protocol_bindings( - pointer-constraints "${WAYLAND_PROTOCOLS_DIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" ) # Relative-pointer. generate_protocol_bindings( - relative-pointer "${WAYLAND_PROTOCOLS_DIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" ) # Tablet. generate_protocol_bindings( - tablet "${WAYLAND_PROTOCOLS_DIR}/unstable/tablet/tablet-unstable-v2.xml" ) add_definitions(-DWITH_GHOST_WAYLAND) + + unset(INC_DST) endif() if(WITH_INPUT_NDOF) @@ -400,13 +407,11 @@ elseif(WIN32) intern/GHOST_Wintab.h ) - if(NOT WITH_GL_EGL) - list(APPEND SRC - intern/GHOST_ContextWGL.cpp + list(APPEND SRC + intern/GHOST_ContextWGL.cpp - intern/GHOST_ContextWGL.h - ) - endif() + intern/GHOST_ContextWGL.h + ) if(WITH_INPUT_IME) add_definitions(-DWITH_INPUT_IME) @@ -427,7 +432,7 @@ elseif(WIN32) endif() endif() -if(WITH_GL_EGL AND NOT (WITH_HEADLESS OR WITH_GHOST_SDL)) +if(UNIX AND NOT APPLE) list(APPEND SRC intern/GHOST_ContextEGL.cpp @@ -517,11 +522,8 @@ if(WITH_XR_OPENXR) list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_WAYLAND) endif() if(WITH_GHOST_X11) - if(WITH_GL_EGL) - list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_EGL) - else() - list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_XLIB) - endif() + list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_EGL) + list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_XLIB) endif() endif() @@ -530,6 +532,4 @@ if(WITH_XR_OPENXR) unset(XR_PLATFORM_DEFINES) endif() -add_definitions(${GL_DEFINITIONS}) - blender_add_lib(bf_intern_ghost "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index d27be40af0c..399ee67a8fa 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -27,6 +27,7 @@ typedef bool (*GHOST_EventCallbackProcPtr)(GHOST_EventHandle event, GHOST_TUserD * \return a handle to the system. */ extern GHOST_SystemHandle GHOST_CreateSystem(void); +extern GHOST_SystemHandle GHOST_CreateSystemBackground(void); /** * Specifies whether debug messages are to be enabled for the specific system handle. @@ -49,7 +50,7 @@ extern GHOST_TSuccess GHOST_DisposeSystem(GHOST_SystemHandle systemhandle); * \param message: Message of the message box. * \param help_label: Text to show on the help button that opens a link. * \param continue_label: Text to show on the ok button that continues. - * \param link: Optional (hyper)link to a webpage to show when pressing help. + * \param link: Optional (hyper)link to a web-page to show when pressing help. * \param dialog_options: Options to configure the message box. */ extern void GHOST_ShowMessageBox(GHOST_SystemHandle systemhandle, @@ -364,6 +365,9 @@ extern GHOST_TSuccess GHOST_SetCustomCursorShape(GHOST_WindowHandle windowhandle int hotY, bool canInvertColor); +extern GHOST_TSuccess GHOST_GetCursorBitmap(GHOST_WindowHandle windowhandle, + GHOST_CursorBitmapRef *bitmap); + /** * Returns the visibility state of the cursor. * \param windowhandle: The handle to the window. @@ -380,32 +384,34 @@ extern bool GHOST_GetCursorVisibility(GHOST_WindowHandle windowhandle); extern GHOST_TSuccess GHOST_SetCursorVisibility(GHOST_WindowHandle windowhandle, bool visible); /** - * Returns the current location of the cursor (location in screen coordinates) + * Returns the current location of the cursor (location in client relative coordinates) * \param systemhandle: The handle to the system. * \param x: The x-coordinate of the cursor. * \param y: The y-coordinate of the cursor. * \return Indication of success. */ -extern GHOST_TSuccess GHOST_GetCursorPosition(GHOST_SystemHandle systemhandle, - int32_t *x, - int32_t *y); - +GHOST_TSuccess GHOST_GetCursorPosition(const GHOST_SystemHandle systemhandle, + const GHOST_WindowHandle windowhandle, + int32_t *x, + int32_t *y); /** - * Updates the location of the cursor (location in screen coordinates). + * Updates the location of the cursor (location in client relative coordinates). * Not all operating systems allow the cursor to be moved (without the input device being moved). * \param systemhandle: The handle to the system. * \param x: The x-coordinate of the cursor. * \param y: The y-coordinate of the cursor. * \return Indication of success. */ -extern GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle, - int32_t x, - int32_t y); +GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle, + GHOST_WindowHandle windowhandle, + int32_t x, + int32_t y); void GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle, GHOST_TGrabCursorMode *r_mode, - GHOST_TAxisFlag *r_wrap_axis, - int r_bounds[4]); + GHOST_TAxisFlag *r_axis_flag, + int r_bounds[4], + bool *r_use_software_cursor); /** * Grabs the cursor for a modal operation, to keep receiving @@ -435,7 +441,7 @@ extern GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle, * \return Indication of success. */ extern GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle, - GHOST_TModifierKeyMask mask, + GHOST_TModifierKey mask, bool *r_is_down); /** @@ -446,7 +452,7 @@ extern GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle, * \return Indication of success. */ extern GHOST_TSuccess GHOST_GetButtonState(GHOST_SystemHandle systemhandle, - GHOST_TButtonMask mask, + GHOST_TButton mask, bool *r_is_down); #ifdef WITH_INPUT_NDOF diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h index d946d2d882a..aa893a66a9d 100644 --- a/intern/ghost/GHOST_ISystem.h +++ b/intern/ghost/GHOST_ISystem.h @@ -120,6 +120,7 @@ class GHOST_ISystem { * \return An indication of success. */ static GHOST_TSuccess createSystem(); + static GHOST_TSuccess createSystemBackground(); /** * Disposes the one and only system. @@ -277,8 +278,7 @@ class GHOST_ISystem { */ virtual GHOST_TSuccess beginFullScreen(const GHOST_DisplaySetting &setting, GHOST_IWindow **window, - const bool stereoVisual, - const bool alphaBackground = 0) = 0; + const bool stereoVisual) = 0; /** * Updates the resolution while in fullscreen mode. @@ -365,6 +365,25 @@ class GHOST_ISystem { ***************************************************************************************/ /** + * Returns the current location of the cursor (location in window coordinates) + * \param x: The x-coordinate of the cursor. + * \param y: The y-coordinate of the cursor. + * \return Indication of success. + */ + virtual GHOST_TSuccess getCursorPositionClientRelative(const GHOST_IWindow *window, + int32_t &x, + int32_t &y) const = 0; + /** + * Updates the location of the cursor (location in window coordinates). + * \param x: The x-coordinate of the cursor. + * \param y: The y-coordinate of the cursor. + * \return Indication of success. + */ + virtual GHOST_TSuccess setCursorPositionClientRelative(GHOST_IWindow *window, + int32_t x, + int32_t y) = 0; + + /** * Returns the current location of the cursor (location in screen coordinates) * \param x: The x-coordinate of the cursor. * \param y: The y-coordinate of the cursor. @@ -391,7 +410,7 @@ class GHOST_ISystem { * \param isDown: The state of a modifier key (true == pressed). * \return Indication of success. */ - virtual GHOST_TSuccess getModifierKeyState(GHOST_TModifierKeyMask mask, bool &isDown) const = 0; + virtual GHOST_TSuccess getModifierKeyState(GHOST_TModifierKey mask, bool &isDown) const = 0; /** * Returns the state of a mouse button (outside the message queue). @@ -399,7 +418,7 @@ class GHOST_ISystem { * \param isDown: Button state. * \return Indication of success. */ - virtual GHOST_TSuccess getButtonState(GHOST_TButtonMask mask, bool &isDown) const = 0; + virtual GHOST_TSuccess getButtonState(GHOST_TButton mask, bool &isDown) const = 0; /** * Set which tablet API to use. Only affects Windows, other platforms have a single API. @@ -418,9 +437,9 @@ class GHOST_ISystem { /** * Set the Console State * \param action: console state - * \return current status (1 -visible, 0 - hidden) + * \return current status (true: visible, 0: hidden) */ - virtual int setConsoleWindowState(GHOST_TConsoleWindowState action) = 0; + virtual bool setConsoleWindowState(GHOST_TConsoleWindowState action) = 0; /*************************************************************************************** * Access to clipboard. diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h index 7927190de04..f712d9bd9f0 100644 --- a/intern/ghost/GHOST_IWindow.h +++ b/intern/ghost/GHOST_IWindow.h @@ -258,7 +258,10 @@ class GHOST_IWindow { virtual void getCursorGrabState(GHOST_TGrabCursorMode &mode, GHOST_TAxisFlag &axis_flag, - GHOST_Rect &bounds) = 0; + GHOST_Rect &bounds, + bool &use_software_cursor) = 0; + + virtual bool getCursorGrabUseSoftwareDisplay() = 0; /** * Test if the standard cursor shape is supported by current platform. @@ -282,6 +285,8 @@ class GHOST_IWindow { int hotY, bool canInvertColor) = 0; + virtual GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap) = 0; + /** * Returns the visibility state of the cursor. * \return The visibility state of the cursor. diff --git a/intern/ghost/GHOST_Rect.h b/intern/ghost/GHOST_Rect.h index a8082bf015a..15fa546fb07 100644 --- a/intern/ghost/GHOST_Rect.h +++ b/intern/ghost/GHOST_Rect.h @@ -101,6 +101,12 @@ class GHOST_Rect { * \param y: The y-coordinate of the point. */ virtual inline void wrapPoint(int32_t &x, int32_t &y, int32_t ofs, GHOST_TAxisFlag axis); + /** + * Confine x & y within the rectangle (inclusive). + * \param x: The x-coordinate of the point. + * \param y: The y-coordinate of the point. + */ + virtual inline void clampPoint(int32_t &x, int32_t &y); /** * Returns whether the point is inside this rectangle. @@ -238,7 +244,7 @@ inline void GHOST_Rect::wrapPoint(int32_t &x, int32_t &y, int32_t ofs, GHOST_TAx x -= w - (ofs * 2); } } - if (axis & GHOST_kGrabAxisY) { + if (axis & GHOST_kAxisY) { while (y - ofs < m_t) { y += h - (ofs * 2); } @@ -248,6 +254,23 @@ inline void GHOST_Rect::wrapPoint(int32_t &x, int32_t &y, int32_t ofs, GHOST_TAx } } +inline void GHOST_Rect::clampPoint(int32_t &x, int32_t &y) +{ + if (x < m_l) { + x = m_l; + } + else if (x > m_r) { + x = m_r; + } + + if (y < m_t) { + y = m_t; + } + else if (y > m_b) { + y = m_b; + } +} + inline bool GHOST_Rect::isInside(int32_t x, int32_t y) const { return (x >= m_l) && (x <= m_r) && (y >= m_t) && (y <= m_b); diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 78f2b24ea78..adc45285f94 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -44,6 +44,16 @@ GHOST_DECLARE_HANDLE(GHOST_XrContextHandle); typedef void (*GHOST_TBacktraceFn)(void *file_handle); +/** + * A reference to cursor bitmap data. + */ +typedef struct { + /** `RGBA` bytes. */ + const uint8_t *data; + int data_size[2]; + int hot_spot[2]; +} GHOST_CursorBitmapRef; + typedef struct { int flags; } GHOST_GLSettings; @@ -51,7 +61,6 @@ typedef struct { typedef enum { GHOST_glStereoVisual = (1 << 0), GHOST_glDebugContext = (1 << 1), - GHOST_glAlphaBackground = (1 << 2), } GHOST_GLFlags; typedef enum GHOST_DialogOptions { @@ -113,8 +122,8 @@ typedef enum { GHOST_kModifierKeyLeftControl, GHOST_kModifierKeyRightControl, GHOST_kModifierKeyOS, - GHOST_kModifierKeyNumMasks -} GHOST_TModifierKeyMask; + GHOST_kModifierKeyNum +} GHOST_TModifierKey; typedef enum { GHOST_kWindowStateNormal = 0, @@ -153,8 +162,8 @@ typedef enum { /* Trackballs and programmable buttons. */ GHOST_kButtonMaskButton6, GHOST_kButtonMaskButton7, - GHOST_kButtonNumMasks -} GHOST_TButtonMask; + GHOST_kButtonNum +} GHOST_TButton; typedef enum { GHOST_kEventUnknown = 0, @@ -409,9 +418,9 @@ typedef enum { typedef enum { /** Axis that cursor grab will wrap. */ - GHOST_kGrabAxisNone = 0, + GHOST_kAxisNone = 0, GHOST_kAxisX = (1 << 0), - GHOST_kGrabAxisY = (1 << 1), + GHOST_kAxisY = (1 << 1), } GHOST_TAxisFlag; typedef void *GHOST_TEventDataPtr; @@ -427,7 +436,7 @@ typedef struct { typedef struct { /** The mask of the mouse button. */ - GHOST_TButtonMask button; + GHOST_TButton button; /** Associated tablet data. */ GHOST_TabletData tablet; } GHOST_TEventButtonData; @@ -537,21 +546,15 @@ typedef struct { /** The key code. */ GHOST_TKey key; - /* ascii / utf8: both should always be set when possible, - * - ascii may be '\0' however if the user presses a non ascii key - * - unicode may not be set if the system has no unicode support - * - * These values are intended to be used as follows. - * For text input use unicode when available, fallback to ascii. - * For areas where unicode is not needed, number input for example, always - * use ascii, unicode is ignored - campbell. - */ - /** The ascii code for the key event ('\0' if none). */ - char ascii; /** The unicode character. if the length is 6, not NULL terminated if all 6 are set. */ char utf8_buf[6]; - /** Generated by auto-repeat. */ + /** + * Enabled when the key is held (auto-repeat). + * In this case press events are sent without a corresponding release/up event. + * + * All back-ends must set this variable for correct behavior regarding repeatable keys. + */ char is_repeat; } GHOST_TEventKeyData; @@ -600,7 +603,7 @@ typedef int GHOST_TEmbedderWindowID; /** * A timer task callback routine. * \param task: The timer task object. - * \param time: The current time. + * \param time: Time since this timer started (in milliseconds). */ #ifdef __cplusplus class GHOST_ITimerTask; diff --git a/intern/ghost/intern/GHOST_Buttons.cpp b/intern/ghost/intern/GHOST_Buttons.cpp index 3367d256325..6382729c579 100644 --- a/intern/ghost/intern/GHOST_Buttons.cpp +++ b/intern/ghost/intern/GHOST_Buttons.cpp @@ -12,7 +12,7 @@ GHOST_Buttons::GHOST_Buttons() clear(); } -bool GHOST_Buttons::get(GHOST_TButtonMask mask) const +bool GHOST_Buttons::get(GHOST_TButton mask) const { switch (mask) { case GHOST_kButtonMaskLeft: @@ -34,7 +34,7 @@ bool GHOST_Buttons::get(GHOST_TButtonMask mask) const } } -void GHOST_Buttons::set(GHOST_TButtonMask mask, bool down) +void GHOST_Buttons::set(GHOST_TButton mask, bool down) { switch (mask) { case GHOST_kButtonMaskLeft: diff --git a/intern/ghost/intern/GHOST_Buttons.h b/intern/ghost/intern/GHOST_Buttons.h index 72cb17a3322..e42376b1c76 100644 --- a/intern/ghost/intern/GHOST_Buttons.h +++ b/intern/ghost/intern/GHOST_Buttons.h @@ -27,14 +27,14 @@ struct GHOST_Buttons { * \param mask: Key button to return. * \return The state of the button (pressed == true). */ - bool get(GHOST_TButtonMask mask) const; + bool get(GHOST_TButton mask) const; /** * Updates the state of a single button. * \param mask: Button state to update. * \param down: The new state of the button. */ - void set(GHOST_TButtonMask mask, bool down); + void set(GHOST_TButton mask, bool down); /** * Sets the state of all buttons to up. diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index c8127f59941..710512881e8 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -30,6 +30,14 @@ GHOST_SystemHandle GHOST_CreateSystem(void) return (GHOST_SystemHandle)system; } +GHOST_SystemHandle GHOST_CreateSystemBackground(void) +{ + GHOST_ISystem::createSystemBackground(); + GHOST_ISystem *system = GHOST_ISystem::getSystem(); + + return (GHOST_SystemHandle)system; +} + void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, GHOST_Debug debug) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; @@ -326,6 +334,14 @@ GHOST_TSuccess GHOST_SetCustomCursorShape(GHOST_WindowHandle windowhandle, return window->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor); } +GHOST_TSuccess GHOST_GetCursorBitmap(GHOST_WindowHandle windowhandle, + GHOST_CursorBitmapRef *bitmap) +{ + GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; + + return window->getCursorBitmap(bitmap); +} + bool GHOST_GetCursorVisibility(GHOST_WindowHandle windowhandle) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; @@ -340,19 +356,49 @@ GHOST_TSuccess GHOST_SetCursorVisibility(GHOST_WindowHandle windowhandle, bool v return window->setCursorVisibility(visible); } -GHOST_TSuccess GHOST_GetCursorPosition(GHOST_SystemHandle systemhandle, int32_t *x, int32_t *y) +/* Unused, can expose again if needed although WAYLAND + * can only properly use client relative coordinates, so leave disabled if possible. */ +#if 0 +GHOST_TSuccess GHOST_GetCursorPositionScreenCoords(GHOST_SystemHandle systemhandle, + int32_t *x, + int32_t *y) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; return system->getCursorPosition(*x, *y); } -GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle, int32_t x, int32_t y) +GHOST_TSuccess GHOST_SetCursorPositionScreenCoords(GHOST_SystemHandle systemhandle, + int32_t x, + int32_t y) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; return system->setCursorPosition(x, y); } +#endif + +GHOST_TSuccess GHOST_GetCursorPosition(const GHOST_SystemHandle systemhandle, + const GHOST_WindowHandle windowhandle, + int32_t *x, + int32_t *y) +{ + const GHOST_ISystem *system = (const GHOST_ISystem *)systemhandle; + const GHOST_IWindow *window = (const GHOST_IWindow *)windowhandle; + + return system->getCursorPositionClientRelative(window, *x, *y); +} + +GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle, + GHOST_WindowHandle windowhandle, + int32_t x, + int32_t y) +{ + GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; + GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; + + return system->setCursorPositionClientRelative(window, x, y); +} GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle, GHOST_TGrabCursorMode mode, @@ -379,19 +425,22 @@ GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle, void GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle, GHOST_TGrabCursorMode *r_mode, GHOST_TAxisFlag *r_axis_flag, - int r_bounds[4]) + int r_bounds[4], + bool *r_use_software_cursor) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; GHOST_Rect bounds_rect; - window->getCursorGrabState(*r_mode, *r_axis_flag, bounds_rect); + bool use_software_cursor; + window->getCursorGrabState(*r_mode, *r_axis_flag, bounds_rect, use_software_cursor); r_bounds[0] = bounds_rect.m_l; r_bounds[1] = bounds_rect.m_t; r_bounds[2] = bounds_rect.m_r; r_bounds[3] = bounds_rect.m_b; + *r_use_software_cursor = use_software_cursor; } GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle, - GHOST_TModifierKeyMask mask, + GHOST_TModifierKey mask, bool *r_is_down) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; @@ -405,7 +454,7 @@ GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle, } GHOST_TSuccess GHOST_GetButtonState(GHOST_SystemHandle systemhandle, - GHOST_TButtonMask mask, + GHOST_TButton mask, bool *r_is_down) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; @@ -820,8 +869,7 @@ void GHOST_putClipboard(const char *buffer, bool selection) bool GHOST_setConsoleWindowState(GHOST_TConsoleWindowState action) { GHOST_ISystem *system = GHOST_ISystem::getSystem(); - /* FIXME: use `bool` instead of int for this value. */ - return (bool)system->setConsoleWindowState(action); + return system->setConsoleWindowState(action); } bool GHOST_UseNativePixels(void) diff --git a/intern/ghost/intern/GHOST_Context.cpp b/intern/ghost/intern/GHOST_Context.cpp index f9aa80dc13d..aa379efbc1f 100644 --- a/intern/ghost/intern/GHOST_Context.cpp +++ b/intern/ghost/intern/GHOST_Context.cpp @@ -10,7 +10,7 @@ #include "GHOST_Context.h" #ifdef _WIN32 -# include <GL/wglew.h> // only for symbolic constants, do not use API functions +# include <epoxy/wgl.h> # include <tchar.h> # # ifndef ERROR_PROFILE_DOES_NOT_MATCH_DEVICE @@ -35,7 +35,7 @@ bool win32_silent_chk(bool result) bool win32_chk(bool result, const char *file, int line, const char *text) { if (!result) { - LPTSTR formattedMsg = NULL; + LPTSTR formattedMsg = nullptr; DWORD error = GetLastError(); @@ -87,12 +87,12 @@ bool win32_chk(bool result, const char *file, int line, const char *text) default: { count = FormatMessage((FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS), - NULL, + nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)(&formattedMsg), 0, - NULL); + nullptr); msg = count > 0 ? formattedMsg : "<no system message>\n"; break; @@ -113,8 +113,9 @@ bool win32_chk(bool result, const char *file, int line, const char *text) SetLastError(NO_ERROR); - if (count != 0) + if (count != 0) { LocalFree(formattedMsg); + } } return result; @@ -122,11 +123,6 @@ bool win32_chk(bool result, const char *file, int line, const char *text) #endif // _WIN32 -void GHOST_Context::initContextGLEW() -{ - GLEW_CHK(glewInit()); -} - void GHOST_Context::initClearGL() { glClearColor(0.294, 0.294, 0.294, 0.000); diff --git a/intern/ghost/intern/GHOST_Context.h b/intern/ghost/intern/GHOST_Context.h index d9c2cdce258..3546fb6bbc7 100644 --- a/intern/ghost/intern/GHOST_Context.h +++ b/intern/ghost/intern/GHOST_Context.h @@ -11,7 +11,7 @@ #include "GHOST_IContext.h" #include "GHOST_Types.h" -#include "glew-mx.h" +#include <epoxy/gl.h> #include <cstdlib> // for NULL @@ -93,6 +93,22 @@ class GHOST_Context : public GHOST_IContext { } /** + * Get user data. + */ + void *getUserData() + { + return m_user_data; + } + + /** + * Set user data (intended for the caller to use as needed). + */ + void setUserData(void *user_data) + { + m_user_data = user_data; + } + + /** * Stereo visual created. Only necessary for 'real' stereo support, * ie quad buffered stereo. This is not always possible, depends on * the graphics h/w @@ -120,10 +136,11 @@ class GHOST_Context : public GHOST_IContext { } protected: - void initContextGLEW(); - bool m_stereoVisual; + /** Caller specified, not for internal use. */ + void *m_user_data = nullptr; + static void initClearGL(); #ifdef WITH_CXX_GUARDEDALLOC diff --git a/intern/ghost/intern/GHOST_ContextCGL.h b/intern/ghost/intern/GHOST_ContextCGL.h index badc3241107..fa6d6fc6fa0 100644 --- a/intern/ghost/intern/GHOST_ContextCGL.h +++ b/intern/ghost/intern/GHOST_ContextCGL.h @@ -105,8 +105,6 @@ class GHOST_ContextCGL : public GHOST_Context { /** The virtualized default frame-buffer's texture. */ MTLTexture *m_defaultFramebufferMetalTexture; - bool m_coreProfile; - const bool m_debug; /** The first created OpenGL context (for sharing display lists) */ diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm index dd800ef52a3..488aa58aa59 100644 --- a/intern/ghost/intern/GHOST_ContextCGL.mm +++ b/intern/ghost/intern/GHOST_ContextCGL.mm @@ -58,12 +58,6 @@ GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual, m_defaultFramebufferMetalTexture(nil), m_debug(false) { -#if defined(WITH_GL_PROFILE_CORE) - m_coreProfile = true; -#else - m_coreProfile = false; -#endif - if (m_metalView) { metalInit(); } @@ -197,17 +191,17 @@ GHOST_TSuccess GHOST_ContextCGL::updateDrawingContext() } static void makeAttribList(std::vector<NSOpenGLPixelFormatAttribute> &attribs, - bool coreProfile, bool stereoVisual, bool needAlpha, - bool softwareGL) + bool softwareGL, + bool increasedSamplerLimit) { attribs.clear(); attribs.push_back(NSOpenGLPFAOpenGLProfile); - attribs.push_back(coreProfile ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy); + attribs.push_back(NSOpenGLProfileVersion3_2Core); - /* Pixel Format Attributes for the windowed NSOpenGLContext. */ + /* Pixel Format Attributes for the windowed #NSOpenGLContext. */ attribs.push_back(NSOpenGLPFADoubleBuffer); if (softwareGL) { @@ -217,6 +211,12 @@ static void makeAttribList(std::vector<NSOpenGLPixelFormatAttribute> &attribs, else { attribs.push_back(NSOpenGLPFAAccelerated); attribs.push_back(NSOpenGLPFANoRecovery); + + /* Attempt to initialize device with extended sampler limit. + * Resolves EEVEE purple rendering artifacts on macOS. */ + if (increasedSamplerLimit) { + attribs.push_back((NSOpenGLPixelFormatAttribute)400); + } } if (stereoVisual) @@ -232,82 +232,123 @@ static void makeAttribList(std::vector<NSOpenGLPixelFormatAttribute> &attribs, GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext() { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + @autoreleasepool { #ifdef GHOST_OPENGL_ALPHA - static const bool needAlpha = true; + static const bool needAlpha = true; #else - static const bool needAlpha = false; + static const bool needAlpha = false; #endif - /* Command-line argument would be better. */ - static bool softwareGL = getenv("BLENDER_SOFTWAREGL"); - - std::vector<NSOpenGLPixelFormatAttribute> attribs; - attribs.reserve(40); - makeAttribList(attribs, m_coreProfile, m_stereoVisual, needAlpha, softwareGL); + /* Command-line argument would be better. */ + static bool softwareGL = getenv("BLENDER_SOFTWAREGL"); + + NSOpenGLPixelFormat *pixelFormat = nil; + std::vector<NSOpenGLPixelFormatAttribute> attribs; + bool increasedSamplerLimit = false; + + /* Attempt to initialize device with increased sampler limit. + * If this is unsupported and initialization fails, initialize GL Context as normal. + * + * NOTE: This is not available when using the SoftwareGL path, or for Intel-based + * platforms. */ + if (!softwareGL) { + if (@available(macos 11.0, *)) { + increasedSamplerLimit = true; + } + } + const int max_ctx_attempts = increasedSamplerLimit ? 2 : 1; + for (int ctx_create_attempt = 0; ctx_create_attempt < max_ctx_attempts; ctx_create_attempt++) { + + attribs.clear(); + attribs.reserve(40); + makeAttribList(attribs, m_stereoVisual, needAlpha, softwareGL, increasedSamplerLimit); + + pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]]; + if (pixelFormat == nil) { + /* If pixel format creation fails when testing increased sampler limit, + * attempt initialization again with feature disabled, otherwise, fail. */ + if (increasedSamplerLimit) { + increasedSamplerLimit = false; + continue; + } + return GHOST_kFailure; + } - NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]]; - if (pixelFormat == nil) { - goto error; - } + /* Attempt to create context. */ + m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat + shareContext:s_sharedOpenGLContext]; + [pixelFormat release]; + + if (m_openGLContext == nil) { + /* If context creation fails when testing increased sampler limit, + * attempt re-creation with feature disabled. Otherwise, error. */ + if (increasedSamplerLimit) { + increasedSamplerLimit = false; + continue; + } + + /* Default context creation attempt failed. */ + return GHOST_kFailure; + } - m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat - shareContext:s_sharedOpenGLContext]; - [pixelFormat release]; + /* Created GL context successfully, activate. */ + [m_openGLContext makeCurrentContext]; - [m_openGLContext makeCurrentContext]; + /* When increasing sampler limit, verify created context is a supported configuration. */ + if (increasedSamplerLimit) { + const char *vendor = (const char *)glGetString(GL_VENDOR); + const char *renderer = (const char *)glGetString(GL_RENDERER); + + /* If generated context type is unsupported, release existing context and + * fallback to creating a normal context below. */ + if (strstr(vendor, "Intel") || strstr(renderer, "Software")) { + [m_openGLContext release]; + m_openGLContext = nil; + increasedSamplerLimit = false; + continue; + } + } + } - if (m_debug) { - GLint major = 0, minor = 0; - glGetIntegerv(GL_MAJOR_VERSION, &major); - glGetIntegerv(GL_MINOR_VERSION, &minor); - fprintf(stderr, "OpenGL version %d.%d%s\n", major, minor, softwareGL ? " (software)" : ""); - fprintf(stderr, "Renderer: %s\n", glGetString(GL_RENDERER)); - } + if (m_debug) { + GLint major = 0, minor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + fprintf(stderr, "OpenGL version %d.%d%s\n", major, minor, softwareGL ? " (software)" : ""); + fprintf(stderr, "Renderer: %s\n", glGetString(GL_RENDERER)); + } #ifdef GHOST_WAIT_FOR_VSYNC - { - GLint swapInt = 1; - /* Wait for vertical-sync, to avoid tearing artifacts. */ - [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; - } + { + GLint swapInt = 1; + /* Wait for vertical-sync, to avoid tearing artifacts. */ + [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + } #endif - initContextGLEW(); - - if (m_metalView) { - if (m_defaultFramebuffer == 0) { - /* Create a virtual frame-buffer. */ - [m_openGLContext makeCurrentContext]; - metalInitFramebuffer(); + if (m_metalView) { + if (m_defaultFramebuffer == 0) { + /* Create a virtual frame-buffer. */ + [m_openGLContext makeCurrentContext]; + metalInitFramebuffer(); + initClearGL(); + } + } + else if (m_openGLView) { + [m_openGLView setOpenGLContext:m_openGLContext]; + [m_openGLContext setView:m_openGLView]; initClearGL(); } - } - else if (m_openGLView) { - [m_openGLView setOpenGLContext:m_openGLContext]; - [m_openGLContext setView:m_openGLView]; - initClearGL(); - } - - [m_openGLContext flushBuffer]; - if (s_sharedCount == 0) - s_sharedOpenGLContext = m_openGLContext; + [m_openGLContext flushBuffer]; - s_sharedCount++; - - [pool drain]; + if (s_sharedCount == 0) + s_sharedOpenGLContext = m_openGLContext; + s_sharedCount++; + } return GHOST_kSuccess; - -error: - - [pixelFormat release]; - - [pool drain]; - - return GHOST_kFailure; } GHOST_TSuccess GHOST_ContextCGL::releaseNativeHandles() diff --git a/intern/ghost/intern/GHOST_ContextD3D.cpp b/intern/ghost/intern/GHOST_ContextD3D.cpp index ded76daa145..857323941ea 100644 --- a/intern/ghost/intern/GHOST_ContextD3D.cpp +++ b/intern/ghost/intern/GHOST_ContextD3D.cpp @@ -10,8 +10,7 @@ #include <iostream> #include <string> -#include <GL/glew.h> -#include <GL/wglew.h> +#include <epoxy/wgl.h> #include "GHOST_ContextD3D.h" #include "GHOST_ContextWGL.h" /* For shared drawing */ @@ -124,8 +123,6 @@ class GHOST_SharedOpenGLResource { ID3D11RenderTargetView *render_target = nullptr) : m_device(device), m_device_ctx(device_ctx), m_cur_width(width), m_cur_height(height) { - ID3D11Resource *backbuffer_res; - if (!render_target) { D3D11_TEXTURE2D_DESC texDesc{}; D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc{}; @@ -158,11 +155,12 @@ class GHOST_SharedOpenGLResource { m_render_target = render_target; if (m_render_target) { + ID3D11Resource *backbuffer_res = nullptr; m_render_target->GetResource(&backbuffer_res); - } - if (backbuffer_res) { - backbuffer_res->QueryInterface<ID3D11Texture2D>(&m_render_target_tex); - backbuffer_res->Release(); + if (backbuffer_res) { + backbuffer_res->QueryInterface<ID3D11Texture2D>(&m_render_target_tex); + backbuffer_res->Release(); + } } if (!m_render_target || !m_render_target_tex) { diff --git a/intern/ghost/intern/GHOST_ContextEGL.cpp b/intern/ghost/intern/GHOST_ContextEGL.cpp index 8c44dfe0158..ef13133d3a3 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.cpp +++ b/intern/ghost/intern/GHOST_ContextEGL.cpp @@ -151,19 +151,6 @@ static bool egl_chk(bool result, # define EGL_CHK(x) egl_chk(x) #endif -static inline bool bindAPI(EGLenum api) -{ - if (EGLEW_VERSION_1_2) { - return (EGL_CHK(eglBindAPI(api)) == EGL_TRUE); - } - - return false; -} - -#ifdef WITH_GL_ANGLE -HMODULE GHOST_ContextEGL::s_d3dcompiler = nullptr; -#endif - EGLContext GHOST_ContextEGL::s_gl_sharedContext = EGL_NO_CONTEXT; EGLint GHOST_ContextEGL::s_gl_sharedCount = 0; @@ -256,7 +243,7 @@ GHOST_TSuccess GHOST_ContextEGL::swapBuffers() GHOST_TSuccess GHOST_ContextEGL::setSwapInterval(int interval) { - if (EGLEW_VERSION_1_1) { + if (epoxy_egl_version(m_display) >= 11) { if (EGL_CHK(::eglSwapInterval(m_display, interval))) { m_swap_interval = interval; @@ -313,26 +300,13 @@ GHOST_TSuccess GHOST_ContextEGL::releaseDrawingContext() return GHOST_kFailure; } -bool GHOST_ContextEGL::initContextEGLEW() +inline bool GHOST_ContextEGL::bindAPI(EGLenum api) { - /* We have to manually get this function before we can call eglewInit, since - * it requires a display argument. glewInit() does the same, but we only want - * to initialize EGLEW here. */ - eglGetDisplay = (PFNEGLGETDISPLAYPROC)eglGetProcAddress("eglGetDisplay"); - if (eglGetDisplay == nullptr) { - return false; - } - - if (!EGL_CHK((m_display = ::eglGetDisplay(m_nativeDisplay)) != EGL_NO_DISPLAY)) { - return false; - } - - if (GLEW_CHK(eglewInit(m_display)) != GLEW_OK) { - fprintf(stderr, "Warning! EGLEW failed to initialize properly.\n"); - return false; + if (epoxy_egl_version(m_display) >= 12) { + return (EGL_CHK(eglBindAPI(api)) == EGL_TRUE); } - return true; + return false; } static const std::string &api_string(EGLenum api) @@ -355,34 +329,41 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() } m_stereoVisual = false; /* It doesn't matter what the Window wants. */ - if (!initContextEGLEW()) { - return GHOST_kFailure; - } - -#ifdef WITH_GL_ANGLE - /* `d3dcompiler_XX.dll` needs to be loaded before ANGLE will work. */ - if (s_d3dcompiler == nullptr) { - s_d3dcompiler = LoadLibrary(D3DCOMPILER); - - WIN32_CHK(s_d3dcompiler != nullptr); - - if (s_d3dcompiler == nullptr) { - fprintf(stderr, "LoadLibrary(\"" D3DCOMPILER "\") failed!\n"); - return GHOST_kFailure; - } - } -#endif - EGLDisplay prev_display = eglGetCurrentDisplay(); EGLSurface prev_draw = eglGetCurrentSurface(EGL_DRAW); EGLSurface prev_read = eglGetCurrentSurface(EGL_READ); EGLContext prev_context = eglGetCurrentContext(); - EGLint egl_major, egl_minor; + EGLint egl_major = 0, egl_minor = 0; - if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor))) { + if (!EGL_CHK((m_display = ::eglGetDisplay(m_nativeDisplay)) != EGL_NO_DISPLAY)) { goto error; } + + if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor)) || + (egl_major == 0 && egl_minor == 0)) { + /* We failed to create a regular render window, retry and see if we can create a headless + * render context. */ + ::eglTerminate(m_display); + + const char *egl_extension_st = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + assert(egl_extension_st != nullptr); + assert(strstr(egl_extension_st, "EGL_MESA_platform_surfaceless") != nullptr); + if (egl_extension_st == nullptr || + strstr(egl_extension_st, "EGL_MESA_platform_surfaceless") == nullptr) { + goto error; + } + + m_display = eglGetPlatformDisplayEXT( + EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, nullptr); + + if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor))) { + goto error; + } + /* Because the first eglInitialize will print an error to the terminal, print a "success" + * message here to let the user know that we successfully recovered from the error. */ + fprintf(stderr, "\nManaged to successfully fallback to surfaceless EGL rendering!\n\n"); + } #ifdef WITH_GHOST_DEBUG fprintf(stderr, "EGL Version %d.%d\n", egl_major, egl_minor); #endif @@ -398,7 +379,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() attrib_list.reserve(20); - if (m_api == EGL_OPENGL_ES_API && EGLEW_VERSION_1_2) { + if (m_api == EGL_OPENGL_ES_API && epoxy_egl_version(m_display) >= 12) { /* According to the spec it seems that you are required to set EGL_RENDERABLE_TYPE, * but some implementations (ANGLE) do not seem to care. */ @@ -421,9 +402,11 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() m_contextMinorVersion); } - if (!((m_contextMajorVersion == 1) || (m_contextMajorVersion == 2 && EGLEW_VERSION_1_3) || - (m_contextMajorVersion == 3 && /*EGLEW_VERSION_1_4 &&*/ EGLEW_KHR_create_context) || - (m_contextMajorVersion == 3 && EGLEW_VERSION_1_5))) { + if (!((m_contextMajorVersion == 1) || + (m_contextMajorVersion == 2 && epoxy_egl_version(m_display) >= 13) || + (m_contextMajorVersion == 3 && + epoxy_has_egl_extension(m_display, "KHR_create_context")) || + (m_contextMajorVersion == 3 && epoxy_egl_version(m_display) >= 15))) { fprintf(stderr, "Warning! May not be able to create a version %d.%d ES context with version %d.%d " "of EGL\n", @@ -488,7 +471,8 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() } attrib_list.clear(); - if (EGLEW_VERSION_1_5 || EGLEW_KHR_create_context) { + if (epoxy_egl_version(m_display) >= 15 || + epoxy_has_egl_extension(m_display, "KHR_create_context")) { if (m_api == EGL_OPENGL_API || m_api == EGL_OPENGL_ES_API) { if (m_contextMajorVersion != 0) { attrib_list.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR); @@ -530,7 +514,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() } } - if (m_api == EGL_OPENGL_API || EGLEW_VERSION_1_5) { + if (m_api == EGL_OPENGL_API || epoxy_egl_version(m_display) >= 15) { if (m_contextResetNotificationStrategy != 0) { attrib_list.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR); attrib_list.push_back(m_contextResetNotificationStrategy); @@ -598,10 +582,10 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() goto error; } - initContextGLEW(); - - initClearGL(); - ::eglSwapBuffers(m_display, m_surface); + if (m_nativeWindow != 0) { + initClearGL(); + ::eglSwapBuffers(m_display, m_surface); + } return GHOST_kSuccess; diff --git a/intern/ghost/intern/GHOST_ContextEGL.h b/intern/ghost/intern/GHOST_ContextEGL.h index 3250dc94978..3ccd34f0338 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.h +++ b/intern/ghost/intern/GHOST_ContextEGL.h @@ -10,7 +10,8 @@ #include "GHOST_Context.h" #include "GHOST_System.h" -#include <GL/eglew.h> +#include <epoxy/egl.h> +#include <epoxy/gl.h> #ifndef GHOST_OPENGL_EGL_CONTEXT_FLAGS # define GHOST_OPENGL_EGL_CONTEXT_FLAGS 0 @@ -96,7 +97,7 @@ class GHOST_ContextEGL : public GHOST_Context { EGLContext getContext() const; private: - bool initContextEGLEW(); + bool bindAPI(EGLenum api); const GHOST_System *const m_system; @@ -129,8 +130,4 @@ class GHOST_ContextEGL : public GHOST_Context { static EGLContext s_vg_sharedContext; static EGLint s_vg_sharedCount; - -#ifdef WITH_GL_ANGLE - static HMODULE s_d3dcompiler; -#endif }; diff --git a/intern/ghost/intern/GHOST_ContextGLX.cpp b/intern/ghost/intern/GHOST_ContextGLX.cpp index b4a076e4598..ed1c874c236 100644 --- a/intern/ghost/intern/GHOST_ContextGLX.cpp +++ b/intern/ghost/intern/GHOST_ContextGLX.cpp @@ -95,11 +95,6 @@ GHOST_TSuccess GHOST_ContextGLX::releaseDrawingContext() return ::glXMakeCurrent(m_display, None, nullptr) ? GHOST_kSuccess : GHOST_kFailure; } -void GHOST_ContextGLX::initContextGLXEW() -{ - initContextGLEW(); -} - GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext() { GHOST_X11_ERROR_HANDLERS_OVERRIDE(handler_store); @@ -278,18 +273,11 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext() glXMakeCurrent(m_display, m_window, m_context); - /* Seems that this has to be called after #glXMakeCurrent, - * which means we cannot use `glX` extensions until after we create a context. */ - initContextGLXEW(); - if (m_window) { initClearGL(); ::glXSwapBuffers(m_display, m_window); } - /* re initialize to get the extensions properly */ - initContextGLXEW(); - version = glGetString(GL_VERSION); if (!version || version[0] < '3' || ((version[0] == '3') && (version[2] < '3'))) { @@ -318,7 +306,7 @@ GHOST_TSuccess GHOST_ContextGLX::releaseNativeHandles() GHOST_TSuccess GHOST_ContextGLX::setSwapInterval(int interval) { - if (!GLXEW_EXT_swap_control) { + if (!epoxy_has_glx_extension(m_display, DefaultScreen(m_display), "GLX_EXT_swap_control")) { ::glXSwapIntervalEXT(m_display, m_window, interval); return GHOST_kSuccess; } @@ -327,7 +315,7 @@ GHOST_TSuccess GHOST_ContextGLX::setSwapInterval(int interval) GHOST_TSuccess GHOST_ContextGLX::getSwapInterval(int &intervalOut) { - if (GLXEW_EXT_swap_control) { + if (epoxy_has_glx_extension(m_display, DefaultScreen(m_display), "GLX_EXT_swap_control")) { unsigned int interval = 0; ::glXQueryDrawable(m_display, m_window, GLX_SWAP_INTERVAL_EXT, &interval); diff --git a/intern/ghost/intern/GHOST_ContextGLX.h b/intern/ghost/intern/GHOST_ContextGLX.h index c6184bbd3da..d526e6b1b32 100644 --- a/intern/ghost/intern/GHOST_ContextGLX.h +++ b/intern/ghost/intern/GHOST_ContextGLX.h @@ -9,7 +9,7 @@ #include "GHOST_Context.h" -#include <GL/glxew.h> +#include <epoxy/glx.h> #ifndef GHOST_OPENGL_GLX_CONTEXT_FLAGS /* leave as convenience define for the future */ @@ -89,8 +89,6 @@ class GHOST_ContextGLX : public GHOST_Context { GHOST_TSuccess getSwapInterval(int &intervalOut); private: - void initContextGLXEW(); - Display *m_display; GLXFBConfig m_fbconfig; Window m_window; diff --git a/intern/ghost/intern/GHOST_ContextSDL.cpp b/intern/ghost/intern/GHOST_ContextSDL.cpp index 5b02fe1c1e6..63b5927895d 100644 --- a/intern/ghost/intern/GHOST_ContextSDL.cpp +++ b/intern/ghost/intern/GHOST_ContextSDL.cpp @@ -138,8 +138,6 @@ GHOST_TSuccess GHOST_ContextSDL::initializeDrawingContext() success = (SDL_GL_MakeCurrent(m_window, m_context) < 0) ? GHOST_kFailure : GHOST_kSuccess; - initContextGLEW(); - initClearGL(); SDL_GL_SwapWindow(m_window); diff --git a/intern/ghost/intern/GHOST_ContextWGL.cpp b/intern/ghost/intern/GHOST_ContextWGL.cpp index 7417358e9ae..d3c190a13b1 100644 --- a/intern/ghost/intern/GHOST_ContextWGL.cpp +++ b/intern/ghost/intern/GHOST_ContextWGL.cpp @@ -87,7 +87,7 @@ GHOST_TSuccess GHOST_ContextWGL::swapBuffers() GHOST_TSuccess GHOST_ContextWGL::setSwapInterval(int interval) { - if (WGLEW_EXT_swap_control) + if (epoxy_has_wgl_extension(m_hDC, "WGL_EXT_swap_control")) return WIN32_CHK(::wglSwapIntervalEXT(interval)) == TRUE ? GHOST_kSuccess : GHOST_kFailure; else return GHOST_kFailure; @@ -95,7 +95,7 @@ GHOST_TSuccess GHOST_ContextWGL::setSwapInterval(int interval) GHOST_TSuccess GHOST_ContextWGL::getSwapInterval(int &intervalOut) { - if (WGLEW_EXT_swap_control) { + if (epoxy_has_wgl_extension(m_hDC, "WGL_EXT_swap_control")) { intervalOut = ::wglGetSwapIntervalEXT(); return GHOST_kSuccess; } @@ -266,89 +266,6 @@ static HWND clone_window(HWND hWnd, LPVOID lpParam) return hwndCloned; } -void GHOST_ContextWGL::initContextWGLEW(PIXELFORMATDESCRIPTOR &preferredPFD) -{ - HWND dummyHWND = NULL; - - HDC dummyHDC = NULL; - HGLRC dummyHGLRC = NULL; - - HDC prevHDC; - HGLRC prevHGLRC; - - int iPixelFormat; - - SetLastError(NO_ERROR); - - prevHDC = ::wglGetCurrentDC(); - WIN32_CHK(GetLastError() == NO_ERROR); - - prevHGLRC = ::wglGetCurrentContext(); - WIN32_CHK(GetLastError() == NO_ERROR); - - iPixelFormat = choose_pixel_format_legacy(m_hDC, preferredPFD); - - if (iPixelFormat == 0) - goto finalize; - - PIXELFORMATDESCRIPTOR chosenPFD; - if (!WIN32_CHK( - ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD))) - goto finalize; - - if (m_hWnd) { - dummyHWND = clone_window(m_hWnd, NULL); - - if (dummyHWND == NULL) - goto finalize; - - dummyHDC = GetDC(dummyHWND); - } - - if (!WIN32_CHK(dummyHDC != NULL)) - goto finalize; - - if (!WIN32_CHK(::SetPixelFormat(dummyHDC, iPixelFormat, &chosenPFD))) - goto finalize; - - dummyHGLRC = ::wglCreateContext(dummyHDC); - - if (!WIN32_CHK(dummyHGLRC != NULL)) - goto finalize; - - if (!WIN32_CHK(::wglMakeCurrent(dummyHDC, dummyHGLRC))) - goto finalize; - - if (GLEW_CHK(glewInit()) != GLEW_OK) { - fprintf(stderr, "Warning! Dummy GLEW/WGLEW failed to initialize properly.\n"); - } - - /* The following are not technically WGLEW, but they also require a context to work. */ - -#ifndef NDEBUG - free((void *)m_dummyRenderer); - free((void *)m_dummyVendor); - free((void *)m_dummyVersion); - - m_dummyRenderer = _strdup(reinterpret_cast<const char *>(glGetString(GL_RENDERER))); - m_dummyVendor = _strdup(reinterpret_cast<const char *>(glGetString(GL_VENDOR))); - m_dummyVersion = _strdup(reinterpret_cast<const char *>(glGetString(GL_VERSION))); -#endif - -finalize: - WIN32_CHK(::wglMakeCurrent(prevHDC, prevHGLRC)); - - if (dummyHGLRC != NULL) - WIN32_CHK(::wglDeleteContext(dummyHGLRC)); - - if (dummyHWND != NULL) { - if (dummyHDC != NULL) - WIN32_CHK(::ReleaseDC(dummyHWND, dummyHDC)); - - WIN32_CHK(::DestroyWindow(dummyHWND)); - } -} - static void makeAttribList(std::vector<int> &out, bool stereoVisual, bool needAlpha) { out.clear(); @@ -385,6 +302,130 @@ static void makeAttribList(std::vector<int> &out, bool stereoVisual, bool needAl out.push_back(0); } +/* Temporary context used to create the actual context. We need ARB pixel format + * and context extensions, which are only available within a context. */ +struct DummyContextWGL { + HWND dummyHWND = NULL; + + HDC dummyHDC = NULL; + HGLRC dummyHGLRC = NULL; + + HDC prevHDC = NULL; + HGLRC prevHGLRC = NULL; + + int dummyPixelFormat = 0; + + PIXELFORMATDESCRIPTOR preferredPFD; + + bool has_WGL_ARB_pixel_format = false; + bool has_WGL_ARB_create_context = false; + bool has_WGL_ARB_create_context_profile = false; + bool has_WGL_ARB_create_context_robustness = false; + + DummyContextWGL(HDC hDC, HWND hWnd, bool stereoVisual, bool needAlpha) + { + preferredPFD = { + sizeof(PIXELFORMATDESCRIPTOR), /* size */ + 1, /* version */ + (DWORD)(PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | + PFD_DOUBLEBUFFER | /* support double-buffering */ + (stereoVisual ? PFD_STEREO : 0) | /* support stereo */ + ( +#ifdef WIN32_COMPOSITING + /* Support composition for transparent background. */ + needAlpha ? PFD_SUPPORT_COMPOSITION : +#endif + 0)), + PFD_TYPE_RGBA, /* color type */ + (BYTE)(needAlpha ? 32 : 24), /* preferred color depth */ + 0, + 0, + 0, + 0, + 0, + 0, /* color bits (ignored) */ + (BYTE)(needAlpha ? 8 : 0), /* alpha buffer */ + 0, /* alpha shift (ignored) */ + 0, /* no accumulation buffer */ + 0, + 0, + 0, + 0, /* accum bits (ignored) */ + 0, /* depth buffer */ + 0, /* stencil buffer */ + 0, /* no auxiliary buffers */ + PFD_MAIN_PLANE, /* main layer */ + 0, /* reserved */ + 0, + 0, + 0 /* layer, visible, and damage masks (ignored) */ + }; + + SetLastError(NO_ERROR); + + prevHDC = ::wglGetCurrentDC(); + WIN32_CHK(GetLastError() == NO_ERROR); + + prevHGLRC = ::wglGetCurrentContext(); + WIN32_CHK(GetLastError() == NO_ERROR); + + dummyPixelFormat = choose_pixel_format_legacy(hDC, preferredPFD); + + if (dummyPixelFormat == 0) + return; + + PIXELFORMATDESCRIPTOR chosenPFD; + if (!WIN32_CHK(::DescribePixelFormat( + hDC, dummyPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD))) + return; + + if (hWnd) { + dummyHWND = clone_window(hWnd, NULL); + + if (dummyHWND == NULL) + return; + + dummyHDC = GetDC(dummyHWND); + } + + if (!WIN32_CHK(dummyHDC != NULL)) + return; + + if (!WIN32_CHK(::SetPixelFormat(dummyHDC, dummyPixelFormat, &chosenPFD))) + return; + + dummyHGLRC = ::wglCreateContext(dummyHDC); + + if (!WIN32_CHK(dummyHGLRC != NULL)) + return; + + if (!WIN32_CHK(::wglMakeCurrent(dummyHDC, dummyHGLRC))) + return; + + has_WGL_ARB_pixel_format = epoxy_has_wgl_extension(hDC, "WGL_ARB_pixel_format"); + has_WGL_ARB_create_context = epoxy_has_wgl_extension(hDC, "WGL_ARB_create_context"); + has_WGL_ARB_create_context_profile = epoxy_has_wgl_extension(hDC, + "WGL_ARB_create_context_profile"); + has_WGL_ARB_create_context_robustness = epoxy_has_wgl_extension( + hDC, "WGL_ARB_create_context_robustness"); + } + + ~DummyContextWGL() + { + WIN32_CHK(::wglMakeCurrent(prevHDC, prevHGLRC)); + + if (dummyHGLRC != NULL) + WIN32_CHK(::wglDeleteContext(dummyHGLRC)); + + if (dummyHWND != NULL) { + if (dummyHDC != NULL) + WIN32_CHK(::ReleaseDC(dummyHWND, dummyHDC)); + + WIN32_CHK(::DestroyWindow(dummyHWND)); + } + } +}; + int GHOST_ContextWGL::_choose_pixel_format_arb_1(bool stereoVisual, bool needAlpha) { std::vector<int> iAttributes; @@ -454,58 +495,6 @@ int GHOST_ContextWGL::choose_pixel_format_arb(bool stereoVisual, bool needAlpha) return iPixelFormat; } -int GHOST_ContextWGL::choose_pixel_format(bool stereoVisual, bool needAlpha) -{ - PIXELFORMATDESCRIPTOR preferredPFD = { - sizeof(PIXELFORMATDESCRIPTOR), /* size */ - 1, /* version */ - (DWORD)(PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | - PFD_DOUBLEBUFFER | /* support double-buffering */ - (stereoVisual ? PFD_STEREO : 0) | /* support stereo */ - ( -#ifdef WIN32_COMPOSITING - /* Support composition for transparent background. */ - needAlpha ? PFD_SUPPORT_COMPOSITION : -#endif - 0)), - PFD_TYPE_RGBA, /* color type */ - (BYTE)(needAlpha ? 32 : 24), /* preferred color depth */ - 0, - 0, - 0, - 0, - 0, - 0, /* color bits (ignored) */ - (BYTE)(needAlpha ? 8 : 0), /* alpha buffer */ - 0, /* alpha shift (ignored) */ - 0, /* no accumulation buffer */ - 0, - 0, - 0, - 0, /* accum bits (ignored) */ - 0, /* depth buffer */ - 0, /* stencil buffer */ - 0, /* no auxiliary buffers */ - PFD_MAIN_PLANE, /* main layer */ - 0, /* reserved */ - 0, - 0, - 0 /* layer, visible, and damage masks (ignored) */ - }; - - initContextWGLEW(preferredPFD); - - int iPixelFormat = 0; - - if (WGLEW_ARB_pixel_format) - iPixelFormat = choose_pixel_format_arb(stereoVisual, needAlpha); - - if (iPixelFormat == 0) - iPixelFormat = choose_pixel_format_legacy(m_hDC, preferredPFD); - - return iPixelFormat; -} - #ifndef NDEBUG static void reportContextString(const char *name, const char *dummy, const char *context) { @@ -526,107 +515,96 @@ GHOST_TSuccess GHOST_ContextWGL::initializeDrawingContext() HDC prevHDC = ::wglGetCurrentDC(); WIN32_CHK(GetLastError() == NO_ERROR); - if (!WGLEW_ARB_create_context || ::GetPixelFormat(m_hDC) == 0) { + { const bool needAlpha = m_alphaBackground; - int iPixelFormat; - int lastPFD; - - PIXELFORMATDESCRIPTOR chosenPFD; - - iPixelFormat = choose_pixel_format(m_stereoVisual, needAlpha); + DummyContextWGL dummy(m_hDC, m_hWnd, m_stereoVisual, needAlpha); - if (iPixelFormat == 0) { - goto error; - } + if (!dummy.has_WGL_ARB_create_context || ::GetPixelFormat(m_hDC) == 0) { + int iPixelFormat = 0; - lastPFD = ::DescribePixelFormat( - m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD); - - if (!WIN32_CHK(lastPFD != 0)) { - goto error; - } + if (dummy.has_WGL_ARB_pixel_format) + iPixelFormat = choose_pixel_format_arb(m_stereoVisual, needAlpha); - if (needAlpha && chosenPFD.cAlphaBits == 0) - fprintf(stderr, "Warning! Unable to find a pixel format with an alpha channel.\n"); + if (iPixelFormat == 0) + iPixelFormat = choose_pixel_format_legacy(m_hDC, dummy.preferredPFD); - if (!WIN32_CHK(::SetPixelFormat(m_hDC, iPixelFormat, &chosenPFD))) { - goto error; - } - } + if (iPixelFormat == 0) { + goto error; + } - if (WGLEW_ARB_create_context) { - int profileBitCore = m_contextProfileMask & WGL_CONTEXT_CORE_PROFILE_BIT_ARB; - int profileBitCompat = m_contextProfileMask & WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + PIXELFORMATDESCRIPTOR chosenPFD; + int lastPFD = ::DescribePixelFormat( + m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD); -#ifdef WITH_GLEW_ES - int profileBitES = m_contextProfileMask & WGL_CONTEXT_ES_PROFILE_BIT_EXT; -#endif + if (!WIN32_CHK(lastPFD != 0)) { + goto error; + } - if (!WGLEW_ARB_create_context_profile && profileBitCore) - fprintf(stderr, "Warning! OpenGL core profile not available.\n"); + if (needAlpha && chosenPFD.cAlphaBits == 0) + fprintf(stderr, "Warning! Unable to find a pixel format with an alpha channel.\n"); - if (!WGLEW_ARB_create_context_profile && profileBitCompat) - fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n"); + if (!WIN32_CHK(::SetPixelFormat(m_hDC, iPixelFormat, &chosenPFD))) { + goto error; + } + } -#ifdef WITH_GLEW_ES - if (!WGLEW_EXT_create_context_es_profile && profileBitES && m_contextMajorVersion == 1) - fprintf(stderr, "Warning! OpenGL ES profile not available.\n"); + if (dummy.has_WGL_ARB_create_context) { + int profileBitCore = m_contextProfileMask & WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + int profileBitCompat = m_contextProfileMask & WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; - if (!WGLEW_EXT_create_context_es2_profile && profileBitES && m_contextMajorVersion == 2) - fprintf(stderr, "Warning! OpenGL ES2 profile not available.\n"); -#endif + if (!dummy.has_WGL_ARB_create_context_profile && profileBitCore) + fprintf(stderr, "Warning! OpenGL core profile not available.\n"); - int profileMask = 0; + if (!dummy.has_WGL_ARB_create_context_profile && profileBitCompat) + fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n"); - if (WGLEW_ARB_create_context_profile && profileBitCore) - profileMask |= profileBitCore; + int profileMask = 0; - if (WGLEW_ARB_create_context_profile && profileBitCompat) - profileMask |= profileBitCompat; + if (dummy.has_WGL_ARB_create_context_profile && profileBitCore) + profileMask |= profileBitCore; -#ifdef WITH_GLEW_ES - if (WGLEW_EXT_create_context_es_profile && profileBitES) - profileMask |= profileBitES; -#endif + if (dummy.has_WGL_ARB_create_context_profile && profileBitCompat) + profileMask |= profileBitCompat; - if (profileMask != m_contextProfileMask) - fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits."); + if (profileMask != m_contextProfileMask) + fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits."); - std::vector<int> iAttributes; + std::vector<int> iAttributes; - if (profileMask) { - iAttributes.push_back(WGL_CONTEXT_PROFILE_MASK_ARB); - iAttributes.push_back(profileMask); - } - - if (m_contextMajorVersion != 0) { - iAttributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); - iAttributes.push_back(m_contextMajorVersion); - } + if (profileMask) { + iAttributes.push_back(WGL_CONTEXT_PROFILE_MASK_ARB); + iAttributes.push_back(profileMask); + } - if (m_contextMinorVersion != 0) { - iAttributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); - iAttributes.push_back(m_contextMinorVersion); - } + if (m_contextMajorVersion != 0) { + iAttributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); + iAttributes.push_back(m_contextMajorVersion); + } - if (m_contextFlags != 0) { - iAttributes.push_back(WGL_CONTEXT_FLAGS_ARB); - iAttributes.push_back(m_contextFlags); - } + if (m_contextMinorVersion != 0) { + iAttributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); + iAttributes.push_back(m_contextMinorVersion); + } - if (m_contextResetNotificationStrategy != 0) { - if (WGLEW_ARB_create_context_robustness) { - iAttributes.push_back(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB); - iAttributes.push_back(m_contextResetNotificationStrategy); + if (m_contextFlags != 0) { + iAttributes.push_back(WGL_CONTEXT_FLAGS_ARB); + iAttributes.push_back(m_contextFlags); } - else { - fprintf(stderr, "Warning! Cannot set the reset notification strategy."); + + if (m_contextResetNotificationStrategy != 0) { + if (dummy.has_WGL_ARB_create_context_robustness) { + iAttributes.push_back(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB); + iAttributes.push_back(m_contextResetNotificationStrategy); + } + else { + fprintf(stderr, "Warning! Cannot set the reset notification strategy."); + } } - } - iAttributes.push_back(0); + iAttributes.push_back(0); - m_hGLRC = ::wglCreateContextAttribsARB(m_hDC, NULL, &(iAttributes[0])); + m_hGLRC = ::wglCreateContextAttribsARB(m_hDC, NULL, &(iAttributes[0])); + } } /* Silence warnings interpreted as errors by users when trying to get @@ -651,8 +629,6 @@ GHOST_TSuccess GHOST_ContextWGL::initializeDrawingContext() goto error; } - initContextGLEW(); - if (is_crappy_intel_card()) { /* Some Intel cards with context 4.1 or 4.2 * don't have the point sprite enabled by default. diff --git a/intern/ghost/intern/GHOST_ContextWGL.h b/intern/ghost/intern/GHOST_ContextWGL.h index ca0bf70b128..c02c0616422 100644 --- a/intern/ghost/intern/GHOST_ContextWGL.h +++ b/intern/ghost/intern/GHOST_ContextWGL.h @@ -11,7 +11,7 @@ #include "GHOST_Context.h" -#include <GL/wglew.h> +#include <epoxy/wgl.h> #ifndef GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY # define GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY 0 @@ -86,12 +86,9 @@ class GHOST_ContextWGL : public GHOST_Context { GHOST_TSuccess getSwapInterval(int &intervalOut); private: - int choose_pixel_format(bool stereoVisual, bool needAlpha); int choose_pixel_format_arb(bool stereoVisual, bool needAlpha); int _choose_pixel_format_arb_1(bool stereoVisual, bool needAlpha); - void initContextWGLEW(PIXELFORMATDESCRIPTOR &preferredPFD); - HWND m_hWnd; HDC m_hDC; diff --git a/intern/ghost/intern/GHOST_Debug.h b/intern/ghost/intern/GHOST_Debug.h index e6bdf974d59..ec1a0b34be6 100644 --- a/intern/ghost/intern/GHOST_Debug.h +++ b/intern/ghost/intern/GHOST_Debug.h @@ -15,12 +15,12 @@ # endif #endif -#ifdef WITH_GHOST_DEBUG +#if defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG)) # include <iostream> # include <stdio.h> //for printf() #endif // WITH_GHOST_DEBUG -#ifdef WITH_GHOST_DEBUG +#if defined(WITH_GHOST_DEBUG) # define GHOST_PRINT(x) \ { \ std::cout << x; \ @@ -31,10 +31,10 @@ printf(x, __VA_ARGS__); \ } \ (void)0 -#else // WITH_GHOST_DEBUG +#else # define GHOST_PRINT(x) # define GHOST_PRINTF(x, ...) -#endif // WITH_GHOST_DEBUG +#endif /* `!defined(WITH_GHOST_DEBUG)` */ #ifdef WITH_ASSERT_ABORT # include <stdio.h> //for fprintf() @@ -49,7 +49,8 @@ } \ } \ (void)0 -#elif defined(WITH_GHOST_DEBUG) +/* Assert in non-release builds too. */ +#elif defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG)) # define GHOST_ASSERT(x, info) \ { \ if (!(x)) { \ @@ -59,6 +60,6 @@ } \ } \ (void)0 -#else // WITH_GHOST_DEBUG +#else /* `defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG))` */ # define GHOST_ASSERT(x, info) ((void)0) -#endif // WITH_GHOST_DEBUG +#endif /* `defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG))` */ diff --git a/intern/ghost/intern/GHOST_DisplayManagerNULL.h b/intern/ghost/intern/GHOST_DisplayManagerNULL.h index ba72dcbe8dd..dbef4fafd8f 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerNULL.h +++ b/intern/ghost/intern/GHOST_DisplayManagerNULL.h @@ -8,38 +8,38 @@ #pragma once #include "GHOST_DisplayManager.h" -#include "GHOST_SystemNULL.h" +#include "GHOST_SystemHeadless.h" -class GHOST_SystemNULL; +class GHOST_SystemHeadless; class GHOST_DisplayManagerNULL : public GHOST_DisplayManager { public: - GHOST_DisplayManagerNULL(GHOST_SystemNULL *system) : GHOST_DisplayManager(), m_system(system) + GHOST_DisplayManagerNULL() : GHOST_DisplayManager() { /* nop */ } - GHOST_TSuccess getNumDisplays(uint8_t &numDisplays) const + GHOST_TSuccess getNumDisplays(uint8_t & /*numDisplays*/) const override { return GHOST_kFailure; } - GHOST_TSuccess getNumDisplaySettings(uint8_t display, int32_t &numSettings) const + GHOST_TSuccess getNumDisplaySettings(uint8_t /*display*/, + int32_t & /*numSettings*/) const override { return GHOST_kFailure; } - GHOST_TSuccess getDisplaySetting(uint8_t display, - int32_t index, - GHOST_DisplaySetting &setting) const + GHOST_TSuccess getDisplaySetting(uint8_t /*display*/, + int32_t /*index*/, + GHOST_DisplaySetting & /*setting*/) const override { return GHOST_kFailure; } - GHOST_TSuccess getCurrentDisplaySetting(uint8_t display, GHOST_DisplaySetting &setting) const + GHOST_TSuccess getCurrentDisplaySetting(uint8_t display, + GHOST_DisplaySetting &setting) const override { return getDisplaySetting(display, int32_t(0), setting); } - GHOST_TSuccess setCurrentDisplaySetting(uint8_t display, const GHOST_DisplaySetting &setting) + GHOST_TSuccess setCurrentDisplaySetting(uint8_t /*display*/, + const GHOST_DisplaySetting & /*setting*/) override { return GHOST_kSuccess; } - - private: - GHOST_SystemNULL *m_system; }; diff --git a/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp b/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp index 3d8920d7c52..ee79792bb7e 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp +++ b/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp @@ -11,8 +11,9 @@ #define WIN32_LEAN_AND_MEAN #include <windows.h> -// We do not support multiple monitors at the moment +/* We do not support multiple monitors at the moment. */ #define COMPILE_MULTIMON_STUBS + #include <multimon.h> GHOST_DisplayManagerWin32::GHOST_DisplayManagerWin32(void) @@ -31,16 +32,15 @@ static BOOL get_dd(DWORD d, DISPLAY_DEVICE *dd) return ::EnumDisplayDevices(NULL, d, dd, 0); } -/* - * When you call EnumDisplaySettings with iModeNum set to zero, the operating system - * initializes and caches information about the display device. When you call - * EnumDisplaySettings with iModeNum set to a non-zero value, the function returns - * the information that was cached the last time the function was called with iModeNum - * set to zero. - */ GHOST_TSuccess GHOST_DisplayManagerWin32::getNumDisplaySettings(uint8_t display, int32_t &numSettings) const { + /* When you call #EnumDisplaySettings with #iModeNum set to zero, the operating system + * initializes and caches information about the display device. + * When you call #EnumDisplaySettings with #iModeNum set to a non-zero value, + * the function returns the information that was cached the last time the + * function was called with #iModeNum set to zero. */ + DISPLAY_DEVICE display_device; if (!get_dd(display, &display_device)) return GHOST_kFailure; @@ -70,21 +70,20 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::getDisplaySetting(uint8_t display, dm.dmPelsHeight, dm.dmBitsPerPel, dm.dmDisplayFrequency); -#endif // WITH_GHOST_DEBUG +#endif /* WITH_GHOST_DEBUG */ setting.xPixels = dm.dmPelsWidth; setting.yPixels = dm.dmPelsHeight; setting.bpp = dm.dmBitsPerPel; - /* When you call the EnumDisplaySettings function, the dmDisplayFrequency member + /* When you call the #EnumDisplaySettings function, the #dmDisplayFrequency member * may return with the value 0 or 1. These values represent the display hardware's * default refresh rate. This default rate is typically set by switches on a display * card or computer motherboard, or by a configuration program that does not use - * Win32 display functions such as ChangeDisplaySettings. - */ - /* First, we tried to explicitly set the frequency to 60 if EnumDisplaySettings + * Win32 display functions such as #ChangeDisplaySettings. */ + + /* First, we tried to explicitly set the frequency to 60 if #EnumDisplaySettings * returned 0 or 1 but this doesn't work since later on an exact match will * be searched. And this will never happen if we change it to 60. Now we rely - * on the default h/w setting. - */ + * on the default hardware setting. */ setting.frequency = dm.dmDisplayFrequency; success = GHOST_kSuccess; } @@ -117,22 +116,23 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::setCurrentDisplaySetting( break; } } - /* - * dm.dmBitsPerPel = match.bpp; - * dm.dmPelsWidth = match.xPixels; - * dm.dmPelsHeight = match.yPixels; - * dm.dmDisplayFrequency = match.frequency; - * dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; - * dm.dmSize = sizeof(DEVMODE); - * dm.dmDriverExtra = 0; - */ +#if 0 + dm.dmBitsPerPel = match.bpp; + dm.dmPelsWidth = match.xPixels; + dm.dmPelsHeight = match.yPixels; + dm.dmDisplayFrequency = match.frequency; + dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; + dm.dmSize = sizeof(DEVMODE); + dm.dmDriverExtra = 0; +#endif + #ifdef WITH_GHOST_DEBUG printf("display change: Requested settings:\n"); printf(" dmBitsPerPel=%d\n", dm.dmBitsPerPel); printf(" dmPelsWidth=%d\n", dm.dmPelsWidth); printf(" dmPelsHeight=%d\n", dm.dmPelsHeight); printf(" dmDisplayFrequency=%d\n", dm.dmDisplayFrequency); -#endif // WITH_GHOST_DEBUG +#endif /* WITH_GHOST_DEBUG */ LONG status = ::ChangeDisplaySettings(&dm, CDS_FULLSCREEN); #ifdef WITH_GHOST_DEBUG @@ -166,6 +166,6 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::setCurrentDisplaySetting( printf("display change: Return value invalid\n"); break; } -#endif // WITH_GHOST_DEBUG +#endif /* WITH_GHOST_DEBUG */ return status == DISP_CHANGE_SUCCESSFUL ? GHOST_kSuccess : GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_DropTargetWin32.cpp b/intern/ghost/intern/GHOST_DropTargetWin32.cpp index a82a31e7386..2831f2ee8ad 100644 --- a/intern/ghost/intern/GHOST_DropTargetWin32.cpp +++ b/intern/ghost/intern/GHOST_DropTargetWin32.cpp @@ -13,9 +13,9 @@ #include "utfconv.h" #ifdef WITH_GHOST_DEBUG -// utility +/* utility */ void printLastError(void); -#endif // WITH_GHOST_DEBUG +#endif /* WITH_GHOST_DEBUG */ GHOST_DropTargetWin32::GHOST_DropTargetWin32(GHOST_WindowWin32 *window, GHOST_SystemWin32 *system) : m_window(window), m_system(system) @@ -32,22 +32,21 @@ GHOST_DropTargetWin32::~GHOST_DropTargetWin32() /* * IUnknown::QueryInterface */ -HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface(REFIID riid, void **ppvObj) +HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface(REFIID riid, void **ppv_obj) { - if (!ppvObj) + if (!ppv_obj) { return E_INVALIDARG; - *ppvObj = NULL; + } + *ppv_obj = NULL; if (riid == IID_IUnknown || riid == IID_IDropTarget) { AddRef(); - *ppvObj = (void *)this; + *ppv_obj = (void *)this; return S_OK; } - else { - *ppvObj = NULL; - return E_NOINTERFACE; - } + *ppv_obj = NULL; + return E_NOINTERFACE; } /* @@ -78,16 +77,16 @@ ULONG __stdcall GHOST_DropTargetWin32::Release(void) /* * Implementation of IDropTarget::DragEnter */ -HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *pDataObject, - DWORD grfKeyState, +HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *p_data_object, + DWORD grf_key_state, POINTL pt, - DWORD *pdwEffect) + DWORD *pdw_effect) { - // we accept all drop by default + /* We accept all drop by default. */ m_window->setAcceptDragOperation(true); - *pdwEffect = DROPEFFECT_NONE; + *pdw_effect = DROPEFFECT_NONE; - m_draggedObjectType = getGhostType(pDataObject); + m_draggedObjectType = getGhostType(p_data_object); m_system->pushDragDropEvent( GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, NULL); return S_OK; @@ -96,15 +95,17 @@ HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *pDataObject, /* * Implementation of IDropTarget::DragOver */ -HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) +HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grf_key_state, + POINTL pt, + DWORD *pdw_effect) { if (m_window->canAcceptDragOperation()) { - *pdwEffect = allowedDropEffect(*pdwEffect); + *pdw_effect = allowedDropEffect(*pdw_effect); } else { - *pdwEffect = DROPEFFECT_NONE; - // XXX Uncomment to test drop. Drop will not be called if pdwEffect == DROPEFFECT_NONE. - // *pdwEffect = DROPEFFECT_COPY; + *pdw_effect = DROPEFFECT_NONE; + /* XXX Uncomment to test drop. Drop will not be called if `pdw_effect == DROPEFFECT_NONE`. */ + // *pdw_effect = DROPEFFECT_COPY; } m_system->pushDragDropEvent( GHOST_kEventDraggingUpdated, m_draggedObjectType, m_window, pt.x, pt.y, NULL); @@ -123,25 +124,25 @@ HRESULT __stdcall GHOST_DropTargetWin32::DragLeave(void) } /* Implementation of IDropTarget::Drop - * This function will not be called if pdwEffect is set to DROPEFFECT_NONE in + * This function will not be called if pdw_effect is set to DROPEFFECT_NONE in * the implementation of IDropTarget::DragOver */ -HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *pDataObject, - DWORD grfKeyState, +HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *p_data_object, + DWORD grf_key_state, POINTL pt, - DWORD *pdwEffect) + DWORD *pdw_effect) { - void *data = getGhostData(pDataObject); + void *data = getGhostData(p_data_object); if (m_window->canAcceptDragOperation()) { - *pdwEffect = allowedDropEffect(*pdwEffect); + *pdw_effect = allowedDropEffect(*pdw_effect); } else { - *pdwEffect = DROPEFFECT_NONE; + *pdw_effect = DROPEFFECT_NONE; } - if (data) + if (data) { m_system->pushDragDropEvent( GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, pt.x, pt.y, data); - + } m_draggedObjectType = GHOST_kDragnDropTypeUnknown; return S_OK; } @@ -150,150 +151,132 @@ HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *pDataObject, * Helpers */ -DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dwAllowed) +DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dw_allowed) { - DWORD dwEffect = DROPEFFECT_NONE; - if (dwAllowed & DROPEFFECT_COPY) - dwEffect = DROPEFFECT_COPY; - - return dwEffect; + DWORD dw_effect = DROPEFFECT_NONE; + if (dw_allowed & DROPEFFECT_COPY) { + dw_effect = DROPEFFECT_COPY; + } + return dw_effect; } -GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject *pDataObject) +GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject *p_data_object) { /* Text * NOTE: Unicode text is available as CF_TEXT too, the system can do the * conversion, but we do the conversion our self with #WC_NO_BEST_FIT_CHARS. */ FORMATETC fmtetc = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; - if (pDataObject->QueryGetData(&fmtetc) == S_OK) { + if (p_data_object->QueryGetData(&fmtetc) == S_OK) { return GHOST_kDragnDropTypeString; } - // Filesnames + /* Files-names. */ fmtetc.cfFormat = CF_HDROP; - if (pDataObject->QueryGetData(&fmtetc) == S_OK) { + if (p_data_object->QueryGetData(&fmtetc) == S_OK) { return GHOST_kDragnDropTypeFilenames; } return GHOST_kDragnDropTypeUnknown; } -void *GHOST_DropTargetWin32::getGhostData(IDataObject *pDataObject) +void *GHOST_DropTargetWin32::getGhostData(IDataObject *p_data_object) { - GHOST_TDragnDropTypes type = getGhostType(pDataObject); + GHOST_TDragnDropTypes type = getGhostType(p_data_object); switch (type) { case GHOST_kDragnDropTypeFilenames: - return getDropDataAsFilenames(pDataObject); - break; + return getDropDataAsFilenames(p_data_object); case GHOST_kDragnDropTypeString: - return getDropDataAsString(pDataObject); - break; + return getDropDataAsString(p_data_object); case GHOST_kDragnDropTypeBitmap: - // return getDropDataAsBitmap(pDataObject); + // return getDropDataAsBitmap(p_data_object); break; default: #ifdef WITH_GHOST_DEBUG ::printf("\nGHOST_kDragnDropTypeUnknown"); -#endif // WITH_GHOST_DEBUG +#endif /* WITH_GHOST_DEBUG */ return NULL; - break; } return NULL; } -void *GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject *pDataObject) +void *GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject *p_data_object) { - UINT totfiles, nvalid = 0; - WCHAR fpath[MAX_PATH]; - char *temp_path; - GHOST_TStringArray *strArray = NULL; + GHOST_TStringArray *str_array = NULL; FORMATETC fmtetc = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; - STGMEDIUM stgmed; - HDROP hdrop; - - // Check if dataobject supplies the format we want. - // Double checking here, first in getGhostType. - if (pDataObject->QueryGetData(&fmtetc) == S_OK) { - if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) { - hdrop = (HDROP)::GlobalLock(stgmed.hGlobal); - - totfiles = ::DragQueryFileW(hdrop, -1, NULL, 0); - if (!totfiles) { - ::GlobalUnlock(stgmed.hGlobal); - return NULL; - } - strArray = (GHOST_TStringArray *)::malloc(sizeof(GHOST_TStringArray)); - strArray->count = 0; - strArray->strings = (uint8_t **)::malloc(totfiles * sizeof(uint8_t *)); - - for (UINT nfile = 0; nfile < totfiles; nfile++) { - if (::DragQueryFileW(hdrop, nfile, fpath, MAX_PATH) > 0) { - if (!(temp_path = alloc_utf_8_from_16(fpath, 0))) { - continue; + /* Check if data-object supplies the format we want. + * Double checking here, first in #getGhostType. */ + if (p_data_object->QueryGetData(&fmtetc) == S_OK) { + STGMEDIUM stgmed; + if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) { + const HDROP hdrop = (HDROP)::GlobalLock(stgmed.hGlobal); + + const UINT totfiles = ::DragQueryFileW(hdrop, -1, NULL, 0); + if (totfiles) { + str_array = (GHOST_TStringArray *)::malloc(sizeof(GHOST_TStringArray)); + str_array->count = 0; + str_array->strings = (uint8_t **)::malloc(totfiles * sizeof(uint8_t *)); + + for (UINT nfile = 0; nfile < totfiles; nfile++) { + WCHAR fpath[MAX_PATH]; + if (::DragQueryFileW(hdrop, nfile, fpath, MAX_PATH) > 0) { + char *temp_path; + if (!(temp_path = alloc_utf_8_from_16(fpath, 0))) { + /* Just ignore paths that could not be converted verbatim. */ + continue; + } + str_array->strings[str_array->count++] = (uint8_t *)temp_path; } - // Just ignore paths that could not be converted verbatim. - - strArray->strings[nvalid] = (uint8_t *)temp_path; - strArray->count = nvalid + 1; - nvalid++; } } - // Free up memory. + /* Free up memory. */ ::GlobalUnlock(stgmed.hGlobal); ::ReleaseStgMedium(&stgmed); - - return strArray; } } - return NULL; + return str_array; } -void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *pDataObject) +void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *p_data_object) { char *tmp_string; FORMATETC fmtetc = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM stgmed; - // Try unicode first. - // Check if dataobject supplies the format we want. - if (pDataObject->QueryGetData(&fmtetc) == S_OK) { - if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) { + /* Try unicode first. + * Check if data-object supplies the format we want. */ + if (p_data_object->QueryGetData(&fmtetc) == S_OK) { + if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) { LPCWSTR wstr = (LPCWSTR)::GlobalLock(stgmed.hGlobal); - if (!(tmp_string = alloc_utf_8_from_16((wchar_t *)wstr, 0))) { - ::GlobalUnlock(stgmed.hGlobal); - return NULL; - } - // Free memory + + tmp_string = alloc_utf_8_from_16((wchar_t *)wstr, 0); + + /* Free memory. */ ::GlobalUnlock(stgmed.hGlobal); ::ReleaseStgMedium(&stgmed); + #ifdef WITH_GHOST_DEBUG - ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n", - tmp_string); -#endif // WITH_GHOST_DEBUG + if (tmp_string) { + ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n", + tmp_string); + } +#endif /* WITH_GHOST_DEBUG */ return tmp_string; } } fmtetc.cfFormat = CF_TEXT; - if (pDataObject->QueryGetData(&fmtetc) == S_OK) { - if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) { + if (p_data_object->QueryGetData(&fmtetc) == S_OK) { + if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) { char *str = (char *)::GlobalLock(stgmed.hGlobal); tmp_string = (char *)::malloc(::strlen(str) + 1); - if (!tmp_string) { - ::GlobalUnlock(stgmed.hGlobal); - return NULL; - } - - if (!::strcpy(tmp_string, str)) { - ::free(tmp_string); - ::GlobalUnlock(stgmed.hGlobal); - return NULL; + if (tmp_string) { + ::strcpy(tmp_string, str); } - // Free memory + /* Free memory. */ ::GlobalUnlock(stgmed.hGlobal); ::ReleaseStgMedium(&stgmed); @@ -307,13 +290,13 @@ void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *pDataObject) int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out) { int size; - out = NULL; // caller should free if != NULL + out = NULL; /* caller should free if != NULL */ - // Get the required size. - size = ::WideCharToMultiByte(CP_ACP, // System Default Codepage - 0x00000400, // WC_NO_BEST_FIT_CHARS + /* Get the required size. */ + size = ::WideCharToMultiByte(CP_ACP, /* System Default Codepage */ + 0x00000400, /* WC_NO_BEST_FIT_CHARS */ in, - -1, //-1 null terminated, makes output null terminated too. + -1, /* -1 null terminated, makes output null terminated too. */ NULL, 0, NULL, @@ -322,7 +305,7 @@ int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out) if (!size) { #ifdef WITH_GHOST_DEBUG ::printLastError(); -#endif // WITH_GHOST_DEBUG +#endif /* WITH_GHOST_DEBUG */ return 0; } @@ -337,7 +320,7 @@ int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out) if (!size) { #ifdef WITH_GHOST_DEBUG ::printLastError(); -#endif // WITH_GHOST_DEBUG +#endif /* WITH_GHOST_DEBUG */ ::free(out); out = NULL; } @@ -362,4 +345,4 @@ void printLastError(void) LocalFree(s); } } -#endif // WITH_GHOST_DEBUG +#endif /* WITH_GHOST_DEBUG */ diff --git a/intern/ghost/intern/GHOST_DropTargetWin32.h b/intern/ghost/intern/GHOST_DropTargetWin32.h index 2f628e81b56..03e5492fcb1 100644 --- a/intern/ghost/intern/GHOST_DropTargetWin32.h +++ b/intern/ghost/intern/GHOST_DropTargetWin32.h @@ -21,7 +21,7 @@ class GHOST_DropTargetWin32 : public IDropTarget { * inherited, directly or indirectly, from IUnknown. Therefore, the three * methods in IUnknown are the first entries in the VTable for every interface. */ - HRESULT __stdcall QueryInterface(REFIID riid, void **ppvObj); + HRESULT __stdcall QueryInterface(REFIID riid, void **ppv_obj); ULONG __stdcall AddRef(void); ULONG __stdcall Release(void); @@ -44,13 +44,16 @@ class GHOST_DropTargetWin32 : public IDropTarget { * RevokeDragDrop functions. */ - HRESULT __stdcall DragEnter(IDataObject *pDataObject, - DWORD grfKeyState, + HRESULT __stdcall DragEnter(IDataObject *p_data_object, + DWORD grf_key_state, POINTL pt, - DWORD *pdwEffect); - HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); + DWORD *pdw_effect); + HRESULT __stdcall DragOver(DWORD grf_key_state, POINTL pt, DWORD *pdw_effect); HRESULT __stdcall DragLeave(void); - HRESULT __stdcall Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); + HRESULT __stdcall Drop(IDataObject *p_data_object, + DWORD grf_key_state, + POINTL pt, + DWORD *pdw_effect); /** * Constructor @@ -76,36 +79,36 @@ class GHOST_DropTargetWin32 : public IDropTarget { * \param dwAllowed: Drop sources allowed drop effect. * \return The allowed drop effect. */ - DWORD allowedDropEffect(DWORD dwAllowed); + DWORD allowedDropEffect(DWORD dw_allowed); /** * Query DataObject for the data types it supports. - * \param pDataObject: Pointer to the DataObject. + * \param p_data_object: Pointer to the DataObject. * \return GHOST data type. */ - GHOST_TDragnDropTypes getGhostType(IDataObject *pDataObject); + GHOST_TDragnDropTypes getGhostType(IDataObject *p_data_object); /** * Get data to pass in event. * It checks the type and calls specific functions for each type. - * \param pDataObject: Pointer to the DataObject. + * \param p_data_object: Pointer to the DataObject. * \return Pointer to data. */ - void *getGhostData(IDataObject *pDataObject); + void *getGhostData(IDataObject *p_data_object); /** * Allocate data as file array to pass in event. - * \param pDataObject: Pointer to the DataObject. + * \param p_data_object: Pointer to the DataObject. * \return Pointer to data. */ - void *getDropDataAsFilenames(IDataObject *pDataObject); + void *getDropDataAsFilenames(IDataObject *p_data_object); /** * Allocate data as string to pass in event. - * \param pDataObject: Pointer to the DataObject. + * \param p_data_object: Pointer to the DataObject. * \return Pointer to data. */ - void *getDropDataAsString(IDataObject *pDataObject); + void *getDropDataAsString(IDataObject *p_data_object); /** * Convert Unicode to ANSI, replacing uncomfortable chars with '?'. diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp index 900e46c3732..4da3c7c996d 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.cpp +++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp @@ -7,6 +7,8 @@ #include "GHOST_DropTargetX11.h" #include "GHOST_Debug.h" +#include "GHOST_PathUtils.h" +#include "GHOST_utildefines.h" #include <cassert> #include <cctype> @@ -34,7 +36,7 @@ int GHOST_DropTargetX11::m_refCounter = 0; void GHOST_DropTargetX11::Initialize() { Display *display = m_system->getXDisplay(); - int dndTypesCount = sizeof(m_dndMimeTypes) / sizeof(char *); + int dndTypesCount = ARRAY_SIZE(m_dndMimeTypes); int counter; xdnd_init(&m_dndClass, display); @@ -96,89 +98,10 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11() } } -/* Based on: https://stackoverflow.com/a/2766963/432509 */ - -using DecodeState_e = enum DecodeState_e { - /** Searching for an ampersand to convert. */ - STATE_SEARCH = 0, - /** Convert the two proceeding characters from hex. */ - STATE_CONVERTING -}; - -void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn) -{ - unsigned int i; - unsigned int len = strlen(encodedIn); - DecodeState_e state = STATE_SEARCH; - int j; - unsigned int asciiCharacter; - char tempNumBuf[3] = {0}; - bool bothDigits = true; - - memset(decodedOut, 0, bufferSize); - - for (i = 0; i < len; ++i) { - switch (state) { - case STATE_SEARCH: - if (encodedIn[i] != '%') { - strncat(decodedOut, &encodedIn[i], 1); - assert((int)strlen(decodedOut) < bufferSize); - break; - } - - /* We are now converting */ - state = STATE_CONVERTING; - break; - - case STATE_CONVERTING: - bothDigits = true; - - /* Create a buffer to hold the hex. For example, if %20, this - * buffer would hold 20 (in ASCII) */ - memset(tempNumBuf, 0, sizeof(tempNumBuf)); - - /* Conversion complete (i.e. don't convert again next iter) */ - state = STATE_SEARCH; - - strncpy(tempNumBuf, &encodedIn[i], 2); - - /* Ensure both characters are hexadecimal */ - - for (j = 0; j < 2; ++j) { - if (!isxdigit(tempNumBuf[j])) { - bothDigits = false; - } - } - - if (!bothDigits) { - break; - } - /* Convert two hexadecimal characters into one character */ - sscanf(tempNumBuf, "%x", &asciiCharacter); - - /* Ensure we aren't going to overflow */ - assert((int)strlen(decodedOut) < bufferSize); - - /* Concatenate this character onto the output */ - strncat(decodedOut, (char *)&asciiCharacter, 1); - - /* Skip the next character */ - i++; - break; - } - } -} - char *GHOST_DropTargetX11::FileUrlDecode(char *fileUrl) { if (strncmp(fileUrl, "file://", 7) == 0) { - /* assume one character of encoded URL can be expanded to 4 chars max */ - int decodedSize = 4 * strlen(fileUrl) + 1; - char *decodedPath = (char *)malloc(decodedSize); - - UrlDecode(decodedPath, decodedSize, fileUrl + 7); - - return decodedPath; + return GHOST_URL_decode_alloc(fileUrl + 7); } return nullptr; diff --git a/intern/ghost/intern/GHOST_DropTargetX11.h b/intern/ghost/intern/GHOST_DropTargetX11.h index f0ef27697e1..db73ddff70f 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.h +++ b/intern/ghost/intern/GHOST_DropTargetX11.h @@ -65,14 +65,6 @@ class GHOST_DropTargetX11 { void *getURIListGhostData(unsigned char *dropBuffer, int dropBufferSize); /** - * Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`) - * \param decodedOut: - buffer for decoded URL. - * \param bufferSize: - size of output buffer. - * \param encodedIn: - input encoded buffer to be decoded. - */ - void UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn); - - /** * Fully decode file URL (i.e. converts `file:///a%20b/test` to `/a b/test`) * \param fileUrl: - file path URL to be fully decoded. * \return decoded file path (result should be free-d). diff --git a/intern/ghost/intern/GHOST_Event.h b/intern/ghost/intern/GHOST_Event.h index 0813378a819..1e1c428e2ae 100644 --- a/intern/ghost/intern/GHOST_Event.h +++ b/intern/ghost/intern/GHOST_Event.h @@ -22,7 +22,7 @@ class GHOST_Event : public GHOST_IEvent { * \param window: The generating window (or NULL if system event). */ GHOST_Event(uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window) - : m_type(type), m_time(msec), m_window(window), m_data(NULL) + : m_type(type), m_time(msec), m_window(window), m_data(nullptr) { } diff --git a/intern/ghost/intern/GHOST_EventButton.h b/intern/ghost/intern/GHOST_EventButton.h index d68e401ffc4..f42805e2c6b 100644 --- a/intern/ghost/intern/GHOST_EventButton.h +++ b/intern/ghost/intern/GHOST_EventButton.h @@ -27,7 +27,7 @@ class GHOST_EventButton : public GHOST_Event { GHOST_EventButton(uint64_t time, GHOST_TEventType type, GHOST_IWindow *window, - GHOST_TButtonMask button, + GHOST_TButton button, const GHOST_TabletData &tablet) : GHOST_Event(time, type, window), m_buttonEventData({button, tablet}) { diff --git a/intern/ghost/intern/GHOST_EventKey.h b/intern/ghost/intern/GHOST_EventKey.h index 1c3156c27d2..f85a674be9f 100644 --- a/intern/ghost/intern/GHOST_EventKey.h +++ b/intern/ghost/intern/GHOST_EventKey.h @@ -22,13 +22,13 @@ class GHOST_EventKey : public GHOST_Event { * \param msec: The time this event was generated. * \param type: The type of key event. * \param key: The key code of the key. + * \param is_repeat: Enabled for key repeat events (only for press events). */ GHOST_EventKey( uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key, bool is_repeat) : GHOST_Event(msec, type, window) { m_keyEventData.key = key; - m_keyEventData.ascii = '\0'; m_keyEventData.utf8_buf[0] = '\0'; m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; @@ -39,23 +39,24 @@ class GHOST_EventKey : public GHOST_Event { * \param msec: The time this event was generated. * \param type: The type of key event. * \param key: The key code of the key. - * \param ascii: The ascii code for the key event. + * \param is_repeat: Enabled for key repeat events (only for press events). + * \param utf8_buf: The text associated with this key event (only for press events). */ GHOST_EventKey(uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key, - char ascii, - const char utf8_buf[6], - bool is_repeat) + bool is_repeat, + const char utf8_buf[6]) : GHOST_Event(msec, type, window) { m_keyEventData.key = key; - m_keyEventData.ascii = ascii; - if (utf8_buf) + if (utf8_buf) { memcpy(m_keyEventData.utf8_buf, utf8_buf, sizeof(m_keyEventData.utf8_buf)); - else + } + else { m_keyEventData.utf8_buf[0] = '\0'; + } m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; } diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp index 745d5faeed4..13eccf661f5 100644 --- a/intern/ghost/intern/GHOST_ISystem.cpp +++ b/intern/ghost/intern/GHOST_ISystem.cpp @@ -9,14 +9,14 @@ * Copyright (C) 2001 NaN Technologies B.V. */ +#include <stdexcept> + #include "GHOST_ISystem.h" +#include "GHOST_SystemHeadless.h" -#if defined(WITH_HEADLESS) -# include "GHOST_SystemNULL.h" -#elif defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND) +#if defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND) # include "GHOST_SystemWayland.h" # include "GHOST_SystemX11.h" -# include <stdexcept> #elif defined(WITH_GHOST_X11) # include "GHOST_SystemX11.h" #elif defined(WITH_GHOST_WAYLAND) @@ -37,27 +37,62 @@ GHOST_TSuccess GHOST_ISystem::createSystem() { GHOST_TSuccess success; if (!m_system) { + #if defined(WITH_HEADLESS) - m_system = new GHOST_SystemNULL(); + /* Pass. */ +#elif defined(WITH_GHOST_WAYLAND) +# if defined(WITH_GHOST_WAYLAND_DYNLOAD) + const bool has_wayland_libraries = ghost_wl_dynload_libraries(); +# else + const bool has_wayland_libraries = true; +# endif +#endif + +#if defined(WITH_HEADLESS) + /* Pass. */ #elif defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND) /* Special case, try Wayland, fall back to X11. */ try { - m_system = new GHOST_SystemWayland(); + m_system = has_wayland_libraries ? new GHOST_SystemWayland() : nullptr; } catch (const std::runtime_error &) { - /* fallback to X11. */ delete m_system; m_system = nullptr; } if (!m_system) { - m_system = new GHOST_SystemX11(); + /* Try to fallback to X11. */ + try { + m_system = new GHOST_SystemX11(); + } + catch (const std::runtime_error &) { + delete m_system; + m_system = nullptr; + } } #elif defined(WITH_GHOST_X11) - m_system = new GHOST_SystemX11(); + try { + m_system = new GHOST_SystemX11(); + } + catch (const std::runtime_error &) { + delete m_system; + m_system = nullptr; + } #elif defined(WITH_GHOST_WAYLAND) - m_system = new GHOST_SystemWayland(); + try { + m_system = has_wayland_libraries ? new GHOST_SystemWayland() : nullptr; + } + catch (const std::runtime_error &) { + delete m_system; + m_system = nullptr; + } #elif defined(WITH_GHOST_SDL) - m_system = new GHOST_SystemSDL(); + try { + m_system = new GHOST_SystemSDL(); + } + catch (const std::runtime_error &) { + delete m_system; + m_system = nullptr; + } #elif defined(WIN32) m_system = new GHOST_SystemWin32(); #elif defined(__APPLE__) @@ -74,6 +109,30 @@ GHOST_TSuccess GHOST_ISystem::createSystem() return success; } +GHOST_TSuccess GHOST_ISystem::createSystemBackground() +{ + GHOST_TSuccess success; + if (!m_system) { +#if !defined(WITH_HEADLESS) + /* Try to create a off-screen render surface with the graphical systems. */ + success = createSystem(); + if (success) { + return success; + } + /* Try to fallback to headless mode if all else fails. */ +#endif + m_system = new GHOST_SystemHeadless(); + success = m_system != nullptr ? GHOST_kSuccess : GHOST_kFailure; + } + else { + success = GHOST_kFailure; + } + if (success) { + success = m_system->init(); + } + return success; +} + GHOST_TSuccess GHOST_ISystem::disposeSystem() { GHOST_TSuccess success = GHOST_kSuccess; diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h index 1246aa19a99..152b6b68026 100644 --- a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h +++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h @@ -17,11 +17,8 @@ class GHOST_IXrGraphicsBinding { public: union { #if defined(WITH_GHOST_X11) -# if defined(WITH_GL_EGL) XrGraphicsBindingEGLMNDX egl; -# else XrGraphicsBindingOpenGLXlibKHR glx; -# endif #elif defined(WIN32) XrGraphicsBindingOpenGLWin32KHR wgl; XrGraphicsBindingD3D11KHR d3d11; diff --git a/intern/ghost/intern/GHOST_ImeWin32.cpp b/intern/ghost/intern/GHOST_ImeWin32.cpp index c3fcd7214ca..0a62359cd77 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.cpp +++ b/intern/ghost/intern/GHOST_ImeWin32.cpp @@ -512,4 +512,4 @@ void GHOST_ImeWin32::UpdateInfo(HWND window_handle) } } -#endif // WITH_INPUT_IME +#endif /* WITH_INPUT_IME */ diff --git a/intern/ghost/intern/GHOST_ImeWin32.h b/intern/ghost/intern/GHOST_ImeWin32.h index 0ae2bbc59e9..85c8ed7b4bd 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.h +++ b/intern/ghost/intern/GHOST_ImeWin32.h @@ -351,4 +351,4 @@ class GHOST_ImeWin32 { bool is_first, is_enable; }; -#endif // WITH_INPUT_IME +#endif /* WITH_INPUT_IME */ diff --git a/intern/ghost/intern/GHOST_ModifierKeys.cpp b/intern/ghost/intern/GHOST_ModifierKeys.cpp index e6e433ba332..d31dc8f0770 100644 --- a/intern/ghost/intern/GHOST_ModifierKeys.cpp +++ b/intern/ghost/intern/GHOST_ModifierKeys.cpp @@ -20,7 +20,7 @@ GHOST_ModifierKeys::~GHOST_ModifierKeys() { } -GHOST_TKey GHOST_ModifierKeys::getModifierKeyCode(GHOST_TModifierKeyMask mask) +GHOST_TKey GHOST_ModifierKeys::getModifierKeyCode(GHOST_TModifierKey mask) { GHOST_TKey key; switch (mask) { @@ -53,7 +53,7 @@ GHOST_TKey GHOST_ModifierKeys::getModifierKeyCode(GHOST_TModifierKeyMask mask) return key; } -bool GHOST_ModifierKeys::get(GHOST_TModifierKeyMask mask) const +bool GHOST_ModifierKeys::get(GHOST_TModifierKey mask) const { switch (mask) { case GHOST_kModifierKeyLeftShift: @@ -75,7 +75,7 @@ bool GHOST_ModifierKeys::get(GHOST_TModifierKeyMask mask) const } } -void GHOST_ModifierKeys::set(GHOST_TModifierKeyMask mask, bool down) +void GHOST_ModifierKeys::set(GHOST_TModifierKey mask, bool down) { switch (mask) { case GHOST_kModifierKeyLeftShift: diff --git a/intern/ghost/intern/GHOST_ModifierKeys.h b/intern/ghost/intern/GHOST_ModifierKeys.h index ca76ba6c704..ce1bf3df2ae 100644 --- a/intern/ghost/intern/GHOST_ModifierKeys.h +++ b/intern/ghost/intern/GHOST_ModifierKeys.h @@ -27,21 +27,21 @@ struct GHOST_ModifierKeys { * \param mask: The mask of the modifier key. * \return The modifier key's key code. */ - static GHOST_TKey getModifierKeyCode(GHOST_TModifierKeyMask mask); + static GHOST_TKey getModifierKeyCode(GHOST_TModifierKey mask); /** * Returns the state of a single modifier key. * \param mask: Key state to return. * \return The state of the key (pressed == true). */ - bool get(GHOST_TModifierKeyMask mask) const; + bool get(GHOST_TModifierKey mask) const; /** * Updates the state of a single modifier key. * \param mask: Key state to update. * \param down: The new state of the key. */ - void set(GHOST_TModifierKeyMask mask, bool down); + void set(GHOST_TModifierKey mask, bool down); /** * Sets the state of all modifier keys to up. diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp index 2298ba86521..746e3532b03 100644 --- a/intern/ghost/intern/GHOST_NDOFManager.cpp +++ b/intern/ghost/intern/GHOST_NDOFManager.cpp @@ -5,6 +5,7 @@ #include "GHOST_EventKey.h" #include "GHOST_EventNDOF.h" #include "GHOST_WindowManager.h" +#include "GHOST_utildefines.h" #include <climits> #include <cmath> @@ -128,7 +129,7 @@ static const NDOF_ButtonT Generic_HID_map[] = { NDOF_BUTTON_C, }; -static const int genericButtonCount = sizeof(Generic_HID_map) / sizeof(NDOF_ButtonT); +static const int genericButtonCount = ARRAY_SIZE(Generic_HID_map); GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System &sys) : m_system(sys), @@ -410,8 +411,9 @@ static bool nearHomePosition(GHOST_TEventNDOFMotionData *ndof, float threshold) bool GHOST_NDOFManager::sendMotionEvent() { - if (!m_motionEventPending) + if (!m_motionEventPending) { return false; + } m_motionEventPending = false; /* Any pending motion is handled right now. */ diff --git a/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp b/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp index 36202278ea1..43f31cb2368 100644 --- a/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp +++ b/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp @@ -7,10 +7,10 @@ GHOST_NDOFManagerWin32::GHOST_NDOFManagerWin32(GHOST_System &sys) : GHOST_NDOFMa /* pass */ } -// whether multi-axis functionality is available (via the OS or driver) -// does not imply that a device is plugged in or being used +/* Whether multi-axis functionality is available (via the OS or driver) + * does not imply that a device is plugged in or being used. */ bool GHOST_NDOFManagerWin32::available() { - // always available since RawInput is built into Windows + /* Always available since RawInput is built into Windows. */ return true; } diff --git a/intern/ghost/intern/GHOST_Path-api.cpp b/intern/ghost/intern/GHOST_Path-api.cpp index 1b1c72d8a4b..58f36dc096d 100644 --- a/intern/ghost/intern/GHOST_Path-api.cpp +++ b/intern/ghost/intern/GHOST_Path-api.cpp @@ -49,10 +49,10 @@ const char *GHOST_getBinaryDir() return systemPaths ? systemPaths->getBinaryDir() : nullptr; } -void GHOST_addToSystemRecentFiles(const char *filename) +void GHOST_addToSystemRecentFiles(const char *filepath) { GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get(); if (systemPaths) { - systemPaths->addToSystemRecentFiles(filename); + systemPaths->addToSystemRecentFiles(filepath); } } diff --git a/intern/ghost/intern/GHOST_PathUtils.cpp b/intern/ghost/intern/GHOST_PathUtils.cpp new file mode 100644 index 00000000000..3b57480039a --- /dev/null +++ b/intern/ghost/intern/GHOST_PathUtils.cpp @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2010 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup GHOST + */ + +#include <cassert> +#include <cctype> +#include <cstdio> +#include <cstdlib> +#include <cstring> + +#include "GHOST_PathUtils.h" +#include "GHOST_Types.h" + +/* Based on: https://stackoverflow.com/a/2766963/432509 */ + +using DecodeState_e = enum DecodeState_e { + /** Searching for an ampersand to convert. */ + STATE_SEARCH = 0, + /** Convert the two proceeding characters from hex. */ + STATE_CONVERTING +}; + +void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src) +{ + const unsigned int buf_src_len = strlen(buf_src); + DecodeState_e state = STATE_SEARCH; + unsigned int ascii_character; + char temp_num_buf[3] = {0}; + + memset(buf_dst, 0, buf_dst_size); + + for (unsigned int i = 0; i < buf_src_len; i++) { + switch (state) { + case STATE_SEARCH: { + if (buf_src[i] != '%') { + strncat(buf_dst, &buf_src[i], 1); + assert((int)strlen(buf_dst) < buf_dst_size); + break; + } + + /* We are now converting. */ + state = STATE_CONVERTING; + break; + } + case STATE_CONVERTING: { + bool both_digits = true; + + /* Create a buffer to hold the hex. For example, if `%20`, + * this buffer would hold 20 (in ASCII). */ + memset(temp_num_buf, 0, sizeof(temp_num_buf)); + + /* Conversion complete (i.e. don't convert again next iteration). */ + state = STATE_SEARCH; + + strncpy(temp_num_buf, &buf_src[i], 2); + + /* Ensure both characters are hexadecimal. */ + for (int j = 0; j < 2; j++) { + if (!isxdigit(temp_num_buf[j])) { + both_digits = false; + } + } + + if (!both_digits) { + break; + } + /* Convert two hexadecimal characters into one character. */ + sscanf(temp_num_buf, "%x", &ascii_character); + + /* Ensure we aren't going to overflow. */ + assert((int)strlen(buf_dst) < buf_dst_size); + + /* Concatenate this character onto the output. */ + strncat(buf_dst, (char *)&ascii_character, 1); + + /* Skip the next character. */ + i++; + break; + } + } + } +} + +char *GHOST_URL_decode_alloc(const char *buf_src) +{ + /* Assume one character of encoded URL can be expanded to 4 chars max. */ + const size_t decoded_size_max = 4 * strlen(buf_src) + 1; + char *buf_dst = (char *)malloc(decoded_size_max); + GHOST_URL_decode(buf_dst, decoded_size_max, buf_src); + const size_t decoded_size = strlen(buf_dst) + 1; + if (decoded_size != decoded_size_max) { + char *buf_dst_trim = (char *)malloc(decoded_size); + memcpy(buf_dst_trim, buf_dst, decoded_size); + free(buf_dst); + buf_dst = buf_dst_trim; + } + return buf_dst; +} diff --git a/intern/ghost/intern/GHOST_PathUtils.h b/intern/ghost/intern/GHOST_PathUtils.h new file mode 100644 index 00000000000..26a31d1f5c6 --- /dev/null +++ b/intern/ghost/intern/GHOST_PathUtils.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2010 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup GHOST + */ + +#pragma once + +/** + * Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`) + * + * \param buf_dst: Buffer for decoded URL. + * \param buf_dst_maxlen: Size of output buffer. + * \param buf_src: Input encoded buffer to be decoded. + */ +void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src); +/** + * A version of #GHOST_URL_decode that allocates the string & returns it. + * + * \param buf_src: Input encoded buffer to be decoded. + * \return The decoded output buffer. + */ +char *GHOST_URL_decode_alloc(const char *buf_src); diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp index cc8d0915c5a..bfb7c958048 100644 --- a/intern/ghost/intern/GHOST_System.cpp +++ b/intern/ghost/intern/GHOST_System.cpp @@ -110,8 +110,7 @@ bool GHOST_System::validWindow(GHOST_IWindow *window) GHOST_TSuccess GHOST_System::beginFullScreen(const GHOST_DisplaySetting &setting, GHOST_IWindow **window, - const bool stereoVisual, - const bool alphaBackground) + const bool stereoVisual) { GHOST_TSuccess success = GHOST_kFailure; GHOST_ASSERT(m_windowManager, "GHOST_System::beginFullScreen(): invalid window manager"); @@ -125,8 +124,7 @@ GHOST_TSuccess GHOST_System::beginFullScreen(const GHOST_DisplaySetting &setting setting); if (success == GHOST_kSuccess) { // GHOST_PRINT("GHOST_System::beginFullScreen(): creating full-screen window\n"); - success = createFullScreenWindow( - (GHOST_Window **)window, setting, stereoVisual, alphaBackground); + success = createFullScreenWindow((GHOST_Window **)window, setting, stereoVisual); if (success == GHOST_kSuccess) { m_windowManager->beginFullScreen(*window, stereoVisual); } @@ -260,7 +258,30 @@ GHOST_TSuccess GHOST_System::pushEvent(GHOST_IEvent *event) return success; } -GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bool &isDown) const +GHOST_TSuccess GHOST_System::getCursorPositionClientRelative(const GHOST_IWindow *window, + int32_t &x, + int32_t &y) const +{ + /* Sub-classes that can implement this directly should do so. */ + int32_t screen_x, screen_y; + GHOST_TSuccess success = getCursorPosition(screen_x, screen_y); + if (success == GHOST_kSuccess) { + window->screenToClient(screen_x, screen_y, x, y); + } + return success; +} + +GHOST_TSuccess GHOST_System::setCursorPositionClientRelative(GHOST_IWindow *window, + int32_t x, + int32_t y) +{ + /* Sub-classes that can implement this directly should do so. */ + int32_t screen_x, screen_y; + window->clientToScreen(x, y, screen_x, screen_y); + return setCursorPosition(screen_x, screen_y); +} + +GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKey mask, bool &isDown) const { GHOST_ModifierKeys keys; /* Get the state of all modifier keys. */ @@ -272,7 +293,7 @@ GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bo return success; } -GHOST_TSuccess GHOST_System::getButtonState(GHOST_TButtonMask mask, bool &isDown) const +GHOST_TSuccess GHOST_System::getButtonState(GHOST_TButton mask, bool &isDown) const { GHOST_Buttons buttons; /* Get the state of all mouse buttons. */ @@ -350,18 +371,13 @@ GHOST_TSuccess GHOST_System::exit() GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window, const GHOST_DisplaySetting &settings, - const bool stereoVisual, - const bool alphaBackground) + const bool stereoVisual) { GHOST_GLSettings glSettings = {0}; if (stereoVisual) { glSettings.flags |= GHOST_glStereoVisual; } - if (alphaBackground) { - glSettings.flags |= GHOST_glAlphaBackground; - } - /* NOTE: don't use #getCurrentDisplaySetting() because on X11 we may * be zoomed in and the desktop may be bigger than the viewport. */ GHOST_ASSERT(m_displayManager, diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h index b60ce09f743..8c51b3421b2 100644 --- a/intern/ghost/intern/GHOST_System.h +++ b/intern/ghost/intern/GHOST_System.h @@ -120,8 +120,7 @@ class GHOST_System : public GHOST_ISystem { */ GHOST_TSuccess beginFullScreen(const GHOST_DisplaySetting &setting, GHOST_IWindow **window, - const bool stereoVisual, - const bool alphaBackground); + const bool stereoVisual); /** * Updates the resolution while in fullscreen mode. @@ -203,6 +202,15 @@ class GHOST_System : public GHOST_ISystem { * Cursor management functionality ***************************************************************************************/ + /* Client relative functions use a default implementation + * that converts from screen-coordinates to client coordinates. + * Implementations may override. */ + + GHOST_TSuccess getCursorPositionClientRelative(const GHOST_IWindow *window, + int32_t &x, + int32_t &y) const; + GHOST_TSuccess setCursorPositionClientRelative(GHOST_IWindow *window, int32_t x, int32_t y); + /** * Inherited from GHOST_ISystem but left pure virtual * <pre> @@ -221,7 +229,7 @@ class GHOST_System : public GHOST_ISystem { * \param isDown: The state of a modifier key (true == pressed). * \return Indication of success. */ - GHOST_TSuccess getModifierKeyState(GHOST_TModifierKeyMask mask, bool &isDown) const; + GHOST_TSuccess getModifierKeyState(GHOST_TModifierKey mask, bool &isDown) const; /** * Returns the state of a mouse button (outside the message queue). @@ -229,7 +237,7 @@ class GHOST_System : public GHOST_ISystem { * \param isDown: Button state. * \return Indication of success. */ - GHOST_TSuccess getButtonState(GHOST_TButtonMask mask, bool &isDown) const; + GHOST_TSuccess getButtonState(GHOST_TButton mask, bool &isDown) const; /** * Set which tablet API to use. Only affects Windows, other platforms have a single API. @@ -367,8 +375,7 @@ class GHOST_System : public GHOST_ISystem { */ GHOST_TSuccess createFullScreenWindow(GHOST_Window **window, const GHOST_DisplaySetting &settings, - const bool stereoVisual, - const bool alphaBackground = 0); + const bool stereoVisual); /** The display manager (platform dependent). */ GHOST_DisplayManager *m_displayManager; diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h index 8b6dfb4efed..a9e659d3565 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.h +++ b/intern/ghost/intern/GHOST_SystemCocoa.h @@ -236,9 +236,9 @@ class GHOST_SystemCocoa : public GHOST_System { /** * \see GHOST_ISystem */ - int setConsoleWindowState(GHOST_TConsoleWindowState action) + bool setConsoleWindowState(GHOST_TConsoleWindowState action) { - return 0; + return false; } /** diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 8677c0b9552..5562db7d67f 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -37,7 +37,7 @@ #pragma mark KeyMap, mouse converters -static GHOST_TButtonMask convertButton(int button) +static GHOST_TButton convertButton(int button) { switch (button) { case 0: @@ -787,7 +787,7 @@ GHOST_IWindow *GHOST_SystemCocoa::getWindowUnderCursor(int32_t x, int32_t y) } /** - * \note : returns coordinates in Cocoa screen coordinates + * \note returns coordinates in Cocoa screen coordinates. */ GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(int32_t &x, int32_t &y) const { @@ -800,7 +800,7 @@ GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(int32_t &x, int32_t &y) cons } /** - * \note : expect Cocoa screen coordinates + * \note expect Cocoa screen coordinates. */ GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(int32_t x, int32_t y) { @@ -1123,6 +1123,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType case GHOST_kEventDraggingEntered: case GHOST_kEventDraggingUpdated: case GHOST_kEventDraggingExited: + window->clientToScreenIntern(mouseX, mouseY, mouseX, mouseY); pushEvent(new GHOST_EventDragnDrop( getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, NULL)); break; @@ -1331,6 +1332,8 @@ GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType return GHOST_kFailure; break; } + + window->clientToScreenIntern(mouseX, mouseY, mouseX, mouseY); pushEvent(new GHOST_EventDragnDrop( getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, eventData)); @@ -1779,7 +1782,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) NSString *characters; NSData *convertedCharacters; GHOST_TKey keyCode; - unsigned char ascii; NSString *charsIgnoringModifiers; window = m_windowManager->getWindowAssociatedWithOSWindow((void *)[event window]); @@ -1789,7 +1791,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) } char utf8_buf[6] = {'\0'}; - ascii = 0; switch ([event type]) { @@ -1809,7 +1810,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) kUCKeyActionUp); } - /* handling both unicode or ascii */ characters = [event characters]; if ([characters length] > 0) { convertedCharacters = [characters dataUsingEncoding:NSUTF8StringEncoding]; @@ -1835,41 +1835,31 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSEventModifierFlagCommand)) break; // Cmd-Q is directly handled by Cocoa - /* ascii is a subset of unicode */ - if (utf8_buf[0] && !utf8_buf[1]) { - ascii = utf8_buf[0]; - } - if ([event type] == NSEventTypeKeyDown) { pushEvent(new GHOST_EventKey([event timestamp] * 1000, GHOST_kEventKeyDown, window, keyCode, - ascii, - utf8_buf, - [event isARepeat])); + [event isARepeat], + utf8_buf)); #if 0 - printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n", + printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n", [event keyCode], [charsIgnoringModifiers length] > 0 ? [charsIgnoringModifiers characterAtIndex:0] : ' ', keyCode, - ascii, - ascii, utf8_buf); #endif } else { pushEvent(new GHOST_EventKey( - [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, 0, NULL, false)); + [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, false, NULL)); #if 0 - printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n", + printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n", [event keyCode], [charsIgnoringModifiers length] > 0 ? [charsIgnoringModifiers characterAtIndex:0] : ' ', keyCode, - ascii, - ascii, utf8_buf); #endif } diff --git a/intern/ghost/intern/GHOST_SystemHeadless.h b/intern/ghost/intern/GHOST_SystemHeadless.h new file mode 100644 index 00000000000..b02a82fc9eb --- /dev/null +++ b/intern/ghost/intern/GHOST_SystemHeadless.h @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup GHOST + * Declaration of GHOST_SystemHeadless class. + */ + +#pragma once + +#include "../GHOST_Types.h" +#include "GHOST_DisplayManagerNULL.h" +#include "GHOST_System.h" +#include "GHOST_WindowNULL.h" + +#ifdef __linux__ +# include "GHOST_ContextEGL.h" +#endif +#include "GHOST_ContextNone.h" + +class GHOST_WindowNULL; + +class GHOST_SystemHeadless : public GHOST_System { + public: + GHOST_SystemHeadless() : GHOST_System() + { /* nop */ + } + ~GHOST_SystemHeadless() override = default; + + bool processEvents(bool /*waitForEvent*/) override + { + return false; + } + bool setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) override + { + return 0; + } + GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys & /*keys*/) const override + { + return GHOST_kSuccess; + } + GHOST_TSuccess getButtons(GHOST_Buttons & /*buttons*/) const override + { + return GHOST_kSuccess; + } + char *getClipboard(bool /*selection*/) const override + { + return nullptr; + } + void putClipboard(const char * /*buffer*/, bool /*selection*/) const override + { /* nop */ + } + uint64_t getMilliSeconds() const override + { + return 0; + } + uint8_t getNumDisplays() const override + { + return uint8_t(1); + } + GHOST_TSuccess getCursorPosition(int32_t & /*x*/, int32_t & /*y*/) const override + { + return GHOST_kFailure; + } + GHOST_TSuccess setCursorPosition(int32_t /*x*/, int32_t /*y*/) override + { + return GHOST_kFailure; + } + void getMainDisplayDimensions(uint32_t & /*width*/, uint32_t & /*height*/) const override + { /* nop */ + } + void getAllDisplayDimensions(uint32_t & /*width*/, uint32_t & /*height*/) const override + { /* nop */ + } + GHOST_IContext *createOffscreenContext(GHOST_GLSettings /*glSettings*/) override + { +#ifdef __linux__ + GHOST_Context *context; + for (int minor = 6; minor >= 0; --minor) { + context = new GHOST_ContextEGL((GHOST_System *)this, + false, + EGLNativeWindowType(0), + EGLNativeDisplayType(EGL_DEFAULT_DISPLAY), + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + 4, + minor, + GHOST_OPENGL_EGL_CONTEXT_FLAGS, + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); + + if (context->initializeDrawingContext()) { + return context; + } + delete context; + context = nullptr; + } + + context = new GHOST_ContextEGL((GHOST_System *)this, + false, + EGLNativeWindowType(0), + EGLNativeDisplayType(EGL_DEFAULT_DISPLAY), + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + 3, + 3, + GHOST_OPENGL_EGL_CONTEXT_FLAGS, + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); + + if (context->initializeDrawingContext() != GHOST_kSuccess) { + delete context; + context = nullptr; + } + return context; +#else + return nullptr; +#endif + } + GHOST_TSuccess disposeContext(GHOST_IContext *context) override + { + delete context; + + return GHOST_kSuccess; + } + + GHOST_TSuccess init() override + { + GHOST_TSuccess success = GHOST_System::init(); + + if (success) { + m_displayManager = new GHOST_DisplayManagerNULL(); + + if (m_displayManager) { + return GHOST_kSuccess; + } + } + + return GHOST_kFailure; + } + + GHOST_IWindow *createWindow(const char *title, + int32_t left, + int32_t top, + uint32_t width, + uint32_t height, + GHOST_TWindowState state, + GHOST_TDrawingContextType type, + GHOST_GLSettings glSettings, + const bool /*exclusive*/, + const bool /*is_dialog*/, + const GHOST_IWindow *parentWindow) override + { + return new GHOST_WindowNULL(title, + left, + top, + width, + height, + state, + parentWindow, + type, + ((glSettings.flags & GHOST_glStereoVisual) != 0)); + } + + GHOST_IWindow *getWindowUnderCursor(int32_t /*x*/, int32_t /*y*/) override + { + return nullptr; + } +}; diff --git a/intern/ghost/intern/GHOST_SystemNULL.h b/intern/ghost/intern/GHOST_SystemNULL.h deleted file mode 100644 index 644eb1ba0a5..00000000000 --- a/intern/ghost/intern/GHOST_SystemNULL.h +++ /dev/null @@ -1,122 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup GHOST - * Declaration of GHOST_SystemNULL class. - */ - -#pragma once - -#include "../GHOST_Types.h" -#include "GHOST_DisplayManagerNULL.h" -#include "GHOST_System.h" -#include "GHOST_WindowNULL.h" - -class GHOST_WindowNULL; - -class GHOST_SystemNULL : public GHOST_System { - public: - GHOST_SystemNULL() : GHOST_System() - { /* nop */ - } - ~GHOST_SystemNULL() - { /* nop */ - } - bool processEvents(bool waitForEvent) - { - return false; - } - int setConsoleWindowState(GHOST_TConsoleWindowState action) - { - return 0; - } - GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const - { - return GHOST_kSuccess; - } - GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const - { - return GHOST_kSuccess; - } - char *getClipboard(bool selection) const - { - return nullptr; - } - void putClipboard(const char *buffer, bool selection) const - { /* nop */ - } - uint64_t getMilliSeconds() const - { - return 0; - } - uint8_t getNumDisplays() const - { - return uint8_t(1); - } - GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const - { - return GHOST_kFailure; - } - GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) - { - return GHOST_kFailure; - } - void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const - { /* nop */ - } - void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const - { /* nop */ - } - GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings) - { - return nullptr; - } - GHOST_TSuccess disposeContext(GHOST_IContext *context) - { - return GHOST_kFailure; - } - - GHOST_TSuccess init() - { - GHOST_TSuccess success = GHOST_System::init(); - - if (success) { - m_displayManager = new GHOST_DisplayManagerNULL(this); - - if (m_displayManager) { - return GHOST_kSuccess; - } - } - - return GHOST_kFailure; - } - - GHOST_IWindow *createWindow(const char *title, - int32_t left, - int32_t top, - uint32_t width, - uint32_t height, - GHOST_TWindowState state, - GHOST_TDrawingContextType type, - GHOST_GLSettings glSettings, - const bool exclusive, - const bool is_dialog, - const GHOST_IWindow *parentWindow) - { - return new GHOST_WindowNULL(this, - title, - left, - top, - width, - height, - state, - parentWindow, - type, - ((glSettings.flags & GHOST_glStereoVisual) != 0)); - } - - GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y) - { - return nullptr; - } -}; diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 36c912d8821..6d0b2b8aa55 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -5,6 +5,7 @@ */ #include <cassert> +#include <stdexcept> #include "GHOST_ContextSDL.h" #include "GHOST_SystemSDL.h" @@ -20,7 +21,7 @@ GHOST_SystemSDL::GHOST_SystemSDL() : GHOST_System() { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) { - printf("Error initializing SDL: %s\n", SDL_GetError()); + throw std::runtime_error("Error initializing SDL: " + std::string(SDL_GetError())); } SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); @@ -279,6 +280,141 @@ static GHOST_TKey convertSDLKey(SDL_Scancode key) } #undef GXMAP +static char convert_keyboard_event_to_ascii(const SDL_KeyboardEvent &sdl_sub_evt) +{ + SDL_Keycode sym = sdl_sub_evt.keysym.sym; + if (sym > 127) { + switch (sym) { + case SDLK_KP_DIVIDE: + sym = '/'; + break; + case SDLK_KP_MULTIPLY: + sym = '*'; + break; + case SDLK_KP_MINUS: + sym = '-'; + break; + case SDLK_KP_PLUS: + sym = '+'; + break; + case SDLK_KP_1: + sym = '1'; + break; + case SDLK_KP_2: + sym = '2'; + break; + case SDLK_KP_3: + sym = '3'; + break; + case SDLK_KP_4: + sym = '4'; + break; + case SDLK_KP_5: + sym = '5'; + break; + case SDLK_KP_6: + sym = '6'; + break; + case SDLK_KP_7: + sym = '7'; + break; + case SDLK_KP_8: + sym = '8'; + break; + case SDLK_KP_9: + sym = '9'; + break; + case SDLK_KP_0: + sym = '0'; + break; + case SDLK_KP_PERIOD: + sym = '.'; + break; + default: + sym = 0; + break; + } + } + else { + if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { + /* Weak US keyboard assumptions. */ + if (sym >= 'a' && sym <= ('a' + 32)) { + sym -= 32; + } + else { + switch (sym) { + case '`': + sym = '~'; + break; + case '1': + sym = '!'; + break; + case '2': + sym = '@'; + break; + case '3': + sym = '#'; + break; + case '4': + sym = '$'; + break; + case '5': + sym = '%'; + break; + case '6': + sym = '^'; + break; + case '7': + sym = '&'; + break; + case '8': + sym = '*'; + break; + case '9': + sym = '('; + break; + case '0': + sym = ')'; + break; + case '-': + sym = '_'; + break; + case '=': + sym = '+'; + break; + case '[': + sym = '{'; + break; + case ']': + sym = '}'; + break; + case '\\': + sym = '|'; + break; + case ';': + sym = ':'; + break; + case '\'': + sym = '"'; + break; + case ',': + sym = '<'; + break; + case '.': + sym = '>'; + break; + case '/': + sym = '?'; + break; + default: + break; + } + } + } + } + return (char)sym; +} + /** * Events don't always have valid windows, * but GHOST needs a window _always_. fallback to the GL window. @@ -410,7 +546,7 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONDOWN: { SDL_MouseButtonEvent &sdl_sub_evt = sdl_event->button; - GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft; + GHOST_TButton gbmask = GHOST_kButtonMaskLeft; GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp; @@ -454,9 +590,9 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) case SDL_KEYDOWN: case SDL_KEYUP: { SDL_KeyboardEvent &sdl_sub_evt = sdl_event->key; - SDL_Keycode sym = sdl_sub_evt.keysym.sym; GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; + const bool is_repeat = sdl_sub_evt.repeat != 0; GHOST_WindowSDL *window = findGhostWindow( SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID)); @@ -465,138 +601,15 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) GHOST_TKey gkey = convertSDLKey(sdl_sub_evt.keysym.scancode); /* NOTE: the `sdl_sub_evt.keysym.sym` is truncated, * for unicode support ghost has to be modified. */ - // printf("%d\n", sym); - if (sym > 127) { - switch (sym) { - case SDLK_KP_DIVIDE: - sym = '/'; - break; - case SDLK_KP_MULTIPLY: - sym = '*'; - break; - case SDLK_KP_MINUS: - sym = '-'; - break; - case SDLK_KP_PLUS: - sym = '+'; - break; - case SDLK_KP_1: - sym = '1'; - break; - case SDLK_KP_2: - sym = '2'; - break; - case SDLK_KP_3: - sym = '3'; - break; - case SDLK_KP_4: - sym = '4'; - break; - case SDLK_KP_5: - sym = '5'; - break; - case SDLK_KP_6: - sym = '6'; - break; - case SDLK_KP_7: - sym = '7'; - break; - case SDLK_KP_8: - sym = '8'; - break; - case SDLK_KP_9: - sym = '9'; - break; - case SDLK_KP_0: - sym = '0'; - break; - case SDLK_KP_PERIOD: - sym = '.'; - break; - default: - sym = 0; - break; - } - } - else { - if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { - /* lame US keyboard assumptions */ - if (sym >= 'a' && sym <= ('a' + 32)) { - sym -= 32; - } - else { - switch (sym) { - case '`': - sym = '~'; - break; - case '1': - sym = '!'; - break; - case '2': - sym = '@'; - break; - case '3': - sym = '#'; - break; - case '4': - sym = '$'; - break; - case '5': - sym = '%'; - break; - case '6': - sym = '^'; - break; - case '7': - sym = '&'; - break; - case '8': - sym = '*'; - break; - case '9': - sym = '('; - break; - case '0': - sym = ')'; - break; - case '-': - sym = '_'; - break; - case '=': - sym = '+'; - break; - case '[': - sym = '{'; - break; - case ']': - sym = '}'; - break; - case '\\': - sym = '|'; - break; - case ';': - sym = ':'; - break; - case '\'': - sym = '"'; - break; - case ',': - sym = '<'; - break; - case '.': - sym = '>'; - break; - case '/': - sym = '?'; - break; - default: - break; - } - } - } + + /* TODO(@campbellbarton): support full unicode, SDL supports this but it needs to be + * explicitly enabled via #SDL_StartTextInput which GHOST would have to wrap. */ + char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'}; + if (type == GHOST_kEventKeyDown) { + utf8_buf[0] = convert_keyboard_event_to_ascii(sdl_sub_evt); } - g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, nullptr, false); + g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf); break; } } diff --git a/intern/ghost/intern/GHOST_SystemSDL.h b/intern/ghost/intern/GHOST_SystemSDL.h index aefea5eda34..bee277ba674 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.h +++ b/intern/ghost/intern/GHOST_SystemSDL.h @@ -33,9 +33,9 @@ class GHOST_SystemSDL : public GHOST_System { bool processEvents(bool waitForEvent); - int setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) + bool setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) { - return 0; + return false; } GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const; diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 2b0abd2cc41..4c663e98824 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -11,12 +11,25 @@ #include "GHOST_EventDragnDrop.h" #include "GHOST_EventKey.h" #include "GHOST_EventWheel.h" +#include "GHOST_PathUtils.h" #include "GHOST_TimerManager.h" +#include "GHOST_WaylandUtils.h" #include "GHOST_WindowManager.h" +#include "GHOST_utildefines.h" #include "GHOST_ContextEGL.h" -#include <EGL/egl.h> +#ifdef WITH_INPUT_NDOF +# include "GHOST_NDOFManagerUnix.h" +#endif + +#ifdef WITH_GHOST_WAYLAND_DYNLOAD +# include <wayland_dynload_API.h> /* For `ghost_wl_dynload_libraries`. */ +#endif + +#ifdef WITH_GHOST_WAYLAND_DYNLOAD +# include <wayland_dynload_egl.h> +#endif #include <wayland-egl.h> #include <algorithm> @@ -26,15 +39,21 @@ #include <unordered_map> #include <unordered_set> -#include "GHOST_WaylandCursorSettings.h" -#include <pointer-constraints-client-protocol.h> -#include <relative-pointer-client-protocol.h> -#include <tablet-client-protocol.h> +#ifdef WITH_GHOST_WAYLAND_DYNLOAD +# include <wayland_dynload_cursor.h> +#endif #include <wayland-cursor.h> -#include <xdg-output-client-protocol.h> + +#include "GHOST_WaylandCursorSettings.h" #include <xkbcommon/xkbcommon.h> +/* Generated by `wayland-scanner`. */ +#include <pointer-constraints-unstable-v1-client-protocol.h> +#include <relative-pointer-unstable-v1-client-protocol.h> +#include <tablet-unstable-v2-client-protocol.h> +#include <xdg-output-unstable-v1-client-protocol.h> + #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> @@ -42,16 +61,45 @@ #include <cstring> #include <mutex> -static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface); +/* Logging, use `ghost.wl.*` prefix. */ +#include "CLG_log.h" -/* -------------------------------------------------------------------- */ -/** \name Private Types & Defines - * \{ */ +static void keyboard_handle_key_repeat_cancel(struct GWL_Seat *seat); +static void output_handle_done(void *data, struct wl_output *wl_output); + +/** + * GNOME (mutter 42.2 had a bug with confine not respecting scale - Hi-DPI), See: T98793. + * Even though this has been fixed, at time of writing it's not yet in a release. + * Workaround the problem by implementing confine with a software cursor. + * While this isn't ideal, it's not adding a lot of overhead as software + * cursors are already used for warping (which WAYLAND doesn't support). + */ +#define USE_GNOME_CONFINE_HACK /** + * Always use software confine (not just in GNOME). + * Useful for developing with compositors that don't need this workaround. + */ +// #define USE_GNOME_CONFINE_HACK_ALWAYS_ON + +#ifdef USE_GNOME_CONFINE_HACK +static bool use_gnome_confine_hack = false; +#endif + +#define XKB_STATE_MODS_ALL \ + (enum xkb_state_component)(XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | \ + XKB_STATE_MODS_LOCKED | XKB_STATE_MODS_EFFECTIVE) + +/* -------------------------------------------------------------------- */ +/** \name Inline Event Codes + * * Selected input event code defines from `linux/input-event-codes.h` * We include some of the button input event codes here, since the header is - * only available in more recent kernel versions. The event codes are used to + * only available in more recent kernel versions. + * \{ */ + +/** + * The event codes are used to * to differentiate from which mouse button an event comes from. */ #define BTN_LEFT 0x110 @@ -64,30 +112,50 @@ static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface); // #define BTN_TASK 0x117 /* UNUSED. */ /** - * Tablet events, also from `linux/input-event-codes.h`. + * Tablet events. */ #define BTN_STYLUS 0x14b /* Use as right-mouse. */ #define BTN_STYLUS2 0x14c /* Use as middle-mouse. */ /* NOTE(@campbellbarton): Map to an additional button (not sure which hardware uses this). */ #define BTN_STYLUS3 0x149 -struct buffer_t { - void *data = nullptr; - size_t size = 0; -}; +/** + * Keyboard scan-codes. + */ +#define KEY_GRAVE 41 -struct cursor_t { +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Private Types & Defines + * \{ */ + +/** + * From XKB internals, use for converting a scan-code from WAYLAND to a #xkb_keycode_t. + * Ideally this wouldn't need a local define. + */ +#define EVDEV_OFFSET 8 + +struct GWL_Cursor { bool visible = false; + /** + * When false, hide the hardware cursor, while the cursor is still considered to be `visible`, + * since the grab-mode determines the state of the software cursor, + * this may change - removing the need for a software cursor and in this case it's important + * the hardware cursor is used. + */ + bool is_hardware = true; + bool is_custom = false; struct wl_surface *wl_surface = nullptr; struct wl_buffer *wl_buffer = nullptr; struct wl_cursor_image wl_image = {0}; struct wl_cursor_theme *wl_theme = nullptr; - struct buffer_t *file_buffer = nullptr; + void *custom_data = nullptr; + size_t custom_data_size = 0; int size = 0; std::string theme_name; - /** Outputs on which the cursor is visible. */ - std::unordered_set<const output_t *> outputs; - int scale = 1; + + int custom_scale = 1; }; /** @@ -95,76 +163,171 @@ struct cursor_t { * WAYLAND exposes tools via #zwp_tablet_tool_v2. * Since are no API's to access properties of the tool, store the values here. */ -struct tablet_tool_input_t { - struct input_t *input = nullptr; - struct wl_surface *cursor_surface = nullptr; +struct GWL_TabletTool { + struct GWL_Seat *seat = nullptr; + struct wl_surface *wl_surface_cursor = nullptr; + /** Used to delay clearing tablet focused wl_surface until the frame is handled. */ + bool proximity = false; GHOST_TabletData data = GHOST_TABLET_DATA_NONE; }; -struct data_offer_t { +struct GWL_DataOffer { std::unordered_set<std::string> types; uint32_t source_actions = 0; uint32_t dnd_action = 0; struct wl_data_offer *id = nullptr; std::atomic<bool> in_use = false; struct { - /** Compatible with #input_t.xy coordinates. */ + /** Compatible with #GWL_Seat.xy coordinates. */ wl_fixed_t xy[2] = {0, 0}; } dnd; }; -struct data_source_t { +struct GWL_DataSource { struct wl_data_source *data_source = nullptr; char *buffer_out = nullptr; }; -struct key_repeat_payload_t { - GHOST_SystemWayland *system = nullptr; - GHOST_IWindow *window = nullptr; - GHOST_TEventKeyData key_data = {GHOST_kKeyUnknown}; -}; - -struct input_t { - GHOST_SystemWayland *system = nullptr; - - std::string name; - struct wl_seat *wl_seat = nullptr; - struct wl_pointer *wl_pointer = nullptr; - struct wl_keyboard *wl_keyboard = nullptr; - struct zwp_tablet_seat_v2 *tablet_seat = nullptr; +/** + * Data used to implement client-side key-repeat. + * + * \note it's important not to store the target window here + * as it can be closed while the key is repeating, + * instead use the focused keyboard from #GWL_Seat which is cleared when windows are closed. + * Therefor keyboard events must always check the window has not been cleared. + */ +struct GWL_KeyRepeatPlayload { + struct GWL_Seat *seat = nullptr; - /** All currently active tablet tools (needed for changing the cursor). */ - std::unordered_set<zwp_tablet_tool_v2 *> tablet_tools; + xkb_keycode_t key_code; - uint32_t pointer_serial = 0; - uint32_t tablet_serial = 0; + /** + * Don't cache the `utf8_buf` as this changes based on modifiers which may be pressed + * while key repeat is enabled. + */ + struct { + GHOST_TKey gkey; + } key_data; +}; - /** Use to check if the last cursor input was tablet or pointer. */ - uint32_t cursor_serial = 0; +/** Internal variables used to track grab-state. */ +struct GWL_SeatStateGrab { + bool use_lock = false; + bool use_confine = false; +}; +/** + * State of the pointing device (tablet or mouse). + */ +struct GWL_SeatStatePointer { /** - * High precision mouse coordinates (pointer or tablet). + * High precision coordinates. * + * Mapping to pixels requires the window scale. * The following example converts these values to screen coordinates. * \code{.cc} * const wl_fixed_t scale = win->scale(); * const int event_xy[2] = { - * wl_fixed_to_int(scale * input->xy[0]), - * wl_fixed_to_int(scale * input->xy[1]), + * wl_fixed_to_int(scale * seat_state_pointer->xy[0]), + * wl_fixed_to_int(scale * seat_state_pointer->xy[1]), * }; * \endcode */ wl_fixed_t xy[2] = {0, 0}; + + /** Outputs on which the cursor is visible. */ + std::unordered_set<const GWL_Output *> outputs; + + int theme_scale = 1; + + /** The serial of the last used pointer or tablet. */ + uint32_t serial = 0; + + /** + * The wl_surface last used with this pointing device + * (events with this pointing device will be sent here). + */ + struct wl_surface *wl_surface = nullptr; + GHOST_Buttons buttons = GHOST_Buttons(); - struct cursor_t cursor; +}; + +/** + * State of the keyboard (in #GWL_Seat). + */ +struct GWL_SeatStateKeyboard { + /** The serial of the last used pointer or tablet. */ + uint32_t serial = 0; + + /** + * The wl_surface last used with this pointing device + * (events with this pointing device will be sent here). + */ + struct wl_surface *wl_surface = nullptr; +}; + +struct GWL_Seat { + GHOST_SystemWayland *system = nullptr; + + std::string name; + struct wl_seat *wl_seat = nullptr; + struct wl_pointer *wl_pointer = nullptr; + struct wl_keyboard *wl_keyboard = nullptr; + struct zwp_tablet_seat_v2 *tablet_seat = nullptr; + + /** All currently active tablet tools (needed for changing the cursor). */ + std::unordered_set<zwp_tablet_tool_v2 *> tablet_tools; + + /** Use to check if the last cursor input was tablet or pointer. */ + uint32_t cursor_source_serial = 0; + + GWL_SeatStatePointer pointer; + + /** Mostly this can be interchanged with `pointer` however it can't be locked/confined. */ + GWL_SeatStatePointer tablet; + + GWL_SeatStateKeyboard keyboard; + +#ifdef USE_GNOME_CONFINE_HACK + bool use_pointer_software_confine = false; +#endif + /** The cursor location (in pixel-space) when hidden grab started (#GHOST_kGrabHide). */ + wl_fixed_t grab_lock_xy[2] = {0, 0}; + + struct GWL_Cursor cursor; struct zwp_relative_pointer_v1 *relative_pointer = nullptr; struct zwp_locked_pointer_v1 *locked_pointer = nullptr; struct zwp_confined_pointer_v1 *confined_pointer = nullptr; struct xkb_context *xkb_context = nullptr; + struct xkb_state *xkb_state = nullptr; + /** + * Keep a state with no modifiers active, use for symbol lookups. + */ + struct xkb_state *xkb_state_empty = nullptr; + /** + * Keep a state with number-lock enabled, use to access predictable key-pad symbols. + * If number-lock is not supported by the key-map, this is set to NULL. + */ + struct xkb_state *xkb_state_empty_with_numlock = nullptr; + + /** + * Cache result of `xkb_keymap_mod_get_index` + * so every time a modifier is accessed a string lookup isn't required. + * Be sure to check for #XKB_MOD_INVALID before using. + */ + struct { + xkb_mod_index_t shift; /* #XKB_MOD_NAME_SHIFT */ + xkb_mod_index_t caps; /* #XKB_MOD_NAME_CAPS */ + xkb_mod_index_t ctrl; /* #XKB_MOD_NAME_CTRL */ + xkb_mod_index_t alt; /* #XKB_MOD_NAME_ALT */ + xkb_mod_index_t num; /* #XKB_MOD_NAME_NUM */ + xkb_mod_index_t logo; /* #XKB_MOD_NAME_LOGO */ + } xkb_keymap_mod_index; + struct { /** Key repetition in character per second. */ int32_t rate = 0; @@ -174,51 +337,50 @@ struct input_t { GHOST_ITimerTask *timer = nullptr; } key_repeat; - struct wl_surface *focus_tablet = nullptr; - struct wl_surface *focus_pointer = nullptr; - struct wl_surface *focus_keyboard = nullptr; - struct wl_surface *focus_dnd = nullptr; + struct wl_surface *wl_surface_focus_dnd = nullptr; struct wl_data_device *data_device = nullptr; /** Drag & Drop. */ - struct data_offer_t *data_offer_dnd = nullptr; + struct GWL_DataOffer *data_offer_dnd = nullptr; std::mutex data_offer_dnd_mutex; /** Copy & Paste. */ - struct data_offer_t *data_offer_copy_paste = nullptr; + struct GWL_DataOffer *data_offer_copy_paste = nullptr; std::mutex data_offer_copy_paste_mutex; - struct data_source_t *data_source = nullptr; + struct GWL_DataSource *data_source = nullptr; std::mutex data_source_mutex; /** Last device that was active. */ uint32_t data_source_serial = 0; }; -struct display_t { +struct GWL_Display { GHOST_SystemWayland *system = nullptr; struct wl_display *display = nullptr; struct wl_compositor *compositor = nullptr; + +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + struct libdecor *decor_context = nullptr; +#else struct xdg_wm_base *xdg_shell = nullptr; struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr; +#endif + struct zxdg_output_manager_v1 *xdg_output_manager = nullptr; struct wl_shm *shm = nullptr; - std::vector<output_t *> outputs; - std::vector<input_t *> inputs; - struct { - std::string theme; - int size = 0; - } cursor; + std::vector<GWL_Output *> outputs; + std::vector<GWL_Seat *> seats; + struct wl_data_device_manager *data_device_manager = nullptr; struct zwp_tablet_manager_v2 *tablet_manager = nullptr; struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr; struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr; - - std::vector<struct wl_surface *> os_surfaces; - std::vector<struct wl_egl_window *> os_egl_windows; }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ @@ -247,7 +409,31 @@ static void ghost_wayland_log_handler(const char *msg, va_list arg) } } -static void display_destroy(display_t *d) +static GWL_SeatStatePointer *seat_state_pointer_active(GWL_Seat *seat) +{ + if (seat->pointer.serial == seat->cursor_source_serial) { + return &seat->pointer; + } + if (seat->tablet.serial == seat->cursor_source_serial) { + return &seat->tablet; + } + return nullptr; +} + +static GWL_SeatStatePointer *seat_state_pointer_from_cursor_surface(GWL_Seat *seat, + const wl_surface *wl_surface) +{ + if (ghost_wl_surface_own_cursor_pointer(wl_surface)) { + return &seat->pointer; + } + if (ghost_wl_surface_own_cursor_tablet(wl_surface)) { + return &seat->tablet; + } + GHOST_ASSERT(0, "Surface found without pointer/tablet tag"); + return nullptr; +} + +static void display_destroy(GWL_Display *d) { if (d->data_device_manager) { wl_data_device_manager_destroy(d->data_device_manager); @@ -257,76 +443,77 @@ static void display_destroy(display_t *d) zwp_tablet_manager_v2_destroy(d->tablet_manager); } - for (output_t *output : d->outputs) { + for (GWL_Output *output : d->outputs) { wl_output_destroy(output->wl_output); delete output; } - for (input_t *input : d->inputs) { + for (GWL_Seat *seat : d->seats) { /* First handle members that require locking. * While highly unlikely, it's possible they are being used while this function runs. */ { - std::lock_guard lock{input->data_source_mutex}; - if (input->data_source) { - free(input->data_source->buffer_out); - if (input->data_source->data_source) { - wl_data_source_destroy(input->data_source->data_source); + std::lock_guard lock{seat->data_source_mutex}; + if (seat->data_source) { + free(seat->data_source->buffer_out); + if (seat->data_source->data_source) { + wl_data_source_destroy(seat->data_source->data_source); } - delete input->data_source; + delete seat->data_source; } } { - std::lock_guard lock{input->data_offer_dnd_mutex}; - if (input->data_offer_dnd) { - wl_data_offer_destroy(input->data_offer_dnd->id); - delete input->data_offer_dnd; + std::lock_guard lock{seat->data_offer_dnd_mutex}; + if (seat->data_offer_dnd) { + wl_data_offer_destroy(seat->data_offer_dnd->id); + delete seat->data_offer_dnd; } } { - std::lock_guard lock{input->data_offer_copy_paste_mutex}; - if (input->data_offer_copy_paste) { - wl_data_offer_destroy(input->data_offer_copy_paste->id); - delete input->data_offer_copy_paste; + std::lock_guard lock{seat->data_offer_copy_paste_mutex}; + if (seat->data_offer_copy_paste) { + wl_data_offer_destroy(seat->data_offer_copy_paste->id); + delete seat->data_offer_copy_paste; } } - if (input->data_device) { - wl_data_device_release(input->data_device); + if (seat->data_device) { + wl_data_device_release(seat->data_device); } - if (input->wl_pointer) { - if (input->cursor.file_buffer) { - munmap(input->cursor.file_buffer->data, input->cursor.file_buffer->size); - delete input->cursor.file_buffer; - } - if (input->cursor.wl_surface) { - wl_surface_destroy(input->cursor.wl_surface); + + if (seat->cursor.custom_data) { + munmap(seat->cursor.custom_data, seat->cursor.custom_data_size); + } + + if (seat->wl_pointer) { + if (seat->cursor.wl_surface) { + wl_surface_destroy(seat->cursor.wl_surface); } - if (input->cursor.wl_theme) { - wl_cursor_theme_destroy(input->cursor.wl_theme); + if (seat->cursor.wl_theme) { + wl_cursor_theme_destroy(seat->cursor.wl_theme); } - if (input->wl_pointer) { - wl_pointer_destroy(input->wl_pointer); + if (seat->wl_pointer) { + wl_pointer_destroy(seat->wl_pointer); } } - if (input->wl_keyboard) { - if (input->key_repeat.timer) { - delete static_cast<key_repeat_payload_t *>(input->key_repeat.timer->getUserData()); - input->system->removeTimer(input->key_repeat.timer); - input->key_repeat.timer = nullptr; + if (seat->wl_keyboard) { + if (seat->key_repeat.timer) { + keyboard_handle_key_repeat_cancel(seat); } - wl_keyboard_destroy(input->wl_keyboard); - } - if (input->xkb_state) { - xkb_state_unref(input->xkb_state); + wl_keyboard_destroy(seat->wl_keyboard); } - if (input->xkb_context) { - xkb_context_unref(input->xkb_context); - } - wl_seat_destroy(input->wl_seat); - delete input; + + /* Un-referencing checks for NULL case. */ + xkb_state_unref(seat->xkb_state); + xkb_state_unref(seat->xkb_state_empty); + xkb_state_unref(seat->xkb_state_empty_with_numlock); + + xkb_context_unref(seat->xkb_context); + + wl_seat_destroy(seat->wl_seat); + delete seat; } if (d->shm) { @@ -341,18 +528,15 @@ static void display_destroy(display_t *d) zwp_pointer_constraints_v1_destroy(d->pointer_constraints); } - for (wl_egl_window *os_egl_window : d->os_egl_windows) { - wl_egl_window_destroy(os_egl_window); - } - - for (wl_surface *os_surface : d->os_surfaces) { - wl_surface_destroy(os_surface); - } - if (d->compositor) { wl_compositor_destroy(d->compositor); } +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + if (d->decor_context) { + libdecor_unref(d->decor_context); + } +#else if (d->xdg_decoration_manager) { zxdg_decoration_manager_v1_destroy(d->xdg_decoration_manager); } @@ -360,6 +544,7 @@ static void display_destroy(display_t *d) if (d->xdg_shell) { xdg_wm_base_destroy(d->xdg_shell); } +#endif /* !WITH_GHOST_WAYLAND_LIBDECOR */ if (eglGetDisplay) { ::eglTerminate(eglGetDisplay(EGLNativeDisplayType(d->display))); @@ -372,7 +557,7 @@ static void display_destroy(display_t *d) delete d; } -static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym) +static GHOST_TKey xkb_map_gkey(const xkb_keysym_t sym) { GHOST_TKey gkey; @@ -463,8 +648,7 @@ static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym) GXMAP(gkey, XKB_KEY_XF86AudioPrev, GHOST_kKeyMediaFirst); GXMAP(gkey, XKB_KEY_XF86AudioNext, GHOST_kKeyMediaLast); default: - GHOST_PRINT("unhandled key: " << std::hex << std::showbase << sym << std::dec << " (" - << sym << ")" << std::endl); + /* Rely on #xkb_map_gkey_or_scan_code to report when no key can be found. */ gkey = GHOST_kKeyUnknown; } #undef GXMAP @@ -473,6 +657,41 @@ static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym) return gkey; } +/** + * Map the keys using the users keyboard layout, if that fails fall back to physical locations. + * This is needed so users with keyboard layouts that don't expose #GHOST_kKeyAccentGrave + * (typically the key under escape) in the layout can still use this key in keyboard shortcuts. + * + * \param key: The key's scan-code, compatible with values in `linux/input-event-codes.h`. + */ +static GHOST_TKey xkb_map_gkey_or_scan_code(const xkb_keysym_t sym, const uint32_t key) +{ + GHOST_TKey gkey = xkb_map_gkey(sym); + + if (UNLIKELY(gkey == GHOST_kKeyUnknown)) { + /* Fall back to physical location for keys that would otherwise do nothing. */ + switch (key) { + case KEY_GRAVE: { + gkey = GHOST_kKeyAccentGrave; + break; + } + default: { + GHOST_PRINT( + /* Key-code. */ + "unhandled key: " << std::hex << std::showbase << sym << /* Hex. */ + std::dec << " (" << sym << "), " << /* Decimal. */ + /* Scan-code. */ + "scan-code: " << std::hex << std::showbase << key << /* Hex. */ + std::dec << " (" << key << ")" << /* Decimal. */ + std::endl); + break; + } + } + } + + return gkey; +} + static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wl_tablet_tool_type) { switch (wl_tablet_tool_type) { @@ -496,12 +715,12 @@ static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wl_ta static const int default_cursor_size = 24; -static const std::unordered_map<GHOST_TStandardCursor, std::string> cursors = { +static const std::unordered_map<GHOST_TStandardCursor, const char *> cursors = { {GHOST_kStandardCursorDefault, "left_ptr"}, {GHOST_kStandardCursorRightArrow, "right_ptr"}, {GHOST_kStandardCursorLeftArrow, "left_ptr"}, {GHOST_kStandardCursorInfo, ""}, - {GHOST_kStandardCursorDestroy, ""}, + {GHOST_kStandardCursorDestroy, "pirate"}, {GHOST_kStandardCursorHelp, "question_arrow"}, {GHOST_kStandardCursorWait, "watch"}, {GHOST_kStandardCursorText, "xterm"}, @@ -509,21 +728,21 @@ static const std::unordered_map<GHOST_TStandardCursor, std::string> cursors = { {GHOST_kStandardCursorCrosshairA, ""}, {GHOST_kStandardCursorCrosshairB, ""}, {GHOST_kStandardCursorCrosshairC, ""}, - {GHOST_kStandardCursorPencil, ""}, + {GHOST_kStandardCursorPencil, "pencil"}, {GHOST_kStandardCursorUpArrow, "sb_up_arrow"}, {GHOST_kStandardCursorDownArrow, "sb_down_arrow"}, - {GHOST_kStandardCursorVerticalSplit, ""}, - {GHOST_kStandardCursorHorizontalSplit, ""}, + {GHOST_kStandardCursorVerticalSplit, "split_v"}, + {GHOST_kStandardCursorHorizontalSplit, "split_h"}, {GHOST_kStandardCursorEraser, ""}, {GHOST_kStandardCursorKnife, ""}, - {GHOST_kStandardCursorEyedropper, ""}, - {GHOST_kStandardCursorZoomIn, ""}, - {GHOST_kStandardCursorZoomOut, ""}, + {GHOST_kStandardCursorEyedropper, "color-picker"}, + {GHOST_kStandardCursorZoomIn, "zoom-in"}, + {GHOST_kStandardCursorZoomOut, "zoom-out"}, {GHOST_kStandardCursorMove, "move"}, - {GHOST_kStandardCursorNSEWScroll, ""}, - {GHOST_kStandardCursorNSScroll, ""}, - {GHOST_kStandardCursorEWScroll, ""}, - {GHOST_kStandardCursorStop, ""}, + {GHOST_kStandardCursorNSEWScroll, "size_all"}, /* Not an exact match. */ + {GHOST_kStandardCursorNSScroll, "size_ver"}, /* Not an exact match. */ + {GHOST_kStandardCursorEWScroll, "size_hor"}, /* Not an exact match. */ + {GHOST_kStandardCursorStop, "not-allowed"}, {GHOST_kStandardCursorUpDown, "sb_v_double_arrow"}, {GHOST_kStandardCursorLeftRight, "sb_h_double_arrow"}, {GHOST_kStandardCursorTopSide, "top_side"}, @@ -562,6 +781,84 @@ static const std::vector<std::string> mime_send = { "text/plain", }; +static int memfd_create_sealed(const char *name) +{ +#ifdef HAVE_MEMFD_CREATE + const int fd = memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) { + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + } + return fd; +#else /* HAVE_MEMFD_CREATE */ + char *path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + char *tmpname; + asprintf(&tmpname, "%s/%s-XXXXXX", path, name); + const int fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) { + unlink(tmpname); + } + free(tmpname); + return fd; +#endif /* !HAVE_MEMFD_CREATE */ +} + +static size_t ghost_wl_shm_format_as_size(enum wl_shm_format format) +{ + switch (format) { + case WL_SHM_FORMAT_ARGB8888: { + return 4; + } + default: { + /* Support other formats as needed. */ + GHOST_ASSERT(0, "Unexpected format passed in!"); + return 4; + } + } +} + +/** + * Return a #wl_buffer, ready to have it's data filled in or NULL in case of failure. + * The caller is responsible for calling `unmap(buffer_data, buffer_size)`. + * + * \param r_buffer_data: The buffer to be filled. + * \param r_buffer_data_size: The size of `r_buffer_data` in bytes. + */ +static wl_buffer *ghost_wl_buffer_create_for_image(struct wl_shm *shm, + const int32_t size_xy[2], + enum wl_shm_format format, + void **r_buffer_data, + size_t *r_buffer_data_size) +{ + const int fd = memfd_create_sealed("ghost-wl-buffer"); + wl_buffer *buffer = nullptr; + if (fd >= 0) { + const int32_t buffer_stride = size_xy[0] * ghost_wl_shm_format_as_size(format); + const int32_t buffer_size = buffer_stride * size_xy[1]; + if (posix_fallocate(fd, 0, buffer_size) == 0) { + void *buffer_data = mmap(nullptr, buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (buffer_data != MAP_FAILED) { + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, buffer_size); + buffer = wl_shm_pool_create_buffer(pool, 0, UNPACK2(size_xy), buffer_stride, format); + wl_shm_pool_destroy(pool); + if (buffer) { + *r_buffer_data = buffer_data; + *r_buffer_data_size = (size_t)buffer_size; + } + else { + /* Highly unlikely. */ + munmap(buffer_data, buffer_size); + } + } + } + close(fd); + } + return buffer; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -571,70 +868,110 @@ static const std::vector<std::string> mime_send = { * an event is received from the compositor. * \{ */ +static CLG_LogRef LOG_WL_RELATIVE_POINTER = {"ghost.wl.handle.relative_pointer"}; +#define LOG (&LOG_WL_RELATIVE_POINTER) + +/** + * The caller is responsible for setting the value of `seat->xy`. + */ +static void relative_pointer_handle_relative_motion_impl(GWL_Seat *seat, + GHOST_WindowWayland *win, + const wl_fixed_t xy[2]) +{ + const wl_fixed_t scale = win->scale(); + + seat->pointer.xy[0] = xy[0]; + seat->pointer.xy[1] = xy[1]; + +#ifdef USE_GNOME_CONFINE_HACK + if (seat->use_pointer_software_confine) { + GHOST_Rect bounds; + win->getClientBounds(bounds); + /* Needed or the cursor is considered outside the window and doesn't restore the location. */ + bounds.m_r -= 1; + bounds.m_b -= 1; + + bounds.m_l = wl_fixed_from_int(bounds.m_l) / scale; + bounds.m_t = wl_fixed_from_int(bounds.m_t) / scale; + bounds.m_r = wl_fixed_from_int(bounds.m_r) / scale; + bounds.m_b = wl_fixed_from_int(bounds.m_b) / scale; + bounds.clampPoint(UNPACK2(seat->pointer.xy)); + } +#endif + seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(), + GHOST_kEventCursorMove, + win, + wl_fixed_to_int(scale * seat->pointer.xy[0]), + wl_fixed_to_int(scale * seat->pointer.xy[1]), + GHOST_TABLET_DATA_NONE)); +} + static void relative_pointer_handle_relative_motion( void *data, struct zwp_relative_pointer_v1 * /*zwp_relative_pointer_v1*/, - uint32_t /*utime_hi*/, - uint32_t /*utime_lo*/, - wl_fixed_t dx, - wl_fixed_t dy, - wl_fixed_t /*dx_unaccel*/, - wl_fixed_t /*dy_unaccel*/) -{ - input_t *input = static_cast<input_t *>(data); - GHOST_WindowWayland *win = window_from_surface(input->focus_pointer); - if (!win) { - return; + const uint32_t /*utime_hi*/, + const uint32_t /*utime_lo*/, + const wl_fixed_t dx, + const wl_fixed_t dy, + const wl_fixed_t /*dx_unaccel*/, + const wl_fixed_t /*dy_unaccel*/) +{ + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) { + CLOG_INFO(LOG, 2, "relative_motion"); + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + const wl_fixed_t scale = win->scale(); + const wl_fixed_t xy_next[2] = { + seat->pointer.xy[0] + (dx / scale), + seat->pointer.xy[1] + (dy / scale), + }; + relative_pointer_handle_relative_motion_impl(seat, win, xy_next); + } + else { + CLOG_INFO(LOG, 2, "relative_motion (skipped)"); } - const wl_fixed_t scale = win->scale(); - input->xy[0] += dx / scale; - input->xy[1] += dy / scale; - - input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), - GHOST_kEventCursorMove, - win, - wl_fixed_to_int(scale * input->xy[0]), - wl_fixed_to_int(scale * input->xy[1]), - GHOST_TABLET_DATA_NONE)); } static const zwp_relative_pointer_v1_listener relative_pointer_listener = { relative_pointer_handle_relative_motion, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Data Source), #wl_data_source_listener * \{ */ -static void dnd_events(const input_t *const input, const GHOST_TEventType event) +static CLG_LogRef LOG_WL_DATA_SOURCE = {"ghost.wl.handle.data_source"}; +#define LOG (&LOG_WL_DATA_SOURCE) + +static void dnd_events(const GWL_Seat *const seat, const GHOST_TEventType event) { - /* NOTE: `input->data_offer_dnd_mutex` must already be locked. */ - const uint64_t time = input->system->getMilliSeconds(); - GHOST_WindowWayland *const win = static_cast<GHOST_WindowWayland *>( - wl_surface_get_user_data(input->focus_dnd)); - if (!win) { - return; - } - const wl_fixed_t scale = win->scale(); - const int event_xy[2] = { - wl_fixed_to_int(scale * input->data_offer_dnd->dnd.xy[0]), - wl_fixed_to_int(scale * input->data_offer_dnd->dnd.xy[1]), - }; + /* NOTE: `seat->data_offer_dnd_mutex` must already be locked. */ + if (wl_surface *wl_surface_focus = seat->wl_surface_focus_dnd) { + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + const wl_fixed_t scale = win->scale(); + const int event_xy[2] = { + wl_fixed_to_int(scale * seat->data_offer_dnd->dnd.xy[0]), + wl_fixed_to_int(scale * seat->data_offer_dnd->dnd.xy[1]), + }; - for (const std::string &type : mime_preference_order) { - input->system->pushEvent(new GHOST_EventDragnDrop( - time, event, mime_dnd.at(type), win, event_xy[0], event_xy[1], nullptr)); + const uint64_t time = seat->system->getMilliSeconds(); + for (const std::string &type : mime_preference_order) { + seat->system->pushEvent(new GHOST_EventDragnDrop( + time, event, mime_dnd.at(type), win, UNPACK2(event_xy), nullptr)); + } } } -static std::string read_pipe(data_offer_t *data_offer, +static std::string read_pipe(GWL_DataOffer *data_offer, const std::string mime_receive, std::mutex *mutex) { int pipefd[2]; - if (pipe(pipefd) != 0) { + if (UNLIKELY(pipe(pipefd) != 0)) { return {}; } wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]); @@ -668,18 +1005,20 @@ static void data_source_handle_target(void * /*data*/, struct wl_data_source * /*wl_data_source*/, const char * /*mime_type*/) { - /* pass */ + CLOG_INFO(LOG, 2, "target"); } static void data_source_handle_send(void *data, struct wl_data_source * /*wl_data_source*/, const char * /*mime_type*/, - int32_t fd) + const int32_t fd) { - input_t *input = static_cast<input_t *>(data); - std::lock_guard lock{input->data_source_mutex}; + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + std::lock_guard lock{seat->data_source_mutex}; + + CLOG_INFO(LOG, 2, "send"); - const char *const buffer = input->data_source->buffer_out; + const char *const buffer = seat->data_source->buffer_out; if (write(fd, buffer, strlen(buffer)) < 0) { GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl); } @@ -688,6 +1027,7 @@ static void data_source_handle_send(void *data, static void data_source_handle_cancelled(void * /*data*/, struct wl_data_source *wl_data_source) { + CLOG_INFO(LOG, 2, "cancelled"); wl_data_source_destroy(wl_data_source); } @@ -701,7 +1041,7 @@ static void data_source_handle_cancelled(void * /*data*/, struct wl_data_source static void data_source_handle_dnd_drop_performed(void * /*data*/, struct wl_data_source * /*wl_data_source*/) { - /* pass */ + CLOG_INFO(LOG, 2, "dnd_drop_performed"); } /** @@ -714,7 +1054,7 @@ static void data_source_handle_dnd_drop_performed(void * /*data*/, static void data_source_handle_dnd_finished(void * /*data*/, struct wl_data_source * /*wl_data_source*/) { - /* pass */ + CLOG_INFO(LOG, 2, "dnd_finished"); } /** @@ -726,9 +1066,9 @@ static void data_source_handle_dnd_finished(void * /*data*/, */ static void data_source_handle_action(void * /*data*/, struct wl_data_source * /*wl_data_source*/, - uint32_t /*dnd_action*/) + const uint32_t dnd_action) { - /* pass */ + CLOG_INFO(LOG, 2, "handle_action (dnd_action=%u)", dnd_action); } static const struct wl_data_source_listener data_source_listener = { @@ -740,31 +1080,39 @@ static const struct wl_data_source_listener data_source_listener = { data_source_handle_action, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Data Offer), #wl_data_offer_listener * \{ */ +static CLG_LogRef LOG_WL_DATA_OFFER = {"ghost.wl.handle.data_offer"}; +#define LOG (&LOG_WL_DATA_OFFER) + static void data_offer_handle_offer(void *data, struct wl_data_offer * /*wl_data_offer*/, const char *mime_type) { - static_cast<data_offer_t *>(data)->types.insert(mime_type); + CLOG_INFO(LOG, 2, "offer (mime_type=%s)", mime_type); + static_cast<GWL_DataOffer *>(data)->types.insert(mime_type); } static void data_offer_handle_source_actions(void *data, struct wl_data_offer * /*wl_data_offer*/, - uint32_t source_actions) + const uint32_t source_actions) { - static_cast<data_offer_t *>(data)->source_actions = source_actions; + CLOG_INFO(LOG, 2, "source_actions (%u)", source_actions); + static_cast<GWL_DataOffer *>(data)->source_actions = source_actions; } static void data_offer_handle_action(void *data, struct wl_data_offer * /*wl_data_offer*/, - uint32_t dnd_action) + const uint32_t dnd_action) { - static_cast<data_offer_t *>(data)->dnd_action = dnd_action; + CLOG_INFO(LOG, 2, "actions (%u)", dnd_action); + static_cast<GWL_DataOffer *>(data)->dnd_action = dnd_action; } static const struct wl_data_offer_listener data_offer_listener = { @@ -773,34 +1121,48 @@ static const struct wl_data_offer_listener data_offer_listener = { data_offer_handle_action, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Data Device), #wl_data_device_listener * \{ */ +static CLG_LogRef LOG_WL_DATA_DEVICE = {"ghost.wl.handle.data_device"}; +#define LOG (&LOG_WL_DATA_DEVICE) + static void data_device_handle_data_offer(void * /*data*/, struct wl_data_device * /*wl_data_device*/, struct wl_data_offer *id) { - data_offer_t *data_offer = new data_offer_t; + CLOG_INFO(LOG, 2, "data_offer"); + + GWL_DataOffer *data_offer = new GWL_DataOffer; data_offer->id = id; wl_data_offer_add_listener(id, &data_offer_listener, data_offer); } static void data_device_handle_enter(void *data, struct wl_data_device * /*wl_data_device*/, - uint32_t serial, - struct wl_surface *surface, - wl_fixed_t x, - wl_fixed_t y, + const uint32_t serial, + struct wl_surface *wl_surface, + const wl_fixed_t x, + const wl_fixed_t y, struct wl_data_offer *id) { - input_t *input = static_cast<input_t *>(data); - std::lock_guard lock{input->data_offer_dnd_mutex}; + if (!ghost_wl_surface_own(wl_surface)) { + CLOG_INFO(LOG, 2, "enter (skipped)"); + return; + } + CLOG_INFO(LOG, 2, "enter"); - input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); - data_offer_t *data_offer = input->data_offer_dnd; + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + std::lock_guard lock{seat->data_offer_dnd_mutex}; + + delete seat->data_offer_dnd; + seat->data_offer_dnd = static_cast<GWL_DataOffer *>(wl_data_offer_get_user_data(id)); + GWL_DataOffer *data_offer = seat->data_offer_dnd; data_offer->in_use.store(true); data_offer->dnd.xy[0] = x; @@ -815,88 +1177,105 @@ static void data_device_handle_enter(void *data, wl_data_offer_accept(id, serial, type.c_str()); } - input->focus_dnd = surface; - dnd_events(input, GHOST_kEventDraggingEntered); + seat->wl_surface_focus_dnd = wl_surface; + dnd_events(seat, GHOST_kEventDraggingEntered); } static void data_device_handle_leave(void *data, struct wl_data_device * /*wl_data_device*/) { - input_t *input = static_cast<input_t *>(data); - std::lock_guard lock{input->data_offer_dnd_mutex}; + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + std::lock_guard lock{seat->data_offer_dnd_mutex}; + + CLOG_INFO(LOG, 2, "leave"); - dnd_events(input, GHOST_kEventDraggingExited); - input->focus_dnd = nullptr; + dnd_events(seat, GHOST_kEventDraggingExited); + seat->wl_surface_focus_dnd = nullptr; - if (input->data_offer_dnd && !input->data_offer_dnd->in_use.load()) { - wl_data_offer_destroy(input->data_offer_dnd->id); - delete input->data_offer_dnd; - input->data_offer_dnd = nullptr; + if (seat->data_offer_dnd && !seat->data_offer_dnd->in_use.load()) { + wl_data_offer_destroy(seat->data_offer_dnd->id); + delete seat->data_offer_dnd; + seat->data_offer_dnd = nullptr; } } static void data_device_handle_motion(void *data, struct wl_data_device * /*wl_data_device*/, - uint32_t /*time*/, - wl_fixed_t x, - wl_fixed_t y) + const uint32_t /*time*/, + const wl_fixed_t x, + const wl_fixed_t y) { - input_t *input = static_cast<input_t *>(data); - std::lock_guard lock{input->data_offer_dnd_mutex}; + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + std::lock_guard lock{seat->data_offer_dnd_mutex}; + + CLOG_INFO(LOG, 2, "motion"); - input->data_offer_dnd->dnd.xy[0] = x; - input->data_offer_dnd->dnd.xy[1] = y; + seat->data_offer_dnd->dnd.xy[0] = x; + seat->data_offer_dnd->dnd.xy[1] = y; - dnd_events(input, GHOST_kEventDraggingUpdated); + dnd_events(seat, GHOST_kEventDraggingUpdated); } static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_data_device*/) { - input_t *input = static_cast<input_t *>(data); - std::lock_guard lock{input->data_offer_dnd_mutex}; + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + std::lock_guard lock{seat->data_offer_dnd_mutex}; - data_offer_t *data_offer = input->data_offer_dnd; + GWL_DataOffer *data_offer = seat->data_offer_dnd; const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(), mime_preference_order.end(), data_offer->types.begin(), data_offer->types.end()); - auto read_uris_fn = [](input_t *const input, - data_offer_t *data_offer, - wl_surface *surface, + CLOG_INFO(LOG, 2, "drop mime_recieve=%s", mime_receive.c_str()); + + auto read_uris_fn = [](GWL_Seat *const seat, + GWL_DataOffer *data_offer, + wl_surface *wl_surface, const std::string mime_receive) { - const wl_fixed_t xy[2] = {data_offer->dnd.xy[0], data_offer->dnd.xy[1]}; + const wl_fixed_t xy[2] = {UNPACK2(data_offer->dnd.xy)}; const std::string data = read_pipe(data_offer, mime_receive, nullptr); + CLOG_INFO( + LOG, 2, "drop_read_uris mime_receive=%s, data=%s", mime_receive.c_str(), data.c_str()); + wl_data_offer_finish(data_offer->id); wl_data_offer_destroy(data_offer->id); + if (seat->data_offer_dnd == data_offer) { + seat->data_offer_dnd = nullptr; + } delete data_offer; data_offer = nullptr; - GHOST_SystemWayland *const system = input->system; + GHOST_SystemWayland *const system = seat->system; if (mime_receive == mime_text_uri) { static constexpr const char *file_proto = "file://"; - static constexpr const char *crlf = "\r\n"; - - GHOST_WindowWayland *win = window_from_surface(surface); - GHOST_ASSERT(win != nullptr, "Unable to find window for drop event from surface"); + /* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`. + * So support both, once `\n` is found, strip the preceding `\r` if found. */ + static constexpr const char *lf = "\n"; + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface); std::vector<std::string> uris; size_t pos = 0; while (true) { pos = data.find(file_proto, pos); const size_t start = pos + sizeof(file_proto) - 1; - pos = data.find(crlf, pos); - const size_t end = pos; + pos = data.find(lf, pos); if (pos == std::string::npos) { break; } + /* Account for 'CRLF' case. */ + size_t end = pos; + if (data[end - 1] == '\r') { + end -= 1; + } uris.push_back(data.substr(start, end - start)); + CLOG_INFO(LOG, 2, "drop_read_uris pos=%zu, text_uri=\"%s\"", start, uris.back().c_str()); } GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>( @@ -904,10 +1283,10 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat flist->count = int(uris.size()); flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *))); for (size_t i = 0; i < uris.size(); i++) { - flist->strings[i] = static_cast<uint8_t *>(malloc((uris[i].size() + 1) * sizeof(uint8_t))); - memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1); + flist->strings[i] = (uint8_t *)GHOST_URL_decode_alloc(uris[i].c_str()); } + CLOG_INFO(LOG, 2, "drop_read_uris_fn file_count=%d", flist->count); const wl_fixed_t scale = win->scale(); system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(), GHOST_kEventDraggingDropDone, @@ -917,16 +1296,18 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat wl_fixed_to_int(scale * xy[1]), flist)); } - else if (mime_receive == mime_text_plain || mime_receive == mime_text_utf8) { + else if (ELEM(mime_receive, mime_text_plain, mime_text_utf8)) { /* TODO: enable use of internal functions 'txt_insert_buf' and * 'text_update_edited' to behave like dropped text was pasted. */ + CLOG_INFO(LOG, 2, "drop_read_uris_fn (text_plain, text_utf8), unhandled!"); } wl_display_roundtrip(system->display()); }; - /* Pass in `input->focus_dnd` instead of accessing it from `input` since the leave callback - * (#data_device_leave) will clear the value once this function starts. */ - std::thread read_thread(read_uris_fn, input, data_offer, input->focus_dnd, mime_receive); + /* Pass in `seat->wl_surface_focus_dnd` instead of accessing it from `seat` since the leave + * callback (#data_device_handle_leave) will clear the value once this function starts. */ + std::thread read_thread( + read_uris_fn, seat, data_offer, seat->wl_surface_focus_dnd, mime_receive); read_thread.detach(); } @@ -934,32 +1315,35 @@ static void data_device_handle_selection(void *data, struct wl_data_device * /*wl_data_device*/, struct wl_data_offer *id) { - input_t *input = static_cast<input_t *>(data); + GWL_Seat *seat = static_cast<GWL_Seat *>(data); - std::lock_guard lock{input->data_offer_copy_paste_mutex}; + std::lock_guard lock{seat->data_offer_copy_paste_mutex}; - data_offer_t *data_offer = input->data_offer_copy_paste; + GWL_DataOffer *data_offer = seat->data_offer_copy_paste; /* Delete old data offer. */ if (data_offer != nullptr) { wl_data_offer_destroy(data_offer->id); delete data_offer; data_offer = nullptr; + seat->data_offer_copy_paste = nullptr; } if (id == nullptr) { + CLOG_INFO(LOG, 2, "selection: (skipped)"); return; } + CLOG_INFO(LOG, 2, "selection"); /* Get new data offer. */ - data_offer = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); - input->data_offer_copy_paste = data_offer; + data_offer = static_cast<GWL_DataOffer *>(wl_data_offer_get_user_data(id)); + seat->data_offer_copy_paste = data_offer; - auto read_selection_fn = [](input_t *input) { - GHOST_SystemWayland *const system = input->system; - input->data_offer_copy_paste_mutex.lock(); + auto read_selection_fn = [](GWL_Seat *seat) { + GHOST_SystemWayland *const system = seat->system; + seat->data_offer_copy_paste_mutex.lock(); - data_offer_t *data_offer = input->data_offer_copy_paste; + GWL_DataOffer *data_offer = seat->data_offer_copy_paste; std::string mime_receive; for (const std::string type : {mime_text_utf8, mime_text_plain}) { if (data_offer->types.count(type)) { @@ -968,15 +1352,15 @@ static void data_device_handle_selection(void *data, } } const std::string data = read_pipe( - data_offer, mime_receive, &input->data_offer_copy_paste_mutex); + data_offer, mime_receive, &seat->data_offer_copy_paste_mutex); { std::lock_guard lock{system_selection_mutex}; - system->setSelection(data); + system->selection_set(data); } }; - std::thread read_thread(read_selection_fn, input); + std::thread read_thread(read_selection_fn, seat); read_thread.detach(); } @@ -989,59 +1373,62 @@ static const struct wl_data_device_listener data_device_listener = { data_device_handle_selection, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Buffer), #wl_buffer_listener * \{ */ +static CLG_LogRef LOG_WL_CURSOR_BUFFER = {"ghost.wl.handle.cursor_buffer"}; +#define LOG (&LOG_WL_CURSOR_BUFFER) + static void cursor_buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { - cursor_t *cursor = static_cast<cursor_t *>(data); + CLOG_INFO(LOG, 2, "release"); + GWL_Cursor *cursor = static_cast<GWL_Cursor *>(data); wl_buffer_destroy(wl_buffer); if (wl_buffer == cursor->wl_buffer) { - /* the mapped buffer was from a custom cursor */ + /* The mapped buffer was from a custom cursor. */ cursor->wl_buffer = nullptr; } } -const struct wl_buffer_listener cursor_buffer_listener = { +static const struct wl_buffer_listener cursor_buffer_listener = { cursor_buffer_handle_release, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Surface), #wl_surface_listener * \{ */ -static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface) -{ - if (surface) { - for (GHOST_IWindow *iwin : window_manager->getWindows()) { - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin); - if (surface == win->surface()) { - return win; - } - } - } - return nullptr; -} +static CLG_LogRef LOG_WL_CURSOR_SURFACE = {"ghost.wl.handle.cursor_surface"}; +#define LOG (&LOG_WL_CURSOR_SURFACE) -static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) +static bool update_cursor_scale(GWL_Cursor &cursor, + wl_shm *shm, + GWL_SeatStatePointer *seat_state_pointer, + wl_surface *wl_cursor_surface) { int scale = 0; - for (const output_t *output : cursor.outputs) { + for (const GWL_Output *output : seat_state_pointer->outputs) { if (output->scale > scale) { scale = output->scale; } } - if (scale > 0 && cursor.scale != scale) { - cursor.scale = scale; - wl_surface_set_buffer_scale(cursor.wl_surface, scale); + if (scale > 0 && seat_state_pointer->theme_scale != scale) { + seat_state_pointer->theme_scale = scale; + if (!cursor.is_custom) { + wl_surface_set_buffer_scale(wl_cursor_surface, scale); + } wl_cursor_theme_destroy(cursor.wl_theme); cursor.wl_theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm); return true; @@ -1050,125 +1437,145 @@ static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) } static void cursor_surface_handle_enter(void *data, - struct wl_surface * /*wl_surface*/, - struct wl_output *output) + struct wl_surface *wl_surface, + struct wl_output *wl_output) { - input_t *input = static_cast<input_t *>(data); - for (const output_t *reg_output : input->system->outputs()) { - if (reg_output->wl_output == output) { - input->cursor.outputs.insert(reg_output); - } + if (!ghost_wl_output_own(wl_output)) { + CLOG_INFO(LOG, 2, "handle_enter (skipped)"); + return; } - update_cursor_scale(input->cursor, input->system->shm()); + CLOG_INFO(LOG, 2, "handle_enter"); + + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_from_cursor_surface(seat, + wl_surface); + const GWL_Output *reg_output = ghost_wl_output_user_data(wl_output); + seat_state_pointer->outputs.insert(reg_output); + update_cursor_scale(seat->cursor, seat->system->shm(), seat_state_pointer, wl_surface); } static void cursor_surface_handle_leave(void *data, - struct wl_surface * /*wl_surface*/, - struct wl_output *output) + struct wl_surface *wl_surface, + struct wl_output *wl_output) { - input_t *input = static_cast<input_t *>(data); - for (const output_t *reg_output : input->system->outputs()) { - if (reg_output->wl_output == output) { - input->cursor.outputs.erase(reg_output); - } + if (!(wl_output && ghost_wl_output_own(wl_output))) { + CLOG_INFO(LOG, 2, "handle_leave (skipped)"); + return; } - update_cursor_scale(input->cursor, input->system->shm()); + CLOG_INFO(LOG, 2, "handle_leave"); + + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_from_cursor_surface(seat, + wl_surface); + const GWL_Output *reg_output = ghost_wl_output_user_data(wl_output); + seat_state_pointer->outputs.erase(reg_output); + update_cursor_scale(seat->cursor, seat->system->shm(), seat_state_pointer, wl_surface); } -struct wl_surface_listener cursor_surface_listener = { +static const struct wl_surface_listener cursor_surface_listener = { cursor_surface_handle_enter, cursor_surface_handle_leave, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Pointer), #wl_pointer_listener * \{ */ +static CLG_LogRef LOG_WL_POINTER = {"ghost.wl.handle.pointer"}; +#define LOG (&LOG_WL_POINTER) + static void pointer_handle_enter(void *data, struct wl_pointer * /*wl_pointer*/, - uint32_t serial, - struct wl_surface *surface, - wl_fixed_t surface_x, - wl_fixed_t surface_y) + const uint32_t serial, + struct wl_surface *wl_surface, + const wl_fixed_t surface_x, + const wl_fixed_t surface_y) { - GHOST_WindowWayland *win = window_from_surface(surface); - if (!win) { + if (!ghost_wl_surface_own(wl_surface)) { + CLOG_INFO(LOG, 2, "enter (skipped)"); return; } + CLOG_INFO(LOG, 2, "enter"); + + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface); win->activate(); - input_t *input = static_cast<input_t *>(data); - input->pointer_serial = serial; - input->cursor_serial = serial; - input->xy[0] = surface_x; - input->xy[1] = surface_y; - input->focus_pointer = surface; + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + seat->cursor_source_serial = serial; + seat->pointer.serial = serial; + seat->pointer.xy[0] = surface_x; + seat->pointer.xy[1] = surface_y; + seat->pointer.wl_surface = wl_surface; win->setCursorShape(win->getCursorShape()); const wl_fixed_t scale = win->scale(); - input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), - GHOST_kEventCursorMove, - static_cast<GHOST_WindowWayland *>(win), - wl_fixed_to_int(scale * input->xy[0]), - wl_fixed_to_int(scale * input->xy[1]), - GHOST_TABLET_DATA_NONE)); + seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(), + GHOST_kEventCursorMove, + win, + wl_fixed_to_int(scale * seat->pointer.xy[0]), + wl_fixed_to_int(scale * seat->pointer.xy[1]), + GHOST_TABLET_DATA_NONE)); } static void pointer_handle_leave(void *data, struct wl_pointer * /*wl_pointer*/, - uint32_t /*serial*/, - struct wl_surface *surface) + const uint32_t /*serial*/, + struct wl_surface *wl_surface) { - GHOST_IWindow *win = window_from_surface(surface); - if (!win) { - return; + /* First clear the `pointer.wl_surface`, since the window won't exist when closing the window. */ + static_cast<GWL_Seat *>(data)->pointer.wl_surface = nullptr; + if (wl_surface && ghost_wl_surface_own(wl_surface)) { + CLOG_INFO(LOG, 2, "leave"); + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface); + win->deactivate(); + } + else { + CLOG_INFO(LOG, 2, "leave (skipped)"); } - - static_cast<input_t *>(data)->focus_pointer = nullptr; - static_cast<GHOST_WindowWayland *>(win)->deactivate(); } static void pointer_handle_motion(void *data, struct wl_pointer * /*wl_pointer*/, - uint32_t /*time*/, - wl_fixed_t surface_x, - wl_fixed_t surface_y) + const uint32_t /*time*/, + const wl_fixed_t surface_x, + const wl_fixed_t surface_y) { - input_t *input = static_cast<input_t *>(data); - GHOST_WindowWayland *win = window_from_surface(input->focus_pointer); - if (!win) { - return; + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + seat->pointer.xy[0] = surface_x; + seat->pointer.xy[1] = surface_y; + + if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) { + CLOG_INFO(LOG, 2, "motion"); + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + const wl_fixed_t scale = win->scale(); + seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(), + GHOST_kEventCursorMove, + win, + wl_fixed_to_int(scale * seat->pointer.xy[0]), + wl_fixed_to_int(scale * seat->pointer.xy[1]), + GHOST_TABLET_DATA_NONE)); + } + else { + CLOG_INFO(LOG, 2, "motion (skipped)"); } - - input->xy[0] = surface_x; - input->xy[1] = surface_y; - - const wl_fixed_t scale = win->scale(); - input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), - GHOST_kEventCursorMove, - win, - wl_fixed_to_int(scale * input->xy[0]), - wl_fixed_to_int(scale * input->xy[1]), - GHOST_TABLET_DATA_NONE)); } static void pointer_handle_button(void *data, struct wl_pointer * /*wl_pointer*/, - uint32_t serial, - uint32_t /*time*/, - uint32_t button, - uint32_t state) + const uint32_t serial, + const uint32_t /*time*/, + const uint32_t button, + const uint32_t state) { - input_t *input = static_cast<input_t *>(data); - GHOST_IWindow *win = window_from_surface(input->focus_pointer); - if (!win) { - return; - } + CLOG_INFO(LOG, 2, "button (button=%u, state=%u)", button, state); + GWL_Seat *seat = static_cast<GWL_Seat *>(data); GHOST_TEventType etype = GHOST_kEventUnknown; switch (state) { case WL_POINTER_BUTTON_STATE_RELEASED: @@ -1179,7 +1586,7 @@ static void pointer_handle_button(void *data, break; } - GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft; + GHOST_TButton ebutton = GHOST_kButtonMaskLeft; switch (button) { case BTN_LEFT: ebutton = GHOST_kButtonMaskLeft; @@ -1204,30 +1611,59 @@ static void pointer_handle_button(void *data, break; } - input->data_source_serial = serial; - input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); - input->system->pushEvent(new GHOST_EventButton( - input->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE)); + seat->data_source_serial = serial; + seat->pointer.buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); + + if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) { + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + seat->system->pushEvent(new GHOST_EventButton( + seat->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE)); + } } -static void pointer_handle_axis(void *data, +static void pointer_handle_axis(void * /*data*/, struct wl_pointer * /*wl_pointer*/, - uint32_t /*time*/, - uint32_t axis, - wl_fixed_t value) + const uint32_t /*time*/, + const uint32_t axis, + const wl_fixed_t value) { - input_t *input = static_cast<input_t *>(data); - GHOST_IWindow *win = window_from_surface(input->focus_pointer); - if (!win) { - return; - } + CLOG_INFO(LOG, 2, "axis (axis=%u, value=%d)", axis, value); +} +static void pointer_handle_frame(void * /*data*/, struct wl_pointer * /*wl_pointer*/) +{ + CLOG_INFO(LOG, 2, "frame"); +} +static void pointer_handle_axis_source(void * /*data*/, + struct wl_pointer * /*wl_pointer*/, + uint32_t axis_source) +{ + CLOG_INFO(LOG, 2, "axis_source (axis_source=%u)", axis_source); +} +static void pointer_handle_axis_stop(void * /*data*/, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*time*/, + uint32_t axis) +{ + CLOG_INFO(LOG, 2, "axis_stop (axis=%u)", axis); +} +static void pointer_handle_axis_discrete(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t axis, + int32_t discrete) +{ + CLOG_INFO(LOG, 2, "axis_discrete (axis=%u, discrete=%d)", axis, discrete); + + GWL_Seat *seat = static_cast<GWL_Seat *>(data); if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) { return; } - input->system->pushEvent( - new GHOST_EventWheel(input->system->getMilliSeconds(), win, std::signbit(value) ? +1 : -1)); + if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) { + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + seat->system->pushEvent(new GHOST_EventWheel( + seat->system->getMilliSeconds(), win, std::signbit(discrete) ? +1 : -1)); + } } static const struct wl_pointer_listener pointer_listener = { @@ -1236,87 +1672,116 @@ static const struct wl_pointer_listener pointer_listener = { pointer_handle_motion, pointer_handle_button, pointer_handle_axis, + pointer_handle_frame, + pointer_handle_axis_source, + pointer_handle_axis_stop, + pointer_handle_axis_discrete, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Tablet Tool), #zwp_tablet_tool_v2_listener * \{ */ +static CLG_LogRef LOG_WL_TABLET_TOOL = {"ghost.wl.handle.tablet_tool"}; +#define LOG (&LOG_WL_TABLET_TOOL) + static void tablet_tool_handle_type(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t tool_type) + const uint32_t tool_type) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); + CLOG_INFO(LOG, 2, "type (type=%u)", tool_type); - tool_input->data.Active = tablet_tool_map_type((enum zwp_tablet_tool_v2_type)tool_type); + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + + tablet_tool->data.Active = tablet_tool_map_type((enum zwp_tablet_tool_v2_type)tool_type); } static void tablet_tool_handle_hardware_serial(void * /*data*/, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t /*hardware_serial_hi*/, - uint32_t /*hardware_serial_lo*/) + const uint32_t /*hardware_serial_hi*/, + const uint32_t /*hardware_serial_lo*/) { + CLOG_INFO(LOG, 2, "hardware_serial"); } static void tablet_tool_handle_hardware_id_wacom( void * /*data*/, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t /*hardware_id_hi*/, - uint32_t /*hardware_id_lo*/) + const uint32_t /*hardware_id_hi*/, + const uint32_t /*hardware_id_lo*/) { + CLOG_INFO(LOG, 2, "hardware_id_wacom"); } static void tablet_tool_handle_capability(void * /*data*/, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t /*capability*/) + const uint32_t capability) { + CLOG_INFO(LOG, + 2, + "capability (tilt=%d, distance=%d, rotation=%d, slider=%d, wheel=%d)", + (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_TILT) != 0, + (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE) != 0, + (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION) != 0, + (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER) != 0, + (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL) != 0); } static void tablet_tool_handle_done(void * /*data*/, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) { + CLOG_INFO(LOG, 2, "done"); } static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); - input_t *input = tool_input->input; + CLOG_INFO(LOG, 2, "removed"); + + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + GWL_Seat *seat = tablet_tool->seat; - if (tool_input->cursor_surface) { - wl_surface_destroy(tool_input->cursor_surface); + if (tablet_tool->wl_surface_cursor) { + wl_surface_destroy(tablet_tool->wl_surface_cursor); } - input->tablet_tools.erase(zwp_tablet_tool_v2); + seat->tablet_tools.erase(zwp_tablet_tool_v2); - delete tool_input; + delete tablet_tool; } static void tablet_tool_handle_proximity_in(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t serial, + const uint32_t serial, struct zwp_tablet_v2 * /*tablet*/, - struct wl_surface *surface) + struct wl_surface *wl_surface) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); - input_t *input = tool_input->input; + if (!ghost_wl_surface_own(wl_surface)) { + CLOG_INFO(LOG, 2, "proximity_in (skipped)"); + return; + } + CLOG_INFO(LOG, 2, "proximity_in"); + + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + tablet_tool->proximity = true; - input->focus_tablet = surface; - input->tablet_serial = serial; - input->cursor_serial = serial; + GWL_Seat *seat = tablet_tool->seat; + seat->cursor_source_serial = serial; + seat->tablet.wl_surface = wl_surface; + seat->tablet.serial = serial; - input->data_source_serial = serial; + seat->data_source_serial = serial; /* Update #GHOST_TabletData. */ - GHOST_TabletData &td = tool_input->data; + GHOST_TabletData &td = tablet_tool->data; /* Reset, to avoid using stale tilt/pressure. */ td.Xtilt = 0.0f; td.Ytilt = 0.0f; /* In case pressure isn't supported. */ td.Pressure = 1.0f; - GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); - if (!win) { - return; - } + GHOST_WindowWayland *win = ghost_wl_surface_user_data(seat->tablet.wl_surface); + win->activate(); win->setCursorShape(win->getCursorShape()); @@ -1324,158 +1789,145 @@ static void tablet_tool_handle_proximity_in(void *data, static void tablet_tool_handle_proximity_out(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); - input_t *input = tool_input->input; - input->focus_tablet = nullptr; - - GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); - if (!win) { - return; - } - win->setCursorShape(win->getCursorShape()); + CLOG_INFO(LOG, 2, "proximity_out"); + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + /* Defer clearing the wl_surface until the frame is handled. + * Without this, the frame can not access the wl_surface. */ + tablet_tool->proximity = false; } static void tablet_tool_handle_down(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t serial) + const uint32_t serial) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); - input_t *input = tool_input->input; - GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); - if (!win) { - return; - } + CLOG_INFO(LOG, 2, "down"); + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + GWL_Seat *seat = tablet_tool->seat; + const GHOST_TButton ebutton = GHOST_kButtonMaskLeft; const GHOST_TEventType etype = GHOST_kEventButtonDown; - const GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft; - input->data_source_serial = serial; - input->buttons.set(ebutton, true); - input->system->pushEvent(new GHOST_EventButton( - input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data)); + + seat->data_source_serial = serial; + seat->tablet.buttons.set(ebutton, true); + + if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) { + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + seat->system->pushEvent(new GHOST_EventButton( + seat->system->getMilliSeconds(), etype, win, ebutton, tablet_tool->data)); + } } static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); - input_t *input = tool_input->input; - GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); - if (!win) { - return; - } + CLOG_INFO(LOG, 2, "up"); + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + GWL_Seat *seat = tablet_tool->seat; + const GHOST_TButton ebutton = GHOST_kButtonMaskLeft; const GHOST_TEventType etype = GHOST_kEventButtonUp; - const GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft; - input->buttons.set(ebutton, false); - input->system->pushEvent(new GHOST_EventButton( - input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data)); + + seat->tablet.buttons.set(ebutton, false); + + if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) { + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + seat->system->pushEvent(new GHOST_EventButton( + seat->system->getMilliSeconds(), etype, win, ebutton, tablet_tool->data)); + } } static void tablet_tool_handle_motion(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - wl_fixed_t x, - wl_fixed_t y) + const wl_fixed_t x, + const wl_fixed_t y) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); - input_t *input = tool_input->input; - GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); - if (!win) { - return; - } + CLOG_INFO(LOG, 2, "motion"); - input->xy[0] = x; - input->xy[1] = y; + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + GWL_Seat *seat = tablet_tool->seat; - const wl_fixed_t scale = win->scale(); - input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), - GHOST_kEventCursorMove, - win, - wl_fixed_to_int(scale * input->xy[0]), - wl_fixed_to_int(scale * input->xy[1]), - tool_input->data)); + seat->tablet.xy[0] = x; + seat->tablet.xy[1] = y; + + /* NOTE: #tablet_tool_handle_frame generates the event (with updated pressure, tilt... etc). */ } static void tablet_tool_handle_pressure(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t pressure) + const uint32_t pressure) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); - input_t *input = tool_input->input; - GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); - if (!win) { - return; - } + const float pressure_unit = (float)pressure / 65535; + CLOG_INFO(LOG, 2, "pressure (%.4f)", pressure_unit); - GHOST_TabletData &td = tool_input->data; - td.Pressure = (float)pressure / 65535; + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + GHOST_TabletData &td = tablet_tool->data; + td.Pressure = pressure_unit; } static void tablet_tool_handle_distance(void * /*data*/, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t /*distance*/) + const uint32_t distance) { + CLOG_INFO(LOG, 2, "distance (distance=%u)", distance); } + static void tablet_tool_handle_tilt(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - wl_fixed_t tilt_x, - wl_fixed_t tilt_y) + const wl_fixed_t tilt_x, + const wl_fixed_t tilt_y) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); - input_t *input = tool_input->input; - GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); - if (!win) { - return; - } - - GHOST_TabletData &td = tool_input->data; /* Map degrees to `-1.0..1.0`. */ - td.Xtilt = wl_fixed_to_double(tilt_x) / 90.0f; - td.Ytilt = wl_fixed_to_double(tilt_y) / 90.0f; - td.Xtilt = td.Xtilt < -1.0f ? -1.0f : (td.Xtilt > 1.0f ? 1.0f : td.Xtilt); - td.Ytilt = td.Ytilt < -1.0f ? -1.0f : (td.Ytilt > 1.0f ? 1.0f : td.Ytilt); + const float tilt_unit[2] = { + (float)(wl_fixed_to_double(tilt_x) / 90.0), + (float)(wl_fixed_to_double(tilt_y) / 90.0), + }; + CLOG_INFO(LOG, 2, "tilt (x=%.4f, y=%.4f)", UNPACK2(tilt_unit)); + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + GHOST_TabletData &td = tablet_tool->data; + td.Xtilt = tilt_unit[0]; + td.Ytilt = tilt_unit[1]; + CLAMP(td.Xtilt, -1.0f, 1.0f); + CLAMP(td.Ytilt, -1.0f, 1.0f); } static void tablet_tool_handle_rotation(void * /*data*/, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - wl_fixed_t /*degrees*/) + const wl_fixed_t degrees) { - /* Pass. */ + CLOG_INFO(LOG, 2, "rotation (degrees=%.4f)", wl_fixed_to_double(degrees)); } static void tablet_tool_handle_slider(void * /*data*/, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - int32_t /*position*/) + const int32_t position) { - /* Pass. */ + CLOG_INFO(LOG, 2, "slider (position=%d)", position); } static void tablet_tool_handle_wheel(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - wl_fixed_t /*degrees*/, - int32_t clicks) + const wl_fixed_t /*degrees*/, + const int32_t clicks) { if (clicks == 0) { return; } + CLOG_INFO(LOG, 2, "wheel (clicks=%d)", clicks); - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); - input_t *input = tool_input->input; - GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); - if (!win) { - return; + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + GWL_Seat *seat = tablet_tool->seat; + if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) { + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + seat->system->pushEvent(new GHOST_EventWheel(seat->system->getMilliSeconds(), win, clicks)); } - - input->system->pushEvent(new GHOST_EventWheel(input->system->getMilliSeconds(), win, clicks)); } static void tablet_tool_handle_button(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t serial, - uint32_t button, - uint32_t state) + const uint32_t serial, + const uint32_t button, + const uint32_t state) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data); - input_t *input = tool_input->input; - GHOST_WindowWayland *win = window_from_surface(input->focus_tablet); - if (!win) { - return; - } + CLOG_INFO(LOG, 2, "button (button=%u, state=%u)", button, state); + + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + GWL_Seat *seat = tablet_tool->seat; GHOST_TEventType etype = GHOST_kEventUnknown; switch (state) { @@ -1487,7 +1939,7 @@ static void tablet_tool_handle_button(void *data, break; } - GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft; + GHOST_TButton ebutton = GHOST_kButtonMaskLeft; switch (button) { case BTN_STYLUS: ebutton = GHOST_kButtonMaskRight; @@ -1500,15 +1952,42 @@ static void tablet_tool_handle_button(void *data, break; } - input->data_source_serial = serial; - input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); - input->system->pushEvent(new GHOST_EventButton( - input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data)); + seat->data_source_serial = serial; + seat->tablet.buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); + + if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) { + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + seat->system->pushEvent(new GHOST_EventButton( + seat->system->getMilliSeconds(), etype, win, ebutton, tablet_tool->data)); + } } -static void tablet_tool_handle_frame(void * /*data*/, +static void tablet_tool_handle_frame(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/, - uint32_t /*time*/) + const uint32_t /*time*/) { + CLOG_INFO(LOG, 2, "frame"); + + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data); + GWL_Seat *seat = tablet_tool->seat; + + /* No need to check the surfaces origin, it's already known to be owned by GHOST. */ + if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) { + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + const wl_fixed_t scale = win->scale(); + seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(), + GHOST_kEventCursorMove, + win, + wl_fixed_to_int(scale * seat->tablet.xy[0]), + wl_fixed_to_int(scale * seat->tablet.xy[1]), + tablet_tool->data)); + if (tablet_tool->proximity == false) { + win->setCursorShape(win->getCursorShape()); + } + } + + if (tablet_tool->proximity == false) { + seat->tablet.wl_surface = nullptr; + } } static const struct zwp_tablet_tool_v2_listener tablet_tool_listner = { @@ -1533,61 +2012,79 @@ static const struct zwp_tablet_tool_v2_listener tablet_tool_listner = { tablet_tool_handle_frame, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Table Seat), #zwp_tablet_seat_v2_listener * \{ */ +static CLG_LogRef LOG_WL_TABLET_SEAT = {"ghost.wl.handle.tablet_seat"}; +#define LOG (&LOG_WL_TABLET_SEAT) + static void tablet_seat_handle_tablet_added(void * /*data*/, struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, - struct zwp_tablet_v2 * /*id*/) + struct zwp_tablet_v2 *id) { - /* Pass. */ + CLOG_INFO(LOG, 2, "tablet_added (id=%p)", id); } static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, struct zwp_tablet_tool_v2 *id) { - input_t *input = static_cast<input_t *>(data); - tablet_tool_input_t *tool_input = new tablet_tool_input_t(); - tool_input->input = input; + CLOG_INFO(LOG, 2, "tool_added (id=%p)", id); + + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + GWL_TabletTool *tablet_tool = new GWL_TabletTool(); + tablet_tool->seat = seat; - /* Every tool has it's own cursor surface. */ - tool_input->cursor_surface = wl_compositor_create_surface(input->system->compositor()); - wl_surface_add_listener(tool_input->cursor_surface, &cursor_surface_listener, (void *)input); + /* Every tool has it's own cursor wl_surface. */ + tablet_tool->wl_surface_cursor = wl_compositor_create_surface(seat->system->compositor()); + ghost_wl_surface_tag_cursor_tablet(tablet_tool->wl_surface_cursor); - zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tool_input); + wl_surface_add_listener(tablet_tool->wl_surface_cursor, &cursor_surface_listener, (void *)seat); - input->tablet_tools.insert(id); + zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tablet_tool); + + seat->tablet_tools.insert(id); } static void tablet_seat_handle_pad_added(void * /*data*/, struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/, - struct zwp_tablet_pad_v2 * /*id*/) + struct zwp_tablet_pad_v2 *id) { - /* Pass. */ + CLOG_INFO(LOG, 2, "pad_added (id=%p)", id); } -const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { +static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { tablet_seat_handle_tablet_added, tablet_seat_handle_tool_added, tablet_seat_handle_pad_added, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Keyboard), #wl_keyboard_listener * \{ */ -static void keyboard_handle_keymap( - void *data, struct wl_keyboard * /*wl_keyboard*/, uint32_t format, int32_t fd, uint32_t size) +static CLG_LogRef LOG_WL_KEYBOARD = {"ghost.wl.handle.keyboard"}; +#define LOG (&LOG_WL_KEYBOARD) + +static void keyboard_handle_keymap(void *data, + struct wl_keyboard * /*wl_keyboard*/, + const uint32_t format, + const int32_t fd, + const uint32_t size) { - input_t *input = static_cast<input_t *>(data); + GWL_Seat *seat = static_cast<GWL_Seat *>(data); if ((!data) || (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)) { + CLOG_INFO(LOG, 2, "keymap (no data or wrong version)"); close(fd); return; } @@ -1599,54 +2096,134 @@ static void keyboard_handle_keymap( } struct xkb_keymap *keymap = xkb_keymap_new_from_string( - input->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + seat->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); munmap(map_str, size); close(fd); if (!keymap) { + CLOG_INFO(LOG, 2, "keymap (not found)"); return; } - struct xkb_state *xkb_state_next = xkb_state_new(keymap); - if (xkb_state_next) { - if (input->xkb_state) { - xkb_state_unref(input->xkb_state); + CLOG_INFO(LOG, 2, "keymap"); + + /* In practice we can assume `xkb_state_new` always succeeds. */ + xkb_state_unref(seat->xkb_state); + seat->xkb_state = xkb_state_new(keymap); + + xkb_state_unref(seat->xkb_state_empty); + seat->xkb_state_empty = xkb_state_new(keymap); + + xkb_state_unref(seat->xkb_state_empty_with_numlock); + seat->xkb_state_empty_with_numlock = nullptr; + + { + const xkb_mod_index_t mod2 = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM); + const xkb_mod_index_t num = xkb_keymap_mod_get_index(keymap, "NumLock"); + if (num != XKB_MOD_INVALID && mod2 != XKB_MOD_INVALID) { + seat->xkb_state_empty_with_numlock = xkb_state_new(keymap); + xkb_state_update_mask( + seat->xkb_state_empty_with_numlock, (1 << mod2), 0, (1 << num), 0, 0, 0); } - input->xkb_state = xkb_state_next; } + + seat->xkb_keymap_mod_index.shift = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT); + seat->xkb_keymap_mod_index.caps = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS); + seat->xkb_keymap_mod_index.ctrl = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL); + seat->xkb_keymap_mod_index.alt = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT); + seat->xkb_keymap_mod_index.num = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM); + seat->xkb_keymap_mod_index.logo = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_LOGO); + xkb_keymap_unref(keymap); } /** * Enter event. * - * Notification that this seat's keyboard focus is on a certain - * surface. + * Notification that this seat's keyboard focus is on a certain wl_surface. */ static void keyboard_handle_enter(void *data, struct wl_keyboard * /*wl_keyboard*/, - uint32_t /*serial*/, - struct wl_surface *surface, - struct wl_array * /*keys*/) + const uint32_t serial, + struct wl_surface *wl_surface, + struct wl_array *keys) { - if (surface != nullptr) { - static_cast<input_t *>(data)->focus_keyboard = surface; + if (!ghost_wl_surface_own(wl_surface)) { + CLOG_INFO(LOG, 2, "enter (skipped)"); + return; + } + CLOG_INFO(LOG, 2, "enter"); + + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + seat->keyboard.serial = serial; + seat->keyboard.wl_surface = wl_surface; + + if (keys->size != 0) { + /* If there are any keys held when activating the window, + * modifiers will be compared against the seat state, + * only enabling modifiers that were previously disabled. */ + + const xkb_mod_mask_t state = xkb_state_serialize_mods(seat->xkb_state, XKB_STATE_MODS_ALL); + uint32_t *key; + WL_ARRAY_FOR_EACH (key, keys) { + const xkb_keycode_t key_code = *key + EVDEV_OFFSET; + const xkb_keysym_t sym = xkb_state_key_get_one_sym(seat->xkb_state, key_code); + GHOST_TKey gkey = GHOST_kKeyUnknown; + +#define MOD_TEST(state, mod) ((mod != XKB_MOD_INVALID) && (state & (1 << mod))) +#define MOD_TEST_CASE(xkb_key, ghost_key, mod_index) \ + case xkb_key: \ + if (!MOD_TEST(state, seat->xkb_keymap_mod_index.mod_index)) { \ + gkey = ghost_key; \ + } \ + break + + switch (sym) { + MOD_TEST_CASE(XKB_KEY_Shift_L, GHOST_kKeyLeftShift, shift); + MOD_TEST_CASE(XKB_KEY_Shift_R, GHOST_kKeyRightShift, shift); + MOD_TEST_CASE(XKB_KEY_Control_L, GHOST_kKeyLeftControl, ctrl); + MOD_TEST_CASE(XKB_KEY_Control_R, GHOST_kKeyRightControl, ctrl); + MOD_TEST_CASE(XKB_KEY_Alt_L, GHOST_kKeyLeftAlt, alt); + MOD_TEST_CASE(XKB_KEY_Alt_R, GHOST_kKeyRightAlt, alt); + MOD_TEST_CASE(XKB_KEY_Super_L, GHOST_kKeyOS, logo); + MOD_TEST_CASE(XKB_KEY_Super_R, GHOST_kKeyOS, logo); + } + +#undef MOD_TEST +#undef MOD_TEST_CASE + + if (gkey != GHOST_kKeyUnknown) { + GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface); + GHOST_SystemWayland *system = seat->system; + seat->system->pushEvent( + new GHOST_EventKey(system->getMilliSeconds(), GHOST_kEventKeyDown, win, gkey, false)); + } + } } } /** * Leave event. * - * Notification that this seat's keyboard focus is no longer on a - * certain surface. + * Notification that this seat's keyboard focus is no longer on a certain wl_surface. */ static void keyboard_handle_leave(void *data, struct wl_keyboard * /*wl_keyboard*/, - uint32_t /*serial*/, - struct wl_surface *surface) + const uint32_t /*serial*/, + struct wl_surface *wl_surface) { - if (surface != nullptr) { - static_cast<input_t *>(data)->focus_keyboard = nullptr; + if (!(wl_surface && ghost_wl_surface_own(wl_surface))) { + CLOG_INFO(LOG, 2, "leave (skipped)"); + return; + } + CLOG_INFO(LOG, 2, "leave"); + + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + seat->keyboard.wl_surface = nullptr; + + /* Losing focus must stop repeating text. */ + if (seat->key_repeat.timer) { + keyboard_handle_key_repeat_cancel(seat); } } @@ -1654,36 +2231,75 @@ static void keyboard_handle_leave(void *data, * A version of #xkb_state_key_get_one_sym which returns the key without any modifiers pressed. * Needed because #GHOST_TKey uses these values as key-codes. */ -static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(struct xkb_state *xkb_state, - xkb_keycode_t key) +static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers( + struct xkb_state *xkb_state_empty, + struct xkb_state *xkb_state_empty_with_numlock, + const xkb_keycode_t key) { /* Use an empty keyboard state to access key symbol without modifiers. */ - xkb_state_get_keymap(xkb_state); - struct xkb_keymap *keymap = xkb_state_get_keymap(xkb_state); - struct xkb_state *xkb_state_empty = xkb_state_new(keymap); - - /* Enable number-lock. */ - { - const xkb_mod_index_t mod2 = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM); - const xkb_mod_index_t num = xkb_keymap_mod_get_index(keymap, "NumLock"); - if (num != XKB_MOD_INVALID && mod2 != XKB_MOD_INVALID) { - xkb_state_update_mask(xkb_state_empty, (1 << mod2), 0, (1 << num), 0, 0, 0); + xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state_empty, key); + + /* NOTE(@campbellbarton): Only perform the number-locked lookup as a fallback + * when a number-pad key has been pressed. This is important as some key-maps use number lock + * for switching other layers (in particular `de(neo_qwertz)` turns on layer-4), see: T96170. + * Alternative solutions could be to inspect the layout however this could get involved + * and turning on the number-lock is only needed for a limited set of keys. */ + + /* Accounts for key-pad keys typically swapped for numbers when number-lock is enabled: + * `Home Left Up Right Down Prior Page_Up Next Page_Dow End Begin Insert Delete`. */ + if (xkb_state_empty_with_numlock && (sym >= XKB_KEY_KP_Home && sym <= XKB_KEY_KP_Delete)) { + const xkb_keysym_t sym_test = xkb_state_key_get_one_sym(xkb_state_empty_with_numlock, key); + if (sym_test != XKB_KEY_NoSymbol) { + sym = sym_test; } } - const xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state_empty, key); - xkb_state_unref(xkb_state_empty); return sym; } +static void keyboard_handle_key_repeat_cancel(GWL_Seat *seat) +{ + GHOST_ASSERT(seat->key_repeat.timer != nullptr, "Caller much check for timer"); + delete static_cast<GWL_KeyRepeatPlayload *>(seat->key_repeat.timer->getUserData()); + seat->system->removeTimer(seat->key_repeat.timer); + seat->key_repeat.timer = nullptr; +} + +/** + * Restart the key-repeat timer. + * \param use_delay: When false, use the interval + * (prevents pause when the setting changes while the key is held). + */ +static void keyboard_handle_key_repeat_reset(GWL_Seat *seat, const bool use_delay) +{ + GHOST_ASSERT(seat->key_repeat.timer != nullptr, "Caller much check for timer"); + GHOST_SystemWayland *system = seat->system; + GHOST_ITimerTask *timer = seat->key_repeat.timer; + GHOST_TimerProcPtr key_repeat_fn = timer->getTimerProc(); + GHOST_TUserDataPtr payload = seat->key_repeat.timer->getUserData(); + seat->system->removeTimer(seat->key_repeat.timer); + const uint64_t time_step = 1000 / seat->key_repeat.rate; + const uint64_t time_start = use_delay ? seat->key_repeat.delay : time_step; + seat->key_repeat.timer = system->installTimer(time_start, time_step, key_repeat_fn, payload); +} + static void keyboard_handle_key(void *data, struct wl_keyboard * /*wl_keyboard*/, - uint32_t serial, - uint32_t /*time*/, - uint32_t key, - uint32_t state) + const uint32_t serial, + const uint32_t /*time*/, + const uint32_t key, + const uint32_t state) { - input_t *input = static_cast<input_t *>(data); + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + const xkb_keycode_t key_code = key + EVDEV_OFFSET; + + const xkb_keysym_t sym = xkb_state_key_get_one_sym_without_modifiers( + seat->xkb_state_empty, seat->xkb_state_empty_with_numlock, key_code); + if (sym == XKB_KEY_NoSymbol) { + CLOG_INFO(LOG, 2, "key (no symbol, skipped)"); + return; + } + CLOG_INFO(LOG, 2, "key"); GHOST_TEventType etype = GHOST_kEventUnknown; switch (state) { @@ -1695,92 +2311,153 @@ static void keyboard_handle_key(void *data, break; } - const xkb_keysym_t sym = xkb_state_key_get_one_sym_without_modifiers(input->xkb_state, key + 8); - - if (sym == XKB_KEY_NoSymbol) { - return; - } + struct GWL_KeyRepeatPlayload *key_repeat_payload = nullptr; /* Delete previous timer. */ - if (xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key + 8) && - input->key_repeat.timer) { - delete static_cast<key_repeat_payload_t *>(input->key_repeat.timer->getUserData()); - input->system->removeTimer(input->key_repeat.timer); - input->key_repeat.timer = nullptr; - } + if (seat->key_repeat.timer) { + enum { NOP = 1, RESET, CANCEL } timer_action = NOP; + key_repeat_payload = static_cast<GWL_KeyRepeatPlayload *>( + seat->key_repeat.timer->getUserData()); + + if (seat->key_repeat.rate == 0) { + /* Repeat was disabled (unlikely but possible). */ + timer_action = CANCEL; + } + else if (key_code == key_repeat_payload->key_code) { + /* Releasing the key that was held always cancels. */ + timer_action = CANCEL; + } + else if (xkb_keymap_key_repeats(xkb_state_get_keymap(seat->xkb_state), key_code)) { + if (etype == GHOST_kEventKeyDown) { + /* Any other key-down always cancels (and may start it's own repeat timer). */ + timer_action = CANCEL; + } + else { + /* Key-up from keys that were not repeating cause the repeat timer to pause. + * + * NOTE(@campbellbarton): This behavior isn't universal, some text input systems will + * stop the repeat entirely. Choose to pause repeat instead as this is what GTK/WIN32 do, + * and it fits better for keyboard input that isn't related to text entry. */ + timer_action = RESET; + } + } - GHOST_TEventKeyData key_data = { - .key = xkb_map_gkey(sym), - }; + switch (timer_action) { + case NOP: { + /* Don't add a new timer, leave the existing timer owning this `key_repeat_payload`. */ + key_repeat_payload = nullptr; + break; + } + case RESET: { + /* The payload will be added again. */ + seat->system->removeTimer(seat->key_repeat.timer); + seat->key_repeat.timer = nullptr; + break; + } + case CANCEL: { + delete key_repeat_payload; + key_repeat_payload = nullptr; - if (etype == GHOST_kEventKeyDown) { - xkb_state_key_get_utf8( - input->xkb_state, key + 8, key_data.utf8_buf, sizeof(GHOST_TEventKeyData::utf8_buf)); - } - else { - key_data.utf8_buf[0] = '\0'; + seat->system->removeTimer(seat->key_repeat.timer); + seat->key_repeat.timer = nullptr; + break; + } + } } - input->data_source_serial = serial; + const GHOST_TKey gkey = xkb_map_gkey_or_scan_code(sym, key); + char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'}; + if (etype == GHOST_kEventKeyDown) { + xkb_state_key_get_utf8(seat->xkb_state, key_code, utf8_buf, sizeof(utf8_buf)); + } - GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( - wl_surface_get_user_data(input->focus_keyboard)); - input->system->pushEvent(new GHOST_EventKey( - input->system->getMilliSeconds(), etype, win, key_data.key, '\0', key_data.utf8_buf, false)); + seat->data_source_serial = serial; - /* Start timer for repeating key, if applicable. */ - if (input->key_repeat.rate > 0 && - xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key + 8) && - etype == GHOST_kEventKeyDown) { + if (wl_surface *wl_surface_focus = seat->keyboard.wl_surface) { + GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface_focus); + seat->system->pushEvent( + new GHOST_EventKey(seat->system->getMilliSeconds(), etype, win, gkey, false, utf8_buf)); + } - key_repeat_payload_t *payload = new key_repeat_payload_t({ - .system = input->system, - .window = win, - .key_data = key_data, - }); + /* An existing payload means the key repeat timer is reset and will be added again. */ + if (key_repeat_payload == nullptr) { + /* Start timer for repeating key, if applicable. */ + if ((seat->key_repeat.rate > 0) && (etype == GHOST_kEventKeyDown) && + xkb_keymap_key_repeats(xkb_state_get_keymap(seat->xkb_state), key_code)) { + key_repeat_payload = new GWL_KeyRepeatPlayload({ + .seat = seat, + .key_code = key_code, + .key_data = {.gkey = gkey}, + }); + } + } + if (key_repeat_payload) { auto key_repeat_fn = [](GHOST_ITimerTask *task, uint64_t /*time*/) { - struct key_repeat_payload_t *payload = static_cast<key_repeat_payload_t *>( + struct GWL_KeyRepeatPlayload *payload = static_cast<GWL_KeyRepeatPlayload *>( task->getUserData()); - payload->system->pushEvent(new GHOST_EventKey(payload->system->getMilliSeconds(), - GHOST_kEventKeyDown, - payload->window, - payload->key_data.key, - '\0', - payload->key_data.utf8_buf, - true)); + + GWL_Seat *seat = payload->seat; + if (wl_surface *wl_surface_focus = seat->keyboard.wl_surface) { + GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface_focus); + GHOST_SystemWayland *system = seat->system; + /* Calculate this value every time in case modifier keys are pressed. */ + char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'}; + xkb_state_key_get_utf8(seat->xkb_state, payload->key_code, utf8_buf, sizeof(utf8_buf)); + system->pushEvent(new GHOST_EventKey(system->getMilliSeconds(), + GHOST_kEventKeyDown, + win, + payload->key_data.gkey, + true, + utf8_buf)); + } }; - input->key_repeat.timer = input->system->installTimer( - input->key_repeat.delay, 1000 / input->key_repeat.rate, key_repeat_fn, payload); + seat->key_repeat.timer = seat->system->installTimer( + seat->key_repeat.delay, 1000 / seat->key_repeat.rate, key_repeat_fn, key_repeat_payload); } } static void keyboard_handle_modifiers(void *data, struct wl_keyboard * /*wl_keyboard*/, - uint32_t /*serial*/, - uint32_t mods_depressed, - uint32_t mods_latched, - uint32_t mods_locked, - uint32_t group) + const uint32_t /*serial*/, + const uint32_t mods_depressed, + const uint32_t mods_latched, + const uint32_t mods_locked, + const uint32_t group) { - xkb_state_update_mask(static_cast<input_t *>(data)->xkb_state, - mods_depressed, - mods_latched, - mods_locked, - 0, - 0, - group); + CLOG_INFO(LOG, + 2, + "modifiers (depressed=%u, latched=%u, locked=%u, group=%u)", + mods_depressed, + mods_latched, + mods_locked, + group); + + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + xkb_state_update_mask(seat->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); + + /* A modifier changed so reset the timer, + * see comment in #keyboard_handle_key regarding this behavior. */ + if (seat->key_repeat.timer) { + keyboard_handle_key_repeat_reset(seat, true); + } } static void keyboard_repeat_handle_info(void *data, struct wl_keyboard * /*wl_keyboard*/, - int32_t rate, - int32_t delay) + const int32_t rate, + const int32_t delay) { - input_t *input = static_cast<input_t *>(data); + CLOG_INFO(LOG, 2, "info (rate=%d, delay=%d)", rate, delay); - input->key_repeat.rate = rate; - input->key_repeat.delay = delay; + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + seat->key_repeat.rate = rate; + seat->key_repeat.delay = delay; + + /* Unlikely possible this setting changes while repeating. */ + if (seat->key_repeat.timer) { + keyboard_handle_key_repeat_reset(seat, false); + } } static const struct wl_keyboard_listener keyboard_listener = { @@ -1792,41 +2469,57 @@ static const struct wl_keyboard_listener keyboard_listener = { keyboard_repeat_handle_info, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Seat), #wl_seat_listener * \{ */ -static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) +static CLG_LogRef LOG_WL_SEAT = {"ghost.wl.handle.seat"}; +#define LOG (&LOG_WL_SEAT) + +static void seat_handle_capabilities(void *data, + struct wl_seat *wl_seat, + const uint32_t capabilities) { - input_t *input = static_cast<input_t *>(data); - input->wl_pointer = nullptr; - input->wl_keyboard = nullptr; + CLOG_INFO(LOG, + 2, + "capabilities (pointer=%d, keyboard=%d, touch=%d)", + (capabilities & WL_SEAT_CAPABILITY_POINTER) != 0, + (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) != 0, + (capabilities & WL_SEAT_CAPABILITY_TOUCH) != 0); + + GWL_Seat *seat = static_cast<GWL_Seat *>(data); + seat->wl_pointer = nullptr; + seat->wl_keyboard = nullptr; if (capabilities & WL_SEAT_CAPABILITY_POINTER) { - input->wl_pointer = wl_seat_get_pointer(wl_seat); - input->cursor.wl_surface = wl_compositor_create_surface(input->system->compositor()); - input->cursor.visible = true; - input->cursor.wl_buffer = nullptr; - input->cursor.file_buffer = new buffer_t; - if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) { - input->cursor.theme_name = std::string(); - input->cursor.size = default_cursor_size; + seat->wl_pointer = wl_seat_get_pointer(wl_seat); + seat->cursor.wl_surface = wl_compositor_create_surface(seat->system->compositor()); + seat->cursor.visible = true; + seat->cursor.wl_buffer = nullptr; + if (!get_cursor_settings(seat->cursor.theme_name, seat->cursor.size)) { + seat->cursor.theme_name = std::string(); + seat->cursor.size = default_cursor_size; } - wl_pointer_add_listener(input->wl_pointer, &pointer_listener, data); - wl_surface_add_listener(input->cursor.wl_surface, &cursor_surface_listener, data); + wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, data); + + wl_surface_add_listener(seat->cursor.wl_surface, &cursor_surface_listener, data); + ghost_wl_surface_tag_cursor_pointer(seat->cursor.wl_surface); } if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - input->wl_keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(input->wl_keyboard, &keyboard_listener, data); + seat->wl_keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, data); } } static void seat_handle_name(void *data, struct wl_seat * /*wl_seat*/, const char *name) { - static_cast<input_t *>(data)->name = std::string(name); + CLOG_INFO(LOG, 2, "name (name=\"%s\")", name); + static_cast<GWL_Seat *>(data)->name = std::string(name); } static const struct wl_seat_listener seat_listener = { @@ -1834,18 +2527,25 @@ static const struct wl_seat_listener seat_listener = { seat_handle_name, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (XDG Output), #zxdg_output_v1_listener * \{ */ +static CLG_LogRef LOG_WL_XDG_OUTPUT = {"ghost.wl.handle.xdg_output"}; +#define LOG (&LOG_WL_XDG_OUTPUT) + static void xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 * /*xdg_output*/, - int32_t x, - int32_t y) + const int32_t x, + const int32_t y) { - output_t *output = static_cast<output_t *>(data); + CLOG_INFO(LOG, 2, "logical_position [%d, %d]", x, y); + + GWL_Output *output = static_cast<GWL_Output *>(data); output->position_logical[0] = x; output->position_logical[1] = y; output->has_position_logical = true; @@ -1853,11 +2553,12 @@ static void xdg_output_handle_logical_position(void *data, static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 * /*xdg_output*/, - int32_t width, - int32_t height) + const int32_t width, + const int32_t height) { - output_t *output = static_cast<output_t *>(data); + CLOG_INFO(LOG, 2, "logical_size [%d, %d]", width, height); + GWL_Output *output = static_cast<GWL_Output *>(data); if (output->size_logical[0] != 0 && output->size_logical[1] != 0) { /* Original comment from SDL. */ /* FIXME(@flibit): GNOME has a bug where the logical size does not account for @@ -1867,7 +2568,14 @@ static void xdg_output_handle_logical_size(void *data, * done (we can't match exactly because fractional scaling can't be * detected otherwise), then override if necessary. */ if ((output->size_logical[0] == width) && (output->scale_fractional == wl_fixed_from_int(1))) { - GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale"); + GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale\n"); + +#ifdef USE_GNOME_CONFINE_HACK + /* Use a bug in GNOME to check GNOME is in use. If the bug is fixed this won't cause an issue + * as T98793 has been fixed up-stream too, but not in a release at time of writing. */ + use_gnome_confine_hack = true; +#endif + return; } } @@ -1877,24 +2585,29 @@ static void xdg_output_handle_logical_size(void *data, output->has_size_logical = true; } -static void xdg_output_handle_done(void * /*data*/, struct zxdg_output_v1 * /*xdg_output*/) +static void xdg_output_handle_done(void *data, struct zxdg_output_v1 * /*xdg_output*/) { + CLOG_INFO(LOG, 2, "done"); /* NOTE: `xdg-output.done` events are deprecated and only apply below version 3 of the protocol. * `wl-output.done` event will be emitted in version 3 or higher. */ + GWL_Output *output = static_cast<GWL_Output *>(data); + if (zxdg_output_v1_get_version(output->xdg_output) < 3) { + output_handle_done(data, output->wl_output); + } } static void xdg_output_handle_name(void * /*data*/, struct zxdg_output_v1 * /*xdg_output*/, - const char * /*name*/) + const char *name) { - /* Pass. */ + CLOG_INFO(LOG, 2, "name (name=\"%s\")", name); } static void xdg_output_handle_description(void * /*data*/, struct zxdg_output_v1 * /*xdg_output*/, - const char * /*description*/) + const char *description) { - /* Pass. */ + CLOG_INFO(LOG, 2, "description (description=\"%s\")", description); } static const struct zxdg_output_v1_listener xdg_output_listener = { @@ -1905,24 +2618,38 @@ static const struct zxdg_output_v1_listener xdg_output_listener = { xdg_output_handle_description, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Output), #wl_output_listener * \{ */ +static CLG_LogRef LOG_WL_OUTPUT = {"ghost.wl.handle.output"}; +#define LOG (&LOG_WL_OUTPUT) + static void output_handle_geometry(void *data, struct wl_output * /*wl_output*/, - int32_t /*x*/, - int32_t /*y*/, - int32_t physical_width, - int32_t physical_height, - int32_t /*subpixel*/, + const int32_t /*x*/, + const int32_t /*y*/, + const int32_t physical_width, + const int32_t physical_height, + const int32_t /*subpixel*/, const char *make, const char *model, - int32_t transform) + const int32_t transform) { - output_t *output = static_cast<output_t *>(data); + CLOG_INFO(LOG, + 2, + "geometry (make=\"%s\", model=\"%s\", transform=%d, size=[%d, %d])", + make, + model, + transform, + physical_width, + physical_height); + + GWL_Output *output = static_cast<GWL_Output *>(data); output->transform = transform; output->make = std::string(make); output->model = std::string(model); @@ -1932,23 +2659,26 @@ static void output_handle_geometry(void *data, static void output_handle_mode(void *data, struct wl_output * /*wl_output*/, - uint32_t flags, - int32_t width, - int32_t height, - int32_t /*refresh*/) -{ - output_t *output = static_cast<output_t *>(data); - - if (flags & WL_OUTPUT_MODE_CURRENT) { - output->size_native[0] = width; - output->size_native[1] = height; - - /* Don't rotate this yet, `wl-output` coordinates are transformed in - * handle_done and `xdg-output` coordinates are pre-transformed. */ - if (!output->has_size_logical) { - output->size_logical[0] = width; - output->size_logical[1] = height; - } + const uint32_t flags, + const int32_t width, + const int32_t height, + const int32_t /*refresh*/) +{ + if ((flags & WL_OUTPUT_MODE_CURRENT) == 0) { + CLOG_INFO(LOG, 2, "mode (skipped)"); + return; + } + CLOG_INFO(LOG, 2, "mode (size=[%d, %d], flags=%u)", width, height, flags); + + GWL_Output *output = static_cast<GWL_Output *>(data); + output->size_native[0] = width; + output->size_native[1] = height; + + /* Don't rotate this yet, `wl-output` coordinates are transformed in + * handle_done and `xdg-output` coordinates are pre-transformed. */ + if (!output->has_size_logical) { + output->size_logical[0] = width; + output->size_logical[1] = height; } } @@ -1962,11 +2692,13 @@ static void output_handle_mode(void *data, */ static void output_handle_done(void *data, struct wl_output * /*wl_output*/) { - output_t *output = static_cast<output_t *>(data); + CLOG_INFO(LOG, 2, "done"); + + GWL_Output *output = static_cast<GWL_Output *>(data); int32_t size_native[2]; if (output->transform & WL_OUTPUT_TRANSFORM_90) { size_native[0] = output->size_native[1]; - size_native[1] = output->size_native[1]; + size_native[1] = output->size_native[0]; } else { size_native[0] = output->size_native[0]; @@ -1987,9 +2719,17 @@ static void output_handle_done(void *data, struct wl_output * /*wl_output*/) } } -static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor) +static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, const int32_t factor) { - static_cast<output_t *>(data)->scale = factor; + CLOG_INFO(LOG, 2, "scale"); + static_cast<GWL_Output *>(data)->scale = factor; + + if (window_manager) { + for (GHOST_IWindow *iwin : window_manager->getWindows()) { + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin); + win->outputs_changed_update_scale(); + } + } } static const struct wl_output_listener output_listener = { @@ -1999,14 +2739,24 @@ static const struct wl_output_listener output_listener = { output_handle_scale, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (XDG WM Base), #xdg_wm_base_listener * \{ */ -static void shell_handle_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial) +#ifndef WITH_GHOST_WAYLAND_LIBDECOR + +static CLG_LogRef LOG_WL_XDG_WM_BASE = {"ghost.wl.handle.xdg_wm_base"}; +# define LOG (&LOG_WL_XDG_WM_BASE) + +static void shell_handle_ping(void * /*data*/, + struct xdg_wm_base *xdg_wm_base, + const uint32_t serial) { + CLOG_INFO(LOG, 2, "ping"); xdg_wm_base_pong(xdg_wm_base, serial); } @@ -2014,23 +2764,67 @@ static const struct xdg_wm_base_listener shell_listener = { shell_handle_ping, }; +# undef LOG + +#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (LibDecor), #libdecor_interface + * \{ */ + +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + +static CLG_LogRef LOG_WL_LIBDECOR = {"ghost.wl.handle.libdecor"}; +# define LOG (&LOG_WL_LIBDECOR) + +static void decor_handle_error(struct libdecor * /*context*/, + enum libdecor_error error, + const char *message) +{ + CLOG_INFO(LOG, 2, "error (id=%d, message=%s)", error, message); + + (void)(error); + (void)(message); + GHOST_PRINT("decoration error (" << error << "): " << message << std::endl); + exit(EXIT_FAILURE); +} + +static struct libdecor_interface libdecor_interface = { + decor_handle_error, +}; + +# undef LOG + +#endif /* WITH_GHOST_WAYLAND_LIBDECOR. */ + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Registry), #wl_registry_listener * \{ */ +static CLG_LogRef LOG_WL_REGISTRY = {"ghost.wl.handle.registry"}; +#define LOG (&LOG_WL_REGISTRY) + static void global_handle_add(void *data, struct wl_registry *wl_registry, - uint32_t name, + const uint32_t name, const char *interface, - uint32_t /*version*/) + const uint32_t version) { - struct display_t *display = static_cast<struct display_t *>(data); + /* Log last since it can be noted if the interface was handled or not. */ + bool found = true; + + struct GWL_Display *display = static_cast<struct GWL_Display *>(data); if (!strcmp(interface, wl_compositor_interface.name)) { display->compositor = static_cast<wl_compositor *>( wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3)); } +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + /* Pass. */ +#else else if (!strcmp(interface, xdg_wm_base_interface.name)) { display->xdg_shell = static_cast<xdg_wm_base *>( wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1)); @@ -2040,19 +2834,23 @@ static void global_handle_add(void *data, display->xdg_decoration_manager = static_cast<zxdg_decoration_manager_v1 *>( wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1)); } +#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */ else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) { display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>( - wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 3)); - for (output_t *output : display->outputs) { + wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 2)); + for (GWL_Output *output : display->outputs) { output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager, output->wl_output); zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); } } else if (!strcmp(interface, wl_output_interface.name)) { - output_t *output = new output_t; + GWL_Output *output = new GWL_Output; output->wl_output = static_cast<wl_output *>( wl_registry_bind(wl_registry, name, &wl_output_interface, 2)); + ghost_wl_output_tag(output->wl_output); + wl_output_set_user_data(output->wl_output, output); + display->outputs.push_back(output); wl_output_add_listener(output->wl_output, &output_listener, output); @@ -2063,14 +2861,14 @@ static void global_handle_add(void *data, } } else if (!strcmp(interface, wl_seat_interface.name)) { - input_t *input = new input_t; - input->system = display->system; - input->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - input->data_source = new data_source_t; - input->wl_seat = static_cast<wl_seat *>( - wl_registry_bind(wl_registry, name, &wl_seat_interface, 4)); - display->inputs.push_back(input); - wl_seat_add_listener(input->wl_seat, &seat_listener, input); + GWL_Seat *seat = new GWL_Seat; + seat->system = display->system; + seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + seat->data_source = new GWL_DataSource; + seat->wl_seat = static_cast<wl_seat *>( + wl_registry_bind(wl_registry, name, &wl_seat_interface, 5)); + display->seats.push_back(seat); + wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); } else if (!strcmp(interface, wl_shm_interface.name)) { display->shm = static_cast<wl_shm *>( @@ -2092,6 +2890,17 @@ static void global_handle_add(void *data, display->pointer_constraints = static_cast<zwp_pointer_constraints_v1 *>( wl_registry_bind(wl_registry, name, &zwp_pointer_constraints_v1_interface, 1)); } + else { + found = false; + } + + CLOG_INFO(LOG, + 2, + "add %s(interface=%s, version=%u, name=%u)", + found ? "" : "(skipped), ", + interface, + version, + name); } /** @@ -2105,8 +2914,9 @@ static void global_handle_add(void *data, */ static void global_handle_remove(void * /*data*/, struct wl_registry * /*wl_registry*/, - uint32_t /*name*/) + const uint32_t name) { + CLOG_INFO(LOG, 2, "remove (name=%u)", name); } static const struct wl_registry_listener registry_listener = { @@ -2114,15 +2924,17 @@ static const struct wl_registry_listener registry_listener = { global_handle_remove, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ -/** \name Ghost Implementation +/** \name GHOST Implementation * - * Wayland specific implementation of the GHOST_System interface. + * WAYLAND specific implementation of the #GHOST_System interface. * \{ */ -GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t) +GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new GWL_Display) { wl_log_set_handler_client(ghost_wayland_log_handler); @@ -2143,25 +2955,32 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t) wl_display_roundtrip(d->display); wl_registry_destroy(registry); +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + d->decor_context = libdecor_new(d->display, &libdecor_interface); + if (!d->decor_context) { + display_destroy(d); + throw std::runtime_error("Wayland: unable to create window decorations!"); + } +#else if (!d->xdg_shell) { display_destroy(d); throw std::runtime_error("Wayland: unable to access xdg_shell!"); } +#endif /* Register data device per seat for IPC between Wayland clients. */ if (d->data_device_manager) { - for (input_t *input : d->inputs) { - input->data_device = wl_data_device_manager_get_data_device(d->data_device_manager, - input->wl_seat); - wl_data_device_add_listener(input->data_device, &data_device_listener, input); + for (GWL_Seat *seat : d->seats) { + seat->data_device = wl_data_device_manager_get_data_device(d->data_device_manager, + seat->wl_seat); + wl_data_device_add_listener(seat->data_device, &data_device_listener, seat); } } if (d->tablet_manager) { - for (input_t *input : d->inputs) { - input->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(d->tablet_manager, - input->wl_seat); - zwp_tablet_seat_v2_add_listener(input->tablet_seat, &tablet_seat_listener, input); + for (GWL_Seat *seat : d->seats) { + seat->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(d->tablet_manager, seat->wl_seat); + zwp_tablet_seat_v2_add_listener(seat->tablet_seat, &tablet_seat_listener, seat); } } } @@ -2171,9 +2990,36 @@ GHOST_SystemWayland::~GHOST_SystemWayland() display_destroy(d); } +GHOST_TSuccess GHOST_SystemWayland::init() +{ + GHOST_TSuccess success = GHOST_System::init(); + + if (success) { +#ifdef WITH_INPUT_NDOF + m_ndofManager = new GHOST_NDOFManagerUnix(*this); +#endif + return GHOST_kSuccess; + } + + return GHOST_kFailure; +} + bool GHOST_SystemWayland::processEvents(bool waitForEvent) { - const bool fired = getTimerManager()->fireTimers(getMilliSeconds()); + bool any_processed = false; + + if (getTimerManager()->fireTimers(getMilliSeconds())) { + any_processed = true; + } + +#ifdef WITH_INPUT_NDOF + if (static_cast<GHOST_NDOFManagerUnix *>(m_ndofManager)->processEvents()) { + /* As NDOF bypasses WAYLAND event handling, + * never wait for an event when an NDOF event was found. */ + waitForEvent = false; + any_processed = true; + } +#endif /* WITH_INPUT_NDOF */ if (waitForEvent) { wl_display_dispatch(d->display); @@ -2182,56 +3028,69 @@ bool GHOST_SystemWayland::processEvents(bool waitForEvent) wl_display_roundtrip(d->display); } - return fired || (getEventManager()->getNumEvents() > 0); + if ((getEventManager()->getNumEvents() > 0)) { + any_processed = true; + } + + return any_processed; } -int GHOST_SystemWayland::setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) +bool GHOST_SystemWayland::setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) { - return 0; + return false; } GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) const { - if (d->inputs.empty()) { + if (UNLIKELY(d->seats.empty())) { return GHOST_kFailure; } - static const xkb_state_component mods_all = xkb_state_component( - XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED | - XKB_STATE_MODS_EFFECTIVE); + GWL_Seat *seat = d->seats[0]; bool val; - /* NOTE: XKB doesn't seem to differentiate between left/right modifiers. */ + /* NOTE: XKB doesn't differentiate between left/right modifiers + * for it's internal modifier state storage. */ + const xkb_mod_mask_t state = xkb_state_serialize_mods(seat->xkb_state, XKB_STATE_MODS_ALL); + +#define MOD_TEST(state, mod) ((mod != XKB_MOD_INVALID) && (state & (1 << mod))) - val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == 1; + val = MOD_TEST(state, seat->xkb_keymap_mod_index.shift); keys.set(GHOST_kModifierKeyLeftShift, val); keys.set(GHOST_kModifierKeyRightShift, val); - val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_ALT, mods_all) == 1; + val = MOD_TEST(state, seat->xkb_keymap_mod_index.alt); keys.set(GHOST_kModifierKeyLeftAlt, val); keys.set(GHOST_kModifierKeyRightAlt, val); - val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_CTRL, mods_all) == 1; + val = MOD_TEST(state, seat->xkb_keymap_mod_index.ctrl); keys.set(GHOST_kModifierKeyLeftControl, val); keys.set(GHOST_kModifierKeyRightControl, val); - val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_LOGO, mods_all) == 1; + val = MOD_TEST(state, seat->xkb_keymap_mod_index.logo); keys.set(GHOST_kModifierKeyOS, val); - val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_NUM, mods_all) == 1; - keys.set(GHOST_kModifierKeyNumMasks, val); + val = MOD_TEST(state, seat->xkb_keymap_mod_index.num); + keys.set(GHOST_kModifierKeyNum, val); + +#undef MOD_TEST return GHOST_kSuccess; } GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const { - if (d->inputs.empty()) { + if (UNLIKELY(d->seats.empty())) { + return GHOST_kFailure; + } + GWL_Seat *seat = d->seats[0]; + GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_active(seat); + if (!seat_state_pointer) { return GHOST_kFailure; } - buttons = d->inputs[0]->buttons; + buttons = seat_state_pointer->buttons; return GHOST_kSuccess; } @@ -2244,15 +3103,15 @@ char *GHOST_SystemWayland::getClipboard(bool /*selection*/) const void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) const { - if (!d->data_device_manager || d->inputs.empty()) { + if (UNLIKELY(!d->data_device_manager || d->seats.empty())) { return; } - input_t *input = d->inputs[0]; + GWL_Seat *seat = d->seats[0]; - std::lock_guard lock{input->data_source_mutex}; + std::lock_guard lock{seat->data_source_mutex}; - data_source_t *data_source = input->data_source; + GWL_DataSource *data_source = seat->data_source; /* Copy buffer. */ free(data_source->buffer_out); @@ -2262,15 +3121,15 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) c data_source->data_source = wl_data_device_manager_create_data_source(d->data_device_manager); - wl_data_source_add_listener(data_source->data_source, &data_source_listener, input); + wl_data_source_add_listener(data_source->data_source, &data_source_listener, seat); for (const std::string &type : mime_send) { wl_data_source_offer(data_source->data_source, type.c_str()); } - if (input->data_device) { + if (seat->data_device) { wl_data_device_set_selection( - input->data_device, data_source->data_source, input->data_source_serial); + seat->data_device, data_source->data_source, seat->data_source_serial); } } @@ -2279,70 +3138,145 @@ uint8_t GHOST_SystemWayland::getNumDisplays() const return d ? uint8_t(d->outputs.size()) : 0; } -GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) const +static GHOST_TSuccess getCursorPositionClientRelative_impl( + const GWL_SeatStatePointer *seat_state_pointer, + const GHOST_WindowWayland *win, + int32_t &x, + int32_t &y) { - if (d->inputs.empty()) { + const wl_fixed_t scale = win->scale(); + x = wl_fixed_to_int(scale * seat_state_pointer->xy[0]); + y = wl_fixed_to_int(scale * seat_state_pointer->xy[1]); + return GHOST_kSuccess; +} + +static GHOST_TSuccess setCursorPositionClientRelative_impl(GWL_Seat *seat, + GHOST_WindowWayland *win, + const int32_t x, + const int32_t y) +{ + /* NOTE: WAYLAND doesn't support warping the cursor. + * However when grab is enabled, we already simulate a cursor location + * so that can be set to a new location. */ + if (!seat->relative_pointer) { return GHOST_kFailure; } + const wl_fixed_t scale = win->scale(); + const wl_fixed_t xy_next[2] = { + wl_fixed_from_int(x) / scale, + wl_fixed_from_int(y) / scale, + }; - input_t *input = d->inputs[0]; - struct wl_surface *surface = nullptr; - if (input->pointer_serial == input->cursor_serial) { - surface = input->focus_pointer; + /* As the cursor was "warped" generate an event at the new location. */ + relative_pointer_handle_relative_motion_impl(seat, win, xy_next); + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_SystemWayland::getCursorPositionClientRelative(const GHOST_IWindow *window, + int32_t &x, + int32_t &y) const +{ + if (UNLIKELY(d->seats.empty())) { + return GHOST_kFailure; } - else if (input->tablet_serial == input->cursor_serial) { - surface = input->focus_tablet; + GWL_Seat *seat = d->seats[0]; + GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_active(seat); + if (!seat_state_pointer || !seat_state_pointer->wl_surface) { + return GHOST_kFailure; } - if (!surface) { + const GHOST_WindowWayland *win = static_cast<const GHOST_WindowWayland *>(window); + return getCursorPositionClientRelative_impl(seat_state_pointer, win, x, y); +} + +GHOST_TSuccess GHOST_SystemWayland::setCursorPositionClientRelative(GHOST_IWindow *window, + const int32_t x, + const int32_t y) +{ + if (UNLIKELY(d->seats.empty())) { return GHOST_kFailure; } + GWL_Seat *seat = d->seats[0]; + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(window); + return setCursorPositionClientRelative_impl(seat, win, x, y); +} - GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface)); - if (!win) { +GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) const +{ + if (UNLIKELY(d->seats.empty())) { + return GHOST_kFailure; + } + GWL_Seat *seat = d->seats[0]; + GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_active(seat); + if (!seat_state_pointer) { return GHOST_kFailure; } - const wl_fixed_t scale = win->scale(); - x = wl_fixed_to_int(scale * input->xy[0]); - y = wl_fixed_to_int(scale * input->xy[1]); - return GHOST_kSuccess; + if (wl_surface *wl_surface_focus = seat_state_pointer->wl_surface) { + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + return getCursorPositionClientRelative_impl(seat_state_pointer, win, x, y); + } + return GHOST_kFailure; } -GHOST_TSuccess GHOST_SystemWayland::setCursorPosition(int32_t /*x*/, int32_t /*y*/) +GHOST_TSuccess GHOST_SystemWayland::setCursorPosition(const int32_t x, const int32_t y) { + if (UNLIKELY(d->seats.empty())) { + return GHOST_kFailure; + } + GWL_Seat *seat = d->seats[0]; + + /* Intentionally different from `getCursorPosition` which supports both tablet & pointer. + * In the case of setting the cursor location, tablets don't support this. */ + if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) { + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus); + return setCursorPositionClientRelative_impl(seat, win, x, y); + } return GHOST_kFailure; } void GHOST_SystemWayland::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const { - if (getNumDisplays() > 0) { - /* We assume first output as main. */ - width = uint32_t(d->outputs[0]->size_native[0]) / d->outputs[0]->scale; - height = uint32_t(d->outputs[0]->size_native[1]) / d->outputs[0]->scale; + if (getNumDisplays() == 0) { + return; } + /* We assume first output as main. */ + width = uint32_t(d->outputs[0]->size_native[0]); + height = uint32_t(d->outputs[0]->size_native[1]); } void GHOST_SystemWayland::getAllDisplayDimensions(uint32_t &width, uint32_t &height) const { - getMainDisplayDimensions(width, height); + int32_t xy_min[2] = {INT32_MAX, INT32_MAX}; + int32_t xy_max[2] = {INT32_MIN, INT32_MIN}; + + for (const GWL_Output *output : d->outputs) { + int32_t xy[2] = {0, 0}; + if (output->has_position_logical) { + xy[0] = output->position_logical[0]; + xy[1] = output->position_logical[1]; + } + xy_min[0] = std::min(xy_min[0], xy[0]); + xy_min[1] = std::min(xy_min[1], xy[1]); + xy_max[0] = std::max(xy_max[0], xy[0] + output->size_native[0]); + xy_max[1] = std::max(xy_max[1], xy[1] + output->size_native[1]); + } + + width = xy_max[0] - xy_min[0]; + height = xy_max[1] - xy_min[1]; } -GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*glSettings*/) +static GHOST_Context *createOffscreenContext_impl(GHOST_SystemWayland *system, + struct wl_display *wl_display, + wl_egl_window *egl_window) { - /* Create new off-screen window. */ - wl_surface *os_surface = wl_compositor_create_surface(compositor()); - wl_egl_window *os_egl_window = wl_egl_window_create(os_surface, int(1), int(1)); - - d->os_surfaces.push_back(os_surface); - d->os_egl_windows.push_back(os_egl_window); - GHOST_Context *context; for (int minor = 6; minor >= 0; --minor) { - context = new GHOST_ContextEGL(this, + context = new GHOST_ContextEGL(system, false, - EGLNativeWindowType(os_egl_window), - EGLNativeDisplayType(d->display), + EGLNativeWindowType(egl_window), + EGLNativeDisplayType(wl_display), EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, 4, minor, @@ -2356,10 +3290,10 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*g delete context; } - context = new GHOST_ContextEGL(this, + context = new GHOST_ContextEGL(system, false, - EGLNativeWindowType(os_egl_window), - EGLNativeDisplayType(d->display), + EGLNativeWindowType(egl_window), + EGLNativeDisplayType(wl_display), EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, 3, 3, @@ -2371,31 +3305,59 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*g return context; } delete context; + return nullptr; +} - GHOST_PRINT("Cannot create off-screen EGL context" << std::endl); +GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*glSettings*/) +{ + /* Create new off-screen window. */ + wl_surface *wl_surface = wl_compositor_create_surface(compositor()); + wl_egl_window *egl_window = wl_surface ? wl_egl_window_create(wl_surface, 1, 1) : nullptr; - return nullptr; + GHOST_Context *context = createOffscreenContext_impl(this, d->display, egl_window); + + if (!context) { + GHOST_PRINT("Cannot create off-screen EGL context" << std::endl); + if (wl_surface) { + wl_surface_destroy(wl_surface); + } + if (egl_window) { + wl_egl_window_destroy(egl_window); + } + return nullptr; + } + + wl_surface_set_user_data(wl_surface, egl_window); + context->setUserData(wl_surface); + + return context; } GHOST_TSuccess GHOST_SystemWayland::disposeContext(GHOST_IContext *context) { + struct wl_surface *wl_surface = (struct wl_surface *)((GHOST_Context *)context)->getUserData(); + wl_egl_window *egl_window = (wl_egl_window *)wl_surface_get_user_data(wl_surface); + wl_egl_window_destroy(egl_window); + wl_surface_destroy(wl_surface); + delete context; + return GHOST_kSuccess; } GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title, - int32_t left, - int32_t top, - uint32_t width, - uint32_t height, - GHOST_TWindowState state, - GHOST_TDrawingContextType type, - GHOST_GLSettings glSettings, + const int32_t left, + const int32_t top, + const uint32_t width, + const uint32_t height, + const GHOST_TWindowState state, + const GHOST_TDrawingContextType type, + const GHOST_GLSettings glSettings, const bool exclusive, const bool is_dialog, const GHOST_IWindow *parentWindow) { - /* globally store pointer to window manager */ + /* Globally store pointer to window manager. */ if (!window_manager) { window_manager = getWindowManager(); } @@ -2429,106 +3391,209 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title, return window; } -wl_display *GHOST_SystemWayland::display() +/** + * Show the buffer defined by #cursor_buffer_set without changing anything else, + * so #cursor_buffer_hide can be used to display it again. + * + * The caller is responsible for setting `seat->cursor.visible`. + */ +static void cursor_buffer_show(const GWL_Seat *seat) { - return d->display; -} + const GWL_Cursor *c = &seat->cursor; + + if (seat->wl_pointer) { + const int scale = c->is_custom ? c->custom_scale : seat->pointer.theme_scale; + const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; + const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; + if (seat->wl_pointer) { + wl_pointer_set_cursor( + seat->wl_pointer, seat->pointer.serial, c->wl_surface, hotspot_x, hotspot_y); + } + } -wl_compositor *GHOST_SystemWayland::compositor() -{ - return d->compositor; + if (!seat->tablet_tools.empty()) { + const int scale = c->is_custom ? c->custom_scale : seat->tablet.theme_scale; + const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; + const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->tablet_tools) { + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>( + zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); + zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, + seat->tablet.serial, + tablet_tool->wl_surface_cursor, + hotspot_x, + hotspot_y); + } + } } -xdg_wm_base *GHOST_SystemWayland::xdg_shell() +/** + * Hide the buffer defined by #cursor_buffer_set without changing anything else, + * so #cursor_buffer_show can be used to display it again. + * + * The caller is responsible for setting `seat->cursor.visible`. + */ +static void cursor_buffer_hide(const GWL_Seat *seat) { - return d->xdg_shell; + wl_pointer_set_cursor(seat->wl_pointer, seat->pointer.serial, nullptr, 0, 0); + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->tablet_tools) { + zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, seat->tablet.serial, nullptr, 0, 0); + } } -zxdg_decoration_manager_v1 *GHOST_SystemWayland::xdg_decoration_manager() +/** + * Needed to ensure the cursor size is always a multiple of scale. + */ +static int cursor_buffer_compatible_scale_from_image(const struct wl_cursor_image *wl_image, + int scale) { - return d->xdg_decoration_manager; + const int32_t image_size_x = int32_t(wl_image->width); + const int32_t image_size_y = int32_t(wl_image->height); + while (scale > 1) { + if ((image_size_x % scale) == 0 && (image_size_y % scale) == 0) { + break; + } + scale -= 1; + } + return scale; } -const std::vector<output_t *> &GHOST_SystemWayland::outputs() const +static void cursor_buffer_set_surface_impl(const GWL_Seat *seat, + wl_buffer *buffer, + struct wl_surface *wl_surface, + const int scale) { - return d->outputs; + const wl_cursor_image *wl_image = &seat->cursor.wl_image; + const int32_t image_size_x = int32_t(wl_image->width); + const int32_t image_size_y = int32_t(wl_image->height); + GHOST_ASSERT((image_size_x % scale) == 0 && (image_size_y % scale) == 0, + "The size must be a multiple of the scale!"); + + wl_surface_set_buffer_scale(wl_surface, scale); + wl_surface_attach(wl_surface, buffer, 0, 0); + wl_surface_damage(wl_surface, 0, 0, image_size_x, image_size_y); + wl_surface_commit(wl_surface); } -wl_shm *GHOST_SystemWayland::shm() const +static void cursor_buffer_set(const GWL_Seat *seat, wl_buffer *buffer) { - return d->shm; -} + const GWL_Cursor *c = &seat->cursor; + const wl_cursor_image *wl_image = &seat->cursor.wl_image; + const bool visible = (c->visible && c->is_hardware); + + /* This is a requirement of WAYLAND, when this isn't the case, + * it causes Blender's window to close intermittently. */ + if (seat->wl_pointer) { + const int scale = cursor_buffer_compatible_scale_from_image( + wl_image, c->is_custom ? c->custom_scale : seat->pointer.theme_scale); + const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale; + const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale; + cursor_buffer_set_surface_impl(seat, buffer, c->wl_surface, scale); + wl_pointer_set_cursor(seat->wl_pointer, + seat->pointer.serial, + visible ? c->wl_surface : nullptr, + hotspot_x, + hotspot_y); + } -void GHOST_SystemWayland::setSelection(const std::string &selection) -{ - this->selection = selection; + /* Set the cursor for all tablet tools as well. */ + if (!seat->tablet_tools.empty()) { + const int scale = cursor_buffer_compatible_scale_from_image( + wl_image, c->is_custom ? c->custom_scale : seat->tablet.theme_scale); + const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale; + const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale; + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->tablet_tools) { + GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>( + zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); + cursor_buffer_set_surface_impl(seat, buffer, tablet_tool->wl_surface_cursor, scale); + zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, + seat->tablet.serial, + visible ? tablet_tool->wl_surface_cursor : nullptr, + hotspot_x, + hotspot_y); + } + } } -static void set_cursor_buffer(input_t *input, wl_buffer *buffer) -{ - cursor_t *c = &input->cursor; - - c->visible = (buffer != nullptr); - - const int32_t image_size_x = int32_t(c->wl_image.width); - const int32_t image_size_y = int32_t(c->wl_image.height); +enum eCursorSetMode { + CURSOR_VISIBLE_ALWAYS_SET = 1, + CURSOR_VISIBLE_ONLY_HIDE, + CURSOR_VISIBLE_ONLY_SHOW, +}; - const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / c->scale; - const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / c->scale; +static void cursor_visible_set(GWL_Seat *seat, + const bool visible, + const bool is_hardware, + const enum eCursorSetMode set_mode) +{ + GWL_Cursor *cursor = &seat->cursor; + const bool was_visible = cursor->is_hardware && cursor->visible; + const bool use_visible = is_hardware && visible; - if (buffer) { - wl_surface_set_buffer_scale(c->wl_surface, c->scale); - wl_surface_attach(c->wl_surface, buffer, 0, 0); - wl_surface_damage(c->wl_surface, 0, 0, image_size_x, image_size_y); - wl_surface_commit(c->wl_surface); + if (set_mode == CURSOR_VISIBLE_ALWAYS_SET) { + /* Pass. */ + } + else if ((set_mode == CURSOR_VISIBLE_ONLY_SHOW)) { + if (!use_visible) { + return; + } + } + else if ((set_mode == CURSOR_VISIBLE_ONLY_HIDE)) { + if (use_visible) { + return; + } } - wl_pointer_set_cursor(input->wl_pointer, - input->pointer_serial, - c->visible ? c->wl_surface : nullptr, - hotspot_x, - hotspot_y); - - /* Set the cursor for all tablet tools as well. */ - for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>( - zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); - - if (buffer) { - /* FIXME: for some reason cursor scale is applied twice (when the scale isn't 1x), - * this happens both in gnome-shell & KDE. Setting the surface scale here doesn't help. */ - wl_surface_set_buffer_scale(tool_input->cursor_surface, c->scale); - wl_surface_attach(tool_input->cursor_surface, buffer, 0, 0); - wl_surface_damage(tool_input->cursor_surface, 0, 0, image_size_x, image_size_y); - wl_surface_commit(tool_input->cursor_surface); + if (use_visible) { + if (!was_visible) { + cursor_buffer_show(seat); } + } + else { + if (was_visible) { + cursor_buffer_hide(seat); + } + } + cursor->visible = visible; + cursor->is_hardware = is_hardware; +} - zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, - input->tablet_serial, - c->visible ? tool_input->cursor_surface : nullptr, - hotspot_x, - hotspot_y); +static bool cursor_is_software(const GHOST_TGrabCursorMode mode, const bool use_software_confine) +{ + if (mode == GHOST_kGrabWrap) { + return true; + } +#ifdef USE_GNOME_CONFINE_HACK + if (mode == GHOST_kGrabNormal) { + if (use_software_confine) { + return true; + } } +#else + (void)use_software_confine; +#endif + return false; } -GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) +GHOST_TSuccess GHOST_SystemWayland::setCursorShape(const GHOST_TStandardCursor shape) { - if (d->inputs.empty()) { + if (UNLIKELY(d->seats.empty())) { return GHOST_kFailure; } - const std::string cursor_name = cursors.count(shape) ? cursors.at(shape) : - cursors.at(GHOST_kStandardCursorDefault); + auto cursor_find = cursors.find(shape); + const char *cursor_name = (cursor_find == cursors.end()) ? + cursors.at(GHOST_kStandardCursorDefault) : + (*cursor_find).second; - input_t *input = d->inputs[0]; - cursor_t *c = &input->cursor; + GWL_Seat *seat = d->seats[0]; + GWL_Cursor *c = &seat->cursor; if (!c->wl_theme) { - /* The cursor surface hasn't entered an output yet. Initialize theme with scale 1. */ - c->wl_theme = wl_cursor_theme_load( - c->theme_name.c_str(), c->size, d->inputs[0]->system->shm()); + /* The cursor wl_surface hasn't entered an output yet. Initialize theme with scale 1. */ + c->wl_theme = wl_cursor_theme_load(c->theme_name.c_str(), c->size, d->seats[0]->system->shm()); } - wl_cursor *cursor = wl_cursor_theme_get_cursor(c->wl_theme, cursor_name.c_str()); + wl_cursor *cursor = wl_cursor_theme_get_cursor(c->wl_theme, cursor_name); if (!cursor) { GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl); @@ -2541,81 +3606,56 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) return GHOST_kFailure; } + c->visible = true; + c->is_custom = false; c->wl_buffer = buffer; c->wl_image = *image; - set_cursor_buffer(input, buffer); + cursor_buffer_set(seat, buffer); return GHOST_kSuccess; } -GHOST_TSuccess GHOST_SystemWayland::hasCursorShape(GHOST_TStandardCursor cursorShape) +GHOST_TSuccess GHOST_SystemWayland::hasCursorShape(const GHOST_TStandardCursor cursorShape) { - return GHOST_TSuccess(cursors.count(cursorShape) && !cursors.at(cursorShape).empty()); + auto cursor_find = cursors.find(cursorShape); + if (cursor_find == cursors.end()) { + return GHOST_kFailure; + } + const char *value = (*cursor_find).second; + if (*value == '\0') { + return GHOST_kFailure; + } + return GHOST_kSuccess; } GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, uint8_t *mask, - int sizex, - int sizey, - int hotX, - int hotY, - bool /*canInvertColor*/) + const int sizex, + const int sizey, + const int hotX, + const int hotY, + const bool /*canInvertColor*/) { - if (d->inputs.empty()) { - return GHOST_kFailure; - } - - cursor_t *cursor = &d->inputs[0]->cursor; - - static const int32_t stride = sizex * 4; /* ARGB */ - cursor->file_buffer->size = (size_t)stride * sizey; - -#ifdef HAVE_MEMFD_CREATE - const int fd = memfd_create("blender-cursor-custom", MFD_CLOEXEC | MFD_ALLOW_SEALING); - if (fd >= 0) { - fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); - } -#else - char *path = getenv("XDG_RUNTIME_DIR"); - if (!path) { - errno = ENOENT; + if (UNLIKELY(d->seats.empty())) { return GHOST_kFailure; } - char *tmpname; - asprintf(&tmpname, "%s/%s", path, "blender-XXXXXX"); - const int fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0) { - unlink(tmpname); - } - free(tmpname); -#endif - - if (fd < 0) { - return GHOST_kFailure; - } + GWL_Cursor *cursor = &d->seats[0]->cursor; - if (posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size)) != 0) { - return GHOST_kFailure; + if (cursor->custom_data) { + munmap(cursor->custom_data, cursor->custom_data_size); + cursor->custom_data = nullptr; + cursor->custom_data_size = 0; /* Not needed, but the value is no longer meaningful. */ } - cursor->file_buffer->data = mmap( - nullptr, cursor->file_buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - - if (cursor->file_buffer->data == MAP_FAILED) { - close(fd); + const int32_t size_xy[2] = {sizex, sizey}; + wl_buffer *buffer = ghost_wl_buffer_create_for_image( + d->shm, size_xy, WL_SHM_FORMAT_ARGB8888, &cursor->custom_data, &cursor->custom_data_size); + if (buffer == nullptr) { return GHOST_kFailure; } - struct wl_shm_pool *pool = wl_shm_create_pool(d->shm, fd, int32_t(cursor->file_buffer->size)); - - wl_buffer *buffer = wl_shm_pool_create_buffer( - pool, 0, sizex, sizey, stride, WL_SHM_FORMAT_ARGB8888); - - wl_shm_pool_destroy(pool); - close(fd); - wl_buffer_add_listener(buffer, &cursor_buffer_listener, cursor); static constexpr uint32_t black = 0xFF000000; @@ -2626,7 +3666,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, uint32_t *pixel; for (int y = 0; y < sizey; ++y) { - pixel = &static_cast<uint32_t *>(cursor->file_buffer->data)[y * sizex]; + pixel = &static_cast<uint32_t *>(cursor->custom_data)[y * sizex]; for (int x = 0; x < sizex; ++x) { if ((x % 8) == 0) { datab = *bitmap++; @@ -2648,37 +3688,49 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, } } + cursor->visible = true; + cursor->is_custom = true; + cursor->custom_scale = 1; /* TODO: support Hi-DPI custom cursors. */ cursor->wl_buffer = buffer; cursor->wl_image.width = uint32_t(sizex); cursor->wl_image.height = uint32_t(sizey); cursor->wl_image.hotspot_x = uint32_t(hotX); cursor->wl_image.hotspot_y = uint32_t(hotY); - set_cursor_buffer(d->inputs[0], buffer); + cursor_buffer_set(d->seats[0], buffer); return GHOST_kSuccess; } -GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible) +GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap) { - if (d->inputs.empty()) { + GWL_Cursor *cursor = &d->seats[0]->cursor; + if (cursor->custom_data == nullptr) { + return GHOST_kFailure; + } + if (!cursor->is_custom) { return GHOST_kFailure; } - input_t *input = d->inputs[0]; + bitmap->data_size[0] = cursor->wl_image.width; + bitmap->data_size[1] = cursor->wl_image.height; - cursor_t *cursor = &input->cursor; - if (visible) { - if (!cursor->visible) { - set_cursor_buffer(input, cursor->wl_buffer); - } - } - else { - if (cursor->visible) { - set_cursor_buffer(input, nullptr); - } + bitmap->hot_spot[0] = cursor->wl_image.hotspot_x; + bitmap->hot_spot[1] = cursor->wl_image.hotspot_y; + + bitmap->data = (uint8_t *)static_cast<void *>(cursor->custom_data); + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(const bool visible) +{ + if (UNLIKELY(d->seats.empty())) { + return GHOST_kFailure; } + GWL_Seat *seat = d->seats[0]; + cursor_visible_set(seat, visible, seat->cursor.is_hardware, CURSOR_VISIBLE_ALWAYS_SET); return GHOST_kSuccess; } @@ -2695,148 +3747,426 @@ bool GHOST_SystemWayland::supportsWindowPosition() return false; } -GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode, - const GHOST_TGrabCursorMode mode_current, - wl_surface *surface) +bool GHOST_SystemWayland::getCursorGrabUseSoftwareDisplay(const GHOST_TGrabCursorMode mode) { - /* ignore, if the required protocols are not supported */ - if (!d->relative_pointer_manager || !d->pointer_constraints) { - return GHOST_kFailure; + if (UNLIKELY(d->seats.empty())) { + return false; + } + +#ifdef USE_GNOME_CONFINE_HACK + GWL_Seat *seat = d->seats[0]; + const bool use_software_confine = seat->use_pointer_software_confine; +#else + const bool use_software_confine = false; +#endif + + return cursor_is_software(mode, use_software_confine); +} + +#ifdef USE_GNOME_CONFINE_HACK +static bool setCursorGrab_use_software_confine(const GHOST_TGrabCursorMode mode, + wl_surface *wl_surface) +{ +# ifndef USE_GNOME_CONFINE_HACK_ALWAYS_ON + if (use_gnome_confine_hack == false) { + return false; + } +# endif + if (mode != GHOST_kGrabNormal) { + return false; + } + const GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface); + if (!win) { + return false; + } + +# ifndef USE_GNOME_CONFINE_HACK_ALWAYS_ON + if (win->scale() <= 1) { + return false; } +# endif + return true; +} +#endif + +static GWL_SeatStateGrab seat_grab_state_from_mode(const GHOST_TGrabCursorMode mode, + const bool use_software_confine) +{ + /* Initialize all members. */ + const struct GWL_SeatStateGrab grab_state = { + /* Warping happens to require software cursor which also hides. */ + .use_lock = ELEM(mode, GHOST_kGrabWrap, GHOST_kGrabHide) || use_software_confine, + .use_confine = (mode == GHOST_kGrabNormal) && (use_software_confine == false), + }; + return grab_state; +} - if (d->inputs.empty()) { +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public WAYLAND Proxy Ownership API + * \{ */ + +static const char *ghost_wl_output_tag_id = "GHOST-output"; +static const char *ghost_wl_surface_tag_id = "GHOST-window"; +static const char *ghost_wl_surface_cursor_pointer_tag_id = "GHOST-cursor-pointer"; +static const char *ghost_wl_surface_cursor_tablet_tag_id = "GHOST-cursor-tablet"; + +bool ghost_wl_output_own(const struct wl_output *wl_output) +{ + return wl_proxy_get_tag((struct wl_proxy *)wl_output) == &ghost_wl_output_tag_id; +} + +bool ghost_wl_surface_own(const struct wl_surface *wl_surface) +{ + return wl_proxy_get_tag((struct wl_proxy *)wl_surface) == &ghost_wl_surface_tag_id; +} + +bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *wl_surface) +{ + return wl_proxy_get_tag((struct wl_proxy *)wl_surface) == + &ghost_wl_surface_cursor_pointer_tag_id; +} + +bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *wl_surface) +{ + return wl_proxy_get_tag((struct wl_proxy *)wl_surface) == &ghost_wl_surface_cursor_tablet_tag_id; +} + +void ghost_wl_output_tag(struct wl_output *wl_output) +{ + wl_proxy_set_tag((struct wl_proxy *)wl_output, &ghost_wl_output_tag_id); +} + +void ghost_wl_surface_tag(struct wl_surface *wl_surface) +{ + wl_proxy_set_tag((struct wl_proxy *)wl_surface, &ghost_wl_surface_tag_id); +} + +void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *wl_surface) +{ + wl_proxy_set_tag((struct wl_proxy *)wl_surface, &ghost_wl_surface_cursor_pointer_tag_id); +} + +void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *wl_surface) +{ + wl_proxy_set_tag((struct wl_proxy *)wl_surface, &ghost_wl_surface_cursor_tablet_tag_id); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public WAYLAND Direct Data Access + * + * Expose some members via methods. + * \{ */ + +wl_display *GHOST_SystemWayland::display() +{ + return d->display; +} + +wl_compositor *GHOST_SystemWayland::compositor() +{ + return d->compositor; +} + +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + +libdecor *GHOST_SystemWayland::decor_context() +{ + return d->decor_context; +} + +#else /* WITH_GHOST_WAYLAND_LIBDECOR */ + +xdg_wm_base *GHOST_SystemWayland::xdg_shell() +{ + return d->xdg_shell; +} + +zxdg_decoration_manager_v1 *GHOST_SystemWayland::xdg_decoration_manager() +{ + return d->xdg_decoration_manager; +} + +#endif /* !WITH_GHOST_WAYLAND_LIBDECOR */ + +const std::vector<GWL_Output *> &GHOST_SystemWayland::outputs() const +{ + return d->outputs; +} + +wl_shm *GHOST_SystemWayland::shm() const +{ + return d->shm; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public WAYLAND Query Access + * \{ */ + +struct GWL_Output *ghost_wl_output_user_data(struct wl_output *wl_output) +{ + GHOST_ASSERT(wl_output, "output must not be NULL"); + GHOST_ASSERT(ghost_wl_output_own(wl_output), "output is not owned by GHOST"); + GWL_Output *output = static_cast<GWL_Output *>(wl_output_get_user_data(wl_output)); + return output; +} + +GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *wl_surface) +{ + GHOST_ASSERT(wl_surface, "wl_surface must not be NULL"); + GHOST_ASSERT(ghost_wl_surface_own(wl_surface), "wl_surface is not owned by GHOST"); + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>( + wl_surface_get_user_data(wl_surface)); + return win; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public WAYLAND Utility Functions + * + * Functionality only used for the WAYLAND implementation. + * \{ */ + +void GHOST_SystemWayland::selection_set(const std::string &selection) +{ + this->selection = selection; +} + +void GHOST_SystemWayland::window_surface_unref(const wl_surface *wl_surface) +{ +#define SURFACE_CLEAR_PTR(surface_test) \ + if (surface_test == wl_surface) { \ + surface_test = nullptr; \ + } \ + ((void)0); + + /* Only clear window surfaces (not cursors, off-screen surfaces etc). */ + for (GWL_Seat *seat : d->seats) { + SURFACE_CLEAR_PTR(seat->pointer.wl_surface); + SURFACE_CLEAR_PTR(seat->tablet.wl_surface); + SURFACE_CLEAR_PTR(seat->keyboard.wl_surface); + SURFACE_CLEAR_PTR(seat->wl_surface_focus_dnd); + } +#undef SURFACE_CLEAR_PTR +} + +bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mode, + const GHOST_TGrabCursorMode mode_current, + int32_t init_grab_xy[2], + const GHOST_Rect *wrap_bounds, + const GHOST_TAxisFlag wrap_axis, + wl_surface *wl_surface, + const int scale) +{ + /* Ignore, if the required protocols are not supported. */ + if (UNLIKELY(!d->relative_pointer_manager || !d->pointer_constraints)) { return GHOST_kFailure; } + if (UNLIKELY(d->seats.empty())) { + return GHOST_kFailure; + } /* No change, success. */ if (mode == mode_current) { return GHOST_kSuccess; } - input_t *input = d->inputs[0]; + GWL_Seat *seat = d->seats[0]; -#define MODE_NEEDS_LOCK(m) ((m) == GHOST_kGrabWrap || (m) == GHOST_kGrabHide) -#define MODE_NEEDS_HIDE(m) ((m) == GHOST_kGrabHide) -#define MODE_NEEDS_CONFINE(m) ((m) == GHOST_kGrabNormal) +#ifdef USE_GNOME_CONFINE_HACK + const bool was_software_confine = seat->use_pointer_software_confine; + const bool use_software_confine = setCursorGrab_use_software_confine(mode, wl_surface); +#else + const bool was_software_confine = false; + const bool use_software_confine = false; +#endif - const bool was_lock = MODE_NEEDS_LOCK(mode_current); - const bool use_lock = MODE_NEEDS_LOCK(mode); + const struct GWL_SeatStateGrab grab_state_prev = seat_grab_state_from_mode(mode_current, + was_software_confine); + const struct GWL_SeatStateGrab grab_state_next = seat_grab_state_from_mode(mode, + use_software_confine); /* Check for wrap as #supportsCursorWarp isn't supported. */ - const bool was_hide = MODE_NEEDS_HIDE(mode_current) || (mode_current == GHOST_kGrabWrap); - const bool use_hide = MODE_NEEDS_HIDE(mode) || (mode == GHOST_kGrabWrap); - - const bool was_confine = MODE_NEEDS_CONFINE(mode_current); - const bool use_confine = MODE_NEEDS_CONFINE(mode); + const bool use_visible = !(ELEM(mode, GHOST_kGrabHide, GHOST_kGrabWrap) || use_software_confine); + const bool is_hardware_cursor = !cursor_is_software(mode, use_software_confine); -#undef MODE_NEEDS_LOCK -#undef MODE_NEEDS_HIDE -#undef MODE_NEEDS_CONFINE - - if (!use_hide) { - setCursorVisibility(true); - } + /* Only hide so the cursor is not made visible before it's location is restored. + * This function is called again at the end of this function which only shows. */ + cursor_visible_set(seat, use_visible, is_hardware_cursor, CURSOR_VISIBLE_ONLY_HIDE); /* Switching from one grab mode to another, * in this case disable the current locks as it makes logic confusing, * postpone changing the cursor to avoid flickering. */ - if (!use_lock) { - if (input->relative_pointer) { - zwp_relative_pointer_v1_destroy(input->relative_pointer); - input->relative_pointer = nullptr; + if (!grab_state_next.use_lock) { + if (seat->relative_pointer) { + zwp_relative_pointer_v1_destroy(seat->relative_pointer); + seat->relative_pointer = nullptr; } - if (input->locked_pointer) { + if (seat->locked_pointer) { + /* Potentially add a motion event so the application has updated X/Y coordinates. */ + int32_t xy_motion[2] = {0, 0}; + bool xy_motion_create_event = false; + /* Request location to restore to. */ if (mode_current == GHOST_kGrabWrap) { - /* The chance this fails is _very_ low. */ - GHOST_WindowWayland *win = window_from_surface(surface); - if (!win) { - GHOST_PRINT("could not find window from surface when un-grabbing!" << std::endl); + /* Since this call is initiated by Blender, we can be sure the window wasn't closed + * by logic outside this function - as the window was needed to make this call. */ + int32_t xy_next[2] = {UNPACK2(seat->pointer.xy)}; + + GHOST_Rect bounds_scale; + + bounds_scale.m_l = wl_fixed_from_int(wrap_bounds->m_l) / scale; + bounds_scale.m_t = wl_fixed_from_int(wrap_bounds->m_t) / scale; + bounds_scale.m_r = wl_fixed_from_int(wrap_bounds->m_r) / scale; + bounds_scale.m_b = wl_fixed_from_int(wrap_bounds->m_b) / scale; + + bounds_scale.wrapPoint(UNPACK2(xy_next), 0, wrap_axis); + + /* Push an event so the new location is registered. */ + if ((xy_next[0] != seat->pointer.xy[0]) || (xy_next[1] != seat->pointer.xy[1])) { + xy_motion[0] = xy_next[0]; + xy_motion[1] = xy_next[1]; + xy_motion_create_event = true; } - else { - GHOST_Rect bounds; - int32_t xy_new[2] = {input->xy[0], input->xy[1]}; - - /* Fallback to window bounds. */ - if (win->getCursorGrabBounds(bounds) == GHOST_kFailure) { - win->getClientBounds(bounds); - } - - const int scale = win->scale(); - - bounds.m_l = wl_fixed_from_int(bounds.m_l) / scale; - bounds.m_t = wl_fixed_from_int(bounds.m_t) / scale; - bounds.m_r = wl_fixed_from_int(bounds.m_r) / scale; - bounds.m_b = wl_fixed_from_int(bounds.m_b) / scale; - - bounds.wrapPoint(xy_new[0], xy_new[1], 0, win->getCursorGrabAxis()); - - /* Push an event so the new location is registered. */ - if ((xy_new[0] != input->xy[0]) || (xy_new[1] != input->xy[1])) { - input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), - GHOST_kEventCursorMove, - win, - wl_fixed_to_int(scale * xy_new[0]), - wl_fixed_to_int(scale * xy_new[1]), - GHOST_TABLET_DATA_NONE)); - } - input->xy[0] = xy_new[0]; - input->xy[1] = xy_new[1]; - - zwp_locked_pointer_v1_set_cursor_position_hint( - input->locked_pointer, xy_new[0], xy_new[1]); - wl_surface_commit(surface); + seat->pointer.xy[0] = xy_next[0]; + seat->pointer.xy[1] = xy_next[1]; + + zwp_locked_pointer_v1_set_cursor_position_hint(seat->locked_pointer, UNPACK2(xy_next)); + wl_surface_commit(wl_surface); + } + else if (mode_current == GHOST_kGrabHide) { + if ((init_grab_xy[0] != seat->grab_lock_xy[0]) || + (init_grab_xy[1] != seat->grab_lock_xy[1])) { + const wl_fixed_t xy_next[2] = { + wl_fixed_from_int(init_grab_xy[0]) / scale, + wl_fixed_from_int(init_grab_xy[1]) / scale, + }; + zwp_locked_pointer_v1_set_cursor_position_hint(seat->locked_pointer, UNPACK2(xy_next)); + wl_surface_commit(wl_surface); + + /* NOTE(@campbellbarton): The new cursor position is a hint, + * it's possible the hint is ignored. It doesn't seem like there is a good way to + * know if the hint will be used or not, at least not immediately. */ + xy_motion[0] = xy_next[0]; + xy_motion[1] = xy_next[1]; + xy_motion_create_event = true; } } +#ifdef USE_GNOME_CONFINE_HACK + else if (mode_current == GHOST_kGrabNormal) { + if (was_software_confine) { + zwp_locked_pointer_v1_set_cursor_position_hint(seat->locked_pointer, + UNPACK2(seat->pointer.xy)); + wl_surface_commit(wl_surface); + } + } +#endif - zwp_locked_pointer_v1_destroy(input->locked_pointer); - input->locked_pointer = nullptr; + if (xy_motion_create_event) { + seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(), + GHOST_kEventCursorMove, + ghost_wl_surface_user_data(wl_surface), + wl_fixed_to_int(scale * xy_motion[0]), + wl_fixed_to_int(scale * xy_motion[1]), + GHOST_TABLET_DATA_NONE)); + } + + zwp_locked_pointer_v1_destroy(seat->locked_pointer); + seat->locked_pointer = nullptr; } } - if (!use_confine) { - if (input->confined_pointer) { - zwp_confined_pointer_v1_destroy(input->confined_pointer); - input->confined_pointer = nullptr; + if (!grab_state_next.use_confine) { + if (seat->confined_pointer) { + zwp_confined_pointer_v1_destroy(seat->confined_pointer); + seat->confined_pointer = nullptr; } } if (mode != GHOST_kGrabDisable) { - if (use_lock) { - if (!was_lock) { + if (grab_state_next.use_lock) { + if (!grab_state_prev.use_lock) { /* TODO(@campbellbarton): As WAYLAND does not support warping the pointer it may not be * possible to support #GHOST_kGrabWrap by pragmatically settings it's coordinates. * An alternative could be to draw the cursor in software (and hide the real cursor), * or just accept a locked cursor on WAYLAND. */ - input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( - d->relative_pointer_manager, input->wl_pointer); + seat->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( + d->relative_pointer_manager, seat->wl_pointer); zwp_relative_pointer_v1_add_listener( - input->relative_pointer, &relative_pointer_listener, input); - input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer( + seat->relative_pointer, &relative_pointer_listener, seat); + seat->locked_pointer = zwp_pointer_constraints_v1_lock_pointer( d->pointer_constraints, - surface, - input->wl_pointer, + wl_surface, + seat->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); } + if (mode == GHOST_kGrabHide) { + /* Set the initial position to detect any changes when un-grabbing, + * otherwise the unlocked cursor defaults to un-locking in-place. */ + init_grab_xy[0] = wl_fixed_to_int(scale * seat->pointer.xy[0]); + init_grab_xy[1] = wl_fixed_to_int(scale * seat->pointer.xy[1]); + seat->grab_lock_xy[0] = init_grab_xy[0]; + seat->grab_lock_xy[1] = init_grab_xy[1]; + } } - else if (use_confine) { - if (!was_confine) { - input->confined_pointer = zwp_pointer_constraints_v1_confine_pointer( + else if (grab_state_next.use_confine) { + if (!grab_state_prev.use_confine) { + seat->confined_pointer = zwp_pointer_constraints_v1_confine_pointer( d->pointer_constraints, - surface, - input->wl_pointer, + wl_surface, + seat->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); } } - - if (use_hide && !was_hide) { - setCursorVisibility(false); - } } + /* Only show so the cursor is made visible as the last step. */ + cursor_visible_set(seat, use_visible, is_hardware_cursor, CURSOR_VISIBLE_ONLY_SHOW); + +#ifdef USE_GNOME_CONFINE_HACK + seat->use_pointer_software_confine = use_software_confine; +#endif + return GHOST_kSuccess; } +#ifdef WITH_GHOST_WAYLAND_DYNLOAD +bool ghost_wl_dynload_libraries() +{ + /* Only report when `libwayland-client` is not found when building without X11, + * which will be used as a fallback. */ +# ifdef WITH_GHOST_X11 + bool verbose = false; +# else + bool verbose = true; +# endif + + if (wayland_dynload_client_init(verbose) && /* `libwayland-client`. */ + wayland_dynload_cursor_init(verbose) && /* `libwayland-cursor`. */ + wayland_dynload_egl_init(verbose) && /* `libwayland-egl`. */ +# ifdef WITH_GHOST_WAYLAND_LIBDECOR + wayland_dynload_libdecor_init(verbose) && /* `libdecor-0`. */ +# endif + true) { + return true; + } +# ifdef WITH_GHOST_WAYLAND_LIBDECOR + wayland_dynload_libdecor_exit(); +# endif + wayland_dynload_client_exit(); + wayland_dynload_cursor_exit(); + wayland_dynload_egl_exit(); + + return false; +} +#endif /* WITH_GHOST_WAYLAND_DYNLOAD */ + /** \} */ diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index 762ceb80e38..caea7b0d748 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -11,17 +11,51 @@ #include "GHOST_System.h" #include "GHOST_WindowWayland.h" +#ifdef WITH_GHOST_WAYLAND_DYNLOAD +# include <wayland_dynload_client.h> +#endif #include <wayland-client.h> -#include <xdg-decoration-client-protocol.h> -#include <xdg-shell-client-protocol.h> + +#ifdef WITH_GHOST_WAYLAND_LIBDECOR +# ifdef WITH_GHOST_WAYLAND_DYNLOAD +# include <wayland_dynload_libdecor.h> +# endif +# include <libdecor.h> +#else +/* Generated by `wayland-scanner`. */ +# include <xdg-decoration-unstable-v1-client-protocol.h> +# include <xdg-shell-client-protocol.h> +#endif #include <string> class GHOST_WindowWayland; -struct display_t; +struct GWL_Display; + +bool ghost_wl_output_own(const struct wl_output *wl_output); +void ghost_wl_output_tag(struct wl_output *wl_output); +struct GWL_Output *ghost_wl_output_user_data(struct wl_output *wl_output); + +bool ghost_wl_surface_own(const struct wl_surface *surface); +void ghost_wl_surface_tag(struct wl_surface *surface); +GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *surface); + +bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *surface); +void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface); + +bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface); +void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *surface); -struct output_t { +#ifdef WITH_GHOST_WAYLAND_DYNLOAD +/** + * Return true when all required WAYLAND libraries are present, + * Performs dynamic loading when `WITH_GHOST_WAYLAND_DYNLOAD` is in use. + */ +bool ghost_wl_dynload_libraries(); +#endif + +struct GWL_Output { struct wl_output *wl_output = nullptr; struct zxdg_output_v1 *xdg_output = nullptr; /** Dimensions in pixels. */ @@ -32,6 +66,7 @@ struct output_t { int32_t size_logical[2] = {0, 0}; bool has_size_logical = false; + /** Monitor position in pixels. */ int32_t position_logical[2] = {0, 0}; bool has_position_logical = false; @@ -58,9 +93,11 @@ class GHOST_SystemWayland : public GHOST_System { ~GHOST_SystemWayland() override; + GHOST_TSuccess init(); + bool processEvents(bool waitForEvent) override; - int setConsoleWindowState(GHOST_TConsoleWindowState action) override; + bool setConsoleWindowState(GHOST_TConsoleWindowState action) override; GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override; @@ -72,8 +109,14 @@ class GHOST_SystemWayland : public GHOST_System { uint8_t getNumDisplays() const override; - GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override; + GHOST_TSuccess getCursorPositionClientRelative(const GHOST_IWindow *window, + int32_t &x, + int32_t &y) const override; + GHOST_TSuccess setCursorPositionClientRelative(GHOST_IWindow *window, + int32_t x, + int32_t y) override; + GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override; GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override; void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override; @@ -96,20 +139,6 @@ class GHOST_SystemWayland : public GHOST_System { const bool is_dialog, const GHOST_IWindow *parentWindow) override; - wl_display *display(); - - wl_compositor *compositor(); - - xdg_wm_base *xdg_shell(); - - zxdg_decoration_manager_v1 *xdg_decoration_manager(); - - const std::vector<output_t *> &outputs() const; - - wl_shm *shm() const; - - void setSelection(const std::string &selection); - GHOST_TSuccess setCursorShape(GHOST_TStandardCursor shape); GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor cursorShape); @@ -122,16 +151,48 @@ class GHOST_SystemWayland : public GHOST_System { int hotY, bool canInvertColor); + GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap); + GHOST_TSuccess setCursorVisibility(bool visible); bool supportsCursorWarp(); bool supportsWindowPosition(); - GHOST_TSuccess setCursorGrab(const GHOST_TGrabCursorMode mode, - const GHOST_TGrabCursorMode mode_current, - wl_surface *surface); + bool getCursorGrabUseSoftwareDisplay(const GHOST_TGrabCursorMode mode); + + /* WAYLAND direct-data access. */ + + wl_display *display(); + + wl_compositor *compositor(); + +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + libdecor *decor_context(); +#else + xdg_wm_base *xdg_shell(); + zxdg_decoration_manager_v1 *xdg_decoration_manager(); +#endif + + const std::vector<GWL_Output *> &outputs() const; + + wl_shm *shm() const; + + /* WAYLAND utility functions. */ + + void selection_set(const std::string &selection); + + /** Clear all references to this surface to prevent accessing NULL pointers. */ + void window_surface_unref(const wl_surface *wl_surface); + + bool window_cursor_grab_set(const GHOST_TGrabCursorMode mode, + const GHOST_TGrabCursorMode mode_current, + int32_t init_grab_xy[2], + const GHOST_Rect *wrap_bounds, + GHOST_TAxisFlag wrap_axis, + wl_surface *wl_surface, + int scale); private: - struct display_t *d; + struct GWL_Display *d; std::string selection; }; diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 28c86db53e2..6cb36337b55 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -42,47 +42,48 @@ # include "GHOST_NDOFManagerWin32.h" #endif -// Key code values not found in winuser.h +/* Key code values not found in `winuser.h`. */ #ifndef VK_MINUS # define VK_MINUS 0xBD -#endif // VK_MINUS +#endif /* VK_MINUS */ #ifndef VK_SEMICOLON # define VK_SEMICOLON 0xBA -#endif // VK_SEMICOLON +#endif /* VK_SEMICOLON */ #ifndef VK_PERIOD # define VK_PERIOD 0xBE -#endif // VK_PERIOD +#endif /* VK_PERIOD */ #ifndef VK_COMMA # define VK_COMMA 0xBC -#endif // VK_COMMA +#endif /* VK_COMMA */ #ifndef VK_BACK_QUOTE # define VK_BACK_QUOTE 0xC0 -#endif // VK_BACK_QUOTE +#endif /* VK_BACK_QUOTE */ #ifndef VK_SLASH # define VK_SLASH 0xBF -#endif // VK_SLASH +#endif /* VK_SLASH */ #ifndef VK_BACK_SLASH # define VK_BACK_SLASH 0xDC -#endif // VK_BACK_SLASH +#endif /* VK_BACK_SLASH */ #ifndef VK_EQUALS # define VK_EQUALS 0xBB -#endif // VK_EQUALS +#endif /* VK_EQUALS */ #ifndef VK_OPEN_BRACKET # define VK_OPEN_BRACKET 0xDB -#endif // VK_OPEN_BRACKET +#endif /* VK_OPEN_BRACKET */ #ifndef VK_CLOSE_BRACKET # define VK_CLOSE_BRACKET 0xDD -#endif // VK_CLOSE_BRACKET +#endif /* VK_CLOSE_BRACKET */ #ifndef VK_GR_LESS # define VK_GR_LESS 0xE2 -#endif // VK_GR_LESS +#endif /* VK_GR_LESS */ -/* Workaround for some laptop touchpads, some of which seems to +/** + * Workaround for some laptop touch-pads, some of which seems to * have driver issues which makes it so window function receives - * the message, but PeekMessage doesn't pick those messages for + * the message, but #PeekMessage doesn't pick those messages for * some reason. * - * We send a dummy WM_USER message to force PeekMessage to receive + * We send a dummy WM_USER message to force #PeekMessage to receive * something, making it so blender's window manager sees the new * messages coming in. */ @@ -101,22 +102,23 @@ static void initRawInput() RAWINPUTDEVICE devices[DEVICE_COUNT]; memset(devices, 0, DEVICE_COUNT * sizeof(RAWINPUTDEVICE)); - // Initiates WM_INPUT messages from keyboard - // That way GHOST can retrieve true keys + /* Initiates WM_INPUT messages from keyboard + * That way GHOST can retrieve true keys. */ devices[0].usUsagePage = 0x01; devices[0].usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */ #ifdef WITH_INPUT_NDOF - // multi-axis mouse (SpaceNavigator, etc.) + /* multi-axis mouse (SpaceNavigator, etc.). */ devices[1].usUsagePage = 0x01; devices[1].usUsage = 0x08; #endif - if (RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE))) - ; // yay! - else + if (RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE))) { + /* Success. */ + } + else { GHOST_PRINTF("could not register for RawInput: %d\n", (int)GetLastError()); - + } #undef DEVICE_COUNT } @@ -129,17 +131,17 @@ GHOST_SystemWin32::GHOST_SystemWin32() GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n"); m_displayManager->initialize(); - m_consoleStatus = 1; + m_consoleStatus = true; - // Tell Windows we are per monitor DPI aware. This disables the default - // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI. + /* Tell Windows we are per monitor DPI aware. This disables the default + * blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI. */ SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - // Check if current keyboard layout uses AltGr and save keylayout ID for - // specialized handling if keys like VK_OEM_*. I.e. french keylayout - // generates VK_OEM_8 for their exclamation key (key left of right shift) + /* Check if current keyboard layout uses AltGr and save keylayout ID for + * specialized handling if keys like VK_OEM_*. I.e. french keylayout + * generates #VK_OEM_8 for their exclamation key (key left of right shift). */ this->handleKeyboardChange(); - // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32. + /* Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32. */ OleInitialize(0); #ifdef WITH_INPUT_NDOF @@ -149,7 +151,7 @@ GHOST_SystemWin32::GHOST_SystemWin32() GHOST_SystemWin32::~GHOST_SystemWin32() { - // Shutdown COM + /* Shutdown COM. */ OleUninitialize(); if (isStartedFromCommandPrompt()) { @@ -159,7 +161,7 @@ GHOST_SystemWin32::~GHOST_SystemWin32() uint64_t GHOST_SystemWin32::performanceCounterToMillis(__int64 perf_ticks) const { - // Calculate the time passed since system initialization. + /* Calculate the time passed since system initialization. */ __int64 delta = (perf_ticks - m_start) * 1000; uint64_t t = (uint64_t)(delta / m_freq); @@ -173,12 +175,12 @@ uint64_t GHOST_SystemWin32::tickCountToMillis(__int64 ticks) const uint64_t GHOST_SystemWin32::getMilliSeconds() const { - // Hardware does not support high resolution timers. We will use GetTickCount instead then. + /* Hardware does not support high resolution timers. We will use GetTickCount instead then. */ if (!m_hasPerformanceCounter) { return tickCountToMillis(::GetTickCount()); } - // Retrieve current count + /* Retrieve current count */ __int64 count = 0; ::QueryPerformanceCounter((LARGE_INTEGER *)&count); @@ -227,13 +229,13 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title, state, type, ((glSettings.flags & GHOST_glStereoVisual) != 0), - ((glSettings.flags & GHOST_glAlphaBackground) != 0), + false, (GHOST_WindowWin32 *)parentWindow, ((glSettings.flags & GHOST_glDebugContext) != 0), is_dialog); if (window->getValid()) { - // Store the pointer to the window + /* Store the pointer to the window */ m_windowManager->addWindow(window); m_windowManager->setActiveWindow(window); } @@ -272,7 +274,7 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GLSettings glSet HDC mHDC = GetDC(wnd); HDC prev_hdc = wglGetCurrentDC(); HGLRC prev_context = wglGetCurrentContext(); -#if defined(WITH_GL_PROFILE_CORE) + for (int minor = 5; minor >= 0; --minor) { context = new GHOST_ContextWGL(false, true, @@ -310,29 +312,6 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GLSettings glSet return NULL; } -#elif defined(WITH_GL_PROFILE_COMPAT) - // ask for 2.1 context, driver gives any GL version >= 2.1 - // (hopefully the latest compatibility profile) - // 2.1 ignores the profile bit & is incompatible with core profile - context = new GHOST_ContextWGL(false, - true, - NULL, - NULL, - 0, // no profile bit - 2, - 1, - (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); - - if (context->initializeDrawingContext()) { - return context; - } - else { - delete context; - } -#else -# error // must specify either core or compat at build time -#endif finished: wglMakeCurrent(prev_hdc, prev_context); return context; @@ -374,6 +353,7 @@ GHOST_ContextD3D *GHOST_SystemWin32::createOffscreenContextD3D() context = new GHOST_ContextD3D(false, wnd); if (context->initializeDrawingContext() == GHOST_kFailure) { delete context; + context = nullptr; } return context; @@ -418,10 +398,10 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent) driveTrackpad(); - // Process all the events waiting for us + /* Process all the events waiting for us. */ while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) { - // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data. - // Needed for MapVirtualKey or if we ever need to get chars from wm_ime_char or similar. + /* #TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data. + * Needed for #MapVirtualKey or if we ever need to get chars from wm_ime_char or similar. */ ::TranslateMessage(&msg); ::DispatchMessageW(&msg); hasEventHandled = true; @@ -453,8 +433,9 @@ GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(int32_t &x, int32_t &y) cons GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(int32_t x, int32_t y) { - if (!::GetActiveWindow()) + if (!::GetActiveWindow()) { return GHOST_kFailure; + } return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure; } @@ -477,10 +458,12 @@ GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys &keys) cons bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0; bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0; - if (lwindown || rwindown) + if (lwindown || rwindown) { keys.set(GHOST_kModifierKeyOS, true); - else + } + else { keys.set(GHOST_kModifierKeyOS, false); + } return GHOST_kSuccess; } @@ -512,7 +495,7 @@ GHOST_TSuccess GHOST_SystemWin32::init() initRawInput(); m_lfstart = ::GetTickCount(); - // Determine whether this system has a high frequency performance counter. */ + /* Determine whether this system has a high frequency performance counter. */ m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE; if (m_hasPerformanceCounter) { GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n"); @@ -543,7 +526,7 @@ GHOST_TSuccess GHOST_SystemWin32::init() wc.lpszMenuName = 0; wc.lpszClassName = L"GHOST_WindowClass"; - // Use RegisterClassEx for setting small icon + /* Use #RegisterClassEx for setting small icon. */ if (::RegisterClassW(&wc) == 0) { success = GHOST_kFailure; } @@ -557,76 +540,15 @@ GHOST_TSuccess GHOST_SystemWin32::exit() return GHOST_System::exit(); } -GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, - bool *r_keyDown, - bool *r_is_repeated_modifier) +GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, bool *r_key_down) { - bool is_repeated_modifier = false; - - GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - GHOST_TKey key = GHOST_kKeyUnknown; - GHOST_ModifierKeys modifiers; - system->retrieveModifierKeys(modifiers); - - // RI_KEY_BREAK doesn't work for sticky keys release, so we also - // check for the up message + /* #RI_KEY_BREAK doesn't work for sticky keys release, so we also check for the up message. */ unsigned int msg = raw.data.keyboard.Message; - *r_keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP; - - key = this->convertKey(raw.data.keyboard.VKey, - raw.data.keyboard.MakeCode, - (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0))); - - // extra handling of modifier keys: don't send repeats out from GHOST - if (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt) { - bool changed = false; - GHOST_TModifierKeyMask modifier; - switch (key) { - case GHOST_kKeyLeftShift: { - changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != *r_keyDown); - modifier = GHOST_kModifierKeyLeftShift; - break; - } - case GHOST_kKeyRightShift: { - changed = (modifiers.get(GHOST_kModifierKeyRightShift) != *r_keyDown); - modifier = GHOST_kModifierKeyRightShift; - break; - } - case GHOST_kKeyLeftControl: { - changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != *r_keyDown); - modifier = GHOST_kModifierKeyLeftControl; - break; - } - case GHOST_kKeyRightControl: { - changed = (modifiers.get(GHOST_kModifierKeyRightControl) != *r_keyDown); - modifier = GHOST_kModifierKeyRightControl; - break; - } - case GHOST_kKeyLeftAlt: { - changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != *r_keyDown); - modifier = GHOST_kModifierKeyLeftAlt; - break; - } - case GHOST_kKeyRightAlt: { - changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != *r_keyDown); - modifier = GHOST_kModifierKeyRightAlt; - break; - } - default: - break; - } - - if (changed) { - modifiers.set(modifier, *r_keyDown); - system->storeModifierKeys(modifiers); - } - else { - is_repeated_modifier = true; - } - } + *r_key_down = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP; - *r_is_repeated_modifier = is_repeated_modifier; - return key; + return this->convertKey(raw.data.keyboard.VKey, + raw.data.keyboard.MakeCode, + (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0))); } /** @@ -638,6 +560,11 @@ GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const { GHOST_TKey key = GHOST_kKeyUnknown; + if (vKey == 0xFF) { + /* 0xFF is not a valid virtual key code. */ + return key; + } + char ch = (char)MapVirtualKeyA(vKey, MAPVK_VK_TO_CHAR); switch (ch) { case u'\"': @@ -675,11 +602,11 @@ GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short exten GHOST_TKey key; if ((vKey >= '0') && (vKey <= '9')) { - // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) + /* VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39). */ key = (GHOST_TKey)(vKey - '0' + GHOST_kKey0); } else if ((vKey >= 'A') && (vKey <= 'Z')) { - // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) + /* VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A). */ key = (GHOST_TKey)(vKey - 'A' + GHOST_kKeyA); } else if ((vKey >= VK_F1) && (vKey <= VK_F24)) { @@ -864,7 +791,7 @@ GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short exten GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, GHOST_WindowWin32 *window, - GHOST_TButtonMask mask) + GHOST_TButton mask) { GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); @@ -974,11 +901,11 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window) new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData)); mouseMoveHandled = true; - break; } else { WINTAB_PRINTF(" ... but no system button\n"); } + break; } case GHOST_kEventButtonUp: { WINTAB_PRINTF("HWND %p Wintab button up", window->getHWND()); @@ -1053,7 +980,7 @@ void GHOST_SystemWin32::processPointerEvent( } switch (type) { - case WM_POINTERUPDATE: + case WM_POINTERUPDATE: { /* Coalesced pointer events are reverse chronological order, reorder chronologically. * Only contiguous move events are coalesced. */ for (uint32_t i = pointerInfo.size(); i-- > 0;) { @@ -1068,7 +995,8 @@ void GHOST_SystemWin32::processPointerEvent( /* Leave event unhandled so that system cursor is moved. */ break; - case WM_POINTERDOWN: + } + case WM_POINTERDOWN: { /* Move cursor to point of contact because GHOST_EventButton does not include position. */ system->pushEvent(new GHOST_EventCursor(pointerInfo[0].time, GHOST_kEventCursorMove, @@ -1087,7 +1015,8 @@ void GHOST_SystemWin32::processPointerEvent( eventHandled = true; break; - case WM_POINTERUP: + } + case WM_POINTERUP: { system->pushEvent(new GHOST_EventButton(pointerInfo[0].time, GHOST_kEventButtonUp, window, @@ -1099,8 +1028,10 @@ void GHOST_SystemWin32::processPointerEvent( eventHandled = true; break; - default: + } + default: { break; + } } } @@ -1117,6 +1048,12 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind system->getCursorPosition(x_screen, y_screen); if (window->getCursorGrabModeIsWarp()) { + /* WORKAROUND: + * Sometimes Windows ignores `SetCursorPos()` or `SendInput()` calls or the mouse event is + * outdated. Identify these cases by checking if the cursor is not yet within bounds. */ + static bool is_warping_x = false; + static bool is_warping_y = false; + int32_t x_new = x_screen; int32_t y_new = y_screen; int32_t x_accum, y_accum; @@ -1133,29 +1070,41 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind window->getCursorGrabAccum(x_accum, y_accum); if (x_new != x_screen || y_new != y_screen) { + system->setCursorPosition(x_new, y_new); /* wrap */ + + /* Do not update the accum values if we are an outdated or failed pos-warp event. */ + if (!is_warping_x) { + is_warping_x = x_new != x_screen; + if (is_warping_x) { + x_accum += (x_screen - x_new); + } + } + + if (!is_warping_y) { + is_warping_y = y_new != y_screen; + if (is_warping_y) { + y_accum += (y_screen - y_new); + } + } + window->setCursorGrabAccum(x_accum, y_accum); + /* When wrapping we don't need to add an event because the setCursorPosition call will cause * a new event after. */ - system->setCursorPosition(x_new, y_new); /* wrap */ - window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new)); - } - else { - return new GHOST_EventCursor(system->getMilliSeconds(), - GHOST_kEventCursorMove, - window, - x_screen + x_accum, - y_screen + y_accum, - GHOST_TABLET_DATA_NONE); + return NULL; } + + is_warping_x = false; + is_warping_y = false; + x_screen += x_accum; + y_screen += y_accum; } - else { - return new GHOST_EventCursor(system->getMilliSeconds(), - GHOST_kEventCursorMove, - window, - x_screen, - y_screen, - GHOST_TABLET_DATA_NONE); - } - return NULL; + + return new GHOST_EventCursor(system->getMilliSeconds(), + GHOST_kEventCursorMove, + window, + x_screen, + y_screen, + GHOST_TABLET_DATA_NONE); } void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam) @@ -1166,7 +1115,7 @@ void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wPar int delta = GET_WHEEL_DELTA_WPARAM(wParam); if (acc * delta < 0) { - // scroll direction reversed. + /* Scroll direction reversed. */ acc = 0; } acc += delta; @@ -1182,48 +1131,49 @@ void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wPar GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw) { - bool keyDown = false; - bool is_repeated_modifier = false; + const char vk = raw.data.keyboard.VKey; + bool key_down = false; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - GHOST_TKey key = system->hardKey(raw, &keyDown, &is_repeated_modifier); + GHOST_TKey key = system->hardKey(raw, &key_down); GHOST_EventKey *event; + bool is_repeat = false; + bool is_repeated_modifier = false; + if (key_down) { + if (system->m_keycode_last_repeat_key == vk) { + is_repeat = true; + is_repeated_modifier = (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt); + } + system->m_keycode_last_repeat_key = vk; + } + else { + if (system->m_keycode_last_repeat_key == vk) { + system->m_keycode_last_repeat_key = 0; + } + } + /* We used to check `if (key != GHOST_kKeyUnknown)`, but since the message * values `WM_SYSKEYUP`, `WM_KEYUP` and `WM_CHAR` are ignored, we capture * those events here as well. */ if (!is_repeated_modifier) { - char vk = raw.data.keyboard.VKey; char utf8_char[6] = {0}; - char ascii = 0; - bool is_repeat = false; + BYTE state[256]; + const BOOL has_state = GetKeyboardState((PBYTE)state); + const bool ctrl_pressed = has_state && state[VK_CONTROL] & 0x80; + const bool alt_pressed = has_state && state[VK_MENU] & 0x80; - /* Unlike on Linux, not all keys can send repeat events. E.g. modifier keys don't. */ - if (keyDown) { - if (system->m_keycode_last_repeat_key == vk) { - is_repeat = true; - } - system->m_keycode_last_repeat_key = vk; + if (!key_down) { + /* Pass. */ } - else { - if (system->m_keycode_last_repeat_key == vk) { - system->m_keycode_last_repeat_key = 0; - } - } - - wchar_t utf16[3] = {0}; - BYTE state[256] = {0}; - int r; - GetKeyboardState((PBYTE)state); - bool ctrl_pressed = state[VK_CONTROL] & 0x80; - bool alt_pressed = state[VK_MENU] & 0x80; - /* No text with control key pressed (Alt can be used to insert special characters though!). */ - if (ctrl_pressed && !alt_pressed) { - utf8_char[0] = '\0'; + else if (ctrl_pressed && !alt_pressed) { + /* Pass. */ } - // Don't call ToUnicodeEx on dead keys as it clears the buffer and so won't allow diacritical - // composition. + /* Don't call #ToUnicodeEx on dead keys as it clears the buffer and so won't allow diacritical + * composition. */ else if (MapVirtualKeyW(vk, 2) != 0) { + wchar_t utf16[3] = {0}; + int r; /* TODO: #ToUnicodeEx can respond with up to 4 utf16 chars (only 2 here). * Could be up to 24 utf8 bytes. */ if ((r = ToUnicodeEx( @@ -1238,29 +1188,25 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA } } - if (!keyDown) { - utf8_char[0] = '\0'; - ascii = '\0'; - } - else { - ascii = utf8_char[0] & 0x80 ? '?' : utf8_char[0]; - } - #ifdef WITH_INPUT_IME - if (window->getImeInput()->IsImeKeyEvent(ascii, key)) { - return NULL; + if (key_down && ((utf8_char[0] & 0x80) == 0)) { + const char ascii = utf8_char[0]; + if (window->getImeInput()->IsImeKeyEvent(ascii, key)) { + return NULL; + } } #endif /* WITH_INPUT_IME */ event = new GHOST_EventKey(system->getMilliSeconds(), - keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, + key_down ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, key, - ascii, - utf8_char, - is_repeat); + is_repeat, + utf8_char); - // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter +#if 0 /* we already get this info via EventPrinter. */ + GHOST_PRINTF("%c\n", ascii); +#endif } else { event = NULL; @@ -1281,9 +1227,7 @@ GHOST_Event *GHOST_SystemWin32::processWindowSizeEvent(GHOST_WindowWin32 *window system->dispatchEvents(); return NULL; } - else { - return sizeEvent; - } + return sizeEvent; } GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, @@ -1364,54 +1308,53 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw) uint64_t now = getMilliSeconds(); static bool firstEvent = true; - if (firstEvent) { // determine exactly which device is plugged in + if (firstEvent) { /* Determine exactly which device is plugged in. */ RID_DEVICE_INFO info; unsigned infoSize = sizeof(RID_DEVICE_INFO); info.cbSize = infoSize; GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, &info, &infoSize); - if (info.dwType == RIM_TYPEHID) + if (info.dwType == RIM_TYPEHID) { m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId); - else + } + else { GHOST_PRINT("<!> not a HID device... mouse/kb perhaps?\n"); - + } firstEvent = false; } - // The NDOF manager sends button changes immediately, and *pretends* to - // send motion. Mark as 'sent' so motion will always get dispatched. + /* The NDOF manager sends button changes immediately, and *pretends* to + * send motion. Mark as 'sent' so motion will always get dispatched. */ eventSent = true; BYTE const *data = raw.data.hid.bRawData; BYTE packetType = data[0]; switch (packetType) { - case 1: // translation - { + case 1: { /* Translation. */ const short *axis = (short *)(data + 1); - // massage into blender view coords (same goes for rotation) + /* Massage into blender view coords (same goes for rotation). */ const int t[3] = {axis[0], -axis[2], axis[1]}; m_ndofManager->updateTranslation(t, now); if (raw.data.hid.dwSizeHid == 13) { - // this report also includes rotation + /* This report also includes rotation. */ const int r[3] = {-axis[3], axis[5], -axis[4]}; m_ndofManager->updateRotation(r, now); - // I've never gotten one of these, has anyone else? + /* I've never gotten one of these, has anyone else? */ GHOST_PRINT("ndof: combined T + R\n"); } break; } - case 2: // rotation - { + case 2: { /* Rotation. */ + const short *axis = (short *)(data + 1); const int r[3] = {-axis[0], axis[2], -axis[1]}; m_ndofManager->updateRotation(r, now); break; } - case 3: // buttons - { + case 3: { /* Buttons. */ int button_bits; memcpy(&button_bits, data + 1, sizeof(button_bits)); m_ndofManager->updateButtons(button_bits, now); @@ -1420,7 +1363,7 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw) } return eventSent; } -#endif // WITH_INPUT_NDOF +#endif /* WITH_INPUT_NDOF */ void GHOST_SystemWin32::driveTrackpad() { @@ -1483,8 +1426,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, if (hwnd) { if (msg == WM_NCCREATE) { - // Tell Windows to automatically handle scaling of non-client areas - // such as the caption bar. EnableNonClientDpiScaling was introduced in Windows 10 + /* Tell Windows to automatically handle scaling of non-client areas + * such as the caption bar. #EnableNonClientDpiScaling was introduced in Windows 10. */ HMODULE m_user32 = ::LoadLibrary("User32.dll"); if (m_user32) { GHOST_WIN32_EnableNonClientDpiScaling fpEnableNonClientDpiScaling = @@ -1499,7 +1442,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, GHOST_WindowWin32 *window = (GHOST_WindowWin32 *)::GetWindowLongPtr(hwnd, GWLP_USERDATA); if (window) { switch (msg) { - // we need to check if new key layout has AltGr + /* We need to check if new key layout has AltGr. */ case WM_INPUTLANGCHANGE: { system->handleKeyboardChange(); #ifdef WITH_INPUT_IME @@ -1508,9 +1451,9 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, #endif break; } - //////////////////////////////////////////////////////////////////////// - // Keyboard events, processed - //////////////////////////////////////////////////////////////////////// + /* ========================== + * Keyboard events, processed + * ========================== */ case WM_INPUT: { RAWINPUT raw; RAWINPUT *raw_ptr = &raw; @@ -1519,7 +1462,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER)); switch (raw.header.dwType) { - case RIM_TYPEKEYBOARD: + case RIM_TYPEKEYBOARD: { event = processKeyEvent(window, raw); if (!event) { GHOST_PRINT("GHOST_SystemWin32::wndProc: key event "); @@ -1527,20 +1470,22 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, GHOST_PRINT(" key ignored\n"); } break; + } #ifdef WITH_INPUT_NDOF - case RIM_TYPEHID: + case RIM_TYPEHID: { if (system->processNDOF(raw)) { eventHandled = true; } break; + } #endif } break; } #ifdef WITH_INPUT_IME - //////////////////////////////////////////////////////////////////////// - // IME events, processed, read more in GHOST_IME.h - //////////////////////////////////////////////////////////////////////// + /* ================================================= + * IME events, processed, read more in `GHOST_IME.h` + * ================================================= */ case WM_IME_NOTIFY: { /* Update conversion status when IME is changed or input mode is changed. */ if (wParam == IMN_SETOPENSTATUS || wParam == IMN_SETCONVERSIONMODE) { @@ -1588,56 +1533,53 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, break; } #endif /* WITH_INPUT_IME */ - //////////////////////////////////////////////////////////////////////// - // Keyboard events, ignored - //////////////////////////////////////////////////////////////////////// + /* ======================== + * Keyboard events, ignored + * ======================== */ case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: - /* These functions were replaced by #WM_INPUT. */ + /* These functions were replaced by #WM_INPUT. */ case WM_CHAR: - /* The WM_CHAR message is posted to the window with the keyboard focus when - * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR - * contains the character code of the key that was pressed. - */ + /* The #WM_CHAR message is posted to the window with the keyboard focus when + * a WM_KEYDOWN message is translated by the #TranslateMessage function. + * WM_CHAR contains the character code of the key that was pressed. */ case WM_DEADCHAR: - /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a - * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR + /* The #WM_DEADCHAR message is posted to the window with the keyboard focus when a + * WM_KEYUP message is translated by the #TranslateMessage function. WM_DEADCHAR * specifies a character code generated by a dead key. A dead key is a key that * generates a character, such as the umlaut (double-dot), that is combined with * another character to form a composite character. For example, the umlaut-O * character (Ö) is generated by typing the dead key for the umlaut character, and - * then typing the O key. - */ + * then typing the O key. */ break; case WM_SYSDEADCHAR: - /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when - * a WM_SYSKEYDOWN message is translated by the TranslateMessage function. - * WM_SYSDEADCHAR specifies the character code of a system dead key - that is, - * a dead key that is pressed while holding down the alt key. - */ - case WM_SYSCHAR: - /* The WM_SYSCHAR message is sent to the window with the keyboard focus when - * a WM_SYSCHAR message is translated by the TranslateMessage function. + /* The #WM_SYSDEADCHAR message is sent to the window with the keyboard focus when + * a WM_SYSKEYDOWN message is translated by the #TranslateMessage function. + * WM_SYSDEADCHAR specifies the character code of a system dead key - that is, + * a dead key that is pressed while holding down the alt key. */ + case WM_SYSCHAR: { + /* #The WM_SYSCHAR message is sent to the window with the keyboard focus when + * a WM_SYSCHAR message is translated by the #TranslateMessage function. * WM_SYSCHAR specifies the character code of a dead key - that is, * a dead key that is pressed while holding down the alt key. - * To prevent the sound, DefWindowProc must be avoided by return - */ + * To prevent the sound, #DefWindowProc must be avoided by return. */ break; - case WM_SYSCOMMAND: - /* The WM_SYSCOMMAND message is sent to the window when system commands such as + } + case WM_SYSCOMMAND: { + /* The #WM_SYSCOMMAND message is sent to the window when system commands such as * maximize, minimize or close the window are triggered. Also it is sent when ALT - * button is press for menu. To prevent this we must return preventing DefWindowProc. + * button is press for menu. To prevent this we must return preventing #DefWindowProc. * * Note that the four low-order bits of the wParam parameter are used internally by the * OS. To obtain the correct result when testing the value of wParam, an application must - * combine the value 0xFFF0 with the wParam value by using the bit-wise AND operator. - */ + * combine the value 0xFFF0 with the wParam value by using the bit-wise AND operator. */ switch (wParam & 0xFFF0) { - case SC_KEYMENU: + case SC_KEYMENU: { eventHandled = true; break; + } case SC_RESTORE: { ::ShowWindow(hwnd, SW_RESTORE); window->setState(window->getState()); @@ -1668,9 +1610,10 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } } break; - //////////////////////////////////////////////////////////////////////// - // Wintab events, processed - //////////////////////////////////////////////////////////////////////// + } + /* ======================== + * Wintab events, processed + * ======================== */ case WT_CSRCHANGE: { WINTAB_PRINTF("HWND %p HCTX %p WT_CSRCHANGE\n", window->getHWND(), (void *)lParam); GHOST_Wintab *wt = window->getWintab(); @@ -1722,44 +1665,53 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, eventHandled = true; break; } - case WT_PACKET: + case WT_PACKET: { processWintabEvent(window); eventHandled = true; break; - //////////////////////////////////////////////////////////////////////// - // Wintab events, debug - //////////////////////////////////////////////////////////////////////// - case WT_CTXOPEN: + } + /* ==================== + * Wintab events, debug + * ==================== */ + case WT_CTXOPEN: { WINTAB_PRINTF("HWND %p HCTX %p WT_CTXOPEN\n", window->getHWND(), (void *)wParam); break; - case WT_CTXCLOSE: + } + case WT_CTXCLOSE: { WINTAB_PRINTF("HWND %p HCTX %p WT_CTXCLOSE\n", window->getHWND(), (void *)wParam); break; - case WT_CTXUPDATE: + } + case WT_CTXUPDATE: { WINTAB_PRINTF("HWND %p HCTX %p WT_CTXUPDATE\n", window->getHWND(), (void *)wParam); break; - case WT_CTXOVERLAP: + } + case WT_CTXOVERLAP: { WINTAB_PRINTF("HWND %p HCTX %p WT_CTXOVERLAP", window->getHWND(), (void *)wParam); switch (lParam) { - case CXS_DISABLED: + case CXS_DISABLED: { WINTAB_PRINTF(" CXS_DISABLED\n"); break; - case CXS_OBSCURED: + } + case CXS_OBSCURED: { WINTAB_PRINTF(" CXS_OBSCURED\n"); break; - case CXS_ONTOP: + } + case CXS_ONTOP: { WINTAB_PRINTF(" CXS_ONTOP\n"); break; + } } break; - //////////////////////////////////////////////////////////////////////// - // Pointer events, processed - //////////////////////////////////////////////////////////////////////// + } + /* ========================= + * Pointer events, processed + * ========================= */ case WM_POINTERUPDATE: case WM_POINTERDOWN: - case WM_POINTERUP: + case WM_POINTERUP: { processPointerEvent(msg, window, wParam, lParam, eventHandled); break; + } case WM_POINTERLEAVE: { uint32_t pointerId = GET_POINTERID_WPARAM(wParam); POINTER_INFO pointerInfo; @@ -1774,19 +1726,22 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; } - //////////////////////////////////////////////////////////////////////// - // Mouse events, processed - //////////////////////////////////////////////////////////////////////// - case WM_LBUTTONDOWN: + /* ======================= + * Mouse events, processed + * ======================= */ + case WM_LBUTTONDOWN: { event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft); break; - case WM_MBUTTONDOWN: + } + case WM_MBUTTONDOWN: { event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle); break; - case WM_RBUTTONDOWN: + } + case WM_RBUTTONDOWN: { event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight); break; - case WM_XBUTTONDOWN: + } + case WM_XBUTTONDOWN: { if ((short)HIWORD(wParam) == XBUTTON1) { event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4); } @@ -1794,16 +1749,20 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5); } break; - case WM_LBUTTONUP: + } + case WM_LBUTTONUP: { event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft); break; - case WM_MBUTTONUP: + } + case WM_MBUTTONUP: { event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle); break; - case WM_RBUTTONUP: + } + case WM_RBUTTONUP: { event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight); break; - case WM_XBUTTONUP: + } + case WM_XBUTTONUP: { if ((short)HIWORD(wParam) == XBUTTON1) { event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4); } @@ -1811,7 +1770,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5); } break; - case WM_MOUSEMOVE: + } + case WM_MOUSEMOVE: { if (!window->m_mousePresent) { WINTAB_PRINTF("HWND %p mouse enter\n", window->getHWND()); TRACKMOUSEEVENT tme = {sizeof(tme)}; @@ -1828,6 +1788,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, event = processCursorEvent(window); break; + } case WM_MOUSEWHEEL: { /* The WM_MOUSEWHEEL message is sent to the focus window * when the mouse wheel is rotated. The DefWindowProc @@ -1843,7 +1804,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, #endif break; } - case WM_SETCURSOR: + case WM_SETCURSOR: { /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor * to move within a window and mouse input is not captured. * This means we have to set the cursor shape every time the mouse moves! @@ -1851,16 +1812,17 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, * arrow if it is not in the client area. */ if (LOWORD(lParam) == HTCLIENT) { - // Load the current cursor + /* Load the current cursor. */ window->loadCursor(window->getCursorVisibility(), window->getCursorShape()); - // Bypass call to DefWindowProc + /* Bypass call to #DefWindowProc. */ return 0; } else { - // Outside of client area show standard cursor + /* Outside of client area show standard cursor. */ window->loadCursor(true, GHOST_kStandardCursorDefault); } break; + } case WM_MOUSELEAVE: { WINTAB_PRINTF("HWND %p mouse leave\n", window->getHWND()); window->m_mousePresent = false; @@ -1873,26 +1835,27 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; } - //////////////////////////////////////////////////////////////////////// - // Mouse events, ignored - //////////////////////////////////////////////////////////////////////// - case WM_NCMOUSEMOVE: - /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved - * within the non-client area of the window. This message is posted to the window that - * contains the cursor. If a window has captured the mouse, this message is not posted. - */ - case WM_NCHITTEST: + /* ===================== + * Mouse events, ignored + * ===================== */ + case WM_NCMOUSEMOVE: { + /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved + * within the non-client area of the window. This message is posted to the window that + * contains the cursor. If a window has captured the mouse, this message is not posted. + */ + } + case WM_NCHITTEST: { /* The WM_NCHITTEST message is sent to a window when the cursor moves, or * when a mouse button is pressed or released. If the mouse is not captured, * the message is sent to the window beneath the cursor. Otherwise, the message * is sent to the window that has captured the mouse. */ break; - - //////////////////////////////////////////////////////////////////////// - // Window events, processed - //////////////////////////////////////////////////////////////////////// - case WM_CLOSE: + } + /* ======================== + * Window events, processed + * ======================== */ + case WM_CLOSE: { /* The WM_CLOSE message is sent as a signal that a window * or an application should terminate. Restore if minimized. */ if (IsIconic(hwnd)) { @@ -1900,31 +1863,29 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } event = processWindowEvent(GHOST_kEventWindowClose, window); break; - case WM_ACTIVATE: + } + case WM_ACTIVATE: { /* The WM_ACTIVATE message is sent to both the window being activated and the window * being deactivated. If the windows use the same input queue, the message is sent * synchronously, first to the window procedure of the top-level window being * deactivated, then to the window procedure of the top-level window being activated. * If the windows use different input queues, the message is sent asynchronously, * so the window is activated immediately. */ - { - GHOST_ModifierKeys modifiers; - modifiers.clear(); - system->storeModifierKeys(modifiers); - system->m_wheelDeltaAccum = 0; - system->m_keycode_last_repeat_key = 0; - event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : - GHOST_kEventWindowDeactivate, - window); - /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL - * will not be dispatched to OUR active window if we minimize one of OUR windows. */ - if (LOWORD(wParam) == WA_INACTIVE) - window->lostMouseCapture(); - - lResult = ::DefWindowProc(hwnd, msg, wParam, lParam); - break; + + system->m_wheelDeltaAccum = 0; + system->m_keycode_last_repeat_key = 0; + event = processWindowEvent( + LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window); + /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL + * will not be dispatched to OUR active window if we minimize one of OUR windows. */ + if (LOWORD(wParam) == WA_INACTIVE) { + window->lostMouseCapture(); } - case WM_ENTERSIZEMOVE: + + lResult = ::DefWindowProc(hwnd, msg, wParam, lParam); + break; + } + case WM_ENTERSIZEMOVE: { /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving * or sizing modal loop. The window enters the moving or sizing modal loop when the user * clicks the window's title bar or sizing border, or when the window passes the @@ -1934,10 +1895,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, */ window->m_inLiveResize = 1; break; - case WM_EXITSIZEMOVE: + } + case WM_EXITSIZEMOVE: { window->m_inLiveResize = 0; break; - case WM_PAINT: + } + case WM_PAINT: { /* An application sends the WM_PAINT message when the system or another application * makes a request to paint a portion of an application's window. The message is sent * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage @@ -1952,7 +1915,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, eventHandled = true; } break; - case WM_GETMINMAXINFO: + } + case WM_GETMINMAXINFO: { /* The WM_GETMINMAXINFO message is sent to a window when the size or * position of the window is about to change. An application can use * this message to override the window's default maximized size and @@ -1961,10 +1925,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, processMinMaxInfo((MINMAXINFO *)lParam); /* Let DefWindowProc handle it. */ break; - case WM_SIZING: + } + case WM_SIZING: { event = processWindowSizeEvent(window); break; - case WM_SIZE: + } + case WM_SIZE: { /* The WM_SIZE message is sent to a window after its size has changed. * The WM_SIZE and WM_MOVE messages are not sent if an application handles the * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient @@ -1973,15 +1939,17 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, */ event = processWindowSizeEvent(window); break; - case WM_CAPTURECHANGED: + } + case WM_CAPTURECHANGED: { window->lostMouseCapture(); break; + } case WM_MOVING: /* The WM_MOVING message is sent to a window that the user is moving. By processing * this message, an application can monitor the size and position of the drag rectangle * and, if needed, change its size or position. */ - case WM_MOVE: + case WM_MOVE: { /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient * to perform any move or size change processing during the WM_WINDOWPOSCHANGED @@ -1997,33 +1965,33 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; - case WM_DPICHANGED: + } + case WM_DPICHANGED: { /* The WM_DPICHANGED message is sent when the effective dots per inch (dpi) for a * window has changed. The DPI is the scale factor for a window. There are multiple * events that can cause the DPI to change such as when the window is moved to a monitor - * with a different DPI. - */ - { - // The suggested new size and position of the window. - RECT *const suggestedWindowRect = (RECT *)lParam; + * with a different DPI. */ - // Push DPI change event first - system->pushEvent(processWindowEvent(GHOST_kEventWindowDPIHintChanged, window)); - system->dispatchEvents(); - eventHandled = true; + /* The suggested new size and position of the window. */ + RECT *const suggestedWindowRect = (RECT *)lParam; - // Then move and resize window - SetWindowPos(hwnd, - NULL, - suggestedWindowRect->left, - suggestedWindowRect->top, - suggestedWindowRect->right - suggestedWindowRect->left, - suggestedWindowRect->bottom - suggestedWindowRect->top, - SWP_NOZORDER | SWP_NOACTIVATE); + /* Push DPI change event first. */ + system->pushEvent(processWindowEvent(GHOST_kEventWindowDPIHintChanged, window)); + system->dispatchEvents(); + eventHandled = true; - window->updateDPI(); - } + /* Then move and resize window. */ + SetWindowPos(hwnd, + NULL, + suggestedWindowRect->left, + suggestedWindowRect->top, + suggestedWindowRect->right - suggestedWindowRect->left, + suggestedWindowRect->bottom - suggestedWindowRect->top, + SWP_NOZORDER | SWP_NOACTIVATE); + + window->updateDPI(); break; + } case WM_DISPLAYCHANGE: { GHOST_Wintab *wt = window->getWintab(); if (wt) { @@ -2031,7 +1999,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; } - case WM_KILLFOCUS: + case WM_KILLFOCUS: { /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard * focus. We want to prevent this if a window is still active and it loses focus to * nowhere. */ @@ -2039,73 +2007,73 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, ::SetFocus(hwnd); } break; - case WM_SETTINGCHANGE: + } + case WM_SETTINGCHANGE: { /* Microsoft: "Note that some applications send this message with lParam set to NULL" */ if ((lParam != NULL) && (wcscmp(LPCWSTR(lParam), L"ImmersiveColorSet") == 0)) { window->ThemeRefresh(); } break; - //////////////////////////////////////////////////////////////////////// - // Window events, ignored - //////////////////////////////////////////////////////////////////////// + } + /* ====================== + * Window events, ignored + * ====================== */ case WM_WINDOWPOSCHANGED: - /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place - * in the Z order has changed as a result of a call to the SetWindowPos function or - * another window-management function. - * The WM_SIZE and WM_MOVE messages are not sent if an application handles the - * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient - * to perform any move or size change processing during the WM_WINDOWPOSCHANGED - * message without calling DefWindowProc. - */ + /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place + * in the Z order has changed as a result of a call to the SetWindowPos function or + * another window-management function. + * The WM_SIZE and WM_MOVE messages are not sent if an application handles the + * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient + * to perform any move or size change processing during the WM_WINDOWPOSCHANGED + * message without calling DefWindowProc. + */ case WM_ERASEBKGND: - /* An application sends the WM_ERASEBKGND message when the window background must be - * erased (for example, when a window is resized). The message is sent to prepare an - * invalidated portion of a window for painting. - */ + /* An application sends the WM_ERASEBKGND message when the window background must be + * erased (for example, when a window is resized). The message is sent to prepare an + * invalidated portion of a window for painting. */ case WM_NCPAINT: - /* An application sends the WM_NCPAINT message to a window - * when its frame must be painted. */ + /* An application sends the WM_NCPAINT message to a window + * when its frame must be painted. */ case WM_NCACTIVATE: - /* The WM_NCACTIVATE message is sent to a window when its non-client area needs to be - * changed to indicate an active or inactive state. */ + /* The WM_NCACTIVATE message is sent to a window when its non-client area needs to be + * changed to indicate an active or inactive state. */ case WM_DESTROY: - /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the - * window procedure of the window being destroyed after the window is removed from the - * screen. This message is sent first to the window being destroyed and then to the child - * windows (if any) as they are destroyed. During the processing of the message, it can - * be assumed that all child windows still exist. */ - case WM_NCDESTROY: + /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the + * window procedure of the window being destroyed after the window is removed from the + * screen. This message is sent first to the window being destroyed and then to the child + * windows (if any) as they are destroyed. During the processing of the message, it can + * be assumed that all child windows still exist. */ + case WM_NCDESTROY: { /* The WM_NCDESTROY message informs a window that its non-client area is being * destroyed. The DestroyWindow function sends the WM_NCDESTROY message to the window * following the WM_DESTROY message. WM_DESTROY is used to free the allocated memory * object associated with the window. */ break; + } case WM_SHOWWINDOW: - /* The WM_SHOWWINDOW message is sent to a window when the window is - * about to be hidden or shown. */ + /* The WM_SHOWWINDOW message is sent to a window when the window is + * about to be hidden or shown. */ case WM_WINDOWPOSCHANGING: - /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in - * the Z order is about to change as a result of a call to the SetWindowPos function or - * another window-management function. - */ - case WM_SETFOCUS: + /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in + * the Z order is about to change as a result of a call to the SetWindowPos function or + * another window-management function. */ + case WM_SETFOCUS: { /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */ break; - //////////////////////////////////////////////////////////////////////// - // Other events - //////////////////////////////////////////////////////////////////////// + } + /* ============ + * Other events + * ============ */ case WM_GETTEXT: - /* An application sends a WM_GETTEXT message to copy the text that - * corresponds to a window into a buffer provided by the caller. - */ + /* An application sends a WM_GETTEXT message to copy the text that + * corresponds to a window into a buffer provided by the caller. */ case WM_ACTIVATEAPP: - /* The WM_ACTIVATEAPP message is sent when a window belonging to a - * different application than the active window is about to be activated. - * The message is sent to the application whose window is being activated - * and to the application whose window is being deactivated. - */ - case WM_TIMER: + /* The WM_ACTIVATEAPP message is sent when a window belonging to a + * different application than the active window is about to be activated. + * The message is sent to the application whose window is being activated + * and to the application whose window is being deactivated. */ + case WM_TIMER: { /* The WIN32 docs say: * The WM_TIMER message is posted to the installing thread's message queue * when a timer expires. You can process the message by providing a WM_TIMER @@ -2113,19 +2081,20 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, * call the TimerProc callback function specified in the call to the SetTimer * function used to install the timer. * - * In GHOST, we let DefWindowProc call the timer callback. - */ + * In GHOST, we let DefWindowProc call the timer callback. */ break; - case DM_POINTERHITTEST: + } + case DM_POINTERHITTEST: { /* The DM_POINTERHITTEST message is sent to a window, when pointer input is first * detected, in order to determine the most probable input target for Direct * Manipulation. */ window->onPointerHitTest(wParam); break; + } } } else { - // Event found for a window before the pointer to the class has been set. + /* Event found for a window before the pointer to the class has been set. */ GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n"); /* These are events we typically miss at this point: * WM_GETMINMAXINFO 0x24 @@ -2137,7 +2106,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } } else { - // Events without valid hwnd + /* Events without valid `hwnd`. */ GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n"); } @@ -2146,16 +2115,15 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, eventHandled = true; } - if (!eventHandled) + if (!eventHandled) { lResult = ::DefWindowProcW(hwnd, msg, wParam, lParam); + } return lResult; } char *GHOST_SystemWin32::getClipboard(bool selection) const { - char *temp_buff; - if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) { wchar_t *buffer; HANDLE hData = GetClipboardData(CF_UNICODETEXT); @@ -2169,7 +2137,7 @@ char *GHOST_SystemWin32::getClipboard(bool selection) const return NULL; } - temp_buff = alloc_utf_8_from_16(buffer, 0); + char *temp_buff = alloc_utf_8_from_16(buffer, 0); /* Buffer mustn't be accessed after CloseClipboard * it would like accessing free-d memory */ @@ -2178,7 +2146,7 @@ char *GHOST_SystemWin32::getClipboard(bool selection) const return temp_buff; } - else if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL)) { + if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL)) { char *buffer; size_t len = 0; HANDLE hData = GetClipboardData(CF_TEXT); @@ -2193,7 +2161,7 @@ char *GHOST_SystemWin32::getClipboard(bool selection) const } len = strlen(buffer); - temp_buff = (char *)malloc(len + 1); + char *temp_buff = (char *)malloc(len + 1); strncpy(temp_buff, buffer, len); temp_buff[len] = '\0'; @@ -2204,21 +2172,19 @@ char *GHOST_SystemWin32::getClipboard(bool selection) const return temp_buff; } - else { - return NULL; - } + return nullptr; } void GHOST_SystemWin32::putClipboard(const char *buffer, bool selection) const { if (selection || !buffer) { return; - } // for copying the selection, used on X11 + } /* For copying the selection, used on X11. */ if (OpenClipboard(NULL)) { EmptyClipboard(); - // Get length of buffer including the terminating null + /* Get length of buffer including the terminating null. */ size_t len = count_utf_16_from_8(buffer); HGLOBAL clipbuffer = GlobalAlloc(GMEM_MOVEABLE, sizeof(wchar_t) * len); @@ -2275,7 +2241,7 @@ GHOST_TSuccess GHOST_SystemWin32::showMessageBox(const char *title, case IDCONTINUE: break; default: - break; // should never happen + break; /* Should never happen. */ } free((void *)title_16); @@ -2343,14 +2309,15 @@ static bool isStartedFromCommandPrompt() } /* When we're starting from a wrapper we need to compare with parent process ID. */ - if (pid != (start_from_launcher ? ppid : GetCurrentProcessId())) + if (pid != (start_from_launcher ? ppid : GetCurrentProcessId())) { return true; + } } return false; } -int GHOST_SystemWin32::setConsoleWindowState(GHOST_TConsoleWindowState action) +bool GHOST_SystemWin32::setConsoleWindowState(GHOST_TConsoleWindowState action) { HWND wnd = GetConsoleWindow(); @@ -2358,28 +2325,31 @@ int GHOST_SystemWin32::setConsoleWindowState(GHOST_TConsoleWindowState action) case GHOST_kConsoleWindowStateHideForNonConsoleLaunch: { if (!isStartedFromCommandPrompt()) { ShowWindow(wnd, SW_HIDE); - m_consoleStatus = 0; + m_consoleStatus = false; } break; } - case GHOST_kConsoleWindowStateHide: + case GHOST_kConsoleWindowStateHide: { ShowWindow(wnd, SW_HIDE); - m_consoleStatus = 0; + m_consoleStatus = false; break; - case GHOST_kConsoleWindowStateShow: + } + case GHOST_kConsoleWindowStateShow: { ShowWindow(wnd, SW_SHOW); if (!isStartedFromCommandPrompt()) { DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND); } - m_consoleStatus = 1; + m_consoleStatus = true; break; - case GHOST_kConsoleWindowStateToggle: + } + case GHOST_kConsoleWindowStateToggle: { ShowWindow(wnd, m_consoleStatus ? SW_HIDE : SW_SHOW); m_consoleStatus = !m_consoleStatus; if (m_consoleStatus && !isStartedFromCommandPrompt()) { DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND); } break; + } } return m_consoleStatus; diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index 689b78b0317..81c565ae732 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -10,10 +10,10 @@ #ifndef WIN32 # error WIN32 only! -#endif // WIN32 +#endif /* WIN32 */ #define WIN32_LEAN_AND_MEAN -#include <ole2.h> // for drag-n-drop +#include <ole2.h> /* For drag-n-drop. */ #include <windows.h> #include "GHOST_System.h" @@ -295,11 +295,10 @@ class GHOST_SystemWin32 : public GHOST_System { /** * Catches raw WIN32 key codes from WM_INPUT in the wndproc. * \param raw: RawInput structure with detailed info about the key event. - * \param keyDown: Pointer flag that specify if a key is down. - * \param vk: Pointer to virtual key. + * \param r_key_down: Set true when the key is pressed, otherwise false. * \return The GHOST key (GHOST_kKeyUnknown if no match). */ - GHOST_TKey hardKey(RAWINPUT const &raw, bool *r_keyDown, bool *r_is_repeated_modifier); + GHOST_TKey hardKey(RAWINPUT const &raw, bool *r_key_down); /** * Creates mouse button event. @@ -310,7 +309,7 @@ class GHOST_SystemWin32 : public GHOST_System { */ static GHOST_EventButton *processButtonEvent(GHOST_TEventType type, GHOST_WindowWin32 *window, - GHOST_TButtonMask mask); + GHOST_TButton mask); /** * Creates tablet events from Wintab events. @@ -387,7 +386,7 @@ class GHOST_SystemWin32 : public GHOST_System { static GHOST_Event *processImeEvent(GHOST_TEventType type, GHOST_WindowWin32 *window, GHOST_TEventImeData *data); -#endif // WITH_INPUT_IME +#endif /* WITH_INPUT_IME */ /** * Handles minimum window size. @@ -417,19 +416,6 @@ class GHOST_SystemWin32 : public GHOST_System { void processTrackpad(); /** - * Returns the local state of the modifier keys (from the message queue). - * \param keys: The state of the keys. - */ - inline void retrieveModifierKeys(GHOST_ModifierKeys &keys) const; - - /** - * Stores the state of the modifier keys locally. - * For internal use only! - * param keys The new state of the modifier keys. - */ - inline void storeModifierKeys(const GHOST_ModifierKeys &keys); - - /** * Check current key layout for AltGr */ inline void handleKeyboardChange(void); @@ -444,10 +430,8 @@ class GHOST_SystemWin32 : public GHOST_System { * \param action: console state * \return current status (1 -visible, 0 - hidden) */ - int setConsoleWindowState(GHOST_TConsoleWindowState action); + bool setConsoleWindowState(GHOST_TConsoleWindowState action); - /** The current state of the modifier keys. */ - GHOST_ModifierKeys m_modifierKeys; /** The virtual-key code (VKey) of the last press event. Used to detect repeat events. */ unsigned short m_keycode_last_repeat_key; /** State variable set at initialization. */ @@ -466,36 +450,26 @@ class GHOST_SystemWin32 : public GHOST_System { HKL m_keylayout; /** Console status. */ - int m_consoleStatus; + bool m_consoleStatus; /** Wheel delta accumulator. */ int m_wheelDeltaAccum; }; -inline void GHOST_SystemWin32::retrieveModifierKeys(GHOST_ModifierKeys &keys) const -{ - keys = m_modifierKeys; -} - -inline void GHOST_SystemWin32::storeModifierKeys(const GHOST_ModifierKeys &keys) -{ - m_modifierKeys = keys; -} - inline void GHOST_SystemWin32::handleKeyboardChange(void) { - m_keylayout = GetKeyboardLayout(0); // get keylayout for current thread + m_keylayout = GetKeyboardLayout(0); /* Get keylayout for current thread. */ int i; SHORT s; - // save the language identifier. + /* Save the language identifier. */ m_langId = LOWORD(m_keylayout); for (m_hasAltGr = false, i = 32; i < 256; ++i) { s = VkKeyScanEx((char)i, m_keylayout); - // s == -1 means no key that translates passed char code - // high byte contains shift state. bit 2 ctrl pressed, bit 4 alt pressed - // if both are pressed, we have AltGr keycombo on keylayout + /* `s == -1` means no key that translates passed char code high byte contains shift state. + * bit 2 Control pressed, bit 4 `Alt` pressed if both are pressed, + * we have `AltGr` key-combination on key-layout. */ if (s != -1 && (s & 0x600) == 0x600) { m_hasAltGr = true; break; diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index bed9cd6c784..bb98c0de19b 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -25,6 +25,7 @@ #ifdef WITH_INPUT_NDOF # include "GHOST_NDOFManagerUnix.h" #endif +#include "GHOST_utildefines.h" #ifdef WITH_XDND # include "GHOST_DropTargetX11.h" @@ -32,12 +33,8 @@ #include "GHOST_Debug.h" -#if defined(WITH_GL_EGL) -# include "GHOST_ContextEGL.h" -# include <EGL/eglext.h> -#else -# include "GHOST_ContextGLX.h" -#endif +#include "GHOST_ContextEGL.h" +#include "GHOST_ContextGLX.h" #ifdef WITH_XF86KEYSYM # include <X11/XF86keysym.h> @@ -104,8 +101,7 @@ GHOST_SystemX11::GHOST_SystemX11() : GHOST_System(), m_xkb_descr(nullptr), m_sta m_display = XOpenDisplay(nullptr); if (!m_display) { - std::cerr << "Unable to open a display" << std::endl; - abort(); /* was return before, but this would just mean it will crash later */ + throw std::runtime_error("X11: Unable to open a display"); } #ifdef USE_X11_ERROR_HANDLERS @@ -234,10 +230,6 @@ GHOST_SystemX11::~GHOST_SystemX11() clearXInputDevices(); #endif /* WITH_X11_XINPUT */ -#ifdef WITH_GL_EGL - ::eglTerminate(::eglGetDisplay(m_display)); -#endif - if (m_xkb_descr) { XkbFreeKeyboard(m_xkb_descr, XkbAllComponentsMask, true); } @@ -286,7 +278,7 @@ uint8_t GHOST_SystemX11::getNumDisplays() const void GHOST_SystemX11::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const { if (m_display) { - /* NOTE(campbell): for this to work as documented, + /* NOTE(@campbellbarton): for this to work as documented, * we would need to use Xinerama check r54370 for code that did this, * we've since removed since its not worth the extra dependency. */ getAllDisplayDimensions(width, height); @@ -353,7 +345,6 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title, is_dialog, ((glSettings.flags & GHOST_glStereoVisual) != 0), exclusive, - ((glSettings.flags & GHOST_glAlphaBackground) != 0), (glSettings.flags & GHOST_glDebugContext) != 0); if (window) { @@ -374,6 +365,56 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title, return window; } +#ifdef USE_EGL +static GHOST_Context *create_egl_context( + GHOST_SystemX11 *system, Display *display, bool debug_context, int ver_major, int ver_minor) +{ + GHOST_Context *context; + context = new GHOST_ContextEGL(system, + false, + EGLNativeWindowType(nullptr), + EGLNativeDisplayType(display), + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + ver_major, + ver_minor, + GHOST_OPENGL_EGL_CONTEXT_FLAGS | + (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0), + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); + + if (context->initializeDrawingContext()) { + return context; + } + delete context; + + return nullptr; +} +#endif + +static GHOST_Context *create_glx_context(Display *display, + bool debug_context, + int ver_major, + int ver_minor) +{ + GHOST_Context *context; + context = new GHOST_ContextGLX(false, + (Window) nullptr, + display, + (GLXFBConfig) nullptr, + GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + ver_major, + ver_minor, + GHOST_OPENGL_GLX_CONTEXT_FLAGS | + (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), + GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY); + + if (context->initializeDrawingContext()) { + return context; + } + delete context; + + return nullptr; +} /** * Create a new off-screen context. * Never explicitly delete the context, use #disposeContext() instead. @@ -393,98 +434,33 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSetti const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0; -#if defined(WITH_GL_PROFILE_CORE) - { - const char *version_major = (char *)glewGetString(GLEW_VERSION_MAJOR); - if (version_major != nullptr && version_major[0] == '1') { - fprintf(stderr, "Error: GLEW version 2.0 and above is required.\n"); - abort(); - } - } -#endif - - const int profile_mask = -#ifdef WITH_GL_EGL -# if defined(WITH_GL_PROFILE_CORE) - EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT; -# elif defined(WITH_GL_PROFILE_COMPAT) - EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT; -# else -# error // must specify either core or compat at build time -# endif -#else -# if defined(WITH_GL_PROFILE_CORE) - GLX_CONTEXT_CORE_PROFILE_BIT_ARB; -# elif defined(WITH_GL_PROFILE_COMPAT) - GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; -# else -# error // must specify either core or compat at build time -# endif -#endif - GHOST_Context *context; +#ifdef USE_EGL + /* Try to initialize an EGL context. */ for (int minor = 5; minor >= 0; --minor) { -#if defined(WITH_GL_EGL) - context = new GHOST_ContextEGL(this, - false, - EGLNativeWindowType(nullptr), - EGLNativeDisplayType(m_display), - profile_mask, - 4, - minor, - GHOST_OPENGL_EGL_CONTEXT_FLAGS | - (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0), - GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, - EGL_OPENGL_API); -#else - context = new GHOST_ContextGLX(false, - (Window) nullptr, - m_display, - (GLXFBConfig) nullptr, - profile_mask, - 4, - minor, - GHOST_OPENGL_GLX_CONTEXT_FLAGS | - (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY); -#endif - - if (context->initializeDrawingContext()) { + context = create_egl_context(this, m_display, debug_context, 4, minor); + if (context != nullptr) { return context; } - delete context; + } + context = create_egl_context(this, m_display, debug_context, 3, 3); + if (context != nullptr) { + return context; } -#if defined(WITH_GL_EGL) - context = new GHOST_ContextEGL(this, - false, - EGLNativeWindowType(nullptr), - EGLNativeDisplayType(m_display), - profile_mask, - 3, - 3, - GHOST_OPENGL_EGL_CONTEXT_FLAGS | - (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0), - GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, - EGL_OPENGL_API); -#else - context = new GHOST_ContextGLX(false, - (Window) nullptr, - m_display, - (GLXFBConfig) nullptr, - profile_mask, - 3, - 3, - GHOST_OPENGL_GLX_CONTEXT_FLAGS | - (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY); + /* EGL initialization failed, try to fallback to a GLX context. */ #endif - - if (context->initializeDrawingContext()) { + for (int minor = 5; minor >= 0; --minor) { + context = create_glx_context(m_display, debug_context, 4, minor); + if (context != nullptr) { + return context; + } + } + context = create_glx_context(m_display, debug_context, 3, 3); + if (context != nullptr) { return context; } - delete context; return nullptr; } @@ -513,8 +489,9 @@ static void destroyIMCallback(XIM /*xim*/, XPointer ptr, XPointer /*data*/) bool GHOST_SystemX11::openX11_IM() { - if (!m_display) + if (!m_display) { return false; + } /* set locale modifiers such as `@im=ibus` specified by XMODIFIERS. */ XSetLocaleModifiers(""); @@ -584,7 +561,7 @@ struct init_timestamp_data { Time timestamp; }; -static Bool init_timestamp_scanner(Display *, XEvent *event, XPointer arg) +static Bool init_timestamp_scanner(Display * /*display*/, XEvent *event, XPointer arg) { init_timestamp_data *data = reinterpret_cast<init_timestamp_data *>(arg); switch (event->type) { @@ -663,7 +640,7 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) /* open connection to XIM server and create input context (XIC) * when receiving the first FocusIn or KeyPress event after startup, * or recover XIM and XIC when the XIM server has been restarted */ - if (xevent.type == FocusIn || xevent.type == KeyPress) { + if (ELEM(xevent.type, FocusIn, KeyPress)) { if (!m_xim && openX11_IM()) { GHOST_PRINT("Connected to XIM server\n"); } @@ -672,11 +649,12 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) GHOST_WindowX11 *window = findGhostWindow(xevent.xany.window); if (window && !window->getX11_XIC() && window->createX11_XIC()) { GHOST_PRINT("XIM input context created\n"); - if (xevent.type == KeyPress) + if (xevent.type == KeyPress) { /* we can assume the window has input focus * here, because key events are received only * when the window is focused. */ XSetICFocus(window->getX11_XIC()); + } } } } @@ -724,9 +702,9 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) * in order to confirm the window is active. */ XPeekEvent(m_display, &xev_next); - if (xev_next.type == KeyPress || xev_next.type == KeyRelease) { - /* XK_Hyper_L/R currently unused */ - const static KeySym modifiers[8] = { + if (ELEM(xev_next.type, KeyPress, KeyRelease)) { + /* XK_Hyper_L/R currently unused. */ + const static KeySym modifiers[] = { XK_Shift_L, XK_Shift_R, XK_Control_L, @@ -737,16 +715,15 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) XK_Super_R, }; - for (int i = 0; i < (int)(sizeof(modifiers) / sizeof(*modifiers)); i++) { + for (int i = 0; i < (int)ARRAY_SIZE(modifiers); i++) { KeyCode kc = XKeysymToKeycode(m_display, modifiers[i]); if (kc != 0 && ((xevent.xkeymap.key_vector[kc >> 3] >> (kc & 7)) & 1) != 0) { pushEvent(new GHOST_EventKey(getMilliSeconds(), GHOST_kEventKeyDown, window, ghost_key_from_keysym(modifiers[i]), - '\0', - nullptr, - false)); + false, + nullptr)); } } } @@ -822,7 +799,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) /* Detect auto-repeat. */ bool is_repeat = false; - if (xe->type == KeyPress || xe->type == KeyRelease) { + if (ELEM(xe->type, KeyPress, KeyRelease)) { XKeyEvent *xke = &(xe->xkey); /* Set to true if this key will repeat. */ @@ -889,9 +866,11 @@ void GHOST_SystemX11::processEvent(XEvent *xe) if (xe->type == xi_presence) { XDevicePresenceNotifyEvent *notify_event = (XDevicePresenceNotifyEvent *)xe; - if ((notify_event->devchange == DeviceEnabled) || - (notify_event->devchange == DeviceDisabled) || - (notify_event->devchange == DeviceAdded) || (notify_event->devchange == DeviceRemoved)) { + if (ELEM(notify_event->devchange, + DeviceEnabled, + DeviceDisabled, + DeviceAdded, + DeviceRemoved)) { refreshXInputDevices(); /* update all window events */ @@ -1014,7 +993,9 @@ void GHOST_SystemX11::processEvent(XEvent *xe) case KeyRelease: { XKeyEvent *xke = &(xe->xkey); KeySym key_sym; + char *utf8_buf = nullptr; char ascii; + #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* utf8_array[] is initial buffer used for Xutf8LookupString(). * if the length of the utf8 string exceeds this array, allocate @@ -1023,12 +1004,10 @@ void GHOST_SystemX11::processEvent(XEvent *xe) * at the end of this buffer when the constructor of GHOST_EventKey * reads 6 bytes regardless of the effective data length. */ char utf8_array[16 * 6 + 5]; /* 16 utf8 characters */ - char *utf8_buf = utf8_array; - int len = 1; /* at least one null character will be stored */ + int len = 1; /* at least one null character will be stored */ #else - char *utf8_buf = nullptr; + char utf8_array[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'}; #endif - GHOST_TEventType type = (xke->type == KeyPress) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; GHOST_TKey gkey; @@ -1148,81 +1127,94 @@ void GHOST_SystemX11::processEvent(XEvent *xe) #endif #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) - /* Setting unicode on key-up events gives #XLookupNone status. */ - XIC xic = window->getX11_XIC(); - if (xic && xke->type == KeyPress) { - Status status; + /* Only used for key-press. */ + XIC xic = nullptr; +#endif - /* Use utf8 because its not locale repentant, from XORG docs. */ - if (!(len = Xutf8LookupString( - xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) { - utf8_buf[0] = '\0'; - } + if (xke->type == KeyPress) { + utf8_buf = utf8_array; +#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) + /* Setting unicode on key-up events gives #XLookupNone status. */ + xic = window->getX11_XIC(); + if (xic) { + Status status; + + /* Use utf8 because its not locale repentant, from XORG docs. */ + if (!(len = Xutf8LookupString( + xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) { + utf8_buf[0] = '\0'; + } - if (status == XBufferOverflow) { - utf8_buf = (char *)malloc(len + 5); - len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status); - } + if (status == XBufferOverflow) { + utf8_buf = (char *)malloc(len + 5); + len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status); + } - if ((status == XLookupChars || status == XLookupBoth)) { - if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */ - /* do nothing for now, this is valid utf8 */ + if (ELEM(status, XLookupChars, XLookupBoth)) { + if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */ + /* do nothing for now, this is valid utf8 */ + } + else { + utf8_buf[0] = '\0'; + } + } + else if (status == XLookupKeySym) { + /* this key doesn't have a text representation, it is a command + * key of some sort */ } else { - utf8_buf[0] = '\0'; + printf("Bad keycode lookup. Keysym 0x%x Status: %s\n", + (unsigned int)key_sym, + (status == XLookupNone ? "XLookupNone" : + status == XLookupKeySym ? "XLookupKeySym" : + "Unknown status")); + + printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim); } } - else if (status == XLookupKeySym) { - /* this key doesn't have a text representation, it is a command - * key of some sort */ - } else { - printf("Bad keycode lookup. Keysym 0x%x Status: %s\n", - (unsigned int)key_sym, - (status == XLookupNone ? "XLookupNone" : - status == XLookupKeySym ? "XLookupKeySym" : - "Unknown status")); - - printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim); + utf8_buf[0] = '\0'; } - } - else { - utf8_buf[0] = '\0'; - } #endif + if (!utf8_buf[0] && ascii) { + utf8_buf[0] = ascii; + utf8_buf[1] = '\0'; + } + } - g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, ascii, utf8_buf, is_repeat); + g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf); #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* when using IM for some languages such as Japanese, * one event inserts multiple utf8 characters */ - if (xic && xke->type == KeyPress) { + if (xke->type == KeyPress && xic) { unsigned char c; int i = 0; - while (1) { - /* search character boundary */ - if ((unsigned char)utf8_buf[i++] > 0x7f) { + while (true) { + /* Search character boundary. */ + if ((uchar)utf8_buf[i++] > 0x7f) { for (; i < len; ++i) { c = utf8_buf[i]; - if (c < 0x80 || c > 0xbf) + if (c < 0x80 || c > 0xbf) { break; + } } } - if (i >= len) + if (i >= len) { break; - - /* enqueue previous character */ + } + /* Enqueue previous character. */ pushEvent(g_event); g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, '\0', &utf8_buf[i], is_repeat); + getMilliSeconds(), type, window, gkey, is_repeat, &utf8_buf[i]); } } - if (utf8_buf != utf8_array) + if (utf8_buf != utf8_array) { free(utf8_buf); + } #endif break; @@ -1231,7 +1223,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) case ButtonPress: case ButtonRelease: { XButtonEvent &xbe = xe->xbutton; - GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft; + GHOST_TButton gbmask = GHOST_kButtonMaskLeft; GHOST_TEventType type = (xbe.type == ButtonPress) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp; @@ -1306,10 +1298,12 @@ void GHOST_SystemX11::processEvent(XEvent *xe) #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) XIC xic = window->getX11_XIC(); if (xic) { - if (xe->type == FocusIn) + if (xe->type == FocusIn) { XSetICFocus(xic); - else + } + else { XUnsetICFocus(xic); + } } #endif @@ -1449,8 +1443,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) nxe.xselection.time = xse->time; /* Check to see if the requester is asking for String */ - if (xse->target == utf8_string || xse->target == string || xse->target == compound_text || - xse->target == c_string) { + if (ELEM(xse->target, utf8_string, string, compound_text, c_string)) { if (xse->selection == XInternAtom(m_display, "PRIMARY", False)) { XChangeProperty(m_display, xse->requestor, @@ -1503,7 +1496,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) default: { #ifdef WITH_X11_XINPUT for (GHOST_TabletX11 &xtablet : m_xtablets) { - if (xe->type == xtablet.MotionEvent || xe->type == xtablet.PressEvent) { + if (ELEM(xe->type, xtablet.MotionEvent, xtablet.PressEvent)) { XDeviceMotionEvent *data = (XDeviceMotionEvent *)xe; if (data->deviceid != xtablet.ID) { continue; @@ -2367,10 +2360,11 @@ class DialogData { /* Is the mouse inside the given button */ bool isInsideButton(XEvent &e, uint button_num) { - return ((e.xmotion.y > height - padding_y - button_height) && - (e.xmotion.y < height - padding_y) && - (e.xmotion.x > width - (padding_x + button_width) * button_num) && - (e.xmotion.x < width - padding_x - (padding_x + button_width) * (button_num - 1))); + return ( + (e.xmotion.y > (int)(height - padding_y - button_height)) && + (e.xmotion.y < (int)(height - padding_y)) && + (e.xmotion.x > (int)(width - (padding_x + button_width) * button_num)) && + (e.xmotion.x < (int)(width - padding_x - (padding_x + button_width) * (button_num - 1)))); } }; @@ -2574,7 +2568,7 @@ int GHOST_X11_ApplicationIOErrorHandler(Display * /*display*/) static bool is_filler_char(char c) { - return isspace(c) || c == '_' || c == '-' || c == ';' || c == ':'; + return isspace(c) || ELEM(c, '_', '-', ';', ':'); } /* These C functions are copied from Wine 3.12's `wintab.c` */ @@ -2674,7 +2668,7 @@ void GHOST_SystemX11::refreshXInputDevices() XFree((void *)device_type); } - if (!(tablet_mode == GHOST_kTabletModeStylus || tablet_mode == GHOST_kTabletModeEraser)) { + if (!ELEM(tablet_mode, GHOST_kTabletModeStylus, GHOST_kTabletModeEraser)) { continue; } diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h index 7938aa2b646..bd6ace101e9 100644 --- a/intern/ghost/intern/GHOST_SystemX11.h +++ b/intern/ghost/intern/GHOST_SystemX11.h @@ -253,7 +253,7 @@ class GHOST_SystemX11 : public GHOST_System { /** * \see GHOST_ISystem */ - int setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) + bool setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) { return 0; } diff --git a/intern/ghost/intern/GHOST_TimerManager.cpp b/intern/ghost/intern/GHOST_TimerManager.cpp index 504cdbfb6c8..e54c2515029 100644 --- a/intern/ghost/intern/GHOST_TimerManager.cpp +++ b/intern/ghost/intern/GHOST_TimerManager.cpp @@ -71,10 +71,11 @@ uint64_t GHOST_TimerManager::nextFireTime() TTimerVector::iterator iter; for (iter = m_timers.begin(); iter != m_timers.end(); ++iter) { - uint64_t next = (*iter)->getNext(); + const uint64_t next = (*iter)->getNext(); - if (next < smallest) + if (next < smallest) { smallest = next; + } } return smallest; @@ -86,8 +87,9 @@ bool GHOST_TimerManager::fireTimers(uint64_t time) bool anyProcessed = false; for (iter = m_timers.begin(); iter != m_timers.end(); ++iter) { - if (fireTimer(time, *iter)) + if (fireTimer(time, *iter)) { anyProcessed = true; + } } return anyProcessed; @@ -113,9 +115,7 @@ bool GHOST_TimerManager::fireTimer(uint64_t time, GHOST_TimerTask *task) return true; } - else { - return false; - } + return false; } void GHOST_TimerManager::disposeTimers() diff --git a/intern/ghost/intern/GHOST_TimerManager.h b/intern/ghost/intern/GHOST_TimerManager.h index 090a84d1f14..4458a107190 100644 --- a/intern/ghost/intern/GHOST_TimerManager.h +++ b/intern/ghost/intern/GHOST_TimerManager.h @@ -87,7 +87,7 @@ class GHOST_TimerManager { */ void disposeTimers(); - typedef std::vector<GHOST_TimerTask *> TTimerVector; + using TTimerVector = std::vector<GHOST_TimerTask *>; /** The list with event consumers. */ TTimerVector m_timers; diff --git a/intern/ghost/intern/GHOST_WaylandCursorSettings.h b/intern/ghost/intern/GHOST_WaylandCursorSettings.h index 2491f6ca31f..f5649d20850 100644 --- a/intern/ghost/intern/GHOST_WaylandCursorSettings.h +++ b/intern/ghost/intern/GHOST_WaylandCursorSettings.h @@ -5,9 +5,13 @@ */ #pragma once -#include <dbus/dbus.h> #include <string> +#ifdef WITH_GHOST_WAYLAND_DBUS +# include <dbus/dbus.h> +#endif + +#ifdef WITH_GHOST_WAYLAND_DBUS static DBusMessage *get_setting_sync(DBusConnection *const connection, const char *key, const char *value) @@ -66,9 +70,11 @@ static bool parse_type(DBusMessage *const reply, const int type, void *value) return true; } +#endif /* WITH_GHOST_WAYLAND_DBUS */ static bool get_cursor_settings(std::string &theme, int &size) { +#ifdef WITH_GHOST_WAYLAND_DBUS static const char name[] = "org.gnome.desktop.interface"; static const char key_theme[] = "cursor-theme"; static const char key_size[] = "cursor-size"; @@ -113,4 +119,11 @@ static bool get_cursor_settings(std::string &theme, int &size) dbus_message_unref(reply); return true; +#else + /* NOTE: eventually we could have alternative ways to access the theme, + * this uses the "default" theme which is functional (instead of a user-defined theme). */ + (void)theme; + (void)size; + return false; +#endif /* !WITH_GHOST_WAYLAND_DBUS */ } diff --git a/intern/ghost/intern/GHOST_WaylandUtils.h b/intern/ghost/intern/GHOST_WaylandUtils.h new file mode 100644 index 00000000000..0e1e133bc4c --- /dev/null +++ b/intern/ghost/intern/GHOST_WaylandUtils.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup GHOST + */ + +#pragma once + +#ifdef __cplusplus +# undef wl_array_for_each +/** + * This macro causes a warning for C++ code, define our own. + * See: https://gitlab.freedesktop.org/wayland/wayland/-/issues/34 + */ +# define WL_ARRAY_FOR_EACH(pos, array) \ + for (pos = (decltype(pos))((array)->data); \ + (const char *)pos < ((const char *)(array)->data + (array)->size); \ + (pos)++) +#endif diff --git a/intern/ghost/intern/GHOST_Window.cpp b/intern/ghost/intern/GHOST_Window.cpp index de7c5422d3f..db4d6c3bb71 100644 --- a/intern/ghost/intern/GHOST_Window.cpp +++ b/intern/ghost/intern/GHOST_Window.cpp @@ -23,6 +23,7 @@ GHOST_Window::GHOST_Window(uint32_t width, : m_drawingContextType(GHOST_kDrawingContextTypeNone), m_cursorVisible(true), m_cursorGrab(GHOST_kGrabDisable), + m_cursorGrabAxis(GHOST_kAxisNone), m_cursorShape(GHOST_kStandardCursorDefault), m_wantStereoVisual(wantStereoVisual), m_context(new GHOST_ContextNone(false)) @@ -164,7 +165,8 @@ GHOST_TSuccess GHOST_Window::getCursorGrabBounds(GHOST_Rect &bounds) void GHOST_Window::getCursorGrabState(GHOST_TGrabCursorMode &mode, GHOST_TAxisFlag &wrap_axis, - GHOST_Rect &bounds) + GHOST_Rect &bounds, + bool &use_software_cursor) { mode = m_cursorGrab; if (m_cursorGrab == GHOST_kGrabWrap) { @@ -176,8 +178,16 @@ void GHOST_Window::getCursorGrabState(GHOST_TGrabCursorMode &mode, bounds.m_r = -1; bounds.m_t = -1; bounds.m_b = -1; - wrap_axis = GHOST_kGrabAxisNone; + wrap_axis = GHOST_kAxisNone; } + use_software_cursor = (m_cursorGrab != GHOST_kGrabDisable) ? getCursorGrabUseSoftwareDisplay() : + false; +} + +bool GHOST_Window::getCursorGrabUseSoftwareDisplay() +{ + /* Sub-classes may override, by default don't use software cursor. */ + return false; } GHOST_TSuccess GHOST_Window::setCursorShape(GHOST_TStandardCursor cursorShape) @@ -199,6 +209,12 @@ GHOST_TSuccess GHOST_Window::setCustomCursorShape( return GHOST_kFailure; } +GHOST_TSuccess GHOST_Window::getCursorBitmap(GHOST_CursorBitmapRef * /*bitmap*/) +{ + /* Sub-classes may override. */ + return GHOST_kFailure; +} + void GHOST_Window::setAcceptDragOperation(bool canAccept) { m_canAcceptDragOperation = canAccept; diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h index adbc29eb84e..5ff91c05b16 100644 --- a/intern/ghost/intern/GHOST_Window.h +++ b/intern/ghost/intern/GHOST_Window.h @@ -117,6 +117,8 @@ class GHOST_Window : public GHOST_IWindow { int hotY, bool canInvertColor); + GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap); + /** * Returns the visibility state of the cursor. * \return The visibility state of the cursor. @@ -154,7 +156,12 @@ class GHOST_Window : public GHOST_IWindow { void getCursorGrabState(GHOST_TGrabCursorMode &mode, GHOST_TAxisFlag &axis_flag, - GHOST_Rect &bounds); + GHOST_Rect &bounds, + bool &use_software_cursor); + /** + * Return true when a software cursor should be used. + */ + bool getCursorGrabUseSoftwareDisplay(); /** * Sets the progress bar value displayed in the window/application icon diff --git a/intern/ghost/intern/GHOST_WindowManager.cpp b/intern/ghost/intern/GHOST_WindowManager.cpp index 19684a44169..e8785cbdb24 100644 --- a/intern/ghost/intern/GHOST_WindowManager.cpp +++ b/intern/ghost/intern/GHOST_WindowManager.cpp @@ -162,17 +162,3 @@ GHOST_IWindow *GHOST_WindowManager::getWindowAssociatedWithOSWindow(void *osWind } return nullptr; } - -bool GHOST_WindowManager::getAnyModifiedState() -{ - bool isAnyModified = false; - std::vector<GHOST_IWindow *>::iterator iter; - - for (iter = m_windows.begin(); iter != m_windows.end(); ++iter) { - if ((*iter)->getModifiedState()) { - isAnyModified = true; - } - } - - return isAnyModified; -} diff --git a/intern/ghost/intern/GHOST_WindowManager.h b/intern/ghost/intern/GHOST_WindowManager.h index 9d20413c433..bf7a0f4ec61 100644 --- a/intern/ghost/intern/GHOST_WindowManager.h +++ b/intern/ghost/intern/GHOST_WindowManager.h @@ -109,12 +109,6 @@ class GHOST_WindowManager { */ GHOST_IWindow *getWindowAssociatedWithOSWindow(void *osWindow); - /** - * Return true if any windows has a modified status - * \return True if any window has unsaved changes - */ - bool getAnyModifiedState(); - protected: /** The list of windows managed */ std::vector<GHOST_IWindow *> m_windows; diff --git a/intern/ghost/intern/GHOST_WindowNULL.h b/intern/ghost/intern/GHOST_WindowNULL.h index 01b50251d69..f9c0a593d5f 100644 --- a/intern/ghost/intern/GHOST_WindowNULL.h +++ b/intern/ghost/intern/GHOST_WindowNULL.h @@ -11,32 +11,31 @@ #include <map> -class GHOST_SystemNULL; +class GHOST_SystemHeadless; class GHOST_WindowNULL : public GHOST_Window { public: - GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor) + GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor /*cursorShape*/) override { return GHOST_kSuccess; } - GHOST_WindowNULL(GHOST_SystemNULL *system, - const char *title, - int32_t left, - int32_t top, + GHOST_WindowNULL(const char *title, + int32_t /*left*/, + int32_t /*top*/, uint32_t width, uint32_t height, GHOST_TWindowState state, - const GHOST_IWindow *parentWindow, - GHOST_TDrawingContextType type, + const GHOST_IWindow * /*parentWindow*/, + GHOST_TDrawingContextType /*type*/, const bool stereoVisual) - : GHOST_Window(width, height, state, stereoVisual, false), m_system(system) + : GHOST_Window(width, height, state, stereoVisual, false) { setTitle(title); } protected: - GHOST_TSuccess installDrawingContext(GHOST_TDrawingContextType type) + GHOST_TSuccess installDrawingContext(GHOST_TDrawingContextType /*type*/) { return GHOST_kSuccess; } @@ -44,114 +43,111 @@ class GHOST_WindowNULL : public GHOST_Window { { return GHOST_kSuccess; } - GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) + GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode /*mode*/) override { return GHOST_kSuccess; } - GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape) + GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor /*shape*/) override { return GHOST_kSuccess; } - GHOST_TSuccess setWindowCustomCursorShape(uint8_t *bitmap, - uint8_t *mask, - int sizex, - int sizey, - int hotX, - int hotY, - bool canInvertColor) + GHOST_TSuccess setWindowCustomCursorShape(uint8_t * /*bitmap*/, + uint8_t * /*mask*/, + int /*sizex*/, + int /*sizey*/, + int /*hotX*/, + int /*hotY*/, + bool /*canInvertColor*/) override { return GHOST_kSuccess; } - bool getValid() const + bool getValid() const override { return true; } - void setTitle(const char *title) + void setTitle(const char * /*title*/) override { /* nothing */ } - std::string getTitle() const + std::string getTitle() const override { return "untitled"; } - void getWindowBounds(GHOST_Rect &bounds) const + void getWindowBounds(GHOST_Rect &bounds) const override { getClientBounds(bounds); } - void getClientBounds(GHOST_Rect &bounds) const + void getClientBounds(GHOST_Rect & /*bounds*/) const override { /* nothing */ } - GHOST_TSuccess setClientWidth(uint32_t width) + GHOST_TSuccess setClientWidth(uint32_t /*width*/) override { return GHOST_kFailure; } - GHOST_TSuccess setClientHeight(uint32_t height) + GHOST_TSuccess setClientHeight(uint32_t /*height*/) override { return GHOST_kFailure; } - GHOST_TSuccess setClientSize(uint32_t width, uint32_t height) + GHOST_TSuccess setClientSize(uint32_t /*width*/, uint32_t /*height*/) override { return GHOST_kFailure; } - void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const + void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const override { outX = inX; outY = inY; } - void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const + void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const override { outX = inX; outY = inY; } - GHOST_TSuccess swapBuffers() + GHOST_TSuccess swapBuffers() override { return GHOST_kFailure; } - GHOST_TSuccess activateDrawingContext() + GHOST_TSuccess activateDrawingContext() override { return GHOST_kFailure; } - ~GHOST_WindowNULL() - { /* nothing */ - } - GHOST_TSuccess setWindowCursorVisibility(bool visible) + ~GHOST_WindowNULL() override = default; + + GHOST_TSuccess setWindowCursorVisibility(bool /*visible*/) override { return GHOST_kSuccess; } - GHOST_TSuccess setState(GHOST_TWindowState state) + GHOST_TSuccess setState(GHOST_TWindowState /*state*/) override { return GHOST_kSuccess; } - GHOST_TWindowState getState() const + GHOST_TWindowState getState() const override { return GHOST_kWindowStateNormal; } - GHOST_TSuccess invalidate() + GHOST_TSuccess invalidate() override { return GHOST_kSuccess; } - GHOST_TSuccess setOrder(GHOST_TWindowOrder order) + GHOST_TSuccess setOrder(GHOST_TWindowOrder /*order*/) override { return GHOST_kSuccess; } - GHOST_TSuccess beginFullScreen() const + GHOST_TSuccess beginFullScreen() const override { return GHOST_kSuccess; } - GHOST_TSuccess endFullScreen() const + GHOST_TSuccess endFullScreen() const override { return GHOST_kSuccess; } private: - GHOST_SystemNULL *m_system; - /** * \param type: The type of rendering context create. * \return Indication of success. */ - GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type) + GHOST_Context *newDrawingContext(GHOST_TDrawingContextType /*type*/) override { return nullptr; } diff --git a/intern/ghost/intern/GHOST_WindowSDL.cpp b/intern/ghost/intern/GHOST_WindowSDL.cpp index 09192d989e4..59dc80cf7e6 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.cpp +++ b/intern/ghost/intern/GHOST_WindowSDL.cpp @@ -6,7 +6,6 @@ #include "GHOST_WindowSDL.h" #include "SDL_mouse.h" -#include "glew-mx.h" #include "GHOST_ContextSDL.h" diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 21e3793d3b1..5f3cb3e3f3a 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -6,20 +6,39 @@ #include "GHOST_WindowWayland.h" #include "GHOST_SystemWayland.h" +#include "GHOST_WaylandUtils.h" #include "GHOST_WindowManager.h" +#include "GHOST_utildefines.h" #include "GHOST_Event.h" #include "GHOST_ContextEGL.h" #include "GHOST_ContextNone.h" +#include <wayland-client-protocol.h> + +#ifdef WITH_GHOST_WAYLAND_DYNLOAD +# include <wayland_dynload_egl.h> +#endif #include <wayland-egl.h> #include <algorithm> /* For `std::find`. */ +#ifdef WITH_GHOST_WAYLAND_LIBDECOR +# ifdef WITH_GHOST_WAYLAND_DYNLOAD +# include <wayland_dynload_libdecor.h> +# endif +# include <libdecor.h> +#endif + +/* Logging, use `ghost.wl.*` prefix. */ +#include "CLG_log.h" + static constexpr size_t base_dpi = 96; -struct window_t { +static GHOST_WindowManager *window_manager = nullptr; + +struct GWL_Window { GHOST_WindowWayland *w = nullptr; struct wl_surface *wl_surface = nullptr; /** @@ -28,7 +47,7 @@ struct window_t { * This is an ordered set (whoever adds to this is responsible for keeping members unique). * In practice this is rarely manipulated and is limited by the number of physical displays. */ - std::vector<output_t *> outputs; + std::vector<GWL_Output *> outputs; /** The scale value written to #wl_surface_set_buffer_scale. */ int scale = 0; @@ -40,10 +59,17 @@ struct window_t { */ uint32_t dpi = 0; +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + struct libdecor_frame *decor_frame = nullptr; + bool decor_configured = false; +#else struct xdg_surface *xdg_surface = nullptr; - struct xdg_toplevel *xdg_toplevel = nullptr; struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr; + struct xdg_toplevel *xdg_toplevel = nullptr; + enum zxdg_toplevel_decoration_v1_mode decoration_mode = (enum zxdg_toplevel_decoration_v1_mode)0; +#endif + wl_egl_window *egl_window = nullptr; bool is_maximised = false; bool is_fullscreen = false; @@ -61,7 +87,7 @@ struct window_t { /** * Return -1 if `output_a` has a scale smaller than `output_b`, 0 when there equal, otherwise 1. */ -static int output_scale_cmp(const output_t *output_a, const output_t *output_b) +static int output_scale_cmp(const GWL_Output *output_a, const GWL_Output *output_b) { if (output_a->scale < output_b->scale) { return -1; @@ -86,12 +112,12 @@ static int output_scale_cmp(const output_t *output_a, const output_t *output_b) return 0; } -static int outputs_max_scale_or_default(const std::vector<output_t *> &outputs, +static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs, const int32_t scale_default, uint32_t *r_dpi) { - const output_t *output_max = nullptr; - for (const output_t *reg_output : outputs) { + const GWL_Output *output_max = nullptr; + for (const GWL_Output *reg_output : outputs) { if (!output_max || (output_scale_cmp(output_max, reg_output) == -1)) { output_max = reg_output; } @@ -120,10 +146,21 @@ static int outputs_max_scale_or_default(const std::vector<output_t *> &outputs, /** \name Listener (XDG Top Level), #xdg_toplevel_listener * \{ */ -static void xdg_toplevel_handle_configure( - void *data, xdg_toplevel * /*xdg_toplevel*/, int32_t width, int32_t height, wl_array *states) +#ifndef WITH_GHOST_WAYLAND_LIBDECOR + +static CLG_LogRef LOG_WL_XDG_TOPLEVEL = {"ghost.wl.handle.xdg_toplevel"}; +# define LOG (&LOG_WL_XDG_TOPLEVEL) + +static void xdg_toplevel_handle_configure(void *data, + xdg_toplevel * /*xdg_toplevel*/, + const int32_t width, + const int32_t height, + wl_array *states) { - window_t *win = static_cast<window_t *>(data); + /* TODO: log `states`, not urgent. */ + CLOG_INFO(LOG, 2, "configure (size=[%d, %d])", width, height); + + GWL_Window *win = static_cast<GWL_Window *>(data); win->size_pending[0] = win->scale * width; win->size_pending[1] = win->scale * height; @@ -131,12 +168,8 @@ static void xdg_toplevel_handle_configure( win->is_fullscreen = false; win->is_active = false; - /* Note that the macro 'wl_array_for_each' would typically be used to simplify this logic, - * however it's not compatible with C++, so perform casts instead. - * If this needs to be done more often we could define our own C++ compatible macro. */ - for (enum xdg_toplevel_state *state = static_cast<xdg_toplevel_state *>(states->data); - reinterpret_cast<uint8_t *>(state) < (static_cast<uint8_t *>(states->data) + states->size); - state++) { + enum xdg_toplevel_state *state; + WL_ARRAY_FOR_EACH (state, states) { switch (*state) { case XDG_TOPLEVEL_STATE_MAXIMIZED: win->is_maximised = true; @@ -155,7 +188,8 @@ static void xdg_toplevel_handle_configure( static void xdg_toplevel_handle_close(void *data, xdg_toplevel * /*xdg_toplevel*/) { - static_cast<window_t *>(data)->w->close(); + CLOG_INFO(LOG, 2, "close"); + static_cast<GWL_Window *>(data)->w->close(); } static const xdg_toplevel_listener toplevel_listener = { @@ -163,42 +197,144 @@ static const xdg_toplevel_listener toplevel_listener = { xdg_toplevel_handle_close, }; +# undef LOG + +#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Listener (LibDecor Frame), #libdecor_frame_interface + * \{ */ + +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + +static CLG_LogRef LOG_WL_LIBDECOR_FRAME = {"ghost.wl.handle.libdecor_frame"}; +# define LOG (&LOG_WL_LIBDECOR_FRAME) + +static void frame_handle_configure(struct libdecor_frame *frame, + struct libdecor_configuration *configuration, + void *data) +{ + CLOG_INFO(LOG, 2, "configure"); + + GWL_Window *win = static_cast<GWL_Window *>(data); + + int size_next[2]; + enum libdecor_window_state window_state; + struct libdecor_state *state; + + if (!libdecor_configuration_get_content_size( + configuration, frame, &size_next[0], &size_next[1])) { + size_next[0] = win->size[0] / win->scale; + size_next[1] = win->size[1] / win->scale; + } + + win->size[0] = win->scale * size_next[0]; + win->size[1] = win->scale * size_next[1]; + + wl_egl_window_resize(win->egl_window, UNPACK2(win->size), 0, 0); + win->w->notify_size(); + + if (!libdecor_configuration_get_window_state(configuration, &window_state)) { + window_state = LIBDECOR_WINDOW_STATE_NONE; + } + + win->is_maximised = window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED; + win->is_fullscreen = window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN; + win->is_active = window_state & LIBDECOR_WINDOW_STATE_ACTIVE; + + win->is_active ? win->w->activate() : win->w->deactivate(); + + state = libdecor_state_new(UNPACK2(size_next)); + libdecor_frame_commit(frame, state, configuration); + libdecor_state_free(state); + + win->decor_configured = true; +} + +static void frame_handle_close(struct libdecor_frame * /*frame*/, void *data) +{ + CLOG_INFO(LOG, 2, "close"); + + static_cast<GWL_Window *>(data)->w->close(); +} + +static void frame_handle_commit(struct libdecor_frame * /*frame*/, void *data) +{ + CLOG_INFO(LOG, 2, "commit"); + + /* We have to swap twice to keep any pop-up menus alive. */ + static_cast<GWL_Window *>(data)->w->swapBuffers(); + static_cast<GWL_Window *>(data)->w->swapBuffers(); +} + +static struct libdecor_frame_interface libdecor_frame_iface = { + frame_handle_configure, + frame_handle_close, + frame_handle_commit, +}; + +# undef LOG + +#endif /* WITH_GHOST_WAYLAND_LIBDECOR. */ + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (XDG Decoration Listener), #zxdg_toplevel_decoration_v1_listener * \{ */ +#ifndef WITH_GHOST_WAYLAND_LIBDECOR + +static CLG_LogRef LOG_WL_XDG_TOPLEVEL_DECORATION = {"ghost.wl.handle.xdg_toplevel_decoration"}; +# define LOG (&LOG_WL_XDG_TOPLEVEL_DECORATION) + static void xdg_toplevel_decoration_handle_configure( void *data, struct zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/, - uint32_t mode) + const uint32_t mode) { - static_cast<window_t *>(data)->decoration_mode = zxdg_toplevel_decoration_v1_mode(mode); + CLOG_INFO(LOG, 2, "configure (mode=%u)", mode); + static_cast<GWL_Window *>(data)->decoration_mode = (zxdg_toplevel_decoration_v1_mode)mode; } static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = { xdg_toplevel_decoration_handle_configure, }; +# undef LOG + +#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */ + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (XDG Surface Handle Configure), #xdg_surface_listener * \{ */ -static void xdg_surface_handle_configure(void *data, xdg_surface *xdg_surface, uint32_t serial) +#ifndef WITH_GHOST_WAYLAND_LIBDECOR + +static CLG_LogRef LOG_WL_XDG_SURFACE = {"ghost.wl.handle.xdg_surface"}; +# define LOG (&LOG_WL_XDG_SURFACE) + +static void xdg_surface_handle_configure(void *data, + xdg_surface *xdg_surface, + const uint32_t serial) { - window_t *win = static_cast<window_t *>(data); + GWL_Window *win = static_cast<GWL_Window *>(data); if (win->xdg_surface != xdg_surface) { + CLOG_INFO(LOG, 2, "configure (skipped)"); return; } + const bool do_resize = win->size_pending[0] != 0 && win->size_pending[1] != 0; + CLOG_INFO(LOG, 2, "configure (do_resize=%d)", do_resize); - if (win->size_pending[0] != 0 && win->size_pending[1] != 0) { + if (do_resize) { win->size[0] = win->size_pending[0]; win->size[1] = win->size_pending[1]; - wl_egl_window_resize(win->egl_window, win->size[0], win->size[1], 0, 0); + wl_egl_window_resize(win->egl_window, UNPACK2(win->size), 0, 0); win->size_pending[0] = 0; win->size_pending[1] = 0; win->w->notify_size(); @@ -218,53 +354,66 @@ static const xdg_surface_listener xdg_surface_listener = { xdg_surface_handle_configure, }; +# undef LOG + +#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */ + /** \} */ /* -------------------------------------------------------------------- */ /** \name Listener (Surface), #wl_surface_listener * \{ */ +static CLG_LogRef LOG_WL_SURFACE = {"ghost.wl.handle.surface"}; +#define LOG (&LOG_WL_SURFACE) + static void surface_handle_enter(void *data, struct wl_surface * /*wl_surface*/, - struct wl_output *output) + struct wl_output *wl_output) { - GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data); - output_t *reg_output = w->output_find_by_wl(output); - if (reg_output == nullptr) { + if (!ghost_wl_output_own(wl_output)) { + CLOG_INFO(LOG, 2, "enter (skipped)"); return; } + CLOG_INFO(LOG, 2, "enter"); - if (w->outputs_enter(reg_output)) { - w->outputs_changed_update_scale(); + GWL_Output *reg_output = ghost_wl_output_user_data(wl_output); + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(data); + if (win->outputs_enter(reg_output)) { + win->outputs_changed_update_scale(); } } static void surface_handle_leave(void *data, struct wl_surface * /*wl_surface*/, - struct wl_output *output) + struct wl_output *wl_output) { - GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data); - output_t *reg_output = w->output_find_by_wl(output); - if (reg_output == nullptr) { + if (!ghost_wl_output_own(wl_output)) { + CLOG_INFO(LOG, 2, "leave (skipped)"); return; } + CLOG_INFO(LOG, 2, "leave"); - if (w->outputs_leave(reg_output)) { - w->outputs_changed_update_scale(); + GWL_Output *reg_output = ghost_wl_output_user_data(wl_output); + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(data); + if (win->outputs_leave(reg_output)) { + win->outputs_changed_update_scale(); } } -struct wl_surface_listener wl_surface_listener = { +static struct wl_surface_listener wl_surface_listener = { surface_handle_enter, surface_handle_leave, }; +#undef LOG + /** \} */ /* -------------------------------------------------------------------- */ -/** \name Ghost Implementation +/** \name GHOST Implementation * - * Wayland specific implementation of the GHOST_Window interface. + * WAYLAND specific implementation of the #GHOST_Window interface. * \{ */ GHOST_TSuccess GHOST_WindowWayland::hasCursorShape(GHOST_TStandardCursor cursorShape) @@ -274,20 +423,25 @@ GHOST_TSuccess GHOST_WindowWayland::hasCursorShape(GHOST_TStandardCursor cursorS GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, const char *title, - int32_t /*left*/, - int32_t /*top*/, - uint32_t width, - uint32_t height, - GHOST_TWindowState state, + const int32_t /*left*/, + const int32_t /*top*/, + const uint32_t width, + const uint32_t height, + const GHOST_TWindowState state, const GHOST_IWindow *parentWindow, - GHOST_TDrawingContextType type, + const GHOST_TDrawingContextType type, const bool is_dialog, const bool stereoVisual, const bool exclusive) : GHOST_Window(width, height, state, stereoVisual, exclusive), m_system(system), - w(new window_t) + w(new GWL_Window) { + /* Globally store pointer to window manager. */ + if (!window_manager) { + window_manager = m_system->getWindowManager(); + } + w->w = this; w->size[0] = int32_t(width); @@ -308,20 +462,37 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, /* Window surfaces. */ w->wl_surface = wl_compositor_create_surface(m_system->compositor()); - wl_surface_set_buffer_scale(this->surface(), w->scale); + ghost_wl_surface_tag(w->wl_surface); + + wl_surface_set_buffer_scale(w->wl_surface, w->scale); wl_surface_add_listener(w->wl_surface, &wl_surface_listener, this); w->egl_window = wl_egl_window_create(w->wl_surface, int(w->size[0]), int(w->size[1])); - w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->xdg_shell(), w->wl_surface); - w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface); - /* NOTE: The limit is in points (not pixels) so Hi-DPI will limit to larger number of pixels. * This has the advantage that the size limit is the same when moving the window between monitors * with different scales set. If it was important to limit in pixels it could be re-calculated * when the `w->scale` changed. */ - xdg_toplevel_set_min_size(w->xdg_toplevel, 320, 240); + const int32_t size_min[2] = {320, 240}; + +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + /* create window decorations */ + w->decor_frame = libdecor_decorate( + m_system->decor_context(), w->wl_surface, &libdecor_frame_iface, w); + libdecor_frame_map(w->decor_frame); + + libdecor_frame_set_min_content_size(w->decor_frame, UNPACK2(size_min)); + + if (parentWindow) { + libdecor_frame_set_parent( + w->decor_frame, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->decor_frame); + } +#else + w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->xdg_shell(), w->wl_surface); + w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface); + + xdg_toplevel_set_min_size(w->xdg_toplevel, UNPACK2(size_min)); if (m_system->xdg_decoration_manager()) { w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( @@ -332,8 +503,6 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } - wl_surface_set_user_data(w->wl_surface, this); - xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w); xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w); @@ -342,147 +511,63 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, w->xdg_toplevel, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->xdg_toplevel); } +#endif /* !WITH_GHOST_WAYLAND_LIBDECOR */ + + setTitle(title); + + wl_surface_set_user_data(w->wl_surface, this); + /* Call top-level callbacks. */ wl_surface_commit(w->wl_surface); wl_display_roundtrip(m_system->display()); +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + /* It's important not to return until the window is configured or + * calls to `setState` from Blender will crash `libdecor`. */ + while (!w->decor_configured) { + if (libdecor_dispatch(m_system->decor_context(), 0) < 0) { + break; + } + } +#endif + #ifdef GHOST_OPENGL_ALPHA setOpaque(); #endif +#ifndef WITH_GHOST_WAYLAND_LIBDECOR /* Causes a glitch with `libdecor` for some reason. */ setState(state); - - setTitle(title); +#endif /* EGL context. */ if (setDrawingContextType(type) == GHOST_kFailure) { GHOST_PRINT("Failed to create EGL context" << std::endl); } - /* set swap interval to 0 to prevent blocking */ + /* Set swap interval to 0 to prevent blocking. */ setSwapInterval(0); } -GHOST_TSuccess GHOST_WindowWayland::close() -{ - return m_system->pushEvent( - new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowClose, this)); -} - -GHOST_TSuccess GHOST_WindowWayland::activate() -{ - if (m_system->getWindowManager()->setActiveWindow(this) == GHOST_kFailure) { - return GHOST_kFailure; - } - return m_system->pushEvent( - new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowActivate, this)); -} - -GHOST_TSuccess GHOST_WindowWayland::deactivate() -{ - m_system->getWindowManager()->setWindowInactive(this); - return m_system->pushEvent( - new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowDeactivate, this)); -} - -GHOST_TSuccess GHOST_WindowWayland::notify_size() -{ -#ifdef GHOST_OPENGL_ALPHA - setOpaque(); -#endif - - return m_system->pushEvent( - new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this)); -} - -wl_surface *GHOST_WindowWayland::surface() const -{ - return w->wl_surface; -} - -const std::vector<output_t *> &GHOST_WindowWayland::outputs() -{ - return w->outputs; -} - -output_t *GHOST_WindowWayland::output_find_by_wl(struct wl_output *output) +GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode) { - for (output_t *reg_output : this->m_system->outputs()) { - if (reg_output->wl_output == output) { - return reg_output; + GHOST_Rect bounds_buf; + GHOST_Rect *bounds = nullptr; + if (m_cursorGrab == GHOST_kGrabWrap) { + if (getCursorGrabBounds(bounds_buf) == GHOST_kFailure) { + getClientBounds(bounds_buf); } + bounds = &bounds_buf; } - return nullptr; -} - -bool GHOST_WindowWayland::outputs_changed_update_scale() -{ - uint32_t dpi_next; - const int scale_next = outputs_max_scale_or_default(this->outputs(), 0, &dpi_next); - if (scale_next == 0) { - return false; - } - - window_t *win = this->w; - const uint32_t dpi_curr = win->dpi; - const int scale_curr = win->scale; - bool changed = false; - - if (scale_next != scale_curr) { - /* Unlikely but possible there is a pending size change is set. */ - win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next; - win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next; - - win->scale = scale_next; - wl_surface_set_buffer_scale(this->surface(), scale_next); - changed = true; - } - - if (dpi_next != dpi_curr) { - /* Using the real DPI will cause wrong scaling of the UI - * use a multiplier for the default DPI as workaround. */ - win->dpi = dpi_next; - changed = true; + if (m_system->window_cursor_grab_set(mode, + m_cursorGrab, + m_cursorGrabInitPos, + bounds, + m_cursorGrabAxis, + w->wl_surface, + w->scale)) { + return GHOST_kSuccess; } - - return changed; -} - -bool GHOST_WindowWayland::outputs_enter(output_t *reg_output) -{ - std::vector<output_t *> &outputs = w->outputs; - auto it = std::find(outputs.begin(), outputs.end(), reg_output); - if (it != outputs.end()) { - return false; - } - outputs.push_back(reg_output); - return true; -} - -bool GHOST_WindowWayland::outputs_leave(output_t *reg_output) -{ - std::vector<output_t *> &outputs = w->outputs; - auto it = std::find(outputs.begin(), outputs.end(), reg_output); - if (it == outputs.end()) { - return false; - } - outputs.erase(it); - return true; -} - -uint16_t GHOST_WindowWayland::dpi() -{ - return w->dpi; -} - -int GHOST_WindowWayland::scale() -{ - return w->scale; -} - -GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode) -{ - return m_system->setCursorGrab(mode, m_cursorGrab, w->wl_surface); + return GHOST_kFailure; } GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape) @@ -492,16 +577,32 @@ GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor s return ok; } +bool GHOST_WindowWayland::getCursorGrabUseSoftwareDisplay() +{ + return m_system->getCursorGrabUseSoftwareDisplay(m_cursorGrab); +} + GHOST_TSuccess GHOST_WindowWayland::setWindowCustomCursorShape( uint8_t *bitmap, uint8_t *mask, int sizex, int sizey, int hotX, int hotY, bool canInvertColor) { return m_system->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor); } +GHOST_TSuccess GHOST_WindowWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap) +{ + return m_system->getCursorBitmap(bitmap); +} + void GHOST_WindowWayland::setTitle(const char *title) { +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + libdecor_frame_set_app_id(w->decor_frame, title); + libdecor_frame_set_title(w->decor_frame, title); +#else xdg_toplevel_set_title(w->xdg_toplevel, title); xdg_toplevel_set_app_id(w->xdg_toplevel, title); +#endif + this->title = title; } @@ -517,20 +618,20 @@ void GHOST_WindowWayland::getWindowBounds(GHOST_Rect &bounds) const void GHOST_WindowWayland::getClientBounds(GHOST_Rect &bounds) const { - bounds.set(0, 0, w->size[0], w->size[1]); + bounds.set(0, 0, UNPACK2(w->size)); } -GHOST_TSuccess GHOST_WindowWayland::setClientWidth(uint32_t width) +GHOST_TSuccess GHOST_WindowWayland::setClientWidth(const uint32_t width) { return setClientSize(width, uint32_t(w->size[1])); } -GHOST_TSuccess GHOST_WindowWayland::setClientHeight(uint32_t height) +GHOST_TSuccess GHOST_WindowWayland::setClientHeight(const uint32_t height) { return setClientSize(uint32_t(w->size[0]), height); } -GHOST_TSuccess GHOST_WindowWayland::setClientSize(uint32_t width, uint32_t height) +GHOST_TSuccess GHOST_WindowWayland::setClientSize(const uint32_t width, const uint32_t height) { wl_egl_window_resize(w->egl_window, int(width), int(height), 0, 0); @@ -569,13 +670,28 @@ GHOST_WindowWayland::~GHOST_WindowWayland() releaseNativeHandles(); wl_egl_window_destroy(w->egl_window); + +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + libdecor_frame_unref(w->decor_frame); +#else if (w->xdg_toplevel_decoration) { zxdg_toplevel_decoration_v1_destroy(w->xdg_toplevel_decoration); } xdg_toplevel_destroy(w->xdg_toplevel); xdg_surface_destroy(w->xdg_surface); +#endif + + /* Clear any pointers to this window. This is needed because there are no guarantees + * that flushing the display will the "leave" handlers before handling events. */ + m_system->window_surface_unref(w->wl_surface); + wl_surface_destroy(w->wl_surface); + /* NOTE(@campbellbarton): Flushing will often run the appropriate handlers event + * (#wl_surface_listener.leave in particular) to avoid attempted access to the freed surfaces. + * This is not fool-proof though, hence the call to #window_surface_unref, see: T99078. */ + wl_display_flush(m_system->display()); + delete w; } @@ -596,23 +712,43 @@ GHOST_TSuccess GHOST_WindowWayland::setState(GHOST_TWindowState state) /* Unset states. */ switch (getState()) { case GHOST_kWindowStateMaximized: +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + libdecor_frame_unset_maximized(w->decor_frame); +#else xdg_toplevel_unset_maximized(w->xdg_toplevel); +#endif break; case GHOST_kWindowStateFullScreen: +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + libdecor_frame_unset_fullscreen(w->decor_frame); +#else xdg_toplevel_unset_fullscreen(w->xdg_toplevel); +#endif break; default: break; } break; case GHOST_kWindowStateMaximized: +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + libdecor_frame_set_maximized(w->decor_frame); +#else xdg_toplevel_set_maximized(w->xdg_toplevel); +#endif break; case GHOST_kWindowStateMinimized: +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + libdecor_frame_set_minimized(w->decor_frame); +#else xdg_toplevel_set_minimized(w->xdg_toplevel); +#endif break; case GHOST_kWindowStateFullScreen: +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + libdecor_frame_set_fullscreen(w->decor_frame, nullptr); +#else xdg_toplevel_set_fullscreen(w->xdg_toplevel, nullptr); +#endif break; case GHOST_kWindowStateEmbedded: return GHOST_kFailure; @@ -643,13 +779,21 @@ GHOST_TSuccess GHOST_WindowWayland::setOrder(GHOST_TWindowOrder /*order*/) GHOST_TSuccess GHOST_WindowWayland::beginFullScreen() const { +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + libdecor_frame_set_fullscreen(w->decor_frame, nullptr); +#else xdg_toplevel_set_fullscreen(w->xdg_toplevel, nullptr); +#endif return GHOST_kSuccess; } GHOST_TSuccess GHOST_WindowWayland::endFullScreen() const { +#ifdef WITH_GHOST_WAYLAND_LIBDECOR + libdecor_frame_unset_fullscreen(w->decor_frame); +#else xdg_toplevel_unset_fullscreen(w->xdg_toplevel); +#endif return GHOST_kSuccess; } @@ -665,7 +809,7 @@ void GHOST_WindowWayland::setOpaque() const /* Make the window opaque. */ region = wl_compositor_create_region(m_system->compositor()); - wl_region_add(region, 0, 0, w->size[0], w->size[1]); + wl_region_add(region, 0, 0, UNPACK2(w->size)); wl_surface_set_opaque_region(w->surface, region); wl_region_destroy(region); } @@ -716,3 +860,143 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public WAYLAND Direct Data Access + * + * Expose some members via methods. + * \{ */ + +uint16_t GHOST_WindowWayland::dpi() const +{ + return w->dpi; +} + +int GHOST_WindowWayland::scale() const +{ + return w->scale; +} + +wl_surface *GHOST_WindowWayland::wl_surface() const +{ + return w->wl_surface; +} + +const std::vector<GWL_Output *> &GHOST_WindowWayland::outputs() +{ + return w->outputs; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public WAYLAND Window Level Functions + * + * High Level Windowing Utilities. + * \{ */ + +GHOST_TSuccess GHOST_WindowWayland::close() +{ + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowClose, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::activate() +{ + if (m_system->getWindowManager()->setActiveWindow(this) == GHOST_kFailure) { + return GHOST_kFailure; + } + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowActivate, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::deactivate() +{ + m_system->getWindowManager()->setWindowInactive(this); + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowDeactivate, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::notify_size() +{ +#ifdef GHOST_OPENGL_ALPHA + setOpaque(); +#endif + + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this)); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public WAYLAND Utility Functions + * + * Functionality only used for the WAYLAND implementation. + * \{ */ + +/** + * Return true when the windows scale or DPI changes. + */ +bool GHOST_WindowWayland::outputs_changed_update_scale() +{ + uint32_t dpi_next; + const int scale_next = outputs_max_scale_or_default(this->outputs(), 0, &dpi_next); + if (UNLIKELY(scale_next == 0)) { + return false; + } + + GWL_Window *win = this->w; + const uint32_t dpi_curr = win->dpi; + const int scale_curr = win->scale; + bool changed = false; + + if (scale_next != scale_curr) { + /* Unlikely but possible there is a pending size change is set. */ + win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next; + win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next; + + win->scale = scale_next; + wl_surface_set_buffer_scale(w->wl_surface, scale_next); + changed = true; + } + + if (dpi_next != dpi_curr) { + /* Using the real DPI will cause wrong scaling of the UI + * use a multiplier for the default DPI as workaround. */ + win->dpi = dpi_next; + changed = true; + + /* As this is a low-level function, we might want adding this event to be optional, + * always add the event unless it causes issues. */ + GHOST_System *system = (GHOST_System *)GHOST_ISystem::getSystem(); + system->pushEvent( + new GHOST_Event(system->getMilliSeconds(), GHOST_kEventWindowDPIHintChanged, this)); + } + + return changed; +} + +bool GHOST_WindowWayland::outputs_enter(GWL_Output *output) +{ + std::vector<GWL_Output *> &outputs = w->outputs; + auto it = std::find(outputs.begin(), outputs.end(), output); + if (it != outputs.end()) { + return false; + } + outputs.push_back(output); + return true; +} + +bool GHOST_WindowWayland::outputs_leave(GWL_Output *output) +{ + std::vector<GWL_Output *> &outputs = w->outputs; + auto it = std::find(outputs.begin(), outputs.end(), output); + if (it == outputs.end()) { + return false; + } + outputs.erase(it); + return true; +} + +/** \} */ diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h index b6d9fa04079..9b4c17ecd95 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.h +++ b/intern/ghost/intern/GHOST_WindowWayland.h @@ -10,13 +10,12 @@ #include "GHOST_Window.h" -#include <unordered_set> #include <vector> class GHOST_SystemWayland; -struct output_t; -struct window_t; +struct GWL_Output; +struct GWL_Window; class GHOST_WindowWayland : public GHOST_Window { public: @@ -37,10 +36,10 @@ class GHOST_WindowWayland : public GHOST_Window { ~GHOST_WindowWayland() override; - uint16_t getDPIHint() override; - /* Ghost API */ + uint16_t getDPIHint() override; + GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override; GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape) override; @@ -52,6 +51,9 @@ class GHOST_WindowWayland : public GHOST_Window { int hotX, int hotY, bool canInvertColor) override; + bool getCursorGrabUseSoftwareDisplay() override; + + GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap) override; void setTitle(const char *title) override; @@ -91,33 +93,30 @@ class GHOST_WindowWayland : public GHOST_Window { void setOpaque() const; #endif - /* WAYLAND utility functions. */ + /* WAYLAND direct-data access. */ - GHOST_TSuccess close(); + uint16_t dpi() const; + int scale() const; + struct wl_surface *wl_surface() const; + const std::vector<GWL_Output *> &outputs(); - GHOST_TSuccess activate(); + /* WAYLAND window-level functions. */ + GHOST_TSuccess close(); + GHOST_TSuccess activate(); GHOST_TSuccess deactivate(); - GHOST_TSuccess notify_size(); - struct wl_surface *surface() const; - - output_t *output_find_by_wl(struct wl_output *output); + /* WAYLAND utility functions. */ - const std::vector<output_t *> &outputs(); + bool outputs_enter(GWL_Output *output); + bool outputs_leave(GWL_Output *output); - bool outputs_enter(output_t *reg_output); - bool outputs_leave(output_t *reg_output); bool outputs_changed_update_scale(); - uint16_t dpi(); - - int scale(); - private: GHOST_SystemWayland *m_system; - struct window_t *w; + struct GWL_Window *w; std::string title; /** diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 2e17454d24f..50ee9385e39 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -26,7 +26,7 @@ #ifndef GET_POINTERID_WPARAM # define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam)) -#endif // GET_POINTERID_WPARAM +#endif /* GET_POINTERID_WPARAM */ const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass"; const int GHOST_WindowWin32::s_maxTitleLength = 128; @@ -93,18 +93,18 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, adjustWindowRectForClosestMonitor(&win_rect, style, extended_style); wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); - m_hWnd = ::CreateWindowExW(extended_style, // window extended style - s_windowClassName, // pointer to registered class name - title_16, // pointer to window name - style, // window style - win_rect.left, // horizontal position of window - win_rect.top, // vertical position of window - win_rect.right - win_rect.left, // window width - win_rect.bottom - win_rect.top, // window height - m_parentWindowHwnd, // handle to parent or owner window - 0, // handle to menu or child-window identifier - ::GetModuleHandle(0), // handle to application instance - 0); // pointer to window-creation data + m_hWnd = ::CreateWindowExW(extended_style, /* window extended style */ + s_windowClassName, /* pointer to registered class name */ + title_16, /* pointer to window name */ + style, /* window style */ + win_rect.left, /* horizontal position of window */ + win_rect.top, /* vertical position of window */ + win_rect.right - win_rect.left, /* window width */ + win_rect.bottom - win_rect.top, /* window height */ + m_parentWindowHwnd, /* handle to parent or owner window */ + 0, /* handle to menu or child-window identifier */ + ::GetModuleHandle(0), /* handle to application instance */ + 0); /* pointer to window-creation data */ free(title_16); if (m_hWnd == NULL) { @@ -197,7 +197,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, /* Force an initial paint of the window. */ ::UpdateWindow(m_hWnd); - /* Initialize Wintab. */ + /* Initialize WINTAB. */ if (system->getTabletAPI() != GHOST_kTabletWinPointer) { loadWintab(GHOST_kWindowStateMinimized != state); } @@ -221,7 +221,7 @@ void GHOST_WindowWin32::updateDirectManipulation() void GHOST_WindowWin32::onPointerHitTest(WPARAM wParam) { - /* Only DM_POINTERHITTEST can be the first message of input sequence of touchpad input. */ + /* Only #DM_POINTERHITTEST can be the first message of input sequence of touch-pad input. */ if (!m_directManipulationHelper) { return; @@ -280,9 +280,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32() } if (m_dropTarget) { - // Disable DragDrop + /* Disable DragDrop. */ RevokeDragDrop(m_hWnd); - // Release our reference of the DropTarget and it will delete itself eventually. + /* Release our reference of the DropTarget and it will delete itself eventually. */ m_dropTarget->Release(); m_dropTarget = NULL; } @@ -531,7 +531,8 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state) break; } ::SetWindowLongPtr(m_hWnd, GWL_STYLE, style); - /* SetWindowLongPtr Docs: frame changes not visible until SetWindowPos with SWP_FRAMECHANGED. */ + /* #SetWindowLongPtr Docs: + * Frame changes not visible until #SetWindowPos with #SWP_FRAMECHANGED. */ ::SetWindowPos(m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure; } @@ -580,7 +581,6 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty if (type == GHOST_kDrawingContextTypeOpenGL) { GHOST_Context *context; -#if defined(WITH_GL_PROFILE_CORE) /* - AMD and Intel give us exactly this version * - NVIDIA gives at least this version <-- desired behavior * So we ask for 4.5, 4.4 ... 3.3 in descending order @@ -619,40 +619,14 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty } return context; - -#elif defined(WITH_GL_PROFILE_COMPAT) - // ask for 2.1 context, driver gives any GL version >= 2.1 - // (hopefully the latest compatibility profile) - // 2.1 ignores the profile bit & is incompatible with core profile - context = new GHOST_ContextWGL(m_wantStereoVisual, - m_wantAlphaBackground, - m_hWnd, - m_hDC, - 0, // no profile bit - 2, - 1, - (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); - - if (context->initializeDrawingContext()) { - return context; - } - else { - delete context; - } -#else -# error // must specify either core or compat at build time -#endif } else if (type == GHOST_kDrawingContextTypeD3D) { GHOST_Context *context; context = new GHOST_ContextD3D(false, m_hWnd); - if (context->initializeDrawingContext()) { - return context; - } - else { + if (!context->initializeDrawingContext()) { delete context; + context = nullptr; } return context; @@ -705,7 +679,7 @@ void GHOST_WindowWin32::updateMouseCapture(GHOST_MouseCaptureEventWin32 event) HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const { - // Convert GHOST cursor to Windows OEM cursor + /* Convert GHOST cursor to Windows OEM cursor. */ HANDLE cursor = NULL; HMODULE module = ::GetModuleHandle(0); uint32_t flags = LR_SHARED | LR_DEFAULTSIZE; @@ -763,36 +737,36 @@ HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const break; case GHOST_kStandardCursorHelp: cursor = ::LoadImage(NULL, IDC_HELP, IMAGE_CURSOR, cx, cy, flags); - break; // Arrow and question mark + break; /* Arrow and question mark */ case GHOST_kStandardCursorWait: cursor = ::LoadImage(NULL, IDC_WAIT, IMAGE_CURSOR, cx, cy, flags); - break; // Hourglass + break; /* Hourglass */ case GHOST_kStandardCursorText: cursor = ::LoadImage(NULL, IDC_IBEAM, IMAGE_CURSOR, cx, cy, flags); - break; // I-beam + break; /* I-beam */ case GHOST_kStandardCursorCrosshair: cursor = ::LoadImage(module, "cross_cursor", IMAGE_CURSOR, cx, cy, flags); - break; // Standard Cross + break; /* Standard Cross */ case GHOST_kStandardCursorCrosshairA: cursor = ::LoadImage(module, "crossA_cursor", IMAGE_CURSOR, cx, cy, flags); - break; // Crosshair A + break; /* Crosshair A */ case GHOST_kStandardCursorCrosshairB: cursor = ::LoadImage(module, "crossB_cursor", IMAGE_CURSOR, cx, cy, flags); - break; // Diagonal Crosshair B + break; /* Diagonal Crosshair B */ case GHOST_kStandardCursorCrosshairC: cursor = ::LoadImage(module, "crossC_cursor", IMAGE_CURSOR, cx, cy, flags); - break; // Minimal Crosshair C + break; /* Minimal Crosshair C */ case GHOST_kStandardCursorBottomSide: case GHOST_kStandardCursorUpDown: cursor = ::LoadImage(module, "movens_cursor", IMAGE_CURSOR, cx, cy, flags); - break; // Double-pointed arrow pointing north and south + break; /* Double-pointed arrow pointing north and south */ case GHOST_kStandardCursorLeftSide: case GHOST_kStandardCursorLeftRight: cursor = ::LoadImage(module, "moveew_cursor", IMAGE_CURSOR, cx, cy, flags); - break; // Double-pointed arrow pointing west and east + break; /* Double-pointed arrow pointing west and east */ case GHOST_kStandardCursorTopSide: cursor = ::LoadImage(NULL, IDC_UPARROW, IMAGE_CURSOR, cx, cy, flags); - break; // Vertical arrow + break; /* Vertical arrow */ case GHOST_kStandardCursorTopLeftCorner: cursor = ::LoadImage(NULL, IDC_SIZENWSE, IMAGE_CURSOR, cx, cy, flags); break; @@ -814,7 +788,7 @@ HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const case GHOST_kStandardCursorDestroy: case GHOST_kStandardCursorStop: cursor = ::LoadImage(module, "forbidden_cursor", IMAGE_CURSOR, cx, cy, flags); - break; // Slashed circle + break; /* Slashed circle */ case GHOST_kStandardCursorDefault: cursor = NULL; break; @@ -863,8 +837,9 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]); setCursorGrabAccum(0, 0); - if (mode == GHOST_kGrabHide) + if (mode == GHOST_kGrabHide) { setWindowCursorVisibility(false); + } } updateMouseCapture(OperatorGrab); } @@ -874,9 +849,9 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode setWindowCursorVisibility(true); } if (m_cursorGrab != GHOST_kGrabNormal) { - /* use to generate a mouse move event, otherwise the last event + /* Use to generate a mouse move event, otherwise the last event * blender gets can be outside the screen causing menus not to show - * properly unless the user moves the mouse */ + * properly unless the user moves the mouse. */ int32_t pos[2]; m_system->getCursorPosition(pos[0], pos[1]); m_system->setCursorPosition(pos[0], pos[1]); @@ -927,7 +902,7 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo( for (uint32_t i = 0; i < outCount; i++) { POINTER_INFO pointerApiInfo = pointerPenInfo[i].pointerInfo; - // Obtain the basic information from the event + /* Obtain the basic information from the event. */ outPointerInfo[i].pointerId = pointerId; outPointerInfo[i].isPrimary = isPrimary; @@ -1069,8 +1044,9 @@ void GHOST_WindowWin32::ThemeRefresh() &pcbData) == ERROR_SUCCESS) { BOOL DarkMode = !lightMode; - /* 20 == DWMWA_USE_IMMERSIVE_DARK_MODE in Windows 11 SDK. This value was undocumented for - * Windows 10 versions 2004 and later, supported for Windows 11 Build 22000 and later. */ + /* `20 == DWMWA_USE_IMMERSIVE_DARK_MODE` in Windows 11 SDK. + * This value was undocumented for Windows 10 versions 2004 and later, + * supported for Windows 11 Build 22000 and later. */ DwmSetWindowAttribute(this->m_hWnd, 20, &DarkMode, sizeof(DarkMode)); } } @@ -1126,8 +1102,9 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape( int x, y, cols; cols = sizeX / 8; /* Number of whole bytes per row (width of bitmap/mask). */ - if (sizeX % 8) + if (sizeX % 8) { cols++; + } if (m_customCursor) { DestroyCursor(m_customCursor); @@ -1165,16 +1142,18 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape( GHOST_TSuccess GHOST_WindowWin32::setProgressBar(float progress) { /* #SetProgressValue sets state to #TBPF_NORMAL automatically. */ - if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000)) + if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000)) { return GHOST_kSuccess; + } return GHOST_kFailure; } GHOST_TSuccess GHOST_WindowWin32::endProgressBar() { - if (m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS)) + if (m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS)) { return GHOST_kSuccess; + } return GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index c958a89ac48..44071f0915e 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -10,7 +10,7 @@ #ifndef WIN32 # error WIN32 only! -#endif // WIN32 +#endif /* WIN32 */ #include "GHOST_TaskbarWin32.h" #include "GHOST_TrackpadWin32.h" @@ -25,7 +25,7 @@ class GHOST_SystemWin32; class GHOST_DropTargetWin32; -// typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions +/* typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions. */ typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND); typedef BOOL(API *GHOST_WIN32_AdjustWindowRectExForDpi)( @@ -34,7 +34,7 @@ typedef BOOL(API *GHOST_WIN32_AdjustWindowRectExForDpi)( struct GHOST_PointerInfoWin32 { int32_t pointerId; int32_t isPrimary; - GHOST_TButtonMask buttonMask; + GHOST_TButton buttonMask; POINT pixelLocation; uint64_t time; GHOST_TabletData tabletData; diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index ac7a476c76f..0b2617c1b9e 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -10,24 +10,19 @@ #include <X11/Xmd.h> #include <X11/Xutil.h> #include <X11/cursorfont.h> -#ifdef WITH_X11_ALPHA -# include <X11/extensions/Xrender.h> -#endif + #include "GHOST_Debug.h" #include "GHOST_IconX11.h" #include "GHOST_SystemX11.h" #include "GHOST_WindowX11.h" +#include "GHOST_utildefines.h" #ifdef WITH_XDND # include "GHOST_DropTargetX11.h" #endif -#ifdef WITH_GL_EGL -# include "GHOST_ContextEGL.h" -# include <EGL/eglext.h> -#else -# include "GHOST_ContextGLX.h" -#endif +#include "GHOST_ContextEGL.h" +#include "GHOST_ContextGLX.h" /* for XIWarpPointer */ #ifdef WITH_X11_XINPUT @@ -87,9 +82,7 @@ enum { #define _NET_WM_STATE_ADD 1 // #define _NET_WM_STATE_TOGGLE 2 // UNUSED -#ifdef WITH_GL_EGL - -static XVisualInfo *x11_visualinfo_from_egl(Display *display) +static XVisualInfo *get_x11_visualinfo(Display *display) { int num_visuals; XVisualInfo vinfo_template; @@ -97,103 +90,6 @@ static XVisualInfo *x11_visualinfo_from_egl(Display *display) return XGetVisualInfo(display, VisualScreenMask, &vinfo_template, &num_visuals); } -#else - -static XVisualInfo *x11_visualinfo_from_glx(Display *display, - bool stereoVisual, - bool needAlpha, - GLXFBConfig *fbconfig) -{ - int glx_major, glx_minor, glx_version; /* GLX version: major.minor */ - int glx_attribs[64]; - - *fbconfig = nullptr; - - /* Set up the minimum attributes that we require and see if - * X can find us a visual matching those requirements. */ - - if (!glXQueryVersion(display, &glx_major, &glx_minor)) { - fprintf(stderr, - "%s:%d: X11 glXQueryVersion() failed, " - "verify working openGL system!\n", - __FILE__, - __LINE__); - - return nullptr; - } - glx_version = glx_major * 100 + glx_minor; -# ifndef WITH_X11_ALPHA - (void)glx_version; -# endif - -# ifdef WITH_X11_ALPHA - if (needAlpha && glx_version >= 103 && - (glXChooseFBConfig || (glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)glXGetProcAddressARB( - (const GLubyte *)"glXChooseFBConfig")) != nullptr) && - (glXGetVisualFromFBConfig || - (glXGetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC)glXGetProcAddressARB( - (const GLubyte *)"glXGetVisualFromFBConfig")) != nullptr)) { - - GHOST_X11_GL_GetAttributes(glx_attribs, 64, stereoVisual, needAlpha, true); - - int nbfbconfig; - GLXFBConfig *fbconfigs = glXChooseFBConfig( - display, DefaultScreen(display), glx_attribs, &nbfbconfig); - - /* Any sample level or even zero, which means oversampling disabled, is good - * but we need a valid visual to continue */ - if (nbfbconfig > 0) { - /* take a frame buffer config that has alpha cap */ - for (int i = 0; i < nbfbconfig; i++) { - XVisualInfo *visual = (XVisualInfo *)glXGetVisualFromFBConfig(display, fbconfigs[i]); - if (!visual) - continue; - /* if we don't need a alpha background, the first config will do, otherwise - * test the alphaMask as it won't necessarily be present */ - if (needAlpha) { - XRenderPictFormat *pict_format = XRenderFindVisualFormat(display, visual->visual); - if (!pict_format) - continue; - if (pict_format->direct.alphaMask <= 0) - continue; - } - - *fbconfig = fbconfigs[i]; - XFree(fbconfigs); - - return visual; - } - - XFree(fbconfigs); - } - } - else -# endif - { - /* legacy, don't use extension */ - GHOST_X11_GL_GetAttributes(glx_attribs, 64, stereoVisual, needAlpha, false); - - XVisualInfo *visual = glXChooseVisual(display, DefaultScreen(display), glx_attribs); - - /* Any sample level or even zero, which means oversampling disabled, is good - * but we need a valid visual to continue */ - if (visual != nullptr) { - return visual; - } - } - - /* All options exhausted, cannot continue */ - fprintf(stderr, - "%s:%d: X11 glXChooseVisual() failed, " - "verify working openGL system!\n", - __FILE__, - __LINE__); - - return nullptr; -} - -#endif // WITH_GL_EGL - GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, Display *display, const char *title, @@ -207,7 +103,6 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, const bool is_dialog, const bool stereoVisual, const bool exclusive, - const bool alphaBackground, const bool is_debug) : GHOST_Window(width, height, state, stereoVisual, exclusive), m_display(display), @@ -231,13 +126,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, m_is_debug_context(is_debug) { if (type == GHOST_kDrawingContextTypeOpenGL) { -#ifdef WITH_GL_EGL - m_visualInfo = x11_visualinfo_from_egl(m_display); - (void)alphaBackground; -#else - m_visualInfo = x11_visualinfo_from_glx( - m_display, stereoVisual, alphaBackground, (GLXFBConfig *)&m_fbconfig); -#endif + m_visualInfo = get_x11_visualinfo(m_display); } else { XVisualInfo tmp = {nullptr}; @@ -296,7 +185,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, GHOST_PRINT("Set drop target\n"); #endif - if (state == GHOST_kWindowStateMaximized || state == GHOST_kWindowStateFullScreen) { + if (ELEM(state, GHOST_kWindowStateMaximized, GHOST_kWindowStateFullScreen)) { Atom atoms[2]; int count = 0; if (state == GHOST_kWindowStateMaximized) { @@ -412,7 +301,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, 32, PropModeReplace, (unsigned char *)BLENDER_ICONS_WM_X11, - sizeof(BLENDER_ICONS_WM_X11) / sizeof(unsigned long)); + ARRAY_SIZE(BLENDER_ICONS_WM_X11)); } /* set the process ID (_NET_WM_PID) */ @@ -486,8 +375,9 @@ static Bool destroyICCallback(XIC /*xic*/, XPointer ptr, XPointer /*data*/) bool GHOST_WindowX11::createX11_XIC() { XIM xim = m_system->getX11_XIM(); - if (!xim) + if (!xim) { return false; + } XICCallback destroy; destroy.callback = (XICProc)destroyICCallback; @@ -535,17 +425,21 @@ void GHOST_WindowX11::refreshXInputDevices() XEventClass ev; DeviceMotionNotify(xtablet.Device, xtablet.MotionEvent, ev); - if (ev) + if (ev) { xevents.push_back(ev); + } DeviceButtonPress(xtablet.Device, xtablet.PressEvent, ev); - if (ev) + if (ev) { xevents.push_back(ev); + } ProximityIn(xtablet.Device, xtablet.ProxInEvent, ev); - if (ev) + if (ev) { xevents.push_back(ev); + } ProximityOut(xtablet.Device, xtablet.ProxOutEvent, ev); - if (ev) + if (ev) { xevents.push_back(ev); + } } XSelectExtensionEvent(m_display, m_window, xevents.data(), (int)xevents.size()); @@ -657,15 +551,15 @@ GHOST_TSuccess GHOST_WindowX11::setClientSize(uint32_t width, uint32_t height) void GHOST_WindowX11::screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const { - /* This is correct! */ - int ax, ay; Window temp; + /* Use (0, 0) instead of (inX, inY) to work around overflow of signed int16 in + * the implementation of this function. */ XTranslateCoordinates( - m_display, RootWindow(m_display, m_visualInfo->screen), m_window, inX, inY, &ax, &ay, &temp); - outX = ax; - outY = ay; + m_display, RootWindow(m_display, m_visualInfo->screen), m_window, 0, 0, &ax, &ay, &temp); + outX = ax + inX; + outY = ay + inY; } void GHOST_WindowX11::clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const @@ -984,7 +878,7 @@ GHOST_TWindowState GHOST_WindowX11::getState() const * In the Iconic and Withdrawn state, the window * is unmapped, so only need return a Minimized state. */ - if ((state == IconicState) || (state == WithdrawnState)) { + if (ELEM(state, IconicState, WithdrawnState)) { state_ret = GHOST_kWindowStateMinimized; } else if (netwmIsFullScreen() == True) { @@ -1284,6 +1178,65 @@ GHOST_WindowX11::~GHOST_WindowX11() } } +#ifdef USE_EGL +static GHOST_Context *create_egl_context(GHOST_SystemX11 *system, + Window window, + Display *display, + bool want_stereo, + bool debug_context, + int ver_major, + int ver_minor) +{ + GHOST_Context *context; + context = new GHOST_ContextEGL(system, + want_stereo, + EGLNativeWindowType(window), + EGLNativeDisplayType(display), + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + ver_major, + ver_minor, + GHOST_OPENGL_EGL_CONTEXT_FLAGS | + (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0), + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); + + if (context->initializeDrawingContext()) { + return context; + } + delete context; + + return nullptr; +} +#endif + +static GHOST_Context *create_glx_context(Window window, + Display *display, + GLXFBConfig fbconfig, + bool want_stereo, + bool debug_context, + int ver_major, + int ver_minor) +{ + GHOST_Context *context; + context = new GHOST_ContextGLX(want_stereo, + window, + display, + fbconfig, + GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + ver_major, + ver_minor, + GHOST_OPENGL_GLX_CONTEXT_FLAGS | + (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), + GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY); + + if (context->initializeDrawingContext()) { + return context; + } + delete context; + + return nullptr; +} + GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type) { if (type == GHOST_kDrawingContextTypeOpenGL) { @@ -1298,99 +1251,48 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type * - Try 3.3 core profile * - No fall-backs. */ -#if defined(WITH_GL_PROFILE_CORE) - { - const char *version_major = (char *)glewGetString(GLEW_VERSION_MAJOR); - if (version_major != nullptr && version_major[0] == '1') { - fprintf(stderr, "Error: GLEW version 2.0 and above is required.\n"); - abort(); - } - } -#endif - - const int profile_mask = -#ifdef WITH_GL_EGL -# if defined(WITH_GL_PROFILE_CORE) - EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT; -# elif defined(WITH_GL_PROFILE_COMPAT) - EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT; -# else -# error // must specify either core or compat at build time -# endif -#else -# if defined(WITH_GL_PROFILE_CORE) - GLX_CONTEXT_CORE_PROFILE_BIT_ARB; -# elif defined(WITH_GL_PROFILE_COMPAT) - GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; -# else -# error // must specify either core or compat at build time -# endif -#endif - GHOST_Context *context; +#ifdef USE_EGL + /* Try to initialize an EGL context. */ for (int minor = 5; minor >= 0; --minor) { -#ifdef WITH_GL_EGL - context = new GHOST_ContextEGL( - this->m_system, - m_wantStereoVisual, - EGLNativeWindowType(m_window), - EGLNativeDisplayType(m_display), - profile_mask, - 4, - minor, - GHOST_OPENGL_EGL_CONTEXT_FLAGS | - (m_is_debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0), - GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, - EGL_OPENGL_API); -#else - context = new GHOST_ContextGLX(m_wantStereoVisual, - m_window, - m_display, - (GLXFBConfig)m_fbconfig, - profile_mask, - 4, - minor, - GHOST_OPENGL_GLX_CONTEXT_FLAGS | - (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY); -#endif - - if (context->initializeDrawingContext()) { + context = create_egl_context( + this->m_system, m_window, m_display, m_wantStereoVisual, m_is_debug_context, 4, minor); + if (context != nullptr) { return context; } - delete context; } -#ifdef WITH_GL_EGL - context = new GHOST_ContextEGL(this->m_system, - m_wantStereoVisual, - EGLNativeWindowType(m_window), - EGLNativeDisplayType(m_display), - profile_mask, - 3, - 3, - GHOST_OPENGL_EGL_CONTEXT_FLAGS | - (m_is_debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0), - GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, - EGL_OPENGL_API); -#else - context = new GHOST_ContextGLX(m_wantStereoVisual, - m_window, + context = create_egl_context( + this->m_system, m_window, m_display, m_wantStereoVisual, m_is_debug_context, 3, 3); + if (context != nullptr) { + return context; + } + + /* EGL initialization failed, try to fallback to a GLX context. */ +#endif + for (int minor = 5; minor >= 0; --minor) { + context = create_glx_context(m_window, m_display, (GLXFBConfig)m_fbconfig, - profile_mask, - 3, - 3, - GHOST_OPENGL_GLX_CONTEXT_FLAGS | - (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY); -#endif - - if (context->initializeDrawingContext()) { + m_wantStereoVisual, + m_is_debug_context, + 4, + minor); + if (context != nullptr) { + return context; + } + } + context = create_glx_context(m_window, + m_display, + (GLXFBConfig)m_fbconfig, + m_wantStereoVisual, + m_is_debug_context, + 3, + 3); + if (context != nullptr) { return context; } - delete context; /* Ugly, but we get crashes unless a whole bunch of systems are patched. */ fprintf(stderr, "Error! Unsupported graphics card or driver.\n"); @@ -1514,7 +1416,7 @@ GHOST_TSuccess GHOST_WindowX11::setWindowCursorGrab(GHOST_TGrabCursorMode mode) { if (mode != GHOST_kGrabDisable) { if (mode != GHOST_kGrabNormal) { - m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]); + m_system->getCursorPosition(UNPACK2(m_cursorGrabInitPos)); setCursorGrabAccum(0, 0); if (mode == GHOST_kGrabHide) { @@ -1535,7 +1437,7 @@ GHOST_TSuccess GHOST_WindowX11::setWindowCursorGrab(GHOST_TGrabCursorMode mode) } else { if (m_cursorGrab == GHOST_kGrabHide) { - m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]); + m_system->setCursorPosition(UNPACK2(m_cursorGrabInitPos)); } if (m_cursorGrab != GHOST_kGrabNormal) { diff --git a/intern/ghost/intern/GHOST_WindowX11.h b/intern/ghost/intern/GHOST_WindowX11.h index ac4edd83549..c7a6b5e7357 100644 --- a/intern/ghost/intern/GHOST_WindowX11.h +++ b/intern/ghost/intern/GHOST_WindowX11.h @@ -47,7 +47,6 @@ class GHOST_WindowX11 : public GHOST_Window { * \param parentWindow: Parent (embedder) window. * \param type: The type of drawing context installed in this window. * \param stereoVisual: Stereo visual for quad buffered stereo. - * \param alphaBackground: Enable alpha blending of window with display background. */ GHOST_WindowX11(GHOST_SystemX11 *system, Display *display, @@ -62,7 +61,6 @@ class GHOST_WindowX11 : public GHOST_Window { const bool is_dialog = false, const bool stereoVisual = false, const bool exclusive = false, - const bool alphaBackground = false, const bool is_debug = false); bool getValid() const; diff --git a/intern/ghost/intern/GHOST_Wintab.cpp b/intern/ghost/intern/GHOST_Wintab.cpp index b136acbe098..c75a39bb34b 100644 --- a/intern/ghost/intern/GHOST_Wintab.cpp +++ b/intern/ghost/intern/GHOST_Wintab.cpp @@ -373,7 +373,7 @@ void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo) /* Iterate over button flag indices until all flags are clear. */ for (WORD buttonIndex = 0; buttonsChanged; buttonIndex++, buttonsChanged >>= 1) { if (buttonsChanged & 1) { - GHOST_TButtonMask button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex); + GHOST_TButton button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex); if (button != GHOST_kButtonMaskNone) { /* If this is not the first button found, push info for the prior Wintab button. */ @@ -397,7 +397,7 @@ void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo) } } -GHOST_TButtonMask GHOST_Wintab::mapWintabToGhostButton(UINT cursor, WORD physicalButton) +GHOST_TButton GHOST_Wintab::mapWintabToGhostButton(UINT cursor, WORD physicalButton) { const WORD numButtons = 32; BYTE logicalButtons[numButtons] = {0}; diff --git a/intern/ghost/intern/GHOST_Wintab.h b/intern/ghost/intern/GHOST_Wintab.h index 80eacf1f3fa..565aeb6ca02 100644 --- a/intern/ghost/intern/GHOST_Wintab.h +++ b/intern/ghost/intern/GHOST_Wintab.h @@ -54,7 +54,7 @@ struct GHOST_WintabInfoWin32 { int32_t x = 0; int32_t y = 0; GHOST_TEventType type = GHOST_kEventCursorMove; - GHOST_TButtonMask button = GHOST_kButtonMaskNone; + GHOST_TButton button = GHOST_kButtonMaskNone; uint64_t time = 0; GHOST_TabletData tabletData = GHOST_TABLET_DATA_NONE; }; @@ -243,7 +243,7 @@ class GHOST_Wintab { * \param physicalButton: The physical button ID to inspect. * \return The system mapped button. */ - GHOST_TButtonMask mapWintabToGhostButton(UINT cursor, WORD physicalButton); + GHOST_TButton mapWintabToGhostButton(UINT cursor, WORD physicalButton); /** * Applies common modifications to Wintab context. diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index 2ac3d9ec2a5..413e2670750 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -436,9 +436,11 @@ void GHOST_XrContext::getExtensionsToEnable( r_ext_names.push_back(gpu_binding); } -#if defined(WITH_GHOST_X11) && defined(WITH_GL_EGL) - assert(openxr_extension_is_available(m_oxr->extensions, XR_MNDX_EGL_ENABLE_EXTENSION_NAME)); - r_ext_names.push_back(XR_MNDX_EGL_ENABLE_EXTENSION_NAME); +#if defined(WITH_GHOST_X11) + if (openxr_extension_is_available(m_oxr->extensions, XR_MNDX_EGL_ENABLE_EXTENSION_NAME)) { + /* Use EGL if that backend is available. */ + r_ext_names.push_back(XR_MNDX_EGL_ENABLE_EXTENSION_NAME); + } #endif for (const std::string_view &ext : try_ext) { diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp index aa230bf8deb..6a7eb25925a 100644 --- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp +++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp @@ -8,17 +8,16 @@ #include <list> #include <sstream> -#if defined(WITH_GL_EGL) +#if defined(WITH_GHOST_X11) # include "GHOST_ContextEGL.h" -# if defined(WITH_GHOST_X11) -# include "GHOST_SystemX11.h" -# endif -# if defined(WITH_GHOST_WAYLAND) -# include "GHOST_SystemWayland.h" -# endif -#elif defined(WITH_GHOST_X11) # include "GHOST_ContextGLX.h" -#elif defined(WIN32) +# include "GHOST_SystemX11.h" +#endif +#if defined(WITH_GHOST_WAYLAND) +# include "GHOST_ContextEGL.h" +# include "GHOST_SystemWayland.h" +#endif +#if defined(WIN32) # include "GHOST_ContextD3D.h" # include "GHOST_ContextWGL.h" # include "GHOST_SystemWin32.h" @@ -61,19 +60,30 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { XrSystemId system_id, std::string *r_requirement_info) const override { -#if defined(WITH_GL_EGL) - GHOST_ContextEGL &ctx_gl = static_cast<GHOST_ContextEGL &>(ghost_ctx); -#elif defined(WITH_GHOST_X11) - GHOST_ContextGLX &ctx_gl = static_cast<GHOST_ContextGLX &>(ghost_ctx); -#else + int gl_major_version, gl_minor_version; +#if defined(WIN32) GHOST_ContextWGL &ctx_gl = static_cast<GHOST_ContextWGL &>(ghost_ctx); + gl_major_version = ctx_gl.m_contextMajorVersion; + gl_minor_version = ctx_gl.m_contextMinorVersion; +#elif defined(WITH_GHOST_X11) || defined(WITH_GHOST_WAYLAND) + if (dynamic_cast<GHOST_ContextEGL *>(&ghost_ctx)) { + GHOST_ContextEGL &ctx_gl = static_cast<GHOST_ContextEGL &>(ghost_ctx); + gl_major_version = ctx_gl.m_contextMajorVersion; + gl_minor_version = ctx_gl.m_contextMinorVersion; + } +# if defined(WITH_GHOST_X11) + else { + GHOST_ContextGLX &ctx_gl = static_cast<GHOST_ContextGLX &>(ghost_ctx); + gl_major_version = ctx_gl.m_contextMajorVersion; + gl_minor_version = ctx_gl.m_contextMinorVersion; + } +# endif #endif static PFN_xrGetOpenGLGraphicsRequirementsKHR s_xrGetOpenGLGraphicsRequirementsKHR_fn = nullptr; // static XrInstance s_instance = XR_NULL_HANDLE; XrGraphicsRequirementsOpenGLKHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR}; - const XrVersion gl_version = XR_MAKE_VERSION( - ctx_gl.m_contextMajorVersion, ctx_gl.m_contextMinorVersion, 0); + const XrVersion gl_version = XR_MAKE_VERSION(gl_major_version, gl_minor_version, 0); /* Although it would seem reasonable that the proc address would not change if the instance was * the same, in testing, repeated calls to #xrGetInstanceProcAddress() with the same instance @@ -112,45 +122,67 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { void initFromGhostContext(GHOST_Context &ghost_ctx) override { -#if defined(WITH_GHOST_X11) -# if defined(WITH_GL_EGL) - GHOST_ContextEGL &ctx_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx); - - if (dynamic_cast<const GHOST_SystemX11 *const>(ctx_egl.m_system)) { - oxr_binding.egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX; - oxr_binding.egl.getProcAddress = eglGetProcAddress; - oxr_binding.egl.display = ctx_egl.getDisplay(); - oxr_binding.egl.config = ctx_egl.getConfig(); - oxr_binding.egl.context = ctx_egl.getContext(); - } +#if defined(WITH_GHOST_X11) || defined(WITH_GHOST_WAYLAND) + /* WAYLAND/X11 may be dynamically selected at load time but both may also be + * supported at compile time individually. + * Without `is_ctx_egl` & `is_wayland` preprocessor checks become an unmanageable soup. */ + const bool is_ctx_egl = dynamic_cast<GHOST_ContextEGL *>(&ghost_ctx) != nullptr; + if (is_ctx_egl) { + GHOST_ContextEGL &ctx_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx); + const bool is_wayland = ( +# if defined(WITH_GHOST_WAYLAND) + dynamic_cast<const GHOST_SystemWayland *const>(ctx_egl.m_system) != nullptr # else - GHOST_ContextGLX &ctx_glx = static_cast<GHOST_ContextGLX &>(ghost_ctx); - XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx.m_display, ctx_glx.m_fbconfig); + false +# endif + ); + + if (is_wayland) { +# if defined(WITH_GHOST_WAYLAND) + /* #GHOST_SystemWayland */ + oxr_binding.wl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR; + oxr_binding.wl.display = (struct wl_display *)ctx_egl.m_nativeDisplay; +# else + GHOST_ASSERT(false, "Unexpected State: logical error, unreachable!"); +# endif /* !WITH_GHOST_WAYLAND */ + } + else { /* `!is_wayland` */ +# if defined(WITH_GHOST_X11) + /* #GHOST_SystemX11. */ + oxr_binding.egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX; + oxr_binding.egl.getProcAddress = eglGetProcAddress; + oxr_binding.egl.display = ctx_egl.getDisplay(); + oxr_binding.egl.config = ctx_egl.getConfig(); + oxr_binding.egl.context = ctx_egl.getContext(); +# else + GHOST_ASSERT(false, "Unexpected State: built with only WAYLAND and no System found!"); +# endif /* !WITH_GHOST_X11 */ + } + } + else { /* `!is_ctx_egl` */ +# if defined(WITH_GHOST_X11) + GHOST_ContextGLX &ctx_glx = static_cast<GHOST_ContextGLX &>(ghost_ctx); + XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx.m_display, ctx_glx.m_fbconfig); - oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR; - oxr_binding.glx.xDisplay = ctx_glx.m_display; - oxr_binding.glx.glxFBConfig = ctx_glx.m_fbconfig; - oxr_binding.glx.glxDrawable = ctx_glx.m_window; - oxr_binding.glx.glxContext = ctx_glx.m_context; - oxr_binding.glx.visualid = visual_info->visualid; + oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR; + oxr_binding.glx.xDisplay = ctx_glx.m_display; + oxr_binding.glx.glxFBConfig = ctx_glx.m_fbconfig; + oxr_binding.glx.glxDrawable = ctx_glx.m_window; + oxr_binding.glx.glxContext = ctx_glx.m_context; + oxr_binding.glx.visualid = visual_info->visualid; - XFree(visual_info); -# endif + XFree(visual_info); +# else + GHOST_ASSERT(false, "Unexpected State: built without X11 and no EGL context is available!"); +# endif /* !WITH_GHOST_X11 */ + } #elif defined(WIN32) GHOST_ContextWGL &ctx_wgl = static_cast<GHOST_ContextWGL &>(ghost_ctx); oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR; oxr_binding.wgl.hDC = ctx_wgl.m_hDC; oxr_binding.wgl.hGLRC = ctx_wgl.m_hGLRC; -#endif - -#if defined(WITH_GHOST_WAYLAND) - GHOST_ContextEGL &ctx_wl_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx); - if (dynamic_cast<const GHOST_SystemWayland *const>(ctx_wl_egl.m_system)) { - oxr_binding.wl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR; - oxr_binding.wl.display = (struct wl_display *)ctx_wl_egl.m_nativeDisplay; - } -#endif +#endif /* WIN32 */ /* Generate a frame-buffer to use for blitting into the texture. */ glGenFramebuffers(1, &m_fbo); diff --git a/intern/ghost/intern/GHOST_Xr_openxr_includes.h b/intern/ghost/intern/GHOST_Xr_openxr_includes.h index 9706f51c027..1c16f746f7a 100644 --- a/intern/ghost/intern/GHOST_Xr_openxr_includes.h +++ b/intern/ghost/intern/GHOST_Xr_openxr_includes.h @@ -28,13 +28,8 @@ # include <d3d12.h> #endif #ifdef WITH_GHOST_X11 -# ifdef WITH_GL_EGL -/* TODO: Why do we have to create this typedef manually? */ -typedef void (*(*PFNEGLGETPROCADDRESSPROC)(const char *procname))(void); -# include <GL/eglew.h> -# else -# include <GL/glxew.h> -# endif +# include <epoxy/egl.h> +# include <epoxy/glx.h> #endif #include <openxr/openxr.h> diff --git a/intern/ghost/intern/GHOST_utildefines.h b/intern/ghost/intern/GHOST_utildefines.h new file mode 100644 index 00000000000..f0ae6e12d3e --- /dev/null +++ b/intern/ghost/intern/GHOST_utildefines.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup GHOST + * + * Utility defines (avoid depending on `BLI_utildefines.h`). + */ + +#pragma once + +#include "GHOST_utildefines_variadic.h" + +/* -------------------------------------------------------------------- */ +/** \name Branch Prediction Macros + * \{ */ + +/* hints for branch prediction, only use in code that runs a _lot_ where */ +#ifdef __GNUC__ +# define LIKELY(x) __builtin_expect(!!(x), 1) +# define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +# define LIKELY(x) (x) +# define UNLIKELY(x) (x) +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Array Unpacking Macros + * \{ */ + +/* unpack vector for args */ +#define UNPACK2(a) ((a)[0]), ((a)[1]) +#define UNPACK3(a) UNPACK2(a), ((a)[2]) +#define UNPACK4(a) UNPACK3(a), ((a)[3]) +/* pre may be '&', '*' or func, post may be '->member' */ +#define UNPACK2_EX(pre, a, post) (pre((a)[0]) post), (pre((a)[1]) post) +#define UNPACK3_EX(pre, a, post) UNPACK2_EX(pre, a, post), (pre((a)[2]) post) +#define UNPACK4_EX(pre, a, post) UNPACK3_EX(pre, a, post), (pre((a)[3]) post) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Array Macros + * \{ */ + +/* Assuming a static array. */ +#if defined(__GNUC__) && !defined(__cplusplus) && !defined(__clang__) && !defined(__INTEL_COMPILER) +# define ARRAY_SIZE(arr) \ + ((sizeof(struct { int isnt_array : ((const void *)&(arr) == &(arr)[0]); }) * 0) + \ + (sizeof(arr) / sizeof(*(arr)))) +#else +# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr))) +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Equal to Any Element (ELEM) Macro + * \{ */ + +/* Manual line breaks for readability. */ +/* clang-format off */ + +/* ELEM#(v, ...): is the first arg equal any others? */ +/* internal helpers. */ +#define _VA_ELEM2(v, a) ((v) == (a)) +#define _VA_ELEM3(v, a, b) \ + (_VA_ELEM2(v, a) || _VA_ELEM2(v, b)) +#define _VA_ELEM4(v, a, b, c) \ + (_VA_ELEM3(v, a, b) || _VA_ELEM2(v, c)) +#define _VA_ELEM5(v, a, b, c, d) \ + (_VA_ELEM4(v, a, b, c) || _VA_ELEM2(v, d)) +#define _VA_ELEM6(v, a, b, c, d, e) \ + (_VA_ELEM5(v, a, b, c, d) || _VA_ELEM2(v, e)) +#define _VA_ELEM7(v, a, b, c, d, e, f) \ + (_VA_ELEM6(v, a, b, c, d, e) || _VA_ELEM2(v, f)) +#define _VA_ELEM8(v, a, b, c, d, e, f, g) \ + (_VA_ELEM7(v, a, b, c, d, e, f) || _VA_ELEM2(v, g)) +#define _VA_ELEM9(v, a, b, c, d, e, f, g, h) \ + (_VA_ELEM8(v, a, b, c, d, e, f, g) || _VA_ELEM2(v, h)) +#define _VA_ELEM10(v, a, b, c, d, e, f, g, h, i) \ + (_VA_ELEM9(v, a, b, c, d, e, f, g, h) || _VA_ELEM2(v, i)) +#define _VA_ELEM11(v, a, b, c, d, e, f, g, h, i, j) \ + (_VA_ELEM10(v, a, b, c, d, e, f, g, h, i) || _VA_ELEM2(v, j)) +#define _VA_ELEM12(v, a, b, c, d, e, f, g, h, i, j, k) \ + (_VA_ELEM11(v, a, b, c, d, e, f, g, h, i, j) || _VA_ELEM2(v, k)) +#define _VA_ELEM13(v, a, b, c, d, e, f, g, h, i, j, k, l) \ + (_VA_ELEM12(v, a, b, c, d, e, f, g, h, i, j, k) || _VA_ELEM2(v, l)) +#define _VA_ELEM14(v, a, b, c, d, e, f, g, h, i, j, k, l, m) \ + (_VA_ELEM13(v, a, b, c, d, e, f, g, h, i, j, k, l) || _VA_ELEM2(v, m)) +#define _VA_ELEM15(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n) \ + (_VA_ELEM14(v, a, b, c, d, e, f, g, h, i, j, k, l, m) || _VA_ELEM2(v, n)) +#define _VA_ELEM16(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) \ + (_VA_ELEM15(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n) || _VA_ELEM2(v, o)) +#define _VA_ELEM17(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ + (_VA_ELEM16(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) || _VA_ELEM2(v, p)) +/* clang-format on */ + +/* reusable ELEM macro */ +#define ELEM(...) VA_NARGS_CALL_OVERLOAD(_VA_ELEM, __VA_ARGS__) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clamp Macros + * \{ */ + +#define CLAMPIS(a, b, c) ((a) < (b) ? (b) : (a) > (c) ? (c) : (a)) + +#define CLAMP(a, b, c) \ + { \ + if ((a) < (b)) { \ + (a) = (b); \ + } \ + else if ((a) > (c)) { \ + (a) = (c); \ + } \ + } \ + (void)0 + +#define CLAMP_MAX(a, c) \ + { \ + if ((a) > (c)) { \ + (a) = (c); \ + } \ + } \ + (void)0 + +#define CLAMP_MIN(a, b) \ + { \ + if ((a) < (b)) { \ + (a) = (b); \ + } \ + } \ + (void)0 + +#define CLAMP2(vec, b, c) \ + { \ + CLAMP((vec)[0], b, c); \ + CLAMP((vec)[1], b, c); \ + } \ + (void)0 + +#define CLAMP2_MIN(vec, b) \ + { \ + CLAMP_MIN((vec)[0], b); \ + CLAMP_MIN((vec)[1], b); \ + } \ + (void)0 + +#define CLAMP2_MAX(vec, b) \ + { \ + CLAMP_MAX((vec)[0], b); \ + CLAMP_MAX((vec)[1], b); \ + } \ + (void)0 + +#define CLAMP3(vec, b, c) \ + { \ + CLAMP((vec)[0], b, c); \ + CLAMP((vec)[1], b, c); \ + CLAMP((vec)[2], b, c); \ + } \ + (void)0 + +#define CLAMP3_MIN(vec, b) \ + { \ + CLAMP_MIN((vec)[0], b); \ + CLAMP_MIN((vec)[1], b); \ + CLAMP_MIN((vec)[2], b); \ + } \ + (void)0 + +#define CLAMP3_MAX(vec, b) \ + { \ + CLAMP_MAX((vec)[0], b); \ + CLAMP_MAX((vec)[1], b); \ + CLAMP_MAX((vec)[2], b); \ + } \ + (void)0 + +#define CLAMP4(vec, b, c) \ + { \ + CLAMP((vec)[0], b, c); \ + CLAMP((vec)[1], b, c); \ + CLAMP((vec)[2], b, c); \ + CLAMP((vec)[3], b, c); \ + } \ + (void)0 + +#define CLAMP4_MIN(vec, b) \ + { \ + CLAMP_MIN((vec)[0], b); \ + CLAMP_MIN((vec)[1], b); \ + CLAMP_MIN((vec)[2], b); \ + CLAMP_MIN((vec)[3], b); \ + } \ + (void)0 + +#define CLAMP4_MAX(vec, b) \ + { \ + CLAMP_MAX((vec)[0], b); \ + CLAMP_MAX((vec)[1], b); \ + CLAMP_MAX((vec)[2], b); \ + CLAMP_MAX((vec)[3], b); \ + } \ + (void)0 + +/** \} */ diff --git a/intern/ghost/intern/GHOST_utildefines_variadic.h b/intern/ghost/intern/GHOST_utildefines_variadic.h new file mode 100644 index 00000000000..4ee306a27b2 --- /dev/null +++ b/intern/ghost/intern/GHOST_utildefines_variadic.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + */ + +/* NOTE: copied from `BLI_utildefines_variadic.h` which would be a bad-level include. */ + +/* Over wrapped args. */ +/* clang-format off */ + +/* --- internal helpers --- */ +#define _VA_NARGS_GLUE(x, y) x y +#define _VA_NARGS_RETURN_COUNT(\ + _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, \ + _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, \ + _33_, _34_, _35_, _36_, _37_, _38_, _39_, _40_, _41_, _42_, _43_, _44_, _45_, _46_, _47_, _48_, \ + _49_, _50_, _51_, _52_, _53_, _54_, _55_, _56_, _57_, _58_, _59_, _60_, _61_, _62_, _63_, _64_, \ + count, ...) count +#define _VA_NARGS_EXPAND(args) _VA_NARGS_RETURN_COUNT args +#define _VA_NARGS_OVERLOAD_MACRO2(name, count) name##count +#define _VA_NARGS_OVERLOAD_MACRO1(name, count) _VA_NARGS_OVERLOAD_MACRO2(name, count) +#define _VA_NARGS_OVERLOAD_MACRO(name, count) _VA_NARGS_OVERLOAD_MACRO1(name, count) +/* --- expose for re-use --- */ +/* 64 args max */ +#define VA_NARGS_COUNT(...) _VA_NARGS_EXPAND((__VA_ARGS__, \ + 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, \ + 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, \ + 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ + 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define VA_NARGS_CALL_OVERLOAD(name, ...) \ + _VA_NARGS_GLUE(_VA_NARGS_OVERLOAD_MACRO(name, VA_NARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__)) + +/* clang-format on */ diff --git a/intern/ghost/test/CMakeLists.txt b/intern/ghost/test/CMakeLists.txt index 9cc406313c7..3f74f97f115 100644 --- a/intern/ghost/test/CMakeLists.txt +++ b/intern/ghost/test/CMakeLists.txt @@ -89,8 +89,6 @@ if(UNIX AND NOT APPLE) set(WITH_GHOST_X11 ON) endif() -# for now... default to this -add_definitions(-DWITH_GL_PROFILE_COMPAT) # BLF needs this to ignore GPU library add_definitions(-DBLF_STANDALONE) @@ -155,13 +153,6 @@ suffix_relpaths(SRC_NEW "${SRC}" "../../../extern/wcwidth/") include_directories(${INC_NEW}) add_library(wcwidth_lib ${SRC_NEW}) -# glew-mx -include(${CMAKE_SOURCE_DIR}/../../../intern/glew-mx/CMakeLists.txt) -suffix_relpaths(INC_NEW "${INC}" "../../../intern/glew-mx/") -suffix_relpaths(SRC_NEW "${SRC}" "../../../intern/glew-mx/") -include_directories(${INC_NEW}) -add_library(glewmx_lib ${SRC_NEW}) - # grr, blenfont needs BLI include_directories( "../../../source/blender/blenlib" @@ -217,21 +208,12 @@ endif() if(UNIX AND NOT APPLE) find_package(X11 REQUIRED) - find_package(GLEW) - - if(NOT GLEW_FOUND) - message(FATAL_ERROR "GLEW is required to build blender, install it or disable WITH_SYSTEM_GLEW") - endif() set(PLATFORM_LINKLIBS ${X11_X11_LIB} ${X11_Xinput_LIB} - ${GLEW_LIBRARY} -lpthread ) -else() - # set(GLEW_LIBRARY "") # unused - set(GLEW_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/extern/glew/include") endif() string(APPEND CMAKE_C_FLAGS " ${PLATFORM_CFLAGS}") @@ -246,7 +228,6 @@ add_executable(gears_c target_link_libraries(gears_c ghost_lib - glewmx_lib string_lib ${OPENGL_gl_LIBRARY} ${CMAKE_DL_LIBS} @@ -260,7 +241,6 @@ add_executable(gears_cpp target_link_libraries(gears_cpp ghost_lib - glewmx_lib string_lib ${OPENGL_gl_LIBRARY} ${CMAKE_DL_LIBS} @@ -287,7 +267,6 @@ target_link_libraries(multitest_c # imbuf_lib ghost_lib bli_lib # again... - glewmx_lib string_lib numaapi_lib guardedalloc_lib diff --git a/intern/ghost/test/gears/GHOST_Test.cpp b/intern/ghost/test/gears/GHOST_Test.cpp index 5a43543d163..f5ef2527d75 100644 --- a/intern/ghost/test/gears/GHOST_Test.cpp +++ b/intern/ghost/test/gears/GHOST_Test.cpp @@ -559,10 +559,12 @@ bool Application::processEvent(GHOST_IEvent *event) break; case GHOST_kKeyS: // toggle mono and stereo - if (stereo) + if (stereo) { stereo = false; - else + } + else { stereo = true; + } break; case GHOST_kKeyT: @@ -680,8 +682,9 @@ int main(int /*argc*/, char ** /*argv*/) if (lresult == ERROR_SUCCESS) printf("Successfully set value for key\n"); regkey.Close(); - if (lresult == ERROR_SUCCESS) + if (lresult == ERROR_SUCCESS) { printf("Successfully closed key\n"); + } // regkey.Write("2"); } #endif // WIN32 diff --git a/intern/ghost/test/multitest/EventToBuf.c b/intern/ghost/test/multitest/EventToBuf.c index baab32328c3..846a867a371 100644 --- a/intern/ghost/test/multitest/EventToBuf.c +++ b/intern/ghost/test/multitest/EventToBuf.c @@ -218,8 +218,10 @@ void event_to_buf(GHOST_EventHandle evt, char buf[128]) case GHOST_kEventKeyUp: { GHOST_TEventKeyData *kd = data; pos += sprintf(pos, " - key: %s (%d)", keytype_to_string(kd->key), kd->key); - if (kd->ascii) - pos += sprintf(pos, " ascii: '%c' (%d)", kd->ascii, kd->ascii); + /* TODO: ideally this would print the unicode character. */ + if (kd->utf8_buf[0]) { + pos += sprintf(pos, " ascii: '%c' (%d)", kd->utf8_buf[0], kd->utf8_buf[0]); + } break; } } diff --git a/intern/ghost/test/multitest/MultiTest.c b/intern/ghost/test/multitest/MultiTest.c index 157e4f1b0f2..99b88dfb525 100644 --- a/intern/ghost/test/multitest/MultiTest.c +++ b/intern/ghost/test/multitest/MultiTest.c @@ -179,37 +179,44 @@ static void mainwindow_do_key(MainWindow *mw, GHOST_TKey key, int press) { switch (key) { case GHOST_kKeyC: - if (press) + if (press) { GHOST_SetCursorShape(mw->win, (GHOST_TStandardCursor)(rand() % (GHOST_kStandardCursorNumCursors))); + } break; case GHOST_kKeyLeftBracket: - if (press) + if (press) { GHOST_SetCursorVisibility(mw->win, 0); + } break; case GHOST_kKeyRightBracket: - if (press) + if (press) { GHOST_SetCursorVisibility(mw->win, 1); + } break; case GHOST_kKeyE: - if (press) + if (press) { multitestapp_toggle_extra_window(mw->app); + } break; case GHOST_kKeyQ: - if (press) + if (press) { multitestapp_exit(mw->app); + } break; case GHOST_kKeyT: - if (press) + if (press) { mainwindow_log(mw, "TextTest~|`hello`\"world\",<>/"); + } break; case GHOST_kKeyR: if (press) { int i; mainwindow_log(mw, "Invalidating window 10 times"); - for (i = 0; i < 10; i++) + for (i = 0; i < 10; i++) { GHOST_InvalidateWindow(mw->win); + } } break; case GHOST_kKeyF11: @@ -328,9 +335,7 @@ MainWindow *mainwindow_new(MultiTestApp *app) return mw; } - else { - return NULL; - } + return NULL; } void mainwindow_free(MainWindow *mw) |