ref: ba9a38b8266fd42f2bc327b7e6e60866812cbb92
dir: /pt_pal_editor/src/main.c/
// for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER #include <crtdbg.h> #endif #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <math.h> #include <sys/stat.h> #ifdef _WIN32 #define WIN32_MEAN_AND_LEAN #include <windows.h> #else #include <unistd.h> #endif #include <SDL2/SDL.h> #include "config.h" #include "palette.h" #include "gui.h" #include "hpc.h" #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) #ifdef _MSC_VER #pragma warning(disable: 4204) #pragma warning(disable: 4221) #endif static hpc_t vblankHpc; static SDL_Window *window; static SDL_Renderer *renderer; static SDL_Texture *texture; // globals volatile bool programRunning, redrawScreen, allowSigterm = true; uint32_t *frameBuffer; keyb_t keyb; mouse_t mouse; static bool setupVideo(void); static void readMouseXY(void); static void handleInput(void); #ifdef __APPLE__ static void osxSetDirToProgramDirFromArgs(char **argv); #endif int main(int argc, char *argv[]) { #if defined _WIN32 || defined __APPLE__ SDL_version sdlVer; #endif // for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif (void)argc; (void)argv; SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); SDL_EnableScreenSaver(); // allow screensaver to activate // on Windows and macOS, test what version SDL2.DLL is (against library version used in compilation) #if defined _WIN32 || defined __APPLE__ SDL_GetVersion(&sdlVer); if (sdlVer.major != SDL_MAJOR_VERSION || sdlVer.minor != SDL_MINOR_VERSION || sdlVer.patch != SDL_PATCHLEVEL) { #ifdef _WIN32 showErrorMsgBox("SDL2.dll is not the expected version, the program will terminate.\n\n" \ "Loaded dll version: %d.%d.%d\n" \ "Required (compiled with) version: %d.%d.%d", sdlVer.major, sdlVer.minor, sdlVer.patch, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL); #else showErrorMsgBox("The loaded SDL2 library is not the expected version, the program will terminate.\n\n" \ "Loaded library version: %d.%d.%d\n" \ "Required (compiled with) version: %d.%d.%d", sdlVer.major, sdlVer.minor, sdlVer.patch, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL); #endif return 0; } #endif #ifdef _WIN32 #ifndef _MSC_VER SetProcessDPIAware(); #endif #endif #ifdef __APPLE__ osxSetDirToProgramDirFromArgs(argv); #endif if (!setupVideo()) { SDL_Quit(); return 0; } SDL_StopTextInput(); hpc_Init(); hpc_SetDurationInHz(&vblankHpc, 60); setupGUI(); redrawScreen = true; programRunning = true; hpc_ResetCounters(&vblankHpc); // this must be the last thing we do before entering the main loop while (programRunning) { readMouseXY(); handleInput(); if (redrawScreen) { redrawScreen = false; // redraw screen SDL_UpdateTexture(texture, NULL, frameBuffer, SCREEN_W * sizeof (int32_t)); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); } hpc_Wait(&vblankHpc); } if (loadedFile != NULL) free(loadedFile); free(frameBuffer); SDL_Quit(); return 0; } static void readMouseXY(void) { int32_t windowX, windowY, mx, my; SDL_GetGlobalMouseState(&mx, &my); SDL_GetWindowPosition(window, &windowX, &windowY); mx -= windowX; my -= windowY; mouse.x = (int32_t)(mx * mouse.fMouseXMul); mouse.y = (int32_t)(my * mouse.fMouseYMul); } static void showAskToSaveDialog(void) { int32_t whichButtonPressed; const SDL_MessageBoxButtonData buttons[2] = { { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "No" }, { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Yes" }, }; const SDL_MessageBoxData messageBoxData = { SDL_MESSAGEBOX_WARNING, window, "Warning", "Colors are unsaved. Save before quitting?", SDL_arraysize(buttons), buttons, NULL }; if (SDL_ShowMessageBox(&messageBoxData, &whichButtonPressed) < 0) { allowSigterm = true; programRunning = false; return; } if (whichButtonPressed == 1) savePalette(0); programRunning = false; } void showErrorMsgBox(const char *fmt, ...) { char strBuf[1024]; va_list args; // format the text string va_start(args, fmt); vsnprintf(strBuf, sizeof (strBuf), fmt, args); va_end(args); // window can be NULL here, no problem... SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", strBuf, NULL); } static void handleInput(void) { SDL_Event inputEvent; while (SDL_PollEvent(&inputEvent)) { if (inputEvent.type == SDL_QUIT && allowSigterm) { if (!configIsSaved) { allowSigterm = false; showAskToSaveDialog(); } else { programRunning = false; } } else if (inputEvent.type == SDL_KEYDOWN) { keyDownHandler(inputEvent.key.keysym.sym); } else if (inputEvent.type == SDL_MOUSEBUTTONUP) { if (inputEvent.button.button == SDL_BUTTON_LEFT) mouseButtonUpHandler(); } else if (inputEvent.type == SDL_MOUSEBUTTONDOWN) { if (inputEvent.button.button == SDL_BUTTON_LEFT) mouseButtonDownHandler(); } } handleSlidersHeldDown(); handleRainbowHeldDown(); handleRainbowUpDownButtons(); } // macOS/OS X specific routines #ifdef __APPLE__ static void osxSetDirToProgramDirFromArgs(char **argv) { char *tmpPath; int32_t i, tmpPathLen; /* OS X/macOS: hackish way of setting the current working directory to the place where we double clicked ** on the icon (for protracker.ini loading) */ // if we launched from the terminal, argv[0][0] would be '.' if (argv[0] != NULL && argv[0][0] == '/') // don't do the hack if we launched from the terminal { tmpPath = strdup(argv[0]); if (tmpPath != NULL) { // cut off program filename tmpPathLen = strlen(tmpPath); for (i = tmpPathLen-1; i >= 0; i--) { if (tmpPath[i] == '/') { tmpPath[i] = '\0'; break; } } chdir(tmpPath); // path to binary chdir("../../../"); // we should now be in the directory where the config can be. free(tmpPath); } } } #endif static bool setupVideo(void) { int32_t renderW, renderH; /* SDL 2.0.9 for Windows has a serious bug where you need to initialize the joystick subsystem ** (even if you don't use it) or else weird things happen like random stutters, keyboard (rarely) being ** reinitialized in Windows and what not. ** Ref.: https://bugzilla.libsdl.org/show_bug.cgi?id=4391 */ #if defined _WIN32 && SDL_MAJOR_VERSION == 2 && SDL_MINOR_VERSION == 0 && SDL_PATCHLEVEL == 9 if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) != 0) #else if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO) != 0) #endif { showErrorMsgBox("Couldn't initialize SDL: %s", SDL_GetError()); return false; } window = SDL_CreateWindow("Palette editor for ProTracker 2 clone", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_W * 2, SCREEN_H * 2, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI); if (window == NULL) { showErrorMsgBox("Couldn't create SDL window:\n%s", SDL_GetError()); return false; } renderer = SDL_CreateRenderer(window, -1, 0); if (renderer == NULL) { showErrorMsgBox("Couldn't create SDL renderer:\n%s\n\n" \ "Is your GPU (+ driver) too old?", SDL_GetError()); return false; } SDL_RenderSetLogicalSize(renderer, SCREEN_W, SCREEN_H); #if SDL_MINOR_VERSION >= 24 || (SDL_MINOR_VERSION == 0 && SDL_PATCHLEVEL >= 5) SDL_RenderSetIntegerScale(renderer, SDL_TRUE); SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); #endif SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); SDL_SetHint("SDL_RENDER_SCALE_QUALITY", "nearest"); texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_W, SCREEN_H); if (texture == NULL) { showErrorMsgBox("Couldn't create an SDL2 texture: %s", SDL_GetError()); return false; } SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_NONE); // frame buffer used by SDL (for texture) frameBuffer = (uint32_t *)malloc(SCREEN_W * SCREEN_H * sizeof (int32_t)); if (frameBuffer == NULL) { showErrorMsgBox("Out of memory!"); return false; } SDL_GetWindowSize(window, &renderW, &renderH); if (renderW > 0) mouse.fMouseXMul = (float)SCREEN_W / renderW; if (renderH > 0) mouse.fMouseYMul = (float)SCREEN_H / renderH; return true; }