qt: Show emulated notification LED

This commit is contained in:
PabloMK7 2026-04-09 15:24:38 +02:00
parent d29e15f219
commit 3d69741076
5 changed files with 140 additions and 0 deletions

View File

@ -174,6 +174,8 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
multiplayer/state.cpp
multiplayer/state.h
multiplayer/validation.h
notification_led.cpp
notification_led.h
precompiled_headers.h
qt_image_interface.cpp
qt_image_interface.h

View File

@ -352,6 +352,8 @@ GMainWindow::GMainWindow(Core::System& system_)
Camera::RegisterFactory("image", std::make_unique<Camera::StillImageCameraFactory>());
Camera::RegisterFactory("qt", std::make_unique<Camera::QtMultimediaCameraFactory>(qt_cameras));
system.RegisterInfoLEDColorChanged([this]() { emit InfoLEDColorChanged(); });
LoadTranslation();
Pica::g_debug_context = Pica::DebugContext::Construct();
@ -606,6 +608,20 @@ void GMainWindow::InitializeWidgets() {
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText());
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon());
QFrame* sep = new QFrame(this);
sep->setFrameShape(QFrame::VLine);
sep->setFrameShadow(QFrame::Sunken);
sep->setFixedHeight(16);
statusBar()->addPermanentWidget(sep);
notification_led = new LedWidget();
notification_led->setToolTip(tr("Emulated notification LED"));
statusBar()->addPermanentWidget(notification_led);
connect(this, &GMainWindow::InfoLEDColorChanged, this, [this] {
auto led_color = system.GetInfoLEDColor();
notification_led->setColor(QColor(led_color.r(), led_color.g(), led_color.b()));
});
statusBar()->setVisible(true);
// Removes an ugly inner border from the status bar widgets under Linux
@ -1600,6 +1616,7 @@ void GMainWindow::ShutdownGame() {
emu_speed_label->setVisible(false);
game_fps_label->setVisible(false);
emu_frametime_label->setVisible(false);
notification_led->setColor(QColor(0, 0, 0));
UpdateSaveStates();

View File

@ -21,6 +21,7 @@
#include <QTranslator>
#include "citra_qt/compatibility_list.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/notification_led.h"
#include "citra_qt/user_data_migration.h"
#include "core/core.h"
#include "core/savestate.h"
@ -147,6 +148,7 @@ signals:
void CIAInstallReport(Service::AM::InstallStatus status, QString filepath);
void CompressFinished(bool is_compress, bool success);
void CIAInstallFinished();
void InfoLEDColorChanged();
// Signal that tells widgets to update icons to use the current theme
void UpdateThemedIcons();
@ -364,6 +366,8 @@ private:
MultiplayerState* multiplayer_state = nullptr;
LedWidget* notification_led = nullptr;
// Created before `config` to ensure that emu data directory
// isn't created before the check is performed
UserDataMigrator user_data_migrator;

View File

@ -0,0 +1,87 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cmath>
#include <QPainter>
#include <QRadialGradient>
#include "citra_qt/notification_led.h"
LedWidget::LedWidget(QWidget* parent) : QWidget(parent), color(0, 0, 0) {
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
}
QSize LedWidget::sizeHint() const {
return QSize(16, 16);
}
QSize LedWidget::minimumSizeHint() const {
return QSize(16, 16);
}
void LedWidget::setColor(const QColor& _color) {
if (color == _color)
return;
color = _color;
update();
}
QColor LedWidget::lerpColor(const QColor& a, const QColor& b, float t) {
t = std::clamp(t, 0.0f, 1.0f);
return QColor(int(a.red() + (b.red() - a.red()) * t),
int(a.green() + (b.green() - a.green()) * t),
int(a.blue() + (b.blue() - a.blue()) * t));
}
QColor LedWidget::blendLedColor(int r, int g, int b) const {
// Default "off" color
const QColor off_color(64, 64, 64);
// If completely off, just show gray and skip further calculations
if (r == 0 && g == 0 && b == 0)
return off_color;
// Normalize lit color so hue stays pure
int max_c = std::max({r, g, b});
QColor lit_color((r * 255) / max_c, (g * 255) / max_c, (b * 255) / max_c);
// Convert PWM duty to perceived brightness.
// This gives better results as LED RGB values
// are not linear.
constexpr float gamma = 2.4f;
float pwm = max_c / 255.0;
float t = std::powf(pwm, 1.f / gamma);
return lerpColor(off_color, lit_color, t * 0.8f);
}
void LedWidget::paintEvent(QPaintEvent*) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QRectF rect = this->rect().adjusted(0, 2, 0, -2);
qreal size = std::min(rect.width(), rect.height());
QRectF circle((rect.center().x() - size / 2.f) - 2, rect.center().y() - size / 2.f, size, size);
QPointF center = circle.center();
qreal radius = circle.width() / 2.f;
QColor base = blendLedColor(color.red(), color.green(), color.blue());
QRadialGradient g(center, radius);
QColor inner = base.lighter(135);
QColor outer = base.darker(125);
g.setColorAt(0.f, inner);
g.setColorAt(0.7f, base);
g.setColorAt(1.f, outer);
p.setPen(Qt::NoPen);
p.setBrush(g);
p.drawEllipse(circle);
}

View File

@ -0,0 +1,30 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <QColor>
#include <QWidget>
class LedWidget : public QWidget {
Q_OBJECT
public:
explicit LedWidget(QWidget* parent = nullptr);
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
void setColor(const QColor& color);
protected:
void paintEvent(QPaintEvent* event) override;
private:
QColor blendLedColor(int r, int g, int b) const;
static QColor lerpColor(const QColor& a, const QColor& b, float t);
private:
QColor color;
};