diff --git a/src/citron/controller_overlay.cpp b/src/citron/controller_overlay.cpp index c2066f43e..52ccb9af7 100644 --- a/src/citron/controller_overlay.cpp +++ b/src/citron/controller_overlay.cpp @@ -7,12 +7,13 @@ #include "core/core.h" #include "hid_core/hid_core.h" +#include #include #include #include #include #include -#include // Required for Wayland dragging +#include #include namespace { @@ -26,27 +27,40 @@ Core::HID::EmulatedController* GetPlayer1Controller(Core::System* system) { } return hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); } + +// Helper to detect Gamescope at runtime +bool IsGamescope() { + static bool gamescope = qgetenv("XDG_CURRENT_DESKTOP") == "gamescope" || + !qgetenv("GAMESCOPE_WIDTH").isEmpty(); + return gamescope; +} } ControllerOverlay::ControllerOverlay(GMainWindow* parent) - : QWidget(parent, Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint), - main_window(parent) { + : QWidget(parent), main_window(parent) { + + // Gamescope requires ToolTip to stay visible over the game surface, + // but Desktop Wayland/Windows needs Tool to behave correctly in the taskbar/stack. + if (IsGamescope()) { + setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus); + setAttribute(Qt::WA_ShowWithoutActivating); + setMinimumSize(112, 87); // Use the smaller Gamescope-optimized scale + } else { + setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); + setMinimumSize(225, 175); // Desktop standard scale + } setAttribute(Qt::WA_TranslucentBackground); + setAttribute(Qt::WA_NoSystemBackground); auto* layout = new QGridLayout(this); setLayout(layout); - // Set margins to 0 so the controller can go right to the edge of the resizable window layout->setContentsMargins(0, 0, 0, 0); - // Create the widget that draws the controller and make it transparent + // Create the widget that draws the controller controller_widget = new PlayerControlPreview(this); controller_widget->setAttribute(Qt::WA_TranslucentBackground); - - // Disable the raw joystick (deadzone) visualization controller_widget->SetRawJoystickVisible(false); - - // Allow the widget to expand and shrink with the window controller_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); layout->addWidget(controller_widget, 0, 0); @@ -54,18 +68,42 @@ ControllerOverlay::ControllerOverlay(GMainWindow* parent) size_grip = new QSizeGrip(this); layout->addWidget(size_grip, 0, 0, Qt::AlignBottom | Qt::AlignRight); - // Start the timer for continuous updates + // Timer for updates connect(&update_timer, &QTimer::timeout, this, &ControllerOverlay::UpdateControllerState); update_timer.start(16); // ~60 FPS - // Set a minimum size and a default starting size - setMinimumSize(225, 175); - resize(450, 350); + // Initial Resize + if (IsGamescope()) { + resize(225, 175); + } else { + resize(450, 350); + } } ControllerOverlay::~ControllerOverlay() = default; void ControllerOverlay::UpdateControllerState() { + if (!main_window) return; + + if (IsGamescope()) { + bool sub_window_visible = false; + for (QWidget* w : QApplication::topLevelWidgets()) { + if (w->isWindow() && w->isVisible() && w != main_window && w != this && + !w->inherits("GRenderWindow") && !w->inherits("PerformanceOverlay") && !w->inherits("VramOverlay")) { + sub_window_visible = true; + break; + } + } + if (sub_window_visible) { + if (!this->isHidden()) this->hide(); + return; + } + } + + if (this->isHidden()) { + this->show(); + } + Core::System* system = main_window->GetSystem(); Core::HID::EmulatedController* controller = GetPlayer1Controller(system); if (controller_widget && controller) { @@ -75,22 +113,23 @@ void ControllerOverlay::UpdateControllerState() { } } -// The paint event is now empty, which makes the background fully transparent. void ControllerOverlay::paintEvent(QPaintEvent* event) { Q_UNUSED(event); - // Intentionally left blank to achieve a fully transparent window background. } -// These functions handle dragging the frameless window void ControllerOverlay::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton && !size_grip->geometry().contains(event->pos())) { + + // LOGIC BRANCH: Desktop Linux (Wayland) requires system move. + // Gamescope and Windows require manual dragging. #if defined(Q_OS_LINUX) - // Use system move on Wayland/Linux for proper dragging - if (windowHandle()) { + if (!IsGamescope() && windowHandle()) { windowHandle()->startSystemMove(); + } else { + is_dragging = true; + drag_start_pos = event->globalPosition().toPoint() - this->pos(); } #else - // Original dragging implementation for other platforms (Windows, etc.) is_dragging = true; drag_start_pos = event->globalPosition().toPoint() - this->pos(); #endif @@ -99,15 +138,11 @@ void ControllerOverlay::mousePressEvent(QMouseEvent* event) { } void ControllerOverlay::mouseMoveEvent(QMouseEvent* event) { -#if !defined(Q_OS_LINUX) + // Only handle manual dragging if we aren't using startSystemMove (which handles its own move) if (is_dragging) { move(event->globalPosition().toPoint() - drag_start_pos); event->accept(); } -#else - // On Linux, the window manager handles the move, so we do nothing here. - Q_UNUSED(event); -#endif } void ControllerOverlay::mouseReleaseEvent(QMouseEvent* event) { @@ -119,6 +154,5 @@ void ControllerOverlay::mouseReleaseEvent(QMouseEvent* event) { void ControllerOverlay::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); - // This ensures the layout and its widgets (like the size grip) are correctly repositioned on resize. layout()->update(); }