Add opt-in support for trackpad touch events. Add touch device types.

- Add new touch device type enum ("touchscreen", "touchpad", "touchpadrelative"). Coordinates for touchscreens are window-relative DPI-scaled pixels, and coordinates for touchpads are normalized [0, 1] values.
- Add new devicetype enum and ismouse boolean params to touch callbacks.
- Add love.touch.getDeviceType(touchid), love.touch.isMouse(touchid), and optional device type filter param for love.touch.getTouches.
- Add t.trackpadtouch boolean (defaults to false) to love.conf. Trackpad touch events will not be generated unless it's enabled.

Currently the trackpadtouch setting only works on macOS.

Resolves #1633.
Resolves #2093.
This commit is contained in:
Sasha Szpakowski
2024-12-25 13:15:17 -04:00
parent aa93494f54
commit 639580cbd1
11 changed files with 216 additions and 56 deletions
+1
View File
@@ -1182,6 +1182,7 @@ target_link_libraries(love_timer PUBLIC
#
add_library(love_touch_root STATIC
src/modules/touch/Touch.cpp
src/modules/touch/Touch.h
src/modules/touch/wrap_Touch.cpp
src/modules/touch/wrap_Touch.h
@@ -51,6 +51,8 @@
217DFC111D9F6D490055D849 /* usocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBD51D9F6D490055D849 /* usocket.c */; };
217DFC121D9F6D490055D849 /* usocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 217DFBD61D9F6D490055D849 /* usocket.h */; };
D923E7D3296B85B9002FF1B3 /* harfbuzz.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = D923E7D2296B85B9002FF1B3 /* harfbuzz.xcframework */; };
D93660F82D1C727C00C0EC4B /* Touch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D93660F72D1C727C00C0EC4B /* Touch.cpp */; };
D93660F92D1C727C00C0EC4B /* Touch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D93660F72D1C727C00C0EC4B /* Touch.cpp */; };
D943E58E2A24D56000D80361 /* PhysfsIo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D943E58C2A24D56000D80361 /* PhysfsIo.cpp */; };
D943E58F2A24D56000D80361 /* PhysfsIo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D943E58C2A24D56000D80361 /* PhysfsIo.cpp */; };
D943E5902A24D56000D80361 /* PhysfsIo.h in Headers */ = {isa = PBXBuildFile; fileRef = D943E58D2A24D56000D80361 /* PhysfsIo.h */; };
@@ -1413,6 +1415,7 @@
217DFBD51D9F6D490055D849 /* usocket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = usocket.c; sourceTree = "<group>"; };
217DFBD61D9F6D490055D849 /* usocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = usocket.h; sourceTree = "<group>"; };
D923E7D2296B85B9002FF1B3 /* harfbuzz.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = harfbuzz.xcframework; path = ios/libraries/harfbuzz.xcframework; sourceTree = "<group>"; };
D93660F72D1C727C00C0EC4B /* Touch.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Touch.cpp; sourceTree = "<group>"; };
D943E58C2A24D56000D80361 /* PhysfsIo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PhysfsIo.cpp; sourceTree = "<group>"; };
D943E58D2A24D56000D80361 /* PhysfsIo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhysfsIo.h; sourceTree = "<group>"; };
D9596F602CBAC93800BE58C1 /* SDL3.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = SDL3.xcframework; path = shared/Frameworks/SDL3.xcframework; sourceTree = "<group>"; };
@@ -3386,8 +3389,9 @@
children = (
FA0B7CBF1A95902C000E1D17 /* sdl */,
FA0B7CC21A95902C000E1D17 /* Touch.h */,
FA0B7CC31A95902C000E1D17 /* wrap_Touch.cpp */,
D93660F72D1C727C00C0EC4B /* Touch.cpp */,
FA0B7CC41A95902C000E1D17 /* wrap_Touch.h */,
FA0B7CC31A95902C000E1D17 /* wrap_Touch.cpp */,
);
path = touch;
sourceTree = "<group>";
@@ -4797,6 +4801,7 @@
FA0B7DBC1A95902C000E1D17 /* Joystick.cpp in Sources */,
FA0B7DAF1A95902C000E1D17 /* wrap_CompressedImageData.cpp in Sources */,
FA0B7AD51A958EA3000E1D17 /* unix.c in Sources */,
D93660F92D1C727C00C0EC4B /* Touch.cpp in Sources */,
FAB17BE71ABFAA9000F9BA27 /* lz4.c in Sources */,
FAE64A902071364800BC7981 /* physfs_unicode.c in Sources */,
FACA06B1293EE5CD001A2557 /* Sensor.cpp in Sources */,
@@ -5229,6 +5234,7 @@
FA0B7DC41A95902C000E1D17 /* wrap_JoystickModule.cpp in Sources */,
FA0B7E6F1A95902C000E1D17 /* wrap_RopeJoint.cpp in Sources */,
FA24348721D401CB00B8918A /* attribute.cpp in Sources */,
D93660F82D1C727C00C0EC4B /* Touch.cpp in Sources */,
FA0B7AB51A958EA3000E1D17 /* ddsparse.cpp in Sources */,
FACA02F41F5E396B0084B28F /* wrap_CompressedData.cpp in Sources */,
FAF140AC1E20934C00F898D2 /* Versions.cpp in Sources */,
+41 -35
View File
@@ -317,45 +317,51 @@ Message *Event::convert(const SDL_Event &e)
case SDL_EVENT_FINGER_DOWN:
case SDL_EVENT_FINGER_UP:
case SDL_EVENT_FINGER_MOTION:
// TODO: Expose APIs to enable different touch device types.
if (SDL_GetTouchDeviceType(e.tfinger.touchID) == SDL_TOUCH_DEVICE_DIRECT)
{
touchinfo.id = (int64) e.tfinger.fingerID;
touchinfo.x = e.tfinger.x;
touchinfo.y = e.tfinger.y;
touchinfo.dx = e.tfinger.dx;
touchinfo.dy = e.tfinger.dy;
touchinfo.pressure = e.tfinger.pressure;
touchinfo.id = (int64)e.tfinger.fingerID;
touchinfo.x = e.tfinger.x;
touchinfo.y = e.tfinger.y;
touchinfo.dx = e.tfinger.dx;
touchinfo.dy = e.tfinger.dy;
touchinfo.pressure = e.tfinger.pressure;
touchinfo.deviceType = love::touch::sdl::Touch::getDeviceType(SDL_GetTouchDeviceType(e.tfinger.touchID));
touchinfo.mouse = e.tfinger.touchID == SDL_MOUSE_TOUCHID;
// SDL's coords are normalized to [0, 1], but we want screen coords.
// SDL's coords are normalized to [0, 1], but we want screen coords for direct touches.
if (touchinfo.deviceType == love::touch::Touch::DEVICE_TOUCHSCREEN)
{
normalizedToDPICoords(&touchinfo.x, &touchinfo.y);
normalizedToDPICoords(&touchinfo.dx, &touchinfo.dy);
// We need to update the love.touch.sdl internal state from here.
touchmodule = (touch::sdl::Touch *) Module::getInstance("love.touch.sdl");
if (touchmodule)
touchmodule->onEvent(e.type, touchinfo);
// This is a bit hackish and we lose the higher 32 bits of the id on
// 32-bit systems, but SDL only ever gives id's that at most use as many
// bits as can fit in a pointer (for now.)
// We use lightuserdata instead of a lua_Number (double) because doubles
// can't represent all possible id values on 64-bit systems.
vargs.emplace_back((void *) (intptr_t) touchinfo.id);
vargs.emplace_back(touchinfo.x);
vargs.emplace_back(touchinfo.y);
vargs.emplace_back(touchinfo.dx);
vargs.emplace_back(touchinfo.dy);
vargs.emplace_back(touchinfo.pressure);
if (e.type == SDL_EVENT_FINGER_DOWN)
txt = "touchpressed";
else if (e.type == SDL_EVENT_FINGER_UP)
txt = "touchreleased";
else
txt = "touchmoved";
msg = new Message(txt, vargs);
}
// We need to update the love.touch.sdl internal state from here.
touchmodule = (touch::sdl::Touch *) Module::getInstance("love.touch.sdl");
if (touchmodule)
touchmodule->onEvent(e.type, touchinfo);
if (!love::touch::Touch::getConstant(touchinfo.deviceType, txt))
txt = "unknown";
// This is a bit hackish and we lose the higher 32 bits of the id on
// 32-bit systems, but SDL only ever gives id's that at most use as many
// bits as can fit in a pointer (for now.)
// We use lightuserdata instead of a lua_Number (double) because doubles
// can't represent all possible id values on 64-bit systems.
vargs.emplace_back((void *)(intptr_t)touchinfo.id);
vargs.emplace_back(touchinfo.x);
vargs.emplace_back(touchinfo.y);
vargs.emplace_back(touchinfo.dx);
vargs.emplace_back(touchinfo.dy);
vargs.emplace_back(touchinfo.pressure);
vargs.emplace_back(txt, strlen(txt));
vargs.emplace_back(touchinfo.mouse);
if (e.type == SDL_EVENT_FINGER_DOWN)
txt = "touchpressed";
else if (e.type == SDL_EVENT_FINGER_UP)
txt = "touchreleased";
else
txt = "touchmoved";
msg = new Message(txt, vargs);
break;
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
case SDL_EVENT_JOYSTICK_BUTTON_UP:
+5
View File
@@ -220,6 +220,7 @@ function love.init()
highdpi = false,
renderers = nil, -- Moved to t.graphics.
excluderenderers = nil, -- Moved to t.graphics.
trackpadtouch = false,
}
-- Console hack, part 1.
@@ -315,6 +316,10 @@ function love.init()
love._setHighDPIAllowed(c.highdpi)
end
if love._setTrackpadTouch then
love._setTrackpadTouch(c.trackpadtouch)
end
if love._setAudioMixWithSystem then
if c.audio and c.audio.mixwithsystem ~= nil then
love._setAudioMixWithSystem(c.audio.mixwithsystem)
+8 -8
View File
@@ -52,14 +52,14 @@ function love.createhandlers()
wheelmoved = function (x,y,px,py,dir)
if love.wheelmoved then return love.wheelmoved(x,y,px,py,dir) end
end,
touchpressed = function (id,x,y,dx,dy,p)
if love.touchpressed then return love.touchpressed(id,x,y,dx,dy,p) end
touchpressed = function (id,x,y,dx,dy,p,t,m)
if love.touchpressed then return love.touchpressed(id,x,y,dx,dy,p,t,m) end
end,
touchreleased = function (id,x,y,dx,dy,p)
if love.touchreleased then return love.touchreleased(id,x,y,dx,dy,p) end
touchreleased = function (id,x,y,dx,dy,p,t,m)
if love.touchreleased then return love.touchreleased(id,x,y,dx,dy,p,t,m) end
end,
touchmoved = function (id,x,y,dx,dy,p)
if love.touchmoved then return love.touchmoved(id,x,y,dx,dy,p) end
touchmoved = function (id,x,y,dx,dy,p,t,m)
if love.touchmoved then return love.touchmoved(id,x,y,dx,dy,p,t,m) end
end,
joystickpressed = function (j,b)
if love.joystickpressed then return love.joystickpressed(j,b) end
@@ -163,13 +163,13 @@ function love.run()
-- Process events.
if love.event then
love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do
for name, a,b,c,d,e,f,g,h in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a or 0, b
end
end
love.handlers[name](a,b,c,d,e,f)
love.handlers[name](a,b,c,d,e,f,g,h)
end
end
+16
View File
@@ -78,6 +78,11 @@
# include "system/System.h"
#endif
// For love::touch::setTrackpadTouch.
#ifdef LOVE_ENABLE_TOUCH
# include "touch/Touch.h"
#endif
// Scripts.
#include "scripts/nogame.lua.h"
@@ -439,6 +444,14 @@ static int w__setHighDPIAllowed(lua_State *L)
return 0;
}
static int w__setTrackpadTouch(lua_State *L)
{
#ifdef LOVE_ENABLE_TOUCH
love::touch::setTrackpadTouch((bool) lua_toboolean(L, 1));
#endif
return 0;
}
static int w__setAudioMixWithSystem(lua_State *L)
{
bool success = false;
@@ -570,6 +583,9 @@ int luaopen_love(lua_State *L)
lua_pushcfunction(L, w__setHighDPIAllowed);
lua_setfield(L, -2, "_setHighDPIAllowed");
lua_pushcfunction(L, w__setTrackpadTouch);
lua_setfield(L, -2, "_setTrackpadTouch");
// Exposed here because we need to be able to call it before the audio
// module is initialized.
lua_pushcfunction(L, w__setAudioMixWithSystem);
+46
View File
@@ -0,0 +1,46 @@
/**
* Copyright (c) 2006-2024 LOVE Development Team
*
* 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 "Touch.h"
namespace love
{
namespace touch
{
// TODO: find a cleaner way to do this...
// The touch backend (e.g. love.touch.sdl) is expected to implement this.
void setTrackpadTouchImplementation(bool enable);
void setTrackpadTouch(bool enable)
{
setTrackpadTouchImplementation(enable);
}
STRINGMAP_CLASS_BEGIN(Touch, Touch::DeviceType, Touch::DEVICE_MAX_ENUM, deviceType)
{
{ "touchscreen", Touch::DEVICE_TOUCHSCREEN },
{ "touchpad", Touch::DEVICE_TOUCHPAD },
{ "touchpadrelative", Touch::DEVICE_TOUCHPAD_RELATIVE },
}
STRINGMAP_CLASS_END(Touch, Touch::DeviceType, Touch::DEVICE_MAX_ENUM, deviceType)
} // touch
} // love
+19 -4
View File
@@ -25,6 +25,7 @@
#include "common/int.h"
#include "common/Object.h"
#include "common/Module.h"
#include "common/StringMap.h"
// C++
#include <vector>
@@ -35,18 +36,30 @@ namespace love
namespace touch
{
void setTrackpadTouch(bool enable);
class Touch : public Module
{
public:
enum DeviceType
{
DEVICE_TOUCHSCREEN,
DEVICE_TOUCHPAD,
DEVICE_TOUCHPAD_RELATIVE,
DEVICE_MAX_ENUM
};
struct TouchInfo
{
int64 id; // Identifier. Only unique for the duration of the touch-press.
double x; // Position in pixels along the x-axis.
double y; // Position in pixels along the y-axis.
double dx; // Amount in pixels moved along the x-axis.
double dy; // Amount in pixels moved along the y-axis.
double x; // Position in pixels (for touchscreens) or normalized [0, 1] position (for touchpads) along the x-axis.
double y; // Position in pixels (for touchscreens) or normalized [0, 1] position (for touchpads) along the y-axis.
double dx; // Amount moved along the x-axis.
double dy; // Amount moved along the y-axis.
double pressure;
DeviceType deviceType;
bool mouse;
};
virtual ~Touch() {}
@@ -61,6 +74,8 @@ public:
**/
virtual const TouchInfo &getTouch(int64 id) const = 0;
STRINGMAP_CLASS_DECLARE(DeviceType);
protected:
Touch(const char *name)
+20
View File
@@ -26,10 +26,19 @@
// C++
#include <algorithm>
#include <SDL3/SDL_hints.h>
namespace love
{
namespace touch
{
// See src/modules/touch/Touch.cpp.
void setTrackpadTouchImplementation(bool enable)
{
SDL_SetHint(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, enable ? "1" : "0");
}
namespace sdl
{
@@ -84,6 +93,17 @@ void Touch::onEvent(Uint32 eventtype, const TouchInfo &info)
}
}
Touch::DeviceType Touch::getDeviceType(SDL_TouchDeviceType sdltype)
{
switch (sdltype)
{
case SDL_TOUCH_DEVICE_DIRECT: return DEVICE_TOUCHSCREEN;
case SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE: return DEVICE_TOUCHPAD;
case SDL_TOUCH_DEVICE_INDIRECT_RELATIVE: return DEVICE_TOUCHPAD_RELATIVE;
default: return DEVICE_TOUCHSCREEN;
}
}
} // sdl
} // touch
} // love
+2
View File
@@ -51,6 +51,8 @@ public:
// love::event::sdl::Event::convert.
void onEvent(Uint32 eventtype, const TouchInfo &info);
static DeviceType getDeviceType(SDL_TouchDeviceType sdltype);
private:
// All current touches.
+51 -8
View File
@@ -24,6 +24,7 @@
#include "wrap_Touch.h"
#include "sdl/Touch.h"
#include "common/Optional.h"
namespace love
{
@@ -42,19 +43,33 @@ int64 luax_checktouchid(lua_State *L, int idx)
int w_getTouches(lua_State *L)
{
Optional<Touch::DeviceType> typefilter;
if (!lua_isnoneornil(L, 1))
{
const char *typestr = luaL_checkstring(L, 1);
if (!Touch::getConstant(typestr, typefilter.value))
return luax_enumerror(L, "touch device type", Touch::getConstants(typefilter.value), typestr);
typefilter.hasValue = true;
}
const std::vector<Touch::TouchInfo> &touches = instance()->getTouches();
lua_createtable(L, (int) touches.size(), 0);
for (size_t i = 0; i < touches.size(); i++)
int filteredindex = 1;
for (const Touch::TouchInfo &touch : touches)
{
// This is a bit hackish and we lose the higher 32 bits of the id on
// 32-bit systems, but SDL only ever gives id's that at most use as many
// bits as can fit in a pointer (for now.)
// We use lightuserdata instead of a lua_Number (double) because doubles
// can't represent all possible id values on 64-bit systems.
lua_pushlightuserdata(L, (void *) (intptr_t) touches[i].id);
lua_rawseti(L, -2, (int) i + 1);
if (!typefilter.hasValue || typefilter.value == touch.deviceType)
{
// This is a bit hackish and we lose the higher 32 bits of the id on
// 32-bit systems, but SDL only ever gives id's that at most use as many
// bits as can fit in a pointer (for now.)
// We use lightuserdata instead of a lua_Number (double) because doubles
// can't represent all possible id values on 64-bit systems.
lua_pushlightuserdata(L, (void *)(intptr_t)touch.id);
lua_rawseti(L, -2, filteredindex);
filteredindex++;
}
}
return 1;
@@ -84,11 +99,39 @@ int w_getPressure(lua_State *L)
return 1;
}
int w_getDeviceType(lua_State *L)
{
int64 id = luax_checktouchid(L, 1);
Touch::TouchInfo touch = {};
luax_catchexcept(L, [&]() { touch = instance()->getTouch(id); });
const char *typestr = nullptr;
if (!Touch::getConstant(touch.deviceType, typestr))
return luaL_error(L, "Unknown touch device type.");
lua_pushstring(L, typestr);
return 1;
}
int w_isMouse(lua_State *L)
{
int64 id = luax_checktouchid(L, 1);
Touch::TouchInfo touch = {};
luax_catchexcept(L, [&]() { touch = instance()->getTouch(id); });
luax_pushboolean(L, touch.mouse);
return 1;
}
static const luaL_Reg functions[] =
{
{ "getTouches", w_getTouches },
{ "getPosition", w_getPosition },
{ "getPressure", w_getPressure },
{ "getDeviceType", w_getDeviceType },
{ "isMouse", w_isMouse },
{ 0, 0 }
};