summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-12-11 11:39:36 -0500
committerpommicket <pommicket@gmail.com>2022-12-11 11:39:36 -0500
commit1d06c1f0d3fa058b67e8fe999c654d10d7f46896 (patch)
treef4aabd7d15b8a57ab7b20462e28fa7f292b02cc4 /src
shaders
Diffstat (limited to 'src')
-rw-r--r--src/main.rs56
-rw-r--r--src/sdl.rs1116
-rw-r--r--src/win.rs866
3 files changed, 2038 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..fdd2f54
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,56 @@
+mod sdl;
+pub mod win;
+
+fn try_main() -> Result<(), String> {
+ let mut window = win::Window::new("AutoSDF", 1280, 720, true)
+ .map_err(|e| format!("Error creating window: {e}"))?;
+
+ let program = window.create_program(
+ "attribute vec2 v_pos;
+ void main() {
+ gl_Position = vec4(v_pos, 0.0, 1.0);
+ }",
+ "void main() {
+ o_color = vec4(1.0, 0.0, 0.0, 1.0);
+ }"
+ ).map_err(|e| format!("Error compiling shader:\n{e}"))?;
+
+ let mut buffer = window.create_buffer();
+ let data: &[[f32; 2]] = &[
+ [-1.0, -1.0],
+ [1.0, -1.0],
+ [1.0, 1.0],
+ [-1.0, -1.0],
+ [1.0, 1.0],
+ [-1.0, 1.0],
+ ];
+ window.set_buffer_data(&mut buffer, data);
+ let mut array = window.create_vertex_array(buffer, &program);
+ window.array_attrib2f(&mut array, "v_pos", 0);
+
+ 'mainloop: loop {
+ while let Some(event) = window.next_event() {
+ use win::Event::*;
+ match event {
+ Quit => break 'mainloop,
+ _ => {},
+ }
+ println!("{event:?}");
+ }
+
+ window.clear_screen(win::ColorF32::BLACK);
+ window.use_program(&program);
+
+ window.draw_array(&array);
+
+ window.swap();
+ }
+
+ Ok(())
+}
+
+fn main() {
+ if let Err(e) = try_main() {
+ win::display_error_message(&e);
+ }
+}
diff --git a/src/sdl.rs b/src/sdl.rs
new file mode 100644
index 0000000..11873aa
--- /dev/null
+++ b/src/sdl.rs
@@ -0,0 +1,1116 @@
+#![allow(non_camel_case_types)]
+#![allow(dead_code)]
+#![allow(non_snake_case)]
+
+use std::ffi::{c_char, c_float, c_int, c_void, CStr, CString};
+use std::mem;
+
+// not a real type, just here to differentiate *mut () from SDL_GLContext
+#[repr(C)]
+pub struct SDL_GLContextData(u8);
+pub type SDL_bool = c_int;
+pub type SDL_EventType = u32;
+#[repr(C)]
+pub struct SDL_Window(u8);
+pub type SDL_SysWMmsg = c_void;
+pub type SDL_Keycode = u32;
+pub type SDL_Scancode = i32;
+pub type SDL_JoystickID = i32;
+pub type SDL_JoystickPowerLevel = c_int;
+pub type SDL_GestureID = i64;
+pub type SDL_TouchID = i64;
+pub type SDL_FingerID = i64;
+pub type SDL_AudioFormat = u16;
+pub type SDL_AudioCallback = extern "C" fn(*mut c_void, *mut u8, c_int);
+pub type SDL_AudioDeviceID = u32;
+pub type SDL_WindowEventID = c_int;
+pub type SDL_GLContext = *mut SDL_GLContextData;
+pub type SDL_GLattr = c_int;
+
+pub const SDL_WINDOWPOS_UNDEFINED: c_int = 0x1FFF0000;
+pub const SDL_QUIT: SDL_EventType = 0x100;
+pub const SDL_APP_TERMINATING: SDL_EventType = 0x101;
+pub const SDL_APP_LOWMEMORY: SDL_EventType = 0x102;
+pub const SDL_APP_WILLENTERBACKGROUND: SDL_EventType = 0x103;
+pub const SDL_APP_DIDENTERBACKGROUND: SDL_EventType = 0x104;
+pub const SDL_APP_WILLENTERFOREGROUND: SDL_EventType = 0x105;
+pub const SDL_APP_DIDENTERFOREGROUND: SDL_EventType = 0x106;
+pub const SDL_LOCALECHANGED: SDL_EventType = 0x107;
+pub const SDL_DISPLAYEVENT: SDL_EventType = 0x150;
+pub const SDL_WINDOWEVENT: SDL_EventType = 0x200;
+pub const SDL_SYSWMEVENT: SDL_EventType = 0x201;
+pub const SDL_KEYDOWN: SDL_EventType = 0x300;
+pub const SDL_KEYUP: SDL_EventType = 0x301;
+pub const SDL_TEXTEDITING: SDL_EventType = 0x302;
+pub const SDL_TEXTINPUT: SDL_EventType = 0x303;
+pub const SDL_KEYMAPCHANGED: SDL_EventType = 0x304;
+pub const SDL_TEXTEDITING_EXT: SDL_EventType = 0x305;
+pub const SDL_MOUSEMOTION: SDL_EventType = 0x400;
+pub const SDL_MOUSEBUTTONDOWN: SDL_EventType = 0x401;
+pub const SDL_MOUSEBUTTONUP: SDL_EventType = 0x402;
+pub const SDL_MOUSEWHEEL: SDL_EventType = 0x403;
+pub const SDL_JOYAXISMOTION: SDL_EventType = 0x600;
+pub const SDL_JOYBALLMOTION: SDL_EventType = 0x601;
+pub const SDL_JOYHATMOTION: SDL_EventType = 0x602;
+pub const SDL_JOYBUTTONDOWN: SDL_EventType = 0x603;
+pub const SDL_JOYBUTTONUP: SDL_EventType = 0x604;
+pub const SDL_JOYDEVICEADDED: SDL_EventType = 0x605;
+pub const SDL_JOYDEVICEREMOVED: SDL_EventType = 0x606;
+pub const SDL_JOYBATTERYUPDATED: SDL_EventType = 0x607;
+pub const SDL_CONTROLLERAXISMOTION: SDL_EventType = 0x650;
+pub const SDL_CONTROLLERBUTTONDOWN: SDL_EventType = 0x651;
+pub const SDL_CONTROLLERBUTTONUP: SDL_EventType = 0x652;
+pub const SDL_CONTROLLERDEVICEADDED: SDL_EventType = 0x653;
+pub const SDL_CONTROLLERDEVICEREMOVED: SDL_EventType = 0x654;
+pub const SDL_CONTROLLERDEVICEREMAPPED: SDL_EventType = 0x655;
+pub const SDL_CONTROLLERTOUCHPADDOWN: SDL_EventType = 0x656;
+pub const SDL_CONTROLLERTOUCHPADMOTION: SDL_EventType = 0x657;
+pub const SDL_CONTROLLERTOUCHPADUP: SDL_EventType = 0x658;
+pub const SDL_CONTROLLERSENSORUPDATE: SDL_EventType = 0x659;
+pub const SDL_FINGERDOWN: SDL_EventType = 0x700;
+pub const SDL_FINGERUP: SDL_EventType = 0x701;
+pub const SDL_FINGERMOTION: SDL_EventType = 0x702;
+pub const SDL_DOLLARGESTURE: SDL_EventType = 0x800;
+pub const SDL_DOLLARRECORD: SDL_EventType = 0x801;
+pub const SDL_MULTIGESTURE: SDL_EventType = 0x802;
+pub const SDL_CLIPBOARDUPDATE: SDL_EventType = 0x900;
+pub const SDL_DROPFILE: SDL_EventType = 0x1000;
+pub const SDL_DROPTEXT: SDL_EventType = 0x1001;
+pub const SDL_DROPBEGIN: SDL_EventType = 0x1002;
+pub const SDL_DROPCOMPLETE: SDL_EventType = 0x1003;
+pub const SDL_AUDIODEVICEADDED: SDL_EventType = 0x1100;
+pub const SDL_AUDIODEVICEREMOVED: SDL_EventType = 0x1101;
+pub const SDL_SENSORUPDATE: SDL_EventType = 0x1200;
+pub const SDL_RENDER_TARGETS_RESET: SDL_EventType = 0x2000;
+pub const SDL_RENDER_DEVICE_RESET: SDL_EventType = 0x2001;
+pub const SDL_POLLSENTINEL: SDL_EventType = 0x7F00;
+pub const SDL_USEREVENT: SDL_EventType = 0x8000;
+
+pub const SDL_WINDOWEVENT_SHOWN: SDL_WindowEventID = 1;
+pub const SDL_WINDOWEVENT_HIDDEN: SDL_WindowEventID = 2;
+pub const SDL_WINDOWEVENT_EXPOSED: SDL_WindowEventID = 3;
+pub const SDL_WINDOWEVENT_MOVED: SDL_WindowEventID = 4;
+pub const SDL_WINDOWEVENT_RESIZED: SDL_WindowEventID = 5;
+pub const SDL_WINDOWEVENT_SIZE_CHANGED: SDL_WindowEventID = 6;
+pub const SDL_WINDOWEVENT_MINIMIZED: SDL_WindowEventID = 7;
+pub const SDL_WINDOWEVENT_MAXIMIZED: SDL_WindowEventID = 8;
+pub const SDL_WINDOWEVENT_RESTORED: SDL_WindowEventID = 9;
+pub const SDL_WINDOWEVENT_ENTER: SDL_WindowEventID = 10;
+pub const SDL_WINDOWEVENT_LEAVE: SDL_WindowEventID = 11;
+pub const SDL_WINDOWEVENT_FOCUS_GAINED: SDL_WindowEventID = 12;
+pub const SDL_WINDOWEVENT_FOCUS_LOST: SDL_WindowEventID = 13;
+pub const SDL_WINDOWEVENT_CLOSE: SDL_WindowEventID = 14;
+pub const SDL_WINDOWEVENT_TAKE_FOCUS: SDL_WindowEventID = 15;
+pub const SDL_WINDOWEVENT_HIT_TEST: SDL_WindowEventID = 16;
+pub const SDL_WINDOWEVENT_ICCPROF_CHANGED: SDL_WindowEventID = 17;
+pub const SDL_WINDOWEVENT_DISPLAY_CHANGED: SDL_WindowEventID = 18;
+
+pub const SDL_INIT_TIMER: u32 = 0x00000001;
+pub const SDL_INIT_AUDIO: u32 = 0x00000010;
+pub const SDL_INIT_VIDEO: u32 = 0x00000020;
+pub const SDL_INIT_JOYSTICK: u32 = 0x00000200;
+pub const SDL_INIT_HAPTIC: u32 = 0x00001000;
+pub const SDL_INIT_GAMECONTROLLER: u32 = 0x00002000;
+pub const SDL_INIT_EVENTS: u32 = 0x00004000;
+pub const SDL_INIT_SENSOR: u32 = 0x00008000;
+pub const SDL_INIT_EVERYTHING: u32 = SDL_INIT_TIMER
+ | SDL_INIT_AUDIO
+ | SDL_INIT_VIDEO
+ | SDL_INIT_EVENTS
+ | SDL_INIT_JOYSTICK
+ | SDL_INIT_HAPTIC
+ | SDL_INIT_GAMECONTROLLER
+ | SDL_INIT_SENSOR;
+
+pub const SDL_WINDOW_FULLSCREEN: u32 = 0x00000001;
+pub const SDL_WINDOW_OPENGL: u32 = 0x00000002;
+pub const SDL_WINDOW_SHOWN: u32 = 0x00000004;
+pub const SDL_WINDOW_HIDDEN: u32 = 0x00000008;
+pub const SDL_WINDOW_BORDERLESS: u32 = 0x00000010;
+pub const SDL_WINDOW_RESIZABLE: u32 = 0x00000020;
+pub const SDL_WINDOW_MINIMIZED: u32 = 0x00000040;
+pub const SDL_WINDOW_MAXIMIZED: u32 = 0x00000080;
+pub const SDL_WINDOW_MOUSE_GRABBED: u32 = 0x00000100;
+pub const SDL_WINDOW_INPUT_FOCUS: u32 = 0x00000200;
+pub const SDL_WINDOW_MOUSE_FOCUS: u32 = 0x00000400;
+pub const SDL_WINDOW_FULLSCREEN_DESKTOP: u32 = SDL_WINDOW_FULLSCREEN | 0x00001000;
+pub const SDL_WINDOW_FOREIGN: u32 = 0x00000800;
+pub const SDL_WINDOW_ALLOW_HIGHDPI: u32 = 0x00002000;
+pub const SDL_WINDOW_MOUSE_CAPTURE: u32 = 0x00004000;
+pub const SDL_WINDOW_ALWAYS_ON_TOP: u32 = 0x00008000;
+pub const SDL_WINDOW_SKIP_TASKBAR: u32 = 0x00010000;
+pub const SDL_WINDOW_UTILITY: u32 = 0x00020000;
+pub const SDL_WINDOW_TOOLTIP: u32 = 0x00040000;
+pub const SDL_WINDOW_POPUP_MENU: u32 = 0x00080000;
+pub const SDL_WINDOW_KEYBOARD_GRABBED: u32 = 0x00100000;
+pub const SDL_WINDOW_VULKAN: u32 = 0x10000000;
+pub const SDL_WINDOW_METAL: u32 = 0x20000000;
+
+pub const fn sdl_button_mask(x: u8) -> u32 {
+ 1u32 << (x - 1)
+}
+
+pub const SDL_BUTTON_LEFT: u8 = 1;
+pub const SDL_BUTTON_MIDDLE: u8 = 2;
+pub const SDL_BUTTON_RIGHT: u8 = 3;
+pub const SDL_BUTTON_X1: u8 = 4;
+pub const SDL_BUTTON_X2: u8 = 5;
+pub const SDL_BUTTON_LMASK: u32 = sdl_button_mask(SDL_BUTTON_LEFT);
+pub const SDL_BUTTON_MMASK: u32 = sdl_button_mask(SDL_BUTTON_MIDDLE);
+pub const SDL_BUTTON_RMASK: u32 = sdl_button_mask(SDL_BUTTON_RIGHT);
+pub const SDL_BUTTON_X1MASK: u32 = sdl_button_mask(SDL_BUTTON_X1);
+pub const SDL_BUTTON_X2MASK: u32 = sdl_button_mask(SDL_BUTTON_X2);
+
+pub const SDL_GL_RED_SIZE: SDL_GLattr = 0;
+pub const SDL_GL_GREEN_SIZE: SDL_GLattr = 1;
+pub const SDL_GL_BLUE_SIZE: SDL_GLattr = 2;
+pub const SDL_GL_ALPHA_SIZE: SDL_GLattr = 3;
+pub const SDL_GL_BUFFER_SIZE: SDL_GLattr = 4;
+pub const SDL_GL_DOUBLEBUFFER: SDL_GLattr = 5;
+pub const SDL_GL_DEPTH_SIZE: SDL_GLattr = 6;
+pub const SDL_GL_STENCIL_SIZE: SDL_GLattr = 7;
+pub const SDL_GL_ACCUM_RED_SIZE: SDL_GLattr = 8;
+pub const SDL_GL_ACCUM_GREEN_SIZE: SDL_GLattr = 9;
+pub const SDL_GL_ACCUM_BLUE_SIZE: SDL_GLattr = 10;
+pub const SDL_GL_ACCUM_ALPHA_SIZE: SDL_GLattr = 11;
+pub const SDL_GL_STEREO: SDL_GLattr = 12;
+pub const SDL_GL_MULTISAMPLEBUFFERS: SDL_GLattr = 13;
+pub const SDL_GL_MULTISAMPLESAMPLES: SDL_GLattr = 14;
+pub const SDL_GL_ACCELERATED_VISUAL: SDL_GLattr = 15;
+pub const SDL_GL_RETAINED_BACKING: SDL_GLattr = 16;
+pub const SDL_GL_CONTEXT_MAJOR_VERSION: SDL_GLattr = 17;
+pub const SDL_GL_CONTEXT_MINOR_VERSION: SDL_GLattr = 18;
+pub const SDL_GL_CONTEXT_DEBUG_FLAG: i32 = 0x0001;
+pub const SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG: i32 = 0x0002;
+pub const SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG: i32 = 0x0004;
+pub const SDL_GL_CONTEXT_RESET_ISOLATION_FLAG: i32 = 0x0008;
+pub const SDL_GL_CONTEXT_EGL: SDL_GLattr = 19;
+pub const SDL_GL_CONTEXT_FLAGS: SDL_GLattr = 20;
+pub const SDL_GL_CONTEXT_PROFILE_MASK: SDL_GLattr = 21;
+pub const SDL_GL_SHARE_WITH_CURRENT_CONTEXT: SDL_GLattr = 22;
+pub const SDL_GL_FRAMEBUFFER_SRGB_CAPABLE: SDL_GLattr = 23;
+pub const SDL_GL_CONTEXT_RELEASE_BEHAVIOR: SDL_GLattr = 24;
+pub const SDL_GL_CONTEXT_RESET_NOTIFICATION: SDL_GLattr = 25;
+pub const SDL_GL_CONTEXT_NO_ERROR: SDL_GLattr = 26;
+pub const SDL_GL_FLOATBUFFERS: SDL_GLattr = 27;
+
+pub const SDL_MESSAGEBOX_ERROR: u32 = 0x00000010;
+pub const SDL_MESSAGEBOX_WARNING: u32 = 0x00000020;
+pub const SDL_MESSAGEBOX_INFORMATION: u32 = 0x00000040;
+
+pub const SDL_AUDIO_ALLOW_FREQUENCY_CHANGE: c_int = 0x00000001;
+pub const SDL_AUDIO_ALLOW_FORMAT_CHANGE: c_int = 0x00000002;
+pub const SDL_AUDIO_ALLOW_CHANNELS_CHANGE: c_int = 0x00000004;
+pub const SDL_AUDIO_ALLOW_SAMPLES_CHANGE: c_int = 0x00000008;
+
+pub const AUDIO_U8: u16 = 0x0008;
+pub const AUDIO_S8: u16 = 0x8008;
+pub const AUDIO_U16LSB: u16 = 0x0010;
+pub const AUDIO_S16LSB: u16 = 0x8010;
+pub const AUDIO_U16MSB: u16 = 0x1010;
+pub const AUDIO_S16MSB: u16 = 0x9010;
+pub const AUDIO_U16: u16 = AUDIO_U16LSB;
+pub const AUDIO_S16: u16 = AUDIO_S16LSB;
+pub const AUDIO_S32LSB: u16 = 0x8020;
+pub const AUDIO_S32MSB: u16 = 0x9020;
+pub const AUDIO_S32: u16 = AUDIO_S32LSB;
+pub const AUDIO_F32LSB: u16 = 0x8120;
+pub const AUDIO_F32MSB: u16 = 0x9120;
+pub const AUDIO_F32: u16 = AUDIO_F32LSB;
+
+// NOTE: ideally we wouldn't need Copy on all of these
+// but otherwise we wouldn't be able to put them in a union
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_CommonEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_DisplayEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub display: u32,
+ pub event: u8,
+ pub padding1: u8,
+ pub padding2: u8,
+ pub padding3: u8,
+ pub data1: i32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_WindowEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub windowID: u32,
+ pub event: u8,
+ pub padding1: u8,
+ pub padding2: u8,
+ pub padding3: u8,
+ pub data1: i32,
+ pub data2: i32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_Keysym {
+ pub scancode: SDL_Scancode,
+ pub sym: SDL_Keycode,
+ pub r#mod: u16,
+ pub unused: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_KeyboardEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub windowID: u32,
+ pub state: u8,
+ pub repeat: u8,
+ pub padding2: u8,
+ pub padding3: u8,
+ pub keysym: SDL_Keysym,
+}
+
+pub const SDL_TEXTEDITINGEVENT_TEXT_SIZE: usize = 32;
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_TextEditingEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub windowID: u32,
+ pub text: [c_char; SDL_TEXTEDITINGEVENT_TEXT_SIZE],
+ pub start: i32,
+ pub length: i32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_TextEditingExtEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub windowID: u32,
+ pub text: *mut c_char,
+ pub start: i32,
+ pub length: i32,
+}
+
+pub const SDL_TEXTINPUTEVENT_TEXT_SIZE: usize = 32;
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_TextInputEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub windowID: u32,
+ pub text: [c_char; SDL_TEXTINPUTEVENT_TEXT_SIZE],
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_MouseMotionEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub windowID: u32,
+ pub which: u32,
+ pub state: u32,
+ pub x: i32,
+ pub y: i32,
+ pub xrel: i32,
+ pub yrel: i32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_MouseButtonEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub windowID: u32,
+ pub which: u32,
+ pub button: u8,
+ pub state: u8,
+ pub clicks: u8,
+ pub padding1: u8,
+ pub x: i32,
+ pub y: i32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_MouseWheelEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub windowID: u32,
+ pub which: u32,
+ pub x: i32,
+ pub y: i32,
+ pub direction: u32,
+ pub preciseX: c_float,
+ pub preciseY: c_float,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_JoyAxisEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: SDL_JoystickID,
+ pub axis: u8,
+ pub padding1: u8,
+ pub padding2: u8,
+ pub padding3: u8,
+ pub value: i16,
+ pub padding4: u16,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_JoyBallEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: SDL_JoystickID,
+ pub ball: u8,
+ pub padding1: u8,
+ pub padding2: u8,
+ pub padding3: u8,
+ pub xrel: i16,
+ pub yrel: i16,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_JoyHatEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: SDL_JoystickID,
+ pub hat: u8,
+ pub value: u8,
+ pub padding1: u8,
+ pub padding2: u8,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_JoyButtonEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: SDL_JoystickID,
+ pub button: u8,
+ pub state: u8,
+ pub padding1: u8,
+ pub padding2: u8,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_JoyDeviceEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: i32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_JoyBatteryEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: SDL_JoystickID,
+ pub level: SDL_JoystickPowerLevel,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_ControllerAxisEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: SDL_JoystickID,
+ pub axis: u8,
+ pub padding1: u8,
+ pub padding2: u8,
+ pub padding3: u8,
+ pub value: i16,
+ pub padding4: u16,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_ControllerButtonEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: SDL_JoystickID,
+ pub button: u8,
+ pub state: u8,
+ pub padding1: u8,
+ pub padding2: u8,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_ControllerDeviceEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: i32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_ControllerTouchpadEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: SDL_JoystickID,
+ pub touchpad: i32,
+ pub finger: i32,
+ pub x: c_float,
+ pub y: c_float,
+ pub pressure: c_float,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_ControllerSensorEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: SDL_JoystickID,
+ pub sensor: i32,
+ pub data: [c_float; 3],
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_AudioDeviceEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: u32,
+ pub iscapture: u8,
+ pub padding1: u8,
+ pub padding2: u8,
+ pub padding3: u8,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_TouchFingerEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub touchId: SDL_TouchID,
+ pub fingerId: SDL_FingerID,
+ pub x: c_float,
+ pub y: c_float,
+ pub dx: c_float,
+ pub dy: c_float,
+ pub pressure: c_float,
+ pub windowID: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_MultiGestureEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub touchId: SDL_TouchID,
+ pub dTheta: c_float,
+ pub dDist: c_float,
+ pub x: c_float,
+ pub y: c_float,
+ pub numFingers: u16,
+ pub padding: u16,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_DollarGestureEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub touchId: SDL_TouchID,
+ pub gestureId: SDL_GestureID,
+ pub numFingers: u32,
+ pub error: c_float,
+ pub x: c_float,
+ pub y: c_float,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_DropEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub file: *mut c_char,
+ pub windowID: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_SensorEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub which: i32,
+ pub data: [c_float; 6],
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_QuitEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_OSEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_UserEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub windowID: u32,
+ pub code: i32,
+ pub data1: *mut c_void,
+ pub data2: *mut c_void,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SDL_SysWMEvent {
+ pub r#type: u32,
+ pub timestamp: u32,
+ pub msg: *mut SDL_SysWMmsg,
+}
+
+#[repr(C)]
+pub union SDL_Event {
+ pub r#type: u32,
+ pub common: SDL_CommonEvent,
+ pub display: SDL_DisplayEvent,
+ pub window: SDL_WindowEvent,
+ pub key: SDL_KeyboardEvent,
+ pub edit: SDL_TextEditingEvent,
+ pub editExt: SDL_TextEditingExtEvent,
+ pub text: SDL_TextInputEvent,
+ pub motion: SDL_MouseMotionEvent,
+ pub button: SDL_MouseButtonEvent,
+ pub wheel: SDL_MouseWheelEvent,
+ pub jaxis: SDL_JoyAxisEvent,
+ pub jball: SDL_JoyBallEvent,
+ pub jhat: SDL_JoyHatEvent,
+ pub jbutton: SDL_JoyButtonEvent,
+ pub jdevice: SDL_JoyDeviceEvent,
+ pub jbattery: SDL_JoyBatteryEvent,
+ pub caxis: SDL_ControllerAxisEvent,
+ pub cbutton: SDL_ControllerButtonEvent,
+ pub cdevice: SDL_ControllerDeviceEvent,
+ pub ctouchpad: SDL_ControllerTouchpadEvent,
+ pub csensor: SDL_ControllerSensorEvent,
+ pub adevice: SDL_AudioDeviceEvent,
+ pub sensor: SDL_SensorEvent,
+ pub quit: SDL_QuitEvent,
+ pub user: SDL_UserEvent,
+ pub syswm: SDL_SysWMEvent,
+ pub tfinger: SDL_TouchFingerEvent,
+ pub mgesture: SDL_MultiGestureEvent,
+ pub dgesture: SDL_DollarGestureEvent,
+ pub r#drop: SDL_DropEvent,
+}
+
+#[repr(C)]
+pub struct SDL_AudioSpec {
+ pub freq: c_int,
+ pub format: SDL_AudioFormat,
+ pub channels: u8,
+ pub silence: u8,
+ pub samples: u16,
+ pub size: u32,
+ pub callback: SDL_AudioCallback,
+ pub userdata: *mut c_void,
+}
+
+impl SDL_AudioSpec {
+ pub fn new(
+ callback: SDL_AudioCallback,
+ userdata: *mut c_void,
+ channels: u8,
+ format: u16,
+ freq: c_int,
+ samples: u16,
+ ) -> Self {
+ Self {
+ callback,
+ userdata,
+ channels,
+ format,
+ freq,
+ samples,
+ silence: 0,
+ size: 0,
+ }
+ }
+}
+
+#[link(name = "SDL2", kind = "dylib")]
+extern "C" {
+ fn SDL_Init(flags: u32) -> c_int;
+ fn SDL_CreateWindow(
+ title: *const c_char,
+ x: c_int,
+ y: c_int,
+ w: c_int,
+ h: c_int,
+ flags: u32,
+ ) -> *mut SDL_Window;
+ fn SDL_ShowWindow(window: *mut SDL_Window);
+ fn SDL_ShowSimpleMessageBox(
+ flags: u32,
+ title: *const c_char,
+ message: *const c_char,
+ window: *mut SDL_Window,
+ );
+ fn SDL_DestroyWindow(window: *mut SDL_Window);
+ fn SDL_GetWindowSize(window: *mut SDL_Window, w: *mut c_int, h: *mut c_int);
+ fn SDL_GetWindowID(window: *mut SDL_Window) -> u32;
+ fn SDL_SetWindowResizable(window: *mut SDL_Window, resizable: SDL_bool);
+ fn SDL_SetWindowSize(window: *mut SDL_Window, w: c_int, h: c_int);
+ fn SDL_GetError() -> *const c_char;
+ fn SDL_SetHint(name: *const c_char, value: *const c_char) -> SDL_bool;
+ fn SDL_GL_SetAttribute(attr: SDL_GLattr, value: c_int);
+ fn SDL_GL_SetSwapInterval(interval: c_int) -> c_int;
+ fn SDL_GL_CreateContext(window: *mut SDL_Window) -> SDL_GLContext;
+ fn SDL_GL_DeleteContext(ctx: SDL_GLContext);
+ fn SDL_GL_MakeCurrent(window: *mut SDL_Window, context: SDL_GLContext) -> c_int;
+ fn SDL_GL_SwapWindow(window: *mut SDL_Window);
+ fn SDL_GL_GetProcAddress(proc: *const c_char) -> *mut c_void;
+ fn SDL_PollEvent(event: *mut SDL_Event) -> c_int;
+ fn SDL_GetKeyboardState(numkeys: *mut c_int) -> *const u8;
+ fn SDL_OpenAudioDevice(
+ device: *const c_char,
+ iscapture: c_int,
+ desired: *const SDL_AudioSpec,
+ obtained: *mut SDL_AudioSpec,
+ allowed_changes: c_int,
+ ) -> SDL_AudioDeviceID;
+ fn SDL_PauseAudioDevice(dev: SDL_AudioDeviceID, pause_on: c_int);
+ fn SDL_CloseAudioDevice(dev: SDL_AudioDeviceID);
+}
+
+pub mod scancode {
+ use crate::sdl::SDL_Scancode;
+ pub const UNKNOWN: SDL_Scancode = 0;
+ pub const A: SDL_Scancode = 4;
+ pub const B: SDL_Scancode = 5;
+ pub const C: SDL_Scancode = 6;
+ pub const D: SDL_Scancode = 7;
+ pub const E: SDL_Scancode = 8;
+ pub const F: SDL_Scancode = 9;
+ pub const G: SDL_Scancode = 10;
+ pub const H: SDL_Scancode = 11;
+ pub const I: SDL_Scancode = 12;
+ pub const J: SDL_Scancode = 13;
+ pub const K: SDL_Scancode = 14;
+ pub const L: SDL_Scancode = 15;
+ pub const M: SDL_Scancode = 16;
+ pub const N: SDL_Scancode = 17;
+ pub const O: SDL_Scancode = 18;
+ pub const P: SDL_Scancode = 19;
+ pub const Q: SDL_Scancode = 20;
+ pub const R: SDL_Scancode = 21;
+ pub const S: SDL_Scancode = 22;
+ pub const T: SDL_Scancode = 23;
+ pub const U: SDL_Scancode = 24;
+ pub const V: SDL_Scancode = 25;
+ pub const W: SDL_Scancode = 26;
+ pub const X: SDL_Scancode = 27;
+ pub const Y: SDL_Scancode = 28;
+ pub const Z: SDL_Scancode = 29;
+ pub const N1: SDL_Scancode = 30;
+ pub const N2: SDL_Scancode = 31;
+ pub const N3: SDL_Scancode = 32;
+ pub const N4: SDL_Scancode = 33;
+ pub const N5: SDL_Scancode = 34;
+ pub const N6: SDL_Scancode = 35;
+ pub const N7: SDL_Scancode = 36;
+ pub const N8: SDL_Scancode = 37;
+ pub const N9: SDL_Scancode = 38;
+ pub const N0: SDL_Scancode = 39;
+ pub const RETURN: SDL_Scancode = 40;
+ pub const ESCAPE: SDL_Scancode = 41;
+ pub const BACKSPACE: SDL_Scancode = 42;
+ pub const TAB: SDL_Scancode = 43;
+ pub const SPACE: SDL_Scancode = 44;
+ pub const MINUS: SDL_Scancode = 45;
+ pub const EQUALS: SDL_Scancode = 46;
+ pub const LEFTBRACKET: SDL_Scancode = 47;
+ pub const RIGHTBRACKET: SDL_Scancode = 48;
+ pub const BACKSLASH: SDL_Scancode = 49;
+ pub const NONUSHASH: SDL_Scancode = 50;
+ pub const SEMICOLON: SDL_Scancode = 51;
+ pub const APOSTROPHE: SDL_Scancode = 52;
+ pub const GRAVE: SDL_Scancode = 53;
+ pub const COMMA: SDL_Scancode = 54;
+ pub const PERIOD: SDL_Scancode = 55;
+ pub const SLASH: SDL_Scancode = 56;
+ pub const CAPSLOCK: SDL_Scancode = 57;
+ pub const F1: SDL_Scancode = 58;
+ pub const F2: SDL_Scancode = 59;
+ pub const F3: SDL_Scancode = 60;
+ pub const F4: SDL_Scancode = 61;
+ pub const F5: SDL_Scancode = 62;
+ pub const F6: SDL_Scancode = 63;
+ pub const F7: SDL_Scancode = 64;
+ pub const F8: SDL_Scancode = 65;
+ pub const F9: SDL_Scancode = 66;
+ pub const F10: SDL_Scancode = 67;
+ pub const F11: SDL_Scancode = 68;
+ pub const F12: SDL_Scancode = 69;
+ pub const PRINTSCREEN: SDL_Scancode = 70;
+ pub const SCROLLLOCK: SDL_Scancode = 71;
+ pub const PAUSE: SDL_Scancode = 72;
+ pub const INSERT: SDL_Scancode = 73;
+ pub const HOME: SDL_Scancode = 74;
+ pub const PAGEUP: SDL_Scancode = 75;
+ pub const DELETE: SDL_Scancode = 76;
+ pub const END: SDL_Scancode = 77;
+ pub const PAGEDOWN: SDL_Scancode = 78;
+ pub const RIGHT: SDL_Scancode = 79;
+ pub const LEFT: SDL_Scancode = 80;
+ pub const DOWN: SDL_Scancode = 81;
+ pub const UP: SDL_Scancode = 82;
+ pub const NUMLOCKCLEAR: SDL_Scancode = 83;
+ pub const KP_DIVIDE: SDL_Scancode = 84;
+ pub const KP_MULTIPLY: SDL_Scancode = 85;
+ pub const KP_MINUS: SDL_Scancode = 86;
+ pub const KP_PLUS: SDL_Scancode = 87;
+ pub const KP_ENTER: SDL_Scancode = 88;
+ pub const KP_1: SDL_Scancode = 89;
+ pub const KP_2: SDL_Scancode = 90;
+ pub const KP_3: SDL_Scancode = 91;
+ pub const KP_4: SDL_Scancode = 92;
+ pub const KP_5: SDL_Scancode = 93;
+ pub const KP_6: SDL_Scancode = 94;
+ pub const KP_7: SDL_Scancode = 95;
+ pub const KP_8: SDL_Scancode = 96;
+ pub const KP_9: SDL_Scancode = 97;
+ pub const KP_0: SDL_Scancode = 98;
+ pub const KP_PERIOD: SDL_Scancode = 99;
+ pub const NONUSBACKSLASH: SDL_Scancode = 100;
+ pub const APPLICATION: SDL_Scancode = 101;
+ pub const POWER: SDL_Scancode = 102;
+ pub const KP_EQUALS: SDL_Scancode = 103;
+ pub const F13: SDL_Scancode = 104;
+ pub const F14: SDL_Scancode = 105;
+ pub const F15: SDL_Scancode = 106;
+ pub const F16: SDL_Scancode = 107;
+ pub const F17: SDL_Scancode = 108;
+ pub const F18: SDL_Scancode = 109;
+ pub const F19: SDL_Scancode = 110;
+ pub const F20: SDL_Scancode = 111;
+ pub const F21: SDL_Scancode = 112;
+ pub const F22: SDL_Scancode = 113;
+ pub const F23: SDL_Scancode = 114;
+ pub const F24: SDL_Scancode = 115;
+ pub const EXECUTE: SDL_Scancode = 116;
+ pub const HELP: SDL_Scancode = 117;
+ pub const MENU: SDL_Scancode = 118;
+ pub const SELECT: SDL_Scancode = 119;
+ pub const STOP: SDL_Scancode = 120;
+ pub const AGAIN: SDL_Scancode = 121;
+ pub const UNDO: SDL_Scancode = 122;
+ pub const CUT: SDL_Scancode = 123;
+ pub const COPY: SDL_Scancode = 124;
+ pub const PASTE: SDL_Scancode = 125;
+ pub const FIND: SDL_Scancode = 126;
+ pub const MUTE: SDL_Scancode = 127;
+ pub const VOLUMEUP: SDL_Scancode = 128;
+ pub const VOLUMEDOWN: SDL_Scancode = 129;
+ pub const KP_COMMA: SDL_Scancode = 133;
+ pub const KP_EQUALSAS400: SDL_Scancode = 134;
+ pub const INTERNATIONAL1: SDL_Scancode = 135;
+ pub const INTERNATIONAL2: SDL_Scancode = 136;
+ pub const INTERNATIONAL3: SDL_Scancode = 137;
+ pub const INTERNATIONAL4: SDL_Scancode = 138;
+ pub const INTERNATIONAL5: SDL_Scancode = 139;
+ pub const INTERNATIONAL6: SDL_Scancode = 140;
+ pub const INTERNATIONAL7: SDL_Scancode = 141;
+ pub const INTERNATIONAL8: SDL_Scancode = 142;
+ pub const INTERNATIONAL9: SDL_Scancode = 143;
+ pub const LANG1: SDL_Scancode = 144;
+ pub const LANG2: SDL_Scancode = 145;
+ pub const LANG3: SDL_Scancode = 146;
+ pub const LANG4: SDL_Scancode = 147;
+ pub const LANG5: SDL_Scancode = 148;
+ pub const LANG6: SDL_Scancode = 149;
+ pub const LANG7: SDL_Scancode = 150;
+ pub const LANG8: SDL_Scancode = 151;
+ pub const LANG9: SDL_Scancode = 152;
+ pub const ALTERASE: SDL_Scancode = 153;
+ pub const SYSREQ: SDL_Scancode = 154;
+ pub const CANCEL: SDL_Scancode = 155;
+ pub const CLEAR: SDL_Scancode = 156;
+ pub const PRIOR: SDL_Scancode = 157;
+ pub const RETURN2: SDL_Scancode = 158;
+ pub const SEPARATOR: SDL_Scancode = 159;
+ pub const OUT: SDL_Scancode = 160;
+ pub const OPER: SDL_Scancode = 161;
+ pub const CLEARAGAIN: SDL_Scancode = 162;
+ pub const CRSEL: SDL_Scancode = 163;
+ pub const EXSEL: SDL_Scancode = 164;
+ pub const KP_00: SDL_Scancode = 176;
+ pub const KP_000: SDL_Scancode = 177;
+ pub const THOUSANDSSEPARATOR: SDL_Scancode = 178;
+ pub const DECIMALSEPARATOR: SDL_Scancode = 179;
+ pub const CURRENCYUNIT: SDL_Scancode = 180;
+ pub const CURRENCYSUBUNIT: SDL_Scancode = 181;
+ pub const KP_LEFTPAREN: SDL_Scancode = 182;
+ pub const KP_RIGHTPAREN: SDL_Scancode = 183;
+ pub const KP_LEFTBRACE: SDL_Scancode = 184;
+ pub const KP_RIGHTBRACE: SDL_Scancode = 185;
+ pub const KP_TAB: SDL_Scancode = 186;
+ pub const KP_BACKSPACE: SDL_Scancode = 187;
+ pub const KP_A: SDL_Scancode = 188;
+ pub const KP_B: SDL_Scancode = 189;
+ pub const KP_C: SDL_Scancode = 190;
+ pub const KP_D: SDL_Scancode = 191;
+ pub const KP_E: SDL_Scancode = 192;
+ pub const KP_F: SDL_Scancode = 193;
+ pub const KP_XOR: SDL_Scancode = 194;
+ pub const KP_POWER: SDL_Scancode = 195;
+ pub const KP_PERCENT: SDL_Scancode = 196;
+ pub const KP_LESS: SDL_Scancode = 197;
+ pub const KP_GREATER: SDL_Scancode = 198;
+ pub const KP_AMPERSAND: SDL_Scancode = 199;
+ pub const KP_DBLAMPERSAND: SDL_Scancode = 200;
+ pub const KP_VERTICALBAR: SDL_Scancode = 201;
+ pub const KP_DBLVERTICALBAR: SDL_Scancode = 202;
+ pub const KP_COLON: SDL_Scancode = 203;
+ pub const KP_HASH: SDL_Scancode = 204;
+ pub const KP_SPACE: SDL_Scancode = 205;
+ pub const KP_AT: SDL_Scancode = 206;
+ pub const KP_EXCLAM: SDL_Scancode = 207;
+ pub const KP_MEMSTORE: SDL_Scancode = 208;
+ pub const KP_MEMRECALL: SDL_Scancode = 209;
+ pub const KP_MEMCLEAR: SDL_Scancode = 210;
+ pub const KP_MEMADD: SDL_Scancode = 211;
+ pub const KP_MEMSUBTRACT: SDL_Scancode = 212;
+ pub const KP_MEMMULTIPLY: SDL_Scancode = 213;
+ pub const KP_MEMDIVIDE: SDL_Scancode = 214;
+ pub const KP_PLUSMINUS: SDL_Scancode = 215;
+ pub const KP_CLEAR: SDL_Scancode = 216;
+ pub const KP_CLEARENTRY: SDL_Scancode = 217;
+ pub const KP_BINARY: SDL_Scancode = 218;
+ pub const KP_OCTAL: SDL_Scancode = 219;
+ pub const KP_DECIMAL: SDL_Scancode = 220;
+ pub const KP_HEXADECIMAL: SDL_Scancode = 221;
+ pub const LCTRL: SDL_Scancode = 224;
+ pub const LSHIFT: SDL_Scancode = 225;
+ pub const LALT: SDL_Scancode = 226;
+ pub const LGUI: SDL_Scancode = 227;
+ pub const RCTRL: SDL_Scancode = 228;
+ pub const RSHIFT: SDL_Scancode = 229;
+ pub const RALT: SDL_Scancode = 230;
+ pub const RGUI: SDL_Scancode = 231;
+ pub const MODE: SDL_Scancode = 257;
+ pub const AUDIONEXT: SDL_Scancode = 258;
+ pub const AUDIOPREV: SDL_Scancode = 259;
+ pub const AUDIOSTOP: SDL_Scancode = 260;
+ pub const AUDIOPLAY: SDL_Scancode = 261;
+ pub const AUDIOMUTE: SDL_Scancode = 262;
+ pub const MEDIASELECT: SDL_Scancode = 263;
+ pub const WWW: SDL_Scancode = 264;
+ pub const MAIL: SDL_Scancode = 265;
+ pub const CALCULATOR: SDL_Scancode = 266;
+ pub const COMPUTER: SDL_Scancode = 267;
+ pub const AC_SEARCH: SDL_Scancode = 268;
+ pub const AC_HOME: SDL_Scancode = 269;
+ pub const AC_BACK: SDL_Scancode = 270;
+ pub const AC_FORWARD: SDL_Scancode = 271;
+ pub const AC_STOP: SDL_Scancode = 272;
+ pub const AC_REFRESH: SDL_Scancode = 273;
+ pub const AC_BOOKMARKS: SDL_Scancode = 274;
+ pub const BRIGHTNESSDOWN: SDL_Scancode = 275;
+ pub const BRIGHTNESSUP: SDL_Scancode = 276;
+ pub const DISPLAYSWITCH: SDL_Scancode = 277;
+ pub const KBDILLUMTOGGLE: SDL_Scancode = 278;
+ pub const KBDILLUMDOWN: SDL_Scancode = 279;
+ pub const KBDILLUMUP: SDL_Scancode = 280;
+ pub const EJECT: SDL_Scancode = 281;
+ pub const SLEEP: SDL_Scancode = 282;
+ pub const APP1: SDL_Scancode = 283;
+ pub const APP2: SDL_Scancode = 284;
+ pub const AUDIOREWIND: SDL_Scancode = 285;
+ pub const AUDIOFASTFORWARD: SDL_Scancode = 286;
+ pub const SOFTLEFT: SDL_Scancode = 287;
+ pub const SOFTRIGHT: SDL_Scancode = 288;
+ pub const CALL: SDL_Scancode = 289;
+ pub const ENDCALL: SDL_Scancode = 290;
+ pub const NUM_SCANCODES: SDL_Scancode = 512;
+}
+
+fn cstring(s: &str) -> Result<CString, String> {
+ CString::new(s).map_err(|e| format!("{e}"))
+}
+
+unsafe fn get_err() -> String {
+ let cstr = CStr::from_ptr(SDL_GetError());
+ String::from_utf8_lossy(cstr.to_bytes()).to_string()
+}
+
+pub unsafe fn create_window(
+ title: &str,
+ width: i32,
+ height: i32,
+ flags: u32,
+) -> Result<*mut SDL_Window, String> {
+ let tstr = cstring(title)?;
+ let window = SDL_CreateWindow(
+ tstr.as_ptr(),
+ SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ width,
+ height,
+ flags,
+ );
+ if window.is_null() {
+ Err(get_err())
+ } else {
+ Ok(window)
+ }
+}
+
+pub unsafe fn gl_create_context(window: *mut SDL_Window) -> Result<SDL_GLContext, String> {
+ let ctx = SDL_GL_CreateContext(window);
+ if ctx.is_null() {
+ Err(get_err())
+ } else {
+ Ok(ctx)
+ }
+}
+
+// NOTE: the buffer returned by SDL does really have a static lifetime. we're not lying here.
+pub unsafe fn get_keyboard_state() -> &'static [u8] {
+ let state = SDL_GetKeyboardState(0 as _);
+ std::slice::from_raw_parts(state, scancode::NUM_SCANCODES as usize)
+}
+
+pub unsafe fn poll_event() -> Option<SDL_Event> {
+ let mut event = mem::MaybeUninit::zeroed();
+ if SDL_PollEvent(event.as_mut_ptr()) != 0 {
+ let event = event.assume_init();
+ Some(event)
+ } else {
+ None
+ }
+}
+
+pub unsafe fn init() -> Result<(), String> {
+ if SDL_Init(SDL_INIT_EVERYTHING) == 0 {
+ Ok(())
+ } else {
+ Err(get_err())
+ }
+}
+
+pub unsafe fn set_hint(hint: &str, value: &str) {
+ // NOTE: hint,value are probably string literals, so cstring is very unlikely to fail
+ if let Ok(hstr) = cstring(hint) {
+ if let Ok(vstr) = cstring(value) {
+ SDL_SetHint(hstr.as_ptr(), vstr.as_ptr());
+ }
+ }
+}
+
+pub unsafe fn gl_set_attribute(attr: i32, value: i32) {
+ SDL_GL_SetAttribute(attr, value);
+}
+
+pub unsafe fn gl_set_context_version(major: i32, minor: i32) {
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
+}
+
+pub unsafe fn gl_get_proc_address(name: &str) -> *const std::os::raw::c_void {
+ match cstring(name) {
+ Ok(cstr) => SDL_GL_GetProcAddress(cstr.as_ptr()),
+ Err(_) => 0 as _,
+ }
+}
+
+pub unsafe fn gl_set_swap_interval(interval: i32) {
+ SDL_GL_SetSwapInterval(interval);
+}
+
+pub unsafe fn show_window(window: *mut SDL_Window) {
+ SDL_ShowWindow(window);
+}
+
+pub unsafe fn gl_swap_window(window: *mut SDL_Window) {
+ SDL_GL_SwapWindow(window);
+}
+
+pub unsafe fn gl_delete_context(ctx: SDL_GLContext) {
+ SDL_GL_DeleteContext(ctx);
+}
+
+pub unsafe fn destroy_window(window: *mut SDL_Window) {
+ SDL_DestroyWindow(window);
+}
+
+pub unsafe fn show_simple_message_box(
+ flags: u32,
+ title: &str,
+ message: &str,
+ window: *mut SDL_Window,
+) -> Result<(), String> {
+ let tstr = cstring(title)?;
+ let mstr = cstring(message)?;
+ SDL_ShowSimpleMessageBox(flags, tstr.as_ptr(), mstr.as_ptr(), window);
+ Ok(())
+}
+
+pub unsafe fn get_window_size(window: *mut SDL_Window, x: &mut i32, y: &mut i32) {
+ SDL_GetWindowSize(window, x as _, y as _);
+}
+
+pub unsafe fn open_audio_device(
+ device: Option<&str>,
+ iscapture: bool,
+ desired: &SDL_AudioSpec,
+ allowed_changes: c_int,
+) -> Result<(SDL_AudioSpec, SDL_AudioDeviceID), String> {
+ let mut obtained = mem::MaybeUninit::zeroed();
+ let mut devstr = None;
+ if let Some(dev) = device {
+ devstr = CString::new(dev).ok();
+ }
+
+ let devptr: *const c_char = match devstr {
+ None => 0 as _,
+ Some(s) => s.as_ptr(),
+ };
+ let id = SDL_OpenAudioDevice(
+ devptr,
+ iscapture.into(),
+ desired as _,
+ obtained.as_mut_ptr(),
+ allowed_changes,
+ );
+ if id == 0 {
+ return Err(get_err());
+ }
+ let obtained = obtained.assume_init();
+ Ok((obtained, id))
+}
+
+pub unsafe fn pause_audio_device(dev: SDL_AudioDeviceID, pause_on: bool) {
+ SDL_PauseAudioDevice(dev, pause_on.into());
+}
+
+pub unsafe fn close_audio_device(dev: SDL_AudioDeviceID) {
+ SDL_CloseAudioDevice(dev);
+}
diff --git a/src/win.rs b/src/win.rs
new file mode 100644
index 0000000..781156d
--- /dev/null
+++ b/src/win.rs
@@ -0,0 +1,866 @@
+// all OpenGL calls are done through the Window.
+// this is because OpenGL is not thread safe.
+use crate::sdl;
+use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint};
+#[allow(unused_imports)]
+use std::ffi::{c_char, c_int, c_uint, c_void, CStr, CString};
+use std::sync::Mutex;
+use std::{fmt, mem};
+use mem::size_of;
+
+pub type AudioCallback = fn(sample_rate: u32, samples: &mut [f32]);
+
+struct AudioData {
+ callback: AudioCallback,
+ device: sdl::SDL_AudioDeviceID,
+ sample_rate: u32,
+}
+
+pub struct Window {
+ sdlwin: *mut sdl::SDL_Window,
+ glctx: sdl::SDL_GLContext,
+ used_program: GLuint,
+ audio_data: Option<Box<AudioData>>,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Key {
+ A,
+ B,
+ C,
+ D,
+ E,
+ F,
+ G,
+ H,
+ I,
+ J,
+ K,
+ L,
+ M,
+ N,
+ O,
+ P,
+ Q,
+ R,
+ S,
+ T,
+ U,
+ V,
+ W,
+ X,
+ Y,
+ Z,
+ N0,
+ N1,
+ N2,
+ N3,
+ N4,
+ N5,
+ N6,
+ N7,
+ N8,
+ N9,
+ Up,
+ Left,
+ Right,
+ Down,
+ Space,
+ Enter,
+ Escape,
+}
+
+impl Key {
+ fn from_sdl(scancode: sdl::SDL_Scancode) -> Option<Self> {
+ use sdl::scancode::*;
+ Some(match scancode {
+ A => Key::A,
+ B => Key::B,
+ C => Key::C,
+ D => Key::D,
+ E => Key::E,
+ F => Key::F,
+ G => Key::G,
+ H => Key::H,
+ I => Key::I,
+ J => Key::J,
+ K => Key::K,
+ L => Key::L,
+ M => Key::M,
+ N => Key::N,
+ O => Key::O,
+ P => Key::P,
+ Q => Key::Q,
+ R => Key::R,
+ S => Key::S,
+ T => Key::T,
+ U => Key::U,
+ V => Key::V,
+ W => Key::W,
+ X => Key::X,
+ Y => Key::Y,
+ Z => Key::Z,
+ RETURN => Key::Enter,
+ SPACE => Key::Space,
+ N0 => Key::N0,
+ N1 => Key::N1,
+ N2 => Key::N2,
+ N3 => Key::N3,
+ N4 => Key::N4,
+ N5 => Key::N5,
+ N6 => Key::N6,
+ N7 => Key::N7,
+ N8 => Key::N8,
+ N9 => Key::N9,
+ UP => Key::Up,
+ LEFT => Key::Left,
+ RIGHT => Key::Right,
+ DOWN => Key::Down,
+ ESCAPE => Key::Escape,
+ _ => return None,
+ })
+ }
+
+ fn to_sdl(self) -> sdl::SDL_Scancode {
+ use sdl::scancode::*;
+ match self {
+ Key::A => A,
+ Key::B => B,
+ Key::C => C,
+ Key::D => D,
+ Key::E => E,
+ Key::F => F,
+ Key::G => G,
+ Key::H => H,
+ Key::I => I,
+ Key::J => J,
+ Key::K => K,
+ Key::L => L,
+ Key::M => M,
+ Key::N => N,
+ Key::O => O,
+ Key::P => P,
+ Key::Q => Q,
+ Key::R => R,
+ Key::S => S,
+ Key::T => T,
+ Key::U => U,
+ Key::V => V,
+ Key::W => W,
+ Key::X => X,
+ Key::Y => Y,
+ Key::Z => Z,
+ Key::Enter => RETURN,
+ Key::Space => SPACE,
+ Key::N0 => N0,
+ Key::N1 => N1,
+ Key::N2 => N2,
+ Key::N3 => N3,
+ Key::N4 => N4,
+ Key::N5 => N5,
+ Key::N6 => N6,
+ Key::N7 => N7,
+ Key::N8 => N8,
+ Key::N9 => N9,
+ Key::Up => UP,
+ Key::Left => LEFT,
+ Key::Right => RIGHT,
+ Key::Down => DOWN,
+ Key::Escape => ESCAPE,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum Event {
+ Quit,
+ KeyDown(Key),
+ KeyUp(Key),
+}
+
+pub fn display_error_message(message: &str) {
+ let result = unsafe {
+ sdl::show_simple_message_box(sdl::SDL_MESSAGEBOX_ERROR, "Error", message, 0 as _)
+ };
+ if result.is_err() {
+ eprintln!("{}", message);
+ }
+}
+
+#[derive(Clone, Copy)]
+pub struct ColorF32 {
+ pub r: f32,
+ pub g: f32,
+ pub b: f32,
+ pub a: f32,
+}
+
+#[derive(Clone, Copy)]
+pub struct ColorU8 {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+ pub a: u8,
+}
+
+impl fmt::Display for ColorU8 {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "#{:02x}{:02x}{:02x}{:02x}",
+ self.r, self.g, self.b, self.a
+ )
+ }
+}
+
+impl fmt::Debug for ColorU8 {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self)
+ }
+}
+
+impl ColorU8 {
+ pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
+ ColorU8 { r, g, b, a }
+ }
+
+ pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
+ ColorU8 { r, g, b, a: 255 }
+ }
+
+ pub fn tint(&mut self, other: ColorU8) {
+ self.r = ((self.r as u32 * other.r as u32) >> 8) as u8;
+ self.g = ((self.g as u32 * other.g as u32) >> 8) as u8;
+ self.b = ((self.b as u32 * other.b as u32) >> 8) as u8;
+ self.a = ((self.a as u32 * other.a as u32) >> 8) as u8;
+ }
+
+ pub fn from_bytes(bytes: &[u8]) -> Self {
+ assert!(bytes.len() >= 4);
+ Self {
+ r: bytes[0],
+ g: bytes[1],
+ b: bytes[2],
+ a: bytes[3],
+ }
+ }
+
+ pub fn as_bytes(self, bytes: &mut [u8]) {
+ assert!(bytes.len() >= 4);
+ bytes[0] = self.r;
+ bytes[1] = self.g;
+ bytes[2] = self.b;
+ bytes[3] = self.a;
+ }
+
+ pub fn as_tuple(self) -> (u8, u8, u8, u8) {
+ (self.r, self.g, self.b, self.a)
+ }
+}
+
+impl From<u32> for ColorU8 {
+ fn from(color: u32) -> ColorU8 {
+ ColorU8::new(
+ (color >> 24) as u8,
+ (color >> 16) as u8,
+ (color >> 8) as u8,
+ color as u8,
+ )
+ }
+}
+
+impl ColorF32 {
+ pub const BLACK: Self = Self::rgb(0.0, 0.0, 0.0);
+
+ pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
+ ColorF32 { r, g, b, a: 1.0 }
+ }
+
+ pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
+ ColorF32 { r, g, b, a }
+ }
+}
+
+pub struct Shader {
+ id: GLuint,
+}
+
+impl Shader {
+ fn new(r#type: GLenum, source: &str) -> Result<Self, String> {
+ let id = unsafe { gl::CreateShader(r#type) };
+ let result = Self::new_with_id(id, r#type, source);
+ if result.is_err() {
+ unsafe { gl::DeleteShader(id) };
+ }
+ result
+ }
+
+ fn new_with_id(id: GLuint, r#type: GLenum, source: &str) -> Result<Self, String> {
+ if id == 0 {
+ return Err(format!("couldn't create shader (GL error {})", unsafe {
+ gl::GetError()
+ }));
+ }
+
+ {
+ //set source
+ // @TODO(eventually): support for older versions of GLSL
+ let header = if r#type == gl::FRAGMENT_SHADER { "#version 130
+#define IN in
+#define OUT out
+#define gl_FragColor o_color
+out vec4 o_color;
+#line 1
+" } else {
+"#version 130
+#define IN in
+#define OUT out
+#define ATTRIBUTE in
+#line 1
+"
+};
+ let hdrptr = header.as_bytes().as_ptr() as *const GLchar;
+ let srcptr = source.as_bytes().as_ptr() as *const GLchar;
+ let sources = [hdrptr, srcptr];
+ let lengths = [header.len() as GLint, source.len() as GLint];
+
+ let sources_ptr = &sources[0] as *const *const GLchar;
+ let lengths_ptr = &lengths[0] as *const GLint;
+
+ unsafe { gl::ShaderSource(id, sources.len() as _, sources_ptr, lengths_ptr) };
+ }
+
+ unsafe { gl::CompileShader(id) };
+ {
+ //check log
+ let mut log = [0u8; 1024];
+ let mut len: GLsizei = 0;
+ let logp = &mut log as *mut u8 as *mut GLchar;
+ let lenp = &mut len as *mut GLsizei;
+ unsafe { gl::GetShaderInfoLog(id, log.len() as GLsizei, lenp, logp) };
+ if len > 0 {
+ eprintln!("{}", String::from_utf8_lossy(&log[..len as usize]));
+ }
+ }
+ {
+ let mut status: GLint = 0;
+ unsafe { gl::GetShaderiv(id, gl::COMPILE_STATUS, (&mut status) as _) };
+ if status == 0 {
+ return Err(format!("failed to compile"));
+ }
+ }
+
+ Ok(Self { id })
+ }
+}
+
+impl Drop for Shader {
+ fn drop(&mut self) {
+ unsafe { gl::DeleteShader(self.id) };
+ }
+}
+
+pub struct Program {
+ id: GLuint,
+}
+
+impl Program {
+ fn new(shaders: &[Shader]) -> Result<Self, String> {
+ let id = unsafe { gl::CreateProgram() };
+ let result = Self::new_with_id(id, shaders);
+ if result.is_err() {
+ unsafe { gl::DeleteShader(id) };
+ }
+ result
+ }
+
+ fn new_with_id(id: GLuint, shaders: &[Shader]) -> Result<Self, String> {
+ for shader in shaders {
+ unsafe { gl::AttachShader(id, shader.id) };
+ }
+ unsafe { gl::LinkProgram(id) };
+ {
+ // check log
+ let mut log = [0u8; 1024];
+ let mut len: GLsizei = 0;
+ let logp = &mut log as *mut u8 as *mut GLchar;
+ let lenp = &mut len as *mut GLsizei;
+ unsafe { gl::GetProgramInfoLog(id, log.len() as GLsizei, lenp, logp) };
+ if len > 0 {
+ eprintln!("{}", String::from_utf8_lossy(&log[..len as usize]));
+ }
+ }
+
+ {
+ let mut status: GLint = 0;
+ unsafe { gl::GetProgramiv(id, gl::LINK_STATUS, (&mut status) as _) };
+ if status == 0 {
+ return Err(format!("failed to link"));
+ }
+ }
+
+ Ok(Self { id })
+ }
+}
+
+impl Drop for Program {
+ fn drop(&mut self) {
+ unsafe { gl::DeleteProgram(self.id) };
+ }
+}
+
+pub struct Buffer {
+ id: GLuint,
+ stride: u32,
+ count: u32,
+}
+
+impl Buffer {
+ fn new() -> Self {
+ let mut id = 0;
+ unsafe { gl::CreateBuffers(1, &mut id as *mut GLuint) };
+ Self {
+ id,
+ stride: 0,
+ count: 0,
+ }
+ }
+
+ fn bind(&self) {
+ unsafe { gl::BindBuffer(gl::ARRAY_BUFFER, self.id) };
+ }
+
+ fn set_data<T>(&mut self, data: &[T]) {
+ unsafe {
+ gl::BindBuffer(gl::ARRAY_BUFFER, self.id);
+ }
+ self.count = data.len() as u32;
+ self.stride = mem::size_of::<T>() as u32;
+
+ unsafe {
+ gl::BufferData(
+ gl::ARRAY_BUFFER,
+ (self.count * self.stride) as _,
+ data.as_ptr() as _,
+ gl::STATIC_DRAW,
+ );
+ }
+ }
+}
+
+impl Drop for Buffer {
+ fn drop(&mut self) {
+ unsafe { gl::DeleteBuffers(1, &self.id as *const GLuint) };
+ }
+}
+
+pub struct VertexArray {
+ buffer: Buffer,
+ id: GLuint,
+ program: GLuint,
+}
+
+impl VertexArray {
+ fn new(buffer: Buffer, program: &Program) -> Self {
+ let mut id: GLuint = 0;
+
+ unsafe { gl::GenVertexArrays(1, &mut id as *mut GLuint) };
+
+ Self { id, buffer, program: program.id }
+ }
+
+ fn bind(&self) {
+ unsafe { gl::BindVertexArray(self.id) };
+ }
+
+ fn attribnf(&mut self, n: u8, name: &str, offset: usize) -> bool {
+ let Ok(cstring) = CString::new(name) else { return false };
+ let cstr = cstring.as_ptr() as *const GLchar;
+ let loc = unsafe { gl::GetAttribLocation(self.program, cstr) };
+ let Ok(loc) = loc.try_into() else { return false };
+
+ if offset + usize::from(n) * size_of::<f32>() > self.buffer.stride as usize {
+ // offset too large
+ return false;
+ }
+
+ self.bind();
+ self.buffer.bind();
+ unsafe {
+ gl::VertexAttribPointer(loc, n.into(), gl::FLOAT, 0, self.buffer.stride as _, offset as _)
+ };
+ unsafe { gl::EnableVertexAttribArray(loc) };
+ true
+ }
+
+ fn draw(&self) {
+ self.bind();
+ unsafe { gl::DrawArrays(gl::TRIANGLES, 0, self.buffer.count as i32) };
+ }
+}
+
+impl Drop for VertexArray {
+ fn drop(&mut self) {
+ unsafe { gl::DeleteVertexArrays(1, &self.id as *const GLuint) };
+ }
+}
+
+#[cfg(debug_assertions)]
+extern "system" fn gl_message_callback(
+ _source: GLenum,
+ _type: GLenum,
+ _id: c_uint,
+ severity: GLenum,
+ _length: GLsizei,
+ message: *const c_char,
+ _user_param: *mut c_void,
+) {
+ let message = String::from_utf8_lossy(unsafe { CStr::from_ptr(message) }.to_bytes());
+ if severity == gl::DEBUG_SEVERITY_NOTIFICATION {
+ return;
+ }
+ println!("Message from opengl: {message}");
+}
+
+pub struct Texture {
+ id: GLuint,
+ params: TextureParams,
+}
+
+impl Drop for Texture {
+ fn drop(&mut self) {
+ unsafe { gl::DeleteTextures(1, (&self.id) as *const GLuint) };
+ }
+}
+
+#[derive(Copy, Clone)]
+pub enum TextureFilter {
+ Nearest,
+ Linear,
+}
+
+impl TextureFilter {
+ fn to_gl(self) -> GLint {
+ use TextureFilter::*;
+ match self {
+ Nearest => gl::NEAREST as _,
+ Linear => gl::LINEAR as _,
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct TextureParams {
+ pub min_filter: TextureFilter,
+ pub mag_filter: TextureFilter,
+}
+
+impl Default for TextureParams {
+ fn default() -> Self {
+ Self {
+ min_filter: TextureFilter::Nearest,
+ mag_filter: TextureFilter::Linear,
+ }
+ }
+}
+
+impl Window {
+ pub fn new(title: &str, width: i32, height: i32, shown: bool) -> Result<Self, String> {
+ {
+ static WINDOW_CREATED: Mutex<bool> = Mutex::new(false);
+ let guard = WINDOW_CREATED.lock();
+ match guard {
+ Err(_) => return Err("couldn't lock mutex.".to_string()),
+ Ok(x) if *x => return Err("window already created".to_string()),
+ Ok(mut x) => *x = true,
+ }
+ }
+
+ unsafe {
+ sdl::set_hint("SDL_NO_SIGNAL_HANDLERS", "1"); // don't replace Ctrl+C, TERM with quit event
+ sdl::init()?;
+ #[cfg(debug_assertions)]
+ {
+ sdl::gl_set_context_version(4, 3);
+ sdl::gl_set_attribute(sdl::SDL_GL_CONTEXT_FLAGS, sdl::SDL_GL_CONTEXT_DEBUG_FLAG);
+ }
+ #[cfg(not(debug_assertions))]
+ sdl::gl_set_context_version(3, 0);
+ }
+ let mut flags = sdl::SDL_WINDOW_OPENGL;
+ if !shown {
+ flags |= sdl::SDL_WINDOW_HIDDEN;
+ }
+ let sdlwin = unsafe { sdl::create_window(title, width, height, flags) }?;
+ let ctx = unsafe { sdl::gl_create_context(sdlwin) }?;
+ gl::load_with(|name| unsafe { sdl::gl_get_proc_address(name) });
+ unsafe {
+ sdl::gl_set_swap_interval(1);
+ let mut flags: GLint = 0;
+ gl::GetIntegerv(gl::CONTEXT_FLAGS, (&mut flags) as _);
+ #[cfg(debug_assertions)]
+ if (flags as GLuint & gl::CONTEXT_FLAG_DEBUG_BIT) != 0 {
+ gl::DebugMessageCallback(Some(gl_message_callback), 0 as _);
+ gl::DebugMessageControl(
+ gl::DONT_CARE,
+ gl::DONT_CARE,
+ gl::DONT_CARE,
+ 0,
+ 0 as _,
+ gl::TRUE,
+ );
+ gl::Enable(gl::DEBUG_OUTPUT);
+ gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS);
+ }
+ }
+
+ Ok(Window {
+ sdlwin,
+ glctx: ctx,
+ used_program: 0,
+ audio_data: None,
+ })
+ }
+
+ pub fn show(&mut self) {
+ unsafe { sdl::show_window(self.sdlwin) };
+ }
+
+ pub fn create_program(
+ &mut self,
+ source_vshader: &str,
+ source_fshader: &str,
+ ) -> Result<Program, String> {
+ let vshader = Shader::new(gl::VERTEX_SHADER, source_vshader)?;
+ let fshader = Shader::new(gl::FRAGMENT_SHADER, source_fshader)?;
+ Program::new(&[vshader, fshader])
+ }
+
+ pub fn create_buffer(&mut self) -> Buffer {
+ Buffer::new()
+ }
+
+ pub fn set_buffer_data<T>(&mut self, buffer: &mut Buffer, data: &[T]) {
+ buffer.set_data(data);
+ }
+
+ pub fn create_vertex_array(&mut self, buffer: Buffer, program: &Program) -> VertexArray {
+ VertexArray::new(buffer, program)
+ }
+
+ fn array_attribnf(&mut self, array: &mut VertexArray, n: u8, name: &str, offset: usize) -> bool {
+ array.attribnf(n, name, offset)
+ }
+
+ pub fn array_attrib2f(&mut self, array: &mut VertexArray, name: &str, offset: usize) -> bool {
+ self.array_attribnf(array, 2, name, offset)
+ }
+
+ pub fn array_attrib3f(&mut self, array: &mut VertexArray, name: &str, offset: usize) -> bool {
+ self.array_attribnf(array, 3, name, offset)
+ }
+
+ pub fn array_attrib4f(&mut self, array: &mut VertexArray, name: &str, offset: usize) -> bool {
+ self.array_attribnf(array, 4, name, offset)
+ }
+
+ pub fn size(&mut self) -> (i32, i32) {
+ let mut x = 0;
+ let mut y = 0;
+ unsafe { sdl::get_window_size(self.sdlwin, &mut x, &mut y) };
+ (x, y)
+ }
+
+ pub fn viewport(&mut self, x: i32, y: i32, w: i32, h: i32) {
+ unsafe { gl::Viewport(x, y, w, h) };
+ }
+
+ pub fn next_event(&mut self) -> Option<Event> {
+ loop {
+ let sdl = unsafe { sdl::poll_event() }?;
+ let r#type = unsafe { sdl.r#type };
+ match r#type {
+ sdl::SDL_QUIT => return Some(Event::Quit),
+ sdl::SDL_KEYDOWN | sdl::SDL_KEYUP => {
+ let scancode = unsafe { sdl.key }.keysym.scancode;
+ if let Some(k) = Key::from_sdl(scancode) {
+ if r#type == sdl::SDL_KEYDOWN {
+ return Some(Event::KeyDown(k));
+ } else {
+ return Some(Event::KeyUp(k));
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+
+ pub fn clear_screen(&mut self, color: ColorF32) {
+ unsafe {
+ gl::ClearColor(color.r, color.g, color.b, color.a);
+ gl::Clear(gl::COLOR_BUFFER_BIT);
+ }
+ }
+
+ pub fn create_rgba_texture(&mut self, params: &TextureParams) -> Texture {
+ let mut id: GLuint = 0;
+ unsafe {
+ gl::GenTextures(1, (&mut id) as *mut GLuint);
+ }
+ Texture { id, params: params.clone() }
+ }
+
+ pub fn set_texture_data(
+ &mut self,
+ texture: &mut Texture,
+ data: &[u8],
+ width: usize,
+ height: usize,
+ ) -> Result<(), String> {
+ let width = width as GLsizei;
+ let height = height as GLsizei;
+ let expected_len = 4 * width * height;
+ if data.len() as GLsizei != expected_len {
+ return Err(format!(
+ "bad data length (expected {}, got {})",
+ expected_len,
+ data.len()
+ ));
+ }
+ let params = &texture.params;
+ unsafe {
+ gl::BindTexture(gl::TEXTURE_2D, texture.id);
+ gl::TexImage2D(
+ gl::TEXTURE_2D,
+ 0,
+ gl::RGBA as _,
+ width,
+ height,
+ 0,
+ gl::RGBA,
+ gl::UNSIGNED_BYTE,
+ data.as_ptr() as _,
+ );
+ gl::TexParameteri(
+ gl::TEXTURE_2D,
+ gl::TEXTURE_MIN_FILTER,
+ params.min_filter.to_gl(),
+ );
+ gl::TexParameteri(
+ gl::TEXTURE_2D,
+ gl::TEXTURE_MAG_FILTER,
+ params.mag_filter.to_gl(),
+ );
+ }
+ Ok(())
+ }
+
+ pub fn set_audio_callback(&mut self, callback: AudioCallback) -> Result<(), String> {
+ if self.audio_data.is_some() {
+ return Err("audio callback already set.".into());
+ }
+
+ extern "C" fn sdl_callback(userdata: *mut c_void, stream: *mut u8, len: c_int) {
+ let data = unsafe { (userdata as *const AudioData).as_ref() }.unwrap();
+ // this should never panick, since SDL shouldn't pass us a negative length.
+ let samples: usize = (len / 4).try_into().unwrap();
+ let slice = unsafe { std::slice::from_raw_parts_mut(stream as *mut f32, samples) };
+ slice.fill(0.0);
+ (data.callback)(data.sample_rate, slice);
+ }
+
+ let mut data = Box::new(AudioData {
+ callback,
+ device: 0,
+ sample_rate: 0,
+ });
+
+ let desired = sdl::SDL_AudioSpec::new(
+ sdl_callback,
+ &*data as *const AudioData as *mut c_void,
+ 2,
+ sdl::AUDIO_F32,
+ 44100,
+ 4096,
+ );
+
+ let (obtained, id) = unsafe {
+ sdl::open_audio_device(
+ None,
+ false,
+ &desired,
+ sdl::SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | sdl::SDL_AUDIO_ALLOW_SAMPLES_CHANGE,
+ )?
+ };
+ data.sample_rate = obtained.freq.try_into().unwrap();
+ data.device = id;
+
+ self.audio_data = Some(data);
+
+ unsafe { sdl::pause_audio_device(id, false) };
+
+ Ok(())
+ }
+
+ pub fn use_program(&mut self, program: &Program) {
+ if self.used_program != program.id {
+ unsafe { gl::UseProgram(program.id) };
+ self.used_program = program.id;
+ }
+ }
+
+ fn get_uniform_location(&self, name: &str) -> Option<GLint> {
+ if self.used_program == 0 {
+ return None;
+ }
+ let cstring = CString::new(name).ok()?;
+ let cstr = cstring.as_ptr() as *const GLchar;
+ let loc = unsafe { gl::GetUniformLocation(self.used_program, cstr) };
+ if loc == -1 {
+ None
+ } else {
+ Some(loc)
+ }
+ }
+
+ pub fn active_texture(&mut self, slot: u32, texture: &Texture) {
+ unsafe {
+ gl::ActiveTexture(gl::TEXTURE0 + slot);
+ gl::BindTexture(gl::TEXTURE_2D, texture.id);
+ }
+ }
+
+ pub fn uniform_texture(&mut self, name: &str, slot: u32) {
+ let loc = self.get_uniform_location(name).unwrap_or(-1);
+ unsafe {
+ gl::Uniform1i(loc, slot as i32);
+ }
+ }
+
+ pub fn draw_array(&mut self, array: &VertexArray) {
+ array.draw();
+ }
+
+ #[allow(dead_code)]
+ pub fn is_key_down(&mut self, key: Key) -> bool {
+ let kbd_state = unsafe { sdl::get_keyboard_state() };
+ kbd_state[key.to_sdl() as usize] != 0
+ }
+
+ pub fn swap(&mut self) {
+ unsafe { sdl::gl_swap_window(self.sdlwin) };
+ }
+}
+
+impl Drop for Window {
+ fn drop(&mut self) {
+ unsafe {
+ if let Some(audio_data) = &self.audio_data {
+ sdl::close_audio_device(audio_data.device);
+ }
+ sdl::gl_delete_context(self.glctx);
+ sdl::destroy_window(self.sdlwin);
+ }
+ }
+}