diff --git a/src/engine/app/input-manager.hpp b/src/engine/app/input-manager.hpp index acb38ca6..123a1816 100644 --- a/src/engine/app/input-manager.hpp +++ b/src/engine/app/input-manager.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace app { @@ -21,17 +22,13 @@ namespace app { class input_manager { public: - /** - * Allocates and returns an input manager. - */ + /** Allocates and returns an input manager. */ static std::unique_ptr instance(); - /// Destructs an input manager. + /** Destructs an input manager. */ virtual ~input_manager() = default; - /** - * Processes input events. - */ + /** Processes input events. */ virtual void update() = 0; /** @@ -49,9 +46,17 @@ class input_manager virtual void set_relative_mouse_mode(bool enabled) = 0; /** - * Returns the event dispatcher associated with registered input devices. + * Sets the clipboard text. + * + * @param text UTF-8 text. */ + virtual void set_clipboard_text(const std::string& text) = 0; + + /** Returns UTF-8 text from the clipboard. */ + [[nodiscard]] virtual std::string get_clipboard_text() const = 0; + /// @{ + /** Returns the event dispatcher associated with registered input devices. */ [[nodiscard]] inline const ::event::dispatcher& get_event_dispatcher() const noexcept { return m_event_dispatcher; @@ -81,24 +86,24 @@ class input_manager } protected: + /// @{ /** * Registers an input device. * * @param device Input device to register. */ - /// @{ void register_device(input::device& device); void register_gamepad(input::gamepad& device); void register_keyboard(input::keyboard& device); void register_mouse(input::mouse& device); /// @} + /// @{ /** * Unregisters an input device. * * @param device Input device to unregister. */ - /// @{ void unregister_device(input::device& device); void unregister_gamepad(input::gamepad& device); void unregister_keyboard(input::keyboard& device); diff --git a/src/engine/app/sdl/sdl-input-manager.cpp b/src/engine/app/sdl/sdl-input-manager.cpp index f388dee5..351d4a0b 100644 --- a/src/engine/app/sdl/sdl-input-manager.cpp +++ b/src/engine/app/sdl/sdl-input-manager.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -72,7 +73,7 @@ void sdl_input_manager::update() { case SDL_QUIT: debug::log_debug("Application quit requested"); - this->m_event_dispatcher.dispatch({}); + m_event_dispatcher.dispatch({}); break; default: @@ -85,7 +86,7 @@ void sdl_input_manager::update() { // Get next display or window event SDL_Event event; - const int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_KEYDOWN, SDL_LASTEVENT); + const int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_KEYDOWN, SDL_CLIPBOARDUPDATE); if (!status) { @@ -293,13 +294,19 @@ void sdl_input_manager::update() break; } + [[unlikely]] case SDL_CLIPBOARDUPDATE: + { + m_event_dispatcher.dispatch({}); + break; + } + default: break; } } // Dispatch input update event - this->m_event_dispatcher.dispatch({}); + m_event_dispatcher.dispatch({}); } void sdl_input_manager::set_cursor_visible(bool visible) @@ -320,4 +327,27 @@ void sdl_input_manager::set_relative_mouse_mode(bool enabled) } } +void sdl_input_manager::set_clipboard_text(const std::string& text) +{ + if (SDL_SetClipboardText(text.c_str()) != 0) + { + debug::log_error("Failed to set clipboard text: \"{}\"", SDL_GetError()); + SDL_ClearError(); + } +} + +std::string sdl_input_manager::get_clipboard_text() const +{ + // Get SDL-allocated text from clipboard + auto sdl_clipboard_text = SDL_GetClipboardText(); + + // Copy text into string + std::string clipboard_text = sdl_clipboard_text; + + // Free SDL-allocated text + SDL_free(sdl_clipboard_text); + + return clipboard_text; +} + } // namespace app diff --git a/src/engine/app/sdl/sdl-input-manager.hpp b/src/engine/app/sdl/sdl-input-manager.hpp index fdbc6c10..df78f7d0 100644 --- a/src/engine/app/sdl/sdl-input-manager.hpp +++ b/src/engine/app/sdl/sdl-input-manager.hpp @@ -17,19 +17,17 @@ class sdl_window; class sdl_input_manager: public input_manager { public: - /** - * Constructs an SDL input manager. - */ + /** Constructs an SDL input manager. */ sdl_input_manager(); - /** - * Destructs an SDL input manager. - */ + /** Destructs an SDL input manager. */ ~sdl_input_manager() override; void update() override; void set_cursor_visible(bool visible) override; void set_relative_mouse_mode(bool enabled) override; + void set_clipboard_text(const std::string& text) override; + [[nodiscard]] std::string get_clipboard_text() const override; private: input::keyboard m_keyboard; diff --git a/src/engine/app/sdl/sdl-window-manager.cpp b/src/engine/app/sdl/sdl-window-manager.cpp index 430ab068..b519ce39 100644 --- a/src/engine/app/sdl/sdl-window-manager.cpp +++ b/src/engine/app/sdl/sdl-window-manager.cpp @@ -21,6 +21,13 @@ sdl_window_manager::sdl_window_manager() } debug::log_trace("Initialized SDL events and video subsystems"); + // Disable unused events + SDL_EventState(SDL_AUDIODEVICEADDED, SDL_IGNORE); + SDL_EventState(SDL_AUDIODEVICEREMOVED, SDL_IGNORE); + SDL_EventState(SDL_RENDER_TARGETS_RESET, SDL_IGNORE); + SDL_EventState(SDL_RENDER_DEVICE_RESET, SDL_IGNORE); + SDL_EventState(SDL_USEREVENT, SDL_IGNORE); + // Query displays const int display_count = SDL_GetNumVideoDisplays(); if (display_count < 1) @@ -117,7 +124,7 @@ void sdl_window_manager::update() for (;;) { - // Get next window or display event + // Get next window or displayp event SDL_Event event; int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_DISPLAYEVENT, SDL_SYSWMEVENT); @@ -387,6 +394,79 @@ void sdl_window_manager::update() } } } + + for (;;) + { + // Get next drop event + SDL_Event event; + int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_DROPFILE, SDL_DROPCOMPLETE); + + if (!status) + { + break; + } + else if (status < 0) + { + debug::log_error("Failed to peep SDL events: {}", SDL_GetError()); + throw std::runtime_error("Failed to peep SDL events"); + } + + switch (event.type) + { + case SDL_DROPFILE: + { + // Get window + auto window = get_window(SDL_GetWindowFromID(event.drop.windowID)); + + // Publish drop file event + window->m_drop_file_publisher.publish({window, std::filesystem::path(event.drop.file)}); + + // Free file path + SDL_free(event.drop.file); + + break; + } + + case SDL_DROPTEXT: + { + // Get window + auto window = get_window(SDL_GetWindowFromID(event.drop.windowID)); + + // Publish drop text event + window->m_drop_text_publisher.publish({window, std::string(event.drop.file)}); + + // Free text + SDL_free(event.drop.file); + + break; + } + + case SDL_DROPBEGIN: + { + // Get window + auto window = get_window(SDL_GetWindowFromID(event.drop.windowID)); + + // Publish drop begin event + window->m_drop_begin_publisher.publish({window}); + + break; + } + + case SDL_DROPCOMPLETE: + { + // Get window + auto window = get_window(SDL_GetWindowFromID(event.drop.windowID)); + + // Publish drop end event + window->m_drop_end_publisher.publish({window}); + + break; + } + + default: + break; + } + } } sdl_window* sdl_window_manager::get_window(SDL_Window* internal_window) diff --git a/src/engine/app/window-events.hpp b/src/engine/app/window-events.hpp index fe18499b..03303582 100644 --- a/src/engine/app/window-events.hpp +++ b/src/engine/app/window-events.hpp @@ -5,6 +5,8 @@ #define ANTKEEPER_APP_WINDOW_EVENTS_HPP #include +#include +#include namespace app { @@ -82,6 +84,48 @@ struct window_resized_event math::ivec2 size{0, 0}; }; +/** + * Event generated when data is about to be dropped onto a window. + */ +struct window_drop_begin_event +{ + /// Pointer to the window onto which data has been dropped. + window* window{nullptr}; +}; + +/** + * Event generated when data has finished dropping onto a window. + */ +struct window_drop_end_event +{ + /// Pointer to the window onto which data has been dropped. + window* window{nullptr}; +}; + +/** + * Event generated when a file is dropped onto a window. + */ +struct window_drop_file_event +{ + /// Pointer to the window onto which a file has been dropped. + window* window{nullptr}; + + /// Path to the dropped file. + std::filesystem::path file_path; +}; + +/** + * Event generated when text is dropped onto a window. + */ +struct window_drop_text_event +{ + /// Pointer to the window onto which text has been dropped. + window* window{nullptr}; + + /// String containing the dropped text. + std::string text; +}; + } // namespace app #endif // ANTKEEPER_APP_WINDOW_EVENTS_HPP diff --git a/src/engine/app/window.hpp b/src/engine/app/window.hpp index d1a84a52..8cd956df 100644 --- a/src/engine/app/window.hpp +++ b/src/engine/app/window.hpp @@ -164,47 +164,71 @@ class window /// @} /// Returns the channel through which window closed events are published. - [[nodiscard]] inline event::channel& get_closed_channel() noexcept + [[nodiscard]] inline auto& get_closed_channel() noexcept { return m_closed_publisher.channel(); } /// Returns the channel through which window focus changed events are published. - [[nodiscard]] inline event::channel& get_focus_changed_channel() noexcept + [[nodiscard]] inline auto& get_focus_changed_channel() noexcept { return m_focus_changed_publisher.channel(); } /// Returns the channel through which window maximized events are published. - [[nodiscard]] inline event::channel& get_maximized_channel() noexcept + [[nodiscard]] inline auto& get_maximized_channel() noexcept { return m_maximized_publisher.channel(); } /// Returns the channel through which window minimized events are published. - [[nodiscard]] inline event::channel& get_minimized_channel() noexcept + [[nodiscard]] inline auto& get_minimized_channel() noexcept { return m_minimized_publisher.channel(); } /// Returns the channel through which window moved events are published. - [[nodiscard]] inline event::channel& get_moved_channel() noexcept + [[nodiscard]] inline auto& get_moved_channel() noexcept { return m_moved_publisher.channel(); } /// Returns the channel through which window resized events are published. - [[nodiscard]] inline event::channel& get_resized_channel() noexcept + [[nodiscard]] inline auto& get_resized_channel() noexcept { return m_resized_publisher.channel(); } /// Returns the channel through which window restored events are published. - [[nodiscard]] inline event::channel& get_restored_channel() noexcept + [[nodiscard]] inline auto& get_restored_channel() noexcept { return m_restored_publisher.channel(); } + /// Returns the channel through which window drop begin events are published. + [[nodiscard]] inline auto& get_drop_begin_channel() noexcept + { + return m_drop_begin_publisher.channel(); + } + + /// Returns the channel through which window drop end events are published. + [[nodiscard]] inline auto& get_drop_end_channel() noexcept + { + return m_drop_end_publisher.channel(); + } + + /// Returns the channel through which window drop file events are published. + [[nodiscard]] inline auto& get_drop_file_channel() noexcept + { + return m_drop_file_publisher.channel(); + } + + /// Returns the channel through which window drop text events are published. + [[nodiscard]] inline auto& get_drop_text_channel() noexcept + { + return m_drop_text_publisher.channel(); + } + protected: friend class window_manager; @@ -227,6 +251,10 @@ class window event::publisher m_moved_publisher; event::publisher m_resized_publisher; event::publisher m_restored_publisher; + event::publisher m_drop_begin_publisher; + event::publisher m_drop_end_publisher; + event::publisher m_drop_file_publisher; + event::publisher m_drop_text_publisher; }; } // namespace app diff --git a/src/engine/input/clipboard-events.hpp b/src/engine/input/clipboard-events.hpp new file mode 100644 index 00000000..619ddb61 --- /dev/null +++ b/src/engine/input/clipboard-events.hpp @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 C. J. Howard +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef ANTKEEPER_INPUT_CLIPBOARD_EVENTS_HPP +#define ANTKEEPER_INPUT_CLIPBOARD_EVENTS_HPP + +namespace input { + +/** + * Event generated when the clipboard has been updated. + */ +struct clipboard_updated_event {}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_CLIPBOARD_EVENTS_HPP diff --git a/src/game/states/main-menu-state.cpp b/src/game/states/main-menu-state.cpp index 9a586118..c0d22db5 100644 --- a/src/game/states/main-menu-state.cpp +++ b/src/game/states/main-menu-state.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -275,15 +276,6 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in): ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); debug::log_trace("Entered main menu state"); - - using namespace math; - ivec3 a{0, 1, 2}; - ivec3 b{0, 1, 2}; - - debug::log_debug("a < b: {}", static_cast(a < b)); - debug::log_debug("a > b: {}", static_cast(a > b)); - debug::log_debug("a == b: {}", static_cast(a == b)); - debug::log_debug("a != b: {}", static_cast(a != b)); } main_menu_state::~main_menu_state() diff --git a/src/game/states/main-menu-state.hpp b/src/game/states/main-menu-state.hpp index 2d27cc2e..5d694d2d 100644 --- a/src/game/states/main-menu-state.hpp +++ b/src/game/states/main-menu-state.hpp @@ -31,6 +31,7 @@ class main_menu_state: public game_state animation title_fade_animation; std::shared_ptr window_resized_subscription; + std::vector> drop_subscriptions; }; #endif // ANTKEEPER_MAIN_MENU_STATE_HPP