当前位置: 首页 > news >正文

做个minecraft kiosk

水鱼拿起尘封已久的树莓派4B干了件大事。创建用户minecraft,给到需要的权限(id minecraft
uid=1001(minecraft) gid=1001(minecraft) groups=1001(minecraft),46(plugdev),100(users),102(input)),创建~/.xinitrc文件,给到以下。

#!/bin/bash xset s off xset -dpms exec /usr/bin/minecraft-pi-reborn

这个就是伪kiosk精髓了,开机默认minecraft用户,然后开游戏。

讲究点加个音效文件,记得找libminecraftpe06+08.so。其他在树莓派上是不灵光的,没有声音。

再讲究点加个70元xbox手柄,但是自己要写映射键位,据说可以不写映射直接玩,我是没试成功,游戏无响应。

#路径是/usr/local/bin/mcpi-pad #!/usr/bin/env bash set -euo pipefail ACTION="${1:-}" PID_FILE="/run/mcpi-pad.pid" LOG_FILE="/tmp/mcpi-pad.log" DAEMON="/usr/local/bin/mcpi-pad-daemon.py" is_running() { if [[ ! -f "$PID_FILE" ]]; then return 1 fi local pid pid="$(cat "$PID_FILE" 2>/dev/null || true)" [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null } start_pad() { if is_running; then echo "mcpi-pad daemon is already running" ps -fp "$(cat "$PID_FILE")" || true return 0 fi sudo pkill -9 -x xboxdrv 2>/dev/null || true nohup python3 "$DAEMON" >"$LOG_FILE" 2>&1 & echo $! | sudo tee "$PID_FILE" >/dev/null sleep 0.5 if is_running; then echo "mcpi-pad daemon started" ps -fp "$(cat "$PID_FILE")" else echo "mcpi-pad daemon failed to start" [[ -f "$LOG_FILE" ]] && tail -n 80 "$LOG_FILE" exit 1 fi } stop_pad() { if is_running; then local pid pid="$(cat "$PID_FILE")" kill "$pid" 2>/dev/null || true sleep 0.2 kill -9 "$pid" 2>/dev/null || true sudo rm -f "$PID_FILE" echo "mcpi-pad daemon stopped" else echo "mcpi-pad daemon is not running" fi } status_pad() { if is_running; then echo "mcpi-pad daemon is running" ps -fp "$(cat "$PID_FILE")" else echo "mcpi-pad daemon is not running" fi } case "$ACTION" in start) start_pad ;; stop) stop_pad ;; restart) stop_pad start_pad ;; status) status_pad ;; log) [[ -f "$LOG_FILE" ]] && tail -n 120 "$LOG_FILE" || echo "No log file: $LOG_FILE" ;; *) echo "Usage: sudo mcpi-pad {start|stop|restart|status|log}" exit 2 ;; esac

以及

#路径/usr/local/bin/mcpi-pad-daemon.py #!/usr/bin/env python3 import signal import sys import time from collections import defaultdict from evdev import InputDevice, UInput, ecodes DEVICE_NAME = "Xbox Wireless Controller" THRESHOLD = 12000 CENTER = 32768 MOUSE_DEADZONE = 5000 MOUSE_MAX_STEP = 25 def find_event_device() -> str: with open('/proc/bus/input/devices', 'r', encoding='utf-8') as f: blocks = f.read().split('\n\n') for block in blocks: if DEVICE_NAME in block: for tok in block.split(): if tok.startswith('event') and tok[5:].isdigit(): return f'/dev/input/{tok}' raise RuntimeError('Xbox controller event device not found') pressed = defaultdict(set) def press(ui: UInput, src: str, key: int) -> None: if key in pressed[src]: return pressed[src].add(key) if not any(key in v for k, v in pressed.items() if k != src): ui.write(ecodes.EV_KEY, key, 1) def release(ui: UInput, src: str, key: int) -> None: if key not in pressed[src]: return pressed[src].remove(key) if not any(key in v for v in pressed.values()): ui.write(ecodes.EV_KEY, key, 0) def set_axis(ui: UInput, src: str, neg: int, pos: int, value: int) -> None: delta = value - CENTER if delta < -THRESHOLD: press(ui, src, neg) release(ui, src, pos) elif delta > THRESHOLD: press(ui, src, pos) release(ui, src, neg) else: release(ui, src, neg) release(ui, src, pos) def set_hat(ui: UInput, src: str, neg: int, pos: int, value: int) -> None: if value < 0: press(ui, src, neg) release(ui, src, pos) elif value > 0: press(ui, src, pos) release(ui, src, neg) else: release(ui, src, neg) release(ui, src, pos) def emit_mouse_rel(ui: UInput, rel_code: int, value: int, invert: bool = False) -> bool: delta = value - CENTER if abs(delta) <= MOUSE_DEADZONE: return False span = 32767 - MOUSE_DEADZONE magnitude = int(((abs(delta) - MOUSE_DEADZONE) / span) * MOUSE_MAX_STEP) magnitude = max(1, min(MOUSE_MAX_STEP, magnitude)) direction = -1 if delta < 0 else 1 if invert: direction *= -1 ui.write(ecodes.EV_REL, rel_code, direction * magnitude) return True def release_all(ui: UInput) -> None: keys = set() for ks in pressed.values(): keys.update(ks) for key in keys: ui.write(ecodes.EV_KEY, key, 0) ui.syn() def main() -> int: event_dev = find_event_device() pad = InputDevice(event_dev) kb_ui = UInput( { ecodes.EV_KEY: [ ecodes.KEY_W, ecodes.KEY_A, ecodes.KEY_S, ecodes.KEY_D, ecodes.KEY_SPACE, ecodes.KEY_LEFTCTRL, ecodes.KEY_ESC, ecodes.KEY_E, ] }, name='MCPI Pad Keyboard', bustype=0x03, ) mouse_ui = UInput( { ecodes.EV_KEY: [ecodes.BTN_LEFT, ecodes.BTN_RIGHT], ecodes.EV_REL: [ecodes.REL_X, ecodes.REL_Y], }, name='MCPI Pad Mouse', bustype=0x03, ) btn_map = { ecodes.BTN_SOUTH: ecodes.KEY_SPACE, ecodes.BTN_EAST: ecodes.KEY_LEFTCTRL, ecodes.BTN_C: ecodes.KEY_ESC, ecodes.BTN_Z: ecodes.KEY_E, } stop = False def _stop(_sig, _frm): nonlocal stop stop = True signal.signal(signal.SIGTERM, _stop) signal.signal(signal.SIGINT, _stop) while not stop: try: for ev in pad.read_loop(): if stop: break if ev.type == ecodes.EV_KEY and ev.code in btn_map: key = btn_map[ev.code] if ev.value: press(kb_ui, 'btn', key) else: release(kb_ui, 'btn', key) kb_ui.syn() elif ev.type == ecodes.EV_ABS: if ev.code == ecodes.ABS_X: set_axis(kb_ui, 'lx', ecodes.KEY_A, ecodes.KEY_D, ev.value) kb_ui.syn() elif ev.code == ecodes.ABS_Y: set_axis(kb_ui, 'ly', ecodes.KEY_W, ecodes.KEY_S, ev.value) kb_ui.syn() elif ev.code == ecodes.ABS_RX: if emit_mouse_rel(mouse_ui, ecodes.REL_X, ev.value): mouse_ui.syn() elif ev.code == ecodes.ABS_RY: if emit_mouse_rel(mouse_ui, ecodes.REL_Y, ev.value, invert=True): mouse_ui.syn() elif ev.code == ecodes.ABS_HAT0X: set_hat(kb_ui, 'hx', ecodes.KEY_A, ecodes.KEY_D, ev.value) kb_ui.syn() elif ev.code == ecodes.ABS_HAT0Y: set_hat(kb_ui, 'hy', ecodes.KEY_W, ecodes.KEY_S, ev.value) kb_ui.syn() except OSError: time.sleep(0.2) try: event_dev = find_event_device() pad = InputDevice(event_dev) except Exception: time.sleep(0.5) release_all(kb_ui) pad.close() kb_ui.close() mouse_ui.close() return 0 if __name__ == '__main__': sys.exit(main())

反正最后游戏柄还是很难调试,还是键鼠套餐香。

http://www.jsqmd.com/news/1203949/

相关文章:

  • Claude Context:基于语义搜索的AI编程助手代码库增强工具
  • Cursor与JetBrains双IDE协同开发实战指南
  • Windows 11 26H2管理员保护功能解析与配置指南
  • 微信网页版受限?手把手教你打造便携版微信桌面工作流
  • 联想电脑杜比音效恢复与优化全攻略
  • 全志H3开发板外设功能测试与开发指南
  • WSL 2安装配置与开发环境优化指南
  • Agent Skills 是 AI 的职业资格证,不是插件
  • 【远程操控 Agent 技术解析】Codex 主力 + UU 远程兜底的组合方案
  • ARM、DSP与FPGA电源设计差异与优化策略
  • Cursor 3.6 汉化实战:绕过 Configure Display Language 的四步法
  • 2026年EmEditor安装排雷指南:避开杀毒拦截与权限陷阱
  • Windows Hello PIN不可用问题排查与解决方案
  • ima.copilot:面向工业工作流的垂直领域AI协作者
  • Gemma 4 12B多模态统一架构解析与应用实践
  • NanaZip完全指南:Windows 11现代化压缩工具的终极解决方案
  • Trae Skill 工作流:用 Markdown 定义可审计的 AI 智能体能力
  • OpenDataLoader PDF:为RAG定制的高可信PDF解析器
  • Linux YUM包管理器核心命令与高级应用详解
  • Windows 64位一键安装的ArangoDB 3.8.4社区版(内置图/文档/键值三模型)
  • Matlab/Simulink与Python基于原生TCP的双向实时通信工程模板(含跨平台可运行代码)
  • 基于CLIP的大规模图片检索实践与优化
  • 【LingBot-VLA 2.0 技术解析】6 万小时炼成的跨实体机器人通用大脑
  • MinIO分布式集群部署避坑指南:硬件、网络与共识机制深度解析
  • RA4M2开发环境搭建与GPIO按键控制实战
  • GTA5线上小助手:3分钟掌握洛圣都自由穿梭终极指南
  • 中山夏令营哪家正规:军博营地资质合规 - 云溪自乐
  • macOS Sonoma 14.4 iCloud文件版本控制Bug分析与解决方案
  • VisualCppRedist AIO:一站式解决Windows软件运行库依赖问题的技术指南
  • Mac读写NTFS硬盘的完整解决方案与优化技巧