Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -21,7 +21,7 @@ include(FeatureSummary) include(KDEClangFormat) -find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Test) +find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Test Sensors) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Config DBusAddons Index: common/globals.h =================================================================== --- common/globals.h +++ common/globals.h @@ -21,6 +21,16 @@ namespace Globals { + enum class DeviceOrientation { + Undefined, + TopUp, + TopDown, + LeftUp, + RightUp, + FaceUp, + FaceDown + }; + void setDirPath(const QString &path); QString dirPath(); } Index: common/orientation_sensor.h =================================================================== --- common/orientation_sensor.h +++ common/orientation_sensor.h @@ -1,5 +1,5 @@ /******************************************************************** -Copyright 2018 Roman Gilg +Copyright © 2019 Roman Gilg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,15 +14,34 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ -#ifndef COMMON_GLOBALS_H -#define COMMON_GLOBALS_H +#pragma once -#include +#include "globals.h" -namespace Globals +#include + +class QOrientationSensor; + +using namespace Globals; + +class OrientationSensor : public QObject { - void setDirPath(const QString &path); - QString dirPath(); -} + Q_OBJECT +public: + explicit OrientationSensor(QObject *parent = nullptr); + ~OrientationSensor() override final; + + DeviceOrientation value() const; + bool isValid() const; + +Q_SIGNALS: + void valueChanged(DeviceOrientation orientation); + void validChanged(bool valid); + +private: + void refresh(); + void updateState(); -#endif + QOrientationSensor *m_sensor; + DeviceOrientation m_value = DeviceOrientation::Undefined; +}; Index: common/orientation_sensor.cpp =================================================================== --- /dev/null +++ common/orientation_sensor.cpp @@ -0,0 +1,81 @@ +/******************************************************************** +Copyright © 2019 Roman Gilg + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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 this program. If not, see . +*********************************************************************/ +#include "orientation_sensor.h" + +#include +#include + +OrientationSensor::OrientationSensor(QObject *parent) + : QObject(parent) + , m_sensor(new QOrientationSensor(this)) +{ + connect(m_sensor, &QOrientationSensor::readingChanged, this, &OrientationSensor::updateState); + connect(m_sensor, &QOrientationSensor::activeChanged, this, &OrientationSensor::refresh); + + m_sensor->start(); +} + +OrientationSensor::~OrientationSensor() = default; + +void OrientationSensor::updateState() +{ + auto toOrientation = [] (QOrientationReading *reading) { + switch (reading->orientation()) { + case QOrientationReading::Undefined: + return DeviceOrientation::Undefined; + case QOrientationReading::TopUp: + return DeviceOrientation::TopUp; + case QOrientationReading::TopDown: + return DeviceOrientation::TopDown; + case QOrientationReading::LeftUp: + return DeviceOrientation::LeftUp; + case QOrientationReading::RightUp: + return DeviceOrientation::RightUp; + case QOrientationReading::FaceUp: + return DeviceOrientation::FaceUp; + case QOrientationReading::FaceDown: + return DeviceOrientation::FaceDown; + default: + Q_UNREACHABLE(); + } + }; + const auto orientation = toOrientation(m_sensor->reading()); + if (m_value != orientation) { + m_value = orientation; + emit valueChanged(orientation); + } +} + +void OrientationSensor::refresh() +{ + if (m_sensor->isActive()) { + updateState(); + emit validChanged(true); + } else { + emit validChanged(false); + } +} + +DeviceOrientation OrientationSensor::value() const +{ + return m_value; +} + +bool OrientationSensor::isValid() const +{ + return m_sensor->isActive(); +} Index: kded/CMakeLists.txt =================================================================== --- kded/CMakeLists.txt +++ kded/CMakeLists.txt @@ -13,6 +13,7 @@ osdaction.cpp ${CMAKE_SOURCE_DIR}/common/globals.cpp ${CMAKE_SOURCE_DIR}/common/control.cpp + ${CMAKE_SOURCE_DIR}/common/orientation_sensor.cpp ${CMAKE_SOURCE_DIR}/common/utils.cpp ) @@ -32,6 +33,7 @@ target_link_libraries(kscreen Qt5::Widgets Qt5::DBus Qt5::Quick + Qt5::Sensors KF5::Declarative KF5::Screen KF5::DBusAddons Index: kded/config.h =================================================================== --- kded/config.h +++ kded/config.h @@ -17,6 +17,7 @@ #ifndef KDED_CONFIG_H #define KDED_CONFIG_H +#include "../common/globals.h" #include @@ -44,6 +45,7 @@ } void activateControlWatching(); + void setDeviceOrientation(Globals::DeviceOrientation orientation); void log(); void setValidityFlags(KScreen::Config::ValidityFlags flags) { Index: kded/config.cpp =================================================================== --- kded/config.cpp +++ kded/config.cpp @@ -17,7 +17,6 @@ *********************************************************************/ #include "config.h" #include "output.h" -#include "../common/globals.h" #include "../common/control.h" #include "kscreen_daemon_debug.h" #include "device.h" @@ -68,6 +67,19 @@ m_control->activateWatcher(); } +void Config::setDeviceOrientation(Globals::DeviceOrientation orientation) +{ + for (KScreen::OutputPtr &output : m_data->outputs()) { + if (!m_control->getAutoRotate(output)) { + continue; + } + if (Output::updateOrientation(output, orientation)) { + // TODO: call Layouter to find fitting positions for other outputs again + return; + } + } +} + bool Config::fileExists() const { return (QFile::exists(configsDirPath() % id()) || QFile::exists(configsDirPath() % s_fixedConfigFileName)); Index: kded/daemon.h =================================================================== --- kded/daemon.h +++ kded/daemon.h @@ -19,7 +19,9 @@ #ifndef KSCREEN_DAEMON_H #define KSCREEN_DAEMON_H +#include "../common/globals.h" #include "osdaction.h" + #include #include @@ -29,6 +31,7 @@ #include class Config; +class OrientationSensor; namespace KScreen { @@ -81,12 +84,16 @@ void disableOutput(KScreen::OutputPtr &output); void showOsd(const QString &icon, const QString &text); + void updateOrientation(Globals::DeviceOrientation orientation); + std::unique_ptr m_monitoredConfig; bool m_monitoring; + bool m_configDirty = true; QTimer* m_changeCompressor; QTimer* m_saveTimer; QTimer* m_lidClosedTimer; KScreen::OsdManager *m_osdManager; + OrientationSensor *m_orientationSensor; bool m_startingUp = true; }; Index: kded/daemon.cpp =================================================================== --- kded/daemon.cpp +++ kded/daemon.cpp @@ -20,6 +20,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "daemon.h" + +#include "../common/orientation_sensor.h" #include "config.h" #include "generator.h" #include "device.h" @@ -50,7 +52,7 @@ , m_changeCompressor(new QTimer(this)) , m_saveTimer(nullptr) , m_lidClosedTimer(new QTimer(this)) - + , m_orientationSensor(new OrientationSensor(this)) { KScreen::Log::instance(); QMetaObject::invokeMethod(this, "getInitialConfig", Qt::QueuedConnection); @@ -71,6 +73,9 @@ init(); }); + + connect(m_orientationSensor, &OrientationSensor::valueChanged, + this, &KScreenDaemon::updateOrientation); } KScreenDaemon::~KScreenDaemon() @@ -125,35 +130,65 @@ monitorConnectedChange(); } +void KScreenDaemon::updateOrientation(Globals::DeviceOrientation orientation) +{ + using Orientation = Globals::DeviceOrientation; + + if (orientation == Orientation::Undefined) { + // Orientation sensor went off. Do not change current orientation. + return; + } else if (orientation == Orientation::FaceUp || + orientation == Orientation::FaceDown) { + // We currently don't do anything with FaceUp/FaceDown, but in the future we could use them + // to shut off and switch on again a display when display is facing downwards/upwards. + return; + } + + if (m_monitoredConfig) { + m_monitoredConfig->setDeviceOrientation(orientation); + if (m_monitoring) { + doApplyConfig(m_monitoredConfig->data()); + } else { + m_configDirty = true; + } + } +} + void KScreenDaemon::doApplyConfig(const KScreen::ConfigPtr& config) { qCDebug(KSCREEN_KDED) << "Do set and apply specific config"; auto configWrapper = std::unique_ptr(new Config(config)); configWrapper->setValidityFlags(KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen); configWrapper->activateControlWatching(); connect(configWrapper.get(), &Config::controlChanged, this, [this]() { - // TODO + updateOrientation(m_orientationSensor->value()); }); doApplyConfig(std::move(configWrapper)); } void KScreenDaemon::doApplyConfig(std::unique_ptr config) { - setMonitorForChanges(false); // TODO: remove? m_monitoredConfig = std::move(config); refreshConfig(); } void KScreenDaemon::refreshConfig() { setMonitorForChanges(false); + m_configDirty = false; KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig->data()); - connect(new KScreen::SetConfigOperation(m_monitoredConfig->data()), &KScreen::SetConfigOperation::finished, this, - [&]() { - qCDebug(KSCREEN_KDED) << "Config applied"; - setMonitorForChanges(true); - }); + connect(new KScreen::SetConfigOperation(m_monitoredConfig->data()), + &KScreen::SetConfigOperation::finished, + this, [this]() { + qCDebug(KSCREEN_KDED) << "Config applied"; + if (m_configDirty) { + // Config changed in the meantime again, apply. + doApplyConfig(m_monitoredConfig->data()); + } else { + setMonitorForChanges(true); + } + }); } void KScreenDaemon::applyConfig() Index: kded/output.h =================================================================== --- kded/output.h +++ kded/output.h @@ -18,6 +18,7 @@ #define KDED_OUTPUT_H #include "../common/control.h" +#include "../common/globals.h" #include @@ -34,14 +35,16 @@ static QString dirPath(); + static bool updateOrientation(KScreen::OutputPtr &output, + Globals::DeviceOrientation orientation); + private: static QString globalFileName(const QString &hash); static QVariantMap getGlobalData(KScreen::OutputPtr output); static void readIn(KScreen::OutputPtr output, const QVariantMap &info, Control::OutputRetention retention); static bool readInGlobal(KScreen::OutputPtr output); static void readInGlobalPartFromInfo(KScreen::OutputPtr output, const QVariantMap &info); - /* * When a global output value (scale, rotation) is changed we might * need to reposition the outputs when another config is read. Index: kded/output.cpp =================================================================== --- kded/output.cpp +++ kded/output.cpp @@ -16,7 +16,6 @@ *********************************************************************/ #include "output.h" #include "config.h" -#include "../common/globals.h" #include "kscreen_daemon_debug.h" #include "generator.h" @@ -119,6 +118,44 @@ return true; } +KScreen::Output::Rotation orientationToRotation(Globals::DeviceOrientation orientation, + KScreen::Output::Rotation fallback) +{ + using namespace Globals; + + switch (orientation) { + case DeviceOrientation::TopUp: + return KScreen::Output::Rotation::None; + case DeviceOrientation::TopDown: + return KScreen::Output::Rotation::Inverted; + case DeviceOrientation::LeftUp: + return KScreen::Output::Rotation::Right; + case DeviceOrientation::RightUp: + return KScreen::Output::Rotation::Left; + case DeviceOrientation::Undefined: + case DeviceOrientation::FaceUp: + case DeviceOrientation::FaceDown: + return fallback; + default: + Q_UNREACHABLE(); + } +} + +bool Output::updateOrientation(KScreen::OutputPtr &output, Globals::DeviceOrientation orientation) +{ + if (output->type() != KScreen::Output::Type::Panel) { + return false; + } + const auto currentRotation = output->rotation(); + const auto rotation = orientationToRotation(orientation, currentRotation); + if (rotation == currentRotation) { + return true; + } + output->setRotation(rotation); + return true; +} + +// TODO: move this into the Layouter class. void Output::adjustPositions(KScreen::ConfigPtr config, const QVariantList &outputsInfo) { typedef QPair Out;