You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
408 lines
8.3 KiB
Python
408 lines
8.3 KiB
Python
#!/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())
|