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