Translate

Archives

GNOME Shell 3.6 DBus Support

I have written before about the DBus support in GNOME Shell. In GNOME Shell 3.6, there are significant changes to the DBus interface; hence this post.

The file that contains the DBus interface definitions is shellDBus.js. It contains the following two DBus interface definitions:

  <interface name="org.gnome.Shell">
    <method name="Eval">
      <arg type="s" name="script" direction="in">
      </arg>
      <arg type="b" name="success" direction="out">
      </arg>
      <arg type="s" name="result" direction="out">
      </arg>
    </method>
    <method name="ScreenshotArea">
      <arg type="i" name="x" direction="in">
      </arg>
      <arg type="i" name="y" direction="in">
      </arg>
      <arg type="i" name="width" direction="in">
      </arg>
      <arg type="i" name="height" direction="in">
      </arg>
      <arg type="b" name="flash" direction="in">
      </arg>
      <arg type="s" name="filename" direction="in">
      </arg>
      <arg type="b" name="success" direction="out">
      </arg>
    </method>
    <method name="ScreenshotWindow">
      <arg type="b" name="include_frame" direction="in">
      </arg>
      <arg type="b" name="include_cursor" direction="in">
      </arg>
      <arg type="b" name="flash" direction="in">
      </arg>
      <arg type="s" name="filename" direction="in">
      </arg>
      <arg type="b" name="success" direction="out">
      </arg>
    </method>
    <method name="Screenshot">
      <arg type="b" name="include_cursor" direction="in">
      </arg>
      <arg type="b" name="flash" direction="in">
      </arg>
      <arg type="s" name="filename" direction="in">
      </arg>
      <arg type="b" name="success" direction="out">
      </arg>
    </method>
    <method name="FlashArea">
      <arg type="i" name="x" direction="in">
      </arg>
      <arg type="i" name="y" direction="in">
      </arg>
      <arg type="i" name="width" direction="in">
      </arg>
      <arg type="i" name="height" direction="in">
      </arg>
    </method>
    <property type="b" name="OverviewActive" access="readwrite">
    </property>
    <property type="s" name="ShellVersion" access="read">
    </property>
  </interface>

  <interface name="org.gnome.Shell.Extensions">
    <method name="ListExtensions">
      <arg type="a{sa{sv}}" name="extensions" direction="out">
      </arg>
    </method>
    <method name="GetExtensionInfo">
      <arg type="s" name="extension" direction="in">
      </arg>
      <arg type="a{sv}" name="info" direction="out">
      </arg>
    </method>
    <method name="GetExtensionErrors">
      <arg type="s" name="extension" direction="in">
      </arg>
      <arg type="as" name="errors" direction="out">
      </arg>
    </method>
    <method name="InstallRemoteExtension">
      <arg type="s" name="uuid" direction="in">
      </arg>
      <arg type="s" name="result" direction="out">
      </arg>
    </method>
    <method name="UninstallExtension">
      <arg type="s" name="uuid" direction="in">
      </arg>
      <arg type="b" name="success" direction="out">
      </arg>
    </method>
    <method name="LaunchExtensionPrefs">
      <arg type="s" name="uuid" direction="in">
      </arg>
    </method>
    <method name="ReloadExtension">
      <arg type="s" name="uuid" direction="in">
      </arg>
    </method>
    <method name="CheckForUpdates">
    </method>
    <signal name="ExtensionStatusChanged">
      <arg type="s" name="uuid">
      </arg>
      <arg type="i" name="state">
      </arg>
      <arg type="s" name="error">
      </arg>
    </signal>
    <property type="s" name="ShellVersion" access="read">
    </property>
  </interface>
</node>


This is different than in previous versions of GNOME Shell which had only one Dbus interface.

For example, here is the GNOME Shell DBus interface definition from GNOME 3.4

<interface name="org.gnome.Shell">
<method name="Eval">
    <arg type="s" direction="in" name="script" />
    <arg type="b" direction="out" name="success" />
    <arg type="s" direction="out" name="result" />
</method>
<method name="ListExtensions">
    <arg type="a{sa{sv}}" direction="out" name="extensions" />
</method>
<method name="GetExtensionInfo">
    <arg type="s" direction="in" name="extension" />
    <arg type="a{sv}" direction="out" name="info" />
</method>
<method name="GetExtensionErrors">
    <arg type="s" direction="in" name="extension" />
    <arg type="as" direction="out" name="errors" />
</method>
<method name="ScreenshotArea">
    <arg type="i" direction="in" name="x"/>
    <arg type="i" direction="in" name="y"/>
    <arg type="i" direction="in" name="width"/>
    <arg type="i" direction="in" name="height"/>
    <arg type="b" direction="in" name="flash"/>
    <arg type="s" direction="in" name="filename"/>
    <arg type="b" direction="out" name="success"/>
</method>
<method name="ScreenshotWindow">
    <arg type="b" direction="in" name="include_frame"/>
    <arg type="b" direction="in" name="include_cursor"/>
    <arg type="b" direction="in" name="flash"/>
    <arg type="s" direction="in" name="filename"/>
    <arg type="b" direction="out" name="success"/>
</method>
<method name="Screenshot">
    <arg type="b" direction="in" name="include_cursor"/>
    <arg type="b" direction="in" name="flash"/>
    <arg type="s" direction="in" name="filename"/>
    <arg type="b" direction="out" name="success"/>
</method>
<method name="FlashArea">
    <arg type="i" direction="in" name="x"/>
    <arg type="i" direction="in" name="y"/>
    <arg type="i" direction="in" name="width"/>
    <arg type="i" direction="in" name="height"/>
</method>
<method name="EnableExtension">
    <arg type="s" direction="in" name="uuid"/>
</method>
<method name="DisableExtension">
    <arg type="s" direction="in" name="uuid"/>
</method>
<method name="InstallRemoteExtension">
    <arg type="s" direction="in" name="uuid"/>
    <arg type="s" direction="in" name="version"/>
</method>
<method name="UninstallExtension">
    <arg type="s" direction="in" name="uuid"/>
    <arg type="b" direction="out" name="success"/>
</method>
<method name="LaunchExtensionPrefs">
    <arg type="s" direction="in" name="uuid"/>
</method>
<property name="OverviewActive" type="b" access="readwrite" />
<property name="ApiVersion" type="i" access="read" />
<property name="ShellVersion" type="s" access="read" />
<signal name="ExtensionStatusChanged">
    <arg type="s" name="uuid"/>
    <arg type="i" name="state"/>
    <arg type="s" name="error"/>
</signal>
</interface>


Most of the changes make sense except for the spurious removal of ApiVersion which was removed by Jasper StPierre in July 2012. Of course, as usual, Jasper provided no rationale or advance warning for the removal of this DBus method. Typical GNOME cabal behaviour! By the way, 2012 was the year that GNOME users learned that their was no community around GNOME – there was simply the GNOME cabal and the unwashed rest.

Here is a small Python utility which uses the new DBus interface to list your installed extensions:

#!/usr/bin/python
 
try:
    import os
    import sys
except ImportError, detail:
    print detail
    sys.exit(1)
 
from gi.repository import Gio, GLib
 
 
extstate = { 1:"enabled",
             2:"disabled",
             3:"error",
             4:"out of date",
             5:"downloading",
             6:"initialized",
            99:"uninstalled"}
 
exttype  = { 1:"system",
             2:"per user"}
 
 
GNOME_SHELL_IFACE = 'org.gnome.Shell'
GNOME_SHELL_PATH  = '/org/gnome/Shell'

 
class ExtensionTool:
    def __init__(self):
        try:
            self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            self.proxy = Gio.DBusProxy.new_sync( self.bus, Gio.DBusProxyFlags.NONE, None,
                 GNOME_SHELL_IFACE, GNOME_SHELL_PATH, 'org.gnome.Shell.Extensions', None)
        except:
            print "Exception: %s" % sys.exec_info()[1]
 
    def list_extensions(self):
        return self.proxy.ListExtensions()
 
def main():
    s = ExtensionTool()
    l = s.list_extensions()
    for k, v in l.iteritems():
        print '%s: %s, %s' % (v["uuid"], extstate[v["state"]], exttype[v["type"]])
    print
 
if __name__ == "__main__":
    main()


here is what the above Python script outputted on the system on which I am writing this post:

./example1.py
startofweek@fpmurphy.com: enabled, per user
poweroptions@fpmurphy.com: enabled, per user
removeusername@fpmurphy.com: initialized, per user
removeusermenuseparators@fpmurphy.com: enabled, per user
user-theme@gnome-shell-extensions.gcampax.github.com: enabled, system
disableactivitiesbutton@fpmurphy.com: initialized, per user
noa11y@fpmurphy.com: enabled, per user
weather@fpmurphy.com: enabled, per user
lookingglassbutton@fpmurphy.com: enabled, per user


The following simple Python utility using DBus to remove an installed extension:

#!/usr/bin/python
 
try:
    import os
    import sys
    import argparse
except ImportError, detail:
    print detail
    sys.exit(1)
 
from gi.repository import Gio, GLib
 
 
GNOME_SHELL_IFACE = 'org.gnome.Shell'
GNOME_SHELL_PATH  = '/org/gnome/Shell'
 
class ExtensionTool:
    def __init__(self):
        try:
            self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            self.proxy = Gio.DBusProxy.new_sync( self.bus, Gio.DBusProxyFlags.NONE, None,
                 GNOME_SHELL_IFACE, GNOME_SHELL_PATH, 'org.gnome.Shell.Extensions', None)
        except:
            print "Exception: %s" % sys.exec_info()[1]
 
    def uninstall_extension(self, uuid):
        return self.proxy.UninstallExtension('(s)', uuid)
 
def main():
    parser = argparse.ArgumentParser(description="GNOME Shell uninstall tool")
    group = parser.add_mutually_exclusive_group()
    group.add_argument("-u", "--uninstall", nargs=1, action="store", dest="uninstall",
                       metavar="uuid", help="uninstall extension")

    args = parser.parse_args()

    if args.uninstall:
        s = ExtensionTool()
        s.uninstall_extension("".join(args.uninstall))
        print "%r is now uninstalled." % ("".join(args.uninstall))
    else:
        parser.print_usage()
        sys.exit(0)

if __name__ == "__main__":
    main()


and, yes, it really does remove all the files belonging to the extension!

Here is a simple Python utility which you can use to automatically download and install an extension from http://extensions.gnome.org:

#!/usr/bin/python
 
try:
    import os
    import sys
    import argparse
except ImportError, detail:
    print detail
    sys.exit(1)
 
from gi.repository import Gio, GLib
 
 
GNOME_SHELL_IFACE = 'org.gnome.Shell'
GNOME_SHELL_PATH  = '/org/gnome/Shell'
 
class ExtensionTool:
    def __init__(self):
        try:
            self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            self.proxy = Gio.DBusProxy.new_sync( self.bus, Gio.DBusProxyFlags.NONE, None,
                 GNOME_SHELL_IFACE, GNOME_SHELL_PATH, 'org.gnome.Shell.Extensions', None)
        except:
            print "Exception: %s" % sys.exec_info()[1]
 
    def install_extension(self, uuid):
        return self.proxy.InstallRemoteExtension('(s)', uuid)
 
def main():
    parser = argparse.ArgumentParser(description="GNOME Shell install tool")
    group = parser.add_mutually_exclusive_group()
    group.add_argument("-i", "--install", nargs=1, action="store", dest="install",
                       metavar="uuid", help="install extension")

    args = parser.parse_args()

    if args.install:
        s = ExtensionTool()
        s.install_extension("".join(args.install))
        print "%r is now installed." % ("".join(args.install))
    else:
        parser.print_usage()
        sys.exit(0)

if __name__ == "__main__":
    main()


You need to know the UUID of the extension that you want to install in order to use this utility. For example the UUID for the hidetopbar by
Mathieu Lutfy is hidetopbar@mathieu.bidon.ca. Mathieu based this extension, with my permission, on one of my early GNOME Shell extensions which hid the top panel when not required. Note this utility will fail to install the extension if unzip is not installed on your system or on your path as there is a builtin dependency on this utility in the GNOME Shell extensionDownloader.js source.

For my final example, this simple utility flashes an area of your screen. Why this method would ever be required by Gnome Shell, I have no idea.

#!/usr/bin/python
 
try:
    import os
    import sys
    import argparse
except ImportError, detail:
    print detail
    sys.exit(1)
 
from gi.repository import Gio, GLib
 
 
GNOME_SHELL_IFACE = 'org.gnome.Shell'
GNOME_SHELL_PATH  = '/org/gnome/Shell'
 
class ExtensionTool:
    def __init__(self):
        try:
            self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            self.proxy = Gio.DBusProxy.new_sync( self.bus, Gio.DBusProxyFlags.NONE, None,
                 GNOME_SHELL_IFACE, GNOME_SHELL_PATH, 'org.gnome.Shell', None)
        except:
            print "Exception: %s" % sys.exec_info()[1]
 
    def flash_area(self, x, y, width, height):
        return self.proxy.FlashArea('(iiii)', x, y, width, height)
 
def main():
    parser = argparse.ArgumentParser(description="GNOME Shell flash tool")
    parser.add_argument("x", type=int)
    parser.add_argument("y", type=int)
    parser.add_argument("width", type=int)
    parser.add_argument("height", type=int)
    args = parser.parse_args()

    # hack - better argument checking should be provided
    if len(sys.argv) == 5:
        s = ExtensionTool()
        s.flash_area(args.x, args.y, args.width, args.height)
    else:
        parser.print_usage()
        sys.exit(0)

if __name__ == "__main__":
    main()


You need to pass in 4 arguments on this utility’s command line, i.e x, y, width and height.

Other than the changes to the DBus interface, differences between Gnome Shell 3.4 and 3.6, as far as GNOME Shell extensions are concerned, are fairly easy to code around. Certainly they are far less significant to deal with than the changes that were required to port GNOME Shell extensions from 3.2 to 3.4.

Comments are closed.