#!/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()