diff --git a/build/depends.py b/build/depends.py index 936c73aab438..80155c76273b 100644 --- a/build/depends.py +++ b/build/depends.py @@ -280,7 +280,8 @@ def configure(self, build, conf): raise Exception('Qt >= 5.0 not found') elif not qt5 and not conf.CheckForPKG('QtCore', '4.6'): raise Exception('QT >= 4.6 not found') - + + qt_modules.extend(['QtDBus']) # This automatically converts QtXXX to Qt5XXX where appropriate. if qt5: build.env.EnableQt5Modules(qt_modules, debug=False) @@ -435,7 +436,6 @@ def configure(self, build, conf): build.env.Append(CPPPATH="#lib/gtest-1.7.0/include") class FidLib(Dependence): - def sources(self, build): symbol = None if build.platform_is_windows: @@ -1119,6 +1119,7 @@ def sources(self, build): "util/cmdlineargs.cpp", "util/audiosignal.cpp", "util/autohidpi.cpp", + "util/screensaver.cpp", '#res/mixxx.qrc' ] diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp index 92086b393b73..e30ff7a5f63a 100644 --- a/src/controllers/controller.cpp +++ b/src/controllers/controller.cpp @@ -11,6 +11,7 @@ #include "controllers/controller.h" #include "controllers/controllerdebug.h" #include "controllers/defs_controllers.h" +#include "util/screensaver.h" Controller::Controller() : QObject(), @@ -19,6 +20,7 @@ Controller::Controller() m_bIsInputDevice(false), m_bIsOpen(false), m_bLearning(false) { + m_userActivityInhibitTimer.start(); } Controller::~Controller() { @@ -95,13 +97,23 @@ void Controller::send(QList data, unsigned int length) { send(msg); } +void Controller::triggerActivity() +{ + // Inhibit Updates for 1000 milliseconds + if (m_userActivityInhibitTimer.elapsed() > 1000) { + mixxx::ScreenSaverHelper::triggerUserActivity(); + m_userActivityInhibitTimer.start(); + } +} void Controller::receive(const QByteArray data, mixxx::Duration timestamp) { + if (m_pEngine == NULL) { //qWarning() << "Controller::receive called with no active engine!"; // Don't complain, since this will always show after closing a device as // queued signals flush out return; } + triggerActivity(); int length = data.size(); if (ControllerDebug::enabled()) { diff --git a/src/controllers/controller.h b/src/controllers/controller.h index 185a2801feb6..9cc6be386ce2 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -101,6 +101,9 @@ class Controller : public QObject, ConstControllerPresetVisitor { // To be called in sub-class' close() functions after stopping any input // polling/processing but before closing the device. void stopEngine(); + + // To be called when receiving events + void triggerActivity(); inline ControllerEngine* getEngine() const { return m_pEngine; @@ -157,6 +160,7 @@ class Controller : public QObject, ConstControllerPresetVisitor { // Indicates whether or not the device has been opened for input/output. bool m_bIsOpen; bool m_bLearning; + QTime m_userActivityInhibitTimer; // accesses lots of our stuff, but in the same thread friend class ControllerManager; diff --git a/src/controllers/midi/midicontroller.cpp b/src/controllers/midi/midicontroller.cpp index 731c4e6ea79c..c2848d302023 100644 --- a/src/controllers/midi/midicontroller.cpp +++ b/src/controllers/midi/midicontroller.cpp @@ -15,6 +15,7 @@ #include "errordialoghandler.h" #include "mixer/playermanager.h" #include "util/math.h" +#include "util/screensaver.h" MidiController::MidiController() : Controller() { @@ -206,6 +207,7 @@ void MidiController::receive(unsigned char status, unsigned char control, channel, opCode, timestamp)); MidiKey mappingKey(status, control); + triggerActivity(); if (isLearning()) { emit(messageReceived(status, control, value)); @@ -455,6 +457,7 @@ void MidiController::receive(QByteArray data, mixxx::Duration timestamp) { MidiKey mappingKey(data.at(0), 0xFF); + triggerActivity(); // TODO(rryan): Need to review how MIDI learn works with sysex messages. I // don't think this actually does anything useful. if (isLearning()) { diff --git a/src/mixer/playerinfo.cpp b/src/mixer/playerinfo.cpp index a668be273404..dd38804712b0 100644 --- a/src/mixer/playerinfo.cpp +++ b/src/mixer/playerinfo.cpp @@ -119,7 +119,7 @@ void PlayerInfo::updateCurrentPlayingDeck() { continue; } - if (pDc->m_pregain.get() <= 0.5) { + if (pDc->m_pregain.get() <= 0.25) { continue; } @@ -150,7 +150,6 @@ void PlayerInfo::updateCurrentPlayingDeck() { maxVolume = dvol; } } - if (maxDeck != m_currentlyPlayingDeck) { m_currentlyPlayingDeck = maxDeck; locker.unlock(); diff --git a/src/mixer/playerinfo.h b/src/mixer/playerinfo.h index a16537ba179c..78e566e636a3 100644 --- a/src/mixer/playerinfo.h +++ b/src/mixer/playerinfo.h @@ -33,6 +33,7 @@ class PlayerInfo : public QObject { TrackPointer getTrackInfo(const QString& group); void setTrackInfo(const QString& group, const TrackPointer& trackInfoObj); TrackPointer getCurrentPlayingTrack(); + int getCurrentPlayingDeck(); QMap getLoadedTracks(); bool isTrackLoaded(const TrackPointer& pTrack) const; bool isFileLoaded(const QString& track_location) const; @@ -62,7 +63,6 @@ class PlayerInfo : public QObject { void clearControlCache(); void timerEvent(QTimerEvent* pTimerEvent); void updateCurrentPlayingDeck(); - int getCurrentPlayingDeck(); DeckControls* getDeckControls(int i); PlayerInfo(); diff --git a/src/mixxx.cpp b/src/mixxx.cpp index beaa63d26006..f901c9424f1d 100644 --- a/src/mixxx.cpp +++ b/src/mixxx.cpp @@ -28,6 +28,7 @@ #include "dialog/dlgabout.h" #include "preferences/dialog/dlgpreferences.h" #include "preferences/dialog/dlgprefeq.h" +#include "preferences/constants.h" #include "dialog/dlgdevelopertools.h" #include "engine/enginemaster.h" #include "effects/effectsmanager.h" @@ -64,6 +65,7 @@ #include "skin/launchimage.h" #include "preferences/settingsmanager.h" #include "widget/wmainmenubar.h" +#include "util/screensaver.h" #ifdef __VINYLCONTROL__ #include "vinylcontrol/vinylcontrolmanager.h" @@ -304,6 +306,17 @@ void MixxxMainWindow::initialize(QApplication* pApp, const CmdlineArgs& args) { connect(this, SIGNAL(newSkinLoaded()), m_pLibrary, SLOT(onSkinLoadFinished())); + // Inhibit the screensaver if the option is set. (Do it before creating the preferences dialog) + int inhibit = pConfig->getValue(ConfigKey("[Config]","InhibitScreensaver"),-1); + if (inhibit == -1) { + inhibit = static_cast(mixxx::ScreenSaverPreference::PREVENT_ON); + pConfig->setValue(ConfigKey("[Config]","InhibitScreensaver"), inhibit); + } + m_inhibitScreensaver = static_cast(inhibit); + if (m_inhibitScreensaver == mixxx::ScreenSaverPreference::PREVENT_ON) { + mixxx::ScreenSaverHelper::inhibit(); + } + // Initialize preference dialog m_pPrefDlg = new DlgPreferences(this, m_pSkinLoader, m_pSoundManager, m_pPlayerManager, m_pControllerManager, m_pVCManager, m_pEffectsManager, @@ -368,6 +381,7 @@ void MixxxMainWindow::initialize(QApplication* pApp, const CmdlineArgs& args) { } emit(newSkinLoaded()); + // Wait until all other ControlObjects are set up before initializing // controllers m_pControllerManager->setUpDevices(); @@ -441,6 +455,10 @@ void MixxxMainWindow::initialize(QApplication* pApp, const CmdlineArgs& args) { SIGNAL(currentPlayingTrackChanged(TrackPointer)), this, SLOT(slotUpdateWindowTitle(TrackPointer))); + connect(&PlayerInfo::instance(), + SIGNAL(currentPlayingDeckChanged(int)), + this, SLOT(slotChangedPlayingDeck(int))); + // this has to be after the OpenGL widgets are created or depending on a // million different variables the first waveform may be horribly // corrupted. See bug 521509 -- bkgood ?? -- vrince @@ -454,7 +472,12 @@ void MixxxMainWindow::finalize() { Timer t("MixxxMainWindow::~finalize"); t.start(); - // Save the current window state (position, maximized, etc) + if (m_inhibitScreensaver != mixxx::ScreenSaverPreference::PREVENT_OFF) { + mixxx::ScreenSaverHelper::uninhibit(); + } + + + // Save the current window state (position, maximized, etc) m_pSettingsManager->settings()->set(ConfigKey("[MainWindow]", "geometry"), QString(saveGeometry().toBase64())); m_pSettingsManager->settings()->set(ConfigKey("[MainWindow]", "state"), @@ -1082,6 +1105,17 @@ void MixxxMainWindow::slotNoMicrophoneInputConfigured() { m_pPrefDlg->showSoundHardwarePage(); } +void MixxxMainWindow::slotChangedPlayingDeck(int deck) { + if (m_inhibitScreensaver == mixxx::ScreenSaverPreference::PREVENT_ON_PLAY) { + if (deck==-1) { + // If no deck is playing, allow the screensaver to run. + mixxx::ScreenSaverHelper::uninhibit(); + } else { + mixxx::ScreenSaverHelper::inhibit(); + } + } +} + void MixxxMainWindow::slotHelpAbout() { DlgAbout* about = new DlgAbout(this); about->show(); @@ -1287,6 +1321,30 @@ bool MixxxMainWindow::confirmExit() { return true; } +void MixxxMainWindow::setInhibitScreensaver(mixxx::ScreenSaverPreference newInhibit) +{ + UserSettingsPointer pConfig = m_pSettingsManager->settings(); + + if (m_inhibitScreensaver != mixxx::ScreenSaverPreference::PREVENT_OFF) { + mixxx::ScreenSaverHelper::uninhibit(); + } + + if (newInhibit == mixxx::ScreenSaverPreference::PREVENT_ON) { + mixxx::ScreenSaverHelper::inhibit(); + } else if (newInhibit == mixxx::ScreenSaverPreference::PREVENT_ON_PLAY + && PlayerInfo::instance().getCurrentPlayingDeck()!=-1) { + mixxx::ScreenSaverHelper::inhibit(); + } + int inhibit_int = static_cast(newInhibit); + pConfig->setValue(ConfigKey("[Config]","InhibitScreensaver"), inhibit_int); + m_inhibitScreensaver = newInhibit; +} + +mixxx::ScreenSaverPreference MixxxMainWindow::getInhibitScreensaver() +{ + return m_inhibitScreensaver; +} + void MixxxMainWindow::launchProgress(int progress) { if (m_pLaunchImage) { m_pLaunchImage->progress(progress); diff --git a/src/mixxx.h b/src/mixxx.h index da793a501190..c8e2fd43ec4f 100644 --- a/src/mixxx.h +++ b/src/mixxx.h @@ -65,6 +65,8 @@ class MixxxMainWindow : public QMainWindow { // creates the menu_bar and inserts the file Menu void createMenuBar(); void connectMenuBar(); + void setInhibitScreensaver(mixxx::ScreenSaverPreference inhibit); + mixxx::ScreenSaverPreference getInhibitScreensaver(); void setToolTipsCfg(mixxx::TooltipsPreference tt); inline mixxx::TooltipsPreference getToolTipsCfg() { return m_toolTipsCfg; } @@ -88,6 +90,7 @@ class MixxxMainWindow : public QMainWindow { void slotDeveloperToolsClosed(); void slotUpdateWindowTitle(TrackPointer pTrack); + void slotChangedPlayingDeck(int deck); // Warn the user when inputs are not configured. void slotNoMicrophoneInputConfigured(); @@ -175,6 +178,7 @@ class MixxxMainWindow : public QMainWindow { const CmdlineArgs& m_cmdLineArgs; ControlPushButton* m_pTouchShift; + mixxx::ScreenSaverPreference m_inhibitScreensaver; static const int kMicrophoneCount; static const int kAuxiliaryCount; diff --git a/src/preferences/constants.h b/src/preferences/constants.h index 137ca5782222..4bc73c32ec96 100644 --- a/src/preferences/constants.h +++ b/src/preferences/constants.h @@ -11,6 +11,13 @@ enum class TooltipsPreference { TOOLTIPS_ONLY_IN_LIBRARY = 2, }; +// Settings to enable or disable the prevention to run the screensaver. +enum class ScreenSaverPreference { + PREVENT_OFF = 0, + PREVENT_ON = 1, + PREVENT_ON_PLAY = 2 +}; + } // namespace mixxx #endif /* PREFERENCES_CONSTANTS_H */ diff --git a/src/preferences/dialog/dlgprefcontrols.cpp b/src/preferences/dialog/dlgprefcontrols.cpp index 0c8b3a401db2..9a4f8c0fb739 100644 --- a/src/preferences/dialog/dlgprefcontrols.cpp +++ b/src/preferences/dialog/dlgprefcontrols.cpp @@ -35,8 +35,10 @@ #include "skin/skinloader.h" #include "skin/legacyskinparser.h" #include "mixer/playermanager.h" +#include "mixer/playerinfo.h" #include "control/controlobject.h" #include "mixxx.h" +#include "util/screensaver.h" #include "defs_urls.h" #include "util/autohidpi.h" @@ -390,6 +392,21 @@ DlgPrefControls::DlgPrefControls(QWidget * parent, MixxxMainWindow * mixxx, ConfigKey("[Config]", "StartInFullscreen")).toInt()==1); connect(checkBoxStartFullScreen, SIGNAL(toggled(bool)), this, SLOT(slotSetStartInFullScreen(bool))); + + // + // Screensaver mode + // + comboBoxScreensaver->clear(); + comboBoxScreensaver->addItem(tr("Allow screensaver to run"), + static_cast(mixxx::ScreenSaverPreference::PREVENT_OFF)); + comboBoxScreensaver->addItem(tr("Prevent screensaver from running"), + static_cast(mixxx::ScreenSaverPreference::PREVENT_ON)); + comboBoxScreensaver->addItem(tr("Prevent screensaver while playing"), + static_cast(mixxx::ScreenSaverPreference::PREVENT_ON_PLAY)); + + int inhibitsettings = static_cast(mixxx->getInhibitScreensaver()); + comboBoxScreensaver->setCurrentIndex(comboBoxScreensaver->findData(inhibitsettings)); + // // Tooltip configuration // @@ -551,6 +568,10 @@ void DlgPrefControls::slotResetToDefaults() { // Don't start in full screen. checkBoxStartFullScreen->setChecked(false); + // Inhibit the screensaver + comboBoxScreensaver->setCurrentIndex(comboBoxScreensaver->findData( + static_cast(mixxx::ScreenSaverPreference::PREVENT_ON))); + // Tooltips on everywhere. radioButtonTooltipsLibraryAndSkin->setChecked(true); @@ -592,7 +613,6 @@ void DlgPrefControls::slotSetRateRange(int pos) { slotSetRateRangePercent(ComboBoxRateRange->itemData(pos).toInt()); } - void DlgPrefControls::slotSetRateRangePercent (int rateRangePercent) { double rateRange = rateRangePercent / 100.; @@ -818,6 +838,15 @@ void DlgPrefControls::slotApply() { int configSPAutoReset = BaseTrackPlayer::RESET_NONE; + // screensaver mode update + int inhibitcombo = comboBoxScreensaver->itemData( + comboBoxScreensaver->currentIndex()).toInt(); + int inhibitsettings = static_cast(m_mixxx->getInhibitScreensaver()); + if (inhibitcombo != inhibitsettings) { + m_mixxx->setInhibitScreensaver(static_cast(inhibitcombo)); + } + + if (m_speedAutoReset && m_pitchAutoReset) { configSPAutoReset = BaseTrackPlayer::RESET_PITCH_AND_SPEED; } diff --git a/src/preferences/dialog/dlgprefcontrolsdlg.ui b/src/preferences/dialog/dlgprefcontrolsdlg.ui index 0613eb24fb88..e22094056ec9 100644 --- a/src/preferences/dialog/dlgprefcontrolsdlg.ui +++ b/src/preferences/dialog/dlgprefcontrolsdlg.ui @@ -7,7 +7,7 @@ 0 0 562 - 711 + 723 @@ -211,6 +211,16 @@ + + + + Screen saver + + + + + + diff --git a/src/util/screensaver.cpp b/src/util/screensaver.cpp new file mode 100644 index 000000000000..e7d9e7485935 --- /dev/null +++ b/src/util/screensaver.cpp @@ -0,0 +1,338 @@ +#include + +/** +Documentation: +OSX: https://developer.apple.com/reference/iokit/1557134-iopmassertioncreatewithname +Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373208(v=vs.85).aspx +Freedesktop: https://people.freedesktop.org/~hadess/idle-inhibition-spec/re01.html +XScreenSaver: https://linux.die.net/man/3/xscreensaversuspend +GTK: 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#L299 +https://github.com/videolan/vlc/blob/fbaa27ae2d7fcf5ccee7f0ca424ec0cc5bf01f4c/modules/video_output/win32/events.c#L168 +https://github.com/GNOME/totem/blob/0c18deceed780e5ca13ba2b97446d81d70cfbec6/src/plugins/screensaver/totem-screensaver.c#L76 +https://github.com/awjackson/bsnes-classic/blob/038e2e051ffc8abe7c56a3bf27e3016c433ee563/bsnes/ui-qt/platform/platform_x.cpp + +**/ + +#include "util/screensaver.h" + +#if defined(Q_OS_MAC) +# include "util/mac.h" +#elif defined(Q_OS_WIN) +# include +#elif defined(Q_OS_LINUX) +# include +#elif HAVE_XSCREENSAVER_SUSPEND +# include +#endif // Q_OS_WIN + +#if defined(Q_OS_LINUX) || HAVE_XSCREENSAVER_SUSPEND +# define None XNone +# define Window XWindow +# include +# 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_MAC +IOPMAssertionID 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) +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 0 +const 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); + XResetScreenSaver(display); + XCloseDisplay(display); + return; +} +// Disabling the method with DBus since it seems to be failing on several systems. +#if 0 +void 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 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 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 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 HAS_XWINDOW_SCREENSAVER +// This is untested. +struct LibXtst : public library { + function 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; +} + +#else +void 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_MAC + + +} // namespace mixxx + diff --git a/src/util/screensaver.h b/src/util/screensaver.h new file mode 100644 index 000000000000..4a5633fd8d46 --- /dev/null +++ b/src/util/screensaver.h @@ -0,0 +1,39 @@ +#ifndef MIXXX_SCREENSAVER_H +#define MIXXX_SCREENSAVER_H + +#ifdef Q_OS_MAC +#import +#endif // Q_OS_MAC + +namespace mixxx { + +// Code related to interacting with the screensaver. +// +// Main use is to prevent the screensaver from starting if Mixxx is being used. +// +class ScreenSaverHelper { +public: + + static void inhibit(); + static void uninhibit(); + static void triggerUserActivity(); + +private: + static void inhibitInternal(); + static void uninhibitInternal(); + + static bool s_enabled; + static bool s_sendActivity; +#if defined(Q_OS_MAC) + /* sleep management */ + static IOPMAssertionID s_systemSleepAssertionID; + static IOPMAssertionID s_userActivityAssertionID; +#elif defined(Q_OS_LINUX) + static uint32_t s_cookie; + static int s_saverindex; +#endif // Q_OS_MAC +}; + +} + +#endif // MIXXX_SCREENSAVER_H