/* vim:expandtab:ts=2 sw=2: */ /* Grafx2 - The Ultimate 256-color bitmap paint program Copyright 2018 Thomas Bernard Copyright 2011 Pawel Góralski Copyright 2009 Pasi Kallinen Copyright 2008 Peter Gordon Copyright 2008 Franck Charlet Copyright 2007 Adrien Destugues Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud) Grafx2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. Grafx2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Grafx2; if not, see */ /** * @file main.c * Program entry point, global variables and global functions. * * @mainpage * GrafX2 is a bitmap paint program inspired by the Amiga programs * Deluxe Paint and Brilliance. Specialized in 256-color drawing, * it includes a very large number of tools and effects that make * it particularly suitable for pixel art, game graphics, * and generally any detailed graphics painted with a mouse. * * The program is mostly developed on Haiku, Linux, FreeBSD * and Windows, but is also portable on many other platforms : * It can be built using SDL 1.x or SDL 2.x libraries (see https://www.libsdl.org/) * or Xlib or Win32 API. * * Web for the project users is http://grafx2.tk/. * * Developpers are welcome to contribute : * the code is hosted on gitlab https://gitlab.com/GrafX2/grafX2 * and a bug tracker, wiki, etc. is available on * https://pulkomandy.tk/projects/GrafX2. * * This Doxygen documentation is browsable on * https://pulkomandy.tk/projects/GrafX2/doxygen/ (updated nightly). * * The inline help is also available here : * http://pulkomandy.tk/GrafX2/ */ /// declare global variables in main.c #define GLOBAL_VARIABLES // time.h defines timeval which conflicts with the one in amiga SDK #ifdef __amigaos__ #include #else #include #endif #include #include #include #include #ifndef _MSC_VER #include #else #if _MSC_VER < 1900 #define snprintf _snprintf #endif #endif #if defined(USE_SDL) || defined(USE_SDL2) #include #include #endif #if defined(WIN32) #include #include #elif defined (__MINT__) #include #elif defined(__macosx__) #import #import #elif defined(__FreeBSD__) #include #endif #if defined(__macosx__) #include #elif defined(__FreeBSD__) #include #elif !defined(WIN32) #include #endif #include "gfx2log.h" #include "const.h" #include "struct.h" #include "global.h" #include "graph.h" #include "misc.h" #include "init.h" #include "buttons.h" #include "engine.h" #include "pages.h" #include "loadsave.h" #include "screen.h" #include "errors.h" #include "readini.h" #include "saveini.h" #include "io.h" #include "text.h" #include "setup.h" #include "windows.h" #include "brush.h" #include "palette.h" #include "realpath.h" #include "input.h" #include "help.h" #include "filesel.h" #if defined(WIN32) && !(defined(USE_SDL) || defined(USE_SDL2)) #include "win32screen.h" #endif #if defined (WIN32) && (defined(USE_SDL) || defined(USE_SDL2)) // On Windows, SDL_putenv is not present in any compilable header. // It can be linked anyway, this declaration only avoids // a compilation warning. extern DECLSPEC int SDLCALL SDL_putenv(const char *variable); #endif extern char Program_version[]; // generated in pversion.c static int setsize_width; static int setsize_height; #if (defined(USE_SDL) || defined(USE_SDL2)) && defined(USE_JOYSTICK) /// Pointer to the current joystick controller. static SDL_Joystick* Joystick; #endif /** * Show the command line syntax and available video modes. * * Output to standard outout (stdout) and show a message box under MS Windows, * where standard output is not available */ void Display_syntax(void) { int mode_index, i; char modes[1024*2]; const char * syntax = "Syntax: grafx2 [] [] []\n\n" " can be:\n" "\t-? -h -H -help for this help screen\n" "\t-verbose to increase log verbosity\n" "\t-wide to emulate a video mode with wide pixels (2x1)\n" "\t-tall to emulate a video mode with tall pixels (1x2)\n" "\t-double to emulate a video mode with double pixels (2x2)\n" "\t-wide2 to emulate a video mode with double wide pixels (4x2)\n" "\t-tall2 to emulate a video mode with double tall pixels (2x4)\n" "\t-triple to emulate a video mode with triple pixels (3x3)\n" "\t-quadruple to emulate a video mode with quadruple pixels (4x4)\n" "\t-rgb n to reduce RGB precision (2 to 256, 256=max precision)\n" "\t-gamma n to adjust Gamma correction (1 to 30, 10=no correction)\n" "\t-skin to use an alternate file with the menu graphics\n" "\t-mode to set a video mode\n" "\t-size to set the image size\n" "Arguments can be prefixed either by / - or --\n" "They can also be abbreviated.\n\n"; fputs(syntax, stdout); i = snprintf(modes, sizeof(modes), "Available video modes:\n\n"); for (mode_index = 0; mode_index < Nb_video_modes; mode_index += 6) { int k; for (k = 0; k < 6; k++) { if (mode_index + k >= Nb_video_modes) break; i += snprintf(modes + i, sizeof(modes) - i, "%12s", Mode_label(mode_index + k)); } i += snprintf(modes + i, sizeof(modes) - i, "\n"); } fputs(modes, stdout); #if defined(WIN32) MessageBoxA(GFX2_Get_Window_Handle(), syntax, "GrafX2", MB_OK); MessageBoxA(GFX2_Get_Window_Handle(), modes, "GrafX2", MB_OK); #endif } // ---------------------------- Sortie impromptue ---------------------------- void Warning_function(const char *message, const char *filename, int line_number, const char *function_name) { GFX2_Log(GFX2_WARNING, "Warning in file %s, line %d, function %s : %s\n", filename, line_number, function_name, message); } // ---------------------------- Sortie impromptue ---------------------------- void Error_function(int error_code, const char *filename, int line_number, const char *function_name) { T_Palette temp_palette; T_Palette backup_palette; int index; char msg_buffer[512]; snprintf(msg_buffer, sizeof(msg_buffer), "Error number %d occurred in file %s, line %d, function %s.\n", error_code, filename,line_number,function_name); fputs(msg_buffer, stderr); #if defined(_MSC_VER) && defined(_DEBUG) OutputDebugStringA(msg_buffer); #endif if (error_code==0) { // L'erreur 0 n'est pas une vraie erreur, elle fait seulement un flash rouge de l'écran pour dire qu'il y a un problème. // Toutes les autres erreurs déclenchent toujours une sortie en catastrophe du programme ! memcpy(backup_palette, Get_current_palette(), sizeof(T_Palette)); memcpy(temp_palette, backup_palette, sizeof(T_Palette)); for (index=0;index<=255;index++) temp_palette[index].R=255; Set_palette(temp_palette); Delay_with_active_mouse(50); // Half a second of red flash Set_palette(backup_palette); } else { const char * msg = NULL; switch (error_code) { case ERROR_GUI_MISSING : snprintf(msg_buffer, sizeof(msg_buffer), "Error: File containing the GUI graphics is missing!\n" "This program cannot run without this file.\n" "\n%s", Gui_loading_error_message); msg = msg_buffer; break; case ERROR_GUI_CORRUPTED : msg = "Error: File containing the GUI graphics couldn't be parsed!\n" "This program cannot run without a correct version of this file.\n"; break; case ERROR_INI_MISSING : msg = "Error: File gfx2def.ini is missing!\n" "This program cannot run without this file.\n"; break; case ERROR_MEMORY : msg = "Error: Not enough memory!\n\n" "You should try exiting other programs to free some bytes for Grafx2.\n\n"; break; case ERROR_FORBIDDEN_MODE : msg = "Error: The requested video mode has been disabled from the resolution menu!\n" "If you want to run the program in this mode, you'll have to start it with an\n" "enabled mode, then enter the resolution menu and enable the mode you want.\n" "Check also if the 'Default_video_mode' parameter in gfx2.ini is correct.\n"; break; case ERROR_FORBIDDEN_SIZE : msg = "Error: The image dimensions all have to be in the range 1-9999!\n"; break; case ERROR_COMMAND_LINE : msg = "Error: Invalid parameter or file not found.\n\n"; break; case ERROR_SAVING_CFG : msg = "Error: Write error while saving settings!\n" "Settings have not been saved correctly, and the gfx2.cfg file may have been\n" "corrupt. If so, please delete it and Grafx2 will restore default settings.\n"; break; case ERROR_MISSING_DIRECTORY : msg = "Error: Directory you ran the program from not found!\n"; break; case ERROR_INI_CORRUPTED : snprintf(msg_buffer, sizeof(msg_buffer), "Error: File gfx2.ini is corrupt!\n" "It contains bad values at line %d.\n" "You can re-generate it by deleting the file and running GrafX2 again.\n", Line_number_in_INI_file); msg = msg_buffer; break; case ERROR_SAVING_INI : msg = "Error: Cannot rewrite file gfx2.ini!\n"; break; case ERROR_SORRY_SORRY_SORRY : msg = "Error: Sorry! Sorry! Sorry! Please forgive me!\n"; break; } if(msg != NULL) { fputs(msg, stderr); #if defined(WIN32) #if defined(_DEBUG) OutputDebugStringA(msg); #endif MessageBoxA(GFX2_Get_Window_Handle(), msg, "GrafX2 error", MB_OK | MB_ICONERROR); #endif } if (error_code == ERROR_COMMAND_LINE) Display_syntax(); #if defined(USE_SDL) || defined(USE_SDL2) SDL_Quit(); #endif exit(error_code); } } enum CMD_PARAMS { CMDPARAM_HELP, CMDPARAM_MODE, CMDPARAM_PIXELRATIO_TALL, CMDPARAM_PIXELRATIO_WIDE, CMDPARAM_PIXELRATIO_DOUBLE, CMDPARAM_PIXELRATIO_TRIPLE, CMDPARAM_PIXELRATIO_QUAD, CMDPARAM_PIXELRATIO_TALL2, CMDPARAM_PIXELRATIO_TALL3, CMDPARAM_PIXELRATIO_WIDE2, CMDPARAM_RGB, CMDPARAM_GAMMA, CMDPARAM_SKIN, CMDPARAM_SIZE, CMDPARAM_VERBOSE, }; struct { const char *param; int id; } cmdparams[] = { {"?", CMDPARAM_HELP}, {"h", CMDPARAM_HELP}, {"H", CMDPARAM_HELP}, {"help", CMDPARAM_HELP}, {"mode", CMDPARAM_MODE}, {"tall", CMDPARAM_PIXELRATIO_TALL}, {"wide", CMDPARAM_PIXELRATIO_WIDE}, {"double", CMDPARAM_PIXELRATIO_DOUBLE}, {"triple", CMDPARAM_PIXELRATIO_TRIPLE}, {"quadruple", CMDPARAM_PIXELRATIO_QUAD}, {"tall2", CMDPARAM_PIXELRATIO_TALL2}, {"tall3", CMDPARAM_PIXELRATIO_TALL3}, {"wide2", CMDPARAM_PIXELRATIO_WIDE2}, {"rgb", CMDPARAM_RGB}, {"gamma", CMDPARAM_GAMMA}, {"skin", CMDPARAM_SKIN}, {"size", CMDPARAM_SIZE}, {"verbose", CMDPARAM_VERBOSE}, }; #define ARRAY_SIZE(x) (int)(sizeof(x) / sizeof(x[0])) /** * Parse the command line. * * @param argc argument count * @param argv argument values * @param main_filename pointer to receive 1st file name * @param main_directory pointer to receive 1st file directory * @param spare_filename pointer to receive 2nd file name * @param spare_directory pointer to receive 2nd file directory * @return the number of file to open (0, 1 or 2) */ int Analyze_command_line(int argc, char * argv[], char *main_filename, char *main_directory, char *spare_filename, char *spare_directory) { char *buffer ; int index; int file_in_command_line; file_in_command_line = 0; Resolution_in_command_line = 0; Current_resolution = Config.Default_resolution; for (index = 1; index 256) { Error(ERROR_COMMAND_LINE); exit(0); } Set_palette_RGB_scale(scale); } else { Error(ERROR_COMMAND_LINE); exit(0); } break; case CMDPARAM_GAMMA: /* Gamma correction */ index++; if (index 30) { Error(ERROR_COMMAND_LINE); exit(0); } Set_palette_Gamma(scale); } else { Error(ERROR_COMMAND_LINE); exit(0); } break; case CMDPARAM_SKIN: // GUI skin file index++; if (index 9999 || setsize_width < 1 || setsize_width > 9999) { Error(ERROR_FORBIDDEN_SIZE); exit(0); } } else { Error(ERROR_COMMAND_LINE); exit(0); } break; case CMDPARAM_VERBOSE: GFX2_verbosity_level++; break; default: // Si ce n'est pas un paramètre, c'est le nom du fichier à ouvrir if (file_in_command_line > 1) { // Il y a déjà 2 noms de fichiers et on vient d'en trouver un 3ème Error(ERROR_COMMAND_LINE); exit(0); } else if (File_exists(argv[index])) { file_in_command_line ++; buffer = Realpath(argv[index], NULL); if (file_in_command_line == 1) { // Separate path from filename Extract_path(main_directory, buffer); Extract_filename(main_filename, buffer); } else { // Separate path from filename Extract_path(spare_directory, buffer); Extract_filename(spare_filename, buffer); } free(buffer); buffer = NULL; } else { Error(ERROR_COMMAND_LINE); exit(0); } break; } } return file_in_command_line; } // Compile-time assertions: #define CT_ASSERT(e) extern char (*ct_assert(void)) [sizeof(char[1 - 2*!(e)])] // This line will raise an error at compile time // when sizeof(T_Components) is not 3. CT_ASSERT(sizeof(T_Components)==3); // This line will raise an error at compile time // when sizeof(T_Palette) is not 768. CT_ASSERT(sizeof(T_Palette)==768); #if defined(__MINT__) static void Exit_handler(void) { printf("Press any key to quit.\n"); (void)Cnecin(); } #endif /** * Initialize the program. * * @param argc command line argument count * @param argv command line argument values * @return 0 on fail * @return 1 on success */ int Init_program(int argc,char * argv[]) { int temp; int starting_videomode; enum IMAGE_MODES starting_image_mode; static char program_directory[MAX_PATH_CHARACTERS]; T_Gui_skin *gfx; int file_in_command_line; T_Gradient_array initial_gradients; static char main_filename [MAX_PATH_CHARACTERS]; static char main_directory[MAX_PATH_CHARACTERS]; static char spare_filename [MAX_PATH_CHARACTERS]; static char spare_directory[MAX_PATH_CHARACTERS]; static word filename_unicode[MAX_PATH_CHARACTERS]; #if defined(__MINT__) printf("===============================\n"); printf(" /|\\ GrafX2 %.19s\n", Program_version); printf(" compilation date: %.16s\n", __DATE__); printf("===============================\n"); atexit(Exit_handler); #endif #ifdef ENABLE_FILENAMES_ICONV // iconv is used to convert filenames cd = iconv_open(TOCODE, FROMCODE); // From UTF8 to ANSI cd_inv = iconv_open(FROMCODE, TOCODE); // From ANSI to UTF8 #if (defined(SDL_BYTEORDER) && (SDL_BYTEORDER == SDL_BIG_ENDIAN)) || (defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)) cd_utf16 = iconv_open("UTF-16BE", FROMCODE); // From UTF8 to UTF16 cd_utf16_inv = iconv_open(FROMCODE, "UTF-16BE"); // From UTF16 to UTF8 #else cd_utf16 = iconv_open("UTF-16LE", FROMCODE); // From UTF8 to UTF16 cd_utf16_inv = iconv_open(FROMCODE, "UTF-16LE"); // From UTF16 to UTF8 #endif #endif /* ENABLE_FILENAMES_ICONV */ // On crée dès maintenant les descripteurs des listes de pages pour la page // principale et la page de brouillon afin que leurs champs ne soient pas // invalide lors des appels aux multiples fonctions manipulées à // l'initialisation du programme. Main.backups=(T_List_of_pages *)malloc(sizeof(T_List_of_pages)); Spare.backups=(T_List_of_pages *)malloc(sizeof(T_List_of_pages)); Init_list_of_pages(Main.backups); Init_list_of_pages(Spare.backups); // Determine the executable directory Set_program_directory(argv[0],program_directory); // Choose directory for data (read only) Set_data_directory(program_directory,Data_directory); // Choose directory for settings (read/write) Set_config_directory(program_directory,Config_directory); // On détermine le répertoire courant: Get_current_directory(Main.selector.Directory,Main.selector.Directory_unicode,MAX_PATH_CHARACTERS); // On en profite pour le mémoriser dans le répertoire principal: strcpy(Initial_directory,Main.selector.Directory); // On initialise les données sur le nom de fichier de l'image de brouillon: strcpy(Spare.selector.Directory,Main.selector.Directory); Main.fileformat=DEFAULT_FILEFORMAT; Spare.fileformat =DEFAULT_FILEFORMAT; strcpy(Brush_selector.Directory,Main.selector.Directory); strcpy(Brush_file_directory,Main.selector.Directory); strcpy(Brush_filename ,"NO_NAME.GIF"); Brush_filename_unicode[0] = 0; Brush_fileformat =DEFAULT_FILEFORMAT; strcpy(Palette_selector.Directory,Main.selector.Directory); // On initialise ce qu'il faut pour que les fileselects ne plantent pas: Main.selector.Position=0; // Au début, le fileselect est en haut de la liste des fichiers Main.selector.Offset=0; // Au début, le fileselect est en haut de la liste des fichiers Main.selector.Format_filter=FORMAT_ALL_IMAGES; Main.current_layer=0; Main.layers_visible=0xFFFFFFFF; Main.layers_visible_backup=0xFFFFFFFF; Spare.current_layer=0; Spare.layers_visible=0xFFFFFFFF; Spare.layers_visible_backup=0xFFFFFFFF; Spare.selector.Position=0; Spare.selector.Offset=0; Spare.selector.Format_filter=FORMAT_ALL_IMAGES; Brush_selector.Position=0; Brush_selector.Offset=0; Brush_selector.Format_filter=FORMAT_ALL_IMAGES; Palette_selector.Position=0; Palette_selector.Offset=0; Palette_selector.Format_filter=FORMAT_ALL_PALETTES; // On initialise d'ot' trucs Main.offset_X=0; Main.offset_Y=0; Main.separator_position=0; Main.X_zoom=0; Main.separator_proportion=INITIAL_SEPARATOR_PROPORTION; Main.magnifier_mode=0; Main.magnifier_factor=DEFAULT_ZOOM_FACTOR; Main.magnifier_height=0; Main.magnifier_width=0; Main.magnifier_offset_X=0; Main.magnifier_offset_Y=0; Spare.offset_X=0; Spare.offset_Y=0; Spare.separator_position=0; Spare.X_zoom=0; Spare.separator_proportion=INITIAL_SEPARATOR_PROPORTION; Spare.magnifier_mode=0; Spare.magnifier_factor=DEFAULT_ZOOM_FACTOR; Spare.magnifier_height=0; Spare.magnifier_width=0; Spare.magnifier_offset_X=0; Spare.magnifier_offset_Y=0; Keyboard_click_allowed = 1; Main.safety_backup_prefix = SAFETYBACKUP_PREFIX_A[0]; Spare.safety_backup_prefix = SAFETYBACKUP_PREFIX_B[0]; Main.time_of_safety_backup = 0; Spare.time_of_safety_backup = 0; #if defined(USE_SDL) || defined(USE_SDL2) // SDL if (SDL_Init(SDL_INIT_VIDEO #if defined(USE_JOYSTICK) | SDL_INIT_JOYSTICK #endif ) < 0) { // The program can't continue without that anyway printf("Couldn't initialize SDL.\n"); return(0); } #if defined(USE_SDL2) SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE); #endif #endif #if defined(USE_SDL) SDL_EnableKeyRepeat(250, 32); SDL_EnableUNICODE(SDL_ENABLE); SDL_WM_SetCaption("GrafX2","GrafX2"); #endif Define_icon(); // Texte Init_text(); // Initialize all video modes Set_all_video_modes(); // Analyse command-line as soon as possible. // This must come after video mode initialization because // a video mode may be requested as a command-line parameter file_in_command_line=Analyze_command_line(argc, argv, main_filename, main_directory, spare_filename, spare_directory); #if defined(USE_JOYSTICK) && (defined(USE_SDL) || defined(USE_SDL2)) GFX2_Log(GFX2_DEBUG, "%d joystick(s) attached\n", SDL_NumJoysticks()); if (SDL_NumJoysticks() > 0) { Joystick = SDL_JoystickOpen(0); if (Joystick == NULL) { GFX2_Log(GFX2_ERROR, "Failed to open joystick #0 : %s\n", SDL_GetError()); } else { GFX2_Log(GFX2_DEBUG, "Joystick #0 open : \"%s\" %d axes, %d buttons, %d balls, %d hats\n", SDL_JoystickName(Joystick), SDL_JoystickNumAxes(Joystick), SDL_JoystickNumButtons(Joystick), SDL_JoystickNumBalls(Joystick), SDL_JoystickNumHats(Joystick)); SDL_JoystickEventState(SDL_ENABLE); } } #endif Pixel_ratio=PIXEL_SIMPLE; // On initialise les données sur l'état du programme: // Donnée sur la sortie du programme: Quit_is_required=0; Quitting=0; // Données sur l'état du menu: Menu_is_visible=1; // Données sur les couleurs et la palette: First_color_in_palette=0; // Données sur le curseur: Cursor_shape=CURSOR_SHAPE_TARGET; Cursor_hidden=0; // Données sur le pinceau: Paintbrush_X=0; Paintbrush_Y=0; Paintbrush_hidden=0; // On initialise tout ce qui concerne les opérations et les effets Operation_stack_size=0; Selected_freehand_mode=OPERATION_CONTINUOUS_DRAW; Selected_line_mode =OPERATION_LINE; Selected_curve_mode =OPERATION_3_POINTS_CURVE; Effect_function=No_effect; // On initialise les infos de la loupe: Main.magnifier_mode=0; Main.magnifier_factor=DEFAULT_ZOOM_FACTOR; Main.separator_proportion=INITIAL_SEPARATOR_PROPORTION; Spare.separator_proportion=INITIAL_SEPARATOR_PROPORTION; // On initialise les infos du mode smear: Smear_mode=0; Smear_brush_width=PAINTBRUSH_WIDTH; Smear_brush_height=PAINTBRUSH_HEIGHT; // On initialise les infos du mode smooth: Smooth_mode=0; // On initialise les infos du mode shade: Shade_mode=0; // Les autres infos du Shade sont chargées avec la config Quick_shade_mode=0; // idem // On initialise les infos sur les dégradés: Gradient_pixel =Display_pixel; // Les autres infos sont chargées avec la config // On initialise les infos de la grille: Snap_mode=0; Snap_width=8; Snap_height=8; Snap_offset_X=0; Snap_offset_Y=0; // On initialise les infos du mode Colorize: Colorize_mode=0; // Mode colorize inactif par défaut Colorize_opacity=50; // Une interpolation de 50% par défaut Colorize_current_mode=0; // Par défaut, la méthode par interpolation Compute_colorize_table(); // On initialise les infos du mode Tiling: Tiling_mode=0; // Pas besoin d'initialiser les décalages car ça se fait // en prenant une brosse (toujours mis à 0). // On initialise les infos du mode Mask: Mask_mode=0; // Infos du Spray Airbrush_mode=1; // Mode Mono Airbrush_size=31; Airbrush_delay=1; Airbrush_mono_flow=10; memset(Airbrush_multi_flow,0,256); srand(time(NULL)); // On randomize un peu tout ça... // Initialisation des boutons Init_buttons(); // Initialisation des opérations Init_operations(); // Initialize the brush container Init_brush_container(); Windows_open=0; // Paintbrush if (!(Paintbrush_sprite=(byte *)malloc(MAX_PAINTBRUSH_SIZE*MAX_PAINTBRUSH_SIZE))) Error(ERROR_MEMORY); // Load preset paintbrushes (uses Paintbrush_ variables) Init_paintbrushes(); // Set a valid paintbrush afterwards *Paintbrush_sprite=1; Paintbrush_width=1; Paintbrush_height=1; Paintbrush_offset_X=0; Paintbrush_offset_Y=0; Paintbrush_shape=PAINTBRUSH_SHAPE_ROUND; #if defined(__GP2X__) || defined(__WIZ__) || defined(__CAANOO__) // Prefer cycling active by default Cycling_mode=1; #endif // Charger la configuration des touches Set_config_defaults(); switch(Load_CFG(1)) { case ERROR_CFG_MISSING: // Pas un problème, on a les valeurs par défaut. break; case ERROR_CFG_CORRUPTED: GFX2_Log(GFX2_ERROR, "Corrupted CFG file.\n"); break; case ERROR_CFG_OLD: GFX2_Log(GFX2_WARNING, "Unknown CFG file version, not loaded.\n"); break; } // Charger la configuration du .INI temp=Load_INI(&Config); if (temp) Error(temp); if(!Config.Allow_multi_shortcuts) { Remove_duplicate_shortcuts(); } Compute_menu_offsets(); Current_help_section=0; Help_position=0; // Load sprites, palette etc. gfx = Load_graphics(Config.Skin_file, &initial_gradients); if (gfx == NULL) { gfx = Load_graphics(DEFAULT_SKIN_FILENAME, &initial_gradients); if (gfx == NULL) { Error(ERROR_GUI_MISSING); } } Set_current_skin(Config.Skin_file, gfx); // Override colors Gfx->Default_palette[MC_Black]=Config.Fav_menu_colors[0]; Gfx->Default_palette[MC_Dark] =Config.Fav_menu_colors[1]; Gfx->Default_palette[MC_Light]=Config.Fav_menu_colors[2]; Gfx->Default_palette[MC_White]=Config.Fav_menu_colors[3]; // Even when using the skin's palette, if RGB range is small // the colors will be unusable. Compute_optimal_menu_colors(Gfx->Default_palette); // Infos sur les trames (Sieve) Sieve_mode=0; Copy_preset_sieve(0); // Font if (!(Menu_font=Load_font(Config.Font_file, 1))) if (!(Menu_font=Load_font(DEFAULT_FONT_FILENAME, 1))) { snprintf(Gui_loading_error_message, sizeof(Gui_loading_error_message), "Unable to open the default font file: %s\n", DEFAULT_FONT_FILENAME); Error(ERROR_GUI_MISSING); } Load_Unicode_fonts(); memcpy(Main.palette, Gfx->Default_palette, sizeof(T_Palette)); Fore_color=Best_color_range(255,255,255,Config.Palette_cells_X*Config.Palette_cells_Y); Back_color=Best_color_range(0,0,0,Config.Palette_cells_X*Config.Palette_cells_Y); // Allocation de mémoire pour la brosse if (!(Brush =(byte *)malloc( 1* 1))) Error(ERROR_MEMORY); if (!(Smear_brush =(byte *)malloc(MAX_PAINTBRUSH_SIZE*MAX_PAINTBRUSH_SIZE))) Error(ERROR_MEMORY); starting_videomode=Current_resolution; Horizontal_line_buffer=NULL; Screen_width=Screen_height=Current_resolution=0; Init_mode_video( Video_mode[starting_videomode].Width, Video_mode[starting_videomode].Height, Video_mode[starting_videomode].Fullscreen, Pixel_ratio); // Windows only: move back the window to its original position. #if defined(WIN32) if (!Video_mode[starting_videomode].Fullscreen) { if (Config.Window_pos_x != 9999 && Config.Window_pos_y != 9999) { //RECT r; #if defined(USE_SDL) || defined(USE_SDL2) //GetWindowRect(window, &r); SetWindowPos(GFX2_Get_Window_Handle(), 0, Config.Window_pos_x, Config.Window_pos_y, 0, 0, SWP_NOSIZE); #endif } } // Open a console for debugging... //ActivateConsole(); #endif Main.image_width=Screen_width/Pixel_width; Main.image_height=Screen_height/Pixel_height; Spare.image_width=Screen_width/Pixel_width; Spare.image_height=Screen_height/Pixel_height; // set the Mouse cursor at the center of the screen Mouse_X = Screen_width / 2; Mouse_Y = Screen_height / 2; starting_image_mode = Config.Default_mode_layers ? IMAGE_MODE_LAYERED : IMAGE_MODE_ANIMATION; // Allocation de mémoire pour les différents écrans virtuels (et brosse) if (Init_all_backup_lists(starting_image_mode , Screen_width, Screen_height)==0) Error(ERROR_MEMORY); // Update toolbars' visibility, now that the current image has a mode Check_menu_mode(); // Nettoyage de l'écran virtuel (les autres recevront celui-ci par copie) memset(Main_screen,0,Main.image_width*Main.image_height); // If image size was specified on command line, set that now if (setsize_width != 0 && setsize_height != 0) { Main.image_width=setsize_width; Main.image_height=setsize_height; Spare.image_width=setsize_width; Spare.image_height=setsize_height; } // Now that the backup system is there, we can store the gradients. memcpy(Main.backups->Pages->Gradients->Range, initial_gradients.Range, sizeof(initial_gradients.Range)); memcpy(Spare.backups->Pages->Gradients->Range, initial_gradients.Range, sizeof(initial_gradients.Range)); Gradient_function=Gradient_basic; Gradient_lower_bound=0; Gradient_upper_bound=0; Gradient_random_factor=1; Gradient_bounds_range=1; Current_gradient=0; // Initialisation de diverses variables par calcul: Compute_magnifier_data(); Compute_limits(); Compute_paintbrush_coordinates(); // On affiche le menu: Display_paintbrush_in_menu(); Display_sprite_in_menu(BUTTON_PAL_LEFT,Config.Palette_vertical?MENU_SPRITE_VERTICAL_PALETTE_SCROLL:-1); Display_menu(); Draw_menu_button(BUTTON_PAL_LEFT,BUTTON_RELEASED); Draw_menu_button(BUTTON_PAL_RIGHT,BUTTON_RELEASED); // On affiche le curseur pour débuter correctement l'état du programme: Display_cursor(); Spare.image_is_modified=0; Main.image_is_modified=0; // Gestionnaire de signaux, quand il ne reste plus aucun espoir Init_sighandler(); // Le programme débute en mode de dessin à la main Select_button(BUTTON_DRAW,LEFT_SIDE); // On initialise la brosse initiale à 1 pixel blanc: Brush_width=1; Brush_height=1; for (temp=0;temp<256;temp++) Brush_colormap[temp]=temp; Capture_brush(0,0,0,0,0); *Brush=MC_White; *Brush_original_pixels=MC_White; // Make sure the load dialog points to the right place when first shown. // Done after loading everything else, but before checking for emergency // backups if (file_in_command_line > 0) { strcpy(Main.selector.Directory, main_directory); } // Test de recuperation de fichiers sauvés switch (Check_recovery()) { T_IO_Context context; 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); Check_menu_mode(); 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; 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(); // Load default palette { FILE * f; Init_context_layered_image(&context, DEFAULTPAL_FILENAME, Config_directory); context.Type = CONTEXT_PALETTE; context.Format = FORMAT_PAL; f = Open_file_read(&context); if (f != NULL) // silently fail if the file cannot be open { fclose(f); Load_image(&context); if (File_error == 0) { Hide_cursor(); Compute_optimal_menu_colors(Main.palette); Display_menu(); Display_cursor(); memcpy(Spare.palette, Main.palette, sizeof(T_Palette)); } } Destroy_context(&context); } break; case 2: // Load this file Init_context_layered_image(&context, spare_filename, spare_directory); if (Get_Unicode_Filename(filename_unicode, spare_filename, spare_directory)) context.File_name_unicode = filename_unicode; Load_image(&context); Destroy_context(&context); Redraw_layered_image(); End_of_modification(); Button_Page(BUTTON_PAGE); // no break ! proceed with the other file now #if defined(__GNUC__) && (__GNUC__ >= 7) __attribute__ ((fallthrough)); #endif case 1: Init_context_layered_image(&context, main_filename, main_directory); if (Get_Unicode_Filename(filename_unicode, main_filename, main_directory)) context.File_name_unicode = filename_unicode; Load_image(&context); Destroy_context(&context); Redraw_layered_image(); End_of_modification(); // If only one image was loaded, assume the spare has same image type if (file_in_command_line==1) { if (Main.backups->Pages->Image_mode <= IMAGE_MODE_ANIMATION) Spare.backups->Pages->Image_mode = Main.backups->Pages->Image_mode; } Hide_cursor(); Compute_optimal_menu_colors(Main.palette); Back_color=Main.backups->Pages->Background_transparent ? Main.backups->Pages->Transparent_color : Best_color_range(0,0,0,Config.Palette_cells_X*Config.Palette_cells_Y); Fore_color=Main.palette[Back_color].R+Main.palette[Back_color].G+Main.palette[Back_color].B < 3*127 ? Best_color_range(255,255,255,Config.Palette_cells_X*Config.Palette_cells_Y) : Best_color_range(0,0,0,Config.Palette_cells_X*Config.Palette_cells_Y); Check_menu_mode(); Display_all_screen(); Display_menu(); Display_cursor(); Resolution_in_command_line = 0; break; default: break; } } Allow_drag_and_drop(1); return(1); } #define FREE_POINTER(p) free(p); p = NULL //!< Make free the memory and make sure the pointer is set to NULL /** * Program Shutdown. * * Free all allocated resources. */ void Program_shutdown(void) { int i; int return_code; // Windows only: Recover the window position. #if defined(WIN32) #if defined(USE_SDL) || defined(USE_SDL2) { RECT r; GetWindowRect(GFX2_Get_Window_Handle(), &r); Config.Window_pos_x = r.left; Config.Window_pos_y = r.top; } #endif // Config.Window_pos_x / Config.Window_pos_y are set in win32screen.c #elif !defined(USE_X11) // All other targets: irrelevant dimensions. // Do not attempt to force them back on next program run. Config.Window_pos_x = 9999; Config.Window_pos_y = 9999; #endif // Remove the safety backups, this is normal exit Delete_safety_backups(); // On libère le buffer de gestion de lignes free(Horizontal_line_buffer); Horizontal_line_buffer = NULL; // On libère le pinceau spécial free(Paintbrush_sprite); Paintbrush_sprite = NULL; // Free Brushes FREE_POINTER(Brush); FREE_POINTER(Smear_brush); FREE_POINTER(Brush_original_pixels); // Free all images Set_number_of_backups(-1); // even delete the main page FREE_POINTER(Main.visible_image.Image); FREE_POINTER(Spare.visible_image.Image); FREE_POINTER(Main_visible_image_backup.Image); FREE_POINTER(Main_visible_image_depth_buffer.Image); FREE_POINTER(Main.backups); FREE_POINTER(Spare.backups); // Free the skin (Gui graphics) data free(Gfx); Gfx=NULL; free(Menu_font); Menu_font = NULL; while (Unicode_fonts != NULL) { T_Unicode_Font * ufont = Unicode_fonts->Next; free(Unicode_fonts->FontData); free(Unicode_fonts); Unicode_fonts = ufont; } // On prend bien soin de passer dans le répertoire initial: if (Change_directory(Initial_directory)==0) { // On sauvegarde les données dans le .CFG et dans le .INI if (Config.Auto_save) { return_code=Save_CFG(); if (return_code) Error(return_code); return_code=Save_INI(&Config); if (return_code) Error(return_code); } } else Error(ERROR_MISSING_DIRECTORY); // Free Config FREE_POINTER(Config.Skin_file); FREE_POINTER(Config.Font_file); for (i=0;i 0) { for (k = 0; ShortFileName[k] != 0; k++) arg_buffer[i++] = ShortFileName[k]; } else { for (k = 0; TmpArg[k] != 0; k++) arg_buffer[i++] = TmpArg[k]; } arg_buffer[i++] = 0; k = 0; continue; } } TmpArg[k++] = pCmdLine[j]; } TmpArg[k] = '\0'; if (k > 0) { argv[argc++] = arg_buffer + i; if (GetShortPathName(TmpArg, ShortFileName, MAX_PATH) > 0) { for (k = 0; ShortFileName[k] != 0; k++) arg_buffer[i++] = ShortFileName[k]; } else { for (k = 0; TmpArg[k] != 0; k++) arg_buffer[i++] = TmpArg[k]; } arg_buffer[i++] = 0; } // TODO : nCmdShow indicates if the window must be maximized, etc. #endif if(!Init_program(argc,argv)) { Program_shutdown(); return 0; } #ifdef _MSC_VER GFX2_Log(GFX2_DEBUG, "built with _MSC_VER=%d Windows ANSI Code Page=%u\n", _MSC_VER, GetACP()); #endif Main_handler(); Program_shutdown(); return 0; } #if defined(WIN32) && !defined(USE_SDL) && !defined(USE_SDL2) && !defined(_MSC_VER) /** * MS Window entry point. * * This function is used when building with MinGW */ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR _lpCmdLine, int nCmdShow) { WCHAR *lpCmdLine = GetCommandLineW(); if (__argc == 1) { // avoids GetCommandLineW bug that does not always quote the program name if no arguments do { ++lpCmdLine; } while (*lpCmdLine); } else { BOOL quoted = lpCmdLine[0] == L'"'; ++lpCmdLine; // skips the " or the first letter (all paths are at least 1 letter) while (*lpCmdLine) { if (quoted && lpCmdLine[0] == L'"') quoted = FALSE; // found end quote else if (!quoted && lpCmdLine[0] == L' ') { // found an unquoted space, now skip all spaces do { ++lpCmdLine; } while (lpCmdLine[0] == L' '); break; } ++lpCmdLine; } } return wWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } #endif