1 /*
2  * Copyright (C) 2018-2021 Alexandros Theodotou <alex at zrythm dot org>
3  *
4  * This file is part of Zrythm
5  *
6  * Zrythm is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero 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  * Zrythm 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 Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "zrythm-config.h"
21 
22 #ifndef _WOE32
23 #include <sys/mman.h>
24 #endif
25 #include <stdlib.h>
26 
27 #include "actions/actions.h"
28 #include "actions/undo_manager.h"
29 #include "audio/engine.h"
30 #include "audio/router.h"
31 #include "audio/quantize_options.h"
32 #include "audio/recording_manager.h"
33 #include "audio/track.h"
34 #include "audio/tracklist.h"
35 #include "gui/accel.h"
36 #include "gui/backend/event_manager.h"
37 #include "gui/backend/file_manager.h"
38 #include "gui/backend/piano_roll.h"
39 #include "gui/widgets/main_window.h"
40 #include "gui/widgets/splash.h"
41 #include "plugins/plugin_manager.h"
42 #include "project.h"
43 #include "settings/settings.h"
44 #include "utils/arrays.h"
45 #include "utils/cairo.h"
46 #include "utils/curl.h"
47 #include "utils/env.h"
48 #include "utils/gtk.h"
49 #include "utils/localization.h"
50 #include "utils/log.h"
51 #include "utils/object_pool.h"
52 #include "utils/object_utils.h"
53 #include "utils/objects.h"
54 #include "utils/io.h"
55 #include "utils/pcg_rand.h"
56 #include "utils/string.h"
57 #include "utils/symap.h"
58 #include "utils/ui.h"
59 #include "zrythm.h"
60 #include "zrythm_app.h"
61 
62 #include "Wrapper.h"
63 
64 #include <gdk-pixbuf/gdk-pixbuf.h>
65 #include <glib/gi18n.h>
66 #include <gtk/gtk.h>
67 
68 typedef enum
69 {
70   Z_ZRYTHM_ERROR_CANNOT_GET_LATEST_RELEASE,
71 } ZZrythmError;
72 
73 #define Z_ZRYTHM_ERROR \
74   z_zrythm_error_quark ()
75 GQuark z_zrythm_error_quark (void);
76 G_DEFINE_QUARK (
77   z-zrythm-error-quark, z_zrythm_error)
78 
79 /** This is declared extern in zrythm.h. */
80 Zrythm * zrythm = NULL;
81 
82 /**
83  * FIXME move somewhere else.
84  */
85 void
zrythm_add_to_recent_projects(Zrythm * self,const char * _filepath)86 zrythm_add_to_recent_projects (
87   Zrythm * self,
88   const char * _filepath)
89 {
90   /* if we are at max
91    * projects */
92   if (ZRYTHM->num_recent_projects ==
93         MAX_RECENT_PROJECTS)
94     {
95       /* free the last one and delete it */
96       g_free (ZRYTHM->recent_projects[
97                 MAX_RECENT_PROJECTS - 1]);
98       array_delete (
99         ZRYTHM->recent_projects,
100         ZRYTHM->num_recent_projects,
101         ZRYTHM->recent_projects[
102           ZRYTHM->num_recent_projects - 1]);
103     }
104 
105   char * filepath =
106     g_strdup (_filepath);
107 
108   array_insert (
109     ZRYTHM->recent_projects,
110     ZRYTHM->num_recent_projects,
111     0,
112     filepath);
113 
114   /* set last element to NULL because the call
115    * takes a NULL terminated array */
116   ZRYTHM->recent_projects[
117     ZRYTHM->num_recent_projects] = NULL;
118 
119   g_settings_set_strv (
120     S_GENERAL, "recent-projects",
121     (const char * const *) ZRYTHM->recent_projects);
122 }
123 
124 void
zrythm_remove_recent_project(char * filepath)125 zrythm_remove_recent_project (
126   char * filepath)
127 {
128   /* FIXME use GStrvBuilder */
129   for (int i = 0; i < ZRYTHM->num_recent_projects;
130        i++)
131     {
132       const char * recent_project =
133         ZRYTHM->recent_projects[i];
134       g_return_if_fail (recent_project);
135       if (string_is_equal (filepath, recent_project))
136         {
137           array_delete (ZRYTHM->recent_projects,
138                         ZRYTHM->num_recent_projects,
139                         ZRYTHM->recent_projects[i]);
140 
141           ZRYTHM->recent_projects[
142             ZRYTHM->num_recent_projects] = NULL;
143 
144           g_settings_set_strv (
145             S_GENERAL, "recent-projects",
146             (const char * const *)
147               ZRYTHM->recent_projects);
148         }
149 
150     }
151 }
152 
153 /**
154  * Returns the version string.
155  *
156  * Must be g_free()'d.
157  *
158  * @param with_v Include a starting "v".
159  */
160 char *
zrythm_get_version(bool with_v)161 zrythm_get_version (
162   bool with_v)
163 {
164   const char * ver = PACKAGE_VERSION;
165 
166   if (with_v)
167     {
168       if (ver[0] == 'v')
169         return g_strdup (ver);
170       else
171         return
172           g_strdup_printf ("v%s", ver);
173     }
174   else
175     {
176       if (ver[0] == 'v')
177         return g_strdup (ver + 1);
178       else
179         return g_strdup (ver);
180     }
181 }
182 
183 /**
184  * Returns the veresion and the capabilities.
185  */
186 void
zrythm_get_version_with_capabilities(char * str)187 zrythm_get_version_with_capabilities (
188   char * str)
189 {
190   char * ver = zrythm_get_version (0);
191 
192   sprintf (
193     str,
194     "%s %s%s (%s)\n"
195     "  built with %s %s for %s%s\n"
196 #ifdef HAVE_CARLA
197     "    +carla\n"
198 #endif
199 
200 #ifdef HAVE_JACK2
201     "    +jack2\n"
202 #elif defined (HAVE_JACK)
203     "    +jack1\n"
204 #endif
205 
206 #ifdef MANUAL_PATH
207     "    +manual\n"
208 #endif
209 #ifdef HAVE_PULSEAUDIO
210     "    +pulse\n"
211 #endif
212 #ifdef HAVE_RTMIDI
213     "    +rtmidi\n"
214 #endif
215 #ifdef HAVE_RTAUDIO
216     "    +rtaudio\n"
217 #endif
218 #ifdef HAVE_SDL
219     "    +sdl2\n"
220 #endif
221 
222 #ifdef HAVE_GTK_SOURCE_VIEW_4
223     "    +gtksourceview4\n"
224 #elif defined (HAVE_GTK_SOURCE_VIEW_3)
225     "    +gtksourceview3\n"
226 #endif
227 
228 #ifdef HAVE_GUILE
229     "    +guile\n"
230 #endif
231 
232 #ifdef HAVE_LSP_DSP
233     "    +lsp-dsp-lib\n"
234 #endif
235 
236     "",
237     PROGRAM_NAME,
238 
239 #ifdef TRIAL_VER
240     "(trial) ",
241 #else
242     "",
243 #endif
244 
245     ver,
246 
247 #if 0
248     "optimization " OPTIMIZATION
249 #ifdef IS_DEBUG_BUILD
250     " - debug"
251 #endif
252 #endif
253     BUILD_TYPE
254     ,
255 
256     COMPILER, COMPILER_VERSION, HOST_MACHINE_SYSTEM,
257 
258 #ifdef APPIMAGE_BUILD
259     " (appimage)"
260 #elif defined (INSTALLER_VER)
261     " (installer)"
262 #else
263     ""
264 #endif
265 
266     );
267 
268   g_free (ver);
269 }
270 
271 /**
272  * Returns whether the current Zrythm version is a
273  * release version.
274  *
275  * @note This only does regex checking.
276  */
277 bool
zrythm_is_release(bool official)278 zrythm_is_release (
279   bool official)
280 {
281 #ifndef INSTALLER_VER
282   if (official)
283     {
284       return false;
285     }
286 #endif
287 
288   return
289     !string_contains_substr (PACKAGE_VERSION, "g");
290 }
291 
292 /**
293  * Returns the latest release version.
294  */
295 char *
zrythm_fetch_latest_release_ver(void)296 zrythm_fetch_latest_release_ver (void)
297 {
298   static char * ver = NULL;
299   static bool called = false;
300 
301   if (called && ver)
302     {
303       return g_strdup (ver);
304     }
305 
306   char * page =
307     z_curl_get_page_contents_default (
308       "https://www.zrythm.org/releases/?C=M;O=D");
309   if (!page)
310     {
311       g_warning ("failed to get page");
312       return NULL;
313     }
314 
315   ver =
316     string_get_regex_group (
317       page, "title=\"zrythm-(.+).tar.xz\"", 1);
318   g_free (page);
319 
320   if (!ver)
321     {
322       g_warning ("failed to parse version");
323       return NULL;
324     }
325 
326   g_debug ("latest release: %s", ver);
327   called = true;
328 
329   return g_strdup (ver);
330 }
331 
332 /**
333  * Returns whether this is the latest release.
334  *
335  * @p error will be set if an error occured and the
336  * return value should be ignored.
337  */
338 bool
zrythm_is_latest_release(GError ** error)339 zrythm_is_latest_release (
340   GError ** error)
341 {
342   g_return_val_if_fail (
343     *error == NULL, false);
344 
345   char * latest_release =
346     zrythm_fetch_latest_release_ver ();
347   if (!latest_release)
348     {
349       g_set_error_literal (
350         error, Z_ZRYTHM_ERROR,
351         Z_ZRYTHM_ERROR_CANNOT_GET_LATEST_RELEASE,
352         "Error getting latest release");
353       return false;
354     }
355 
356   bool ret = false;
357   if (string_is_equal (
358         latest_release, PACKAGE_VERSION))
359     {
360       ret = true;
361     }
362   g_free (latest_release);
363 
364   return ret;
365 }
366 
367 /**
368  * Returns the prefix or in the case of windows
369  * the root dir (C/program files/zrythm) or in the
370  * case of macos the bundle path.
371  *
372  * In all cases, "share" is expected to be found
373  * in this dir.
374  *
375  * @return A newly allocated string.
376  */
377 char *
zrythm_get_prefix(void)378 zrythm_get_prefix (void)
379 {
380 #if defined (_WOE32) && defined (INSTALLER_VER)
381   return
382     io_get_registry_string_val ("InstallPath");
383 #elif defined (__APPLE__) && defined (INSTALLER_VER)
384   char bundle_path[PATH_MAX];
385   int ret = io_get_bundle_path (bundle_path);
386   g_return_val_if_fail (ret == 0, NULL);
387   return io_path_get_parent_dir (bundle_path);
388 #elif defined (APPIMAGE_BUILD)
389   g_return_val_if_fail (
390     zrythm_app->appimage_runtime_path, NULL);
391   return
392     g_build_filename (
393       zrythm_app->appimage_runtime_path, "usr",
394       NULL);
395 #else
396   return g_strdup (PREFIX);
397 #endif
398 }
399 
400 /**
401  * Gets the zrythm directory, either from the
402  * settings if non-empty, or the default
403  * ($XDG_DATA_DIR/zrythm).
404  *
405  * @param force_default Ignore the settings and get
406  *   the default dir.
407  *
408  * Must be free'd by caler.
409  */
410 char *
zrythm_get_user_dir(bool force_default)411 zrythm_get_user_dir (
412   bool  force_default)
413 {
414   if (ZRYTHM_TESTING)
415     {
416       if (!ZRYTHM->testing_dir)
417         {
418           ZRYTHM->testing_dir =
419             g_dir_make_tmp (
420               "zrythm_test_dir_XXXXXX", NULL);
421         }
422       g_debug (
423         "returning user dir: %s",
424         ZRYTHM->testing_dir);
425       return g_strdup (ZRYTHM->testing_dir);
426     }
427 
428   char * dir = NULL;
429   if (SETTINGS && S_P_GENERAL_PATHS)
430     {
431       dir =
432         g_settings_get_string (
433           S_P_GENERAL_PATHS, "zrythm-dir");
434     }
435   else
436     {
437       GSettings * settings =
438         g_settings_new (
439           GSETTINGS_ZRYTHM_PREFIX
440           ".preferences.general.paths");
441       g_return_val_if_fail (settings, NULL);
442 
443       dir =
444         g_settings_get_string (
445           settings, "zrythm-dir");
446 
447       g_object_unref (settings);
448     }
449 
450   if (force_default || strlen (dir) == 0)
451     {
452       g_free (dir);
453       dir =
454         g_build_filename (
455           g_get_user_data_dir (), "zrythm", NULL);
456     }
457 
458   return dir;
459 }
460 
461 /**
462  * Returns the default user "zrythm" dir.
463  *
464  * This is used when resetting or when the
465  * dir is not selected by the user yet.
466  */
467 char *
zrythm_get_default_user_dir(void)468 zrythm_get_default_user_dir (void)
469 {
470   return zrythm_get_user_dir (true);
471 }
472 
473 /**
474  * Returns a Zrythm directory specified by
475  * \ref type.
476  *
477  * @return A newly allocated string.
478  */
479 char *
zrythm_get_dir(ZrythmDirType type)480 zrythm_get_dir (
481   ZrythmDirType type)
482 {
483   char * res = NULL;
484 
485   /* handle system dirs */
486   if (type < ZRYTHM_DIR_USER_TOP)
487     {
488       char * prefix = zrythm_get_prefix ();
489 
490       switch (type)
491         {
492         case ZRYTHM_DIR_SYSTEM_PREFIX:
493           res = g_strdup (prefix);
494           break;
495         case ZRYTHM_DIR_SYSTEM_BINDIR:
496           res =
497             g_build_filename (
498               prefix, "bin", NULL);
499           break;
500         case ZRYTHM_DIR_SYSTEM_PARENT_DATADIR:
501           res =
502             g_build_filename (
503               prefix, "share", NULL);
504           break;
505         case ZRYTHM_DIR_SYSTEM_PARENT_LIBDIR:
506           res =
507             g_build_filename (
508               prefix, "lib", NULL);
509           break;
510         case ZRYTHM_DIR_SYSTEM_ZRYTHM_LIBDIR:
511           res =
512             g_build_filename (
513               prefix, "lib", "zrythm", NULL);
514           break;
515         case ZRYTHM_DIR_SYSTEM_LOCALEDIR:
516           res =
517             g_build_filename (
518               prefix, "share", "locale", NULL);
519           break;
520         case ZRYTHM_DIR_SYSTEM_SOURCEVIEW_LANGUAGE_SPECS_DIR:
521           res =
522             g_build_filename (
523               prefix, "share",
524 #ifdef HAVE_GTK_SOURCE_VIEW_4
525               "gtksourceview-4",
526 #else
527               "gtksourceview-3.0",
528 #endif
529               "language-specs", NULL);
530           break;
531         case ZRYTHM_DIR_SYSTEM_ZRYTHM_DATADIR:
532           res =
533             g_build_filename (
534               prefix, "share", "zrythm", NULL);
535           break;
536         case ZRYTHM_DIR_SYSTEM_SAMPLESDIR:
537           res =
538             g_build_filename (
539               prefix, "share", "zrythm",
540               "samples", NULL);
541           break;
542         case ZRYTHM_DIR_SYSTEM_SCRIPTSDIR:
543           res =
544             g_build_filename (
545               prefix, "share", "zrythm",
546               "scripts", NULL);
547           break;
548         case ZRYTHM_DIR_SYSTEM_THEMESDIR:
549           res =
550             g_build_filename (
551               prefix, "share", "zrythm",
552               "themes", NULL);
553           break;
554         case ZRYTHM_DIR_SYSTEM_THEMES_CSS_DIR:
555           res =
556             g_build_filename (
557               prefix, "share", "zrythm",
558               "themes", "css", NULL);
559           break;
560         case ZRYTHM_DIR_SYSTEM_LV2_PLUGINS_DIR:
561           res =
562             g_build_filename (
563               prefix, "share", "zrythm",
564               "lv2", NULL);
565           break;
566         default:
567           break;
568         }
569 
570       g_free (prefix);
571     }
572   /* handle user dirs */
573   else
574     {
575       char * user_dir = zrythm_get_user_dir (false);
576 
577       switch (type)
578         {
579         case ZRYTHM_DIR_USER_TOP:
580           res = g_strdup (user_dir);
581           break;
582         case ZRYTHM_DIR_USER_PROJECTS:
583           res =
584             g_build_filename (
585               user_dir, ZRYTHM_PROJECTS_DIR, NULL);
586           break;
587         case ZRYTHM_DIR_USER_TEMPLATES:
588           res =
589             g_build_filename (
590               user_dir, "templates", NULL);
591           break;
592         case ZRYTHM_DIR_USER_LOG:
593           res =
594             g_build_filename (
595               user_dir, "log", NULL);
596           break;
597         case ZRYTHM_DIR_USER_SCRIPTS:
598           res =
599             g_build_filename (
600               user_dir, "scripts", NULL);
601           break;
602         case ZRYTHM_DIR_USER_THEMES:
603           res =
604             g_build_filename (
605               user_dir, "themes", NULL);
606           break;
607         case ZRYTHM_DIR_USER_THEMES_CSS:
608           res =
609             g_build_filename (
610               user_dir, "themes", "css", NULL);
611           break;
612         case ZRYTHM_DIR_USER_PROFILING:
613           res =
614             g_build_filename (
615               user_dir, "profiling", NULL);
616           break;
617         case ZRYTHM_DIR_USER_GDB:
618           res =
619             g_build_filename (
620               user_dir, "gdb", NULL);
621           break;
622         case ZRYTHM_DIR_USER_BACKTRACE:
623           res =
624             g_build_filename (
625               user_dir, "backtraces", NULL);
626           break;
627         default:
628           break;
629         }
630 
631       g_free (user_dir);
632     }
633 
634   return res;
635 }
636 
637 /**
638  * Initializes/creates the default dirs/files in
639  * the user directory.
640  */
641 NONNULL
642 void
zrythm_init_user_dirs_and_files(Zrythm * self)643 zrythm_init_user_dirs_and_files (
644   Zrythm * self)
645 {
646   g_message ("initing dirs and files");
647   char * dir;
648 
649 #define MK_USER_DIR(x) \
650   dir =  zrythm_get_dir (ZRYTHM_DIR_USER_##x); \
651   io_mkdir (dir); \
652   g_free (dir)
653 
654   MK_USER_DIR (TOP);
655   MK_USER_DIR (PROJECTS);
656   MK_USER_DIR (TEMPLATES);
657   MK_USER_DIR (LOG);
658   MK_USER_DIR (THEMES);
659   MK_USER_DIR (THEMES_CSS);
660   MK_USER_DIR (PROFILING);
661   MK_USER_DIR (GDB);
662 
663 #undef MK_USER_DIR
664 }
665 
666 /**
667  * Initializes the array of project templates.
668  */
669 void
zrythm_init_templates(Zrythm * self)670 zrythm_init_templates (
671   Zrythm * self)
672 {
673   g_message ("Initializing templates...");
674 
675   char * user_templates_dir =
676     zrythm_get_dir (ZRYTHM_DIR_USER_TEMPLATES);
677   ZRYTHM->templates =
678     io_get_files_in_dir (user_templates_dir, true);
679   g_return_if_fail (ZRYTHM->templates);
680   g_free (user_templates_dir);
681 
682   g_message ("done");
683 }
684 
685 /**
686  * Frees the instance and any unfreed members.
687  */
688 void
zrythm_free(Zrythm * self)689 zrythm_free (
690   Zrythm * self)
691 {
692   g_message (
693     "%s: deleting Zrythm instance...",
694     __func__);
695 
696   object_free_w_func_and_null (
697     project_free, self->project);
698 
699   self->have_ui = false;
700 
701   object_free_w_func_and_null (
702     z_cairo_caches_free, self->cairo_caches);
703   object_free_w_func_and_null (
704     recording_manager_free,
705     self->recording_manager);
706   object_free_w_func_and_null (
707     plugin_manager_free,
708     self->plugin_manager);
709   object_free_w_func_and_null (
710     event_manager_free, self->event_manager);
711   object_free_w_func_and_null (
712     file_manager_free, self->file_manager);
713 
714   /* free object utils around last */
715   object_free_w_func_and_null (
716     object_utils_free, self->object_utils);
717 
718   object_free_w_func_and_null (
719     symap_free, self->symap);
720   object_free_w_func_and_null (
721     symap_free, self->error_domain_symap);
722 
723   if (ZRYTHM == self)
724     {
725       ZRYTHM = NULL;
726     }
727 
728   object_zero_and_free (self);
729 
730   g_message ("%s: done", __func__);
731 }
732 
733 /**
734  * Creates a new Zrythm instance.
735  *
736  * @param have_ui Whether Zrythm is instantiated
737  *   with a UI (false if headless).
738  * @param testing Whether this is a unit test.
739  */
740 Zrythm *
zrythm_new(const char * exe_path,bool have_ui,bool testing,bool optimized_dsp)741 zrythm_new (
742   const char * exe_path,
743   bool         have_ui,
744   bool         testing,
745   bool         optimized_dsp)
746 {
747   Zrythm * self = object_new (Zrythm);
748   ZRYTHM = self;
749 
750   self->rand = pcg_rand_new ();
751 
752   self->exe_path = g_strdup (exe_path);
753 
754   self->version = zrythm_get_version (false);
755   self->have_ui = have_ui;
756   self->testing = testing;
757   self->use_optimized_dsp = optimized_dsp;
758   self->settings = settings_new ();
759   self->object_utils = object_utils_new ();
760   self->recording_manager =
761     recording_manager_new ();
762   self->plugin_manager = plugin_manager_new ();
763   self->symap = symap_new ();
764   self->error_domain_symap = symap_new ();
765   self->file_manager = file_manager_new ();
766   self->cairo_caches = z_cairo_caches_new ();
767 
768   if (have_ui)
769     {
770       self->event_manager =
771         event_manager_new ();
772     }
773 
774   return self;
775 }
776