Refactor pen interface into driver system.

This commit is contained in:
Matthias Melcher 2025-11-27 23:43:08 +01:00
parent 86b9df01ee
commit 147247f8f1
8 changed files with 328 additions and 222 deletions

View File

@ -241,7 +241,7 @@ if(FLTK_USE_X11 AND NOT FLTK_USE_WAYLAND)
drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.cxx
drivers/Xlib/Fl_Xlib_Image_Surface_Driver.cxx
drivers/X11/fl_X11_platform_init.cxx
drivers/Stubs/Fl_Stubs_Pen_Events.cxx
drivers/Base/Fl_Base_Pen_Events.cxx
Fl_x.cxx
fl_dnd_x.cxx
Fl_Native_File_Chooser_FLTK.cxx
@ -291,6 +291,7 @@ if(FLTK_USE_X11 AND NOT FLTK_USE_WAYLAND)
drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.H
drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H
drivers/Unix/Fl_Unix_System_Driver.H
drivers/Base/Fl_Base_Pen_Events.H
)
if(FLTK_USE_CAIRO)
set(DRIVER_HEADER_FILES ${DRIVER_HEADER_FILES}
@ -318,7 +319,7 @@ elseif(FLTK_USE_WAYLAND)
drivers/Wayland/fl_wayland_clipboard_dnd.cxx
drivers/Wayland/fl_wayland_platform_init.cxx
drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx
drivers/Stubs/Fl_Stubs_Pen_Events.cxx
drivers/Base/Fl_Base_Pen_Events.cxx
Fl_Native_File_Chooser_FLTK.cxx
Fl_Native_File_Chooser_GTK.cxx
)
@ -350,6 +351,7 @@ elseif(FLTK_USE_WAYLAND)
drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.H
drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H
drivers/Unix/Fl_Unix_System_Driver.H
drivers/Base/Fl_Base_Pen_Events.H
)
elseif(APPLE)
@ -371,6 +373,7 @@ elseif(APPLE)
drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx
drivers/Posix/Fl_Posix_System_Driver.cxx
drivers/Darwin/Fl_Darwin_System_Driver.cxx
drivers/Base/Fl_Base_Pen_Events.cxx
Fl_get_key_mac.cxx
drivers/Darwin/fl_macOS_platform_init.cxx
)
@ -384,6 +387,7 @@ elseif(APPLE)
drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.H
drivers/Quartz/Fl_Font.H
drivers/Quartz/Fl_Quartz_Image_Surface_Driver.H
drivers/Base/Fl_Base_Pen_Events.H
)
else()
@ -405,7 +409,7 @@ else()
drivers/GDI/Fl_GDI_Graphics_Driver_vertex.cxx
drivers/GDI/Fl_GDI_Copy_Surface_Driver.cxx
drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx
drivers/Stubs/Fl_Stubs_Pen_Events.cxx
drivers/Base/Fl_Base_Pen_Events.cxx
Fl_win32.cxx
fl_dnd_win32.cxx
Fl_Native_File_Chooser_WIN32.cxx
@ -420,6 +424,7 @@ else()
drivers/GDI/Fl_Font.H
drivers/GDI/Fl_GDI_Copy_Surface_Driver.H
drivers/GDI/Fl_GDI_Image_Surface_Driver.H
drivers/Base/Fl_Base_Pen_Events.H
)
endif(FLTK_USE_X11 AND NOT FLTK_USE_WAYLAND)

View File

@ -0,0 +1,116 @@
//
// Definition of default Pen/Tablet event driver.
//
// Copyright 2025 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
//
#ifndef FL_BASE_PEN_EVENTS_H
#define FL_BASE_PEN_EVENTS_H
#include <config.h>
#include <FL/core/pen_events.H>
#include <FL/Fl.H>
#include <map>
#include <memory>
class Fl_Widget;
namespace Fl {
namespace Pen {
/* Pen event data storage.
A second storage may be useful if the driver needs to collect pen data
from multiple events, or if one system event can send multiple FLTK events.
*/
typedef struct EventData {
double x { 0.0 };
double y { 0.0 };
double rx { 0.0 };
double ry { 0.0 };
double tilt_x { 0.0 };
double tilt_y { 0.0 };
double pressure { 1.0 };
double proximity { 0.0 };
double barrel_pressure { 0.0 };
double twist { 0.0 };
int pen_id { 0 };
Fl::Pen::State state { (Fl::Pen::State)0 };
Fl::Pen::State trigger { (Fl::Pen::State)0 };
} EventData;
extern EventData e;
/*
Widgets and windows must subscribe to pen events. This is to reduce the amount
of events sent into the widget hierarchy.
Usually there is a pretty small number of subscribers, so looping through the
subscriber list should not be an issue.
All subscribers track their widget. If a widget is deleted while subscribed,
including during event handling, the driver will remove the subscription.
There is no need to explicitly unsubscribe.
*/
class Subscriber : public Fl_Widget_Tracker {
public:
Subscriber(Fl_Widget *w) : Fl_Widget_Tracker(w) { }
};
/*
Manage a list of subscribers.
*/
class SubscriberList : public std::map<Fl_Widget*, std::shared_ptr<Subscriber>> {
public:
SubscriberList() = default;
void cleanup();
std::shared_ptr<Subscriber> add(Fl_Widget *w);
void remove(Fl_Widget *w);
};
extern SubscriberList subscriber_list_;
extern std::shared_ptr<Subscriber> pushed_;
extern std::shared_ptr<Subscriber> below_pen_;
/* The base driver for calls by apps into the pen system.
Most traffic is generated by system events. The data is then converted
for the FLTK API and stored in Fl::Pen::e. The Pen interface then sends
the appropriate FLTK events to the subscribers.
This driver class manages calls from the app into FLTK, including subscriber
management and queries for driver an pen abilities.
*/
class Driver {
public:
Driver() = default;
virtual void subscribe(Fl_Widget* widget);
virtual void unsubscribe(Fl_Widget* widget);
virtual void release();
virtual Trait traits();
virtual Trait pen_traits(int pen_id);
};
extern Driver& driver;
} // namespace Pen
} // namespace Fl
#endif // FL_BASE_PEN_EVENTS_H

View File

@ -0,0 +1,148 @@
//
// Implementation of default Pen/Tablet event driver.
//
// Copyright 2025 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"
class Fl_Widget;
namespace Fl {
namespace Pen {
EventData e;
SubscriberList subscriber_list_;
std::shared_ptr<Subscriber> pushed_;
std::shared_ptr<Subscriber> below_pen_;
} // namespace Pen
} // namespace Fl
using namespace Fl::Pen;
// ---- SubscriberList implementation ------------------------------------------
/* Remove subscribers that have a nullptr as a widget */
void Fl::Pen::SubscriberList::cleanup() {
for (auto it = begin(); it != end(); ) {
if (!it->second->widget()) {
it = erase(it);
} else {
++it;
}
}
}
/* Add a new subscriber, or return an existing one. */
std::shared_ptr<Subscriber> Fl::Pen::SubscriberList::add(Fl_Widget *w) {
cleanup();
auto it = find(w);
if (it == end()) {
auto sub = std::make_shared<Subscriber>(w);
insert(std::make_pair(w, sub));
return sub;
} else {
return it->second;
}
}
/* Remove a subscriber from the list. */
void Fl::Pen::SubscriberList::remove(Fl_Widget *w) {
auto it = find(w);
if (it != end()) {
it->second->clear();
erase(it);
}
}
// ---- Driver implementation --------------------------------------------------
// Override the methods below to handle subscriptions and queries by user apps.
void Fl::Pen::Driver::subscribe(Fl_Widget* widget) {
if (widget == nullptr) return;
subscriber_list_.add(widget);
}
void Fl::Pen::Driver::unsubscribe(Fl_Widget* widget) {
if (widget == nullptr) return;
subscriber_list_.remove(widget);
}
void Fl::Pen::Driver::release() {
pushed_ = nullptr;
below_pen_ = nullptr;
}
Trait Fl::Pen::Driver::traits() {
return Trait::NONE;
}
Trait Fl::Pen::Driver::pen_traits(int pen_id) {
(void)pen_id;
return Trait::NONE;
}
// ---- Fl::Pen API ------------------------------------------------------------
void Fl::Pen::subscribe(Fl_Widget* widget) {
driver.subscribe(widget);
}
void Fl::Pen::unsubscribe(Fl_Widget* widget) {
driver.unsubscribe(widget);
}
void Fl::Pen::release() {
driver.release();
}
Trait Fl::Pen::driver_traits() {
return driver.traits();
}
Trait Fl::Pen::pen_traits(int pen_id) {
return driver.pen_traits(pen_id);
}
double Fl::Pen::event_x() { return e.x; }
double Fl::Pen::event_y() { return e.y; }
double Fl::Pen::event_x_root() { return e.rx; }
double Fl::Pen::event_y_root() { return e.ry; }
int Fl::Pen::event_pen_id() { return e.pen_id; }
double Fl::Pen::event_pressure() { return e.pressure; }
double Fl::Pen::event_barrel_pressure() { return e.barrel_pressure; }
double Fl::Pen::event_tilt_x() { return e.tilt_x; }
double Fl::Pen::event_tilt_y() { return e.tilt_y; }
double Fl::Pen::event_twist() { return e.twist; }
double Fl::Pen::event_proximity() { return e.proximity; }
State Fl::Pen::event_state() { return e.state; }
State Fl::Pen::event_trigger() { return e.trigger; }

View File

@ -14,81 +14,17 @@
// https://www.fltk.org/bugs.php
//
#include "src/drivers/Base/Fl_Base_Pen_Events.H"
#include <config.h>
#include <FL/platform.H>
#include <FL/core/pen_events.H>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Tooltip.H>
#include <FL/Fl_Widget_Tracker.H>
#include "../../Fl_Screen_Driver.H"
#import <Cocoa/Cocoa.h>
#include <map>
#include <memory>
extern Fl_Window *fl_xmousewin;
/*
Widgets and windows must subscribe to pen events. This is to reduce the amount
of events sent into the widget hierarchy.
Usually there is a pretty small number of subscribers, so looping through the
subscriber list should not be an issue.
All subscribers track their widget. If a widget is deleted while subscribed,
including during event handling, the driver will remove the subscription.
There is no need to explicitly unsubscribe.
*/
class Subscriber : public Fl_Widget_Tracker {
public:
Subscriber(Fl_Widget *w) : Fl_Widget_Tracker(w) { }
};
/*
Manage a list of subscribers.
*/
class SubscriberList : public std::map<Fl_Widget*, std::shared_ptr<Subscriber>> {
public:
SubscriberList() = default;
/* Remove subscribers that have a nullptr as a widget */
void cleanup() {
for (auto it = begin(); it != end(); ) {
if (!it->second->widget()) {
it = erase(it);
} else {
++it;
}
}
}
/* Add a new subscriber, or return an existing one. */
std::shared_ptr<Subscriber> add(Fl_Widget *w) {
cleanup();
auto it = find(w);
if (it == end()) {
auto sub = std::make_shared<Subscriber>(w);
insert(std::make_pair(w, sub));
return sub;
} else {
return it->second;
}
}
/* Remove a subscriber from the list. */
void remove(Fl_Widget *w) {
auto it = find(w);
if (it != end()) {
it->second->clear();
erase(it);
}
}
};
static SubscriberList subscriber_list_;
static std::shared_ptr<Subscriber> pushed_;
static std::shared_ptr<Subscriber> below_pen_;
static NSPointingDeviceType device_type_ { NSPointingDeviceTypePen };
// The trait list keeps track of traits for every pen ID that appears while
@ -109,36 +45,44 @@ static Fl::Pen::Trait driver_traits_ {
// Notably missing: PROXIMITY
};
struct EventData {
double x { 0.0 };
double y { 0.0 };
double rx { 0.0 };
double ry { 0.0 };
double tilt_x { 0.0 };
double tilt_y { 0.0 };
double pressure { 1.0 };
double barrel_pressure { 0.0 };
double twist { 0.0 };
int pen_id { 0 };
Fl::Pen::State state { (Fl::Pen::State)0 };
Fl::Pen::State trigger { (Fl::Pen::State)0 };
};
// Temporary storage of event data for the driver;
static struct EventData ev;
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 {
// The event data that is made available to the user during event handling
struct EventData e;
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
@ -147,65 +91,7 @@ struct EventData e;
using namespace Fl::Pen;
// Return a bit for everything that AppKit could return.
Trait Fl::Pen::driver_traits() {
return driver_traits_;
}
Trait Fl::Pen::pen_traits(int pen_id) {
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;
}
}
void Fl::Pen::subscribe(Fl_Widget* widget) {
if (widget == nullptr) return;
subscriber_list_.add(widget);
}
void Fl::Pen::unsubscribe(Fl_Widget* widget) {
if (widget == nullptr) return;
subscriber_list_.remove(widget);
}
void Fl::Pen::release() {
pushed_ = nullptr;
below_pen_ = nullptr;
}
double Fl::Pen::event_x() { return e.x; }
double Fl::Pen::event_y() { return e.y; }
double Fl::Pen::event_x_root() { return e.rx; }
double Fl::Pen::event_y_root() { return e.ry; }
int Fl::Pen::event_pen_id() { return e.pen_id; }
double Fl::Pen::event_pressure() { return e.pressure; }
double Fl::Pen::event_barrel_pressure() { return e.barrel_pressure; }
double Fl::Pen::event_tilt_x() { return e.tilt_x; }
double Fl::Pen::event_tilt_y() { return e.tilt_y; }
double Fl::Pen::event_twist() { return e.twist; }
// Not supported in AppKit NSEvent
double Fl::Pen::event_proximity() { return 0.0; }
State Fl::Pen::event_state() { return e.state; }
State Fl::Pen::event_trigger() { return e.trigger; }
/**
/*
Copy the event state.
*/
static void copy_state() {
@ -218,7 +104,7 @@ static void copy_state() {
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) {
@ -321,8 +207,7 @@ static State button_to_trigger(NSInteger button, bool down) {
/*
Handle events coming from Cocoa.
TODO: clickCount: store in Fl::event_clicks()
capabilityMask is useless, because it is vendor defined
`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) {
@ -477,7 +362,7 @@ bool fl_cocoa_tablet_handler(NSEvent *event, Fl_Window *eventWindow) {
return 0;
}
} else {
// Anything to do here?
// Proximity events were handled earlier.
}
if (!receiver)

View File

@ -1,73 +0,0 @@
//
// Definition of default Pen/Tablet event driver.
//
// Copyright 2025 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 <config.h>
#include <FL/platform.H>
#include <FL/core/pen_events.H>
#include <FL/Fl.H>
class Fl_Widget;
namespace Fl {
namespace Pen {
// double e_pressure_;
} // namespace Pen
} // namespace Fl
using namespace Fl::Pen;
Trait Fl::Pen::driver_traits() { return Trait::NONE; }
Trait Fl::Pen::pen_traits(int pen_id) { return Trait::NONE; }
void Fl::Pen::subscribe(Fl_Widget* widget) { }
void Fl::Pen::unsubscribe(Fl_Widget* widget) { }
void Fl::Pen::release() { }
double Fl::Pen::event_x() { return 0.0; }
double Fl::Pen::event_y() { return 0.0; }
double Fl::Pen::event_x_root() { return 0.0; }
double Fl::Pen::event_y_root() { return 0.0; }
int Fl::Pen::event_pen_id() { return 0; }
double Fl::Pen::event_pressure() { return 1.0; }
double Fl::Pen::event_barrel_pressure() { return 0.0; }
double Fl::Pen::event_tilt_x() { return 0.0; }
double Fl::Pen::event_tilt_y() { return 0.0; }
double Fl::Pen::event_twist() { return 0.0; }
double Fl::Pen::event_proximity() { return 0.0; }
State Fl::Pen::event_state() { return Fl::Pen::State::NONE; }
State Fl::Pen::event_trigger() { return Fl::Pen::State::NONE; }

View File

@ -21,6 +21,7 @@
#include "../Unix/Fl_Unix_System_Driver.H"
#include "Fl_Wayland_Window_Driver.H"
#include "Fl_Wayland_Image_Surface_Driver.H"
#include "../Base/Fl_Base_Pen_Events.H"
#ifdef FLTK_USE_X11
# include "../Xlib/Fl_Xlib_Copy_Surface_Driver.H"
# include "../Cairo/Fl_X11_Cairo_Graphics_Driver.H"
@ -143,3 +144,11 @@ Fl_Image_Surface_Driver *Fl_Image_Surface_Driver::newImageSurfaceDriver(int w, i
#endif
return new Fl_Wayland_Image_Surface_Driver(w, h, high_res, off);
}
namespace FL {
namespace Pen{
Driver default_driver;
Driver& driver { default_driver };
} // namespace Pen
} // namespace FL

View File

@ -21,6 +21,7 @@
#include "Fl_WinAPI_System_Driver.H"
#include "Fl_WinAPI_Window_Driver.H"
#include "../GDI/Fl_GDI_Image_Surface_Driver.H"
#include "../Base/Fl_Base_Pen_Events.H"
Fl_Copy_Surface_Driver *Fl_Copy_Surface_Driver::newCopySurfaceDriver(int w, int h)
@ -81,3 +82,10 @@ Fl_Image_Surface_Driver *Fl_Image_Surface_Driver::newImageSurfaceDriver(int w, i
{
return new Fl_GDI_Image_Surface_Driver(w, h, high_res, off);
}
namespace FL {
namespace Pen{
Driver default_driver;
Driver& driver { default_driver };
} // namespace Pen
} // namespace FL

View File

@ -26,6 +26,7 @@
#include "../Unix/Fl_Unix_System_Driver.H"
#include "Fl_X11_Window_Driver.H"
#include "../Xlib/Fl_Xlib_Image_Surface_Driver.H"
#include "../Base/Fl_Base_Pen_Events.H"
Fl_Copy_Surface_Driver *Fl_Copy_Surface_Driver::newCopySurfaceDriver(int w, int h)
@ -73,3 +74,10 @@ Fl_Image_Surface_Driver *Fl_Image_Surface_Driver::newImageSurfaceDriver(int w, i
{
return new Fl_Xlib_Image_Surface_Driver(w, h, high_res, off);
}
namespace FL {
namespace Pen{
Driver default_driver;
Driver& driver { default_driver };
} // namespace Pen
} // namespace FL