Mercurial > touhou
view pytouhou/lib/sdl.pyx @ 792:11bc22bad1bf default tip
python: Replace the image crate with png
We weren’t using any of its features anyway, so the png crate is exactly what
we need, without the many heavy dependencies of image.
https://github.com/image-rs/image-png/pull/670 will eventually make it even
faster to build.
| author | Link Mauve <linkmauve@linkmauve.fr> |
|---|---|
| date | Sat, 17 Jan 2026 22:22:25 +0100 |
| parents | 7e940ebeb5fd |
| children |
line wrap: on
line source
# -*- encoding: utf-8 -*- ## ## Copyright (C) 2013 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published ## by the Free Software Foundation; version 3 only. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## import pytouhou.lib.gui as gui from pytouhou.utils.helpers import get_logger logger = get_logger(__name__) GL_CONTEXT_MAJOR_VERSION = SDL_GL_CONTEXT_MAJOR_VERSION GL_CONTEXT_MINOR_VERSION = SDL_GL_CONTEXT_MINOR_VERSION GL_CONTEXT_PROFILE_MASK = SDL_GL_CONTEXT_PROFILE_MASK GL_DOUBLEBUFFER = SDL_GL_DOUBLEBUFFER GL_RED_SIZE = SDL_GL_RED_SIZE GL_GREEN_SIZE = SDL_GL_GREEN_SIZE GL_BLUE_SIZE = SDL_GL_BLUE_SIZE GL_DEPTH_SIZE = SDL_GL_DEPTH_SIZE GL_CONTEXT_PROFILE_CORE = SDL_GL_CONTEXT_PROFILE_CORE GL_CONTEXT_PROFILE_COMPATIBILITY = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY GL_CONTEXT_PROFILE_ES = SDL_GL_CONTEXT_PROFILE_ES WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED WINDOW_OPENGL = SDL_WINDOW_OPENGL WINDOW_RESIZABLE = SDL_WINDOW_RESIZABLE SCANCODE_Z = SDL_SCANCODE_Z SCANCODE_X = SDL_SCANCODE_X SCANCODE_P = SDL_SCANCODE_P SCANCODE_LSHIFT = SDL_SCANCODE_LSHIFT SCANCODE_UP = SDL_SCANCODE_UP SCANCODE_DOWN = SDL_SCANCODE_DOWN SCANCODE_LEFT = SDL_SCANCODE_LEFT SCANCODE_RIGHT = SDL_SCANCODE_RIGHT SCANCODE_LCTRL = SDL_SCANCODE_LCTRL SCANCODE_ESCAPE = SDL_SCANCODE_ESCAPE SCANCODE_HOME = SDL_SCANCODE_HOME WINDOWEVENT_RESIZED = SDL_WINDOWEVENT_RESIZED KEYDOWN = SDL_KEYDOWN QUIT = SDL_QUIT WINDOWEVENT = SDL_WINDOWEVENT class SDLError(gui.Error): def __init__(self): error = SDL_GetError() Exception.__init__(self, error.decode()) class SDL: def __init__(self, *, video=True): self.video = video def __enter__(self): global keyboard_state IF UNAME_SYSNAME == "Windows": SDL_SetMainReady() init(SDL_INIT_VIDEO if self.video else 0) ttf_init() keyboard_state = SDL_GetKeyboardState(NULL) def __exit__(self, *args): TTF_Quit() SDL_Quit() cdef class Window(gui.Window): def __init__(self, str title, int x, int y, int w, int h, Uint32 flags): title_bytes = title.encode() self.window = SDL_CreateWindow(title_bytes, x, y, w, h, flags) if self.window == NULL: raise SDLError() def __dealloc__(self): if self.context != NULL: SDL_GL_DeleteContext(self.context) if self.window != NULL: SDL_DestroyWindow(self.window) cdef void create_gl_context(self) except *: self.context = SDL_GL_CreateContext(self.window) if self.context == NULL: raise SDLError() cdef void present(self) nogil: if self.renderer == NULL: SDL_GL_SwapWindow(self.window) else: SDL_RenderPresent(self.renderer) cdef void set_window_size(self, int width, int height) nogil: SDL_SetWindowSize(self.window, width, height) cdef void set_swap_interval(self, int interval) except *: if SDL_GL_SetSwapInterval(interval) < 0: raise SDLError() cdef list get_events(self): cdef SDL_Event event ret = [] while SDL_PollEvent(&event): if event.type == SDL_KEYDOWN: scancode = event.key.keysym.scancode if scancode == SDL_SCANCODE_ESCAPE: ret.append((gui.PAUSE, None)) elif scancode in (SDL_SCANCODE_P, SDL_SCANCODE_HOME): ret.append((gui.SCREENSHOT, None)) elif scancode == SDL_SCANCODE_DOWN: ret.append((gui.DOWN, None)) elif scancode == SDL_SCANCODE_F11: ret.append((gui.FULLSCREEN, None)) elif scancode == SDL_SCANCODE_RETURN: mod = event.key.keysym.mod if mod & KMOD_ALT: ret.append((gui.FULLSCREEN, None)) elif event.type == SDL_QUIT: ret.append((gui.EXIT, None)) elif event.type == SDL_WINDOWEVENT: if event.window.event == SDL_WINDOWEVENT_RESIZED: ret.append((gui.RESIZE, (event.window.data1, event.window.data2))) return ret cdef int get_keystate(self) nogil: cdef int keystate = 0 cdef const Uint8 *keys = keyboard_state if keys[SCANCODE_Z]: keystate |= 1 if keys[SCANCODE_X]: keystate |= 2 if keys[SCANCODE_LSHIFT]: keystate |= 4 if keys[SCANCODE_UP]: keystate |= 16 if keys[SCANCODE_DOWN]: keystate |= 32 if keys[SCANCODE_LEFT]: keystate |= 64 if keys[SCANCODE_RIGHT]: keystate |= 128 if keys[SCANCODE_LCTRL]: keystate |= 256 return keystate cdef void toggle_fullscreen(self) nogil: ret = SDL_SetWindowFullscreen(self.window, 0 if self.is_fullscreen else SDL_WINDOW_FULLSCREEN_DESKTOP) if ret == -1: with gil: raise SDLError() self.is_fullscreen = not self.is_fullscreen # The following functions are there for the pure SDL backend. cdef bint create_renderer(self, Uint32 flags) except True: self.renderer = SDL_CreateRenderer(self.window, -1, flags) if self.renderer == NULL: raise SDLError() cpdef bint render_clear(self) except True: ret = SDL_RenderClear(self.renderer) if ret == -1: raise SDLError() cpdef bint render_copy(self, Texture texture, Rect srcrect, Rect dstrect) except True: ret = SDL_RenderCopy(self.renderer, texture.texture, &srcrect.rect, &dstrect.rect) if ret == -1: raise SDLError() cpdef bint render_copy_ex(self, Texture texture, Rect srcrect, Rect dstrect, double angle, bint flip) except True: ret = SDL_RenderCopyEx(self.renderer, texture.texture, &srcrect.rect, &dstrect.rect, angle, NULL, flip) if ret == -1: raise SDLError() cpdef bint render_set_clip_rect(self, Rect rect) except True: ret = SDL_RenderSetClipRect(self.renderer, &rect.rect) if ret == -1: raise SDLError() cpdef bint render_set_viewport(self, Rect rect) except True: ret = SDL_RenderSetViewport(self.renderer, &rect.rect) if ret == -1: raise SDLError() cdef Texture create_texture_from_surface(self, Surface surface): texture = Texture() texture.texture = SDL_CreateTextureFromSurface(self.renderer, surface.surface) if texture.texture == NULL: raise SDLError() return texture cdef class Texture: cpdef set_color_mod(self, Uint8 r, Uint8 g, Uint8 b): ret = SDL_SetTextureColorMod(self.texture, r, g, b) if ret == -1: raise SDLError() cpdef set_alpha_mod(self, Uint8 alpha): ret = SDL_SetTextureAlphaMod(self.texture, alpha) if ret == -1: raise SDLError() cpdef set_blend_mode(self, SDL_BlendMode blend_mode): ret = SDL_SetTextureBlendMode(self.texture, blend_mode) if ret == -1: raise SDLError() cdef class Rect: def __init__(self, int x, int y, int w, int h): self.rect.x = x self.rect.y = y self.rect.w = w self.rect.h = h cdef class Color: def __init__(self, Uint8 b, Uint8 g, Uint8 r, Uint8 a=255): self.color.r = r self.color.g = g self.color.b = b self.color.a = a cdef class Surface: def __init__(self, bytes data=None): self.data = data def __dealloc__(self): if self.surface != NULL: SDL_FreeSurface(self.surface) property pixels: def __get__(self): return bytes(self.surface.pixels[:self.surface.w * self.surface.h * 4]) cdef bint blit(self, Surface other) except True: if SDL_BlitSurface(other.surface, NULL, self.surface, NULL) < 0: raise SDLError() cdef void set_alpha(self, Surface alpha_surface) nogil: nb_pixels = self.surface.w * self.surface.h image = self.surface.pixels alpha = alpha_surface.surface.pixels for i in range(nb_pixels): # Only use the red value, assume the others are equal. image[3+4*i] = alpha[3*i] cdef class Font: def __init__(self, str filename, int ptsize): path = filename.encode() self.font = TTF_OpenFont(path, ptsize) if self.font == NULL: raise SDLError() def __dealloc__(self): if self.font != NULL: TTF_CloseFont(self.font) cdef Surface render(self, unicode text): cdef SDL_Color white white = SDL_Color(255, 255, 255, 255) surface = Surface() string = text.encode('utf-8') surface.surface = TTF_RenderUTF8_Blended(self.font, string, white) if surface.surface == NULL: raise SDLError() return surface cdef bint init(Uint32 flags) except True: if SDL_Init(flags) < 0: raise SDLError() cdef bint ttf_init() except True: if TTF_Init() < 0: raise SDLError() cdef bint gl_set_attribute(SDL_GLattr attr, int value) except True: if SDL_GL_SetAttribute(attr, value) < 0: raise SDLError() cdef Surface create_rgba_surface(bytes data, int width, int height): surface = Surface(data) cdef char *pixels = <char*>data depth = 32 pitch = width * 4 fmt = SDL_PIXELFORMAT_ABGR8888 surface.surface = SDL_CreateRGBSurfaceWithFormatFrom(pixels, width, height, depth, pitch, fmt) if surface.surface == NULL: raise SDLError() return surface cdef Uint32 get_ticks() nogil: return SDL_GetTicks() cdef void delay(Uint32 ms) nogil: SDL_Delay(ms) cpdef bint show_simple_message_box(unicode message) except True: text = message.encode('UTF-8') ret = SDL_ShowSimpleMessageBox(1, 'PyTouhou', text, NULL) if ret == -1: raise SDLError()
