From 6b1761316a20cf1ac013b9995b9925c1f0ecdda8 Mon Sep 17 00:00:00 2001 From: Igor Sikachyna Date: Mon, 12 Jul 2021 12:44:08 +0300 Subject: [PATCH] SYNERGY-856 - Force prevent sleep (#7047) * [UB-856] - Set display as busy * SYNERGY-856 - Prevent sleep on Mac * SYNERGY-856 - Prevent sleep on Linux * SYNERGY-856 - Update changelog * SYNERGY-856 - Set user activity on for Mac to prevent sleep * SYNERGY-856 - Prevent screen sleep continuosly * SYNERGY-856 - Try implementing DBus in the server/client * SYNERGY-856 - Move inhibitor to arch instead of gui * SYNERGY-856 - Undo undesired changes * SYNERGY-856 - Undo remaining undersired changes * SYNERGY-856 - Compile on MacOS * SYNERGY-856 - Reduce if/else depth * SYNERGY-856 - Remove AppDelegate include * SYNERGY-856 - Update inhibitor code to have more verbose debug output * SYNERGY-856 - Make methods static * SYNERGY-856 - Remove unused declarations --- ChangeLog | 2 + src/lib/arch/CMakeLists.txt | 6 +++ src/lib/arch/unix/ArchSystemUnix.cpp | 76 ++++++++++++++++++++++++++++ src/lib/arch/unix/ArchSystemUnix.h | 12 ++++- src/lib/platform/MSWindowsScreen.cpp | 2 + src/lib/platform/OSXScreen.mm | 6 ++- src/lib/platform/XWindowsScreen.cpp | 63 ++++++++++++++++++----- src/lib/platform/XWindowsScreen.h | 6 +++ 8 files changed, 156 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3f60fb993..67e1aed12 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,7 @@ Bug fixes: - #7036 Fix tray icon not changing theme on Big Sur - #7046 Fix MacOS 10.13 build - #7049 Scroll doesn't work in case of macOS is server +- #7047 Fix prevent sleep option on Mac and Windows Enhancements: - #6998 Remove functionality related to the screen saver synchronisation @@ -20,6 +21,7 @@ Enhancements: - #7030 | #7041 | #7043 Add user notification for secure input on Mac - #7044 Apply natural scroll setting independently on each client - #7040 Support "Kana" and "Eisu" keys on Japanese Apple Pro Keyboard (JIS) +- #7047 Add prevent sleep option support to Linux =========== v1.14.0-stable diff --git a/src/lib/arch/CMakeLists.txt b/src/lib/arch/CMakeLists.txt index 3bc0ccc29..2aa4cf65e 100644 --- a/src/lib/arch/CMakeLists.txt +++ b/src/lib/arch/CMakeLists.txt @@ -41,4 +41,10 @@ add_library(arch STATIC ${sources}) if (UNIX) target_link_libraries(arch ${CMAKE_DL_LIBS} ${libs}) + + if (NOT APPLE) + find_package (Qt5 COMPONENTS DBus) + include_directories(${Qt5DBus_INCLUDE_DIRS}) + target_link_libraries (arch Qt5::DBus) + endif() endif() diff --git a/src/lib/arch/unix/ArchSystemUnix.cpp b/src/lib/arch/unix/ArchSystemUnix.cpp index a25e95af1..2a21b1bda 100644 --- a/src/lib/arch/unix/ArchSystemUnix.cpp +++ b/src/lib/arch/unix/ArchSystemUnix.cpp @@ -23,6 +23,9 @@ #include #include #include +#ifndef __APPLE__ +#include +#endif // // ArchSystemUnix @@ -100,3 +103,76 @@ ArchSystemUnix::runCommand(const std::string& cmd) } return result; } + +#ifndef __APPLE__ +bool +ArchSystemUnix::DBusInhibitScreenCall(InhibitScreenServices serviceID, bool state, std::string& error) +{ + error = ""; + static const std::array services = + { + "org.freedesktop.ScreenSaver", + "org.gnome.SessionManager" + }; + static const std::array paths = + { + "/org/freedesktop/ScreenSaver", + "/org/gnome/SessionManager" + }; + static std::array cookies; + + auto serviceNum = static_cast(serviceID); + + QDBusConnection bus = QDBusConnection::sessionBus(); + if (!bus.isConnected()) + { + error = "bus failed to connect"; + return false; + } + + QDBusInterface screenSaverInterface( + services[serviceNum], + paths[serviceNum], + services[serviceNum], + bus); + + if (!screenSaverInterface.isValid()) + { + error = "screen saver interface failed to initialize"; + return false; + } + + QDBusReply reply; + if(state) + { + if (cookies[serviceNum]) + { + error = "coockies are not empty"; + return false; + } + + reply = screenSaverInterface.call("Inhibit", "Synergy", "Sleep is manually prevented by the Synergy preferences"); + if (reply.isValid()) + cookies[serviceNum] = reply.value(); + } + else + { + if(!cookies[serviceNum]) + { + error = "coockies are empty"; + return false; + } + reply = screenSaverInterface.call("UnInhibit", cookies[serviceNum]); + cookies[serviceNum] = 0; + } + + if(!reply.isValid()) + { + QDBusError qerror = reply.error(); + error = qerror.name().toStdString() + " : " + qerror.message().toStdString(); + return false; + } + + return true; +} +#endif diff --git a/src/lib/arch/unix/ArchSystemUnix.h b/src/lib/arch/unix/ArchSystemUnix.h index a8e9fa002..e4e6724e3 100644 --- a/src/lib/arch/unix/ArchSystemUnix.h +++ b/src/lib/arch/unix/ArchSystemUnix.h @@ -2,11 +2,11 @@ * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -36,4 +36,12 @@ public: virtual std::string getLibsUsed(void) const; static std::string runCommand(const std::string& cmd); + +#ifndef __APPLE__ + enum class InhibitScreenServices { + kScreenSaver, + kSessionManager + }; + static bool DBusInhibitScreenCall(InhibitScreenServices serviceID, bool state, std::string& error); +#endif }; diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 6034e20eb..ac1ca8be3 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -251,6 +251,7 @@ MSWindowsScreen::enable() if (App::instance().argsBase().m_preventSleep) { ArchMiscWindows::addBusyState(ArchMiscWindows::kSYSTEM); + ArchMiscWindows::addBusyState(ArchMiscWindows::kDISPLAY); } } @@ -270,6 +271,7 @@ MSWindowsScreen::disable() else { // allow the system to enter power saving mode ArchMiscWindows::removeBusyState(ArchMiscWindows::kSYSTEM); + ArchMiscWindows::removeBusyState(ArchMiscWindows::kDISPLAY); } // tell key state diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm index b1d31a2d9..0bf2731bc 100644 --- a/src/lib/platform/OSXScreen.mm +++ b/src/lib/platform/OSXScreen.mm @@ -756,8 +756,10 @@ OSXScreen::enable() { if(App::instance().argsBase().m_preventSleep) { CFStringRef reasonForActivity = CFSTR("Synergy application"); - IOReturn result = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, - kIOPMAssertionLevelOn, reasonForActivity, &m_sleepPreventionAssertionID); + + IOReturn result = IOPMAssertionCreateWithName(kIOPMAssertPreventUserIdleDisplaySleep, + kIOPMAssertionLevelOn, reasonForActivity, + &m_sleepPreventionAssertionID); if(result != kIOReturnSuccess) { m_sleepPreventionAssertionID = 0; LOG((CLOG_ERR "failed to disable system idle sleep")); diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 5a9633d1a..bf2472d66 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -2,11 +2,11 @@ * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -26,6 +26,8 @@ #include "synergy/Clipboard.h" #include "synergy/KeyMap.h" #include "synergy/XScreen.h" +#include "synergy/ArgsBase.h" +#include "synergy/App.h" #include "arch/XArch.h" #include "arch/Arch.h" #include "base/Log.h" @@ -129,11 +131,11 @@ XWindowsScreen::XWindowsScreen( if (mouseScrollDelta==0) m_mouseScrollDelta=120; s_screen = this; - + if (!disableXInitThreads) { // initializes Xlib support for concurrent threads. if (XInitThreads() == 0) - throw XArch("XInitThreads() returned zero"); + throw XArch("XInitThreads() returned zero"); } else { LOG((CLOG_DEBUG "skipping XInitThreads()")); } @@ -245,6 +247,12 @@ XWindowsScreen::enable() // warp the mouse to the cursor center fakeMouseMove(m_xCenter, m_yCenter); } + + // disable sleep if the flag is set + if (App::instance().argsBase().m_preventSleep && + !disableIdleSleep()) { + LOG((CLOG_INFO "Failed to prevent system from going to sleep")); + } } void @@ -263,6 +271,12 @@ XWindowsScreen::disable() if (!m_isPrimary && m_autoRepeat) { //XAutoRepeatOn(m_display); } + + // enable sleep when the display is disabled + if (App::instance().argsBase().m_preventSleep && + !enableIdleSleep()) { + LOG((CLOG_INFO "Failed to enable system idle sleep")); + } } void @@ -289,21 +303,21 @@ XWindowsScreen::enter() CARD16 powerlevel; BOOL enabled; if (DPMSQueryExtension(m_display, &dummy, &dummy) && - DPMSCapable(m_display) && - DPMSInfo(m_display, &powerlevel, &enabled)) + DPMSCapable(m_display) && + DPMSInfo(m_display, &powerlevel, &enabled)) { if (enabled && powerlevel != DPMSModeOn) DPMSForceLevel(m_display, DPMSModeOn); } #endif - + // unmap the hider/grab window. this also ungrabs the mouse and // keyboard if they're grabbed. XUnmapWindow(m_display, m_window); /* maybe call this if entering for the screensaver // set keyboard focus to root window. the screensaver should then - // pick up key events for when the user enters a password to unlock. + // pick up key events for when the user enters a password to unlock. XSetInputFocus(m_display, PointerRoot, PointerRoot, CurrentTime); */ @@ -1332,7 +1346,7 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*) XFreeEventData(m_display, cookie); return; } - XFreeEventData(m_display, cookie); + XFreeEventData(m_display, cookie); } } #endif @@ -1539,9 +1553,9 @@ XWindowsScreen::onKeyPress(XKeyEvent& xkey) false, false, key, mask, 1, keycode); } } - else { + else { LOG((CLOG_DEBUG1 "can't map keycode to key id")); - } + } } void @@ -1864,7 +1878,7 @@ XWindowsScreen::doSelectEvents(Window w) const // select events of interest. do this before querying the tree so // we'll get notifications of children created after the XQueryTree() // so we won't miss them. - XSelectInput(m_display, w, mask); + XSelectInput(m_display, w, mask); // recurse on child windows Window rw, pw, *cw; @@ -2156,7 +2170,7 @@ XWindowsScreen::selectXIRawMotion() mask.mask = (unsigned char*)calloc(mask.mask_len, sizeof(char)); mask.deviceid = XIAllMasterDevices; memset(mask.mask, 0, 2); - XISetMask(mask.mask, XI_RawKeyRelease); + XISetMask(mask.mask, XI_RawKeyRelease); XISetMask(mask.mask, XI_RawMotion); XISelectEvents(m_display, DefaultRootWindow(m_display), &mask, 1); free(mask.mask); @@ -2186,3 +2200,26 @@ XWindowsScreen::updateScrollDirection() scrollDirectionUpdateThread.detach(); } } + +bool XWindowsScreen::sleepInhibitCall(bool state, ArchSystemUnix::InhibitScreenServices serviceID) +{ + std::string error; + if(!ArchSystemUnix::DBusInhibitScreenCall(serviceID, state, error)) + { + LOG((CLOG_DEBUG "DBus inhibit error %s", error.c_str())); + return false; + } + return true; +} + +bool XWindowsScreen::disableIdleSleep() +{ + return sleepInhibitCall(true, ArchSystemUnix::InhibitScreenServices::kScreenSaver) || + sleepInhibitCall(true, ArchSystemUnix::InhibitScreenServices::kSessionManager); +} + +bool XWindowsScreen::enableIdleSleep() +{ + return sleepInhibitCall(false, ArchSystemUnix::InhibitScreenServices::kScreenSaver) || + sleepInhibitCall(false, ArchSystemUnix::InhibitScreenServices::kSessionManager); +} diff --git a/src/lib/platform/XWindowsScreen.h b/src/lib/platform/XWindowsScreen.h index facafa0f5..9e1c4e8c6 100644 --- a/src/lib/platform/XWindowsScreen.h +++ b/src/lib/platform/XWindowsScreen.h @@ -18,6 +18,7 @@ #pragma once +#include "arch/Arch.h" #include "synergy/PlatformScreen.h" #include "synergy/KeyMap.h" #include "common/stdset.h" @@ -117,6 +118,11 @@ private: void onError(); static int ioErrorHandler(Display*); + // sleep management + static bool sleepInhibitCall(bool state, ArchSystemUnix::InhibitScreenServices serviceID); + static bool disableIdleSleep(); + static bool enableIdleSleep(); + private: class KeyEventFilter { public: