diff --git a/CMakeLists.txt b/CMakeLists.txt index 92fe9d4..05777d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,36 +1,40 @@ project(KScreen) set(PROJECT_VERSION "5.12.80") cmake_minimum_required(VERSION 2.8.12) set(KSCREEN_VERSION ${PROJECT_VERSION}) add_definitions("-DKSCREEN_VERSION=\"${KSCREEN_VERSION}\"") find_package(ECM 1.6.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) find_package(Qt5 REQUIRED COMPONENTS QuickWidgets Test) -find_package(KF5 REQUIRED COMPONENTS DBusAddons Config ConfigWidgets I18n XmlGui GlobalAccel WidgetsAddons) +find_package(KF5 REQUIRED COMPONENTS DBusAddons Config ConfigWidgets I18n XmlGui GlobalAccel WidgetsAddons + Declarative IconThemes Plasma +) +# WARNING PlasmaQuick provides unversioned CMake config +find_package(KF5 REQUIRED COMPONENTS PlasmaQuick) set(MIN_LIBKSCREEN_VERSION "5.2.91") find_package(KF5Screen ${MIN_LIBKSCREEN_VERSION} REQUIRED) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMInstallIcons) include(ECMMarkAsTest) include(FeatureSummary) include(ECMQtDeclareLoggingCategory) add_subdirectory(icons) add_subdirectory(kcm) #add_subdirectory(plasma) add_subdirectory(kded) add_subdirectory(tests) add_subdirectory(console) install( FILES kscreen.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/kded/CMakeLists.txt b/kded/CMakeLists.txt index 317f743..5f20388 100644 --- a/kded/CMakeLists.txt +++ b/kded/CMakeLists.txt @@ -1,33 +1,53 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kscreen\") -include_directories(${CMAKE_CURRENT_BINARY_DIR}/../) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/../ ${CMAKE_SOURCE_DIR}/kcm/src) set(kscreen_daemon_SRCS daemon.cpp serializer.cpp generator.cpp device.cpp + osd.cpp + osdmanager.cpp + ${CMAKE_SOURCE_DIR}/kcm/src/utils.cpp ) ecm_qt_declare_logging_category(kscreen_daemon_SRCS HEADER kscreen_daemon_debug.h IDENTIFIER KSCREEN_KDED CATEGORY_NAME kscreen.kded) qt5_add_dbus_interface(kscreen_daemon_SRCS org.freedesktop.DBus.Properties.xml freedesktop_interface) qt5_add_dbus_adaptor(kscreen_daemon_SRCS org.kde.KScreen.xml daemon.h KScreenDaemon ) add_library(kscreen MODULE ${kscreen_daemon_SRCS}) -target_link_libraries(kscreen Qt5::Widgets Qt5::DBus KF5::Screen KF5::DBusAddons KF5::I18n KF5::XmlGui KF5::GlobalAccel) +target_link_libraries(kscreen Qt5::Widgets + Qt5::DBus + Qt5::Quick + KF5::Declarative + KF5::Screen + KF5::DBusAddons + KF5::I18n + KF5::XmlGui + KF5::GlobalAccel) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kscreen.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/kscreen.desktop @ONLY) kcoreaddons_desktop_to_json(kscreen ${CMAKE_CURRENT_BINARY_DIR}/kscreen.desktop) install(TARGETS kscreen DESTINATION ${PLUGIN_INSTALL_DIR}/kf5/kded) + +set(QML_FILES + qml/Osd.qml + qml/OsdItem.qml + qml/OsdSelector.qml + qml/OutputIdentifier.qml +) + +install(FILES ${QML_FILES} DESTINATION ${DATA_INSTALL_DIR}/kded_kscreen/qml) diff --git a/kded/daemon.cpp b/kded/daemon.cpp index 84d1544..4bc98be 100644 --- a/kded/daemon.cpp +++ b/kded/daemon.cpp @@ -1,440 +1,502 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * Copyright 2016 by Sebastian Kügler * * * * 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, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "daemon.h" #include "serializer.h" #include "generator.h" #include "device.h" #include "kscreenadaptor.h" #include "kscreen_daemon_debug.h" +#include "osdmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KScreenDaemonFactory, "kscreen.json", registerPlugin();) KScreenDaemon::KScreenDaemon(QObject* parent, const QList< QVariant >& ) : KDEDModule(parent) , m_monitoredConfig(nullptr) , m_iteration(Generator::None) , m_monitoring(false) , m_changeCompressor(new QTimer(this)) , m_buttonTimer(new QTimer(this)) , m_saveTimer(new QTimer(this)) , m_lidClosedTimer(new QTimer(this)) { KScreen::Log::instance(); QMetaObject::invokeMethod(this, "requestConfig", Qt::QueuedConnection); } void KScreenDaemon::requestConfig() { connect(new KScreen::GetConfigOperation, &KScreen::GetConfigOperation::finished, this, &KScreenDaemon::configReady); } void KScreenDaemon::configReady(KScreen::ConfigOperation* op) { if (op->hasError()) { return; } m_monitoredConfig = qobject_cast(op)->config(); qCDebug(KSCREEN_KDED) << "Config" << m_monitoredConfig.data() << "is ready"; KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig); init(); } KScreenDaemon::~KScreenDaemon() { Generator::destroy(); Device::destroy(); } void KScreenDaemon::init() { KActionCollection *coll = new KActionCollection(this); QAction* action = coll->addAction(QStringLiteral("display")); action->setText(i18n("Switch Display" )); QList switchDisplayShortcuts({Qt::Key_Display, Qt::MetaModifier + Qt::Key_P}); KGlobalAccel::self()->setGlobalShortcut(action, switchDisplayShortcuts); connect(action, &QAction::triggered, [&](bool) { displayButton(); }); new KScreenAdaptor(this); + // Initialize OSD manager to register its dbus interface + KScreen::OsdManager::self(); m_buttonTimer->setInterval(300); m_buttonTimer->setSingleShot(true); connect(m_buttonTimer, &QTimer::timeout, this, &KScreenDaemon::applyGenericConfig); m_saveTimer->setInterval(300); m_saveTimer->setSingleShot(true); connect(m_saveTimer, &QTimer::timeout, this, &KScreenDaemon::saveCurrentConfig); m_changeCompressor->setInterval(10); m_changeCompressor->setSingleShot(true); connect(m_changeCompressor, &QTimer::timeout, this, &KScreenDaemon::applyConfig); m_lidClosedTimer->setInterval(1000); m_lidClosedTimer->setSingleShot(true); connect(m_lidClosedTimer, &QTimer::timeout, this, &KScreenDaemon::lidClosedTimeout); connect(Device::self(), &Device::lidClosedChanged, this, &KScreenDaemon::lidClosedChanged); connect(Device::self(), &Device::resumingFromSuspend, this, [&]() { KScreen::Log::instance()->setContext("resuming"); qCDebug(KSCREEN_KDED) << "Resumed from suspend, checking for screen changes"; // We don't care about the result, we just want to force the backend // to query XRandR so that it will detect possible changes that happened // while the computer was suspended, and will emit the change events. new KScreen::GetConfigOperation(KScreen::GetConfigOperation::NoEDID, this); }); connect(Device::self(), &Device::aboutToSuspend, this, [&]() { qCDebug(KSCREEN_KDED) << "System is going to suspend, won't be changing config (waited for " << (m_lidClosedTimer->interval() - m_lidClosedTimer->remainingTime()) << "ms)"; m_lidClosedTimer->stop(); }); connect(Generator::self(), &Generator::ready, this, &KScreenDaemon::applyConfig); Generator::self()->setCurrentConfig(m_monitoredConfig); monitorConnectedChange(); } void KScreenDaemon::doApplyConfig(const KScreen::ConfigPtr& config) { qCDebug(KSCREEN_KDED) << "doApplyConfig()"; setMonitorForChanges(false); m_monitoredConfig = config; KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig); connect(new KScreen::SetConfigOperation(config), &KScreen::SetConfigOperation::finished, this, [&]() { qCDebug(KSCREEN_KDED) << "Config applied"; setMonitorForChanges(true); }); } void KScreenDaemon::applyConfig() { qCDebug(KSCREEN_KDED) << "Applying config"; if (Serializer::configExists(m_monitoredConfig)) { applyKnownConfig(); return; } applyIdealConfig(); } void KScreenDaemon::applyKnownConfig() { const QString configId = Serializer::configId(m_monitoredConfig); qCDebug(KSCREEN_KDED) << "Applying known config" << configId; // We may look for a config that has been set when the lid was closed, Bug: 353029 if (Device::self()->isLaptop() && !Device::self()->isLidClosed()) { Serializer::moveConfig(configId + QStringLiteral("_lidOpened"), configId); } KScreen::ConfigPtr config = Serializer::config(m_monitoredConfig, configId); // It's possible that the Serializer returned a nullptr if (!config || !KScreen::Config::canBeApplied(config, KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen)) { return applyIdealConfig(); } doApplyConfig(config); } +void KScreenDaemon::applyOsdAction(KScreen::OsdAction::Action action) +{ + switch (action) { + case KScreen::OsdAction::NoAction: + qCDebug(KSCREEN_KDED) << "OSD: no action"; + return; + case KScreen::OsdAction::SwitchToInternal: + qCDebug(KSCREEN_KDED) << "OSD: switch to internal"; + doApplyConfig(Generator::self()->displaySwitch(Generator::TurnOffExternal)); + return; + case KScreen::OsdAction::SwitchToExternal: + qCDebug(KSCREEN_KDED) << "OSD: switch to external"; + doApplyConfig(Generator::self()->displaySwitch(Generator::TurnOffEmbedded)); + return; + case KScreen::OsdAction::ExtendLeft: + qCDebug(KSCREEN_KDED) << "OSD: extend left"; + doApplyConfig(Generator::self()->displaySwitch(Generator::ExtendToLeft)); + return; + case KScreen::OsdAction::ExtendRight: + qCDebug(KSCREEN_KDED) << "OSD: extend right"; + doApplyConfig(Generator::self()->displaySwitch(Generator::ExtendToRight)); + return; + case KScreen::OsdAction::Clone: + qCDebug(KSCREEN_KDED) << "OSD: clone"; + doApplyConfig(Generator::self()->displaySwitch(Generator::Clone)); + return; + } + + Q_UNREACHABLE(); +} + void KScreenDaemon::applyIdealConfig() { - qCDebug(KSCREEN_KDED) << "Applying ideal config"; - doApplyConfig(Generator::self()->idealConfig(m_monitoredConfig)); + + if (m_monitoredConfig->connectedOutputs().count() < 2) { + KScreen::OsdManager::self()->hideOsd(); + doApplyConfig(Generator::self()->idealConfig(m_monitoredConfig)); + } else { + qCDebug(KSCREEN_KDED) << "Getting ideal config from user via OSD..."; + auto action = KScreen::OsdManager::self()->showActionSelector(); + connect(action, &KScreen::OsdAction::selected, + this, &KScreenDaemon::applyOsdAction); + } } void logConfig(const KScreen::ConfigPtr &config) { if (config) { foreach (auto o, config->outputs()) { if (o->isConnected()) { qCDebug(KSCREEN_KDED) << o; } } } } void KScreenDaemon::configChanged() { qCDebug(KSCREEN_KDED) << "Change detected"; logConfig(m_monitoredConfig); // Modes may have changed, fix-up current mode id Q_FOREACH(const KScreen::OutputPtr &output, m_monitoredConfig->outputs()) { if (output->isConnected() && output->isEnabled() && output->currentMode().isNull()) { qCDebug(KSCREEN_KDED) << "Current mode" << output->currentModeId() << "invalid, setting preferred mode" << output->preferredModeId(); output->setCurrentModeId(output->preferredModeId()); doApplyConfig(m_monitoredConfig); } } // Reset timer, delay the writeback m_saveTimer->start(); } void KScreenDaemon::saveCurrentConfig() { qCDebug(KSCREEN_KDED) << "Saving current config to file"; // We assume the config is valid, since it's what we got, but we are interested // in the "at least one enabled screen" check const bool valid = KScreen::Config::canBeApplied(m_monitoredConfig, KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen); if (valid) { Serializer::saveConfig(m_monitoredConfig, Serializer::configId(m_monitoredConfig)); logConfig(m_monitoredConfig); } else { qCWarning(KSCREEN_KDED) << "Config does not have at least one screen enabled, WILL NOT save this config, this is not what user wants."; logConfig(m_monitoredConfig); } } void KScreenDaemon::showOsd(const QString &icon, const QString &text) { QDBusMessage msg = QDBusMessage::createMethodCall( QLatin1Literal("org.kde.plasmashell"), QLatin1Literal("/org/kde/osdService"), QLatin1Literal("org.kde.osdService"), QLatin1Literal("showText") ); msg << icon << text; QDBusConnection::sessionBus().asyncCall(msg); } +void KScreenDaemon::showOutputIdentifier() +{ + KScreen::OsdManager::self()->showOutputIdentifiers(); +} + void KScreenDaemon::displayButton() { qCDebug(KSCREEN_KDED) << "displayBtn triggered"; QString message = i18nc("OSD text after XF86Display button press", "No External Display"); if (m_monitoredConfig && m_monitoredConfig->connectedOutputs().count() > 1) { message = i18nc("OSD text after XF86Display button press", "Changing Screen Layout"); } - showOsd(QStringLiteral("preferences-desktop-display-randr"), message); if (m_buttonTimer->isActive()) { qCDebug(KSCREEN_KDED) << "Too fast, cowboy"; return; } m_buttonTimer->start(); } void KScreenDaemon::resetDisplaySwitch() { qCDebug(KSCREEN_KDED) << "resetDisplaySwitch()"; m_iteration = Generator::None; } void KScreenDaemon::applyGenericConfig() { if (m_iteration == Generator::ExtendToRight) { m_iteration = Generator::None; } m_iteration = Generator::DisplaySwitchAction(static_cast(m_iteration) + 1); qCDebug(KSCREEN_KDED) << "displayButton: " << m_iteration; + static QHash actionMessages({ + {Generator::DisplaySwitchAction::None, i18nc("osd when displaybutton is pressed", "No Action")}, + {Generator::DisplaySwitchAction::Clone, i18nc("osd when displaybutton is pressed", "Cloned Display")}, + {Generator::DisplaySwitchAction::ExtendToLeft, i18nc("osd when displaybutton is pressed", "Extend Left")}, + {Generator::DisplaySwitchAction::TurnOffEmbedded, i18nc("osd when displaybutton is pressed", "External Only")}, + {Generator::DisplaySwitchAction::TurnOffExternal, i18nc("osd when displaybutton is pressed", "Internal Only")}, + {Generator::DisplaySwitchAction::ExtendToRight, i18nc("osd when displaybutton is pressed", "Extended Right")} + }); + const QString &message = actionMessages.value(m_iteration); + + // We delay the OSD for two seconds and hope that X and hardware are done setting everything up. + QTimer::singleShot(2000, + [message]() { + KScreen::OsdManager::self()->showOsd(QStringLiteral("preferences-desktop-display-randr"), message); + } + ); + doApplyConfig(Generator::self()->displaySwitch(m_iteration)); } void KScreenDaemon::lidClosedChanged(bool lidIsClosed) { // Ignore this when we don't have any external monitors, we can't turn off our // only screen if (m_monitoredConfig->connectedOutputs().count() == 1) { return; } if (lidIsClosed) { // Lid is closed, now we wait for couple seconds to find out whether it // will trigger a suspend (see Device::aboutToSuspend), or whether we should // turn off the screen qCDebug(KSCREEN_KDED) << "Lid closed, waiting to see if the computer goes to sleep..."; m_lidClosedTimer->start(); return; } else { qCDebug(KSCREEN_KDED) << "Lid opened!"; // We should have a config with "_lidOpened" suffix lying around. If not, // then the configuration has changed while the lid was closed and we just // use applyConfig() and see what we can do ... const QString openConfigId = Serializer::configId(m_monitoredConfig) + QStringLiteral("_lidOpened"); if (Serializer::configExists(openConfigId)) { const KScreen::ConfigPtr openedConfig = Serializer::config(m_monitoredConfig, openConfigId); Serializer::removeConfig(openConfigId); doApplyConfig(openedConfig); } } } void KScreenDaemon::lidClosedTimeout() { // Make sure nothing has changed in the past second... :-) if (!Device::self()->isLidClosed()) { return; } // If we are here, it means that closing the lid did not result in suspend // action. // FIXME: This could be simply because the suspend took longer than m_lidClosedTimer // timeout. Ideally we need to be able to look into PowerDevil config to see // what's the configured action for lid events, but there's no API to do that // and I'm no parsing PowerDevil's configs... qCDebug(KSCREEN_KDED) << "Lid closed without system going to suspend -> turning off the screen"; for (KScreen::OutputPtr &output : m_monitoredConfig->outputs()) { if (output->type() == KScreen::Output::Panel) { if (output->isConnected() && output->isEnabled()) { // Save the current config with opened lid, just so that we know // how to restore it later const QString configId = Serializer::configId(m_monitoredConfig) + QStringLiteral("_lidOpened"); Serializer::saveConfig(m_monitoredConfig, configId); disableOutput(m_monitoredConfig, output); doApplyConfig(m_monitoredConfig); return; } } } } void KScreenDaemon::outputConnectedChanged() { if (!m_changeCompressor->isActive()) { m_changeCompressor->start(); } resetDisplaySwitch(); KScreen::Output *output = qobject_cast(sender()); qCDebug(KSCREEN_KDED) << "outputConnectedChanged():" << output->name(); if (output->isConnected()) { Q_EMIT outputConnected(output->name()); if (!Serializer::configExists(m_monitoredConfig)) { Q_EMIT unknownOutputConnected(output->name()); } } } void KScreenDaemon::monitorConnectedChange() { KScreen::OutputList outputs = m_monitoredConfig->outputs(); Q_FOREACH(const KScreen::OutputPtr &output, outputs) { connect(output.data(), &KScreen::Output::isConnectedChanged, this, &KScreenDaemon::outputConnectedChanged, Qt::UniqueConnection); } connect(m_monitoredConfig.data(), &KScreen::Config::outputAdded, this, [this] (const KScreen::OutputPtr output) { if (output->isConnected()) { m_changeCompressor->start(); } connect(output.data(), &KScreen::Output::isConnectedChanged, this, &KScreenDaemon::outputConnectedChanged, Qt::UniqueConnection); }, Qt::UniqueConnection ); connect(m_monitoredConfig.data(), &KScreen::Config::outputRemoved, this, &KScreenDaemon::applyConfig, static_cast(Qt::QueuedConnection | Qt::UniqueConnection)); } void KScreenDaemon::setMonitorForChanges(bool enabled) { if (m_monitoring == enabled) { return; } qCDebug(KSCREEN_KDED) << "Monitor for changes: " << enabled; m_monitoring = enabled; if (m_monitoring) { connect(KScreen::ConfigMonitor::instance(), &KScreen::ConfigMonitor::configurationChanged, this, &KScreenDaemon::configChanged, Qt::UniqueConnection); } else { disconnect(KScreen::ConfigMonitor::instance(), &KScreen::ConfigMonitor::configurationChanged, this, &KScreenDaemon::configChanged); } } void KScreenDaemon::disableOutput(KScreen::ConfigPtr &config, KScreen::OutputPtr &output) { const QRect geom = output->geometry(); qCDebug(KSCREEN_KDED) << "Laptop geometry:" << geom << output->pos() << (output->currentMode() ? output->currentMode()->size() : QSize()); // Move all outputs right from the @p output to left for (KScreen::OutputPtr &otherOutput : config->outputs()) { if (otherOutput == output || !otherOutput->isConnected() || !otherOutput->isEnabled()) { continue; } QPoint otherPos = otherOutput->pos(); if (otherPos.x() >= geom.right() && otherPos.y() >= geom.top() && otherPos.y() <= geom.bottom()) { otherPos.setX(otherPos.x() - geom.width()); } qCDebug(KSCREEN_KDED) << "Moving" << otherOutput->name() << "from" << otherOutput->pos() << "to" << otherPos; otherOutput->setPos(otherPos); } // Disable the output output->setEnabled(false); } KScreen::OutputPtr KScreenDaemon::findEmbeddedOutput(const KScreen::ConfigPtr &config) { Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { if (output->type() == KScreen::Output::Panel) { return output; } } return KScreen::OutputPtr(); } - #include "daemon.moc" diff --git a/kded/daemon.h b/kded/daemon.h index ba48e1c..f95d809 100644 --- a/kded/daemon.h +++ b/kded/daemon.h @@ -1,85 +1,88 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * 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, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #ifndef KSCREEN_DAEMON_H #define KSCREEN_DAEMON_H #include #include #include #include "generator.h" +#include "osdmanager.h" class QTimer; namespace KScreen { class ConfigOperation; } class Q_DECL_EXPORT KScreenDaemon : public KDEDModule { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KScreen") public: KScreenDaemon(QObject *parent, const QList&); virtual ~KScreenDaemon(); public Q_SLOTS: virtual void requestConfig(); void configReady(KScreen::ConfigOperation *op); void init(); void applyConfig(); void applyKnownConfig(); void applyIdealConfig(); void configChanged(); void saveCurrentConfig(); void displayButton(); void resetDisplaySwitch(); void applyGenericConfig(); void lidClosedChanged(bool lidIsClosed); void lidClosedTimeout(); void setMonitorForChanges(bool enabled); void outputConnectedChanged(); + void showOutputIdentifier(); + void applyOsdAction(KScreen::OsdAction::Action action); Q_SIGNALS: void outputConnected(const QString &outputName); void unknownOutputConnected(const QString &outputName); protected: virtual void doApplyConfig(const KScreen::ConfigPtr &config); void monitorConnectedChange(); static KScreen::OutputPtr findEmbeddedOutput(const KScreen::ConfigPtr &config); void disableOutput(KScreen::ConfigPtr &config, KScreen::OutputPtr &output); void showOsd(const QString &icon, const QString &text); KScreen::ConfigPtr m_monitoredConfig; Generator::DisplaySwitchAction m_iteration; bool m_monitoring; QTimer* m_changeCompressor; QTimer* m_buttonTimer; QTimer* m_saveTimer; QTimer* m_lidClosedTimer; }; #endif /*KSCREEN_DAEMON_H*/ diff --git a/kded/osd.cpp b/kded/osd.cpp new file mode 100644 index 0000000..be45020 --- /dev/null +++ b/kded/osd.cpp @@ -0,0 +1,212 @@ +/* + * Copyright 2014 Martin Klapetek + * Copyright 2016 Sebastian Kügler + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "osd.h" +#include "utils.h" +#include "kscreen_daemon_debug.h" + +#include + +#include +#include +#include +#include + + +using namespace KScreen; + +Osd::Osd(const KScreen::OutputPtr output, QObject *parent) + : QObject(parent) + , m_output(output) +{ + connect(output.data(), &KScreen::Output::isConnectedChanged, + this, &Osd::onOutputAvailabilityChanged); + connect(output.data(), &KScreen::Output::isEnabledChanged, + this, &Osd::onOutputAvailabilityChanged); + connect(output.data(), &KScreen::Output::currentModeIdChanged, + this, &Osd::updatePosition); + connect(output.data(), &KScreen::Output::destroyed, + this, &Osd::hideOsd); +} + +Osd::~Osd() +{ +} + +bool Osd::initOsd() +{ + if (m_osdObject) { + return true; + } + + const QString &osdPath = QStandardPaths::locate(QStandardPaths::QStandardPaths::GenericDataLocation, QStringLiteral("kded_kscreen/qml/Osd.qml")); + if (osdPath.isEmpty()) { + qCWarning(KSCREEN_KDED) << "Failed to find OSD QML file" << osdPath; + return false; + } + + m_osdObject = new KDeclarative::QmlObject(this); + m_osdObject->setSource(QUrl::fromLocalFile(osdPath)); + + if (m_osdObject->status() != QQmlComponent::Ready) { + qCWarning(KSCREEN_KDED) << "Failed to load OSD QML file" << osdPath; + delete m_osdObject; + m_osdObject = nullptr; + return false; + } + + m_timeout = m_osdObject->rootObject()->property("timeout").toInt(); + m_osdTimer = new QTimer(this); + m_osdTimer->setSingleShot(true); + connect(m_osdTimer, &QTimer::timeout, this, &Osd::hideOsd); + return true; + +} + +void Osd::showGenericOsd(const QString &icon, const QString &text) +{ + if (!initOsd()) { + return; + } + + m_outputGeometry = m_output->geometry(); + auto *rootObject = m_osdObject->rootObject(); + rootObject->setProperty("itemSource", QStringLiteral("OsdItem.qml")); + rootObject->setProperty("infoText", text); + rootObject->setProperty("icon", icon); + + showOsd(); +} + +void Osd::showOutputIdentifier(const KScreen::OutputPtr output) +{ + if (!initOsd()) { + return; + } + + m_outputGeometry = output->geometry(); + + auto *rootObject = m_osdObject->rootObject(); + auto mode = output->currentMode(); + QSize realSize = mode->size(); + if (!output->isHorizontal()) { + realSize.transpose(); + } + rootObject->setProperty("itemSource", QStringLiteral("OutputIdentifier.qml")); + rootObject->setProperty("modeName", Utils::sizeToString(realSize)); + rootObject->setProperty("outputName", Utils::outputName(output)); + showOsd(); +} + +void Osd::showActionSelector() +{ + if (!initOsd()) { + return; + } + + m_outputGeometry = m_output->geometry(); + auto *rootObject = m_osdObject->rootObject(); + rootObject->setProperty("itemSource", QStringLiteral("OsdSelector.qml")); + rootObject->setProperty("timeout", 0); + rootObject->setProperty("outputOnly", false); + auto osdItem = rootObject->property("osdItem").value(); + connect(osdItem, SIGNAL(clicked(int)), + this, SLOT(onOsdActionSelected(int))); + m_timeout = 0; // no timeout for this one + + showOsd(); +} + +void Osd::onOsdActionSelected(int action) +{ + Q_EMIT osdActionSelected(static_cast(action)); + hideOsd(); +} + +void Osd::onOutputAvailabilityChanged() +{ + if (!m_output || !m_output->isConnected() || !m_output->isEnabled() || !m_output->currentMode()) { + hideOsd(); + } +} + +void Osd::updatePosition() +{ + if (!initOsd()) { + return; + } + + const auto geometry = m_output->geometry(); + if (!geometry.isValid()) { + hideOsd(); + } + + auto *rootObject = m_osdObject->rootObject(); + + const int dialogWidth = rootObject->property("width").toInt(); + const int dialogHeight = rootObject->property("height").toInt(); + const int relx = geometry.x(); + const int rely = geometry.y(); + const int pos_x = relx + (geometry.width() - dialogWidth) / 2; + const int pos_y = rely + (geometry.height() - dialogHeight) / 2; + + rootObject->setProperty("x", pos_x); + rootObject->setProperty("y", pos_y); +} + +void Osd::showOsd() +{ + m_osdTimer->stop(); + + auto *rootObject = m_osdObject->rootObject(); + + // only animate on X11, wayland plugin doesn't support this and + // pukes loads of warnings into our logs + if (qGuiApp->platformName() == QLatin1String("xcb")) { + if (rootObject->property("timeout").toInt() > 0) { + rootObject->setProperty("animateOpacity", false); + rootObject->setProperty("opacity", 1); + rootObject->setProperty("visible", true); + rootObject->setProperty("animateOpacity", true); + rootObject->setProperty("opacity", 0); + } else { + rootObject->setProperty("visible", true); + } + } else { + rootObject->setProperty("visible", true); + } + QTimer::singleShot(0, this, &Osd::updatePosition); + if (m_timeout > 0) { + m_osdTimer->start(m_timeout); + } +} + +void Osd::hideOsd() +{ + if (!initOsd()) { + return; + } + + auto *rootObject = m_osdObject->rootObject(); + if (!rootObject) { + return; + } + rootObject->setProperty("visible", false); +} + diff --git a/kded/osd.h b/kded/osd.h new file mode 100644 index 0000000..55e452f --- /dev/null +++ b/kded/osd.h @@ -0,0 +1,74 @@ +/* + * Copyright 2014 Martin Klapetek + * Copyright 2016 Sebastian Kügler + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KSCREEN_OSD_H +#define KSCREEN_OSD_H + +#include +#include +#include + +#include + +#include "osdmanager.h" + +namespace KDeclarative { + class QmlObject; +} + +class QTimer; + +namespace KScreen { + +class Osd : public QObject { + + Q_OBJECT + +public: + Osd(const KScreen::OutputPtr output, QObject *parent = nullptr); + ~Osd() override; + + void showGenericOsd(const QString &icon, const QString &text); + void showOutputIdentifier(const KScreen::OutputPtr output); + void showActionSelector(); + void hideOsd(); + +Q_SIGNALS: + void osdActionSelected(OsdAction::Action action); + +private Q_SLOTS: + void onOsdActionSelected(int action); + void onOutputAvailabilityChanged(); + +private: + bool initOsd(); + void showOsd(); + void updatePosition(); + + KScreen::OutputPtr m_output; + QRect m_outputGeometry; + KDeclarative::QmlObject *m_osdObject = nullptr; + QTimer *m_osdTimer = nullptr; + int m_timeout = 0; + +}; + +} // ns + +#endif // KSCREEN_OSD_H diff --git a/kded/osdmanager.cpp b/kded/osdmanager.cpp new file mode 100644 index 0000000..4982c3f --- /dev/null +++ b/kded/osdmanager.cpp @@ -0,0 +1,221 @@ +/* + * Copyright 2016 Sebastian Kügler + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "osdmanager.h" +#include "osd.h" +#include "kscreen_daemon_debug.h" + +#include +#include +#include + +#include + +#include + +namespace KScreen { + +OsdManager* OsdManager::s_instance = nullptr; + +OsdAction::OsdAction(QObject *parent) + : QObject(parent) +{ +} + +class OsdActionImpl : public OsdAction +{ + Q_OBJECT +public: + OsdActionImpl(QObject *parent = nullptr) + : OsdAction(parent) + {} + + void setOsd(Osd *osd) { + connect(osd, &Osd::osdActionSelected, + this, [this](Action action) { + Q_EMIT selected(action); + deleteLater(); + }); + } +}; + +OsdManager::OsdManager(QObject *parent) + : QObject(parent) + , m_cleanupTimer(new QTimer(this)) +{ + qmlRegisterUncreatableType("org.kde.KScreen", 1, 0, "OsdAction", "You cannot create OsdAction"); + + // free up memory when the osd hasn't been used for more than 1 minute + m_cleanupTimer->setInterval(60000); + m_cleanupTimer->setSingleShot(true); + connect(m_cleanupTimer, &QTimer::timeout, this, [this]() { + hideOsd(); + }); + QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kscreen.osdService")); + if (!QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/kscreen/osdService"), this, QDBusConnection::ExportAllSlots)) { + qCWarning(KSCREEN_KDED) << "Failed to registerObject"; + } +} + +void OsdManager::hideOsd() +{ + qDeleteAll(m_osds); + m_osds.clear(); +} + +OsdManager::~OsdManager() +{ +} + +OsdManager* OsdManager::self() +{ + if (!OsdManager::s_instance) { + s_instance = new OsdManager(); + } + return s_instance; +} + +void OsdManager::showOutputIdentifiers() +{ + connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, + this, &OsdManager::slotIdentifyOutputs); +} + +void OsdManager::slotIdentifyOutputs(KScreen::ConfigOperation *op) +{ + if (op->hasError()) { + return; + } + + const KScreen::ConfigPtr config = qobject_cast(op)->config(); + + Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { + if (!output->isConnected() || !output->isEnabled() || !output->currentMode()) { + continue; + } + auto osd = m_osds.value(output->name()); + if (!osd) { + osd = new KScreen::Osd(output, this); + m_osds.insert(output->name(), osd); + } + osd->showOutputIdentifier(output); + } + m_cleanupTimer->start(); +} + +void OsdManager::showOsd(const QString& icon, const QString& text) +{ + qDeleteAll(m_osds); + m_osds.clear(); + connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, + this, [this, icon, text] (KScreen::ConfigOperation *op) { + if (op->hasError()) { + return; + } + + const KScreen::ConfigPtr config = qobject_cast(op)->config(); + + Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { + if (!output->isConnected() || !output->isEnabled() || !output->currentMode()) { + continue; + } + auto osd = m_osds.value(output->name()); + if (!osd) { + osd = new KScreen::Osd(output, this); + m_osds.insert(output->name(), osd); + } + osd->showGenericOsd(icon, text); + } + m_cleanupTimer->start(); + } + ); +} + +OsdAction *OsdManager::showActionSelector() +{ + hideOsd(); + + OsdActionImpl *action = new OsdActionImpl(this); + connect(action, &OsdActionImpl::selected, + this, [this]() { + for (auto osd : qAsConst(m_osds)) { + osd->hideOsd(); + } + }); + connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, + this, [this, action](const KScreen::ConfigOperation *op) { + if (op->hasError()) { + qCWarning(KSCREEN_KDED) << op->errorString(); + return; + } + + // Show selector on alll enabled screens + const auto outputs = op->config()->outputs(); + KScreen::OutputPtr osdOutput; + for (const auto &output : outputs) { + if (!output->isConnected() || !output->isEnabled() || !output->currentMode()) { + continue; + } + + // Prefer laptop screen + if (output->type() == KScreen::Output::Panel) { + osdOutput = output; + break; + } + + // Fallback to primary + if (output->isPrimary()) { + osdOutput = output; + break; + } + } + // no laptop or primary screen, just take the first usable one + if (!osdOutput) { + for (const auto &output : outputs) { + if (output->isConnected() && output->isEnabled() && output->currentMode()) { + osdOutput = output; + break; + } + } + } + + if (!osdOutput) { + // huh!? + return; + } + + KScreen::Osd* osd = nullptr; + if (m_osds.contains(osdOutput->name())) { + osd = m_osds.value(osdOutput->name()); + } else { + osd = new KScreen::Osd(osdOutput, this); + m_osds.insert(osdOutput->name(), osd); + } + action->setOsd(osd); + osd->showActionSelector(); + m_cleanupTimer->start(); + } + ); + + return action; +} + + +} + +#include "osdmanager.moc" diff --git a/kded/osdmanager.h b/kded/osdmanager.h new file mode 100644 index 0000000..75c6250 --- /dev/null +++ b/kded/osdmanager.h @@ -0,0 +1,81 @@ +/* + * Copyright 2016 Sebastian Kügler + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef OSDM_H +#define OSDM_H + +#include +#include +#include +#include + +class QmlObject; + +namespace KScreen { + +class ConfigOperation; +class Osd; +class Output; + +class OsdAction : public QObject +{ + Q_OBJECT +public: + enum Action { + NoAction, + SwitchToExternal, + SwitchToInternal, + Clone, + ExtendLeft, + ExtendRight + }; + Q_ENUM(Action) + +Q_SIGNALS: + void selected(Action action); + +protected: + explicit OsdAction(QObject *parent = nullptr); +}; + + +class OsdManager : public QObject { + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kscreen.osdService") + +public: + ~OsdManager() override; + static OsdManager* self(); + +public Q_SLOTS: + void showOutputIdentifiers(); + void showOsd(const QString &icon, const QString &text); + void hideOsd(); + OsdAction *showActionSelector(); + +private: + OsdManager(QObject *parent = nullptr); + void slotIdentifyOutputs(KScreen::ConfigOperation *op); + QMap m_osds; + + static OsdManager* s_instance; + QTimer* m_cleanupTimer; +}; + +} // ns +#endif // OSDM_H diff --git a/kded/qml/Osd.qml b/kded/qml/Osd.qml new file mode 100644 index 0000000..e04a1e0 --- /dev/null +++ b/kded/qml/Osd.qml @@ -0,0 +1,64 @@ +/* + * Copyright 2014 Martin Klapetek + * Copyright 2016 Sebastian Kügler + * + * 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 . + */ + +import QtQuick 2.0 +import QtQuick.Window 2.2 +import org.kde.plasma.core 2.0 as PlasmaCore + +PlasmaCore.Dialog { + id: root + location: PlasmaCore.Types.Floating + type: PlasmaCore.Dialog.OnScreenDisplay + outputOnly: true + + // OSD Timeout in msecs - how long it will stay on the screen + property int timeout: 5000 + + // Icon name to display + property string icon + property string infoText + property string outputName + property string modeName + property bool animateOpacity: false + property string itemSource + property QtObject osdItem + + Behavior on opacity { + SequentialAnimation { + // prevent press and hold from flickering + PauseAnimation { duration: root.timeout * 0.8 } + + NumberAnimation { + duration: root.timeout * 0.2 + easing.type: Easing.InQuad + } + } + enabled: root.timeout > 0 && root.animateOpacity + } + + mainItem: Loader { + source: itemSource + onItemChanged: { + if (item != undefined) { + item.rootItem = root; + root.osdItem = item + } + } + + } +} diff --git a/kded/qml/OsdItem.qml b/kded/qml/OsdItem.qml new file mode 100644 index 0000000..43750f5 --- /dev/null +++ b/kded/qml/OsdItem.qml @@ -0,0 +1,56 @@ +/* + * Copyright 2014 Martin Klapetek + * Copyright 2016 Sebastian Kügler + * + * 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 . + */ + +import QtQuick 2.5 +import QtQuick.Window 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras + +Item { + property QtObject rootItem + + height: Math.min(units.gridUnit * 15, Screen.desktopAvailableHeight / 5) + width: height + + PlasmaCore.IconItem { + id: icon + height: parent.height - label.height - ((units.smallSpacing/2) * 3) + width: parent.width + source: rootItem.icon + } + + PlasmaExtras.Heading { + id: label + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + margins: Math.floor(units.smallSpacing / 2) + } + + text: rootItem.infoText + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + maximumLineCount: 2 + elide: Text.ElideLeft + minimumPointSize: theme.defaultFont.pointSize + fontSizeMode: Text.HorizontalFit + } + Component.onCompleted: print("osditem loaded..." + root.infoText); +} diff --git a/kded/qml/OsdSelector.qml b/kded/qml/OsdSelector.qml new file mode 100644 index 0000000..0db6efd --- /dev/null +++ b/kded/qml/OsdSelector.qml @@ -0,0 +1,116 @@ +/* + * Copyright 2017 Daniel Vrátil + * + * 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 . + */ + +import QtQuick 2.5 +import QtQuick.Window 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + +import org.kde.KScreen 1.0 + +Item { + id: root + property QtObject rootItem + + signal clicked(int actionId) + + height: Math.min(units.gridUnit * 15, Screen.desktopAvailableHeight / 5) + width: buttonRow.width + + PlasmaComponents.ButtonRow { + id: buttonRow + + exclusive: false + + height: parent.height - label.height - ((units.smallSpacing/2) * 3) + width: (actionRepeater.count * height) + ((actionRepeater.count - 1) * buttonRow.spacing); + + Repeater { + id: actionRepeater + model: [ + { + iconSource: "osd-shutd-laptop", + label: i18n("Switch to external screen"), + action: OsdAction.SwitchToExternal + }, + { + iconSource: "osd-shutd-screen", + label: i18n("Switch to laptop screen"), + action: OsdAction.SwitchToInternal + }, + { + iconSource: "osd-duplicate", + label: i18n("Unify outputs"), + action: OsdAction.Clonse + }, + { + iconSource: "osd-sbs-left", + label: i18n("Extend to left"), + action: OsdAction.ExtendLeft + }, + { + iconSource: "osd-sbs-sright", + label: i18n("Extend to right"), + action: OsdAction.ExtendRight + }, + { + iconSource: "dialog-cancel", + label: i18n("Leave unchanged"), + action: OsdAction.NoAction + } + ] + delegate: PlasmaComponents.Button { + PlasmaCore.IconItem { + source: modelData.iconSource + height: buttonRow.height - ((units.smallSpacing / 2) * 3) + width: height + anchors.centerIn: parent + } + height: parent.height + width: height + + onHoveredChanged: rootItem.infoText = (hovered ? modelData.label : "") + + onClicked: root.clicked(modelData.action) + } + } + } + + // TODO: keep? remove? + PlasmaExtras.Heading { + id: label + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + margins: Math.floor(units.smallSpacing / 2) + } + + text: rootItem.infoText + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + maximumLineCount: 2 + elide: Text.ElideLeft + minimumPointSize: theme.defaultFont.pointSize + fontSizeMode: Text.HorizontalFit + } + + Component.onCompleted: print("OsdSelector loaded..."); +} + diff --git a/kded/qml/OutputIdentifier.qml b/kded/qml/OutputIdentifier.qml new file mode 100644 index 0000000..6245928 --- /dev/null +++ b/kded/qml/OutputIdentifier.qml @@ -0,0 +1,57 @@ +/* + * Copyright 2016 Sebastian Kügler + * + * 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 . + */ + +import QtQuick 2.5 +import QtQuick.Layouts 1.3 +import QtQuick.Window 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +ColumnLayout { + + property QtObject rootItem + + property string outputName: rootItem ? rootItem.outputName : "" + property string modeName: rootItem ? rootItem.modeName : "" + + PlasmaComponents.Label { + id: displayName + + Layout.maximumWidth: Screen.width * 0.8 + Layout.maximumHeight: Screen.height * 0.8 + Layout.margins: units.largeSpacing + Layout.bottomMargin: units.smallSpacing + + text: root.outputName + font.pointSize: theme.defaultFont.pointSize * 3 + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + maximumLineCount: 2 + elide: Text.ElideLeft + } + + PlasmaComponents.Label { + id: modeLabel; + + Layout.fillWidth: true + Layout.bottomMargin: units.largeSpacing + + text: root.modeName; + horizontalAlignment: Text.AlignHCenter; + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5cf202e..6509a3d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1 +1,2 @@ -add_subdirectory(kded) \ No newline at end of file +add_subdirectory(kded) +add_subdirectory(osd) diff --git a/tests/osd/CMakeLists.txt b/tests/osd/CMakeLists.txt new file mode 100644 index 0000000..a98ed36 --- /dev/null +++ b/tests/osd/CMakeLists.txt @@ -0,0 +1,23 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/kcm/src + ${CMAKE_BINARY_DIR}/kded +) + +add_executable(osdtest main.cpp + osdtest.cpp + ../../kded/osd.cpp + ../../kded/osdmanager.cpp + ../../kcm/src/utils.cpp +) + +target_link_libraries(osdtest Qt5::Core + Qt5::DBus + Qt5::Quick + Qt5::Qml + KF5::Screen + KF5::I18n + KF5::Declarative +) + +add_test(kscreen-kded-osdtest osdtest) +ecm_mark_as_test(osdtest) diff --git a/tests/osd/main.cpp b/tests/osd/main.cpp new file mode 100644 index 0000000..4bbff93 --- /dev/null +++ b/tests/osd/main.cpp @@ -0,0 +1,64 @@ +/************************************************************************************* + * Copyright 2014-2016 by Sebastian Kügler * + * * + * 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, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + *************************************************************************************/ + +#include "osdtest.h" + +#include +#include + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + QCommandLineOption dbus = QCommandLineOption(QStringList() << QStringLiteral("d") << "dbus", + QStringLiteral("Call over dbus")); + QCommandLineOption outputid = QCommandLineOption(QStringList() << QStringLiteral("o") << "outputidentifiers", + QStringLiteral("Show output identifier")); + QCommandLineOption icon = QCommandLineOption(QStringList() << QStringLiteral("i") << "icon", + QStringLiteral("Icon to use for OSD"), QStringLiteral("preferences-desktop-display-randr")); + QCommandLineOption message = QCommandLineOption(QStringList() << QStringLiteral("m") << "message", + QStringLiteral("Icon to use for OSD"), QStringLiteral("OSD Test")); + QCommandLineOption selector = QCommandLineOption({ QStringLiteral("s"), QStringLiteral("selector") }, + QStringLiteral("Show new screen action selector")); + KScreen::OsdTest osdtest; + QCommandLineParser parser; + parser.addHelpOption(); + parser.addOption(dbus); + parser.addOption(outputid); + parser.addOption(icon); + parser.addOption(message); + parser.addOption(selector); + parser.process(app); + + + + if (parser.isSet(dbus)) { + osdtest.setUseDBus(true); + } + if (parser.isSet(outputid)) { + osdtest.showOutputIdentifiers(); + } else if (parser.isSet(selector)) { + osdtest.showActionSelector(); + } else { + osdtest.showGenericOsd(parser.value(icon), parser.value(message)); + } + if (parser.isSet(outputid)) { + } + + return app.exec(); +} diff --git a/tests/osd/osdtest.cpp b/tests/osd/osdtest.cpp new file mode 100644 index 0000000..aedae2b --- /dev/null +++ b/tests/osd/osdtest.cpp @@ -0,0 +1,93 @@ +/************************************************************************************* + * Copyright 2016 Sebastian Kügler * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "osdtest.h" +#include "../../kded/osdmanager.h" + +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(KSCREEN_KDED, "kscreen.kded") + +namespace KScreen { +OsdTest::OsdTest(QObject *parent) + : QObject(parent) +{ +} + +OsdTest::~OsdTest() +{ +} + +void OsdTest::showOutputIdentifiers() +{ + if (!m_useDBus) { + QTimer::singleShot(5500, qApp, &QCoreApplication::quit); + KScreen::OsdManager::self()->showOutputIdentifiers(); + } else { + QDBusMessage msg = QDBusMessage::createMethodCall( + QLatin1Literal("org.kde.kscreen.osdService"), + QLatin1Literal("/org/kde/kscreen/osdService"), + QLatin1Literal("org.kde.kscreen.osdService"), + QLatin1Literal("showOutputIdentifiers") + ); + //msg << icon << text; + QDBusConnection::sessionBus().asyncCall(msg); + + qCWarning(KSCREEN_KDED) << "Sent dbus message."; + QTimer::singleShot(500, qApp, &QCoreApplication::quit); + } +} + + +void OsdTest::setUseDBus(bool yesno) +{ + m_useDBus = yesno; +} + +void OsdTest::showGenericOsd(const QString& icon, const QString& message) +{ + if (!m_useDBus) { + QTimer::singleShot(5500, qApp, &QCoreApplication::quit); + KScreen::OsdManager::self()->showOsd(!icon.isEmpty() ? icon : QStringLiteral("preferences-desktop-display-randr"), + !message.isEmpty() ? message : QStringLiteral("On-Screen-Display")); + } else { + qCWarning(KSCREEN_KDED) << "Implement me."; + QTimer::singleShot(100, qApp, &QCoreApplication::quit); + } +} + +void OsdTest::showActionSelector() +{ + if (!m_useDBus) { + auto action = KScreen::OsdManager::self()->showActionSelector(); + connect(action, &KScreen::OsdAction::selected, + [](KScreen::OsdAction::Action action) { + qCDebug(KSCREEN_KDED) << "Selected action:" << action; + qApp->quit(); + }); + } else { + qCWarning(KSCREEN_KDED) << "Implement me."; + QTimer::singleShot(100, qApp, &QCoreApplication::quit); + } +} + +} // ns diff --git a/tests/osd/osdtest.h b/tests/osd/osdtest.h new file mode 100644 index 0000000..3cf3c0a --- /dev/null +++ b/tests/osd/osdtest.h @@ -0,0 +1,48 @@ +/************************************************************************************* + * Copyright 2016 Sebastian Kügler * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_OSDTEST_H +#define KSCREEN_OSDTEST_H + +#include + + +namespace KScreen +{ + +class OsdTest : public QObject +{ + Q_OBJECT + +public: + explicit OsdTest(QObject *parent = nullptr); + virtual ~OsdTest(); + + void setUseDBus(bool yesno); + + void showGenericOsd(const QString &icon, const QString &message); + void showOutputIdentifiers(); + void showActionSelector(); + +private: + bool m_useDBus = false; +}; + +} // namespace + +#endif // KSCREEN_OSDTEST_H