diff options
Diffstat (limited to 'src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm')
| -rw-r--r-- | src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm | 439 |
1 files changed, 0 insertions, 439 deletions
diff --git a/src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm b/src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm deleted file mode 100644 index 9c75c7ff2..000000000 --- a/src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm +++ /dev/null @@ -1,439 +0,0 @@ -// -// Definition of macOS Cocoa Pen/Tablet event driver. -// -// Copyright 2025-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "src/drivers/Base/Fl_Base_Pen_Events.H" - -#include <FL/platform.H> -#include <FL/Fl.H> -#include <FL/Fl_Window.H> -#include "../../Fl_Screen_Driver.H" - -#import <Cocoa/Cocoa.h> - - -extern Fl_Window *fl_xmousewin; - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 - -typedef short NSEventSubtype; -#define NSPointingDeviceTypePen NSPenPointingDevice -#define NSEventTypeMouseEntered NSMouseEntered -#define NSEventTypeMouseExited NSMouseExited -#define NSEventTypeTabletProximity NSTabletProximity -#define NSEventTypeTabletPoint NSTabletPoint -#define NSEventSubtypeTabletProximity NSTabletProximityEventSubtype -#define NSEventSubtypeTabletPoint NSTabletPointEventSubtype -#define NSEventSubtypeMouseEvent NSMouseEventSubtype -#define NSEventTypeLeftMouseDown NSLeftMouseDown -#define NSEventTypeLeftMouseUp NSLeftMouseUp -#define NSEventTypeLeftMouseDragged NSLeftMouseDragged -#define NSEventTypeMouseMoved NSMouseMoved -#define NSEventTypeRightMouseDown NSRightMouseDown -#define NSEventTypeRightMouseUp NSRightMouseUp -#define NSEventTypeRightMouseDragged NSRightMouseDragged -#define NSEventTypeOtherMouseUp NSOtherMouseUp -#define NSEventTypeOtherMouseDown NSOtherMouseDown -#define NSEventTypeOtherMouseDragged NSOtherMouseDragged -#define NSPointingDeviceTypeEraser NSEraserPointingDevice - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 - -static NSPointingDeviceType device_type_ { NSPointingDeviceTypePen }; - -// The trait list keeps track of traits for every pen ID that appears while -// handling events. -// AppKit does not tell us what traits are available per pen or tablet, so -// we use the first 5 motion events to discover event values that are not -// the default value, and enter that knowledge into the traits database. -typedef std::map<int, Fl::Pen::Trait> TraitList; -static TraitList trait_list_; -static int trait_countdown_ { 5 }; -static int current_pen_id_ { -1 }; -static Fl::Pen::Trait current_pen_trait_ { Fl::Pen::Trait::DRIVER_AVAILABLE }; -static Fl::Pen::Trait driver_traits_ { - Fl::Pen::Trait::DRIVER_AVAILABLE | Fl::Pen::Trait::PEN_ID | - Fl::Pen::Trait::ERASER | Fl::Pen::Trait::PRESSURE | - Fl::Pen::Trait::BARREL_PRESSURE | Fl::Pen::Trait::TILT_X | - Fl::Pen::Trait::TILT_Y | Fl::Pen::Trait::TWIST - // Notably missing: PROXIMITY -}; - -// Temporary storage of event data for the driver; -static Fl::Pen::EventData ev; - - -namespace Fl { - -namespace Private { - -// Global mouse position at mouse down event -extern int e_x_down; -extern int e_y_down; - -}; // namespace Private - -namespace Pen { - -class Cocoa_Driver : public Driver { -public: - Cocoa_Driver() = default; - //virtual void subscribe(Fl_Widget* widget) override; - //virtual void unsubscribe(Fl_Widget* widget) override; - //virtual void release() override; - virtual Trait traits() override { return driver_traits_; } - virtual Trait pen_traits(int pen_id) override { - auto it = trait_list_.find(pen_id); - if (pen_id == 0) - return current_pen_trait_; - if (it == trait_list_.end()) { - return Trait::DRIVER_AVAILABLE; - } else { - return it->second; - } - } -}; - -Cocoa_Driver cocoa_driver; -Driver& driver = cocoa_driver; - -} // namespace Pen - -} // namespace Fl - - -using namespace Fl::Pen; - - -/* - Copy the event state. - */ -static void copy_state() { - Fl::Pen::State tr = (Fl::Pen::State)((uint32_t)Fl::Pen::e.state ^ (uint32_t)ev.state); - Fl::Pen::e = ev; - Fl::Pen::e.trigger = tr; - Fl::e_x = (int)ev.x; - Fl::e_y = (int)ev.y; - Fl::e_x_root = (int)ev.rx; - Fl::e_y_root = (int)ev.ry; -} - -/* - Offset coordinates for subwindows and subsubwindows. - */ -static void offset_subwindow_event(Fl_Widget *w, double &x, double &y) { - Fl_Widget *p = w, *q; - while (p) { - q = p->parent(); - if (p->as_window() && q) { - x -= p->x(); - y -= p->y(); - } - p = q; - }; -} - -/* - Check if coordinates are within the widget box. - Coordinates are in top_window space. We iterate up the hierarchy to ensure - that we handle subwindows correctly. - */ -static bool event_inside(Fl_Widget *w, double x, double y) { - offset_subwindow_event(w, x, y); - if (w->as_window()) { - return ((x >= 0) && (y >= 0) && (x < w->w()) && (y < w->h())); - } else { - return ((x >= w->x()) && (y >= w->y()) && (x < w->x() + w->w()) && (y < w->y() + w->h())); - } -} - -/* - Find the widget under the pen event. - Search the subscriber list for widgets that are inside the same top window, - are visible, and are within the give coordinates. Subwindow aware. - */ -static Fl_Widget *find_below_pen(Fl_Window *win, double x, double y) { - for (auto &sub: subscriber_list_) { - Fl_Widget *candidate = sub.second->widget(); - if (candidate && (candidate->top_window() == win)) { - if (candidate->visible() && event_inside(candidate, x, y)) { - return candidate; - } - } - } - return nullptr; -} - -/* - Send the current event and event data to a widget. - Note: we will get the wrong coordinates if the widget is not a child of - the current event window (LEAVE events between windows). - */ -static int pen_send(Fl_Widget *w, int event, State trigger, bool &copied) { - // Copy most event data only once - if (!copied) { - copy_state(); - copied = true; - } - // Copy the top_window coordinates again as they may change when w changes - e.x = ev.x; - e.y = ev.y; - offset_subwindow_event(w, e.x, e.y); - Fl::e_x = e.x; - Fl::e_y = e.y; - // Send the event. - e.trigger = trigger; - return w->handle(event); -} - -/* - Send an event to all subscribers. - */ -static int pen_send_all(int event, State trigger) { - bool copied = false; - // use local value because handler may still change ev values - for (auto &it: subscriber_list_) { - auto w = it.second->widget(); - if (w) - pen_send(w, event, trigger, copied); - } - return 1; -} - -/* - Convert the NSEvent button number to Fl::Pen::State, - */ -static State button_to_trigger(NSInteger button, bool down) { - switch (button) { - case 0: - if ( (ev.state & (State::ERASER_DOWN | State::ERASER_HOVERS)) != State::NONE ) { - return down ? State::ERASER_DOWN : State::ERASER_HOVERS; - } else { - return down ? State::TIP_DOWN : State::TIP_HOVERS; - } - case 1: return State::BUTTON0; - case 2: return State::BUTTON1; - case 3: return State::BUTTON2; - case 4: return State::BUTTON3; - default: return State::NONE; - } -} - -/* - Handle events coming from Cocoa. - `capabilityMask` is useless, because it is vendor defined - If a modal window is open, AppKit will send window specific events only there. - */ -bool fl_cocoa_tablet_handler(NSEvent *event, Fl_Window *eventWindow) { - // Quick access to the main type. - auto type = [event type]; - - // There seems nothing useful here. Ignore for now. - if ((type == NSEventTypeMouseEntered) || (type == NSEventTypeMouseExited)) { - return false; - } - - // Sort out tablet-only events and mouse plus tablet events. - bool is_mouse = ((type != NSEventTypeTabletPoint) && (type != NSEventTypeTabletProximity)); - - // Set the subtype if one is available. Only NSEventSubtypeTabletPoint and - // NSEventSubtypeTabletProximity matter in this context - NSEventSubtype subtype = is_mouse ? [event subtype] : NSEventSubtypeMouseEvent; - - // Is this a change in proximity event? - bool is_proximity = ((type == NSEventTypeTabletProximity) || (subtype == NSEventSubtypeTabletProximity)); - - // Is this a pen pointer event? - bool is_point = ((type == NSEventTypeTabletPoint) || (subtype == NSEventSubtypeTabletPoint)); - - // Check if any of the pen down, move, drag, or up events was triggered. - bool is_down = ((type == NSEventTypeLeftMouseDown) || (type == NSEventTypeRightMouseDown) || (type == NSEventTypeOtherMouseDown)); - bool is_up = ((type == NSEventTypeLeftMouseUp) || (type == NSEventTypeRightMouseUp) || (type == NSEventTypeOtherMouseUp)); - bool is_drag = ((type == NSEventTypeLeftMouseDragged) || (type == NSEventTypeRightMouseDragged) || (type == NSEventTypeOtherMouseDragged)); - bool is_motion = is_drag || (type == NSEventTypeMouseMoved); - - // Find out if we can get the pen position - bool has_position = (eventWindow != nullptr) && (is_up || is_down || is_motion || is_proximity || is_point); - - // Event has extended pen data set: - if (has_position) { - // Get the position data. - auto pt = [event locationInWindow]; - double s = Fl::screen_driver()->scale(0); - ev.x = pt.x/s; - ev.y = eventWindow->h() - pt.y/s; - ev.rx = ev.x + eventWindow->x(); - ev.ry = ev.y + eventWindow->y(); - if (!is_proximity) { - // Get the pressure data. - ev.pressure = [event pressure]; - ev.barrel_pressure = [event tangentialPressure]; - // Get the tilt - auto tilt = [event tilt]; - ev.tilt_x = -tilt.x; - ev.tilt_y = tilt.y; - // Other stuff - ev.twist = [event rotation]; // TODO: untested - // ev.proximity = [event proximity]; // not supported in AppKit - } - if (device_type_ == NSPointingDeviceTypeEraser) { - if ([event buttonMask] & 1) - ev.state = State::ERASER_DOWN; - else - ev.state = State::ERASER_HOVERS; - } else { - if ([event buttonMask] & 1) - ev.state = State::TIP_DOWN; - else - ev.state = State::TIP_HOVERS; - } - if ([event buttonMask] & 0x0002) ev.state |= State::BUTTON0; - if ([event buttonMask] & 0x0004) ev.state |= State::BUTTON1; - if ([event buttonMask] & 0x0008) ev.state |= State::BUTTON2; - if ([event buttonMask] & 0x0010) ev.state |= State::BUTTON3; - // printf("0x%08x\n", [event buttonMask]); - } - if (is_proximity) { - ev.pen_id = (int)[event vendorID]; - device_type_ = [event pointingDeviceType]; - } - if (type == NSEventTypeTabletProximity) { - if ([event isEnteringProximity]) { - // Check if this is the first time we see this pen, or if the pen changed - if (current_pen_id_ != ev.pen_id) { - current_pen_id_ = ev.pen_id; - auto it = trait_list_.find(current_pen_id_); - if (it == trait_list_.end()) { // not found, create a new entry - trait_list_[current_pen_id_] = Trait::DRIVER_AVAILABLE; - trait_countdown_ = 5; - pen_send_all(Fl::Pen::DETECTED, State::NONE); - // printf("IN RANGE, NEW PEN\n"); - } else { - pen_send_all(Fl::Pen::CHANGED, State::NONE); - // printf("IN RANGE, CHANGED PEN\n"); - } - trait_list_[0] = trait_list_[current_pen_id_]; // set current pen traits - } else { - pen_send_all(Fl::Pen::IN_RANGE, State::NONE); - // printf("IN RANGE\n"); - } - } else { - pen_send_all(Fl::Pen::OUT_OF_RANGE, State::NONE); - // printf("OUT OF RANGE\n"); - } - } - - Fl_Widget *receiver = nullptr; - bool pushed = false; - bool event_data_copied = false; - - if (has_position) { - if (trait_countdown_) { - trait_countdown_--; - if (ev.tilt_x != 0.0) current_pen_trait_ |= Trait::TILT_X; - if (ev.tilt_y != 0.0) current_pen_trait_ |= Trait::TILT_Y; - if (ev.pressure != 1.0) current_pen_trait_ |= Trait::PRESSURE; - if (ev.barrel_pressure != 0.0) current_pen_trait_ |= Trait::BARREL_PRESSURE; - if (ev.pen_id != 0) current_pen_trait_ |= Trait::PEN_ID; - if (ev.twist != 0.0) current_pen_trait_ |= Trait::TWIST; - //if (ev.proximity != 0) current_pen_trait_ |= Trait::PROXIMITY; - trait_list_[current_pen_id_] = current_pen_trait_; - } - fl_xmousewin = eventWindow; - if (pushed_ && pushed_->widget() && (Fl::pushed() == pushed_->widget())) { - receiver = pushed_->widget(); - if (Fl::grab() && (Fl::grab() != receiver->top_window())) - return 0; - if (Fl::modal() && (Fl::modal() != receiver->top_window())) - return 0; - pushed = true; - } else { - if (Fl::grab() && (Fl::grab() != eventWindow)) - return 0; - if (Fl::modal() && (Fl::modal() != eventWindow)) - return 0; - auto bpen = below_pen_ ? below_pen_->widget() : nullptr; - auto bmouse = Fl::belowmouse(); - auto bpen_old = bmouse && (bmouse == bpen) ? bpen : nullptr; - auto bpen_now = find_below_pen(eventWindow, ev.x, ev.y); - - if (bpen_now != bpen_old) { - if (bpen_old) { - pen_send(bpen_old, Fl::Pen::LEAVE, State::NONE, event_data_copied); - } - below_pen_ = nullptr; - if (bpen_now) { - State state = (device_type_ == NSPointingDeviceTypeEraser) ? State::ERASER_HOVERS : State::TIP_HOVERS; - if (pen_send(bpen_now, Fl::Pen::ENTER, state, event_data_copied)) { - below_pen_ = subscriber_list_[bpen_now]; - Fl::belowmouse(bpen_now); - } - } - } - - receiver = below_pen_ ? below_pen_->widget() : nullptr; - if (!receiver) - return 0; - } - } else { - // Proximity events were handled earlier. - } - - if (!receiver) - return 0; - - if (is_down) { - if (!pushed) { - pushed_ = subscriber_list_[receiver]; - Fl::pushed(receiver); - } - State trigger = button_to_trigger([event buttonNumber], true); - if ([event buttonNumber] == 0) { - Fl::e_is_click = 1; - Fl::Private::e_x_down = (int)ev.x; - Fl::Private::e_y_down = (int)ev.y; - if ([event clickCount] > 1) - Fl::e_clicks++; - else - Fl::e_clicks = 0; - pen_send(receiver, Fl::Pen::TOUCH, trigger, event_data_copied); - } else { - pen_send(receiver, Fl::Pen::BUTTON_PUSH, trigger, event_data_copied); - } - } else if (is_up) { - if ( (ev.state & State::ANY_DOWN) == State::NONE ) { - Fl::pushed(nullptr); - pushed_ = nullptr; - } - State trigger = button_to_trigger([event buttonNumber], true); - if ([event buttonNumber] == 0) - pen_send(receiver, Fl::Pen::LIFT, trigger, event_data_copied); - else - pen_send(receiver, Fl::Pen::BUTTON_RELEASE, trigger, event_data_copied); - } else if (is_motion) { - if ( Fl::e_is_click && - ( (fabs((int)ev.x - Fl::Private::e_x_down) > 5) || - (fabs((int)ev.y - Fl::Private::e_y_down) > 5) ) ) - Fl::e_is_click = 0; - if (pushed) { - pen_send(receiver, Fl::Pen::DRAW, State::NONE, event_data_copied); - } else { - pen_send(receiver, Fl::Pen::HOVER, State::NONE, event_data_copied); - } - } - // Always return 1 because at this point, we capture pen events and don't - // want mouse events anymore! - return 1; -} |
