mirror of
https://github.com/sipeed/NanoKVM-USB.git
synced 2026-11-09 17:04:27 -06:00
Merge pull request #46 from kevinzhangzj/dev
feat: add mouse jiggler function
This commit is contained in:
@@ -4,6 +4,7 @@ import { useAtom, useSetAtom } from 'jotai';
|
||||
import { MouseIcon } from 'lucide-react';
|
||||
|
||||
import {
|
||||
mouseJigglerModeAtom,
|
||||
mouseModeAtom,
|
||||
mouseStyleAtom,
|
||||
scrollDirectionAtom,
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
import * as storage from '@/libs/storage';
|
||||
|
||||
import { Direction } from './direction.tsx';
|
||||
import { Jiggler } from './jiggler.tsx';
|
||||
import { Mode } from './mode.tsx';
|
||||
import { Speed } from './speed.tsx';
|
||||
import { Style } from './style.tsx';
|
||||
@@ -21,6 +23,7 @@ export const Mouse = () => {
|
||||
const [mouseMode, setMouseMode] = useAtom(mouseModeAtom);
|
||||
const setScrollDirection = useSetAtom(scrollDirectionAtom);
|
||||
const setScrollInterval = useSetAtom(scrollIntervalAtom);
|
||||
const [mouseJigglerMode, setMouseJigglerMode] = useAtom(mouseJigglerModeAtom);
|
||||
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
|
||||
@@ -48,6 +51,11 @@ export const Mouse = () => {
|
||||
if (interval) {
|
||||
setScrollInterval(interval);
|
||||
}
|
||||
|
||||
const jiggler = storage.getMouseJigglerMode();
|
||||
if (mouseJigglerMode !== jiggler) {
|
||||
setMouseJigglerMode(jiggler);
|
||||
}
|
||||
}
|
||||
|
||||
const content = (
|
||||
@@ -56,6 +64,7 @@ export const Mouse = () => {
|
||||
<Mode />
|
||||
<Direction />
|
||||
<Speed />
|
||||
<Jiggler />
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
49
browser/src/components/menu/mouse/jiggler.tsx
Normal file
49
browser/src/components/menu/mouse/jiggler.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Popover } from 'antd';
|
||||
import clsx from 'clsx';
|
||||
import { useAtom } from 'jotai';
|
||||
import { MousePointerIcon } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { mouseJigglerModeAtom } from '@/jotai/mouse.ts';
|
||||
import * as storage from '@/libs/storage';
|
||||
|
||||
export const Jiggler = () => {
|
||||
const { t } = useTranslation();
|
||||
const [mouseJigglerMode, setMouseJigglerMode] = useAtom(mouseJigglerModeAtom);
|
||||
const mouseJigglerModes = [
|
||||
{ name: t('mouse.jiggler.enable'), value: 'enable' },
|
||||
{ name: t('mouse.jiggler.disable'), value: 'disable' }
|
||||
];
|
||||
|
||||
function update(mode: string) {
|
||||
setMouseJigglerMode(mode);
|
||||
storage.setMouseJigglerMode(mode);
|
||||
}
|
||||
|
||||
const content = (
|
||||
<>
|
||||
{mouseJigglerModes.map((mode) => (
|
||||
<div
|
||||
key={mode.value}
|
||||
className={clsx(
|
||||
'my-1 flex cursor-pointer items-center space-x-1 rounded py-1 pl-2 pr-5 hover:bg-neutral-700/50',
|
||||
mode.value === mouseJigglerMode ? 'text-blue-500' : 'text-neutral-300'
|
||||
)}
|
||||
onClick={() => update(mode.value)}
|
||||
>
|
||||
{mode.name}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-1 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<div className="flex h-[14px] w-[20px] items-end">
|
||||
<MousePointerIcon size={16} />
|
||||
</div>
|
||||
<span>{t('mouse.jiggler.title')}</span>
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
@@ -1,8 +1,13 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
|
||||
import { resolutionAtom } from '@/jotai/device.ts';
|
||||
import { scrollDirectionAtom, scrollIntervalAtom } from '@/jotai/mouse.ts';
|
||||
import {
|
||||
mouseJigglerModeAtom,
|
||||
mouseLastMoveTimeAtom,
|
||||
scrollDirectionAtom,
|
||||
scrollIntervalAtom
|
||||
} from '@/jotai/mouse.ts';
|
||||
import { device } from '@/libs/device';
|
||||
import { Key } from '@/libs/device/mouse.ts';
|
||||
|
||||
@@ -10,6 +15,8 @@ export const Absolute = () => {
|
||||
const resolution = useAtomValue(resolutionAtom);
|
||||
const scrollDirection = useAtomValue(scrollDirectionAtom);
|
||||
const scrollInterval = useAtomValue(scrollIntervalAtom);
|
||||
const mouseJigglerMode = useAtomValue(mouseJigglerModeAtom);
|
||||
const setMouseLastMoveTime = useSetAtom(mouseLastMoveTimeAtom);
|
||||
|
||||
const keyRef = useRef<Key>(new Key());
|
||||
const lastScrollTimeRef = useRef(0);
|
||||
@@ -74,6 +81,11 @@ export const Absolute = () => {
|
||||
async function handleMouseMove(event: any) {
|
||||
disableEvent(event);
|
||||
await send(event);
|
||||
|
||||
// mouse jiggler record last move time
|
||||
if (mouseJigglerMode === 'enable') {
|
||||
setMouseLastMoveTime(Date.now());
|
||||
}
|
||||
}
|
||||
|
||||
// mouse scroll
|
||||
@@ -109,7 +121,7 @@ export const Absolute = () => {
|
||||
canvas.removeEventListener('click', disableEvent);
|
||||
canvas.removeEventListener('contextmenu', disableEvent);
|
||||
};
|
||||
}, [resolution, scrollDirection, scrollInterval]);
|
||||
}, [resolution, scrollDirection, scrollInterval, mouseJigglerMode, setMouseLastMoveTime]);
|
||||
|
||||
// disable default events
|
||||
function disableEvent(event: any) {
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useAtom, useAtomValue } from 'jotai';
|
||||
|
||||
import { mouseModeAtom } from '@/jotai/mouse.ts';
|
||||
import {
|
||||
mouseJigglerIntervalAtom,
|
||||
mouseJigglerModeAtom,
|
||||
mouseJigglerTimerAtom,
|
||||
mouseLastMoveTimeAtom,
|
||||
mouseModeAtom
|
||||
} from '@/jotai/mouse.ts';
|
||||
import { device } from '@/libs/device/index.ts';
|
||||
import { Key } from '@/libs/device/mouse.ts';
|
||||
|
||||
import { Absolute } from './absolute.tsx';
|
||||
import { Relative } from './relative.tsx';
|
||||
@@ -8,5 +17,48 @@ import { Relative } from './relative.tsx';
|
||||
export const Mouse = () => {
|
||||
const mouseMode = useAtomValue(mouseModeAtom);
|
||||
|
||||
// mouse jiggler
|
||||
const mouseJigglerMode = useAtomValue(mouseJigglerModeAtom);
|
||||
const [mouseJigglerTimer, setMouseJigglerTimer] = useAtom(mouseJigglerTimerAtom);
|
||||
const mouseJigglerInterval = useAtomValue(mouseJigglerIntervalAtom);
|
||||
const mouseLastMoveTime = useAtomValue(mouseLastMoveTimeAtom);
|
||||
const mouseLastMoveTimeRef = useRef(mouseLastMoveTime);
|
||||
const emptyKeyRef = useRef<Key>(new Key());
|
||||
useEffect(() => {
|
||||
// sync mouseLastMoveTime through ref
|
||||
mouseLastMoveTimeRef.current = mouseLastMoveTime;
|
||||
}, [mouseLastMoveTime]);
|
||||
useEffect(() => {
|
||||
async function jigglerTimerCallback() {
|
||||
if (Date.now() - mouseLastMoveTimeRef.current < mouseJigglerInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = document.getElementById('video')!.getBoundingClientRect();
|
||||
|
||||
await device.sendMouseAbsoluteData(
|
||||
emptyKeyRef.current,
|
||||
rect.width,
|
||||
rect.height,
|
||||
rect.width / 2,
|
||||
rect.height / 2,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
// configure interval timer
|
||||
if (mouseJigglerMode === 'enable') {
|
||||
if (mouseJigglerTimer === null) {
|
||||
const timer = setInterval(jigglerTimerCallback, mouseJigglerInterval);
|
||||
setMouseJigglerTimer(timer);
|
||||
}
|
||||
} else {
|
||||
if (mouseJigglerTimer) {
|
||||
clearInterval(mouseJigglerTimer);
|
||||
setMouseJigglerTimer(null);
|
||||
}
|
||||
}
|
||||
}, [mouseJigglerMode]);
|
||||
|
||||
return <>{mouseMode === 'relative' ? <Relative /> : <Absolute />}</>;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { message } from 'antd';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { resolutionAtom } from '@/jotai/device.ts';
|
||||
import { scrollDirectionAtom, scrollIntervalAtom } from '@/jotai/mouse.ts';
|
||||
import {
|
||||
mouseJigglerModeAtom,
|
||||
mouseLastMoveTimeAtom,
|
||||
scrollDirectionAtom,
|
||||
scrollIntervalAtom
|
||||
} from '@/jotai/mouse.ts';
|
||||
import { device } from '@/libs/device';
|
||||
import { Key } from '@/libs/device/mouse.ts';
|
||||
|
||||
@@ -15,6 +20,8 @@ export const Relative = () => {
|
||||
const resolution = useAtomValue(resolutionAtom);
|
||||
const scrollDirection = useAtomValue(scrollDirectionAtom);
|
||||
const scrollInterval = useAtomValue(scrollIntervalAtom);
|
||||
const mouseJigglerMode = useAtomValue(mouseJigglerModeAtom);
|
||||
const setMouseLastMoveTime = useSetAtom(mouseLastMoveTimeAtom);
|
||||
|
||||
const isLockedRef = useRef(false);
|
||||
const keyRef = useRef<Key>(new Key());
|
||||
@@ -110,6 +117,11 @@ export const Relative = () => {
|
||||
if (x === 0 && y === 0) return;
|
||||
|
||||
await send(Math.abs(x) < 10 ? x * 2 : x, Math.abs(y) < 10 ? y * 2 : y, 0);
|
||||
|
||||
// mouse jiggler record last move time
|
||||
if (mouseJigglerMode === 'enable') {
|
||||
setMouseLastMoveTime(Date.now());
|
||||
}
|
||||
}
|
||||
|
||||
// mouse scroll
|
||||
@@ -138,7 +150,7 @@ export const Relative = () => {
|
||||
canvas.removeEventListener('wheel', handleWheel);
|
||||
canvas.removeEventListener('contextmenu', disableEvent);
|
||||
};
|
||||
}, [resolution, scrollDirection, scrollInterval]);
|
||||
}, [resolution, scrollDirection, scrollInterval, mouseJigglerMode, setMouseLastMoveTime]);
|
||||
|
||||
async function send(x: number, y: number, scroll: number) {
|
||||
await device.sendMouseRelativeData(keyRef.current, x, y, scroll);
|
||||
|
||||
@@ -59,7 +59,12 @@ const en = {
|
||||
speed: 'Wheel speed',
|
||||
fast: 'Fast',
|
||||
slow: 'Slow',
|
||||
requestPointer: 'Using relative mode. Please click desktop to get mouse pointer.'
|
||||
requestPointer: 'Using relative mode. Please click desktop to get mouse pointer.',
|
||||
jiggler: {
|
||||
title: 'Mouse Jiggler',
|
||||
enable: 'Enable',
|
||||
disable: 'Disable'
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
language: 'Language',
|
||||
|
||||
@@ -57,7 +57,12 @@ const zh = {
|
||||
speed: '滚轮速度',
|
||||
fast: '快',
|
||||
slow: '慢',
|
||||
requestPointer: '正在使用鼠标相对模式,请点击桌面获取鼠标指针。'
|
||||
requestPointer: '正在使用鼠标相对模式,请点击桌面获取鼠标指针。',
|
||||
jiggler: {
|
||||
title: '闲时晃动',
|
||||
enable: '启用',
|
||||
disable: '禁用'
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
language: '语言',
|
||||
|
||||
@@ -9,6 +9,17 @@ export const mouseModeAtom = atom('absolute');
|
||||
// mouse scroll direction: 1 or -1
|
||||
export const scrollDirectionAtom = atom(1);
|
||||
|
||||
// mouse scroll interval (unit: ms)
|
||||
// mouse scroll interval (unit: ms)
|
||||
export const scrollIntervalAtom = atom(0);
|
||||
|
||||
// mouse jiggler mode: enable or disable
|
||||
export const mouseJigglerModeAtom = atom('disable');
|
||||
|
||||
// mouse jiggler timer id
|
||||
export const mouseJigglerTimerAtom = atom<number | null>(null);
|
||||
|
||||
// mouse jiggler interval (unit: ms)
|
||||
export const mouseJigglerIntervalAtom = atom(15_000);
|
||||
|
||||
// mouse jiggler last move time
|
||||
export const mouseLastMoveTimeAtom = atom(0);
|
||||
|
||||
@@ -9,6 +9,7 @@ const MOUSE_STYLE_KEY = 'nanokvm-usb-mouse-style';
|
||||
const MOUSE_MODE_KEY = 'nanokvm-usb-mouse-mode';
|
||||
const MOUSE_SCROLL_DIRECTION_KEY = 'nanokvm-usb-mouse-scroll-direction';
|
||||
const MOUSE_SCROLL_INTERVAL_KEY = 'nanokvm-usb-mouse-scroll-interval';
|
||||
const MOUSE_JIGGLER_MODE_KEY = 'nanokvm-usb-mouse-jiggler-mode';
|
||||
|
||||
export function getLanguage() {
|
||||
return localStorage.getItem(LANGUAGE_KEY);
|
||||
@@ -107,3 +108,12 @@ export function getMouseScrollInterval(): number | null {
|
||||
export function setMouseScrollInterval(interval: number): void {
|
||||
localStorage.setItem(MOUSE_SCROLL_INTERVAL_KEY, String(interval));
|
||||
}
|
||||
|
||||
export function getMouseJigglerMode(): string {
|
||||
const jiggler = localStorage.getItem(MOUSE_JIGGLER_MODE_KEY);
|
||||
return jiggler && jiggler === 'enable' ? 'enable' : 'disable';
|
||||
}
|
||||
|
||||
export function setMouseJigglerMode(jiggler: string): void {
|
||||
localStorage.setItem(MOUSE_JIGGLER_MODE_KEY, jiggler);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user