Inhibit screensaver when slideshow is running
authorThomas White <taw@bitwiz.org.uk>
Tue, 17 Sep 2013 20:38:50 +0000 (22:38 +0200)
committerThomas White <taw@bitwiz.org.uk>
Tue, 17 Sep 2013 20:38:50 +0000 (22:38 +0200)
AUTHORS
Makefile.am
configure.ac
src/inhibit_screensaver.c [new file with mode: 0644]
src/inhibit_screensaver.h [new file with mode: 0644]
src/presentation.c
src/presentation.h
src/slideshow.c

diff --git a/AUTHORS b/AUTHORS
index 53415e1..25a0f4b 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1,5 @@
-Thomas White <taw@bitwiz.org.uk>
+Copyright © 2013 Thomas White <taw@bitwiz.org.uk>
+
+Screensaver inhibition code, from VLC (src/inhibit_screensaver.c):
+ Copyright © 2009-2012 Rémi Denis-Courmont
+ Copyright © 2007-2012 Rafaël Carré
index 859ae33..ae8eedc 100644 (file)
@@ -14,13 +14,15 @@ src_colloquium_SOURCES = src/colloquium.c src/render.c \
                          src/mainwindow.c src/presentation.c \
                          src/stylesheet.c src/loadsave.c src/frame.c \
                          src/slideshow.c src/wrap.c src/storycode.c \
-                         src/imagestore.c src/notes.c src/pr_clock.c
+                         src/imagestore.c src/notes.c src/pr_clock.c \
+                         src/inhibit_screensaver.c
 
 INCLUDES = -Iharfatum/src
 
 EXTRA_DIST += src/presentation.h src/render.h src/wrap.h \
               src/stylesheet.h src/loadsave.h src/slideshow.h src/storycode.h \
-              src/imagestore.h src/notes.h src/pr_clock.h
+              src/imagestore.h src/notes.h src/pr_clock.h \
+              src/inhibit_screensaver.h
 
 src/default_stylesheet.o: src/default_stylesheet.sty
        ld -r -b binary -o src/default_stylesheet.o src/default_stylesheet.sty
index eff1f2a..fc161e2 100644 (file)
@@ -55,6 +55,14 @@ PKG_CHECK_MODULES([GDK_pixbuf], [gdk-pixbuf], [],
 ])
 
 
+PKG_CHECK_MODULES([DBUS], [dbus-1 >= 1.0.0],
+[  ],
+[
+   AC_MSG_ERROR([dbus is required.])
+])
+
+
+
 gl_IGNORE_UNUSED_LIBRARIES
 
 LIBHARFATUM_CFLAGS="$CFLAGS -D_GNU_SOURCE $Cairo_CFLAGS"
@@ -62,10 +70,10 @@ LIBHARFATUM_LIBS="$LIBS $Cairo_LIBS"
 AC_SUBST([LIBHARFATUM_LIBS])
 
 CFLAGS="$CFLAGS $GTK_CFLAGS -D_GNU_SOURCE $libPNG_CFLAGS $Cairo_CFLAGS"
-CFLAGS="$CFLAGS $GDK_pixbuf_CFLAGS $GDK_pixbuf_2_CFLAGS"
+CFLAGS="$CFLAGS $GDK_pixbuf_CFLAGS $GDK_pixbuf_2_CFLAGS $DBUS_CFLAGS"
 
 LIBS="$LIBS -lm -lz $GTK_LIBS $libPNG_LIBS $Cairo_LIBS $GDK_pixbuf_LIBS"
-LIBS="$LIBS $GDK_pixbuf_2_LIBS $LDFLAGS"
+LIBS="$LIBS $GDK_pixbuf_2_LIBS $DBUS_LIBS $LDFLAGS"
 
 
 AC_CONFIG_FILES(Makefile lib/Makefile)
diff --git a/src/inhibit_screensaver.c b/src/inhibit_screensaver.c
new file mode 100644 (file)
index 0000000..007c550
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * inhibit_screensaver.h
+ *
+ * Copyright © 2013 Thomas White <taw@bitwiz.org.uk>
+ * Copyright © 2009-2012 Rémi Denis-Courmont
+ * Copyright © 2007-2012 Rafaël Carré
+ *
+ * This file is part of Colloquium.
+ *
+ * Colloquium 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 3 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 <http://www.gnu.org/licenses/>.
+ *
+ * >>> This file is based on dbus.c from VLC. The original licence is below. <<<
+ *
+ */
+
+/*****************************************************************************
+ * dbus.c: power management inhibition using D-Bus
+ *****************************************************************************
+ * Copyright © 2009-2012 Rémi Denis-Courmont
+ * Copyright © 2007-2012 Rafaël Carré
+ *
+ * This program 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 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+/*
+ * Based on freedesktop Power Management Specification version 0.2
+ * http://people.freedesktop.org/~hughsient/temp/power-management-spec-0.2.html
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dbus/dbus.h>
+
+
+enum inhibit_api
+{
+       FDO_SS, /**< KDE >= 4 and GNOME >= 3.6 */
+       FDO_PM, /**< KDE and GNOME <= 2.26 */
+       MATE,   /**< >= 1.0 */
+       GNOME,  /**< GNOME 2.26..3.4 */
+};
+
+#define MAX_API (GNOME+1)
+
+
+/* Currently, all services have identical service and interface names. */
+static const char dbus_service[][40] =
+{
+       [FDO_SS] = "org.freedesktop.ScreenSaver",
+       [FDO_PM] = "org.freedesktop.PowerManagement.Inhibit",
+       [MATE]   = "org.mate.SessionManager",
+       [GNOME]  = "org.gnome.SessionManager",
+};
+
+
+static const char dbus_path[][33] =
+{
+       [FDO_SS] = "/ScreenSaver",
+       [FDO_PM] = "/org/freedesktop/PowerManagement",
+       [MATE]   = "/org/mate/SessionManager",
+       [GNOME]  = "/org/gnome/SessionManager",
+};
+
+
+static const char dbus_method_uninhibit[][10] =
+{
+       [FDO_SS] = "UnInhibit",
+       [FDO_PM] = "UnInhibit",
+       [MATE]   = "Uninhibit",
+       [GNOME]  = "Uninhibit",
+};
+
+
+struct inhibit_sys
+{
+       DBusConnection *conn;
+       DBusPendingCall *pending;
+       dbus_uint32_t cookie;
+       enum inhibit_api api;
+};
+
+
+void do_inhibit(struct inhibit_sys *sys, int flags)
+{
+       enum inhibit_api type = sys->api;
+
+       /* Receive reply from previous request, possibly hours later ;-) */
+       if ( sys->pending != NULL ) {
+
+               DBusMessage *reply;
+
+               /* NOTE: Unfortunately, the pending reply cannot simply be
+                * cancelled.   Otherwise, the cookie would be lost and
+                * inhibition would remain on (until complete disconnection from
+                * the bus). */
+               dbus_pending_call_block(sys->pending);
+               reply = dbus_pending_call_steal_reply(sys->pending);
+               dbus_pending_call_unref(sys->pending);
+               sys->pending = NULL;
+
+               if ( reply != NULL ) {
+                       if ( !dbus_message_get_args(reply, NULL,
+                                                   DBUS_TYPE_UINT32,
+                                                   &sys->cookie,
+                                                   DBUS_TYPE_INVALID))
+                       {
+                               sys->cookie = 0;
+                       }
+                       dbus_message_unref(reply);
+               }
+               fprintf(stderr, "got cookie %i", (int)sys->cookie);
+
+       }
+
+       /* FIXME: This check is incorrect if flags change from one non-zero value
+        * to another one. But the D-Bus API cannot currently inhibit suspend
+        * independently from the screensaver. */
+       if ( !sys->cookie == !flags ) return; /* nothing to do */
+
+       /* Send request */
+       const char *method = flags ? "Inhibit" : dbus_method_uninhibit[type];
+       dbus_bool_t ret;
+
+       DBusMessage *msg = dbus_message_new_method_call(dbus_service[type],
+                                                       dbus_path[type],
+                                                       dbus_service[type],
+                                                       method);
+       if ( msg == NULL ) return;
+
+    if (flags)
+    {
+        const char *app = PACKAGE;
+        const char *reason = "Presentation in progress";
+
+        assert(sys->cookie == 0);
+
+        switch (type)
+        {
+            case MATE:
+            case GNOME:
+            {
+                dbus_uint32_t xid = 0; // FIXME ?
+                dbus_uint32_t gflags = 0xC;
+
+                ret = dbus_message_append_args(msg, DBUS_TYPE_STRING, &app,
+                                                    DBUS_TYPE_UINT32, &xid,
+                                                    DBUS_TYPE_STRING, &reason,
+                                                    DBUS_TYPE_UINT32, &gflags,
+                                                    DBUS_TYPE_INVALID);
+                break;
+            }
+            default:
+                ret = dbus_message_append_args(msg, DBUS_TYPE_STRING, &app,
+                                                    DBUS_TYPE_STRING, &reason,
+                                                    DBUS_TYPE_INVALID);
+                break;
+        }
+
+        if (!ret
+        || !dbus_connection_send_with_reply(sys->conn, msg, &sys->pending, -1))
+            sys->pending = NULL;
+    }
+    else
+    {
+        assert(sys->cookie != 0);
+        if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &sys->cookie,
+                                           DBUS_TYPE_INVALID)
+         || !dbus_connection_send (sys->conn, msg, NULL))
+            sys->cookie = 0;
+    }
+    dbus_connection_flush(sys->conn);
+    dbus_message_unref(msg);
+}
+
+
+void inhibit_cleanup(struct inhibit_sys *sys)
+{
+       if ( sys->pending != NULL ) {
+               dbus_pending_call_cancel(sys->pending);
+               dbus_pending_call_unref(sys->pending);
+       }
+
+       dbus_connection_close(sys->conn);
+       dbus_connection_unref(sys->conn);
+       free(sys);
+}
+
+
+struct inhibit_sys *inhibit_prepare()
+{
+       struct inhibit_sys *sys;
+       DBusError err;
+       int i;
+
+       sys = malloc(sizeof(struct inhibit_sys));
+       if ( sys == NULL ) {
+               fprintf(stderr, "Failed to allocate inhibit.\n");
+               return NULL;
+       }
+
+       dbus_error_init(&err);
+
+       sys->conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err);
+       if ( sys->conn == NULL ) {
+               fprintf(stderr, "cannot connect to session bus: %s",
+                       err.message);
+               dbus_error_free(&err);
+               free(sys);
+               return NULL;
+       }
+
+       sys->pending = NULL;
+       sys->cookie = 0;
+
+       for ( i=0; i<MAX_API; i++ ) {
+               if ( dbus_bus_name_has_owner(sys->conn, dbus_service[i], NULL) )
+               {
+                       fprintf(stderr, "found service %s", dbus_service[i]);
+                       sys->api = i;
+                       return sys;
+               }
+
+               fprintf(stderr, "cannot find service %s", dbus_service[i]);
+       }
+
+       inhibit_cleanup(sys);
+       return NULL;
+}
diff --git a/src/inhibit_screensaver.h b/src/inhibit_screensaver.h
new file mode 100644 (file)
index 0000000..0a62adc
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * inhibit_screensaver.h
+ *
+ * Copyright © 2013 Thomas White <taw@bitwiz.org.uk>
+ *
+ * This file is part of Colloquium.
+ *
+ * Colloquium 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 3 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef INHIBIT_SCREENSAVER_H
+#define INHIBIT_SCREENSAVER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+struct inhibit_sys;
+
+extern void do_inhibit(struct inhibit_sys *sys, int flags);
+extern void inhibit_cleanup(struct inhibit_sys *sys);
+extern struct inhibit_sys *inhibit_prepare(void);
+
+#endif /* INHIBIT_SCREENSAVER_H */
index 3c4e593..0847e84 100644 (file)
@@ -38,6 +38,7 @@
 #include "imagestore.h"
 #include "wrap.h"
 #include "notes.h"
+#include "inhibit_screensaver.h"
 
 
 static int num_presentations = 0;
@@ -55,6 +56,8 @@ void free_presentation(struct presentation *p)
        (*p->num_presentations)--;
        if ( *p->num_presentations == 0 ) final = 1;
 
+       if ( p->inhibit != NULL ) inhibit_cleanup(p->inhibit);
+
        /* FIXME: Loads of stuff leaks here */
        free(p->filename);
        imagestore_destroy(p->is);
index ff065a6..5af844c 100644 (file)
@@ -186,6 +186,8 @@ struct presentation
        StyleSheet       *ss;
        unsigned int      num_slides;
        struct slide    **slides;
+
+       struct inhibit_sys *inhibit;
 };
 
 
index 4847077..66e4994 100644 (file)
@@ -35,6 +35,7 @@
 #include "mainwindow.h"
 #include "render.h"
 #include "pr_clock.h"
+#include "inhibit_screensaver.h"
 
 
 /* Force a redraw of the slideshow */
@@ -134,6 +135,7 @@ static gint next_slide_sig(GtkWidget *widget, struct presentation *p)
 
 void end_slideshow(struct presentation *p)
 {
+       if ( p->inhibit != NULL ) do_inhibit(p->inhibit, 0);
        gtk_widget_destroy(p->ss_drawingarea);
        gtk_widget_destroy(p->slideshow);
        p->slideshow = NULL;
@@ -223,6 +225,10 @@ void try_start_slideshow(struct presentation *p)
 
        p->ss_blank = 0;
 
+       if ( p->inhibit == NULL ) {
+               p->inhibit = inhibit_prepare();
+       }
+
        n = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
        p->ss_drawingarea = gtk_drawing_area_new();
@@ -266,6 +272,8 @@ void try_start_slideshow(struct presentation *p)
        gtk_window_fullscreen(GTK_WINDOW(n));
        gtk_widget_show_all(GTK_WIDGET(n));
 
+       if ( p->inhibit != NULL ) do_inhibit(p->inhibit, 1);
+
        //if ( p->prefs->open_notes ) open_notes(p);  FIXME!
 
        p->cur_proj_slide = p->cur_edit_slide;