diff --git a/io.c b/io.c index 0edbc0d9..26d8caf0 100644 --- a/io.c +++ b/io.c @@ -284,3 +284,72 @@ void Get_full_filename(char * output_name, char * file_name, char * directory_na } strcat(output_name,file_name); } + +/// Lock file used to prevent several instances of grafx2 from harming each others' backups +#ifdef __WIN32__ +HANDLE Lock_file_handle = INVALID_HANDLE_VALUE; +#else +int Lock_file_handle = -1; +#endif + +byte Create_lock_file(const char *file_directory) +{ + char lock_filename[MAX_PATH_CHARACTERS]; + + strcpy(lock_filename,file_directory); + strcat(lock_filename,"gfx2.lck"); + + #ifdef __WIN32__ + // Windowzy method for creating a lock file + Lock_file_handle = CreateFile( + lock_filename, + GENERIC_WRITE, + 0, // No sharing + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (Lock_file_handle == INVALID_HANDLE_VALUE) + { + return -1; + } + #else + // Unixy method for lock file + Lock_file_handle = open(lock_filename,O_WRONLY|O_CREAT); + if (Lock_file_handle == -1) + { + // Usually write-protected media + return -1; + } + if (lockf(Lock_file_handle, F_TLOCK, 0)==-1) + { + close(Lock_file_handle); + // Usually write-protected media + return -1; + } + #endif + return 0; +} + +void Release_lock_file(const char *file_directory) +{ + char lock_filename[MAX_PATH_CHARACTERS]; + + #ifdef __WIN32__ + if (Lock_file_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(Lock_file_handle); + } + #else + if (Lock_file_handle != -1) + { + close(Lock_file_handle); + Lock_file_handle = -1; + } + #endif + + // Actual deletion + strcpy(lock_filename,file_directory); + strcat(lock_filename,"gfx2.lck"); + remove(lock_filename); +} \ No newline at end of file diff --git a/io.h b/io.h index 10937153..b0f0e571 100644 --- a/io.h +++ b/io.h @@ -94,3 +94,12 @@ void For_each_file(const char * directory_name, void Callback(const char *)); /// Creates a fully qualified name from a directory and filename. /// The point is simply to insert a PATH_SEPARATOR when needed. void Get_full_filename(char * output_name, char * file_name, char * directory_name); + +/// +/// Creates a lock file, to check if an other instance of Grafx2 is running. +/// @return 0 on success (first instance), -1 on failure (others are running) +byte Create_lock_file(const char *file_directory); + +/// +/// Release a lock file created by ::Create_Lock_file +void Release_lock_file(const char *file_directory); \ No newline at end of file diff --git a/loadsave.c b/loadsave.c index 2ced475d..e3509c0e 100644 --- a/loadsave.c +++ b/loadsave.c @@ -1162,6 +1162,7 @@ T_String_list *Backups_main = NULL; /// A list of files, used for scanning a directory T_String_list *Backups_spare = NULL; + // Settings for safety backup (frequency, numbers, etc) const int Rotation_safety_backup = 8; @@ -1187,9 +1188,9 @@ void Add_backup_file(const char *name) Extract_filename(file_name, name); // Check first character - if (file_name[0]=='a') + if (file_name[0]==Main_safety_backup_prefix) list = &Backups_main; - else if (file_name[0]=='b') + else if (file_name[0]==Spare_safety_backup_prefix) list = &Backups_spare; else { // Not a good file @@ -1291,13 +1292,22 @@ byte Process_backups(T_String_list **list) } +/// Global indicator that tells if the safety backup system is active +byte Safety_backup_active = 0; + /// /// Checks if there are any pending safety backups, and then opens them. -/// +/// @return 0 if no problem, -1 if the backup system cannot be activated, >=1 if some backups are restored int Check_recovery(void) { int restored_spare; int restored_main; + + // First check if can write backups + if (Create_lock_file(Config_directory)) + return -1; + + Safety_backup_active=1; Backups_main = NULL; Backups_spare = NULL; @@ -1327,13 +1337,7 @@ int Check_recovery(void) Compute_paintbrush_coordinates(); Redraw_layered_image(); } - /* - if (restored_main||restored_spare) - { - Display_all_screen(); - return 1; - }*/ - return restored_main || restored_spare; + return restored_main + restored_spare; } void Rotate_safety_backups(void) @@ -1343,6 +1347,9 @@ void Rotate_safety_backups(void) char file_name[12+1]; char deleted_file[MAX_PATH_CHARACTERS]; + if (!Safety_backup_active) + return; + now = SDL_GetTicks(); // It's time to save if either: // - Many edits have taken place @@ -1384,6 +1391,9 @@ void Delete_safety_backups(void) { T_String_list *element; + if (!Safety_backup_active) + return; + Backups_main = NULL; Backups_spare = NULL; @@ -1401,4 +1411,7 @@ void Delete_safety_backups(void) printf("Failed to delete %s\n",element->String); } + // Release lock file + Release_lock_file(Config_directory); + } diff --git a/main.c b/main.c index c7e8f28c..6f5a12df 100644 --- a/main.c +++ b/main.c @@ -760,54 +760,74 @@ int Init_program(int argc,char * argv[]) *Brush=MC_White; // Test de recuperation de fichiers sauvés - if (Check_recovery()) - { - // Some files were loaded from last crash-exit. - // Do not load files from command-line, nor show splash screen. - Compute_optimal_menu_colors(Main_palette); - Display_all_screen(); - Display_menu(); - Display_cursor(); - } - else + switch (Check_recovery()) { T_IO_Context context; - - switch (file_in_command_line) - { - case 0: - if (Config.Opening_message) - Button_Message_initial(); - break; - case 2: - // Load this file - Init_context_layered_image(&context, spare_filename, spare_directory); - Load_image(&context); - Destroy_context(&context); - End_of_modification(); - Redraw_layered_image(); - - Button_Page(); - // no break ! proceed with the other file now - case 1: - Init_context_layered_image(&context, main_filename, main_directory); - Load_image(&context); - Destroy_context(&context); - End_of_modification(); - Redraw_layered_image(); - - Hide_cursor(); - Compute_optimal_menu_colors(Main_palette); - Display_all_screen(); - Display_menu(); - Display_cursor(); - Resolution_in_command_line = 0; - break; + default: + // Some files were loaded from last crash-exit. + // Do not load files from command-line, nor show splash screen. + Compute_optimal_menu_colors(Main_palette); + Display_all_screen(); + Display_menu(); + Display_cursor(); + Verbose_message("Images recovered", + "Grafx2 has recovered images from\n" + "last session, before a crash or\n" + "shutdown. Browse the history using\n" + "the Undo/Redo button, and when\n" + "you find a state that you want to\n" + "save, use the 'Save as' button to\n" + "save the image.\n" + "Some backups can be present in\n" + "the spare page too.\n"); + break; - default: - break; - } + case -1: // Unable to write lock file + Verbose_message("Warning", + "Safety backups (every minute) are\n" + "disabled because Grafx2 is running\n" + "from a read-only device, or other\n" + "instances are running."); + break; + + case 0: + + switch (file_in_command_line) + { + case 0: + if (Config.Opening_message) + Button_Message_initial(); + break; + + case 2: + // Load this file + Init_context_layered_image(&context, spare_filename, spare_directory); + Load_image(&context); + Destroy_context(&context); + End_of_modification(); + Redraw_layered_image(); + + Button_Page(); + // no break ! proceed with the other file now + case 1: + Init_context_layered_image(&context, main_filename, main_directory); + Load_image(&context); + Destroy_context(&context); + End_of_modification(); + Redraw_layered_image(); + + Hide_cursor(); + Compute_optimal_menu_colors(Main_palette); + Display_all_screen(); + Display_menu(); + Display_cursor(); + Resolution_in_command_line = 0; + break; + + default: + break; + } } return(1); }