How do I override touch zoom and pan in firefox & chrome with pointer events

Refresh

February 2019

Views

31 time

2

This goes for other browsers too but primary targets are Firefox and Chrome.

I have registered all pointer event listeners and had to register mouse down and moue up listeners to get the button state changes ( button states in pointer events are not accurate or "chorded" )

But even if I do event.preventDefault() on all pointer:over,enter,down,up,out,and,leave events (except for pointerType mouse for down, as it will disable the mousedown and mouse up listeners) I have remaining issues:

Multi-touch (more than one finger) will be taken over and perform a page-zoom operation rendering multi-touch in my app useless.

If there are scrollbars on the page a single touch will be taken over by the touch pan/scroll operation.

The preventDefault() is not implemented properly with the operating system (ie: windows 10) and a pointer down over my element will also generate a synthetic mouse down and synthetically move the mouse to the touched location. I can ignore the synthetic down and up events with messy code) but it places the mouse in unexpected locations and prevents using the mouse and touch simultaneously as separate inputs when desired. (fixing this will likely require a flag on the DOM element itself and not on a per event basis as the internal event queue will place latency between the OS and the browser dispatched events - I have solved this issue in my native apps )

I am testing with the web developer console so I can log status - I don't know if this influences things.

I have the same touch pan/zoom lack of control on Chrome. The mouse-up/down behavior is significantly different enough on Chrome vs Firefox to necessitate a separate code path. PK

For the code junkies - note this is an interface device "front end" for a "webasm" (game or game of life) engine :)

The objective is to get a "clean raw" event stream for any pointer entering the canvas element ( that is not captured by another element's handler ) Mirror the state of all buttons on all supported devices with captured pointers until capture is lost. Also to be able to use the mouse and touch (and any other supported devices) as separate devices ignoring all synthetic events and desiring to turn off anything where one device I am listening to is emulated by another device I am listening to (so they don't fight). Also to fully control what is done with the events until capture of the event stream for a pointer is lost. I would like to capture all touches that go down after and while the "primary" touch goes down on our element for a touch device (tablet, panel, screen etc) and optionally let them "fall through" if they are not used our UI.

Below are the event handlers which are attached to a "canvas" element. They don't do anything but forward the data to the webasm application. Of course will be cleaned up after it works. This is Firefox code. :)

canvas.addEventListener('pointerover', this.onPeOED, false);
canvas.addEventListener('pointerenter', this.onPeOED, false);
canvas.addEventListener('pointerdown', this.onPeOED, false);

canvas.addEventListener('pointermove', this.onPeMove, false);

canvas.addEventListener('pointerup', this.onPeUOCL, false);
canvas.addEventListener('pointerout', this.onPeUOCL, false);
canvas.addEventListener('pointercancel', this.onPeUOCL, false);
canvas.addEventListener('pointerleave', this.onPeUOCL, false);

canvas.addEventListener('mousedown', this.onMouseDown, false);
canvas.addEventListener('mouseup', this.onMouseUp, false);

 onPeOED: (function(e) {
    var handled = 0;
    var code = e.type.charCodeAt(7);
    var target = e.currentTarget;
    var posX = e.clientX;
    var posY = e.clientY;

    var flags = (1 << (9 + 16)); // fPointerInside
    if(posX < 0 || posY < 0 || posX >= target.width || posY >= target.height) {
        flags = 0;
    }
    // o == over, e == enter, d == down,

    if(code == 100) { // 100 == 'd'
        flags |= (1 << (10 + 16)); // in contact << 16
    }

    target._mousePointerId_ = -1;

    switch(e.pointerType) {
        case 'mouse':
            target._mousePointerId_ = e.pointerId;
            if(code == 100) { // 100 == 'd' handled by onMouseDown
                target.setPointerCapture(e.pointerId);
                return;
            }
            flags |= 0x0100;  // UID_MOUSE << 8
            break;
        case 'pen':
            flags |= 0x0200;  // UID_STYLUS << 8
            break;
        case 'touch':
            flags |= 0x0300;  // UID_FINGER << 8
            break;
        default:
            Module.print("unknown UID");
            break;
    }

    if(code == 100) { // 100 == 'd'
        target.setPointerCapture(e.pointerId);
    }

    // Module.print("ON uid " + e.pointerType + " " + e.type + " buttons " + e.which + " inside " + ((flags & (1 << (9 + 16))) != 0));

    if(e.buttons != 0) {
        flags |= (e.buttons << 16) | (1 << (10 + 16)); // fPointerInContact
    }

    handled = ccall('onPointerFlagsOn', 'number', EngineConnector.number_11_Sig,
        //  target  type|flags    time      id     screenX  screenY   pageX    pageY   targetX  targetY  pressure
        [   target._CPPHandle_,
            (code | flags),
            e.timeStamp,
            e.pointerId,
            e.screenX,
            e.screenY,
            e.pageX,
            e.pageY,
            posX,
            posY,
            e.pressure
        ]);
    e.preventDefault();
}),

onMouseDown: (function(e) {

    var target = e.currentTarget;
    var posX = e.clientX;
    var posY = e.clientY;

    // Module.print("mouse ME down " + target._mousePointerId_);

    if(target._mousePointerId_ < 0 || posX < 0 || posY < 0 || posX >= target.width || posY >= target.height) {
        Module.print("mouse ignored");
        e.preventDefault();
        return; // ignore downs while outide element
    }
    var wentDown = (e.buttons & (~(target._mouseButtons_)));
    target._mouseButtons_ = e.buttons;

    // Module.print("mouse down " + e.buttons + " went down " + wentDown);

    handled = ccall('onPointerFlagsOn', 'number', EngineConnector.number_11_Sig,
        //  target    type    time      id     screenX  screenY   pageX    pageY   targetX  targetY  pressure
        [   target._CPPHandle_,
          // 'd'  UID_MOUSE                       fPointerInside  fPointerInContact
            (100 | 0x0100 | (1 << (9 + 16)) | (1 << (10 + 16)) | (wentDown << 16)),
            e.timeStamp,
            e.pointerId,
            e.screenX,
            e.screenY,
            e.pageX,
            e.pageY,
            posX,
            posY,
            e.pressure
        ]);
    // e.preventDefault();

}),
fireButtonUp: (function(e, buttons) {

    // Module.print("ME fire up true " + buttons );

    var ptrId = e.currentTarget._mousePointerId_;
    if(ptrId < 0) {
        return;
    }

    ccall('onPointerFlagsOff', 'number', EngineConnector.number_11_Sig,
        //  target    type    time      id     screenX  screenY   pageX    pageY   targetX  targetY  pressure
        [e.currentTarget._CPPHandle_,
            (117 | 0x0100 | (buttons << 16)),  // 117 == 'u' == 'up', 1 == UID_MOUSE
            e.timeStamp,
            ptrId,
            e.screenX,
            e.screenY,
            e.pageX,
            e.pageY,
            e.clientX,
            e.clientY,
            0,
        ]);

}),
// up/leave
onMouseUp: (function(e) {

    var target = e.currentTarget;
    var posX = e.clientX;
    var posY = e.clientY;

    if(posX < 0 || posY < 0 || posX >= target.width || posY >= target.height) {
        // we have to kill ALL the buttons
        EngineConnector.fireButtonUp(e,target._mouseButtons_ | (1 << 10));
        target._mouseButtons_ = 0;
    } else {
        var released = (target._mouseButtons_ & (~e.buttons)); // will leave the one we released
        target._mouseButtons_ = e.buttons;
        if(e.buttons == 0) {
            released |= (1 << 10); // fPointerInContact
        }
        EngineConnector.fireButtonUp(e,(released | (1 << 9)));
    }
    e.preventDefault();

}),

onPeMove: (function(e) {

    var target = e.currentTarget;
    var posX = e.clientX;
    var posY = e.clientY;

    var flags = (1 << 9);
    if(posX < 0 || posY < 0 || posX >= target.width || posY >= target.height) {
        flags = 0;
    }

    var pType =  e.pointerType.charCodeAt(0);
    if(pType == 109) { // 'm' == 109
        // MOUSE
        if(target._mousePointerId_ < 0) {
            e.preventDefault();
            return;
        }
    } else {
        Module.print("move !!!");
    }

    flags |= pType; // 'm', 'p', 't'

    handled = ccall('onPointerMove', 'number', EngineConnector.number_11_Sig,
        //  target    flags time      id     screenX  screenY   pageX    pageY   targetX  targetY  pressure
        [ target._CPPHandle_,
            flags,
            e.timeStamp,
            e.pointerId,
            e.screenX,
            e.screenY,
            e.pageX,
            e.pageY,
            posX,
            posY,
            e.pressure
        ]);
}),
onPeUOCL: (function(e) {

    var code = e.type.charCodeAt(7);
    var target = e.currentTarget;
    target._poinsePointerId_ = -1;

    switch(e.pointerType) {
        case 'mouse':
            target._poinsePointerId_ = e.pointerId;
            if(code == 117) { // 117 == 'u' mouseup handled by mouse event handler
                return;
            }
            code |= 0x0100;  // UID_MOUSE << 8
            // let leave and cancel get reported
            break;
        case 'pen':
            code |= 0x0200;  // UID_STYLUS << 8
            if(code == 117) {
                code |= ((1 << 16) << e.which);
            }
            break;
        case 'touch':
            code |= 0x0300;  // UID_FINGER << 8
            if(code == 117) {
                code |= ((1 << 16) << e.which);
            }
            break;
        default:
            Module.print("unknown UID");
            break;
    }

    var posX = e.clientX;
    var posY = e.clientY;

    if(posX >= 0 && posY >= 0 || posX < target.width || posY < target.height) {
        code |= (1 << (9 + 16));
    }

    // Module.print("off uid " + e.pointerType + " " + e.type + " buttons " + e.which + " inside " + ((code & (1 << (9 + 16))) != 0));

    var handled = 0;

    handled = ccall('onPointerFlagsOff', 'number', EngineConnector.number_11_Sig,
        //  target    type    time      id     screenX  screenY   pageX    pageY   targetX  targetY  pressure
        [e.currentTarget._CPPHandle_,
            code,
            e.timeStamp,
            e.pointerId,
            e.screenX,
            e.screenY,
            e.pageX,
            e.pageY,
            posX,
            posY,
            e.pressure,
        ]);
    e.preventDefault();
}),

A quote from Me when I was working on getting Microsoft to create DirectX almost 30 years ago - it's Deja Vous all over again :)

"For any system to be truly flexible one must be able to implement that system from scratch with the facilities it provides."

0 answers