Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/ghost')
-rw-r--r--intern/ghost/CMakeLists.txt9
-rw-r--r--intern/ghost/GHOST_C-api.h107
-rw-r--r--intern/ghost/GHOST_IXrContext.h6
-rw-r--r--intern/ghost/GHOST_Types.h60
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp148
-rw-r--r--intern/ghost/intern/GHOST_DropTargetX11.cpp3
-rw-r--r--intern/ghost/intern/GHOST_IconX11.h4
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.cpp285
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.h17
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp99
-rw-r--r--intern/ghost/intern/GHOST_Util.h45
-rw-r--r--intern/ghost/intern/GHOST_WaylandCursorSettings.h130
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.mm5
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.cpp130
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.h17
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp84
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h174
-rw-r--r--intern/ghost/intern/GHOST_XrAction.cpp477
-rw-r--r--intern/ghost/intern/GHOST_XrAction.h145
-rw-r--r--intern/ghost/intern/GHOST_XrContext.cpp20
-rw-r--r--intern/ghost/intern/GHOST_XrContext.h5
-rw-r--r--intern/ghost/intern/GHOST_XrException.h5
-rw-r--r--intern/ghost/intern/GHOST_XrSession.cpp365
-rw-r--r--intern/ghost/intern/GHOST_XrSession.h37
-rw-r--r--intern/ghost/intern/GHOST_Xr_intern.h24
-rw-r--r--intern/ghost/test/gears/GHOST_C-Test.c2
-rw-r--r--intern/ghost/test/multitest/MultiTest.c2
28 files changed, 1978 insertions, 429 deletions
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 1739659ab88..1b5cdb3cda0 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -79,6 +79,7 @@ set(SRC
intern/GHOST_SystemPaths.h
intern/GHOST_TimerManager.h
intern/GHOST_TimerTask.h
+ intern/GHOST_Util.h
intern/GHOST_Window.h
intern/GHOST_WindowManager.h
)
@@ -281,6 +282,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
${wayland-egl_INCLUDE_DIRS}
${xkbcommon_INCLUDE_DIRS}
${wayland-cursor_INCLUDE_DIRS}
+ ${dbus_INCLUDE_DIRS}
)
list(APPEND SRC
@@ -320,6 +322,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
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"
+ )
# Pointer-constraints.
generate_protocol_bindings(
pointer-constraints
@@ -438,6 +445,7 @@ endif()
if(WITH_XR_OPENXR)
list(APPEND SRC
intern/GHOST_Xr.cpp
+ intern/GHOST_XrAction.cpp
intern/GHOST_XrContext.cpp
intern/GHOST_XrEvent.cpp
intern/GHOST_XrGraphicsBinding.cpp
@@ -446,6 +454,7 @@ if(WITH_XR_OPENXR)
GHOST_IXrContext.h
intern/GHOST_IXrGraphicsBinding.h
+ intern/GHOST_XrAction.h
intern/GHOST_XrContext.h
intern/GHOST_XrException.h
intern/GHOST_XrSession.h
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index d79111b742f..2bd9af6df5c 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -276,7 +276,7 @@ extern int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle);
* wait (block) until the next event before returning.
* \return Indication of the presence of events.
*/
-extern int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent);
+extern bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent);
/**
* Retrieves events from the queue and send them to the event consumers.
@@ -1059,7 +1059,110 @@ int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_context
* \returns GHOST_kSuccess if any event was handled, otherwise GHOST_kFailure.
*/
GHOST_TSuccess GHOST_XrEventsHandle(GHOST_XrContextHandle xr_context);
-#endif
+
+/* actions */
+/**
+ * Create an OpenXR action set for input/output.
+ */
+int GHOST_XrCreateActionSet(GHOST_XrContextHandle xr_context, const GHOST_XrActionSetInfo *info);
+
+/**
+ * Destroy a previously created OpenXR action set.
+ */
+void GHOST_XrDestroyActionSet(GHOST_XrContextHandle xr_context, const char *action_set_name);
+
+/**
+ * Create OpenXR input/output actions.
+ */
+int GHOST_XrCreateActions(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionInfo *infos);
+
+/**
+ * Destroy previously created OpenXR actions.
+ */
+void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const char *const *action_names);
+
+/**
+ * Create spaces for pose-based OpenXR actions.
+ */
+int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos);
+
+/**
+ * Destroy previously created spaces for OpenXR actions.
+ */
+void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos);
+
+/**
+ * Create input/output path bindings for OpenXR actions.
+ */
+int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos);
+
+/**
+ * Destroy previously created bindings for OpenXR actions.
+ */
+void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos);
+
+/**
+ * Attach all created action sets to the current OpenXR session.
+ */
+int GHOST_XrAttachActionSets(GHOST_XrContextHandle xr_context);
+
+/**
+ * Update button/tracking states for OpenXR actions.
+ *
+ * \param action_set_name: The name of the action set to sync. If NULL, all action sets
+ * attached to the session will be synced.
+ */
+int GHOST_XrSyncActions(GHOST_XrContextHandle xr_context, const char *action_set_name);
+
+/**
+ * Apply an OpenXR haptic output action.
+ */
+int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 *duration,
+ const float *frequency,
+ const float *amplitude);
+
+/**
+ * Stop a previously applied OpenXR haptic output action.
+ */
+void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ const char *action_name);
+
+/**
+ * Get action set custom data (owned by Blender, not GHOST).
+ */
+void *GHOST_XrGetActionSetCustomdata(GHOST_XrContextHandle xr_context,
+ const char *action_set_name);
+
+/**
+ * Get action custom data (owned by Blender, not GHOST).
+ */
+void *GHOST_XrGetActionCustomdata(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ const char *action_name);
+
+#endif /* WITH_XR_OPENXR */
#ifdef __cplusplus
}
diff --git a/intern/ghost/GHOST_IXrContext.h b/intern/ghost/GHOST_IXrContext.h
index dd266a3b6ae..86fe78814a7 100644
--- a/intern/ghost/GHOST_IXrContext.h
+++ b/intern/ghost/GHOST_IXrContext.h
@@ -22,6 +22,8 @@
#include "GHOST_Types.h"
+class GHOST_XrSession;
+
class GHOST_IXrContext {
public:
virtual ~GHOST_IXrContext() = default;
@@ -31,6 +33,10 @@ class GHOST_IXrContext {
virtual bool isSessionRunning() const = 0;
virtual void drawSessionViews(void *draw_customdata) = 0;
+ /* Needed for the GHOST C api. */
+ virtual GHOST_XrSession *getSession() = 0;
+ virtual const GHOST_XrSession *getSession() const = 0;
+
virtual void dispatchErrorMessage(const class GHOST_XrException *) const = 0;
virtual void setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index bd7ba6657f0..3a8d0fbfecf 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -416,7 +416,10 @@ typedef enum {
GHOST_kGrabNormal,
/** Wrap the mouse location to prevent limiting screen bounds. */
GHOST_kGrabWrap,
- /** Hide the mouse while grabbing and restore the original location on release (numbuts). */
+ /**
+ * Hide the mouse while grabbing and restore the original location on release
+ * (used for number buttons and some other draggable UI elements).
+ */
GHOST_kGrabHide,
} GHOST_TGrabCursorMode;
@@ -631,7 +634,9 @@ typedef enum GHOST_TXrGraphicsBinding {
typedef void (*GHOST_XrErrorHandlerFn)(const struct GHOST_XrError *);
+typedef void (*GHOST_XrSessionCreateFn)(void);
typedef void (*GHOST_XrSessionExitFn)(void *customdata);
+typedef void (*GHOST_XrCustomdataFreeFn)(void *customdata);
typedef void *(*GHOST_XrGraphicsContextBindFn)(void);
typedef void (*GHOST_XrGraphicsContextUnbindFn)(GHOST_ContextHandle graphics_context);
@@ -662,6 +667,7 @@ typedef struct {
typedef struct {
GHOST_XrPose base_pose;
+ GHOST_XrSessionCreateFn create_fn;
GHOST_XrSessionExitFn exit_fn;
void *exit_customdata;
} GHOST_XrSessionBeginInfo;
@@ -688,4 +694,54 @@ typedef struct GHOST_XrError {
void *customdata;
} GHOST_XrError;
-#endif
+typedef struct GHOST_XrActionSetInfo {
+ const char *name;
+
+ GHOST_XrCustomdataFreeFn customdata_free_fn;
+ void *customdata; /* wmXrActionSet */
+} GHOST_XrActionSetInfo;
+
+/** XR action type. Enum values match those in OpenXR's
+ * XrActionType enum for consistency. */
+typedef enum GHOST_XrActionType {
+ GHOST_kXrActionTypeBooleanInput = 1,
+ GHOST_kXrActionTypeFloatInput = 2,
+ GHOST_kXrActionTypeVector2fInput = 3,
+ GHOST_kXrActionTypePoseInput = 4,
+ GHOST_kXrActionTypeVibrationOutput = 100,
+} GHOST_XrActionType;
+
+typedef struct GHOST_XrActionInfo {
+ const char *name;
+ GHOST_XrActionType type;
+ GHOST_TUns32 count_subaction_paths;
+ const char **subaction_paths;
+ /** States for each subaction path. */
+ void *states;
+
+ GHOST_XrCustomdataFreeFn customdata_free_fn;
+ void *customdata; /* wmXrAction */
+} GHOST_XrActionInfo;
+
+typedef struct GHOST_XrActionSpaceInfo {
+ const char *action_name;
+ GHOST_TUns32 count_subaction_paths;
+ const char **subaction_paths;
+ /** Poses for each subaction path. */
+ const GHOST_XrPose *poses;
+} GHOST_XrActionSpaceInfo;
+
+typedef struct GHOST_XrActionBindingInfo {
+ const char *action_name;
+ GHOST_TUns32 count_interaction_paths;
+ /** Interaction path: User (subaction) path + component path. */
+ const char **interaction_paths;
+} GHOST_XrActionBindingInfo;
+
+typedef struct GHOST_XrActionProfileInfo {
+ const char *profile_path;
+ GHOST_TUns32 count_bindings;
+ const GHOST_XrActionBindingInfo *bindings;
+} GHOST_XrActionProfileInfo;
+
+#endif /* WITH_XR_OPENXR */
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index af65e1cd4d2..955f35274ea 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -33,6 +33,7 @@
#include "intern/GHOST_Debug.h"
#ifdef WITH_XR_OPENXR
# include "GHOST_IXrContext.h"
+# include "intern/GHOST_XrSession.h"
#endif
#include "intern/GHOST_CallbackEventConsumer.h"
#include "intern/GHOST_XrException.h"
@@ -248,11 +249,11 @@ int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle)
return (int)system->getFullScreen();
}
-int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent)
+bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
- return (int)system->processEvents(waitForEvent ? true : false);
+ return system->processEvents(waitForEvent);
}
void GHOST_DispatchEvents(GHOST_SystemHandle systemhandle)
@@ -953,4 +954,145 @@ int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_context
return 0; /* Only reached if exception is thrown. */
}
-#endif
+int GHOST_XrCreateActionSet(GHOST_XrContextHandle xr_contexthandle,
+ const GHOST_XrActionSetInfo *info)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActionSet(*info), xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActionSet(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActionSet(action_set_name), xr_context);
+}
+
+int GHOST_XrCreateActions(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActions(action_set_name, count, infos), xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const char *const *action_names)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActions(action_set_name, count, action_names), xr_context);
+}
+
+int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActionSpaces(action_set_name, count, infos),
+ xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActionSpaces(action_set_name, count, infos), xr_context);
+}
+
+int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActionBindings(action_set_name, count, infos),
+ xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActionBindings(action_set_name, count, infos), xr_context);
+}
+
+int GHOST_XrAttachActionSets(GHOST_XrContextHandle xr_contexthandle)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->attachActionSets(), xr_context);
+ return 0;
+}
+
+int GHOST_XrSyncActions(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->syncActions(action_set_name), xr_context);
+ return 0;
+}
+
+int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 *duration,
+ const float *frequency,
+ const float *amplitude)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->applyHapticAction(
+ action_set_name, action_name, *duration, *frequency, *amplitude),
+ xr_context);
+ return 0;
+}
+
+void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ const char *action_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->stopHapticAction(action_set_name, action_name), xr_context);
+}
+
+void *GHOST_XrGetActionSetCustomdata(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->getActionSetCustomdata(action_set_name), xr_context);
+ return 0;
+}
+
+void *GHOST_XrGetActionCustomdata(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ const char *action_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->getActionCustomdata(action_set_name, action_name),
+ xr_context);
+ return 0;
+}
+
+#endif /* WITH_XR_OPENXR */
diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp
index 82ed9de230d..a62db31c952 100644
--- a/intern/ghost/intern/GHOST_DropTargetX11.cpp
+++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp
@@ -112,8 +112,7 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11()
}
}
-/* based on a code from Saul Rennison
- * http://stackoverflow.com/questions/2673207/c-c-url-decode-library */
+/* Based on: https://stackoverflow.com/a/2766963/432509 */
typedef enum DecodeState_e {
STATE_SEARCH = 0, ///< searching for an ampersand to convert
diff --git a/intern/ghost/intern/GHOST_IconX11.h b/intern/ghost/intern/GHOST_IconX11.h
index 615a7dae6b5..403d611b71d 100644
--- a/intern/ghost/intern/GHOST_IconX11.h
+++ b/intern/ghost/intern/GHOST_IconX11.h
@@ -24,7 +24,8 @@
#pragma once
-/*
+/**
+ * \code{.py}
* import bpy
* import textwrap
*
@@ -42,6 +43,7 @@
* print("%d,%d," % (w, h))
* text = ", ".join(["0x%x" % p for p in pixels])
* print(textwrap.fill(text, width=120), end=",\n")
+ * \endcode
*/
/**
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index 050c90097c5..6ddb7f031f5 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -1711,7 +1711,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
dx = [event scrollingDeltaX];
dy = [event scrollingDeltaY];
- /* However, wacom tablet (intuos5) needs old deltas,
+ /* However, Wacom tablet (intuos5) needs old deltas,
* it then has momentum and phase at zero. */
if (phase == NSEventPhaseNone && momentumPhase == NSEventPhaseNone) {
dx = [event deltaX];
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 81d9824ed26..83b9b2ba36b 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -40,6 +40,7 @@
#include <unordered_map>
#include <unordered_set>
+#include "GHOST_WaylandCursorSettings.h"
#include <pointer-constraints-client-protocol.h>
#include <relative-pointer-client-protocol.h>
#include <wayland-cursor.h>
@@ -52,15 +53,6 @@
#include <cstring>
-struct output_t {
- struct wl_output *output;
- int32_t width, height;
- int transform;
- int scale;
- std::string make;
- std::string model;
-};
-
struct buffer_t {
void *data;
size_t size;
@@ -72,6 +64,12 @@ struct cursor_t {
struct wl_buffer *buffer;
struct wl_cursor_image image;
struct buffer_t *file_buffer = nullptr;
+ struct wl_cursor_theme *theme = nullptr;
+ int size;
+ std::string theme_name;
+ // outputs on which the cursor is visible
+ std::unordered_set<const output_t *> outputs;
+ int scale = 1;
};
struct data_offer_t {
@@ -142,10 +140,14 @@ struct display_t {
struct wl_display *display;
struct wl_compositor *compositor = nullptr;
struct xdg_wm_base *xdg_shell = nullptr;
+ struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr;
struct wl_shm *shm = nullptr;
std::vector<output_t *> outputs;
std::vector<input_t *> inputs;
- struct wl_cursor_theme *cursor_theme = nullptr;
+ struct {
+ std::string theme;
+ int size;
+ } cursor;
struct wl_data_device_manager *data_device_manager = nullptr;
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr;
struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr;
@@ -154,6 +156,8 @@ struct display_t {
std::vector<struct wl_egl_window *> os_egl_windows;
};
+static GHOST_WindowManager *window_manager = nullptr;
+
static void display_destroy(display_t *d)
{
if (d->data_device_manager) {
@@ -188,6 +192,9 @@ static void display_destroy(display_t *d)
if (input->cursor.surface) {
wl_surface_destroy(input->cursor.surface);
}
+ if (input->cursor.theme) {
+ wl_cursor_theme_destroy(input->cursor.theme);
+ }
if (input->pointer) {
wl_pointer_destroy(input->pointer);
}
@@ -210,10 +217,6 @@ static void display_destroy(display_t *d)
delete input;
}
- if (d->cursor_theme) {
- wl_cursor_theme_destroy(d->cursor_theme);
- }
-
if (d->shm) {
wl_shm_destroy(d->shm);
}
@@ -238,6 +241,10 @@ static void display_destroy(display_t *d)
wl_compositor_destroy(d->compositor);
}
+ if (d->xdg_decoration_manager) {
+ zxdg_decoration_manager_v1_destroy(d->xdg_decoration_manager);
+ }
+
if (d->xdg_shell) {
xdg_wm_base_destroy(d->xdg_shell);
}
@@ -478,7 +485,9 @@ static void dnd_events(const input_t *const input, const GHOST_TEventType event)
static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive)
{
int pipefd[2];
- pipe(pipefd);
+ if (pipe(pipefd) != 0) {
+ return {};
+ }
wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]);
close(pipefd[1]);
@@ -513,7 +522,9 @@ static void data_source_send(void *data,
int32_t fd)
{
const char *const buffer = static_cast<char *>(data);
- write(fd, buffer, strlen(buffer) + 1);
+ if (write(fd, buffer, strlen(buffer) + 1) < 0) {
+ GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl);
+ }
close(fd);
}
@@ -789,13 +800,80 @@ static void cursor_buffer_release(void *data, struct wl_buffer *wl_buffer)
cursor_t *cursor = static_cast<cursor_t *>(data);
wl_buffer_destroy(wl_buffer);
- cursor->buffer = nullptr;
+
+ if (wl_buffer == cursor->buffer) {
+ /* the mapped buffer was from a custom cursor */
+ cursor->buffer = nullptr;
+ }
}
const struct wl_buffer_listener cursor_buffer_listener = {
cursor_buffer_release,
};
+static GHOST_IWindow *get_window(struct wl_surface *surface)
+{
+ if (!surface) {
+ return nullptr;
+ }
+
+ for (GHOST_IWindow *win : window_manager->getWindows()) {
+ if (surface == static_cast<const GHOST_WindowWayland *>(win)->surface()) {
+ return win;
+ }
+ }
+ return nullptr;
+}
+
+static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm)
+{
+ int scale = 0;
+ for (const output_t *output : cursor.outputs) {
+ if (output->scale > scale)
+ scale = output->scale;
+ }
+
+ if (scale > 0 && cursor.scale != scale) {
+ cursor.scale = scale;
+ wl_surface_set_buffer_scale(cursor.surface, scale);
+ wl_cursor_theme_destroy(cursor.theme);
+ cursor.theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm);
+ return true;
+ }
+ return false;
+}
+
+static void cursor_surface_enter(void *data,
+ struct wl_surface * /*wl_surface*/,
+ struct wl_output *output)
+{
+ input_t *input = static_cast<input_t *>(data);
+ for (const output_t *reg_output : input->system->outputs()) {
+ if (reg_output->output == output) {
+ input->cursor.outputs.insert(reg_output);
+ }
+ }
+ update_cursor_scale(input->cursor, input->system->shm());
+}
+
+static void cursor_surface_leave(void *data,
+ struct wl_surface * /*wl_surface*/,
+ struct wl_output *output)
+{
+ input_t *input = static_cast<input_t *>(data);
+ for (const output_t *reg_output : input->system->outputs()) {
+ if (reg_output->output == output) {
+ input->cursor.outputs.erase(reg_output);
+ }
+ }
+ update_cursor_scale(input->cursor, input->system->shm());
+}
+
+struct wl_surface_listener cursor_surface_listener = {
+ cursor_surface_enter,
+ cursor_surface_leave,
+};
+
static void pointer_enter(void *data,
struct wl_pointer * /*wl_pointer*/,
uint32_t serial,
@@ -803,22 +881,28 @@ static void pointer_enter(void *data,
wl_fixed_t surface_x,
wl_fixed_t surface_y)
{
- if (!surface) {
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(surface));
+
+ if (!win) {
return;
}
+
+ win->activate();
+
input_t *input = static_cast<input_t *>(data);
input->pointer_serial = serial;
- input->x = wl_fixed_to_int(surface_x);
- input->y = wl_fixed_to_int(surface_y);
+ input->x = win->scale() * wl_fixed_to_int(surface_x);
+ input->y = win->scale() * wl_fixed_to_int(surface_y);
input->focus_pointer = surface;
- input->system->pushEvent(
- new GHOST_EventCursor(input->system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface)),
- input->x,
- input->y,
- GHOST_TABLET_DATA_NONE));
+ win->setCursorShape(win->getCursorShape());
+
+ input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ static_cast<GHOST_WindowWayland *>(win),
+ input->x,
+ input->y,
+ GHOST_TABLET_DATA_NONE));
}
static void pointer_leave(void *data,
@@ -826,9 +910,14 @@ static void pointer_leave(void *data,
uint32_t /*serial*/,
struct wl_surface *surface)
{
- if (surface != nullptr) {
- static_cast<input_t *>(data)->focus_pointer = nullptr;
+ GHOST_IWindow *win = get_window(surface);
+
+ if (!win) {
+ return;
}
+
+ static_cast<input_t *>(data)->focus_pointer = nullptr;
+ static_cast<GHOST_WindowWayland *>(win)->deactivate();
}
static void pointer_motion(void *data,
@@ -839,21 +928,20 @@ static void pointer_motion(void *data,
{
input_t *input = static_cast<input_t *>(data);
- GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
- wl_surface_get_user_data(input->focus_pointer));
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_pointer));
if (!win) {
return;
}
- input->x = wl_fixed_to_int(surface_x);
- input->y = wl_fixed_to_int(surface_y);
+ input->x = win->scale() * wl_fixed_to_int(surface_x);
+ input->y = win->scale() * wl_fixed_to_int(surface_y);
input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
GHOST_kEventCursorMove,
win,
- wl_fixed_to_int(surface_x),
- wl_fixed_to_int(surface_y),
+ input->x,
+ input->y,
GHOST_TABLET_DATA_NONE));
}
@@ -864,6 +952,14 @@ static void pointer_button(void *data,
uint32_t button,
uint32_t state)
{
+ input_t *input = static_cast<input_t *>(data);
+
+ GHOST_IWindow *win = get_window(input->focus_pointer);
+
+ if (!win) {
+ return;
+ }
+
GHOST_TEventType etype = GHOST_kEventUnknown;
switch (state) {
case WL_POINTER_BUTTON_STATE_RELEASED:
@@ -887,9 +983,6 @@ static void pointer_button(void *data,
break;
}
- input_t *input = static_cast<input_t *>(data);
- GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
- wl_surface_get_user_data(input->focus_pointer));
input->data_source->source_serial = serial;
input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
input->system->pushEvent(new GHOST_EventButton(
@@ -902,12 +995,18 @@ static void pointer_axis(void *data,
uint32_t axis,
wl_fixed_t value)
{
+ input_t *input = static_cast<input_t *>(data);
+
+ GHOST_IWindow *win = get_window(input->focus_pointer);
+
+ if (!win) {
+ return;
+ }
+
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) {
return;
}
- input_t *input = static_cast<input_t *>(data);
- GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
- wl_surface_get_user_data(input->focus_pointer));
+
input->system->pushEvent(
new GHOST_EventWheel(input->system->getMilliSeconds(), win, std::signbit(value) ? +1 : -1));
}
@@ -1137,7 +1236,12 @@ static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capa
input->cursor.visible = true;
input->cursor.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;
+ }
wl_pointer_add_listener(input->pointer, &pointer_listener, data);
+ wl_surface_add_listener(input->cursor.surface, &cursor_surface_listener, data);
}
if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
@@ -1160,8 +1264,8 @@ static void output_geometry(void *data,
struct wl_output * /*wl_output*/,
int32_t /*x*/,
int32_t /*y*/,
- int32_t /*physical_width*/,
- int32_t /*physical_height*/,
+ int32_t physical_width,
+ int32_t physical_height,
int32_t /*subpixel*/,
const char *make,
const char *model,
@@ -1171,6 +1275,8 @@ static void output_geometry(void *data,
output->transform = transform;
output->make = std::string(make);
output->model = std::string(model);
+ output->width_mm = physical_width;
+ output->height_mm = physical_height;
}
static void output_mode(void *data,
@@ -1181,8 +1287,8 @@ static void output_mode(void *data,
int32_t /*refresh*/)
{
output_t *output = static_cast<output_t *>(data);
- output->width = width;
- output->height = height;
+ output->width_pxl = width;
+ output->height_pxl = height;
}
/**
@@ -1227,13 +1333,17 @@ static void global_add(void *data,
struct display_t *display = static_cast<struct display_t *>(data);
if (!strcmp(interface, wl_compositor_interface.name)) {
display->compositor = static_cast<wl_compositor *>(
- wl_registry_bind(wl_registry, name, &wl_compositor_interface, 1));
+ wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3));
}
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));
xdg_wm_base_add_listener(display->xdg_shell, &shell_listener, nullptr);
}
+ else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name)) {
+ display->xdg_decoration_manager = static_cast<zxdg_decoration_manager_v1 *>(
+ wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1));
+ }
else if (!strcmp(interface, wl_output_interface.name)) {
output_t *output = new output_t;
output->scale = 1;
@@ -1335,16 +1445,6 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t)
wl_data_device_add_listener(input->data_device, &data_device_listener, input);
}
}
-
- const char *theme = std::getenv("XCURSOR_THEME");
- const char *size = std::getenv("XCURSOR_SIZE");
- const int sizei = size ? std::stoi(size) : default_cursor_size;
-
- d->cursor_theme = wl_cursor_theme_load(theme, sizei, d->shm);
- if (!d->cursor_theme) {
- display_destroy(d);
- throw std::runtime_error("Wayland: unable to access cursor themes!");
- }
}
GHOST_SystemWayland::~GHOST_SystemWayland()
@@ -1471,8 +1571,8 @@ void GHOST_SystemWayland::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TU
{
if (getNumDisplays() > 0) {
/* We assume first output as main. */
- width = uint32_t(d->outputs[0]->width);
- height = uint32_t(d->outputs[0]->height);
+ width = uint32_t(d->outputs[0]->width_pxl) / d->outputs[0]->scale;
+ height = uint32_t(d->outputs[0]->height_pxl) / d->outputs[0]->scale;
}
}
@@ -1481,7 +1581,7 @@ void GHOST_SystemWayland::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUn
getMainDisplayDimensions(width, height);
}
-GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings glSettings)
+GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*glSettings*/)
{
/* Create new off-screen window. */
wl_surface *os_surface = wl_compositor_create_surface(compositor());
@@ -1530,6 +1630,11 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
const bool is_dialog,
const GHOST_IWindow *parentWindow)
{
+ /* globally store pointer to window manager */
+ if (!window_manager) {
+ window_manager = getWindowManager();
+ }
+
GHOST_WindowWayland *window = new GHOST_WindowWayland(
this,
title,
@@ -1574,6 +1679,21 @@ xdg_wm_base *GHOST_SystemWayland::shell()
return d->xdg_shell;
}
+zxdg_decoration_manager_v1 *GHOST_SystemWayland::decoration_manager()
+{
+ return d->xdg_decoration_manager;
+}
+
+const std::vector<output_t *> &GHOST_SystemWayland::outputs() const
+{
+ return d->outputs;
+}
+
+wl_shm *GHOST_SystemWayland::shm() const
+{
+ return d->shm;
+}
+
void GHOST_SystemWayland::setSelection(const std::string &selection)
{
this->selection = selection;
@@ -1581,23 +1701,20 @@ void GHOST_SystemWayland::setSelection(const std::string &selection)
static void set_cursor_buffer(input_t *input, wl_buffer *buffer)
{
- input->cursor.visible = (buffer != nullptr);
+ cursor_t *c = &input->cursor;
- wl_surface_attach(input->cursor.surface, buffer, 0, 0);
- wl_surface_commit(input->cursor.surface);
+ c->visible = (buffer != nullptr);
- if (input->cursor.visible) {
- wl_surface_damage(input->cursor.surface,
- 0,
- 0,
- int32_t(input->cursor.image.width),
- int32_t(input->cursor.image.height));
- wl_pointer_set_cursor(input->pointer,
- input->pointer_serial,
- input->cursor.surface,
- int32_t(input->cursor.image.hotspot_x),
- int32_t(input->cursor.image.hotspot_y));
- }
+ wl_surface_attach(c->surface, buffer, 0, 0);
+
+ wl_surface_damage(c->surface, 0, 0, int32_t(c->image.width), int32_t(c->image.height));
+ wl_pointer_set_cursor(input->pointer,
+ input->pointer_serial,
+ c->visible ? c->surface : nullptr,
+ int32_t(c->image.hotspot_x) / c->scale,
+ int32_t(c->image.hotspot_y) / c->scale);
+
+ wl_surface_commit(c->surface);
}
GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
@@ -1608,7 +1725,15 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
const std::string cursor_name = cursors.count(shape) ? cursors.at(shape) :
cursors.at(GHOST_kStandardCursorDefault);
- wl_cursor *cursor = wl_cursor_theme_get_cursor(d->cursor_theme, cursor_name.c_str());
+ input_t *input = d->inputs[0];
+ cursor_t *c = &input->cursor;
+
+ if (!c->theme) {
+ /* The cursor surface hasn't entered an output yet. Initialize theme with scale 1. */
+ c->theme = wl_cursor_theme_load(c->theme_name.c_str(), c->size, d->inputs[0]->system->shm());
+ }
+
+ wl_cursor *cursor = wl_cursor_theme_get_cursor(c->theme, cursor_name.c_str());
if (!cursor) {
GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl);
@@ -1620,11 +1745,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
if (!buffer) {
return GHOST_kFailure;
}
- cursor_t *c = &d->inputs[0]->cursor;
+
c->buffer = buffer;
c->image = *image;
- set_cursor_buffer(d->inputs[0], buffer);
+ set_cursor_buffer(input, buffer);
return GHOST_kSuccess;
}
@@ -1735,6 +1860,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible)
GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode,
wl_surface *surface)
{
+ /* ignore, if the required protocols are not supported */
+ if (!d->relative_pointer_manager || !d->pointer_constraints) {
+ return GHOST_kFailure;
+ }
+
if (d->inputs.empty()) {
return GHOST_kFailure;
}
@@ -1754,6 +1884,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo
break;
case GHOST_kGrabNormal:
+ break;
case GHOST_kGrabWrap:
input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
d->relative_pointer_manager, input->pointer);
diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h
index 10b9ef6bd62..3a08a0d3b16 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.h
+++ b/intern/ghost/intern/GHOST_SystemWayland.h
@@ -26,6 +26,7 @@
#include "GHOST_WindowWayland.h"
#include <wayland-client.h>
+#include <xdg-decoration-client-protocol.h>
#include <xdg-shell-client-protocol.h>
#include <string>
@@ -34,6 +35,16 @@ class GHOST_WindowWayland;
struct display_t;
+struct output_t {
+ struct wl_output *output;
+ int32_t width_pxl, height_pxl; // dimensions in pixel
+ int32_t width_mm, height_mm; // dimensions in millimeter
+ int transform;
+ int scale;
+ std::string make;
+ std::string model;
+};
+
class GHOST_SystemWayland : public GHOST_System {
public:
GHOST_SystemWayland();
@@ -84,6 +95,12 @@ class GHOST_SystemWayland : public GHOST_System {
xdg_wm_base *shell();
+ zxdg_decoration_manager_v1 *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);
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 430e8216ae3..c3a243cc22c 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -32,6 +32,7 @@
#include <commctrl.h>
#include <psapi.h>
#include <shellapi.h>
+#include <shellscalingapi.h>
#include <shlobj.h>
#include <tlhelp32.h>
#include <windowsx.h>
@@ -97,41 +98,6 @@
# define VK_GR_LESS 0xE2
#endif // VK_GR_LESS
-#ifndef VK_MEDIA_NEXT_TRACK
-# define VK_MEDIA_NEXT_TRACK 0xB0
-#endif // VK_MEDIA_NEXT_TRACK
-#ifndef VK_MEDIA_PREV_TRACK
-# define VK_MEDIA_PREV_TRACK 0xB1
-#endif // VK_MEDIA_PREV_TRACK
-#ifndef VK_MEDIA_STOP
-# define VK_MEDIA_STOP 0xB2
-#endif // VK_MEDIA_STOP
-#ifndef VK_MEDIA_PLAY_PAUSE
-# define VK_MEDIA_PLAY_PAUSE 0xB3
-#endif // VK_MEDIA_PLAY_PAUSE
-
-// Window message newer than Windows 7
-#ifndef WM_DPICHANGED
-# define WM_DPICHANGED 0x02E0
-#endif // WM_DPICHANGED
-
-// WM_POINTER API messages minimum Windows 7
-#ifndef WM_POINTERENTER
-# define WM_POINTERENTER 0x0249
-#endif // WM_POINTERENTER
-#ifndef WM_POINTERDOWN
-# define WM_POINTERDOWN 0x0246
-#endif // WM_POINTERDOWN
-#ifndef WM_POINTERUPDATE
-# define WM_POINTERUPDATE 0x0245
-#endif // WM_POINTERUPDATE
-#ifndef WM_POINTERUP
-# define WM_POINTERUP 0x0247
-#endif // WM_POINTERUP
-#ifndef WM_POINTERLEAVE
-# define WM_POINTERLEAVE 0x024A
-#endif // WM_POINTERLEAVE
-
/* Workaround for some laptop touchpads, 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
@@ -173,24 +139,6 @@ static void initRawInput()
#undef DEVICE_COUNT
}
-#ifndef DPI_ENUMS_DECLARED
-typedef enum PROCESS_DPI_AWARENESS {
- PROCESS_DPI_UNAWARE = 0,
- PROCESS_SYSTEM_DPI_AWARE = 1,
- PROCESS_PER_MONITOR_DPI_AWARE = 2
-} PROCESS_DPI_AWARENESS;
-
-typedef enum MONITOR_DPI_TYPE {
- MDT_EFFECTIVE_DPI = 0,
- MDT_ANGULAR_DPI = 1,
- MDT_RAW_DPI = 2,
- MDT_DEFAULT = MDT_EFFECTIVE_DPI
-} MONITOR_DPI_TYPE;
-
-# define USER_DEFAULT_SCREEN_DPI 96
-
-# define DPI_ENUMS_DECLARED
-#endif
typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS);
typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND);
@@ -205,15 +153,7 @@ GHOST_SystemWin32::GHOST_SystemWin32()
// 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.
- HMODULE m_shcore = ::LoadLibrary("Shcore.dll");
- if (m_shcore) {
- GHOST_WIN32_SetProcessDpiAwareness fpSetProcessDpiAwareness =
- (GHOST_WIN32_SetProcessDpiAwareness)::GetProcAddress(m_shcore, "SetProcessDpiAwareness");
-
- if (fpSetProcessDpiAwareness) {
- fpSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
- }
- }
+ 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
@@ -581,14 +521,7 @@ GHOST_TSuccess GHOST_SystemWin32::init()
InitCommonControls();
/* Disable scaling on high DPI displays on Vista */
- HMODULE
- user32 = ::LoadLibraryA("user32.dll");
- typedef BOOL(WINAPI * LPFNSETPROCESSDPIAWARE)();
- LPFNSETPROCESSDPIAWARE SetProcessDPIAware = (LPFNSETPROCESSDPIAWARE)GetProcAddress(
- user32, "SetProcessDPIAware");
- if (SetProcessDPIAware)
- SetProcessDPIAware();
- FreeLibrary(user32);
+ SetProcessDPIAware();
initRawInput();
m_lfstart = ::GetTickCount();
@@ -1442,12 +1375,23 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
*/
break;
case WM_SYSCOMMAND:
- /* The WM_SYSCHAR message is sent to the window when system commands such as
+ /* 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.
+ *
+ * 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 bitwise AND operator.
*/
- if (wParam == SC_KEYMENU) {
- eventHandled = true;
+ switch (wParam & 0xFFF0) {
+ case SC_KEYMENU:
+ eventHandled = true;
+ break;
+ case SC_RESTORE:
+ ::ShowWindow(hwnd, SW_RESTORE);
+ window->setState(window->getState());
+ eventHandled = true;
+ break;
}
break;
////////////////////////////////////////////////////////////////////////
@@ -1518,14 +1462,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* since DefWindowProc propagates it up the parent chain
* until it finds a window that processes it.
*/
-
- /* Get the window under the mouse and send event to its queue. */
- POINT mouse_pos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
- HWND mouse_hwnd = ChildWindowFromPoint(HWND_DESKTOP, mouse_pos);
- GHOST_WindowWin32 *mouse_window = (GHOST_WindowWin32 *)::GetWindowLongPtr(mouse_hwnd,
- GWLP_USERDATA);
-
- processWheelEvent(mouse_window ? mouse_window : window, wParam, lParam);
+ processWheelEvent(window, wParam, lParam);
eventHandled = true;
#ifdef BROKEN_PEEK_TOUCHPAD
PostMessage(hwnd, WM_USER, 0, 0);
diff --git a/intern/ghost/intern/GHOST_Util.h b/intern/ghost/intern/GHOST_Util.h
new file mode 100644
index 00000000000..8be5e373b28
--- /dev/null
+++ b/intern/ghost/intern/GHOST_Util.h
@@ -0,0 +1,45 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#pragma once
+
+#include <functional>
+
+/**
+ * RAII wrapper for typical C `void *` custom data.
+ * Used for exception safe custom-data handling during constructor calls.
+ */
+struct GHOST_C_CustomDataWrapper {
+ using FreeFn = std::function<void(void *)>;
+
+ void *custom_data_;
+ FreeFn free_fn_;
+
+ GHOST_C_CustomDataWrapper(void *custom_data, FreeFn free_fn)
+ : custom_data_(custom_data), free_fn_(free_fn)
+ {
+ }
+ ~GHOST_C_CustomDataWrapper()
+ {
+ if (free_fn_ != nullptr && custom_data_ != nullptr) {
+ free_fn_(custom_data_);
+ }
+ }
+};
diff --git a/intern/ghost/intern/GHOST_WaylandCursorSettings.h b/intern/ghost/intern/GHOST_WaylandCursorSettings.h
new file mode 100644
index 00000000000..c7d2d82ff84
--- /dev/null
+++ b/intern/ghost/intern/GHOST_WaylandCursorSettings.h
@@ -0,0 +1,130 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#pragma once
+#include <dbus/dbus.h>
+#include <string>
+
+static DBusMessage *get_setting_sync(DBusConnection *const connection,
+ const char *key,
+ const char *value)
+{
+ DBusError error;
+ dbus_bool_t success;
+ DBusMessage *message;
+ DBusMessage *reply;
+
+ dbus_error_init(&error);
+
+ message = dbus_message_new_method_call("org.freedesktop.portal.Desktop",
+ "/org/freedesktop/portal/desktop",
+ "org.freedesktop.portal.Settings",
+ "Read");
+
+ success = dbus_message_append_args(
+ message, DBUS_TYPE_STRING, &key, DBUS_TYPE_STRING, &value, DBUS_TYPE_INVALID);
+
+ if (!success) {
+ return NULL;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(
+ connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error);
+
+ dbus_message_unref(message);
+
+ if (dbus_error_is_set(&error)) {
+ return NULL;
+ }
+
+ return reply;
+}
+
+static bool parse_type(DBusMessage *const reply, const int type, void *value)
+{
+ DBusMessageIter iter[3];
+
+ dbus_message_iter_init(reply, &iter[0]);
+ if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) {
+ return false;
+ }
+
+ dbus_message_iter_recurse(&iter[0], &iter[1]);
+ if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) {
+ return false;
+ }
+
+ dbus_message_iter_recurse(&iter[1], &iter[2]);
+ if (dbus_message_iter_get_arg_type(&iter[2]) != type) {
+ return false;
+ }
+
+ dbus_message_iter_get_basic(&iter[2], value);
+
+ return true;
+}
+
+static bool get_cursor_settings(std::string &theme, int &size)
+{
+ static const char name[] = "org.gnome.desktop.interface";
+ static const char key_theme[] = "cursor-theme";
+ static const char key_size[] = "cursor-size";
+
+ DBusError error;
+ DBusConnection *connection;
+ DBusMessage *reply;
+ const char *value_theme = NULL;
+
+ dbus_error_init(&error);
+
+ connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
+
+ if (dbus_error_is_set(&error)) {
+ return false;
+ }
+
+ reply = get_setting_sync(connection, name, key_theme);
+ if (!reply) {
+ return false;
+ }
+
+ if (!parse_type(reply, DBUS_TYPE_STRING, &value_theme)) {
+ dbus_message_unref(reply);
+ return false;
+ }
+
+ theme = std::string(value_theme);
+
+ dbus_message_unref(reply);
+
+ reply = get_setting_sync(connection, name, key_size);
+ if (!reply) {
+ return false;
+ }
+
+ if (!parse_type(reply, DBUS_TYPE_INT32, &size)) {
+ dbus_message_unref(reply);
+ return false;
+ }
+
+ dbus_message_unref(reply);
+
+ return true;
+}
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm
index 6a5a088d163..1776b0d5ce0 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.mm
+++ b/intern/ghost/intern/GHOST_WindowCocoa.mm
@@ -1131,7 +1131,8 @@ GHOST_TSuccess GHOST_WindowCocoa::hasCursorShape(GHOST_TStandardCursor shape)
return success;
}
-/** Reverse the bits in a GHOST_TUns8
+/* Reverse the bits in a GHOST_TUns8 */
+#if 0
static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
{
ch= ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
@@ -1139,7 +1140,7 @@ static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
ch= ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
return ch;
}
-*/
+#endif
/** Reverse the bits in a GHOST_TUns16 */
static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp
index 1e1d0814d3a..cbac2d6eaa1 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.cpp
+++ b/intern/ghost/intern/GHOST_WindowWayland.cpp
@@ -29,11 +29,19 @@
#include <wayland-egl.h>
+static constexpr size_t base_dpi = 96;
+
struct window_t {
GHOST_WindowWayland *w;
wl_surface *surface;
+ // outputs on which the window is currently shown on
+ std::unordered_set<const output_t *> outputs;
+ GHOST_TUns16 dpi = 0;
+ int scale = 1;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
+ struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr;
+ enum zxdg_toplevel_decoration_v1_mode decoration_mode;
wl_egl_window *egl_window;
int32_t pending_width, pending_height;
bool is_maximised;
@@ -93,17 +101,30 @@ static const xdg_toplevel_listener toplevel_listener = {
toplevel_close,
};
+static void toplevel_decoration_configure(
+ void *data,
+ struct zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/,
+ uint32_t mode)
+{
+ static_cast<window_t *>(data)->decoration_mode = zxdg_toplevel_decoration_v1_mode(mode);
+}
+
+static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = {
+ toplevel_decoration_configure,
+};
+
static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial)
{
window_t *win = static_cast<window_t *>(data);
- int w, h;
- wl_egl_window_get_attached_size(win->egl_window, &w, &h);
- if (win->pending_width != 0 && win->pending_height != 0 && win->pending_width != w &&
- win->pending_height != h) {
- win->width = win->pending_width;
- win->height = win->pending_height;
- wl_egl_window_resize(win->egl_window, win->pending_width, win->pending_height, 0, 0);
+ if (win->xdg_surface != xdg_surface) {
+ return;
+ }
+
+ if (win->pending_width != 0 && win->pending_height != 0) {
+ win->width = win->scale * win->pending_width;
+ win->height = win->scale * win->pending_height;
+ wl_egl_window_resize(win->egl_window, win->width, win->height, 0, 0);
win->pending_width = 0;
win->pending_height = 0;
win->w->notify_size();
@@ -123,6 +144,52 @@ static const xdg_surface_listener surface_listener = {
surface_configure,
};
+static bool update_scale(GHOST_WindowWayland *window)
+{
+ int scale = 0;
+ for (const output_t *output : window->outputs_active()) {
+ if (output->scale > scale)
+ scale = output->scale;
+ }
+
+ if (scale > 0 && window->scale() != scale) {
+ window->scale() = scale;
+ // using the real DPI will cause wrong scaling of the UI
+ // use a multiplier for the default DPI as workaround
+ window->dpi() = scale * base_dpi;
+ wl_surface_set_buffer_scale(window->surface(), scale);
+ return true;
+ }
+ return false;
+}
+
+static void surface_enter(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output)
+{
+ GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
+ for (const output_t *reg_output : w->outputs()) {
+ if (reg_output->output == output) {
+ w->outputs_active().insert(reg_output);
+ }
+ }
+ update_scale(w);
+}
+
+static void surface_leave(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output)
+{
+ GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
+ for (const output_t *reg_output : w->outputs()) {
+ if (reg_output->output == output) {
+ w->outputs_active().erase(reg_output);
+ }
+ }
+ update_scale(w);
+}
+
+struct wl_surface_listener wl_surface_listener = {
+ surface_enter,
+ surface_leave,
+};
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -161,17 +228,28 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
/* Window surfaces. */
w->surface = wl_compositor_create_surface(m_system->compositor());
+ wl_surface_add_listener(w->surface, &wl_surface_listener, this);
+
w->egl_window = wl_egl_window_create(w->surface, int(width), int(height));
w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface);
w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface);
+ if (m_system->decoration_manager()) {
+ w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
+ m_system->decoration_manager(), w->xdg_toplevel);
+ zxdg_toplevel_decoration_v1_add_listener(
+ w->xdg_toplevel_decoration, &toplevel_decoration_v1_listener, w);
+ zxdg_toplevel_decoration_v1_set_mode(w->xdg_toplevel_decoration,
+ ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+ }
+
wl_surface_set_user_data(w->surface, this);
xdg_surface_add_listener(w->xdg_surface, &surface_listener, w);
xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w);
- if (parentWindow) {
+ if (parentWindow && is_dialog) {
xdg_toplevel_set_parent(
w->xdg_toplevel, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->xdg_toplevel);
}
@@ -192,6 +270,9 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
if (setDrawingContextType(type) == GHOST_kFailure) {
GHOST_PRINT("Failed to create EGL context" << std::endl);
}
+
+ /* set swap interval to 0 to prevent blocking */
+ setSwapInterval(0);
}
GHOST_TSuccess GHOST_WindowWayland::close()
@@ -226,6 +307,31 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size()
new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this));
}
+wl_surface *GHOST_WindowWayland::surface() const
+{
+ return w->surface;
+}
+
+const std::vector<output_t *> &GHOST_WindowWayland::outputs() const
+{
+ return m_system->outputs();
+}
+
+std::unordered_set<const output_t *> &GHOST_WindowWayland::outputs_active()
+{
+ return w->outputs;
+}
+
+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, w->surface);
@@ -310,6 +416,9 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
releaseNativeHandles();
wl_egl_window_destroy(w->egl_window);
+ 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);
wl_surface_destroy(w->surface);
@@ -317,6 +426,11 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
delete w;
}
+GHOST_TUns16 GHOST_WindowWayland::getDPIHint()
+{
+ return w->dpi;
+}
+
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorVisibility(bool visible)
{
return m_system->setCursorVisibility(visible);
diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h
index b62b5c24d60..dbddc7c469e 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.h
+++ b/intern/ghost/intern/GHOST_WindowWayland.h
@@ -24,9 +24,14 @@
#include "GHOST_Window.h"
+#include <unordered_set>
+#include <vector>
+
class GHOST_SystemWayland;
struct window_t;
+struct wl_surface;
+struct output_t;
class GHOST_WindowWayland : public GHOST_Window {
public:
@@ -47,6 +52,8 @@ class GHOST_WindowWayland : public GHOST_Window {
~GHOST_WindowWayland() override;
+ GHOST_TUns16 getDPIHint() override;
+
GHOST_TSuccess close();
GHOST_TSuccess activate();
@@ -55,6 +62,16 @@ class GHOST_WindowWayland : public GHOST_Window {
GHOST_TSuccess notify_size();
+ wl_surface *surface() const;
+
+ const std::vector<output_t *> &outputs() const;
+
+ std::unordered_set<const output_t *> &outputs_active();
+
+ uint16_t &dpi();
+
+ int &scale();
+
protected:
GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override;
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index 70901954df2..eeafe333633 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -84,9 +84,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_wantAlphaBackground(alphaBackground),
m_normal_state(GHOST_kWindowStateNormal),
m_user32(NULL),
- m_fpGetPointerInfoHistory(NULL),
- m_fpGetPointerPenInfoHistory(NULL),
- m_fpGetPointerTouchInfoHistory(NULL),
m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP),
m_debug_context(is_debug)
{
@@ -121,22 +118,30 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
monitor.dwFlags = 0;
GetMonitorInfo(MonitorFromRect(&win_rect, MONITOR_DEFAULTTONEAREST), &monitor);
- /* Adjust our requested size to allow for caption and borders and constrain to monitor. */
- AdjustWindowRectEx(&win_rect, WS_CAPTION, FALSE, 0);
+ /* Constrain requested size and position to fit within this monitor. */
width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect.right - win_rect.left);
- left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width);
height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect.bottom - win_rect.top);
- top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height);
-
- m_hWnd = ::CreateWindowExW(extended_style, // window extended style
- s_windowClassName, // pointer to registered class name
- title_16, // pointer to window name
- style, // window style
- left, // horizontal position of window
- top, // vertical position of window
- width, // window width
- height, // window height
- m_parentWindowHwnd, // handle to parent or owner window
+ win_rect.left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width);
+ win_rect.right = win_rect.left + width;
+ win_rect.top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height);
+ win_rect.bottom = win_rect.top + height;
+
+ /* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be
+ * correctly outside of monitor bounds. Note: You cannot specify WS_OVERLAPPED when calling. */
+ AdjustWindowRectEx(&win_rect, style & ~WS_OVERLAPPED, FALSE, extended_style);
+
+ /* But never allow a top position that can hide part of the title bar. */
+ win_rect.top = max(monitor.rcWork.top, win_rect.top);
+
+ 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
@@ -145,19 +150,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_user32 = ::LoadLibrary("user32.dll");
if (m_hWnd) {
- if (m_user32) {
- // Touch enabled screens with pen support by default have gestures
- // enabled, which results in a delay between the pointer down event
- // and the first move when using the stylus. RegisterTouchWindow
- // disables the new gesture architecture enabling the events to be
- // sent immediately to the application rather than being absorbed by
- // the gesture API.
- GHOST_WIN32_RegisterTouchWindow pRegisterTouchWindow = (GHOST_WIN32_RegisterTouchWindow)
- GetProcAddress(m_user32, "RegisterTouchWindow");
- if (pRegisterTouchWindow) {
- pRegisterTouchWindow(m_hWnd, 0);
- }
- }
+ RegisterTouchWindow(m_hWnd, 0);
// Register this window as a droptarget. Requires m_hWnd to be valid.
// Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
@@ -224,16 +217,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
}
}
- // Initialize Windows Ink
- if (m_user32) {
- m_fpGetPointerInfoHistory = (GHOST_WIN32_GetPointerInfoHistory)::GetProcAddress(
- m_user32, "GetPointerInfoHistory");
- m_fpGetPointerPenInfoHistory = (GHOST_WIN32_GetPointerPenInfoHistory)::GetProcAddress(
- m_user32, "GetPointerPenInfoHistory");
- m_fpGetPointerTouchInfoHistory = (GHOST_WIN32_GetPointerTouchInfoHistory)::GetProcAddress(
- m_user32, "GetPointerTouchInfoHistory");
- }
-
// Initialize Wintab
m_wintab.handle = ::LoadLibrary("Wintab32.dll");
if (m_wintab.handle && m_system->getTabletAPI() != GHOST_kTabletNative) {
@@ -318,9 +301,6 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
if (m_user32) {
FreeLibrary(m_user32);
m_user32 = NULL;
- m_fpGetPointerInfoHistory = NULL;
- m_fpGetPointerPenInfoHistory = NULL;
- m_fpGetPointerTouchInfoHistory = NULL;
}
if (m_customCursor) {
@@ -521,7 +501,7 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
switch (state) {
case GHOST_kWindowStateMinimized:
- wp.showCmd = SW_SHOWMINIMIZED;
+ wp.showCmd = SW_MINIMIZE;
break;
case GHOST_kWindowStateMaximized:
wp.showCmd = SW_SHOWMAXIMIZED;
@@ -533,11 +513,18 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
wp.showCmd = SW_SHOWMAXIMIZED;
wp.ptMaxPosition.x = 0;
wp.ptMaxPosition.y = 0;
- style &= ~WS_CAPTION;
+ style &= ~(WS_CAPTION | WS_MAXIMIZE);
break;
case GHOST_kWindowStateNormal:
default:
- wp.showCmd = SW_SHOWNORMAL;
+ if (curstate == GHOST_kWindowStateFullScreen &&
+ m_normal_state == GHOST_kWindowStateMaximized) {
+ wp.showCmd = SW_SHOWMAXIMIZED;
+ m_normal_state = GHOST_kWindowStateNormal;
+ }
+ else {
+ wp.showCmd = SW_SHOWNORMAL;
+ }
break;
}
::SetWindowLongPtr(m_hWnd, GWL_STYLE, style);
@@ -935,15 +922,14 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
GHOST_TUns32 outCount;
- if (!(m_fpGetPointerInfoHistory && m_fpGetPointerInfoHistory(pointerId, &outCount, NULL))) {
+ if (!(GetPointerInfoHistory(pointerId, &outCount, NULL))) {
return GHOST_kFailure;
}
auto pointerPenInfo = std::vector<POINTER_PEN_INFO>(outCount);
outPointerInfo.resize(outCount);
- if (!(m_fpGetPointerPenInfoHistory &&
- m_fpGetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) {
+ if (!(GetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) {
return GHOST_kFailure;
}
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index d86a5c12040..a13bd876667 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -53,177 +53,12 @@ typedef BOOL(API *GHOST_WIN32_WTPacket)(HCTX, UINT, LPVOID);
typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL);
typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL);
-// typedef to user32 functions to disable gestures on windows
-typedef BOOL(API *GHOST_WIN32_RegisterTouchWindow)(HWND hwnd, ULONG ulFlags);
-
// typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions
typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND);
#ifndef USER_DEFAULT_SCREEN_DPI
# define USER_DEFAULT_SCREEN_DPI 96
#endif // USER_DEFAULT_SCREEN_DPI
-// typedefs for user32 functions to allow pointer functions
-enum tagPOINTER_INPUT_TYPE {
- PT_POINTER = 1, // Generic pointer
- PT_TOUCH = 2, // Touch
- PT_PEN = 3, // Pen
- PT_MOUSE = 4, // Mouse
-#if (WINVER >= 0x0603)
- PT_TOUCHPAD = 5, // Touchpad
-#endif /* WINVER >= 0x0603 */
-};
-
-typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
- POINTER_CHANGE_NONE,
- POINTER_CHANGE_FIRSTBUTTON_DOWN,
- POINTER_CHANGE_FIRSTBUTTON_UP,
- POINTER_CHANGE_SECONDBUTTON_DOWN,
- POINTER_CHANGE_SECONDBUTTON_UP,
- POINTER_CHANGE_THIRDBUTTON_DOWN,
- POINTER_CHANGE_THIRDBUTTON_UP,
- POINTER_CHANGE_FOURTHBUTTON_DOWN,
- POINTER_CHANGE_FOURTHBUTTON_UP,
- POINTER_CHANGE_FIFTHBUTTON_DOWN,
- POINTER_CHANGE_FIFTHBUTTON_UP,
-} POINTER_BUTTON_CHANGE_TYPE;
-
-typedef DWORD POINTER_INPUT_TYPE;
-typedef UINT32 POINTER_FLAGS;
-
-#define POINTER_FLAG_NONE 0x00000000
-#define POINTER_FLAG_NEW 0x00000001
-#define POINTER_FLAG_INRANGE 0x00000002
-#define POINTER_FLAG_INCONTACT 0x00000004
-#define POINTER_FLAG_FIRSTBUTTON 0x00000010
-#define POINTER_FLAG_SECONDBUTTON 0x00000020
-#define POINTER_FLAG_THIRDBUTTON 0x00000040
-#define POINTER_FLAG_FOURTHBUTTON 0x00000080
-#define POINTER_FLAG_FIFTHBUTTON 0x00000100
-#define POINTER_FLAG_PRIMARY 0x00002000
-#define POINTER_FLAG_CONFIDENCE 0x000004000
-#define POINTER_FLAG_CANCELED 0x000008000
-#define POINTER_FLAG_DOWN 0x00010000
-#define POINTER_FLAG_UPDATE 0x00020000
-#define POINTER_FLAG_UP 0x00040000
-#define POINTER_FLAG_WHEEL 0x00080000
-#define POINTER_FLAG_HWHEEL 0x00100000
-#define POINTER_FLAG_CAPTURECHANGED 0x00200000
-#define POINTER_FLAG_HASTRANSFORM 0x00400000
-
-typedef struct tagPOINTER_INFO {
- POINTER_INPUT_TYPE pointerType;
- UINT32 pointerId;
- UINT32 frameId;
- POINTER_FLAGS pointerFlags;
- HANDLE sourceDevice;
- HWND hwndTarget;
- POINT ptPixelLocation;
- POINT ptHimetricLocation;
- POINT ptPixelLocationRaw;
- POINT ptHimetricLocationRaw;
- DWORD dwTime;
- UINT32 historyCount;
- INT32 InputData;
- DWORD dwKeyStates;
- UINT64 PerformanceCount;
- POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
-} POINTER_INFO;
-
-typedef UINT32 PEN_FLAGS;
-#define PEN_FLAG_NONE 0x00000000 // Default
-#define PEN_FLAG_BARREL 0x00000001 // The barrel button is pressed
-#define PEN_FLAG_INVERTED 0x00000002 // The pen is inverted
-#define PEN_FLAG_ERASER 0x00000004 // The eraser button is pressed
-
-typedef UINT32 PEN_MASK;
-#define PEN_MASK_NONE 0x00000000 // Default - none of the optional fields are valid
-#define PEN_MASK_PRESSURE 0x00000001 // The pressure field is valid
-#define PEN_MASK_ROTATION 0x00000002 // The rotation field is valid
-#define PEN_MASK_TILT_X 0x00000004 // The tiltX field is valid
-#define PEN_MASK_TILT_Y 0x00000008 // The tiltY field is valid
-
-typedef struct tagPOINTER_PEN_INFO {
- POINTER_INFO pointerInfo;
- PEN_FLAGS penFlags;
- PEN_MASK penMask;
- UINT32 pressure;
- UINT32 rotation;
- INT32 tiltX;
- INT32 tiltY;
-} POINTER_PEN_INFO;
-
-/*
- * Flags that appear in pointer input message parameters
- */
-#define POINTER_MESSAGE_FLAG_NEW 0x00000001 // New pointer
-#define POINTER_MESSAGE_FLAG_INRANGE 0x00000002 // Pointer has not departed
-#define POINTER_MESSAGE_FLAG_INCONTACT 0x00000004 // Pointer is in contact
-#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010 // Primary action
-#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020 // Secondary action
-#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040 // Third button
-#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080 // Fourth button
-#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100 // Fifth button
-#define POINTER_MESSAGE_FLAG_PRIMARY 0x00002000 // Pointer is primary
-#define POINTER_MESSAGE_FLAG_CONFIDENCE \
- 0x00004000 // Pointer is considered unlikely to be accidental
-#define POINTER_MESSAGE_FLAG_CANCELED 0x00008000 // Pointer is departing in an abnormal manner
-
-typedef UINT32 TOUCH_FLAGS;
-#define TOUCH_FLAG_NONE 0x00000000 // Default
-
-typedef UINT32 TOUCH_MASK;
-#define TOUCH_MASK_NONE 0x00000000 // Default - none of the optional fields are valid
-#define TOUCH_MASK_CONTACTAREA 0x00000001 // The rcContact field is valid
-#define TOUCH_MASK_ORIENTATION 0x00000002 // The orientation field is valid
-#define TOUCH_MASK_PRESSURE 0x00000004 // The pressure field is valid
-
-typedef struct tagPOINTER_TOUCH_INFO {
- POINTER_INFO pointerInfo;
- TOUCH_FLAGS touchFlags;
- TOUCH_MASK touchMask;
- RECT rcContact;
- RECT rcContactRaw;
- UINT32 orientation;
- UINT32 pressure;
-} POINTER_TOUCH_INFO;
-
-/*
- * Macros to retrieve information from pointer input message parameters
- */
-#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
-#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD(wParam) & (flag)) == (flag))
-#define IS_POINTER_NEW_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_NEW)
-#define IS_POINTER_INRANGE_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INRANGE)
-#define IS_POINTER_INCONTACT_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INCONTACT)
-#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON)
-#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON)
-#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON)
-#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON)
-#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON)
-#define IS_POINTER_PRIMARY_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_PRIMARY)
-#define HAS_POINTER_CONFIDENCE_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CONFIDENCE)
-#define IS_POINTER_CANCELED_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED)
-
-typedef BOOL(WINAPI *GHOST_WIN32_GetPointerInfoHistory)(UINT32 pointerId,
- UINT32 *entriesCount,
- POINTER_INFO *pointerInfo);
-typedef BOOL(WINAPI *GHOST_WIN32_GetPointerPenInfoHistory)(UINT32 pointerId,
- UINT32 *entriesCount,
- POINTER_PEN_INFO *penInfo);
-typedef BOOL(WINAPI *GHOST_WIN32_GetPointerTouchInfoHistory)(UINT32 pointerId,
- UINT32 *entriesCount,
- POINTER_TOUCH_INFO *touchInfo);
-
struct GHOST_PointerInfoWin32 {
GHOST_TInt32 pointerId;
GHOST_TInt32 isPrimary;
@@ -242,7 +77,7 @@ typedef enum {
} GHOST_MouseCaptureEventWin32;
/**
- * GHOST window on M$ Windows OSs.
+ * GHOST window on MS Windows OSs.
*/
class GHOST_WindowWin32 : public GHOST_Window {
public:
@@ -555,7 +390,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
/* Wintab API */
struct {
- /** WinTab DLL handle. */
+ /** `WinTab.dll` handle. */
HMODULE handle = NULL;
/** API functions */
@@ -574,11 +409,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
GHOST_TWindowState m_normal_state;
- /** user32 dll handle*/
+ /** `user32.dll` handle */
HMODULE m_user32;
- GHOST_WIN32_GetPointerInfoHistory m_fpGetPointerInfoHistory;
- GHOST_WIN32_GetPointerPenInfoHistory m_fpGetPointerPenInfoHistory;
- GHOST_WIN32_GetPointerTouchInfoHistory m_fpGetPointerTouchInfoHistory;
HWND m_parentWindowHwnd;
diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp
new file mode 100644
index 00000000000..172ac40c84f
--- /dev/null
+++ b/intern/ghost/intern/GHOST_XrAction.cpp
@@ -0,0 +1,477 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#include <cassert>
+#include <cstring>
+
+#include "GHOST_Types.h"
+
+#include "GHOST_XrException.h"
+#include "GHOST_Xr_intern.h"
+
+#include "GHOST_XrAction.h"
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrActionSpace
+ *
+ * \{ */
+
+GHOST_XrActionSpace::GHOST_XrActionSpace(XrInstance instance,
+ XrSession session,
+ XrAction action,
+ const GHOST_XrActionSpaceInfo &info,
+ uint32_t subaction_idx)
+{
+ const char *subaction_path = info.subaction_paths[subaction_idx];
+ CHECK_XR(xrStringToPath(instance, subaction_path, &m_subaction_path),
+ (std::string("Failed to get user path \"") + subaction_path + "\".").data());
+
+ XrActionSpaceCreateInfo action_space_info{XR_TYPE_ACTION_SPACE_CREATE_INFO};
+ action_space_info.action = action;
+ action_space_info.subactionPath = m_subaction_path;
+ copy_ghost_pose_to_openxr_pose(info.poses[subaction_idx], action_space_info.poseInActionSpace);
+
+ CHECK_XR(xrCreateActionSpace(session, &action_space_info, &m_space),
+ (std::string("Failed to create space \"") + subaction_path + "\" for action \"" +
+ info.action_name + "\".")
+ .data());
+}
+
+GHOST_XrActionSpace::~GHOST_XrActionSpace()
+{
+ if (m_space != XR_NULL_HANDLE) {
+ CHECK_XR_ASSERT(xrDestroySpace(m_space));
+ }
+}
+
+XrSpace GHOST_XrActionSpace::getSpace() const
+{
+ return m_space;
+}
+
+const XrPath &GHOST_XrActionSpace::getSubactionPath() const
+{
+ return m_subaction_path;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrActionProfile
+ *
+ * \{ */
+
+GHOST_XrActionProfile::GHOST_XrActionProfile(XrInstance instance,
+ XrAction action,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info)
+{
+ CHECK_XR(
+ xrStringToPath(instance, profile_path, &m_profile),
+ (std::string("Failed to get interaction profile path \"") + profile_path + "\".").data());
+
+ /* Create bindings. */
+ XrInteractionProfileSuggestedBinding bindings_info{
+ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
+ bindings_info.interactionProfile = m_profile;
+ bindings_info.countSuggestedBindings = 1;
+
+ for (uint32_t interaction_idx = 0; interaction_idx < info.count_interaction_paths;
+ ++interaction_idx) {
+ const char *interaction_path = info.interaction_paths[interaction_idx];
+ if (m_bindings.find(interaction_path) != m_bindings.end()) {
+ continue;
+ }
+
+ XrActionSuggestedBinding sbinding;
+ sbinding.action = action;
+ CHECK_XR(xrStringToPath(instance, interaction_path, &sbinding.binding),
+ (std::string("Failed to get interaction path \"") + interaction_path + "\".").data());
+ bindings_info.suggestedBindings = &sbinding;
+
+ /* Although the bindings will be re-suggested in GHOST_XrSession::attachActionSets(), it
+ * greatly improves error checking to suggest them here first. */
+ CHECK_XR(xrSuggestInteractionProfileBindings(instance, &bindings_info),
+ (std::string("Failed to create binding for profile \"") + profile_path +
+ "\" and action \"" + info.action_name +
+ "\". Are the profile and action paths correct?")
+ .data());
+
+ m_bindings.insert({interaction_path, sbinding.binding});
+ }
+}
+
+void GHOST_XrActionProfile::getBindings(
+ XrAction action, std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
+{
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>>::iterator it = r_bindings.find(
+ m_profile);
+ if (it == r_bindings.end()) {
+ it = r_bindings
+ .emplace(std::piecewise_construct, std::make_tuple(m_profile), std::make_tuple())
+ .first;
+ }
+
+ std::vector<XrActionSuggestedBinding> &sbindings = it->second;
+
+ for (auto &[path, binding] : m_bindings) {
+ XrActionSuggestedBinding sbinding;
+ sbinding.action = action;
+ sbinding.binding = binding;
+
+ sbindings.push_back(std::move(sbinding));
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrAction
+ *
+ * \{ */
+
+GHOST_XrAction::GHOST_XrAction(XrInstance instance,
+ XrActionSet action_set,
+ const GHOST_XrActionInfo &info)
+ : m_type(info.type),
+ m_states(info.states),
+ m_custom_data_(
+ std::make_unique<GHOST_C_CustomDataWrapper>(info.customdata, info.customdata_free_fn))
+{
+ m_subaction_paths.resize(info.count_subaction_paths);
+
+ for (uint32_t i = 0; i < info.count_subaction_paths; ++i) {
+ CHECK_XR(xrStringToPath(instance, info.subaction_paths[i], &m_subaction_paths[i]),
+ (std::string("Failed to get user path \"") + info.subaction_paths[i] + "\".").data());
+ }
+
+ XrActionCreateInfo action_info{XR_TYPE_ACTION_CREATE_INFO};
+ strcpy(action_info.actionName, info.name);
+ strcpy(action_info.localizedActionName, info.name); /* Just use same name for localized. This can
+ be changed in the future if necessary. */
+
+ switch (info.type) {
+ case GHOST_kXrActionTypeBooleanInput:
+ action_info.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
+ break;
+ case GHOST_kXrActionTypeFloatInput:
+ action_info.actionType = XR_ACTION_TYPE_FLOAT_INPUT;
+ break;
+ case GHOST_kXrActionTypeVector2fInput:
+ action_info.actionType = XR_ACTION_TYPE_VECTOR2F_INPUT;
+ break;
+ case GHOST_kXrActionTypePoseInput:
+ action_info.actionType = XR_ACTION_TYPE_POSE_INPUT;
+ break;
+ case GHOST_kXrActionTypeVibrationOutput:
+ action_info.actionType = XR_ACTION_TYPE_VIBRATION_OUTPUT;
+ break;
+ }
+ action_info.countSubactionPaths = info.count_subaction_paths;
+ action_info.subactionPaths = m_subaction_paths.data();
+
+ CHECK_XR(xrCreateAction(action_set, &action_info, &m_action),
+ (std::string("Failed to create action \"") + info.name +
+ "\". Action name and/or paths are invalid. Name must not contain upper "
+ "case letters or special characters other than '-', '_', or '.'.")
+ .data());
+}
+
+GHOST_XrAction::~GHOST_XrAction()
+{
+ if (m_action != XR_NULL_HANDLE) {
+ CHECK_XR_ASSERT(xrDestroyAction(m_action));
+ }
+}
+
+bool GHOST_XrAction::createSpace(XrInstance instance,
+ XrSession session,
+ const GHOST_XrActionSpaceInfo &info)
+{
+ uint32_t subaction_idx = 0;
+ for (; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
+ if (m_spaces.find(info.subaction_paths[subaction_idx]) != m_spaces.end()) {
+ return false;
+ }
+ }
+
+ for (subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
+ m_spaces.emplace(std::piecewise_construct,
+ std::make_tuple(info.subaction_paths[subaction_idx]),
+ std::make_tuple(instance, session, m_action, info, subaction_idx));
+ }
+
+ return true;
+}
+
+void GHOST_XrAction::destroySpace(const char *subaction_path)
+{
+ if (m_spaces.find(subaction_path) != m_spaces.end()) {
+ m_spaces.erase(subaction_path);
+ }
+}
+
+bool GHOST_XrAction::createBinding(XrInstance instance,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info)
+{
+ if (m_profiles.find(profile_path) != m_profiles.end()) {
+ return false;
+ }
+
+ m_profiles.emplace(std::piecewise_construct,
+ std::make_tuple(profile_path),
+ std::make_tuple(instance, m_action, profile_path, info));
+
+ return true;
+}
+
+void GHOST_XrAction::destroyBinding(const char *interaction_profile_path)
+{
+ if (m_profiles.find(interaction_profile_path) != m_profiles.end()) {
+ m_profiles.erase(interaction_profile_path);
+ }
+}
+
+void GHOST_XrAction::updateState(XrSession session,
+ const char *action_name,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time)
+{
+ XrActionStateGetInfo state_info{XR_TYPE_ACTION_STATE_GET_INFO};
+ state_info.action = m_action;
+
+ const size_t count_subaction_paths = m_subaction_paths.size();
+ for (size_t subaction_idx = 0; subaction_idx < count_subaction_paths; ++subaction_idx) {
+ state_info.subactionPath = m_subaction_paths[subaction_idx];
+
+ switch (m_type) {
+ case GHOST_kXrActionTypeBooleanInput: {
+ XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN};
+ CHECK_XR(xrGetActionStateBoolean(session, &state_info, &state),
+ (std::string("Failed to get state for boolean action \"") + action_name + "\".")
+ .data());
+ if (state.isActive) {
+ ((bool *)m_states)[subaction_idx] = state.currentState;
+ }
+ break;
+ }
+ case GHOST_kXrActionTypeFloatInput: {
+ XrActionStateFloat state{XR_TYPE_ACTION_STATE_FLOAT};
+ CHECK_XR(
+ xrGetActionStateFloat(session, &state_info, &state),
+ (std::string("Failed to get state for float action \"") + action_name + "\".").data());
+ if (state.isActive) {
+ ((float *)m_states)[subaction_idx] = state.currentState;
+ }
+ break;
+ }
+ case GHOST_kXrActionTypeVector2fInput: {
+ XrActionStateVector2f state{XR_TYPE_ACTION_STATE_VECTOR2F};
+ CHECK_XR(xrGetActionStateVector2f(session, &state_info, &state),
+ (std::string("Failed to get state for vector2f action \"") + action_name + "\".")
+ .data());
+ if (state.isActive) {
+ memcpy(((float(*)[2])m_states)[subaction_idx], &state.currentState, sizeof(float[2]));
+ }
+ break;
+ }
+ case GHOST_kXrActionTypePoseInput: {
+ XrActionStatePose state{XR_TYPE_ACTION_STATE_POSE};
+ CHECK_XR(
+ xrGetActionStatePose(session, &state_info, &state),
+ (std::string("Failed to get state for pose action \"") + action_name + "\".").data());
+ if (state.isActive) {
+ XrSpace pose_space = XR_NULL_HANDLE;
+ for (auto &[path, space] : m_spaces) {
+ if (space.getSubactionPath() == state_info.subactionPath) {
+ pose_space = space.getSpace();
+ break;
+ }
+ }
+
+ if (pose_space != XR_NULL_HANDLE) {
+ XrSpaceLocation space_location{XR_TYPE_SPACE_LOCATION};
+ CHECK_XR(
+ xrLocateSpace(
+ pose_space, reference_space, predicted_display_time, &space_location),
+ (std::string("Failed to query pose space for action \"") + action_name + "\".")
+ .data());
+ copy_openxr_pose_to_ghost_pose(space_location.pose,
+ ((GHOST_XrPose *)m_states)[subaction_idx]);
+ }
+ }
+ break;
+ }
+ case GHOST_kXrActionTypeVibrationOutput: {
+ break;
+ }
+ }
+ }
+}
+
+void GHOST_XrAction::applyHapticFeedback(XrSession session,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude)
+{
+ XrHapticVibration vibration{XR_TYPE_HAPTIC_VIBRATION};
+ vibration.duration = (duration == 0) ? XR_MIN_HAPTIC_DURATION :
+ static_cast<XrDuration>(duration);
+ vibration.frequency = frequency;
+ vibration.amplitude = amplitude;
+
+ XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO};
+ haptic_info.action = m_action;
+
+ for (std::vector<XrPath>::iterator it = m_subaction_paths.begin(); it != m_subaction_paths.end();
+ ++it) {
+ haptic_info.subactionPath = *it;
+ CHECK_XR(xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration),
+ (std::string("Failed to apply haptic action \"") + action_name + "\".").data());
+ }
+}
+
+void GHOST_XrAction::stopHapticFeedback(XrSession session, const char *action_name)
+{
+ XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO};
+ haptic_info.action = m_action;
+
+ for (std::vector<XrPath>::iterator it = m_subaction_paths.begin(); it != m_subaction_paths.end();
+ ++it) {
+ haptic_info.subactionPath = *it;
+ CHECK_XR(xrStopHapticFeedback(session, &haptic_info),
+ (std::string("Failed to stop haptic action \"") + action_name + "\".").data());
+ }
+}
+
+void *GHOST_XrAction::getCustomdata()
+{
+ if (m_custom_data_ == nullptr) {
+ return nullptr;
+ }
+ return m_custom_data_->custom_data_;
+}
+
+void GHOST_XrAction::getBindings(
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
+{
+ for (auto &[path, profile] : m_profiles) {
+ profile.getBindings(m_action, r_bindings);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrActionSet
+ *
+ * \{ */
+
+GHOST_XrActionSet::GHOST_XrActionSet(XrInstance instance, const GHOST_XrActionSetInfo &info)
+ : m_custom_data_(
+ std::make_unique<GHOST_C_CustomDataWrapper>(info.customdata, info.customdata_free_fn))
+{
+ XrActionSetCreateInfo action_set_info{XR_TYPE_ACTION_SET_CREATE_INFO};
+ strcpy(action_set_info.actionSetName, info.name);
+ strcpy(action_set_info.localizedActionSetName,
+ info.name); /* Just use same name for localized. This can be changed in the future if
+ necessary. */
+ action_set_info.priority = 0; /* Use same (default) priority for all action sets. */
+
+ CHECK_XR(xrCreateActionSet(instance, &action_set_info, &m_action_set),
+ (std::string("Failed to create action set \"") + info.name +
+ "\". Name must not contain upper case letters or special characters "
+ "other than '-', '_', or '.'.")
+ .data());
+}
+
+GHOST_XrActionSet::~GHOST_XrActionSet()
+{
+ /* This needs to be done before xrDestroyActionSet() to avoid an assertion in the GHOST_XrAction
+ * destructor (which calls xrDestroyAction()). */
+ m_actions.clear();
+
+ if (m_action_set != XR_NULL_HANDLE) {
+ CHECK_XR_ASSERT(xrDestroyActionSet(m_action_set));
+ }
+}
+
+bool GHOST_XrActionSet::createAction(XrInstance instance, const GHOST_XrActionInfo &info)
+{
+ if (m_actions.find(info.name) != m_actions.end()) {
+ return false;
+ }
+
+ m_actions.emplace(std::piecewise_construct,
+ std::make_tuple(info.name),
+ std::make_tuple(instance, m_action_set, info));
+
+ return true;
+}
+
+void GHOST_XrActionSet::destroyAction(const char *action_name)
+{
+ if (m_actions.find(action_name) != m_actions.end()) {
+ m_actions.erase(action_name);
+ }
+}
+
+GHOST_XrAction *GHOST_XrActionSet::findAction(const char *action_name)
+{
+ std::map<std::string, GHOST_XrAction>::iterator it = m_actions.find(action_name);
+ if (it == m_actions.end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+void GHOST_XrActionSet::updateStates(XrSession session,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time)
+{
+ for (auto &[name, action] : m_actions) {
+ action.updateState(session, name.data(), reference_space, predicted_display_time);
+ }
+}
+
+XrActionSet GHOST_XrActionSet::getActionSet() const
+{
+ return m_action_set;
+}
+
+void *GHOST_XrActionSet::getCustomdata()
+{
+ if (m_custom_data_ == nullptr) {
+ return nullptr;
+ }
+ return m_custom_data_->custom_data_;
+}
+
+void GHOST_XrActionSet::getBindings(
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
+{
+ for (auto &[name, action] : m_actions) {
+ action.getBindings(r_bindings);
+ }
+}
+
+/** \} */
diff --git a/intern/ghost/intern/GHOST_XrAction.h b/intern/ghost/intern/GHOST_XrAction.h
new file mode 100644
index 00000000000..bdc6cafb4a9
--- /dev/null
+++ b/intern/ghost/intern/GHOST_XrAction.h
@@ -0,0 +1,145 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+/* Note: Requires OpenXR headers to be included before this one for OpenXR types (XrSpace, XrPath,
+ * etc.). */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "GHOST_Util.h"
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrActionSpace {
+ public:
+ GHOST_XrActionSpace() = delete; /* Default constructor for map storage. */
+ GHOST_XrActionSpace(XrInstance instance,
+ XrSession session,
+ XrAction action,
+ const GHOST_XrActionSpaceInfo &info,
+ uint32_t subaction_idx);
+ ~GHOST_XrActionSpace();
+
+ XrSpace getSpace() const;
+ const XrPath &getSubactionPath() const;
+
+ private:
+ XrSpace m_space = XR_NULL_HANDLE;
+ XrPath m_subaction_path = XR_NULL_PATH;
+};
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrActionProfile {
+ public:
+ GHOST_XrActionProfile() = delete; /* Default constructor for map storage. */
+ GHOST_XrActionProfile(XrInstance instance,
+ XrAction action,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info);
+ ~GHOST_XrActionProfile() = default;
+
+ void getBindings(XrAction action,
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const;
+
+ private:
+ XrPath m_profile = XR_NULL_PATH;
+ /* Bindings identified by interaction (user (subaction) + component) path. */
+ std::map<std::string, XrPath> m_bindings;
+};
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrAction {
+ public:
+ GHOST_XrAction() = delete; /* Default constructor for map storage. */
+ GHOST_XrAction(XrInstance instance, XrActionSet action_set, const GHOST_XrActionInfo &info);
+ ~GHOST_XrAction();
+
+ bool createSpace(XrInstance instance, XrSession session, const GHOST_XrActionSpaceInfo &info);
+ void destroySpace(const char *subaction_path);
+
+ bool createBinding(XrInstance instance,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info);
+ void destroyBinding(const char *profile_path);
+
+ void updateState(XrSession session,
+ const char *action_name,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time);
+ void applyHapticFeedback(XrSession session,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude);
+ void stopHapticFeedback(XrSession session, const char *action_name);
+
+ void *getCustomdata();
+ void getBindings(std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const;
+
+ private:
+ XrAction m_action = XR_NULL_HANDLE;
+ GHOST_XrActionType m_type;
+ std::vector<XrPath> m_subaction_paths;
+ /** States for each subaction path. */
+ void *m_states;
+
+ std::unique_ptr<GHOST_C_CustomDataWrapper> m_custom_data_ = nullptr; /* wmXrAction */
+
+ /* Spaces identified by user (subaction) path. */
+ std::map<std::string, GHOST_XrActionSpace> m_spaces;
+ /* Profiles identified by interaction profile path. */
+ std::map<std::string, GHOST_XrActionProfile> m_profiles;
+};
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrActionSet {
+ public:
+ GHOST_XrActionSet() = delete; /* Default constructor for map storage. */
+ GHOST_XrActionSet(XrInstance instance, const GHOST_XrActionSetInfo &info);
+ ~GHOST_XrActionSet();
+
+ bool createAction(XrInstance instance, const GHOST_XrActionInfo &info);
+ void destroyAction(const char *action_name);
+ GHOST_XrAction *findAction(const char *action_name);
+
+ void updateStates(XrSession session,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time);
+
+ XrActionSet getActionSet() const;
+ void *getCustomdata();
+ void getBindings(std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const;
+
+ private:
+ XrActionSet m_action_set = XR_NULL_HANDLE;
+
+ std::unique_ptr<GHOST_C_CustomDataWrapper> m_custom_data_ = nullptr; /* wmXrActionSet */
+
+ std::map<std::string, GHOST_XrAction> m_actions;
+};
+
+/* -------------------------------------------------------------------- */
diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp
index 2bf67c121f8..daad0b8190a 100644
--- a/intern/ghost/intern/GHOST_XrContext.cpp
+++ b/intern/ghost/intern/GHOST_XrContext.cpp
@@ -55,7 +55,6 @@ void *GHOST_XrContext::s_error_handler_customdata = nullptr;
/* -------------------------------------------------------------------- */
/** \name Create, Initialize and Destruct
- *
* \{ */
GHOST_XrContext::GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info)
@@ -153,7 +152,6 @@ void GHOST_XrContext::storeInstanceProperties()
/* -------------------------------------------------------------------- */
/** \name Debug Printing
- *
* \{ */
void GHOST_XrContext::printInstanceInfo()
@@ -242,14 +240,13 @@ void GHOST_XrContext::initDebugMessenger()
/* -------------------------------------------------------------------- */
/** \name Error handling
- *
* \{ */
void GHOST_XrContext::dispatchErrorMessage(const GHOST_XrException *exception) const
{
GHOST_XrError error;
- error.user_message = exception->m_msg;
+ error.user_message = exception->m_msg.data();
error.customdata = s_error_handler_customdata;
if (isDebugMode()) {
@@ -273,7 +270,6 @@ void GHOST_XrContext::setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *c
/* -------------------------------------------------------------------- */
/** \name OpenXR API-Layers and Extensions
- *
* \{ */
/**
@@ -378,7 +374,7 @@ void GHOST_XrContext::getAPILayersToEnable(std::vector<const char *> &r_ext_name
for (const std::string &layer : try_layers) {
if (openxr_layer_is_available(m_oxr->layers, layer)) {
- r_ext_names.push_back(layer.c_str());
+ r_ext_names.push_back(layer.data());
}
}
}
@@ -488,6 +484,7 @@ GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToUse(
void GHOST_XrContext::startSession(const GHOST_XrSessionBeginInfo *begin_info)
{
+ m_custom_funcs.session_create_fn = begin_info->create_fn;
m_custom_funcs.session_exit_fn = begin_info->exit_fn;
m_custom_funcs.session_exit_customdata = begin_info->exit_customdata;
@@ -538,6 +535,16 @@ void GHOST_XrContext::handleSessionStateChange(const XrEventDataSessionStateChan
* Public as in, exposed in the Ghost API.
* \{ */
+GHOST_XrSession *GHOST_XrContext::getSession()
+{
+ return m_session.get();
+}
+
+const GHOST_XrSession *GHOST_XrContext::getSession() const
+{
+ return m_session.get();
+}
+
void GHOST_XrContext::setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
GHOST_XrGraphicsContextUnbindFn unbind_fn)
{
@@ -564,7 +571,6 @@ bool GHOST_XrContext::needsUpsideDownDrawing() const
/* -------------------------------------------------------------------- */
/** \name Ghost Internal Accessors and Mutators
- *
* \{ */
GHOST_TXrOpenXRRuntimeID GHOST_XrContext::getOpenXRRuntimeID() const
diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h
index 59c7786ed7a..f29d7349f7e 100644
--- a/intern/ghost/intern/GHOST_XrContext.h
+++ b/intern/ghost/intern/GHOST_XrContext.h
@@ -35,6 +35,7 @@ struct GHOST_XrCustomFuncs {
/** Function to release (possibly free) a graphics context. */
GHOST_XrGraphicsContextUnbindFn gpu_ctx_unbind_fn = nullptr;
+ GHOST_XrSessionCreateFn session_create_fn = nullptr;
GHOST_XrSessionExitFn session_exit_fn = nullptr;
void *session_exit_customdata = nullptr;
@@ -72,6 +73,10 @@ class GHOST_XrContext : public GHOST_IXrContext {
bool isSessionRunning() const override;
void drawSessionViews(void *draw_customdata) override;
+ /** Needed for the GHOST C api. */
+ GHOST_XrSession *getSession() override;
+ const GHOST_XrSession *getSession() const override;
+
static void setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata);
void dispatchErrorMessage(const class GHOST_XrException *exception) const override;
diff --git a/intern/ghost/intern/GHOST_XrException.h b/intern/ghost/intern/GHOST_XrException.h
index 30c33eaf98f..e93164e04c8 100644
--- a/intern/ghost/intern/GHOST_XrException.h
+++ b/intern/ghost/intern/GHOST_XrException.h
@@ -21,6 +21,7 @@
#pragma once
#include <exception>
+#include <string>
class GHOST_XrException : public std::exception {
friend class GHOST_XrContext;
@@ -33,10 +34,10 @@ class GHOST_XrException : public std::exception {
const char *what() const noexcept override
{
- return m_msg;
+ return m_msg.data();
}
private:
- const char *m_msg;
+ std::string m_msg;
int m_result;
};
diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp
index a1fd7fc2781..a7438fae13c 100644
--- a/intern/ghost/intern/GHOST_XrSession.cpp
+++ b/intern/ghost/intern/GHOST_XrSession.cpp
@@ -28,6 +28,7 @@
#include "GHOST_C-api.h"
#include "GHOST_IXrGraphicsBinding.h"
+#include "GHOST_XrAction.h"
#include "GHOST_XrContext.h"
#include "GHOST_XrException.h"
#include "GHOST_XrSwapchain.h"
@@ -46,6 +47,8 @@ struct OpenXRSessionData {
XrSpace view_space;
std::vector<XrView> views;
std::vector<GHOST_XrSwapchain> swapchains;
+
+ std::map<std::string, GHOST_XrActionSet> action_sets;
};
struct GHOST_XrDrawInfo {
@@ -59,7 +62,6 @@ struct GHOST_XrDrawInfo {
/* -------------------------------------------------------------------- */
/** \name Create, Initialize and Destruct
- *
* \{ */
GHOST_XrSession::GHOST_XrSession(GHOST_XrContext &xr_context)
@@ -72,6 +74,7 @@ GHOST_XrSession::~GHOST_XrSession()
unbindGraphicsContext();
m_oxr->swapchains.clear();
+ m_oxr->action_sets.clear();
if (m_oxr->reference_space != XR_NULL_HANDLE) {
CHECK_XR_ASSERT(xrDestroySpace(m_oxr->reference_space));
@@ -110,7 +113,6 @@ void GHOST_XrSession::initSystem()
/* -------------------------------------------------------------------- */
/** \name State Management
- *
* \{ */
static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose &base_pose)
@@ -179,7 +181,7 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info)
std::ostringstream strstream;
strstream << "Available graphics context version does not meet the following requirements: "
<< requirement_str;
- throw GHOST_XrException(strstream.str().c_str());
+ throw GHOST_XrException(strstream.str().data());
}
m_gpu_binding->initFromGhostContext(*m_gpu_ctx);
@@ -196,6 +198,9 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info)
prepareDrawing();
create_reference_spaces(*m_oxr, begin_info->base_pose);
+
+ /* Create and bind actions here. */
+ m_context->getCustomFuncs().session_create_fn();
}
void GHOST_XrSession::requestEnd()
@@ -225,10 +230,9 @@ GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent(
assert(m_oxr->session == XR_NULL_HANDLE || m_oxr->session == lifecycle.session);
switch (lifecycle.state) {
- case XR_SESSION_STATE_READY: {
+ case XR_SESSION_STATE_READY:
beginSession();
break;
- }
case XR_SESSION_STATE_STOPPING:
endSession();
break;
@@ -245,7 +249,6 @@ GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent(
/* -------------------------------------------------------------------- */
/** \name Drawing
- *
* \{ */
void GHOST_XrSession::prepareDrawing()
@@ -352,18 +355,6 @@ void GHOST_XrSession::draw(void *draw_customdata)
endFrameDrawing(layers);
}
-static void copy_openxr_pose_to_ghost_pose(const XrPosef &oxr_pose, GHOST_XrPose &r_ghost_pose)
-{
- /* Set and convert to Blender coordinate space. */
- r_ghost_pose.position[0] = oxr_pose.position.x;
- r_ghost_pose.position[1] = oxr_pose.position.y;
- r_ghost_pose.position[2] = oxr_pose.position.z;
- r_ghost_pose.orientation_quat[0] = oxr_pose.orientation.w;
- r_ghost_pose.orientation_quat[1] = oxr_pose.orientation.x;
- r_ghost_pose.orientation_quat[2] = oxr_pose.orientation.y;
- r_ghost_pose.orientation_quat[3] = oxr_pose.orientation.z;
-}
-
static void ghost_xr_draw_view_info_from_view(const XrView &view, GHOST_XrDrawViewInfo &r_info)
{
/* Set and convert to Blender coordinate space. */
@@ -457,7 +448,6 @@ bool GHOST_XrSession::needsUpsideDownDrawing() const
/* -------------------------------------------------------------------- */
/** \name State Queries
- *
* \{ */
bool GHOST_XrSession::isRunning() const
@@ -505,3 +495,340 @@ void GHOST_XrSession::unbindGraphicsContext()
}
/** \} */ /* Graphics Context Injection */
+
+/* -------------------------------------------------------------------- */
+/** \name Actions
+ *
+ * \{ */
+
+static GHOST_XrActionSet *find_action_set(OpenXRSessionData *oxr, const char *action_set_name)
+{
+ std::map<std::string, GHOST_XrActionSet>::iterator it = oxr->action_sets.find(action_set_name);
+ if (it == oxr->action_sets.end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+bool GHOST_XrSession::createActionSet(const GHOST_XrActionSetInfo &info)
+{
+ std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
+ if (action_sets.find(info.name) != action_sets.end()) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+
+ action_sets.emplace(
+ std::piecewise_construct, std::make_tuple(info.name), std::make_tuple(instance, info));
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActionSet(const char *action_set_name)
+{
+ std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
+ if (action_sets.find(action_set_name) != action_sets.end()) {
+ action_sets.erase(action_set_name);
+ }
+}
+
+bool GHOST_XrSession::createActions(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+
+ for (uint32_t i = 0; i < count; ++i) {
+ if (!action_set->createAction(instance, infos[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActions(const char *action_set_name,
+ uint32_t count,
+ const char *const *action_names)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < count; ++i) {
+ action_set->destroyAction(action_names[i]);
+ }
+}
+
+bool GHOST_XrSession::createActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+ XrSession session = m_oxr->session;
+
+ for (uint32_t action_idx = 0; action_idx < count; ++action_idx) {
+ const GHOST_XrActionSpaceInfo &info = infos[action_idx];
+
+ GHOST_XrAction *action = action_set->findAction(info.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ if (!action->createSpace(instance, session, info)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ for (uint32_t action_idx = 0; action_idx < count; ++action_idx) {
+ const GHOST_XrActionSpaceInfo &info = infos[action_idx];
+
+ GHOST_XrAction *action = action_set->findAction(info.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ for (uint32_t subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
+ action->destroySpace(info.subaction_paths[subaction_idx]);
+ }
+ }
+}
+
+bool GHOST_XrSession::createActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+
+ for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) {
+ const GHOST_XrActionProfileInfo &info = infos[profile_idx];
+ const char *profile_path = info.profile_path;
+
+ for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) {
+ const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx];
+
+ GHOST_XrAction *action = action_set->findAction(binding.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ action->createBinding(instance, profile_path, binding);
+ }
+ }
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) {
+ const GHOST_XrActionProfileInfo &info = infos[profile_idx];
+ const char *profile_path = info.profile_path;
+
+ for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) {
+ const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx];
+
+ GHOST_XrAction *action = action_set->findAction(binding.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ action->destroyBinding(profile_path);
+ }
+ }
+}
+
+bool GHOST_XrSession::attachActionSets()
+{
+ /* Suggest action bindings for all action sets. */
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> profile_bindings;
+ for (auto &[name, action_set] : m_oxr->action_sets) {
+ action_set.getBindings(profile_bindings);
+ }
+
+ if (profile_bindings.size() < 1) {
+ return false;
+ }
+
+ XrInteractionProfileSuggestedBinding bindings_info{
+ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
+ XrInstance instance = m_context->getInstance();
+
+ for (auto &[profile, bindings] : profile_bindings) {
+ bindings_info.interactionProfile = profile;
+ bindings_info.countSuggestedBindings = (uint32_t)bindings.size();
+ bindings_info.suggestedBindings = bindings.data();
+
+ CHECK_XR(xrSuggestInteractionProfileBindings(instance, &bindings_info),
+ "Failed to suggest interaction profile bindings.");
+ }
+
+ /* Attach action sets. */
+ XrSessionActionSetsAttachInfo attach_info{XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO};
+ attach_info.countActionSets = (uint32_t)m_oxr->action_sets.size();
+
+ /* Create an aligned copy of the action sets to pass to xrAttachSessionActionSets(). */
+ std::vector<XrActionSet> action_sets(attach_info.countActionSets);
+ uint32_t i = 0;
+ for (auto &[name, action_set] : m_oxr->action_sets) {
+ action_sets[i++] = action_set.getActionSet();
+ }
+ attach_info.actionSets = action_sets.data();
+
+ CHECK_XR(xrAttachSessionActionSets(m_oxr->session, &attach_info),
+ "Failed to attach XR action sets.");
+
+ return true;
+}
+
+bool GHOST_XrSession::syncActions(const char *action_set_name)
+{
+ std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
+
+ XrActionsSyncInfo sync_info{XR_TYPE_ACTIONS_SYNC_INFO};
+ sync_info.countActiveActionSets = (action_set_name != nullptr) ? 1 :
+ (uint32_t)action_sets.size();
+ if (sync_info.countActiveActionSets < 1) {
+ return false;
+ }
+
+ std::vector<XrActiveActionSet> active_action_sets(sync_info.countActiveActionSets);
+ GHOST_XrActionSet *action_set = nullptr;
+
+ if (action_set_name != nullptr) {
+ action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrActiveActionSet &active_action_set = active_action_sets[0];
+ active_action_set.actionSet = action_set->getActionSet();
+ active_action_set.subactionPath = XR_NULL_PATH;
+ }
+ else {
+ uint32_t i = 0;
+ for (auto &[name, action_set] : action_sets) {
+ XrActiveActionSet &active_action_set = active_action_sets[i++];
+ active_action_set.actionSet = action_set.getActionSet();
+ active_action_set.subactionPath = XR_NULL_PATH;
+ }
+ }
+ sync_info.activeActionSets = active_action_sets.data();
+
+ CHECK_XR(xrSyncActions(m_oxr->session, &sync_info), "Failed to synchronize XR actions.");
+
+ /* Update action states (i.e. Blender custom data). */
+ XrSession session = m_oxr->session;
+ XrSpace reference_space = m_oxr->reference_space;
+ const XrTime &predicted_display_time = m_draw_info->frame_state.predictedDisplayTime;
+
+ if (action_set != nullptr) {
+ action_set->updateStates(session, reference_space, predicted_display_time);
+ }
+ else {
+ for (auto &[name, action_set] : action_sets) {
+ action_set.updateStates(session, reference_space, predicted_display_time);
+ }
+ }
+
+ return true;
+}
+
+bool GHOST_XrSession::applyHapticAction(const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ GHOST_XrAction *action = action_set->findAction(action_name);
+ if (action == nullptr) {
+ return false;
+ }
+
+ action->applyHapticFeedback(m_oxr->session, action_name, duration, frequency, amplitude);
+
+ return true;
+}
+
+void GHOST_XrSession::stopHapticAction(const char *action_set_name, const char *action_name)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ GHOST_XrAction *action = action_set->findAction(action_name);
+ if (action == nullptr) {
+ return;
+ }
+
+ action->stopHapticFeedback(m_oxr->session, action_name);
+}
+
+void *GHOST_XrSession::getActionSetCustomdata(const char *action_set_name)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return nullptr;
+ }
+
+ return action_set->getCustomdata();
+}
+
+void *GHOST_XrSession::getActionCustomdata(const char *action_set_name, const char *action_name)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return nullptr;
+ }
+
+ GHOST_XrAction *action = action_set->findAction(action_name);
+ if (action == nullptr) {
+ return nullptr;
+ }
+
+ return action->getCustomdata();
+}
+
+/** \} */ /* Actions */
diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h
index 79a586411e9..d09c78e1ea7 100644
--- a/intern/ghost/intern/GHOST_XrSession.h
+++ b/intern/ghost/intern/GHOST_XrSession.h
@@ -52,6 +52,43 @@ class GHOST_XrSession {
void draw(void *draw_customdata);
+ /** Action functions to be called pre-session start.
+ * Note: The "destroy" functions can also be called post-session start. */
+ bool createActionSet(const GHOST_XrActionSetInfo &info);
+ void destroyActionSet(const char *action_set_name);
+ bool createActions(const char *action_set_name, uint32_t count, const GHOST_XrActionInfo *infos);
+ void destroyActions(const char *action_set_name,
+ uint32_t count,
+ const char *const *action_names);
+ bool createActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos);
+ void destroyActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos);
+ bool createActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos);
+ void destroyActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos);
+ bool attachActionSets();
+
+ /** Action functions to be called post-session start. */
+ bool syncActions(
+ const char *action_set_name = nullptr); /* If action_set_name is nullptr, all attached
+ * action sets will be synced. */
+ bool applyHapticAction(const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude);
+ void stopHapticAction(const char *action_set_name, const char *action_name);
+
+ /* Custom data (owned by Blender, not GHOST) accessors. */
+ void *getActionSetCustomdata(const char *action_set_name);
+ void *getActionCustomdata(const char *action_set_name, const char *action_name);
+
private:
/** Pointer back to context managing this session. Would be nice to avoid, but needed to access
* custom callbacks set before session start. */
diff --git a/intern/ghost/intern/GHOST_Xr_intern.h b/intern/ghost/intern/GHOST_Xr_intern.h
index 137541c4528..0616e426da3 100644
--- a/intern/ghost/intern/GHOST_Xr_intern.h
+++ b/intern/ghost/intern/GHOST_Xr_intern.h
@@ -45,3 +45,27 @@
(void)_res; \
} \
(void)0
+
+inline void copy_ghost_pose_to_openxr_pose(const GHOST_XrPose &ghost_pose, XrPosef &r_oxr_pose)
+{
+ /* Set and convert to OpenXR coordinate space. */
+ r_oxr_pose.position.x = ghost_pose.position[0];
+ r_oxr_pose.position.y = ghost_pose.position[1];
+ r_oxr_pose.position.z = ghost_pose.position[2];
+ r_oxr_pose.orientation.w = ghost_pose.orientation_quat[0];
+ r_oxr_pose.orientation.x = ghost_pose.orientation_quat[1];
+ r_oxr_pose.orientation.y = ghost_pose.orientation_quat[2];
+ r_oxr_pose.orientation.z = ghost_pose.orientation_quat[3];
+}
+
+inline void copy_openxr_pose_to_ghost_pose(const XrPosef &oxr_pose, GHOST_XrPose &r_ghost_pose)
+{
+ /* Set and convert to Blender coordinate space. */
+ r_ghost_pose.position[0] = oxr_pose.position.x;
+ r_ghost_pose.position[1] = oxr_pose.position.y;
+ r_ghost_pose.position[2] = oxr_pose.position.z;
+ r_ghost_pose.orientation_quat[0] = oxr_pose.orientation.w;
+ r_ghost_pose.orientation_quat[1] = oxr_pose.orientation.x;
+ r_ghost_pose.orientation_quat[2] = oxr_pose.orientation.y;
+ r_ghost_pose.orientation_quat[3] = oxr_pose.orientation.z;
+}
diff --git a/intern/ghost/test/gears/GHOST_C-Test.c b/intern/ghost/test/gears/GHOST_C-Test.c
index 4283f990cfb..8cd1b5acb89 100644
--- a/intern/ghost/test/gears/GHOST_C-Test.c
+++ b/intern/ghost/test/gears/GHOST_C-Test.c
@@ -477,7 +477,7 @@ int main(int argc, char **argv)
/* Enter main loop */
while (!sExitRequested) {
- if (!GHOST_ProcessEvents(shSystem, 0)) {
+ if (!GHOST_ProcessEvents(shSystem, false)) {
#ifdef WIN32
/* If there were no events, be nice to other applications */
Sleep(10);
diff --git a/intern/ghost/test/multitest/MultiTest.c b/intern/ghost/test/multitest/MultiTest.c
index 8c8858fc6d8..3b424f1ca89 100644
--- a/intern/ghost/test/multitest/MultiTest.c
+++ b/intern/ghost/test/multitest/MultiTest.c
@@ -926,7 +926,7 @@ void multitestapp_exit(MultiTestApp *app)
void multitestapp_run(MultiTestApp *app)
{
while (!app->exit) {
- GHOST_ProcessEvents(app->sys, 1);
+ GHOST_ProcessEvents(app->sys, true);
GHOST_DispatchEvents(app->sys);
}
}