mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-06-01 01:17:40 +00: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
29 changed files with 1429 additions and 5146 deletions
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;
|
||||
SDL_PenID pen;
|
||||
Uint8 r, g, b;
|
||||
float axes[SDL_PEN_NUM_AXES];
|
||||
float x;
|
||||
float y;
|
||||
Uint32 buttons;
|
||||
SDL_bool eraser;
|
||||
SDL_bool touching;
|
||||
struct Pen *next;
|
||||
} Pen;
|
||||
|
||||
if (!renderer) {
|
||||
return;
|
||||
}
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
static SDLTest_CommonState *state = NULL;
|
||||
static SDL_Texture *white_pixel = NULL;
|
||||
static Pen pens;
|
||||
|
||||
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 SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
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);
|
||||
}
|
||||
SDL_srand(0);
|
||||
|
||||
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];
|
||||
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;
|
||||
|
||||
SDL_GUIDToString(guid, guid_str, sizeof(guid_str));
|
||||
|
||||
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)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
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 1;
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
state->window_title = "Pressure-Sensitive Pen Test";
|
||||
state->window_w = WIDTH;
|
||||
state->window_h = HEIGHT;
|
||||
state->skip_renderer = SDL_FALSE;
|
||||
/* Enable standard application logging */
|
||||
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
||||
|
||||
if (!SDLTest_CommonDefaultArgs(state, argc, argv) || !SDLTest_CommonInit(state)) {
|
||||
SDLTest_CommonQuit(state);
|
||||
return 1;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
while (!quitting) {
|
||||
loop();
|
||||
state->num_windows = 1;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
SDLTest_CommonQuit(state);
|
||||
return 0;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue