diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/game/client/experiments.cc | 12 | ||||
| -rw-r--r-- | src/game/client/game.cc | 30 | ||||
| -rw-r--r-- | src/game/client/game.hh | 1 | ||||
| -rw-r--r-- | src/game/client/gui/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/game/client/gui/settings.hh | 1 | ||||
| -rw-r--r-- | src/game/client/gui/window_title.cc | 19 | ||||
| -rw-r--r-- | src/game/client/gui/window_title.hh | 15 | ||||
| -rw-r--r-- | src/game/client/io/gamepad.cc | 72 | ||||
| -rw-r--r-- | src/game/client/io/gamepad.hh | 100 | ||||
| -rw-r--r-- | src/game/client/io/glfw.hh | 42 | ||||
| -rw-r--r-- | src/game/client/io/keyboard.cc | 29 | ||||
| -rw-r--r-- | src/game/client/io/keyboard.hh | 83 | ||||
| -rw-r--r-- | src/game/client/io/mouse.cc | 44 | ||||
| -rw-r--r-- | src/game/client/io/mouse.hh | 141 | ||||
| -rw-r--r-- | src/game/client/io/video.cc | 289 | ||||
| -rw-r--r-- | src/game/client/io/video.hh | 115 | ||||
| -rw-r--r-- | src/game/client/main.cc | 177 |
17 files changed, 860 insertions, 314 deletions
diff --git a/src/game/client/experiments.cc b/src/game/client/experiments.cc index dcd3c7d..14637ae 100644 --- a/src/game/client/experiments.cc +++ b/src/game/client/experiments.cc @@ -17,23 +17,23 @@ #include "client/gui/hotbar.hh" #include "client/gui/status_lines.hh" -#include "client/io/glfw.hh" +#include "client/io/mouse.hh" #include "client/world/player_target.hh" #include "client/globals.hh" #include "client/session.hh" -static void on_glfw_mouse_button(const GlfwMouseButtonEvent& event) +static void on_mouse_button(const MouseButtonEvent& event) { if(!globals::gui_screen && session::is_ingame()) { - if((event.action == GLFW_PRESS) && player_target::voxel) { - if(event.button == GLFW_MOUSE_BUTTON_LEFT) { + if(event.is_action(GLFW_PRESS) && player_target::voxel) { + if(event.is_button(GLFW_MOUSE_BUTTON_LEFT)) { experiments::attack(); return; } - if(event.button == GLFW_MOUSE_BUTTON_RIGHT) { + if(event.is_button(GLFW_MOUSE_BUTTON_RIGHT)) { experiments::interact(); return; } @@ -43,7 +43,7 @@ static void on_glfw_mouse_button(const GlfwMouseButtonEvent& event) void experiments::init(void) { - globals::dispatcher.sink<GlfwMouseButtonEvent>().connect<&on_glfw_mouse_button>(); + globals::dispatcher.sink<MouseButtonEvent>().connect<&on_mouse_button>(); } void experiments::init_late(void) diff --git a/src/game/client/game.cc b/src/game/client/game.cc index 09445c4..9b55b4b 100644 --- a/src/game/client/game.cc +++ b/src/game/client/game.cc @@ -64,11 +64,11 @@ #include "client/gui/settings.hh" #include "client/gui/splash.hh" #include "client/gui/status_lines.hh" -#include "client/gui/window_title.hh" #include "client/io/gamepad.hh" -#include "client/io/glfw.hh" +#include "client/io/keyboard.hh" #include "client/io/sound.hh" +#include "client/io/video.hh" #include "client/resource/texture_gui.hh" @@ -93,7 +93,6 @@ constexpr static int PIXEL_SIZE = 2; config::Boolean client_game::streamer_mode(false); -config::Boolean client_game::vertical_sync(true); config::Boolean client_game::world_curvature(true); config::Unsigned client_game::fog_mode(1U, 0U, 2U); config::String client_game::username("player"); @@ -125,7 +124,7 @@ static ImFont* load_font(std::string_view path, float size, ImFontConfig& font_c return font_ptr; } -static void on_glfw_framebuffer_size(const GlfwFramebufferSizeEvent& event) +static void on_framebuffer_size(const FramebufferSizeEvent& event) { if(globals::world_fbo) { glDeleteRenderbuffers(1, &globals::world_fbo_depth); @@ -137,8 +136,8 @@ static void on_glfw_framebuffer_size(const GlfwFramebufferSizeEvent& event) glGenTextures(1, &globals::world_fbo_color); glGenRenderbuffers(1, &globals::world_fbo_depth); - scaled_width = event.size.x / PIXEL_SIZE; - scaled_height = event.size.y / PIXEL_SIZE; + scaled_width = event.wide() / PIXEL_SIZE; + scaled_height = event.tall() / PIXEL_SIZE; glBindTexture(GL_TEXTURE_2D, globals::world_fbo_color); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, scaled_width, scaled_height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); @@ -159,9 +158,9 @@ static void on_glfw_framebuffer_size(const GlfwFramebufferSizeEvent& event) } } -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_key(const KeyEvent& event) { - if(!globals::gui_keybind_ptr && hide_hud_toggle.equals(event.key) && (event.action == GLFW_PRESS)) { + if(!globals::gui_keybind_ptr && hide_hud_toggle.equals(event.keycode()) && (event.is_action(GLFW_PRESS))) { client_game::hide_hud = !client_game::hide_hud; } } @@ -191,7 +190,6 @@ void client_game::init(void) client_splash::render(); globals::client_config.add_value("game.streamer_mode", client_game::streamer_mode); - globals::client_config.add_value("game.vertical_sync", client_game::vertical_sync); globals::client_config.add_value("game.world_curvature", client_game::world_curvature); globals::client_config.add_value("game.fog_mode", client_game::fog_mode); globals::client_config.add_value("game.username", client_game::username); @@ -200,7 +198,6 @@ void client_game::init(void) settings::init(); settings::add_checkbox(0, client_game::streamer_mode, settings_location::VIDEO_GUI, "game.streamer_mode", true); - settings::add_checkbox(5, client_game::vertical_sync, settings_location::VIDEO, "game.vertical_sync", false); settings::add_checkbox(4, client_game::world_curvature, settings_location::VIDEO, "game.world_curvature", true); settings::add_stepper(3, client_game::fog_mode, settings_location::VIDEO, "game.fog_mode", false); settings::add_input(1, client_game::username, settings_location::GENERAL, "game.username", true, false); @@ -351,8 +348,8 @@ void client_game::init(void) experiments::init(); - globals::dispatcher.sink<GlfwFramebufferSizeEvent>().connect<&on_glfw_framebuffer_size>(); - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<FramebufferSizeEvent>().connect<&on_framebuffer_size>(); + globals::dispatcher.sink<KeyEvent>().connect<&on_key>(); } void client_game::init_late(void) @@ -428,7 +425,7 @@ void client_game::init_late(void) client_splash::init_late(); - window_title::update(); + video::update_window_title(); } void client_game::shutdown(void) @@ -591,13 +588,6 @@ void client_game::update_late(void) gamepad::update_late(); chunk_visibility::update_late(); - - if(client_game::vertical_sync.get_value()) { - glfwSwapInterval(1); - } - else { - glfwSwapInterval(0); - } } void client_game::render(void) diff --git a/src/game/client/game.hh b/src/game/client/game.hh index 7ed663b..66b28cd 100644 --- a/src/game/client/game.hh +++ b/src/game/client/game.hh @@ -17,7 +17,6 @@ class Unsigned; namespace client_game { extern config::Boolean streamer_mode; -extern config::Boolean vertical_sync; extern config::Boolean world_curvature; extern config::Unsigned fog_mode; extern config::String username; diff --git a/src/game/client/gui/CMakeLists.txt b/src/game/client/gui/CMakeLists.txt index e3d8b7a..3eae65f 100644 --- a/src/game/client/gui/CMakeLists.txt +++ b/src/game/client/gui/CMakeLists.txt @@ -37,6 +37,4 @@ target_sources(vclient PRIVATE "${CMAKE_CURRENT_LIST_DIR}/splash.cc" "${CMAKE_CURRENT_LIST_DIR}/splash.hh" "${CMAKE_CURRENT_LIST_DIR}/status_lines.cc" - "${CMAKE_CURRENT_LIST_DIR}/status_lines.hh" - "${CMAKE_CURRENT_LIST_DIR}/window_title.cc" - "${CMAKE_CURRENT_LIST_DIR}/window_title.hh") + "${CMAKE_CURRENT_LIST_DIR}/status_lines.hh") diff --git a/src/game/client/gui/settings.hh b/src/game/client/gui/settings.hh index 1a3a1f8..b48ab8c 100644 --- a/src/game/client/gui/settings.hh +++ b/src/game/client/gui/settings.hh @@ -94,6 +94,7 @@ void add_gamepad_button(int priority, config::GamepadButton& value, settings_loc namespace settings { void add_language_select(int priority, settings_location location, std::string_view name); +void add_video_mode_select(int priority, settings_location location, std::string_view name); } // namespace settings #endif diff --git a/src/game/client/gui/window_title.cc b/src/game/client/gui/window_title.cc deleted file mode 100644 index ea65b64..0000000 --- a/src/game/client/gui/window_title.cc +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause -// Copyright (c) 2025 Kirill Dmitrievich -// File: window_title.cc -// Description: Random MOTD in the window title - -#include "client/pch.hh" - -#include "client/gui/window_title.hh" - -#include "core/version.hh" - -#include "shared/splash.hh" - -#include "client/globals.hh" - -void window_title::update(void) -{ - glfwSetWindowTitle(globals::window, std::format("Voxelius {}: {}", version::triplet, splash::get()).c_str()); -} diff --git a/src/game/client/gui/window_title.hh b/src/game/client/gui/window_title.hh deleted file mode 100644 index 78f7326..0000000 --- a/src/game/client/gui/window_title.hh +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause -// Copyright (c) 2025 Kirill Dmitrievich -// File: window_title.hh -// Description: Random MOTD in the window title - -#ifndef CLIENT_GUI_WINDOW_TITLE_HH -#define CLIENT_GUI_WINDOW_TITLE_HH -#pragma once - -namespace window_title -{ -void update(void); -} // namespace window_title - -#endif diff --git a/src/game/client/io/gamepad.cc b/src/game/client/io/gamepad.cc index 768b18b..ba65599 100644 --- a/src/game/client/io/gamepad.cc +++ b/src/game/client/io/gamepad.cc @@ -9,12 +9,14 @@ #include "core/config/boolean.hh" #include "core/config/number.hh" + #include "core/io/cmdline.hh" #include "core/io/config_map.hh" +#include "core/io/physfs.hh" + #include "core/math/constexpr.hh" #include "client/gui/settings.hh" -#include "client/io/glfw.hh" #include "client/globals.hh" #include "client/toggles.hh" @@ -48,37 +50,37 @@ static void on_toggle_disable(const ToggleDisabledEvent& event) } } -static void on_glfw_joystick_event(const GlfwJoystickEvent& event) +static void on_joystick_glfw(int joystick_id, int event_type) { - if((event.event_type == GLFW_CONNECTED) && glfwJoystickIsGamepad(event.joystick_id) && (active_gamepad_id == INVALID_GAMEPAD_ID)) { + if((event_type == GLFW_CONNECTED) && glfwJoystickIsGamepad(joystick_id) && (active_gamepad_id == INVALID_GAMEPAD_ID)) { gamepad::available = true; - active_gamepad_id = event.joystick_id; + active_gamepad_id = joystick_id; - for(int i = 0; i < NUM_AXES; gamepad::last_state.axes[i++] = 0.0f) { - // empty + for(int i = 0; i < NUM_AXES; ++i) { + gamepad::last_state.axes[i] = 0.0f; } - for(int i = 0; i < NUM_BUTTONS; gamepad::last_state.buttons[i++] = GLFW_RELEASE) { - // empty + for(int i = 0; i < NUM_BUTTONS; ++i) { + gamepad::last_state.buttons[i] = GLFW_RELEASE; } - spdlog::info("gamepad: detected gamepad: {}", glfwGetGamepadName(event.joystick_id)); + spdlog::info("gamepad: detected gamepad: {}", glfwGetGamepadName(joystick_id)); return; } - if((event.event_type == GLFW_DISCONNECTED) && (active_gamepad_id == event.joystick_id)) { + if((event_type == GLFW_DISCONNECTED) && (active_gamepad_id == joystick_id)) { gamepad::available = false; active_gamepad_id = INVALID_GAMEPAD_ID; - for(int i = 0; i < NUM_AXES; gamepad::last_state.axes[i++] = 0.0f) { - // empty + for(int i = 0; i < NUM_AXES; ++i) { + gamepad::last_state.axes[i] = 0.0f; } - for(int i = 0; i < NUM_BUTTONS; gamepad::last_state.buttons[i++] = GLFW_RELEASE) { - // empty + for(int i = 0; i < NUM_BUTTONS; ++i) { + gamepad::last_state.buttons[i] = GLFW_RELEASE; } spdlog::warn("gamepad: disconnected"); @@ -99,15 +101,12 @@ void gamepad::init(void) settings::add_checkbox(0, gamepad::active, settings_location::GAMEPAD, "gamepad.active", true); settings::add_slider(1, gamepad::deadzone, settings_location::GAMEPAD, "gamepad.deadzone", true, "%.03f"); - auto mappings_path = cmdline::get_cstr("gpmap", "misc/gamecontrollerdb.txt"); - auto mappings_file = PHYSFS_openRead(mappings_path); + std::string mappings_string; + std::string_view mappings_path(cmdline::get("gpmap", "misc/gamecontrollerdb.txt")); - if(mappings_file) { + if(physfs::read_file(mappings_path, mappings_string)) { spdlog::info("gamepad: using mappings from {}", mappings_path); - auto mappings_string = std::string(PHYSFS_fileLength(mappings_file), char(0x00)); - PHYSFS_readBytes(mappings_file, mappings_string.data(), mappings_string.size()); glfwUpdateGamepadMappings(mappings_string.c_str()); - PHYSFS_close(mappings_file); } for(int joystick = 0; joystick <= GLFW_JOYSTICK_LAST; joystick += 1) { @@ -140,7 +139,9 @@ void gamepad::init(void) globals::dispatcher.sink<ToggleEnabledEvent>().connect<&on_toggle_enable>(); globals::dispatcher.sink<ToggleDisabledEvent>().connect<&on_toggle_disable>(); - globals::dispatcher.sink<GlfwJoystickEvent>().connect<&on_glfw_joystick_event>(); + + spdlog::info("gamepad: taking over device callbacks"); + glfwSetJoystickCallback(&on_joystick_glfw); } void gamepad::update_late(void) @@ -152,21 +153,21 @@ void gamepad::update_late(void) if(glfwGetGamepadState(active_gamepad_id, &gamepad::state)) { for(int i = 0; i < NUM_AXES; ++i) { - if((glm::abs(gamepad::state.axes[i]) > GAMEPAD_AXIS_EVENT_THRESHOLD) - && (glm::abs(gamepad::last_state.axes[i]) <= GAMEPAD_AXIS_EVENT_THRESHOLD)) { - GamepadAxisEvent event; - event.action = GLFW_PRESS; - event.axis = i; - globals::dispatcher.enqueue(event); + auto is_press = true; + is_press = is_press && glm::abs(gamepad::state.axes[i] > GAMEPAD_AXIS_EVENT_THRESHOLD); + is_press = is_press && glm::abs(gamepad::last_state.axes[i] <= GAMEPAD_AXIS_EVENT_THRESHOLD); + + if(is_press) { + globals::dispatcher.enqueue(GamepadAxisEvent(i, GLFW_PRESS)); continue; } - if((glm::abs(gamepad::state.axes[i]) <= GAMEPAD_AXIS_EVENT_THRESHOLD) - && (glm::abs(gamepad::last_state.axes[i]) > GAMEPAD_AXIS_EVENT_THRESHOLD)) { - GamepadAxisEvent event; - event.action = GLFW_RELEASE; - event.axis = i; - globals::dispatcher.enqueue(event); + auto is_release = true; + is_release = is_release && glm::abs(gamepad::state.axes[i]) <= GAMEPAD_AXIS_EVENT_THRESHOLD; + is_release = is_release && glm::abs(gamepad::last_state.axes[i]) > GAMEPAD_AXIS_EVENT_THRESHOLD; + + if(is_release) { + globals::dispatcher.enqueue(GamepadAxisEvent(i, GLFW_RELEASE)); continue; } } @@ -177,10 +178,7 @@ void gamepad::update_late(void) continue; } - GamepadButtonEvent event; - event.action = gamepad::state.buttons[i]; - event.button = i; - globals::dispatcher.enqueue(event); + globals::dispatcher.enqueue(GamepadButtonEvent(i, gamepad::state.buttons[i])); } } diff --git a/src/game/client/io/gamepad.hh b/src/game/client/io/gamepad.hh index b506cc2..ff6af4a 100644 --- a/src/game/client/io/gamepad.hh +++ b/src/game/client/io/gamepad.hh @@ -10,6 +10,43 @@ constexpr static int INVALID_GAMEPAD_AXIS = INT_MAX; constexpr static int INVALID_GAMEPAD_BUTTON = INT_MAX; +// This simulates buttons using axes. When an axis +// value exceeds 1.5 times the deadzone, the event is +// queued with a GLFW_PRESS action, when it falls back +// below the threshold, the event is queued with GLFW_RELEASE action +class GamepadAxisEvent final { +public: + constexpr explicit GamepadAxisEvent(int axis, int action); + + constexpr int axis(void) const noexcept; + constexpr int action(void) const noexcept; + + constexpr bool is_axis(int axis) const noexcept; + constexpr bool is_action(int action) const noexcept; + +private: + int m_axis; + int m_action; +}; + +// This smears GLFW event sugar over gamepad polling +// system. Whenever it detects a state change, the event +// is queued with an appropriate action +class GamepadButtonEvent final { +public: + constexpr explicit GamepadButtonEvent(int button, int action); + + constexpr int button(void) const noexcept; + constexpr int action(void) const noexcept; + + constexpr bool is_button(int button) const noexcept; + constexpr bool is_action(int action) const noexcept; + +private: + int m_action; + int m_button; +}; + namespace config { class Boolean; @@ -33,21 +70,54 @@ void init(void); void update_late(void); } // namespace gamepad -// This simulates buttons using axes. When an axis -// value exceeds 1.5 times the deadzone, the event is -// queued with a GLFW_PRESS action, when it falls back -// below the threshold, the event is queued with GLFW_RELEASE action -struct GamepadAxisEvent final { - int action; - int axis; -}; +constexpr GamepadAxisEvent::GamepadAxisEvent(int axis, int action) : m_axis(axis), m_action(action) +{ + // empty +} -// This smears GLFW event sugar over gamepad polling -// system. Whenever it detects a state change, the event -// is queued with an appropriate action -struct GamepadButtonEvent final { - int action; - int button; -}; +constexpr int GamepadAxisEvent::axis(void) const noexcept +{ + return m_axis; +} + +constexpr int GamepadAxisEvent::action(void) const noexcept +{ + return m_action; +} + +constexpr bool GamepadAxisEvent::is_axis(int axis) const noexcept +{ + return m_axis == axis; +} + +constexpr bool GamepadAxisEvent::is_action(int action) const noexcept +{ + return m_action == action; +} + +constexpr GamepadButtonEvent::GamepadButtonEvent(int button, int action) : m_button(button), m_action(action) +{ + // empty +} + +constexpr int GamepadButtonEvent::button(void) const noexcept +{ + return m_button; +} + +constexpr int GamepadButtonEvent::action(void) const noexcept +{ + return m_action; +} + +constexpr bool GamepadButtonEvent::is_button(int button) const noexcept +{ + return m_button == button; +} + +constexpr bool GamepadButtonEvent::is_action(int action) const noexcept +{ + return m_action == action; +} #endif diff --git a/src/game/client/io/glfw.hh b/src/game/client/io/glfw.hh deleted file mode 100644 index 9da978b..0000000 --- a/src/game/client/io/glfw.hh +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause -// Copyright (c) 2025 Kirill Dmitrievich -// File: glfw.hh -// Description: GLFW events passed through EnTT's signal system - -#ifndef CLIENT_IO_GLFW_HH -#define CLIENT_IO_GLFW_HH -#pragma once - -struct GlfwCursorPosEvent final { - glm::fvec2 pos; -}; - -struct GlfwFramebufferSizeEvent final { - glm::ivec2 size; - float aspect; -}; - -struct GlfwJoystickEvent final { - int joystick_id; - int event_type; -}; - -struct GlfwKeyEvent final { - int key { GLFW_KEY_UNKNOWN }; - int scancode; - int action; - int mods; -}; - -struct GlfwMouseButtonEvent final { - int button { GLFW_KEY_UNKNOWN }; - int action; - int mods; -}; - -struct GlfwScrollEvent final { - float dx; - float dy; -}; - -#endif diff --git a/src/game/client/io/keyboard.cc b/src/game/client/io/keyboard.cc new file mode 100644 index 0000000..71db460 --- /dev/null +++ b/src/game/client/io/keyboard.cc @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright (c) 2025 Kirill Dmitrievich +// File: keyboard.cc; Created: Tue Dec 30 2025 12:27:50 +// Description: Keyboard handling + +#include "client/pch.hh" + +#include "client/io/keyboard.hh" + +#include "client/globals.hh" + +static void on_char_glfw(GLFWwindow* window, unsigned int codepoint) +{ + ImGui_ImplGlfw_CharCallback(window, codepoint); +} + +static void on_key_glfw(GLFWwindow* window, int keycode, int scancode, int action, int modbits) +{ + globals::dispatcher.trigger(KeyEvent(keycode, scancode, action, modbits)); + + ImGui_ImplGlfw_KeyCallback(window, keycode, scancode, action, modbits); +} + +void keyboard::init(void) +{ + spdlog::info("keyboard: taking over device events"); + glfwSetCharCallback(globals::window, &on_char_glfw); + glfwSetKeyCallback(globals::window, &on_key_glfw); +} diff --git a/src/game/client/io/keyboard.hh b/src/game/client/io/keyboard.hh new file mode 100644 index 0000000..566d38b --- /dev/null +++ b/src/game/client/io/keyboard.hh @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright (c) 2025 Kirill Dmitrievich +// File: keyboard.hh; Created: Tue Dec 30 2025 12:27:04 +// Description: Keyboard handling + +#ifndef CLIENT_IO_KEYBOARD_HH +#define CLIENT_IO_KEYBOARD_HH +#pragma once + +class KeyEvent final { +public: + constexpr explicit KeyEvent(int keycode, int scancode, int action, int modbits); + + constexpr int keycode(void) const noexcept; + constexpr int scancode(void) const noexcept; + constexpr int action(void) const noexcept; + constexpr int modbits(void) const noexcept; + + constexpr bool is_keycode(int keycode) const noexcept; + constexpr bool is_action(int action) const noexcept; + constexpr bool has_modbits(int modbits) const noexcept; + + constexpr bool is_valid(void) const noexcept; + +private: + int m_keycode; + int m_scancode; + int m_action; + int m_modbits; +}; + +namespace keyboard +{ +void init(void); +} // namespace keyboard + +constexpr KeyEvent::KeyEvent(int keycode, int scancode, int action, int modbits) + : m_keycode(keycode), m_scancode(scancode), m_action(action), m_modbits(modbits) +{ + // empty +} + +constexpr int KeyEvent::keycode(void) const noexcept +{ + return m_keycode; +} + +constexpr int KeyEvent::scancode(void) const noexcept +{ + return m_scancode; +} + +constexpr int KeyEvent::action(void) const noexcept +{ + return m_action; +} + +constexpr int KeyEvent::modbits(void) const noexcept +{ + return m_modbits; +} + +constexpr bool KeyEvent::is_keycode(int keycode) const noexcept +{ + return m_keycode == keycode; +} + +constexpr bool KeyEvent::is_action(int action) const noexcept +{ + return m_action == action; +} + +constexpr bool KeyEvent::has_modbits(int modbits) const noexcept +{ + return static_cast<bool>(m_modbits & modbits); +} + +constexpr bool KeyEvent::is_valid(void) const noexcept +{ + return m_keycode >= 0 && m_keycode <= GLFW_KEY_LAST; +} + +#endif diff --git a/src/game/client/io/mouse.cc b/src/game/client/io/mouse.cc new file mode 100644 index 0000000..b79365c --- /dev/null +++ b/src/game/client/io/mouse.cc @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright (c) 2025 Kirill Dmitrievich +// File: mouse.cc; Created: Tue Dec 30 2025 12:39:32 +// Description: Mouse and scroll wheel handling + +#include "client/pch.hh" + +#include "client/io/mouse.hh" + +#include "client/globals.hh" + +static void on_cursor_enter_glfw(GLFWwindow* window, int entered) +{ + ImGui_ImplGlfw_CursorEnterCallback(window, entered); +} +static void on_cursor_pos_glfw(GLFWwindow* window, double xpos, double ypos) +{ + globals::dispatcher.trigger(CursorPosEvent(xpos, ypos)); + + ImGui_ImplGlfw_CursorPosCallback(window, xpos, ypos); +} + +static void on_mouse_button_glfw(GLFWwindow* window, int button, int action, int modbits) +{ + globals::dispatcher.trigger(MouseButtonEvent(button, action, modbits)); + + ImGui_ImplGlfw_MouseButtonCallback(window, button, action, modbits); +} + +static void on_scroll_glfw(GLFWwindow* window, double xoffset, double yoffset) +{ + globals::dispatcher.trigger(ScrollEvent(xoffset, yoffset)); + + ImGui_ImplGlfw_ScrollCallback(window, xoffset, yoffset); +} + +void mouse::init(void) +{ + spdlog::info("mouse: taking over device events"); + glfwSetCursorEnterCallback(globals::window, &on_cursor_enter_glfw); + glfwSetCursorPosCallback(globals::window, &on_cursor_pos_glfw); + glfwSetMouseButtonCallback(globals::window, &on_mouse_button_glfw); + glfwSetScrollCallback(globals::window, &on_scroll_glfw); +} diff --git a/src/game/client/io/mouse.hh b/src/game/client/io/mouse.hh new file mode 100644 index 0000000..3a58dc7 --- /dev/null +++ b/src/game/client/io/mouse.hh @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright (c) 2025 Kirill Dmitrievich +// File: mouse.hh; Created: Tue Dec 30 2025 12:33:48 +// Description: Mouse and scroll wheel handling + +#ifndef CLIENT_IO_MOUSE_HH +#define CLIENT_IO_MOUSE_HH +#pragma once + +class CursorPosEvent final { +public: + constexpr explicit CursorPosEvent(double xpos, double ypos); + + constexpr float xpos(void) const noexcept; + constexpr float ypos(void) const noexcept; + constexpr const glm::fvec2& position(void) const noexcept; + +private: + glm::fvec2 m_position; +}; + +class MouseButtonEvent final { +public: + constexpr explicit MouseButtonEvent(int button, int action, int modbits); + + constexpr int button(void) const noexcept; + constexpr int action(void) const noexcept; + constexpr int modbits(void) const noexcept; + + constexpr bool is_button(int button) const noexcept; + constexpr bool is_action(int action) const noexcept; + constexpr bool has_modbits(int modbits) const noexcept; + + constexpr bool is_valid(void) const noexcept; + +private: + int m_button; + int m_action; + int m_modbits; +}; + +class ScrollEvent final { +public: + constexpr explicit ScrollEvent(double xoffset, double yoffset); + + constexpr float xoffset(void) const noexcept; + constexpr float yoffset(void) const noexcept; + constexpr const glm::fvec2& offset(void) const noexcept; + +private: + glm::fvec2 m_offset; +}; + +namespace mouse +{ +void init(void); +} // namespace mouse + +constexpr CursorPosEvent::CursorPosEvent(double xpos, double ypos) +{ + m_position.x = static_cast<float>(xpos); + m_position.y = static_cast<float>(ypos); +} + +constexpr float CursorPosEvent::xpos(void) const noexcept +{ + return m_position.x; +} + +constexpr float CursorPosEvent::ypos(void) const noexcept +{ + return m_position.y; +} + +constexpr const glm::fvec2& CursorPosEvent::position(void) const noexcept +{ + return m_position; +} + +constexpr MouseButtonEvent::MouseButtonEvent(int button, int action, int modbits) : m_button(button), m_action(action), m_modbits(modbits) +{ + // empty +} + +constexpr int MouseButtonEvent::button(void) const noexcept +{ + return m_button; +} + +constexpr int MouseButtonEvent::action(void) const noexcept +{ + return m_action; +} + +constexpr int MouseButtonEvent::modbits(void) const noexcept +{ + return m_modbits; +} + +constexpr bool MouseButtonEvent::is_button(int button) const noexcept +{ + return m_button == button; +} + +constexpr bool MouseButtonEvent::is_action(int action) const noexcept +{ + return m_action == action; +} + +constexpr bool MouseButtonEvent::has_modbits(int modbits) const noexcept +{ + return static_cast<bool>(m_modbits & modbits); +} + +constexpr bool MouseButtonEvent::is_valid(void) const noexcept +{ + return m_button >= 0 && m_button <= GLFW_MOUSE_BUTTON_LAST; +} + +constexpr ScrollEvent::ScrollEvent(double xoffset, double yoffset) +{ + m_offset.x = static_cast<float>(xoffset); + m_offset.y = static_cast<float>(yoffset); +} + +constexpr float ScrollEvent::xoffset(void) const noexcept +{ + return m_offset.x; +} + +constexpr float ScrollEvent::yoffset(void) const noexcept +{ + return m_offset.y; +} + +constexpr const glm::fvec2& ScrollEvent::offset(void) const noexcept +{ + return m_offset; +} + +#endif diff --git a/src/game/client/io/video.cc b/src/game/client/io/video.cc new file mode 100644 index 0000000..361b167 --- /dev/null +++ b/src/game/client/io/video.cc @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright (c) 2025 Kirill Dmitrievich +// File: video.cc; Created: Tue Dec 30 2025 13:00:24 +// Description: Video mode handling + +#include "client/pch.hh" + +#include "client/io/video.hh" + +#include "core/config/boolean.hh" +#include "core/config/string.hh" + +#include "core/io/cmdline.hh" +#include "core/io/config_map.hh" + +#include "core/resource/image.hh" +#include "core/resource/resource.hh" + +#include "core/version.hh" + +#include "shared/splash.hh" + +#include "client/gui/settings.hh" + +#include "client/const.hh" +#include "client/globals.hh" + +static glm::ivec2 last_windowed_size; +static config::Boolean enable_vsync(true); +static config::String current_mode("windowed"); + +static GLFWmonitor* fullscreen_monitor; +static std::vector<VideoMode> fullscreen_modes; + +static void on_glfw_error(int error, const char* description) +{ + spdlog::error("video: GLFW error [{}]: {}", error, description); +} + +static void on_glfw_framebuffer_size(GLFWwindow* window, int wide, int tall) +{ + globals::width = wide; + globals::height = tall; + globals::aspect = static_cast<float>(wide) / static_cast<float>(tall); + + if(nullptr == glfwGetWindowMonitor(window)) { + last_windowed_size.x = wide; + last_windowed_size.y = tall; + } + + globals::dispatcher.trigger(FramebufferSizeEvent(wide, tall)); +} + +static void on_glfw_window_close(GLFWwindow* window) +{ + // We don't really have a good way to + // pass "i want to quit" boolean to the main loop, + // so instead we just raise an external interrupt signal + // which handler latches an internal flag in the main loop + std::raise(SIGINT); +} + +static void on_window_focus_glfw(GLFWwindow* window, int focused) +{ + ImGui_ImplGlfw_WindowFocusCallback(window, focused); +} + +void video::init(void) +{ + last_windowed_size.x = BASE_WIDTH; + last_windowed_size.y = BASE_HEIGHT; + + globals::client_config.add_value("video.enable_vsync", enable_vsync); + globals::client_config.add_value("video.current_mode", current_mode); + +#ifdef __unix__ + // Wayland constantly throws random bullshit at me + // when I'm dealing with pretty much anything cross-platform + // on pretty much any kind of UNIX and Linux distribution + glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); +#endif + + glfwSetErrorCallback(&on_glfw_error); + + if(!glfwInit()) { + spdlog::critical("glfw: initialize failed"); + std::terminate(); + } + + unsigned int monitor_index; + auto monitor_arg = cmdline::get("monitor"); + auto monitor_check = std::from_chars(monitor_arg.data(), monitor_arg.data() + monitor_arg.size(), monitor_index); + + if(monitor_check.ec == std::errc()) { + int monitor_count; + const auto monitors = glfwGetMonitors(&monitor_count); + + if(monitor_index < static_cast<unsigned int>(monitor_count)) { + fullscreen_monitor = monitors[monitor_index]; + } + else { + // If the user wants to, say run the game on a monitor + // number 2 and there are only 2 monitors (remember, zero-based index) + // it's a good idea to silently fall back to the primary monitor + fullscreen_monitor = glfwGetPrimaryMonitor(); + } + } + else { + fullscreen_monitor = glfwGetPrimaryMonitor(); + } + + int video_mode_count; + const auto video_modes = glfwGetVideoModes(fullscreen_monitor, &video_mode_count); + + fullscreen_modes.clear(); + fullscreen_modes.reserve(video_mode_count); + + for(int i = 0; i < video_mode_count; ++i) { + auto& mode = video_modes[i]; + + // Only allow video modes that are at least as large as the base resolution + // to be used by the video subsystem, otherwise we're going to end up with + // some bizzare UI scaling issues because there is no UI scale less than 1.0 + if(mode.width >= BASE_WIDTH && mode.height >= BASE_HEIGHT) { + fullscreen_modes.push_back(VideoMode(mode)); + } + } + + // Setup GLFW window hints + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + +#if defined(__APPLE__) + // Enable forward compatibility because Apple + // just decided to be the autistic kid of the class + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + + globals::window = glfwCreateWindow(BASE_WIDTH, BASE_HEIGHT, "Client", nullptr, nullptr); + + if(globals::window == nullptr) { + spdlog::critical("glfw: window creation failed"); + std::terminate(); + } + + glfwSetWindowSizeLimits(globals::window, BASE_WIDTH, BASE_HEIGHT, GLFW_DONT_CARE, GLFW_DONT_CARE); + glfwMakeContextCurrent(globals::window); + glfwSwapInterval(1); + + glfwSetFramebufferSizeCallback(globals::window, &on_glfw_framebuffer_size); + glfwSetWindowCloseCallback(globals::window, &on_glfw_window_close); + glfwSetWindowFocusCallback(globals::window, &on_window_focus_glfw); + + if(auto image = resource::load<Image>("textures/gui/window_icon.png")) { + GLFWimage icon_image; + icon_image.width = image->size.x; + icon_image.height = image->size.y; + icon_image.pixels = reinterpret_cast<unsigned char*>(image->pixels); + glfwSetWindowIcon(globals::window, 1, &icon_image); + } + + settings::add_video_mode_select(0, settings_location::VIDEO, "video.current_mode"); + settings::add_checkbox(1, enable_vsync, settings_location::VIDEO, "video.enable_vsync", true); + + update_window_title(); +} + +void video::init_late(void) +{ + std::string mode_string(current_mode.get_value()); + + if(0 == mode_string.compare("windowed")) { + request_windowed(last_windowed_size.x, last_windowed_size.y); + return; + } + + int target_width; + int target_height; + int target_refresh_rate; + + if(3 == std::sscanf(mode_string.c_str(), "%d:%d:%d", &target_width, &target_height, &target_refresh_rate)) { + request_fullscreen(target_width, target_height, target_refresh_rate); + return; + } + + int current_width; + int current_height; + glfwGetFramebufferSize(globals::window, ¤t_width, ¤t_height); + on_glfw_framebuffer_size(globals::window, current_width, current_height); +} + +void video::shutdown(void) +{ + glfwDestroyWindow(globals::window); + glfwTerminate(); +} + +void video::update(void) +{ + glfwGetFramebufferSize(globals::window, &globals::width, &globals::height); + globals::aspect = static_cast<float>(globals::width) / static_cast<float>(globals::height); +} + +void video::update_late(void) +{ + glfwSwapInterval(enable_vsync.get_value() ? 1 : 0); +} + +void video::query_current_mode(int& wide, int& tall) noexcept +{ + glfwGetFramebufferSize(globals::window, &wide, &tall); +} + +void video::query_current_mode(int& wide, int& tall, bool& fullscreen) noexcept +{ + glfwGetFramebufferSize(globals::window, &wide, &tall); + fullscreen = static_cast<bool>(glfwGetWindowMonitor(globals::window)); +} + +const std::vector<VideoMode>& video::query_fullscreen_modes(void) noexcept +{ + return fullscreen_modes; +} + +void video::request_fullscreen(int wide, int tall, int rate) noexcept +{ + assert(wide >= BASE_WIDTH); + assert(tall >= BASE_HEIGHT); + + std::size_t best_index = 0; + auto best_score = std::numeric_limits<int>::max(); + + for(std::size_t i = 0; i < fullscreen_modes.size(); ++i) { + const auto& mode = fullscreen_modes[i]; + + auto score_width = std::abs(mode.wide() - wide); + auto score_height = std::abs(mode.tall() - tall); + auto score_refresh_rate = std::abs(mode.rate() - rate); + auto total_score = score_width + score_height + score_refresh_rate; + + if(total_score < best_score) { + best_score = total_score; + best_index = i; + } + } + + const auto& best_mode = fullscreen_modes[best_index]; + + glfwSetWindowMonitor(globals::window, fullscreen_monitor, 0, 0, best_mode.wide(), best_mode.tall(), best_mode.rate()); + glfwSetWindowAttrib(globals::window, GLFW_AUTO_ICONIFY, GLFW_FALSE); + glfwSetWindowAttrib(globals::window, GLFW_RESIZABLE, GLFW_FALSE); + + current_mode.set(std::format("{}:{}:{}", best_mode.wide(), best_mode.tall(), best_mode.rate())); + + spdlog::debug("video: set mode to: {}x{} ({} Hz)", best_mode.wide(), best_mode.tall(), best_mode.rate()); +} + +void video::request_windowed(int wide, int tall) noexcept +{ + glfwSetWindowMonitor(globals::window, nullptr, 0, 0, wide, tall, GLFW_DONT_CARE); + glfwSetWindowAttrib(globals::window, GLFW_AUTO_ICONIFY, GLFW_FALSE); + glfwSetWindowAttrib(globals::window, GLFW_RESIZABLE, GLFW_TRUE); + + int workarea_xpos, workarea_ypos; + int workarea_width, workarea_height; + glfwGetMonitorWorkarea(fullscreen_monitor, &workarea_xpos, &workarea_ypos, &workarea_width, &workarea_height); + + auto center_x = workarea_xpos + (workarea_width - wide) / 2; + auto center_y = workarea_ypos + (workarea_height - tall) / 2; + + glfwSetWindowPos(globals::window, center_x, center_y); + + current_mode.set("windowed"); + + spdlog::debug("video: set mode to: windowed {}x{}", wide, tall); +} + +void video::request_windowed(void) noexcept +{ + request_windowed(last_windowed_size.x, last_windowed_size.y); +} + +void video::update_window_title(void) +{ + auto title = std::format("Voxelius {}: {}", version::triplet, splash::get()); + glfwSetWindowTitle(globals::window, title.c_str()); +} diff --git a/src/game/client/io/video.hh b/src/game/client/io/video.hh new file mode 100644 index 0000000..858983f --- /dev/null +++ b/src/game/client/io/video.hh @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright (c) 2025 Kirill Dmitrievich +// File: video.hh; Created: Tue Dec 30 2025 12:59:09 +// Description: Video mode handling + +#ifndef CLIENT_IO_VIDEO_HH +#define CLIENT_IO_VIDEO_HH +#pragma once + +class VideoMode final { +public: + constexpr explicit VideoMode(const GLFWvidmode& mode) noexcept; + constexpr explicit VideoMode(int wide, int tall, int rate = GLFW_DONT_CARE) noexcept; + + constexpr int wide(void) const noexcept; + constexpr int tall(void) const noexcept; + constexpr int rate(void) const noexcept; + + constexpr bool operator==(const VideoMode& other) const noexcept; + +private: + int m_wide; + int m_tall; + int m_rate; +}; + +class FramebufferSizeEvent final { +public: + constexpr explicit FramebufferSizeEvent(int wide, int tall) noexcept; + constexpr int wide(void) const noexcept; + constexpr int tall(void) const noexcept; + +private: + int m_wide; + int m_tall; +}; + +namespace video +{ +void init(void); +void init_late(void); +void shutdown(void); +void update(void); +void update_late(void); +} // namespace video + +namespace video +{ +void query_current_mode(int& wide, int& tall) noexcept; +void query_current_mode(int& wide, int& tall, bool& fullscreen) noexcept; +const std::vector<VideoMode>& query_fullscreen_modes(void) noexcept; +} // namespace video + +namespace video +{ +void request_fullscreen(int wide, int tall, int rate) noexcept; +void request_windowed(int wide, int tall) noexcept; +void request_windowed(void) noexcept; +} // namespace video + +namespace video +{ +void update_window_title(void); +} // namespace video + +constexpr VideoMode::VideoMode(int wide, int tall, int rate) noexcept : m_wide(wide), m_tall(tall), m_rate(rate) +{ + // empty +} + +constexpr VideoMode::VideoMode(const GLFWvidmode& mode) noexcept : m_wide(mode.width), m_tall(mode.height), m_rate(mode.refreshRate) +{ + // empty +} + +constexpr int VideoMode::wide(void) const noexcept +{ + return m_wide; +} + +constexpr int VideoMode::tall(void) const noexcept +{ + return m_tall; +} + +constexpr int VideoMode::rate(void) const noexcept +{ + return m_rate; +} + +constexpr bool VideoMode::operator==(const VideoMode& other) const noexcept +{ + auto result = true; + result = result && (m_wide == other.m_wide || m_wide == GLFW_DONT_CARE || other.m_wide == GLFW_DONT_CARE); + result = result && (m_tall == other.m_tall || m_tall == GLFW_DONT_CARE || other.m_tall == GLFW_DONT_CARE); + result = result && (m_rate == other.m_rate || m_rate == GLFW_DONT_CARE || other.m_rate == GLFW_DONT_CARE); + return result; +} + +constexpr FramebufferSizeEvent::FramebufferSizeEvent(int wide, int tall) noexcept : m_wide(wide), m_tall(tall) +{ + // empty +} + +constexpr int FramebufferSizeEvent::wide(void) const noexcept +{ + return m_wide; +} + +constexpr int FramebufferSizeEvent::tall(void) const noexcept +{ + return m_tall; +} + +#endif diff --git a/src/game/client/main.cc b/src/game/client/main.cc index ab45db5..b58d840 100644 --- a/src/game/client/main.cc +++ b/src/game/client/main.cc @@ -19,9 +19,10 @@ #include "shared/game.hh" #include "shared/splash.hh" -#include "client/gui/window_title.hh" - -#include "client/io/glfw.hh" +#include "client/io/gamepad.hh" +#include "client/io/keyboard.hh" +#include "client/io/mouse.hh" +#include "client/io/video.hh" #include "client/resource/sound_effect.hh" #include "client/resource/texture_gui.hh" @@ -35,101 +36,7 @@ extern "C" __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; extern "C" __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #endif -static void on_glfw_error(int code, const char* message) -{ - spdlog::error("glfw: {}", message); -} - -static void on_glfw_char(GLFWwindow* window, unsigned int codepoint) -{ - ImGui_ImplGlfw_CharCallback(window, codepoint); -} - -static void on_glfw_cursor_enter(GLFWwindow* window, int entered) -{ - ImGui_ImplGlfw_CursorEnterCallback(window, entered); -} - -static void on_glfw_cursor_pos(GLFWwindow* window, double xpos, double ypos) -{ - GlfwCursorPosEvent event; - event.pos.x = static_cast<float>(xpos); - event.pos.y = static_cast<float>(ypos); - globals::dispatcher.trigger(event); - - ImGui_ImplGlfw_CursorPosCallback(window, xpos, ypos); -} - -static void on_glfw_framebuffer_size(GLFWwindow* window, int width, int height) -{ - if(glfwGetWindowAttrib(window, GLFW_ICONIFIED)) { - // Don't do anything if the window was just - // iconified (minimized); as it turns out minimized - // windows on WIN32 seem to be forced into 0x0 - return; - } - - globals::width = width; - globals::height = height; - globals::aspect = static_cast<float>(width) / static_cast<float>(height); - - GlfwFramebufferSizeEvent fb_event; - fb_event.size.x = globals::width; - fb_event.size.y = globals::height; - fb_event.aspect = globals::aspect; - globals::dispatcher.trigger(fb_event); -} - -static void on_glfw_key(GLFWwindow* window, int key, int scancode, int action, int mods) -{ - GlfwKeyEvent event; - event.key = key; - event.scancode = scancode; - event.action = action; - event.mods = mods; - globals::dispatcher.trigger(event); - - ImGui_ImplGlfw_KeyCallback(window, key, scancode, action, mods); -} - -static void on_glfw_joystick(int joystick_id, int event_type) -{ - GlfwJoystickEvent event; - event.joystick_id = joystick_id; - event.event_type = event_type; - globals::dispatcher.trigger(event); -} - -static void on_glfw_monitor_event(GLFWmonitor* monitor, int event) -{ - ImGui_ImplGlfw_MonitorCallback(monitor, event); -} - -static void on_glfw_mouse_button(GLFWwindow* window, int button, int action, int mods) -{ - GlfwMouseButtonEvent event; - event.button = button; - event.action = action; - event.mods = mods; - globals::dispatcher.trigger(event); - - ImGui_ImplGlfw_MouseButtonCallback(window, button, action, mods); -} - -static void on_glfw_scroll(GLFWwindow* window, double dx, double dy) -{ - GlfwScrollEvent event; - event.dx = static_cast<float>(dx); - event.dy = static_cast<float>(dy); - globals::dispatcher.trigger(event); - - ImGui_ImplGlfw_ScrollCallback(window, dx, dy); -} - -static void on_glfw_window_focus(GLFWwindow* window, int focused) -{ - ImGui_ImplGlfw_WindowFocusCallback(window, focused); -} +std::atomic_bool is_running; static void GLAD_API_PTR on_opengl_message(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* param) @@ -140,7 +47,8 @@ static void GLAD_API_PTR on_opengl_message(GLenum source, GLenum type, GLuint id static void on_termination_signal(int) { spdlog::warn("client: received termination signal"); - glfwSetWindowShouldClose(globals::window, true); + + is_running = false; } int main(int argc, char** argv) @@ -167,32 +75,11 @@ int main(int argc, char** argv) spdlog::info("Voxelius Client {}", version::full); - glfwSetErrorCallback(&on_glfw_error); - -#if defined(__unix__) - // Wayland constantly throws random bullshit at me - // when I'm dealing with pretty much anything cross-platform - // on pretty much any kind of UNIX and Linux distribution - glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); -#endif - - if(!glfwInit()) { - spdlog::critical("glfw: init failed"); - std::terminate(); - } - - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_SAMPLES, 0); + video::init(); - globals::window = glfwCreateWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, "Client", nullptr, nullptr); - - if(!globals::window) { - spdlog::critical("glfw: failed to open a window"); - std::terminate(); - } + keyboard::init(); + mouse::init(); + gamepad::init(); std::signal(SIGINT, &on_termination_signal); std::signal(SIGTERM, &on_termination_signal); @@ -241,31 +128,6 @@ int main(int argc, char** argv) ImGui_ImplGlfw_InitForOpenGL(globals::window, false); ImGui_ImplOpenGL3_Init(nullptr); - // The UI is scaled against a resolution defined by BASE_WIDTH and BASE_HEIGHT - // constants. However, UI scale of 1 doesn't look that good, so the window size is - // limited to a resolution that allows at least UI scale of 2 and is defined by MIN_WIDTH and MIN_HEIGHT. - glfwSetWindowSizeLimits(globals::window, MIN_WIDTH, MIN_HEIGHT, GLFW_DONT_CARE, GLFW_DONT_CARE); - - glfwSetCharCallback(globals::window, &on_glfw_char); - glfwSetCursorEnterCallback(globals::window, &on_glfw_cursor_enter); - glfwSetCursorPosCallback(globals::window, &on_glfw_cursor_pos); - glfwSetFramebufferSizeCallback(globals::window, &on_glfw_framebuffer_size); - glfwSetKeyCallback(globals::window, &on_glfw_key); - glfwSetMouseButtonCallback(globals::window, &on_glfw_mouse_button); - glfwSetScrollCallback(globals::window, &on_glfw_scroll); - glfwSetWindowFocusCallback(globals::window, &on_glfw_window_focus); - - glfwSetJoystickCallback(&on_glfw_joystick); - glfwSetMonitorCallback(&on_glfw_monitor_event); - - if(auto image = resource::load<Image>("textures/gui/window_icon.png")) { - GLFWimage icon_image; - icon_image.width = image->size.x; - icon_image.height = image->size.y; - icon_image.pixels = reinterpret_cast<unsigned char*>(image->pixels); - glfwSetWindowIcon(globals::window, 1, &icon_image); - } - if(cmdline::contains("nosound")) { spdlog::warn("client: sound disabled [per command line]"); globals::sound_dev = nullptr; @@ -303,7 +165,7 @@ int main(int argc, char** argv) splash::init_client(); - window_title::update(); + video::update_window_title(); ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; @@ -334,15 +196,13 @@ int main(int argc, char** argv) client_game::init(); - int wwidth, wheight; - glfwGetFramebufferSize(globals::window, &wwidth, &wheight); - on_glfw_framebuffer_size(globals::window, wwidth, wheight); - threading::init(); globals::client_config.load_file("client.conf"); globals::client_config.load_cmdline(); + video::init_late(); + client_game::init_late(); auto last_curtime = globals::curtime; @@ -370,6 +230,8 @@ int main(int argc, char** argv) last_curtime = globals::curtime; + video::update(); + for(std::uint64_t i = 0; i < globals::fixed_framecount; ++i) client_game::fixed_update(); client_game::update(); @@ -403,6 +265,10 @@ int main(int argc, char** argv) glfwSwapBuffers(globals::window); + video::update_late(); + + gamepad::update_late(); + for(std::uint64_t i = 0; i < globals::fixed_framecount; ++i) client_game::fixed_update_late(); client_game::update_late(); @@ -441,8 +307,7 @@ int main(int argc, char** argv) alcCloseDevice(globals::sound_dev); } - glfwDestroyWindow(globals::window); - glfwTerminate(); + video::shutdown(); globals::client_config.save_file("client.conf"); |
