add the function of MouseJiggler

This commit is contained in:
LingkongSky
2025-04-02 23:02:29 +08:00
parent 49220d8024
commit f9d9677bc0
9 changed files with 230 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
#!/bin/sh
#
# mouse-jiggler Starts mouse-jiggler.
#
PIDFILE="/var/run/mouse-jiggler.pid"
start() {
if [ ! -f "$PIDFILE" ];then
echo "Starting Mouse Jiggler"
(
status=true
while true;do
if [ "$status" = true ];then
echo -ne \\x00\\x1\\x00\\x00 > /dev/hidg1
status=false
else
echo -ne \\x1\\x00\\x00\\x00 > /dev/hidg1
status=true
fi
sleep 60
done
) >/dev/null 2>&1 &
echo $! > "$PIDFILE"
else
echo "Mouse Jiggler already started"
fi
}
stop() {
echo "Stopping Mouse Jiggler"
[ -f "$PIDFILE" ] && kill $(cat "$PIDFILE") && rm -f "$PIDFILE"
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0

View File

@@ -88,6 +88,10 @@ type GetSwapStateRsp struct {
Enabled bool `json:"enabled"`
}
type GetMouseJigglerStateRsp struct {
Enabled bool `json:"enabled"`
}
type GetMdnsStateRsp struct {
Enabled bool `json:"enabled"`
}

View File

@@ -44,6 +44,10 @@ func vmRouter(r *gin.Engine) {
api.POST("/vm/swap/enable", service.EnableSwap) // enable Swap
api.POST("/vm/swap/disable", service.DisableSwap) // disable Swap
api.GET("/vm/mouseJiggler", service.GetMouseJigglerState) // get MouseJiggler state
api.POST("/vm/mouseJiggler/enable", service.EnableMouseJiggler) // enable MouseJiggler
api.POST("/vm/mouseJiggler/disable", service.DisableMouseJiggler) // disable MouseJiggler
api.GET("/vm/mdns", service.GetMdnsState) // get mDNS state
api.POST("/vm/mdns/enable", service.EnableMdns) // enable mDNS
api.POST("/vm/mdns/disable", service.DisableMdns) // disable mDNS

View File

@@ -0,0 +1,76 @@
package vm
import (
"NanoKVM-Server/proto"
"errors"
"fmt"
"os"
"os/exec"
"strings"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
)
const (
MouseJigglerStartFlag = "/var/run/mouse-jiggler.pid"
MouseJigglerScript = "/etc/init.d/S50mouse-jiggler"
MouseJigglerBackupScript = "/kvmapp/system/init.d/S50mouse-jiggler"
)
func (s *Service) GetMouseJigglerState(c *gin.Context) {
var rsp proto.Response
enabled := isMouseJigglerEnabled()
rsp.OkRspWithData(c, &proto.GetMouseJigglerStateRsp{
Enabled: enabled,
})
}
func (s *Service) EnableMouseJiggler(c *gin.Context) {
var rsp proto.Response
commands := []string{
fmt.Sprintf("cp -f %s %s", MouseJigglerBackupScript, MouseJigglerScript),
fmt.Sprintf("chmod +x %s", MouseJigglerScript),
fmt.Sprintf("%s start", MouseJigglerScript),
}
command := strings.Join(commands, " && ")
err := exec.Command("sh", "-c", command).Run()
if err != nil {
log.Errorf("failed to run MouseJiggler script: %s", err)
rsp.ErrRsp(c, -1, "operation failed")
return
}
rsp.OkRsp(c)
log.Debugf("MouseJiggler enabled")
}
func (s *Service) DisableMouseJiggler(c *gin.Context) {
var rsp proto.Response
command := fmt.Sprintf("%s stop", MouseJigglerScript)
err := exec.Command("sh", "-c", command).Run()
if err != nil {
log.Errorf("failed to run MouseJiggler script: %s", err)
rsp.ErrRsp(c, -1, "operation failed")
return
}
_ = os.Remove(MouseJigglerScript)
rsp.OkRsp(c)
log.Debugf("MouseJiggler disabled")
}
func isMouseJigglerEnabled() bool {
_, err := os.Stat(MouseJigglerStartFlag)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return false
}
}
return true
}

View File

@@ -92,6 +92,21 @@ export function disableSwap() {
return http.post('/api/vm/swap/disable');
}
// get MouseJiggler state
export function getMouseJigglerState() {
return http.get('/api/vm/mouseJiggler');
}
// enable MouseJiggler
export function enableMouseJiggler() {
return http.post('/api/vm/mouseJiggler/enable');
}
// disable MouseJiggler
export function disableMouseJiggler() {
return http.post('/api/vm/mouseJiggler/disable');
}
// get mDNS state
export function getMdnsState() {
return http.get('/api/vm/mdns');

View File

@@ -224,6 +224,11 @@ const en = {
description: 'Enable the swap partition',
tip: 'Enable the default swap partition(128M)'
},
mouseJiggler: {
title: "Mouse Jiggler",
description: 'Enable the mouse jiggler',
tip: "Automatically jiggles the mouse when inactive for 60 seconds to prevent system sleep"
},
mdns: {
description: 'Enable mDNS discovery service',
tip: "Turning it off if it's not needed"

View File

@@ -1,3 +1,5 @@
import { t } from "i18next";
const zh = {
translation: {
head: {
@@ -208,6 +210,11 @@ const zh = {
description: '启用交换分区',
tip: '启用默认的交换分区128M'
},
mouseJiggler:{
title: '鼠标抖动',
description: '启用鼠标抖动',
tip: '在鼠标离开60s未操作时自动进行抖动以防止系统睡眠'
},
mdns: {
description: '启用 mDNS 发现服务',
tip: '如果您未使用此功能,建议将其关闭'

View File

@@ -14,6 +14,7 @@ import { Ssh } from './ssh.tsx';
import { VirtualDevices } from './virtual-devices.tsx';
import { Wifi } from './wifi.tsx';
import { Swap } from './swap.tsx';
import { MouseJiggler } from './mouse-jiggler.tsx';
export const Device = () => {
const { t } = useTranslation();
@@ -39,6 +40,7 @@ export const Device = () => {
<Ssh />
<Mdns />
<Swap />
<MouseJiggler />
{hidMode === 'normal' ? <VirtualDevices /> : <HidMode />}
</div>
<Divider />

View File

@@ -0,0 +1,65 @@
import { useEffect, useState } from 'react';
import { Switch, Tooltip } from 'antd';
import { CircleAlertIcon } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import * as api from '@/api/vm.ts';
export const MouseJiggler = () => {
const { t } = useTranslation();
const [isEnabled, setIsEnabled] = useState(false);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
api
.getMouseJigglerState()
.then((rsp) => {
if (rsp.data?.enabled) {
setIsEnabled(true);
}
})
.finally(() => {
setIsLoading(false);
});
}, []);
async function update() {
if (isLoading) return;
setIsLoading(true);
const rsp = isEnabled ? await api.disableMouseJiggler() : await api.enableMouseJiggler();
setIsLoading(false);
if (rsp.code !== 0) {
console.log(rsp.msg);
return;
}
setIsEnabled(!isEnabled);
}
return (
<div className="flex items-center justify-between">
<div className="flex flex-col">
<div className="flex items-center space-x-2">
<span>{t('settings.device.mouseJiggler.title')}</span>
<Tooltip
title={t('settings.device.mouseJiggler.tip')}
className="cursor-pointer"
placement="bottom"
overlayStyle={{ maxWidth: '300px' }}
>
<CircleAlertIcon size={15} />
</Tooltip>
</div>
<span className="text-xs text-neutral-500">{t('settings.device.mouseJiggler.description')}</span>
</div>
<Switch checked={isEnabled} loading={isLoading} onChange={update} />
</div>
);
};