summaryrefslogtreecommitdiff
path: root/src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm')
-rw-r--r--src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm439
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;
-}