// SPDX-License-Identifier: GPL-3.0-or-later /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH * Copyright (C) 2024 - 2025, chargebyte austria GmbH * * This file is part of nymea-app. * * nymea-app is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * nymea-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with nymea-app. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "screenhelper.h" #include #include #include #include #include #include #include Q_DECLARE_LOGGING_CATEGORY(dcPlatformIntegration) ScreenHelper::ScreenHelper(QObject *parent) : QObject(parent) { // Try generic backlight QDir backlightDir("/sys/class/backlight"); foreach (const QFileInfo &fi, backlightDir.entryInfoList({"*_backlight"}, QDir::Dirs)) { qCDebug(dcPlatformIntegration()) << "Checking backlight directory:" << fi.absoluteFilePath(); m_powerFile.setFileName(fi.absoluteFilePath() + "/bl_power"); m_brightnessFile.setFileName(fi.absoluteFilePath() + "/brightness"); if (!m_powerFile.open(QFile::ReadWrite | QFile::Text)) { qCDebug(dcPlatformIntegration()) << "Cannot open" << m_powerFile.fileName() << "for writing"; continue; } if (!m_brightnessFile.open(QFile::ReadWrite | QFile::Text)) { qCDebug(dcPlatformIntegration()) << "Cannot open" << m_brightnessFile.fileName() << "for writing"; continue; } QFile maxBrightnessFile(fi.absoluteFilePath() + "/max_brightness"); if (!maxBrightnessFile.open(QFile::ReadOnly)) { qCDebug(dcPlatformIntegration()) << "Cannot open" << m_brightnessFile.fileName() << "for reading"; continue; } bool ok; m_maxBrightness = maxBrightnessFile.readLine().trimmed().toInt(&ok); if (!ok) { qCDebug(dcPlatformIntegration()) << "Error reading max brightness value from" << maxBrightnessFile.fileName(); m_maxBrightness = -1; continue; } // All good. Let's use this and not check more files break; } if (!m_powerFile.isOpen() || !m_brightnessFile.isOpen()) { qCInfo(dcPlatformIntegration()) << "No backlight support on this platform"; return; } qCInfo(dcPlatformIntegration()) << "Backlight control enabled on" << m_powerFile.fileName(); bool ok; int currentBrightness = m_brightnessFile.readLine().trimmed().toInt(&ok); m_currentBrightness = currentBrightness * 100 / m_maxBrightness; qCInfo(dcPlatformIntegration()).nospace() << "Brigness: Absolute: " << currentBrightness << "/" << m_maxBrightness << " Percentage:" << m_currentBrightness; screenOn(); foreach (QWindow *w, qApp->topLevelWindows()) { w->installEventFilter(this); m_watchedWindows.append(w); } connect(qApp, &QGuiApplication::focusWindowChanged, this, [=](QWindow *w){ if (!m_watchedWindows.contains(w)) { w->installEventFilter(this); m_watchedWindows.append(w); } }); QSettings settings; m_currentBrightness = settings.value("screenBrightness", 80).toInt(); m_screenDimTimer.setInterval(settings.value("screenOffTimeout", 30000).toInt()); m_screenDimTimer.setSingleShot(true); connect(&m_screenDimTimer, &QTimer::timeout, this, &ScreenHelper::dimScreen); if (m_screenDimTimer.interval() > 0) { m_screenDimTimer.start(); } m_screenOffTimer.setInterval(5000); m_screenOffTimer.setSingleShot(true); connect(&m_screenOffTimer, &QTimer::timeout, this, &ScreenHelper::screenOff); // Hide the mouse cursor right away, it'll be restored on mouse move events QApplication::setOverrideCursor(Qt::BlankCursor); m_cursorHidden = true; } bool ScreenHelper::active() const { return m_powerFile.isOpen(); } int ScreenHelper::screenTimeout() const { return m_screenDimTimer.interval(); } void ScreenHelper::setScreenTimeout(int timeout) { qCInfo(dcPlatformIntegration()) << "Set screen timeout to" << timeout << "ms"; m_screenDimTimer.setInterval(timeout); QSettings settings; settings.setValue("screenOffTimeout", timeout); if (timeout > 0) { m_screenDimTimer.start(); } else { m_screenDimTimer.stop(); screenOn(); } } int ScreenHelper::screenBrightness() const { return m_currentBrightness; } void ScreenHelper::setScreenBrightness(int percent) { QSettings settings; settings.setValue("screenBrightness", percent); m_currentBrightness = percent; applyBrightness(m_currentBrightness); } bool ScreenHelper::eventFilter(QObject *watched, QEvent *event) { if (m_screenDimTimer.interval() == 0) { return QObject::eventFilter(watched, event); } QList watchedTypes = { QEvent::ActivationChange, QEvent::ApplicationStateChange, QEvent::KeyPress, QEvent::KeyRelease, QEvent::MouseButtonPress, QEvent::MouseButtonRelease, QEvent::MouseMove, QEvent::Show, QEvent::TouchBegin, QEvent::TouchEnd, QEvent::TouchUpdate, }; if (!watchedTypes.contains(event->type())) { return QObject::eventFilter(watched, event); } // Hide the mouse cursor if touchscreen events are coming in QList touchTypes = { QEvent::TouchBegin, QEvent::TouchUpdate, QEvent::TouchEnd }; if (touchTypes.contains(event->type()) && !m_cursorHidden) { QApplication::setOverrideCursor(Qt::BlankCursor); m_cursorHidden = true; } // Restore the mouse cursor if hidden and mouse events come in QList mouseTypes = { QEvent::MouseMove, QEvent::MouseButtonPress, QEvent::GrabMouse }; if (mouseTypes.contains(event->type()) && m_cursorHidden) { QApplication::restoreOverrideCursor(); m_cursorHidden = false; } // No timer is active, means we've off already => turn on and start dim timer, eating the input event if (!m_screenDimTimer.isActive() && !m_screenOffTimer.isActive()) { screenOn(); m_screenDimTimer.start(); return true; } // Screen off timer is active, means we've dimmed down already => dim up again and stop offTimer if (m_screenOffTimer.isActive()) { m_screenOffTimer.stop(); applyBrightness(m_currentBrightness); } // restart dim timer m_screenDimTimer.start(); return QObject::eventFilter(watched, event); } void ScreenHelper::screenOn() { qCInfo(dcPlatformIntegration()) << "Turning screen on"; qint64 ret = m_powerFile.write("0\n"); m_powerFile.flush(); if (ret < 0) { qCWarning(dcPlatformIntegration()) << "Failed to power on screen"; } } void ScreenHelper::screenOff() { qCInfo(dcPlatformIntegration()) << "Turning screen off"; qint64 ret = m_powerFile.write("1\n"); m_powerFile.flush(); if (ret < 0) { qCWarning(dcPlatformIntegration()) << "Failed to power off screen"; } applyBrightness(m_currentBrightness); } void ScreenHelper::dimScreen() { applyBrightness(qRound(m_currentBrightness * 0.5)); m_screenOffTimer.start(); } void ScreenHelper::applyBrightness(int percent) { int absolue = percent * m_maxBrightness / 100; qCDebug(dcPlatformIntegration()) << "Setting screen brightness to" << absolue << "(" << percent << "%)"; m_brightnessFile.write(QString("%1\n").arg(absolue).toUtf8()); m_brightnessFile.flush(); }