From 787c9579b69aa489861ef024ba9ab50d3dad6afa Mon Sep 17 00:00:00 2001 From: a740g Date: Sat, 9 Mar 2024 21:55:34 +0530 Subject: [PATCH] Add support for _MOUSEWHEEL and _MOUSEMOVEMENTx on macOS --- Makefile | 2 +- internal/c/libqb.cpp | 14 +++- internal/c/libqb/build.mk | 2 +- internal/c/libqb/src/glut-main-thread.cpp | 52 ++++++++------- internal/c/libqb/src/mac-mouse-support.cpp | 74 ++++++++++++++++++++++ internal/c/libqb/src/mac-mouse-support.h | 6 ++ 6 files changed, 123 insertions(+), 27 deletions(-) create mode 100644 internal/c/libqb/src/mac-mouse-support.cpp create mode 100644 internal/c/libqb/src/mac-mouse-support.h diff --git a/Makefile b/Makefile index eff339d19..66ca5482e 100644 --- a/Makefile +++ b/Makefile @@ -155,7 +155,7 @@ ifeq ($(OS),win) endif ifeq ($(OS),osx) - CXXLIBS += -framework OpenGL -framework IOKit -framework GLUT -framework Cocoa + CXXLIBS += -framework OpenGL -framework IOKit -framework GLUT -framework Cocoa -framework ApplicationServices # OSX doesn't strip using objcopy, so we're using `-s` instead ifneq ($(STRIP_SYMBOLS),n) diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index 8aad3d986..86983233e 100644 --- a/internal/c/libqb.cpp +++ b/internal/c/libqb.cpp @@ -29582,11 +29582,19 @@ void GLUT_MOUSE_FUNC(int glut_button, int state, int x, int y) { # endif } +// This is used to save the last mouse position which is then paired with the mouse wheel event on macOS +int g_MouseX = 0; +int g_MouseY = 0; + void GLUT_MOTION_FUNC(int x, int y) { int32 i, last_i; int32 handle; int32 xrel, yrel; + + g_MouseX = x; + g_MouseY = y; + handle = mouse_message_queue_first; mouse_message_queue_struct *queue = (mouse_message_queue_struct *)list_get(mouse_message_queue_handles, handle); @@ -29601,12 +29609,14 @@ void GLUT_MOTION_FUNC(int x, int y) { nextIndex = 0; queue->current = nextIndex; } -# ifdef QB64_WINDOWS + +# if defined(QB64_WINDOWS) || defined(QB64_MACOSX) // Windows calculates relative movement by intercepting WM_INPUT events - // instead + // macOS uses the Quartz Event Services to get relative movements xrel = 0; yrel = 0; # else + // TODO: This needs to be correctly implemented on Linux xrel = x - queue->queue[queue->last].x; yrel = y - queue->queue[queue->last].y; # endif diff --git a/internal/c/libqb/build.mk b/internal/c/libqb/build.mk index fa27b7535..1a13623d6 100644 --- a/internal/c/libqb/build.mk +++ b/internal/c/libqb/build.mk @@ -34,7 +34,7 @@ libqb-objs-y$(DEP_CONSOLE_ONLY) += $(PATH_LIBQB)/src/glut-msg-queue.o libqb-objs-$(DEP_CONSOLE_ONLY) += $(PATH_LIBQB)/src/console-only-main-thread.o ifeq ($(OS),osx) -libqb-objs-y$(DEP_CONSOLE_ONLY) += $(PATH_LIBQB)/src/mac-key-monitor.o +libqb-objs-y$(DEP_CONSOLE_ONLY) += $(PATH_LIBQB)/src/mac-key-monitor.o $(PATH_LIBQB)/src/mac-mouse-support.o endif $(PATH_LIBQB)/src/%.o: $(PATH_LIBQB)/src/%.cpp diff --git a/internal/c/libqb/src/glut-main-thread.cpp b/internal/c/libqb/src/glut-main-thread.cpp index d311428c1..a6339d082 100644 --- a/internal/c/libqb/src/glut-main-thread.cpp +++ b/internal/c/libqb/src/glut-main-thread.cpp @@ -1,14 +1,14 @@ #include "libqb-common.h" -#include -#include -#include -#include +#include #include #include +#include +#include +#include +#include #include -#include "GL/glew.h" // note: MacOSX uses Apple's GLUT not FreeGLUT #ifdef QB64_MACOSX @@ -18,12 +18,13 @@ # include #endif -#include "mutex.h" -#include "thread.h" #include "completion.h" +#include "glut-thread.h" #include "gui.h" #include "mac-key-monitor.h" -#include "glut-thread.h" +#include "mac-mouse-support.h" +#include "mutex.h" +#include "thread.h" // FIXME: These extern variable and function definitions should probably go // somewhere more global so that they can be referenced by libqb.cpp @@ -58,20 +59,20 @@ static void glutWarning(const char *fmt, va_list lst) { // Performs all of the FreeGLUT initialization except for calling glutMainLoop() static void initialize_glut(int argc, char **argv) { -# ifdef CORE_FREEGLUT +#ifdef CORE_FREEGLUT glutInitWarningFunc(glutWarning); glutInitErrorFunc(glutWarning); -# endif +#endif glutInit(&argc, argv); mac_register_key_handler(); -# ifdef QB64_WINDOWS +#ifdef QB64_WINDOWS glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE); -# else +#else glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); -# endif +#endif glutInitWindowSize(640, 400); // cannot be changed unless display_x(etc) are modified @@ -96,11 +97,11 @@ static void initialize_glut(int argc, char **argv) { glutDisplayFunc(GLUT_DISPLAY_REQUEST); -# ifdef QB64_WINDOWS +#ifdef QB64_WINDOWS glutTimerFunc(8, GLUT_TIMER_EVENT, 0); -# else +#else glutIdleFunc(GLUT_IDLEFUNC); -# endif +#endif glutKeyboardFunc(GLUT_KEYBOARD_FUNC); glutKeyboardUpFunc(GLUT_KEYBOARDUP_FUNC); @@ -111,9 +112,13 @@ static void initialize_glut(int argc, char **argv) { glutPassiveMotionFunc(GLUT_PASSIVEMOTION_FUNC); glutReshapeFunc(GLUT_RESHAPE_FUNC); -# ifdef CORE_FREEGLUT +#ifdef CORE_FREEGLUT glutMouseWheelFunc(GLUT_MOUSEWHEEL_FUNC); -# endif +#endif + +#ifdef QB64_MACOSX + macMouseInit(); +#endif } static bool glut_is_started; @@ -136,9 +141,7 @@ void libqb_start_glut_thread() { } // Checks whether the GLUT thread is running -bool libqb_is_glut_up() { - return glut_is_started; -} +bool libqb_is_glut_up() { return glut_is_started; } void libqb_glut_presetup(int argc, char **argv) { if (!screen_hide) { @@ -178,8 +181,11 @@ void libqb_start_main_thread(int argc, char **argv) { // from two threads at the same time). // // This is accomplished by simply queuing a GLUT message that calls exit() for us. -void libqb_exit(int exitcode) -{ +void libqb_exit(int exitcode) { +#ifdef QB64_MACOSX + macMouseDone(); +#endif + // If GLUT isn't running then we're free to do the exit() call from here if (!libqb_is_glut_up()) exit(exitcode); diff --git a/internal/c/libqb/src/mac-mouse-support.cpp b/internal/c/libqb/src/mac-mouse-support.cpp new file mode 100644 index 000000000..5615a9d8b --- /dev/null +++ b/internal/c/libqb/src/mac-mouse-support.cpp @@ -0,0 +1,74 @@ +// Mouse support functions for macOS +// These are required to overcome the limitations of GLUT + +#include "libqb-common.h" + +#include "mac-mouse-support.h" + +#include +#include +#include + +#define QB64_EVENT_CLOSE 1 +#define QB64_EVENT_KEY 2 +#define QB64_EVENT_RELATIVE_MOUSE_MOVEMENT 3 +#define QB64_EVENT_FILE_DROP 4 + +extern "C" int qb64_custom_event(int event, int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, void *p1, void *p2); +void GLUT_MOUSEWHEEL_FUNC(int wheel, int direction, int x, int y); + +extern int g_MouseX, g_MouseY; + +static CFMachPortRef g_EventTap = nullptr; +static CFRunLoopSourceRef g_RunLoopSource = nullptr; + +static CGEventRef macMouseCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *userInfo) { + auto appPID = reinterpret_cast(userInfo); + + if (CGEventGetIntegerValueField(event, kCGEventTargetUnixProcessID) == appPID) { + if (type == kCGEventScrollWheel) { + auto deltaScroll = CGEventGetIntegerValueField(event, kCGScrollWheelEventPointDeltaAxis1); + GLUT_MOUSEWHEEL_FUNC(0, deltaScroll, g_MouseX, g_MouseY); + } else { + auto deltaX = CGEventGetIntegerValueField(event, kCGMouseEventDeltaX); + auto deltaY = CGEventGetIntegerValueField(event, kCGMouseEventDeltaY); + if (deltaX || deltaY) + qb64_custom_event(QB64_EVENT_RELATIVE_MOUSE_MOVEMENT, deltaX, deltaY, 0, 0, 0, 0, 0, 0, nullptr, nullptr); + } + } + + return event; +} + +void macMouseInit() { + if (!g_EventTap) { + CGEventMask eventMask = CGEventMaskBit(kCGEventLeftMouseDown) | CGEventMaskBit(kCGEventLeftMouseUp) | CGEventMaskBit(kCGEventRightMouseDown) | + CGEventMaskBit(kCGEventRightMouseUp) | CGEventMaskBit(kCGEventMouseMoved) | CGEventMaskBit(kCGEventLeftMouseDragged) | + CGEventMaskBit(kCGEventRightMouseDragged) | CGEventMaskBit(kCGEventScrollWheel) | CGEventMaskBit(kCGEventOtherMouseDown) | + CGEventMaskBit(kCGEventOtherMouseUp) | CGEventMaskBit(kCGEventOtherMouseDragged); + + g_EventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, eventMask, macMouseCallback, + reinterpret_cast(getpid())); + if (g_EventTap) { + g_RunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, g_EventTap, 0); + + if (g_RunLoopSource) { + CFRunLoopAddSource(CFRunLoopGetCurrent(), g_RunLoopSource, kCFRunLoopCommonModes); + CGEventTapEnable(g_EventTap, true); + } else { + CFRelease(g_EventTap); + } + } + } +} + +void macMouseDone() { + if (g_EventTap) { + CGEventTapEnable(g_EventTap, false); + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), g_RunLoopSource, kCFRunLoopCommonModes); + CFRelease(g_RunLoopSource); + CFRelease(g_EventTap); + g_RunLoopSource = nullptr; + g_EventTap = nullptr; + } +} diff --git a/internal/c/libqb/src/mac-mouse-support.h b/internal/c/libqb/src/mac-mouse-support.h new file mode 100644 index 000000000..0a2bc4831 --- /dev/null +++ b/internal/c/libqb/src/mac-mouse-support.h @@ -0,0 +1,6 @@ +#pragma once + +#ifdef QB64_MACOSX +void macMouseInit(); +void macMouseDone(); +#endif