Merge branch 'stable' into 'main'
[ladish.git] / ladish_control
blob6ea20394c7517c1fca6d872491e9e9c270ae60df
1 #!/usr/bin/python3
3 # LADI Session Handler (ladish)
5 # Copyright (C) 2008, 2009, 2010, 2011 Nedko Arnaudov <nedko@arnaudov.name>
7 #*************************************************************************
8 # This file contains code of the commandline control app
9 #*************************************************************************
11 # LADI Session Handler is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # LADI Session Handler is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
23 # or write to the Free Software Foundation, Inc.,
24 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 service_name = 'org.ladish'
28 control_object_path = "/org/ladish/Control"
29 studio_object_path = "/org/ladish/Studio"
31 control_interface_name = 'org.ladish.Control'
32 studio_interface_name = 'org.ladish.Studio'
33 app_supervisor_interface_name = 'org.ladish.AppSupervisor'
34 room_interface_name = 'org.ladish.Room'
35 patchbay_interface_name = 'org.jackaudio.JackPatchbay'
37 import sys
38 import os
39 import time
40 from traceback import print_exc
42 import dbus
44 def bool_convert(str_value):
45     if str_value.lower() == "false":
46         return False
48     if str_value.lower() == "off":
49         return False
51     if str_value.lower() == "no":
52         return False
54     if str_value == "0":
55         return False
57     if str_value.lower() == "(null)":
58         return False
60     return bool(str_value)
62 def dbus_type_to_python_type(dbus_value):
63     if type(dbus_value) == dbus.Boolean:
64         return bool(dbus_value)
65     if type(dbus_value) == dbus.Int32 or type(dbus_value) == dbus.UInt32:
66         return int(dbus_value)
67     return dbus_value
69 def dbus_type_to_type_string(dbus_value):
70     if type(dbus_value) == dbus.Boolean:
71         return "bool"
72     if type(dbus_value) == dbus.Int32:
73         return "sint"
74     if type(dbus_value) == dbus.UInt32:
75         return "uint"
76     if type(dbus_value) == dbus.Byte:
77         return "char"
78     if type(dbus_value) == dbus.String:
79         return "str"
81     return None                         # throw exception here?
83 def dbus_typesig_to_type_string(type_char):
84     type_char = str(type_char)
85     if type_char == 'i':
86         return "sint"
87     if type_char == 'u':
88         return "uint"
89     if type_char == 'y':
90         return "char"
91     if type_char == 's':
92         return "str"
93     if type_char == 'b':
94         return "bool"
96     print('unknown dbus typesig')
97     return None                         # throw exception here?
99 def parse_new_app_args(params_array):
100     #print params_array
101     cmdline = params_array[0]
102     index = 1
104     name = ''
105     level = '0'
106     term = False
108     if index < len(params_array):
109         if params_array[index] == '-':
110             return index + 1, cmdline, name, level, term
111         name = params_array[index]
112         index += 1
114     if index < len(params_array):
115         if params_array[index] == '-':
116             return index + 1, cmdline, name, level, term
117         # direct conversion to dbus.Byte is wrong because ord() is used ("1" -> 0x31 instead of "1" -> 0x01)
118         level = params_array[index]
119         index += 1
121     if index < len(params_array):
122         if params_array[index] == '-':
123             return index + 1, cmdline, name, level, term
124         if params_array[index] == 'term':
125             term = True
126             index += 1
128     if index < len(params_array) and  params_array[index] == '-':
129         index += 1
131     return index, cmdline, name, level, term
133 def add_app(obj, cmdline, name, level, term):
134     dbus.Interface(obj, app_supervisor_interface_name).RunCustom2(term, cmdline, name, level)
136 def get_room_obj_by_name(bus, studio_iface, room_name):
137     for room in studio_iface.GetRoomList():
138         #print repr(room)
139         opath = room[0]
140         name = room[1]["name"]
141         if name == room_name:
142             return bus.get_object(service_name, opath)
144 def dump_graph(obj):
145     patchbay_iface = dbus.Interface(obj, patchbay_interface_name)
146     graph = patchbay_iface.GetGraph(0)
147     for client in graph[1]:
148         print('"%s"' % client[1])
149         for port in client[2]:
150             port_flags = port[2]
151             if (port_flags & 1) != 0:
152                 port_flags = "input"
153             elif (port_flags & 2) != 0:
154                 port_flags = "output"
155             else:
156                 port_flags = "unknown"
158             port_type = port[3]
159             if port_type == 0:
160                 port_type = "audio"
161             elif port_type == 1:
162                 port_type = "midi"
163             else:
164                 port_type = "unknown"
166             print('  "%s" [%s %s]' % (port[1], port_flags, port_type))
167     print
168     if len(graph[2]):
169         if len(graph[2]) == 1:
170             print("1 connection:")
171         else:
172             print("%u connections:" % len(graph[2]))
173         for connection in graph[2]:
174             print('"%s":"%s" -> "%s":"%s"' % (connection[1], connection[3], connection[5], connection[7]))
175     else:
176         print("0 connections.")
178 def main():
179     if len(sys.argv) == 1:
180         argv0 = os.path.basename(sys.argv[0])
181         print("Usage: %s [command] [command] ..." % argv0)
182         print("Commands:")
183         print("    exit                      - exit ladish dbus service")
184         print("    slist                     - list studios")
185         print("    alist                     - list apps")
186         print("    sload <studioname>        - load studio")
187         print("    sdel <studioname>         - delete studio")
188         print("    snew [studioname]         - new studio")
189         print("    sisloaded                 - is studio loaded?")
190         print("    sname                     - get studio name")
191         print("    ssave                     - save studio")
192         print("    sunload                   - unload studio")
193         print("    srename <studioname>      - rename studio")
194         print("    sstart                    - start studio")
195         print("    sstop                     - stop studio")
196         print("    rtlist                    - list room templates")
197         print("    rtdel <roomtemplatename>  - delete room template")
198         print("    rtnew <roomtemplatename>  - create new room template")
199         print("    snewroom <rname> <rtname> - create new studio room")
200         print("    srlist                    - list studio rooms")
201         print("    sdelroom <rname>          - delete studio room")
202         print("    pload <rname> <proj_dir>  - load project into room")
203         print("    punload <rname>           - unload project from room")
204         print("    psave <rname>             - save project")
205         print("    psaveas <rname> <proj_dir> <proj_name>  - save as project")
206         print("    snewapp <appargs>         - add new app to studio (see below for more info)")
207         print("    rnewapp <rname> <appargs> - add new app to room (see below for more info)")
208         print("    sgdump                    - studio graph dump")
209         print("    rgdump <rname>            - room graph dump")
210         print("    sconnect <client1> <port1> <client2> <port2>            - connect ports in studio");
211         print("    sdisconnect <client1> <port1> <client2> <port2>         - disconnect ports in studio");
212         print("    rconnect <rname> <client1> <port1> <client2> <port2>    - connect ports in studio");
213         print("    rdisconnect <rname> <client1> <port1> <client2> <port2> - disconnect ports in room");
214         print("");
215         print("Add new app arguments:");
216         print("    <commandline> [<name> [<level>] [term]] [-]");
217         print("");
218         print("    <commandline> - the commandline to execut");
219         print("    <name>  - app name");
220         print("    <level> - level, default is 0");
221         print("    term    - if specified, app will be run in terminal");
222         print("    -       - marks end of new app params, useful if there are other commands following");
223         print("");
224         print("Examples:");
225         print("");
226         print(" * Add to studio jack_mixer instance named \"mixer\", at level 1, without terminal");
227         print("");
228         print("    $ %s snewapp jack_mixer mixer 1" % argv0);
229         print("");
230         print(" * Add to room \"main\" fluidjack instance named \"fluid\", at level 0, with terminal");
231         print("");
232         print("    $ %s rnewapp main \"fluidjack FluidR3.SF2\" fluid 0 term" % argv0);
233         print("");
234         sys.exit(0)
235     
236     bus = dbus.SessionBus()
237     control_obj = None
238     studio_obj = None
240     # check arguments
241     index = 1
242     while index < len(sys.argv):
243         arg = sys.argv[index]
244         index += 1
245         try:
246             if not control_obj:
247                 control_obj = bus.get_object(service_name, control_object_path)
248                 control_iface = dbus.Interface(control_obj, control_interface_name)
249             
250             if arg == "exit":
251                 print("--- exit")
252                 control_iface.Exit()
253                 time.sleep(1)
254                 # we have deactivated the object and we need to get new connection if there are more commands
255                 control_obj = None
256                 control_iface = None
257             elif arg == 'slist':
258                 print("--- studio list")
259                 for studio in control_iface.GetStudioList():
260                     name = studio[0]
261                     mtime = studio[1]['Modification Time']
262                     print('"%s" last modified on %s' % (name, time.ctime(mtime)))
263             elif arg == 'alist':
264                 print("--- app list")
265                 for app in control_iface.GetApplicationList():
266                     print(app)
267             elif arg == 'sload':
268                 print("--- studio load")
269                 if index >= len(sys.argv):
270                     print("load studio command requires studio name argument")
271                     sys.exit()
273                 arg = sys.argv[index]
274                 index += 1
276                 open_options = {}
277                 #open_options["option1"] = "asd"
278                 #open_options["option2"] = True
280                 control_iface.LoadStudio(arg, open_options)
281             elif arg == 'sdel':
282                 print("--- studio delete")
283                 if index >= len(sys.argv):
284                     print("delete studio command requires studio name argument")
285                     sys.exit()
287                 arg = sys.argv[index]
288                 index += 1
290                 control_iface.DeleteStudio(arg)
291             elif arg == 'snew':
292                 print("--- studio new")
293                 name = ""
294                 if index < len(sys.argv):
295                     name = sys.argv[index]
296                     index += 1
298                 control_iface.NewStudio(name)
299             elif arg == 'sisloaded':
300                 print("--- studio is loaded")
301                 if control_iface.IsStudioLoaded():
302                     print("yes")
303                 else:
304                     print("no")
305             elif arg == 'rtlist':
306                 print("--- list room templates")
307                 for studio in control_iface.GetRoomTemplateList():
308                     name = studio[0]
309                     print('"%s"' % name)
310             elif arg == 'rtnew':
311                 print("--- create new room template")
312                 if index >= len(sys.argv):
313                     print("create new room template command requires room template name argument")
314                     sys.exit()
316                 arg = sys.argv[index]
317                 index += 1
319                 control_iface.CreateRoomTemplate(arg)
320             elif arg == 'rtdel':
321                 print("--- delete room template")
322                 if index >= len(sys.argv):
323                     print("delete room template command requires room template name argument")
324                     sys.exit()
326                 arg = sys.argv[index]
327                 index += 1
329                 control_iface.DeleteRoomTemplate(arg)
330             else:
331                 if not studio_obj:
332                     studio_obj = bus.get_object(service_name, studio_object_path)
333                     studio_iface = dbus.Interface(studio_obj, studio_interface_name)
335                 if arg == 'sname':
336                     print("--- studio get name")
337                     print("\"%s\"" % studio_iface.GetName())
338                 elif arg == 'ssave':
339                     print("--- studio save")
340                     studio_iface.Save()
341                 elif arg == 'sunload':
342                     print("--- studio unload")
343                     studio_iface.Unload()
344                     studio_obj = None
345                     studio_iface = None
346                 elif arg == 'srename':
347                     print("--- studio rename")
348                     if index >= len(sys.argv):
349                         print("rename studio command requires studio name argument")
350                         sys.exit()
352                     arg = sys.argv[index]
353                     index += 1
355                     studio_iface.Rename(arg)
356                 elif arg == 'sstart':
357                     print("--- studio start")
358                     studio_iface.Start()
359                 elif arg == 'sstop':
360                     print("--- studio stop")
361                     studio_iface.Stop()
362                 elif arg == 'snewroom':
363                     print("--- create new studio room")
364                     if index + 1 >= len(sys.argv):
365                         print("creation of studio room requires room name and room template name arguments")
366                         sys.exit()
368                     room_name = sys.argv[index]
369                     index += 1
370                     room_template_name = sys.argv[index]
371                     index += 1
373                     studio_iface.CreateRoom(room_name, room_template_name)
374                 elif arg == 'srlist':
375                     print("--- list studio rooms")
376                     for room in studio_iface.GetRoomList():
377                         #print repr(room)
378                         opath = room[0]
379                         name = room[1]["name"]
380                         if room[1].has_key("template"):
381                             template = str(room[1]["template"])
382                         else:
383                             template = None
385                         if template:
386                             print('"%s" from template "%s" (%s)' % (name, template, opath))
387                         else:
388                             print('"%s" (%s)' % (name, opath))
389                 elif arg == 'sdelroom':
390                     print("--- delete studio room")
391                     if index >= len(sys.argv):
392                         print("delete studio room command requires room name argument")
393                         sys.exit()
395                     arg = sys.argv[index]
396                     index += 1
398                     studio_iface.DeleteRoom(arg)
399                 elif arg == 'pload':
400                     print("--- load project")
401                     if index + 1 >= len(sys.argv):
402                         print("load project command requires room name and project dir arguments")
403                         sys.exit()
405                     room_name = sys.argv[index]
406                     index += 1
407                     project_dir = sys.argv[index]
408                     index += 1
410                     dbus.Interface(get_room_obj_by_name(bus, studio_iface, room_name), room_interface_name).LoadProject(project_dir)
411                 elif arg == 'punload':
412                     print("--- unload project")
413                     if index >= len(sys.argv):
414                         print("load project command requires room name argument")
415                         sys.exit()
417                     room_name = sys.argv[index]
418                     index += 1
420                     dbus.Interface(get_room_obj_by_name(bus, studio_iface, room_name), room_interface_name).UnloadProject()
421                 elif arg == 'psave':
422                     print("--- save project")
423                     if index >= len(sys.argv):
424                         print("save project command requires room name argument")
425                         sys.exit()
427                     room_name = sys.argv[index]
428                     index += 1
430                     dbus.Interface(get_room_obj_by_name(bus, studio_iface, room_name), room_interface_name).SaveProject("", "")
431                 elif arg == 'psaveas':
432                     print("--- save project as")
433                     if index + 2 >= len(sys.argv):
434                         print("save project as command requires room name, project dir and project name arguments")
435                         sys.exit()
437                     room_name = sys.argv[index]
438                     index += 1
439                     project_dir = sys.argv[index]
440                     index += 1
441                     project_name = sys.argv[index]
442                     index += 1
444                     dbus.Interface(get_room_obj_by_name(bus, studio_iface, room_name), room_interface_name).SaveProject(project_dir, project_name)
445                 elif arg == 'snewapp':
446                     print("--- new studio app")
447                     count, cmdline, name, level, term = parse_new_app_args(sys.argv[index:])
448                     index += count
449                     add_app(studio_obj, cmdline, name, level, term)
450                 elif arg == 'rnewapp':
451                     print("--- new room app")
452                     arg = sys.argv[index]
453                     index += 1
455                     count, cmdline, name, level, term = parse_new_app_args(sys.argv[index:])
456                     index += count
458                     add_app(get_room_obj_by_name(bus, studio_iface, arg), cmdline, name, level, term)
459                 elif arg == "sgdump":
460                     print('--- dump studio graph')
461                     dump_graph(studio_obj)
462                 elif arg == "rgdump":
463                     if index >= len(sys.argv):
464                         print("rgdump command requires room name argument")
465                         sys.exit()
466                     rname = sys.argv[index]
467                     index += 1
468                     print('--- dump room "' + rname + '" graph')
469                     room_obj = get_room_obj_by_name(bus, studio_obj, rname)
470                     dump_graph(room_obj)
471                 elif arg == "sconnect":
472                     if index + 4 > len(sys.argv):
473                         print("sconnect command requires client1, port1, client2 and port2 arguments")
474                         sys.exit()
475                     c1 = sys.argv[index]
476                     p1 = sys.argv[index + 1]
477                     c2 = sys.argv[index + 2]
478                     p2 = sys.argv[index + 3]
479                     index += 4
480                     print('--- connect studio port "' + c1 + '":"' + p1 + '" to "' + c2 + '":"' + p2 + '"')
481                     patchbay_iface = dbus.Interface(studio_obj, patchbay_interface_name)
482                     patchbay_iface.ConnectPortsByName(c1, p1, c2, p2)
483                 elif arg == "sdisconnect":
484                     if index + 4 > len(sys.argv):
485                         print("sdisconnect command requires client1, port1, client2 and port2 arguments")
486                         sys.exit()
487                     c1 = sys.argv[index]
488                     p1 = sys.argv[index + 1]
489                     c2 = sys.argv[index + 2]
490                     p2 = sys.argv[index + 3]
491                     index += 4
492                     print('--- disconnect studio port "' + c2 + '":"' + p2 + '" from "' + c1 + '":"' + p1 + '"')
493                     patchbay_iface = dbus.Interface(studio_obj, patchbay_interface_name)
494                     patchbay_iface.DisconnectPortsByName(c1, p1, c2, p2)
495                 elif arg == "rconnect":
496                     if index + 5 > len(sys.argv):
497                         print("rconnect command requires rname, client1, port1, client2 and port2 arguments")
498                         sys.exit()
499                     rname = sys.argv[index]
500                     c1 = sys.argv[index + 1]
501                     p1 = sys.argv[index + 2]
502                     c2 = sys.argv[index + 3]
503                     p2 = sys.argv[index + 4]
504                     index += 5
505                     print('--- connect room "' + rname + '" port "' + c1 + '":"' + p1 + '" to "' + c2 + '":"' + p2 + '"')
506                     room_obj = get_room_obj_by_name(bus, studio_iface, rname)
507                     patchbay_iface = dbus.Interface(room_obj, patchbay_interface_name)
508                     patchbay_iface.ConnectPortsByName(c1, p1, c2, p2)
509                 elif arg == "rdisconnect":
510                     if index + 5 > len(sys.argv):
511                         print("rdisconnect command requires rname, client1, port1, client2 and port2 arguments")
512                         sys.exit()
513                     rname = sys.argv[index]
514                     c1 = sys.argv[index + 1]
515                     p1 = sys.argv[index + 2]
516                     c2 = sys.argv[index + 3]
517                     p2 = sys.argv[index + 4]
518                     index += 5
519                     print('--- disconnect room "' + rname + '" port "' + c2 + '":"' + p2 + '" from "' + c1 + '":"' + p1 + '"')
520                     room_obj = get_room_obj_by_name(bus, studio_iface, rname)
521                     patchbay_iface = dbus.Interface(room_obj, patchbay_interface_name)
522                     patchbay_iface.DisconnectPortsByName(c1, p1, c2, p2)
523                 else:
524                     print("Unknown command '%s'" % arg)
525         except dbus.DBusException as e:
526             print("DBus exception: %s" % str(e))
528 if __name__ == '__main__':
529     main()