forked from Green-Sky/tomato
1100 lines
43 KiB
C
1100 lines
43 KiB
C
|
/*
|
||
|
Simple DirectMedia Layer
|
||
|
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
||
|
|
||
|
This software is provided 'as-is', without any express or implied
|
||
|
warranty. In no event will the authors be held liable for any damages
|
||
|
arising from the use of this software.
|
||
|
|
||
|
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.
|
||
|
*/
|
||
|
#include "SDL_internal.h"
|
||
|
|
||
|
/* Pressure-sensitive pen handling code for SDL */
|
||
|
|
||
|
#include "../SDL_hints_c.h"
|
||
|
#include "SDL_events_c.h"
|
||
|
#include "SDL_pen_c.h"
|
||
|
|
||
|
#define PEN_MOUSE_EMULATE 0 /* pen behaves like mouse */
|
||
|
#define PEN_MOUSE_STATELESS 1 /* pen does not update mouse state */
|
||
|
#define PEN_MOUSE_DISABLED 2 /* pen does not send mouse events */
|
||
|
|
||
|
/* flags that are not SDL_PEN_FLAG_ */
|
||
|
#define PEN_FLAGS_CAPABILITIES (~(SDL_PEN_FLAG_NEW | SDL_PEN_FLAG_DETACHED | SDL_PEN_FLAG_STALE))
|
||
|
|
||
|
#define PEN_GET_PUBLIC_STATUS_MASK(pen) (((pen)->header.flags & (SDL_PEN_ERASER_MASK | SDL_PEN_DOWN_MASK)))
|
||
|
|
||
|
static int pen_mouse_emulation_mode = PEN_MOUSE_EMULATE; /* SDL_HINT_PEN_NOT_MOUSE */
|
||
|
|
||
|
static int pen_delay_mouse_button_mode = 1; /* SDL_HINT_PEN_DELAY_MOUSE_BUTTON */
|
||
|
|
||
|
#ifndef SDL_THREADS_DISABLED
|
||
|
static SDL_Mutex *SDL_pen_access_lock;
|
||
|
# define SDL_LOCK_PENS() SDL_LockMutex(SDL_pen_access_lock)
|
||
|
# define SDL_UNLOCK_PENS() SDL_UnlockMutex(SDL_pen_access_lock)
|
||
|
#else
|
||
|
# define SDL_LOCK_PENS()
|
||
|
# define SDL_UNLOCK_PENS()
|
||
|
#endif
|
||
|
|
||
|
static struct
|
||
|
{
|
||
|
SDL_Pen *pens; /* if "sorted" is SDL_TRUE:
|
||
|
sorted by: (is-attached, id):
|
||
|
- first all attached pens, in ascending ID order
|
||
|
- then all detached pens, in ascending ID order */
|
||
|
size_t pens_allocated; /* # entries allocated to "pens" */
|
||
|
size_t pens_known; /* <= pens_allocated; this includes detached pens */
|
||
|
size_t pens_attached; /* <= pens_known; all attached pens are at the beginning of "pens" */
|
||
|
SDL_bool sorted; /* This is SDL_FALSE in the period between SDL_PenGCMark() and SDL_PenGCSWeep() */
|
||
|
} pen_handler;
|
||
|
|
||
|
static SDL_PenID pen_invalid = { SDL_PEN_INVALID };
|
||
|
|
||
|
static SDL_GUID pen_guid_zero = { { 0 } };
|
||
|
|
||
|
#define SDL_LOAD_LOCK_PEN(penvar, instance_id, err_return) \
|
||
|
SDL_Pen *penvar; \
|
||
|
if (instance_id == SDL_PEN_INVALID) { \
|
||
|
SDL_SetError("Invalid SDL_PenID"); \
|
||
|
return (err_return); \
|
||
|
} \
|
||
|
SDL_LOCK_PENS();\
|
||
|
penvar = SDL_GetPenPtr(instance_id); \
|
||
|
if (!(penvar)) { \
|
||
|
SDL_SetError("Stale SDL_PenID"); \
|
||
|
return (err_return); \
|
||
|
}
|
||
|
|
||
|
static int SDL_GUIDCompare(SDL_GUID lhs, SDL_GUID rhs)
|
||
|
{
|
||
|
return SDL_memcmp(lhs.data, rhs.data, sizeof(lhs.data));
|
||
|
}
|
||
|
|
||
|
static int SDLCALL pen_compare(const void *lhs, const void *rhs)
|
||
|
{
|
||
|
int left_inactive = (((const SDL_Pen *)lhs)->header.flags & SDL_PEN_FLAG_DETACHED);
|
||
|
int right_inactive = (((const SDL_Pen *)rhs)->header.flags & SDL_PEN_FLAG_DETACHED);
|
||
|
if (left_inactive && !right_inactive) {
|
||
|
return 1;
|
||
|
}
|
||
|
if (!left_inactive && right_inactive) {
|
||
|
return -1;
|
||
|
}
|
||
|
return ((const SDL_Pen *)lhs)->header.id - ((const SDL_Pen *)rhs)->header.id;
|
||
|
}
|
||
|
|
||
|
static int SDLCALL pen_header_compare(const void *lhs, const void *rhs)
|
||
|
{
|
||
|
const struct SDL_Pen_header *l = lhs;
|
||
|
const struct SDL_Pen_header *r = rhs;
|
||
|
int l_detached = l->flags & SDL_PEN_FLAG_DETACHED;
|
||
|
int r_detached = r->flags & SDL_PEN_FLAG_DETACHED;
|
||
|
|
||
|
if (l_detached != r_detached) {
|
||
|
if (l_detached) {
|
||
|
return -1;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return l->id - r->id;
|
||
|
}
|
||
|
|
||
|
SDL_Pen *SDL_GetPenPtr(Uint32 instance_id)
|
||
|
{
|
||
|
unsigned int i;
|
||
|
|
||
|
if (!pen_handler.pens) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (pen_handler.sorted) {
|
||
|
struct SDL_Pen_header key;
|
||
|
SDL_Pen *pen;
|
||
|
|
||
|
SDL_zero(key);
|
||
|
key.id = instance_id;
|
||
|
|
||
|
pen = SDL_bsearch(&key, pen_handler.pens,
|
||
|
pen_handler.pens_known, sizeof(SDL_Pen),
|
||
|
pen_header_compare);
|
||
|
if (pen) {
|
||
|
return pen;
|
||
|
}
|
||
|
/* If the pen is not active, fall through */
|
||
|
}
|
||
|
|
||
|
/* fall back to linear search */
|
||
|
for (i = 0; i < pen_handler.pens_known; ++i) {
|
||
|
if (pen_handler.pens[i].header.id == instance_id) {
|
||
|
return &pen_handler.pens[i];
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
SDL_PenID *SDL_GetPens(int *count)
|
||
|
{
|
||
|
int i;
|
||
|
int pens_nr = (int)pen_handler.pens_attached;
|
||
|
SDL_PenID *pens = SDL_calloc(pens_nr + 1, sizeof(SDL_PenID));
|
||
|
if (!pens) { /* OOM */
|
||
|
return pens;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < pens_nr; ++i) {
|
||
|
pens[i] = pen_handler.pens[i].header.id;
|
||
|
}
|
||
|
|
||
|
if (count) {
|
||
|
*count = pens_nr;
|
||
|
}
|
||
|
return pens;
|
||
|
}
|
||
|
|
||
|
SDL_PenID SDL_GetPenFromGUID(SDL_GUID guid)
|
||
|
{
|
||
|
unsigned int i;
|
||
|
/* Must do linear search */
|
||
|
SDL_LOCK_PENS();
|
||
|
for (i = 0; i < pen_handler.pens_known; ++i) {
|
||
|
SDL_Pen *pen = &pen_handler.pens[i];
|
||
|
|
||
|
if (0 == SDL_GUIDCompare(guid, pen->guid)) {
|
||
|
SDL_UNLOCK_PENS();
|
||
|
return pen->header.id;
|
||
|
}
|
||
|
}
|
||
|
SDL_UNLOCK_PENS();
|
||
|
return pen_invalid;
|
||
|
}
|
||
|
|
||
|
SDL_bool SDL_PenConnected(SDL_PenID instance_id)
|
||
|
{
|
||
|
SDL_Pen *pen;
|
||
|
SDL_bool result;
|
||
|
|
||
|
if (instance_id == SDL_PEN_INVALID) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
|
||
|
SDL_LOCK_PENS();
|
||
|
pen = SDL_GetPenPtr(instance_id);
|
||
|
if (!pen) {
|
||
|
SDL_UNLOCK_PENS();
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
result = (pen->header.flags & SDL_PEN_FLAG_DETACHED) ? SDL_FALSE : SDL_TRUE;
|
||
|
SDL_UNLOCK_PENS();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
SDL_GUID SDL_GetPenGUID(SDL_PenID instance_id)
|
||
|
{
|
||
|
SDL_GUID result;
|
||
|
SDL_LOAD_LOCK_PEN(pen, instance_id, pen_guid_zero);
|
||
|
result = pen->guid;
|
||
|
SDL_UNLOCK_PENS();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
const char *SDL_GetPenName(SDL_PenID instance_id)
|
||
|
{
|
||
|
const char *result;
|
||
|
SDL_LOAD_LOCK_PEN(pen, instance_id, NULL);
|
||
|
result = pen->name; /* Allocated separately from the pen table, so it is safe to hand to client code */
|
||
|
SDL_UNLOCK_PENS();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
SDL_PenSubtype SDL_GetPenType(SDL_PenID instance_id)
|
||
|
{
|
||
|
SDL_PenSubtype result;
|
||
|
SDL_LOAD_LOCK_PEN(pen, instance_id, 0u);
|
||
|
result = pen->type;
|
||
|
SDL_UNLOCK_PENS();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Uint32 SDL_GetPenCapabilities(SDL_PenID instance_id, SDL_PenCapabilityInfo *info)
|
||
|
{
|
||
|
Uint32 result;
|
||
|
SDL_LOAD_LOCK_PEN(pen, instance_id, 0u);
|
||
|
if (info) {
|
||
|
*info = pen->info;
|
||
|
}
|
||
|
result = pen->header.flags & PEN_FLAGS_CAPABILITIES;
|
||
|
SDL_UNLOCK_PENS();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Uint32 SDL_GetPenStatus(SDL_PenID instance_id, float *x, float *y, float *axes, size_t num_axes)
|
||
|
{
|
||
|
Uint32 result;
|
||
|
SDL_LOAD_LOCK_PEN(pen, instance_id, 0u);
|
||
|
|
||
|
if (x) {
|
||
|
*x = pen->last.x;
|
||
|
}
|
||
|
if (y) {
|
||
|
*y = pen->last.y;
|
||
|
}
|
||
|
if (axes && num_axes) {
|
||
|
size_t axes_to_copy = SDL_min(num_axes, SDL_PEN_NUM_AXES);
|
||
|
SDL_memcpy(axes, pen->last.axes, sizeof(float) * axes_to_copy);
|
||
|
}
|
||
|
result = pen->last.buttons | (pen->header.flags & (SDL_PEN_INK_MASK | SDL_PEN_ERASER_MASK | SDL_PEN_DOWN_MASK));
|
||
|
SDL_UNLOCK_PENS();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/* Backend functionality */
|
||
|
|
||
|
/* Sort all pens. Only safe during SDL_LOCK_PENS. */
|
||
|
static void pen_sort(void)
|
||
|
{
|
||
|
SDL_qsort(pen_handler.pens,
|
||
|
pen_handler.pens_known,
|
||
|
sizeof(SDL_Pen),
|
||
|
pen_compare);
|
||
|
pen_handler.sorted = SDL_TRUE;
|
||
|
}
|
||
|
|
||
|
SDL_Pen *SDL_PenModifyBegin(Uint32 instance_id)
|
||
|
{
|
||
|
SDL_PenID id = { 0 };
|
||
|
const size_t alloc_growth_constant = 1; /* Expect few pens */
|
||
|
SDL_Pen *pen;
|
||
|
|
||
|
id = instance_id;
|
||
|
|
||
|
if (id == SDL_PEN_INVALID) {
|
||
|
SDL_SetError("Invalid SDL_PenID");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
SDL_LOCK_PENS();
|
||
|
pen = SDL_GetPenPtr(id);
|
||
|
|
||
|
if (!pen) {
|
||
|
if (!pen_handler.pens || pen_handler.pens_known == pen_handler.pens_allocated) {
|
||
|
size_t pens_to_allocate = pen_handler.pens_allocated + alloc_growth_constant;
|
||
|
SDL_Pen *pens;
|
||
|
if (pen_handler.pens) {
|
||
|
pens = SDL_realloc(pen_handler.pens, sizeof(SDL_Pen) * pens_to_allocate);
|
||
|
SDL_memset(pens + pen_handler.pens_known, 0,
|
||
|
sizeof(SDL_Pen) * (pens_to_allocate - pen_handler.pens_allocated));
|
||
|
} else {
|
||
|
pens = SDL_calloc(sizeof(SDL_Pen), pens_to_allocate);
|
||
|
}
|
||
|
pen_handler.pens = pens;
|
||
|
pen_handler.pens_allocated = pens_to_allocate;
|
||
|
}
|
||
|
pen = &pen_handler.pens[pen_handler.pens_known];
|
||
|
pen_handler.pens_known += 1;
|
||
|
|
||
|
/* Default pen initialisation */
|
||
|
pen->header.id = id;
|
||
|
pen->header.flags = SDL_PEN_FLAG_NEW;
|
||
|
pen->info.num_buttons = SDL_PEN_INFO_UNKNOWN;
|
||
|
pen->info.max_tilt = SDL_PEN_INFO_UNKNOWN;
|
||
|
pen->type = SDL_PEN_TYPE_PEN;
|
||
|
pen->name = SDL_calloc(1, SDL_PEN_MAX_NAME); /* Never deallocated */
|
||
|
}
|
||
|
return pen;
|
||
|
}
|
||
|
|
||
|
void SDL_PenModifyAddCapabilities(SDL_Pen *pen, Uint32 capabilities)
|
||
|
{
|
||
|
if (capabilities & SDL_PEN_ERASER_MASK) {
|
||
|
pen->header.flags &= ~SDL_PEN_INK_MASK;
|
||
|
} else if (capabilities & SDL_PEN_INK_MASK) {
|
||
|
pen->header.flags &= ~SDL_PEN_ERASER_MASK;
|
||
|
}
|
||
|
pen->header.flags |= (capabilities & PEN_FLAGS_CAPABILITIES);
|
||
|
}
|
||
|
|
||
|
static void pen_hotplug_attach(SDL_Pen *pen)
|
||
|
{
|
||
|
if (!pen->header.window) {
|
||
|
/* Attach to default window */
|
||
|
const SDL_Mouse *mouse = SDL_GetMouse();
|
||
|
SDL_SendPenWindowEvent(0, pen->header.id, mouse->focus);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void pen_hotplug_detach(SDL_Pen *pen)
|
||
|
{
|
||
|
SDL_SendPenWindowEvent(0, pen->header.id, NULL);
|
||
|
}
|
||
|
|
||
|
void SDL_PenModifyEnd(SDL_Pen *pen, SDL_bool attach)
|
||
|
{
|
||
|
SDL_bool is_new = pen->header.flags & SDL_PEN_FLAG_NEW;
|
||
|
SDL_bool was_attached = !(pen->header.flags & (SDL_PEN_FLAG_DETACHED | SDL_PEN_FLAG_NEW));
|
||
|
SDL_bool broke_sort_order = SDL_FALSE;
|
||
|
|
||
|
if (pen->type == SDL_PEN_TYPE_NONE) {
|
||
|
/* remove pen */
|
||
|
if (!is_new) {
|
||
|
if (!(pen->header.flags & SDL_PEN_FLAG_ERROR)) {
|
||
|
SDL_Log("Error: attempt to remove known pen %lu", (unsigned long) pen->header.id);
|
||
|
pen->header.flags |= SDL_PEN_FLAG_ERROR;
|
||
|
}
|
||
|
|
||
|
/* Treat as detached pen of unknown type instead */
|
||
|
pen->type = SDL_PEN_TYPE_PEN;
|
||
|
attach = SDL_FALSE;
|
||
|
} else {
|
||
|
pen_handler.pens_known -= 1;
|
||
|
SDL_memset(pen, 0, sizeof(SDL_Pen));
|
||
|
SDL_UNLOCK_PENS();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pen->header.flags &= ~(SDL_PEN_FLAG_NEW | SDL_PEN_FLAG_STALE | SDL_PEN_FLAG_DETACHED);
|
||
|
if (attach == SDL_FALSE) {
|
||
|
pen->header.flags |= SDL_PEN_FLAG_DETACHED;
|
||
|
if (was_attached) {
|
||
|
broke_sort_order = SDL_TRUE;
|
||
|
if (!is_new) {
|
||
|
pen_handler.pens_attached -= 1;
|
||
|
}
|
||
|
pen_hotplug_detach(pen);
|
||
|
}
|
||
|
} else if (!was_attached || is_new) {
|
||
|
broke_sort_order = SDL_TRUE;
|
||
|
pen_handler.pens_attached += 1;
|
||
|
pen_hotplug_attach(pen);
|
||
|
}
|
||
|
|
||
|
if (is_new) {
|
||
|
/* default: name */
|
||
|
if (!pen->name[0]) {
|
||
|
SDL_snprintf(pen->name, SDL_PEN_MAX_NAME,
|
||
|
"%s %lu",
|
||
|
pen->type == SDL_PEN_TYPE_ERASER ? "Eraser" : "Pen",
|
||
|
(unsigned long) pen->header.id);
|
||
|
}
|
||
|
|
||
|
/* default: enabled axes */
|
||
|
if (!(pen->header.flags & (SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK))) {
|
||
|
pen->info.max_tilt = 0; /* no tilt => no max_tilt */
|
||
|
}
|
||
|
|
||
|
/* sanity-check GUID */
|
||
|
if (0 == SDL_GUIDCompare(pen->guid, pen_guid_zero)) {
|
||
|
SDL_Log("Error: pen %lu: has GUID 0", (unsigned long) pen->header.id);
|
||
|
}
|
||
|
|
||
|
/* pen or eraser? */
|
||
|
if (pen->type == SDL_PEN_TYPE_ERASER || pen->header.flags & SDL_PEN_ERASER_MASK) {
|
||
|
pen->header.flags = (pen->header.flags & ~SDL_PEN_INK_MASK) | SDL_PEN_ERASER_MASK;
|
||
|
pen->type = SDL_PEN_TYPE_ERASER;
|
||
|
} else {
|
||
|
pen->header.flags = (pen->header.flags & ~SDL_PEN_ERASER_MASK) | SDL_PEN_INK_MASK;
|
||
|
}
|
||
|
|
||
|
broke_sort_order = SDL_TRUE;
|
||
|
}
|
||
|
if (broke_sort_order && pen_handler.sorted) {
|
||
|
/* Maintain sortedness invariant */
|
||
|
pen_sort();
|
||
|
}
|
||
|
SDL_UNLOCK_PENS();
|
||
|
}
|
||
|
|
||
|
void SDL_PenGCMark(void)
|
||
|
{
|
||
|
unsigned int i;
|
||
|
SDL_LOCK_PENS();
|
||
|
for (i = 0; i < pen_handler.pens_known; ++i) {
|
||
|
SDL_Pen *pen = &pen_handler.pens[i];
|
||
|
pen->header.flags |= SDL_PEN_FLAG_STALE;
|
||
|
}
|
||
|
pen_handler.sorted = SDL_FALSE;
|
||
|
SDL_UNLOCK_PENS();
|
||
|
}
|
||
|
|
||
|
void SDL_PenGCSweep(void *context, void (*free_deviceinfo)(Uint32, void *, void *))
|
||
|
{
|
||
|
unsigned int i;
|
||
|
pen_handler.pens_attached = 0;
|
||
|
|
||
|
SDL_LOCK_PENS();
|
||
|
/* We don't actually free the SDL_Pen entries, so that we can still answer queries about
|
||
|
formerly active SDL_PenIDs later. */
|
||
|
for (i = 0; i < pen_handler.pens_known; ++i) {
|
||
|
SDL_Pen *pen = &pen_handler.pens[i];
|
||
|
|
||
|
if (pen->header.flags & SDL_PEN_FLAG_STALE) {
|
||
|
pen->header.flags |= SDL_PEN_FLAG_DETACHED;
|
||
|
pen_hotplug_detach(pen);
|
||
|
if (pen->deviceinfo) {
|
||
|
free_deviceinfo(pen->header.id, pen->deviceinfo, context);
|
||
|
pen->deviceinfo = NULL;
|
||
|
}
|
||
|
} else {
|
||
|
pen_handler.pens_attached += 1;
|
||
|
}
|
||
|
|
||
|
pen->header.flags &= ~SDL_PEN_FLAG_STALE;
|
||
|
}
|
||
|
pen_sort();
|
||
|
/* We could test for changes in the above and send a hotplugging event here */
|
||
|
SDL_UNLOCK_PENS();
|
||
|
}
|
||
|
|
||
|
static void pen_relative_coordinates(SDL_Window *window, SDL_bool window_relative, float *x, float *y)
|
||
|
{
|
||
|
int win_x, win_y;
|
||
|
|
||
|
if (window_relative) {
|
||
|
return;
|
||
|
}
|
||
|
SDL_GetWindowPosition(window, &win_x, &win_y);
|
||
|
*x -= win_x;
|
||
|
*y -= win_y;
|
||
|
}
|
||
|
|
||
|
/* Initialises timestamp, windowID, which, pen_state, x, y, and axes */
|
||
|
static void event_setup(const SDL_Pen *pen, const SDL_Window *window, Uint64 timestamp, const SDL_PenStatusInfo *status, SDL_Event *event)
|
||
|
{
|
||
|
Uint16 last_buttons = pen->last.buttons;
|
||
|
|
||
|
if (timestamp == 0) {
|
||
|
/* Generate timestamp ourselves, if needed, so that we report the same timestamp
|
||
|
for the pen event and for any emulated mouse event */
|
||
|
timestamp = SDL_GetTicksNS();
|
||
|
}
|
||
|
|
||
|
/* This code assumes that all of the SDL_Pen*Event structures have the same layout for these fields.
|
||
|
* This is checked by testautomation_pen.c, pen_memoryLayout(). */
|
||
|
event->pmotion.timestamp = timestamp;
|
||
|
event->pmotion.windowID = window ? window->id : 0;
|
||
|
event->pmotion.which = pen->header.id;
|
||
|
event->pmotion.pen_state = last_buttons | PEN_GET_PUBLIC_STATUS_MASK(pen);
|
||
|
event->pmotion.x = status->x;
|
||
|
event->pmotion.y = status->y;
|
||
|
SDL_memcpy(event->pmotion.axes, status->axes, SDL_PEN_NUM_AXES * sizeof(float));
|
||
|
}
|
||
|
|
||
|
|
||
|
int SDL_SendPenMotion(Uint64 timestamp,
|
||
|
SDL_PenID instance_id,
|
||
|
SDL_bool window_relative,
|
||
|
const SDL_PenStatusInfo *status)
|
||
|
{
|
||
|
const SDL_Mouse *mouse = SDL_GetMouse();
|
||
|
int i;
|
||
|
SDL_Pen *pen = SDL_GetPenPtr(instance_id);
|
||
|
SDL_Event event;
|
||
|
SDL_bool posted = SDL_FALSE;
|
||
|
float x = status->x;
|
||
|
float y = status->y;
|
||
|
float last_x = pen->last.x;
|
||
|
float last_y = pen->last.y;
|
||
|
/* Suppress mouse updates for axis changes or sub-pixel movement: */
|
||
|
SDL_bool send_mouse_update;
|
||
|
SDL_bool axes_changed = SDL_FALSE;
|
||
|
SDL_Window *window;
|
||
|
|
||
|
if (!pen) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
window = pen->header.window;
|
||
|
if (!window) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
|
||
|
pen_relative_coordinates(window, window_relative, &x, &y);
|
||
|
|
||
|
/* Check if the event actually modifies any cached axis or location, update as neeed */
|
||
|
if (x != last_x || y != last_y) {
|
||
|
axes_changed = SDL_TRUE;
|
||
|
pen->last.x = status->x;
|
||
|
pen->last.y = status->y;
|
||
|
}
|
||
|
for (i = 0; i < SDL_PEN_NUM_AXES; ++i) {
|
||
|
if ((pen->header.flags & SDL_PEN_AXIS_CAPABILITY(i)) && (status->axes[i] != pen->last.axes[i])) {
|
||
|
axes_changed = SDL_TRUE;
|
||
|
pen->last.axes[i] = status->axes[i];
|
||
|
}
|
||
|
}
|
||
|
if (!axes_changed) {
|
||
|
/* No-op event */
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
|
||
|
send_mouse_update = (x != last_x) || (y != last_y);
|
||
|
|
||
|
if (!(SDL_MousePositionInWindow(window, mouse->mouseID, x, y))) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
|
||
|
if (SDL_EventEnabled(SDL_EVENT_PEN_MOTION)) {
|
||
|
event_setup(pen, window, timestamp, status, &event);
|
||
|
event.pmotion.type = SDL_EVENT_PEN_MOTION;
|
||
|
|
||
|
posted = SDL_PushEvent(&event) > 0;
|
||
|
|
||
|
if (!posted) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (send_mouse_update) {
|
||
|
switch (pen_mouse_emulation_mode) {
|
||
|
case PEN_MOUSE_EMULATE:
|
||
|
return (SDL_SendMouseMotion(0, window, SDL_PEN_MOUSEID, 0, x, y)) || posted;
|
||
|
|
||
|
case PEN_MOUSE_STATELESS:
|
||
|
/* Report mouse event but don't update mouse state */
|
||
|
if (SDL_EventEnabled(SDL_EVENT_MOUSE_MOTION)) {
|
||
|
event.motion.windowID = window->id;
|
||
|
event.motion.timestamp = timestamp;
|
||
|
event.motion.which = SDL_PEN_MOUSEID;
|
||
|
event.motion.type = SDL_EVENT_MOUSE_MOTION;
|
||
|
event.motion.state = pen->last.buttons | PEN_GET_PUBLIC_STATUS_MASK(pen);
|
||
|
event.motion.x = x;
|
||
|
event.motion.y = y;
|
||
|
event.motion.xrel = last_x - x;
|
||
|
event.motion.yrel = last_y - y;
|
||
|
return (SDL_PushEvent(&event) > 0) || posted;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return posted;
|
||
|
}
|
||
|
|
||
|
int SDL_SendPenTipEvent(Uint64 timestamp, SDL_PenID instance_id, Uint8 state)
|
||
|
{
|
||
|
SDL_Mouse *mouse = SDL_GetMouse();
|
||
|
SDL_Pen *pen = SDL_GetPenPtr(instance_id);
|
||
|
SDL_Event event;
|
||
|
SDL_bool posted = SDL_FALSE;
|
||
|
SDL_PenStatusInfo *last = &pen->last;
|
||
|
Uint8 mouse_button = SDL_BUTTON_LEFT;
|
||
|
SDL_Window *window;
|
||
|
|
||
|
if (!pen) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
window = pen->header.window;
|
||
|
|
||
|
if ((state == SDL_PRESSED) && !(window && SDL_MousePositionInWindow(window, mouse->mouseID, last->x, last->y))) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
|
||
|
if (state == SDL_PRESSED) {
|
||
|
event.pbutton.type = SDL_EVENT_PEN_DOWN;
|
||
|
pen->header.flags |= SDL_PEN_DOWN_MASK;
|
||
|
} else {
|
||
|
event.pbutton.type = SDL_EVENT_PEN_UP;
|
||
|
pen->header.flags &= ~SDL_PEN_DOWN_MASK;
|
||
|
}
|
||
|
|
||
|
if (SDL_EventEnabled(event.ptip.type)) {
|
||
|
event_setup(pen, window, timestamp, &pen->last, &event);
|
||
|
|
||
|
/* Used as eraser? Report eraser event, otherwise ink event */
|
||
|
event.ptip.tip = (pen->header.flags & SDL_PEN_ERASER_MASK) ? SDL_PEN_TIP_ERASER : SDL_PEN_TIP_INK;
|
||
|
event.ptip.state = state == SDL_PRESSED ? SDL_PRESSED : SDL_RELEASED;
|
||
|
|
||
|
posted = SDL_PushEvent(&event) > 0;
|
||
|
|
||
|
if (!posted) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Mouse emulation */
|
||
|
if (pen_delay_mouse_button_mode) {
|
||
|
/* Send button events when pen touches / leaves surface */
|
||
|
mouse_button = pen->last_mouse_button;
|
||
|
if (mouse_button == 0) {
|
||
|
mouse_button = SDL_BUTTON_LEFT; /* No current button? Instead report left mouse button */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch (pen_mouse_emulation_mode) {
|
||
|
case PEN_MOUSE_EMULATE:
|
||
|
return (SDL_SendMouseButton(timestamp, window, SDL_PEN_MOUSEID, state, mouse_button)) || posted;
|
||
|
|
||
|
case PEN_MOUSE_STATELESS:
|
||
|
/* Report mouse event without updating mouse state */
|
||
|
event.button.type = state == SDL_PRESSED ? SDL_EVENT_MOUSE_BUTTON_DOWN : SDL_EVENT_MOUSE_BUTTON_UP;
|
||
|
if (SDL_EventEnabled(event.button.type)) {
|
||
|
event.button.windowID = window ? window->id : 0;
|
||
|
event.button.timestamp = timestamp;
|
||
|
event.button.which = SDL_PEN_MOUSEID;
|
||
|
|
||
|
event.button.state = state;
|
||
|
event.button.button = mouse_button;
|
||
|
event.button.clicks = 1;
|
||
|
event.button.x = last->x;
|
||
|
event.button.y = last->y;
|
||
|
return (SDL_PushEvent(&event) > 0) || posted;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return posted;
|
||
|
}
|
||
|
|
||
|
int SDL_SendPenButton(Uint64 timestamp,
|
||
|
SDL_PenID instance_id,
|
||
|
Uint8 state, Uint8 button)
|
||
|
{
|
||
|
SDL_Mouse *mouse = SDL_GetMouse();
|
||
|
SDL_Pen *pen = SDL_GetPenPtr(instance_id);
|
||
|
SDL_Event event;
|
||
|
SDL_bool posted = SDL_FALSE;
|
||
|
SDL_PenStatusInfo *last = &pen->last;
|
||
|
Uint8 mouse_button = button + 1; /* For mouse emulation, PEN_DOWN counts as button 1, so the first actual button is mouse button 2 */
|
||
|
SDL_Window *window;
|
||
|
|
||
|
if (!pen) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
window = pen->header.window;
|
||
|
|
||
|
if ((state == SDL_PRESSED) && !(window && SDL_MousePositionInWindow(window, mouse->mouseID, last->x, last->y))) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
|
||
|
if (state == SDL_PRESSED) {
|
||
|
event.pbutton.type = SDL_EVENT_PEN_BUTTON_DOWN;
|
||
|
pen->last.buttons |= (1 << (button - 1));
|
||
|
} else {
|
||
|
event.pbutton.type = SDL_EVENT_PEN_BUTTON_UP;
|
||
|
pen->last.buttons &= ~(1 << (button - 1));
|
||
|
}
|
||
|
|
||
|
if (SDL_EventEnabled(event.pbutton.type)) {
|
||
|
event_setup(pen, window, timestamp, &pen->last, &event);
|
||
|
|
||
|
event.pbutton.button = button;
|
||
|
event.pbutton.state = state == SDL_PRESSED ? SDL_PRESSED : SDL_RELEASED;
|
||
|
|
||
|
posted = SDL_PushEvent(&event) > 0;
|
||
|
|
||
|
if (!posted) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Mouse emulation */
|
||
|
if (pen_delay_mouse_button_mode) {
|
||
|
/* Can only change active mouse button while not touching the surface */
|
||
|
if (!(pen->header.flags & SDL_PEN_DOWN_MASK)) {
|
||
|
if (state == SDL_RELEASED) {
|
||
|
pen->last_mouse_button = 0;
|
||
|
} else {
|
||
|
pen->last_mouse_button = mouse_button;
|
||
|
}
|
||
|
}
|
||
|
/* Defer emulation event */
|
||
|
return SDL_TRUE;
|
||
|
}
|
||
|
|
||
|
switch (pen_mouse_emulation_mode) {
|
||
|
case PEN_MOUSE_EMULATE:
|
||
|
return (SDL_SendMouseButton(timestamp, window, SDL_PEN_MOUSEID, state, mouse_button)) || posted;
|
||
|
|
||
|
case PEN_MOUSE_STATELESS:
|
||
|
/* Report mouse event without updating mouse state */
|
||
|
event.button.type = state == SDL_PRESSED ? SDL_EVENT_MOUSE_BUTTON_DOWN : SDL_EVENT_MOUSE_BUTTON_UP;
|
||
|
if (SDL_EventEnabled(event.button.type)) {
|
||
|
event.button.windowID = window ? window->id : 0;
|
||
|
event.button.timestamp = timestamp;
|
||
|
event.button.which = SDL_PEN_MOUSEID;
|
||
|
|
||
|
event.button.state = state;
|
||
|
event.button.button = mouse_button;
|
||
|
event.button.clicks = 1;
|
||
|
event.button.x = last->x;
|
||
|
event.button.y = last->y;
|
||
|
return (SDL_PushEvent(&event) > 0) || posted;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return posted;
|
||
|
}
|
||
|
|
||
|
int SDL_SendPenWindowEvent(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window)
|
||
|
{
|
||
|
SDL_EventType event_id = window ? SDL_EVENT_WINDOW_PEN_ENTER : SDL_EVENT_WINDOW_PEN_LEAVE;
|
||
|
SDL_Event event = { 0 };
|
||
|
SDL_Pen *pen = SDL_GetPenPtr(instance_id);
|
||
|
SDL_bool posted;
|
||
|
|
||
|
if (!pen) {
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
|
||
|
if (pen->header.window == window) { /* (TRIVIAL-EVENT) Nothing new to report */
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
|
||
|
if (timestamp == 0) {
|
||
|
/* Generate timestamp ourselves, if needed, so that we report the same timestamp
|
||
|
for the pen event and for any emulated mouse event */
|
||
|
timestamp = SDL_GetTicksNS();
|
||
|
}
|
||
|
|
||
|
event.window.type = event_id;
|
||
|
/* If window == NULL and not (TRIVIAL-EVENT), then pen->header.window != NULL */
|
||
|
event.window.timestamp = timestamp;
|
||
|
event.window.windowID = window ? window->id : pen->header.window->id;
|
||
|
event.window.data1 = instance_id;
|
||
|
posted = (SDL_PushEvent(&event) > 0);
|
||
|
|
||
|
/* Update after assembling event */
|
||
|
pen->header.window = window;
|
||
|
|
||
|
switch (pen_mouse_emulation_mode) {
|
||
|
case PEN_MOUSE_EMULATE:
|
||
|
SDL_SetMouseFocus(event_id == SDL_EVENT_WINDOW_PEN_ENTER ? window : NULL);
|
||
|
break;
|
||
|
|
||
|
case PEN_MOUSE_STATELESS:
|
||
|
/* Send event without going through mouse API */
|
||
|
if (event_id == SDL_EVENT_WINDOW_PEN_ENTER) {
|
||
|
event.window.type = SDL_EVENT_WINDOW_MOUSE_ENTER;
|
||
|
} else {
|
||
|
event.window.type = SDL_EVENT_WINDOW_MOUSE_LEAVE;
|
||
|
}
|
||
|
posted = posted || (SDL_PushEvent(&event) > 0);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return posted;
|
||
|
}
|
||
|
|
||
|
static void SDLCALL SDL_PenUpdateHint(void *userdata, const char *name, const char *oldvalue, const char *newvalue)
|
||
|
{
|
||
|
int *var = userdata;
|
||
|
if (newvalue == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (0 == SDL_strcmp("2", newvalue)) {
|
||
|
*var = 2;
|
||
|
} else if (0 == SDL_strcmp("1", newvalue)) {
|
||
|
*var = 1;
|
||
|
} else if (0 == SDL_strcmp("0", newvalue)) {
|
||
|
*var = 0;
|
||
|
} else {
|
||
|
SDL_Log("Unexpected value for pen hint: '%s'", newvalue);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SDL_PenInit(void)
|
||
|
{
|
||
|
#if (SDL_PEN_DEBUG_NOID | SDL_PEN_DEBUG_NONWACOM | SDL_PEN_DEBUG_UNKNOWN_WACOM)
|
||
|
printf("[pen] Debugging enabled: noid=%d nonwacom=%d unknown-wacom=%d\n",
|
||
|
SDL_PEN_DEBUG_NOID, SDL_PEN_DEBUG_NONWACOM, SDL_PEN_DEBUG_UNKNOWN_WACOM);
|
||
|
#endif
|
||
|
SDL_AddHintCallback(SDL_HINT_PEN_NOT_MOUSE,
|
||
|
SDL_PenUpdateHint, &pen_mouse_emulation_mode);
|
||
|
|
||
|
SDL_AddHintCallback(SDL_HINT_PEN_DELAY_MOUSE_BUTTON,
|
||
|
SDL_PenUpdateHint, &pen_delay_mouse_button_mode);
|
||
|
#ifndef SDL_THREADS_DISABLED
|
||
|
SDL_pen_access_lock = SDL_CreateMutex();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void SDL_PenQuit(void)
|
||
|
{
|
||
|
SDL_DelHintCallback(SDL_HINT_PEN_NOT_MOUSE,
|
||
|
SDL_PenUpdateHint, &pen_mouse_emulation_mode);
|
||
|
|
||
|
SDL_DelHintCallback(SDL_HINT_PEN_DELAY_MOUSE_BUTTON,
|
||
|
SDL_PenUpdateHint, &pen_delay_mouse_button_mode);
|
||
|
#ifndef SDL_THREADS_DISABLED
|
||
|
SDL_DestroyMutex(SDL_pen_access_lock);
|
||
|
SDL_pen_access_lock = NULL;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
SDL_bool SDL_PenPerformHitTest(void)
|
||
|
{
|
||
|
return pen_mouse_emulation_mode == PEN_MOUSE_EMULATE;
|
||
|
}
|
||
|
|
||
|
/* Vendor-specific bits */
|
||
|
|
||
|
/* Default pen names */
|
||
|
#define PEN_NAME_AES 0
|
||
|
#define PEN_NAME_ART 1
|
||
|
#define PEN_NAME_AIRBRUSH 2
|
||
|
#define PEN_NAME_GENERAL 3
|
||
|
#define PEN_NAME_GRIP 4
|
||
|
#define PEN_NAME_INKING 5
|
||
|
#define PEN_NAME_PRO 6
|
||
|
#define PEN_NAME_PRO2 7
|
||
|
#define PEN_NAME_PRO3 8
|
||
|
#define PEN_NAME_PRO3D 9
|
||
|
#define PEN_NAME_PRO_SLIM 10
|
||
|
#define PEN_NAME_STROKE 11
|
||
|
|
||
|
#define PEN_NAME_LAST PEN_NAME_STROKE
|
||
|
#define PEN_NUM_NAMES (PEN_NAME_LAST + 1)
|
||
|
|
||
|
static const char *default_pen_names[] = {
|
||
|
/* PEN_NAME_AES */
|
||
|
"AES Pen",
|
||
|
/* PEN_NAME_ART */
|
||
|
"Art Pen",
|
||
|
/* PEN_NAME_AIRBRUSH */
|
||
|
"Airbrush Pen",
|
||
|
/* PEN_NAME_GENERAL */
|
||
|
"Pen",
|
||
|
/* PEN_NAME_GRIP */
|
||
|
"Grip Pen",
|
||
|
/* PEN_NAME_INKING */
|
||
|
"Inking Pen",
|
||
|
/* PEN_NAME_PRO */
|
||
|
"Pro Pen",
|
||
|
/* PEN_NAME_PRO2 */
|
||
|
"Pro Pen 2",
|
||
|
/* PEN_NAME_PRO3 */
|
||
|
"Pro Pen 3",
|
||
|
/* PEN_NAME_PRO3D */
|
||
|
"Pro Pen 3D",
|
||
|
/* PEN_NAME_PRO_SLIM */
|
||
|
"Pro Pen Slim",
|
||
|
/* PEN_NAME_STROKE */
|
||
|
"Stroke Pen"
|
||
|
};
|
||
|
SDL_COMPILE_TIME_ASSERT(default_pen_names, SDL_arraysize(default_pen_names) == PEN_NUM_NAMES);
|
||
|
|
||
|
#define PEN_SPEC_TYPE_SHIFT 0
|
||
|
#define PEN_SPEC_TYPE_MASK 0x0000000fu
|
||
|
#define PEN_SPEC_BUTTONS_SHIFT 4
|
||
|
#define PEN_SPEC_BUTTONS_MASK 0x000000f0u
|
||
|
#define PEN_SPEC_NAME_SHIFT 8
|
||
|
#define PEN_SPEC_NAME_MASK 0x00000f00u
|
||
|
#define PEN_SPEC_AXES_SHIFT 0
|
||
|
#define PEN_SPEC_AXES_MASK 0xffff0000u
|
||
|
|
||
|
#define PEN_WACOM_ID_INVALID 0xffffffffu /* Generic "invalid ID" marker */
|
||
|
|
||
|
#define PEN_SPEC(name, buttons, type, axes) (0 | (PEN_SPEC_NAME_MASK & ((name) << PEN_SPEC_NAME_SHIFT)) | (PEN_SPEC_BUTTONS_MASK & ((buttons) << PEN_SPEC_BUTTONS_SHIFT)) | (PEN_SPEC_TYPE_MASK & ((type) << PEN_SPEC_TYPE_SHIFT)) | (PEN_SPEC_AXES_MASK & ((axes) << PEN_SPEC_AXES_SHIFT)))
|
||
|
|
||
|
/* Returns a suitable pen name string from default_pen_names on success, otherwise NULL. */
|
||
|
static const char *pen_wacom_identify_tool(Uint32 requested_wacom_id, int *num_buttons, int *tool_type, int *axes)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/* List of known Wacom pens, extracted from libwacom.stylus and wacom_wac.c in the Linux kernel.
|
||
|
Could be complemented by dlopen()ing libwacom, in the future (if new pens get added). */
|
||
|
struct
|
||
|
{
|
||
|
/* Compress properties to 8 bytes per device in order to keep memory cost well below 1k.
|
||
|
Could be compressed further with more complex code. */
|
||
|
Uint32 wacom_id; /* Must be != PEN_WACOM_ID_INVALID */
|
||
|
Uint32 properties;
|
||
|
} tools[] = {
|
||
|
{ 0x0001, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0011, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0019, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0021, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0031, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0039, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0049, PEN_SPEC(PEN_NAME_GENERAL, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0071, PEN_SPEC(PEN_NAME_GENERAL, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0200, PEN_SPEC(PEN_NAME_PRO3, 3, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0221, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0231, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0271, PEN_SPEC(PEN_NAME_GENERAL, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0421, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0431, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0621, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0631, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK) },
|
||
|
{ 0x0801, PEN_SPEC(PEN_NAME_INKING, 0, SDL_PEN_TYPE_PENCIL, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0802, PEN_SPEC(PEN_NAME_GRIP, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0804, PEN_SPEC(PEN_NAME_ART, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK | SDL_PEN_AXIS_ROTATION_MASK) },
|
||
|
{ 0x080a, PEN_SPEC(PEN_NAME_GRIP, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x080c, PEN_SPEC(PEN_NAME_ART, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0812, PEN_SPEC(PEN_NAME_INKING, 0, SDL_PEN_TYPE_PENCIL, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0813, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x081b, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0822, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0823, PEN_SPEC(PEN_NAME_GRIP, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x082a, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x082b, PEN_SPEC(PEN_NAME_GRIP, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0832, PEN_SPEC(PEN_NAME_STROKE, 0, SDL_PEN_TYPE_BRUSH, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0842, PEN_SPEC(PEN_NAME_PRO2, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x084a, PEN_SPEC(PEN_NAME_PRO2, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0852, PEN_SPEC(PEN_NAME_GRIP, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x085a, PEN_SPEC(PEN_NAME_GRIP, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0862, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0885, PEN_SPEC(PEN_NAME_ART, 0, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK | SDL_PEN_AXIS_ROTATION_MASK) },
|
||
|
{ 0x08e2, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0902, PEN_SPEC(PEN_NAME_AIRBRUSH, 1, SDL_PEN_TYPE_AIRBRUSH, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK | SDL_PEN_AXIS_SLIDER_MASK) },
|
||
|
{ 0x090a, PEN_SPEC(PEN_NAME_AIRBRUSH, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0912, PEN_SPEC(PEN_NAME_AIRBRUSH, 1, SDL_PEN_TYPE_AIRBRUSH, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK | SDL_PEN_AXIS_SLIDER_MASK) },
|
||
|
{ 0x0913, PEN_SPEC(PEN_NAME_AIRBRUSH, 1, SDL_PEN_TYPE_AIRBRUSH, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x091a, PEN_SPEC(PEN_NAME_AIRBRUSH, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x091b, PEN_SPEC(PEN_NAME_AIRBRUSH, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x0d12, PEN_SPEC(PEN_NAME_AIRBRUSH, 1, SDL_PEN_TYPE_AIRBRUSH, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK | SDL_PEN_AXIS_SLIDER_MASK) },
|
||
|
{ 0x0d1a, PEN_SPEC(PEN_NAME_AIRBRUSH, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x8051, PEN_SPEC(PEN_NAME_AES, 0, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK) },
|
||
|
{ 0x805b, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK) },
|
||
|
{ 0x806b, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK) },
|
||
|
{ 0x807b, PEN_SPEC(PEN_NAME_GENERAL, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK) },
|
||
|
{ 0x826b, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK) },
|
||
|
{ 0x846b, PEN_SPEC(PEN_NAME_AES, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK) },
|
||
|
{ 0x2802, PEN_SPEC(PEN_NAME_INKING, 0, SDL_PEN_TYPE_PENCIL, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x4200, PEN_SPEC(PEN_NAME_PRO3, 3, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x4802, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x480a, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x8842, PEN_SPEC(PEN_NAME_PRO3D, 3, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x10802, PEN_SPEC(PEN_NAME_GRIP, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x10804, PEN_SPEC(PEN_NAME_ART, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK | SDL_PEN_AXIS_ROTATION_MASK) },
|
||
|
{ 0x1080a, PEN_SPEC(PEN_NAME_GRIP, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x1080c, PEN_SPEC(PEN_NAME_ART, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x10842, PEN_SPEC(PEN_NAME_PRO_SLIM, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x1084a, PEN_SPEC(PEN_NAME_PRO_SLIM, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x10902, PEN_SPEC(PEN_NAME_AIRBRUSH, 1, SDL_PEN_TYPE_AIRBRUSH, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK | SDL_PEN_AXIS_SLIDER_MASK) },
|
||
|
{ 0x1090a, PEN_SPEC(PEN_NAME_AIRBRUSH, 1, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x12802, PEN_SPEC(PEN_NAME_INKING, 0, SDL_PEN_TYPE_PENCIL, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x14802, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x1480a, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x16802, PEN_SPEC(PEN_NAME_PRO, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x1680a, PEN_SPEC(PEN_NAME_PRO, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x18802, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_PEN, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0x1880a, PEN_SPEC(PEN_NAME_GENERAL, 2, SDL_PEN_TYPE_ERASER, SDL_PEN_AXIS_PRESSURE_MASK | SDL_PEN_AXIS_XTILT_MASK | SDL_PEN_AXIS_YTILT_MASK | SDL_PEN_AXIS_DISTANCE_MASK) },
|
||
|
{ 0, 0 }
|
||
|
};
|
||
|
|
||
|
/* The list of pens is sorted, so we could do binary search, but this call should be pretty rare. */
|
||
|
for (i = 0; tools[i].wacom_id; ++i) {
|
||
|
if (tools[i].wacom_id == requested_wacom_id) {
|
||
|
Uint32 properties = tools[i].properties;
|
||
|
int name_index = (properties & PEN_SPEC_NAME_MASK) >> PEN_SPEC_NAME_SHIFT;
|
||
|
|
||
|
*num_buttons = (properties & PEN_SPEC_BUTTONS_MASK) >> PEN_SPEC_BUTTONS_SHIFT;
|
||
|
*tool_type = (properties & PEN_SPEC_TYPE_MASK) >> PEN_SPEC_TYPE_SHIFT;
|
||
|
*axes = (properties & PEN_SPEC_AXES_MASK) >> PEN_SPEC_AXES_SHIFT;
|
||
|
|
||
|
return default_pen_names[name_index];
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void SDL_PenUpdateGUIDForGeneric(SDL_GUID *guid, Uint32 upper, Uint32 lower)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < 4; ++i) {
|
||
|
guid->data[8 + i] = (lower >> (i * 8)) & 0xff;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 4; ++i) {
|
||
|
guid->data[12 + i] = (upper >> (i * 8)) & 0xff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SDL_PenUpdateGUIDForType(SDL_GUID *guid, SDL_PenSubtype pentype)
|
||
|
{
|
||
|
guid->data[7] = pentype;
|
||
|
}
|
||
|
|
||
|
void SDL_PenUpdateGUIDForWacom(SDL_GUID *guid, Uint32 wacom_devicetype_id, Uint32 wacom_serial_id)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < 4; ++i) {
|
||
|
guid->data[0 + i] = (wacom_serial_id >> (i * 8)) & 0xff;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 3; ++i) { /* 24 bit values */
|
||
|
guid->data[4 + i] = (wacom_devicetype_id >> (i * 8)) & 0xff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int SDL_PenModifyForWacomID(SDL_Pen *pen, Uint32 wacom_devicetype_id, Uint32 *axis_flags)
|
||
|
{
|
||
|
const char *name = NULL;
|
||
|
int num_buttons = 0;
|
||
|
int tool_type = 0;
|
||
|
int axes = 0;
|
||
|
|
||
|
#if SDL_PEN_DEBUG_UNKNOWN_WACOM
|
||
|
wacom_devicetype_id = PEN_WACOM_ID_INVALID; /* force detection to fail */
|
||
|
#endif
|
||
|
|
||
|
#if defined(__LINUX__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||
|
/* According to Ping Cheng, the curent Wacom for Linux maintainer, device IDs on Linux
|
||
|
squeeze a "0" nibble after the 3rd (least significant) nibble.
|
||
|
This may also affect the *BSDs, so they are heuristically included here.
|
||
|
On those platforms, we first try the "patched" ID: */
|
||
|
if (0 == (wacom_devicetype_id & 0x0000f000u)) {
|
||
|
const Uint32 lower_mask = 0xfffu;
|
||
|
int wacom_devicetype_alt_id = ((wacom_devicetype_id & ~lower_mask) >> 4) | (wacom_devicetype_id & lower_mask);
|
||
|
|
||
|
name = pen_wacom_identify_tool(wacom_devicetype_alt_id, &num_buttons, &tool_type, &axes);
|
||
|
if (name) {
|
||
|
wacom_devicetype_id = wacom_devicetype_alt_id;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
if (name == NULL) {
|
||
|
name = pen_wacom_identify_tool(wacom_devicetype_id, &num_buttons, &tool_type, &axes);
|
||
|
}
|
||
|
|
||
|
if (!name) {
|
||
|
*axis_flags = 0;
|
||
|
return SDL_FALSE;
|
||
|
}
|
||
|
|
||
|
*axis_flags = axes;
|
||
|
|
||
|
/* Override defaults */
|
||
|
if (pen->info.num_buttons == SDL_PEN_INFO_UNKNOWN) {
|
||
|
pen->info.num_buttons = (Sint8)SDL_min(num_buttons, SDL_MAX_SINT8);
|
||
|
}
|
||
|
if (pen->type == SDL_PEN_TYPE_PEN) {
|
||
|
pen->type = (SDL_PenSubtype)tool_type;
|
||
|
}
|
||
|
if (pen->info.max_tilt == SDL_PEN_INFO_UNKNOWN) {
|
||
|
/* supposedly: 64 degrees left, 63 right, as reported by the Wacom X11 driver */
|
||
|
pen->info.max_tilt = 64.0f;
|
||
|
}
|
||
|
pen->info.wacom_id = wacom_devicetype_id;
|
||
|
if (0 == pen->name[0]) {
|
||
|
SDL_snprintf(pen->name, SDL_PEN_MAX_NAME,
|
||
|
"Wacom %s%s", name, (tool_type == SDL_PEN_TYPE_ERASER) ? " Eraser" : "");
|
||
|
}
|
||
|
return SDL_TRUE;
|
||
|
}
|