mirror of
https://github.com/libsdl-org/SDL.git
synced 2024-11-23 10:53:27 +08:00
pen: Rework public API.
This changes the API in various ways, and updates the backends for this. Overall, this is a massive simplification of the API, as most future backends can't support the previously-offered API. This also removes the testautomation pen code (not only did these interfaces change completely, it also did something no other test did: mock the internal API), and replaces testpen.c with a different implementation (the existing code was fine, it was just easier to start from scratch than update it).
This commit is contained in:
parent
2b853121fe
commit
a9d70dbacb
@ -212,12 +212,6 @@
|
||||
<ClCompile Include="..\..\..\test\testautomation_main.c" />
|
||||
<ClCompile Include="..\..\..\test\testautomation_math.c" />
|
||||
<ClCompile Include="..\..\..\test\testautomation_mouse.c" />
|
||||
<ClCompile Include="..\..\..\test\testautomation_pen.c">
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\test\testautomation_pixels.c" />
|
||||
<ClCompile Include="..\..\..\test\testautomation_platform.c" />
|
||||
<ClCompile Include="..\..\..\test\testautomation_properties.c" />
|
||||
|
@ -1274,7 +1274,6 @@
|
||||
4537749212091504002F0F45 /* testshape.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testshape.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
453774A4120915E3002F0F45 /* testshape.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testshape.c; sourceTree = "<group>"; };
|
||||
66E88E8A203B778F0004D44E /* testyuv_cvt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testyuv_cvt.c; sourceTree = "<group>"; };
|
||||
A1A859442BC72FC20045DD6C /* testautomation_pen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testautomation_pen.c; sourceTree = "<group>"; };
|
||||
A1A859482BC72FC20045DD6C /* testautomation_properties.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testautomation_properties.c; sourceTree = "<group>"; };
|
||||
A1A859492BC72FC20045DD6C /* testautomation_subsystems.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testautomation_subsystems.c; sourceTree = "<group>"; };
|
||||
A1A8594A2BC72FC20045DD6C /* testautomation_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testautomation_log.c; sourceTree = "<group>"; };
|
||||
@ -1804,7 +1803,6 @@
|
||||
F35E56B62983130A00A43A5F /* testautomation_main.c */,
|
||||
F35E56BA2983130B00A43A5F /* testautomation_math.c */,
|
||||
F35E56CD2983130F00A43A5F /* testautomation_mouse.c */,
|
||||
A1A859442BC72FC20045DD6C /* testautomation_pen.c */,
|
||||
F35E56C02983130C00A43A5F /* testautomation_pixels.c */,
|
||||
F35E56C32983130D00A43A5F /* testautomation_platform.c */,
|
||||
A1A859482BC72FC20045DD6C /* testautomation_properties.c */,
|
||||
|
@ -151,8 +151,6 @@ typedef enum SDL_EventType
|
||||
in an event watcher, the window handle is still valid and can still be used to retrieve any userdata
|
||||
associated with the window. Otherwise, the handle has already been destroyed and all resources
|
||||
associated with it are invalid */
|
||||
SDL_EVENT_WINDOW_PEN_ENTER, /**< Window has gained focus of the pressure-sensitive pen with ID "data1" */
|
||||
SDL_EVENT_WINDOW_PEN_LEAVE, /**< Window has lost focus of the pressure-sensitive pen with ID "data1" */
|
||||
SDL_EVENT_WINDOW_HDR_STATE_CHANGED, /**< Window HDR properties have changed */
|
||||
SDL_EVENT_WINDOW_FIRST = SDL_EVENT_WINDOW_SHOWN,
|
||||
SDL_EVENT_WINDOW_LAST = SDL_EVENT_WINDOW_HDR_STATE_CHANGED,
|
||||
@ -227,11 +225,14 @@ typedef enum SDL_EventType
|
||||
SDL_EVENT_SENSOR_UPDATE = 0x1200, /**< A sensor was updated */
|
||||
|
||||
/* Pressure-sensitive pen events */
|
||||
SDL_EVENT_PEN_DOWN = 0x1300, /**< Pressure-sensitive pen touched drawing surface */
|
||||
SDL_EVENT_PEN_PROXIMITY_IN = 0x1300, /**< Pressure-sensitive pen has become available */
|
||||
SDL_EVENT_PEN_PROXIMITY_OUT, /**< Pressure-sensitive pen has become unavailable */
|
||||
SDL_EVENT_PEN_DOWN, /**< Pressure-sensitive pen touched drawing surface */
|
||||
SDL_EVENT_PEN_UP, /**< Pressure-sensitive pen stopped touching drawing surface */
|
||||
SDL_EVENT_PEN_MOTION, /**< Pressure-sensitive pen moved, or angle/pressure changed */
|
||||
SDL_EVENT_PEN_BUTTON_DOWN, /**< Pressure-sensitive pen button pressed */
|
||||
SDL_EVENT_PEN_BUTTON_UP, /**< Pressure-sensitive pen button released */
|
||||
SDL_EVENT_PEN_MOTION, /**< Pressure-sensitive pen is moving on the tablet */
|
||||
SDL_EVENT_PEN_AXIS, /**< Pressure-sensitive pen angle/pressure/etc changed */
|
||||
|
||||
/* Camera hotplug events */
|
||||
SDL_EVENT_CAMERA_DEVICE_ADDED = 0x1400, /**< A new camera device is available */
|
||||
@ -426,7 +427,7 @@ typedef struct SDL_MouseMotionEvent
|
||||
Uint32 reserved;
|
||||
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
|
||||
SDL_WindowID windowID; /**< The window with mouse focus, if any */
|
||||
SDL_MouseID which; /**< The mouse instance id, SDL_TOUCH_MOUSEID, or SDL_PEN_MOUSEID */
|
||||
SDL_MouseID which; /**< The mouse instance id or SDL_TOUCH_MOUSEID */
|
||||
SDL_MouseButtonFlags state; /**< The current button state */
|
||||
float x; /**< X coordinate, relative to window */
|
||||
float y; /**< Y coordinate, relative to window */
|
||||
@ -445,7 +446,7 @@ typedef struct SDL_MouseButtonEvent
|
||||
Uint32 reserved;
|
||||
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
|
||||
SDL_WindowID windowID; /**< The window with mouse focus, if any */
|
||||
SDL_MouseID which; /**< The mouse instance id, SDL_TOUCH_MOUSEID, or SDL_PEN_MOUSEID */
|
||||
SDL_MouseID which; /**< The mouse instance id, SDL_TOUCH_MOUSEID */
|
||||
Uint8 button; /**< The mouse button index */
|
||||
Uint8 state; /**< SDL_PRESSED or SDL_RELEASED */
|
||||
Uint8 clicks; /**< 1 for single-click, 2 for double-click, etc. */
|
||||
@ -465,7 +466,7 @@ typedef struct SDL_MouseWheelEvent
|
||||
Uint32 reserved;
|
||||
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
|
||||
SDL_WindowID windowID; /**< The window with mouse focus, if any */
|
||||
SDL_MouseID which; /**< The mouse instance id, SDL_TOUCH_MOUSEID, or SDL_PEN_MOUSEID */
|
||||
SDL_MouseID which; /**< The mouse instance id, SDL_TOUCH_MOUSEID */
|
||||
float x; /**< The amount scrolled horizontally, positive to the right and negative to the left */
|
||||
float y; /**< The amount scrolled vertically, positive away from the user and negative toward the user */
|
||||
SDL_MouseWheelDirection direction; /**< Set to one of the SDL_MOUSEWHEEL_* defines. When FLIPPED the values in X and Y will be opposite. Multiply by -1 to change them back */
|
||||
@ -714,30 +715,38 @@ typedef struct SDL_TouchFingerEvent
|
||||
SDL_WindowID windowID; /**< The window underneath the finger, if any */
|
||||
} SDL_TouchFingerEvent;
|
||||
|
||||
|
||||
/**
|
||||
* Pressure-sensitive pen touched or stopped touching surface (event.ptip.*)
|
||||
* Pressure-sensitive pen proximity event structure (event.pmotion.*)
|
||||
*
|
||||
* When a pen becomes visible to the system (it is close enough to a tablet,
|
||||
* etc), SDL will send an SDL_EVENT_PEN_PROXIMITY_IN event with the new
|
||||
* pen's ID. This ID is valid until the pen leaves proximity again (has
|
||||
* been removed from the tablet's area, the tablet has been unplugged, etc).
|
||||
* If the same pen reenters proximity again, it will be given a new ID.
|
||||
*
|
||||
* Note that "proximity" means "close enough for the tablet to know the tool
|
||||
* is there." The pen touching and lifting off from the tablet while not
|
||||
* leaving the area are handled by SDL_EVENT_PEN_DOWN and SDL_EVENT_PEN_UP.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef struct SDL_PenTipEvent
|
||||
typedef struct SDL_PenProximityEvent
|
||||
{
|
||||
SDL_EventType type; /**< SDL_EVENT_PEN_DOWN or SDL_EVENT_PEN_UP */
|
||||
SDL_EventType type; /**< SDL_EVENT_PEN_PROXIMITY_IN or SDL_EVENT_PEN_PROXIMITY_OUT */
|
||||
Uint32 reserved;
|
||||
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
|
||||
SDL_WindowID windowID; /**< The window with pen focus, if any */
|
||||
SDL_WindowID windowID; /**< The window with mouse focus, if any */
|
||||
SDL_PenID which; /**< The pen instance id */
|
||||
Uint8 tip; /**< SDL_PEN_TIP_INK when using a regular pen tip, or SDL_PEN_TIP_ERASER if the pen is being used as an eraser (e.g., flipped to use the eraser tip) */
|
||||
Uint8 state; /**< SDL_PRESSED on SDL_EVENT_PEN_DOWN and SDL_RELEASED on SDL_EVENT_PEN_UP */
|
||||
Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */
|
||||
float x; /**< X coordinate, relative to window */
|
||||
float y; /**< Y coordinate, relative to window */
|
||||
float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per SDL_PenAxis) */
|
||||
} SDL_PenTipEvent;
|
||||
} SDL_PenProximityEvent;
|
||||
|
||||
/**
|
||||
* Pressure-sensitive pen motion / pressure / angle event structure
|
||||
* (event.pmotion.*)
|
||||
* Pressure-sensitive pen motion event structure (event.pmotion.*)
|
||||
*
|
||||
* Depending on the hardware, you may get motion events when the
|
||||
* pen is not touching a tablet, for tracking a pen even when it
|
||||
* isn't drawing. You should listen for SDL_EVENT_PEN_DOWN and
|
||||
* SDL_EVENT_PEN_UP events, or check `pen_state & SDL_PEN_INPUT_DOWN`
|
||||
* to decide if a pen is "drawing" when dealing with pen motion.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
*/
|
||||
@ -746,19 +755,42 @@ typedef struct SDL_PenMotionEvent
|
||||
SDL_EventType type; /**< SDL_EVENT_PEN_MOTION */
|
||||
Uint32 reserved;
|
||||
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
|
||||
SDL_WindowID windowID; /**< The window with pen focus, if any */
|
||||
SDL_WindowID windowID; /**< The window with mouse focus, if any */
|
||||
SDL_PenID which; /**< The pen instance id */
|
||||
Uint8 padding1;
|
||||
Uint8 padding2;
|
||||
Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */
|
||||
float x; /**< X coordinate, relative to window */
|
||||
float y; /**< Y coordinate, relative to window */
|
||||
float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per SDL_PenAxis) */
|
||||
SDL_PenInputFlags pen_state; /**< Complete pen input state at time of event */
|
||||
float x; /**< X position of pen on tablet */
|
||||
float y; /**< Y position of pen on tablet */
|
||||
} SDL_PenMotionEvent;
|
||||
|
||||
/**
|
||||
* Pressure-sensitive pen touched event structure (event.ptouch.*)
|
||||
*
|
||||
* These events come when a pen touches a surface (a tablet, etc),
|
||||
* or lifts off from one.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef struct SDL_PenTouchEvent
|
||||
{
|
||||
SDL_EventType type; /**< SDL_EVENT_PEN_DOWN or SDL_EVENT_PEN_UP */
|
||||
Uint32 reserved;
|
||||
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
|
||||
SDL_WindowID windowID; /**< The window with pen focus, if any */
|
||||
SDL_PenID which; /**< The pen instance id */
|
||||
SDL_PenInputFlags pen_state; /**< Complete pen input state at time of event */
|
||||
float x; /**< X position of pen on tablet */
|
||||
float y; /**< Y position of pen on tablet */
|
||||
Uint8 eraser; /**< Non-zero if eraser end is used (not all pens support this). */
|
||||
Uint8 state; /**< SDL_PRESSED (pen is touching) or SDL_RELEASED (pen is lifted off) */
|
||||
} SDL_PenTouchEvent;
|
||||
|
||||
/**
|
||||
* Pressure-sensitive pen button event structure (event.pbutton.*)
|
||||
*
|
||||
* This is for buttons on the pen itself that the user might click.
|
||||
* The pen itself pressing down to draw triggers a SDL_EVENT_PEN_DOWN
|
||||
* event instead.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef struct SDL_PenButtonEvent
|
||||
@ -766,15 +798,36 @@ typedef struct SDL_PenButtonEvent
|
||||
SDL_EventType type; /**< SDL_EVENT_PEN_BUTTON_DOWN or SDL_EVENT_PEN_BUTTON_UP */
|
||||
Uint32 reserved;
|
||||
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
|
||||
SDL_WindowID windowID; /**< The window with mouse focus, if any */
|
||||
SDL_PenID which; /**< The pen instance id */
|
||||
SDL_PenInputFlags pen_state; /**< Complete pen input state at time of event */
|
||||
float x; /**< X position of pen on tablet */
|
||||
float y; /**< Y position of pen on tablet */
|
||||
Uint8 button; /**< The pen button index (first button is 1). */
|
||||
Uint8 state; /**< SDL_PRESSED or SDL_RELEASED */
|
||||
} SDL_PenButtonEvent;
|
||||
|
||||
/**
|
||||
* Pressure-sensitive pen pressure / angle event structure
|
||||
* (event.paxis.*)
|
||||
*
|
||||
* You might get some of these events even if the pen isn't touching the tablet.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef struct SDL_PenAxisEvent
|
||||
{
|
||||
SDL_EventType type; /**< SDL_EVENT_PEN_AXIS */
|
||||
Uint32 reserved;
|
||||
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
|
||||
SDL_WindowID windowID; /**< The window with pen focus, if any */
|
||||
SDL_PenID which; /**< The pen instance id */
|
||||
Uint8 button; /**< The pen button index (1 represents the pen tip for compatibility with mouse events) */
|
||||
Uint8 state; /**< SDL_PRESSED or SDL_RELEASED */
|
||||
Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */
|
||||
float x; /**< X coordinate, relative to window */
|
||||
float y; /**< Y coordinate, relative to window */
|
||||
float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per SDL_PenAxis) */
|
||||
} SDL_PenButtonEvent;
|
||||
SDL_PenInputFlags pen_state; /**< Complete pen input state at time of event */
|
||||
float x; /**< X position of pen on tablet */
|
||||
float y; /**< Y position of pen on tablet */
|
||||
SDL_PenAxis axis; /**< Axis that has changed */
|
||||
float value; /**< New value of axis */
|
||||
} SDL_PenAxisEvent;
|
||||
|
||||
/**
|
||||
* An event used to drop text or request a file open by the system
|
||||
@ -894,9 +947,11 @@ typedef union SDL_Event
|
||||
SDL_QuitEvent quit; /**< Quit request event data */
|
||||
SDL_UserEvent user; /**< Custom event data */
|
||||
SDL_TouchFingerEvent tfinger; /**< Touch finger event data */
|
||||
SDL_PenTipEvent ptip; /**< Pen tip touching or leaving drawing surface */
|
||||
SDL_PenMotionEvent pmotion; /**< Pen change in position, pressure, or angle */
|
||||
SDL_PenButtonEvent pbutton; /**< Pen button press */
|
||||
SDL_PenProximityEvent pproximity; /**< Pen proximity event data */
|
||||
SDL_PenTouchEvent ptouch; /**< Pen tip touching event data */
|
||||
SDL_PenMotionEvent pmotion; /**< Pen motion event data */
|
||||
SDL_PenButtonEvent pbutton; /**< Pen button event data */
|
||||
SDL_PenAxisEvent paxis; /**< Pen axis event data */
|
||||
SDL_DropEvent drop; /**< Drag and drop event data */
|
||||
SDL_ClipboardEvent clipboard; /**< Clipboard event data */
|
||||
|
||||
|
@ -22,56 +22,65 @@
|
||||
/**
|
||||
* # CategoryPen
|
||||
*
|
||||
* Include file for SDL pen event handling.
|
||||
* SDL pen event handling.
|
||||
*
|
||||
* This file describes operations for pressure-sensitive pen (stylus and/or
|
||||
* SDL provides an API for pressure-sensitive pen (stylus and/or
|
||||
* eraser) handling, e.g., for input and drawing tablets or suitably equipped
|
||||
* mobile / tablet devices.
|
||||
*
|
||||
* To get started with pens:
|
||||
* To get started with pens, simply handle SDL_EVENT_PEN_* events. When a pen
|
||||
* starts providing input, SDL will assign it a unique SDL_PenID, which will
|
||||
* remain for the life of the process, as long as the pen stays connected.
|
||||
*
|
||||
* - Listen to SDL_PenMotionEvent and SDL_PenButtonEvent
|
||||
* - To avoid treating pen events as mouse events, ignore SDL_MouseMotionEvent
|
||||
* and SDL_MouseButtonEvent whenever `which` == SDL_PEN_MOUSEID.
|
||||
*
|
||||
* We primarily identify pens by SDL_PenID. The implementation makes a best
|
||||
* effort to relate each SDL_PenID to the same physical device during a
|
||||
* session. Formerly valid SDL_PenID values remain valid even if a device
|
||||
* disappears.
|
||||
*
|
||||
* For identifying pens across sessions, the API provides the type SDL_GUID .
|
||||
* Pens may provide more than simple touch input; they might have other axes,
|
||||
* such as pressure, tilt, rotation, etc.
|
||||
*/
|
||||
|
||||
#ifndef SDL_pen_h_
|
||||
#define SDL_pen_h_
|
||||
|
||||
#include <SDL3/SDL_error.h>
|
||||
#include <SDL3/SDL_guid.h>
|
||||
#include <SDL3/SDL_mouse.h>
|
||||
#include <SDL3/SDL_stdinc.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef Uint32 SDL_PenID; /**< SDL_PenIDs identify pens uniquely within a session */
|
||||
/**
|
||||
* SDL pen instance IDs.
|
||||
*
|
||||
* Zero is used to signify an invalid/null device.
|
||||
*
|
||||
* These show up in pen events when SDL sees input from them. They remain
|
||||
* consistent as long as SDL can recognize a tool to be the same pen; but if
|
||||
* a pen physically leaves the area and returns, it might get a new ID.
|
||||
*
|
||||
* \since This datatype is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef Uint32 SDL_PenID;
|
||||
|
||||
#define SDL_PEN_INVALID ((SDL_PenID)0) /**< Reserved invalid SDL_PenID is valid */
|
||||
|
||||
#define SDL_PEN_MOUSEID ((SDL_MouseID)-2) /**< Device ID for mouse events triggered by pen events */
|
||||
|
||||
#define SDL_PEN_INFO_UNKNOWN (-1) /**< Marks unknown information when querying the pen */
|
||||
|
||||
/**
|
||||
* Pen axis indices
|
||||
* Pen input flags, as reported by various pen events' `pen_state` field.
|
||||
*
|
||||
* Below are the valid indices to the "axis" array from SDL_PenMotionEvent and
|
||||
* SDL_PenButtonEvent. The axis indices form a contiguous range of ints from 0
|
||||
* to SDL_PEN_AXIS_LAST, inclusive. All "axis[]" entries are either normalised
|
||||
* to 0..1 or report a (positive or negative) angle in degrees, with 0.0
|
||||
* representing the centre. Not all pens/backends support all axes:
|
||||
* unsupported entries are always "0.0f".
|
||||
* \since This datatype is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef Uint32 SDL_PenInputFlags;
|
||||
#define SDL_PEN_INPUT_DOWN (1u << 0) /**< & to see if pen is pressed down */
|
||||
#define SDL_PEN_INPUT_BUTTON_1 (1u << 1) /**< & to see if button 1 is pressed */
|
||||
#define SDL_PEN_INPUT_BUTTON_2 (1u << 2) /**< & to see if button 2 is pressed */
|
||||
#define SDL_PEN_INPUT_BUTTON_3 (1u << 3) /**< & to see if button 3 is pressed */
|
||||
#define SDL_PEN_INPUT_BUTTON_4 (1u << 4) /**< & to see if button 4 is pressed */
|
||||
#define SDL_PEN_INPUT_BUTTON_5 (1u << 5) /**< & to see if button 5 is pressed */
|
||||
#define SDL_PEN_INPUT_ERASER_TIP (1u << 30) /**< & to see if eraser tip is used */
|
||||
|
||||
/**
|
||||
* Pen axis indices.
|
||||
*
|
||||
* These are the valid values for the `axis` field in SDL_PenAxisEvent.
|
||||
* All axes are either normalised to 0..1 or report a (positive or negative) angle
|
||||
* in degrees, with 0.0 representing the centre. Not all pens/backends support all
|
||||
* axes: unsupported axes are always zero.
|
||||
*
|
||||
* To convert angles for tilt and rotation into vector representation, use
|
||||
* SDL_sinf on the XTILT, YTILT, or ROTATION component, for example:
|
||||
@ -82,200 +91,17 @@ typedef Uint32 SDL_PenID; /**< SDL_PenIDs identify pens uniquely within a sessio
|
||||
*/
|
||||
typedef enum SDL_PenAxis
|
||||
{
|
||||
SDL_PEN_AXIS_PRESSURE = 0, /**< Pen pressure. Unidirectional: 0..1.0 */
|
||||
SDL_PEN_AXIS_XTILT, /**< Pen horizontal tilt angle. Bidirectional: -90.0..90.0 (left-to-right).
|
||||
The physical max/min tilt may be smaller than -90.0 / 90.0, cf. SDL_PenCapabilityInfo */
|
||||
SDL_PEN_AXIS_YTILT, /**< Pen vertical tilt angle. Bidirectional: -90.0..90.0 (top-to-down).
|
||||
The physical max/min tilt may be smaller than -90.0 / 90.0, cf. SDL_PenCapabilityInfo */
|
||||
SDL_PEN_AXIS_DISTANCE, /**< Pen distance to drawing surface. Unidirectional: 0.0..1.0 */
|
||||
SDL_PEN_AXIS_ROTATION, /**< Pen barrel rotation. Bidirectional: -180..179.9 (clockwise, 0 is facing up, -180.0 is facing down). */
|
||||
SDL_PEN_AXIS_SLIDER, /**< Pen finger wheel or slider (e.g., Airbrush Pen). Unidirectional: 0..1.0 */
|
||||
SDL_PEN_NUM_AXES, /**< Last valid axis index */
|
||||
SDL_PEN_AXIS_LAST = SDL_PEN_NUM_AXES - 1 /**< Last axis index plus 1 */
|
||||
SDL_PEN_AXIS_PRESSURE, /**< Pen pressure. Unidirectional: 0 to 1.0 */
|
||||
SDL_PEN_AXIS_XTILT, /**< Pen horizontal tilt angle. Bidirectional: -90.0 to 90.0 (left-to-right).
|
||||
The physical max/min tilt may be smaller than -90.0 / 90.0, check SDL_PenCapabilityInfo */
|
||||
SDL_PEN_AXIS_YTILT, /**< Pen vertical tilt angle. Bidirectional: -90.0 to 90.0 (top-to-down).
|
||||
The physical max/min tilt may be smaller than -90.0 / 90.0 check SDL_PenCapabilityInfo */
|
||||
SDL_PEN_AXIS_DISTANCE, /**< Pen distance to drawing surface. Unidirectional: 0.0 to 1.0 */
|
||||
SDL_PEN_AXIS_ROTATION, /**< Pen barrel rotation. Bidirectional: -180 to 179.9 (clockwise, 0 is facing up, -180.0 is facing down). */
|
||||
SDL_PEN_AXIS_SLIDER, /**< Pen finger wheel or slider (e.g., Airbrush Pen). Unidirectional: 0 to 1.0 */
|
||||
SDL_PEN_NUM_AXES /**< Total known pen axis types in this version of SDL. This number may grow in future releases! */
|
||||
} SDL_PenAxis;
|
||||
|
||||
/* Pen flags. These share a bitmask space with SDL_BUTTON_LEFT and friends. */
|
||||
#define SDL_PEN_FLAG_DOWN_BIT_INDEX 13 /* Bit for storing that pen is touching the surface */
|
||||
#define SDL_PEN_FLAG_INK_BIT_INDEX 14 /* Bit for storing has-non-eraser-capability status */
|
||||
#define SDL_PEN_FLAG_ERASER_BIT_INDEX 15 /* Bit for storing is-eraser or has-eraser-capability property */
|
||||
#define SDL_PEN_FLAG_AXIS_BIT_OFFSET 16 /* Bit for storing has-axis-0 property */
|
||||
|
||||
#define SDL_PEN_CAPABILITY(capbit) (1ul << (capbit))
|
||||
#define SDL_PEN_AXIS_CAPABILITY(axis) SDL_PEN_CAPABILITY((axis) + SDL_PEN_FLAG_AXIS_BIT_OFFSET)
|
||||
|
||||
/* Pen tips */
|
||||
#define SDL_PEN_TIP_INK SDL_PEN_FLAG_INK_BIT_INDEX /**< Regular pen tip (for drawing) touched the surface */
|
||||
#define SDL_PEN_TIP_ERASER SDL_PEN_FLAG_ERASER_BIT_INDEX /**< Eraser pen tip touched the surface */
|
||||
|
||||
/**
|
||||
* Pen capabilities reported by SDL_GetPenCapabilities.
|
||||
*
|
||||
* \since This datatype is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef Uint32 SDL_PenCapabilityFlags;
|
||||
|
||||
#define SDL_PEN_DOWN_MASK SDL_PEN_CAPABILITY(SDL_PEN_FLAG_DOWN_BIT_INDEX) /**< Pen tip is currently touching the drawing surface. */
|
||||
#define SDL_PEN_INK_MASK SDL_PEN_CAPABILITY(SDL_PEN_FLAG_INK_BIT_INDEX) /**< Pen has a regular drawing tip (SDL_GetPenCapabilities). For events (SDL_PenButtonEvent, SDL_PenMotionEvent, SDL_GetPenStatus) this flag is mutually exclusive with SDL_PEN_ERASER_MASK . */
|
||||
#define SDL_PEN_ERASER_MASK SDL_PEN_CAPABILITY(SDL_PEN_FLAG_ERASER_BIT_INDEX) /**< Pen has an eraser tip (SDL_GetPenCapabilities) or is being used as eraser (SDL_PenButtonEvent , SDL_PenMotionEvent , SDL_GetPenStatus) */
|
||||
#define SDL_PEN_AXIS_PRESSURE_MASK SDL_PEN_AXIS_CAPABILITY(SDL_PEN_AXIS_PRESSURE) /**< Pen provides pressure information in axis SDL_PEN_AXIS_PRESSURE */
|
||||
#define SDL_PEN_AXIS_XTILT_MASK SDL_PEN_AXIS_CAPABILITY(SDL_PEN_AXIS_XTILT) /**< Pen provides horizontal tilt information in axis SDL_PEN_AXIS_XTILT */
|
||||
#define SDL_PEN_AXIS_YTILT_MASK SDL_PEN_AXIS_CAPABILITY(SDL_PEN_AXIS_YTILT) /**< Pen provides vertical tilt information in axis SDL_PEN_AXIS_YTILT */
|
||||
#define SDL_PEN_AXIS_DISTANCE_MASK SDL_PEN_AXIS_CAPABILITY(SDL_PEN_AXIS_DISTANCE) /**< Pen provides distance to drawing tablet in SDL_PEN_AXIS_DISTANCE */
|
||||
#define SDL_PEN_AXIS_ROTATION_MASK SDL_PEN_AXIS_CAPABILITY(SDL_PEN_AXIS_ROTATION) /**< Pen provides barrel rotation information in axis SDL_PEN_AXIS_ROTATION */
|
||||
#define SDL_PEN_AXIS_SLIDER_MASK SDL_PEN_AXIS_CAPABILITY(SDL_PEN_AXIS_SLIDER) /**< Pen provides slider / finger wheel or similar in axis SDL_PEN_AXIS_SLIDER */
|
||||
#define SDL_PEN_AXIS_BIDIRECTIONAL_MASKS (SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK)
|
||||
|
||||
/**
|
||||
* Pen types
|
||||
*
|
||||
* Some pens identify as a particular type of drawing device (e.g., an
|
||||
* airbrush or a pencil).
|
||||
*
|
||||
* \since This enum is available since SDL 3.0.0
|
||||
*/
|
||||
typedef enum SDL_PenSubtype
|
||||
{
|
||||
SDL_PEN_TYPE_UNKNOWN = 0,
|
||||
SDL_PEN_TYPE_ERASER = 1, /**< Eraser */
|
||||
SDL_PEN_TYPE_PEN, /**< Generic pen; this is the default. */
|
||||
SDL_PEN_TYPE_PENCIL, /**< Pencil */
|
||||
SDL_PEN_TYPE_BRUSH, /**< Brush-like device */
|
||||
SDL_PEN_TYPE_AIRBRUSH, /**< Airbrush device that "sprays" ink */
|
||||
SDL_PEN_TYPE_LAST = SDL_PEN_TYPE_AIRBRUSH /**< Last valid pen type */
|
||||
} SDL_PenSubtype;
|
||||
|
||||
|
||||
/* Function prototypes */
|
||||
|
||||
/**
|
||||
* Retrieves all pens that are connected to the system.
|
||||
*
|
||||
* Yields an array of SDL_PenID values. These identify and track pens
|
||||
* throughout a session. To track pens across sessions (program restart), use
|
||||
* SDL_GUID .
|
||||
*
|
||||
* \param count the number of pens in the array (number of array elements
|
||||
* minus 1, i.e., not counting the terminator 0).
|
||||
* \returns a 0 terminated array of SDL_PenID values, or NULL on failure. The
|
||||
* array must be freed with SDL_free(). On a NULL return,
|
||||
* SDL_GetError() is set.
|
||||
*
|
||||
* \since This function is available since SDL 3.0.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC SDL_PenID * SDLCALL SDL_GetPens(int *count);
|
||||
|
||||
/**
|
||||
* Retrieves the pen's current status.
|
||||
*
|
||||
* If the pen is detached (cf. SDL_PenConnected), this operation may return
|
||||
* default values.
|
||||
*
|
||||
* \param instance_id the pen to query.
|
||||
* \param x out-mode parameter for pen x coordinate. May be NULL.
|
||||
* \param y out-mode parameter for pen y coordinate. May be NULL.
|
||||
* \param axes out-mode parameter for axis information. May be null. The axes
|
||||
* are in the same order as SDL_PenAxis.
|
||||
* \param num_axes maximum number of axes to write to "axes".
|
||||
* \returns a bit mask with the current pen button states (SDL_BUTTON_LMASK
|
||||
* etc.), possibly SDL_PEN_DOWN_MASK, and exactly one of
|
||||
* SDL_PEN_INK_MASK or SDL_PEN_ERASER_MASK , or 0 on error (see
|
||||
* SDL_GetError()).
|
||||
*
|
||||
* \since This function is available since SDL 3.0.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetPenStatus(SDL_PenID instance_id, float *x, float *y, float *axes, size_t num_axes);
|
||||
|
||||
/**
|
||||
* Retrieves an SDL_PenID for the given SDL_GUID.
|
||||
*
|
||||
* \param guid a pen GUID.
|
||||
* \returns a valid SDL_PenID, or SDL_PEN_INVALID if there is no matching
|
||||
* SDL_PenID.
|
||||
*
|
||||
* \since This function is available since SDL 3.0.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC SDL_PenID SDLCALL SDL_GetPenFromGUID(SDL_GUID guid);
|
||||
|
||||
/**
|
||||
* Retrieves the SDL_GUID for a given SDL_PenID.
|
||||
*
|
||||
* \param instance_id the pen to query.
|
||||
* \returns the corresponding pen GUID; persistent across multiple sessions.
|
||||
* If "instance_id" is SDL_PEN_INVALID, returns an all-zeroes GUID.
|
||||
*
|
||||
* \since This function is available since SDL 3.0.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC SDL_GUID SDLCALL SDL_GetPenGUID(SDL_PenID instance_id);
|
||||
|
||||
/**
|
||||
* Checks whether a pen is still attached.
|
||||
*
|
||||
* If a pen is detached, it will not show up for SDL_GetPens(). Other
|
||||
* operations will still be available but may return default values.
|
||||
*
|
||||
* \param instance_id a pen ID.
|
||||
* \returns SDL_TRUE if "instance_id" is valid and the corresponding pen is
|
||||
* attached, or SDL_FALSE otherwise.
|
||||
*
|
||||
* \since This function is available since SDL 3.0.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC SDL_bool SDLCALL SDL_PenConnected(SDL_PenID instance_id);
|
||||
|
||||
/**
|
||||
* Retrieves a human-readable description for a SDL_PenID.
|
||||
*
|
||||
* \param instance_id the pen to query.
|
||||
* \returns a string that contains the name of the pen, intended for human
|
||||
* consumption. The string might or might not be localised, depending
|
||||
* on platform settings. It is not guaranteed to be unique; use
|
||||
* SDL_GetPenGUID() for (best-effort) unique identifiers. The pointer
|
||||
* is managed by the SDL pen subsystem and must not be deallocated.
|
||||
* The pointer remains valid until SDL is shut down. Returns NULL on
|
||||
* error (cf. SDL_GetError()).
|
||||
*
|
||||
* \since This function is available since SDL 3.0.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC const char * SDLCALL SDL_GetPenName(SDL_PenID instance_id);
|
||||
|
||||
/**
|
||||
* Pen capabilities, as reported by SDL_GetPenCapabilities()
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef struct SDL_PenCapabilityInfo
|
||||
{
|
||||
float max_tilt; /**< Physical maximum tilt angle, for XTILT and YTILT, or SDL_PEN_INFO_UNKNOWN . Pens cannot typically tilt all the way to 90 degrees, so this value is usually less than 90.0. */
|
||||
Uint32 wacom_id; /**< For Wacom devices: wacom tool type ID, otherwise 0 (useful e.g. with libwacom) */
|
||||
Sint8 num_buttons; /**< Number of pen buttons (not counting the pen tip), or SDL_PEN_INFO_UNKNOWN */
|
||||
} SDL_PenCapabilityInfo;
|
||||
|
||||
/**
|
||||
* Retrieves capability flags for a given SDL_PenID.
|
||||
*
|
||||
* \param instance_id the pen to query.
|
||||
* \param capabilities detail information about pen capabilities, such as the
|
||||
* number of buttons.
|
||||
* \returns a set of capability flags, cf. SDL_PEN_CAPABILITIES.
|
||||
*
|
||||
* \since This function is available since SDL 3.0.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC SDL_PenCapabilityFlags SDLCALL SDL_GetPenCapabilities(SDL_PenID instance_id, SDL_PenCapabilityInfo *capabilities);
|
||||
|
||||
/**
|
||||
* Retrieves the pen type for a given SDL_PenID.
|
||||
*
|
||||
* \param instance_id the pen to query.
|
||||
* \returns the corresponding pen type (cf. SDL_PenSubtype) or 0 on error.
|
||||
* Note that the pen type does not dictate whether the pen tip is
|
||||
* SDL_PEN_TIP_INK or SDL_PEN_TIP_ERASER; to determine whether a pen
|
||||
* is being used for drawing or in eraser mode, check either the pen
|
||||
* tip on SDL_EVENT_PEN_DOWN, or the flag SDL_PEN_ERASER_MASK in the
|
||||
* pen state.
|
||||
*
|
||||
* \since This function is available since SDL 3.0.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC SDL_PenSubtype SDLCALL SDL_GetPenType(SDL_PenID instance_id);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -355,13 +355,6 @@ SDL3_0.0.0 {
|
||||
SDL_GetNumberProperty;
|
||||
SDL_GetOriginalMemoryFunctions;
|
||||
SDL_GetPathInfo;
|
||||
SDL_GetPenCapabilities;
|
||||
SDL_GetPenFromGUID;
|
||||
SDL_GetPenGUID;
|
||||
SDL_GetPenName;
|
||||
SDL_GetPenStatus;
|
||||
SDL_GetPenType;
|
||||
SDL_GetPens;
|
||||
SDL_GetPerformanceCounter;
|
||||
SDL_GetPerformanceFrequency;
|
||||
SDL_GetPixelFormatDetails;
|
||||
@ -611,7 +604,6 @@ SDL3_0.0.0 {
|
||||
SDL_PauseAudioStreamDevice;
|
||||
SDL_PauseHaptic;
|
||||
SDL_PeepEvents;
|
||||
SDL_PenConnected;
|
||||
SDL_PlayHapticRumble;
|
||||
SDL_PollEvent;
|
||||
SDL_PremultiplyAlpha;
|
||||
|
@ -380,13 +380,6 @@
|
||||
#define SDL_GetNumberProperty SDL_GetNumberProperty_REAL
|
||||
#define SDL_GetOriginalMemoryFunctions SDL_GetOriginalMemoryFunctions_REAL
|
||||
#define SDL_GetPathInfo SDL_GetPathInfo_REAL
|
||||
#define SDL_GetPenCapabilities SDL_GetPenCapabilities_REAL
|
||||
#define SDL_GetPenFromGUID SDL_GetPenFromGUID_REAL
|
||||
#define SDL_GetPenGUID SDL_GetPenGUID_REAL
|
||||
#define SDL_GetPenName SDL_GetPenName_REAL
|
||||
#define SDL_GetPenStatus SDL_GetPenStatus_REAL
|
||||
#define SDL_GetPenType SDL_GetPenType_REAL
|
||||
#define SDL_GetPens SDL_GetPens_REAL
|
||||
#define SDL_GetPerformanceCounter SDL_GetPerformanceCounter_REAL
|
||||
#define SDL_GetPerformanceFrequency SDL_GetPerformanceFrequency_REAL
|
||||
#define SDL_GetPixelFormatDetails SDL_GetPixelFormatDetails_REAL
|
||||
@ -636,7 +629,6 @@
|
||||
#define SDL_PauseAudioStreamDevice SDL_PauseAudioStreamDevice_REAL
|
||||
#define SDL_PauseHaptic SDL_PauseHaptic_REAL
|
||||
#define SDL_PeepEvents SDL_PeepEvents_REAL
|
||||
#define SDL_PenConnected SDL_PenConnected_REAL
|
||||
#define SDL_PlayHapticRumble SDL_PlayHapticRumble_REAL
|
||||
#define SDL_PollEvent SDL_PollEvent_REAL
|
||||
#define SDL_PremultiplyAlpha SDL_PremultiplyAlpha_REAL
|
||||
|
@ -400,13 +400,6 @@ SDL_DYNAPI_PROC(int,SDL_GetNumVideoDrivers,(void),(),return)
|
||||
SDL_DYNAPI_PROC(Sint64,SDL_GetNumberProperty,(SDL_PropertiesID a, const char *b, Sint64 c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(void,SDL_GetOriginalMemoryFunctions,(SDL_malloc_func *a, SDL_calloc_func *b, SDL_realloc_func *c, SDL_free_func *d),(a,b,c,d),)
|
||||
SDL_DYNAPI_PROC(int,SDL_GetPathInfo,(const char *a, SDL_PathInfo *b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_PenCapabilityFlags,SDL_GetPenCapabilities,(SDL_PenID a, SDL_PenCapabilityInfo *b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_PenID,SDL_GetPenFromGUID,(SDL_GUID a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_GUID,SDL_GetPenGUID,(SDL_PenID a),(a),return)
|
||||
SDL_DYNAPI_PROC(const char*,SDL_GetPenName,(SDL_PenID a),(a),return)
|
||||
SDL_DYNAPI_PROC(Uint32,SDL_GetPenStatus,(SDL_PenID a, float *b, float *c, float *d, size_t e),(a,b,c,d,e),return)
|
||||
SDL_DYNAPI_PROC(SDL_PenSubtype,SDL_GetPenType,(SDL_PenID a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_PenID*,SDL_GetPens,(int *a),(a),return)
|
||||
SDL_DYNAPI_PROC(Uint64,SDL_GetPerformanceCounter,(void),(),return)
|
||||
SDL_DYNAPI_PROC(Uint64,SDL_GetPerformanceFrequency,(void),(),return)
|
||||
SDL_DYNAPI_PROC(const SDL_PixelFormatDetails*,SDL_GetPixelFormatDetails,(SDL_PixelFormat a),(a),return)
|
||||
@ -647,7 +640,6 @@ SDL_DYNAPI_PROC(int,SDL_PauseAudioDevice,(SDL_AudioDeviceID a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_PauseAudioStreamDevice,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_PauseHaptic,(SDL_Haptic *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_PeepEvents,(SDL_Event *a, int b, SDL_EventAction c, Uint32 d, Uint32 e),(a,b,c,d,e),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_PenConnected,(SDL_PenID a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_PlayHapticRumble,(SDL_Haptic *a, float b, Uint32 c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_PollEvent,(SDL_Event *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_PremultiplyAlpha,(int a, int b, SDL_PixelFormat c, const void *d, int e, SDL_PixelFormat f, void *g, int h, SDL_bool i),(a,b,c,d,e,f,g,h,i),return)
|
||||
|
@ -153,17 +153,24 @@ SDL_EventCategory SDL_GetEventCategory(Uint32 type)
|
||||
case SDL_EVENT_SENSOR_UPDATE:
|
||||
return SDL_EVENTCATEGORY_SENSOR;
|
||||
|
||||
case SDL_EVENT_PEN_PROXIMITY_IN:
|
||||
case SDL_EVENT_PEN_PROXIMITY_OUT:
|
||||
return SDL_EVENTCATEGORY_PPROXIMITY;
|
||||
|
||||
case SDL_EVENT_PEN_DOWN:
|
||||
case SDL_EVENT_PEN_UP:
|
||||
return SDL_EVENTCATEGORY_PTIP;
|
||||
|
||||
case SDL_EVENT_PEN_MOTION:
|
||||
return SDL_EVENTCATEGORY_PMOTION;
|
||||
return SDL_EVENTCATEGORY_PTOUCH;
|
||||
|
||||
case SDL_EVENT_PEN_BUTTON_DOWN:
|
||||
case SDL_EVENT_PEN_BUTTON_UP:
|
||||
return SDL_EVENTCATEGORY_PBUTTON;
|
||||
|
||||
case SDL_EVENT_PEN_MOTION:
|
||||
return SDL_EVENTCATEGORY_PMOTION;
|
||||
|
||||
case SDL_EVENT_PEN_AXIS:
|
||||
return SDL_EVENTCATEGORY_PAXIS;
|
||||
|
||||
case SDL_EVENT_CAMERA_DEVICE_ADDED:
|
||||
case SDL_EVENT_CAMERA_DEVICE_REMOVED:
|
||||
case SDL_EVENT_CAMERA_DEVICE_APPROVED:
|
||||
@ -207,14 +214,20 @@ SDL_Window *SDL_GetWindowFromEvent(const SDL_Event *event)
|
||||
case SDL_EVENTCATEGORY_TFINGER:
|
||||
windowID = event->tfinger.windowID;
|
||||
break;
|
||||
case SDL_EVENTCATEGORY_PTIP:
|
||||
windowID = event->ptip.windowID;
|
||||
case SDL_EVENTCATEGORY_PPROXIMITY:
|
||||
windowID = event->pproximity.windowID;
|
||||
break;
|
||||
case SDL_EVENTCATEGORY_PTOUCH:
|
||||
windowID = event->ptouch.windowID;
|
||||
break;
|
||||
case SDL_EVENTCATEGORY_PBUTTON:
|
||||
windowID = event->pbutton.windowID;
|
||||
break;
|
||||
case SDL_EVENTCATEGORY_PMOTION:
|
||||
windowID = event->pmotion.windowID;
|
||||
break;
|
||||
case SDL_EVENTCATEGORY_PBUTTON:
|
||||
windowID = event->pbutton.windowID;
|
||||
case SDL_EVENTCATEGORY_PAXIS:
|
||||
windowID = event->paxis.windowID;
|
||||
break;
|
||||
case SDL_EVENTCATEGORY_DROP:
|
||||
windowID = event->drop.windowID;
|
||||
|
@ -54,9 +54,11 @@ typedef enum SDL_EventCategory
|
||||
SDL_EVENTCATEGORY_QUIT,
|
||||
SDL_EVENTCATEGORY_USER,
|
||||
SDL_EVENTCATEGORY_TFINGER,
|
||||
SDL_EVENTCATEGORY_PTIP,
|
||||
SDL_EVENTCATEGORY_PPROXIMITY,
|
||||
SDL_EVENTCATEGORY_PTOUCH,
|
||||
SDL_EVENTCATEGORY_PMOTION,
|
||||
SDL_EVENTCATEGORY_PBUTTON,
|
||||
SDL_EVENTCATEGORY_PAXIS,
|
||||
SDL_EVENTCATEGORY_DROP,
|
||||
SDL_EVENTCATEGORY_CLIPBOARD,
|
||||
} SDL_EventCategory;
|
||||
|
@ -378,6 +378,9 @@ static void SDLCALL SDL_EventLoggingChanged(void *userdata, const char *name, co
|
||||
|
||||
static void SDL_LogEvent(const SDL_Event *event)
|
||||
{
|
||||
static const char *pen_axisnames[] = { "PRESSURE", "XTILT", "YTILT", "DISTANCE", "ROTATION", "SLIDER" };
|
||||
SDL_COMPILE_TIME_ASSERT(pen_axisnames_array_matches, SDL_arraysize(pen_axisnames) == SDL_PEN_NUM_AXES);
|
||||
|
||||
char name[64];
|
||||
char details[128];
|
||||
|
||||
@ -385,6 +388,7 @@ static void SDL_LogEvent(const SDL_Event *event)
|
||||
if ((SDL_EventLoggingVerbosity < 2) &&
|
||||
((event->type == SDL_EVENT_MOUSE_MOTION) ||
|
||||
(event->type == SDL_EVENT_FINGER_MOTION) ||
|
||||
(event->type == SDL_EVENT_PEN_AXIS) ||
|
||||
(event->type == SDL_EVENT_PEN_MOTION) ||
|
||||
(event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION) ||
|
||||
(event->type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE) ||
|
||||
@ -482,8 +486,6 @@ static void SDL_LogEvent(const SDL_Event *event)
|
||||
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_RESTORED);
|
||||
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MOUSE_ENTER);
|
||||
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MOUSE_LEAVE);
|
||||
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_PEN_ENTER);
|
||||
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_PEN_LEAVE);
|
||||
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_FOCUS_GAINED);
|
||||
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_FOCUS_LOST);
|
||||
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_CLOSE_REQUESTED);
|
||||
@ -699,45 +701,44 @@ static void SDL_LogEvent(const SDL_Event *event)
|
||||
break;
|
||||
#undef PRINT_FINGER_EVENT
|
||||
|
||||
#define PRINT_PTIP_EVENT(event) \
|
||||
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u tip=%u state=%s x=%g y=%g)", \
|
||||
(uint)event->ptip.timestamp, (uint)event->ptip.windowID, \
|
||||
(uint)event->ptip.which, (uint)event->ptip.tip, \
|
||||
event->ptip.state == SDL_PRESSED ? "down" : "up", \
|
||||
event->ptip.x, event->ptip.y)
|
||||
#define PRINT_PTOUCH_EVENT(event) \
|
||||
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u pen_state=%u x=%g y=%g eraser=%s state=%s)", \
|
||||
(uint)event->ptouch.timestamp, (uint)event->ptouch.windowID, (uint)event->ptouch.which, (uint)event->ptouch.pen_state, event->ptouch.x, event->ptouch.y, \
|
||||
event->ptouch.eraser ? "yes" : "no", event->ptouch.state == SDL_PRESSED ? "down" : "up");
|
||||
SDL_EVENT_CASE(SDL_EVENT_PEN_DOWN)
|
||||
PRINT_PTIP_EVENT(event);
|
||||
PRINT_PTOUCH_EVENT(event);
|
||||
break;
|
||||
SDL_EVENT_CASE(SDL_EVENT_PEN_UP)
|
||||
PRINT_PTIP_EVENT(event);
|
||||
PRINT_PTOUCH_EVENT(event);
|
||||
break;
|
||||
#undef PRINT_PTOUCH_EVENT
|
||||
|
||||
#define PRINT_PPROXIMITY_EVENT(event) \
|
||||
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u)", \
|
||||
(uint)event->pproximity.timestamp, (uint)event->pproximity.windowID, (uint)event->pproximity.which);
|
||||
SDL_EVENT_CASE(SDL_EVENT_PEN_PROXIMITY_IN)
|
||||
PRINT_PPROXIMITY_EVENT(event);
|
||||
break;
|
||||
SDL_EVENT_CASE(SDL_EVENT_PEN_PROXIMITY_OUT)
|
||||
PRINT_PPROXIMITY_EVENT(event);
|
||||
break;
|
||||
#undef PRINT_PPROXIMITY_EVENT
|
||||
|
||||
SDL_EVENT_CASE(SDL_EVENT_PEN_AXIS)
|
||||
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u pen_state=%u x=%g y=%g axis=%s value=%g)",
|
||||
(uint)event->paxis.timestamp, (uint)event->paxis.windowID, (uint)event->paxis.which, (uint)event->paxis.pen_state, event->paxis.x, event->paxis.y,
|
||||
((event->paxis.axis >= 0) && (event->paxis.axis < SDL_arraysize(pen_axisnames))) ? pen_axisnames[event->paxis.axis] : "[UNKNOWN]", event->paxis.value);
|
||||
break;
|
||||
#undef PRINT_PTIP_EVENT
|
||||
|
||||
SDL_EVENT_CASE(SDL_EVENT_PEN_MOTION)
|
||||
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u state=%08x x=%g y=%g [%g, %g, %g, %g, %g, %g])",
|
||||
(uint)event->pmotion.timestamp, (uint)event->pmotion.windowID,
|
||||
(uint)event->pmotion.which, (uint)event->pmotion.pen_state,
|
||||
event->pmotion.x, event->pmotion.y,
|
||||
event->pmotion.axes[SDL_PEN_AXIS_PRESSURE],
|
||||
event->pmotion.axes[SDL_PEN_AXIS_XTILT],
|
||||
event->pmotion.axes[SDL_PEN_AXIS_YTILT],
|
||||
event->pmotion.axes[SDL_PEN_AXIS_DISTANCE],
|
||||
event->pmotion.axes[SDL_PEN_AXIS_ROTATION],
|
||||
event->pmotion.axes[SDL_PEN_AXIS_SLIDER]);
|
||||
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u pen_state=%u x=%g y=%g)",
|
||||
(uint)event->pmotion.timestamp, (uint)event->pmotion.windowID, (uint)event->pmotion.which, (uint)event->pmotion.pen_state, event->pmotion.x, event->pmotion.y);
|
||||
break;
|
||||
|
||||
#define PRINT_PBUTTON_EVENT(event) \
|
||||
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u tip=%u state=%s x=%g y=%g axes=[%g, %g, %g, %g, %g, %g])", \
|
||||
(uint)event->pbutton.timestamp, (uint)event->pbutton.windowID, \
|
||||
(uint)event->pbutton.which, (uint)event->pbutton.button, \
|
||||
event->pbutton.state == SDL_PRESSED ? "pressed" : "released", \
|
||||
event->pbutton.x, event->pbutton.y, \
|
||||
event->pbutton.axes[SDL_PEN_AXIS_PRESSURE], \
|
||||
event->pbutton.axes[SDL_PEN_AXIS_XTILT], \
|
||||
event->pbutton.axes[SDL_PEN_AXIS_YTILT], \
|
||||
event->pbutton.axes[SDL_PEN_AXIS_DISTANCE], \
|
||||
event->pbutton.axes[SDL_PEN_AXIS_ROTATION], \
|
||||
event->pbutton.axes[SDL_PEN_AXIS_SLIDER])
|
||||
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u pen_state=%u x=%g y=%g button=%u state=%s)", \
|
||||
(uint)event->pbutton.timestamp, (uint)event->pbutton.windowID, (uint)event->pbutton.which, (uint)event->pbutton.pen_state, event->pbutton.x, event->pbutton.y, \
|
||||
(uint)event->pbutton.button, event->pbutton.state == SDL_PRESSED ? "down" : "up");
|
||||
SDL_EVENT_CASE(SDL_EVENT_PEN_BUTTON_DOWN)
|
||||
PRINT_PBUTTON_EVENT(event);
|
||||
break;
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "SDL_keyboard_c.h"
|
||||
#include "SDL_mouse_c.h"
|
||||
#include "SDL_touch_c.h"
|
||||
#include "SDL_pen_c.h"
|
||||
#include "SDL_windowevents_c.h"
|
||||
|
||||
/* Start and stop the event processing loop */
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "../video/SDL_sysvideo.h"
|
||||
#include "SDL_events_c.h"
|
||||
#include "SDL_mouse_c.h"
|
||||
#include "SDL_pen_c.h"
|
||||
#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_GDK)
|
||||
#include "../core/windows/SDL_windows.h" // For GetDoubleClickTime()
|
||||
#endif
|
||||
@ -254,6 +253,7 @@ int SDL_PreInitMouse(void)
|
||||
mouse->was_touch_mouse_events = SDL_FALSE; /* no touch to mouse movement event pending */
|
||||
|
||||
mouse->cursor_shown = SDL_TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -272,8 +272,6 @@ void SDL_PostInitMouse(void)
|
||||
SDL_DestroySurface(surface);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_PenInit();
|
||||
}
|
||||
|
||||
SDL_bool SDL_IsMouse(Uint16 vendor, Uint16 product)
|
||||
@ -741,7 +739,7 @@ static int SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_
|
||||
float xrel = 0.0f;
|
||||
float yrel = 0.0f;
|
||||
|
||||
if ((!mouse->relative_mode || mouse->warp_emulation_active) && mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID) {
|
||||
if ((!mouse->relative_mode || mouse->warp_emulation_active) && mouseID != SDL_TOUCH_MOUSEID) {
|
||||
/* We're not in relative mode, so all mouse events are global mouse events */
|
||||
mouseID = SDL_GLOBAL_MOUSE_ID;
|
||||
}
|
||||
@ -935,7 +933,7 @@ static int SDL_PrivateSendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_
|
||||
Uint32 buttonstate;
|
||||
SDL_MouseInputSource *source;
|
||||
|
||||
if (!mouse->relative_mode && mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID) {
|
||||
if (!mouse->relative_mode && mouseID != SDL_TOUCH_MOUSEID) {
|
||||
/* We're not in relative mode, so all mouse events are global mouse events */
|
||||
mouseID = SDL_GLOBAL_MOUSE_ID;
|
||||
}
|
||||
@ -1101,7 +1099,6 @@ void SDL_QuitMouse(void)
|
||||
}
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
SDL_ShowCursor();
|
||||
SDL_PenQuit();
|
||||
|
||||
if (mouse->def_cursor) {
|
||||
SDL_SetDefaultCursor(NULL);
|
||||
|
1380
src/events/SDL_pen.c
1380
src/events/SDL_pen.c
File diff suppressed because it is too large
Load Diff
@ -27,316 +27,71 @@
|
||||
#include "../../include/SDL3/SDL_pen.h"
|
||||
#include "SDL_mouse_c.h"
|
||||
|
||||
/* For testing alternate code paths: */
|
||||
#define SDL_PEN_DEBUG_NOID 0 /* Pretend that pen device does not supply ID / ID is some default value \
|
||||
affects: SDL_x11pen.c \
|
||||
SDL_waylandevents.c */
|
||||
#define SDL_PEN_DEBUG_NONWACOM 0 /* Pretend that no attached device is a Wacom device \
|
||||
affects: SDL_x11pen.c \
|
||||
SDL_waylandevents.c */
|
||||
#define SDL_PEN_DEBUG_UNKNOWN_WACOM 0 /* Pretend that any attached Wacom device is of an unknown make \
|
||||
affects: SDL_PenModifyFromWacomID() */
|
||||
#define SDL_PEN_DEBUG_NOSERIAL_WACOM 0 /* Pretend that any attached Wacom device has serial number 0 \
|
||||
affects: SDL_x11pen.c \
|
||||
SDL_waylandevents.c */
|
||||
typedef Uint32 SDL_PenCapabilityFlags;
|
||||
#define SDL_PEN_CAPABILITY_PRESSURE (1u << 0) /**< Provides pressure information on SDL_PEN_AXIS_PRESSURE. */
|
||||
#define SDL_PEN_CAPABILITY_XTILT (1u << 1) /**< Provides horizontal tilt information on SDL_PEN_AXIS_XTILT. */
|
||||
#define SDL_PEN_CAPABILITY_YTILT (1u << 2) /**< Provides vertical tilt information on SDL_PEN_AXIS_YTILT. */
|
||||
#define SDL_PEN_CAPABILITY_DISTANCE (1u << 3) /**< Provides distance to drawing tablet on SDL_PEN_AXIS_DISTANCE. */
|
||||
#define SDL_PEN_CAPABILITY_ROTATION (1u << 4) /**< Provides barrel rotation info on SDL_PEN_AXIS_ROTATION. */
|
||||
#define SDL_PEN_CAPABILITY_SLIDER (1u << 5) /**< Provides slider/finger wheel/etc on SDL_PEN_AXIS_SLIDER. */
|
||||
#define SDL_PEN_CAPABILITY_ERASER (1u << 6) /**< Pen also has an eraser tip. */
|
||||
|
||||
#define SDL_PEN_TYPE_NONE 0 /**< Pen type for non-pens (use to cancel pen registration) */
|
||||
|
||||
#define SDL_PEN_MAX_NAME 64
|
||||
|
||||
#define SDL_PEN_FLAG_ERROR (1ul << 28) /* Printed an internal API usage error about this pen (used to prevent spamming) */
|
||||
#define SDL_PEN_FLAG_NEW (1ul << 29) /* Pen was registered in most recent call to SDL_PenRegisterBegin() */
|
||||
#define SDL_PEN_FLAG_DETACHED (1ul << 30) /* Detached (not re-registered before last SDL_PenGCSweep()) */
|
||||
#define SDL_PEN_FLAG_STALE (1ul << 31) /* Not re-registered since last SDL_PenGCMark() */
|
||||
|
||||
typedef struct SDL_PenStatusInfo
|
||||
typedef enum SDL_PenSubtype
|
||||
{
|
||||
float x, y;
|
||||
float axes[SDL_PEN_NUM_AXES];
|
||||
Uint16 buttons; /* SDL_BUTTON(1) | SDL_BUTTON(2) | ... | SDL_PEN_DOWN_MASK */
|
||||
} SDL_PenStatusInfo;
|
||||
SDL_PEN_TYPE_UNKNOWN, /**< Unknown pen device */
|
||||
SDL_PEN_TYPE_ERASER, /**< Eraser */
|
||||
SDL_PEN_TYPE_PEN, /**< Generic pen; this is the default. */
|
||||
SDL_PEN_TYPE_PENCIL, /**< Pencil */
|
||||
SDL_PEN_TYPE_BRUSH, /**< Brush-like device */
|
||||
SDL_PEN_TYPE_AIRBRUSH /**< Airbrush device that "sprays" ink */
|
||||
} SDL_PenSubtype;
|
||||
|
||||
typedef struct
|
||||
typedef struct SDL_PenInfo
|
||||
{
|
||||
SDL_PenID id; /* id determines sort order unless SDL_PEN_FLAG_DETACHED is set */
|
||||
Uint32 flags; /* SDL_PEN_FLAG_* | SDK_PEN_DOWN_MASK | SDL_PEN_INK_MASK | SDL_PEN_ERASER_MASK | SDL_PEN_AXIS_* */
|
||||
SDL_Window *window; /* Current SDL window for this pen, or NULL */
|
||||
} SDL_PenHeader;
|
||||
SDL_PenCapabilityFlags capabilities; /**< bitflags of device capabilities */
|
||||
float max_tilt; /**< Physical maximum tilt angle, for XTILT and YTILT, or -1.0f if unknown. Pens cannot typically tilt all the way to 90 degrees, so this value is usually less than 90.0. */
|
||||
Uint32 wacom_id; /**< For Wacom devices: wacom tool type ID, otherwise 0 (useful e.g. with libwacom) */
|
||||
int num_buttons; /**< Number of pen buttons (not counting the pen tip), or -1 if unknown. */
|
||||
SDL_PenSubtype subtype; /**< type of pen device */
|
||||
} SDL_PenInfo;
|
||||
|
||||
/**
|
||||
* Internal (backend driver-independent) pen representation
|
||||
*
|
||||
* Implementation-specific backend drivers may read and write most of this structure, and
|
||||
* are expected to initialise parts of it when registering a new pen. They must not write
|
||||
* to the "header" section.
|
||||
*/
|
||||
typedef struct SDL_Pen
|
||||
{
|
||||
/* Backend driver MUST NOT not write to: */
|
||||
SDL_PenHeader header;
|
||||
// Backend calls this when a new pen device is hotplugged, plus once for each pen already connected at startup.
|
||||
// Note that name and info are copied but currently unused; this is placeholder for a potentially more robust API later.
|
||||
// Both are allowed to be NULL.
|
||||
extern SDL_PenID SDL_AddPenDevice(Uint64 timestamp, const char *name, const SDL_PenInfo *info, void *handle);
|
||||
|
||||
SDL_PenStatusInfo last; /* Last reported status, normally read-only for backend */
|
||||
// Backend calls this when an existing pen device is disconnected during runtime. They must free their own stuff separately.
|
||||
extern void SDL_RemovePenDevice(Uint64 timestamp, SDL_PenID instance_id);
|
||||
|
||||
/* Backend: MUST initialise this block when pen is first registered: */
|
||||
SDL_GUID guid; /* GUID, MUST be set by backend.
|
||||
MUST be unique (no other pen ID with same GUID).
|
||||
SHOULD be persistent across sessions. */
|
||||
// Backend can call this to remove all pens, probably during shutdown, with a callback to let them free their own handle.
|
||||
extern void SDL_RemoveAllPenDevices(void (*callback)(SDL_PenID instance_id, void *handle, void *userdata), void *userdata);
|
||||
|
||||
/* Backend: SHOULD initialise this block when pen is first registered if it can
|
||||
Otherwise: Set to sane default values during SDL_PenModifyEnd() */
|
||||
SDL_PenCapabilityInfo info; /* Detail information about the pen (buttons, tilt) */
|
||||
SDL_PenSubtype type;
|
||||
Uint8 last_mouse_button; /* For mouse button emulation: last emulated button */
|
||||
char *name; /* Preallocated; set via SDL_strlcpy(pen->name, src, SDL_PEN_MAX_NAME) */
|
||||
/* We hand this exact pointer to client code, so it must not be modified after
|
||||
creation. */
|
||||
// Backend calls this when a pen's button changes, to generate events and update state.
|
||||
extern int SDL_SendPenTouch(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, Uint8 state, Uint8 eraser);
|
||||
|
||||
void *deviceinfo; /* implementation-specific information */
|
||||
} SDL_Pen;
|
||||
// Backend calls this when a pen moves on the tablet, to generate events and update state.
|
||||
extern int SDL_SendPenMotion(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, float x, float y);
|
||||
|
||||
/* ---- API for backend driver only ---- */
|
||||
// Backend calls this when a pen's axis changes, to generate events and update state.
|
||||
extern int SDL_SendPenAxis(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, SDL_PenAxis axis, float value);
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Look up a pen by pen ID
|
||||
*
|
||||
* \param instance_id A Uint32 pen identifier (driver-dependent meaning). Must not be 0 = SDL_PEN_INVALID.
|
||||
* The same ID is exposed to clients as SDL_PenID.
|
||||
*
|
||||
* The pen pointer is only valid until the next call to SDL_PenModifyEnd() or SDL_PenGCSweep()
|
||||
*
|
||||
* \return pen, if it exists, or NULL
|
||||
*/
|
||||
extern SDL_Pen *SDL_GetPenPtr(Uint32 instance_id);
|
||||
// Backend calls this when a pen's button changes, to generate events and update state.
|
||||
extern int SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, Uint8 state, Uint8 button);
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Start registering a new pen or updating an existing pen.
|
||||
*
|
||||
* Acquires the pen mutex, which is held until the next call to SDL_PenModifyEnd() .
|
||||
*
|
||||
* If the PenID already exists, returns the existing entry. Otherwise initialise fresh SDL_Pen.
|
||||
* For new pens, sets SDL_PEN_FLAG_NEW.
|
||||
*
|
||||
* Usage:
|
||||
* - SDL_PenModifyStart()
|
||||
* - update pen object, in any order:
|
||||
* - SDL_PenModifyAddCapabilities()
|
||||
* - pen->guid (MUST be set for new pens, e.g. via ::SDL_PenUpdateGUIDForGeneric and related operations)
|
||||
* - pen->info.num_buttons
|
||||
* - pen->info.max_tilt
|
||||
* - pen->type
|
||||
* - pen->name
|
||||
* - pen->deviceinfo (backend-specific)
|
||||
* - SDL_PenModifyEnd()
|
||||
*
|
||||
* For new pens, sets defaults for:
|
||||
* - num_buttons (SDL_PEN_INFO_UNKNOWN)
|
||||
* - max_tilt (SDL_PEN_INFO_UNKNOWN)
|
||||
* - pen_type (SDL_PEN_TYPE_PEN)
|
||||
* - Zeroes all other (non-header) fields
|
||||
*
|
||||
* \param instance_id Pen ID to allocate (must not be 0 = SDL_PEN_ID_INVALID)
|
||||
* \returns SDL_Pen pointer; only valid until the call to SDL_PenModifyEnd()
|
||||
*/
|
||||
extern SDL_Pen *SDL_PenModifyBegin(Uint32 instance_id);
|
||||
// Backend can optionally use this to find the SDL_PenID for the `handle` that was passed to SDL_AddPenDevice.
|
||||
extern SDL_PenID SDL_FindPenByHandle(void *handle);
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Add capabilities to a pen (cf. SDL_PenModifyBegin()).
|
||||
*
|
||||
* Adds capabilities to a pen obtained via SDL_PenModifyBegin(). Can be called more than once.
|
||||
*
|
||||
* \param pen The pen to update
|
||||
* \param capabilities Capabilities flags, out of: SDL_PEN_AXIS_*, SDL_PEN_ERASER_MASK, SDL_PEN_INK_MASK
|
||||
* Setting SDL_PEN_ERASER_MASK will clear SDL_PEN_INK_MASK, and vice versa.
|
||||
*/
|
||||
extern void SDL_PenModifyAddCapabilities(SDL_Pen *pen, Uint32 capabilities);
|
||||
// Backend can optionally use this to find a SDL_PenID, selected by a callback examining all devices. Zero if not found.
|
||||
extern SDL_PenID SDL_FindPenByCallback(SDL_bool (*callback)(void *handle, void *userdata), void *userdata);
|
||||
|
||||
/**
|
||||
* Set up a pen structure for a Wacom device.
|
||||
*
|
||||
* Some backends (e.g., XInput2, Wayland) can only partially identify the capabilities of a given
|
||||
* pen but can identify Wacom pens and obtain their Wacom-specific device type identifiers.
|
||||
* This function partly automates device setup in those cases.
|
||||
*
|
||||
* This function does NOT set up the pen's GUID. Use ::SD_PenModifyGUIDForWacom instead.
|
||||
*
|
||||
* This function does NOT call SDL_PenModifyAddCapabilities() ifself, since some backends may
|
||||
* not have access to all pen axes (e.g., Xinput2).
|
||||
*
|
||||
* \param pen The pen to initialise
|
||||
* \param wacom_devicetype_id The Wacom-specific device type identifier
|
||||
* \param[out] axis_flags The set of physically supported axes for this pen, suitable for passing to
|
||||
* SDL_PenModifyAddCapabilities()
|
||||
*
|
||||
* \returns SDL_TRUE if the device ID could be identified, otherwise SDL_FALSE
|
||||
*/
|
||||
extern int SDL_PenModifyForWacomID(SDL_Pen *pen, Uint32 wacom_devicetype_id, Uint32 *axis_flags);
|
||||
// Backend can use this to map an axis to a capability bit.
|
||||
SDL_PenCapabilityFlags SDL_GetPenCapabilityFromAxis(SDL_PenAxis axis);
|
||||
|
||||
/**
|
||||
* Updates a GUID for a generic pen device.
|
||||
*
|
||||
* Assumes that the GUID has been pre-initialised (typically to 0).
|
||||
* Idempotent, and commutative with ::SDL_PenUpdateGUIDForWacom and ::SDL_PenUpdateGUIDForType
|
||||
*
|
||||
* \param[out] guid The GUID to update
|
||||
* \param upper Upper half of the device ID (assume lower entropy than "lower"; pass 0 if not available)
|
||||
* \param lower Lower half of the device ID (assume higher entropy than "upper")
|
||||
*/
|
||||
extern void SDL_PenUpdateGUIDForGeneric(SDL_GUID *guid, Uint32 upper, Uint32 lower);
|
||||
// Higher-level SDL video subsystem code calls this when starting up. Backends shouldn't.
|
||||
extern int SDL_InitPen(void);
|
||||
|
||||
/**
|
||||
* Updates a GUID based on a pen type
|
||||
*
|
||||
* Assumes that the GUID has been pre-initialised (typically to 0).
|
||||
* Idempotent, and commutative with ::SDL_PenUpdateGUIDForWacom and ::SDL_PenUpdateGUIDForGeneric
|
||||
*
|
||||
* \param[out] guid The GUID to update
|
||||
* \param pentype The pen type to insert
|
||||
*/
|
||||
extern void SDL_PenUpdateGUIDForType(SDL_GUID *guid, SDL_PenSubtype pentype);
|
||||
|
||||
/**
|
||||
* Updates a GUID for a Wacom pen device.
|
||||
*
|
||||
* Assumes that the GUID has been pre-initialised (typically to 0).
|
||||
* Idempotent, and commutative with ::SDL_PenUpdateGUIDForType and ::SDL_PenUpdateGUIDForGeneric
|
||||
*
|
||||
* This update is identical to the one written by ::SDL_PenModifyFromWacomID .
|
||||
*
|
||||
* \param[out] guid The GUID to update
|
||||
* \param wacom_devicetype_id The Wacom-specific device type identifier
|
||||
* \param wacom_serial_id The Wacom-specific serial number
|
||||
*/
|
||||
extern void SDL_PenUpdateGUIDForWacom(SDL_GUID *guid, Uint32 wacom_devicetype_id, Uint32 wacom_serial_id);
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Finish updating a pen.
|
||||
*
|
||||
* Releases the pen mutex acquired by SDL_PenModifyBegin() .
|
||||
*
|
||||
* If pen->type == SDL_PEN_TYPE_NONE, removes the pen entirely (only
|
||||
* for new pens). This allows backends to start registering a
|
||||
* potential pen device and to abort if the device turns out to not be
|
||||
* a pen.
|
||||
*
|
||||
* For new pens, this call will also set the following:
|
||||
* - name (default name, if not yet set)
|
||||
*
|
||||
* \param pen The pen to register. That pointer is no longer valid after this call.
|
||||
* \param attach Whether the pen should be attached (SDL_TRUE) or detached (SDL_FALSE).
|
||||
*
|
||||
* If the pen is detached or removed, it is the caller's responsibility to free
|
||||
* and null "deviceinfo".
|
||||
*/
|
||||
extern void SDL_PenModifyEnd(SDL_Pen *pen, SDL_bool attach);
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Mark all current pens for garbage collection.
|
||||
*
|
||||
* Must not be called while the pen mutex is held (by SDL_PenModifyBegin() ).
|
||||
*
|
||||
* SDL_PenGCMark() / SDL_PenGCSweep() provide a simple mechanism for
|
||||
* detaching all known pens that are not discoverable. This allows
|
||||
* backends to use the same code for pen discovery and for
|
||||
* hotplugging:
|
||||
*
|
||||
* - SDL_PenGCMark() and start backend-specific discovery
|
||||
* - for each discovered pen: SDL_PenModifyBegin() + SDL_PenModifyEnd() (this will retain existing state)
|
||||
* - SDL_PenGCSweep() (will now detach all pens that were not re-registered).
|
||||
*/
|
||||
extern void SDL_PenGCMark(void);
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Detach pens that haven't been reported attached since the last call to SDL_PenGCMark().
|
||||
*
|
||||
* Must not be called while the pen mutex is held (by SDL_PenModifyBegin() ).
|
||||
*
|
||||
* See SDL_PenGCMark() for details.
|
||||
*
|
||||
* \param context Extra parameter to pass through to "free_deviceinfo"
|
||||
* \param free_deviceinfo Operation to call on any non-NULL "backend.deviceinfo".
|
||||
*
|
||||
* \sa SDL_PenGCMark()
|
||||
*/
|
||||
extern void SDL_PenGCSweep(void *context, void (*free_deviceinfo)(Uint32 instance_id, void *deviceinfo, void *context));
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Send a pen motion event.
|
||||
*
|
||||
* Suppresses pen motion events that do not change the current pen state.
|
||||
* May also send a mouse motion event, if mouse emulation is enabled and the pen position has
|
||||
* changed sufficiently for the motion to be visible to mouse event listeners.
|
||||
*
|
||||
* \param timestamp Event timestamp in nanoseconds, or 0 to ask SDL to use SDL_GetTicksNS() .
|
||||
* While 0 is safe to report, your backends may be able to report more precise
|
||||
* timing information.
|
||||
* Keep in mind that you should never report timestamps that are greater than
|
||||
* SDL_GetTicksNS() . In particular, SDL_GetTicksNS() reports nanoseconds since the start
|
||||
* of the SDL session, and your backend may use a different starting point as "timestamp zero".
|
||||
* \param instance_id Pen
|
||||
* \param window_relative Coordinates are already window-relative
|
||||
* \param status Coordinates and axes (buttons are ignored)
|
||||
*
|
||||
* \returns SDL_TRUE if at least one event was sent
|
||||
*/
|
||||
extern int SDL_SendPenMotion(Uint64 timestamp, SDL_PenID instance_id, SDL_bool window_relative, const SDL_PenStatusInfo *status);
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Send a pen button event
|
||||
*
|
||||
* \param timestamp Event timestamp in nanoseconds, or 0 to ask SDL to use SDL_GetTicksNS() .
|
||||
* See SDL_SendPenMotion() for a discussion about how to handle timestamps.
|
||||
* \param instance_id Pen
|
||||
* \param state SDL_PRESSED or SDL_RELEASED
|
||||
* \param button Button number: 1 (first physical button) etc.
|
||||
*
|
||||
* \returns SDL_TRUE if at least one event was sent
|
||||
*/
|
||||
extern int SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, Uint8 state, Uint8 button);
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Send a pen tip event (touching or no longer touching the surface)
|
||||
*
|
||||
* Note: the backend should perform hit testing on window decoration elements to allow the pen
|
||||
* to e.g. resize/move the window, just as for mouse events, unless ::SDL_SendPenTipEvent is false.
|
||||
*
|
||||
* \param timestamp Event timestamp in nanoseconds, or 0 to ask SDL to use SDL_GetTicksNS() .
|
||||
* See SDL_SendPenMotion() for a discussion about how to handle timestamps.
|
||||
* \param instance_id Pen
|
||||
* \param state SDL_PRESSED (for PEN_DOWN) or SDL_RELEASED (for PEN_UP)
|
||||
*
|
||||
* \returns SDL_TRUE if at least one event was sent
|
||||
*/
|
||||
extern int SDL_SendPenTipEvent(Uint64 timestamp, SDL_PenID instance_id, Uint8 state);
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Check if a PEN_DOWN event should perform hit box testing.
|
||||
*
|
||||
* \returns SDL_TRUE if and only if the backend should perform hit testing
|
||||
*/
|
||||
extern SDL_bool SDL_PenPerformHitTest(void);
|
||||
|
||||
/**
|
||||
* (Only for backend driver) Send a pen window event.
|
||||
*
|
||||
* Tracks when a pen has entered/left a window.
|
||||
* Don't call this when reporting new pens or removing known pens; those cases are handled automatically.
|
||||
*
|
||||
* \param timestamp Event timestamp in nanoseconds, or 0 to ask SDL to use SDL_GetTicksNS() .
|
||||
* See SDL_SendPenMotion() for a discussion about how to handle timestamps.
|
||||
* \param instance_id Pen
|
||||
* \param window Window to enter, or NULL to exit
|
||||
*/
|
||||
extern int SDL_SendPenWindowEvent(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window);
|
||||
|
||||
/**
|
||||
* Initialises the pen subsystem.
|
||||
*/
|
||||
extern void SDL_PenInit(void);
|
||||
|
||||
/**
|
||||
* De-initialises the pen subsystem.
|
||||
*/
|
||||
extern void SDL_PenQuit(void);
|
||||
// Higher-level SDL video subsystem code calls this when shutting down. Backends shouldn't.
|
||||
extern void SDL_QuitPen(void);
|
||||
|
||||
#endif /* SDL_pen_c_h_ */
|
||||
|
||||
|
@ -563,6 +563,7 @@ int SDL_VideoInit(const char *driver_name)
|
||||
SDL_bool init_keyboard = SDL_FALSE;
|
||||
SDL_bool init_mouse = SDL_FALSE;
|
||||
SDL_bool init_touch = SDL_FALSE;
|
||||
SDL_bool init_pen = SDL_FALSE;
|
||||
int i = 0;
|
||||
|
||||
/* Check to make sure we don't overwrite '_this' */
|
||||
@ -589,6 +590,10 @@ int SDL_VideoInit(const char *driver_name)
|
||||
goto pre_driver_error;
|
||||
}
|
||||
init_touch = SDL_TRUE;
|
||||
if (SDL_InitPen() < 0) {
|
||||
goto pre_driver_error;
|
||||
}
|
||||
init_pen = SDL_TRUE;
|
||||
|
||||
/* Select the proper video driver */
|
||||
video = NULL;
|
||||
@ -673,6 +678,9 @@ int SDL_VideoInit(const char *driver_name)
|
||||
|
||||
pre_driver_error:
|
||||
SDL_assert(_this == NULL);
|
||||
if (init_pen) {
|
||||
SDL_QuitPen();
|
||||
}
|
||||
if (init_touch) {
|
||||
SDL_QuitTouch();
|
||||
}
|
||||
@ -4217,6 +4225,7 @@ void SDL_VideoQuit(void)
|
||||
}
|
||||
|
||||
/* Halt event processing before doing anything else */
|
||||
SDL_QuitPen();
|
||||
SDL_QuitTouch();
|
||||
SDL_QuitMouse();
|
||||
SDL_QuitKeyboard();
|
||||
|
@ -2364,421 +2364,255 @@ void Wayland_create_text_input(SDL_VideoData *d)
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_PenID Wayland_get_penid(void *data, struct zwp_tablet_tool_v2 *tool)
|
||||
|
||||
// Pen/Tablet support...
|
||||
|
||||
typedef struct SDL_WaylandPenTool // a stylus, etc, on a tablet.
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
return sdltool->penid;
|
||||
}
|
||||
|
||||
/* For registering pens */
|
||||
static SDL_Pen *Wayland_get_current_pen(void *data, struct zwp_tablet_tool_v2 *tool)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
|
||||
if (!input->current_pen.builder) {
|
||||
/* Starting new pen or updating one? */
|
||||
SDL_PenID penid = sdltool->penid;
|
||||
|
||||
if (penid == 0) {
|
||||
/* Found completely new pen? */
|
||||
penid = ++input->num_pens;
|
||||
sdltool->penid = penid;
|
||||
}
|
||||
input->current_pen.builder = SDL_GetPenPtr(penid);
|
||||
if (!input->current_pen.builder) {
|
||||
/* Must register as new pen */
|
||||
input->current_pen.builder = SDL_PenModifyBegin(penid);
|
||||
}
|
||||
}
|
||||
return input->current_pen.builder;
|
||||
}
|
||||
SDL_PenID instance_id;
|
||||
SDL_PenInfo info;
|
||||
SDL_Window *tool_focus;
|
||||
struct zwp_tablet_tool_v2 *wltool;
|
||||
float x;
|
||||
float y;
|
||||
SDL_bool frame_motion_set;
|
||||
float frame_axes[SDL_PEN_NUM_AXES];
|
||||
Uint32 frame_axes_set;
|
||||
int frame_pen_down;
|
||||
int frame_buttons[3];
|
||||
} SDL_WaylandPenTool;
|
||||
|
||||
static void tablet_tool_handle_type(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t type)
|
||||
{
|
||||
SDL_Pen *pen = Wayland_get_current_pen(data, tool);
|
||||
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
switch (type) {
|
||||
case ZWP_TABLET_TOOL_V2_TYPE_ERASER:
|
||||
pen->type = SDL_PEN_TYPE_ERASER;
|
||||
break;
|
||||
|
||||
case ZWP_TABLET_TOOL_V2_TYPE_PEN:
|
||||
pen->type = SDL_PEN_TYPE_PEN;
|
||||
break;
|
||||
|
||||
case ZWP_TABLET_TOOL_V2_TYPE_PENCIL:
|
||||
pen->type = SDL_PEN_TYPE_PENCIL;
|
||||
break;
|
||||
|
||||
case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH:
|
||||
pen->type = SDL_PEN_TYPE_AIRBRUSH;
|
||||
break;
|
||||
|
||||
case ZWP_TABLET_TOOL_V2_TYPE_BRUSH:
|
||||
pen->type = SDL_PEN_TYPE_BRUSH;
|
||||
break;
|
||||
|
||||
case ZWP_TABLET_TOOL_V2_TYPE_FINGER:
|
||||
case ZWP_TABLET_TOOL_V2_TYPE_MOUSE:
|
||||
case ZWP_TABLET_TOOL_V2_TYPE_LENS:
|
||||
default:
|
||||
pen->type = SDL_PEN_TYPE_NONE; /* Mark for deregistration */
|
||||
#define CASE(typ) case ZWP_TABLET_TOOL_V2_TYPE_##typ: sdltool->info.subtype = SDL_PEN_TYPE_##typ; return
|
||||
CASE(ERASER);
|
||||
CASE(PEN);
|
||||
CASE(PENCIL);
|
||||
CASE(AIRBRUSH);
|
||||
CASE(BRUSH);
|
||||
#undef CASE
|
||||
default: sdltool->info.subtype = SDL_PEN_TYPE_UNKNOWN; // we'll decline to add this when the `done` event comes through.
|
||||
}
|
||||
|
||||
SDL_PenUpdateGUIDForType(&pen->guid, pen->type);
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_hardware_serial(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial_hi, uint32_t serial_lo)
|
||||
{
|
||||
#if !(SDL_PEN_DEBUG_NOID)
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
|
||||
if (!input->current_pen.builder_guid_complete) {
|
||||
SDL_Pen *pen = Wayland_get_current_pen(data, tool);
|
||||
SDL_PenUpdateGUIDForGeneric(&pen->guid, serial_hi, serial_lo);
|
||||
if (serial_hi || serial_lo) {
|
||||
input->current_pen.builder_guid_complete = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// don't care about this atm.
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t id_hi, uint32_t id_lo)
|
||||
{
|
||||
#if !(SDL_PEN_DEBUG_NOID | SDL_PEN_DEBUG_NONWACOM)
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
SDL_Pen *pen = Wayland_get_current_pen(data, tool);
|
||||
Uint32 axis_flags;
|
||||
|
||||
#if SDL_PEN_DEBUG_NOSERIAL_WACOM /* Check: have we disabled pen serial ID decoding for testing? */
|
||||
id_hi = 0;
|
||||
#endif
|
||||
|
||||
SDL_PenUpdateGUIDForWacom(&pen->guid, id_lo, id_hi);
|
||||
if (id_hi) { /* Have a serial number? */
|
||||
input->current_pen.builder_guid_complete = SDL_TRUE;
|
||||
}
|
||||
|
||||
if (SDL_PenModifyForWacomID(pen, id_lo, &axis_flags)) {
|
||||
SDL_PenModifyAddCapabilities(pen, axis_flags);
|
||||
}
|
||||
#endif
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
sdltool->info.wacom_id = id_lo;
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_capability(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t capability)
|
||||
{
|
||||
SDL_Pen *pen = Wayland_get_current_pen(data, tool);
|
||||
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
switch (capability) {
|
||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT:
|
||||
SDL_PenModifyAddCapabilities(pen, SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK);
|
||||
break;
|
||||
|
||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE:
|
||||
SDL_PenModifyAddCapabilities(pen, SDL_PEN_AXIS_PRESSURE_MASK);
|
||||
break;
|
||||
|
||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE:
|
||||
SDL_PenModifyAddCapabilities(pen, SDL_PEN_AXIS_DISTANCE_MASK);
|
||||
break;
|
||||
|
||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION:
|
||||
SDL_PenModifyAddCapabilities(pen, SDL_PEN_AXIS_ROTATION_MASK);
|
||||
break;
|
||||
|
||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER:
|
||||
SDL_PenModifyAddCapabilities(pen, SDL_PEN_AXIS_SLIDER_MASK);
|
||||
break;
|
||||
|
||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL:
|
||||
/* Presumably for tools other than pens? */
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
#define CASE(wltyp,sdltyp) case ZWP_TABLET_TOOL_V2_CAPABILITY_##wltyp: sdltool->info.capabilities |= sdltyp; return
|
||||
CASE(TILT, SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT);
|
||||
CASE(PRESSURE, SDL_PEN_CAPABILITY_PRESSURE);
|
||||
CASE(DISTANCE, SDL_PEN_CAPABILITY_DISTANCE);
|
||||
CASE(ROTATION, SDL_PEN_CAPABILITY_ROTATION);
|
||||
CASE(SLIDER, SDL_PEN_CAPABILITY_SLIDER);
|
||||
#undef CASE
|
||||
default: break; // unsupported here.
|
||||
}
|
||||
}
|
||||
|
||||
static void Wayland_tool_builder_reset(struct SDL_WaylandTabletInput *input)
|
||||
{
|
||||
input->current_pen.builder = NULL;
|
||||
input->current_pen.builder_guid_complete = SDL_FALSE;
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_done(void *data, struct zwp_tablet_tool_v2 *tool)
|
||||
{
|
||||
SDL_Pen *pen = Wayland_get_current_pen(data, tool);
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
|
||||
if (!input->current_pen.builder_guid_complete) {
|
||||
/* No complete GUID? Use tablet and tool device index */
|
||||
SDL_PenUpdateGUIDForGeneric(&pen->guid, input->id, sdltool->penid);
|
||||
}
|
||||
|
||||
SDL_PenModifyEnd(pen, SDL_TRUE);
|
||||
|
||||
Wayland_tool_builder_reset(input);
|
||||
}
|
||||
|
||||
static void Wayland_tool_destroy(struct zwp_tablet_tool_v2 *tool)
|
||||
{
|
||||
if (tool) {
|
||||
struct SDL_WaylandTool *waypen = zwp_tablet_tool_v2_get_user_data(tool);
|
||||
if (waypen) {
|
||||
SDL_free(waypen);
|
||||
}
|
||||
zwp_tablet_tool_v2_destroy(tool);
|
||||
}
|
||||
}
|
||||
|
||||
static void tablet_object_list_remove(struct SDL_WaylandTabletObjectListNode *head, void *object);
|
||||
|
||||
static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *tool)
|
||||
{
|
||||
struct SDL_WaylandTool *waypen = zwp_tablet_tool_v2_get_user_data(tool);
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
SDL_Pen *pen = Wayland_get_current_pen(data, tool);
|
||||
if (pen) {
|
||||
SDL_PenModifyEnd(pen, SDL_FALSE);
|
||||
Wayland_tool_builder_reset(waypen->tablet);
|
||||
Wayland_tool_destroy(tool);
|
||||
} else {
|
||||
zwp_tablet_tool_v2_destroy(tool);
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
if (sdltool->instance_id) {
|
||||
SDL_RemovePenDevice(0, sdltool->instance_id);
|
||||
}
|
||||
|
||||
tablet_object_list_remove(input->tools, tool);
|
||||
zwp_tablet_tool_v2_destroy(tool);
|
||||
SDL_free(sdltool);
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_proximity_in(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
SDL_WindowData *window;
|
||||
SDL_PenID penid = Wayland_get_penid(data, tool);
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
SDL_WindowData *windowdata = surface ? Wayland_GetWindowDataForOwnedSurface(surface) : NULL;
|
||||
sdltool->tool_focus = windowdata ? windowdata->sdlwindow : NULL;
|
||||
|
||||
if (!surface) {
|
||||
return;
|
||||
SDL_assert(sdltool->instance_id == 0); // shouldn't be added at this point.
|
||||
if (sdltool->info.subtype != SDL_PEN_TYPE_UNKNOWN) { // don't tell SDL about it if we don't know its role.
|
||||
sdltool->instance_id = SDL_AddPenDevice(0, NULL, &sdltool->info, sdltool);
|
||||
}
|
||||
|
||||
window = Wayland_GetWindowDataForOwnedSurface(surface);
|
||||
|
||||
if (window) {
|
||||
input->tool_focus = window;
|
||||
input->tool_prox_serial = serial;
|
||||
|
||||
if (penid) {
|
||||
SDL_SendPenWindowEvent(0, penid, window->sdlwindow);
|
||||
} else {
|
||||
SDL_SetMouseFocus(window->sdlwindow);
|
||||
}
|
||||
SDL_SetCursor(NULL);
|
||||
}
|
||||
// According to the docs, this should be followed by a motion event, where we'll send our SDL events.
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_proximity_out(void *data, struct zwp_tablet_tool_v2 *tool)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
SDL_PenID penid = Wayland_get_penid(data, tool);
|
||||
if (input->tool_focus) {
|
||||
if (penid) {
|
||||
SDL_SendPenWindowEvent(0, penid, NULL);
|
||||
} else {
|
||||
SDL_SetMouseFocus(NULL);
|
||||
}
|
||||
input->tool_focus = NULL;
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
sdltool->tool_focus = NULL;
|
||||
|
||||
if (sdltool->instance_id) {
|
||||
SDL_RemovePenDevice(0, sdltool->instance_id);
|
||||
sdltool->instance_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
|
||||
input->current_pen.buttons_pressed |= SDL_PEN_DOWN_MASK;
|
||||
|
||||
input->current_pen.serial = serial;
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
sdltool->frame_pen_down = 1;
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 *tool)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
input->current_pen.buttons_released |= SDL_PEN_DOWN_MASK;
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
sdltool->frame_pen_down = 0;
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t sx_w, wl_fixed_t sy_w)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
SDL_WindowData *window = input->tool_focus;
|
||||
SDL_PenID penid = Wayland_get_penid(data, tool);
|
||||
|
||||
input->sx_w = sx_w;
|
||||
input->sy_w = sy_w;
|
||||
|
||||
if (input->tool_focus) {
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
SDL_Window *window = sdltool->tool_focus;
|
||||
if (window) {
|
||||
const SDL_WindowData *windowdata = window->internal;
|
||||
const float sx_f = (float)wl_fixed_to_double(sx_w);
|
||||
const float sy_f = (float)wl_fixed_to_double(sy_w);
|
||||
const float sx = sx_f * window->pointer_scale.x;
|
||||
const float sy = sy_f * window->pointer_scale.y;
|
||||
|
||||
if (penid != SDL_PEN_INVALID) {
|
||||
input->current_pen.update_status.x = sx;
|
||||
input->current_pen.update_status.y = sy;
|
||||
input->current_pen.update_window = window;
|
||||
} else {
|
||||
/* Plain mouse event */
|
||||
SDL_SendMouseMotion(0, window->sdlwindow, SDL_GLOBAL_MOUSE_ID, SDL_FALSE, sx, sy);
|
||||
}
|
||||
const float sx = sx_f * windowdata->pointer_scale.x;
|
||||
const float sy = sy_f * windowdata->pointer_scale.y;
|
||||
sdltool->x = sx;
|
||||
sdltool->y = sy;
|
||||
sdltool->frame_motion_set = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_pressure(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t pressure)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
input->current_pen.update_status.axes[SDL_PEN_AXIS_PRESSURE] = pressure / 65535.0f;
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
sdltool->frame_axes[SDL_PEN_AXIS_PRESSURE] = ((float) pressure) / 65535.0f;
|
||||
sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_PRESSURE);
|
||||
if (pressure) {
|
||||
input->current_pen.update_status.axes[SDL_PEN_AXIS_DISTANCE] = 0.0f;
|
||||
sdltool->frame_axes[SDL_PEN_AXIS_DISTANCE] = 0.0f;
|
||||
sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_DISTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_distance(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t distance)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
input->current_pen.update_status.axes[SDL_PEN_AXIS_DISTANCE] = distance / 65535.0f;
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
sdltool->frame_axes[SDL_PEN_AXIS_DISTANCE] = ((float) distance) / 65535.0f;
|
||||
sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_DISTANCE);
|
||||
if (distance) {
|
||||
input->current_pen.update_status.axes[SDL_PEN_AXIS_PRESSURE] = 0.0f;
|
||||
sdltool->frame_axes[SDL_PEN_AXIS_PRESSURE] = 0.0f;
|
||||
sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_PRESSURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_tilt(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t xtilt, wl_fixed_t ytilt)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
|
||||
input->current_pen.update_status.axes[SDL_PEN_AXIS_XTILT] = (float)(wl_fixed_to_double(xtilt));
|
||||
input->current_pen.update_status.axes[SDL_PEN_AXIS_YTILT] = (float)(wl_fixed_to_double(ytilt));
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
sdltool->frame_axes[SDL_PEN_AXIS_XTILT] = (float)(wl_fixed_to_double(xtilt));
|
||||
sdltool->frame_axes[SDL_PEN_AXIS_YTILT] = (float)(wl_fixed_to_double(ytilt));
|
||||
sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_XTILT) | (1u << SDL_PEN_AXIS_YTILT);
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_button(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, uint32_t button, uint32_t state)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
Uint16 mask = 0;
|
||||
SDL_bool pressed = state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
/* record event serial number to report it later in tablet_tool_handle_frame() */
|
||||
input->current_pen.serial = serial;
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
int sdlbutton;
|
||||
|
||||
switch (button) {
|
||||
/* see %{_includedir}/linux/input-event-codes.h */
|
||||
case 0x14b: /* BTN_STYLUS */
|
||||
mask = SDL_BUTTON_LMASK;
|
||||
sdlbutton = 1;
|
||||
break;
|
||||
case 0x14c: /* BTN_STYLUS2 */
|
||||
mask = SDL_BUTTON_MMASK;
|
||||
sdlbutton = 2;
|
||||
break;
|
||||
case 0x149: /* BTN_STYLUS3 */
|
||||
mask = SDL_BUTTON_RMASK;
|
||||
sdlbutton = 3;
|
||||
break;
|
||||
default:
|
||||
return; // don't care about this button, I guess.
|
||||
}
|
||||
|
||||
if (pressed) {
|
||||
input->current_pen.buttons_pressed |= mask;
|
||||
} else {
|
||||
input->current_pen.buttons_released |= mask;
|
||||
}
|
||||
SDL_assert((sdlbutton >= 1) && (sdlbutton <= SDL_arraysize(sdltool->frame_buttons)));
|
||||
sdltool->frame_buttons[sdlbutton-1] = (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_rotation(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t degrees)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
float rotation = (float)(wl_fixed_to_double(degrees));
|
||||
|
||||
/* map to -180.0f ... 179.0f range: */
|
||||
input->current_pen.update_status.axes[SDL_PEN_AXIS_ROTATION] = rotation > 180.0f ? rotation - 360.0f : rotation;
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
const float rotation = (float)(wl_fixed_to_double(degrees));
|
||||
sdltool->frame_axes[SDL_PEN_AXIS_ROTATION] = (rotation > 180.0f) ? (rotation - 360.0f) : rotation; // map to -180.0f ... 179.0f range
|
||||
sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_ROTATION);
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_slider(void *data, struct zwp_tablet_tool_v2 *tool, int32_t position)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
input->current_pen.update_status.axes[SDL_PEN_AXIS_SLIDER] = position / 65535.f;
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
sdltool->frame_axes[SDL_PEN_AXIS_SLIDER] = position / 65535.f;
|
||||
sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_SLIDER);
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_wheel(void *data, struct zwp_tablet_tool_v2 *tool, int32_t degrees, int32_t clicks)
|
||||
{
|
||||
/* not supported at the moment */
|
||||
// not supported at the moment
|
||||
}
|
||||
|
||||
static void tablet_tool_handle_frame(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t time)
|
||||
{
|
||||
struct SDL_WaylandTool *sdltool = data;
|
||||
struct SDL_WaylandTabletInput *input = sdltool->tablet;
|
||||
SDL_PenID penid = Wayland_get_penid(data, tool);
|
||||
SDL_WindowData *window = input->current_pen.update_window;
|
||||
SDL_PenStatusInfo *status = &input->current_pen.update_status;
|
||||
int button;
|
||||
int button_mask;
|
||||
Uint64 timestamp = Wayland_GetEventTimestamp(SDL_MS_TO_NS(time));
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
|
||||
|
||||
if (penid == 0 || !window) { /* Not a pen, or event reported out of focus */
|
||||
return;
|
||||
if (!sdltool->instance_id) {
|
||||
return; // Not a pen we report on.
|
||||
}
|
||||
/* window == input->tool_focus */
|
||||
|
||||
/* All newly released buttons + PEN_UP event */
|
||||
button_mask = input->current_pen.buttons_released;
|
||||
if (button_mask & SDL_PEN_DOWN_MASK) {
|
||||
/* Perform hit test, if appropriate */
|
||||
if (!SDL_PenPerformHitTest()
|
||||
|| !ProcessHitTest(window, input->sdlWaylandInput->seat, input->sx_w, input->sy_w, input->current_pen.serial)) {
|
||||
SDL_SendPenTipEvent(timestamp, penid, SDL_RELEASED);
|
||||
}
|
||||
}
|
||||
button_mask &= ~SDL_PEN_DOWN_MASK;
|
||||
const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_MS_TO_NS(time));
|
||||
const SDL_PenID instance_id = sdltool->instance_id;
|
||||
const SDL_Window *window = sdltool->tool_focus;
|
||||
|
||||
for (button = 1; button_mask; ++button, button_mask >>= 1) {
|
||||
if (button_mask & 1) {
|
||||
SDL_SendPenButton(timestamp, penid, SDL_RELEASED, button);
|
||||
// I don't know if this is necessary (or makes sense), but send motion before pen downs, but after pen ups, so you don't get unexpected lines drawn.
|
||||
if (sdltool->frame_motion_set && (sdltool->frame_pen_down != -1)) {
|
||||
if (sdltool->frame_pen_down) {
|
||||
SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
|
||||
SDL_SendPenTouch(timestamp, instance_id, window, SDL_PRESSED, 0); // !!! FIXME: how do we know what tip is in use?
|
||||
} else {
|
||||
SDL_SendPenTouch(timestamp, instance_id, window, SDL_RELEASED, 0); // !!! FIXME: how do we know what tip is in use?
|
||||
SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
|
||||
}
|
||||
} else {
|
||||
if (sdltool->frame_pen_down != -1) {
|
||||
SDL_SendPenTouch(timestamp, instance_id, window, sdltool->frame_pen_down ? SDL_PRESSED : SDL_RELEASED, 0); // !!! FIXME: how do we know what tip is in use?
|
||||
}
|
||||
|
||||
if (sdltool->frame_motion_set) {
|
||||
SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
|
||||
}
|
||||
}
|
||||
|
||||
/* All newly pressed buttons + PEN_DOWN event */
|
||||
button_mask = input->current_pen.buttons_pressed;
|
||||
if (button_mask & SDL_PEN_DOWN_MASK) {
|
||||
/* Perform hit test, if appropriate */
|
||||
if (!SDL_PenPerformHitTest()
|
||||
|| !ProcessHitTest(window, input->sdlWaylandInput->seat, input->sx_w, input->sy_w, input->current_pen.serial)) {
|
||||
SDL_SendPenTipEvent(timestamp, penid, SDL_PRESSED);
|
||||
}
|
||||
}
|
||||
button_mask &= ~SDL_PEN_DOWN_MASK;
|
||||
|
||||
for (button = 1; button_mask; ++button, button_mask >>= 1) {
|
||||
if (button_mask & 1) {
|
||||
SDL_SendPenButton(timestamp, penid, SDL_PRESSED, button);
|
||||
for (SDL_PenAxis i = 0; i < SDL_PEN_NUM_AXES; i++) {
|
||||
if (sdltool->frame_axes_set & (1u << i)) {
|
||||
SDL_SendPenAxis(timestamp, instance_id, window, i, sdltool->frame_axes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_SendPenMotion(timestamp, penid, SDL_TRUE, status);
|
||||
for (int i = 0; i < SDL_arraysize(sdltool->frame_buttons); i++) {
|
||||
const int state = sdltool->frame_buttons[i];
|
||||
if (state != -1) {
|
||||
SDL_SendPenButton(timestamp, instance_id, window, state ? SDL_PRESSED : SDL_RELEASED, (Uint8) (i + 1));
|
||||
sdltool->frame_buttons[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wayland_UpdateImplicitGrabSerial will ignore serial 0, so it is safe to call with the default value */
|
||||
Wayland_UpdateImplicitGrabSerial(input->sdlWaylandInput, input->current_pen.serial);
|
||||
|
||||
/* Reset masks for next tool frame */
|
||||
input->current_pen.buttons_pressed = 0;
|
||||
input->current_pen.buttons_released = 0;
|
||||
input->current_pen.serial = 0;
|
||||
// reset for next frame.
|
||||
sdltool->frame_pen_down = -1;
|
||||
sdltool->frame_motion_set = SDL_FALSE;
|
||||
sdltool->frame_axes_set = 0;
|
||||
}
|
||||
|
||||
static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
|
||||
@ -2803,92 +2637,34 @@ static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
|
||||
tablet_tool_handle_frame
|
||||
};
|
||||
|
||||
static struct SDL_WaylandTabletObjectListNode *tablet_object_list_new_node(void *object)
|
||||
{
|
||||
struct SDL_WaylandTabletObjectListNode *node;
|
||||
|
||||
node = SDL_calloc(1, sizeof(*node));
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->next = NULL;
|
||||
node->object = object;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void tablet_object_list_append(struct SDL_WaylandTabletObjectListNode *head, void *object)
|
||||
{
|
||||
if (!head->object) {
|
||||
head->object = object;
|
||||
return;
|
||||
}
|
||||
|
||||
while (head->next) {
|
||||
head = head->next;
|
||||
}
|
||||
|
||||
head->next = tablet_object_list_new_node(object);
|
||||
}
|
||||
|
||||
static void tablet_object_list_destroy(struct SDL_WaylandTabletObjectListNode *head, void (*deleter)(void *object))
|
||||
{
|
||||
while (head) {
|
||||
struct SDL_WaylandTabletObjectListNode *next = head->next;
|
||||
if (head->object) {
|
||||
(*deleter)(head->object);
|
||||
}
|
||||
SDL_free(head);
|
||||
head = next;
|
||||
}
|
||||
}
|
||||
|
||||
void tablet_object_list_remove(struct SDL_WaylandTabletObjectListNode *head, void *object)
|
||||
{
|
||||
struct SDL_WaylandTabletObjectListNode **head_p = &head;
|
||||
while (*head_p && (*head_p)->object != object) {
|
||||
head_p = &((*head_p)->next);
|
||||
}
|
||||
|
||||
if (*head_p) {
|
||||
struct SDL_WaylandTabletObjectListNode *object_head = *head_p;
|
||||
|
||||
if (object_head == head) {
|
||||
/* Must not remove head node */
|
||||
head->object = NULL;
|
||||
} else {
|
||||
*head_p = object_head->next;
|
||||
SDL_free(object_head);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void tablet_seat_handle_tablet_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_v2 *tablet)
|
||||
{
|
||||
struct SDL_WaylandTabletInput *input = data;
|
||||
|
||||
tablet_object_list_append(input->tablets, tablet);
|
||||
// don't care atm.
|
||||
}
|
||||
|
||||
static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_tool_v2 *tool)
|
||||
{
|
||||
struct SDL_WaylandTabletInput *input = data;
|
||||
struct SDL_WaylandTool *sdltool = SDL_calloc(1, sizeof(struct SDL_WaylandTool));
|
||||
SDL_WaylandPenTool *sdltool = SDL_calloc(1, sizeof(*sdltool));
|
||||
|
||||
if (sdltool) { // if allocation failed, oh well, we won't report this device.
|
||||
sdltool->wltool = tool;
|
||||
sdltool->info.max_tilt = -1.0f;
|
||||
sdltool->info.num_buttons = -1;
|
||||
sdltool->frame_pen_down = -1;
|
||||
for (int i = 0; i < SDL_arraysize(sdltool->frame_buttons); i++) {
|
||||
sdltool->frame_buttons[i] = -1;
|
||||
}
|
||||
|
||||
// this will send a bunch of zwp_tablet_tool_v2 events right up front to tell
|
||||
// us device details, with a "done" event to let us know we have everything.
|
||||
zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, sdltool);
|
||||
zwp_tablet_tool_v2_set_user_data(tool, sdltool);
|
||||
|
||||
sdltool->tablet = input;
|
||||
|
||||
tablet_object_list_append(input->tools, tool);
|
||||
}
|
||||
}
|
||||
|
||||
static void tablet_seat_handle_pad_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_pad_v2 *pad)
|
||||
{
|
||||
struct SDL_WaylandTabletInput *input = data;
|
||||
|
||||
tablet_object_list_append(input->pads, pad);
|
||||
// we don't care atm.
|
||||
}
|
||||
|
||||
static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
|
||||
@ -2897,44 +2673,39 @@ static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
|
||||
tablet_seat_handle_pad_added
|
||||
};
|
||||
|
||||
void Wayland_input_add_tablet(struct SDL_WaylandInput *input, struct SDL_WaylandTabletManager *tablet_manager)
|
||||
void Wayland_input_init_tablet_support(struct SDL_WaylandInput *input, struct zwp_tablet_manager_v2 *tablet_manager)
|
||||
{
|
||||
struct SDL_WaylandTabletInput *tablet_input;
|
||||
static Uint32 num_tablets = 0;
|
||||
|
||||
if (!tablet_manager || !input->seat) {
|
||||
return;
|
||||
}
|
||||
|
||||
tablet_input = SDL_calloc(1, sizeof(*tablet_input));
|
||||
SDL_WaylandTabletInput *tablet_input = SDL_calloc(1, sizeof(*tablet_input));
|
||||
if (!tablet_input) {
|
||||
return;
|
||||
}
|
||||
|
||||
input->tablet = tablet_input;
|
||||
tablet_input->input = input;
|
||||
tablet_input->seat = zwp_tablet_manager_v2_get_tablet_seat(tablet_manager, input->seat);
|
||||
|
||||
tablet_input->sdlWaylandInput = input;
|
||||
tablet_input->seat = zwp_tablet_manager_v2_get_tablet_seat((struct zwp_tablet_manager_v2 *)tablet_manager, input->seat);
|
||||
|
||||
tablet_input->tablets = tablet_object_list_new_node(NULL);
|
||||
tablet_input->tools = tablet_object_list_new_node(NULL);
|
||||
tablet_input->pads = tablet_object_list_new_node(NULL);
|
||||
tablet_input->id = num_tablets++;
|
||||
|
||||
zwp_tablet_seat_v2_add_listener((struct zwp_tablet_seat_v2 *)tablet_input->seat, &tablet_seat_listener, tablet_input);
|
||||
zwp_tablet_seat_v2_add_listener(tablet_input->seat, &tablet_seat_listener, tablet_input);
|
||||
}
|
||||
|
||||
#define TABLET_OBJECT_LIST_DELETER(fun) (void (*)(void *)) fun
|
||||
void Wayland_input_destroy_tablet(struct SDL_WaylandInput *input)
|
||||
static void Wayland_remove_all_pens_callback(SDL_PenID instance_id, void *handle, void *userdata)
|
||||
{
|
||||
tablet_object_list_destroy(input->tablet->pads, TABLET_OBJECT_LIST_DELETER(zwp_tablet_pad_v2_destroy));
|
||||
tablet_object_list_destroy(input->tablet->tools, TABLET_OBJECT_LIST_DELETER(Wayland_tool_destroy));
|
||||
tablet_object_list_destroy(input->tablet->tablets, TABLET_OBJECT_LIST_DELETER(zwp_tablet_v2_destroy));
|
||||
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) handle;
|
||||
zwp_tablet_tool_v2_destroy(sdltool->wltool);
|
||||
SDL_free(sdltool);
|
||||
}
|
||||
|
||||
zwp_tablet_seat_v2_destroy(input->tablet->seat);
|
||||
void Wayland_input_quit_tablet_support(struct SDL_WaylandInput *input)
|
||||
{
|
||||
SDL_RemoveAllPenDevices(Wayland_remove_all_pens_callback, NULL);
|
||||
|
||||
SDL_free(input->tablet);
|
||||
input->tablet = NULL;
|
||||
if (input && input->tablet_input) {
|
||||
zwp_tablet_seat_v2_destroy(input->tablet_input->seat);
|
||||
SDL_free(input->tablet_input);
|
||||
input->tablet_input = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Wayland_input_initialize_seat(SDL_VideoData *d)
|
||||
@ -2957,7 +2728,7 @@ void Wayland_input_initialize_seat(SDL_VideoData *d)
|
||||
wl_seat_set_user_data(input->seat, input);
|
||||
|
||||
if (d->tablet_manager) {
|
||||
Wayland_input_add_tablet(input, d->tablet_manager);
|
||||
Wayland_input_init_tablet_support(d->input, d->tablet_manager);
|
||||
}
|
||||
|
||||
WAYLAND_wl_display_flush(d->display);
|
||||
@ -3049,8 +2820,8 @@ void Wayland_display_destroy_input(SDL_VideoData *d)
|
||||
}
|
||||
}
|
||||
|
||||
if (input->tablet) {
|
||||
Wayland_input_destroy_tablet(input);
|
||||
if (input->tablet_input) {
|
||||
Wayland_input_quit_tablet_support(input);
|
||||
}
|
||||
|
||||
if (input->seat) {
|
||||
|
@ -41,41 +41,11 @@ enum SDL_WaylandAxisEvent
|
||||
|
||||
struct SDL_WaylandTabletSeat;
|
||||
|
||||
struct SDL_WaylandTabletObjectListNode
|
||||
typedef struct SDL_WaylandTabletInput
|
||||
{
|
||||
void *object;
|
||||
struct SDL_WaylandTabletObjectListNode *next;
|
||||
};
|
||||
|
||||
struct SDL_WaylandTabletInput
|
||||
{
|
||||
struct SDL_WaylandInput *sdlWaylandInput;
|
||||
struct SDL_WaylandInput *input;
|
||||
struct zwp_tablet_seat_v2 *seat;
|
||||
|
||||
struct SDL_WaylandTabletObjectListNode *tablets;
|
||||
struct SDL_WaylandTabletObjectListNode *tools;
|
||||
struct SDL_WaylandTabletObjectListNode *pads;
|
||||
|
||||
Uint32 id;
|
||||
Uint32 num_pens; /* next pen ID is num_pens+1 */
|
||||
struct SDL_WaylandCurrentPen
|
||||
{
|
||||
SDL_Pen *builder; /* pen that is being defined or receiving updates, if any */
|
||||
SDL_bool builder_guid_complete; /* have complete/precise GUID information */
|
||||
SDL_PenStatusInfo update_status; /* collects pen update information before sending event */
|
||||
Uint16 buttons_pressed; /* Mask of newly pressed buttons, plus SDL_PEN_DOWN_MASK for PEN_DOWN */
|
||||
Uint16 buttons_released; /* Mask of newly pressed buttons, plus SDL_PEN_DOWN_MASK for PEN_UP */
|
||||
Uint32 serial; /* Most recent serial event number observed, or 0 */
|
||||
SDL_WindowData *update_window; /* NULL while no event is in progress, otherwise the affected window */
|
||||
} current_pen;
|
||||
|
||||
SDL_WindowData *tool_focus;
|
||||
uint32_t tool_prox_serial;
|
||||
|
||||
/* Last motion end location (kept separate from sx_w, sy_w for the mouse pointer) */
|
||||
wl_fixed_t sx_w;
|
||||
wl_fixed_t sy_w;
|
||||
};
|
||||
} SDL_WaylandTabletInput;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -169,7 +139,7 @@ struct SDL_WaylandInput
|
||||
|
||||
SDL_WaylandKeyboardRepeat keyboard_repeat;
|
||||
|
||||
struct SDL_WaylandTabletInput *tablet;
|
||||
SDL_WaylandTabletInput *tablet_input;
|
||||
|
||||
SDL_bool keyboard_is_virtual;
|
||||
|
||||
@ -178,11 +148,6 @@ struct SDL_WaylandInput
|
||||
SDL_Keymod locked_modifiers;
|
||||
};
|
||||
|
||||
struct SDL_WaylandTool
|
||||
{
|
||||
SDL_PenID penid;
|
||||
struct SDL_WaylandTabletInput *tablet;
|
||||
};
|
||||
|
||||
extern Uint64 Wayland_GetTouchTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms);
|
||||
|
||||
@ -209,8 +174,8 @@ extern int Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input, SDL_W
|
||||
extern int Wayland_input_grab_keyboard(SDL_Window *window, struct SDL_WaylandInput *input);
|
||||
extern int Wayland_input_ungrab_keyboard(SDL_Window *window);
|
||||
|
||||
extern void Wayland_input_add_tablet(struct SDL_WaylandInput *input, struct SDL_WaylandTabletManager *tablet_manager);
|
||||
extern void Wayland_input_destroy_tablet(struct SDL_WaylandInput *input);
|
||||
extern void Wayland_input_init_tablet_support(struct SDL_WaylandInput *input, struct zwp_tablet_manager_v2 *tablet_manager);
|
||||
extern void Wayland_input_quit_tablet_support(struct SDL_WaylandInput *input);
|
||||
|
||||
extern void Wayland_RegisterTimestampListeners(struct SDL_WaylandInput *input);
|
||||
extern void Wayland_CreateCursorShapeDevice(struct SDL_WaylandInput *input);
|
||||
|
@ -1167,7 +1167,7 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
|
||||
d->decoration_manager = wl_registry_bind(d->registry, id, &zxdg_decoration_manager_v1_interface, 1);
|
||||
} else if (SDL_strcmp(interface, "zwp_tablet_manager_v2") == 0) {
|
||||
d->tablet_manager = wl_registry_bind(d->registry, id, &zwp_tablet_manager_v2_interface, 1);
|
||||
Wayland_input_add_tablet(d->input, d->tablet_manager);
|
||||
Wayland_input_init_tablet_support(d->input, d->tablet_manager);
|
||||
} else if (SDL_strcmp(interface, "zxdg_output_manager_v1") == 0) {
|
||||
version = SDL_min(version, 3); /* Versions 1 through 3 are supported. */
|
||||
d->xdg_output_manager = wl_registry_bind(d->registry, id, &zxdg_output_manager_v1_interface, version);
|
||||
|
@ -33,7 +33,6 @@
|
||||
|
||||
struct xkb_context;
|
||||
struct SDL_WaylandInput;
|
||||
struct SDL_WaylandTabletManager;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -84,10 +83,10 @@ struct SDL_VideoData
|
||||
struct wp_alpha_modifier_v1 *wp_alpha_modifier_v1;
|
||||
struct kde_output_order_v1 *kde_output_order;
|
||||
struct frog_color_management_factory_v1 *frog_color_management_factory_v1;
|
||||
struct zwp_tablet_manager_v2 *tablet_manager;
|
||||
|
||||
struct xkb_context *xkb_context;
|
||||
struct SDL_WaylandInput *input;
|
||||
struct SDL_WaylandTabletManager *tablet_manager;
|
||||
struct wl_list output_list;
|
||||
struct wl_list output_order;
|
||||
|
||||
|
@ -1996,9 +1996,6 @@ void X11_PumpEvents(SDL_VideoDevice *_this)
|
||||
}
|
||||
|
||||
if (data->xinput_hierarchy_changed) {
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
|
||||
X11_InitPen(_this);
|
||||
#endif
|
||||
X11_Xinput2UpdateDevices(_this, SDL_FALSE);
|
||||
data->xinput_hierarchy_changed = SDL_FALSE;
|
||||
}
|
||||
|
@ -28,101 +28,79 @@
|
||||
#include "SDL_x11video.h"
|
||||
#include "SDL_x11xinput2.h"
|
||||
|
||||
#define PEN_ERASER_ID_MAXLEN 256 /* Max # characters of device name to scan */
|
||||
/* Does this device have a valuator for pressure sensitivity? */
|
||||
static SDL_bool X11_XInput2DeviceIsPen(SDL_VideoDevice *_this, const XIDeviceInfo *dev)
|
||||
{
|
||||
const SDL_VideoData *data = (SDL_VideoData *)_this->internal;
|
||||
for (int i = 0; i < dev->num_classes; i++) {
|
||||
const XIAnyClassInfo *classinfo = dev->classes[i];
|
||||
if (classinfo->type == XIValuatorClass) {
|
||||
const XIValuatorClassInfo *val_classinfo = (const XIValuatorClassInfo *)classinfo;
|
||||
if (val_classinfo->label == data->pen_atom_abs_pressure) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Heuristically determines if device is an eraser */
|
||||
static SDL_bool X11_XInput2PenIsEraser(SDL_VideoDevice *_this, int deviceid, char *devicename)
|
||||
{
|
||||
#define PEN_ERASER_NAME_TAG "eraser" /* String constant to identify erasers */
|
||||
|
||||
#define DEBUG_PEN (SDL_PEN_DEBUG_NOID | SDL_PEN_DEBUG_NONWACOM | SDL_PEN_DEBUG_UNKNOWN_WACOM | SDL_PEN_DEBUG_NOSERIAL_WACOM)
|
||||
|
||||
#define SDL_PEN_AXIS_VALUATOR_MISSING -1
|
||||
|
||||
/* X11-specific information attached to each pen */
|
||||
typedef struct xinput2_pen
|
||||
{
|
||||
float axis_min[SDL_PEN_NUM_AXES];
|
||||
float axis_max[SDL_PEN_NUM_AXES];
|
||||
float slider_bias; /* shift value to add to PEN_AXIS_SLIDER (before normalisation) */
|
||||
float rotation_bias; /* rotation to add to PEN_AXIS_ROTATION (after normalisation) */
|
||||
Sint8 valuator_for_axis[SDL_PEN_NUM_AXES]; /* SDL_PEN_AXIS_VALUATOR_MISSING if not supported */
|
||||
} xinput2_pen;
|
||||
|
||||
/* X11 atoms */
|
||||
static struct
|
||||
{
|
||||
int initialized; /* initialised to 0 */
|
||||
Atom device_product_id;
|
||||
Atom abs_pressure;
|
||||
Atom abs_tilt_x;
|
||||
Atom abs_tilt_y;
|
||||
Atom wacom_serial_ids;
|
||||
Atom wacom_tool_type;
|
||||
} pen_atoms;
|
||||
|
||||
/*
|
||||
* Mapping from X11 device IDs to pen IDs
|
||||
*
|
||||
* In X11, the same device ID may represent any number of pens. We
|
||||
* thus cannot directly use device IDs as pen IDs.
|
||||
*/
|
||||
static struct
|
||||
{
|
||||
int num_pens_known; /* Number of currently known pens (based on their GUID); used to give pen ID to new pens */
|
||||
int num_entries; /* Number of X11 device IDs that correspond to pens */
|
||||
|
||||
struct pen_device_id_mapping
|
||||
{
|
||||
Uint32 deviceid;
|
||||
Uint32 pen_id;
|
||||
} * entries; /* Current pen to device ID mappings */
|
||||
} pen_map;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SDL_PEN_VENDOR_UNKNOWN = 0,
|
||||
SDL_PEN_VENDOR_WACOM
|
||||
} sdl_pen_vendor;
|
||||
|
||||
/* Information to identify pens during discovery */
|
||||
typedef struct
|
||||
{
|
||||
sdl_pen_vendor vendor;
|
||||
SDL_GUID guid;
|
||||
SDL_PenSubtype heuristic_type; /* Distinguish pen+eraser devices with shared bus ID */
|
||||
Uint32 devicetype_id, serial; /* used by PEN_VENDOR_WACOM */
|
||||
Uint32 deviceid;
|
||||
} pen_identity;
|
||||
|
||||
int X11_PenIDFromDeviceID(int deviceid)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < pen_map.num_entries; ++i) {
|
||||
if (pen_map.entries[i].deviceid == deviceid) {
|
||||
return pen_map.entries[i].pen_id;
|
||||
}
|
||||
}
|
||||
return SDL_PEN_INVALID;
|
||||
}
|
||||
|
||||
static void pen_atoms_ensure_initialized(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_VideoData *data = (SDL_VideoData *)_this->internal;
|
||||
|
||||
if (pen_atoms.initialized) {
|
||||
return;
|
||||
}
|
||||
/* Create atoms if they don't exist yet to pre-empt hotplugging updates */
|
||||
pen_atoms.device_product_id = X11_XInternAtom(data->display, "Device Product ID", False);
|
||||
pen_atoms.wacom_serial_ids = X11_XInternAtom(data->display, "Wacom Serial IDs", False);
|
||||
pen_atoms.wacom_tool_type = X11_XInternAtom(data->display, "Wacom Tool Type", False);
|
||||
pen_atoms.abs_pressure = X11_XInternAtom(data->display, "Abs Pressure", False);
|
||||
pen_atoms.abs_tilt_x = X11_XInternAtom(data->display, "Abs Tilt X", False);
|
||||
pen_atoms.abs_tilt_y = X11_XInternAtom(data->display, "Abs Tilt Y", False);
|
||||
if (data->pen_atom_wacom_tool_type != None) {
|
||||
Atom type_return;
|
||||
int format_return;
|
||||
unsigned long num_items_return;
|
||||
unsigned long bytes_after_return;
|
||||
unsigned char *tooltype_name_info = NULL;
|
||||
|
||||
pen_atoms.initialized = 1;
|
||||
/* Try Wacom-specific method */
|
||||
if (Success == X11_XIGetProperty(data->display, deviceid,
|
||||
data->pen_atom_wacom_tool_type,
|
||||
0, 32, False,
|
||||
AnyPropertyType, &type_return, &format_return,
|
||||
&num_items_return, &bytes_after_return,
|
||||
&tooltype_name_info) &&
|
||||
tooltype_name_info != NULL && num_items_return > 0) {
|
||||
|
||||
SDL_bool result = SDL_FALSE;
|
||||
char *tooltype_name = NULL;
|
||||
|
||||
if (type_return == XA_ATOM) {
|
||||
/* Atom instead of string? Un-intern */
|
||||
Atom atom = *((Atom *)tooltype_name_info);
|
||||
if (atom != None) {
|
||||
tooltype_name = X11_XGetAtomName(data->display, atom);
|
||||
}
|
||||
} else if (type_return == XA_STRING && format_return == 8) {
|
||||
tooltype_name = (char *)tooltype_name_info;
|
||||
}
|
||||
|
||||
/* Read out an integer property and store into a preallocated Sint32 array, extending 8 and 16 bit values suitably.
|
||||
Returns number of Sint32s written (<= max_words), or 0 on error. */
|
||||
static size_t xinput2_pen_get_int_property(SDL_VideoDevice *_this, int deviceid, Atom property, Sint32 *dest, size_t max_words)
|
||||
if (tooltype_name) {
|
||||
if (0 == SDL_strcasecmp(tooltype_name, PEN_ERASER_NAME_TAG)) {
|
||||
result = SDL_TRUE;
|
||||
}
|
||||
X11_XFree(tooltype_name_info);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Non-Wacom device? */
|
||||
|
||||
/* We assume that a device is an eraser if its name contains the string "eraser".
|
||||
* Unfortunately there doesn't seem to be a clean way to distinguish these cases (as of 2022-03). */
|
||||
return (SDL_strcasestr(devicename, PEN_ERASER_NAME_TAG)) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
// Read out an integer property and store into a preallocated Sint32 array, extending 8 and 16 bit values suitably.
|
||||
// Returns number of Sint32s written (<= max_words), or 0 on error.
|
||||
static size_t X11_XInput2PenGetIntProperty(SDL_VideoDevice *_this, int deviceid, Atom property, Sint32 *dest, size_t max_words)
|
||||
{
|
||||
const SDL_VideoData *data = (SDL_VideoData *)_this->internal;
|
||||
Atom type_return;
|
||||
@ -165,478 +143,229 @@ static size_t xinput2_pen_get_int_property(SDL_VideoDevice *_this, int deviceid,
|
||||
X11_XFree(output);
|
||||
return to_copy;
|
||||
}
|
||||
return 0; /* type mismatch */
|
||||
|
||||
return 0; // type mismatch
|
||||
}
|
||||
|
||||
/* 32 bit vendor + device ID from evdev */
|
||||
static Uint32 xinput2_pen_evdevid(SDL_VideoDevice *_this, int deviceid)
|
||||
// Identify Wacom devices (if SDL_TRUE is returned) and extract their device type and serial IDs
|
||||
static SDL_bool X11_XInput2PenWacomDeviceID(SDL_VideoDevice *_this, int deviceid, Uint32 *wacom_devicetype_id, Uint32 *wacom_serial)
|
||||
{
|
||||
#if !(SDL_PEN_DEBUG_NOID)
|
||||
Sint32 ids[2];
|
||||
|
||||
pen_atoms_ensure_initialized(_this);
|
||||
|
||||
if (2 != xinput2_pen_get_int_property(_this, deviceid, pen_atoms.device_product_id, ids, 2)) {
|
||||
return 0;
|
||||
}
|
||||
return ((ids[0] << 16) | (ids[1] & 0xffff));
|
||||
#else /* Testing: pretend that we have no ID (not sure if this can happen IRL) */
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Gets reasonably-unique GUID for the device */
|
||||
static void xinput2_pen_update_generic_guid(SDL_VideoDevice *_this, pen_identity *pident, int deviceid)
|
||||
{
|
||||
Uint32 evdevid = xinput2_pen_evdevid(_this, deviceid); /* also initialises pen_atoms */
|
||||
|
||||
if (!evdevid) {
|
||||
/* Fallback: if no evdevid is available; try to at least distinguish devices within the
|
||||
current session. This is a poor GUID and our last resort. */
|
||||
evdevid = deviceid;
|
||||
}
|
||||
SDL_PenUpdateGUIDForGeneric(&pident->guid, 0, evdevid);
|
||||
}
|
||||
|
||||
/* Identify Wacom devices (if SDL_TRUE is returned) and extract their device type and serial IDs */
|
||||
static SDL_bool xinput2_wacom_deviceid(SDL_VideoDevice *_this, int deviceid, Uint32 *wacom_devicetype_id, Uint32 *wacom_serial)
|
||||
{
|
||||
#if !(SDL_PEN_DEBUG_NONWACOM) /* Can be disabled for testing */
|
||||
SDL_VideoData *data = (SDL_VideoData *)_this->internal;
|
||||
Sint32 serial_id_buf[3];
|
||||
int result;
|
||||
|
||||
pen_atoms_ensure_initialized(_this);
|
||||
|
||||
if ((result = xinput2_pen_get_int_property(_this, deviceid, pen_atoms.wacom_serial_ids, serial_id_buf, 3)) == 3) {
|
||||
if ((result = X11_XInput2PenGetIntProperty(_this, deviceid, data->pen_atom_wacom_serial_ids, serial_id_buf, 3)) == 3) {
|
||||
*wacom_devicetype_id = serial_id_buf[2];
|
||||
*wacom_serial = serial_id_buf[1];
|
||||
#if SDL_PEN_DEBUG_NOSERIAL_WACOM /* Disabled for testing? */
|
||||
*wacom_serial = 0;
|
||||
#endif
|
||||
return SDL_TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
*wacom_devicetype_id = *wacom_serial = 0;
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Heuristically determines if device is an eraser */
|
||||
static SDL_bool xinput2_pen_is_eraser(SDL_VideoDevice *_this, int deviceid, char *devicename)
|
||||
|
||||
typedef struct FindPenByDeviceIDData
|
||||
{
|
||||
int x11_deviceid;
|
||||
void *handle;
|
||||
} FindPenByDeviceIDData;
|
||||
|
||||
static SDL_bool FindPenByDeviceID(void *handle, void *userdata)
|
||||
{
|
||||
const X11_PenHandle *x11_handle = (const X11_PenHandle *) handle;
|
||||
FindPenByDeviceIDData *data = (FindPenByDeviceIDData *) userdata;
|
||||
if (x11_handle->x11_deviceid != data->x11_deviceid) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
data->handle = handle;
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
X11_PenHandle *X11_FindPenByDeviceID(int deviceid)
|
||||
{
|
||||
FindPenByDeviceIDData data;
|
||||
data.x11_deviceid = deviceid;
|
||||
data.handle = NULL;
|
||||
SDL_FindPenByCallback(FindPenByDeviceID, &data);
|
||||
return (X11_PenHandle *) data.handle;
|
||||
}
|
||||
|
||||
static X11_PenHandle *X11_MaybeAddPen(SDL_VideoDevice *_this, const XIDeviceInfo *dev)
|
||||
{
|
||||
SDL_VideoData *data = (SDL_VideoData *)_this->internal;
|
||||
char dev_name[PEN_ERASER_ID_MAXLEN];
|
||||
int k;
|
||||
SDL_PenCapabilityFlags capabilities = 0;
|
||||
X11_PenHandle *handle = NULL;
|
||||
|
||||
pen_atoms_ensure_initialized(_this);
|
||||
|
||||
if (pen_atoms.wacom_tool_type != None) {
|
||||
Atom type_return;
|
||||
int format_return;
|
||||
unsigned long num_items_return;
|
||||
unsigned long bytes_after_return;
|
||||
unsigned char *tooltype_name_info = NULL;
|
||||
|
||||
/* Try Wacom-specific method */
|
||||
if (Success == X11_XIGetProperty(data->display, deviceid,
|
||||
pen_atoms.wacom_tool_type,
|
||||
0, 32, False,
|
||||
AnyPropertyType, &type_return, &format_return,
|
||||
&num_items_return, &bytes_after_return,
|
||||
&tooltype_name_info) &&
|
||||
tooltype_name_info != NULL && num_items_return > 0) {
|
||||
|
||||
SDL_bool result = SDL_FALSE;
|
||||
char *tooltype_name = NULL;
|
||||
|
||||
if (type_return == XA_ATOM) {
|
||||
/* Atom instead of string? Un-intern */
|
||||
Atom atom = *((Atom *)tooltype_name_info);
|
||||
if (atom != None) {
|
||||
tooltype_name = X11_XGetAtomName(data->display, atom);
|
||||
}
|
||||
} else if (type_return == XA_STRING && format_return == 8) {
|
||||
tooltype_name = (char *)tooltype_name_info;
|
||||
if ((dev->use != XISlavePointer && (dev->use != XIFloatingSlave)) || dev->enabled == 0 || !X11_XInput2DeviceIsPen(_this, dev)) {
|
||||
return NULL; // Only track physical devices that are enabled and look like pens
|
||||
} else if ((handle = X11_FindPenByDeviceID(dev->deviceid)) != 0) {
|
||||
return handle; // already have this pen, skip it.
|
||||
} else if ((handle = SDL_calloc(1, sizeof (*handle))) == NULL) {
|
||||
return NULL; // oh well.
|
||||
}
|
||||
|
||||
if (tooltype_name) {
|
||||
if (0 == SDL_strcasecmp(tooltype_name, PEN_ERASER_NAME_TAG)) {
|
||||
result = SDL_TRUE;
|
||||
}
|
||||
X11_XFree(tooltype_name_info);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Non-Wacom device? */
|
||||
|
||||
/* We assume that a device is an eraser if its name contains the string "eraser".
|
||||
* Unfortunately there doesn't seem to be a clean way to distinguish these cases (as of 2022-03). */
|
||||
|
||||
SDL_strlcpy(dev_name, devicename, PEN_ERASER_ID_MAXLEN);
|
||||
/* lowercase device name string so we can use strstr() */
|
||||
for (k = 0; dev_name[k]; ++k) {
|
||||
dev_name[k] = SDL_tolower(dev_name[k]);
|
||||
for (int i = 0; i < SDL_arraysize(handle->valuator_for_axis); i++) {
|
||||
handle->valuator_for_axis[i] = SDL_X11_PEN_AXIS_VALUATOR_MISSING; // until proven otherwise
|
||||
}
|
||||
|
||||
return (SDL_strstr(dev_name, PEN_ERASER_NAME_TAG)) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Gets GUID and other identifying information for the device using the best known method */
|
||||
static pen_identity xinput2_identify_pen(SDL_VideoDevice *_this, int deviceid, char *name)
|
||||
{
|
||||
pen_identity pident;
|
||||
|
||||
pident.devicetype_id = 0ul;
|
||||
pident.serial = 0ul;
|
||||
pident.deviceid = deviceid;
|
||||
pident.heuristic_type = SDL_PEN_TYPE_PEN;
|
||||
SDL_memset(pident.guid.data, 0, sizeof(pident.guid.data));
|
||||
|
||||
if (xinput2_pen_is_eraser(_this, deviceid, name)) {
|
||||
pident.heuristic_type = SDL_PEN_TYPE_ERASER;
|
||||
}
|
||||
|
||||
if (xinput2_wacom_deviceid(_this, deviceid, &pident.devicetype_id, &pident.serial)) {
|
||||
pident.vendor = SDL_PEN_VENDOR_WACOM;
|
||||
SDL_PenUpdateGUIDForWacom(&pident.guid, pident.devicetype_id, pident.serial);
|
||||
|
||||
#if DEBUG_PEN
|
||||
printf("[pen] Pen %d reports Wacom device_id %x\n",
|
||||
deviceid, pident.devicetype_id);
|
||||
#endif
|
||||
int total_buttons = 0;
|
||||
for (int i = 0; i < dev->num_classes; i++) {
|
||||
const XIAnyClassInfo *classinfo = dev->classes[i];
|
||||
if (classinfo->type == XIButtonClass) {
|
||||
const XIButtonClassInfo *button_classinfo = (const XIButtonClassInfo *)classinfo;
|
||||
total_buttons += button_classinfo->num_buttons;
|
||||
} else if (classinfo->type == XIValuatorClass) {
|
||||
const XIValuatorClassInfo *val_classinfo = (const XIValuatorClassInfo *)classinfo;
|
||||
const Sint8 valuator_nr = val_classinfo->number;
|
||||
const Atom vname = val_classinfo->label;
|
||||
const float min = (float)val_classinfo->min;
|
||||
const float max = (float)val_classinfo->max;
|
||||
SDL_bool use_this_axis = SDL_TRUE;
|
||||
SDL_PenAxis axis = SDL_PEN_NUM_AXES;
|
||||
|
||||
// afaict, SDL_PEN_AXIS_DISTANCE is never reported by XInput2 (Wayland can offer it, though)
|
||||
if (vname == data->pen_atom_abs_pressure) {
|
||||
axis = SDL_PEN_AXIS_PRESSURE;
|
||||
} else if (vname == data->pen_atom_abs_tilt_x) {
|
||||
axis = SDL_PEN_AXIS_XTILT;
|
||||
} else if (vname == data->pen_atom_abs_tilt_y) {
|
||||
axis = SDL_PEN_AXIS_YTILT;
|
||||
} else {
|
||||
pident.vendor = SDL_PEN_VENDOR_UNKNOWN;
|
||||
}
|
||||
if (!pident.serial) {
|
||||
/* If the pen has a serial number, we can move it across tablets and retain its identity.
|
||||
Otherwise, we use the evdev ID as part of its GUID, which may mean that we identify it with the tablet. */
|
||||
xinput2_pen_update_generic_guid(_this, &pident, deviceid);
|
||||
}
|
||||
SDL_PenUpdateGUIDForType(&pident.guid, pident.heuristic_type);
|
||||
return pident;
|
||||
use_this_axis = SDL_FALSE;
|
||||
}
|
||||
|
||||
static void xinput2_pen_free_deviceinfo(Uint32 deviceid, void *x11_peninfo, void *context)
|
||||
// !!! FIXME: there are wacom-specific hacks for getting SDL_PEN_AXIS_(ROTATION|SLIDER) on some devices, but for simplicity, we're skipping all that for now.
|
||||
|
||||
if (use_this_axis) {
|
||||
capabilities |= SDL_GetPenCapabilityFromAxis(axis);
|
||||
handle->valuator_for_axis[axis] = valuator_nr;
|
||||
handle->axis_min[axis] = min;
|
||||
handle->axis_max[axis] = max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We have a pen if and only if the device measures pressure.
|
||||
// We checked this in X11_XInput2DeviceIsPen, so just assert it here.
|
||||
SDL_assert(capabilities & SDL_PEN_CAPABILITY_PRESSURE);
|
||||
|
||||
const SDL_bool is_eraser = X11_XInput2PenIsEraser(_this, dev->deviceid, dev->name);
|
||||
Uint32 wacom_devicetype_id = 0;
|
||||
Uint32 wacom_serial = 0;
|
||||
X11_XInput2PenWacomDeviceID(_this, dev->deviceid, &wacom_devicetype_id, &wacom_serial);
|
||||
|
||||
SDL_PenInfo peninfo;
|
||||
SDL_zero(peninfo);
|
||||
peninfo.capabilities = capabilities;
|
||||
peninfo.max_tilt = -1;
|
||||
peninfo.wacom_id = wacom_devicetype_id;
|
||||
peninfo.num_buttons = total_buttons;
|
||||
peninfo.subtype = is_eraser ? SDL_PEN_TYPE_ERASER : SDL_PEN_TYPE_PEN;
|
||||
if (is_eraser) {
|
||||
peninfo.capabilities |= SDL_PEN_CAPABILITY_ERASER;
|
||||
}
|
||||
|
||||
handle->is_eraser = is_eraser;
|
||||
handle->x11_deviceid = dev->deviceid;
|
||||
|
||||
handle->pen = SDL_AddPenDevice(0, dev->name, &peninfo, handle);
|
||||
if (!handle->pen) {
|
||||
SDL_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
X11_PenHandle *X11_MaybeAddPenByDeviceID(SDL_VideoDevice *_this, int deviceid)
|
||||
{
|
||||
SDL_free(x11_peninfo);
|
||||
SDL_VideoData *data = (SDL_VideoData *)_this->internal;
|
||||
int num_device_info = 0;
|
||||
XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, deviceid, &num_device_info);
|
||||
if (device_info) {
|
||||
SDL_assert(num_device_info == 1);
|
||||
return X11_MaybeAddPen(_this, device_info);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void xinput2_merge_deviceinfo(xinput2_pen *dest, xinput2_pen *src)
|
||||
void X11_RemovePenByDeviceID(int deviceid)
|
||||
{
|
||||
*dest = *src;
|
||||
X11_PenHandle *handle = X11_FindPenByDeviceID(deviceid);
|
||||
if (handle) {
|
||||
SDL_RemovePenDevice(0, handle->pen);
|
||||
SDL_free(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in vendor-specific device information, if available
|
||||
*
|
||||
* For Wacom pens: identify number of buttons and extra axis (if present)
|
||||
*
|
||||
* \param _this global state
|
||||
* \param dev The device to analyse
|
||||
* \param pen The pen to initialise
|
||||
* \param pident Pen identity information
|
||||
* \param[out] valuator_5 Meaning of the valuator with offset 5, if any
|
||||
* (written only if known and if the device has a 6th axis,
|
||||
* e.g., for the Wacom Art Pen and Wacom Airbrush Pen)
|
||||
* \param[out] axes Bitmask of all possibly supported axes
|
||||
*
|
||||
* This function identifies Wacom device types through a Wacom-specific device ID.
|
||||
* It then fills in pen details from an internal database.
|
||||
* If the device seems to be a Wacom pen/eraser but can't be identified, the function
|
||||
* leaves "axes" untouched and sets the other outputs to common defaults.
|
||||
*
|
||||
* There is no explicit support for other vendors, though vendors that
|
||||
* emulate the Wacom API might be supported.
|
||||
*
|
||||
* Unsupported devices will keep the default settings.
|
||||
*/
|
||||
static void xinput2_vendor_peninfo(SDL_VideoDevice *_this, const XIDeviceInfo *dev, SDL_Pen *pen, pen_identity pident, int *valuator_5, Uint32 *axes)
|
||||
{
|
||||
switch (pident.vendor) {
|
||||
case SDL_PEN_VENDOR_WACOM:
|
||||
{
|
||||
if (SDL_PenModifyForWacomID(pen, pident.devicetype_id, axes)) {
|
||||
if (*axes & SDL_PEN_AXIS_SLIDER_MASK) {
|
||||
/* Air Brush Pen or eraser */
|
||||
*valuator_5 = SDL_PEN_AXIS_SLIDER;
|
||||
} else if (*axes & SDL_PEN_AXIS_ROTATION_MASK) {
|
||||
/* Art Pen or eraser, or 6D Art Pen */
|
||||
*valuator_5 = SDL_PEN_AXIS_ROTATION;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
#if DEBUG_PEN
|
||||
printf("[pen] Could not identify wacom pen %d with device id %x, using default settings\n",
|
||||
pident.deviceid, pident.devicetype_id);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
#if DEBUG_PEN
|
||||
printf("[pen] Pen %d is not from a known vendor\n", pident.deviceid);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
/* Fall back to default heuristics for identifying device type */
|
||||
|
||||
SDL_strlcpy(pen->name, dev->name, SDL_PEN_MAX_NAME);
|
||||
|
||||
pen->type = pident.heuristic_type;
|
||||
}
|
||||
|
||||
/* Does this device have a valuator for pressure sensitivity? */
|
||||
static SDL_bool xinput2_device_is_pen(SDL_VideoDevice *_this, const XIDeviceInfo *dev)
|
||||
{
|
||||
int classct;
|
||||
|
||||
pen_atoms_ensure_initialized(_this);
|
||||
|
||||
for (classct = 0; classct < dev->num_classes; ++classct) {
|
||||
const XIAnyClassInfo *classinfo = dev->classes[classct];
|
||||
|
||||
switch (classinfo->type) {
|
||||
case XIValuatorClass:
|
||||
{
|
||||
XIValuatorClassInfo *val_classinfo = (XIValuatorClassInfo *)classinfo;
|
||||
Atom vname = val_classinfo->label;
|
||||
|
||||
if (vname == pen_atoms.abs_pressure) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
void X11_InitPen(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_VideoData *data = (SDL_VideoData *)_this->internal;
|
||||
int i;
|
||||
XIDeviceInfo *device_info;
|
||||
int num_device_info;
|
||||
|
||||
device_info = X11_XIQueryDevice(data->display, XIAllDevices, &num_device_info);
|
||||
if (!device_info) {
|
||||
return;
|
||||
#define LOOKUP_PEN_ATOM(X) X11_XInternAtom(data->display, X, False)
|
||||
data->pen_atom_device_product_id = LOOKUP_PEN_ATOM("Device Product ID");
|
||||
data->pen_atom_wacom_serial_ids = LOOKUP_PEN_ATOM("Wacom Serial IDs");
|
||||
data->pen_atom_wacom_tool_type = LOOKUP_PEN_ATOM("Wacom Tool Type");
|
||||
data->pen_atom_abs_pressure = LOOKUP_PEN_ATOM("Abs Pressure");
|
||||
data->pen_atom_abs_tilt_x = LOOKUP_PEN_ATOM("Abs Tilt X");
|
||||
data->pen_atom_abs_tilt_y = LOOKUP_PEN_ATOM("Abs Tilt Y");
|
||||
#undef LOOKUP_PEN_ATOM
|
||||
|
||||
// Do an initial check on devices. After this, we'll add/remove individual pens when XI_HierarchyChanged events alert us.
|
||||
int num_device_info = 0;
|
||||
XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, XIAllDevices, &num_device_info);
|
||||
if (device_info) {
|
||||
for (int i = 0; i < num_device_info; i++) {
|
||||
X11_MaybeAddPen(_this, &device_info[i]);
|
||||
}
|
||||
X11_XIFreeDeviceInfo(device_info);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the device id -> pen map */
|
||||
if (pen_map.entries) {
|
||||
SDL_free(pen_map.entries);
|
||||
pen_map.entries = NULL;
|
||||
pen_map.num_entries = 0;
|
||||
static void X11_FreePenHandle(SDL_PenID instance_id, void *handle, void *userdata)
|
||||
{
|
||||
SDL_free(handle);
|
||||
}
|
||||
|
||||
SDL_PenGCMark();
|
||||
void X11_QuitPen(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_RemoveAllPenDevices(X11_FreePenHandle, NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_device_info; ++i) {
|
||||
const XIDeviceInfo *dev = &device_info[i];
|
||||
int classct;
|
||||
xinput2_pen pen_device;
|
||||
Uint32 capabilities = 0;
|
||||
Uint32 axis_mask = ~0; /* Permitted axes (default: all) */
|
||||
int valuator_5_axis = -1; /* For Wacom devices, the 6th valuator (offset 5) has a model-specific meaning */
|
||||
pen_identity pident;
|
||||
SDL_PenID pen_id;
|
||||
SDL_Pen *pen;
|
||||
int old_num_pens_known = pen_map.num_pens_known;
|
||||
int k;
|
||||
|
||||
/* Only track physical devices that are enabled and look like pens */
|
||||
if (dev->use != XISlavePointer || dev->enabled == 0 || !xinput2_device_is_pen(_this, dev)) {
|
||||
static void X11_XInput2NormalizePenAxes(const X11_PenHandle *pen, float *coords)
|
||||
{
|
||||
/* Normalise axes */
|
||||
for (int axis = 0; axis < SDL_PEN_NUM_AXES; ++axis) {
|
||||
const int valuator = pen->valuator_for_axis[axis];
|
||||
if (valuator == SDL_X11_PEN_AXIS_VALUATOR_MISSING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pen_device.slider_bias = 0.0f;
|
||||
pen_device.rotation_bias = 0.0f;
|
||||
for (k = 0; k < SDL_PEN_NUM_AXES; ++k) {
|
||||
pen_device.valuator_for_axis[k] = SDL_PEN_AXIS_VALUATOR_MISSING;
|
||||
}
|
||||
|
||||
pident = xinput2_identify_pen(_this, dev->deviceid, dev->name);
|
||||
|
||||
pen_id = SDL_GetPenFromGUID(pident.guid);
|
||||
if (pen_id == SDL_PEN_INVALID) {
|
||||
/* We have never met this pen */
|
||||
pen_id = ++pen_map.num_pens_known; /* start at 1 */
|
||||
}
|
||||
pen = SDL_PenModifyBegin(pen_id);
|
||||
|
||||
/* Complement XF86 driver information with vendor-specific details */
|
||||
xinput2_vendor_peninfo(_this, dev, pen, pident, &valuator_5_axis, &axis_mask);
|
||||
|
||||
for (classct = 0; classct < dev->num_classes; ++classct) {
|
||||
const XIAnyClassInfo *classinfo = dev->classes[classct];
|
||||
|
||||
switch (classinfo->type) {
|
||||
case XIValuatorClass:
|
||||
{
|
||||
XIValuatorClassInfo *val_classinfo = (XIValuatorClassInfo *)classinfo;
|
||||
Sint8 valuator_nr = val_classinfo->number;
|
||||
Atom vname = val_classinfo->label;
|
||||
int axis = -1;
|
||||
|
||||
float min = (float)val_classinfo->min;
|
||||
float max = (float)val_classinfo->max;
|
||||
|
||||
if (vname == pen_atoms.abs_pressure) {
|
||||
axis = SDL_PEN_AXIS_PRESSURE;
|
||||
} else if (vname == pen_atoms.abs_tilt_x) {
|
||||
axis = SDL_PEN_AXIS_XTILT;
|
||||
} else if (vname == pen_atoms.abs_tilt_y) {
|
||||
axis = SDL_PEN_AXIS_YTILT;
|
||||
}
|
||||
|
||||
if (axis == -1 && valuator_nr == 5) {
|
||||
/* Wacom model-specific axis support */
|
||||
/* The meaning of the various axes is highly underspecitied in Xinput2.
|
||||
* As of 2023-08-26, Wacom seems to be the only vendor to support these axes, so the code below
|
||||
* captures the de-facto standard. */
|
||||
axis = valuator_5_axis;
|
||||
|
||||
switch (axis) {
|
||||
case SDL_PEN_AXIS_SLIDER:
|
||||
/* cf. xinput2_wacom_peninfo for how this axis is used.
|
||||
In all current cases, our API wants this value in 0..1, but the xf86 driver
|
||||
starts at a negative offset, so we normalise here. */
|
||||
pen_device.slider_bias = -min;
|
||||
max -= min;
|
||||
min = 0.0f;
|
||||
break;
|
||||
|
||||
case SDL_PEN_AXIS_ROTATION:
|
||||
/* The "0" value points to the left, rather than up, so we must
|
||||
rotate 90 degrees counter-clockwise to have 0 point to the top. */
|
||||
|
||||
pen_device.rotation_bias = -90.0f;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (axis >= 0) {
|
||||
capabilities |= SDL_PEN_AXIS_CAPABILITY(axis);
|
||||
|
||||
pen_device.valuator_for_axis[axis] = valuator_nr;
|
||||
pen_device.axis_min[axis] = min;
|
||||
pen_device.axis_max[axis] = max;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have a pen if and only if the device measures pressure */
|
||||
if (capabilities & SDL_PEN_AXIS_PRESSURE_MASK) {
|
||||
xinput2_pen *xinput2_deviceinfo;
|
||||
Uint64 guid_a, guid_b;
|
||||
|
||||
/* Done collecting data, write to pen */
|
||||
SDL_PenModifyAddCapabilities(pen, capabilities);
|
||||
pen->guid = pident.guid;
|
||||
|
||||
if (pen->deviceinfo) {
|
||||
/* Updating a known pen */
|
||||
xinput2_deviceinfo = (xinput2_pen *)pen->deviceinfo;
|
||||
xinput2_merge_deviceinfo(xinput2_deviceinfo, &pen_device);
|
||||
} else {
|
||||
/* Registering a new pen */
|
||||
xinput2_deviceinfo = SDL_malloc(sizeof(xinput2_pen));
|
||||
SDL_memcpy(xinput2_deviceinfo, &pen_device, sizeof(xinput2_pen));
|
||||
}
|
||||
pen->deviceinfo = xinput2_deviceinfo;
|
||||
|
||||
#if DEBUG_PEN
|
||||
printf("[pen] pen %d [%04x] valuators pressure=%d, xtilt=%d, ytilt=%d [%s]\n",
|
||||
pen->header.id, pen->header.flags,
|
||||
pen_device.valuator_for_axis[SDL_PEN_AXIS_PRESSURE],
|
||||
pen_device.valuator_for_axis[SDL_PEN_AXIS_XTILT],
|
||||
pen_device.valuator_for_axis[SDL_PEN_AXIS_YTILT],
|
||||
pen->name);
|
||||
#endif
|
||||
SDL_memcpy(&guid_a, &pen->guid.data[0], 8);
|
||||
SDL_memcpy(&guid_b, &pen->guid.data[8], 8);
|
||||
if (!(guid_a | guid_b)) {
|
||||
#if DEBUG_PEN
|
||||
printf("[pen] (pen eliminated due to zero GUID)\n");
|
||||
#endif
|
||||
pen->type = SDL_PEN_TYPE_NONE;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Not a pen, mark for deletion */
|
||||
pen->type = SDL_PEN_TYPE_NONE;
|
||||
}
|
||||
SDL_PenModifyEnd(pen, SDL_TRUE);
|
||||
|
||||
if (pen->type != SDL_PEN_TYPE_NONE) {
|
||||
const int map_pos = pen_map.num_entries;
|
||||
|
||||
/* We found a pen: add mapping */
|
||||
if (pen_map.entries == NULL) {
|
||||
pen_map.entries = SDL_calloc(sizeof(struct pen_device_id_mapping), 1);
|
||||
pen_map.num_entries = 1;
|
||||
} else {
|
||||
pen_map.num_entries += 1;
|
||||
pen_map.entries = SDL_realloc(pen_map.entries,
|
||||
pen_map.num_entries * (sizeof(struct pen_device_id_mapping)));
|
||||
}
|
||||
pen_map.entries[map_pos].deviceid = dev->deviceid;
|
||||
pen_map.entries[map_pos].pen_id = pen_id;
|
||||
} else {
|
||||
/* Revert pen number allocation */
|
||||
pen_map.num_pens_known = old_num_pens_known;
|
||||
}
|
||||
}
|
||||
X11_XIFreeDeviceInfo(device_info);
|
||||
|
||||
SDL_PenGCSweep(NULL, xinput2_pen_free_deviceinfo);
|
||||
}
|
||||
|
||||
static void xinput2_normalize_pen_axes(const SDL_Pen *peninfo,
|
||||
const xinput2_pen *xpen,
|
||||
/* inout-mode paramters: */
|
||||
float *coords)
|
||||
{
|
||||
int axis;
|
||||
|
||||
/* Normalise axes */
|
||||
for (axis = 0; axis < SDL_PEN_NUM_AXES; ++axis) {
|
||||
int valuator = xpen->valuator_for_axis[axis];
|
||||
if (valuator != SDL_PEN_AXIS_VALUATOR_MISSING) {
|
||||
float value = coords[axis];
|
||||
float min = xpen->axis_min[axis];
|
||||
float max = xpen->axis_max[axis];
|
||||
const float min = pen->axis_min[axis];
|
||||
const float max = pen->axis_max[axis];
|
||||
|
||||
if (axis == SDL_PEN_AXIS_SLIDER) {
|
||||
value += xpen->slider_bias;
|
||||
value += pen->slider_bias;
|
||||
}
|
||||
|
||||
/* min ... 0 ... max */
|
||||
// min ... 0 ... max
|
||||
if (min < 0.0) {
|
||||
/* Normalise so that 0 remains 0.0 */
|
||||
// Normalise so that 0 remains 0.0
|
||||
if (value < 0) {
|
||||
value = value / (-min);
|
||||
} else {
|
||||
if (max == 0.0) {
|
||||
if (max == 0.0f) {
|
||||
value = 0.0f;
|
||||
} else {
|
||||
value = value / max;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* 0 ... min ... max */
|
||||
/* including 0.0 = min */
|
||||
if (max == 0.0) {
|
||||
// 0 ... min ... max
|
||||
// including 0.0 = min
|
||||
if (max == 0.0f) {
|
||||
value = 0.0f;
|
||||
} else {
|
||||
value = (value - min) / max;
|
||||
@ -646,17 +375,17 @@ static void xinput2_normalize_pen_axes(const SDL_Pen *peninfo,
|
||||
switch (axis) {
|
||||
case SDL_PEN_AXIS_XTILT:
|
||||
case SDL_PEN_AXIS_YTILT:
|
||||
if (peninfo->info.max_tilt > 0.0f) {
|
||||
value *= peninfo->info.max_tilt; /* normalise to physical max */
|
||||
}
|
||||
//if (peninfo->info.max_tilt > 0.0f) {
|
||||
// value *= peninfo->info.max_tilt; // normalize to physical max
|
||||
//}
|
||||
break;
|
||||
|
||||
case SDL_PEN_AXIS_ROTATION:
|
||||
/* normalised to -1..1, so let's convert to degrees */
|
||||
// normalised to -1..1, so let's convert to degrees
|
||||
value *= 180.0f;
|
||||
value += xpen->rotation_bias;
|
||||
value += pen->rotation_bias;
|
||||
|
||||
/* handle simple over/underflow */
|
||||
// handle simple over/underflow
|
||||
if (value >= 180.0f) {
|
||||
value -= 360.0f;
|
||||
} else if (value < -180.0f) {
|
||||
@ -667,30 +396,25 @@ static void xinput2_normalize_pen_axes(const SDL_Pen *peninfo,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
coords[axis] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void X11_PenAxesFromValuators(const SDL_Pen *peninfo,
|
||||
void X11_PenAxesFromValuators(const X11_PenHandle *pen,
|
||||
const double *input_values, const unsigned char *mask, const int mask_len,
|
||||
/* out-mode parameters: */
|
||||
float axis_values[SDL_PEN_NUM_AXES])
|
||||
{
|
||||
const xinput2_pen *pen = (xinput2_pen *)peninfo->deviceinfo;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SDL_PEN_NUM_AXES; ++i) {
|
||||
for (int i = 0; i < SDL_PEN_NUM_AXES; i++) {
|
||||
const int valuator = pen->valuator_for_axis[i];
|
||||
if (valuator == SDL_PEN_AXIS_VALUATOR_MISSING || valuator >= mask_len * 8 || !(XIMaskIsSet(mask, valuator))) {
|
||||
if ((valuator == SDL_X11_PEN_AXIS_VALUATOR_MISSING) || (valuator >= mask_len * 8) || !(XIMaskIsSet(mask, valuator))) {
|
||||
axis_values[i] = 0.0f;
|
||||
} else {
|
||||
axis_values[i] = (float)input_values[valuator];
|
||||
}
|
||||
}
|
||||
xinput2_normalize_pen_axes(peninfo, pen, axis_values);
|
||||
X11_XInput2NormalizePenAxes(pen, axis_values);
|
||||
}
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
|
||||
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
|
@ -23,32 +23,51 @@
|
||||
#ifndef SDL_x11pen_h_
|
||||
#define SDL_x11pen_h_
|
||||
|
||||
// Pressure-sensitive pen support for X11.
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
|
||||
|
||||
#include "SDL_x11video.h"
|
||||
#include "../../events/SDL_pen_c.h"
|
||||
|
||||
/* Pressure-sensitive pen */
|
||||
|
||||
/* Forward definition for SDL_x11video.h */
|
||||
// Forward definition for SDL_x11video.h
|
||||
struct SDL_VideoData;
|
||||
|
||||
/* Function definitions */
|
||||
#define SDL_X11_PEN_AXIS_VALUATOR_MISSING -1
|
||||
|
||||
/* Detect XINPUT2 devices that are pens / erasers, or update the list after hotplugging */
|
||||
typedef struct X11_PenHandle
|
||||
{
|
||||
SDL_PenID pen;
|
||||
SDL_bool is_eraser;
|
||||
int x11_deviceid;
|
||||
int valuator_for_axis[SDL_PEN_NUM_AXES];
|
||||
float slider_bias; // shift value to add to PEN_AXIS_SLIDER (before normalisation)
|
||||
float rotation_bias; // rotation to add to PEN_AXIS_ROTATION (after normalisation)
|
||||
float axis_min[SDL_PEN_NUM_AXES];
|
||||
float axis_max[SDL_PEN_NUM_AXES];
|
||||
} X11_PenHandle;
|
||||
|
||||
// Prep pen support (never fails; pens simply won't be added if there's a problem).
|
||||
extern void X11_InitPen(SDL_VideoDevice *_this);
|
||||
|
||||
/* Converts XINPUT2 valuators into pen axis information, including normalisation */
|
||||
extern void X11_PenAxesFromValuators(const SDL_Pen *pen,
|
||||
// Clean up pen support.
|
||||
extern void X11_QuitPen(SDL_VideoDevice *_this);
|
||||
|
||||
// Converts XINPUT2 valuators into pen axis information, including normalisation.
|
||||
extern void X11_PenAxesFromValuators(const X11_PenHandle *pen,
|
||||
const double *input_values, const unsigned char *mask, const int mask_len,
|
||||
/* out-mode parameters: */
|
||||
float axis_values[SDL_PEN_NUM_AXES]);
|
||||
|
||||
/* Map X11 device ID to pen ID */
|
||||
extern int X11_PenIDFromDeviceID(int deviceid);
|
||||
// Add a pen (if this function's further checks validate it).
|
||||
extern X11_PenHandle *X11_MaybeAddPenByDeviceID(SDL_VideoDevice *_this, int deviceid);
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
|
||||
// Remove a pen. It's okay if deviceid is bogus or not a pen, we'll check it.
|
||||
extern void X11_RemovePenByDeviceID(int deviceid);
|
||||
|
||||
#endif /* SDL_x11pen_h_ */
|
||||
// Map X11 device ID to pen ID.
|
||||
extern X11_PenHandle *X11_FindPenByDeviceID(int deviceid);
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
|
||||
|
||||
#endif // SDL_x11pen_h_
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
|
@ -456,9 +456,7 @@ int X11_VideoInit(SDL_VideoDevice *_this)
|
||||
|
||||
X11_InitTouch(_this);
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
|
||||
X11_InitPen(_this);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -485,6 +483,7 @@ void X11_VideoQuit(SDL_VideoDevice *_this)
|
||||
X11_QuitKeyboard(_this);
|
||||
X11_QuitMouse(_this);
|
||||
X11_QuitTouch(_this);
|
||||
X11_QuitPen(_this);
|
||||
X11_QuitClipboard(_this);
|
||||
X11_QuitXsettings(_this);
|
||||
}
|
||||
|
@ -104,6 +104,14 @@ struct SDL_VideoData
|
||||
Atom XdndSelection;
|
||||
Atom XKLAVIER_STATE;
|
||||
|
||||
/* Pen atoms (these have names that don't map well to C symbols) */
|
||||
Atom pen_atom_device_product_id;
|
||||
Atom pen_atom_abs_pressure;
|
||||
Atom pen_atom_abs_tilt_x;
|
||||
Atom pen_atom_abs_tilt_y;
|
||||
Atom pen_atom_wacom_serial_ids;
|
||||
Atom pen_atom_wacom_tool_type;
|
||||
|
||||
SDL_Scancode key_layout[256];
|
||||
SDL_bool selection_waiting;
|
||||
SDL_bool selection_incr_waiting;
|
||||
@ -141,7 +149,6 @@ struct SDL_VideoData
|
||||
SDL_bool steam_keyboard_open;
|
||||
|
||||
SDL_bool is_xwayland;
|
||||
|
||||
};
|
||||
|
||||
extern SDL_bool X11_UseDirectColorVisuals(void);
|
||||
|
@ -274,18 +274,6 @@ static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata,
|
||||
|
||||
return devinfo;
|
||||
}
|
||||
|
||||
static void xinput2_pen_ensure_window(SDL_VideoDevice *_this, const SDL_Pen *pen, Window window)
|
||||
{
|
||||
/* When "flipping" a Wacom eraser pen, we get an XI_DeviceChanged event
|
||||
* with the newly-activated pen, but this event is global for the display.
|
||||
* We won't get a window until the pen starts triggering motion or
|
||||
* button events, so we instead hook the pen to its window at that point. */
|
||||
const SDL_WindowData *windowdata = X11_FindWindow(_this, window);
|
||||
if (windowdata) {
|
||||
SDL_SendPenWindowEvent(0, pen->header.id, windowdata->window);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
|
||||
@ -303,6 +291,14 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
|
||||
const XIHierarchyEvent *hierev = (const XIHierarchyEvent *)cookie->data;
|
||||
int i;
|
||||
for (i = 0; i < hierev->num_info; i++) {
|
||||
// pen stuff...
|
||||
if ((hierev->info[i].flags & (XISlaveRemoved | XIDeviceDisabled)) != 0) {
|
||||
X11_RemovePenByDeviceID(hierev->info[i].deviceid); // it's okay if this thing isn't actually a pen, it'll handle it.
|
||||
} else if ((hierev->info[i].flags & (XISlaveAdded | XIDeviceEnabled)) != 0) {
|
||||
X11_MaybeAddPenByDeviceID(_this, hierev->info[i].deviceid); // this will do more checks to make sure this is valid.
|
||||
}
|
||||
|
||||
// not pen stuff...
|
||||
if (hierev->info[i].flags & XISlaveRemoved) {
|
||||
xinput2_remove_device_info(videodata, hierev->info[i].deviceid);
|
||||
}
|
||||
@ -310,29 +306,14 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
|
||||
videodata->xinput_hierarchy_changed = SDL_TRUE;
|
||||
} break;
|
||||
|
||||
case XI_PropertyEvent:
|
||||
case XI_DeviceChanged:
|
||||
{
|
||||
// FIXME: We shouldn't rescan all devices for pen changes every time a property or active slave changes
|
||||
X11_InitPen(_this);
|
||||
} break;
|
||||
|
||||
case XI_Enter:
|
||||
case XI_Leave:
|
||||
{
|
||||
const XIEnterEvent *enterev = (const XIEnterEvent *)cookie->data;
|
||||
const SDL_WindowData *windowdata = X11_FindWindow(_this, enterev->event);
|
||||
const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(enterev->sourceid));
|
||||
SDL_Window *window = (windowdata && (cookie->evtype == XI_Enter)) ? windowdata->window : NULL;
|
||||
if (pen) {
|
||||
SDL_SendPenWindowEvent(0, pen->header.id, window);
|
||||
}
|
||||
} break;
|
||||
// !!! FIXME: the pen code used to rescan all devices here, but we can do this device-by-device with XI_HierarchyChanged. When do these events fire and why?
|
||||
//case XI_PropertyEvent:
|
||||
//case XI_DeviceChanged:
|
||||
|
||||
case XI_RawMotion:
|
||||
{
|
||||
const XIRawEvent *rawev = (const XIRawEvent *)cookie->data;
|
||||
const SDL_bool is_pen = X11_PenIDFromDeviceID(rawev->sourceid) != SDL_PEN_INVALID;
|
||||
const SDL_bool is_pen = X11_FindPenByDeviceID(rawev->sourceid) != NULL;
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
SDL_XInput2DeviceInfo *devinfo;
|
||||
double coords[2];
|
||||
@ -341,11 +322,9 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
|
||||
|
||||
videodata->global_mouse_changed = SDL_TRUE;
|
||||
if (is_pen) {
|
||||
break; /* Pens check for XI_Motion instead */
|
||||
break; // Pens check for XI_Motion instead
|
||||
}
|
||||
|
||||
/* Non-pen: */
|
||||
|
||||
if (!mouse->relative_mode || mouse->relative_mode_warp) {
|
||||
break;
|
||||
}
|
||||
@ -426,31 +405,17 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
|
||||
case XI_ButtonRelease:
|
||||
{
|
||||
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
|
||||
const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(xev->deviceid));
|
||||
X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid);
|
||||
const int button = xev->detail;
|
||||
const SDL_bool pressed = (cookie->evtype == XI_ButtonPress) ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
if (pen) {
|
||||
xinput2_pen_ensure_window(_this, pen, xev->event);
|
||||
|
||||
/* Only report button event; if there was also pen movement / pressure changes, we expect
|
||||
an XI_Motion event first anyway */
|
||||
if (button == 1) {
|
||||
/* button 1 is the pen tip */
|
||||
if (pressed && SDL_PenPerformHitTest()) {
|
||||
/* Check whether we should handle window resize / move events */
|
||||
SDL_WindowData *windowdata = X11_FindWindow(_this, xev->event);
|
||||
if (windowdata && X11_TriggerHitTestAction(_this, windowdata, pen->last.x, pen->last.y)) {
|
||||
SDL_SendWindowEvent(windowdata->window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0);
|
||||
break; /* Don't pass on this event */
|
||||
}
|
||||
}
|
||||
SDL_SendPenTipEvent(0, pen->header.id,
|
||||
pressed ? SDL_PRESSED : SDL_RELEASED);
|
||||
// Only report button event; if there was also pen movement / pressure changes, we expect an XI_Motion event first anyway.
|
||||
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
|
||||
if (button == 1) { // button 1 is the pen tip
|
||||
SDL_SendPenTouch(0, pen->pen, window, pressed ? SDL_PRESSED : SDL_RELEASED, pen->is_eraser);
|
||||
} else {
|
||||
SDL_SendPenButton(0, pen->header.id,
|
||||
pressed ? SDL_PRESSED : SDL_RELEASED,
|
||||
button - 1);
|
||||
SDL_SendPenButton(0, pen->pen, window, pressed ? SDL_PRESSED : SDL_RELEASED, button - 1);
|
||||
}
|
||||
} else {
|
||||
/* Otherwise assume a regular mouse */
|
||||
@ -475,7 +440,6 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
|
||||
case XI_Motion:
|
||||
{
|
||||
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
|
||||
const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(xev->deviceid));
|
||||
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
|
||||
SDL_bool pointer_emulated = ((xev->flags & XIPointerEmulated) != 0);
|
||||
#else
|
||||
@ -489,25 +453,20 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
|
||||
break;
|
||||
}
|
||||
|
||||
X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid);
|
||||
if (pen) {
|
||||
SDL_PenStatusInfo pen_status;
|
||||
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
|
||||
SDL_SendPenMotion(0, pen->pen, window, (float) xev->event_x, (float) xev->event_y);
|
||||
|
||||
pen_status.x = (float)xev->event_x;
|
||||
pen_status.y = (float)xev->event_y;
|
||||
float axes[SDL_PEN_NUM_AXES];
|
||||
X11_PenAxesFromValuators(pen, xev->valuators.values, xev->valuators.mask, xev->valuators.mask_len, axes);
|
||||
|
||||
X11_PenAxesFromValuators(pen,
|
||||
xev->valuators.values, xev->valuators.mask, xev->valuators.mask_len,
|
||||
&pen_status.axes[0]);
|
||||
|
||||
xinput2_pen_ensure_window(_this, pen, xev->event);
|
||||
|
||||
SDL_SendPenMotion(0, pen->header.id,
|
||||
SDL_TRUE,
|
||||
&pen_status);
|
||||
break;
|
||||
for (int i = 0; i < SDL_arraysize(axes); i++) {
|
||||
if (pen->valuator_for_axis[i] != SDL_X11_PEN_AXIS_VALUATOR_MISSING) {
|
||||
SDL_SendPenAxis(0, pen->pen, window, (SDL_PenAxis) i, axes[i]);
|
||||
}
|
||||
|
||||
if (!pointer_emulated) {
|
||||
}
|
||||
} else if (!pointer_emulated) {
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
if (!mouse->relative_mode || mouse->relative_mode_warp) {
|
||||
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
|
||||
|
@ -34,9 +34,6 @@ static SDLTest_TestSuiteReference *testSuites[] = {
|
||||
&mainTestSuite,
|
||||
&mathTestSuite,
|
||||
&mouseTestSuite,
|
||||
#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_TVOS)
|
||||
&penTestSuite,
|
||||
#endif
|
||||
&pixelsTestSuite,
|
||||
&platformTestSuite,
|
||||
&propertiesTestSuite,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,6 @@ extern SDLTest_TestSuiteReference logTestSuite;
|
||||
extern SDLTest_TestSuiteReference mainTestSuite;
|
||||
extern SDLTest_TestSuiteReference mathTestSuite;
|
||||
extern SDLTest_TestSuiteReference mouseTestSuite;
|
||||
extern SDLTest_TestSuiteReference penTestSuite;
|
||||
extern SDLTest_TestSuiteReference pixelsTestSuite;
|
||||
extern SDLTest_TestSuiteReference platformTestSuite;
|
||||
extern SDLTest_TestSuiteReference propertiesTestSuite;
|
||||
|
752
test/testpen.c
752
test/testpen.c
@ -1,5 +1,4 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
@ -8,519 +7,284 @@
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
freely.
|
||||
*/
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#define SDL_MAIN_USE_CALLBACKS 1
|
||||
#include <SDL3/SDL_main.h>
|
||||
#include <SDL3/SDL_test.h>
|
||||
#include <SDL3/SDL_test_common.h>
|
||||
|
||||
#define WIDTH 1600
|
||||
#define HEIGHT 1200
|
||||
|
||||
#define VERBOSE 0
|
||||
|
||||
#define ALWAYS_SHOW_PRESSURE_BOX 1
|
||||
|
||||
static SDLTest_CommonState *state;
|
||||
static int quitting = 0;
|
||||
|
||||
static float last_x, last_y;
|
||||
static float last_xtilt, last_ytilt, last_pressure, last_distance, last_rotation;
|
||||
static int last_button;
|
||||
static int last_touching; /* tip touches surface */
|
||||
static int last_was_eraser;
|
||||
|
||||
static SDL_Texture *offscreen_texture = NULL;
|
||||
|
||||
static void DrawScreen(SDL_Renderer *renderer)
|
||||
typedef struct Pen
|
||||
{
|
||||
float xdelta, ydelta, endx, endy;
|
||||
/* off-screen texture to render into */
|
||||
SDL_Texture *window_texture;
|
||||
const float X = 128.0f, Y = 128.0f; /* mid-point in the off-screen texture */
|
||||
SDL_FRect dest_rect;
|
||||
float tilt_vec_x = SDL_sinf(last_xtilt * SDL_PI_F / 180.0f);
|
||||
float tilt_vec_y = SDL_sinf(last_ytilt * SDL_PI_F / 180.0f);
|
||||
int color = last_button + 1;
|
||||
|
||||
if (!renderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer, 0x40, 0x40, 0x40, 0xff);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
if (offscreen_texture == NULL) {
|
||||
offscreen_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, (int)(X * 2.0f), (int)(Y * 2.0f));
|
||||
}
|
||||
|
||||
/* Render into off-screen texture so we can do pixel-precise rendering later */
|
||||
window_texture = SDL_GetRenderTarget(renderer);
|
||||
SDL_SetRenderTarget(renderer, offscreen_texture);
|
||||
|
||||
/* Rendering starts here */
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer, 0x40, 0x40, 0x40, 0xff);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 0xa0, 0xa0, 0xa0, 0xff);
|
||||
if (last_touching) {
|
||||
SDL_FRect rect;
|
||||
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.w = 2.0f * X - 1.0f;
|
||||
rect.h = 2.0f * Y - 1.0f;
|
||||
|
||||
SDL_RenderRect(renderer, &rect);
|
||||
} else {
|
||||
/* Show where the pen is rotating when it isn't touching the surface.
|
||||
Otherwise we draw the rotation angle below together with pressure information. */
|
||||
float rot_vecx = SDL_sinf(last_rotation / 180.0f * SDL_PI_F);
|
||||
float rot_vecy = -SDL_cosf(last_rotation / 180.0f * SDL_PI_F);
|
||||
float px = X + rot_vecx * 100.0f;
|
||||
float py = Y + rot_vecy * 100.0f;
|
||||
float px2 = X + rot_vecx * 80.0f;
|
||||
float py2 = Y + rot_vecy * 80.0f;
|
||||
|
||||
SDL_RenderLine(renderer,
|
||||
px, py,
|
||||
px2 + rot_vecy * 20.0f,
|
||||
py2 - rot_vecx * 20.0f);
|
||||
SDL_RenderLine(renderer,
|
||||
px, py,
|
||||
px2 - rot_vecy * 20.0f,
|
||||
py2 + rot_vecx * 20.0f);
|
||||
}
|
||||
|
||||
if (last_was_eraser) {
|
||||
SDL_FRect rect;
|
||||
|
||||
rect.x = X - 10.0f;
|
||||
rect.y = Y - 10.0f;
|
||||
rect.w = 21.0f;
|
||||
rect.h = 21.0f;
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 0x00, 0xff, 0xff, 0xff);
|
||||
SDL_RenderFillRect(renderer, &rect);
|
||||
} else {
|
||||
float distance = last_distance * 50.0f;
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 0xff, 0, 0, 0xff);
|
||||
SDL_RenderLine(renderer,
|
||||
X - 10.0f - distance, Y,
|
||||
X - distance, Y);
|
||||
SDL_RenderLine(renderer,
|
||||
X + 10.0f + distance, Y,
|
||||
X + distance, Y);
|
||||
SDL_RenderLine(renderer,
|
||||
X, Y - 10.0f - distance,
|
||||
X, Y - distance);
|
||||
SDL_RenderLine(renderer,
|
||||
X, Y + 10.0f + distance,
|
||||
X, Y + distance);
|
||||
|
||||
}
|
||||
|
||||
/* Draw a cone based on the direction the pen is leaning as if it were shining a light. */
|
||||
/* Colour derived from pens, intensity based on pressure: */
|
||||
SDL_SetRenderDrawColor(renderer,
|
||||
(color & 0x01) ? 0xff : 0,
|
||||
(color & 0x02) ? 0xff : 0,
|
||||
(color & 0x04) ? 0xff : 0,
|
||||
(int)(0xff));
|
||||
|
||||
xdelta = -tilt_vec_x * 100.0f;
|
||||
ydelta = -tilt_vec_y * 100.0f;
|
||||
endx = X + xdelta;
|
||||
endy = Y + ydelta;
|
||||
SDL_RenderLine(renderer, X, Y, endx, endy);
|
||||
|
||||
SDL_SetRenderDrawColor(renderer,
|
||||
(color & 0x01) ? 0xff : 0,
|
||||
(color & 0x02) ? 0xff : 0,
|
||||
(color & 0x04) ? 0xff : 0,
|
||||
(Uint8)(0xff * last_pressure));
|
||||
/* Cone base width based on pressure: */
|
||||
SDL_RenderLine(renderer, X, Y, endx + (ydelta * last_pressure / 3.0f), endy - (xdelta * last_pressure / 3.0f));
|
||||
SDL_RenderLine(renderer, X, Y, endx - (ydelta * last_pressure / 3.0f), endy + (xdelta * last_pressure / 3.0f));
|
||||
|
||||
/* If tilt is very small (or zero, for pens that don't have tilt), add some extra lines, rotated by the current rotation value */
|
||||
if (ALWAYS_SHOW_PRESSURE_BOX || (SDL_fabsf(tilt_vec_x) < 0.2f && SDL_fabsf(tilt_vec_y) < 0.2f)) {
|
||||
int rot;
|
||||
float pressure = last_pressure * 80.0f;
|
||||
|
||||
/* Four times, rotated 90 degrees, so that we get a box */
|
||||
for (rot = 0; rot < 4; ++rot) {
|
||||
|
||||
float vecx = SDL_cosf((last_rotation + (rot * 90.0f)) / 180.0f * SDL_PI_F);
|
||||
float vecy = SDL_sinf((last_rotation + (rot * 90.0f)) / 180.0f * SDL_PI_F);
|
||||
|
||||
float px = X + vecx * pressure;
|
||||
float py = Y + vecy * pressure;
|
||||
|
||||
SDL_RenderLine(renderer,
|
||||
px + vecy * 10.0f, py - vecx * 10.0f,
|
||||
px - vecy * 10.0f, py + vecx * 10.0f);
|
||||
|
||||
if (rot == 3) {
|
||||
int r = 0;
|
||||
for (; r >= 0; r -= 2) {
|
||||
float delta = 10.0f - ((float) r);
|
||||
|
||||
SDL_RenderLine(renderer,
|
||||
px + vecy * delta, py - vecx * delta,
|
||||
px + (vecx * pressure * 0.4f),
|
||||
py + (vecy * pressure * 0.4f));
|
||||
SDL_RenderLine(renderer,
|
||||
px - vecy * delta, py + vecx * delta,
|
||||
px + (vecx * pressure * 0.4f),
|
||||
py + (vecy * pressure * 0.4f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_SetRenderTarget(renderer, window_texture);
|
||||
/* Now render to pixel-precise position */
|
||||
dest_rect.x = last_x - X;
|
||||
dest_rect.y = last_y - Y;
|
||||
dest_rect.w = X * 2.0f;
|
||||
dest_rect.h = Y * 2.0f;
|
||||
SDL_RenderTexture(renderer, offscreen_texture, NULL, &dest_rect);
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
|
||||
static void dump_state(void)
|
||||
{
|
||||
int i;
|
||||
int pens_nr;
|
||||
|
||||
/* Make sure this also works with a NULL parameter */
|
||||
SDL_PenID* pens = SDL_GetPens(NULL);
|
||||
if (pens) {
|
||||
SDL_free(pens);
|
||||
}
|
||||
|
||||
pens = SDL_GetPens(&pens_nr);
|
||||
if (!pens) {
|
||||
SDL_Log("Couldn't get pens: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
SDL_Log("Found %d pens (terminated by %u)\n", pens_nr, (unsigned) pens[pens_nr]);
|
||||
|
||||
for (i = 0; i < pens_nr; ++i) {
|
||||
SDL_PenID penid = pens[i];
|
||||
SDL_GUID guid = SDL_GetPenGUID(penid);
|
||||
char guid_str[33];
|
||||
SDL_PenID pen;
|
||||
Uint8 r, g, b;
|
||||
float axes[SDL_PEN_NUM_AXES];
|
||||
float x, y;
|
||||
int k;
|
||||
SDL_PenCapabilityInfo info;
|
||||
Uint32 status = SDL_GetPenStatus(penid, &x, &y, axes, SDL_PEN_NUM_AXES);
|
||||
const SDL_PenCapabilityFlags capabilities = SDL_GetPenCapabilities(penid, &info);
|
||||
char *type;
|
||||
char *buttons_str;
|
||||
float x;
|
||||
float y;
|
||||
Uint32 buttons;
|
||||
SDL_bool eraser;
|
||||
SDL_bool touching;
|
||||
struct Pen *next;
|
||||
} Pen;
|
||||
|
||||
SDL_GUIDToString(guid, guid_str, sizeof(guid_str));
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
static SDLTest_CommonState *state = NULL;
|
||||
static SDL_Texture *white_pixel = NULL;
|
||||
static Pen pens;
|
||||
|
||||
switch (SDL_GetPenType(penid)) {
|
||||
case SDL_PEN_TYPE_ERASER:
|
||||
type = "Eraser";
|
||||
break;
|
||||
case SDL_PEN_TYPE_PEN:
|
||||
type = "Pen";
|
||||
break;
|
||||
case SDL_PEN_TYPE_PENCIL:
|
||||
type = "Pencil";
|
||||
break;
|
||||
case SDL_PEN_TYPE_BRUSH:
|
||||
type = "Brush";
|
||||
break;
|
||||
case SDL_PEN_TYPE_AIRBRUSH:
|
||||
type = "Airbrush";
|
||||
break;
|
||||
default:
|
||||
type = "Unknown (bug?)";
|
||||
}
|
||||
|
||||
switch (info.num_buttons) {
|
||||
case SDL_PEN_INFO_UNKNOWN:
|
||||
SDL_asprintf(&buttons_str, "? buttons");
|
||||
break;
|
||||
case 1:
|
||||
SDL_asprintf(&buttons_str, "1 button");
|
||||
break;
|
||||
default:
|
||||
SDL_asprintf(&buttons_str, "%d button", info.num_buttons);
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_Log("%s %lu: [%s] attached=%d, %s [cap= %08lx:%08lx =status] '%s'\n",
|
||||
type,
|
||||
(unsigned long) penid, guid_str,
|
||||
SDL_PenConnected(penid), /* should always be SDL_TRUE during iteration */
|
||||
buttons_str,
|
||||
(unsigned long) capabilities,
|
||||
(unsigned long) status,
|
||||
SDL_GetPenName(penid));
|
||||
SDL_free(buttons_str);
|
||||
SDL_Log(" pos=(%.2f, %.2f)", x, y);
|
||||
for (k = 0; k < SDL_PEN_NUM_AXES; ++k) {
|
||||
SDL_bool supported = ((capabilities & SDL_PEN_AXIS_CAPABILITY(k)) != 0);
|
||||
if (supported) {
|
||||
if (k == SDL_PEN_AXIS_XTILT || k == SDL_PEN_AXIS_YTILT) {
|
||||
if (info.max_tilt == SDL_PEN_INFO_UNKNOWN) {
|
||||
SDL_Log(" axis %d: %.3f (max tilt unknown)", k, axes[k]);
|
||||
} else {
|
||||
SDL_Log(" axis %d: %.3f (tilt -%.1f..%.1f)", k, axes[k],
|
||||
info.max_tilt, info.max_tilt);
|
||||
}
|
||||
} else {
|
||||
SDL_Log(" axis %d: %.3f", k, axes[k]);
|
||||
}
|
||||
} else {
|
||||
SDL_Log(" axis %d: unsupported (%.3f)", k, axes[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_free(pens);
|
||||
}
|
||||
|
||||
static void update_axes(float *axes)
|
||||
int SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
last_xtilt = axes[SDL_PEN_AXIS_XTILT];
|
||||
last_ytilt = axes[SDL_PEN_AXIS_YTILT];
|
||||
last_pressure = axes[SDL_PEN_AXIS_PRESSURE];
|
||||
last_distance = axes[SDL_PEN_AXIS_DISTANCE];
|
||||
last_rotation = axes[SDL_PEN_AXIS_ROTATION];
|
||||
}
|
||||
|
||||
static void update_axes_from_touch(const float pressure)
|
||||
{
|
||||
last_xtilt = 0;
|
||||
last_ytilt = 0;
|
||||
last_pressure = pressure;
|
||||
last_distance = 0;
|
||||
last_rotation = 0;
|
||||
}
|
||||
|
||||
static void process_event(SDL_Event event)
|
||||
{
|
||||
SDLTest_CommonEvent(state, &event, &quitting);
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
{
|
||||
dump_state();
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
#if VERBOSE
|
||||
{
|
||||
float x, y;
|
||||
SDL_GetMouseState(&x, &y);
|
||||
if (event.type == SDL_EVENT_MOUSE_MOTION) {
|
||||
SDL_Log("[%lu] mouse motion: mouse ID %d is at (%.2f, %.2f) (state: %.2f,%.2f) delta (%.2f, %.2f)\n",
|
||||
event.motion.timestamp,
|
||||
event.motion.which,
|
||||
event.motion.x, event.motion.y,
|
||||
event.motion.xrel, event.motion.yrel,
|
||||
x, y);
|
||||
} else {
|
||||
SDL_Log("[%lu] mouse button: mouse ID %d is at (%.2f, %.2f) (state: %.2f,%.2f)\n",
|
||||
event.button.timestamp,
|
||||
event.button.which,
|
||||
event.button.x, event.button.y,
|
||||
x, y);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (event.motion.which != SDL_PEN_MOUSEID && event.motion.which != SDL_TOUCH_MOUSEID) {
|
||||
SDL_ShowCursor();
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_PEN_MOTION:
|
||||
{
|
||||
SDL_PenMotionEvent *ev = &event.pmotion;
|
||||
|
||||
SDL_HideCursor();
|
||||
last_x = ev->x;
|
||||
last_y = ev->y;
|
||||
update_axes(ev->axes);
|
||||
last_was_eraser = ev->pen_state & SDL_PEN_ERASER_MASK;
|
||||
#if VERBOSE
|
||||
SDL_Log("[%lu] pen motion: %s %u at (%.4f, %.4f); pressure=%.3f, tilt=%.3f/%.3f, dist=%.3f [buttons=%02x]\n",
|
||||
(unsigned long) ev->timestamp,
|
||||
last_was_eraser ? "eraser" : "pen",
|
||||
(unsigned int)ev->which, ev->x, ev->y, last_pressure, last_xtilt, last_ytilt, last_distance,
|
||||
ev->pen_state);
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_PEN_UP:
|
||||
case SDL_EVENT_PEN_DOWN: {
|
||||
SDL_PenTipEvent *ev = &event.ptip;
|
||||
last_x = ev->x;
|
||||
last_y = ev->y;
|
||||
update_axes(ev->axes);
|
||||
last_was_eraser = ev->tip == SDL_PEN_TIP_ERASER;
|
||||
last_button = ev->pen_state & 0xf; /* button mask */
|
||||
last_touching = (event.type == SDL_EVENT_PEN_DOWN);
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_PEN_BUTTON_UP:
|
||||
case SDL_EVENT_PEN_BUTTON_DOWN:
|
||||
{
|
||||
SDL_PenButtonEvent *ev = &event.pbutton;
|
||||
|
||||
SDL_HideCursor();
|
||||
last_x = ev->x;
|
||||
last_y = ev->y;
|
||||
update_axes(ev->axes);
|
||||
if (last_pressure > 0.0f && !last_touching) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_TEST,
|
||||
"[%lu] : reported pressure %.5f even though pen is not touching surface",
|
||||
(unsigned long) ev->timestamp, last_pressure);
|
||||
|
||||
}
|
||||
last_was_eraser = ev->pen_state & SDL_PEN_ERASER_MASK;
|
||||
last_button = ev->pen_state & 0xf; /* button mask */
|
||||
if ((ev->pen_state & SDL_PEN_DOWN_MASK) && !last_touching) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_TEST,
|
||||
"[%lu] : reported flags %x (SDL_PEN_FLAG_DOWN_MASK) despite not receiving SDL_EVENT_PEN_DOWN",
|
||||
(unsigned long) ev->timestamp, ev->pen_state);
|
||||
|
||||
}
|
||||
if (!(ev->pen_state & SDL_PEN_DOWN_MASK) && last_touching) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_TEST,
|
||||
"[%lu] : reported flags %x (no SDL_PEN_FLAG_DOWN_MASK) despite receiving SDL_EVENT_PEN_DOWN without SDL_EVENT_PEN_UP afterwards",
|
||||
(unsigned long) ev->timestamp, ev->pen_state);
|
||||
|
||||
}
|
||||
#if VERBOSE
|
||||
SDL_Log("[%lu] pen button: %s %u at (%.4f, %.4f); BUTTON %d reported %s with event %s [pressure=%.3f, tilt=%.3f/%.3f, dist=%.3f]\n",
|
||||
(unsigned long) ev->timestamp,
|
||||
last_was_eraser ? "eraser" : "pen",
|
||||
(unsigned int)ev->which, ev->x, ev->y,
|
||||
ev->button,
|
||||
(ev->state == SDL_PRESSED) ? "PRESSED"
|
||||
: ((ev->state == SDL_RELEASED) ? "RELEASED" : "--invalid--"),
|
||||
event.type == SDL_EVENT_PEN_BUTTON_UP ? "PENBUTTONUP" : "PENBUTTONDOWN",
|
||||
last_pressure, last_xtilt, last_ytilt, last_distance);
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_WINDOW_PEN_ENTER:
|
||||
SDL_Log("[%lu] Pen %lu entered window %lx",
|
||||
(unsigned long) event.window.timestamp,
|
||||
(unsigned long) event.window.data1,
|
||||
(unsigned long) event.window.windowID);
|
||||
break;
|
||||
|
||||
case SDL_EVENT_WINDOW_PEN_LEAVE:
|
||||
SDL_Log("[%lu] Pen %lu left window %lx",
|
||||
(unsigned long) event.window.timestamp,
|
||||
(unsigned long) event.window.data1,
|
||||
(unsigned long) event.window.windowID);
|
||||
break;
|
||||
|
||||
#if VERBOSE
|
||||
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
||||
SDL_Log("[%lu] Mouse entered window %lx",
|
||||
(unsigned long) event.window.timestamp,
|
||||
(unsigned long) event.window.windowID);
|
||||
break;
|
||||
|
||||
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
||||
SDL_Log("[%lu] Mouse left window %lx",
|
||||
(unsigned long) event.window.timestamp,
|
||||
(unsigned long) event.window.windowID);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SDL_EVENT_FINGER_DOWN:
|
||||
case SDL_EVENT_FINGER_MOTION:
|
||||
case SDL_EVENT_FINGER_UP:
|
||||
{
|
||||
SDL_TouchFingerEvent *ev = &event.tfinger;
|
||||
int w, h;
|
||||
SDL_HideCursor();
|
||||
SDL_GetWindowSize(SDL_GetWindowFromID(ev->windowID), &w, &h);
|
||||
last_x = ev->x * w;
|
||||
last_y = ev->y * h;
|
||||
update_axes_from_touch(ev->pressure);
|
||||
last_was_eraser = SDL_FALSE;
|
||||
last_button = 0;
|
||||
last_touching = (ev->type != SDL_EVENT_FINGER_UP);
|
||||
#if VERBOSE
|
||||
SDL_Log("[%lu] finger %s: %s (touchId: %" SDL_PRIu64 ", fingerId: %" SDL_PRIu64 ") at (%.4f, %.4f); pressure=%.3f\n",
|
||||
(unsigned long) ev->timestamp,
|
||||
ev->type == SDL_EVENT_FINGER_DOWN ? "down" : (ev->type == SDL_EVENT_FINGER_MOTION ? "motion" : "up"),
|
||||
SDL_GetTouchDeviceName(ev->touchId),
|
||||
ev->touchId,
|
||||
ev->fingerId,
|
||||
last_x, last_y, last_pressure);
|
||||
#endif
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void loop(void)
|
||||
{
|
||||
SDL_Event event;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < state->num_windows; ++i) {
|
||||
if (state->renderers[i]) {
|
||||
DrawScreen(state->renderers[i]);
|
||||
}
|
||||
}
|
||||
SDL_srand(0);
|
||||
|
||||
if (SDL_WaitEventTimeout(&event, 10)) {
|
||||
process_event(event);
|
||||
}
|
||||
while (SDL_PollEvent(&event)) {
|
||||
process_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* Initialize test framework */
|
||||
state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
|
||||
if (!state) {
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* Enable standard application logging */
|
||||
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
||||
|
||||
/* Parse commandline */
|
||||
for (i = 1; i < argc;) {
|
||||
int consumed = SDLTest_CommonArg(state, i);
|
||||
if (consumed <= 0) {
|
||||
static const char *options[] = {
|
||||
NULL,
|
||||
};
|
||||
SDLTest_CommonLogUsage(state, argv[0], options);
|
||||
SDL_Quit();
|
||||
SDLTest_CommonDestroyState(state);
|
||||
return 1;
|
||||
}
|
||||
i += consumed;
|
||||
}
|
||||
|
||||
state->window_title = "Pressure-Sensitive Pen Test";
|
||||
state->window_w = WIDTH;
|
||||
state->window_h = HEIGHT;
|
||||
state->skip_renderer = SDL_FALSE;
|
||||
state->num_windows = 1;
|
||||
|
||||
if (!SDLTest_CommonDefaultArgs(state, argc, argv) || !SDLTest_CommonInit(state)) {
|
||||
/* Load the SDL library */
|
||||
if (!SDLTest_CommonInit(state)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE);
|
||||
|
||||
renderer = state->renderers[0];
|
||||
if (!renderer) {
|
||||
/* SDL_Log("Couldn't create renderer: %s", SDL_GetError()); */
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
white_pixel = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, 16, 16);
|
||||
if (!white_pixel) {
|
||||
SDL_Log("Couldn't create white_pixel texture: %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
} else {
|
||||
const SDL_Rect rect = { 0, 0, 16, 16 };
|
||||
Uint32 pixels[16 * 16];
|
||||
SDL_memset(pixels, 0xFF, sizeof (pixels));
|
||||
SDL_UpdateTexture(white_pixel, &rect, pixels, 16 * sizeof (Uint32));
|
||||
}
|
||||
|
||||
SDL_HideCursor();
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
static Pen *FindPen(SDL_PenID which)
|
||||
{
|
||||
Pen *i;
|
||||
for (i = pens.next; i != NULL; i = i->next) {
|
||||
if (i->pen == which) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int SDL_AppEvent(void *appstate, const SDL_Event *event)
|
||||
{
|
||||
Pen *pen = NULL;
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_EVENT_PEN_PROXIMITY_IN: {
|
||||
pen = (Pen *) SDL_calloc(1, sizeof (*pen));
|
||||
if (!pen) {
|
||||
SDL_Log("Out of memory!");
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
SDL_Log("Pen %" SDL_PRIu32 " enters proximity!", event->pproximity.which);
|
||||
pen->pen = event->pproximity.which;
|
||||
pen->r = (Uint8) SDL_rand(256);
|
||||
pen->g = (Uint8) SDL_rand(256);
|
||||
pen->b = (Uint8) SDL_rand(256);
|
||||
pen->x = 320.0f;
|
||||
pen->y = 240.0f;
|
||||
pen->next = pens.next;
|
||||
pens.next = pen;
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
case SDL_EVENT_PEN_PROXIMITY_OUT: {
|
||||
Pen *prev = &pens;
|
||||
Pen *i;
|
||||
|
||||
SDL_Log("Pen %" SDL_PRIu32 " leaves proximity!", event->pproximity.which);
|
||||
for (i = pens.next; i != NULL; i = i->next) {
|
||||
if (i->pen == event->pproximity.which) {
|
||||
prev->next = i->next;
|
||||
SDL_free(i);
|
||||
break;
|
||||
}
|
||||
prev = i;
|
||||
}
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
case SDL_EVENT_PEN_DOWN:
|
||||
/*SDL_Log("Pen %" SDL_PRIu32 " down!", event->ptouch.which);*/
|
||||
pen = FindPen(event->ptouch.which);
|
||||
if (pen) {
|
||||
pen->touching = SDL_TRUE;
|
||||
pen->eraser = (event->ptouch.eraser != 0);
|
||||
}
|
||||
return SDL_APP_CONTINUE;
|
||||
|
||||
case SDL_EVENT_PEN_UP:
|
||||
/*SDL_Log("Pen %" SDL_PRIu32 " up!", event->ptouch.which);*/
|
||||
pen = FindPen(event->ptouch.which);
|
||||
if (pen) {
|
||||
pen->touching = SDL_FALSE;
|
||||
pen->axes[SDL_PEN_AXIS_PRESSURE] = 0.0f;
|
||||
}
|
||||
return SDL_APP_CONTINUE;
|
||||
|
||||
case SDL_EVENT_PEN_BUTTON_DOWN:
|
||||
/*SDL_Log("Pen %" SDL_PRIu32 " button %d down!", event->pbutton.which, (int) event->pbutton.button);*/
|
||||
pen = FindPen(event->ptouch.which);
|
||||
if (pen) {
|
||||
pen->buttons |= (1 << event->pbutton.button);
|
||||
}
|
||||
return SDL_APP_CONTINUE;
|
||||
|
||||
case SDL_EVENT_PEN_BUTTON_UP:
|
||||
/*SDL_Log("Pen %" SDL_PRIu32 " button %d up!", event->pbutton.which, (int) event->pbutton.button);*/
|
||||
pen = FindPen(event->ptouch.which);
|
||||
if (pen) {
|
||||
pen->buttons &= ~(1 << event->pbutton.button);
|
||||
}
|
||||
return SDL_APP_CONTINUE;
|
||||
|
||||
case SDL_EVENT_PEN_MOTION:
|
||||
/*SDL_Log("Pen %" SDL_PRIu32 " moved to (%f,%f)!", event->pmotion.which, event->pmotion.x, event->pmotion.y);*/
|
||||
pen = FindPen(event->ptouch.which);
|
||||
if (pen) {
|
||||
pen->x = event->pmotion.x;
|
||||
pen->y = event->pmotion.y;
|
||||
}
|
||||
return SDL_APP_CONTINUE;
|
||||
|
||||
case SDL_EVENT_PEN_AXIS:
|
||||
/*SDL_Log("Pen %" SDL_PRIu32 " axis %d is now %f!", event->paxis.which, (int) event->paxis.axis, event->paxis.value);*/
|
||||
pen = FindPen(event->ptouch.which);
|
||||
if (pen && (event->paxis.axis < SDL_arraysize(pen->axes))) {
|
||||
pen->axes[event->paxis.axis] = event->paxis.value;
|
||||
}
|
||||
return SDL_APP_CONTINUE;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN: {
|
||||
const SDL_Keycode sym = event->key.key;
|
||||
if (sym == SDLK_ESCAPE || sym == SDLK_AC_BACK) {
|
||||
SDL_Log("Key : Escape!");
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_EVENT_QUIT:
|
||||
return SDL_APP_SUCCESS;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SDLTest_CommonEventMainCallbacks(state, event);
|
||||
}
|
||||
|
||||
static void DrawOnePen(Pen *pen, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* draw button presses for this pen. A square for each in the pen's color, offset down the screen so they don't overlap. */
|
||||
SDL_SetRenderDrawColor(renderer, pen->r, pen->g, pen->b, 255);
|
||||
for (i = 0; i < 8; i++) { /* we assume you don't have more than 8 buttons atm... */
|
||||
if (pen->buttons & (1 << i)) {
|
||||
const SDL_FRect rect = { 30.0f * ((float) i), ((float) num) * 30.0f, 30.0f, 30.0f };
|
||||
SDL_RenderFillRect(renderer, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
/* draw a square to represent pressure. Always green for eraser and blue for pen */
|
||||
/* we do this with a texture, so we can trivially rotate it, which SDL_RenderFillRect doesn't offer. */
|
||||
if (pen->axes[SDL_PEN_AXIS_PRESSURE] > 0.0f) {
|
||||
const float size = (150.0f * pen->axes[SDL_PEN_AXIS_PRESSURE]) + 20.0f;
|
||||
const float halfsize = size / 2.0f;
|
||||
const SDL_FRect rect = { pen->x - halfsize, pen->y - halfsize, size, size };
|
||||
const SDL_FPoint center = { halfsize, halfsize };
|
||||
if (pen->eraser) {
|
||||
SDL_SetTextureColorMod(white_pixel, 0, 255, 0);
|
||||
} else {
|
||||
SDL_SetTextureColorMod(white_pixel, 0, 0, 255);
|
||||
}
|
||||
SDL_RenderTextureRotated(renderer, white_pixel, NULL, &rect, pen->axes[SDL_PEN_AXIS_ROTATION], ¢er, SDL_FLIP_NONE);
|
||||
}
|
||||
|
||||
/* draw a little square for position in the center of the pressure, with the pen-specific color. */
|
||||
{
|
||||
const float distance = pen->touching ? 0.0f : SDL_clamp(pen->axes[SDL_PEN_AXIS_DISTANCE], 0.0f, 1.0f);
|
||||
const float size = 10 + (30.0f * (1.0f - distance));
|
||||
const float halfsize = size / 2.0f;
|
||||
const SDL_FRect rect = { pen->x - halfsize, pen->y - halfsize, size, size };
|
||||
const SDL_FPoint center = { halfsize, halfsize };
|
||||
SDL_SetTextureColorMod(white_pixel, pen->r, pen->g, pen->b);
|
||||
SDL_RenderTextureRotated(renderer, white_pixel, NULL, &rect, pen->axes[SDL_PEN_AXIS_ROTATION], ¢er, SDL_FLIP_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
int SDL_AppIterate(void *appstate)
|
||||
{
|
||||
int num = 0;
|
||||
Pen *pen;
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
for (pen = pens.next; pen != NULL; pen = pen->next, num++) {
|
||||
DrawOnePen(pen, num);
|
||||
}
|
||||
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
void SDL_AppQuit(void *appstate)
|
||||
{
|
||||
Pen *i, *next;
|
||||
for (i = pens.next; i != NULL; i = next) {
|
||||
next = i->next;
|
||||
SDL_free(i);
|
||||
}
|
||||
pens.next = NULL;
|
||||
SDL_DestroyTexture(white_pixel);
|
||||
SDLTest_CommonQuit(state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (!quitting) {
|
||||
loop();
|
||||
}
|
||||
|
||||
SDLTest_CommonQuit(state);
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user