From b76902c0f3ec8eb0213e1b6081a16f2130c8347b Mon Sep 17 00:00:00 2001 From: kevinzhangzj <710895909@qq.com> Date: Sat, 31 May 2025 15:00:10 +0800 Subject: [PATCH 1/2] feat: add desktop version mouse jiggler function feat: update mouse jiggler translation in English and Chinese --- .../src/components/menu/mouse/index.tsx | 8 +++ .../src/components/menu/mouse/jiggler.tsx | 56 +++++++++++++++++++ .../src/components/mouse/absolute.tsx | 3 + .../src/components/mouse/relative.tsx | 3 + desktop/src/renderer/src/i18n/locales/en.ts | 7 ++- desktop/src/renderer/src/i18n/locales/zh.ts | 7 ++- desktop/src/renderer/src/jotai/mouse.ts | 5 +- .../renderer/src/libs/mouse-jiggler/index.ts | 49 ++++++++++++++++ .../src/renderer/src/libs/storage/index.ts | 10 ++++ 9 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 desktop/src/renderer/src/components/menu/mouse/jiggler.tsx create mode 100644 desktop/src/renderer/src/libs/mouse-jiggler/index.ts diff --git a/desktop/src/renderer/src/components/menu/mouse/index.tsx b/desktop/src/renderer/src/components/menu/mouse/index.tsx index 46dc0ce..12e7329 100644 --- a/desktop/src/renderer/src/components/menu/mouse/index.tsx +++ b/desktop/src/renderer/src/components/menu/mouse/index.tsx @@ -4,14 +4,17 @@ import { useAtom, useSetAtom } from 'jotai' import { MouseIcon } from 'lucide-react' import { + mouseJigglerModeAtom, mouseModeAtom, mouseStyleAtom, scrollDirectionAtom, scrollIntervalAtom } from '@renderer/jotai/mouse' +import { mouseJiggler } from '@renderer/libs/mouse-jiggler' import * as storage from '@renderer/libs/storage' import { Direction } from './direction' +import { Jiggler } from './jiggler' import { Mode } from './mode' import { Speed } from './speed' import { Style } from './style' @@ -21,6 +24,7 @@ export const Mouse = (): ReactElement => { const setMouseMode = useSetAtom(mouseModeAtom) const setScrollDirection = useSetAtom(scrollDirectionAtom) const setScrollInterval = useSetAtom(scrollIntervalAtom) + const setMouseJigglerMode = useSetAtom(mouseJigglerModeAtom) const [isPopoverOpen, setIsPopoverOpen] = useState(false) @@ -44,6 +48,9 @@ export const Mouse = (): ReactElement => { if (interval) { setScrollInterval(interval) } + const jiggler = storage.getMouseJigglerMode() + mouseJiggler.setMode(jiggler) + setMouseJigglerMode(jiggler) }, []) const content = ( @@ -52,6 +59,7 @@ export const Mouse = (): ReactElement => { + ) diff --git a/desktop/src/renderer/src/components/menu/mouse/jiggler.tsx b/desktop/src/renderer/src/components/menu/mouse/jiggler.tsx new file mode 100644 index 0000000..5d9bcbf --- /dev/null +++ b/desktop/src/renderer/src/components/menu/mouse/jiggler.tsx @@ -0,0 +1,56 @@ +import { ReactElement, useEffect } from 'react' +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 '@renderer/jotai/mouse' +import { mouseJiggler } from '@renderer/libs/mouse-jiggler' +import * as storage from '@renderer/libs/storage' + +export const Jiggler = (): ReactElement => { + const { t } = useTranslation() + const [jigglerMode, setJigglerMode] = useAtom(mouseJigglerModeAtom) + + const mouseJigglerModes: { name: string; value: 'enable' | 'disable' }[] = [ + { name: t('mouse.jiggler.enable'), value: 'enable' }, + { name: t('mouse.jiggler.disable'), value: 'disable' } + ] + + function update(mode: 'enable' | 'disable'): void { + storage.setMouseJigglerMode(mode) + setJigglerMode(mode) + } + + useEffect(() => { + mouseJiggler.setMode(jigglerMode) + }, [jigglerMode]) + + const content = ( + <> + {mouseJigglerModes.map((mode) => ( +
update(mode.value)} + > + {mode.name} +
+ ))} + + ) + return ( + +
+
+ +
+ {t('mouse.jiggler.title')} +
+
+ ) +} diff --git a/desktop/src/renderer/src/components/mouse/absolute.tsx b/desktop/src/renderer/src/components/mouse/absolute.tsx index 4a2c671..525035d 100644 --- a/desktop/src/renderer/src/components/mouse/absolute.tsx +++ b/desktop/src/renderer/src/components/mouse/absolute.tsx @@ -4,6 +4,7 @@ import { useAtomValue } from 'jotai' import { IpcEvents } from '@common/ipc-events' import { resolutionAtom } from '@renderer/jotai/device' import { scrollDirectionAtom, scrollIntervalAtom } from '@renderer/jotai/mouse' +import { mouseJiggler } from '@renderer/libs/mouse-jiggler' import type { Mouse as MouseKey } from '@renderer/types' export const Absolute = (): ReactElement => { @@ -77,6 +78,8 @@ export const Absolute = (): ReactElement => { async function handleMouseMove(event: MouseEvent): Promise { disableEvent(event) await send(event) + + mouseJiggler.moveEventCallback() } // mouse scroll diff --git a/desktop/src/renderer/src/components/mouse/relative.tsx b/desktop/src/renderer/src/components/mouse/relative.tsx index 5bdd7b1..a270fec 100644 --- a/desktop/src/renderer/src/components/mouse/relative.tsx +++ b/desktop/src/renderer/src/components/mouse/relative.tsx @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next' import { IpcEvents } from '@common/ipc-events' import { resolutionAtom } from '@renderer/jotai/device' import { scrollDirectionAtom, scrollIntervalAtom } from '@renderer/jotai/mouse' +import { mouseJiggler } from '@renderer/libs/mouse-jiggler' import type { Mouse as MouseKey } from '@renderer/types' export const Relative = (): ReactElement => { @@ -110,6 +111,8 @@ export const Relative = (): ReactElement => { if (x === 0 && y === 0) return await send(Math.abs(x) < 10 ? x * 2 : x, Math.abs(y) < 10 ? y * 2 : y, 0) + + mouseJiggler.moveEventCallback() } async function handleWheel(event: WheelEvent): Promise { diff --git a/desktop/src/renderer/src/i18n/locales/en.ts b/desktop/src/renderer/src/i18n/locales/en.ts index 707c660..83e1775 100644 --- a/desktop/src/renderer/src/i18n/locales/en.ts +++ b/desktop/src/renderer/src/i18n/locales/en.ts @@ -51,7 +51,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: { title: 'Settings', diff --git a/desktop/src/renderer/src/i18n/locales/zh.ts b/desktop/src/renderer/src/i18n/locales/zh.ts index f6ea3f1..01b5add 100644 --- a/desktop/src/renderer/src/i18n/locales/zh.ts +++ b/desktop/src/renderer/src/i18n/locales/zh.ts @@ -50,7 +50,12 @@ const zh = { speed: '滚轮速度', fast: '快', slow: '慢', - requestPointer: '正在使用鼠标相对模式,请点击桌面获取鼠标指针。' + requestPointer: '正在使用鼠标相对模式,请点击桌面获取鼠标指针。', + jiggler: { + title: '空闲晃动', + enable: '启用', + disable: '禁用' + } }, settings: { title: '设置', diff --git a/desktop/src/renderer/src/jotai/mouse.ts b/desktop/src/renderer/src/jotai/mouse.ts index 4a33252..01f3620 100644 --- a/desktop/src/renderer/src/jotai/mouse.ts +++ b/desktop/src/renderer/src/jotai/mouse.ts @@ -10,4 +10,7 @@ export const mouseModeAtom = atom('absolute') export const scrollDirectionAtom = atom(1) // mouse scroll interval (unit: ms) -export const scrollIntervalAtom = atom(0); +export const scrollIntervalAtom = atom(0) + +// mouse jiggler mode: enable or disable +export const mouseJigglerModeAtom = atom<'enable' | 'disable'>('disable') diff --git a/desktop/src/renderer/src/libs/mouse-jiggler/index.ts b/desktop/src/renderer/src/libs/mouse-jiggler/index.ts new file mode 100644 index 0000000..b041fc7 --- /dev/null +++ b/desktop/src/renderer/src/libs/mouse-jiggler/index.ts @@ -0,0 +1,49 @@ +import { IpcEvents } from '@common/ipc-events' +import type { Mouse as MouseKey } from '@renderer/types' + +const MOUSE_JIGGLER_INTERVAL = 15_000 +const EMPTY_KEY: MouseKey = { left: false, right: false, mid: false } + +class MouseJiggler { + private lastMoveTime: number + private timer: NodeJS.Timeout | null + private mode: 'enable' | 'disable' + + constructor() { + this.lastMoveTime = Date.now() + this.timer = null + this.mode = 'disable' + } + + // enable or disable mouse jiggler + setMode(mode: 'enable' | 'disable'): void { + this.mode = mode + if (mode === 'disable' && this.timer !== null) { + clearInterval(this.timer) + this.timer = null + } else if (mode === 'enable' && this.timer === null) { + this.timer = setInterval(() => { + this.timeoutCallback() + }, MOUSE_JIGGLER_INTERVAL / 5) + } + } + + // addEventListener to canvas on 'mousemove' event + moveEventCallback(): void { + if (this.mode === 'enable') { + this.lastMoveTime = Date.now() + } + } + + timeoutCallback(): void { + if (Date.now() - this.lastMoveTime > MOUSE_JIGGLER_INTERVAL) { + this.sendJiggle() + } + } + + async sendJiggle(): Promise { + await window.electron.ipcRenderer.invoke(IpcEvents.SEND_MOUSE_RELATIVE, EMPTY_KEY, 10, 10, 0) + await window.electron.ipcRenderer.invoke(IpcEvents.SEND_MOUSE_RELATIVE, EMPTY_KEY, -10, -10, 0) + } +} +export const mouseJiggler = new MouseJiggler() diff --git a/desktop/src/renderer/src/libs/storage/index.ts b/desktop/src/renderer/src/libs/storage/index.ts index 0024a9d..4a4b701 100644 --- a/desktop/src/renderer/src/libs/storage/index.ts +++ b/desktop/src/renderer/src/libs/storage/index.ts @@ -13,6 +13,7 @@ const MOUSE_MODE_KEY = 'nanokvm-usb-mouse-mode' const MOUSE_SCROLL_DIRECTION_KEY = 'nanokvm-usb-mouse-scroll-direction' const SKIP_UPDATE_KEY = 'nano-kvm-check-update' const MOUSE_SCROLL_INTERVAL_KEY = 'nanokvm-usb-mouse-scroll-interval' +const MOUSE_JIGGLER_MODE_KEY = 'nanokvm-usb-mouse-jiggler-mode' export function getLanguage(): string | null { return localStorage.getItem(LANGUAGE_KEY) @@ -132,3 +133,12 @@ export function setSkipUpdate(skip: boolean): void { const expiry = 3 * 24 * 60 * 60 * 1000 setWithExpiry(SKIP_UPDATE_KEY, String(skip), expiry) } + +export function getMouseJigglerMode(): 'enable' | 'disable' { + const jiggler = localStorage.getItem(MOUSE_JIGGLER_MODE_KEY) + return jiggler && jiggler === 'enable' ? 'enable' : 'disable' +} + +export function setMouseJigglerMode(jiggler: 'enable' | 'disable'): void { + localStorage.setItem(MOUSE_JIGGLER_MODE_KEY, jiggler) +} From dbe4251adac0bfeda57c1caeb6428495c49c00b0 Mon Sep 17 00:00:00 2001 From: kevinzhangzj <710895909@qq.com> Date: Sat, 31 May 2025 15:00:10 +0800 Subject: [PATCH 2/2] feat: add desktop version mouse jiggler function feat: update mouse jiggler translation in English and Chinese --- desktop/src/renderer/src/libs/mouse-jiggler/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop/src/renderer/src/libs/mouse-jiggler/index.ts b/desktop/src/renderer/src/libs/mouse-jiggler/index.ts index b041fc7..bfdd4b7 100644 --- a/desktop/src/renderer/src/libs/mouse-jiggler/index.ts +++ b/desktop/src/renderer/src/libs/mouse-jiggler/index.ts @@ -37,6 +37,7 @@ class MouseJiggler { timeoutCallback(): void { if (Date.now() - this.lastMoveTime > MOUSE_JIGGLER_INTERVAL) { + this.lastMoveTime = Date.now() - 1_000 this.sendJiggle() } }