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, &param) == -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