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
path: root/intern
diff options
context:
space:
mode:
Diffstat (limited to 'intern')
-rw-r--r--intern/cycles/blender/blender_light.cpp9
-rw-r--r--intern/cycles/blender/blender_python.cpp12
-rw-r--r--intern/cycles/device/device_optix.cpp25
-rw-r--r--intern/cycles/render/alembic_read.cpp29
-rw-r--r--intern/cycles/test/CMakeLists.txt2
-rw-r--r--intern/cycles/util/util_color.h14
-rw-r--r--intern/cycles/util/util_system.cpp4
-rw-r--r--intern/ffmpeg/ffmpeg_compat.h37
-rw-r--r--intern/ghost/CMakeLists.txt12
-rw-r--r--intern/ghost/GHOST_Types.h84
-rw-r--r--intern/ghost/intern/GHOST_ContextEGL.cpp28
-rw-r--r--intern/ghost/intern/GHOST_ContextEGL.h10
-rw-r--r--intern/ghost/intern/GHOST_DisplayManagerSDL.cpp2
-rw-r--r--intern/ghost/intern/GHOST_IXrGraphicsBinding.h4
-rw-r--r--intern/ghost/intern/GHOST_System.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.cpp2
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp410
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h23
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp2
-rw-r--r--intern/ghost/intern/GHOST_WindowSDL.cpp2
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp255
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h90
-rw-r--r--intern/ghost/intern/GHOST_Wintab.cpp491
-rw-r--r--intern/ghost/intern/GHOST_Wintab.h250
-rw-r--r--intern/ghost/intern/GHOST_XrAction.cpp7
-rw-r--r--intern/ghost/intern/GHOST_XrContext.cpp5
-rw-r--r--intern/ghost/intern/GHOST_XrGraphicsBinding.cpp18
-rw-r--r--intern/ghost/intern/GHOST_XrSession.h9
-rw-r--r--intern/ghost/intern/GHOST_Xr_openxr_includes.h8
-rw-r--r--intern/iksolver/intern/IK_QJacobianSolver.cpp4
-rw-r--r--intern/libmv/libmv/base/scoped_ptr.h2
-rw-r--r--intern/opencolorio/CMakeLists.txt4
32 files changed, 1400 insertions, 456 deletions
diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/blender_light.cpp
index 283ed7d0adc..50cd9e3db5c 100644
--- a/intern/cycles/blender/blender_light.cpp
+++ b/intern/cycles/blender/blender_light.cpp
@@ -34,12 +34,17 @@ void BlenderSync::sync_light(BL::Object &b_parent,
bool *use_portal)
{
/* test if we need to sync */
- Light *light;
ObjectKey key(b_parent, persistent_id, b_ob_instance, false);
BL::Light b_light(b_ob.data());
+ Light *light = light_map.find(key);
+
+ /* Check if the transform was modified, in case a linked collection is moved we do not get a
+ * specific depsgraph update (T88515). This also mimics the behavior for Objects. */
+ const bool tfm_updated = (light && light->get_tfm() != tfm);
+
/* Update if either object or light data changed. */
- if (!light_map.add_or_update(&light, b_ob, b_parent, key)) {
+ if (!light_map.add_or_update(&light, b_ob, b_parent, key) && !tfm_updated) {
Shader *shader;
if (!shader_map.add_or_update(&shader, b_light)) {
if (light->get_is_portal())
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index 785ca6b4e01..c58d2eb6e04 100644
--- a/intern/cycles/blender/blender_python.cpp
+++ b/intern/cycles/blender/blender_python.cpp
@@ -289,11 +289,10 @@ static PyObject *render_func(PyObject * /*self*/, PyObject *args)
RNA_pointer_create(NULL, &RNA_Depsgraph, (ID *)PyLong_AsVoidPtr(pydepsgraph), &depsgraphptr);
BL::Depsgraph b_depsgraph(depsgraphptr);
- /* Allow Blender to execute other Python scripts, and isolate TBB tasks so we
- * don't get deadlocks with Blender threads accessing shared data like images. */
+ /* Allow Blender to execute other Python scripts. */
python_thread_state_save(&session->python_thread_state);
- tbb::this_task_arena::isolate([&] { session->render(b_depsgraph); });
+ session->render(b_depsgraph);
python_thread_state_restore(&session->python_thread_state);
@@ -330,8 +329,7 @@ static PyObject *bake_func(PyObject * /*self*/, PyObject *args)
python_thread_state_save(&session->python_thread_state);
- tbb::this_task_arena::isolate(
- [&] { session->bake(b_depsgraph, b_object, pass_type, pass_filter, width, height); });
+ session->bake(b_depsgraph, b_object, pass_type, pass_filter, width, height);
python_thread_state_restore(&session->python_thread_state);
@@ -377,7 +375,7 @@ static PyObject *reset_func(PyObject * /*self*/, PyObject *args)
python_thread_state_save(&session->python_thread_state);
- tbb::this_task_arena::isolate([&] { session->reset_session(b_data, b_depsgraph); });
+ session->reset_session(b_data, b_depsgraph);
python_thread_state_restore(&session->python_thread_state);
@@ -399,7 +397,7 @@ static PyObject *sync_func(PyObject * /*self*/, PyObject *args)
python_thread_state_save(&session->python_thread_state);
- tbb::this_task_arena::isolate([&] { session->synchronize(b_depsgraph); });
+ session->synchronize(b_depsgraph);
python_thread_state_restore(&session->python_thread_state);
diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp
index b008dfa376f..392fec4d57b 100644
--- a/intern/cycles/device/device_optix.cpp
+++ b/intern/cycles/device/device_optix.cpp
@@ -1196,16 +1196,18 @@ class OptiXDevice : public CUDADevice {
const CUDAContextScope scope(cuContext);
+ const bool use_fast_trace_bvh = (bvh->params.bvh_type == SceneParams::BVH_STATIC);
+
// Compute memory usage
OptixAccelBufferSizes sizes = {};
OptixAccelBuildOptions options = {};
options.operation = operation;
- if (background) {
- // Prefer best performance and lowest memory consumption in background
+ if (use_fast_trace_bvh) {
+ VLOG(2) << "Using fast to trace OptiX BVH";
options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_TRACE | OPTIX_BUILD_FLAG_ALLOW_COMPACTION;
}
else {
- // Prefer fast updates in viewport
+ VLOG(2) << "Using fast to update OptiX BVH";
options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_BUILD | OPTIX_BUILD_FLAG_ALLOW_UPDATE;
}
@@ -1253,15 +1255,16 @@ class OptiXDevice : public CUDADevice {
out_data.device_pointer,
sizes.outputSizeInBytes,
&out_handle,
- background ? &compacted_size_prop : NULL,
- background ? 1 : 0));
+ use_fast_trace_bvh ? &compacted_size_prop : NULL,
+ use_fast_trace_bvh ? 1 : 0));
bvh->traversable_handle = static_cast<uint64_t>(out_handle);
// Wait for all operations to finish
check_result_cuda_ret(cuStreamSynchronize(NULL));
- // Compact acceleration structure to save memory (do not do this in viewport for faster builds)
- if (background) {
+ // Compact acceleration structure to save memory (only if using fast trace as the
+ // OPTIX_BUILD_FLAG_ALLOW_COMPACTION flag is only set in this case).
+ if (use_fast_trace_bvh) {
uint64_t compacted_size = sizes.outputSizeInBytes;
check_result_cuda_ret(
cuMemcpyDtoH(&compacted_size, compacted_size_prop.result, sizeof(compacted_size)));
@@ -1306,6 +1309,8 @@ class OptiXDevice : public CUDADevice {
return;
}
+ const bool use_fast_trace_bvh = (bvh->params.bvh_type == SceneParams::BVH_STATIC);
+
free_bvh_memory_delayed();
BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh);
@@ -1315,10 +1320,10 @@ class OptiXDevice : public CUDADevice {
if (!bvh->params.top_level) {
assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1);
- // Refit is only possible in viewport for now (because AS is built with
- // OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above)
OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD;
- if (refit && !background) {
+ /* Refit is only possible when using fast to trace BVH (because AS is built with
+ * OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above). */
+ if (refit && !use_fast_trace_bvh) {
assert(bvh_optix->traversable_handle != 0);
operation = OPTIX_BUILD_OPERATION_UPDATE;
}
diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp
index 33ce3502879..c53ec668938 100644
--- a/intern/cycles/render/alembic_read.cpp
+++ b/intern/cycles/render/alembic_read.cpp
@@ -736,13 +736,14 @@ static void process_uvs(CachedData &cache,
const IV2fGeomParam::Sample &sample,
double time)
{
- if (scope != kFacevaryingScope) {
+ if (scope != kFacevaryingScope && scope != kVaryingScope && scope != kVertexScope) {
return;
}
const array<int> *uv_loops = cache.uv_loops.data_for_time_no_check(time).get_data_or_null();
- if (!uv_loops) {
+ /* It's ok to not have loop indices, as long as the scope is not face-varying. */
+ if (!uv_loops && scope == kFacevaryingScope) {
return;
}
@@ -766,9 +767,27 @@ static void process_uvs(CachedData &cache,
const uint32_t *indices = sample.getIndices()->get();
const V2f *values = sample.getVals()->get();
- for (const int uv_loop_index : *uv_loops) {
- const uint32_t index = indices[uv_loop_index];
- *data_float2++ = make_float2(values[index][0], values[index][1]);
+ if (scope == kFacevaryingScope) {
+ for (const int uv_loop_index : *uv_loops) {
+ const uint32_t index = indices[uv_loop_index];
+ *data_float2++ = make_float2(values[index][0], values[index][1]);
+ }
+ }
+ else if (scope == kVaryingScope || scope == kVertexScope) {
+ if (triangles) {
+ for (size_t i = 0; i < triangles->size(); i++) {
+ const int3 t = (*triangles)[i];
+ *data_float2++ = make_float2(values[t.x][0], values[t.x][1]);
+ *data_float2++ = make_float2(values[t.y][0], values[t.y][1]);
+ *data_float2++ = make_float2(values[t.z][0], values[t.z][1]);
+ }
+ }
+ else if (corners) {
+ for (size_t i = 0; i < corners->size(); i++) {
+ const int c = (*corners)[i];
+ *data_float2++ = make_float2(values[c][0], values[c][1]);
+ }
+ }
}
attribute.data.add_data(data, time);
diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt
index 51cc47fa704..65a692acd03 100644
--- a/intern/cycles/test/CMakeLists.txt
+++ b/intern/cycles/test/CMakeLists.txt
@@ -15,7 +15,7 @@
if(WITH_GTESTS)
Include(GTestTesting)
- # Otherwise we get warnings here that we cant fix in external projects
+ # Otherwise we get warnings here that we can't fix in external projects
remove_strict_flags()
endif()
diff --git a/intern/cycles/util/util_color.h b/intern/cycles/util/util_color.h
index 203c0b289f6..40c2c431aca 100644
--- a/intern/cycles/util/util_color.h
+++ b/intern/cycles/util/util_color.h
@@ -223,12 +223,14 @@ ccl_device_inline ssef fastpow24(const ssef &arg)
ssef x = fastpow<0x3F4CCCCD, 0x4F55A7FB>(arg); // error max = 0.17 avg = 0.0018 |avg| = 0.05
ssef arg2 = arg * arg;
ssef arg4 = arg2 * arg2;
- x = improve_5throot_solution(x,
- arg4); /* error max = 0.018 avg = 0.0031 |avg| = 0.0031 */
- x = improve_5throot_solution(x,
- arg4); /* error max = 0.00021 avg = 1.6e-05 |avg| = 1.6e-05 */
- x = improve_5throot_solution(x,
- arg4); /* error max = 6.1e-07 avg = 5.2e-08 |avg| = 1.1e-07 */
+
+ /* error max = 0.018 avg = 0.0031 |avg| = 0.0031 */
+ x = improve_5throot_solution(x, arg4);
+ /* error max = 0.00021 avg = 1.6e-05 |avg| = 1.6e-05 */
+ x = improve_5throot_solution(x, arg4);
+ /* error max = 6.1e-07 avg = 5.2e-08 |avg| = 1.1e-07 */
+ x = improve_5throot_solution(x, arg4);
+
return x * (x * x);
}
diff --git a/intern/cycles/util/util_system.cpp b/intern/cycles/util/util_system.cpp
index 03bc5aea1dd..b010881058b 100644
--- a/intern/cycles/util/util_system.cpp
+++ b/intern/cycles/util/util_system.cpp
@@ -282,8 +282,8 @@ static CPUCapabilities &system_cpu_capabilities()
/* actual opcode for xgetbv */
__asm__(".byte 0x0f, 0x01, 0xd0" : "=a"(xcr_feature_mask), "=d"(edx) : "c"(0));
# elif defined(_MSC_VER) && defined(_XCR_XFEATURE_ENABLED_MASK)
- xcr_feature_mask = (uint32_t)_xgetbv(
- _XCR_XFEATURE_ENABLED_MASK); /* min VS2010 SP1 compiler is required */
+ /* Minimum VS2010 SP1 compiler is required. */
+ xcr_feature_mask = (uint32_t)_xgetbv(_XCR_XFEATURE_ENABLED_MASK);
# else
xcr_feature_mask = 0;
# endif
diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h
index 0c22cf82688..17e2a7dc05b 100644
--- a/intern/ffmpeg/ffmpeg_compat.h
+++ b/intern/ffmpeg/ffmpeg_compat.h
@@ -93,40 +93,21 @@ void my_guess_pkt_duration(AVFormatContext *s, AVStream *st, AVPacket *pkt)
#endif
FFMPEG_INLINE
-void my_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
+int64_t timestamp_from_pts_or_dts(int64_t pts, int64_t dts)
{
- int i;
-
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
-
- st->cur_dts = av_rescale(timestamp,
- st->time_base.den * (int64_t)ref_st->time_base.num,
- st->time_base.num * (int64_t)ref_st->time_base.den);
+ /* Some videos do not have any pts values, use dts instead in those cases if
+ * possible. Usually when this happens dts can act as pts because as all frames
+ * should then be presented in their decoded in order. IE pts == dts. */
+ if (pts == AV_NOPTS_VALUE) {
+ return dts;
}
+ return pts;
}
FFMPEG_INLINE
-void av_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
-{
- my_update_cur_dts(s, ref_st, timestamp);
-}
-
-FFMPEG_INLINE
-int64_t av_get_pts_from_frame(AVFormatContext *avctx, AVFrame *picture)
+int64_t av_get_pts_from_frame(AVFrame *picture)
{
- int64_t pts;
- pts = picture->pts;
-
- if (pts == AV_NOPTS_VALUE) {
- pts = picture->pkt_dts;
- }
- if (pts == AV_NOPTS_VALUE) {
- pts = 0;
- }
-
- (void)avctx;
- return pts;
+ return timestamp_from_pts_or_dts(picture->pts, picture->pkt_dts);
}
/* -------------------------------------------------------------------- */
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 1b5cdb3cda0..1815a46591a 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -370,6 +370,7 @@ elseif(WIN32)
intern/GHOST_DropTargetWin32.cpp
intern/GHOST_SystemWin32.cpp
intern/GHOST_WindowWin32.cpp
+ intern/GHOST_Wintab.cpp
intern/GHOST_ContextD3D.h
intern/GHOST_DisplayManagerWin32.h
@@ -377,6 +378,7 @@ elseif(WIN32)
intern/GHOST_SystemWin32.h
intern/GHOST_TaskbarWin32.h
intern/GHOST_WindowWin32.h
+ intern/GHOST_Wintab.h
)
if(NOT WITH_GL_EGL)
@@ -481,10 +483,12 @@ if(WITH_XR_OPENXR)
shlwapi
)
elseif(UNIX AND NOT APPLE)
- list(APPEND XR_PLATFORM_DEFINES
- -DXR_OS_LINUX
- -DXR_USE_PLATFORM_XLIB
- )
+ list(APPEND XR_PLATFORM_DEFINES -DXR_OS_LINUX)
+ if (WITH_GL_EGL)
+ list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_EGL)
+ else()
+ list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_XLIB)
+ endif()
endif()
add_definitions(-DWITH_XR_OPENXR ${XR_PLATFORM_DEFINES})
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index 3a8d0fbfecf..7efbd00c2eb 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -105,7 +105,9 @@ typedef enum {
typedef enum {
GHOST_kTabletAutomatic = 0,
- GHOST_kTabletNative,
+ /* Show as Windows Ink to users to match "Use Windows Ink" in tablet utilities,
+ * but we use the dependent Windows Pointer API. */
+ GHOST_kTabletWinPointer,
GHOST_kTabletWintab,
} GHOST_TTabletAPI;
@@ -168,7 +170,7 @@ typedef enum {
GHOST_kButtonMaskRight,
GHOST_kButtonMaskButton4,
GHOST_kButtonMaskButton5,
- /* Trackballs and programmable buttons */
+ /* Trackballs and programmable buttons. */
GHOST_kButtonMaskButton6,
GHOST_kButtonMaskButton7,
GHOST_kButtonNumMasks
@@ -177,15 +179,15 @@ typedef enum {
typedef enum {
GHOST_kEventUnknown = 0,
- GHOST_kEventCursorMove, /// Mouse move event
- GHOST_kEventButtonDown, /// Mouse button event
- GHOST_kEventButtonUp, /// Mouse button event
- GHOST_kEventWheel, /// Mouse wheel event
- GHOST_kEventTrackpad, /// Trackpad event
+ GHOST_kEventCursorMove, /* Mouse move event. */
+ GHOST_kEventButtonDown, /* Mouse button event. */
+ GHOST_kEventButtonUp, /* Mouse button event. */
+ GHOST_kEventWheel, /* Mouse wheel event. */
+ GHOST_kEventTrackpad, /* Trackpad event. */
#ifdef WITH_INPUT_NDOF
- GHOST_kEventNDOFMotion, /// N degree of freedom device motion event
- GHOST_kEventNDOFButton, /// N degree of freedom device button event
+ GHOST_kEventNDOFMotion, /* N degree of freedom device motion event. */
+ GHOST_kEventNDOFButton, /* N degree of freedom device button event. */
#endif
GHOST_kEventKeyDown,
@@ -207,8 +209,8 @@ typedef enum {
GHOST_kEventDraggingExited,
GHOST_kEventDraggingDropDone,
- GHOST_kEventOpenMainFile, // Needed for Cocoa to open double-clicked .blend file at startup
- GHOST_kEventNativeResolutionChange, // Needed for Cocoa when window moves to other display
+ GHOST_kEventOpenMainFile, /* Needed for Cocoa to open double-clicked .blend file at startup. */
+ GHOST_kEventNativeResolutionChange, /* Needed for Cocoa when window moves to other display. */
GHOST_kEventTimer,
@@ -281,7 +283,7 @@ typedef enum {
GHOST_kKeyPeriod = '.',
GHOST_kKeySlash = '/',
- // Number keys
+ /* Number keys. */
GHOST_kKey0 = '0',
GHOST_kKey1,
GHOST_kKey2,
@@ -296,7 +298,7 @@ typedef enum {
GHOST_kKeySemicolon = ';',
GHOST_kKeyEqual = '=',
- // Character keys
+ /* Character keys. */
GHOST_kKeyA = 'A',
GHOST_kKeyB,
GHOST_kKeyC,
@@ -335,9 +337,9 @@ typedef enum {
GHOST_kKeyRightControl,
GHOST_kKeyLeftAlt,
GHOST_kKeyRightAlt,
- GHOST_kKeyOS, // Command key on Apple, Windows key(s) on Windows
- GHOST_kKeyGrLess, // German PC only!
- GHOST_kKeyApp, /* Also known as menu key. */
+ GHOST_kKeyOS, /* Command key on Apple, Windows key(s) on Windows. */
+ GHOST_kKeyGrLess, /* German PC only! */
+ GHOST_kKeyApp, /* Also known as menu key. */
GHOST_kKeyCapsLock,
GHOST_kKeyNumLock,
@@ -358,7 +360,7 @@ typedef enum {
GHOST_kKeyUpPage,
GHOST_kKeyDownPage,
- // Numpad keys
+ /* Numpad keys. */
GHOST_kKeyNumpad0,
GHOST_kKeyNumpad1,
GHOST_kKeyNumpad2,
@@ -376,7 +378,7 @@ typedef enum {
GHOST_kKeyNumpadAsterisk,
GHOST_kKeyNumpadSlash,
- // Function keys
+ /* Function keys. */
GHOST_kKeyF1,
GHOST_kKeyF2,
GHOST_kKeyF3,
@@ -402,7 +404,7 @@ typedef enum {
GHOST_kKeyF23,
GHOST_kKeyF24,
- // Multimedia keypad buttons
+ /* Multimedia keypad buttons. */
GHOST_kKeyMediaPlay,
GHOST_kKeyMediaStop,
GHOST_kKeyMediaFirst,
@@ -479,9 +481,9 @@ typedef struct {
typedef enum {
GHOST_kDragnDropTypeUnknown = 0,
- GHOST_kDragnDropTypeFilenames, /*Array of strings representing file names (full path) */
- GHOST_kDragnDropTypeString, /* Unformatted text UTF-8 string */
- GHOST_kDragnDropTypeBitmap /*Bitmap image data */
+ GHOST_kDragnDropTypeFilenames, /* Array of strings representing file names (full path). */
+ GHOST_kDragnDropTypeString, /* Unformatted text UTF-8 string. */
+ GHOST_kDragnDropTypeBitmap /* Bitmap image data. */
} GHOST_TDragnDropTypes;
typedef struct {
@@ -527,18 +529,23 @@ typedef enum {
#ifdef WITH_INPUT_NDOF
typedef struct {
/** N-degree of freedom device data v3 [GSoC 2010] */
- // Each component normally ranges from -1 to +1, but can exceed that.
- // These use blender standard view coordinates, with positive rotations being CCW about the axis.
- float tx, ty, tz; // translation
- float rx, ry, rz; // rotation:
- // axis = (rx,ry,rz).normalized
- // amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg]
- float dt; // time since previous NDOF Motion event
- GHOST_TProgress progress; // Starting, InProgress or Finishing (for modal handlers)
+ /* Each component normally ranges from -1 to +1, but can exceed that.
+ * These use blender standard view coordinates,
+ * with positive rotations being CCW about the axis. */
+ /* translation: */
+ float tx, ty, tz;
+ /* rotation:
+ * - `axis = (rx,ry,rz).normalized`
+ * - `amount = (rx,ry,rz).magnitude` [in revolutions, 1.0 = 360 deg]. */
+ float rx, ry, rz;
+ /** Time since previous NDOF Motion event */
+ float dt;
+ /** Starting, #GHOST_kInProgress or #GHOST_kFinishing (for modal handlers) */
+ GHOST_TProgress progress;
} GHOST_TEventNDOFMotionData;
typedef enum { GHOST_kPress, GHOST_kRelease } GHOST_TButtonAction;
-// good for mouse or other buttons too, hmmm?
+/* Good for mouse or other buttons too, hmmm? */
typedef struct {
GHOST_TButtonAction action;
@@ -561,7 +568,7 @@ typedef struct {
*/
/** The ascii code for the key event ('\0' if none). */
char ascii;
- /** The unicode character. if the length is 6, not NULL terminated if all 6 are set */
+ /** The unicode character. if the length is 6, not NULL terminated if all 6 are set. */
char utf8_buf[6];
/** Generated by auto-repeat. */
@@ -594,7 +601,8 @@ typedef void *GHOST_TEmbedderWindowID;
#endif // _WIN32
#ifndef _WIN32
-// I can't use "Window" from "<X11/Xlib.h>" because it conflits with Window defined in winlay.h
+/* I can't use "Window" from `X11/Xlib.h`
+ * because it conflicts with Window defined in `winlay.h`. */
typedef int GHOST_TEmbedderWindowID;
#endif // _WIN32
@@ -642,8 +650,10 @@ typedef void *(*GHOST_XrGraphicsContextBindFn)(void);
typedef void (*GHOST_XrGraphicsContextUnbindFn)(GHOST_ContextHandle graphics_context);
typedef void (*GHOST_XrDrawViewFn)(const struct GHOST_XrDrawViewInfo *draw_view, void *customdata);
-/* An array of GHOST_TXrGraphicsBinding items defining the candidate bindings to use. The first
- * available candidate will be chosen, so order defines priority. */
+/**
+ * An array of #GHOST_TXrGraphicsBinding items defining the candidate bindings to use.
+ * The first available candidate will be chosen, so order defines priority.
+ */
typedef const GHOST_TXrGraphicsBinding *GHOST_XrGraphicsBindingCandidates;
typedef struct {
@@ -684,7 +694,7 @@ typedef struct GHOST_XrDrawViewInfo {
float angle_up, angle_down;
} fov;
- /** Set if the buffer should be submitted with a srgb transfer applied. */
+ /** Set if the buffer should be submitted with a SRGB transfer applied. */
char expects_srgb_buffer;
} GHOST_XrDrawViewInfo;
@@ -734,7 +744,7 @@ typedef struct GHOST_XrActionSpaceInfo {
typedef struct GHOST_XrActionBindingInfo {
const char *action_name;
GHOST_TUns32 count_interaction_paths;
- /** Interaction path: User (subaction) path + component path. */
+ /** Interaction path: User (sub-action) path + component path. */
const char **interaction_paths;
} GHOST_XrActionBindingInfo;
diff --git a/intern/ghost/intern/GHOST_ContextEGL.cpp b/intern/ghost/intern/GHOST_ContextEGL.cpp
index bac7057d953..770ead5962e 100644
--- a/intern/ghost/intern/GHOST_ContextEGL.cpp
+++ b/intern/ghost/intern/GHOST_ContextEGL.cpp
@@ -149,6 +149,9 @@ static bool egl_chk(bool result, const char *file = NULL, int line = 0, const ch
static_cast<unsigned int>(error),
code ? code : "<Unknown>",
msg ? msg : "<Unknown>");
+ (void)(file);
+ (void)(line);
+ (void)(text);
#endif
}
@@ -285,6 +288,21 @@ GHOST_TSuccess GHOST_ContextEGL::getSwapInterval(int &intervalOut)
return GHOST_kSuccess;
}
+EGLDisplay GHOST_ContextEGL::getDisplay() const
+{
+ return m_display;
+}
+
+EGLConfig GHOST_ContextEGL::getConfig() const
+{
+ return m_config;
+}
+
+EGLContext GHOST_ContextEGL::getContext() const
+{
+ return m_context;
+}
+
GHOST_TSuccess GHOST_ContextEGL::activateDrawingContext()
{
if (m_display) {
@@ -456,9 +474,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
attrib_list.push_back(EGL_NONE);
- EGLConfig config;
-
- if (!EGL_CHK(::eglChooseConfig(m_display, &(attrib_list[0]), &config, 1, &num_config)))
+ if (!EGL_CHK(::eglChooseConfig(m_display, &(attrib_list[0]), &m_config, 1, &num_config)))
goto error;
// A common error is to assume that ChooseConfig worked because it returned EGL_TRUE
@@ -466,7 +482,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
goto error;
if (m_nativeWindow != 0) {
- m_surface = ::eglCreateWindowSurface(m_display, config, m_nativeWindow, NULL);
+ m_surface = ::eglCreateWindowSurface(m_display, m_config, m_nativeWindow, NULL);
}
else {
static const EGLint pb_attrib_list[] = {
@@ -476,7 +492,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
1,
EGL_NONE,
};
- m_surface = ::eglCreatePbufferSurface(m_display, config, pb_attrib_list);
+ m_surface = ::eglCreatePbufferSurface(m_display, m_config, pb_attrib_list);
}
if (!EGL_CHK(m_surface != EGL_NO_SURFACE))
@@ -577,7 +593,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
attrib_list.push_back(EGL_NONE);
- m_context = ::eglCreateContext(m_display, config, m_sharedContext, &(attrib_list[0]));
+ m_context = ::eglCreateContext(m_display, m_config, m_sharedContext, &(attrib_list[0]));
if (!EGL_CHK(m_context != EGL_NO_CONTEXT))
goto error;
diff --git a/intern/ghost/intern/GHOST_ContextEGL.h b/intern/ghost/intern/GHOST_ContextEGL.h
index f828271d88d..170647177c2 100644
--- a/intern/ghost/intern/GHOST_ContextEGL.h
+++ b/intern/ghost/intern/GHOST_ContextEGL.h
@@ -36,6 +36,9 @@
#endif
class GHOST_ContextEGL : public GHOST_Context {
+ /* XR code needs low level graphics data to send to OpenXR. */
+ friend class GHOST_XrGraphicsBindingOpenGL;
+
public:
/**
* Constructor.
@@ -100,6 +103,12 @@ class GHOST_ContextEGL : public GHOST_Context {
*/
GHOST_TSuccess getSwapInterval(int &intervalOut);
+ EGLDisplay getDisplay() const;
+
+ EGLConfig getConfig() const;
+
+ EGLContext getContext() const;
+
private:
bool initContextEGLEW();
@@ -117,6 +126,7 @@ class GHOST_ContextEGL : public GHOST_Context {
EGLContext m_context;
EGLSurface m_surface;
EGLDisplay m_display;
+ EGLConfig m_config;
EGLint m_swap_interval;
diff --git a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp
index b836f256b27..11134ad1054 100644
--- a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp
+++ b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp
@@ -160,7 +160,7 @@ GHOST_TSuccess GHOST_DisplayManagerSDL::setCurrentDisplaySetting(
else {
/* this is a problem for the BGE player :S, perhaps SDL2 will resolve at some point.
* we really need SDL_SetDisplayModeForDisplay() to become an API func! - campbell */
- printf("no windows available, cant fullscreen\n");
+ printf("no windows available, can't fullscreen\n");
/* do not fail, we will try again later when the window is created - wander */
return GHOST_kSuccess;
diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
index e9e688b76ab..5508d34e96c 100644
--- a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
+++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
@@ -31,7 +31,11 @@ class GHOST_IXrGraphicsBinding {
public:
union {
#if defined(WITH_GHOST_X11)
+# if defined(WITH_GL_EGL)
+ XrGraphicsBindingEGLMNDX egl;
+# else
XrGraphicsBindingOpenGLXlibKHR glx;
+# endif
#elif defined(WIN32)
XrGraphicsBindingOpenGLWin32KHR wgl;
XrGraphicsBindingD3D11KHR d3d11;
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index 2a7123b293e..9915520691f 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -239,7 +239,7 @@ class GHOST_System : public GHOST_ISystem {
* Set which tablet API to use. Only affects Windows, other platforms have a single API.
* \param api: Enum indicating which API to use.
*/
- void setTabletAPI(GHOST_TTabletAPI api);
+ virtual void setTabletAPI(GHOST_TTabletAPI api) override;
GHOST_TTabletAPI getTabletAPI(void);
#ifdef WITH_INPUT_NDOF
diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp
index 8862d0457f2..eccef18bee2 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.cpp
+++ b/intern/ghost/intern/GHOST_SystemSDL.cpp
@@ -376,7 +376,7 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis());
window->getCursorGrabAccum(x_accum, y_accum);
- // cant use setCursorPosition because the mouse may have no focus!
+ // can't use setCursorPosition because the mouse may have no focus!
if (x_new != x_root || y_new != y_root) {
if (1) { //xme.time > m_last_warp) {
/* when wrapping we don't need to add an event because the
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index c3a243cc22c..a70e5b91674 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -139,7 +139,6 @@ static void initRawInput()
#undef DEVICE_COUNT
}
-typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS);
typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND);
GHOST_SystemWin32::GHOST_SystemWin32()
@@ -867,15 +866,151 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
{
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
- if (type == GHOST_kEventButtonDown) {
- window->updateMouseCapture(MousePressed);
+ GHOST_TabletData td = window->getTabletData();
+
+ /* Move mouse to button event position. */
+ if (window->getTabletData().Active != GHOST_kTabletModeNone) {
+ /* Tablet should be handling in between mouse moves, only move to event position. */
+ DWORD msgPos = ::GetMessagePos();
+ int msgPosX = GET_X_LPARAM(msgPos);
+ int msgPosY = GET_Y_LPARAM(msgPos);
+ system->pushEvent(new GHOST_EventCursor(
+ ::GetMessageTime(), GHOST_kEventCursorMove, window, msgPosX, msgPosY, td));
}
- else if (type == GHOST_kEventButtonUp) {
- window->updateMouseCapture(MouseReleased);
+
+ window->updateMouseCapture(type == GHOST_kEventButtonDown ? MousePressed : MouseReleased);
+ return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask, td);
+}
+
+void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
+{
+ GHOST_Wintab *wt = window->getWintab();
+ if (!wt) {
+ return;
}
- return new GHOST_EventButton(
- system->getMilliSeconds(), type, window, mask, window->getTabletData());
+ GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
+
+ std::vector<GHOST_WintabInfoWin32> wintabInfo;
+ wt->getInput(wintabInfo);
+
+ /* Wintab provided coordinates are untrusted until a Wintab and Win32 button down event match.
+ * This is checked on every button down event, and revoked if there is a mismatch. This can
+ * happen when Wintab incorrectly scales cursor position or is in mouse mode.
+ *
+ * If Wintab was never trusted while processing this Win32 event, a fallback Ghost cursor move
+ * event is created at the position of the Win32 WT_PACKET event. */
+ bool mouseMoveHandled;
+ bool useWintabPos;
+ mouseMoveHandled = useWintabPos = wt->trustCoordinates();
+
+ for (GHOST_WintabInfoWin32 &info : wintabInfo) {
+ switch (info.type) {
+ case GHOST_kEventCursorMove: {
+ if (!useWintabPos) {
+ continue;
+ }
+
+ wt->mapWintabToSysCoordinates(info.x, info.y, info.x, info.y);
+ system->pushEvent(new GHOST_EventCursor(
+ info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData));
+
+ break;
+ }
+ case GHOST_kEventButtonDown: {
+ UINT message;
+ switch (info.button) {
+ case GHOST_kButtonMaskLeft:
+ message = WM_LBUTTONDOWN;
+ break;
+ case GHOST_kButtonMaskRight:
+ message = WM_RBUTTONDOWN;
+ break;
+ case GHOST_kButtonMaskMiddle:
+ message = WM_MBUTTONDOWN;
+ break;
+ default:
+ continue;
+ }
+
+ /* Wintab buttons are modal, but the API does not inform us what mode a pressed button is
+ * in. Only issue button events if we can steal an equivalent Win32 button event from the
+ * event queue. */
+ MSG msg;
+ if (PeekMessage(&msg, window->getHWND(), message, message, PM_NOYIELD) &&
+ msg.message != WM_QUIT) {
+
+ /* Test for Win32/Wintab button down match. */
+ useWintabPos = wt->testCoordinates(msg.pt.x, msg.pt.y, info.x, info.y);
+ if (!useWintabPos) {
+ continue;
+ }
+
+ /* Steal the Win32 event which was previously peeked. */
+ PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD);
+
+ /* Move cursor to button location, to prevent incorrect cursor position when
+ * transitioning from unsynchronized Win32 to Wintab cursor control. */
+ wt->mapWintabToSysCoordinates(info.x, info.y, info.x, info.y);
+ system->pushEvent(new GHOST_EventCursor(
+ info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData));
+
+ window->updateMouseCapture(MousePressed);
+ system->pushEvent(
+ new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
+
+ mouseMoveHandled = true;
+ break;
+ }
+ }
+ case GHOST_kEventButtonUp: {
+ if (!useWintabPos) {
+ continue;
+ }
+
+ UINT message;
+ switch (info.button) {
+ case GHOST_kButtonMaskLeft:
+ message = WM_LBUTTONUP;
+ break;
+ case GHOST_kButtonMaskRight:
+ message = WM_RBUTTONUP;
+ break;
+ case GHOST_kButtonMaskMiddle:
+ message = WM_MBUTTONUP;
+ break;
+ default:
+ continue;
+ }
+
+ /* Wintab buttons are modal, but the API does not inform us what mode a pressed button is
+ * in. Only issue button events if we can steal an equivalent Win32 button event from the
+ * event queue. */
+ MSG msg;
+ if (PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD) &&
+ msg.message != WM_QUIT) {
+
+ window->updateMouseCapture(MouseReleased);
+ system->pushEvent(
+ new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Fallback cursor movement if Wintab position were never trusted while processing this event. */
+ if (!mouseMoveHandled) {
+ DWORD pos = GetMessagePos();
+ int x = GET_X_LPARAM(pos);
+ int y = GET_Y_LPARAM(pos);
+
+ /* TODO supply tablet data */
+ system->pushEvent(new GHOST_EventCursor(
+ system->getMilliSeconds(), GHOST_kEventCursorMove, window, x, y, GHOST_TABLET_DATA_NONE));
+ }
}
void GHOST_SystemWin32::processPointerEvent(
@@ -883,7 +1018,7 @@ void GHOST_SystemWin32::processPointerEvent(
{
/* Pointer events might fire when changing windows for a device which is set to use Wintab, even
* when when Wintab is left enabled but set to the bottom of Wintab overlap order. */
- if (!window->useTabletAPI(GHOST_kTabletNative)) {
+ if (!window->usingTabletAPI(GHOST_kTabletWinPointer)) {
return;
}
@@ -894,20 +1029,21 @@ void GHOST_SystemWin32::processPointerEvent(
return;
}
- if (!pointerInfo[0].isPrimary) {
- eventHandled = true;
- return; // For multi-touch displays we ignore these events
- }
-
switch (type) {
- case WM_POINTERENTER:
- window->m_tabletInRange = true;
- system->pushEvent(new GHOST_EventCursor(pointerInfo[0].time,
- GHOST_kEventCursorMove,
- window,
- pointerInfo[0].pixelLocation.x,
- pointerInfo[0].pixelLocation.y,
- pointerInfo[0].tabletData));
+ case WM_POINTERUPDATE:
+ /* Coalesced pointer events are reverse chronological order, reorder chronologically.
+ * Only contiguous move events are coalesced. */
+ for (GHOST_TUns32 i = pointerInfo.size(); i-- > 0;) {
+ system->pushEvent(new GHOST_EventCursor(pointerInfo[i].time,
+ GHOST_kEventCursorMove,
+ window,
+ pointerInfo[i].pixelLocation.x,
+ pointerInfo[i].pixelLocation.y,
+ pointerInfo[i].tabletData));
+ }
+
+ /* Leave event unhandled so that system cursor is moved. */
+
break;
case WM_POINTERDOWN:
/* Move cursor to point of contact because GHOST_EventButton does not include position. */
@@ -923,18 +1059,10 @@ void GHOST_SystemWin32::processPointerEvent(
pointerInfo[0].buttonMask,
pointerInfo[0].tabletData));
window->updateMouseCapture(MousePressed);
- break;
- case WM_POINTERUPDATE:
- /* Coalesced pointer events are reverse chronological order, reorder chronologically.
- * Only contiguous move events are coalesced. */
- for (GHOST_TUns32 i = pointerInfo.size(); i-- > 0;) {
- system->pushEvent(new GHOST_EventCursor(pointerInfo[i].time,
- GHOST_kEventCursorMove,
- window,
- pointerInfo[i].pixelLocation.x,
- pointerInfo[i].pixelLocation.y,
- pointerInfo[i].tabletData));
- }
+
+ /* Mark event handled so that mouse button events are not generated. */
+ eventHandled = true;
+
break;
case WM_POINTERUP:
system->pushEvent(new GHOST_EventButton(pointerInfo[0].time,
@@ -943,16 +1071,14 @@ void GHOST_SystemWin32::processPointerEvent(
pointerInfo[0].buttonMask,
pointerInfo[0].tabletData));
window->updateMouseCapture(MouseReleased);
- break;
- case WM_POINTERLEAVE:
- window->m_tabletInRange = false;
+
+ /* Mark event handled so that mouse button events are not generated. */
+ eventHandled = true;
+
break;
default:
break;
}
-
- eventHandled = true;
- system->setCursorPosition(pointerInfo[0].pixelLocation.x, pointerInfo[0].pixelLocation.y);
}
GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window)
@@ -960,18 +1086,14 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
GHOST_TInt32 x_screen, y_screen;
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
- if (window->m_tabletInRange) {
- if (window->useTabletAPI(GHOST_kTabletNative)) {
- /* Tablet input handled in WM_POINTER* events. WM_MOUSEMOVE events in response to tablet
- * input aren't normally generated when using WM_POINTER events, but manually moving the
- * system cursor as we do in WM_POINTER handling does. */
- return NULL;
- }
+ if (window->getTabletData().Active != GHOST_kTabletModeNone) {
+ /* While pen devices are in range, cursor movement is handled by tablet input processing. */
+ return NULL;
}
system->getCursorPosition(x_screen, y_screen);
- if (window->getCursorGrabModeIsWarp() && !window->m_tabletInRange) {
+ if (window->getCursorGrabModeIsWarp()) {
GHOST_TInt32 x_new = x_screen;
GHOST_TInt32 y_new = y_screen;
GHOST_TInt32 x_accum, y_accum;
@@ -983,7 +1105,7 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
}
/* Could also clamp to screen bounds wrap with a window outside the view will fail atm.
- * Use offset of 8 in case the window is at screen bounds. */
+ * Use inset in case the window is at screen bounds. */
bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis());
window->getCursorGrabAccum(x_accum, y_accum);
@@ -999,7 +1121,7 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
window,
x_screen + x_accum,
y_screen + y_accum,
- window->getTabletData());
+ GHOST_TABLET_DATA_NONE);
}
}
else {
@@ -1008,7 +1130,7 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
window,
x_screen,
y_screen,
- window->getTabletData());
+ GHOST_TABLET_DATA_NONE);
}
return NULL;
}
@@ -1118,6 +1240,23 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA
return event;
}
+GHOST_Event *GHOST_SystemWin32::processWindowSizeEvent(GHOST_WindowWin32 *window)
+{
+ GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
+ GHOST_Event *sizeEvent = new GHOST_Event(
+ system->getMilliSeconds(), GHOST_kEventWindowSize, window);
+
+ /* We get WM_SIZE before we fully init. Do not dispatch before we are continuously resizing. */
+ if (window->m_inLiveResize) {
+ system->pushEvent(sizeEvent);
+ system->dispatchEvents();
+ return NULL;
+ }
+ else {
+ return sizeEvent;
+ }
+}
+
GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type,
GHOST_WindowWin32 *window)
{
@@ -1125,7 +1264,6 @@ GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type,
if (type == GHOST_kEventWindowActivate) {
system->getWindowManager()->setActiveWindow(window);
- window->bringTabletContextToFront();
}
return new GHOST_Event(system->getMilliSeconds(), type, window);
@@ -1153,6 +1291,31 @@ GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType,
system->getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, data));
}
+void GHOST_SystemWin32::setTabletAPI(GHOST_TTabletAPI api)
+{
+ GHOST_System::setTabletAPI(api);
+
+ /* If API is set to WinPointer (Windows Ink), unload Wintab so that trouble drivers don't disable
+ * Windows Ink. Load Wintab when API is Automatic because decision logic relies on knowing
+ * whether a Wintab device is present. */
+ const bool loadWintab = GHOST_kTabletWinPointer != api;
+ GHOST_WindowManager *wm = getWindowManager();
+
+ for (GHOST_IWindow *win : wm->getWindows()) {
+ GHOST_WindowWin32 *windowWin32 = (GHOST_WindowWin32 *)win;
+ if (loadWintab) {
+ windowWin32->loadWintab(GHOST_kWindowStateMinimized != windowWin32->getState());
+
+ if (windowWin32->usingTabletAPI(GHOST_kTabletWintab)) {
+ windowWin32->resetPointerPenInfo();
+ }
+ }
+ else {
+ windowWin32->closeWintab();
+ }
+ }
+}
+
void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO *minmax)
{
minmax->ptMinTrackSize.x = 320;
@@ -1387,33 +1550,100 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
case SC_KEYMENU:
eventHandled = true;
break;
- case SC_RESTORE:
+ case SC_RESTORE: {
::ShowWindow(hwnd, SW_RESTORE);
window->setState(window->getState());
+
+ GHOST_Wintab *wt = window->getWintab();
+ if (wt) {
+ wt->enable();
+ }
+
eventHandled = true;
break;
+ }
+ case SC_MAXIMIZE: {
+ GHOST_Wintab *wt = window->getWintab();
+ if (wt) {
+ wt->enable();
+ }
+ /* Don't report event as handled so that default handling occurs. */
+ break;
+ }
+ case SC_MINIMIZE: {
+ GHOST_Wintab *wt = window->getWintab();
+ if (wt) {
+ wt->disable();
+ }
+ /* Don't report event as handled so that default handling occurs. */
+ break;
+ }
}
break;
////////////////////////////////////////////////////////////////////////
// Wintab events, processed
////////////////////////////////////////////////////////////////////////
- case WT_PACKET:
- window->processWin32TabletEvent(wParam, lParam);
+ case WT_CSRCHANGE: {
+ GHOST_Wintab *wt = window->getWintab();
+ if (wt) {
+ wt->updateCursorInfo();
+ }
+ eventHandled = true;
+ break;
+ }
+ case WT_PROXIMITY: {
+ GHOST_Wintab *wt = window->getWintab();
+ if (wt) {
+ bool inRange = LOWORD(lParam);
+ if (inRange) {
+ /* Some devices don't emit WT_CSRCHANGE events, so update cursor info here. */
+ wt->updateCursorInfo();
+ }
+ else {
+ wt->leaveRange();
+ }
+ }
+ eventHandled = true;
break;
- case WT_CSRCHANGE:
- case WT_PROXIMITY:
- window->processWin32TabletInitEvent();
+ }
+ case WT_INFOCHANGE: {
+ GHOST_Wintab *wt = window->getWintab();
+ if (wt) {
+ wt->processInfoChange(lParam);
+
+ if (window->usingTabletAPI(GHOST_kTabletWintab)) {
+ window->resetPointerPenInfo();
+ }
+ }
+ eventHandled = true;
+ break;
+ }
+ case WT_PACKET:
+ processWintabEvent(window);
+ eventHandled = true;
break;
////////////////////////////////////////////////////////////////////////
// Pointer events, processed
////////////////////////////////////////////////////////////////////////
- case WM_POINTERENTER:
- case WM_POINTERDOWN:
case WM_POINTERUPDATE:
+ case WM_POINTERDOWN:
case WM_POINTERUP:
- case WM_POINTERLEAVE:
processPointerEvent(msg, window, wParam, lParam, eventHandled);
break;
+ case WM_POINTERLEAVE: {
+ GHOST_TUns32 pointerId = GET_POINTERID_WPARAM(wParam);
+ POINTER_INFO pointerInfo;
+ if (!GetPointerInfo(pointerId, &pointerInfo)) {
+ break;
+ }
+
+ /* Reset pointer pen info if pen device has left tracking range. */
+ if (pointerInfo.pointerType == PT_PEN && !IS_POINTER_INRANGE_WPARAM(wParam)) {
+ window->resetPointerPenInfo();
+ eventHandled = true;
+ }
+ break;
+ }
////////////////////////////////////////////////////////////////////////
// Mouse events, processed
////////////////////////////////////////////////////////////////////////
@@ -1452,7 +1682,20 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
break;
case WM_MOUSEMOVE:
+ if (!window->m_mousePresent) {
+ TRACKMOUSEEVENT tme = {sizeof(tme)};
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hwnd;
+ TrackMouseEvent(&tme);
+ window->m_mousePresent = true;
+ GHOST_Wintab *wt = window->getWintab();
+ if (wt) {
+ wt->gainFocus();
+ }
+ }
+
event = processCursorEvent(window);
+
break;
case WM_MOUSEWHEEL: {
/* The WM_MOUSEWHEEL message is sent to the focus window
@@ -1487,7 +1730,17 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
window->loadCursor(true, GHOST_kStandardCursorDefault);
}
break;
-
+ case WM_MOUSELEAVE: {
+ window->m_mousePresent = false;
+ if (window->getTabletData().Active == GHOST_kTabletModeNone) {
+ processCursorEvent(window);
+ }
+ GHOST_Wintab *wt = window->getWintab();
+ if (wt) {
+ wt->loseFocus();
+ }
+ break;
+ }
////////////////////////////////////////////////////////////////////////
// Mouse events, ignored
////////////////////////////////////////////////////////////////////////
@@ -1535,7 +1788,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* will not be dispatched to OUR active window if we minimize one of OUR windows. */
if (LOWORD(wParam) == WA_INACTIVE)
window->lostMouseCapture();
- window->processWin32TabletActivateEvent(GET_WM_ACTIVATE_STATE(wParam, lParam));
+
lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
break;
}
@@ -1577,6 +1830,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
/* Let DefWindowProc handle it. */
break;
case WM_SIZING:
+ event = processWindowSizeEvent(window);
+ break;
case WM_SIZE:
/* The WM_SIZE message is sent to a window after its size has changed.
* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
@@ -1584,15 +1839,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* to perform any move or size change processing during the WM_WINDOWPOSCHANGED
* message without calling DefWindowProc.
*/
- /* we get first WM_SIZE before we fully init.
- * So, do not dispatch before we continuously resizing. */
- if (window->m_inLiveResize) {
- system->pushEvent(processWindowEvent(GHOST_kEventWindowSize, window));
- system->dispatchEvents();
- }
- else {
- event = processWindowEvent(GHOST_kEventWindowSize, window);
- }
+ event = processWindowSizeEvent(window);
break;
case WM_CAPTURECHANGED:
window->lostMouseCapture();
@@ -1643,6 +1890,21 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
SWP_NOZORDER | SWP_NOACTIVATE);
}
break;
+ case WM_DISPLAYCHANGE: {
+ GHOST_Wintab *wt = window->getWintab();
+ if (wt) {
+ wt->remapCoordinates();
+ }
+ break;
+ }
+ case WM_KILLFOCUS:
+ /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard
+ * focus. We want to prevent this if a window is still active and it loses focus to
+ * nowhere. */
+ if (!wParam && hwnd == ::GetActiveWindow()) {
+ ::SetFocus(hwnd);
+ }
+ break;
////////////////////////////////////////////////////////////////////////
// Window events, ignored
////////////////////////////////////////////////////////////////////////
@@ -1679,12 +1941,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* object associated with the window.
*/
break;
- case WM_KILLFOCUS:
- /* The WM_KILLFOCUS message is sent to a window immediately before it loses the
- * keyboard focus. We want to prevent this if a window is still active and it loses
- * focus to nowhere. */
- if (!wParam && hwnd == ::GetActiveWindow())
- ::SetFocus(hwnd);
case WM_SHOWWINDOW:
/* The WM_SHOWWINDOW message is sent to a window when the window is
* about to be hidden or shown. */
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 51c0c984710..7dd61421d4c 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -265,6 +265,16 @@ class GHOST_SystemWin32 : public GHOST_System {
int mouseY,
void *data);
+ /***************************************************************************************
+ ** Modify tablet API
+ ***************************************************************************************/
+
+ /**
+ * Set which tablet API to use.
+ * \param api: Enum indicating which API to use.
+ */
+ void setTabletAPI(GHOST_TTabletAPI api) override;
+
protected:
/**
* Initializes the system.
@@ -309,6 +319,12 @@ class GHOST_SystemWin32 : public GHOST_System {
GHOST_TButtonMask mask);
/**
+ * Creates tablet events from Wintab events.
+ * \param window: The window receiving the event (the active window).
+ */
+ static void processWintabEvent(GHOST_WindowWin32 *window);
+
+ /**
* Creates tablet events from pointer events.
* \param type: The type of pointer event.
* \param window: The window receiving the event (the active window).
@@ -352,6 +368,13 @@ class GHOST_SystemWin32 : public GHOST_System {
GHOST_TKey processSpecialKey(short vKey, short scanCode) const;
/**
+ * Creates a window size event.
+ * \param window: The window receiving the event (the active window).
+ * \return The event created.
+ */
+ static GHOST_Event *processWindowSizeEvent(GHOST_WindowWin32 *window);
+
+ /**
* Creates a window event.
* \param type: The type of event to create.
* \param window: The window receiving the event (the active window).
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index df7e8ba59df..34b44fde48a 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -2563,7 +2563,7 @@ static bool is_filler_char(char c)
return isspace(c) || c == '_' || c == '-' || c == ';' || c == ':';
}
-/* These C functions are copied from Wine 3.12's wintab.c */
+/* These C functions are copied from Wine 3.12's `wintab.c` */
static bool match_token(const char *haystack, const char *needle)
{
const char *h, *n;
diff --git a/intern/ghost/intern/GHOST_WindowSDL.cpp b/intern/ghost/intern/GHOST_WindowSDL.cpp
index dcb1ab8c78c..ff0c506feb7 100644
--- a/intern/ghost/intern/GHOST_WindowSDL.cpp
+++ b/intern/ghost/intern/GHOST_WindowSDL.cpp
@@ -556,7 +556,7 @@ static SDL_Cursor *sdl_ghost_CreateCursor(
return cursor;
}
-/* TODO, this is currently never freed but it wont leak either. */
+/* TODO, this is currently never freed but it won't leak either. */
static SDL_Cursor *getStandardCursorShape(GHOST_TStandardCursor shape)
{
if (sdl_std_cursor_array[0] == NULL) {
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index eeafe333633..33c79daf11d 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -21,8 +21,6 @@
* \ingroup GHOST
*/
-#define _USE_MATH_DEFINES
-
#include "GHOST_WindowWin32.h"
#include "GHOST_ContextD3D.h"
#include "GHOST_ContextNone.h"
@@ -72,7 +70,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
bool is_debug,
bool dialog)
: GHOST_Window(width, height, state, wantStereoVisual, false),
- m_tabletInRange(false),
+ m_mousePresent(false),
m_inLiveResize(false),
m_system(system),
m_hDC(0),
@@ -82,6 +80,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_nPressedButtons(0),
m_customCursor(0),
m_wantAlphaBackground(alphaBackground),
+ m_wintab(NULL),
+ m_lastPointerTabletData(GHOST_TABLET_DATA_NONE),
m_normal_state(GHOST_kWindowStateNormal),
m_user32(NULL),
m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP),
@@ -90,10 +90,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
RECT win_rect = {left, top, (long)(left + width), (long)(top + height)};
- // Initialize tablet variables
- memset(&m_wintab, 0, sizeof(m_wintab));
- m_tabletData = GHOST_TABLET_DATA_NONE;
-
DWORD style = parentwindow ?
WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX :
WS_OVERLAPPEDWINDOW;
@@ -218,65 +214,10 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
}
// Initialize Wintab
- m_wintab.handle = ::LoadLibrary("Wintab32.dll");
- if (m_wintab.handle && m_system->getTabletAPI() != GHOST_kTabletNative) {
- // Get API functions
- m_wintab.info = (GHOST_WIN32_WTInfo)::GetProcAddress(m_wintab.handle, "WTInfoA");
- m_wintab.open = (GHOST_WIN32_WTOpen)::GetProcAddress(m_wintab.handle, "WTOpenA");
- m_wintab.close = (GHOST_WIN32_WTClose)::GetProcAddress(m_wintab.handle, "WTClose");
- m_wintab.packet = (GHOST_WIN32_WTPacket)::GetProcAddress(m_wintab.handle, "WTPacket");
- m_wintab.enable = (GHOST_WIN32_WTEnable)::GetProcAddress(m_wintab.handle, "WTEnable");
- m_wintab.overlap = (GHOST_WIN32_WTOverlap)::GetProcAddress(m_wintab.handle, "WTOverlap");
-
- // Let's see if we can initialize tablet here.
- // Check if WinTab available by getting system context info.
- LOGCONTEXT lc = {0};
- lc.lcOptions |= CXO_SYSTEM;
- if (m_wintab.open && m_wintab.info && m_wintab.info(WTI_DEFSYSCTX, 0, &lc)) {
- // Now init the tablet
- /* The maximum tablet size, pressure and orientation (tilt) */
- AXIS TabletX, TabletY, Pressure, Orientation[3];
-
- // Open a Wintab context
-
- // Open the context
- lc.lcPktData = PACKETDATA;
- lc.lcPktMode = PACKETMODE;
- lc.lcOptions |= CXO_MESSAGES;
- lc.lcMoveMask = PACKETDATA;
-
- /* Set the entire tablet as active */
- m_wintab.info(WTI_DEVICES, DVC_X, &TabletX);
- m_wintab.info(WTI_DEVICES, DVC_Y, &TabletY);
-
- /* get the max pressure, to divide into a float */
- BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
- if (pressureSupport)
- m_wintab.maxPressure = Pressure.axMax;
- else
- m_wintab.maxPressure = 0;
-
- /* get the max tilt axes, to divide into floats */
- BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
- if (tiltSupport) {
- /* does the tablet support azimuth ([0]) and altitude ([1]) */
- if (Orientation[0].axResolution && Orientation[1].axResolution) {
- /* all this assumes the minimum is 0 */
- m_wintab.maxAzimuth = Orientation[0].axMax;
- m_wintab.maxAltitude = Orientation[1].axMax;
- }
- else { /* No so don't do tilt stuff. */
- m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
- }
- }
-
- // The Wintab spec says we must open the context disabled if we are using cursor masks.
- m_wintab.tablet = m_wintab.open(m_hWnd, &lc, FALSE);
- if (m_wintab.enable && m_wintab.tablet) {
- m_wintab.enable(m_wintab.tablet, TRUE);
- }
- }
+ if (system->getTabletAPI() != GHOST_kTabletWinPointer) {
+ loadWintab(GHOST_kWindowStateMinimized != state);
}
+
CoCreateInstance(
CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
}
@@ -289,14 +230,7 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
m_Bar = NULL;
}
- if (m_wintab.handle) {
- if (m_wintab.close && m_wintab.tablet) {
- m_wintab.close(m_wintab.tablet);
- }
-
- FreeLibrary(m_wintab.handle);
- memset(&m_wintab, 0, sizeof(m_wintab));
- }
+ closeWintab();
if (m_user32) {
FreeLibrary(m_user32);
@@ -913,20 +847,16 @@ GHOST_TSuccess GHOST_WindowWin32::hasCursorShape(GHOST_TStandardCursor cursorSha
GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
std::vector<GHOST_PointerInfoWin32> &outPointerInfo, WPARAM wParam, LPARAM lParam)
{
- if (!useTabletAPI(GHOST_kTabletNative)) {
- return GHOST_kFailure;
- }
-
GHOST_TInt32 pointerId = GET_POINTERID_WPARAM(wParam);
GHOST_TInt32 isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam);
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
- GHOST_TUns32 outCount;
+ GHOST_TUns32 outCount = 0;
- if (!(GetPointerInfoHistory(pointerId, &outCount, NULL))) {
+ if (!(GetPointerPenInfoHistory(pointerId, &outCount, NULL))) {
return GHOST_kFailure;
}
- auto pointerPenInfo = std::vector<POINTER_PEN_INFO>(outCount);
+ std::vector<POINTER_PEN_INFO> pointerPenInfo(outCount);
outPointerInfo.resize(outCount);
if (!(GetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) {
@@ -988,148 +918,77 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
}
}
+ if (!outPointerInfo.empty()) {
+ m_lastPointerTabletData = outPointerInfo.back().tabletData;
+ }
+
return GHOST_kSuccess;
}
-void GHOST_WindowWin32::processWin32TabletActivateEvent(WORD state)
+void GHOST_WindowWin32::resetPointerPenInfo()
{
- if (!useTabletAPI(GHOST_kTabletWintab)) {
- return;
- }
-
- if (m_wintab.enable && m_wintab.tablet) {
- m_wintab.enable(m_wintab.tablet, state);
-
- if (m_wintab.overlap && state) {
- m_wintab.overlap(m_wintab.tablet, TRUE);
- }
- }
+ m_lastPointerTabletData = GHOST_TABLET_DATA_NONE;
}
-bool GHOST_WindowWin32::useTabletAPI(GHOST_TTabletAPI api) const
+GHOST_Wintab *GHOST_WindowWin32::getWintab() const
{
- if (m_system->getTabletAPI() == api) {
- return true;
- }
- else if (m_system->getTabletAPI() == GHOST_kTabletAutomatic) {
- if (m_wintab.tablet)
- return api == GHOST_kTabletWintab;
- else
- return api == GHOST_kTabletNative;
- }
- else {
- return false;
- }
+ return m_wintab;
}
-void GHOST_WindowWin32::processWin32TabletInitEvent()
+void GHOST_WindowWin32::loadWintab(bool enable)
{
- if (!useTabletAPI(GHOST_kTabletWintab)) {
- return;
- }
-
- // Let's see if we can initialize tablet here
- if (m_wintab.info && m_wintab.tablet) {
- AXIS Pressure, Orientation[3]; /* The maximum tablet size */
-
- BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
- if (pressureSupport)
- m_wintab.maxPressure = Pressure.axMax;
- else
- m_wintab.maxPressure = 0;
-
- BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
- if (tiltSupport) {
- /* does the tablet support azimuth ([0]) and altitude ([1]) */
- if (Orientation[0].axResolution && Orientation[1].axResolution) {
- m_wintab.maxAzimuth = Orientation[0].axMax;
- m_wintab.maxAltitude = Orientation[1].axMax;
- }
- else { /* No so don't do tilt stuff. */
- m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
+ if (!m_wintab) {
+ if (m_wintab = GHOST_Wintab::loadWintab(m_hWnd)) {
+ if (enable) {
+ m_wintab->enable();
+
+ /* Focus Wintab if cursor is inside this window. This ensures Wintab is enabled when the
+ * tablet is used to change the Tablet API. */
+ GHOST_TInt32 x, y;
+ if (m_system->getCursorPosition(x, y)) {
+ GHOST_Rect rect;
+ getClientBounds(rect);
+
+ if (rect.isInside(x, y)) {
+ m_wintab->gainFocus();
+ }
+ }
}
}
}
+}
- m_tabletData.Active = GHOST_kTabletModeNone;
+void GHOST_WindowWin32::closeWintab()
+{
+ delete m_wintab;
+ m_wintab = NULL;
}
-void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
+bool GHOST_WindowWin32::usingTabletAPI(GHOST_TTabletAPI api) const
{
- if (!useTabletAPI(GHOST_kTabletWintab)) {
- return;
+ if (m_system->getTabletAPI() == api) {
+ return true;
}
-
- if (m_wintab.packet && m_wintab.tablet) {
- PACKET pkt;
- if (m_wintab.packet((HCTX)lParam, wParam, &pkt)) {
- switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */
- case 0:
- m_tabletData.Active = GHOST_kTabletModeNone; /* puck - not yet supported */
- break;
- case 1:
- m_tabletData.Active = GHOST_kTabletModeStylus; /* stylus */
- break;
- case 2:
- m_tabletData.Active = GHOST_kTabletModeEraser; /* eraser */
- break;
- }
-
- if (m_wintab.maxPressure > 0) {
- m_tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_wintab.maxPressure;
- }
- else {
- m_tabletData.Pressure = 1.0f;
- }
-
- if ((m_wintab.maxAzimuth > 0) && (m_wintab.maxAltitude > 0)) {
- ORIENTATION ort = pkt.pkOrientation;
- float vecLen;
- float altRad, azmRad; /* in radians */
-
- /*
- * from the wintab spec:
- * orAzimuth Specifies the clockwise rotation of the
- * cursor about the z axis through a full circular range.
- *
- * orAltitude Specifies the angle with the x-y plane
- * through a signed, semicircular range. Positive values
- * specify an angle upward toward the positive z axis;
- * negative values specify an angle downward toward the negative z axis.
- *
- * wintab.h defines .orAltitude as a UINT but documents .orAltitude
- * as positive for upward angles and negative for downward angles.
- * WACOM uses negative altitude values to show that the pen is inverted;
- * therefore we cast .orAltitude as an (int) and then use the absolute value.
- */
-
- /* convert raw fixed point data to radians */
- altRad = (float)((fabs((float)ort.orAltitude) / (float)m_wintab.maxAltitude) * M_PI / 2.0);
- azmRad = (float)(((float)ort.orAzimuth / (float)m_wintab.maxAzimuth) * M_PI * 2.0);
-
- /* find length of the stylus' projected vector on the XY plane */
- vecLen = cos(altRad);
-
- /* from there calculate X and Y components based on azimuth */
- m_tabletData.Xtilt = sin(azmRad) * vecLen;
- m_tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
- }
- else {
- m_tabletData.Xtilt = 0.0f;
- m_tabletData.Ytilt = 0.0f;
- }
+ else if (m_system->getTabletAPI() == GHOST_kTabletAutomatic) {
+ if (m_wintab && m_wintab->devicesPresent()) {
+ return api == GHOST_kTabletWintab;
+ }
+ else {
+ return api == GHOST_kTabletWinPointer;
}
}
+ else {
+ return false;
+ }
}
-void GHOST_WindowWin32::bringTabletContextToFront()
+GHOST_TabletData GHOST_WindowWin32::getTabletData()
{
- if (!useTabletAPI(GHOST_kTabletWintab)) {
- return;
+ if (usingTabletAPI(GHOST_kTabletWintab)) {
+ return m_wintab ? m_wintab->getLastTabletData() : GHOST_TABLET_DATA_NONE;
}
-
- if (m_wintab.overlap && m_wintab.tablet) {
- m_wintab.overlap(m_wintab.tablet, TRUE);
+ else {
+ return m_lastPointerTabletData;
}
}
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index a13bd876667..f28ba266ed1 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -30,34 +30,18 @@
#include "GHOST_TaskbarWin32.h"
#include "GHOST_Window.h"
+#include "GHOST_Wintab.h"
#ifdef WITH_INPUT_IME
# include "GHOST_ImeWin32.h"
#endif
#include <vector>
-#include <wintab.h>
-// PACKETDATA and PACKETMODE modify structs in pktdef.h, so make sure they come first
-#define PACKETDATA (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR)
-#define PACKETMODE PK_BUTTONS
-#include <pktdef.h>
-
class GHOST_SystemWin32;
class GHOST_DropTargetWin32;
-// typedefs for WinTab functions to allow dynamic loading
-typedef UINT(API *GHOST_WIN32_WTInfo)(UINT, UINT, LPVOID);
-typedef HCTX(API *GHOST_WIN32_WTOpen)(HWND, LPLOGCONTEXTA, BOOL);
-typedef BOOL(API *GHOST_WIN32_WTClose)(HCTX);
-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);
-
// 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
struct GHOST_PointerInfoWin32 {
GHOST_TInt32 pointerId;
@@ -65,7 +49,6 @@ struct GHOST_PointerInfoWin32 {
GHOST_TButtonMask buttonMask;
POINT pixelLocation;
GHOST_TUns64 time;
-
GHOST_TabletData tabletData;
};
@@ -259,16 +242,11 @@ class GHOST_WindowWin32 : public GHOST_Window {
HCURSOR getStandardCursor(GHOST_TStandardCursor shape) const;
void loadCursor(bool visible, GHOST_TStandardCursor cursorShape) const;
- const GHOST_TabletData &getTabletData()
- {
- return m_tabletData;
- }
-
/**
* Query whether given tablet API should be used.
* \param api: Tablet API to test.
*/
- bool useTabletAPI(GHOST_TTabletAPI api) const;
+ bool usingTabletAPI(GHOST_TTabletAPI api) const;
/**
* Translate WM_POINTER events into GHOST_PointerInfoWin32 structs.
@@ -281,10 +259,34 @@ class GHOST_WindowWin32 : public GHOST_Window {
WPARAM wParam,
LPARAM lParam);
- void processWin32TabletActivateEvent(WORD state);
- void processWin32TabletInitEvent();
- void processWin32TabletEvent(WPARAM wParam, LPARAM lParam);
- void bringTabletContextToFront();
+ /**
+ * Resets pointer pen tablet state.
+ */
+ void resetPointerPenInfo();
+
+ /**
+ * Retrieves pointer to Wintab if Wintab is the set Tablet API.
+ * \return Pointer to Wintab member.
+ */
+ GHOST_Wintab *getWintab() const;
+
+ /**
+ * Loads Wintab context for the window.
+ * \param enable: True if Wintab should be enabled after loading. Wintab should not be enabled if
+ * the window is minimized.
+ */
+ void loadWintab(bool enable);
+
+ /**
+ * Closes Wintab for the window.
+ */
+ void closeWintab();
+
+ /**
+ * Get the most recent Windows Pointer tablet data.
+ * \return Most recent pointer tablet data.
+ */
+ GHOST_TabletData getTabletData();
GHOST_TSuccess beginFullScreen() const
{
@@ -298,10 +300,10 @@ class GHOST_WindowWin32 : public GHOST_Window {
GHOST_TUns16 getDPIHint() override;
- /** Whether a tablet stylus is being tracked. */
- bool m_tabletInRange;
+ /** True if the mouse is either over or captured by the window. */
+ bool m_mousePresent;
- /** if the window currently resizing */
+ /** True if the window currently resizing. */
bool m_inLiveResize;
#ifdef WITH_INPUT_IME
@@ -385,27 +387,11 @@ class GHOST_WindowWin32 : public GHOST_Window {
static const wchar_t *s_windowClassName;
static const int s_maxTitleLength;
- /** Tablet data for GHOST */
- GHOST_TabletData m_tabletData;
-
- /* Wintab API */
- struct {
- /** `WinTab.dll` handle. */
- HMODULE handle = NULL;
-
- /** API functions */
- GHOST_WIN32_WTInfo info;
- GHOST_WIN32_WTOpen open;
- GHOST_WIN32_WTClose close;
- GHOST_WIN32_WTPacket packet;
- GHOST_WIN32_WTEnable enable;
- GHOST_WIN32_WTOverlap overlap;
-
- /** Stores the Tablet context if detected Tablet features using `WinTab.dll` */
- HCTX tablet;
- LONG maxPressure;
- LONG maxAzimuth, maxAltitude;
- } m_wintab;
+ /** Pointer to Wintab manager if Wintab is loaded. */
+ GHOST_Wintab *m_wintab;
+
+ /** Most recent tablet data. */
+ GHOST_TabletData m_lastPointerTabletData;
GHOST_TWindowState m_normal_state;
diff --git a/intern/ghost/intern/GHOST_Wintab.cpp b/intern/ghost/intern/GHOST_Wintab.cpp
new file mode 100644
index 00000000000..cf0309b1521
--- /dev/null
+++ b/intern/ghost/intern/GHOST_Wintab.cpp
@@ -0,0 +1,491 @@
+/*
+ * 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
+ */
+
+#define _USE_MATH_DEFINES
+
+#include "GHOST_Wintab.h"
+
+GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
+{
+ /* Load Wintab library if available. */
+
+ auto handle = unique_hmodule(::LoadLibrary("Wintab32.dll"), &::FreeLibrary);
+ if (!handle) {
+ return nullptr;
+ }
+
+ /* Get Wintab functions. */
+
+ auto info = (GHOST_WIN32_WTInfo)::GetProcAddress(handle.get(), "WTInfoA");
+ if (!info) {
+ return nullptr;
+ }
+
+ auto open = (GHOST_WIN32_WTOpen)::GetProcAddress(handle.get(), "WTOpenA");
+ if (!open) {
+ return nullptr;
+ }
+
+ auto get = (GHOST_WIN32_WTGet)::GetProcAddress(handle.get(), "WTGetA");
+ if (!get) {
+ return nullptr;
+ }
+
+ auto set = (GHOST_WIN32_WTSet)::GetProcAddress(handle.get(), "WTSetA");
+ if (!set) {
+ return nullptr;
+ }
+
+ auto close = (GHOST_WIN32_WTClose)::GetProcAddress(handle.get(), "WTClose");
+ if (!close) {
+ return nullptr;
+ }
+
+ auto packetsGet = (GHOST_WIN32_WTPacketsGet)::GetProcAddress(handle.get(), "WTPacketsGet");
+ if (!packetsGet) {
+ return nullptr;
+ }
+
+ auto queueSizeGet = (GHOST_WIN32_WTQueueSizeGet)::GetProcAddress(handle.get(), "WTQueueSizeGet");
+ if (!queueSizeGet) {
+ return nullptr;
+ }
+
+ auto queueSizeSet = (GHOST_WIN32_WTQueueSizeSet)::GetProcAddress(handle.get(), "WTQueueSizeSet");
+ if (!queueSizeSet) {
+ return nullptr;
+ }
+
+ auto enable = (GHOST_WIN32_WTEnable)::GetProcAddress(handle.get(), "WTEnable");
+ if (!enable) {
+ return nullptr;
+ }
+
+ auto overlap = (GHOST_WIN32_WTOverlap)::GetProcAddress(handle.get(), "WTOverlap");
+ if (!overlap) {
+ return nullptr;
+ }
+
+ /* Build Wintab context. */
+
+ LOGCONTEXT lc = {0};
+ if (!info(WTI_DEFSYSCTX, 0, &lc)) {
+ return nullptr;
+ }
+
+ Coord tablet, system;
+ extractCoordinates(lc, tablet, system);
+ modifyContext(lc);
+
+ /* The Wintab spec says we must open the context disabled if we are using cursor masks. */
+ auto hctx = unique_hctx(open(hwnd, &lc, FALSE), close);
+ if (!hctx) {
+ return nullptr;
+ }
+
+ /* Wintab provides no way to determine the maximum queue size aside from checking if attempts
+ * to change the queue size are successful. */
+ const int maxQueue = 500;
+ int queueSize = queueSizeGet(hctx.get());
+
+ while (queueSize < maxQueue) {
+ int testSize = min(queueSize + 16, maxQueue);
+ if (queueSizeSet(hctx.get(), testSize)) {
+ queueSize = testSize;
+ }
+ else {
+ /* From Windows Wintab Documentation for WTQueueSizeSet:
+ * "If the return value is zero, the context has no queue because the function deletes the
+ * original queue before attempting to create a new one. The application must continue
+ * calling the function with a smaller queue size until the function returns a non - zero
+ * value."
+ *
+ * In our case we start with a known valid queue size and in the event of failure roll
+ * back to the last valid queue size. The Wintab spec dates back to 16 bit Windows, thus
+ * assumes memory recently deallocated may not be available, which is no longer a practical
+ * concern. */
+ if (!queueSizeSet(hctx.get(), queueSize)) {
+ /* If a previously valid queue size is no longer valid, there is likely something wrong in
+ * the Wintab implementation and we should not use it. */
+ return nullptr;
+ }
+ break;
+ }
+ }
+
+ return new GHOST_Wintab(hwnd,
+ std::move(handle),
+ info,
+ get,
+ set,
+ packetsGet,
+ enable,
+ overlap,
+ std::move(hctx),
+ tablet,
+ system,
+ queueSize);
+}
+
+void GHOST_Wintab::modifyContext(LOGCONTEXT &lc)
+{
+ lc.lcPktData = PACKETDATA;
+ lc.lcPktMode = PACKETMODE;
+ lc.lcMoveMask = PACKETDATA;
+ lc.lcOptions |= CXO_CSRMESSAGES | CXO_MESSAGES;
+
+ /* Tablet scaling is handled manually because some drivers don't handle HIDPI or multi-display
+ * correctly; reset tablet scale factors to un-scaled tablet coordinates. */
+ lc.lcOutOrgX = lc.lcInOrgX;
+ lc.lcOutOrgY = lc.lcInOrgY;
+ lc.lcOutExtX = lc.lcInExtX;
+ lc.lcOutExtY = lc.lcInExtY;
+}
+
+void GHOST_Wintab::extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &system)
+{
+ tablet.x.org = lc.lcInOrgX;
+ tablet.x.ext = lc.lcInExtX;
+ tablet.y.org = lc.lcInOrgY;
+ tablet.y.ext = lc.lcInExtY;
+
+ system.x.org = lc.lcSysOrgX;
+ system.x.ext = lc.lcSysExtX;
+ system.y.org = lc.lcSysOrgY;
+ /* Wintab maps y origin to the tablet's bottom; invert y to match Windows y origin mapping to the
+ * screen top. */
+ system.y.ext = -lc.lcSysExtY;
+}
+
+GHOST_Wintab::GHOST_Wintab(HWND hwnd,
+ unique_hmodule handle,
+ GHOST_WIN32_WTInfo info,
+ GHOST_WIN32_WTGet get,
+ GHOST_WIN32_WTSet set,
+ GHOST_WIN32_WTPacketsGet packetsGet,
+ GHOST_WIN32_WTEnable enable,
+ GHOST_WIN32_WTOverlap overlap,
+ unique_hctx hctx,
+ Coord tablet,
+ Coord system,
+ int queueSize)
+ : m_handle{std::move(handle)},
+ m_fpInfo{info},
+ m_fpGet{get},
+ m_fpSet{set},
+ m_fpPacketsGet{packetsGet},
+ m_fpEnable{enable},
+ m_fpOverlap{overlap},
+ m_context{std::move(hctx)},
+ m_tabletCoord{tablet},
+ m_systemCoord{system},
+ m_pkts{queueSize}
+{
+ m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices);
+ updateCursorInfo();
+}
+
+void GHOST_Wintab::enable()
+{
+ m_fpEnable(m_context.get(), true);
+ m_enabled = true;
+}
+
+void GHOST_Wintab::disable()
+{
+ if (m_focused) {
+ loseFocus();
+ }
+ m_fpEnable(m_context.get(), false);
+ m_enabled = false;
+}
+
+void GHOST_Wintab::gainFocus()
+{
+ m_fpOverlap(m_context.get(), true);
+ m_focused = true;
+}
+
+void GHOST_Wintab::loseFocus()
+{
+ if (m_lastTabletData.Active != GHOST_kTabletModeNone) {
+ leaveRange();
+ }
+
+ /* Mouse mode of tablet or display layout may change when Wintab or Window is inactive. Don't
+ * trust for mouse movement until re-verified. */
+ m_coordTrusted = false;
+
+ m_fpOverlap(m_context.get(), false);
+ m_focused = false;
+}
+
+void GHOST_Wintab::leaveRange()
+{
+ /* Button state can't be tracked while out of range, reset it. */
+ m_buttons = 0;
+ /* Set to none to indicate tablet is inactive. */
+ m_lastTabletData = GHOST_TABLET_DATA_NONE;
+ /* Clear the packet queue. */
+ m_fpPacketsGet(m_context.get(), m_pkts.size(), m_pkts.data());
+}
+
+void GHOST_Wintab::remapCoordinates()
+{
+ LOGCONTEXT lc = {0};
+
+ if (m_fpInfo(WTI_DEFSYSCTX, 0, &lc)) {
+ extractCoordinates(lc, m_tabletCoord, m_systemCoord);
+ modifyContext(lc);
+
+ m_fpSet(m_context.get(), &lc);
+ }
+}
+
+void GHOST_Wintab::updateCursorInfo()
+{
+ AXIS Pressure, Orientation[3];
+
+ BOOL pressureSupport = m_fpInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
+ m_maxPressure = pressureSupport ? Pressure.axMax : 0;
+
+ BOOL tiltSupport = m_fpInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
+ /* Check if tablet supports azimuth [0] and altitude [1], encoded in axResolution. */
+ if (tiltSupport && Orientation[0].axResolution && Orientation[1].axResolution) {
+ m_maxAzimuth = Orientation[0].axMax;
+ m_maxAltitude = Orientation[1].axMax;
+ }
+ else {
+ m_maxAzimuth = m_maxAltitude = 0;
+ }
+}
+
+void GHOST_Wintab::processInfoChange(LPARAM lParam)
+{
+ /* Update number of connected Wintab digitizers. */
+ if (LOWORD(lParam) == WTI_INTERFACE && HIWORD(lParam) == IFC_NDEVICES) {
+ m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices);
+ }
+}
+
+bool GHOST_Wintab::devicesPresent()
+{
+ return m_numDevices > 0;
+}
+
+GHOST_TabletData GHOST_Wintab::getLastTabletData()
+{
+ return m_lastTabletData;
+}
+
+void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
+{
+ const int numPackets = m_fpPacketsGet(m_context.get(), m_pkts.size(), m_pkts.data());
+ outWintabInfo.resize(numPackets);
+ size_t outExtent = 0;
+
+ for (int i = 0; i < numPackets; i++) {
+ PACKET pkt = m_pkts[i];
+ GHOST_WintabInfoWin32 &out = outWintabInfo[i + outExtent];
+
+ out.tabletData = GHOST_TABLET_DATA_NONE;
+ /* % 3 for multiple devices ("DualTrack"). */
+ switch (pkt.pkCursor % 3) {
+ case 0:
+ /* Puck - processed as mouse. */
+ out.tabletData.Active = GHOST_kTabletModeNone;
+ break;
+ case 1:
+ out.tabletData.Active = GHOST_kTabletModeStylus;
+ break;
+ case 2:
+ out.tabletData.Active = GHOST_kTabletModeEraser;
+ break;
+ }
+
+ out.x = pkt.pkX;
+ out.y = pkt.pkY;
+
+ if (m_maxPressure > 0) {
+ out.tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
+ }
+
+ if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
+ ORIENTATION ort = pkt.pkOrientation;
+ float vecLen;
+ float altRad, azmRad; /* In radians. */
+
+ /*
+ * From the wintab spec:
+ * orAzimuth: Specifies the clockwise rotation of the cursor about the z axis through a
+ * full circular range.
+ * orAltitude: Specifies the angle with the x-y plane through a signed, semicircular range.
+ * Positive values specify an angle upward toward the positive z axis; negative values
+ * specify an angle downward toward the negative z axis.
+ *
+ * wintab.h defines orAltitude as a UINT but documents orAltitude as positive for upward
+ * angles and negative for downward angles. WACOM uses negative altitude values to show that
+ * the pen is inverted; therefore we cast orAltitude as an (int) and then use the absolute
+ * value.
+ */
+
+ /* Convert raw fixed point data to radians. */
+ altRad = (float)((fabs((float)ort.orAltitude) / (float)m_maxAltitude) * M_PI / 2.0);
+ azmRad = (float)(((float)ort.orAzimuth / (float)m_maxAzimuth) * M_PI * 2.0);
+
+ /* Find length of the stylus' projected vector on the XY plane. */
+ vecLen = cos(altRad);
+
+ /* From there calculate X and Y components based on azimuth. */
+ out.tabletData.Xtilt = sin(azmRad) * vecLen;
+ out.tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
+ }
+
+ out.time = pkt.pkTime;
+
+ /* Some Wintab libraries don't handle relative button input, so we track button presses
+ * manually. */
+ out.button = GHOST_kButtonMaskNone;
+ out.type = GHOST_kEventCursorMove;
+
+ DWORD buttonsChanged = m_buttons ^ pkt.pkButtons;
+ WORD buttonIndex = 0;
+ GHOST_WintabInfoWin32 buttonRef = out;
+ int buttons = 0;
+
+ while (buttonsChanged) {
+ if (buttonsChanged & 1) {
+ /* Find the index for the changed button from the button map. */
+ GHOST_TButtonMask button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex);
+
+ if (button != GHOST_kButtonMaskNone) {
+ /* Extend output if multiple buttons are pressed. We don't extend input until we confirm
+ * a Wintab buttons maps to a system button. */
+ if (buttons > 0) {
+ outWintabInfo.resize(outWintabInfo.size() + 1);
+ outExtent++;
+ GHOST_WintabInfoWin32 &out = outWintabInfo[i + outExtent];
+ out = buttonRef;
+ }
+ buttons++;
+
+ out.button = button;
+ if (buttonsChanged & pkt.pkButtons) {
+ out.type = GHOST_kEventButtonDown;
+ }
+ else {
+ out.type = GHOST_kEventButtonUp;
+ }
+ }
+
+ m_buttons ^= 1 << buttonIndex;
+ }
+
+ buttonsChanged >>= 1;
+ buttonIndex++;
+ }
+ }
+
+ if (!outWintabInfo.empty()) {
+ m_lastTabletData = outWintabInfo.back().tabletData;
+ }
+}
+
+GHOST_TButtonMask GHOST_Wintab::mapWintabToGhostButton(UINT cursor, WORD physicalButton)
+{
+ const WORD numButtons = 32;
+ BYTE logicalButtons[numButtons] = {0};
+ BYTE systemButtons[numButtons] = {0};
+
+ if (!m_fpInfo(WTI_CURSORS + cursor, CSR_BUTTONMAP, &logicalButtons) ||
+ !m_fpInfo(WTI_CURSORS + cursor, CSR_SYSBTNMAP, &systemButtons)) {
+ return GHOST_kButtonMaskNone;
+ }
+
+ if (physicalButton >= numButtons) {
+ return GHOST_kButtonMaskNone;
+ }
+
+ BYTE lb = logicalButtons[physicalButton];
+
+ if (lb >= numButtons) {
+ return GHOST_kButtonMaskNone;
+ }
+
+ switch (systemButtons[lb]) {
+ case SBN_LCLICK:
+ return GHOST_kButtonMaskLeft;
+ case SBN_RCLICK:
+ return GHOST_kButtonMaskRight;
+ case SBN_MCLICK:
+ return GHOST_kButtonMaskMiddle;
+ default:
+ return GHOST_kButtonMaskNone;
+ }
+}
+
+void GHOST_Wintab::mapWintabToSysCoordinates(int x_in, int y_in, int &x_out, int &y_out)
+{
+ /* Maps from range [in.org, in.org + abs(in.ext)] to [out.org, out.org + abs(out.ext)], in
+ * reverse if in.ext and out.ext have differing sign. */
+ auto remap = [](int inPoint, Range in, Range out) -> int {
+ int absInExt = abs(in.ext);
+ int absOutExt = abs(out.ext);
+
+ /* Translate input from range [in.org, in.org + absInExt] to [0, absInExt] */
+ int inMagnitude = inPoint - in.org;
+
+ /* If signs of extents differ, reverse input over range. */
+ if ((in.ext < 0) != (out.ext < 0)) {
+ inMagnitude = absInExt - inMagnitude;
+ }
+
+ /* Scale from [0, absInExt] to [0, absOutExt]. */
+ int outMagnitude = inMagnitude * absOutExt / absInExt;
+
+ /* Translate from range [0, absOutExt] to [out.org, out.org + absOutExt]. */
+ int outPoint = outMagnitude + out.org;
+
+ return outPoint;
+ };
+
+ x_out = remap(x_in, m_tabletCoord.x, m_systemCoord.x);
+ y_out = remap(y_in, m_tabletCoord.y, m_systemCoord.y);
+}
+
+bool GHOST_Wintab::trustCoordinates()
+{
+ return m_coordTrusted;
+}
+
+bool GHOST_Wintab::testCoordinates(int sysX, int sysY, int wtX, int wtY)
+{
+ mapWintabToSysCoordinates(wtX, wtY, wtX, wtY);
+
+ /* Allow off by one pixel tolerance in case of rounding error. */
+ if (abs(sysX - wtX) <= 1 && abs(sysY - wtY) <= 1) {
+ m_coordTrusted = true;
+ return true;
+ }
+ else {
+ m_coordTrusted = false;
+ return false;
+ }
+}
diff --git a/intern/ghost/intern/GHOST_Wintab.h b/intern/ghost/intern/GHOST_Wintab.h
new file mode 100644
index 00000000000..75017aa67d9
--- /dev/null
+++ b/intern/ghost/intern/GHOST_Wintab.h
@@ -0,0 +1,250 @@
+/*
+ * 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
+ * Declaration of GHOST_WintabWin32 class.
+ */
+
+/* Wacom's Wintab documentation is periodically offline, moved, and increasingly hidden away. You
+ * can find a (painstakingly) archived copy of the documentation at
+ * https://web.archive.org/web/20201122230125/https://developer-docs-legacy.wacom.com/display/DevDocs/Windows+Wintab+Documentation
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <wtypes.h>
+
+#include "GHOST_Types.h"
+
+#include <wintab.h>
+/* PACKETDATA and PACKETMODE modify structs in pktdef.h, so make sure they come first. */
+#define PACKETDATA \
+ (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_X | PK_Y | PK_TIME)
+#define PACKETMODE 0
+#include <pktdef.h>
+
+/* Typedefs for Wintab functions to allow dynamic loading. */
+typedef UINT(API *GHOST_WIN32_WTInfo)(UINT, UINT, LPVOID);
+typedef BOOL(API *GHOST_WIN32_WTGet)(HCTX, LPLOGCONTEXTA);
+typedef BOOL(API *GHOST_WIN32_WTSet)(HCTX, LPLOGCONTEXTA);
+typedef HCTX(API *GHOST_WIN32_WTOpen)(HWND, LPLOGCONTEXTA, BOOL);
+typedef BOOL(API *GHOST_WIN32_WTClose)(HCTX);
+typedef int(API *GHOST_WIN32_WTPacketsGet)(HCTX, int, LPVOID);
+typedef int(API *GHOST_WIN32_WTQueueSizeGet)(HCTX);
+typedef BOOL(API *GHOST_WIN32_WTQueueSizeSet)(HCTX, int);
+typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL);
+typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL);
+
+/* Typedefs for Wintab and Windows resource management. */
+typedef std::unique_ptr<std::remove_pointer_t<HMODULE>, decltype(&::FreeLibrary)> unique_hmodule;
+typedef std::unique_ptr<std::remove_pointer_t<HCTX>, GHOST_WIN32_WTClose> unique_hctx;
+
+struct GHOST_WintabInfoWin32 {
+ GHOST_TInt32 x, y;
+ GHOST_TEventType type;
+ GHOST_TButtonMask button;
+ GHOST_TUns64 time;
+ GHOST_TabletData tabletData;
+};
+
+class GHOST_Wintab {
+ public:
+ /**
+ * Loads Wintab if available.
+ * \param hwnd: Window to attach Wintab context to.
+ */
+ static GHOST_Wintab *loadWintab(HWND hwnd);
+
+ /**
+ * Enables Wintab context.
+ */
+ void enable();
+
+ /**
+ * Disables the Wintab context and unwinds Wintab state.
+ */
+ void disable();
+
+ /**
+ * Brings Wintab context to the top of the overlap order.
+ */
+ void gainFocus();
+
+ /**
+ * Puts Wintab context at bottom of overlap order and unwinds Wintab state.
+ */
+ void loseFocus();
+
+ /**
+ * Clean up when Wintab leaves tracking range.
+ */
+ void leaveRange();
+
+ /**
+ * Handle Wintab coordinate changes when DisplayChange events occur.
+ */
+ void remapCoordinates();
+
+ /**
+ * Maps Wintab to Win32 display coordinates.
+ * \param x_in: The tablet x coordinate.
+ * \param y_in: The tablet y coordinate.
+ * \param x_out: Output for the Win32 mapped x coordinate.
+ * \param y_out: Output for the Win32 mapped y coordinate.
+ */
+ void mapWintabToSysCoordinates(int x_in, int y_in, int &x_out, int &y_out);
+
+ /**
+ * Updates cached Wintab properties for current cursor.
+ */
+ void updateCursorInfo();
+
+ /**
+ * Handle Wintab info changes such as change in number of connected tablets.
+ * \param lParam: LPARAM of the event.
+ */
+ void processInfoChange(LPARAM lParam);
+
+ /**
+ * Whether Wintab devices are present.
+ * \return True if Wintab devices are present.
+ */
+ bool devicesPresent();
+
+ /**
+ * Translate Wintab packets into GHOST_WintabInfoWin32 structs.
+ * \param outWintabInfo: Storage to return resulting GHOST_WintabInfoWin32 data.
+ */
+ void getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo);
+
+ /**
+ * Whether Wintab coordinates should be trusted.
+ * \return True if Wintab coordinates should be trusted.
+ */
+ bool trustCoordinates();
+
+ /**
+ * Tests whether Wintab coordinates can be trusted by comparing Win32 and Wintab reported cursor
+ * position.
+ * \param sysX: System cursor x position.
+ * \param sysY: System cursor y position.
+ * \param wtX: Wintab cursor x position.
+ * \param wtY: Wintab cursor y position.
+ * \return True if Win32 and Wintab cursor positions match within tolerance.
+ *
+ * Note: Only test coordinates on button press, not release. This prevents issues when async
+ * mismatch causes mouse movement to replay and snap back, which is only an issue while drawing.
+ */
+ bool testCoordinates(int sysX, int sysY, int wtX, int wtY);
+
+ /**
+ * Retrieve the most recent tablet data, or none if pen is not in range.
+ * \return Most recent tablet data, or none if pen is not in range.
+ */
+ GHOST_TabletData getLastTabletData();
+
+ private:
+ /** Wintab DLL handle. */
+ unique_hmodule m_handle;
+ /** Wintab API functions. */
+ GHOST_WIN32_WTInfo m_fpInfo = nullptr;
+ GHOST_WIN32_WTGet m_fpGet = nullptr;
+ GHOST_WIN32_WTSet m_fpSet = nullptr;
+ GHOST_WIN32_WTPacketsGet m_fpPacketsGet = nullptr;
+ GHOST_WIN32_WTEnable m_fpEnable = nullptr;
+ GHOST_WIN32_WTOverlap m_fpOverlap = nullptr;
+
+ /** Stores the Wintab tablet context. */
+ unique_hctx m_context;
+ /** Whether the context is enabled. */
+ bool m_enabled = false;
+ /** Whether the context has focus and is at the top of overlap order. */
+ bool m_focused = false;
+
+ /** Pressed button map. */
+ GHOST_TUns8 m_buttons = 0;
+
+ /** Range of a coordinate space. */
+ struct Range {
+ /** Origin of range. */
+ int org = 0;
+ /** Extent of range. */
+ int ext = 1;
+ };
+
+ /** 2D Coordinate space. */
+ struct Coord {
+ /** Range of x. */
+ Range x = {};
+ /** Range of y. */
+ Range y = {};
+ };
+ /** Whether Wintab coordinates are trusted. */
+ bool m_coordTrusted = false;
+ /** Tablet input range. */
+ Coord m_tabletCoord = {};
+ /** System output range. */
+ Coord m_systemCoord = {};
+
+ int m_maxPressure = 0;
+ int m_maxAzimuth = 0;
+ int m_maxAltitude = 0;
+
+ /** Number of connected Wintab devices. */
+ UINT m_numDevices = 0;
+ /** Reusable buffer to read in Wintab packets. */
+ std::vector<PACKET> m_pkts;
+ /** Most recently received tablet data, or none if pen is not in range. */
+ GHOST_TabletData m_lastTabletData = GHOST_TABLET_DATA_NONE;
+
+ GHOST_Wintab(HWND hwnd,
+ unique_hmodule handle,
+ GHOST_WIN32_WTInfo info,
+ GHOST_WIN32_WTGet get,
+ GHOST_WIN32_WTSet set,
+ GHOST_WIN32_WTPacketsGet packetsGet,
+ GHOST_WIN32_WTEnable enable,
+ GHOST_WIN32_WTOverlap overlap,
+ unique_hctx hctx,
+ Coord tablet,
+ Coord system,
+ int queueSize);
+
+ /**
+ * Convert Wintab system mapped (mouse) buttons into Ghost button mask.
+ * \param cursor: The Wintab cursor associated to the button.
+ * \param physicalButton: The physical button ID to inspect.
+ * \return The system mapped button.
+ */
+ GHOST_TButtonMask mapWintabToGhostButton(UINT cursor, WORD physicalButton);
+
+ /**
+ * Applies common modifications to Wintab context.
+ * \param lc: Wintab context to modify.
+ */
+ static void modifyContext(LOGCONTEXT &lc);
+
+ /**
+ * Extracts tablet and system coordinates from Wintab context.
+ * \param lc: Wintab context to extract coordinates from.
+ * \param tablet: Tablet coordinates.
+ * \param system: System coordinates.
+ */
+ static void extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &system);
+};
diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp
index 172ac40c84f..b10e001df47 100644
--- a/intern/ghost/intern/GHOST_XrAction.cpp
+++ b/intern/ghost/intern/GHOST_XrAction.cpp
@@ -392,9 +392,10 @@ GHOST_XrActionSet::GHOST_XrActionSet(XrInstance instance, const GHOST_XrActionSe
{
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. */
+
+ /* Just use same name for localized. This can be changed in the future if necessary. */
+ strcpy(action_set_info.localizedActionSetName, info.name);
+
action_set_info.priority = 0; /* Use same (default) priority for all action sets. */
CHECK_XR(xrCreateActionSet(instance, &action_set_info, &m_action_set),
diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp
index daad0b8190a..f057e679d56 100644
--- a/intern/ghost/intern/GHOST_XrContext.cpp
+++ b/intern/ghost/intern/GHOST_XrContext.cpp
@@ -420,6 +420,11 @@ void GHOST_XrContext::getExtensionsToEnable(
r_ext_names.push_back(gpu_binding);
}
+#if defined(WITH_GL_EGL)
+ assert(openxr_extension_is_available(m_oxr->extensions, XR_MNDX_EGL_ENABLE_EXTENSION_NAME));
+ r_ext_names.push_back(XR_MNDX_EGL_ENABLE_EXTENSION_NAME);
+#endif
+
for (const std::string_view &ext : try_ext) {
if (openxr_extension_is_available(m_oxr->extensions, ext)) {
r_ext_names.push_back(ext.data());
diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
index 30a2a238ccd..aeaa6e6b9e0 100644
--- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
+++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
@@ -22,7 +22,9 @@
#include <list>
#include <sstream>
-#if defined(WITH_GHOST_X11)
+#if defined(WITH_GL_EGL)
+# include "GHOST_ContextEGL.h"
+#elif defined(WITH_GHOST_X11)
# include "GHOST_ContextGLX.h"
#elif defined(WIN32)
# include "GHOST_ContextD3D.h"
@@ -66,7 +68,9 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
XrSystemId system_id,
std::string *r_requirement_info) const override
{
-#if defined(WITH_GHOST_X11)
+#if defined(WITH_GL_EGL)
+ GHOST_ContextEGL &ctx_gl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
+#elif defined(WITH_GHOST_X11)
GHOST_ContextGLX &ctx_gl = static_cast<GHOST_ContextGLX &>(ghost_ctx);
#else
GHOST_ContextWGL &ctx_gl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
@@ -106,6 +110,15 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
void initFromGhostContext(GHOST_Context &ghost_ctx) override
{
#if defined(WITH_GHOST_X11)
+# if defined(WITH_GL_EGL)
+ GHOST_ContextEGL &ctx_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
+
+ oxr_binding.egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX;
+ oxr_binding.egl.getProcAddress = eglGetProcAddress;
+ oxr_binding.egl.display = ctx_egl.getDisplay();
+ oxr_binding.egl.config = ctx_egl.getConfig();
+ oxr_binding.egl.context = ctx_egl.getContext();
+# else
GHOST_ContextGLX &ctx_glx = static_cast<GHOST_ContextGLX &>(ghost_ctx);
XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx.m_display, ctx_glx.m_fbconfig);
@@ -117,6 +130,7 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
oxr_binding.glx.visualid = visual_info->visualid;
XFree(visual_info);
+# endif
#elif defined(WIN32)
GHOST_ContextWGL &ctx_wgl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h
index d09c78e1ea7..a2d3cf2e385 100644
--- a/intern/ghost/intern/GHOST_XrSession.h
+++ b/intern/ghost/intern/GHOST_XrSession.h
@@ -74,10 +74,11 @@ class GHOST_XrSession {
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. */
+ /**
+ * Action functions to be called post-session start.
+ * \param action_set_name: When `nullptr`, all attached action sets will be synced.
+ */
+ bool syncActions(const char *action_set_name = nullptr);
bool applyHapticAction(const char *action_set_name,
const char *action_name,
const GHOST_TInt64 &duration,
diff --git a/intern/ghost/intern/GHOST_Xr_openxr_includes.h b/intern/ghost/intern/GHOST_Xr_openxr_includes.h
index d1deaeb0d1a..d6d7cad866c 100644
--- a/intern/ghost/intern/GHOST_Xr_openxr_includes.h
+++ b/intern/ghost/intern/GHOST_Xr_openxr_includes.h
@@ -42,7 +42,13 @@
# include <d3d12.h>
#endif
#ifdef WITH_GHOST_X11
-# include <GL/glxew.h>
+# ifdef WITH_GL_EGL
+/* TODO: Why do we have to create this typedef manually? */
+typedef void (*(*PFNEGLGETPROCADDRESSPROC)(const char *procname))(void);
+# include <GL/eglew.h>
+# else
+# include <GL/glxew.h>
+# endif
#endif
#include <openxr/openxr.h>
diff --git a/intern/iksolver/intern/IK_QJacobianSolver.cpp b/intern/iksolver/intern/IK_QJacobianSolver.cpp
index 6c2c0bacf48..b8ad0b2c09c 100644
--- a/intern/iksolver/intern/IK_QJacobianSolver.cpp
+++ b/intern/iksolver/intern/IK_QJacobianSolver.cpp
@@ -89,7 +89,7 @@ bool IK_QJacobianSolver::Setup(IK_QSegment *root, std::list<IK_QTask *> &tasks)
if (num_dof == 0)
return false;
- // compute task id's and assing weights to task
+ // compute task ids and assign weights to task
int primary_size = 0, primary = 0;
int secondary_size = 0, secondary = 0;
double primary_weight = 0.0, secondary_weight = 0.0;
@@ -237,7 +237,7 @@ void IK_QJacobianSolver::ConstrainPoleVector(IK_QSegment *root, std::list<IK_QTa
bool IK_QJacobianSolver::UpdateAngles(double &norm)
{
- // assing each segment a unique id for the jacobian
+ // assign each segment a unique id for the jacobian
std::vector<IK_QSegment *>::iterator seg;
IK_QSegment *qseg, *minseg = NULL;
double minabsdelta = 1e10, absdelta;
diff --git a/intern/libmv/libmv/base/scoped_ptr.h b/intern/libmv/libmv/base/scoped_ptr.h
index 9bfcfe1d615..45b5722034c 100644
--- a/intern/libmv/libmv/base/scoped_ptr.h
+++ b/intern/libmv/libmv/base/scoped_ptr.h
@@ -77,7 +77,7 @@ class scoped_array {
void reset(T* new_array) {
if (sizeof(T)) {
- delete array_;
+ delete[] array_;
}
array_ = new_array;
}
diff --git a/intern/opencolorio/CMakeLists.txt b/intern/opencolorio/CMakeLists.txt
index d2336692d22..0b46ae471d2 100644
--- a/intern/opencolorio/CMakeLists.txt
+++ b/intern/opencolorio/CMakeLists.txt
@@ -48,6 +48,7 @@ if(WITH_OPENCOLORIO)
)
add_definitions(${GL_DEFINITIONS})
+ add_definitions(${OPENCOLORIO_DEFINITIONS})
list(APPEND INC_SYS
${OPENCOLORIO_INCLUDE_DIRS}
@@ -67,9 +68,6 @@ if(WITH_OPENCOLORIO)
list(APPEND INC_SYS
${BOOST_INCLUDE_DIR}
)
- add_definitions(
- -DOpenColorIO_STATIC
- )
list(APPEND LIB
${BOOST_LIBRARIES}
)