# -*- coding: utf-8 -*- # ruff: noqa: F403, F405 from __future__ import absolute_import from typing import Any from imgui_bundle import imgui from sdl3 import * from .opengl_backend_programmable import ProgrammablePipelineRenderer import ctypes from typing import Dict SdlKey = int class SDL3Renderer(ProgrammablePipelineRenderer): """Basic SDL3 integration implementation.""" key_map: Dict[SdlKey, imgui.Key] MOUSE_WHEEL_OFFSET_SCALE = 0.5 def __init__(self, window): super(SDL3Renderer, self).__init__() self.window = window self._mouse_wheel = 0.0 self._gui_time = None width_ptr = ctypes.pointer(ctypes.c_int(0)) height_ptr = ctypes.pointer(ctypes.c_int(0)) SDL_GetWindowSize(self.window, width_ptr, height_ptr) self.io.display_size = width_ptr[0], height_ptr[0] def get_clipboard_text(_imgui_context: Any) -> str: r: ctypes.c_char_p = SDL_GetClipboardText() return r.decode() if r else "" # type: ignore def set_clipboard_text(_imgui_context: Any, text: str) -> None: SDL_SetClipboardText(ctypes.c_char_p(text.encode())) imgui.get_platform_io().platform_get_clipboard_text_fn = get_clipboard_text imgui.get_platform_io().platform_set_clipboard_text_fn = set_clipboard_text SDL_StartTextInput(self.window) self._map_keys() def _map_keys(self): self.key_map = {} key_map = self.key_map # Control Keys key_map[SDL_SCANCODE_TAB] = imgui.Key.tab key_map[SDL_SCANCODE_BACKSPACE] = imgui.Key.backspace key_map[SDL_SCANCODE_RETURN] = imgui.Key.enter key_map[SDL_SCANCODE_ESCAPE] = imgui.Key.escape key_map[SDL_SCANCODE_INSERT] = imgui.Key.insert key_map[SDL_SCANCODE_DELETE] = imgui.Key.delete key_map[SDL_SCANCODE_SPACE] = imgui.Key.space # Function Keys key_map[SDL_SCANCODE_F1] = imgui.Key.f1 key_map[SDL_SCANCODE_F2] = imgui.Key.f2 key_map[SDL_SCANCODE_F3] = imgui.Key.f3 key_map[SDL_SCANCODE_F4] = imgui.Key.f4 key_map[SDL_SCANCODE_F5] = imgui.Key.f5 key_map[SDL_SCANCODE_F6] = imgui.Key.f6 key_map[SDL_SCANCODE_F7] = imgui.Key.f7 key_map[SDL_SCANCODE_F8] = imgui.Key.f8 key_map[SDL_SCANCODE_F9] = imgui.Key.f9 key_map[SDL_SCANCODE_F10] = imgui.Key.f10 key_map[SDL_SCANCODE_F11] = imgui.Key.f11 key_map[SDL_SCANCODE_F12] = imgui.Key.f12 # Navigation Keys key_map[SDL_SCANCODE_LEFT] = imgui.Key.left_arrow key_map[SDL_SCANCODE_RIGHT] = imgui.Key.right_arrow key_map[SDL_SCANCODE_UP] = imgui.Key.up_arrow key_map[SDL_SCANCODE_DOWN] = imgui.Key.down_arrow key_map[SDL_SCANCODE_PAGEUP] = imgui.Key.page_up key_map[SDL_SCANCODE_PAGEDOWN] = imgui.Key.page_down key_map[SDL_SCANCODE_HOME] = imgui.Key.home key_map[SDL_SCANCODE_END] = imgui.Key.end # Numeric Keys key_map[SDL_SCANCODE_0] = imgui.Key._0 key_map[SDL_SCANCODE_1] = imgui.Key._1 key_map[SDL_SCANCODE_2] = imgui.Key._2 key_map[SDL_SCANCODE_3] = imgui.Key._3 key_map[SDL_SCANCODE_4] = imgui.Key._4 key_map[SDL_SCANCODE_5] = imgui.Key._5 key_map[SDL_SCANCODE_6] = imgui.Key._6 key_map[SDL_SCANCODE_7] = imgui.Key._7 key_map[SDL_SCANCODE_8] = imgui.Key._8 key_map[SDL_SCANCODE_9] = imgui.Key._9 # Keypad Keys key_map[SDL_SCANCODE_KP_ENTER] = imgui.Key.keypad_enter key_map[SDL_SCANCODE_KP_0] = imgui.Key.keypad0 key_map[SDL_SCANCODE_KP_1] = imgui.Key.keypad1 key_map[SDL_SCANCODE_KP_2] = imgui.Key.keypad2 key_map[SDL_SCANCODE_KP_3] = imgui.Key.keypad3 key_map[SDL_SCANCODE_KP_4] = imgui.Key.keypad4 key_map[SDL_SCANCODE_KP_5] = imgui.Key.keypad5 key_map[SDL_SCANCODE_KP_6] = imgui.Key.keypad6 key_map[SDL_SCANCODE_KP_7] = imgui.Key.keypad7 key_map[SDL_SCANCODE_KP_8] = imgui.Key.keypad8 key_map[SDL_SCANCODE_KP_9] = imgui.Key.keypad9 key_map[SDL_SCANCODE_KP_DECIMAL] = imgui.Key.keypad_decimal key_map[SDL_SCANCODE_KP_DIVIDE] = imgui.Key.keypad_divide key_map[SDL_SCANCODE_KP_MULTIPLY] = imgui.Key.keypad_multiply key_map[SDL_SCANCODE_KP_MINUS] = imgui.Key.keypad_subtract key_map[SDL_SCANCODE_KP_PLUS] = imgui.Key.keypad_add key_map[SDL_SCANCODE_KP_EQUALS] = imgui.Key.keypad_equal # Alphabetic Keys key_map[SDL_SCANCODE_A] = imgui.Key.a key_map[SDL_SCANCODE_B] = imgui.Key.b key_map[SDL_SCANCODE_C] = imgui.Key.c key_map[SDL_SCANCODE_D] = imgui.Key.d key_map[SDL_SCANCODE_E] = imgui.Key.e key_map[SDL_SCANCODE_F] = imgui.Key.f key_map[SDL_SCANCODE_G] = imgui.Key.g key_map[SDL_SCANCODE_H] = imgui.Key.h key_map[SDL_SCANCODE_I] = imgui.Key.i key_map[SDL_SCANCODE_J] = imgui.Key.j key_map[SDL_SCANCODE_K] = imgui.Key.k key_map[SDL_SCANCODE_L] = imgui.Key.l key_map[SDL_SCANCODE_M] = imgui.Key.m key_map[SDL_SCANCODE_N] = imgui.Key.n key_map[SDL_SCANCODE_O] = imgui.Key.o key_map[SDL_SCANCODE_P] = imgui.Key.p key_map[SDL_SCANCODE_Q] = imgui.Key.q key_map[SDL_SCANCODE_R] = imgui.Key.r key_map[SDL_SCANCODE_S] = imgui.Key.s key_map[SDL_SCANCODE_T] = imgui.Key.t key_map[SDL_SCANCODE_U] = imgui.Key.u key_map[SDL_SCANCODE_V] = imgui.Key.v key_map[SDL_SCANCODE_W] = imgui.Key.w key_map[SDL_SCANCODE_X] = imgui.Key.x key_map[SDL_SCANCODE_Y] = imgui.Key.y key_map[SDL_SCANCODE_Z] = imgui.Key.z # Modifier Keys key_map[SDL_SCANCODE_LCTRL] = imgui.Key.left_ctrl key_map[SDL_SCANCODE_RCTRL] = imgui.Key.right_ctrl key_map[SDL_SCANCODE_LSHIFT] = imgui.Key.left_shift key_map[SDL_SCANCODE_RSHIFT] = imgui.Key.right_shift key_map[SDL_SCANCODE_LALT] = imgui.Key.left_alt key_map[SDL_SCANCODE_RALT] = imgui.Key.right_alt key_map[SDL_SCANCODE_LGUI] = imgui.Key.left_super key_map[SDL_SCANCODE_RGUI] = imgui.Key.right_super # Media Keys key_map[SDL_SCANCODE_MEDIA_NEXT_TRACK] = imgui.Key.app_forward key_map[SDL_SCANCODE_MEDIA_PREVIOUS_TRACK] = imgui.Key.app_back # key_map[SDL_SCANCODE_MEDIA_STOP] = imgui.Key.none # key_map[SDL_SCANCODE_MEDIA_PLAY] = imgui.Key.none # key_map[SDL_SCANCODE_MUTE] = imgui.Key.none # Application Keys key_map[SDL_SCANCODE_APPLICATION] = imgui.Key.menu # Lock Keys key_map[SDL_SCANCODE_CAPSLOCK] = imgui.Key.caps_lock key_map[SDL_SCANCODE_SCROLLLOCK] = imgui.Key.scroll_lock key_map[SDL_SCANCODE_NUMLOCKCLEAR] = imgui.Key.num_lock # Symbol Keys key_map[SDL_SCANCODE_GRAVE] = imgui.Key.grave_accent # ` key_map[SDL_SCANCODE_MINUS] = imgui.Key.minus # - key_map[SDL_SCANCODE_EQUALS] = imgui.Key.equal # = key_map[SDL_SCANCODE_LEFTBRACKET] = imgui.Key.left_bracket # [ key_map[SDL_SCANCODE_RIGHTBRACKET] = imgui.Key.right_bracket # ] key_map[SDL_SCANCODE_BACKSLASH] = imgui.Key.backslash # \ key_map[SDL_SCANCODE_SEMICOLON] = imgui.Key.semicolon # ; key_map[SDL_SCANCODE_APOSTROPHE] = imgui.Key.apostrophe # ' key_map[SDL_SCANCODE_COMMA] = imgui.Key.comma # , key_map[SDL_SCANCODE_PERIOD] = imgui.Key.period # . key_map[SDL_SCANCODE_SLASH] = imgui.Key.slash # / # Miscellaneous Keys key_map[SDL_SCANCODE_PRINTSCREEN] = imgui.Key.print_screen key_map[SDL_SCANCODE_PAUSE] = imgui.Key.pause def process_event(self, event): io = self.io if event.type == SDL_EVENT_MOUSE_MOTION: if SDL_GetWindowFlags(self.window) & SDL_WINDOW_MOUSE_FOCUS: io.mouse_pos = -1, -1 io.mouse_pos = event.motion.x, event.motion.y return True if event.type == SDL_EVENT_MOUSE_WHEEL: io.add_mouse_wheel_event( event.wheel.x * self.MOUSE_WHEEL_OFFSET_SCALE, event.wheel.y * self.MOUSE_WHEEL_OFFSET_SCALE) return True if event.type == SDL_EVENT_MOUSE_BUTTON_DOWN or event.type == SDL_EVENT_MOUSE_BUTTON_UP: imgui_button = 0 if event.button.button == SDL_BUTTON_LEFT: imgui_button = 0 elif event.button.button == SDL_BUTTON_RIGHT: imgui_button = 1 elif event.button.button == SDL_BUTTON_MIDDLE: imgui_button = 2 down = event.type == SDL_EVENT_MOUSE_BUTTON_DOWN io.add_mouse_button_event(imgui_button, down) return True if event.type == SDL_EVENT_KEY_UP or event.type == SDL_EVENT_KEY_DOWN: sdl_key = event.key.scancode if sdl_key in self.key_map: imgui_key = self.key_map[sdl_key] down = event.type == SDL_EVENT_KEY_DOWN io.add_key_event(imgui_key, down) # Handle modifiers, since ImGui has an additional mod_ctrl / shift / etc if imgui_key == imgui.Key.left_ctrl or imgui_key == imgui.Key.right_ctrl: io.add_key_event(imgui.Key.mod_ctrl, down) if imgui_key == imgui.Key.left_shift or imgui_key == imgui.Key.right_shift: io.add_key_event(imgui.Key.mod_shift, down) if imgui_key == imgui.Key.left_alt or imgui_key == imgui.Key.right_alt: io.add_key_event(imgui.Key.mod_alt, down) if imgui_key == imgui.Key.left_super or imgui_key == imgui.Key.right_super: io.add_key_event(imgui.Key.mod_super, down) return True if event.type == SDL_EVENT_TEXT_INPUT: for char in event.text.text.decode("utf-8"): io.add_input_character(ord(char)) return True if event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN or event.type == SDL_EVENT_GAMEPAD_BUTTON_UP: button = event.gbutton.button is_pressed = event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN # Map SDL_CONTROLLER_BUTTON_* to imgui keys if button == SDL_GAMEPAD_BUTTON_LABEL_A: imgui.get_io().add_key_event(imgui.Key.gamepad_face_down, is_pressed) elif button == SDL_GAMEPAD_BUTTON_LABEL_B: imgui.get_io().add_key_event(imgui.Key.gamepad_face_right, is_pressed) elif button == SDL_GAMEPAD_BUTTON_LABEL_X: imgui.get_io().add_key_event(imgui.Key.gamepad_face_left, is_pressed) elif button == SDL_GAMEPAD_BUTTON_LABEL_Y: imgui.get_io().add_key_event(imgui.Key.gamepad_face_up, is_pressed) elif button == SDL_GAMEPAD_BUTTON_DPAD_UP: imgui.get_io().add_key_event(imgui.Key.gamepad_dpad_up, is_pressed) elif button == SDL_GAMEPAD_BUTTON_DPAD_DOWN: imgui.get_io().add_key_event(imgui.Key.gamepad_dpad_down, is_pressed) elif button == SDL_GAMEPAD_BUTTON_DPAD_LEFT: imgui.get_io().add_key_event(imgui.Key.gamepad_dpad_left, is_pressed) elif button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT: imgui.get_io().add_key_event(imgui.Key.gamepad_dpad_right, is_pressed) return True def process_inputs(self): io = imgui.get_io() s_w = ctypes.pointer(ctypes.c_int(0)) s_h = ctypes.pointer(ctypes.c_int(0)) SDL_GetWindowSize(self.window, s_w, s_h) w = s_w.contents.value h = s_h.contents.value io.display_size = w, h io.display_framebuffer_scale = (1, 1) current_time = SDL_GetTicks() / 1000.0 if self._gui_time: io.delta_time = current_time - self._gui_time else: io.delta_time = 1.0 / 60.0 if io.delta_time <= 0.0: io.delta_time = 1.0 / 1000.0 self._gui_time = current_time