Skip to content

Commit 3ac57d8

Browse files
committed
fix(browser): mouse jiggler
Mouse jiggler feature of browser version has been reimplemented based on the desktop version. Revert Pull Request: #46 Reimplemented Reference: #47
1 parent 4296c58 commit 3ac57d8

9 files changed

Lines changed: 147 additions & 2 deletions

File tree

browser/src/components/menu/mouse/index.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ import { useAtom, useSetAtom } from 'jotai';
44
import { MouseIcon } from 'lucide-react';
55

66
import {
7+
mouseJigglerModeAtom,
78
mouseModeAtom,
89
mouseStyleAtom,
910
scrollDirectionAtom,
1011
scrollIntervalAtom
1112
} from '@/jotai/mouse.ts';
13+
import { mouseJiggler } from '@/libs/mouse-jiggler';
1214
import * as storage from '@/libs/storage';
1315

1416
import { Direction } from './direction.tsx';
17+
import { Jiggler } from './jiggler.tsx';
1518
import { Mode } from './mode.tsx';
1619
import { Speed } from './speed.tsx';
1720
import { Style } from './style.tsx';
@@ -21,6 +24,7 @@ export const Mouse = () => {
2124
const [mouseMode, setMouseMode] = useAtom(mouseModeAtom);
2225
const setScrollDirection = useSetAtom(scrollDirectionAtom);
2326
const setScrollInterval = useSetAtom(scrollIntervalAtom);
27+
const setMouseJigglerMode = useSetAtom(mouseJigglerModeAtom);
2428

2529
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
2630

@@ -48,6 +52,10 @@ export const Mouse = () => {
4852
if (interval) {
4953
setScrollInterval(interval);
5054
}
55+
56+
const jiggler = storage.getMouseJigglerMode();
57+
mouseJiggler.setMode(jiggler);
58+
setMouseJigglerMode(jiggler);
5159
}
5260

5361
const content = (
@@ -56,6 +64,7 @@ export const Mouse = () => {
5664
<Mode />
5765
<Direction />
5866
<Speed />
67+
<Jiggler />
5968
</>
6069
);
6170

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { useEffect } from 'react';
2+
import { Popover } from 'antd';
3+
import clsx from 'clsx';
4+
import { useAtom } from 'jotai';
5+
import { MousePointerIcon } from 'lucide-react';
6+
import { useTranslation } from 'react-i18next';
7+
8+
import { mouseJigglerModeAtom } from '@/jotai/mouse.ts';
9+
import { mouseJiggler } from '@/libs/mouse-jiggler';
10+
import * as storage from '@/libs/storage';
11+
12+
export const Jiggler = () => {
13+
const { t } = useTranslation();
14+
const [jigglerMode, setJigglerMode] = useAtom(mouseJigglerModeAtom);
15+
16+
const mouseJigglerModes: { name: string; value: 'enable' | 'disable' }[] = [
17+
{ name: t('mouse.jiggler.enable'), value: 'enable' },
18+
{ name: t('mouse.jiggler.disable'), value: 'disable' }
19+
];
20+
21+
function update(mode: 'enable' | 'disable'): void {
22+
storage.setMouseJigglerMode(mode);
23+
setJigglerMode(mode);
24+
}
25+
26+
useEffect(() => {
27+
mouseJiggler.setMode(jigglerMode);
28+
}, [jigglerMode]);
29+
30+
const content = (
31+
<>
32+
{mouseJigglerModes.map((mode) => (
33+
<div
34+
key={mode.value}
35+
className={clsx(
36+
'my-1 flex cursor-pointer items-center space-x-1 rounded py-1 pr-5 pl-2 hover:bg-neutral-700/50',
37+
mode.value === jigglerMode ? 'text-blue-500' : 'text-neutral-300'
38+
)}
39+
onClick={() => update(mode.value)}
40+
>
41+
{mode.name}
42+
</div>
43+
))}
44+
</>
45+
);
46+
return (
47+
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
48+
<div className="flex h-[30px] cursor-pointer items-center space-x-1 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
49+
<div className="flex h-[14px] w-[20px] items-end">
50+
<MousePointerIcon size={16} />
51+
</div>
52+
<span>{t('mouse.jiggler.title')}</span>
53+
</div>
54+
</Popover>
55+
);
56+
};

browser/src/components/mouse/absolute.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { resolutionAtom } from '@/jotai/device.ts';
55
import { scrollDirectionAtom, scrollIntervalAtom } from '@/jotai/mouse.ts';
66
import { device } from '@/libs/device';
77
import { Key } from '@/libs/device/mouse.ts';
8+
import { mouseJiggler } from '@/libs/mouse-jiggler';
89

910
export const Absolute = () => {
1011
const resolution = useAtomValue(resolutionAtom);
@@ -74,6 +75,8 @@ export const Absolute = () => {
7475
async function handleMouseMove(event: any) {
7576
disableEvent(event);
7677
await send(event);
78+
79+
mouseJiggler.moveEventCallback();
7780
}
7881

7982
// mouse scroll

browser/src/components/mouse/relative.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { resolutionAtom } from '@/jotai/device.ts';
77
import { scrollDirectionAtom, scrollIntervalAtom } from '@/jotai/mouse.ts';
88
import { device } from '@/libs/device';
99
import { Key } from '@/libs/device/mouse.ts';
10+
import { mouseJiggler } from '@/libs/mouse-jiggler';
1011

1112
export const Relative = () => {
1213
const { t } = useTranslation();
@@ -110,6 +111,8 @@ export const Relative = () => {
110111
if (x === 0 && y === 0) return;
111112

112113
await send(Math.abs(x) < 10 ? x * 2 : x, Math.abs(y) < 10 ? y * 2 : y, 0);
114+
115+
mouseJiggler.moveEventCallback();
113116
}
114117

115118
// mouse scroll

browser/src/i18n/locales/en.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,12 @@ const en = {
6060
speed: 'Wheel speed',
6161
fast: 'Fast',
6262
slow: 'Slow',
63-
requestPointer: 'Using relative mode. Please click desktop to get mouse pointer.'
63+
requestPointer: 'Using relative mode. Please click desktop to get mouse pointer.',
64+
jiggler: {
65+
title: 'Mouse Jiggler',
66+
enable: 'Enable',
67+
disable: 'Disable'
68+
}
6469
},
6570
settings: {
6671
language: 'Language',

browser/src/i18n/locales/zh.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,12 @@ const zh = {
5858
speed: '滚轮速度',
5959
fast: '快',
6060
slow: '慢',
61-
requestPointer: '正在使用鼠标相对模式,请点击桌面获取鼠标指针。'
61+
requestPointer: '正在使用鼠标相对模式,请点击桌面获取鼠标指针。',
62+
jiggler: {
63+
title: '空闲晃动',
64+
enable: '启用',
65+
disable: '禁用'
66+
}
6267
},
6368
settings: {
6469
language: '语言',

browser/src/jotai/mouse.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ export const scrollDirectionAtom = atom(1);
1212
// mouse scroll interval (unit: ms)
1313
// mouse scroll interval (unit: ms)
1414
export const scrollIntervalAtom = atom(0);
15+
16+
// mouse jiggler mode: enable or disable
17+
export const mouseJigglerModeAtom = atom<'enable' | 'disable'>('disable');
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { device } from '@/libs/device';
2+
import { Key } from '@/libs/device/mouse.ts';
3+
4+
const MOUSE_JIGGLER_INTERVAL = 15_000;
5+
const EMPTY_KEY: Key = new Key(false, false, false);
6+
7+
class MouseJiggler {
8+
private lastMoveTime: number;
9+
private timer: number | null;
10+
private mode: 'enable' | 'disable';
11+
12+
constructor() {
13+
this.lastMoveTime = Date.now();
14+
this.timer = null;
15+
this.mode = 'disable';
16+
}
17+
18+
// enable or disable mouse jiggler
19+
setMode(mode: 'enable' | 'disable'): void {
20+
this.mode = mode;
21+
if (mode === 'disable' && this.timer !== null) {
22+
clearInterval(this.timer);
23+
this.timer = null;
24+
} else if (mode === 'enable' && this.timer === null) {
25+
this.timer = setInterval(() => {
26+
this.timeoutCallback();
27+
}, MOUSE_JIGGLER_INTERVAL / 5);
28+
}
29+
}
30+
31+
// addEventListener to canvas on 'mousemove' event
32+
moveEventCallback(): void {
33+
if (this.mode === 'enable') {
34+
this.lastMoveTime = Date.now();
35+
}
36+
}
37+
38+
timeoutCallback(): void {
39+
if (Date.now() - this.lastMoveTime > MOUSE_JIGGLER_INTERVAL) {
40+
this.lastMoveTime = Date.now() - 1_000;
41+
this.sendJiggle();
42+
}
43+
}
44+
45+
async sendJiggle(): Promise<void> {
46+
await device.sendMouseRelativeData(EMPTY_KEY, 10, 10, 0);
47+
await device.sendMouseRelativeData(EMPTY_KEY, -10, -10, 0);
48+
}
49+
}
50+
51+
export const mouseJiggler = new MouseJiggler();

browser/src/libs/storage/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const MOUSE_STYLE_KEY = 'nanokvm-usb-mouse-style';
1010
const MOUSE_MODE_KEY = 'nanokvm-usb-mouse-mode';
1111
const MOUSE_SCROLL_DIRECTION_KEY = 'nanokvm-usb-mouse-scroll-direction';
1212
const MOUSE_SCROLL_INTERVAL_KEY = 'nanokvm-usb-mouse-scroll-interval';
13+
const MOUSE_JIGGLER_MODE_KEY = 'nanokvm-usb-mouse-jiggler-mode'
1314

1415
export function getLanguage() {
1516
return localStorage.getItem(LANGUAGE_KEY);
@@ -120,3 +121,12 @@ export function getMouseScrollInterval(): number | null {
120121
export function setMouseScrollInterval(interval: number): void {
121122
localStorage.setItem(MOUSE_SCROLL_INTERVAL_KEY, String(interval));
122123
}
124+
125+
export function getMouseJigglerMode(): 'enable' | 'disable' {
126+
const jiggler = localStorage.getItem(MOUSE_JIGGLER_MODE_KEY);
127+
return jiggler && jiggler === 'enable' ? 'enable' : 'disable';
128+
}
129+
130+
export function setMouseJigglerMode(jiggler: 'enable' | 'disable'): void {
131+
localStorage.setItem(MOUSE_JIGGLER_MODE_KEY, jiggler);
132+
}

0 commit comments

Comments
 (0)