1#!/usr/local/bin/python3.8
2
3from gi.repository import GLib, Gio
4import sys
5import signal
6
7from xml.etree import ElementTree
8
9signal.signal(signal.SIGINT, signal.SIG_DFL)
10
11class MethodArg:
12    def __init__(self, method, arg_index, arg_type):
13        self.method = method
14        self.arg_index = arg_index
15        self.arg_type = arg_type
16
17class CinnamonDBusCommand:
18    def __init__(self, mainloop):
19        self.mainloop = mainloop
20
21        try:
22            self.cinnamon_proxy = Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SESSION,
23                                                             Gio.DBusProxyFlags.NONE,
24                                                             None,
25                                                             "org.Cinnamon",
26                                                             "/org/Cinnamon",
27                                                             "org.Cinnamon",
28                                                             None)
29        except GLib.Error:
30            self.cinnamon_proxy = None
31            print("Cannot acquire org.Cinnamon proxy")
32
33        introspection_info = Gio.DBusConnection.call_sync(Gio.bus_get_sync(Gio.BusType.SESSION),
34                                                          "org.Cinnamon",
35                                                          "/org/Cinnamon",
36                                                          "org.freedesktop.DBus.Introspectable",
37                                                          "Introspect",
38                                                          None,
39                                                          GLib.VariantType(type_string="(s)"),
40                                                          0,
41                                                          -1,
42                                                          None)
43
44        unpacked = introspection_info.unpack()[0]
45        self.tree = ElementTree.fromstring(unpacked)
46
47        self.arg_list = []
48        list_methods = False
49
50        if len(sys.argv) < 2:
51            list_methods = True
52            print("\nProvide a method name and any parameters.  Available commands are:\n")
53
54        for interface in self.tree:
55            if interface.attrib["name"] == "org.Cinnamon":
56                for entry in interface:
57                    if entry.tag != "method":
58                        continue
59                    arg_str = ""
60
61                    i = 0
62
63                    for arg in entry:
64                        if arg.attrib["direction"] != "in":
65                            continue
66
67                        if list_methods:
68                            arg_str += "--- %s ('%s') " % (arg.attrib["name"], arg.attrib["type"])
69
70                        self.arg_list.append(MethodArg(entry.attrib["name"], i, arg.attrib["type"]))
71                        i += 1
72
73                    if list_methods:
74                        print("%s %s" % (entry.attrib["name"], arg_str))
75
76        if list_methods:
77            print("\nNote: boolean arguments should be specified as 0 (false) and 1 (true)")
78            print("\nAn example is:   cinnamon-dbus-command GetMonitorWorkRect 0\n")
79            exit(1)
80
81        self.parse_command(sys.argv[1:])
82
83    def method_call_finished_callback(self, proxy, result, data=None):
84        try:
85            variant = proxy.call_finish(result)
86            print("Returned: %s" % str(variant.unpack()))
87        except GLib.Error as e:
88            print("An error occurred attempting to run the command: %s" % e.message)
89
90        self.mainloop.quit()
91
92    def parse_command(self, args):
93        method_name = args[0]
94
95        parameters = self.build_variant(method_name, args[1:])
96
97        try:
98            self.cinnamon_proxy.call(method_name,
99                                 parameters,
100                                 0,
101                                 -1,
102                                 None,
103                                 self.method_call_finished_callback,
104                                 None)
105        except TypeError as e:
106            print("Could not send command over the bus: %s" % str(e))
107            exit(1)
108
109    def build_variant(self, method_name, in_args):
110        i = 0
111
112        variant_string = "("
113
114        while i < len(in_args):
115            arg = in_args[i]
116
117            arg_info = self.lookup_arg(method_name, i)
118
119            if arg_info:
120                variant_string += arg_info.arg_type
121            else:
122                print("The parameter '%s' seems invalid for method '%s'" % (arg, method_name))
123                exit(1)
124
125            i += 1
126
127        variant_string += ")"
128
129        parsed_args = []
130        for arg_as_str in in_args:
131            try:
132                parsed_args.append(eval(arg_as_str))
133            except:
134                parsed_args.append(arg_as_str)
135
136        print("Parsed: %s(format_string='%s', args=%s)\n" % (method_name, variant_string, parsed_args))
137
138        try:
139            variant = GLib.Variant(variant_string, tuple(parsed_args))
140        except TypeError as e:
141            print("Could not construct argument variant for method: %s" % str(e))
142            exit(1)
143
144        return variant
145
146    def lookup_arg(self, method_name, index):
147        for arg in self.arg_list:
148            if arg.method == method_name and arg.arg_index == index:
149                return arg
150
151        return None
152
153if __name__ == "__main__":
154    ml = GLib.MainLoop.new(None, True)
155    main = CinnamonDBusCommand(ml)
156
157    ml.run()
158