Checkpoint with dolphin configs
parent
56a8661268
commit
fe4cd8835b
@ -1 +1,8 @@
|
||||
test
|
||||
# Kazzite
|
||||
|
||||
set of scripts for neat bazzite tricks
|
||||
|
||||
# Setup
|
||||
|
||||
./deps.sh
|
||||
./setup.sh
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
sudo rpm-ostree install python3-pygame
|
||||
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const iniFile = process.argv[2];
|
||||
|
||||
if (!iniFile) {
|
||||
console.error("Usage: ini-to-json.js file.ini");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const system = path.basename(iniFile, path.extname(iniFile));
|
||||
|
||||
const lines = fs.readFileSync(iniFile, "utf8").split(/\r?\n/);
|
||||
|
||||
const result = {
|
||||
[system]: {}
|
||||
};
|
||||
|
||||
let section = null;
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
|
||||
if (!trimmed || trimmed.startsWith(";") || trimmed.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const sectionMatch = trimmed.match(/^\[(.+)\]$/);
|
||||
|
||||
if (sectionMatch) {
|
||||
section = sectionMatch[1];
|
||||
result[system][section] ??= {};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!section) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const eq = trimmed.indexOf("=");
|
||||
|
||||
if (eq === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = trimmed.slice(0, eq).trim();
|
||||
const value = trimmed.slice(eq + 1).trim();
|
||||
|
||||
result[system][section][key] = value;
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
export PYGAME_HIDE_SUPPORT_PROMPT=1
|
||||
export DOLPHIN_CONFIG_DIR=/home/kuba/.var/app/org.DolphinEmu.dolphin-emu/config/dolphin-emu
|
||||
|
||||
KAZZITE_DIR=/home/kuba/kazzite
|
||||
|
||||
SCRIPTS_DIR=$KAZZITE_DIR/dolphin-presets/presets
|
||||
|
||||
PICKED_SCRIPT=$($KAZZITE_DIR/gamepad-utils/script-selector.py --scripts "$SCRIPTS_DIR")
|
||||
|
||||
echo PICKED!!!!!!!!!!! $PICKED_SCRIPT
|
||||
|
||||
mapfile -t EXTRA_ARGS < <("$PICKED_SCRIPT")
|
||||
|
||||
/usr/bin/flatpak run \
|
||||
--branch=stable \
|
||||
--arch=x86_64 \
|
||||
--command=/app/bin/dolphin-emu-wrapper \
|
||||
org.DolphinEmu.dolphin-emu \
|
||||
"${EXTRA_ARGS[@]}" \
|
||||
"$@"
|
||||
@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env node
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
const config = {
|
||||
"Dolphin": {
|
||||
"BluetoothPassthrough": {
|
||||
"Enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function format_value(value){
|
||||
if(typeof value== "boolean"){
|
||||
return value? "True" : "False";
|
||||
}else{return value}
|
||||
}
|
||||
|
||||
for (const [system, sections] of Object.entries(config)) {
|
||||
if(system =="WiimoteNew"){
|
||||
let content = "";
|
||||
for (const [section, keys] of Object.entries(sections)) {
|
||||
content += `[${section}]\n`;
|
||||
|
||||
for (const [key, value] of Object.entries(keys)) {
|
||||
content += `${key} = ${format_value(value)}\n`;
|
||||
}
|
||||
|
||||
content += "\n";
|
||||
}
|
||||
fs.writeFileSync(path.resolve(process.env.DOLPHIN_CONFIG_DIR, "WiimoteNew.ini"), content);
|
||||
continue;
|
||||
}
|
||||
for (const [section, keys] of Object.entries(sections)) {
|
||||
for (const [key, value] of Object.entries(keys)) {
|
||||
console.log(`--config=${system}.${section}.${key}=${format_value(value)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env node
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
const config = {
|
||||
"Dolphin": {
|
||||
"BluetoothPassthrough": {
|
||||
"Enabled": false
|
||||
}
|
||||
},
|
||||
"WiimoteNew": {
|
||||
"Wiimote1": {
|
||||
"Device": "SDL/0/Steam Controller",
|
||||
"Buttons/A": "`Button S`",
|
||||
"Buttons/B": "`Trigger R` | `Button E`",
|
||||
"Buttons/1": "`Button W`",
|
||||
"Buttons/2": "`Button N`",
|
||||
"Buttons/-": "Back",
|
||||
"Buttons/+": "Start",
|
||||
"Buttons/Home": "Return",
|
||||
"D-Pad/Up": "`Pad N`",
|
||||
"D-Pad/Down": "`Pad S`",
|
||||
"D-Pad/Left": "`Pad W`",
|
||||
"D-Pad/Right": "`Pad E`",
|
||||
"IR/Up": "`XInput2/0/Virtual core pointer:Cursor Y-`",
|
||||
"IR/Down": "`XInput2/0/Virtual core pointer:Cursor Y+`",
|
||||
"IR/Left": "`XInput2/0/Virtual core pointer:Cursor X-`",
|
||||
"IR/Right": "`XInput2/0/Virtual core pointer:Cursor X+`",
|
||||
"Shake/X": "`Button W`",
|
||||
"Shake/Y": "`Click 2`",
|
||||
"Shake/Z": "`Click 2`",
|
||||
"IRPassthrough/Object 1 X": "`IR Object 1 X`",
|
||||
"IRPassthrough/Object 1 Y": "`IR Object 1 Y`",
|
||||
"IRPassthrough/Object 1 Size": "`IR Object 1 Size`",
|
||||
"IRPassthrough/Object 2 X": "`IR Object 2 X`",
|
||||
"IRPassthrough/Object 2 Y": "`IR Object 2 Y`",
|
||||
"IRPassthrough/Object 2 Size": "`IR Object 2 Size`",
|
||||
"IRPassthrough/Object 3 X": "`IR Object 3 X`",
|
||||
"IRPassthrough/Object 3 Y": "`IR Object 3 Y`",
|
||||
"IRPassthrough/Object 3 Size": "`IR Object 3 Size`",
|
||||
"IRPassthrough/Object 4 X": "`IR Object 4 X`",
|
||||
"IRPassthrough/Object 4 Y": "`IR Object 4 Y`",
|
||||
"IRPassthrough/Object 4 Size": "`IR Object 4 Size`",
|
||||
"IMUAccelerometer/Up": "`Shoulder R`",
|
||||
"IMUAccelerometer/Down": "`Accel Down`",
|
||||
"IMUAccelerometer/Left": "`Accel Left`",
|
||||
"IMUAccelerometer/Right": "`Accel Right`",
|
||||
"IMUAccelerometer/Forward": "`Accel Forward`",
|
||||
"IMUAccelerometer/Backward": "`Accel Backward`",
|
||||
"IMUGyroscope/Pitch Up": "`Gyro Pitch Up`",
|
||||
"IMUGyroscope/Pitch Down": "`Gyro Pitch Down`",
|
||||
"IMUGyroscope/Roll Left": "`Gyro Roll Left`",
|
||||
"IMUGyroscope/Roll Right": "`Gyro Roll Right`",
|
||||
"IMUGyroscope/Yaw Left": "`Gyro Yaw Left`",
|
||||
"IMUGyroscope/Yaw Right": "`Gyro Yaw Right`",
|
||||
"Extension": "Nunchuk",
|
||||
"Nunchuk/Buttons/C": "`Shoulder L`",
|
||||
"Nunchuk/Buttons/Z": "`Trigger L`",
|
||||
"Nunchuk/Stick/Up": "`Left Y+`",
|
||||
"Nunchuk/Stick/Down": "`Left Y-`",
|
||||
"Nunchuk/Stick/Left": "`Left X-`",
|
||||
"Nunchuk/Stick/Right": "`Left X+`",
|
||||
"Nunchuk/Stick/Calibration": "100.00 101.96 108.24 115.81 116.93 116.65 108.24 101.96 100.00 101.96 108.24 109.55 109.52 109.58 108.24 101.96 100.00 101.96 108.24 112.38 113.77 113.06 108.24 101.96 100.00 101.96 108.24 112.86 114.09 114.41 108.24 101.96",
|
||||
"Nunchuk/Shake/X": "`Click 2`",
|
||||
"Nunchuk/Shake/Y": "`Click 2`",
|
||||
"Nunchuk/Shake/Z": "`Click 2`",
|
||||
"Source": "1",
|
||||
"IMUIR/Enabled": "False",
|
||||
"IR/Auto-Hide": "True",
|
||||
"Extension/Attach MotionPlus": "False"
|
||||
},
|
||||
"Wiimote2": {
|
||||
"Device": "XInput2/0/Virtual core pointer",
|
||||
"Source": "0"
|
||||
},
|
||||
"Wiimote3": {
|
||||
"Device": "XInput2/0/Virtual core pointer",
|
||||
"Source": "0"
|
||||
},
|
||||
"Wiimote4": {
|
||||
"Device": "XInput2/0/Virtual core pointer",
|
||||
"Source": "0"
|
||||
},
|
||||
"BalanceBoard": {
|
||||
"Device": "XInput2/0/Virtual core pointer",
|
||||
"Source": "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function format_value(value){
|
||||
if(typeof value== "boolean"){
|
||||
return value? "True" : "False";
|
||||
}else{return value}
|
||||
}
|
||||
|
||||
for (const [system, sections] of Object.entries(config)) {
|
||||
if(system =="WiimoteNew"){
|
||||
let content = "";
|
||||
for (const [section, keys] of Object.entries(sections)) {
|
||||
content += `[${section}]\n`;
|
||||
|
||||
for (const [key, value] of Object.entries(keys)) {
|
||||
content += `${key} = ${format_value(value)}\n`;
|
||||
}
|
||||
|
||||
content += "\n";
|
||||
}
|
||||
fs.writeFileSync(path.resolve(process.env.DOLPHIN_CONFIG_DIR, "WiimoteNew.ini"), content);
|
||||
continue;
|
||||
}
|
||||
for (const [section, keys] of Object.entries(sections)) {
|
||||
for (const [key, value] of Object.entries(keys)) {
|
||||
console.log(`--config=${system}.${section}.${key}=${format_value(value)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
import sys
|
||||
import sdl2
|
||||
import sdl2.ext
|
||||
import time
|
||||
|
||||
def main():
|
||||
if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_EVENTS) != 0:
|
||||
print(f"SDL_Init error: {sdl2.SDL_GetError().decode()}")
|
||||
return 1
|
||||
num_joysticks = sdl2.SDL_NumJoysticks()
|
||||
print("Joystick count: ", num_joysticks)
|
||||
for i in range(num_joysticks):
|
||||
print(i)
|
||||
name = sdl2.SDL_GameControllerNameForIndex(i)
|
||||
print(name)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@ -0,0 +1,407 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import sdl2
|
||||
import sdl2.ext
|
||||
import sdl2.sdlttf as ttf
|
||||
|
||||
|
||||
WIDTH = 1280
|
||||
HEIGHT = 720
|
||||
FONT = "/usr/share/fonts/open-sans/OpenSans-Semibold.ttf"
|
||||
|
||||
|
||||
def debug(msg, enabled):
|
||||
if enabled:
|
||||
print(f"[DEBUG] {msg}", file=sys.stderr)
|
||||
|
||||
|
||||
class ControllerManager:
|
||||
|
||||
def __init__(self, ignore, debug_enabled):
|
||||
self.ignore = ignore
|
||||
self.debug_enabled = debug_enabled
|
||||
self.controllers = {}
|
||||
self.unsupported = {}
|
||||
|
||||
def scan(self):
|
||||
self.controllers.clear()
|
||||
self.unsupported.clear()
|
||||
|
||||
count = sdl2.SDL_NumJoysticks()
|
||||
|
||||
for i in range(count):
|
||||
|
||||
name_ptr = sdl2.SDL_JoystickNameForIndex(i)
|
||||
|
||||
name = (
|
||||
name_ptr.decode()
|
||||
if name_ptr else
|
||||
"Unknown Device"
|
||||
)
|
||||
|
||||
if sdl2.SDL_IsGameController(i):
|
||||
|
||||
if any(
|
||||
x.lower() in name.lower()
|
||||
for x in self.ignore
|
||||
):
|
||||
debug(
|
||||
f"Ignoring {name}",
|
||||
self.debug_enabled
|
||||
)
|
||||
continue
|
||||
|
||||
c = sdl2.SDL_GameControllerOpen(i)
|
||||
|
||||
if c:
|
||||
|
||||
joy = sdl2.SDL_GameControllerGetJoystick(c)
|
||||
|
||||
instance = sdl2.SDL_JoystickInstanceID(
|
||||
joy
|
||||
)
|
||||
|
||||
self.controllers[instance] = {
|
||||
"name": name,
|
||||
"controller": c
|
||||
}
|
||||
|
||||
debug(
|
||||
f"Added controller {name}",
|
||||
self.debug_enabled
|
||||
)
|
||||
|
||||
else:
|
||||
self.unsupported[i] = name
|
||||
|
||||
debug(
|
||||
f"Unsupported joystick {name}",
|
||||
self.debug_enabled
|
||||
)
|
||||
|
||||
def add(self, index):
|
||||
|
||||
name_ptr = sdl2.SDL_JoystickNameForIndex(index)
|
||||
|
||||
name = (
|
||||
name_ptr.decode()
|
||||
if name_ptr else
|
||||
"Unknown Device"
|
||||
)
|
||||
|
||||
if sdl2.SDL_IsGameController(index):
|
||||
|
||||
c = sdl2.SDL_GameControllerOpen(index)
|
||||
|
||||
if c:
|
||||
|
||||
joy = sdl2.SDL_GameControllerGetJoystick(c)
|
||||
|
||||
instance = sdl2.SDL_JoystickInstanceID(
|
||||
joy
|
||||
)
|
||||
|
||||
self.controllers[instance] = {
|
||||
"name": name,
|
||||
"controller": c
|
||||
}
|
||||
|
||||
debug(
|
||||
f"Hotplugged {name}",
|
||||
self.debug_enabled
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
self.unsupported[index] = name
|
||||
|
||||
debug(
|
||||
f"Hotplugged unsupported {name}",
|
||||
self.debug_enabled
|
||||
)
|
||||
|
||||
def remove(self, instance):
|
||||
|
||||
if instance in self.controllers:
|
||||
name = self.controllers[instance]["name"]
|
||||
|
||||
sdl2.SDL_GameControllerClose(
|
||||
self.controllers[instance]["controller"]
|
||||
)
|
||||
|
||||
del self.controllers[instance]
|
||||
|
||||
debug(
|
||||
f"Removed {name}",
|
||||
self.debug_enabled
|
||||
)
|
||||
|
||||
|
||||
def draw_text(renderer, font, text, x, y, color):
|
||||
|
||||
surface = ttf.TTF_RenderUTF8_Blended(
|
||||
font,
|
||||
text.encode("utf-8"),
|
||||
color
|
||||
)
|
||||
|
||||
if not surface:
|
||||
print(
|
||||
"TTF render failed: " +
|
||||
ttf.TTF_GetError().decode(),
|
||||
file=sys.stderr
|
||||
)
|
||||
return
|
||||
|
||||
texture = sdl2.SDL_CreateTextureFromSurface(
|
||||
renderer,
|
||||
surface
|
||||
)
|
||||
|
||||
if not texture:
|
||||
print(
|
||||
"Texture creation failed: " +
|
||||
sdl2.SDL_GetError().decode(),
|
||||
file=sys.stderr
|
||||
)
|
||||
sdl2.SDL_FreeSurface(surface)
|
||||
return
|
||||
|
||||
rect = sdl2.SDL_Rect()
|
||||
rect.x = x
|
||||
rect.y = y
|
||||
rect.w = surface.contents.w
|
||||
rect.h = surface.contents.h
|
||||
|
||||
result = sdl2.SDL_RenderCopy(
|
||||
renderer,
|
||||
texture,
|
||||
None,
|
||||
rect
|
||||
)
|
||||
|
||||
if result != 0:
|
||||
print(
|
||||
"RenderCopy failed: " +
|
||||
sdl2.SDL_GetError().decode(),
|
||||
file=sys.stderr
|
||||
)
|
||||
|
||||
sdl2.SDL_DestroyTexture(texture)
|
||||
sdl2.SDL_FreeSurface(surface)
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument("question")
|
||||
|
||||
parser.add_argument(
|
||||
"--ignore",
|
||||
nargs="*",
|
||||
default=[]
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--debug",
|
||||
action="store_true"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
sdl2.SDL_Init(
|
||||
sdl2.SDL_INIT_VIDEO |
|
||||
sdl2.SDL_INIT_GAMECONTROLLER |
|
||||
sdl2.SDL_INIT_JOYSTICK
|
||||
)
|
||||
|
||||
|
||||
ttf.TTF_Init()
|
||||
|
||||
|
||||
window = sdl2.SDL_CreateWindow(
|
||||
args.question.encode(),
|
||||
sdl2.SDL_WINDOWPOS_CENTERED,
|
||||
sdl2.SDL_WINDOWPOS_CENTERED,
|
||||
WIDTH,
|
||||
HEIGHT,
|
||||
sdl2.SDL_WINDOW_FULLSCREEN
|
||||
)
|
||||
|
||||
|
||||
renderer = sdl2.SDL_CreateRenderer(
|
||||
window,
|
||||
-1,
|
||||
sdl2.SDL_RENDERER_ACCELERATED
|
||||
)
|
||||
|
||||
info = sdl2.SDL_RendererInfo()
|
||||
sdl2.SDL_GetRendererInfo(renderer, info)
|
||||
|
||||
print(
|
||||
"Renderer:",
|
||||
info.name.decode(),
|
||||
file=sys.stderr
|
||||
)
|
||||
|
||||
|
||||
font = ttf.TTF_OpenFont(
|
||||
FONT.encode(),
|
||||
42
|
||||
)
|
||||
|
||||
|
||||
manager = ControllerManager(
|
||||
args.ignore,
|
||||
args.debug
|
||||
)
|
||||
|
||||
manager.scan()
|
||||
|
||||
|
||||
event = sdl2.SDL_Event()
|
||||
|
||||
running = True
|
||||
|
||||
selected_controller = None
|
||||
selected_time = 0
|
||||
|
||||
while running:
|
||||
|
||||
while sdl2.SDL_PollEvent(event):
|
||||
|
||||
if event.type == sdl2.SDL_CONTROLLERDEVICEADDED:
|
||||
manager.add(
|
||||
event.cdevice.which
|
||||
)
|
||||
|
||||
elif event.type == sdl2.SDL_JOYDEVICEADDED:
|
||||
manager.add(
|
||||
event.jdevice.which
|
||||
)
|
||||
|
||||
elif event.type == sdl2.SDL_JOYDEVICEREMOVED:
|
||||
manager.remove(
|
||||
event.jdevice.which
|
||||
)
|
||||
|
||||
|
||||
elif event.type == sdl2.SDL_CONTROLLERBUTTONDOWN:
|
||||
|
||||
instance = event.cbutton.which
|
||||
|
||||
if instance in manager.controllers:
|
||||
selected_controller = manager.controllers[instance]["name"]
|
||||
|
||||
debug(
|
||||
f"Selected {selected_controller}",
|
||||
args.debug
|
||||
)
|
||||
|
||||
selected_time = sdl2.SDL_GetTicks()
|
||||
|
||||
# draw
|
||||
sdl2.SDL_SetRenderDrawColor(
|
||||
renderer,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
255
|
||||
)
|
||||
|
||||
sdl2.SDL_RenderClear(renderer)
|
||||
|
||||
|
||||
draw_text(
|
||||
renderer,
|
||||
font,
|
||||
args.question,
|
||||
50,
|
||||
50,
|
||||
sdl2.SDL_Color(255,255,255,255)
|
||||
)
|
||||
|
||||
|
||||
y = 150
|
||||
|
||||
|
||||
for c in manager.controllers.values():
|
||||
|
||||
draw_text(
|
||||
renderer,
|
||||
font,
|
||||
"✓ " + c["name"],
|
||||
100,
|
||||
y,
|
||||
sdl2.SDL_Color(255,255,255,255)
|
||||
)
|
||||
|
||||
y += 60
|
||||
|
||||
|
||||
for name in manager.unsupported.values():
|
||||
|
||||
draw_text(
|
||||
renderer,
|
||||
font,
|
||||
"✗ " + name,
|
||||
100,
|
||||
y,
|
||||
sdl2.SDL_Color(100,100,100,255)
|
||||
)
|
||||
|
||||
y += 60
|
||||
|
||||
|
||||
if selected_controller:
|
||||
|
||||
elapsed = sdl2.SDL_GetTicks() - selected_time
|
||||
|
||||
draw_text(
|
||||
renderer,
|
||||
font,
|
||||
"Selected:",
|
||||
100,
|
||||
550,
|
||||
sdl2.SDL_Color(255,255,255,255)
|
||||
)
|
||||
|
||||
draw_text(
|
||||
renderer,
|
||||
font,
|
||||
selected_controller,
|
||||
100,
|
||||
610,
|
||||
sdl2.SDL_Color(0,255,0,255)
|
||||
)
|
||||
|
||||
sdl2.SDL_RenderPresent(renderer)
|
||||
|
||||
if elapsed > 2000:
|
||||
print(selected_controller)
|
||||
running = False
|
||||
|
||||
continue
|
||||
else:
|
||||
sdl2.SDL_RenderPresent(renderer)
|
||||
|
||||
# cleanup
|
||||
for c in manager.controllers.values():
|
||||
sdl2.SDL_GameControllerClose(
|
||||
c["controller"]
|
||||
)
|
||||
|
||||
ttf.TTF_CloseFont(font)
|
||||
|
||||
sdl2.SDL_DestroyRenderer(renderer)
|
||||
sdl2.SDL_DestroyWindow(window)
|
||||
|
||||
ttf.TTF_Quit()
|
||||
sdl2.SDL_Quit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import pygame
|
||||
from PIL import Image
|
||||
|
||||
import sdl2
|
||||
import sdl2.ext
|
||||
|
||||
|
||||
# ---------- Configuration ----------
|
||||
DEFAULT_SCRIPTS_PATH = "/home/kuba/kazzite"
|
||||
TITLE = "Select script"
|
||||
BIG_FONT_SIZE = 100
|
||||
SMALL_FONT_SIZE = 40
|
||||
AXIS_DEADZONE = 0.6
|
||||
HAT_REPEAT_DELAY = 0.18
|
||||
FPS = 60
|
||||
|
||||
# ---------- Helpers ----------
|
||||
|
||||
def load_scripts(directory):
|
||||
return [
|
||||
{
|
||||
"name": p.stem,
|
||||
"path": str(p.resolve())
|
||||
}
|
||||
for p in Path(directory).iterdir()
|
||||
]
|
||||
|
||||
# ---------- UI with SDL2 input ----------
|
||||
|
||||
def run_ui(scripts):
|
||||
pygame.init()
|
||||
|
||||
screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
|
||||
screen_w, screen_h = screen.get_size()
|
||||
pygame.display.set_caption(TITLE)
|
||||
|
||||
scale = min(screen_w / 3840.0, screen_h / 2160.0)
|
||||
big_font = pygame.font.SysFont(None, int(BIG_FONT_SIZE * scale))
|
||||
small_font = pygame.font.SysFont(None, int(SMALL_FONT_SIZE * scale))
|
||||
|
||||
clock = pygame.time.Clock()
|
||||
selected = 0
|
||||
last_nav = 0
|
||||
|
||||
# ---------- SDL2 joystick init ----------
|
||||
if sdl2.SDL_Init(sdl2.SDL_INIT_JOYSTICK) != 0:
|
||||
print("SDL_Init Error:", sdl2.SDL_GetError().decode())
|
||||
sys.exit(1)
|
||||
|
||||
joystick_count = sdl2.SDL_NumJoysticks()
|
||||
sdl_joysticks = []
|
||||
for i in range(joystick_count):
|
||||
js = sdl2.SDL_JoystickOpen(i)
|
||||
if js:
|
||||
sdl_joysticks.append(js)
|
||||
|
||||
|
||||
running = True
|
||||
event = sdl2.SDL_Event()
|
||||
while running:
|
||||
now = time.time()
|
||||
pygame.event.pump() # Update pygame keyboard events
|
||||
|
||||
# Handle pygame keyboard events
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_UP:
|
||||
selected = max(0, selected - 1)
|
||||
elif event.key == pygame.K_DOWN:
|
||||
selected = min(len(scripts) - 1, selected + 1)
|
||||
elif event.key in (pygame.K_RETURN, pygame.K_KP_ENTER):
|
||||
path = scripts[selected]['path']
|
||||
print(path)
|
||||
running = False
|
||||
elif event.key == pygame.K_ESCAPE:
|
||||
print("ESCAPE")
|
||||
running = False
|
||||
|
||||
sdl2.SDL_JoystickUpdate()
|
||||
for i, js in enumerate(sdl_joysticks):
|
||||
# Buttons
|
||||
for b in range(sdl2.SDL_JoystickNumButtons(js)):
|
||||
if sdl2.SDL_JoystickGetButton(js, b):
|
||||
if b == 0: # confirm button
|
||||
path = scripts[selected]['path']
|
||||
print(path)
|
||||
running = False
|
||||
elif b == 1: # cancel button
|
||||
print("CANCEL")
|
||||
running = False
|
||||
|
||||
# Hats (D-pad)
|
||||
for h in range(sdl2.SDL_JoystickNumHats(js)):
|
||||
hat = sdl2.SDL_JoystickGetHat(js, h)
|
||||
if hat & sdl2.SDL_HAT_UP and now - last_nav > HAT_REPEAT_DELAY:
|
||||
selected = max(0, selected - 1)
|
||||
last_nav = now
|
||||
elif hat & sdl2.SDL_HAT_DOWN and now - last_nav > HAT_REPEAT_DELAY:
|
||||
selected = min(len(scripts) - 1, selected + 1)
|
||||
last_nav = now
|
||||
|
||||
# ---------- Render pygame UI ----------
|
||||
screen.fill((10, 10, 10))
|
||||
|
||||
title_surf = small_font.render(TITLE, True, (230, 230, 230))
|
||||
screen.blit(title_surf, ((screen_w - title_surf.get_width()) // 2, int(screen_h * 0.08)))
|
||||
|
||||
total = len(scripts)
|
||||
item_height = int(300 * scale)
|
||||
start_y = (screen_h - total * item_height) // 2
|
||||
|
||||
for i, script in enumerate(scripts):
|
||||
y = start_y + i * item_height
|
||||
color = (255, 255, 255) if i == selected else (180, 180, 180)
|
||||
|
||||
if i == selected:
|
||||
pygame.draw.rect(screen, (50, 50, 50), (screen_w * 0.1, y - 20, screen_w * 0.8, item_height - 40), border_radius=20)
|
||||
|
||||
name_surf = big_font.render(script['name'], True, color)
|
||||
screen.blit(name_surf, (int(screen_w * 0.4), y + item_height // 3))
|
||||
|
||||
pygame.display.flip()
|
||||
clock.tick(FPS)
|
||||
|
||||
# Cleanup SDL2 joysticks
|
||||
for js in sdl_joysticks:
|
||||
sdl2.SDL_JoystickClose(js)
|
||||
sdl2.SDL_QuitSubSystem(sdl2.SDL_INIT_JOYSTICK)
|
||||
pygame.quit()
|
||||
|
||||
# ---------- Main ----------
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--scripts", default=str(DEFAULT_SCRIPTS_PATH))
|
||||
args = parser.parse_args()
|
||||
|
||||
scripts = load_scripts(Path(args.scripts))
|
||||
run_ui(scripts)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue