Something went wrong. Try again.
Mixxx DJ software with MCP, Subsonic/Navidrome streaming library support — browse and load tracks from your self-hosted music server mixxx.org
subsonic navidrome streaming audio dj mac linux midi
Something went wrong. Try again.
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416/**Documentation:OSX: https://developer.apple.com/reference/iokit/1557134-iopmassertioncreatewithnameWindows: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373208(v=vs.85).aspxFreedesktop: https://people.freedesktop.org/~hadess/idle-inhibition-spec/re01.htmlXScreenSaver: https://linux.die.net/man/3/xscreensaversuspendGTK: https://developer.gnome.org/gtk3/stable/GtkApplication.html#gtk-application-inhibit
With the help of the following source codes:
https://github.com/videolan/vlc/blob/fbaa27ae2d7fcf5ccee7f0ca424ec0cc5bf01f4c/modules/gui/macosx/VLCInputManager.m#L299https://github.com/videolan/vlc/blob/fbaa27ae2d7fcf5ccee7f0ca424ec0cc5bf01f4c/modules/video_output/win32/events.c#L168https://github.com/GNOME/totem/blob/0c18deceed780e5ca13ba2b97446d81d70cfbec6/src/plugins/screensaver/totem-screensaver.c#L76https://github.com/awjackson/bsnes-classic/blob/038e2e051ffc8abe7c56a3bf27e3016c433ee563/bsnes/ui-qt/platform/platform_x.cpp
**/
#include "util/screensaver.h"
#include <QDebug>#include <QtGlobal>
#include "util/assert.h"
#if defined(Q_OS_MACOS)# include "util/mac.h"#elif defined(Q_OS_IOS)#include "util/screensaverios.h"#elif defined(Q_OS_ANDROID)#include <android/api-level.h>#include <android/log.h>#define HAS_XWINDOW_SCREENSAVER 0#elif defined(_WIN32)# include <windows.h>#elif defined(__LINUX__)#include <QDBusConnection>#include <QDBusInterface>#include <QDBusReply>#elif defined(HAVE_XSCREENSAVER_SUSPEND) && HAVE_XSCREENSAVER_SUSPEND# include <X11/extensions/scrnsaver.h>#endif
#if (defined(__LINUX__) && defined(__X11__)) || \ (defined(HAVE_XSCREENSAVER_SUSPEND) && HAVE_XSCREENSAVER_SUSPEND)# define None XNone# define Window XWindow# include <X11/Xlib.h># undef None# undef Window#endif
namespace mixxx {
bool ScreenSaverHelper::s_enabled = false;
// inhibitInternal and unihibitInternal should be called the same amount of times// depending on the implementation used. This ensures it.void ScreenSaverHelper::inhibit(){ if (!s_enabled) { inhibitInternal(); }}void ScreenSaverHelper::uninhibit(){ if (s_enabled) { uninhibitInternal(); }}
#ifdef Q_OS_MACOSIOPMAssertionID ScreenSaverHelper::s_systemSleepAssertionID=0;IOPMAssertionID ScreenSaverHelper::s_userActivityAssertionID=0;
void ScreenSaverHelper::triggerUserActivity(){ /* Declare user activity. This wakes the display if it is off, and postpones display sleep according to the users system preferences Available from 10.7.3 */ if (&IOPMAssertionDeclareUserActivity) { CFStringRef reasonForActivity = CFStringCreateWithCString(kCFAllocatorDefault, "Mixxx digital DJ software", kCFStringEncodingUTF8); IOReturn success = IOPMAssertionDeclareUserActivity(reasonForActivity, kIOPMUserActiveLocal, &s_userActivityAssertionID); CFRelease(reasonForActivity);
if (success != kIOReturnSuccess) { qWarning("failed to declare user activity."); } }}
void ScreenSaverHelper::inhibitInternal(){ triggerUserActivity(); /* prevent the system from sleeping */ if (s_systemSleepAssertionID > 0) { qDebug() << "IOKit releasing old screensaver inhibitor" << s_systemSleepAssertionID; IOPMAssertionRelease(s_systemSleepAssertionID); }
IOReturn success; CFStringRef reasonForActivity = CFStringCreateWithCString(kCFAllocatorDefault, "Mixxx digital DJ software", kCFStringEncodingUTF8); success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &s_systemSleepAssertionID); CFRelease(reasonForActivity);
if (success == kIOReturnSuccess) { s_enabled = true; qDebug() << "IOKit screensaver inhibited " << s_systemSleepAssertionID; } else { qWarning("failed to prevent system sleep through IOKit"); }}void ScreenSaverHelper::uninhibitInternal(){ /* allow the system to sleep again */ if (s_systemSleepAssertionID > 0) { s_enabled = false; qDebug() << "IOKit screensaver uninhibited " << s_systemSleepAssertionID; IOPMAssertionRelease(s_systemSleepAssertionID); } // JosepMaJAZ: I am not sure if this needs to be called or not, and specifically when // called on on the triggerUserActivity alone. if (s_userActivityAssertionID > 0) { IOPMAssertionRelease(s_userActivityAssertionID); }}
#elif defined(Q_OS_WIN)void ScreenSaverHelper::triggerUserActivity(){ SetThreadExecutionState( ES_DISPLAY_REQUIRED );}void ScreenSaverHelper::inhibitInternal(){ // Calling once without "ES_CONTINUOUS" to force the monitor to wake up. triggerUserActivity(); SetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED | ES_CONTINUOUS ); qDebug() << "screensaver inhibited"; s_enabled = true;}void ScreenSaverHelper::uninhibitInternal(){ SetThreadExecutionState(ES_CONTINUOUS); qDebug() << "screensaver uninhibited"; s_enabled = false;}
#elif (defined(Q_OS_LINUX) && defined(__X11__))const char *SCREENSAVERS[][4] = { // org.freedesktop.ScreenSaver is the standard. should work for gnome and kde too, // but I add their specific names too {"org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver", "Inhibit"}, {"org.gnome.ScreenSaver", "/org/gnome/ScreenSaver", "org.gnome.ScreenSaver", "Inhibit"}, {"org.kde.screensaver", "/ScreenSaver", "org.kde.screensaver", "Inhibit"}, {nullptr, nullptr, nullptr, nullptr}};// Disabling the method with DBus since it seems to be failing on several systems.#if 0const char *USERACTIVITY[][4] = { // "SimulateUserActivity" is not widely supported, but we can try that first. {"org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver", "SimulateUserActivity" }, {"org.cinnamon.ScreenSaver", "/ScreenSaver", "org.cinnamon.ScreenSaver", "SimulateUserActivity"}, {"org.gnome.ScreenSaver", "/org/gnome/ScreenSaver", "org.gnome.ScreenSaver", "SimulateUserActivity"}, {"org.kde.screensaver", "/ScreenSaver", "org.kde.screensaver", "SimulateUserActivity"}, {nullptr, nullptr, nullptr, nullptr}};#endif // 0
uint32_t ScreenSaverHelper::s_cookie = 0;int ScreenSaverHelper::s_saverindex = -1;bool ScreenSaverHelper::s_sendActivity = true;
void ScreenSaverHelper::triggerUserActivity(){ const char* name = ":0.0"; Display *display; if (getenv("DISPLAY")) { name=getenv("DISPLAY"); } display=XOpenDisplay(name); if (display != nullptr) { XResetScreenSaver(display); XCloseDisplay(display); } return;}// Disabling the method with DBus since it seems to be failing on several systems.#if 0void ScreenSaverHelper::triggerUserActivity(){ if (!s_sendActivity) { // If the D-Bus method didn't work, let's try the Xlib method. const char* name = ":0.0"; Display *display; if (getenv("DISPLAY")) name=getenv("DISPLAY"); display=XOpenDisplay(name); XResetScreenSaver(display); XCloseDisplay(display); return; } s_sendActivity = false; if (!QDBusConnection::sessionBus().isConnected()) { qWarning("Cannot connect to the D-Bus session bus.\nTo start it, run:\n" "\teval `dbus-launch --auto-syntax`"); return; } s_sendActivity = false; QString errors; for (int i=0; USERACTIVITY[i][0] != nullptr; i++ ) { QDBusInterface iface(USERACTIVITY[i][0], USERACTIVITY[i][1], USERACTIVITY[i][2], QDBusConnection::sessionBus()); if (iface.isValid()) { QDBusReply<void> reply = iface.call(USERACTIVITY[i][3]); if (reply.isValid()) { s_sendActivity = true; break; } else { errors = errors + "\nCall to inhibit for " + USERACTIVITY[i][0] + " failed: " + reply.error().message(); } } } if (!s_sendActivity) { qWarning() << "Could not send activity using the registered DBus methods. " << "Errors were: " << errors << "\nWill try to use the Xlib XResetScreensaver instead."; }}#endif // 0
void ScreenSaverHelper::inhibitInternal(){ if (!QDBusConnection::sessionBus().isConnected()) { qWarning("Cannot connect to the D-Bus session bus.\nTo start it, run:\n" "\teval `dbus-launch --auto-syntax`"); return; } if (s_cookie > 0) { uninhibit(); } s_cookie = 0; for (int i=0; SCREENSAVERS[i][0] != nullptr; i++ ) { QDBusInterface iface(SCREENSAVERS[i][0], SCREENSAVERS[i][1], SCREENSAVERS[i][2], QDBusConnection::sessionBus()); if (iface.isValid()) { QDBusReply<uint32_t> reply = iface.call(SCREENSAVERS[i][3], "org.mixxxdj","Mixxx active"); if (reply.isValid()) { s_cookie = reply.value(); s_saverindex = i; s_enabled = true; qDebug() << "DBus screensaver " << SCREENSAVERS[i][0] <<" inhibited"; break; } else { qWarning() << "Call to inhibit for " << SCREENSAVERS[i][0] << " failed: " << reply.error().message(); } } else { qDebug() << "DBus interface " << SCREENSAVERS[i][0] << " not valid"; } }}void ScreenSaverHelper::uninhibitInternal(){ if (s_cookie > 0) { s_enabled = false; QDBusInterface iface(SCREENSAVERS[s_saverindex][0], SCREENSAVERS[s_saverindex][1], SCREENSAVERS[s_saverindex][2], QDBusConnection::sessionBus()); if (iface.isValid()) { QDBusReply<void> reply = iface.call("UnInhibit", s_cookie); if (reply.isValid()) { s_cookie = 0; qDebug() << "DBus screensaver " << SCREENSAVERS[s_saverindex][0] << " uninhibited"; } else { qWarning() << "Call to uninhibit for " << SCREENSAVERS[s_saverindex][0] << " failed: " << reply.error().message(); } } else { qDebug() << "DBus interface " << SCREENSAVERS[s_saverindex][0] << " not valid"; } }}
#elif defined(HAS_XWINDOW_SCREENSAVER) && HAS_XWINDOW_SCREENSAVER// This is untested.struct LibXtst : public library { function<int (Display*, unsigned int, Bool, unsigned long)> XTestFakeKeyEvent;
LibXtst() { if(open("Xtst")) { XTestFakeKeyEvent = sym("XTestFakeKeyEvent"); } }} libXtst;
void ScreenSaverHelper::triggerUserActivity(){ char *name = ":0.0"; Display *display; if (getenv("DISPLAY")) name=getenv("DISPLAY"); display=XOpenDisplay(name); libXtst.XTestFakeKeyEvent(display, 255, True, 0); libXtst.XTestFakeKeyEvent(display, 255, False, 0); XCloseDisplay(display);}void ScreenSaverHelper::inhibitInternal(){ char *name = ":0.0"; Display *display; if (getenv("DISPLAY")) name=getenv("DISPLAY"); display=XOpenDisplay(name); XScreenSaverSuspend(display,True); s_enabled = true;}void ScreenSaverHelper::uninhibitInternal(){ char *name = ":0.0"; Display *display; if (getenv("DISPLAY")) name=getenv("DISPLAY"); display=XOpenDisplay(name); XScreenSaverSuspend(display, False); s_enabled = false;}
#elif defined(Q_OS_IOS)void ScreenSaverHelper::triggerUserActivity() {}void ScreenSaverHelper::inhibitInternal() { setIdleTimerDisabled(true); s_enabled = true;}void ScreenSaverHelper::uninhibitInternal() { setIdleTimerDisabled(false); s_enabled = false;}#elif defined(Q_OS_WASM)// Screensavers are not supportedvoid ScreenSaverHelper::triggerUserActivity() {}void ScreenSaverHelper::inhibitInternal() {}void ScreenSaverHelper::uninhibitInternal() {}#elif defined(Q_OS_ANDROID)
QJniObject ScreenSaverHelper::s_wakeLock = {};// Screensavers are not supportedvoid ScreenSaverHelper::triggerUserActivity() {}void ScreenSaverHelper::inhibitInternal() { if (!ScreenSaverHelper::s_wakeLock.isValid()) { QJniObject context = QNativeInterface::QAndroidApplication::context(); QJniObject POWER_SERVICE = QJniObject::getStaticObjectField( "android/content/Context", "POWER_SERVICE", "Ljava/lang/String;"); auto powerService = context.callObjectMethod("getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", POWER_SERVICE.object()); if (!powerService.isValid()) { qDebug() << "powerService invalid"; return; }
jint FULL_WAKE_LOCK = QJniObject::getStaticField<jint>( "android/os/PowerManager", "FULL_WAKE_LOCK"); ScreenSaverHelper::s_wakeLock = powerService.callObjectMethod("newWakeLock", "(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;", FULL_WAKE_LOCK, QJniObject::fromString("Mixxx").object<jstring>()); if (!ScreenSaverHelper::s_wakeLock.isValid()) { __android_log_print(ANDROID_LOG_WARN, "mixxx", "powerService wakeLock invalid"); qWarning() << "ScreenSaverHelper::inhibitInternal - wakeLock invalid"; return; } } ScreenSaverHelper::s_wakeLock.callMethod<void>("acquire");}void ScreenSaverHelper::uninhibitInternal() { // QNativeInterface::QAndroidApplication::runOnAndroidMainThread([]() { if (ScreenSaverHelper::s_wakeLock.isValid()) { ScreenSaverHelper::s_wakeLock.callMethod<void>("release"); } // }).waitForFinished();}#elsevoid ScreenSaverHelper::triggerUserActivity(){ DEBUG_ASSERT(!"Screensaver trigger activity not implemented");}void ScreenSaverHelper::inhibitInternal(){ DEBUG_ASSERT(!"Screensaver suspending not implemented");}void ScreenSaverHelper::uninhibitInternal(){ DEBUG_ASSERT(!"Screensaver suspending not implemented");}#endif // Q_OS_MACOS
} // namespace mixxx