Skip to content

Commit

Permalink
Implement TouchpadMode gesture handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
bitbound committed Feb 4, 2024
1 parent 810d294 commit 6175985
Show file tree
Hide file tree
Showing 6 changed files with 457 additions and 41 deletions.
58 changes: 50 additions & 8 deletions app/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const UI = {
});

// Adapt the interface for touch screen devices
if (isTouchDevice) {
if (isTouchDevice()) {
// Remove the address bar
setTimeout(() => window.scrollTo(0, 1), 100);
}
Expand Down Expand Up @@ -464,6 +464,12 @@ const UI = {
.classList.remove('noVNC_open');
},

/**
* @param {string} text
* @param { "normal" | "info" | "warn" | "warning" | "error" } statusType
* @param {number} time
* @returns
*/
showStatus(text, statusType, time) {
const statusElem = document.getElementById('noVNC_status');

Expand Down Expand Up @@ -1064,8 +1070,10 @@ const UI = {
UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
UI.rfb.showDotCursor = UI.getSetting('show_dot');
UI.rfb.touchpadMode = WebUtil.readSetting('touchpad_mode', 'false') === 'true';

UI.updateViewOnly(); // requires UI.rfb
UI.updateTouchpadMode();
},

disconnect() {
Expand Down Expand Up @@ -1119,6 +1127,12 @@ const UI = {

// Do this last because it can only be used on rendered elements
UI.rfb.focus();

// In touchpad mode, we want the cursor centered in the
// viewport at the start so we can see it.
if (UI.rfb.touchpadMode) {
UI.rfb.centerCursorInViewport();
}
},

disconnectFinished(e) {
Expand Down Expand Up @@ -1348,7 +1362,7 @@ const UI = {
// Can't be clipping if viewport is scaled to fit
UI.forceSetting('view_clip', false);
UI.rfb.clipViewport = false;
} else if (brokenScrollbars) {
} else if (brokenScrollbars || UI.rfb.touchpadMode) {
UI.forceSetting('view_clip', true);
UI.rfb.clipViewport = true;
} else {
Expand All @@ -1372,13 +1386,18 @@ const UI = {

UI.rfb.dragViewport = !UI.rfb.dragViewport;
UI.updateViewDrag();
UI.updateTouchpadMode();
},

updateViewDrag() {
if (!UI.connected) return;

const viewDragButton = document.getElementById('noVNC_view_drag_button');

if (UI.rfb.dragViewport) {
UI.rfb.touchpadMode = false;
}

if ((!UI.rfb.clipViewport || !UI.rfb.clippingViewport) &&
UI.rfb.dragViewport) {
// We are no longer clipping the viewport. Make sure
Expand Down Expand Up @@ -1432,7 +1451,7 @@ const UI = {
* ------v------*/

showVirtualKeyboard() {
if (!isTouchDevice) return;
if (!isTouchDevice()) return;

const input = document.getElementById('noVNC_keyboardinput');

Expand All @@ -1450,7 +1469,7 @@ const UI = {
},

hideVirtualKeyboard() {
if (!isTouchDevice) return;
if (!isTouchDevice()) return;

const input = document.getElementById('noVNC_keyboardinput');

Expand Down Expand Up @@ -1599,12 +1618,33 @@ const UI = {
if (!UI.rfb) return;

UI.rfb.touchpadMode = !UI.rfb.touchpadMode;
UI.updateTouchpadButton();
WebUtil.writeSetting('touchpad_mode', UI.rfb.touchpadMode);
UI.updateTouchpadMode();
UI.updateViewDrag();
},

updateTouchpadButton() {
updateTouchpadMode() {
if (UI.rfb.touchpadMode) {
UI.rfb.dragViewport = false;

UI.forceSetting('resize', 'off');
UI.forceSetting('view_clip', true);
UI.forceSetting('show_dot', true);

UI.rfb.clipViewport = true;
UI.rfb.scaleViewport = false;
UI.rfb.resizeSession = false;
UI.rfb.showDotCursor = true;
}
else {
UI.enableSetting('resize');
UI.enableSetting('view_clip');
UI.enableSetting('show_dot');
}

UI.updateViewDrag

const touchpadButton = document.getElementById('noVNC_touchpad_button');

if (UI.rfb.touchpadMode) {
touchpadButton.classList.add("noVNC_selected");
} else {
Expand Down Expand Up @@ -1730,12 +1770,14 @@ const UI = {
.classList.add('noVNC_hidden');
document.getElementById('noVNC_clipboard_button')
.classList.add('noVNC_hidden');
document.getElementById('noVNC_clipboard_button')
.classList.add('noVNC_hidden');
} else {
document.getElementById('noVNC_keyboard_button')
.classList.remove('noVNC_hidden');
document.getElementById('noVNC_toggle_extra_keys_button')
.classList.remove('noVNC_hidden');
document.getElementById('noVNC_clipboard_button')
document.getElementById('noVNC_touchpad_button')
.classList.remove('noVNC_hidden');
}
},
Expand Down
34 changes: 34 additions & 0 deletions core/display.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,38 @@ export default class Display {
return this._fbHeight;
}

get viewportLocation() {
return this._viewportLoc;
}

// ===== PUBLIC METHODS =====

/**
* Attempt to move the viewport by the specified amounts
* and returns the amount of actual position change.
* @param {number} moveByX
* @param {number} moveByY
* @return {{ x: number, y: number }}
*/
viewportTryMoveBy(moveByX, moveByY) {
if (moveByX === 0 && moveByY === 0) {
return {
x: 0,
y: 0
}
}

const vpX = this._viewportLoc.x;
const vpY = this._viewportLoc.y;

this.viewportChangePos(moveByX, moveByY);

return {
x: this._viewportLoc.x - vpX,
y: this._viewportLoc.y - vpY
}
}

viewportChangePos(deltaX, deltaY) {
const vp = this._viewportLoc;
deltaX = Math.floor(deltaX);
Expand Down Expand Up @@ -433,6 +463,10 @@ export default class Display {
this._rescale(scaleRatio);
}

rescale(factor) {
this._rescale(factor);
}

// ===== PRIVATE METHODS =====

_rescale(factor) {
Expand Down
62 changes: 54 additions & 8 deletions core/input/gesturehandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const GH_PINCH = 64;

const GH_INITSTATE = 127;

const GH_MOVE_THRESHOLD = 50;
const GH_ANGLE_THRESHOLD = 90; // Degrees

// Timeout when waiting for gestures (ms)
Expand All @@ -38,6 +37,7 @@ export default class GestureHandler {
this._target = null;

this._state = GH_INITSTATE;
this._touchpadMode = false;

this._tracked = [];
this._ignored = [];
Expand All @@ -51,6 +51,37 @@ export default class GestureHandler {
this._boundEventHandler = this._eventHandler.bind(this);
}

// ===== PROPERTIES =====

/**
* @returns {boolean}
*/
get touchpadMode() {
return this._touchpadMode;
}

/**
* @param {boolean} enabled
*/
set touchpadMode(enabled) {
this._touchpadMode = enabled;
}

/**
* @returns {number}
*/
get _ghMoveThreshold() {
// In TouchpadMode, we want movements to be very precise,
// so we'll reduce the movement threshold.
if (this._touchpadMode) {
return 5;
}

return 50;
}

// ===== PUBLIC METHODS =====

attach(target) {
this.detach();

Expand All @@ -64,7 +95,6 @@ export default class GestureHandler {
this._target.addEventListener('touchcancel',
this._boundEventHandler);
}

detach() {
if (!this._target) {
return;
Expand All @@ -84,6 +114,10 @@ export default class GestureHandler {
this._target = null;
}

/**
*
* @param {TouchEvent} e
*/
_eventHandler(e) {
let fn;

Expand All @@ -102,7 +136,6 @@ export default class GestureHandler {
fn = this._touchEnd;
break;
}

for (let i = 0; i < e.changedTouches.length; i++) {
let touch = e.changedTouches[i];
fn.call(this, touch.identifier, touch.clientX, touch.clientY);
Expand Down Expand Up @@ -142,9 +175,11 @@ export default class GestureHandler {
firstY: y,
lastX: x,
lastY: y,
angle: 0
movementX: 0,
movementY: 0,
angle: 0,
});

switch (this._tracked.length) {
case 1:
this._startLongpressTimeout();
Expand All @@ -164,7 +199,7 @@ export default class GestureHandler {
}
}

_touchMove(id, x, y) {
_touchMove(id, x, y) {
let touch = this._tracked.find(t => t.id === id);

// If this is an update for a touch we're not tracking, ignore it
Expand All @@ -173,6 +208,8 @@ export default class GestureHandler {
}

// Update the touches last position with the event coordinates
touch.movementX = x - touch.lastX;
touch.movementY = y - touch.lastY;
touch.lastX = x;
touch.lastY = y;

Expand All @@ -187,7 +224,7 @@ export default class GestureHandler {

if (!this._hasDetectedGesture()) {
// Ignore moves smaller than the minimum threshold
if (Math.hypot(deltaX, deltaY) < GH_MOVE_THRESHOLD) {
if (Math.hypot(deltaX, deltaY) < this._ghMoveThreshold) {
return;
}

Expand Down Expand Up @@ -216,7 +253,7 @@ export default class GestureHandler {
// We know that the current touch moved far enough,
// but unless both touches moved further than their
// threshold we don't want to disqualify any gestures
if (prevDeltaMove > GH_MOVE_THRESHOLD) {
if (prevDeltaMove > this._ghMoveThreshold) {

// The angle difference between the direction of the touch points
let deltaAngle = Math.abs(touch.angle - prevTouch.angle);
Expand Down Expand Up @@ -458,6 +495,15 @@ export default class GestureHandler {
detail['clientX'] = pos.x;
detail['clientY'] = pos.y;

if (this._touchpadMode &&
this._tracked.length === 1) {

const touch = this._tracked[0];

detail['movementX'] = touch.movementX;
detail['movementY'] = touch.movementY;
}

// FIXME: other coordinates?

// Some gestures also have a magnitude
Expand Down
Loading

0 comments on commit 6175985

Please sign in to comment.