summaryrefslogtreecommitdiff
path: root/window.ha
diff options
context:
space:
mode:
authorLassi Pulkkinen <lassi@pulk.fi>2024-10-31 03:11:21 +0200
committerLassi Pulkkinen <lassi@pulk.fi>2024-10-31 03:51:35 +0200
commitae44478b30d890fe0fb04022f44d474dcdcc3f9d (patch)
tree5f462459ae4b47d22114eed717d1382d08cf4dfe /window.ha
Initial commit (import old repo)HEADmain
Diffstat (limited to 'window.ha')
-rw-r--r--window.ha333
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);
+};