mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-10-21 05:01:26 -07:00
feat(overlay): forward mpv mouse button and wheel bindings (#279)
This commit is contained in:
1 parent
9fadbc8cdd
commit
9e79fa93c2
18 files changed
+500
-76
No files matched your search
@@ -4,16 +4,19 @@ import {
|
||||
keyboardEventToMpvKey,
|
||||
normalizeMpvInputKey,
|
||||
parseMpvInputBindingKeys,
|
||||
wheelEventToMpvWheel,
|
||||
} from './mpv-input-bindings';
|
||||
|
||||
test('mpv discovery validates entries and excludes inactive, mouse, sequence, and SubMiner keys', () => {
|
||||
test('mpv discovery validates entries and excludes inactive, sequence, and SubMiner keys', () => {
|
||||
assert.deepEqual(
|
||||
parseMpvInputBindingKeys([
|
||||
{ key: 'r', cmd: 'script-binding replay/run', priority: 1, owner: 'replay' },
|
||||
{ key: 'r', cmd: 'show-text duplicate', priority: 0 },
|
||||
{ key: 'Ctrl+A', cmd: 'show-text shifted', priority: 1 },
|
||||
{ key: 'g-g', cmd: 'seek 0', priority: 1 },
|
||||
{ key: 'MBTN_LEFT', cmd: 'cycle pause', priority: 1 },
|
||||
{ key: 'MBTN_LEFT_DBL', cmd: 'cycle fullscreen', priority: 1 },
|
||||
{ key: 'MOUSE_MOVE', cmd: 'script-binding osc/move', priority: 1 },
|
||||
{ key: 'Shift+WHEEL_UP', cmd: 'add volume 2', priority: 1 },
|
||||
{ key: 'q', cmd: 'quit', priority: -1 },
|
||||
{ key: 's', cmd: 'screenshot', priority: 1 },
|
||||
{ key: 's', cmd: 'script-binding subminer/session', priority: 5, owner: 'subminer' },
|
||||
@@ -22,7 +25,7 @@ test('mpv discovery validates entries and excludes inactive, mouse, sequence, an
|
||||
{ key: 'z', cmd: 5, priority: 1 },
|
||||
null,
|
||||
]),
|
||||
['r', 'ctrl+A'],
|
||||
['r', 'ctrl+A', 'MBTN_LEFT_DBL', 'shift+WHEEL_UP'],
|
||||
);
|
||||
assert.deepEqual(parseMpvInputBindingKeys({ key: 'r' }), []);
|
||||
});
|
||||
@@ -33,6 +36,27 @@ test('mpv keys retain printable characters and normalize modifiers', () => {
|
||||
assert.equal(normalizeMpvInputKey('Shift+LEFT'), 'shift+LEFT');
|
||||
assert.equal(normalizeMpvInputKey('F12'), 'F12');
|
||||
assert.equal(normalizeMpvInputKey('UNMAPPED'), null);
|
||||
assert.equal(normalizeMpvInputKey('Ctrl+WHEEL_DOWN'), 'ctrl+WHEEL_DOWN');
|
||||
});
|
||||
|
||||
test('wheel conversion picks the dominant axis and reports mpv notch scale', () => {
|
||||
assert.deepEqual(wheelEventToMpvWheel({ deltaX: 0, deltaY: -240, deltaMode: 0 }), {
|
||||
key: 'WHEEL_UP',
|
||||
notches: 2,
|
||||
});
|
||||
assert.deepEqual(wheelEventToMpvWheel({ deltaX: 3, deltaY: 30, deltaMode: 0 }), {
|
||||
key: 'WHEEL_DOWN',
|
||||
notches: 0.25,
|
||||
});
|
||||
assert.deepEqual(wheelEventToMpvWheel({ deltaX: 120, deltaY: 0, deltaMode: 0 }), {
|
||||
key: 'WHEEL_RIGHT',
|
||||
notches: 1,
|
||||
});
|
||||
assert.deepEqual(wheelEventToMpvWheel({ deltaX: -3, deltaY: 0, deltaMode: 1 }), {
|
||||
key: 'WHEEL_LEFT',
|
||||
notches: 1,
|
||||
});
|
||||
assert.equal(wheelEventToMpvWheel({ deltaX: 0, deltaY: 0, deltaMode: 0 }), null);
|
||||
});
|
||||
|
||||
test('keyboard conversion respects layout characters and skips composition and AltGr', () => {
|
||||
|
||||
@@ -16,13 +16,33 @@ const SPECIAL_KEYS: Record<string, string> = {
|
||||
ArrowUp: 'UP',
|
||||
ArrowDown: 'DOWN',
|
||||
};
|
||||
const MPV_SPECIAL_KEYS = new Set(Object.values(SPECIAL_KEYS));
|
||||
const MPV_WHEEL_KEYS = ['WHEEL_UP', 'WHEEL_DOWN', 'WHEEL_LEFT', 'WHEEL_RIGHT'] as const;
|
||||
export type MpvWheelKey = (typeof MPV_WHEEL_KEYS)[number];
|
||||
// DOM MouseEvent.button to mpv's mouse button names.
|
||||
export const MPV_MOUSE_BUTTON_BY_BUTTON: Readonly<Record<number, string>> = {
|
||||
0: 'MBTN_LEFT',
|
||||
1: 'MBTN_MID',
|
||||
2: 'MBTN_RIGHT',
|
||||
3: 'MBTN_BACK',
|
||||
4: 'MBTN_FORWARD',
|
||||
};
|
||||
// mpv synthesizes these from two quick presses of the base button.
|
||||
const MPV_DOUBLE_CLICK_KEYS = ['MBTN_LEFT_DBL', 'MBTN_MID_DBL', 'MBTN_RIGHT_DBL'];
|
||||
const MPV_SPECIAL_KEYS = new Set<string>([
|
||||
...Object.values(SPECIAL_KEYS),
|
||||
...MPV_WHEEL_KEYS,
|
||||
...Object.values(MPV_MOUSE_BUTTON_BY_BUTTON),
|
||||
...MPV_DOUBLE_CLICK_KEYS,
|
||||
]);
|
||||
// Chromium reports 120 px per wheel notch on both Wayland and X11.
|
||||
const WHEEL_NOTCH_PIXELS = 120;
|
||||
const WHEEL_NOTCH_LINES = 3;
|
||||
// Leading command flags accepted by mpv's input/cmd.c, before the command name.
|
||||
const MPV_COMMAND_PREFIXES =
|
||||
/^(?:(?:no-osd|osd-bar|osd-msg|osd-msg-bar|osd-auto|expand-properties|raw|repeatable|nonrepeatable|nonscalable|async|sync)\s+)+/;
|
||||
|
||||
// Only single keyboard strokes are imported. Mouse input and sequences need
|
||||
// their own focus and conflict rules before they can be forwarded safely.
|
||||
// Single keyboard strokes, mouse buttons, and wheel scrolls are imported. Key sequences
|
||||
// and pointer motion (MOUSE_MOVE) are not.
|
||||
export function normalizeMpvInputKey(value: string): string | null {
|
||||
const modifiers = new Set<string>();
|
||||
let key = value;
|
||||
@@ -59,6 +79,27 @@ export function keyboardEventToMpvKey(
|
||||
return normalizeMpvInputKey([...modifiers, key].join('+'));
|
||||
}
|
||||
|
||||
// Maps a DOM wheel event to mpv's wheel key and its notch count, which mpv uses as the
|
||||
// precise-scroll scale for `keypress <key> <scale>`. Trackpads yield fractional notches.
|
||||
export function wheelEventToMpvWheel(
|
||||
event: Pick<WheelEvent, 'deltaX' | 'deltaY' | 'deltaMode'>,
|
||||
): { key: MpvWheelKey; notches: number } | null {
|
||||
const vertical = Math.abs(event.deltaY) >= Math.abs(event.deltaX);
|
||||
const delta = vertical ? event.deltaY : event.deltaX;
|
||||
if (!Number.isFinite(delta) || delta === 0) return null;
|
||||
const key: MpvWheelKey = vertical
|
||||
? delta < 0
|
||||
? 'WHEEL_UP'
|
||||
: 'WHEEL_DOWN'
|
||||
: delta < 0
|
||||
? 'WHEEL_LEFT'
|
||||
: 'WHEEL_RIGHT';
|
||||
// deltaMode: 0 = pixels, 1 = lines, 2 = pages.
|
||||
const unit =
|
||||
event.deltaMode === 1 ? WHEEL_NOTCH_LINES : event.deltaMode === 2 ? 1 : WHEEL_NOTCH_PIXELS;
|
||||
return { key, notches: Math.abs(delta) / unit };
|
||||
}
|
||||
|
||||
export function parseMpvInputBindingKeys(
|
||||
value: unknown,
|
||||
{ includeIgnored = true }: { includeIgnored?: boolean } = {},
|
||||
|
||||
Reference in new issue
Block a user