Search code examples
linuxsdl-2pysdl2

How to run an SDL2 GUI app on debian without OS gui (no X)?


I have a small python3 and PySDL2 application that needs to run on a GUI-less debian linux. This is an embedded project running on a Librea Computer Board AML-S905X-CC. I have the HDMI output connected to a monitor and I can see the console so the video driver works. The desktop environment costs too much performance and is entirely unnecessary for my use case.

I have found someone else who has been able to achieve this a while back and the process seems familiar: https://discourse.libsdl.org/t/possible-to-run-sdl2-headless/25665

The code is pretty simple:

import sys
import os
import sdl2.ext

os.putenv("SDL_VIDEODRIVER","dummy")
RESOURCES = sdl2.ext.Resources(__file__, "resources")

print("start")

sdl2.ext.init()

window = sdl2.ext.Window("Hello World!",size=(720,480), flags=sdl2.SDL_WINDOW_HIDDEN)
window.show()

while True:
     continue

The application does run, however it just stays on the console screen.

As far as I know if there is no driver, linux just writes to the framebuffer directly. So if I can set up PySDL2 to do that, and show the gui in full screen, that's all I am looking for.

Last note is that I know this is possible because this is something I managed to achieve once before, about a year ago. However, due to a drive crash, that example has been lost.


Solution

  • I got it working. Ok so here's what I did.

    1. Complete OS wipe - fresh debian install (because god knows what i might have messed up in my experiments)
    2. Set up the python env like it complained about when installing PySDL2
    3. Install pysdl2 through pip AND sudo apt install sdl2-dev

    The most important part was running the UpdateWindow(w) and RenderPresent(r) which were not mentioned in the PySDL docs. But maybe that's because it was such a niche use case scenario.

    Bare in mind, this is likely not perfect code and that's because I'm no expert. But since all the experts don't bother to help figure these sort of questions out, this is the best you get:

    import ctypes
    import sys
    from sdl2 import *
    import time
    
    SDL_Init(SDL_INIT_EVENTS)
    
    
    w = SDL_CreateWindow(b"test", 0,0, 800,600, SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_HIDDEN)
    SDL_ShowWindow(w)
    SDL_RaiseWindow(w)
    
    window_surface = SDL_GetWindowSurface(w)
    r = SDL_CreateSoftwareRenderer(window_surface)
    #r = sdl2.ext.Renderer(w, -1, (800,600), sdl2.SDL_RENDERER_SOFTWARE)
    
    #sprite_factory = sdl2.ext.SpriteFactory(sdl2.ext.TEXTURE, renderer=r)
    #sprite = sprite_factory.from_image("resources/imgs/fr.png")
    
    event = SDL_Event()
    
    running = True
    while running:
    
            while SDL_PollEvent(event) != 0:
                    print("events were present", event.key.keysym.sym)
                    #print(SDL_GetKeyName(event.key.keysym.sym))
                    if event.type == SDL_QUIT:
                            running = False
                            break
    
                    if event.type == SDL_KEYDOWN:
                            #if event.key.keysym.sym == sdl2.SDLK_ESCAPE:
                            running = False
                            print("keydown detected")
                            break
    
    
            SDL_RenderClear(r)
    
            #sprite_render = sprite_factory.create_sprite_render_system(r)
            #sprite_render.render(sprite, x=100, y=100)
    
            #WHITE = sdl2.ext.Color(255,255,255,255)
            #r.draw_rect((5,5,64,64), WHITE)
    
            SDL_UpdateWindowSurface(w)
            SDL_RenderPresent(r)
    
    SDL_DestroyWindow(w)
    SDL_Quit()
    exit()