diff options
Diffstat (limited to 'intern')
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} ) |