00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00024 #include <errno.h>
00025 #include <gtk/gtk.h>
00026 #include <glib.h>
00027 #include <glib/gstdio.h>
00028 #include <stdlib.h>
00029 #include <sys/types.h>
00030 #include <unistd.h>
00031
00032 #include "application-data.h"
00033 #include "frame-saving.h"
00034 #include "log-player-dbus.h"
00035 #include "machine.h"
00036 #include "notebook.h"
00037 #include "plugins.h"
00038
00039
00040
00041 static void _error_dialog (GtkWindow *parent, const gchar *buffer)
00042 {
00043 GtkWidget *dialog = gtk_message_dialog_new (parent,
00044 GTK_DIALOG_DESTROY_WITH_PARENT,
00045 GTK_MESSAGE_ERROR,
00046 GTK_BUTTONS_OK,
00047 NULL);
00048
00049 gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), buffer);
00050 gtk_dialog_run (GTK_DIALOG (dialog));
00051
00052 gtk_widget_destroy (dialog);
00053
00054 }
00055
00056
00057 void error_dialog (const gchar *message, ...)
00058 {
00059 synema_instance_t *inst = synema_instance ();
00060 gchar *buffer = NULL;
00061 GtkWindow *parent = NULL;
00062 va_list args;
00063
00064 parent = GTK_WINDOW (gtk_builder_get_object (inst->builder, "synema_window"));
00065
00066 va_start (args, message);
00067 buffer = g_strdup_vprintf (message, args);
00068 va_end (args);
00069
00070 _error_dialog (parent, buffer);
00071 g_free (buffer);
00072 }
00073
00074
00075
00076 void toggle_panels (GtkToolButton *button, gpointer user_data)
00077 {
00078 synema_instance_t *inst = synema_instance ();
00079 gint paneltype = GPOINTER_TO_INT (user_data);
00080 GtkWidget *panel = (GtkWidget *) gtk_builder_get_object (inst->builder, "top_panel");
00081 GtkWidget *top_box = (GtkWidget *) gtk_builder_get_object (inst->builder, "top_box");
00082 GtkWidget *frame_box = (GtkWidget *) gtk_builder_get_object (inst->builder, "notebook");
00083 GtkWidget *pn_open = (GtkWidget *) gtk_builder_get_object (inst->builder, "open_vbox");
00084 GtkWidget *pn_actions = (GtkWidget *) gtk_builder_get_object (inst->builder, "actions_vbox");
00085 GtkWidget *pn_player = (GtkWidget *) gtk_builder_get_object (inst->builder, "player_vbox");
00086
00087
00088
00089 if (paneltype == PANEL_PLAYER) {
00090 gtk_widget_show (pn_player);
00091 gtk_widget_hide (pn_open);
00092 gtk_widget_hide (pn_actions);
00093 } else if (paneltype == PANEL_OPEN) {
00094 gtk_widget_show (pn_open);
00095 gtk_widget_hide (pn_player);
00096 gtk_widget_hide (pn_actions);
00097 } else if (paneltype == PANEL_ACTIONS) {
00098 gtk_widget_show (pn_actions);
00099 gtk_widget_hide (pn_player);
00100 gtk_widget_hide (pn_open);
00101 }
00102
00103
00104
00105 if (gtk_widget_get_parent (frame_box) != panel) {
00106 g_object_ref (frame_box);
00107 gtk_container_remove (GTK_CONTAINER (top_box), frame_box);
00108 gtk_container_add (GTK_CONTAINER (top_box), panel);
00109 gtk_paned_add2 (GTK_PANED (panel), frame_box);
00110 g_object_unref (frame_box);
00111 g_object_unref (panel);
00112 }
00113
00114 else if (paneltype == inst->current_panel) {
00115 g_object_ref (panel);
00116 g_object_ref (frame_box);
00117 gtk_container_remove (GTK_CONTAINER (panel), frame_box);
00118 gtk_container_remove (GTK_CONTAINER (top_box), panel);
00119 gtk_container_add (GTK_CONTAINER (top_box), frame_box);
00120 g_object_unref (frame_box);
00121 }
00122
00123
00124 inst->current_panel = paneltype;
00125 }
00126
00127
00128
00129 gint post_init_restore_frames ()
00130 {
00131 synema_instance_t *inst = synema_instance ();
00132 frame_table_t *tmptab = NULL;
00133 gchar *dirpath = NULL;
00134 gchar *subdirpath = NULL;
00135 gchar *ptr = NULL;
00136 GError *err = NULL;
00137 GFile *dirfile = NULL;
00138 GFileEnumerator *direnum = NULL;
00139 GFileInfo *current = NULL;
00140 GFileInfo *dirinfo = NULL;
00141 GList *iter = NULL;
00142 GtkWidget *frame_box = NULL;
00143
00144 frame_box = (GtkWidget *) gtk_builder_get_object (inst->builder, "notebook");
00145
00146 dirpath = g_strdup_printf ("%s/"SAVED_FRAMES_DIR, inst->config_dir);
00147 dirfile = g_file_new_for_path (dirpath);
00148
00149 dirinfo = g_file_query_info (dirfile, "standard::type", G_FILE_QUERY_INFO_NONE, NULL, &err);
00150 if (err) {
00151
00152
00153 g_clear_error (&err);
00154 g_object_unref (dirfile);
00155 g_free (dirpath);
00156 return -1;
00157 }
00158 if (g_file_info_get_file_type (dirinfo) != G_FILE_TYPE_DIRECTORY) {
00159
00160 g_free (dirpath);
00161 return -1;
00162 }
00163 g_object_unref (dirinfo);
00164
00165 direnum = g_file_enumerate_children (dirfile, "standard::type,standard::name", G_FILE_QUERY_INFO_NONE, NULL, &err);
00166 if (err) {
00167 g_warning ("post_init_restore_frames: Error while reading the "SAVED_FRAMES_DIR" directory (%s)", err->message);
00168 g_free (dirpath);
00169 g_clear_error (&err);
00170 g_object_unref (dirfile);
00171 return -1;
00172 }
00173
00174
00175
00176 while ((current = g_file_enumerator_next_file (direnum, NULL, &err)) != NULL) {
00177 if (err) {
00178 g_warning ("post_init_restore_frames: Error while reading a subdirectory of "SAVED_FRAMES_DIR" (%s)", err->message);
00179 g_clear_error (&err);
00180 } else {
00181 if (g_file_info_get_file_type (current) == G_FILE_TYPE_DIRECTORY) {
00182 subdirpath = g_strdup_printf ("%s/%s", dirpath, g_file_info_get_name (current));
00183 ptr = g_strstr_len (g_strrstr (subdirpath, "/"), -1, "__");
00184 if (!ptr) {
00185 g_warning ("post_init_restore_frames: Malformed directory name %s", subdirpath);
00186 } else {
00187 tmptab = _notebook_add_page (frame_box, ptr + 2);
00188 if (frame_table_load_from_files (subdirpath, tmptab) != 0) {
00189 notebook_remove_page (NULL, tmptab);
00190 }
00191 }
00192 g_free (subdirpath);
00193 }
00194 g_object_unref (current);
00195 }
00196 }
00197
00198 g_free (dirpath);
00199 g_object_unref (direnum);
00200 g_object_unref (dirfile);
00201
00202
00203
00204 inst->tables_list = g_list_sort (inst->tables_list, frame_table_list_cmp_by_position);
00205 if (inst->tables_list) {
00206 iter = inst->tables_list;
00207 do {
00208 tmptab = iter->data;
00209 gtk_notebook_reorder_child (GTK_NOTEBOOK (frame_box), tmptab->table_box, tmptab->position);
00210 } while ((iter = g_list_next (iter)) != NULL);
00211 }
00212
00213
00214
00215 inst->labelcount = gtk_notebook_get_n_pages (GTK_NOTEBOOK (frame_box));
00216
00217 gtk_notebook_set_current_page (GTK_NOTEBOOK (frame_box), inst->settings->last_current_tab);
00218
00219 return 0;
00220 }
00221
00222
00223
00224 void pre_quit_save_frames (GtkObject *object, gpointer user_data)
00225 {
00226 synema_instance_t *inst = synema_instance ();
00227 frame_table_t *tab = NULL;
00228 gchar *path = g_strdup_printf ("%s/"SAVED_FRAMES_DIR, inst->config_dir);
00229 GList *iter = inst->tables_list;
00230
00231 inst->settings->last_current_tab = inst->current_table->position;
00232
00233 if (recursive_remove (path))
00234 g_warning ("pre_quit_save_frames: Could not fully remove previously saved frames.");
00235
00236 if ((iter) && (inst->settings->autosave_frames)) {
00237 do {
00238 tab = iter->data;
00239 frame_table_save_to_files (path, tab);
00240 } while ((iter = g_list_next (iter)) != NULL);
00241 }
00242
00243 g_free (path);
00244 gtk_main_quit ();
00245 }
00246
00247
00248
00249 gint recursive_remove (const gchar *path)
00250 {
00251 gchar *subpath = NULL;
00252 GError *err = NULL;
00253 GFile *file = NULL;
00254 GFileEnumerator *fileenum = NULL;
00255 GFileInfo *current = NULL;
00256 GFileInfo *fileinfo = NULL;
00257
00258 file = g_file_new_for_path (path);
00259
00260 if (!g_file_query_exists (file, NULL))
00261 return 0;
00262
00263 fileinfo = g_file_query_info (file, "standard::type", G_FILE_QUERY_INFO_NONE, NULL, &err);
00264 if (err) {
00265 g_warning ("recursive_remove: Could not get file info for %s (%s)", path, err->message);
00266 g_clear_error (&err);
00267 g_object_unref (file);
00268 return -1;
00269 } else {
00270 if (g_file_info_get_file_type (fileinfo) == G_FILE_TYPE_DIRECTORY) {
00271 fileenum = g_file_enumerate_children (file, "standard::name, standard::type", G_FILE_QUERY_INFO_NONE, NULL, &err);
00272 if (err) {
00273 g_warning ("recursive_remove: Could not enumerate files in %s (%s)", path, err->message);
00274 g_clear_error (&err);
00275 } else {
00276 while ((current = g_file_enumerator_next_file (fileenum, NULL, &err)) != NULL) {
00277 if (err) {
00278 g_warning ("recursive_remove: Could not get next file to remove in %s (%s)", path, err->message);
00279 g_clear_error (&err);
00280 } else {
00281 subpath = g_strdup_printf ("%s/%s", path, g_file_info_get_name (current));
00282 if (recursive_remove (subpath))
00283 g_warning ("recursive_remove: Could not remove file %s", subpath);
00284 g_free (subpath);
00285 g_object_unref (current);
00286 }
00287 }
00288 }
00289 g_object_unref (fileenum);
00290 }
00291 g_object_unref (fileinfo);
00292 if (g_remove (path)) {
00293 g_warning ("recursive_remove: Could not remove file %s (%s)", path, g_strerror (errno));
00294 g_object_unref (file);
00295 return -1;
00296 }
00297 }
00298
00299 g_object_unref (file);
00300 return 0;
00301 }
00302
00303
00304
00305 guint unique_frame_id_generator (GList *list)
00306 {
00307 synema_instance_t *inst = synema_instance ();
00308 frame_t *f = NULL;
00309 gboolean found = FALSE;
00310 GList *iter = list;
00311 guint val;
00312
00313 while (!found) {
00314 val = g_rand_int (inst->rand);
00315 found = TRUE;
00316
00317 if (iter) {
00318 do {
00319 f = iter->data;
00320 found = (f->id != val);
00321 } while (((iter = g_list_next (iter)) != NULL) && (found));
00322 }
00323 }
00324
00325 return val;
00326 }
00327
00328
00329
00340 static synema_instance_t *synema_new ()
00341 {
00342 const gchar* const *data_dirs = g_get_system_data_dirs ();
00343 gboolean create_lock = FALSE;
00344 gboolean locked = FALSE;
00345 gchar *checkout = NULL;
00346 gchar *envbuff = NULL;
00347 gchar lockbuff[10] = {0,0,0,0,0,0,0,0,0,0};
00348 gchar *path = NULL;
00349 GError *err = NULL;
00350 GFile *lock = NULL;
00351 GFileInputStream *instream = NULL;
00352 GFileOutputStream *outstream = NULL;
00353 gint i = 0;
00354 synema_instance_t *inst = g_malloc (sizeof (synema_instance_t));
00355 struct stat buff;
00356
00357
00358
00359 envbuff = g_strdup (getenv (ENV_TMP));
00360 if (envbuff)
00361 inst->tmp_dir = envbuff;
00362 else
00363 inst->tmp_dir = g_strdup_printf ("%s/"APP_NAME_LOWER, g_get_user_cache_dir ());
00364 if (g_stat (inst->tmp_dir, &buff) == 0) {
00365 if (!S_ISDIR (buff.st_mode)) {
00366 g_free (inst->tmp_dir);
00367 inst->tmp_dir = NULL;
00368 g_warning ("synema_new: The tmp directory is not an actual folder.");
00369 } else if (!(S_IWUSR & buff.st_mode)) {
00370 g_free (inst->tmp_dir);
00371 inst->tmp_dir = NULL;
00372 g_warning ("synema_new: Does not have the right to write in the user cache directory.");
00373 }
00374 } else {
00375 g_free (inst->tmp_dir);
00376 inst->tmp_dir = NULL;
00377 }
00378 if (inst->tmp_dir == NULL) {
00379 if (g_get_user_config_dir () != NULL)
00380 inst->tmp_dir = g_strdup_printf ("%s/"APP_NAME_LOWER, g_get_user_cache_dir ());
00381 else
00382 inst->tmp_dir = g_strdup (APP_TMP_DIR);
00383 if (g_mkdir_with_parents (inst->tmp_dir, 0777) != 0) {
00384 g_critical ("synema_new: Attempted to use %s as a cache directory but it failed (%s)",
00385 inst->tmp_dir,
00386 g_strerror (errno));
00387 g_free (inst->tmp_dir);
00388 inst->tmp_dir = NULL;
00389 } else {
00390 g_debug ("Created the %s directory for tmp.\n", inst->tmp_dir);
00391 }
00392 }
00393
00394
00395
00396 envbuff = g_strdup (getenv (ENV_CONFIG));
00397 if (envbuff)
00398 inst->config_dir = envbuff;
00399 else
00400 inst->config_dir = g_strdup_printf ("%s/"APP_NAME_LOWER, g_get_user_config_dir ());
00401 if (g_stat (inst->config_dir, &buff) == 0) {
00402 if (!S_ISDIR (buff.st_mode)) {
00403 g_free (inst->config_dir);
00404 inst->config_dir = NULL;
00405 g_warning ("synema_new: The config directory is not an actual folder.");
00406 }
00407 } else {
00408 g_free (inst->config_dir);
00409 inst->config_dir = NULL;
00410
00411 }
00412 if (inst->config_dir == NULL) {
00413 if (g_get_user_config_dir () != NULL)
00414 inst->config_dir = g_strdup_printf ("%s/"APP_NAME_LOWER, g_get_user_config_dir ());
00415 else
00416 inst->config_dir = g_strdup (APP_TMP_DIR);
00417 if (g_mkdir_with_parents (inst->config_dir, 0777) != 0) {
00418 g_warning ("synema_new: Attempted to use %s as a config directory but it failed (%s)",
00419 inst->config_dir,
00420 g_strerror (errno));
00421 g_free (inst->config_dir);
00422 inst->config_dir = NULL;
00423 } else {
00424 g_debug ("Created the %s directory for config.\n", inst->config_dir);
00425 }
00426 }
00427
00428
00429
00430 envbuff = g_strdup (getenv (ENV_MACHINES));
00431 if (envbuff)
00432 inst->machines_dir = envbuff;
00433 else
00434 inst->machines_dir = g_strdup_printf ("%s/"APP_NAME_LOWER"/"MACHINES_DIR, g_get_user_config_dir ());
00435 if (g_stat (inst->machines_dir, &buff) == 0) {
00436 if (!S_ISDIR (buff.st_mode)) {
00437 g_free (inst->machines_dir);
00438 inst->machines_dir = NULL;
00439 g_warning ("synema_new: The machines directory is not an actual folder.");
00440 }
00441 } else {
00442 g_free (inst->machines_dir);
00443 inst->machines_dir = NULL;
00444
00445 }
00446 if (inst->machines_dir == NULL) {
00447 if (g_get_user_config_dir () != NULL)
00448 inst->machines_dir = g_strdup_printf ("%s/"APP_NAME_LOWER"/"MACHINES_DIR, g_get_user_config_dir ());
00449 else
00450 inst->machines_dir = g_strdup (APP_TMP_DIR"/"MACHINES_DIR);
00451 if (g_mkdir_with_parents (inst->machines_dir, 0777) != 0) {
00452 g_warning ("synema_new: Attempted to use %s as a machines directory but it failed (%s)",
00453 inst->machines_dir,
00454 g_strerror (errno));
00455 g_free (inst->machines_dir);
00456 inst->machines_dir = NULL;
00457 } else {
00458 g_debug ("Created the %s directory for machines.\n", inst->machines_dir);
00459 }
00460 }
00461
00462
00463
00464 envbuff = g_strdup (getenv (ENV_DATA));
00465 if (envbuff)
00466 inst->data_dir = envbuff;
00467 else
00468 inst->data_dir = NULL;
00469 while (!inst->data_dir && data_dirs[i]) {
00470 checkout = g_strdup_printf ("%s/"APP_NAME_LOWER, data_dirs[i]);
00471 if (g_stat (checkout, &buff) == 0) {
00472 if (S_ISDIR (buff.st_mode))
00473 inst->data_dir = checkout;
00474 else
00475 g_free (checkout);
00476 } else {
00477 g_free (checkout);
00478 }
00479 i++;
00480 }
00481 if (inst->data_dir == NULL) {
00482 inst->data_dir = g_strdup (APP_TMP_DIR"/"DATA_DIR);
00483 if (g_mkdir_with_parents (inst->data_dir, 0777) != 0)
00484 g_error ("synema_new: No data directory, aborting (%s)",
00485 g_strerror (errno));
00486 }
00487
00488
00489
00490 envbuff = g_strdup (getenv (ENV_PLUGINS));
00491 if (envbuff)
00492 inst->plugins_dir = envbuff;
00493 else
00494 inst->plugins_dir = g_strdup (PATH_TO_PLUGINS_DIR);
00495
00496
00497
00498 inst->builder = gtk_builder_new ();
00499 path = g_strdup_printf ("%s/window.ui", inst->data_dir);
00500 gtk_builder_add_from_file (inst->builder, path, &err);
00501 g_free (path);
00502 if (err) {
00503 g_error ("synema_new: %s", err->message);
00504 g_clear_error (&err);
00505 exit (EXIT_FAILURE);
00506 }
00507
00508
00509
00510 path = g_strdup_printf ("%s/"APP_LOCK_FILE, inst->tmp_dir);
00511 lock = g_file_new_for_path (path);
00512 g_free (path);
00513
00514 if (g_file_query_exists (lock, NULL)) {
00515
00516 instream = g_file_read (lock, NULL, &err);
00517 if (err) {
00518 g_warning ("synema_new: Could not open the lock file while it's meant to exist (%s)", err->message);
00519 g_clear_error (&err);
00520 locked = TRUE;
00521 }
00522
00523 g_input_stream_read (G_INPUT_STREAM (instream), lockbuff, 10, NULL, &err);
00524 if (err) {
00525 g_warning ("synema_new: Could not read the lock file (%s)", err->message);
00526 g_clear_error (&err);
00527 locked = TRUE;
00528 }
00529 g_object_unref (instream);
00530
00531 path = g_strdup_printf ("/proc/%i", (int)g_ascii_strtod (lockbuff, NULL));
00532 if (g_stat (path, &buff) == 0) {
00533 locked = TRUE;
00534 } else {
00535 g_warning ("synema_new: A lock file exists but the process that created it is not running anymore. This can happen when "APP_NAME" previously crashed.");
00536 create_lock = TRUE;
00537 }
00538 g_free (path);
00539
00540 if (locked) {
00541 _error_dialog (NULL, "Can't launch "APP_NAME" because another instance is already running.");
00542 exit (EXIT_FAILURE);
00543 }
00544 } else {
00545 create_lock = TRUE;
00546 }
00547
00548 if (create_lock) {
00549 outstream = g_file_replace (lock, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &err);
00550 if (err) {
00551 g_warning ("synema_new: Could not create a lock file (%s)", err->message);
00552 g_clear_error (&err);
00553 } else {
00554 g_snprintf (lockbuff, 10, "%u", (unsigned int) getpid());
00555 if (g_output_stream_write (G_OUTPUT_STREAM (outstream), lockbuff, 10, NULL, &err) == -1) {
00556 g_warning ("synema_new: Could not write the pid in the lock file (%s)", err->message);
00557 g_clear_error (&err);
00558 }
00559 g_object_unref (outstream);
00560 }
00561 }
00562 g_object_unref (lock);
00563
00564
00565
00566 inst->rand = g_rand_new ();
00567 inst->labelcount = 0;
00568
00569
00570 inst->tables_list = NULL;
00571 inst->current_table = NULL;
00572
00573
00574 inst->plugins_list = plugin_list_new (inst->plugins_dir);
00575 inst->machines_list = machine_list_new (inst->machines_dir, inst->plugins_list);
00576
00577
00578 inst->player_status = PLAYER_STOPPED;
00579 inst->player_curr_time = 0;
00580 if (!player_dbus_connect (&(inst->dbus_connection), &(inst->dbus_proxy))) {
00581 g_warning ("synema_new: Could not connect to DBus.");
00582 }
00583
00584
00585 inst->settings = settings_load (inst->config_dir, inst->data_dir);
00586 if (inst->settings == NULL) {
00587 g_error ("synema_new: Couldn't load the settings.");
00588 exit (EXIT_FAILURE);
00589 }
00590
00591 inst->current_panel = PANEL_NONE;
00592
00593 return inst;
00594 }
00595
00596
00597
00609 static synema_instance_t *_synema_instance (int setnull)
00610 {
00611 static synema_instance_t *params = NULL;
00612 static gboolean building = FALSE;
00613
00614 if (building)
00615 g_error ("Someone attempted to access the Synema instance while it is building. This reflects a fatal bug in the application.");
00616
00617 if (setnull)
00618 params = NULL;
00619 else if (params == NULL) {
00620 building = TRUE;
00621 params = synema_new ();
00622 building = FALSE;
00623 }
00624
00625 return params;
00626 }
00627
00628
00629
00630 synema_instance_t *synema_instance ()
00631 {
00632 return _synema_instance (0);
00633 }
00634
00635
00636
00637 void synema_instance_free ()
00638 {
00639 synema_instance_t *inst = synema_instance ();
00640
00641 settings_save (inst->settings);
00642
00643 if (inst->tables_list) {
00644 GList *iter = inst->tables_list;
00645 do {
00646 frame_table_free (iter->data);
00647 } while ((iter = g_list_next (iter)) != NULL);
00648 }
00649 g_list_free (inst->tables_list);
00650
00651 g_rand_free (inst->rand);
00652 machine_list_free (inst->machines_list);
00653 plugin_list_free (inst->plugins_list);
00654 g_object_unref (inst->builder);
00655 g_free (inst->settings);
00656
00657 recursive_remove (inst->tmp_dir);
00658
00659 g_free (inst->tmp_dir);
00660 g_free (inst->data_dir);
00661 g_free (inst->config_dir);
00662 g_free (inst->machines_dir);
00663 g_free (inst->plugins_dir);
00664
00665
00666 g_free (inst);
00667 _synema_instance (1);
00668 }