diff --git a/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.cpp b/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.cpp index ea2ed8c5..1e56b3e0 100644 --- a/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.cpp +++ b/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.cpp @@ -231,6 +231,7 @@ struct SdlWindowPresenter::Impl int window_width = 0; int window_height = 0; int presented_frames = 0; + cardputer_zero::platform::SurfacePresenter::PointerState pointer{}; bool startup_inputs_queued = false; bool startup_shortcut_queued = false; std::vector staging{}; @@ -329,6 +330,28 @@ bool SdlWindowPresenter::pump() handleTextInput(impl_->input_queue, event.text); continue; } + if (event.type == SDL_EVENT_MOUSE_MOTION) + { + impl_->pointer.x = static_cast(event.motion.x); + impl_->pointer.y = static_cast(event.motion.y); + continue; + } + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && + event.button.button == SDL_BUTTON_LEFT) + { + impl_->pointer.x = static_cast(event.button.x); + impl_->pointer.y = static_cast(event.button.y); + impl_->pointer.pressed = true; + continue; + } + if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && + event.button.button == SDL_BUTTON_LEFT) + { + impl_->pointer.x = static_cast(event.button.x); + impl_->pointer.y = static_cast(event.button.y); + impl_->pointer.pressed = false; + continue; + } if (event.type == SDL_EVENT_WINDOW_RESIZED) { impl_->window_width = event.window.data1; @@ -346,6 +369,17 @@ std::vector SdlWindowPresenter::drainInput() return drained; } +bool SdlWindowPresenter::supportsPointer() const noexcept +{ + return true; +} + +cardputer_zero::platform::SurfacePresenter::PointerState +SdlWindowPresenter::pointerState() const noexcept +{ + return impl_->pointer; +} + void SdlWindowPresenter::present(const core::Canvas& canvas) { if (canvas.width() != impl_->texture_width || diff --git a/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.h b/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.h index 1c7e3d9b..c038344b 100644 --- a/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.h +++ b/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.h @@ -30,6 +30,8 @@ class SdlWindowPresenter : public cardputer_zero::platform::SurfacePresenter [[nodiscard]] bool pump() override; [[nodiscard]] std::vector drainInput() override; + [[nodiscard]] bool supportsPointer() const noexcept override; + [[nodiscard]] PointerState pointerState() const noexcept override; void present(const cardputer_zero::core::Canvas& canvas) override; private: diff --git a/platform/linux/common/src/platform/surface_presenter.h b/platform/linux/common/src/platform/surface_presenter.h index 79736dfe..260b27fe 100644 --- a/platform/linux/common/src/platform/surface_presenter.h +++ b/platform/linux/common/src/platform/surface_presenter.h @@ -11,10 +11,25 @@ namespace trailmate::cardputer_zero::platform class SurfacePresenter { public: + struct PointerState + { + int x = 0; + int y = 0; + bool pressed = false; + }; + virtual ~SurfacePresenter() = default; [[nodiscard]] virtual bool pump() = 0; [[nodiscard]] virtual std::vector drainInput() = 0; + [[nodiscard]] virtual bool supportsPointer() const noexcept + { + return false; + } + [[nodiscard]] virtual PointerState pointerState() const noexcept + { + return {}; + } virtual void present(const core::Canvas& canvas) = 0; }; diff --git a/platform/linux/uconsole/src/uconsole_desktop_shell.cpp b/platform/linux/uconsole/src/uconsole_desktop_shell.cpp index 06d65e4c..29471b69 100644 --- a/platform/linux/uconsole/src/uconsole_desktop_shell.cpp +++ b/platform/linux/uconsole/src/uconsole_desktop_shell.cpp @@ -2409,8 +2409,12 @@ class UConsoleDesktopShell class UConsoleLvglHost { public: - UConsoleLvglHost(UConsoleDesktopShell& shell, UConsoleShellOptions options) + UConsoleLvglHost(UConsoleDesktopShell& shell, + ::trailmate::cardputer_zero::platform::SurfacePresenter& + presenter, + UConsoleShellOptions options) : shell_(shell), + presenter_(presenter), options_(validateOptions(options)), canvas_(options_.width, options_.height), frame_buffer_(static_cast(options_.width) * @@ -2457,6 +2461,20 @@ class UConsoleLvglHost lv_indev_set_group(keypad_, shell_.inputGroup()); } + if (presenter_.supportsPointer()) + { + pointer_ = lv_indev_create(); + if (pointer_ == nullptr) + { + throw std::runtime_error( + "Failed to create pointer input for uConsole shell."); + } + lv_indev_set_type(pointer_, LV_INDEV_TYPE_POINTER); + lv_indev_set_display(pointer_, display_); + lv_indev_set_user_data(pointer_, this); + lv_indev_set_read_cb(pointer_, readPointerCb); + } + tick(); } @@ -2467,6 +2485,11 @@ class UConsoleLvglHost lv_indev_delete(keypad_); keypad_ = nullptr; } + if (pointer_ != nullptr) + { + lv_indev_delete(pointer_); + pointer_ = nullptr; + } shell_.releaseLvglObjects(); if (display_ != nullptr) { @@ -2520,6 +2543,19 @@ class UConsoleLvglHost data->continue_reading = host->shell_.hasPendingKeyEvent(); } + static void readPointerCb(lv_indev_t* indev, lv_indev_data_t* data) + { + auto* host = + static_cast(lv_indev_get_user_data(indev)); + if (host == nullptr || data == nullptr) return; + + const auto pointer = host->presenter_.pointerState(); + data->point.x = pointer.x; + data->point.y = pointer.y; + data->state = pointer.pressed ? LV_INDEV_STATE_PRESSED + : LV_INDEV_STATE_RELEASED; + } + private: void copyFrameBufferToCanvas() { @@ -2535,9 +2571,11 @@ class UConsoleLvglHost } UConsoleDesktopShell& shell_; + ::trailmate::cardputer_zero::platform::SurfacePresenter& presenter_; UConsoleShellOptions options_{}; lv_display_t* display_ = nullptr; lv_indev_t* keypad_ = nullptr; + lv_indev_t* pointer_ = nullptr; Canvas canvas_; std::vector frame_buffer_{}; bool dirty_ = true; @@ -2550,7 +2588,7 @@ void runUConsoleShell(::trailmate::cardputer_zero::platform::SurfacePresenter& p { options = validateOptions(options); UConsoleDesktopShell shell; - UConsoleLvglHost host{shell, options}; + UConsoleLvglHost host{shell, presenter, options}; auto next_frame = clock::now(); const auto frame_time = std::chrono::milliseconds(options.frame_time_ms);