ladishd: save&restore app associations of vclients. Fix for #149
[ladish.git] / ladish_control
blobbcf55a89aa2404270fd1b4cd1845b21a6dbe73f5
1 #!/usr/bin/env python
3 # LADI Session Handler (ladish)
5 # Copyright (C) 2008, 2009, 2010 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 = int(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).RunCustom(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)
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)
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("rconnect command requires rname, client1, port1, client2 and port2 arguments")
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, e:
526 print("DBus exception: %s" % str(e))
528 if __name__ == '__main__':
529 main()