Search code examples
pythonlinuxubuntudbusscreensaver

Screensaver status on Linux via python-dbus using Python3


Based off of https://stackoverflow.com/a/17981327/9614384:

import dbus

bus = dbus.SessionBus()
screensaver = bus.get_object('org.gnome.ScreenSaver', '/')
print(bool(screensaver.GetActive()))

I'm trying to access the screensaver since this has changed in Ubuntu 18.04, but this code gives me the following error:

dbus.exceptions.DBusException: org.freedesktop.DBus.Error.UnknownMethod: No such interface '(null)' on object at path /

Solution

  • Scott's EDIT produces an error on my Ubuntu 18.04 machine running python3:

    dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NotSupported: This method is not implemented
    

    I suppose the code for all available screensavers should read:

    import dbus
    session_bus = dbus.SessionBus()
    screensaver_list = ['org.gnome.ScreenSaver',
                        'org.cinnamon.ScreenSaver',
                        'org.kde.screensaver',
                        'org.freedesktop.ScreenSaver']
    
    for each in screensaver_list:
        try:
            object_path = '/{0}'.format(each.replace('.', '/'))
            get_object = session_bus.get_object(each, object_path)
            get_interface = dbus.Interface(get_object, each)
            status = bool(get_interface.GetActive())        
            print(status)
        except dbus.exceptions.DBusException:
            pass
    

    BTW, the same exercise in C results in the below code:

    #include<dbus/dbus.h>
    #include<stdbool.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    int scrstat(DBusConnection* conn, const char* scrdbus) {
        bool* retbl = NULL;
        char* scrobj;
        DBusMessage* msg;
        DBusMessageIter MsgIter;
        DBusPendingCall* pending;
        int i;
    
        scrobj = (char *) malloc((1+strlen(scrdbus))*sizeof(char));
        strncpy(scrobj,"/", 2*sizeof(char));
        strncat(scrobj,scrdbus,strlen(scrdbus)*sizeof(char));
        for(i=0;i<strlen(scrobj);i++) {
            if(scrobj[i] == '.') scrobj[i] = '/';   
        }
    
        // create a new method call and check for errors
        msg = dbus_message_new_method_call(scrdbus, // target for the method call
                              scrobj, // object to call on
                              scrdbus, // interface to call on
                              "GetActive"); // method name  
    
        if (NULL == msg) { 
            fprintf(stderr, "Message NULL.\n");
            return(1);
        }
    
        // send message and get a handle for a reply
        if (!dbus_connection_send_with_reply (conn, msg, &pending, -1)) { // -1 is default timeout
            fprintf(stderr, "Out of memory.\n"); 
            return(1);
        }
    
        if (NULL == pending) { 
            fprintf(stderr, "Pending call NULL.\n"); 
            return(1); 
        }
    
        // free message
        dbus_message_unref(msg);
        // block until we recieve a reply
        dbus_pending_call_block(pending);
    
        if(!dbus_message_iter_init(msg, &MsgIter)) { //msg is pointer to dbus message received
            fprintf(stderr, "Message without arguments.\n");
            return(1);
        }
    
        if (DBUS_TYPE_BOOLEAN   == dbus_message_iter_get_arg_type(&MsgIter)){
            dbus_message_iter_get_basic(&MsgIter, &retbl);//this function is used to read basic dbus types like int, string etc. 
            fprintf(stdout, retbl ? "Screensaver status: on.\n" : "Screensaver status: off.\n");
        }
    
        // free the pending message handle
        dbus_pending_call_unref(pending);
        free(scrobj);
    
        return(0);
    }
    
    int main() {
        const char* scrdbus[5];
        scrdbus[0] = "org.cinnamon.ScreenSaver";
        scrdbus[1] = "org.freedesktop.ScreenSaver";
        scrdbus[2] = "org.gnome.ScreenSaver";
        scrdbus[3] = "org.kde.Screensaver";
        scrdbus[4] = NULL;
        DBusConnection* conn;   
        DBusError err;
        int i=0;
    
        // initialise the errors
        dbus_error_init(&err);
        conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
    
        if (dbus_error_is_set(&err)) { 
            fprintf(stderr, "Connection error (%s).\n", err.message); 
            dbus_error_free(&err); 
        }
    
        if (NULL == conn) {
            fprintf(stderr, "Connection NULL.\n"); 
            return(1); 
        }
    
        while(NULL != scrdbus[i]) {
            scrstat(conn, scrdbus[i]);
            i++;
        }
    
        dbus_connection_unref(conn);
        return(0);
    }
    

    The above code compiles using gcc:

    gcc -pthread -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -o scrstat scrstat.c -ldbus-1
    

    The beauty of python3 lies in 17 lines of codes instead of 98 lines. The beauty of C lies in 10 milliseconds execution time instead of 128 milliseconds.