Start with empty scene as last resort option.
[calfbox.git] / app.c
blobe8258de41d0c665f14d700d25dd493df512e7ba6
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2011 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "app.h"
20 #include "blob.h"
21 #include "config-api.h"
22 #include "instr.h"
23 #include "io.h"
24 #include "layer.h"
25 #include "menu.h"
26 #include "menuitem.h"
27 #include "meter.h"
28 #include "midi.h"
29 #include "module.h"
30 #include "scene.h"
31 #include "seq.h"
32 #include "song.h"
33 #include "track.h"
34 #include "ui.h"
35 #include "wavebank.h"
37 #include <assert.h>
38 #include <glib.h>
39 #include <glob.h>
40 #include <getopt.h>
41 #include <math.h>
42 #include <ncurses.h>
43 #include <stdio.h>
44 #include <stdint.h>
45 #include <string.h>
46 #include <unistd.h>
48 int cmd_quit(struct cbox_menu_item_command *item, void *context)
50 return 1;
53 int cmd_load_scene(struct cbox_menu_item_command *item, void *context)
55 GError *error = NULL;
56 struct cbox_scene *scene = app.rt->scene;
57 cbox_scene_clear(scene);
58 if (!cbox_scene_load(scene, item->item.item_context, &error))
59 cbox_print_error(error);
60 return 0;
63 int cmd_load_instrument(struct cbox_menu_item_command *item, void *context)
65 GError *error = NULL;
66 struct cbox_scene *scene = app.rt->scene;
67 cbox_scene_clear(scene);
68 struct cbox_layer *layer = cbox_layer_new_with_instrument(scene, (char *)item->item.item_context, &error);
70 if (layer)
72 if (!cbox_scene_add_layer(scene, layer, &error))
73 cbox_print_error(error);
75 else
77 cbox_print_error(error);
79 return 0;
82 int cmd_load_layer(struct cbox_menu_item_command *item, void *context)
84 GError *error = NULL;
85 struct cbox_scene *scene = app.rt->scene;
86 cbox_scene_clear(scene);
87 struct cbox_layer *layer = cbox_layer_new_from_config(scene, (char *)item->item.item_context, &error);
89 if (layer)
91 if (!cbox_scene_add_layer(scene, layer, &error))
92 cbox_print_error(error);
94 else
96 cbox_print_error(error);
97 CBOX_DELETE(scene);
99 return 0;
102 gchar *scene_format_value(const struct cbox_menu_item_static *item, void *context)
104 return strdup(app.current_scene_name);
107 gchar *transport_format_value(const struct cbox_menu_item_static *item, void *context)
109 // XXXKF
110 // struct cbox_bbt bbt;
111 // cbox_master_to_bbt(app.rt->master, &bbt);
112 if (app.rt->scene->spb == NULL)
113 return g_strdup("N/A");
114 if (!strcmp((const char *)item->item.item_context, "pos"))
115 return g_strdup_printf("%d", (int)app.rt->scene->spb->song_pos_samples);
116 else
117 return g_strdup_printf("%d", (int)app.rt->scene->spb->song_pos_ppqn);
120 struct cbox_config_section_cb_data
122 struct cbox_menu *menu;
123 cbox_menu_item_execute_func func;
124 const char *prefix;
127 static void config_key_process(void *user_data, const char *key)
129 struct cbox_config_section_cb_data *data = user_data;
131 if (!strncmp(key, data->prefix, strlen(data->prefix)))
133 char *title = cbox_config_get_string(key, "title");
134 if (title)
135 cbox_menu_add_item(data->menu, cbox_menu_item_new_command(title, data->func, strdup(key + strlen(data->prefix))));
136 else
137 cbox_menu_add_item(data->menu, cbox_menu_item_new_command(key, data->func, strdup(key + strlen(data->prefix))));
141 struct cbox_menu *create_scene_menu(struct cbox_menu_item_menu *item, void *menu_context)
143 struct cbox_menu *scene_menu = cbox_menu_new();
144 struct cbox_config_section_cb_data cb = { .menu = scene_menu };
146 cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Scenes", NULL, NULL));
147 cb.prefix = "scene:";
148 cb.func = cmd_load_scene;
149 cbox_config_foreach_section(config_key_process, &cb);
150 cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Layers", NULL, NULL));
151 cb.prefix = "layer:";
152 cb.func = cmd_load_layer;
153 cbox_config_foreach_section(config_key_process, &cb);
154 cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Instruments", NULL, NULL));
155 cb.prefix = "instrument:";
156 cb.func = cmd_load_instrument;
157 cbox_config_foreach_section(config_key_process, &cb);
159 cbox_menu_add_item(scene_menu, cbox_menu_item_new_menu("OK", NULL, NULL));
161 return scene_menu;
164 ///////////////////////////////////////////////////////////////////////////////
166 static struct cbox_command_target *find_module_target(const char *type, GError **error)
168 struct cbox_scene *scene = app.rt->scene;
169 for (int i = 0; i < scene->instrument_count; i++)
171 if (!strcmp(scene->instruments[i]->module->engine_name, type))
172 return &scene->instruments[i]->module->cmd_target;
174 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot find a module of type '%s'", type);
175 return NULL;
179 int cmd_stream_rewind(struct cbox_menu_item_command *item, void *context)
181 GError *error = NULL;
182 struct cbox_command_target *target = find_module_target("stream_player", &error);
183 if (target)
184 cbox_execute_on(target, NULL, "/seek", "i", &error, 0);
185 cbox_print_error_if(error);
186 return 0;
189 int cmd_stream_play(struct cbox_menu_item_command *item, void *context)
191 GError *error = NULL;
192 struct cbox_command_target *target = find_module_target("stream_player", &error);
193 if (target)
194 cbox_execute_on(target, NULL, "/play", "", &error);
195 cbox_print_error_if(error);
196 return 0;
199 int cmd_stream_stop(struct cbox_menu_item_command *item, void *context)
201 GError *error = NULL;
202 struct cbox_command_target *target = find_module_target("stream_player", &error);
203 if (target)
204 cbox_execute_on(target, NULL, "/stop", "", &error);
205 cbox_print_error_if(error);
206 return 0;
209 int cmd_stream_unload(struct cbox_menu_item_command *item, void *context)
211 GError *error = NULL;
212 struct cbox_command_target *target = find_module_target("stream_player", &error);
213 if (target)
214 cbox_execute_on(target, NULL, "/unload", "", &error);
215 cbox_print_error_if(error);
216 return 0;
219 gboolean result_parser_status(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
221 cbox_osc_command_dump(cmd);
222 return TRUE;
225 int cmd_stream_status(struct cbox_menu_item_command *item, void *context)
227 struct cbox_command_target response = { NULL, result_parser_status };
228 GError *error = NULL;
229 struct cbox_command_target *target = find_module_target("stream_player", &error);
230 if (target)
231 cbox_execute_on(target, &response, "/status", "", &error);
232 cbox_print_error_if(error);
233 return 0;
236 int cmd_stream_load(struct cbox_menu_item_command *item, void *context)
238 GError *error = NULL;
239 struct cbox_command_target *target = find_module_target("stream_player", &error);
240 if (target)
241 cbox_execute_on(target, NULL, "/load", "si", &error, (gchar *)item->item.item_context, 0);
242 cbox_print_error_if(error);
243 return 0;
246 struct cbox_menu *create_stream_menu(struct cbox_menu_item_menu *item, void *menu_context)
248 struct cbox_menu *menu = cbox_menu_new();
249 struct cbox_config_section_cb_data cb = { .menu = menu };
251 cbox_menu_add_item(menu, cbox_menu_item_new_static("Module commands", NULL, NULL));
252 cbox_menu_add_item(menu, cbox_menu_item_new_command("Play stream", cmd_stream_play, NULL));
253 cbox_menu_add_item(menu, cbox_menu_item_new_command("Stop stream", cmd_stream_stop, NULL));
254 cbox_menu_add_item(menu, cbox_menu_item_new_command("Rewind stream", cmd_stream_rewind, NULL));
255 cbox_menu_add_item(menu, cbox_menu_item_new_command("Describe stream", cmd_stream_status, NULL));
256 cbox_menu_add_item(menu, cbox_menu_item_new_command("Unload stream", cmd_stream_unload, NULL));
258 glob_t g;
259 if (glob("*.wav", GLOB_TILDE_CHECK, NULL, &g) == 0)
261 for (int i = 0; i < g.gl_pathc; i++)
263 cbox_menu_add_item(menu, cbox_menu_item_new_command(g_strdup_printf("Load: %s", g.gl_pathv[i]), cmd_stream_load, g_strdup(g.gl_pathv[i])));
267 globfree(&g);
269 cbox_menu_add_item(menu, cbox_menu_item_new_menu("OK", NULL, NULL));
271 return menu;
274 ///////////////////////////////////////////////////////////////////////////////
276 int cmd_pattern_none(struct cbox_menu_item_command *item, void *context)
278 cbox_song_clear(app.rt->master->song);
279 cbox_rt_update_song_playback(app.rt);
280 return 0;
283 int cmd_pattern_simple(struct cbox_menu_item_command *item, void *context)
285 cbox_song_use_looped_pattern(app.rt->master->song, cbox_midi_pattern_new_metronome(app.rt->master->song, 1));
286 return 0;
289 int cmd_pattern_normal(struct cbox_menu_item_command *item, void *context)
291 cbox_song_use_looped_pattern(app.rt->master->song, cbox_midi_pattern_new_metronome(app.rt->master->song, app.rt->master->timesig_nom));
292 return 0;
295 int cmd_load_drumpattern(struct cbox_menu_item_command *item, void *context)
297 cbox_song_use_looped_pattern(app.rt->master->song, cbox_midi_pattern_load(app.rt->master->song, item->item.item_context, 1));
298 return 0;
301 int cmd_load_drumtrack(struct cbox_menu_item_command *item, void *context)
303 cbox_song_use_looped_pattern(app.rt->master->song, cbox_midi_pattern_load_track(app.rt->master->song, item->item.item_context, 1));
304 return 0;
307 int cmd_load_pattern(struct cbox_menu_item_command *item, void *context)
309 cbox_song_use_looped_pattern(app.rt->master->song, cbox_midi_pattern_load(app.rt->master->song, item->item.item_context, 0));
310 return 0;
313 int cmd_load_track(struct cbox_menu_item_command *item, void *context)
315 cbox_song_use_looped_pattern(app.rt->master->song, cbox_midi_pattern_load_track(app.rt->master->song, item->item.item_context, 0));
316 return 0;
319 struct cbox_menu *create_pattern_menu(struct cbox_menu_item_menu *item, void *menu_context)
321 struct cbox_menu *menu = cbox_menu_new();
322 struct cbox_config_section_cb_data cb = { .menu = menu };
324 cbox_menu_add_item(menu, cbox_menu_item_new_static("Pattern commands", NULL, NULL));
325 cbox_menu_add_item(menu, cbox_menu_item_new_command("No pattern", cmd_pattern_none, NULL));
326 cbox_menu_add_item(menu, cbox_menu_item_new_command("Simple metronome", cmd_pattern_simple, NULL));
327 cbox_menu_add_item(menu, cbox_menu_item_new_command("Normal metronome", cmd_pattern_normal, NULL));
329 cbox_menu_add_item(menu, cbox_menu_item_new_static("Drum tracks", NULL, NULL));
330 cb.prefix = "drumtrack:";
331 cb.func = cmd_load_drumtrack;
332 cbox_config_foreach_section(config_key_process, &cb);
334 cbox_menu_add_item(menu, cbox_menu_item_new_static("Melodic tracks", NULL, NULL));
335 cb.prefix = "track:";
336 cb.func = cmd_load_track;
337 cbox_config_foreach_section(config_key_process, &cb);
339 cbox_menu_add_item(menu, cbox_menu_item_new_static("Drum patterns", NULL, NULL));
340 cb.prefix = "drumpattern:";
341 cb.func = cmd_load_drumpattern;
342 cbox_config_foreach_section(config_key_process, &cb);
344 cbox_menu_add_item(menu, cbox_menu_item_new_static("Melodic patterns", NULL, NULL));
345 cb.prefix = "pattern:";
346 cb.func = cmd_load_pattern;
347 cbox_config_foreach_section(config_key_process, &cb);
349 cbox_menu_add_item(menu, cbox_menu_item_new_menu("OK", NULL, NULL));
351 return menu;
354 struct cbox_menu *create_main_menu()
356 struct cbox_menu *main_menu = cbox_menu_new();
357 cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Current scene:", scene_format_value, NULL));
358 cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Set scene", create_scene_menu, NULL));
359 cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Module control", create_stream_menu, NULL));
360 cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Pattern control", create_pattern_menu, NULL));
362 cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Variables", NULL, NULL));
363 // cbox_menu_add_item(main_menu, cbox_menu_item_new_int("foo:", &var1, 0, 127, NULL));
364 // cbox_menu_add_item(main_menu, "bar:", menu_item_value_double, &mx_double_var2, &var2);
365 cbox_menu_add_item(main_menu, cbox_menu_item_new_static("pos:", transport_format_value, "pos"));
366 cbox_menu_add_item(main_menu, cbox_menu_item_new_static("bbt:", transport_format_value, "bbt"));
367 cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Commands", NULL, NULL));
368 cbox_menu_add_item(main_menu, cbox_menu_item_new_command("Quit", cmd_quit, NULL));
369 return main_menu;
372 static gboolean app_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
374 if (!cmd->command)
376 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "NULL command");
377 return FALSE;
379 if (cmd->command[0] != '/')
381 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid global command path '%s'", cmd->command);
382 return FALSE;
384 const char *obj = &cmd->command[1];
385 const char *pos = strchr(obj, '/');
386 if (pos)
388 int len = pos - obj;
389 if (!strncmp(obj, "master/", 7))
390 return cbox_execute_sub(&app.rt->master->cmd_target, fb, cmd, pos, error);
391 else
392 if (!strncmp(obj, "config/", 7))
393 return cbox_execute_sub(&app.config_cmd_target, fb, cmd, pos, error);
394 else
395 if (!strncmp(obj, "scene/", 6))
396 return cbox_execute_sub(&app.rt->scene->cmd_target, fb, cmd, pos, error);
397 else
398 if (!strncmp(obj, "rt/", 3))
399 return cbox_execute_sub(&app.rt->cmd_target, fb, cmd, pos, error);
400 else
401 if (!strncmp(obj, "song/", 5))
402 return cbox_execute_sub(&app.rt->master->song->cmd_target, fb, cmd, pos, error);
403 else
404 if (!strncmp(obj, "waves/", 6))
405 return cbox_execute_sub(&cbox_waves_cmd_target, fb, cmd, pos, error);
406 else
407 if (!strncmp(obj, "doc/", 4))
408 return cbox_execute_sub(cbox_document_get_cmd_target(app.document), fb, cmd, pos, error);
409 else
411 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types);
412 return FALSE;
415 else
416 if (!strcmp(obj, "on_idle") && !strcmp(cmd->arg_types, ""))
418 cbox_app_on_idle();
419 return TRUE;
421 else
422 if (!strcmp(obj, "send_event") && (!strcmp(cmd->arg_types, "iii") || !strcmp(cmd->arg_types, "ii") || !strcmp(cmd->arg_types, "i")))
424 int mcmd = CBOX_ARG_I(cmd, 0);
425 int arg1 = 0, arg2 = 0;
426 if (cmd->arg_types[1] == 'i')
428 arg1 = CBOX_ARG_I(cmd, 1);
429 if (cmd->arg_types[2] == 'i')
430 arg2 = CBOX_ARG_I(cmd, 2);
432 struct cbox_midi_buffer buf;
433 cbox_midi_buffer_init(&buf);
434 cbox_midi_buffer_write_inline(&buf, 0, mcmd, arg1, arg2);
435 cbox_rt_send_events(app.rt, &buf);
436 return TRUE;
438 else
439 if (!strcmp(obj, "play_note") && !strcmp(cmd->arg_types, "iii"))
441 int channel = CBOX_ARG_I(cmd, 0);
442 int note = CBOX_ARG_I(cmd, 1);
443 int velocity = CBOX_ARG_I(cmd, 2);
444 struct cbox_midi_buffer buf;
445 cbox_midi_buffer_init(&buf);
446 cbox_midi_buffer_write_inline(&buf, 0, 0x90 + ((channel - 1) & 15), note & 127, velocity & 127);
447 cbox_midi_buffer_write_inline(&buf, 1, 0x80 + ((channel - 1) & 15), note & 127, velocity & 127);
448 cbox_rt_send_events(app.rt, &buf);
449 return TRUE;
451 else
452 if (!strcmp(obj, "update_playback") && !strcmp(cmd->arg_types, ""))
454 cbox_rt_update_song_playback(app.rt);
455 return TRUE;
457 else
458 if (!strcmp(obj, "get_pattern") && !strcmp(cmd->arg_types, ""))
460 if (!cbox_check_fb_channel(fb, cmd->command, error))
461 return FALSE;
463 if (app.rt->master->song && app.rt->master->song->tracks)
465 struct cbox_track *track = app.rt->master->song->tracks->data;
466 if (track)
468 struct cbox_track_item *item = track->items->data;
469 struct cbox_midi_pattern *pattern = item->pattern;
470 int length = 0;
471 struct cbox_blob *blob = cbox_midi_pattern_to_blob(pattern, &length);
472 gboolean res = cbox_execute_on(fb, NULL, "/pattern", "bi", error, blob, length);
473 cbox_blob_destroy(blob);
476 return TRUE;
478 else
479 if (!strcmp(obj, "new_meter") && !strcmp(cmd->arg_types, ""))
481 if (!cbox_check_fb_channel(fb, cmd->command, error))
482 return FALSE;
484 struct cbox_meter *meter = cbox_meter_new(app.document, cbox_rt_get_sample_rate(app.rt));
486 return cbox_execute_on(fb, NULL, "/uuid", "o", error, meter);
488 else
489 if (!strcmp(obj, "new_recorder") && !strcmp(cmd->arg_types, "s"))
491 if (!cbox_check_fb_channel(fb, cmd->command, error))
492 return FALSE;
494 struct cbox_recorder *rec = cbox_recorder_new_stream(app.rt, CBOX_ARG_S(cmd, 0));
496 return cbox_execute_on(fb, NULL, "/uuid", "o", error, rec);
498 else
499 if (!strcmp(obj, "new_scene") && !strcmp(cmd->arg_types, "ii"))
501 if (!cbox_check_fb_channel(fb, cmd->command, error))
502 return FALSE;
504 struct cbox_rt *rt = cbox_rt_new(app.document);
505 cbox_rt_set_offline(rt, CBOX_ARG_I(cmd, 0), CBOX_ARG_I(cmd, 1));
506 struct cbox_scene *rec = cbox_scene_new(app.document, rt, TRUE);
508 return cbox_execute_on(fb, NULL, "/uuid", "o", error, rec);
510 else
511 if (!strcmp(obj, "print_s") && !strcmp(cmd->arg_types, "s"))
513 g_message("Print: %s", CBOX_ARG_S(cmd, 0));
514 return TRUE;
516 else
517 if (!strcmp(obj, "print_i") && !strcmp(cmd->arg_types, "i"))
519 g_message("Print: %d", CBOX_ARG_I(cmd, 0));
520 return TRUE;
522 else
523 if (!strcmp(obj, "print_f") && !strcmp(cmd->arg_types, "f"))
525 g_message("Print: %f", CBOX_ARG_F(cmd, 0));
526 return TRUE;
528 else
530 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types);
531 return FALSE;
535 struct config_foreach_data
537 const char *prefix;
538 const char *command;
539 struct cbox_command_target *fb;
540 GError **error;
541 gboolean success;
544 void api_config_cb(void *user_data, const char *key)
546 struct config_foreach_data *cfd = user_data;
547 if (!cfd->success)
548 return;
549 if (cfd->prefix && strncmp(cfd->prefix, key, strlen(cfd->prefix)))
550 return;
552 if (!cbox_execute_on(cfd->fb, NULL, cfd->command, "s", cfd->error, key))
554 cfd->success = FALSE;
555 return;
559 static gboolean config_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
561 if (!strcmp(cmd->command, "/sections") && (!strcmp(cmd->arg_types, "") || !strcmp(cmd->arg_types, "s")))
563 if (!cbox_check_fb_channel(fb, cmd->command, error))
564 return FALSE;
566 struct config_foreach_data cfd = {cmd->arg_types[0] == 's' ? CBOX_ARG_S(cmd, 0) : NULL, "/section", fb, error, TRUE};
567 cbox_config_foreach_section(api_config_cb, &cfd);
568 return cfd.success;
570 else if (!strcmp(cmd->command, "/keys") && (!strcmp(cmd->arg_types, "s") || !strcmp(cmd->arg_types, "ss")))
572 if (!cbox_check_fb_channel(fb, cmd->command, error))
573 return FALSE;
575 struct config_foreach_data cfd = {cmd->arg_types[1] == 's' ? CBOX_ARG_S(cmd, 1) : NULL, "/key", fb, error, TRUE};
576 cbox_config_foreach_key(api_config_cb, CBOX_ARG_S(cmd, 0), &cfd);
577 return cfd.success;
579 else if (!strcmp(cmd->command, "/get") && !strcmp(cmd->arg_types, "ss"))
581 if (!cbox_check_fb_channel(fb, cmd->command, error))
582 return FALSE;
584 const char *value = cbox_config_get_string(CBOX_ARG_S(cmd, 0), CBOX_ARG_S(cmd, 1));
585 if (!value)
586 return TRUE;
587 return cbox_execute_on(fb, NULL, "/value", "s", error, value);
589 else if (!strcmp(cmd->command, "/set") && !strcmp(cmd->arg_types, "sss"))
591 cbox_config_set_string(CBOX_ARG_S(cmd, 0), CBOX_ARG_S(cmd, 1), CBOX_ARG_S(cmd, 2));
592 return TRUE;
594 else if (!strcmp(cmd->command, "/delete") && !strcmp(cmd->arg_types, "ss"))
596 cbox_config_remove_key(CBOX_ARG_S(cmd, 0), CBOX_ARG_S(cmd, 1));
597 return TRUE;
599 else if (!strcmp(cmd->command, "/delete_section") && !strcmp(cmd->arg_types, "s"))
601 cbox_config_remove_section(CBOX_ARG_S(cmd, 0));
602 return TRUE;
604 else if (!strcmp(cmd->command, "/save") && !strcmp(cmd->arg_types, ""))
606 return cbox_config_save(NULL, error);
608 else if (!strcmp(cmd->command, "/save") && !strcmp(cmd->arg_types, "s"))
610 return cbox_config_save(CBOX_ARG_S(cmd, 0), error);
612 else
614 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types);
615 return FALSE;
619 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
621 void cbox_app_on_idle()
623 if (app.rt->io)
625 GError *error = NULL;
626 if (cbox_io_get_disconnect_status(&app.io, &error))
627 cbox_io_poll_ports(&app.io);
628 else
630 if (error)
631 g_error_free(error);
632 int auto_reconnect = cbox_config_get_int("io", "auto_reconnect", 0);
633 if (auto_reconnect > 0)
635 sleep(auto_reconnect);
636 GError *error = NULL;
637 if (!cbox_io_cycle(&app.io, &error))
638 g_warning("Cannot cycle the I/O: %s", (error && error->message) ? error->message : "Unknown error");
642 if (app.rt)
643 cbox_rt_handle_cmd_queue(app.rt);
646 struct cbox_app app =
648 .rt = NULL,
649 .current_scene_name = NULL,
650 .cmd_target =
652 .process_cmd = app_process_cmd,
653 .user_data = &app
655 .config_cmd_target =
657 .process_cmd = config_process_cmd,
658 .user_data = &app