Skip to content

Commit 248be33

Browse files
michaeljbishopjuj
authored andcommitted
Added preliminary support for the SDL2 touch api. Missing items:
1. Timestamps 2. TouchID (the Device ID) As inline with the SDL spec, we will pass a touch ID of `SDL_TOUCH_MOUSEID` for touch events that are simulated by the mouse so games can rely solely on touch events if they like. Includes the SDL2 Copyright notice on the headers that contain SDL2 content. Includes a fix to SDL_PeepEvents.
1 parent ce58885 commit 248be33

File tree

6 files changed

+296
-157
lines changed

6 files changed

+296
-157
lines changed

src/library_browser.js

+30-16
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,8 @@ mergeInto(LibraryManager.library, {
482482
mouseY: 0,
483483
mouseMovementX: 0,
484484
mouseMovementY: 0,
485+
touches: {},
486+
lastTouches: {},
485487

486488
calculateMouseEvent: function(event) { // event should be mousemove, mousedown or mouseup
487489
if (Browser.pointerLock) {
@@ -510,8 +512,9 @@ mergeInto(LibraryManager.library, {
510512
// Otherwise, calculate the movement based on the changes
511513
// in the coordinates.
512514
var rect = Module["canvas"].getBoundingClientRect();
513-
var x, y;
514-
515+
var cw = Module["canvas"].width;
516+
var ch = Module["canvas"].height;
517+
515518
// Neither .scrollX or .pageXOffset are defined in a spec, but
516519
// we prefer .scrollX because it is currently in a spec draft.
517520
// (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/)
@@ -522,26 +525,37 @@ mergeInto(LibraryManager.library, {
522525
// and we have no viable fallback.
523526
assert((typeof scrollX !== 'undefined') && (typeof scrollY !== 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.');
524527
#endif
525-
if (event.type == 'touchstart' ||
526-
event.type == 'touchend' ||
527-
event.type == 'touchmove') {
528-
var t = event.touches.item(0);
529-
if (t) {
530-
x = t.pageX - (scrollX + rect.left);
531-
y = t.pageY - (scrollY + rect.top);
532-
} else {
533-
return;
528+
529+
if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') {
530+
var touch = event.touch;
531+
if (touch === undefined) {
532+
return; // the "touch" property is only defined in SDL
533+
534534
}
535-
} else {
536-
x = event.pageX - (scrollX + rect.left);
537-
y = event.pageY - (scrollY + rect.top);
535+
var adjustedX = touch.pageX - (scrollX + rect.left);
536+
var adjustedY = touch.pageY - (scrollY + rect.top);
537+
538+
adjustedX = adjustedX * (cw / rect.width);
539+
adjustedY = adjustedY * (ch / rect.height);
540+
541+
var coords = {x: adjustedX, y: adjustedY};
542+
543+
if (event.type === 'touchstart') {
544+
Browser.lastTouches[touch.identifier] = coords;
545+
Browser.touches[touch.identifier] = coords;
546+
} else if (event.type === 'touchend' || event.type === 'touchmove') {
547+
Browser.lastTouches[touch.identifier] = Browser.touches[touch.identifier];
548+
Browser.touches[touch.identifier] = { x: adjustedX, y: adjustedY };
549+
}
550+
return;
538551
}
539552

553+
var x = event.pageX - (scrollX + rect.left);
554+
var y = event.pageY - (scrollY + rect.top);
555+
540556
// the canvas might be CSS-scaled compared to its backbuffer;
541557
// SDL-using content will want mouse coordinates in terms
542558
// of backbuffer units.
543-
var cw = Module["canvas"].width;
544-
var ch = Module["canvas"].height;
545559
x = x * (cw / rect.width);
546560
y = y * (ch / rect.height);
547561

src/library_sdl.js

+168-49
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ var LibrarySDL = {
8282

8383
DOMEventToSDLEvent: {},
8484

85+
TOUCH_DEFAULT_ID: 0, // Our default deviceID for touch events (we get nothing from the browser)
86+
8587
keyCodes: { // DOM code ==> SDL code. See https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent and SDL_keycode.h
8688
// For keys that don't have unicode value, we map DOM codes with the corresponding scan codes + 1024 (using "| 1 << 10")
8789
16: 225 | 1<<10, // shift
@@ -417,50 +419,104 @@ var LibrarySDL = {
417419
}
418420
},
419421

420-
touchX: 0, touchY: 0,
422+
// the browser sends out touchstart events with the whole group of touches
423+
// even if we received a previous touchstart for a specific touch identifier.
424+
// You can test this by pressing one finger to the screen, then another. You'll
425+
// receive two touchstart events, the first with a touches count of 1 the second
426+
// with a touches count of two.
427+
// SDL sends out a new touchstart event for only each newly started touch so to
428+
// emulate this, we keep track of previously started touches.
429+
downFingers: {},
421430
savedKeydown: null,
422431

423432
receiveEvent: function(event) {
424433
switch(event.type) {
425-
case 'touchstart':
434+
case 'touchstart': case 'touchmove': {
426435
event.preventDefault();
427-
var touch = event.touches[0];
428-
touchX = touch.pageX;
429-
touchY = touch.pageY;
430-
var event = {
431-
type: 'mousedown',
432-
button: 0,
433-
pageX: touchX,
434-
pageY: touchY
436+
437+
var touches = [];
438+
439+
// Clear out any touchstart events that we've already processed
440+
if (event.type === 'touchstart') {
441+
for(var i = 0; i < event.touches.length; i++) {
442+
var touch = event.touches[i];
443+
if (SDL.downFingers[touch.identifier] != true) {
444+
SDL.downFingers[touch.identifier] = true;
445+
touches.push(touch);
446+
}
447+
}
448+
} else {
449+
touches = event.touches;
450+
}
451+
452+
var firstTouch = touches[0];
453+
if ( event.type == 'touchstart' ) {
454+
SDL.DOMButtons[0] = 1;
435455
};
436-
SDL.DOMButtons[0] = 1;
437-
SDL.events.push(event);
438-
break;
439-
case 'touchmove':
440-
event.preventDefault();
441-
var touch = event.touches[0];
442-
touchX = touch.pageX;
443-
touchY = touch.pageY;
444-
event = {
445-
type: 'mousemove',
456+
var mouseEventType;
457+
switch(event.type) {
458+
case 'touchstart': mouseEventType = 'mousedown'; break;
459+
case 'touchmove': mouseEventType = 'mousemove'; break;
460+
}
461+
var mouseEvent = {
462+
type: mouseEventType,
446463
button: 0,
447-
pageX: touchX,
448-
pageY: touchY
464+
pageX: firstTouch.clientX,
465+
pageY: firstTouch.clientY
466+
};
467+
SDL.events.push(mouseEvent);
468+
469+
for (i=0;i<touches.length;i++) {
470+
var touch = touches[i];
471+
SDL.events.push({
472+
type: event.type,
473+
touch: touch
474+
});
449475
};
450-
SDL.events.push(event);
451476
break;
452-
case 'touchend':
477+
}
478+
case 'touchend': {
453479
event.preventDefault();
454-
event = {
480+
481+
// Remove the entry in the SDL.downFingers hash
482+
// because the finger is no longer down.
483+
for(var i = 0; i < event.changedTouches.length; i++) {
484+
var touch = event.changedTouches[i];
485+
if (SDL.downFingers[touch.identifier] === true) {
486+
delete SDL.downFingers[touch.identifier];
487+
}
488+
}
489+
490+
var mouseEvent = {
455491
type: 'mouseup',
456492
button: 0,
457-
pageX: touchX,
458-
pageY: touchY
493+
pageX: event.changedTouches[0].clientX,
494+
pageY: event.changedTouches[0].clientY
459495
};
460496
SDL.DOMButtons[0] = 0;
461-
SDL.events.push(event);
497+
SDL.events.push(mouseEvent);
498+
499+
for (i = 0; i < event.changedTouches.length; i++) {
500+
var touch = event.changedTouches[i];
501+
SDL.events.push({
502+
type: 'touchend',
503+
touch: touch
504+
});
505+
};
462506
break;
507+
}
463508
case 'mousemove':
509+
if (SDL.DOMButtons[0] === 1) {
510+
SDL.events.push({
511+
type: 'touchmove',
512+
touch: {
513+
identifier: 0,
514+
deviceID: {{{ cDefine('SDL_TOUCH_MOUSEID') }}},
515+
pageX: event.pageX,
516+
pageY: event.pageY
517+
}
518+
});
519+
}
464520
if (Browser.pointerLock) {
465521
// workaround for firefox bug 750111
466522
if ('mozMovementX' in event) {
@@ -502,13 +558,31 @@ var LibrarySDL = {
502558
};
503559
} else if (event.type == 'mousedown') {
504560
SDL.DOMButtons[event.button] = 1;
561+
SDL.events.push({
562+
type: 'touchstart',
563+
touch: {
564+
identifier: 0,
565+
deviceID: {{{ cDefine('SDL_TOUCH_MOUSEID') }}},
566+
pageX: event.pageX,
567+
pageY: event.pageY
568+
}
569+
});
505570
} else if (event.type == 'mouseup') {
506571
// ignore extra ups, can happen if we leave the canvas while pressing down, then return,
507572
// since we add a mouseup in that case
508573
if (!SDL.DOMButtons[event.button]) {
509574
return;
510575
}
511576

577+
SDL.events.push({
578+
type: 'touchend',
579+
touch: {
580+
identifier: 0,
581+
deviceID: {{{ cDefine('SDL_TOUCH_MOUSEID') }}},
582+
pageX: event.pageX,
583+
pageY: event.pageY
584+
}
585+
});
512586
SDL.DOMButtons[event.button] = 0;
513587
}
514588

@@ -600,6 +674,10 @@ var LibrarySDL = {
600674
event.handled = true;
601675

602676
switch (event.type) {
677+
case 'touchstart': case 'touchend': case 'touchmove': {
678+
Browser.calculateMouseEvent(event);
679+
break;
680+
}
603681
case 'keydown': case 'keyup': {
604682
var down = event.type === 'keydown';
605683
var code = event.keyCode;
@@ -690,20 +768,50 @@ var LibrarySDL = {
690768
if (event.type != 'mousemove') {
691769
var down = event.type === 'mousedown';
692770
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
771+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.timestamp, '0', 'i32') }}};
772+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.windowID, '0', 'i32') }}};
773+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.which, '0', 'i32') }}};
693774
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.button, 'event.button+1', 'i8') }}}; // DOM buttons are 0-2, SDL 1-3
694775
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.state, 'down ? 1 : 0', 'i8') }}};
695776
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.x, 'Browser.mouseX', 'i32') }}};
696777
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.y, 'Browser.mouseY', 'i32') }}};
697778
} else {
698779
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
699-
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.state, 'SDL.buttonState', 'i8') }}};
780+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.timestamp, '0', 'i32') }}};
781+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.windowID, '0', 'i32') }}};
782+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.which, '0', 'i32') }}};
783+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.state, 'SDL.buttonState', 'i32') }}};
700784
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.x, 'Browser.mouseX', 'i32') }}};
701785
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.y, 'Browser.mouseY', 'i32') }}};
702786
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.xrel, 'Browser.mouseMovementX', 'i32') }}};
703787
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.yrel, 'Browser.mouseMovementY', 'i32') }}};
704788
}
705789
break;
706790
}
791+
case 'touchstart': case 'touchend': case 'touchmove': {
792+
var touch = event.touch;
793+
var w = Module['canvas'].width;
794+
var h = Module['canvas'].height;
795+
var x = Browser.touches[touch.identifier].x / w;
796+
var y = Browser.touches[touch.identifier].y / h;
797+
var lx = Browser.lastTouches[touch.identifier].x / w;
798+
var ly = Browser.lastTouches[touch.identifier].y / h;
799+
var dx = x - lx;
800+
var dy = y - ly;
801+
if (touch['deviceID'] === undefined)
802+
touch.deviceID = SDL.TOUCH_DEFAULT_ID;
803+
if (dx === 0 && dy === 0 && event.type === 'touchmove') return; // don't send these if nothing happened
804+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
805+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.timestamp, '0', 'i32') }}}; // XXX michaeljbishop - Unimplemented for now
806+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.touchId, 'touch.deviceID', 'i64') }}};
807+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.fingerId, 'touch.identifier', 'i64') }}};
808+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.x, 'x', 'float') }}};
809+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.y, 'y', 'float') }}};
810+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.dx, 'dx', 'float') }}};
811+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.dy, 'dy', 'float') }}};
812+
{{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.pressure, 'touch.force', 'float') }}};
813+
break;
814+
}
707815
case 'unload': {
708816
{{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
709817
break;
@@ -948,14 +1056,17 @@ var LibrarySDL = {
9481056
SDL.keyboardState = _malloc(0x10000); // Our SDL needs 512, but 64K is safe for older SDLs
9491057
_memset(SDL.keyboardState, 0, 0x10000);
9501058
// Initialize this structure carefully for closure
951-
SDL.DOMEventToSDLEvent['keydown'] = 0x300 /* SDL_KEYDOWN */;
952-
SDL.DOMEventToSDLEvent['keyup'] = 0x301 /* SDL_KEYUP */;
953-
SDL.DOMEventToSDLEvent['keypress'] = 0x303 /* SDL_TEXTINPUT */;
954-
SDL.DOMEventToSDLEvent['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */;
955-
SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
956-
SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
957-
SDL.DOMEventToSDLEvent['unload'] = 0x100 /* SDL_QUIT */;
958-
SDL.DOMEventToSDLEvent['resize'] = 0x7001 /* SDL_VIDEORESIZE/SDL_EVENT_COMPAT2 */;
1059+
SDL.DOMEventToSDLEvent['keydown'] = 0x300 /* SDL_KEYDOWN */;
1060+
SDL.DOMEventToSDLEvent['keyup'] = 0x301 /* SDL_KEYUP */;
1061+
SDL.DOMEventToSDLEvent['keypress'] = 0x303 /* SDL_TEXTINPUT */;
1062+
SDL.DOMEventToSDLEvent['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */;
1063+
SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
1064+
SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
1065+
SDL.DOMEventToSDLEvent['touchstart'] = 0x700 /* SDL_FINGERDOWN */;
1066+
SDL.DOMEventToSDLEvent['touchend'] = 0x701 /* SDL_FINGERUP */;
1067+
SDL.DOMEventToSDLEvent['touchmove'] = 0x702 /* SDL_FINGERMOTION */;
1068+
SDL.DOMEventToSDLEvent['unload'] = 0x100 /* SDL_QUIT */;
1069+
SDL.DOMEventToSDLEvent['resize'] = 0x7001 /* SDL_VIDEORESIZE/SDL_EVENT_COMPAT2 */;
9591070
// These are not technically DOM events; the HTML gamepad API is poll-based.
9601071
// However, we define them here, as the rest of the SDL code assumes that
9611072
// all SDL events originate as DOM events.
@@ -1025,7 +1136,7 @@ var LibrarySDL = {
10251136
},
10261137

10271138
SDL_SetVideoMode: function(width, height, depth, flags) {
1028-
['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
1139+
['touchstart', 'touchend', 'touchmove', 'mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
10291140
Module['canvas'].addEventListener(event, SDL.receiveEvent, true);
10301141
});
10311142

@@ -1476,20 +1587,28 @@ var LibrarySDL = {
14761587
return 0;
14771588
},
14781589

1479-
SDL_PeepEvents: function(events, numEvents, action, from, to) {
1590+
SDL_PeepEvents: function(events, requestedEventCount, action, from, to) {
14801591
switch(action) {
14811592
case 2: { // SDL_GETEVENT
1482-
assert(numEvents == 1);
1483-
var got = 0;
1484-
while (SDL.events.length > 0 && numEvents > 0) {
1485-
var type = SDL.DOMEventToSDLEvent[SDL.events[0].type];
1486-
if (type < from || type > to) break;
1487-
SDL.makeCEvent(SDL.events.shift(), events);
1488-
got++;
1489-
numEvents--;
1490-
// events += sizeof(..)
1593+
// We only handle 1 event right now
1594+
assert(requestedEventCount == 1);
1595+
1596+
var index = 0;
1597+
var retrievedEventCount = 0;
1598+
// this should look through the entire queue until it has filled up the events
1599+
// array
1600+
while ( index < SDL.events.length && retrievedEventCount < requestedEventCount ) {
1601+
var event = SDL.events[index];
1602+
var type = SDL.DOMEventToSDLEvent[event.type];
1603+
if ( from <= type && type <= to) {
1604+
SDL.makeCEvent(event, events);
1605+
SDL.events.splice(index, 1);
1606+
retrievedEventCount++;
1607+
} else {
1608+
index++;
1609+
}
14911610
}
1492-
return got;
1611+
return retrievedEventCount;
14931612
}
14941613
default: throw 'SDL_PeepEvents does not yet support that action: ' + action;
14951614
}

0 commit comments

Comments
 (0)