Feature/kasm 2346 extend mouse buttons (#39)

* Extend mouse event button mask to 16 bits

* Mouse button mapping (JS -> Xorg) support

* Refactor: extract XVNC_BUTTONS

* Refactor: XVNC_BUTTONS numbers correspond to X button numbers

* Refactor

Co-authored-by: Lauri Kasanen <cand@gmx.com>
This commit is contained in:
Dmitry Maksyoma 2022-10-15 05:52:19 +13:00 committed by GitHub
parent cac1f25ce4
commit 26c97c1dd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 103 additions and 12 deletions

View File

@ -41,6 +41,7 @@ import KeyTable from "../core/input/keysym.js";
import keysyms from "../core/input/keysymdef.js"; import keysyms from "../core/input/keysymdef.js";
import Keyboard from "../core/input/keyboard.js"; import Keyboard from "../core/input/keyboard.js";
import RFB from "../core/rfb.js"; import RFB from "../core/rfb.js";
import { MouseButtonMapper, XVNC_BUTTONS } from "../core/mousebuttonmapper.js";
import * as WebUtil from "./webutil.js"; import * as WebUtil from "./webutil.js";
const PAGE_TITLE = "KasmVNC"; const PAGE_TITLE = "KasmVNC";
@ -264,6 +265,24 @@ const UI = {
UI.setupSettingLabels(); UI.setupSettingLabels();
UI.updateQuality(); UI.updateQuality();
}, },
initMouseButtonMapper() {
const mouseButtonMapper = new MouseButtonMapper();
const settings = WebUtil.readSetting("mouseButtonMapper");
if (settings) {
mouseButtonMapper.load(settings);
return mouseButtonMapper;
}
mouseButtonMapper.set(0, XVNC_BUTTONS.LEFT_BUTTON);
mouseButtonMapper.set(1, XVNC_BUTTONS.MIDDLE_BUTTON);
mouseButtonMapper.set(2, XVNC_BUTTONS.RIGHT_BUTTON);
mouseButtonMapper.set(3, XVNC_BUTTONS.BACK_BUTTON);
mouseButtonMapper.set(4, XVNC_BUTTONS.FORWARD_BUTTON);
WebUtil.writeSetting("mouseButtonMapper", mouseButtonMapper.dump());
return mouseButtonMapper;
},
// Adds a link to the label elements on the corresponding input elements // Adds a link to the label elements on the corresponding input elements
setupSettingLabels() { setupSettingLabels() {
const labels = document.getElementsByTagName('LABEL'); const labels = document.getElementsByTagName('LABEL');
@ -1381,6 +1400,7 @@ const UI = {
UI.rfb.keyboard.enableIME = UI.getSetting('enable_ime'); UI.rfb.keyboard.enableIME = UI.getSetting('enable_ime');
UI.rfb.clipboardBinary = supportsBinaryClipboard() && UI.rfb.clipboardSeamless; UI.rfb.clipboardBinary = supportsBinaryClipboard() && UI.rfb.clipboardSeamless;
UI.rfb.enableWebRTC = UI.getSetting('enable_webrtc'); UI.rfb.enableWebRTC = UI.getSetting('enable_webrtc');
UI.rfb.mouseButtonMapper = UI.initMouseButtonMapper();
//Only explicitly request permission to clipboard on browsers that support binary clipboard access //Only explicitly request permission to clipboard on browsers that support binary clipboard access
if (supportsBinaryClipboard()) { if (supportsBinaryClipboard()) {

67
core/mousebuttonmapper.js Normal file
View File

@ -0,0 +1,67 @@
export const XVNC_BUTTONS = {
LEFT_BUTTON: 1,
MIDDLE_BUTTON: 2,
RIGHT_BUTTON: 3,
TURN_SCROLL_WHEEL_UP: 4,
TURN_SCROLL_WHEEL_DOWN: 5,
PUSH_SCROLL_WHEEL_LEFT: 6,
PUSH_SCROLL_WHEEL_RIGHT: 7,
BACK_BUTTON: 8,
FORWARD_BUTTON: 9
};
export function xvncButtonToMask(xvncButton) {
return 1 << (xvncButton - 1);
}
export default class MouseButtonMapper {
constructor() {
this.map = new Map();
}
get(mouseButton) {
if (!this.map.has(mouseButton)) {
return mouseButton;
}
return this.map.get(mouseButton);
}
set(mouseButton, xorgMouseButton) {
return this.map.set(mouseButton, xorgMouseButton);
}
delete(mouseButton) {
return this.map.delete(mouseButton);
}
dump() {
return JSON.stringify(this.map, this._replacer);
}
load(json) {
this.map = JSON.parse(json, this._reviver);
}
_replacer(key, value) {
if (!(value instanceof Map)) {
return value;
}
return {
dataType: 'Map',
value: Array.from(value.entries())
};
}
_reviver(key, value) {
if (typeof value === 'object' && value !== null) {
if (value.dataType === 'Map') {
return new Map(value.value);
}
}
return value;
}
}
export { MouseButtonMapper };

View File

@ -26,6 +26,7 @@ import DES from "./des.js";
import KeyTable from "./input/keysym.js"; import KeyTable from "./input/keysym.js";
import XtScancode from "./input/xtscancodes.js"; import XtScancode from "./input/xtscancodes.js";
import { encodings } from "./encodings.js"; import { encodings } from "./encodings.js";
import { MouseButtonMapper, xvncButtonToMask } from "./mousebuttonmapper.js";
import RawDecoder from "./decoders/raw.js"; import RawDecoder from "./decoders/raw.js";
import CopyRectDecoder from "./decoders/copyrect.js"; import CopyRectDecoder from "./decoders/copyrect.js";
@ -192,6 +193,7 @@ export default class RFB extends EventTargetMixin {
this._viewportHasMoved = false; this._viewportHasMoved = false;
this._accumulatedWheelDeltaX = 0; this._accumulatedWheelDeltaX = 0;
this._accumulatedWheelDeltaY = 0; this._accumulatedWheelDeltaY = 0;
this.mouseButtonMapper = null;
// Gesture state // Gesture state
this._gestureLastTapTime = null; this._gestureLastTapTime = null;
@ -1558,6 +1560,7 @@ export default class RFB extends EventTargetMixin {
this._canvas); this._canvas);
} }
const mappedButton = this.mouseButtonMapper.get(ev.button);
switch (ev.type) { switch (ev.type) {
case 'mousedown': case 'mousedown':
setCapture(this._canvas); setCapture(this._canvas);
@ -1575,11 +1578,11 @@ export default class RFB extends EventTargetMixin {
this.checkLocalClipboard(); this.checkLocalClipboard();
this._handleMouseButton(pos.x, pos.y, this._handleMouseButton(pos.x, pos.y,
true, 1 << ev.button); true, xvncButtonToMask(mappedButton));
break; break;
case 'mouseup': case 'mouseup':
this._handleMouseButton(pos.x, pos.y, this._handleMouseButton(pos.x, pos.y,
false, 1 << ev.button); false, xvncButtonToMask(mappedButton));
break; break;
case 'mousemove': case 'mousemove':
this._handleMouseMove(pos.x, pos.y); this._handleMouseMove(pos.x, pos.y);
@ -3659,21 +3662,22 @@ RFB.messages = {
buff[offset] = 5; // msg-type buff[offset] = 5; // msg-type
buff[offset + 1] = mask; buff[offset + 1] = mask >> 8;
buff[offset + 2] = mask;
buff[offset + 2] = x >> 8; buff[offset + 3] = x >> 8;
buff[offset + 3] = x; buff[offset + 4] = x;
buff[offset + 4] = y >> 8; buff[offset + 5] = y >> 8;
buff[offset + 5] = y; buff[offset + 6] = y;
buff[offset + 6] = dX >> 8; buff[offset + 7] = dX >> 8;
buff[offset + 7] = dX; buff[offset + 8] = dX;
buff[offset + 8] = dY >> 8; buff[offset + 9] = dY >> 8;
buff[offset + 9] = dY; buff[offset + 10] = dY;
sock._sQlen += 10; sock._sQlen += 11;
sock.flush(); sock.flush();
}, },