ft2_events.c (12381B)
1 // for finding memory leaks in debug mode with Visual Studio 2 #if defined _DEBUG && defined _MSC_VER 3 #include <crtdbg.h> 4 #endif 5 6 #ifdef _WIN32 7 #define WIN32_MEAN_AND_LEAN 8 #include <windows.h> 9 #include <SDL2/SDL_syswm.h> 10 #else 11 #include <limits.h> 12 #include <signal.h> 13 #include <unistd.h> // chdir() 14 #endif 15 #include <stdio.h> 16 #include <sys/stat.h> 17 #include "ft2_header.h" 18 #include "ft2_config.h" 19 #include "ft2_diskop.h" 20 #include "ft2_module_loader.h" 21 #include "ft2_module_saver.h" 22 #include "ft2_sample_loader.h" 23 #include "ft2_mouse.h" 24 #include "ft2_midi.h" 25 #include "ft2_video.h" 26 #include "ft2_trim.h" 27 #include "ft2_inst_ed.h" 28 #include "ft2_sampling.h" 29 #include "ft2_textboxes.h" 30 #include "ft2_sysreqs.h" 31 #include "ft2_keyboard.h" 32 #include "ft2_sample_ed.h" 33 #include "ft2_sample_ed_features.h" 34 #include "ft2_structs.h" 35 36 #define CRASH_TEXT "Oh no! The Fasttracker II clone has crashed...\nA backup of the song was hopefully " \ 37 "saved to the current module directory.\n\nPlease report this bug if you can.\n" \ 38 "Try to mention what you did before the crash happened.\n" \ 39 "My email is on the bottom of https://16-bits.org" 40 41 static bool backupMadeAfterCrash; 42 43 #ifdef _WIN32 44 #define SYSMSG_FILE_ARG (WM_USER+1) 45 #define ARGV_SHARED_MEM_MAX_LEN ((PATH_MAX+1) * sizeof (WCHAR)) 46 #define SHARED_HWND_NAME TEXT("Local\\FT2CloneHwnd") 47 #define SHARED_FILENAME TEXT("Local\\FT2CloneFilename") 48 static HWND hWnd; 49 static HANDLE oneInstHandle, hMapFile; 50 static LPCTSTR sharedMemBuf; 51 #endif 52 53 static void handleSDLEvents(void); 54 55 void readInput(void) 56 { 57 readMouseXY(); 58 readKeyModifiers(); 59 setSyncedReplayerVars(); 60 handleSDLEvents(); 61 } 62 63 void handleThreadEvents(void) 64 { 65 if (okBoxData.active) 66 { 67 okBoxData.returnData = okBox(okBoxData.type, okBoxData.headline, okBoxData.text, okBoxData.checkBoxCallback); 68 okBoxData.active = false; 69 } 70 } 71 72 void handleEvents(void) 73 { 74 #ifdef HAS_MIDI 75 // called after MIDI has been initialized 76 if (midi.rescanDevicesFlag) 77 { 78 midi.rescanDevicesFlag = false; 79 80 rescanMidiInputDevices(); 81 if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_MIDI_INPUT) 82 drawMidiInputList(); 83 } 84 #endif 85 86 if (editor.trimThreadWasDone) 87 { 88 editor.trimThreadWasDone = false; 89 trimThreadDone(); 90 } 91 92 if (editor.updateCurSmp) 93 { 94 editor.updateCurSmp = false; 95 96 updateNewInstrument(); 97 updateNewSample(); 98 99 diskOpSetFilename(DISKOP_ITEM_SAMPLE, editor.tmpFilenameU); 100 101 removeSampleIsLoadingFlag(); 102 setMouseBusy(false); 103 } 104 105 if (editor.updateCurInstr) 106 { 107 editor.updateCurInstr = false; 108 109 updateNewInstrument(); 110 updateNewSample(); 111 112 diskOpSetFilename(DISKOP_ITEM_INSTR, editor.tmpInstrFilenameU); 113 setMouseBusy(false); 114 } 115 116 // some Disk Op. stuff 117 118 if (editor.diskOpReadDir) 119 { 120 editor.diskOpReadDir = false; 121 diskOp_StartDirReadThread(); 122 } 123 124 if (editor.diskOpReadDone) 125 { 126 editor.diskOpReadDone = false; 127 if (ui.diskOpShown) 128 diskOp_DrawDirectory(); 129 } 130 131 handleLoadMusicEvents(); 132 133 if (editor.samplingAudioFlag) handleSamplingUpdates(); 134 if (ui.setMouseBusy) mouseAnimOn(); 135 if (ui.setMouseIdle) mouseAnimOff(); 136 137 if (editor.updateWindowTitle) 138 { 139 editor.updateWindowTitle = false; 140 updateWindowTitle(false); 141 } 142 } 143 144 // Windows specific routines 145 #ifdef _WIN32 146 static bool instanceAlreadyOpen(void) 147 { 148 hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHARED_HWND_NAME); 149 if (hMapFile != NULL) 150 return true; // another instance is already open 151 152 // no instance is open, let's created a shared memory file with hWnd in it 153 oneInstHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof (HWND), SHARED_HWND_NAME); 154 if (oneInstHandle != NULL) 155 { 156 sharedMemBuf = (LPTSTR)MapViewOfFile(oneInstHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof (HWND)); 157 if (sharedMemBuf != NULL) 158 { 159 CopyMemory((PVOID)sharedMemBuf, &video.hWnd, sizeof (HWND)); 160 UnmapViewOfFile(sharedMemBuf); 161 sharedMemBuf = NULL; 162 } 163 } 164 165 return false; 166 } 167 168 bool handleSingleInstancing(int32_t argc, char **argv) 169 { 170 SDL_SysWMinfo wmInfo; 171 172 SDL_VERSION(&wmInfo.version); 173 if (!SDL_GetWindowWMInfo(video.window, &wmInfo)) 174 return false; 175 176 video.hWnd = wmInfo.info.win.window; 177 if (instanceAlreadyOpen() && argc >= 2 && argv[1][0] != '\0') 178 { 179 sharedMemBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof (HWND)); 180 if (sharedMemBuf != NULL) 181 { 182 memcpy(&hWnd, sharedMemBuf, sizeof (HWND)); 183 184 UnmapViewOfFile(sharedMemBuf); 185 sharedMemBuf = NULL; 186 CloseHandle(hMapFile); 187 hMapFile = NULL; 188 189 hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, ARGV_SHARED_MEM_MAX_LEN, SHARED_FILENAME); 190 if (hMapFile != NULL) 191 { 192 sharedMemBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, ARGV_SHARED_MEM_MAX_LEN); 193 if (sharedMemBuf != NULL) 194 { 195 strcpy((char *)sharedMemBuf, argv[1]); 196 197 UnmapViewOfFile(sharedMemBuf); 198 sharedMemBuf = NULL; 199 200 SendMessage(hWnd, SYSMSG_FILE_ARG, 0, 0); 201 Sleep(80); // wait a bit to make sure first instance received msg 202 203 CloseHandle(hMapFile); 204 hMapFile = NULL; 205 206 return true; // quit instance now 207 } 208 } 209 210 return true; 211 } 212 213 CloseHandle(hMapFile); 214 hMapFile = NULL; 215 } 216 217 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); 218 return false; 219 } 220 221 static void handleSysMsg(SDL_Event inputEvent) 222 { 223 SDL_SysWMmsg *wmMsg = inputEvent.syswm.msg; 224 if (wmMsg->subsystem == SDL_SYSWM_WINDOWS && wmMsg->msg.win.msg == SYSMSG_FILE_ARG) 225 { 226 hMapFile = OpenFileMapping(FILE_MAP_READ, FALSE, SHARED_FILENAME); 227 if (hMapFile != NULL) 228 { 229 sharedMemBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, ARGV_SHARED_MEM_MAX_LEN); 230 if (sharedMemBuf != NULL) 231 { 232 editor.autoPlayOnDrop = true; 233 234 if (video.window != NULL && !video.fullscreen) 235 { 236 if (SDL_GetWindowFlags(video.window) & SDL_WINDOW_MINIMIZED) 237 SDL_RestoreWindow(video.window); 238 239 SDL_RaiseWindow(video.window); 240 } 241 242 loadDroppedFile((char *)sharedMemBuf, true); 243 244 UnmapViewOfFile(sharedMemBuf); 245 sharedMemBuf = NULL; 246 } 247 248 CloseHandle(hMapFile); 249 hMapFile = NULL; 250 } 251 } 252 } 253 254 void closeSingleInstancing(void) 255 { 256 if (oneInstHandle != NULL) 257 { 258 CloseHandle(oneInstHandle); 259 oneInstHandle = NULL; 260 } 261 } 262 263 static LONG WINAPI exceptionHandler(EXCEPTION_POINTERS *ptr) 264 { 265 #define BACKUP_FILES_TO_TRY 1000 266 char fileName[32]; 267 struct stat statBuffer; 268 269 if (oneInstHandle != NULL) 270 CloseHandle(oneInstHandle); 271 272 if (!backupMadeAfterCrash) 273 { 274 if (getDiskOpModPath() != NULL && UNICHAR_CHDIR(getDiskOpModPath()) == 0) 275 { 276 // find a free filename 277 int32_t i; 278 for (i = 1; i < BACKUP_FILES_TO_TRY; i++) 279 { 280 sprintf(fileName, "backup%03d.xm", (int32_t)i); 281 if (stat(fileName, &statBuffer) != 0) 282 break; // filename OK 283 } 284 285 if (i != BACKUP_FILES_TO_TRY) 286 { 287 UNICHAR *fileNameU = cp850ToUnichar(fileName); 288 if (fileNameU != NULL) 289 { 290 saveXM(fileNameU); 291 free(fileNameU); 292 } 293 } 294 } 295 296 backupMadeAfterCrash = true; // set this flag to prevent multiple backups from being saved at once 297 showErrorMsgBox(CRASH_TEXT); 298 } 299 300 return EXCEPTION_CONTINUE_SEARCH; 301 302 (void)ptr; 303 } 304 #else 305 static void exceptionHandler(int32_t signal) 306 { 307 #define BACKUP_FILES_TO_TRY 1000 308 char fileName[32]; 309 struct stat statBuffer; 310 311 if (signal == 15) 312 return; 313 314 if (!backupMadeAfterCrash) 315 { 316 if (getDiskOpModPath() != NULL && UNICHAR_CHDIR(getDiskOpModPath()) == 0) 317 { 318 // find a free filename 319 int32_t i; 320 for (i = 1; i < BACKUP_FILES_TO_TRY; i++) 321 { 322 sprintf(fileName, "backup%03d.xm", i); 323 if (stat(fileName, &statBuffer) != 0) 324 break; // filename OK 325 } 326 327 if (i != BACKUP_FILES_TO_TRY) 328 { 329 UNICHAR *fileNameU = cp850ToUnichar(fileName); 330 if (fileNameU != NULL) 331 { 332 saveXM(fileNameU); 333 free(fileNameU); 334 } 335 } 336 } 337 338 backupMadeAfterCrash = true; // set this flag to prevent multiple backups from being saved at once 339 showErrorMsgBox(CRASH_TEXT); 340 } 341 } 342 #endif 343 344 void setupCrashHandler(void) 345 { 346 #ifndef _DEBUG 347 #ifdef _WIN32 348 SetUnhandledExceptionFilter(exceptionHandler); 349 #else 350 struct sigaction act; 351 struct sigaction oldAct; 352 353 memset(&act, 0, sizeof (act)); 354 act.sa_handler = exceptionHandler; 355 act.sa_flags = SA_RESETHAND; 356 357 sigaction(SIGILL | SIGABRT | SIGFPE | SIGSEGV, &act, &oldAct); 358 sigaction(SIGILL, &act, &oldAct); 359 sigaction(SIGABRT, &act, &oldAct); 360 sigaction(SIGFPE, &act, &oldAct); 361 sigaction(SIGSEGV, &act, &oldAct); 362 #endif 363 #endif 364 } 365 366 void handleWaitVblQuirk(SDL_Event *event) 367 { 368 if (event->type == SDL_WINDOWEVENT) 369 { 370 if (event->window.event == SDL_WINDOWEVENT_HIDDEN) 371 video.windowHidden = true; 372 else if (event->window.event == SDL_WINDOWEVENT_SHOWN) 373 video.windowHidden = false; 374 375 // reset vblank end time if we minimize window 376 if (event->window.event == SDL_WINDOWEVENT_MINIMIZED || event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) 377 hpc_ResetCounters(&video.vblankHpc); 378 } 379 } 380 381 static void handleSDLEvents(void) 382 { 383 SDL_Event event; 384 385 if (!editor.busy) 386 handleLastGUIObjectDown(); // this should be handled before main input poll (on next frame) 387 388 while (SDL_PollEvent(&event)) 389 { 390 handleWaitVblQuirk(&event); 391 392 if (editor.busy) 393 { 394 const uint32_t eventType = event.type; 395 const SDL_Scancode key = event.key.keysym.scancode; 396 397 /* The Echo tool in Smp. Ed. can take forever if abused, let 398 ** mouse buttons/ESC/SIGTERM force-stop it. 399 */ 400 if (eventType == SDL_MOUSEBUTTONDOWN || eventType == SDL_QUIT || 401 (eventType == SDL_KEYUP && key == SDL_SCANCODE_ESCAPE)) 402 { 403 handleEchoToolPanic(); 404 } 405 406 // let certain mouse buttons or keyboard keys stop certain events 407 if (eventType == SDL_MOUSEBUTTONDOWN || 408 (eventType == SDL_KEYDOWN && key != SDL_SCANCODE_MUTE && 409 key != SDL_SCANCODE_AUDIOMUTE && key != SDL_SCANCODE_VOLUMEDOWN && 410 key != SDL_SCANCODE_VOLUMEUP)) 411 { 412 if (editor.samplingAudioFlag) 413 stopSampling(); 414 415 editor.wavIsRendering = false; 416 } 417 418 continue; // another thread is busy with something, drop input 419 } 420 421 #ifdef _WIN32 422 if (event.type == SDL_SYSWMEVENT) 423 handleSysMsg(event); 424 #endif 425 // text input when editing texts 426 if (event.type == SDL_TEXTINPUT) 427 { 428 if (editor.editTextFlag) 429 { 430 if (keyb.ignoreTextEditKey) 431 { 432 keyb.ignoreTextEditKey = false; 433 continue; 434 } 435 436 char *inputText = utf8ToCp850(event.text.text, false); 437 if (inputText != NULL) 438 { 439 if (inputText[0] != '\0') 440 handleTextEditInputChar(inputText[0]); 441 442 free(inputText); 443 } 444 } 445 } 446 else if (event.type == SDL_MOUSEWHEEL) 447 { 448 if (event.wheel.y > 0) 449 mouseWheelHandler(MOUSE_WHEEL_UP); 450 else if (event.wheel.y < 0) 451 mouseWheelHandler(MOUSE_WHEEL_DOWN); 452 } 453 else if (event.type == SDL_DROPFILE) 454 { 455 editor.autoPlayOnDrop = false; 456 457 if (!video.fullscreen) 458 { 459 if (SDL_GetWindowFlags(video.window) & SDL_WINDOW_MINIMIZED) 460 SDL_RestoreWindow(video.window); 461 462 SDL_RaiseWindow(video.window); 463 } 464 465 loadDroppedFile(event.drop.file, true); 466 SDL_free(event.drop.file); 467 } 468 else if (event.type == SDL_QUIT) 469 { 470 if (ui.sysReqShown) 471 continue; 472 473 if (editor.editTextFlag) 474 exitTextEditing(); 475 476 if (!song.isModified) 477 { 478 editor.throwExit = true; 479 } 480 else 481 { 482 if (!video.fullscreen) 483 { 484 // de-minimize window and set focus so that the user sees the message box 485 if (SDL_GetWindowFlags(video.window) & SDL_WINDOW_MINIMIZED) 486 SDL_RestoreWindow(video.window); 487 488 SDL_RaiseWindow(video.window); 489 } 490 491 if (quitBox(true) == 1) 492 editor.throwExit = true; 493 } 494 } 495 else if (event.type == SDL_KEYUP) 496 { 497 keyUpHandler(event.key.keysym.scancode, event.key.keysym.sym); 498 } 499 else if (event.type == SDL_KEYDOWN) 500 { 501 keyDownHandler(event.key.keysym.scancode, event.key.keysym.sym, event.key.repeat); 502 } 503 else if (event.type == SDL_MOUSEBUTTONUP) 504 { 505 mouseButtonUpHandler(event.button.button); 506 #if defined __APPLE__ && defined __aarch64__ 507 armMacGhostMouseCursorFix(); 508 #endif 509 } 510 else if (event.type == SDL_MOUSEBUTTONDOWN) 511 { 512 mouseButtonDownHandler(event.button.button); 513 } 514 #if defined __APPLE__ && defined __aarch64__ 515 else if (event.type == SDL_MOUSEMOTION) 516 { 517 armMacGhostMouseCursorFix(); 518 } 519 #endif 520 521 if (editor.throwExit) 522 editor.programRunning = false; 523 } 524 525 #ifdef HAS_MIDI 526 // MIDI vibrato 527 const uint8_t vibDepth = (midi.currMIDIVibDepth >> 9) & 0x0F; 528 if (vibDepth > 0) 529 recordMIDIEffect(0x04, 0xA0 | vibDepth); 530 #endif 531 }