diff options
author | Lassi Pulkkinen <lassi@pulk.fi> | 2024-10-31 03:11:21 +0200 |
---|---|---|
committer | Lassi Pulkkinen <lassi@pulk.fi> | 2024-10-31 03:51:35 +0200 |
commit | ae44478b30d890fe0fb04022f44d474dcdcc3f9d (patch) | |
tree | 5f462459ae4b47d22114eed717d1382d08cf4dfe /window.ha |
Diffstat (limited to 'window.ha')
-rw-r--r-- | window.ha | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/window.ha b/window.ha new file mode 100644 index 0000000..9d3d129 --- /dev/null +++ b/window.ha @@ -0,0 +1,333 @@ +use fs; +use gl; +use gl::*; +use glm; +use glw; +use math; +use net; +use os; +use sdl2; +use sdl2::*; +use time; +use trace; +use types::c; + +type Window = struct { + timestamp: time::instant, + + scale: u16, + gui_proj: glm::m4, + + mouse_x: f32, + mouse_y: f32, + held_keys: []Key, + held_mouse_buttons: [5]bool, + input_bottom: size, + + window: *SDL_Window, +}; + +let WINDOW: (Window | void) = void; + +// XXX: hack to get around a temporary limitation of tagged unions. +// this will actually stop working once that's resolved... +fn get_WINDOW() *Window = { + WINDOW as Window; + + return (&WINDOW: uintptr + + offset(struct { + tag: int = 0, + w: Window = Window { window = null: *SDL_Window, ... }, + }.w): uintptr): *Window; +}; + +fn gl_get_proc_address(procname: *const c::char) *opaque = + SDL_GL_GetProcAddress(procname); + +fn window_run(addr: str, username: str) void = { + match (SDL_Init(SDL_INIT_VIDEO)) { + case void => void; + case let err: sdl2::error => + die("failed to initialize sdl: {}", err); + }; + defer SDL_Quit(); + + trace::info(&trace::root, "using video {}", + SDL_GetCurrentVideoDriver()); + + match (window_init_gl_attrs()) { + case void => void; + case let err: sdl2::error => + die("failed to set gl attributes: {}", err); + }; + + const window = match (SDL_CreateWindow( + "hacraft", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + 640, 480, + SDL_WindowFlags::RESIZABLE + | SDL_WindowFlags::ALLOW_HIGHDPI + | SDL_WindowFlags::OPENGL)) { + case let window: *SDL_Window => + yield window; + case let err: sdl2::error => + die("failed to create window: {}", err); + }; + defer SDL_DestroyWindow(window); + + match (SDL_GL_CreateContext(window)) { + case *SDL_GLContext => void; + case let err: sdl2::error => + die("failed to create gl context: {}", err); + }; + gl::load_with_fn(&gl_get_proc_address); + + trace::info(&trace::root, "using opengl {} / glsl {} / on {} {}", + glw::get_string(GL_VERSION), + glw::get_string(GL_SHADING_LANGUAGE_VERSION), + glw::get_string(GL_VENDOR), + glw::get_string(GL_RENDERER)); + glw::init_debug_logging(); + + match (SDL_SetRelativeMouseMode(true)) { + case void => void; + case let err: sdl2::error => + trace::error(&trace::root, + "Failed to enable relative mouse: {}", + err): void; + }; + + match (SDL_GL_SetSwapInterval(0)) { + case void => void; + case let err: sdl2::error => + trace::error(&trace::root, + "Failed to set swap interval: {}", + err): void; + }; + + WINDOW = Window { + timestamp = time::now(time::clock::MONOTONIC), + + scale = 1, + gui_proj = glm::m4_new_ident(), + + mouse_x = 0.0, + mouse_y = 0.0, + held_keys = [], + held_mouse_buttons = [false...], + input_bottom = 0, + + window = window, + }; + defer WINDOW = void; + + register_blocks(); + defer destroy_blocks(); + + init_bstates(); + defer destroy_bstates(); + + const assets = os::diropen("resources")!; + defer fs::close(assets); + const assets = Pack { + fs = assets, + kind = "assets", + }; + render_init(&assets); + defer render_destroy(); + + client_connect(addr, username); + defer client_destroy(); + + for (window_frame()) void; +}; + +fn window_init_gl_attrs() (void | sdl2::error) = { + SDL_GL_SetAttribute(SDL_GLattr::GL_CONTEXT_PROFILE_MASK, + SDL_GLprofile::GL_CONTEXT_PROFILE_ES)?; + SDL_GL_SetAttribute(SDL_GLattr::GL_CONTEXT_MAJOR_VERSION, 3)?; + SDL_GL_SetAttribute(SDL_GLattr::GL_CONTEXT_MINOR_VERSION, 2)?; + SDL_GL_SetAttribute(SDL_GLattr::GL_DEPTH_SIZE, 24)?; + SDL_GL_SetAttribute(SDL_GLattr::GL_CONTEXT_FLAGS, SDL_GLcontextFlag::GL_CONTEXT_DEBUG_FLAG)?; + SDL_GL_SetAttribute(SDL_GLattr::GL_FRAMEBUFFER_SRGB_CAPABLE, 1)?; +}; + +fn drawable_size() (u16, u16) = { + const WINDOW = get_WINDOW(); + let width = 0, height = 0; + SDL_GL_GetDrawableSize(WINDOW.window, &width, &height); + assert(width == width: u16: int + && height == height: u16: int, "the fuck?"); + return (width: u16, height: u16); +}; + +fn frame_timestamp() time::instant = { + const WINDOW = get_WINDOW(); + return WINDOW.timestamp; +}; + +fn gui_scale() u16 = { + const WINDOW = get_WINDOW(); + return WINDOW.scale; +}; + +fn gui_proj() *glm::m4 = { + const WINDOW = get_WINDOW(); + return &WINDOW.gui_proj; +}; + +fn window_frame() bool = { + const WINDOW = get_WINDOW(); + + window_update_layer_input(); + + for (true) { + let event = sdl2::event { ... }; + match (SDL_PollEvent(&event)) { + case let pending: int => + if (pending == 0) break; + case let err: sdl2::error => + trace::error(&trace::root, "SDL error: {}", err): void; + return false; + }; + + if (event.event_type == SDL_EventType::QUIT) { + return false; + }; + + window_event(&event); + }; + + const (width, height) = drawable_size(); + const xscale = width / 320; + const yscale = height / 240; + WINDOW.scale = if (xscale < yscale) xscale else yscale; + if (WINDOW.scale == 0) { + WINDOW.scale = 1; + }; + + glm::m4_set_ident(&WINDOW.gui_proj); + glm::scale(&WINDOW.gui_proj, + &[gui_scale(): f32, gui_scale(): f32, 1.0]); + glm::scale(&WINDOW.gui_proj, + &[2.0 / width: f32, -2.0 / height: f32, 1.0]); + glm::translate(&WINDOW.gui_proj, &[-1.0f32, 1.0, 0.0]); + + client_frame(); + layers_render(); + + SDL_GL_SwapWindow(WINDOW.window); + + const then = WINDOW.timestamp; + WINDOW.timestamp = time::now(time::clock::MONOTONIC); + + const dt = time::diff(then, WINDOW.timestamp); + const dt = (dt: f64 / 1000000000.0): f32; + const (sec, subsec) = math::modfracf32(dt); +// trace::debug(&trace::root, "frame took {}.{:03} s / fps {}", +// sec, (subsec * 1000.0): i32, 1.0 / dt); + + return true; +}; + +fn window_event(event: *sdl2::event) void = { + const WINDOW = get_WINDOW(); + + switch (event.event_type) { + case SDL_EventType::KEYDOWN => + const key_event = KeyEvent { + status = ButtonStatus::DOWN, + key = event.key.keysym.scancode: Key, + }; + if (event.key.repeat == 0) { + append(WINDOW.held_keys, key_event.key); + }; + layers_input(key_event, len(LAYERS), false); + window_update_layer_input(); + case SDL_EventType::KEYUP => + let key_event = KeyEvent { + status = ButtonStatus::UP, + key = event.key.keysym.scancode: Key, + }; + for (let i = 0z; i < len(WINDOW.held_keys)) { + if (WINDOW.held_keys[i] != key_event.key) { + i += 1; + continue; + }; + delete(WINDOW.held_keys[i]); + }; + layers_input(key_event, len(LAYERS), false); + window_update_layer_input(); + case SDL_EventType::MOUSEBUTTONDOWN => + WINDOW.held_mouse_buttons[event.button.button] = true; + layers_input(MouseButtonEvent { + status = ButtonStatus::DOWN, + button = event.button.button: MouseButton, + }, len(LAYERS), false); + window_update_layer_input(); + case SDL_EventType::MOUSEBUTTONUP => + WINDOW.held_mouse_buttons[event.button.button] = false; + layers_input(MouseButtonEvent { + status = ButtonStatus::UP, + button = event.button.button: MouseButton, + }, len(LAYERS), false); + window_update_layer_input(); + case SDL_EventType::MOUSEMOTION => + WINDOW.mouse_x = event.motion.x: f32 / gui_scale(): f32; + WINDOW.mouse_y = event.motion.y: f32 / gui_scale(): f32; + window_update_mouse_over(); + case => void; + }; +}; + +fn window_update_layer_input() void = { + const WINDOW = get_WINDOW(); + + let bottom = len(LAYERS); + for (bottom != 0) { + bottom -= 1; + if (LAYERS[bottom].blocks_input()) { + break; + }; + }; + + if (bottom > WINDOW.input_bottom) { + layers_input(CancelEvent, bottom, true); + WINDOW.input_bottom = bottom; + } else if (bottom < WINDOW.input_bottom) { + const old_bottom = WINDOW.input_bottom; + WINDOW.input_bottom = bottom; + window_uncancel(old_bottom); + }; + + window_update_mouse_over(); +}; + +fn window_uncancel(top: size) void = { + const WINDOW = get_WINDOW(); + + window_update_mouse_over(); + + for (let i = 0z; i < len(WINDOW.held_keys); i += 1) { + layers_input(KeyEvent { + status = ButtonStatus::HELD, + key = WINDOW.held_keys[i], + }, top, true); + }; + + for (let i = 0z; i < len(WINDOW.held_mouse_buttons); i += 1) { + if (!WINDOW.held_mouse_buttons[i]) { + continue; + }; + layers_input(MouseButtonEvent { + status = ButtonStatus::HELD, + button = i: MouseButton, + }, top, true); + }; +}; + +fn window_update_mouse_over() void = { + const i = layers_input(MouseOverEvent { covered = false }, + len(LAYERS), false); + layers_input(MouseOverEvent { covered = true }, i, true); +}; |