1 /* GSequencer - Advanced GTK Sequencer
2 * Copyright (C) 2005-2021 Joël Krähemann
3 *
4 * This file is part of GSequencer.
5 *
6 * GSequencer is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GSequencer is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GSequencer. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <glib.h>
21 #include <glib-object.h>
22
23 #include <gdk/gdk.h>
24 #include <pango/pangocairo.h>
25
26 #include "gsequencer_setup_util.h"
27
28 #include <ags/libags.h>
29 #include <ags/libags-audio.h>
30 #include <ags/libags-gui.h>
31
32 #include <ags/X/ags_ui_provider.h>
33 #include <ags/X/ags_xorg_application_context.h>
34 #include <ags/X/ags_window.h>
35
36 #include <ags/X/file/ags_simple_file.h>
37
38 #include <ags/X/task/ags_simple_file_read.h>
39
40 #include <ags/config.h>
41
42 #ifdef AGS_WITH_LIBINSTPATCH
43 #include <libinstpatch/libinstpatch.h>
44 #endif
45
46 #include <libxml/parser.h>
47 #include <libxml/xlink.h>
48 #include <libxml/xpath.h>
49 #include <libxml/valid.h>
50 #include <libxml/xmlIO.h>
51 #include <libxml/xmlmemory.h>
52 #include <libxml/xmlsave.h>
53
54 #include <X11/Xlib.h>
55
56 #include <string.h>
57
58 #include <unistd.h>
59
60 #include <libintl.h>
61 #include <stdio.h>
62 #include <signal.h>
63 #include <unistd.h>
64 #include <sys/resource.h>
65 #include <sys/mman.h>
66
67 #include <sys/types.h>
68 #include <pwd.h>
69
70 #include <locale.h>
71
72 #include "config.h"
73
74 static void ags_test_driver_mutex_create();
75
76 static GRecMutex ags_test_driver_mutex;
77
78 struct sigaction ags_test_sigact;
79
80 extern AgsApplicationContext *ags_application_context;
81
82 void
ags_test_enter()83 ags_test_enter()
84 {
85 g_rec_mutex_lock(ags_test_get_driver_mutex());
86 gdk_threads_enter();
87 }
88
89 void
ags_test_leave()90 ags_test_leave()
91 {
92 gdk_threads_leave();
93 g_rec_mutex_unlock(ags_test_get_driver_mutex());
94 }
95
96 GRecMutex*
ags_test_get_driver_mutex()97 ags_test_get_driver_mutex()
98 {
99 return(&ags_test_driver_mutex);
100 }
101
102 void
ags_test_init(int * argc,char *** argv,gchar * conf_str)103 ags_test_init(int *argc, char ***argv,
104 gchar *conf_str)
105 {
106 AgsConfig *config;
107 AgsPriority *priority;
108
109 gchar *filename;
110
111 gboolean builtin_theme_disabled;
112 guint i;
113
114 #ifdef AGS_WITH_RT
115 struct sched_param param;
116 struct rlimit rl;
117 #endif
118 struct passwd *pw;
119
120 gchar *wdir, *config_file;
121 gchar *rc_filename;
122 gchar *base_dir;
123 gchar *str, *data_dir;
124 gchar path[PATH_MAX];
125
126 uint32_t size = sizeof(path);
127 uid_t uid;
128 int result;
129
130 const rlim_t kStackSize = 64L * 1024L * 1024L; // min stack size = 64 Mb
131
132 base_dir = strdup(SRCDIR);
133 printf("base dir %s\n", base_dir);
134
135 /* set some environment variables */
136 sprintf(path, "%s/gsequencer.share/styles",
137 base_dir);
138 data_dir = realpath(path,
139 NULL);
140 str = malloc(PATH_MAX * sizeof(gchar));
141 sprintf(str,
142 "AGS_RC_FILENAME=%s/ags.rc",
143 data_dir);
144 putenv(str);
145
146 sprintf(path, "%s/gsequencer.share/images",
147 base_dir);
148 data_dir = realpath(path,
149 NULL);
150 str = malloc(PATH_MAX * sizeof(gchar));
151 sprintf(str,
152 "AGS_ANIMATION_FILENAME=%s/ags_supermoon-800x450.png",
153 data_dir);
154 putenv(str);
155
156 sprintf(path, "%s/gsequencer.share/images",
157 base_dir);
158 data_dir = realpath(path,
159 NULL);
160 str = malloc(PATH_MAX * sizeof(gchar));
161 sprintf(str,
162 "AGS_LOGO_FILENAME=%s/ags.png",
163 data_dir);
164 putenv(str);
165
166 sprintf(path, "%s",
167 base_dir);
168 data_dir = realpath(path,
169 NULL);
170 str = malloc(PATH_MAX * sizeof(gchar));
171 sprintf(str,
172 "AGS_LICENSE_FILENAME=%s/COPYING",
173 data_dir);
174 putenv(str);
175
176 /* gettext */
177 setlocale(LC_ALL, "");
178 bindtextdomain(PACKAGE, LOCALEDIR);
179 textdomain(PACKAGE);
180
181 /* parameters */
182 builtin_theme_disabled = FALSE;
183
184 priority = ags_priority_get_instance();
185 ags_priority_load_defaults(priority);
186
187 // g_log_set_fatal_mask("GLib", // "Gtk" , //
188 // G_LOG_LEVEL_CRITICAL); // G_LOG_LEVEL_WARNING
189
190 #ifdef AGS_WITH_RT
191 result = getrlimit(RLIMIT_STACK, &rl);
192
193 /* set stack size 64M */
194 if(result == 0){
195 if(rl.rlim_cur < kStackSize){
196 rl.rlim_cur = kStackSize;
197 result = setrlimit(RLIMIT_STACK, &rl);
198
199 if(result != 0){
200 //TODO:JK
201 }
202 }
203 }
204
205 priority = ags_priority_get_instance();
206
207 param.sched_priority = 1;
208
209 str = ags_priority_get_value(priority,
210 AGS_PRIORITY_RT_THREAD,
211 AGS_PRIORITY_KEY_GUI_MAIN_LOOP);
212
213 if(str != NULL){
214 param.sched_priority = (int) g_ascii_strtoull(str,
215 NULL,
216 10);
217
218 g_free(str);
219 }
220
221 if(sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
222 perror("sched_setscheduler failed");
223 }
224 #endif
225
226 //#ifdef AGS_WITH_X11
227 XInitThreads();
228 //#endif
229
230 /* parse command line parameter */
231 filename = NULL;
232
233 for(i = 0; i < argc[0]; i++){
234 if(!strncmp(argv[0][i], "--help", 7)){
235 printf("GSequencer is an audio sequencer and notation editor\n\n");
236
237 printf("Usage:\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\n",
238 "Report bugs to <jkraehemann@gmail.com>\n",
239 "--filename file open file",
240 "--no-builtin-theme disable built-in theme",
241 "--help display this help and exit",
242 "--version output version information and exit");
243
244 exit(0);
245 }else if(!strncmp(argv[0][i], "--version", 10)){
246 printf("GSequencer %s\n\n", AGS_VERSION);
247
248 printf("%s\n%s\n%s\n\n",
249 "Copyright (C) 2005-2020 Joël Krähemann",
250 "This is free software; see the source for copying conditions. There is NO",
251 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
252
253 printf("Written by Joël Krähemann\n");
254
255 exit(0);
256 }else if(!strncmp(argv[0][i], "--no-builtin-theme", 19)){
257 builtin_theme_disabled = TRUE;
258 }else if(!strncmp(argv[0][i], "--filename", 11)){
259 filename = *argv[i + 1];
260 i++;
261 }
262 }
263
264 uid = getuid();
265 pw = getpwuid(uid);
266
267 /* parse rc file */
268 if(!builtin_theme_disabled){
269 rc_filename = g_strdup_printf("%s/%s/ags.rc",
270 pw->pw_dir,
271 AGS_DEFAULT_DIRECTORY);
272
273 if(!g_file_test(rc_filename,
274 G_FILE_TEST_IS_REGULAR)){
275 g_free(rc_filename);
276
277 #ifdef AGS_RC_FILENAME
278 rc_filename = g_strdup(AGS_RC_FILENAME);
279 #else
280 if((rc_filename = getenv("AGS_RC_FILENAME")) == NULL){
281 rc_filename = g_strdup_printf("%s%s",
282 DESTDIR,
283 "/gsequencer/styles/ags.rc");
284 }else{
285 rc_filename = g_strdup(rc_filename);
286 }
287 #endif
288 }
289
290 gtk_rc_parse(rc_filename);
291 g_free(rc_filename);
292 }
293
294 /**/
295 LIBXML_TEST_VERSION;
296
297 gtk_init(argc, argv);
298
299 if(!builtin_theme_disabled){
300 g_object_set(gtk_settings_get_default(),
301 "gtk-theme-name", "Raleigh",
302 NULL);
303 g_signal_handlers_block_matched(gtk_settings_get_default(),
304 G_SIGNAL_MATCH_DETAIL,
305 g_signal_lookup("set-property",
306 GTK_TYPE_SETTINGS),
307 g_quark_from_string("gtk-theme-name"),
308 NULL,
309 NULL,
310 NULL);
311 }
312
313 #ifdef AGS_WITH_LIBINSTPATCH
314 ipatch_init();
315 #endif
316
317 /* setup */
318 wdir = g_strdup_printf("%s/%s",
319 pw->pw_dir,
320 AGS_DEFAULT_DIRECTORY);
321
322 config_file = g_strdup_printf("%s/%s",
323 wdir,
324 AGS_DEFAULT_CONFIG);
325
326 config = ags_config_get_instance();
327
328 if(conf_str != NULL){
329 ags_config_load_from_data(config,
330 conf_str,
331 strlen(conf_str));
332 }else{
333 ags_config_load_from_file(config,
334 config_file);
335 }
336
337 g_free(wdir);
338 g_free(config_file);
339 }
340
341 void
ags_test_quit()342 ags_test_quit()
343 {
344 AgsXorgApplicationContext *xorg_application_context;
345 AgsWindow *window;
346
347 xorg_application_context = ags_application_context_get_instance();
348
349 ags_test_enter();
350
351 window = xorg_application_context->window;
352
353 window->flags |= AGS_WINDOW_TERMINATING;
354
355 ags_test_leave();
356
357 //FIXME:JK: avoids exit to crash :(
358 sleep(5);
359 }
360
361 void
ags_test_show_file_error(gchar * filename,GError * error)362 ags_test_show_file_error(gchar *filename,
363 GError *error)
364 {
365 GtkDialog *dialog;
366
367 g_warning("could not parse file %s", filename);
368
369 dialog = gtk_message_dialog_new(NULL,
370 0,
371 GTK_MESSAGE_WARNING,
372 GTK_BUTTONS_OK,
373 "Failed to open '%s'",
374 filename);
375 gtk_widget_show_all((GtkWidget *) dialog);
376 g_signal_connect(dialog, "response",
377 G_CALLBACK(gtk_main_quit), NULL);
378 gtk_main();
379 }
380
381 void
ags_test_signal_handler(int signr)382 ags_test_signal_handler(int signr)
383 {
384 if(signr == SIGINT){
385 //TODO:JK: do backup
386
387 exit(-1);
388 }else{
389 sigemptyset(&(ags_test_sigact.sa_mask));
390 }
391 }
392
393 void
ags_test_signal_cleanup()394 ags_test_signal_cleanup()
395 {
396 sigemptyset(&(ags_test_sigact.sa_mask));
397 }
398
399 void
ags_test_setup(int argc,char ** argv)400 ags_test_setup(int argc, char **argv)
401 {
402 AgsLadspaManager *ladspa_manager;
403 AgsDssiManager *dssi_manager;
404 AgsLv2Manager *lv2_manager;
405 AgsLv2uiManager *lv2ui_manager;
406 AgsLv2WorkerManager *lv2_worker_manager;
407
408 AgsLog *log;
409
410 gchar *blacklist_filename;
411 gchar *filename;
412
413 uid_t uid;
414
415 guint i;
416
417 /* check filename */
418 log = ags_log_get_instance();
419 filename = NULL;
420
421 ags_log_add_message(log,
422 "Welcome to Advanced Gtk+ Sequencer");
423
424 for(i = 0; i < argc; i++){
425 if(!strncmp(argv[i], "--filename", 11)){
426 AgsSimpleFile *simple_file;
427
428 xmlXPathContext *xpath_context;
429 xmlXPathObject *xpath_object;
430 xmlNode **node;
431
432 xmlChar *xpath;
433
434 gchar *buffer;
435 guint buffer_length;
436
437 filename = argv[i + 1];
438 simple_file = ags_simple_file_new();
439 g_object_set(simple_file,
440 "filename", filename,
441 NULL);
442 ags_simple_file_open(simple_file,
443 NULL);
444
445 xpath = "/ags-simple-file/ags-sf-config";
446
447 /* Create xpath evaluation context */
448 xpath_context = xmlXPathNewContext(simple_file->doc);
449
450 if(xpath_context == NULL) {
451 g_warning("Error: unable to create new XPath context");
452
453 break;
454 }
455
456 /* Evaluate xpath expression */
457 xpath_object = xmlXPathEval(xpath, xpath_context);
458
459 if(xpath_object == NULL) {
460 g_warning("Error: unable to evaluate xpath expression \"%s\"", xpath);
461 xmlXPathFreeContext(xpath_context);
462
463 break;
464 }
465
466 node = xpath_object->nodesetval->nodeTab;
467
468 for(i = 0; i < xpath_object->nodesetval->nodeNr; i++){
469 if(node[i]->type == XML_ELEMENT_NODE){
470 buffer = xmlNodeGetContent(node[i]);
471 buffer_length = strlen(buffer);
472
473 break;
474 }
475 }
476
477 if(buffer != NULL){
478 // ags_config_clear(ags_config_get_instance());
479 ags_config_load_from_data(ags_config_get_instance(),
480 buffer, buffer_length);
481 }
482
483 break;
484 }
485 }
486
487 /* load ladspa manager */
488 ladspa_manager = ags_ladspa_manager_get_instance();
489
490 blacklist_filename = "ladspa.blacklist";
491 ags_ladspa_manager_load_blacklist(ladspa_manager,
492 blacklist_filename);
493
494 ags_log_add_message(ags_log_get_instance(),
495 "* Loading LADSPA plugins");
496
497 ags_ladspa_manager_load_default_directory(ladspa_manager);
498
499 /* load dssi manager */
500 dssi_manager = ags_dssi_manager_get_instance();
501
502 blacklist_filename = "dssi_plugin.blacklist";
503 ags_dssi_manager_load_blacklist(dssi_manager,
504 blacklist_filename);
505
506 ags_log_add_message(ags_log_get_instance(),
507 "* Loading DSSI plugins");
508
509 ags_dssi_manager_load_default_directory(dssi_manager);
510
511 /* load lv2 manager */
512 lv2_manager = ags_lv2_manager_get_instance();
513 lv2_worker_manager = ags_lv2_worker_manager_get_instance();
514
515 blacklist_filename = "lv2_plugin.blacklist";
516 ags_lv2_manager_load_blacklist(lv2_manager,
517 blacklist_filename);
518
519 ags_log_add_message(ags_log_get_instance(),
520 "* Loading Lv2 plugins");
521
522 ags_lv2_manager_load_default_directory(lv2_manager);
523
524 /* load lv2ui manager */
525 lv2ui_manager = ags_lv2ui_manager_get_instance();
526
527 blacklist_filename = "lv2ui_plugin.blacklist";
528 ags_lv2ui_manager_load_blacklist(lv2ui_manager,
529 blacklist_filename);
530
531 ags_log_add_message(ags_log_get_instance(),
532 "* Loading Lv2ui plugins");
533
534 ags_lv2ui_manager_load_default_directory(lv2ui_manager);
535
536 /* application contex */
537 ags_application_context = (AgsApplicationContext *) ags_xorg_application_context_new();
538 ags_application_context->argc = argc;
539 ags_application_context->argv = argv;
540
541 ags_application_context_register_types(ags_application_context);
542
543 /* fix cross-references in managers */
544 lv2_worker_manager->thread_pool = ((AgsXorgApplicationContext *) ags_application_context)->thread_pool;
545
546 ags_ui_provider_set_show_animation(AGS_UI_PROVIDER(ags_application_context), FALSE);
547 }
548
549 void
ags_test_launch()550 ags_test_launch()
551 {
552 AgsThread *audio_loop;
553 AgsThreadPool *thread_pool;
554
555 AgsConfig *config;
556
557 audio_loop = ags_concurrency_provider_get_main_loop(AGS_CONCURRENCY_PROVIDER(ags_application_context));
558
559 /* start audio loop and thread pool*/
560 ags_thread_start(audio_loop);
561
562 /* wait for audio loop */
563 g_mutex_lock(AGS_THREAD_GET_START_MUTEX(audio_loop));
564
565 if(ags_thread_test_status_flags(audio_loop, AGS_THREAD_STATUS_START_WAIT)){
566 ags_thread_unset_status_flags(audio_loop, AGS_THREAD_STATUS_START_DONE);
567
568 while(ags_thread_test_status_flags(audio_loop, AGS_THREAD_STATUS_START_WAIT) &&
569 !ags_thread_test_status_flags(audio_loop, AGS_THREAD_STATUS_START_DONE)){
570 g_cond_wait(AGS_THREAD_GET_START_COND(audio_loop),
571 AGS_THREAD_GET_START_MUTEX(audio_loop));
572 }
573 }
574
575 g_mutex_unlock(AGS_THREAD_GET_START_MUTEX(audio_loop));
576
577 ags_ui_provider_set_gui_ready(AGS_UI_PROVIDER(ags_application_context),
578 TRUE);
579 }
580
581 void
ags_test_launch_filename(gchar * filename)582 ags_test_launch_filename(gchar *filename)
583 {
584 AgsSimpleFile *simple_file;
585
586 AgsSimpleFileRead *simple_file_read;
587
588 AgsThread *audio_loop;
589 AgsThreadPool *thread_pool;
590 AgsTaskLauncher *task_launcher;
591
592 AgsConfig *config;
593
594 GList *start_queue;
595
596 GError *error;
597
598 /* get main loop and thread pool */
599 audio_loop = ags_concurrency_provider_get_main_loop(AGS_CONCURRENCY_PROVIDER(ags_application_context));
600
601 thread_pool = ags_concurrency_provider_get_thread_pool(AGS_CONCURRENCY_PROVIDER(ags_application_context));
602
603 task_launcher = ags_concurrency_provider_get_task_launcher(AGS_CONCURRENCY_PROVIDER(ags_application_context));
604
605 /* open file */
606 simple_file = (AgsSimpleFile *) g_object_new(AGS_TYPE_SIMPLE_FILE,
607 "filename", filename,
608 NULL);
609 error = NULL;
610 ags_simple_file_open(simple_file,
611 &error);
612
613 if(error != NULL){
614 ags_test_show_file_error(filename,
615 error);
616 ags_application_context_quit(ags_application_context);
617 }
618
619 /* start engine */
620 g_mutex_lock(AGS_THREAD_GET_START_MUTEX(audio_loop));
621
622 start_queue = NULL;
623
624 g_mutex_unlock(AGS_THREAD_GET_START_MUTEX(audio_loop));
625
626 /* start audio loop and thread pool */
627 ags_thread_start(audio_loop);
628
629 ags_thread_pool_start(thread_pool);
630
631 /* wait for audio loop */
632 g_mutex_lock(AGS_THREAD_GET_START_MUTEX(audio_loop));
633
634 if(ags_thread_test_status_flags(audio_loop, AGS_THREAD_STATUS_START_WAIT)){
635 ags_thread_unset_status_flags(audio_loop, AGS_THREAD_STATUS_START_DONE);
636
637 while(ags_thread_test_status_flags(audio_loop, AGS_THREAD_STATUS_START_WAIT) &&
638 !ags_thread_test_status_flags(audio_loop, AGS_THREAD_STATUS_START_DONE)){
639 g_cond_wait(AGS_THREAD_GET_START_COND(audio_loop),
640 AGS_THREAD_GET_START_MUTEX(audio_loop));
641 }
642 }
643
644 g_mutex_unlock(AGS_THREAD_GET_START_MUTEX(audio_loop));
645
646 /* now start read task */
647 simple_file_read = ags_simple_file_read_new(simple_file);
648
649 ags_task_launcher_add_task(task_launcher,
650 (AgsTask *) simple_file_read);
651 }
652