added support for DBus-based screensaver inhibition

This commit is contained in:
F. Duncanh
2025-08-11 14:23:06 -04:00
parent 59c3abe140
commit 9d28d27702
6 changed files with 286 additions and 6 deletions

View File

@@ -31,6 +31,15 @@ if ( ( UNIX AND NOT APPLE ) OR USE_X11 )
endif()
endif()
if( UNIX AND NOT APPLE )
find_package(PkgConfig REQUIRED)
pkg_check_modules(DBUS dbus-1>=1.4.12)
if (DBUS_FOUND )
add_definitions(-DDBUS )
include_directories(${DBUS_INCLUDE_DIRS})
endif()
endif()
if( UNIX AND NOT APPLE )
add_definitions( -DSUPPRESS_AVAHI_COMPAT_WARNING )
# convert AirPlay colormap 1:3:7:1 to sRGB (1:1:7:1), needed on Linux and BSD
@@ -54,6 +63,11 @@ target_link_libraries( uxplay
renderers
airplay
)
if (DBUS_FOUND)
target_link_libraries( uxplay
${DBUS_LIBRARIES}
)
endif()
install( TARGETS uxplay RUNTIME DESTINATION bin )
install( FILES uxplay.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 )

View File

@@ -8,6 +8,15 @@ developed at the GitHub site <a href="https://github.com/FDH2/UxPlay"
class="uri">https://github.com/FDH2/UxPlay</a> (where ALL user issues
should be posted, and latest versions can be found).</strong></h3>
<ul>
<li><p><strong>NEW on github</strong>: (for Linux/*BSD Desktop
Environments using D-Bus). New option <code>-scrsv &lt;n&gt;</code>
provides screensaver inhibition (e.g., to prevent screensaver function
while watching mirrored videos without keyboard or mouse activity): n =
0 (off) n=1 (on during video activity) n=2 (always on while UxPlay is
running). Tested on Gnome/KDE/Cinnamon/Mate/Xfce 4: may need adjustment
for other Desktop Environments (please report). (watch output of
<code>dbus-monitor</code> to verify that inhibition is working).
<em>Might not work on Wayland</em>.</p></li>
<li><p><strong>NEW on github</strong>: option -ca (with no filename
given) will now render Apple Music cover art (in audio-only mode) inside
UxPlay. (-ca <code>&lt;filename&gt;</code> will continue to export cover
@@ -125,6 +134,10 @@ you may wish to add “as pipewiresink” or “vs waylandsink” as defaults to
the file. <em>(Output from terminal commands “ps waux | grep pulse” or
“pactl info” will contain “pipewire” if your Linux/BSD system uses
it).</em></p></li>
<li><p>For Linux/*BSD systems using D-Bus, the option
<code>-scrsv 1</code> inhibits the screensaver while there is video
activity on UxPlay (<code>-scrsv 2</code> inhibits it whenever UxPlay is
running).</p></li>
<li><p>For Linux systems using systemd, there is a
<strong>systemd</strong> service file <strong>uxplay.service</strong>
found in the UxPlay top directory of the distribution, and also
@@ -1042,6 +1055,16 @@ default: 3) allows selection of the version of GStreamers "playbin"
video player to use for playing HLS video. <em>(Playbin v3 is the
recommended player, but if some videos fail to play, you can try with
version 2.)</em></p>
<p><strong>-scrsv n</strong>. (since 1.73) (So far, only implemented on
Linux/*BSD systems using D-Bus). Inhibit the screensaver in the absence
of keyboard input (e.g., while watching video), using the
org.freedesktop.ScreenSaver D-Bus service: n = 0: (off) n= 1 (on during
video activity) n=2 (always on). <em>Note: to verify this feature is
working, you can use <code>dbus-monitor</code> to view events on the
D-Bus; depending on the Desktop Environment, commands like
<code>gnome-session-inhibit -l</code>,
<code>xfce4-screensaver-commannd -q</code>, etc., should list UxPlay
when it is inhibiting the screensaver.</em></p>
<p><strong>-pin [nnnn]</strong>: (since v1.67) use Apple-style
(one-time) “pin” authentication when a new client connects for the first
time: a four-digit pin code is displayed on the terminal, and the client
@@ -1717,8 +1740,9 @@ an AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen,
introduced 2017, running tvOS 12.2.1), so it does not seem to matter
what version UxPlay claims to be.</p>
<h1 id="changelog">Changelog</h1>
<p>xxxx 2025-07-07 Render Audio cover-art inside UxPlay with -ca option
(no file specified).</p>
<p>xxxx 2025-08-11 Render Audio cover-art inside UxPlay with -ca option
(no file specified). (D-Bus based) option -scrsv <n> to inhibit
screensaver while UxPlay is running (Linux/*BSD only).</p>
<p>1.72.2 2025-07-07 Fix bug (typo) in DNS_SD advertisement introduced
with -pw option. Update llhttp to v 9.3.0</p>
<p>1.72.1 2025-06-06 minor update: fix regression in -reg option; add

View File

@@ -2,6 +2,12 @@
### **Now developed at the GitHub site <https://github.com/FDH2/UxPlay> (where ALL user issues should be posted, and latest versions can be found).**
- **NEW on github**: (for Linux/*BSD Desktop Environments using D-Bus). New option `-scrsv <n>` provides screensaver inhibition (e.g., to
prevent screensaver function while watching mirrored videos without keyboard or mouse
activity): n = 0 (off) n=1 (on during video activity) n=2 (always on while UxPlay is running).
Tested on Gnome/KDE/Cinnamon/Mate/Xfce 4: may need adjustment for other Desktop Environments (please report).
(watch output of `dbus-monitor` to verify that inhibition is working). _Might not work on Wayland_.
- **NEW on github**: option -ca (with no filename given) will now render
Apple Music cover art (in audio-only mode) inside
UxPlay. (-ca `<filename>` will continue to export cover art for
@@ -114,6 +120,9 @@ status](https://repology.org/badge/vertical-allrepos/uxplay.svg)](https://repolo
from terminal commands "ps waux \| grep pulse" or "pactl info" will
contain "pipewire" if your Linux/BSD system uses it).*
- For Linux/*BSD systems using D-Bus, the option `-scrsv 1` inhibits the screensaver while
there is video activity on UxPlay (`-scrsv 2` inhibits it whenever UxPlay is running).
- For Linux systems using systemd, there is a **systemd** service file **uxplay.service**
found in the UxPlay top directory of the distribution, and also installed
in `<DOCDIR>/uxplay/systemd/` (where DOCDIR is usually ``/usr/local/share/doc``), that allows users to start
@@ -1036,6 +1045,18 @@ allows selection of the version of GStreamer's
is the recommended player, but if some videos fail to play, you can try
with version 2.)_
**-scrsv n**. (since 1.73) (So far, only implemented
on Linux/*BSD systems using D-Bus). Inhibit the screensaver in the
absence of keyboard input (e.g., while watching video), using the
org.freedesktop.ScreenSaver D-Bus service:
n = 0: (off) n= 1 (on during video activity) n=2 (always on).
_Note: to verify this feature is working, you can use `dbus-monitor`
to view events on the D-Bus; depending on the Desktop Environment,
commands like
`gnome-session-inhibit -l`, ``xfce4-screensaver-commannd -q``, etc.,
should list UxPlay when it is inhibiting
the screensaver._
**-pin \[nnnn\]**: (since v1.67) use Apple-style (one-time) "pin"
authentication when a new client connects for the first time: a
four-digit pin code is displayed on the terminal, and the client screen
@@ -1752,8 +1773,9 @@ introduced 2017, running tvOS 12.2.1), so it does not seem to matter
what version UxPlay claims to be.
# Changelog
xxxx 2025-07-07 Render Audio cover-art inside UxPlay with -ca option (no file
specified).
xxxx 2025-08-11 Render Audio cover-art inside UxPlay with -ca option (no file
specified). (D-Bus based) option -scrsv <n> to inhibit screensaver while UxPlay
is running (Linux/*BSD only).
1.72.2 2025-07-07 Fix bug (typo) in DNS_SD advertisement introduced with -pw
option. Update llhttp to v 9.3.0

View File

@@ -2,6 +2,16 @@
### **Now developed at the GitHub site <https://github.com/FDH2/UxPlay> (where ALL user issues should be posted, and latest versions can be found).**
- **NEW on github**: (for Linux/\*BSD Desktop Environments using
D-Bus). New option `-scrsv <n>` provides screensaver inhibition
(e.g., to prevent screensaver function while watching mirrored
videos without keyboard or mouse activity): n = 0 (off) n=1 (on
during video activity) n=2 (always on while UxPlay is running).
Tested on Gnome/KDE/Cinnamon/Mate/Xfce 4: may need adjustment for
other Desktop Environments (please report). (watch output of
`dbus-monitor` to verify that inhibition is working). *Might not
work on Wayland*.
- **NEW on github**: option -ca (with no filename given) will now
render Apple Music cover art (in audio-only mode) inside UxPlay.
(-ca `<filename>` will continue to export cover art for display by
@@ -121,6 +131,10 @@ status](https://repology.org/badge/vertical-allrepos/uxplay.svg)](https://repolo
commands "ps waux \| grep pulse" or "pactl info" will contain
"pipewire" if your Linux/BSD system uses it).*
- For Linux/\*BSD systems using D-Bus, the option `-scrsv 1` inhibits
the screensaver while there is video activity on UxPlay (`-scrsv 2`
inhibits it whenever UxPlay is running).
- For Linux systems using systemd, there is a **systemd** service file
**uxplay.service** found in the UxPlay top directory of the
distribution, and also installed in `<DOCDIR>/uxplay/systemd/`
@@ -1055,6 +1069,16 @@ allows selection of the version of GStreamer's \"playbin\" video player
to use for playing HLS video. *(Playbin v3 is the recommended player,
but if some videos fail to play, you can try with version 2.)*
**-scrsv n**. (since 1.73) (So far, only implemented on Linux/\*BSD
systems using D-Bus). Inhibit the screensaver in the absence of keyboard
input (e.g., while watching video), using the
org.freedesktop.ScreenSaver D-Bus service: n = 0: (off) n= 1 (on during
video activity) n=2 (always on). *Note: to verify this feature is
working, you can use `dbus-monitor` to view events on the D-Bus;
depending on the Desktop Environment, commands like
`gnome-session-inhibit -l`, `xfce4-screensaver-commannd -q`, etc.,
should list UxPlay when it is inhibiting the screensaver.*
**-pin \[nnnn\]**: (since v1.67) use Apple-style (one-time) "pin"
authentication when a new client connects for the first time: a
four-digit pin code is displayed on the terminal, and the client screen
@@ -1768,8 +1792,9 @@ what version UxPlay claims to be.
# Changelog
xxxx 2025-07-07 Render Audio cover-art inside UxPlay with -ca option (no
file specified).
xxxx 2025-08-11 Render Audio cover-art inside UxPlay with -ca option (no
file specified). (D-Bus based) option -scrsv `<n>`{=html} to inhibit
screensaver while UxPlay is running (Linux/\*BSD only).
1.72.2 2025-07-07 Fix bug (typo) in DNS_SD advertisement introduced with
-pw option. Update llhttp to v 9.3.0

View File

@@ -19,6 +19,8 @@ UxPlay 1.72: An open\-source AirPlay mirroring (+ audio streaming) server:
.IP
v = 2 or 3 (default 3) optionally selects video player version
.TP
\fB\-scrsv\fI n\fR Screensaver override \fIn\fR:0=off 1=on during activity 2=always on.
.TP
\fB\-pin\fI[xxxx]\fRUse a 4-digit pin code to control client access (default: no)
.IP
without option, pin is random: optionally use fixed pin xxxx.

View File

@@ -35,6 +35,7 @@
#include <cstdio>
#include <stdarg.h>
#include <math.h>
#include <inttypes.h>
#ifdef _WIN32 /*modifications for Windows compilation */
#include <glib.h>
@@ -64,6 +65,10 @@
#include "lib/dnssd.h"
#include "renderers/video_renderer.h"
#include "renderers/audio_renderer.h"
#ifdef DBUS
#include <dbus/dbus.h>
#endif
#define VERSION "1.72"
@@ -175,6 +180,28 @@ static guint missed_feedback_limit = MISSED_FEEDBACK_LIMIT;
static guint missed_feedback = 0;
static guint playbin_version = DEFAULT_PLAYBIN_VERSION;
static bool reset_httpd = false;
//Support for D-Bus-based screensaver inhibition (org.freedesktop.ScreenSaver)
static unsigned int scrsv;
#ifdef DBUS
/* these strings can be changed at startup if a non-conforming Desktop Environmemt is detected */
static std::string dbus_service = "org.freedesktop.ScreenSaver";
static std::string dbus_path = "/org/freedesktop/ScreenSaver";
static std::string dbus_interface = "org.freedesktop.ScreenSaver";
static std::string dbus_inhibit = "Inhibit";
static std::string dbus_uninhibit = "UnInhibit";
static DBusConnection *dbus_connection = NULL;
static dbus_uint32_t dbus_cookie = 0;
static DBusPendingCall *dbus_pending = NULL;
static bool dbus_last_message = false;
static const char *appname = DEFAULT_NAME;
static const char *reason_always = "mirroring client: inhibit always";
static const char *reason_active = "actively receiving video";
static int activity_count;
static double activity_threshold = 500000.0; // threshold for FPSdata item txUsageAvg to classify mirror video as "active"
#define MAX_ACTIVITY_COUNT 60
#endif
/* logging */
static void log(int level, const char* format, ...) {
@@ -204,6 +231,73 @@ static void log(int level, const char* format, ...) {
#define LOGW(...) log(LOGGER_WARNING, __VA_ARGS__)
#define LOGE(...) log(LOGGER_ERR, __VA_ARGS__)
#ifdef DBUS
static void dbus_screensaver_inhibiter(bool inhibit) {
g_assert(inhibit != dbus_last_message);
g_assert(scrsv);
/* receive reply from previous request, whenever that was sent
* (may have been sent hours ago ... !)
* (code modeled on vlc/modules/misc/inhibit/dbus.c) */
if (dbus_pending != NULL) {
DBusMessage *reply;
dbus_pending_call_block(dbus_pending);
reply = dbus_pending_call_steal_reply(dbus_pending);
dbus_pending_call_unref(dbus_pending);
dbus_pending = NULL;
if (reply != NULL) {
if (!dbus_message_get_args(reply, NULL,
DBUS_TYPE_UINT32, &dbus_cookie,
DBUS_TYPE_INVALID)) {
dbus_cookie = 0;
}
dbus_message_unref(reply);
}
LOGD("screen_saver: got D-Bus cookie %" PRIu32, (uint32_t) dbus_cookie);
}
if (!dbus_cookie && !inhibit) {
return; /* nothing to do */
}
/* send request */
const char *dbus_method = inhibit ? dbus_inhibit.c_str() : dbus_uninhibit.c_str();
DBusMessage *dbus_message = dbus_message_new_method_call(dbus_service.c_str(),
dbus_path.c_str(),
dbus_interface.c_str(),
dbus_method);
g_assert (dbus_message);
if (inhibit) {
dbus_bool_t ret;
const char *reason = (scrsv == 1) ? reason_active : reason_always;
ret = dbus_message_append_args(dbus_message,
DBUS_TYPE_STRING, &appname,
DBUS_TYPE_STRING, &reason,
DBUS_TYPE_INVALID);
g_assert(ret);
ret = dbus_connection_send_with_reply(dbus_connection, dbus_message, &dbus_pending, -1);
if (!ret) {
dbus_pending = NULL;
}
} else {
g_assert(dbus_cookie);
LOGD("screen_saver: releasing D-Bus cookie %" PRIu32, (uint32_t) dbus_cookie);
if (dbus_message_append_args(dbus_message,
DBUS_TYPE_UINT32, &dbus_cookie,
DBUS_TYPE_INVALID)
&& dbus_connection_send(dbus_connection, dbus_message, NULL)) {
dbus_cookie = 0;
}
}
dbus_connection_flush(dbus_connection);
dbus_message_unref(dbus_message);
dbus_last_message = inhibit;
}
#endif
static bool file_has_write_access (const char * filename) {
bool exists = false;
bool write = false;
@@ -683,6 +777,7 @@ static void print_info (char *name) {
printf("-h265 Support h265 (4K) video (with h265 versions of h264 plugins)\n");
printf("-hls [v] Support HTTP Live Streaming (currently Youtube video only) \n");
printf(" v = 2 or 3 (default 3) optionally selects video player version\n");
printf("-scrsv n Screensaver override n: 0=off 1=on during activity 2=always on\n");
printf("-pin[xxxx]Use a 4-digit pin code to control client access (default: no)\n");
printf(" default pin is random: optionally use fixed pin xxxx\n");
printf("-reg [fn] Keep a register in $HOME/.uxplay.register to verify returning\n");
@@ -947,6 +1042,19 @@ static void parse_arguments (int argc, char *argv[]) {
}
}
}
} else if (arg == "-scrsv") {
if (!option_has_value(i, argc, argv[i], argv[i+1])) exit(1);
unsigned int n = 0;
if (!get_value(argv[++i], &n) || n > 2) {
fprintf(stderr, "invalid \"-scrsv %s\"; values 0, 1, 2 allowed\n", argv[i]);
exit(1);
}
#ifdef DBUS
scrsv = n;
#else
fprintf(stderr,"invalid: option \"-scrsv\" is currently only implemented for Linux/*BSD systems with D-Bus service\n");
exit(1);
#endif
} else if (arg == "-vsync") {
video_sync = true;
if (i < argc - 1) {
@@ -1866,6 +1974,27 @@ extern "C" void video_process (void *cls, raop_ntp_t *ntp, video_decode_struct *
}
}
#ifdef DBUS
extern "C" void mirror_video_activity (void *cls, double *txusage) {
if (scrsv != 1) {
return;
}
if (*txusage > activity_threshold) {
if (activity_count < MAX_ACTIVITY_COUNT) {
activity_count++;
} else if (activity_count == MAX_ACTIVITY_COUNT && !dbus_last_message) {
dbus_screensaver_inhibiter(true);
}
} else {
if (activity_count > 0) {
activity_count--;
} else if (activity_count == 0 && dbus_last_message) {
dbus_screensaver_inhibiter(false);
}
}
}
#endif
extern "C" void video_pause (void *cls) {
if (use_video) {
video_renderer_pause();
@@ -2179,6 +2308,9 @@ static int start_raop_server (unsigned short display[5], unsigned short tcp[3],
raop_cbs.export_dacp = export_dacp;
raop_cbs.video_reset = video_reset;
raop_cbs.video_set_codec = video_set_codec;
#ifdef DBUS
raop_cbs.mirror_video_activity = mirror_video_activity;
#endif
raop_cbs.on_video_play = on_video_play;
raop_cbs.on_video_scrub = on_video_scrub;
raop_cbs.on_video_rate = on_video_rate;
@@ -2310,6 +2442,7 @@ static void read_config_file(const char * filename, const char * uxplay_name) {
fprintf(stderr,"UxPlay: failed to open configuration file at %s\n", config_file.c_str());
}
if (options.size() > 1) {
int argc = options.size();
char **argv = (char **) malloc(sizeof(char*) * argc);
for (int i = 0; i < argc; i++) {
@@ -2319,6 +2452,7 @@ static void read_config_file(const char * filename, const char * uxplay_name) {
free (argv);
}
}
#ifdef GST_MACOS
/* workaround for GStreamer >= 1.22 "Official Builds" on macOS */
#include <TargetConditionals.h>
@@ -2393,6 +2527,52 @@ int main (int argc, char *argv[]) {
LOGI("UxPlay %s: An Open-Source AirPlay mirroring and audio-streaming server.", VERSION);
#ifdef DBUS
if (scrsv) {
DBusError dbus_error;
dbus_error_init(&dbus_error);
dbus_connection = dbus_bus_get(DBUS_BUS_SESSION, &dbus_error);
if (dbus_error_is_set(&dbus_error)) {
dbus_error_free(&dbus_error);
scrsv = 0;
LOGI ("D-Bus session not found: screensaver inhibition option (\"-scrsv\") will not be active");
}
}
if (scrsv) {
LOGD ("D-Bus session support is available, connection %p", dbus_connection);
std::string desktop = getenv("XDG_CURRENT_DESKTOP");
LOGD("Desktop Environment: %s", desktop.c_str());
/* if dbus_service, dbus_path, dbus_interface, dbus_inhibit, dbus_uninhibit *
* in the detected Desktop Environments are still non-conforming to the *
* org.freedesktop.ScreenSaver interface, they can be modifed here */
/* some desktop environments (e.g. Xfce 4, Mate) modify the D-Bus service name */
std::string name;
if (strstr(desktop.c_str(), "XFCE")) {
name = "xfce";
} else if (strstr(desktop.c_str(), "MATE")) {
name = "mate";
}
if (!name.empty()) {
size_t pos;
std::string replace_word = "freedesktop";
pos = dbus_service.find(replace_word);
dbus_service.replace(pos, replace_word.size(), name);
pos = dbus_path.find(replace_word);
dbus_path.replace(pos, replace_word.size(), name);
pos = dbus_interface.find(replace_word);
dbus_interface.replace(pos, replace_word.size(), name);
}
LOGI("Will attempt to use %s (D-Bus screensaver inhibition) %s", dbus_service.c_str(),
(scrsv == 1 ? "only during screen activity" : "always"));
if (scrsv == 2) {
dbus_screensaver_inhibiter(true);
}
}
#endif
if (audiosink == "0") {
use_audio = false;
dump_audio = false;
@@ -2667,4 +2847,17 @@ int main (int argc, char *argv[]) {
if (metadata_filename.length()) {
remove (metadata_filename.c_str());
}
#ifdef DBUS
if (dbus_connection) {
LOGD("Ending D-Bus connection %p", dbus_connection);
if (dbus_last_message) {
dbus_screensaver_inhibiter(false);
}
if (dbus_pending) {
dbus_pending_call_cancel(dbus_pending);
dbus_pending_call_unref(dbus_pending);
}
dbus_connection_unref(dbus_connection);
}
#endif
}