There was a bug related to the appstate in my previous example.
The AppState/ctypes.py_object object was created and released in SDL_AppInit.
When running in debug mode, the example failed when accessing the appstate argument in the SDL_AppEvent and SDL_AppIterate callback functions.
#!/usr/bin/env python3
import ctypes
import dataclasses
import sys
import time
import typing
import sdl3
@dataclasses.dataclass
class AppState:
window: ctypes.POINTER(sdl3.SDL_Window)
renderer: ctypes.POINTER(sdl3.SDL_Renderer)
state: typing.Optional[AppState] = None
def SDL_AppEvent(c_appstate, c_event: ctypes.POINTER(sdl3.SDL_Event)) -> sdl3.SDL_AppResult:
event: sdl3.SDL_Event = c_event.contents
if event.type == sdl3.SDL_EVENT_QUIT:
return sdl3.SDL_APP_SUCCESS
return sdl3.SDL_APP_CONTINUE
def SDL_AppIterate(c_appstate) -> sdl3.SDL_AppResult:
if not sdl3.SDL_SetRenderDrawColor(state.renderer, int(time.time() * 255 / 2) % 256, 0, 0, sdl3.SDL_ALPHA_OPAQUE):
print("SDL_SetRenderDrawColorFloat failed")
return sdl3.SDL_APP_FAILURE
if not sdl3.SDL_RenderClear(state.renderer):
print("SDL_RenderClear failed")
return sdl3.SDL_APP_FAILURE
if not sdl3.SDL_RenderPresent(state.renderer):
print("SDL_RenderPresent failed")
return sdl3.SDL_APP_FAILURE
return sdl3.SDL_APP_CONTINUE
def SDL_AppInit(c_appstate, argc, argv) -> sdl3.SDL_AppResult:
p_window = ctypes.POINTER(sdl3.SDL_Window)()
p_renderer = ctypes.POINTER(sdl3.SDL_Renderer)()
if not sdl3.SDL_CreateWindowAndRenderer(b"Hello world!", 640, 480, 0, ctypes.pointer(p_window), ctypes.pointer(p_renderer)):
print("sdl3.SDL_CreateWindowAndRenderer failed")
return sdl3.SDL_APP_FAILURE
global state
state = AppState(window=p_window.contents, renderer=p_renderer.contents)
c_appstate[0] = ctypes.cast(ctypes.pointer(ctypes.py_object(state)), ctypes.c_void_p)
return sdl3.SDL_APP_CONTINUE
def SDL_AppQuit(c_appstate, result) -> None: # ctypes.POINTER[sdl3.SDL_AppResult]
appstate: AppState = ctypes.cast(c_appstate, ctypes.POINTER(ctypes.py_object)).contents.value
sdl3.SDL_DestroyRenderer(appstate.renderer)
sdl3.SDL_DestroyWindow(appstate.window)
def main():
c_SDL_AppInit_cfunctype = ctypes.CFUNCTYPE(sdl3.SDL_AppResult, ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.POINTER(ctypes.c_char_p))
c_SDL_AppIterate_cfunctype = ctypes.CFUNCTYPE(sdl3.SDL_AppResult, ctypes.c_void_p)
c_SDL_AppEvent_cfunctype = ctypes.CFUNCTYPE(sdl3.SDL_AppResult, ctypes.c_void_p, ctypes.POINTER(sdl3.SDL_Event))
c_SDL_AppQuit_cfunctype = ctypes.CFUNCTYPE(None, ctypes.c_void_p, sdl3.SDL_AppResult)
c_arg_array_t = ctypes.c_char_p * (len(sys.argv) + 0)
c_args = c_arg_array_t(*[ctypes.c_char_p(a.encode()) for a in sys.argv])
result = sdl3.SDL_EnterAppMainCallbacks(ctypes.c_int(len(sys.argv)), c_args,
c_SDL_AppInit_cfunctype(SDL_AppInit),
c_SDL_AppIterate_cfunctype(SDL_AppIterate),
c_SDL_AppEvent_cfunctype(SDL_AppEvent),
c_SDL_AppQuit_cfunctype(SDL_AppQuit))
return result
if __name__ == "__main__":
raise SystemExit(main())