Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native touch gestures support with UltraVNC #1908

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2b738be
Add UltraVNC touch gestures support
May 28, 2024
276836a
Merge branch 'novnc:master' into feature/ultravnc-gestures
EvilAngel00 Jul 31, 2024
c1efe1f
Fix local scaling touch position
Jul 31, 2024
6b770c9
Fix touches with same id not being removed properly
Jul 31, 2024
43ddc6c
Add preventDefault in UltraVNC touch and manage touch ids manually
Sep 24, 2024
56f932d
Merge branch 'novnc:master' into feature/ultravnc-gestures
EvilAngel00 Sep 24, 2024
65bc414
Merge branch 'novnc:master' into feature/ultravnc-gestures
EvilAngel00 Oct 24, 2024
88a5a59
Fix typos in Russian translation
dim5x Nov 2, 2024
43326eb
Fix handling of VideoDecoder.isConfigSupported()
CendioOssman Nov 20, 2024
89e0591
Use common H.264 check in tests
CendioOssman Nov 20, 2024
69750c7
Raise JavaScript version requirement
CendioOssman Nov 21, 2024
3677afe
Do a real H.264 test decode to determine support
CendioOssman Nov 21, 2024
a89dfd6
Handle exceptions from VideoDecoder.flush()
CendioOssman Nov 21, 2024
2463ccd
Detect broken Firefox H.264 decoder
CendioOssman Nov 21, 2024
90a6c7b
Update zh_CN.po
wxtewx Nov 23, 2024
7f5b51a
Consistently use "sentence case" style
CendioOssman Nov 27, 2024
7335bb4
Also adjust to "sentence case" in translations
CendioOssman Nov 27, 2024
52ddb20
Merge branch 'master' of https://github.com/wxtewx/noVNC
CendioOssman Nov 27, 2024
663a198
Add UltraVNC touch gestures support
May 28, 2024
8af21c7
Fix local scaling touch position
Jul 31, 2024
b673af9
Fix touches with same id not being removed properly
Jul 31, 2024
d78d875
Add preventDefault in UltraVNC touch and manage touch ids manually
Sep 24, 2024
8116919
Merge branch 'feature/ultravnc-gestures' of https://github.com/twobra…
Dec 10, 2024
ac91941
Change gestures selector to dropdown in UI, use constants instead of …
Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion app/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ const UI = {
UI.initSetting('bell', 'on');
UI.initSetting('view_only', false);
UI.initSetting('show_dot', false);
UI.initSetting('ultravnc_gestures', false);
UI.initSetting('path', 'websockify');
UI.initSetting('repeaterID', '');
UI.initSetting('reconnect', false);
Expand Down Expand Up @@ -371,6 +372,7 @@ const UI = {
UI.addSettingChangeHandler('view_only', UI.updateViewOnly);
UI.addSettingChangeHandler('show_dot');
UI.addSettingChangeHandler('show_dot', UI.updateShowDotCursor);
UI.addSettingChangeHandler('ultravnc_gestures');
UI.addSettingChangeHandler('host');
UI.addSettingChangeHandler('port');
UI.addSettingChangeHandler('path');
Expand Down Expand Up @@ -441,6 +443,7 @@ const UI = {
UI.disableSetting('port');
UI.disableSetting('path');
UI.disableSetting('repeaterID');
UI.disableSetting('ultravnc_gestures');

// Hide the controlbar after 2 seconds
UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
Expand All @@ -451,6 +454,7 @@ const UI = {
UI.enableSetting('port');
UI.enableSetting('path');
UI.enableSetting('repeaterID');
UI.enableSetting('ultravnc_gestures');
UI.updatePowerButton();
UI.keepControlbar();
}
Expand Down Expand Up @@ -1072,7 +1076,8 @@ const UI = {
url.href,
{ shared: UI.getSetting('shared'),
repeaterID: UI.getSetting('repeaterID'),
credentials: { password: password } });
credentials: { password: password },
useUltraVNCGestures: UI.getSetting('ultravnc_gestures') });
} catch (exc) {
Log.Error("Failed to connect to server: " + exc);
UI.updateVisualState('disconnected');
Expand Down
1 change: 1 addition & 0 deletions core/encodings.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const encodings = {
pseudoEncodingCursor: -239,
pseudoEncodingQEMUExtendedKeyEvent: -258,
pseudoEncodingQEMULedEvent: -261,
pseudoEncodingGii: -305,
pseudoEncodingDesktopName: -307,
pseudoEncodingExtendedDesktopSize: -308,
pseudoEncodingXvp: -309,
Expand Down
128 changes: 128 additions & 0 deletions core/input/touchhandlerultravnc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import * as Log from '../util/logging.js';

export default class TouchHandlerUltraVNC {
static PF_flag = 0x80000000; // Pressed Flag : active if the touch event is pressed, inactive if it's being released.
static R1_flag = 0x40000000; // Reserved 1
static IF_flag = 0x20000000; // Primary Flag : active if the touch event is the primary touch event.
static S1_flag = 0x10000000; // Size Flag : active if the message contains information about the size of the touch event. The events are currently all sent as symetrical ellipses.
static S2_flag = 0x8000000; // Reserved for asymetrical ellipses. Not supported yet and should be 0.
static RT_flag = 0x4000000; // Rectangle : the touch event is a rectangle instead of an ellipse.
static PR_flag = 0x2000000; // Pressure Flag : pressure of the touch. Currently unused.
static TI_flag = 0x1000000; // Timestamp : the timestamp of the touch event.
static HC_flag = 0x800000; // High Performance Counter

static LENGTH_16_flag = 0x10; // 16 bits signed for x touch coordinate followed by 16 bits signed for y together in a 32 bits word
static IDFORMAT_32 = 0x1; // 32 bits ID
static IDFORMAT_CLEAR = 0xF; // No more touch points

constructor() {
this._target = null;

this._currentTouches = [];
this._sendTouchesIntervalId = -1;
this._giiDeviceOrigin = 0;
this._isUltraVNCTouchActivated = false;

this._boundEventHandler = this._handleTouch.bind(this);
}

attach(target) {
this.detach();

this._target = target;
this._target.addEventListener('touchstart',
this._boundEventHandler);
this._target.addEventListener('touchmove',
this._boundEventHandler);
this._target.addEventListener('touchend',
this._boundEventHandler);
this._target.addEventListener('touchcancel',
this._boundEventHandler);
}

detach() {
if (!this._target) {
return;
}

this._target.removeEventListener('touchstart',
this._boundEventHandler);
this._target.removeEventListener('touchmove',
this._boundEventHandler);
this._target.removeEventListener('touchend',
this._boundEventHandler);
this._target.removeEventListener('touchcancel',
this._boundEventHandler);

clearInterval(this._sendTouchesIntervalId);
this._sendTouchesIntervalId = -1;

this._target = null;
}

_handleTouch(ev) {
ev.preventDefault();
ev.stopImmediatePropagation();

if (!this._isUltraVNCTouchActivated) {
return;
}

if (ev.type === "touchstart") {
for (let i = 0; i < ev.changedTouches.length; i++) {
ev.changedTouches[i].touchIdentifier = this._getTouchIdentifier();
this._currentTouches.push({ event: ev.changedTouches[i], status: "POINTER_DOWN" });
}

if (this._sendTouchesIntervalId === -1 && this._target) {
this._dispatchTouchEvent(ev);
this._sendTouchesIntervalId = setInterval(() => {
this._dispatchTouchEvent(ev);
}, 200);
}
} else if (ev.type === "touchmove") {
for (let i = 0; i < ev.changedTouches.length; i++) {
const index = this._currentTouches.findIndex(t => t.event.identifier === ev.changedTouches[i].identifier);
if (index !== -1) {
ev.changedTouches[i].touchIdentifier = this._currentTouches[index].event.touchIdentifier;
this._currentTouches[index].event = ev.changedTouches[i];
this._currentTouches[index].status = "POINTER_UPDATE";
}
}
} else if (ev.type === "touchend" || ev.type === "touchcancel") {
for (let i = 0; i < ev.changedTouches.length; i++) {
const indexes = this._getAllIndexes(this._currentTouches, (t) => t.event.identifier === ev.changedTouches[i].identifier)
indexes.forEach((index) => this._currentTouches[index].status = "POINTER_UP");
}
}
}

_getAllIndexes(arr, func) {
var indexes = [], i;
for (i = 0; i < arr.length; i++)
if (func(arr[i]))
indexes.push(i);
return indexes;
}

_getTouchIdentifier() {
const ids = this._currentTouches.map((ev) => ev.event.touchIdentifier);
let i = 0;
while (ids.includes(i)) { i++; }
return i;
}

_dispatchTouchEvent(ev) {
let tev = new CustomEvent('ultravnctouch', { event: ev, detail: { currentTouches: this._currentTouches, giiDeviceOrigin: this._giiDeviceOrigin } });
this._target.dispatchEvent(tev);
}

_removeTouch(index) {
this._currentTouches.splice(index, 1);
}

_interruptTouches() {
clearInterval(this._sendTouchesIntervalId);
this._sendTouchesIntervalId = -1;
}
}
Loading