// Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include "common/settings.h" #include "core/frontend/emu_window.h" #include "core/frontend/input.h" namespace Core::Frontend { GraphicsContext::~GraphicsContext() = default; class EmuWindow::TouchState : public Input::Factory, public std::enable_shared_from_this { public: std::unique_ptr Create(const Common::ParamPackage&) override { return std::make_unique(shared_from_this()); } std::mutex mutex; Input::TouchStatus status; private: class Device : public Input::TouchDevice { public: explicit Device(std::weak_ptr&& touch_state) : touch_state(touch_state) {} Input::TouchStatus GetStatus() const override { if (auto state = touch_state.lock()) { std::lock_guard guard{state->mutex}; return state->status; } return {}; } private: std::weak_ptr touch_state; }; }; EmuWindow::EmuWindow() { // TODO: Find a better place to set this. config.min_client_area_size = std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height); active_config = config; touch_state = std::make_shared(); Input::RegisterFactory("emu_window", touch_state); } EmuWindow::~EmuWindow() { Input::UnregisterFactory("emu_window"); } /** * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout * @param layout FramebufferLayout object describing the framebuffer size and screen positions * @param framebuffer_x Framebuffer x-coordinate to check * @param framebuffer_y Framebuffer y-coordinate to check * @return True if the coordinates are within the touchpad, otherwise false */ static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigned framebuffer_x, unsigned framebuffer_y) { return (framebuffer_y >= layout.screen.top && framebuffer_y < layout.screen.bottom && framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right); } std::tuple EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) const { new_x = std::max(new_x, framebuffer_layout.screen.left); new_x = std::min(new_x, framebuffer_layout.screen.right - 1); new_y = std::max(new_y, framebuffer_layout.screen.top); new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1); return std::make_tuple(new_x, new_y); } void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) { if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) { return; } if (id >= touch_state->status.size()) { return; } std::lock_guard guard{touch_state->mutex}; const float x = static_cast(framebuffer_x - framebuffer_layout.screen.left) / static_cast(framebuffer_layout.screen.right - framebuffer_layout.screen.left); const float y = static_cast(framebuffer_y - framebuffer_layout.screen.top) / static_cast(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); touch_state->status[id] = std::make_tuple(x, y, true); } void EmuWindow::TouchReleased(std::size_t id) { if (id >= touch_state->status.size()) { return; } std::lock_guard guard{touch_state->mutex}; touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false); } void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) { if (id >= touch_state->status.size()) { return; } if (!std::get<2>(touch_state->status[id])) return; if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); TouchPressed(framebuffer_x, framebuffer_y, id); } void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { NotifyFramebufferLayoutChanged(Layout::DefaultFrameLayout(width, height)); } } // namespace Core::Frontend