Commit 80328814 authored by Roman Gilg's avatar Roman Gilg
Browse files

feat(kded): add orientation sensor

Summary:
With this patch KScreen controls rotation of internal outputs if an orientation
sensor is present and emitting rotation values.

The default behavior is auto-rotation but can be configured through control
files.

This functionality is developed for the Wayland session but might potentially
also work on X11, what needs to be tested more.

Test Plan: Screen of convertible with orientation sensor rotates.

Reviewers: #plasma, davidedmundson

Reviewed By: #plasma, davidedmundson

Subscribers: plasma-devel, davidedmundson

Tags: #plasma

Maniphest Tasks: T11475

Differential Revision: https://phabricator.kde.org/D26037
parent ae5ba868
......@@ -21,7 +21,7 @@ include(ECMQtDeclareLoggingCategory)
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
......
/********************************************************************
Copyright © 2019 Roman Gilg <subdiff@gmail.com>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "orientation_sensor.h"
#include <QOrientationSensor>
OrientationSensor::OrientationSensor(QObject *parent)
: QObject(parent)
, m_sensor(new QOrientationSensor(this))
{
connect(m_sensor, &QOrientationSensor::activeChanged, this, &OrientationSensor::refresh);
}
OrientationSensor::~OrientationSensor() = default;
void OrientationSensor::updateState()
{
const auto orientation = m_sensor->reading()->orientation();
if (m_value != orientation) {
m_value = orientation;
Q_EMIT valueChanged(orientation);
}
}
void OrientationSensor::refresh()
{
if (m_sensor->isActive()) {
if (m_enabled) {
updateState();
}
Q_EMIT availableChanged(true);
} else {
Q_EMIT availableChanged(false);
}
}
QOrientationReading::Orientation OrientationSensor::value() const
{
return m_value;
}
bool OrientationSensor::available() const
{
return m_sensor->connectToBackend();
}
bool OrientationSensor::enabled() const
{
return m_sensor->isActive();
}
void OrientationSensor::setEnabled(bool enable)
{
if (m_enabled == enable) {
return;
}
m_enabled = enable;
if (enable) {
connect(m_sensor, &QOrientationSensor::readingChanged,
this, &OrientationSensor::updateState);
m_sensor->start();
} else {
disconnect(m_sensor, &QOrientationSensor::readingChanged,
this, &OrientationSensor::updateState);
m_value = QOrientationReading::Undefined;
}
Q_EMIT enabledChanged(enable);
}
/********************************************************************
Copyright © 2019 Roman Gilg <subdiff@gmail.com>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#pragma once
#include <QObject>
#include <QOrientationReading>
class OrientationSensor : public QObject
{
Q_OBJECT
public:
explicit OrientationSensor(QObject *parent = nullptr);
~OrientationSensor() override final;
QOrientationReading::Orientation value() const;
bool available() const;
bool enabled() const;
void setEnabled(bool enable);
Q_SIGNALS:
void valueChanged(QOrientationReading::Orientation orientation);
void availableChanged(bool available);
void enabledChanged(bool enabled);
private:
void refresh();
void updateState();
QOrientationSensor *m_sensor;
QOrientationReading::Orientation m_value = QOrientationReading::Undefined;
bool m_enabled = false;
};
......@@ -13,6 +13,7 @@ set(kscreen_daemon_SRCS
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 @@ add_library(kscreen MODULE ${kscreen_daemon_SRCS})
target_link_libraries(kscreen Qt5::Widgets
Qt5::DBus
Qt5::Quick
Qt5::Sensors
KF5::Declarative
KF5::Screen
KF5::DBusAddons
......
......@@ -17,7 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#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,29 @@ void Config::activateControlWatching()
m_control->activateWatcher();
}
bool Config::autoRotationRequested() const
{
for (KScreen::OutputPtr &output : m_data->outputs()) {
if (m_control->getAutoRotate(output)) {
return true;
}
}
return false;
}
void Config::setDeviceOrientation(QOrientationReading::Orientation 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));
......
......@@ -17,9 +17,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef KDED_CONFIG_H
#define KDED_CONFIG_H
#include <kscreen/config.h>
#include <QOrientationReading>
#include <memory>
class ControlConfig;
......@@ -44,6 +45,8 @@ public:
}
void activateControlWatching();
bool autoRotationRequested() const;
void setDeviceOrientation(QOrientationReading::Orientation orientation);
void log();
void setValidityFlags(KScreen::Config::ValidityFlags flags) {
......
......@@ -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"
......@@ -40,6 +42,7 @@
#include <QTimer>
#include <QAction>
#include <QOrientationReading>
#include <QShortcut>
K_PLUGIN_CLASS_WITH_JSON(KScreenDaemon, "kscreen.json")
......@@ -50,8 +53,13 @@ KScreenDaemon::KScreenDaemon(QObject* parent, const QList< QVariant >& )
, m_changeCompressor(new QTimer(this))
, m_saveTimer(nullptr)
, m_lidClosedTimer(new QTimer(this))
, m_orientationSensor(new OrientationSensor(this))
{
connect(m_orientationSensor, &OrientationSensor::availableChanged,
this, &KScreenDaemon::updateOrientation);
connect(m_orientationSensor, &OrientationSensor::valueChanged,
this, &KScreenDaemon::updateOrientation);
KScreen::Log::instance();
QMetaObject::invokeMethod(this, "getInitialConfig", Qt::QueuedConnection);
}
......@@ -125,35 +133,80 @@ void KScreenDaemon::init()
monitorConnectedChange();
}
void KScreenDaemon::updateOrientation()
{
if (!m_monitoredConfig) {
return;
}
const auto features = m_monitoredConfig->data()->supportedFeatures();
if (!features.testFlag(KScreen::Config::Feature::AutoRotation)
|| !features.testFlag(KScreen::Config::Feature::TabletMode) ) {
return;
}
if (!m_orientationSensor->available() || !m_orientationSensor->enabled()) {
return;
}
const auto orientation = m_orientationSensor->value();
if (orientation == QOrientationReading::Undefined) {
// Orientation sensor went off. Do not change current orientation.
return;
}
if (orientation == QOrientationReading::FaceUp ||
orientation == QOrientationReading::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;
}
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<Config>(new Config(config));
configWrapper->setValidityFlags(KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen);
configWrapper->activateControlWatching();
connect(configWrapper.get(), &Config::controlChanged, this, [this]() {
// TODO
});
doApplyConfig(std::move(configWrapper));
}
void KScreenDaemon::doApplyConfig(std::unique_ptr<Config> config)
{
setMonitorForChanges(false); // TODO: remove?
m_monitoredConfig = std::move(config);
m_orientationSensor->setEnabled(m_monitoredConfig->autoRotationRequested());
connect(m_monitoredConfig.get(), &Config::controlChanged, this, [this]() {
m_orientationSensor->setEnabled(m_monitoredConfig->autoRotationRequested());
updateOrientation();
});
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()
......
......@@ -19,7 +19,9 @@
#ifndef KSCREEN_DAEMON_H
#define KSCREEN_DAEMON_H
#include "../common/globals.h"
#include "osdaction.h"
#include <kscreen/config.h>
#include <kdedmodule.h>
......@@ -29,6 +31,7 @@
#include <memory>
class Config;
class OrientationSensor;
namespace KScreen
{
......@@ -81,12 +84,16 @@ private:
void disableOutput(KScreen::OutputPtr &output);
void showOsd(const QString &icon, const QString &text);
void updateOrientation();
std::unique_ptr<Config> 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;
};
......
......@@ -16,7 +16,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "output.h"
#include "config.h"
#include "../common/globals.h"
#include "kscreen_daemon_debug.h"
#include "generator.h"
......@@ -119,6 +118,45 @@ bool Output::readInGlobal(KScreen::OutputPtr output)
return true;
}
KScreen::Output::Rotation orientationToRotation(QOrientationReading::Orientation orientation,
KScreen::Output::Rotation fallback)
{
using Orientation = QOrientationReading::Orientation;
switch (orientation) {
case Orientation::TopUp:
return KScreen::Output::Rotation::None;
case Orientation::TopDown:
return KScreen::Output::Rotation::Inverted;
case Orientation::LeftUp:
return KScreen::Output::Rotation::Right;
case Orientation::RightUp:
return KScreen::Output::Rotation::Left;
case Orientation::Undefined:
case Orientation::FaceUp:
case Orientation::FaceDown:
return fallback;
default:
Q_UNREACHABLE();
}
}
bool Output::updateOrientation(KScreen::OutputPtr &output,
QOrientationReading::Orientation 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<int, QPoint> Out;
......
......@@ -18,9 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define KDED_OUTPUT_H
#include "../common/control.h"
#include "../common/globals.h"
#include <kscreen/types.h>
#include <QOrientationReading>
#include <QVariantMap>
class Output
......@@ -34,6 +36,9 @@ public:
static QString dirPath();
static bool updateOrientation(KScreen::OutputPtr &output,
QOrientationReading::Orientation orientation);
private:
static QString globalFileName(const QString &hash);
static QVariantMap getGlobalData(KScreen::OutputPtr output);
......@@ -41,7 +46,6 @@ private:
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.
......
......@@ -23,7 +23,7 @@ macro(ADD_KDED_TEST testname)
add_executable(${testname} ${test_SRCS})
add_dependencies(${testname} kscreen) # make sure the dbus interfaces are generated
target_compile_definitions(${testname} PRIVATE "-DTEST_DATA=\"${CMAKE_CURRENT_SOURCE_DIR}/\"")
target_link_libraries(${testname} Qt5::Test Qt5::DBus Qt5::Gui KF5::Screen)
target_link_libraries(${testname} Qt5::Test Qt5::DBus Qt5::Gui Qt5::Sensors KF5::Screen)
add_test(NAME kscreen-kded-${testname} COMMAND ${testname})
ecm_mark_as_test(${testname})
endmacro()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment