AdaptiveBrightness/AdaptiveBrightness/main_window.cpp

281 lines
9.3 KiB
C++

#include "main_window.hpp"
#include <algorithm>
#include <numeric>
#include <cmath>
#include <QCloseEvent>
#include <QGroupBox>
#include <QSlider>
#include <QTabWidget>
#include <QWidget>
#include <QtDebug>
#include <QtGlobal>
#include "log_tr.hpp"
#include "monitor_control.hpp"
#include "sensor_driver.hpp"
#include "utils.hpp"
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
{
qDebug(ltr("Creating main window"));
m_ui.setupUi(this);
enumSensors();
enumMonitors();
setupMonitorsTab();
setupTrayIcon();
setupCallbackTimer();
}
MainWindow::~MainWindow()
{
qDebug(ltr("Destroying main window"));
m_timerThread->quit();
if(!m_timerThread->wait()) {
qCritical(ltr("Timer thread did not terminate cleanly"));
}
}
void MainWindow::closeEvent(QCloseEvent* event)
{
m_showHideAction->activate(QAction::Trigger);
event->ignore();
}
void MainWindow::enumSensors()
{
auto sensors = enumerateSensors();
for(auto& sensor: sensors) {
m_sensors.push_back({std::move(sensor)});
}
}
void MainWindow::enumMonitors()
{
m_ui.monitorDropdown->clear();
auto monitors = enumerateMonitors();
for(auto& monitor: monitors) {
const auto currentBrightness = monitor.getBrightness();
m_ui.monitorDropdown->addItem(monitor.getName());
m_monitors.push_back({std::move(monitor), 0.f, 1.f, currentBrightness, false});
}
}
void MainWindow::loadIcons()
{
qDebug(ltr("Loading icons"));
constexpr auto NO_SENSOR_ICON_PATHS = std::array{
":/AdaptiveBrightness/resources/nosense_0.ico", ":/AdaptiveBrightness/resources/nosense_1.ico", ":/AdaptiveBrightness/resources/nosense_2.ico",
":/AdaptiveBrightness/resources/nosense_3.ico", ":/AdaptiveBrightness/resources/nosense_4.ico", ":/AdaptiveBrightness/resources/nosense_5.ico",
":/AdaptiveBrightness/resources/nosense_6.ico", ":/AdaptiveBrightness/resources/nosense_7.ico", ":/AdaptiveBrightness/resources/nosense_8.ico",
};
static_assert(NO_SENSOR_ICON_PATHS.size() == utils::array_size_v<decltype(m_noSensorStateIcons)>, "Number of paths does not match number of icons");
constexpr auto SENSOR_ICON_PATHS = std::array{
":/AdaptiveBrightness/resources/sense_0.ico", ":/AdaptiveBrightness/resources/sense_1.ico", ":/AdaptiveBrightness/resources/sense_2.ico",
":/AdaptiveBrightness/resources/sense_3.ico", ":/AdaptiveBrightness/resources/sense_4.ico", ":/AdaptiveBrightness/resources/sense_5.ico",
":/AdaptiveBrightness/resources/sense_6.ico", ":/AdaptiveBrightness/resources/sense_7.ico", ":/AdaptiveBrightness/resources/sense_8.ico",
};
static_assert(SENSOR_ICON_PATHS.size() == utils::array_size_v<decltype(m_sensorStateIcons)>, "Number of paths does not match number of icons");
constexpr auto ERROR_ICON_PATH = ":/AdaptiveBrightness/resources/error.ico";
for(std::size_t i = 0; i < NO_SENSOR_ICON_PATHS.size(); ++i) {
m_noSensorStateIcons[i] = std::make_unique<QIcon>(NO_SENSOR_ICON_PATHS[i]);
}
for(std::size_t i = 0; i < SENSOR_ICON_PATHS.size(); ++i) {
m_sensorStateIcons[i] = std::make_unique<QIcon>(SENSOR_ICON_PATHS[i]);
}
m_errorIcon = std::make_unique<QIcon>(ERROR_ICON_PATH);
qDebug(ltr("Icons loaded"));
}
void MainWindow::setupMonitorsTab()
{
connect(m_ui.monitorDropdown, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::updateCurrentMonitorGUI);
m_ui.monitorBrightnessSlider->setDisabled(true);
m_ui.monitorOverallBrightnessSlider->setDisabled(true);
m_ui.monitorOverrideCheck->setChecked(false);
connect(m_ui.monitorOverrideCheck, &QCheckBox::stateChanged,
[this](int state) { m_ui.monitorBrightnessSlider->setEnabled(state == Qt::CheckState::Checked); });
m_ui.monitorOverallOverrideCheck->setChecked(false);
connect(m_ui.monitorOverallOverrideCheck, &QCheckBox::stateChanged,
[this](int state) { m_ui.monitorOverallBrightnessSlider->setEnabled(state == Qt::CheckState::Checked); });
connect(m_ui.monitorBrightnessSlider, &QSlider::valueChanged, [this](int value) {
const auto mappedBrightness = utils::map(value, 0, 100, 0.f, 1.f);
const auto newBrightness = std::clamp(mappedBrightness, 0.f, 1.f);
qDebug(ltr("Overriding brightness with %1").arg(newBrightness));
m_brightness = newBrightness;
});
}
void MainWindow::setupTrayIcon()
{
loadIcons();
if(!QSystemTrayIcon::isSystemTrayAvailable()) {
qWarning(ltr("System tray is not available -> tray icon will be disabled"));
}
else {
qDebug(ltr("System tray is available"));
m_trayIconMenu = new QMenu(this);
m_showHideAction = m_trayIconMenu->addAction(tr("Show"), [this]() {
if(m_visibility) {
m_showHideAction->setText(tr("Show"));
hide();
}
else {
m_showHideAction->setText(tr("Hide"));
show();
}
m_visibility = !m_visibility;
});
m_trayIconMenu->addSeparator();
m_trayIconMenu->addAction(tr("Quit"), this, &QCoreApplication::quit);
qDebug(ltr("Tray icon context menu initialized"));
m_trayIcon = new QSystemTrayIcon(this);
m_trayIcon->setIcon(*m_errorIcon);
m_trayIcon->setContextMenu(m_trayIconMenu);
m_trayIcon->show();
m_trayIcon->setToolTip(tr("AdaptiveBrightness"));
connect(m_trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::trayIconClicked);
qDebug(ltr("Tray icon initialized"));
}
}
void MainWindow::setupCallbackTimer()
{
m_timerThread = new QThread(this);
m_timer = new QTimer(nullptr);
m_timer->setInterval(1000);
m_timer->moveToThread(m_timerThread);
m_timerThread->connect(m_timer, &QTimer::timeout, this, &MainWindow::updateState, Qt::DirectConnection);
m_timer->connect(m_timerThread, SIGNAL(started()), SLOT(start()));
m_timerThread->start();
qDebug(ltr("Callback timer started"));
}
void MainWindow::updateCurrentMonitorGUI(int index)
{
const auto brightness = std::clamp<int>(utils::map(m_monitors[index].brightness, 0.f, 1.f, 0, 100), 0, 100);
const auto minimum = std::clamp<int>(utils::map(m_monitors[index].minBrightness, 0.f, 1.f, 0, 100), 0, 100);
const auto maximum = std::clamp<int>(utils::map(m_monitors[index].maxBrightness, 0.f, 1.f, 0, 100), 0, 100);
m_ui.monitorMinBrightnessSlider->setValue(minimum);
m_ui.monitorMinBrightnessValueLabel->setText(QString("%1%").arg(minimum, 3, 10));
m_ui.monitorMaxBrightnessSlider->setValue(maximum);
m_ui.monitorMaxBrightnessValueLabel->setText(QString("%1%").arg(maximum, 3, 10));
m_ui.monitorBrightnessSlider->setValue(brightness);
m_ui.monitorBrightnessValueLabel->setText(QString("%1%").arg(brightness, 3, 10));
m_ui.monitorOverrideCheck->setChecked(m_monitors[index].manualOverride);
}
void MainWindow::updateState()
{
qDebug(ltr("Updating state"));
bool sensorError = false;
bool monitorError = false;
size_t iconBrightness = 0;
const auto getAverageSensorValue = [this](float& brightness) {
if(m_sensors.empty()) {
return false;
}
std::vector<float> values;
for(auto& sensor: m_sensors) {
const auto sensorValues = sensor.driver.readValues();
if(!sensor.driver) {
return false;
}
values.insert(values.end(), sensorValues.begin(), sensorValues.end());
}
brightness = std::accumulate(values.begin(), values.end(), 0.f) / values.size();
return true;
};
const auto setAllMonitorsBrightness = [this](float& brightness) {
if(m_monitors.empty()) {
return false;
}
bool errorOccurred = false;
for(auto& monitor: m_monitors) {
if(!monitor.driver.setBrightness(brightness)) {
errorOccurred = true;
}
}
return !errorOccurred;
};
if(!getAverageSensorValue(m_brightness)) {
enumSensors();
qInfo(ltr("At least one sensor became invalid, re-enumerating sensors found %1 valid sensor(s)").arg(m_sensors.size()));
sensorError = true;
}
const auto roundedBrightness = std::round(utils::map(m_brightness, 0.f, 1.f, 0, 8));
iconBrightness = std::clamp<size_t>(roundedBrightness, 0, 8);
if(!setAllMonitorsBrightness(m_brightness)) {
enumMonitors();
qInfo(ltr("At least one monitor became invalid, re-enumerating monitors found %1 valid monitor(s)").arg(m_monitors.size()));
monitorError = true;
}
if(!sensorError && !monitorError && m_trayIcon) {
m_trayIcon->setIcon(*m_sensorStateIcons[iconBrightness]);
}
else if(sensorError && !monitorError && m_trayIcon) {
m_trayIcon->setIcon(*m_noSensorStateIcons[iconBrightness]);
}
else if(m_trayIcon) {
m_trayIcon->setIcon(*m_errorIcon);
}
if(m_trayIcon) {
m_trayIcon->setToolTip(tr("AdaptiveBrightness - %1%").arg(m_brightness * 100, 0, 'f', 0));
}
m_ui.monitorBrightnessSlider->setValue(utils::map(m_brightness, 0.f, 1.f, 0, 100));
}
void MainWindow::trayIconClicked(QSystemTrayIcon::ActivationReason reason)
{
if(reason == QSystemTrayIcon::ActivationReason::Trigger) {
m_showHideAction->trigger();
}
}