Skip to content

Commit ca16fb8

Browse files
authored
SDL: add multitouch support (#1599)
Partially inspired by the Android implementation. After I added it, I suddenly realized we could've had fake multitouch on desktop for years. There's a right button on the mouse, after all. A possible alternative would be to simply fake right mouse button as being to fingers at once, as opposed to having to truly press left and right at the same time. Also removes an outdated OSX workaround.
1 parent 263b211 commit ca16fb8

File tree

1 file changed

+108
-43
lines changed

1 file changed

+108
-43
lines changed

ffi/SDL2_0.lua

+108-43
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ local function SDL_Linked_Version_AtLeast(x, y, z)
5454
return SDL_VersionNum(getSDLVersion()) >= SDL_VersionNum(x, y, z)
5555
end
5656

57+
-- Used as the device ID for mouse events simulated with touch input.
58+
-- Defined in SDL_touch.h as #define SDL_TOUCH_MOUSEID ((Uint32)-1)
59+
local SDL_TOUCH_MOUSEID = ffi.cast('uint32_t', -1);
60+
5761
local S = {
5862
w = 0, h = 0,
5963
screen = nil,
@@ -201,6 +205,58 @@ local function genEmuEvent(evtype, code, value)
201205
table.insert(inputQueue, ev)
202206
end
203207

208+
-- Keep track of active pointers so we can feed ABS_MT_SLOT 0 and 1 to the frontend for multitouch to work.
209+
-- down, a boolean denoting whether the pointer is currently down for tracking mouse button status.
210+
local pointers = {}
211+
local function setPointerDownState(slot, down)
212+
if not pointers[slot] then
213+
pointers[slot] = { down = down }
214+
else
215+
pointers[slot].down = down
216+
end
217+
end
218+
219+
-- For the moment we pretend there can only be one touchscreen/trackpad/whatever at a time.
220+
-- It's probably close enough to the truth unless you run into a tester.
221+
local function getFingerSlot(event)
222+
if not pointers[tonumber(event.tfinger.fingerId)] then
223+
local num_touch_fingers = SDL.SDL_GetNumTouchFingers(event.tfinger.touchId)
224+
if num_touch_fingers > 1 then
225+
for i=0,num_touch_fingers-1 do
226+
if SDL.SDL_GetTouchFinger(event.tfinger.touchId, i).id == event.tfinger.fingerId then
227+
pointers[tonumber(event.tfinger.fingerId)] = { slot = i }
228+
end
229+
end
230+
else
231+
pointers[tonumber(event.tfinger.fingerId)] = { slot = 0 }
232+
end
233+
end
234+
return pointers[tonumber(event.tfinger.fingerId)].slot
235+
end
236+
237+
local function genTouchDownEvent(event, slot, x, y)
238+
genEmuEvent(C.EV_ABS, C.ABS_MT_SLOT, slot)
239+
genEmuEvent(C.EV_ABS, C.ABS_MT_TRACKING_ID, tonumber(event.tfinger.fingerId))
240+
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_X, x)
241+
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_Y, y)
242+
genEmuEvent(C.EV_SYN, C.SYN_REPORT, 0)
243+
end
244+
245+
local function genTouchUpEvent(event, slot, x, y)
246+
genEmuEvent(C.EV_ABS, C.ABS_MT_SLOT, slot)
247+
genEmuEvent(C.EV_ABS, C.ABS_MT_TRACKING_ID, -1)
248+
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_X, x)
249+
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_Y, y)
250+
genEmuEvent(C.EV_SYN, C.SYN_REPORT, 0)
251+
end
252+
253+
local function genTouchMoveEvent(event, slot, x, y)
254+
genEmuEvent(C.EV_ABS, C.ABS_MT_SLOT, slot)
255+
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_X, x)
256+
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_Y, y)
257+
genEmuEvent(C.EV_SYN, C.SYN_REPORT, 0)
258+
end
259+
204260
local function handleWindowEvent(event_window)
205261
-- The next buffer might always contain garbage, and on X11 without
206262
-- compositing the buffers will be damaged just by moving the window
@@ -273,8 +329,7 @@ local function handleJoyAxisMotionEvent(event)
273329
end
274330

275331
local SDL_BUTTON_LEFT = 1
276-
277-
local is_in_touch = false
332+
local SDL_BUTTON_RIGHT = 3
278333

279334
function S.waitForEvent(sec, usec)
280335
local event = ffi.new("union SDL_Event")
@@ -293,64 +348,74 @@ function S.waitForEvent(sec, usec)
293348
end
294349

295350
-- if we got an event, examine it here and generate events for koreader
296-
if ffi.os == "OSX" and (event.type == SDL.SDL_FINGERMOTION or
297-
event.type == SDL.SDL_FINGERDOWN or
298-
event.type == SDL.SDL_FINGERUP) then
299-
-- noop for trackpad finger inputs which interfere with emulated mouse inputs
300-
do end -- luacheck: ignore 541
301-
elseif event.type == SDL.SDL_KEYDOWN then
351+
if event.type == SDL.SDL_KEYDOWN then
302352
genEmuEvent(C.EV_KEY, event.key.keysym.sym, 1)
303353
elseif event.type == SDL.SDL_KEYUP then
304354
genEmuEvent(C.EV_KEY, event.key.keysym.sym, 0)
305355
elseif event.type == SDL.SDL_TEXTINPUT then
306356
genEmuEvent(C.EV_SDL, SDL.SDL_TEXTINPUT, ffi.string(event.text.text))
307-
elseif event.type == SDL.SDL_MOUSEMOTION
357+
elseif event.type == SDL.SDL_MOUSEMOTION --and event.motion.which ~= SDL_TOUCH_MOUSEID
308358
or event.type == SDL.SDL_FINGERMOTION then
309359
local is_finger = event.type == SDL.SDL_FINGERMOTION
310-
if is_in_touch then
311-
if is_finger then
312-
if event.tfinger.dx ~= 0 then
313-
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_X,
314-
event.tfinger.x * S.w)
315-
end
316-
if event.tfinger.dy ~= 0 then
317-
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_Y,
318-
event.tfinger.y * S.h)
319-
end
320-
else
321-
if event.motion.xrel ~= 0 then
322-
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_X,
323-
event.motion.x)
324-
end
325-
if event.motion.yrel ~= 0 then
326-
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_Y,
327-
event.motion.y)
328-
end
360+
local slot
361+
362+
if is_finger then
363+
slot = getFingerSlot(event)
364+
genTouchMoveEvent(event, slot, event.tfinger.x * S.w, event.tfinger.y * S.h)
365+
else
366+
if pointers[0] and pointers[0].down then
367+
slot = 0 -- left mouse button down
368+
genTouchMoveEvent(event, slot, event.motion.x, event.motion.y)
369+
end
370+
if pointers[1] and pointers[1].down then
371+
slot = 1 -- right mouse button down
372+
genTouchMoveEvent(event, slot, event.motion.x, event.motion.y)
329373
end
330-
genEmuEvent(C.EV_SYN, C.SYN_REPORT, 0)
331374
end
332-
elseif event.type == SDL.SDL_MOUSEBUTTONUP
375+
elseif event.type == SDL.SDL_MOUSEBUTTONUP and event.button.which ~= SDL_TOUCH_MOUSEID
333376
or event.type == SDL.SDL_FINGERUP then
334-
is_in_touch = false
335-
genEmuEvent(C.EV_ABS, C.ABS_MT_TRACKING_ID, -1)
336-
genEmuEvent(C.EV_SYN, C.SYN_REPORT, 0)
337-
elseif event.type == SDL.SDL_MOUSEBUTTONDOWN
377+
local is_finger = event.type == SDL.SDL_FINGERUP
378+
local slot
379+
if is_finger then
380+
slot = getFingerSlot(event)
381+
elseif event.button.button == SDL_BUTTON_RIGHT then
382+
slot = 1
383+
else -- SDL_BUTTON_LEFT
384+
slot = 0
385+
end
386+
387+
local x = is_finger and event.tfinger.x * S.w or event.button.x
388+
local y = is_finger and event.tfinger.y * S.h or event.button.y
389+
genTouchUpEvent(event, slot, x, y)
390+
if is_finger then
391+
pointers[tonumber(event.tfinger.fingerId)] = nil
392+
else
393+
pointers[slot] = nil
394+
end
395+
elseif event.type == SDL.SDL_MOUSEBUTTONDOWN and event.button.which ~= SDL_TOUCH_MOUSEID
338396
or event.type == SDL.SDL_FINGERDOWN then
339397
local is_finger = event.type == SDL.SDL_FINGERDOWN
340-
if not is_finger and event.button.button ~= SDL_BUTTON_LEFT then
341-
-- Not a left-click?
398+
if not is_finger and not (event.button.button == SDL_BUTTON_LEFT or event.button.button == SDL_BUTTON_RIGHT) then
399+
-- We don't do anything with extra buttons for now.
342400
return false, C.ENOSYS
343401
end
402+
344403
-- use mouse click to simulate single tap
345-
is_in_touch = true
346-
genEmuEvent(C.EV_ABS, C.ABS_MT_TRACKING_ID, 0)
347-
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_X,
348-
is_finger and event.tfinger.x * S.w or event.button.x)
349-
genEmuEvent(C.EV_ABS, C.ABS_MT_POSITION_Y,
350-
is_finger and event.tfinger.y * S.h or event.button.y)
351-
genEmuEvent(C.EV_SYN, C.SYN_REPORT, 0)
404+
local slot
405+
if is_finger then
406+
slot = getFingerSlot(event)
407+
elseif event.button.button == SDL_BUTTON_RIGHT then
408+
slot = 1
409+
else -- SDL_BUTTON_LEFT
410+
slot = 0
411+
end
412+
local x = is_finger and event.tfinger.x * S.w or event.button.x
413+
local y = is_finger and event.tfinger.y * S.h or event.button.y
414+
setPointerDownState(slot, true)
415+
genTouchDownEvent(event, slot, x, y)
352416
elseif event.type == SDL.SDL_MULTIGESTURE then
353417
genEmuEvent(C.EV_SDL, SDL.SDL_MULTIGESTURE, event.mgesture)
418+
genEmuEvent(C.EV_SYN, C.SYN_REPORT, 0)
354419
elseif event.type == SDL.SDL_MOUSEWHEEL then
355420
genEmuEvent(C.EV_SDL, SDL.SDL_MOUSEWHEEL, event.wheel)
356421
elseif event.type == SDL.SDL_DROPFILE then

0 commit comments

Comments
 (0)