ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

commit 2247f4c8984d43c50550fbd4444e2eed321007d2
parent 108c333ecdfd8287e12f39f858347db64d33f2f7
Author: Olav Sørensen <olav.sorensen@live.no>
Date:   Thu,  2 Apr 2020 21:58:36 +0200

Pushed v1.16 code

- The mouse system has been rewritten so that we don't need to do mouse
  capturing while interacting with GUI widgets, which could be buggy sometimes.
- The different Disk Op. item paths (mod./instr./smp./pat./trk.) now behave
  exactly like real FT2. I.e. if they haven't been initialized before, they
  will be set to the current working directory.
- Scrollbar thumbs are now limited to minimum 9 pixels in length/height, to
  prevent them from being difficult to click on.

Diffstat:
Msrc/ft2_diskop.c | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/ft2_diskop.h | 2+-
Msrc/ft2_events.c | 23++++++++++++++++-------
Msrc/ft2_header.h | 2+-
Msrc/ft2_inst_ed.c | 2+-
Msrc/ft2_module_loader.c | 32++++++++++++++------------------
Msrc/ft2_mouse.c | 240++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/ft2_mouse.h | 8+++++---
Msrc/ft2_sample_ed.c | 4++--
Msrc/ft2_sample_ed_features.c | 5-----
Msrc/ft2_scrollbars.c | 124++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/ft2_scrollbars.h | 2+-
Msrc/ft2_sysreqs.c | 10----------
Msrc/ft2_textboxes.c | 3++-
Msrc/ft2_video.c | 89++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/ft2_video.h | 10+++++-----
16 files changed, 458 insertions(+), 256 deletions(-)

diff --git a/src/ft2_diskop.c b/src/ft2_diskop.c @@ -75,7 +75,7 @@ static char FReq_SysReqText[196], *FReq_FileName, *FReq_NameTemp; static char *modTmpFName, *insTmpFName, *smpTmpFName, *patTmpFName, *trkTmpFName; static char *modTmpFNameUTF8; // for window title static uint8_t FReq_Item; -static bool FReq_ShowAllFiles; +static bool FReq_ShowAllFiles, modPathSet, insPathSet, smpPathSet, patPathSet, trkPathSet; static int32_t FReq_EntrySelected = -1, FReq_FileCount, FReq_DirPos, lastMouseY; static UNICHAR *FReq_CurPathU, *FReq_ModCurPathU, *FReq_InsCurPathU, *FReq_SmpCurPathU, *FReq_PatCurPathU, *FReq_TrkCurPathU; static DirRec *FReq_Buffer; @@ -176,20 +176,70 @@ static void setupInitialPaths(void) UNICHAR pathU[PATH_MAX + 2]; #endif + UNICHAR_GETCWD(FReq_ModCurPathU, PATH_MAX); + // the UNICHAR paths are already zeroed out #ifdef _WIN32 - if (config.modulesPath[0] != '\0') MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.modulesPath, -1, FReq_ModCurPathU, 80); - if (config.instrPath[0] != '\0') MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.instrPath, -1, FReq_InsCurPathU, 80); - if (config.samplesPath[0] != '\0') MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.samplesPath, -1, FReq_SmpCurPathU, 80); - if (config.patternsPath[0] != '\0') MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.patternsPath, -1, FReq_PatCurPathU, 80); - if (config.tracksPath[0] != '\0') MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.tracksPath, -1, FReq_TrkCurPathU, 80); + if (config.modulesPath[0] != '\0') + { + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.modulesPath, -1, FReq_ModCurPathU, 80); + modPathSet = true; + } + + if (config.instrPath[0] != '\0') + { + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.instrPath, -1, FReq_InsCurPathU, 80); + insPathSet = true; + } + + if (config.samplesPath[0] != '\0') + { + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.samplesPath, -1, FReq_SmpCurPathU, 80); + smpPathSet = true; + } + + if (config.patternsPath[0] != '\0') + { + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.patternsPath, -1, FReq_PatCurPathU, 80); + patPathSet = true; + } + + if (config.tracksPath[0] != '\0') + { + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.tracksPath, -1, FReq_TrkCurPathU, 80); + trkPathSet = true; + } #else - if (config.modulesPath[0] != '\0') strncpy(FReq_ModCurPathU, config.modulesPath, 80); - if (config.instrPath[0] != '\0') strncpy(FReq_InsCurPathU, config.instrPath, 80); - if (config.samplesPath[0] != '\0') strncpy(FReq_SmpCurPathU, config.samplesPath, 80); - if (config.patternsPath[0] != '\0') strncpy(FReq_PatCurPathU, config.patternsPath, 80); - if (config.tracksPath[0] != '\0') strncpy(FReq_TrkCurPathU, config.tracksPath, 80); + if (config.modulesPath[0] != '\0') + { + strncpy(FReq_ModCurPathU, config.modulesPath, 80); + modPathSet = true; + } + + if (config.instrPath[0] != '\0') + { + strncpy(FReq_InsCurPathU, config.instrPath, 80); + insPathSet = true; + } + + if (config.samplesPath[0] != '\0') + { + strncpy(FReq_SmpCurPathU, config.samplesPath, 80); + smpPathSet = true; + } + + if (config.patternsPath[0] != '\0') + { + strncpy(FReq_PatCurPathU, config.patternsPath, 80); + patPathSet = true; + } + + if (config.tracksPath[0] != '\0') + { + strncpy(FReq_TrkCurPathU, config.tracksPath, 80); + trkPathSet = true; + } #endif // set initial path to user directory @@ -200,16 +250,12 @@ static void setupInitialPaths(void) UNICHAR_CHDIR(getenv("HOME")); #endif - // if present in config, set custom "modules" path - if (UNICHAR_CHDIR(FReq_ModCurPathU) != 0) + // if we couldn't set present in config, set custom "modules" path + if (modPathSet && UNICHAR_CHDIR(FReq_ModCurPathU) != 0) + { UNICHAR_GETCWD(FReq_ModCurPathU, PATH_MAX); - - if (UNICHAR_CHDIR(FReq_InsCurPathU) != 0) UNICHAR_STRCPY(FReq_InsCurPathU, FReq_ModCurPathU); - if (UNICHAR_CHDIR(FReq_SmpCurPathU) != 0) UNICHAR_STRCPY(FReq_SmpCurPathU, FReq_ModCurPathU); - if (UNICHAR_CHDIR(FReq_PatCurPathU) != 0) UNICHAR_STRCPY(FReq_PatCurPathU, FReq_ModCurPathU); - if (UNICHAR_CHDIR(FReq_TrkCurPathU) != 0) UNICHAR_STRCPY(FReq_TrkCurPathU, FReq_ModCurPathU); - - UNICHAR_CHDIR(FReq_ModCurPathU); // set back after testing + modPathSet = true; + } } static void freeDirRecBuffer(void) @@ -1969,7 +2015,7 @@ static int32_t SDLCALL diskOp_ReadDirectoryThread(void *ptr) return true; } -void startDiskOpFillThread(void) +void diskOp_StartDirReadThread(void) { editor.diskOpReadDone = false; @@ -2076,11 +2122,69 @@ static void setDiskOpItem(uint8_t item) FReq_Item = item; switch (FReq_Item) { - default: case DISKOP_ITEM_MODULE: FReq_FileName = modTmpFName; FReq_CurPathU = FReq_ModCurPathU; break; - case DISKOP_ITEM_INSTR: FReq_FileName = insTmpFName; FReq_CurPathU = FReq_InsCurPathU; break; - case DISKOP_ITEM_SAMPLE: FReq_FileName = smpTmpFName; FReq_CurPathU = FReq_SmpCurPathU; break; - case DISKOP_ITEM_PATTERN: FReq_FileName = patTmpFName; FReq_CurPathU = FReq_PatCurPathU; break; - case DISKOP_ITEM_TRACK: FReq_FileName = trkTmpFName; FReq_CurPathU = FReq_TrkCurPathU; break; + default: + case DISKOP_ITEM_MODULE: + { + FReq_FileName = modTmpFName; + FReq_CurPathU = FReq_ModCurPathU; + } + break; + + case DISKOP_ITEM_INSTR: + { + FReq_FileName = insTmpFName; + + if (!insPathSet) + { + UNICHAR_STRCPY(FReq_InsCurPathU, FReq_CurPathU); + insPathSet = true; + } + + FReq_CurPathU = FReq_InsCurPathU; + } + break; + + case DISKOP_ITEM_SAMPLE: + { + FReq_FileName = smpTmpFName; + + if (!smpPathSet) + { + UNICHAR_STRCPY(FReq_SmpCurPathU, FReq_CurPathU); + smpPathSet = true; + } + + FReq_CurPathU = FReq_SmpCurPathU; + } + break; + + case DISKOP_ITEM_PATTERN: + { + FReq_FileName = patTmpFName; + + if (!patPathSet) + { + UNICHAR_STRCPY(FReq_SmpCurPathU, FReq_CurPathU); + patPathSet = true; + } + + FReq_CurPathU = FReq_PatCurPathU; + } + break; + + case DISKOP_ITEM_TRACK: + { + FReq_FileName = trkTmpFName; + + if (!trkPathSet) + { + UNICHAR_STRCPY(FReq_TrkCurPathU, FReq_CurPathU); + trkPathSet = true; + } + + FReq_CurPathU = FReq_TrkCurPathU; + } + break; } pathLen = (int32_t)UNICHAR_STRLEN(FReq_CurPathU); @@ -2189,7 +2293,7 @@ void showDiskOpScreen(void) if (editor.diskOpReadOnOpen) { editor.diskOpReadOnOpen = false; - startDiskOpFillThread(); + diskOp_StartDirReadThread(); } } diff --git a/src/ft2_diskop.h b/src/ft2_diskop.h @@ -40,7 +40,7 @@ void pbDiskOpRoot(void); int32_t getExtOffset(char *s, int32_t stringLen); // get byte offset of file extension (last '.') bool testDiskOpMouseDown(bool mouseHeldDown); void testDiskOpMouseRelease(void); -void startDiskOpFillThread(void); +void diskOp_StartDirReadThread(void); void diskOp_DrawDirectory(void); void showDiskOpScreen(void); void hideDiskOpScreen(void); diff --git a/src/ft2_events.c b/src/ft2_events.c @@ -152,7 +152,7 @@ void handleEvents(void) if (editor.diskOpReadDir) { editor.diskOpReadDir = false; - startDiskOpFillThread(); + diskOp_StartDirReadThread(); } if (editor.diskOpReadDone) @@ -407,14 +407,23 @@ static void handleInput(void) while (SDL_PollEvent(&event)) { - if (video.vsync60HzPresent) + if (event.type == SDL_WINDOWEVENT) { - /* if we minimize the window and vsync is present, vsync is temporarily turned off. - ** recalc waitVBL() vars so that it can sleep properly in said mode. */ - if (event.type == SDL_WINDOWEVENT && - (event.window.event == SDL_WINDOWEVENT_MINIMIZED || event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)) + if (event.window.event == SDL_WINDOWEVENT_HIDDEN) + video.windowHidden = true; + else if (event.window.event == SDL_WINDOWEVENT_SHOWN) + video.windowHidden = false; + + if (video.vsync60HzPresent) { - setupWaitVBL(); + /* if we minimize the window and vsync is present, vsync is temporarily turned off. + ** recalc waitVBL() vars so that it can sleep properly in said mode. + */ + if (event.window.event == SDL_WINDOWEVENT_MINIMIZED || + event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) + { + setupWaitVBL(); + } } } diff --git a/src/ft2_header.h b/src/ft2_header.h @@ -12,7 +12,7 @@ #endif #include "ft2_replayer.h" -#define PROG_VER_STR "1.15" +#define PROG_VER_STR "1.16" // do NOT change these! It will only mess things up... diff --git a/src/ft2_inst_ed.c b/src/ft2_inst_ed.c @@ -3146,7 +3146,7 @@ static int32_t SDLCALL loadInstrThread(void *ptr) ins->vibDepth = ih.vibDepth; ins->vibRate = ih.vibRate; ins->fadeOut = ih.fadeOut; - ins->midiOn = (ih.midiOn > 0) ? true : false; + ins->midiOn = (ih.midiOn == 1) ? true : false; ins->midiChannel = ih.midiChannel; ins->midiProgram = ih.midiProgram; ins->midiBend = ih.midiBend; diff --git a/src/ft2_module_loader.c b/src/ft2_module_loader.c @@ -622,16 +622,6 @@ static bool loadMusicMOD(FILE *f, uint32_t fileLength, bool fromExternalThread) s = &instrTmp[1+a]->samp[0]; s->len = 2 * SWAP16(h_MOD31.instr[a].len); - - s->pek = NULL; - s->origPek = (int8_t *)malloc(s->len + LOOP_FIX_LEN); - if (s->origPek == NULL) - { - showMsg(0, "System message", "Not enough memory!"); - goto modLoadError; - } - - s->pek = s->origPek + SMP_DAT_OFFSET; if (modFormat != FORMAT_HMNT) // most of "His Master's Noisetracker" songs have junk sample names, so let's not load them memcpy(s->name, songTmp.instrName[1+a], 22); @@ -655,11 +645,11 @@ static bool loadMusicMOD(FILE *f, uint32_t fileLength, bool fromExternalThread) s->repL = 2; // in The Ultimate SoundTracker, sample loop start is in bytes, not words - if (mightBeSTK) + if (modFormat == FORMAT_STK) s->repS >>= 1; // fix for poorly converted STK (< v2.5) -> PT/NT modules (FIXME: Worth keeping or not?) - if (!mightBeSTK && s->repL > 2 && s->repS+s->repL > s->len) + if (modFormat != FORMAT_STK && s->repL > 2 && s->repS+s->repL > s->len) { if ((s->repS>>1)+s->repL <= s->len) s->repS >>= 1; @@ -692,6 +682,16 @@ static bool loadMusicMOD(FILE *f, uint32_t fileLength, bool fromExternalThread) s->repS = 0; } + s->pek = NULL; + s->origPek = (int8_t *)malloc(s->len + LOOP_FIX_LEN); + if (s->origPek == NULL) + { + showMsg(0, "System message", "Not enough memory!"); + goto modLoadError; + } + + s->pek = s->origPek + SMP_DAT_OFFSET; + int32_t bytesRead = (int32_t)fread(s->pek, 1, s->len, f); if (bytesRead < s->len) { @@ -737,7 +737,7 @@ static uint8_t stmTempoToBPM(uint8_t tempo) // ported from original ST2.3 replay static bool loadMusicSTM(FILE *f, uint32_t fileLength, bool fromExternalThread) { uint8_t typ, tempo; - int16_t i, j, k, ai, ap, tmp; + int16_t i, j, k, ap, tmp; uint16_t a; tonTyp *ton; sampleTyp *s; @@ -899,7 +899,6 @@ static bool loadMusicSTM(FILE *f, uint32_t fileLength, bool fromExternalThread) } } - ai = 31; for (i = 0; i < 31; i++) { // trim off spaces at end of name @@ -1010,7 +1009,6 @@ static int8_t countS3MChannels(uint16_t antPtn) static bool loadMusicS3M(FILE *f, uint32_t dataLength, bool fromExternalThread) { int8_t *tmpSmp; - bool illegalUxx; uint8_t ha[2048]; uint8_t alastnfo[32], alastefx[32], alastvibnfo[32], s3mLastGInstr[32]; uint8_t typ; @@ -1129,8 +1127,6 @@ static bool loadMusicS3M(FILE *f, uint32_t dataLength, bool fromExternalThread) // *** PATTERNS *** - illegalUxx = false; - k = 0; for (i = 0; i < ap; i++) { @@ -2012,7 +2008,7 @@ static bool loadInstrHeader(FILE *f, uint16_t i) ins->vibDepth = ih.vibDepth; ins->vibRate = ih.vibRate; ins->fadeOut = ih.fadeOut; - ins->midiOn = (ih.midiOn > 0) ? true : false; + ins->midiOn = (ih.midiOn == 1) ? true : false; ins->midiChannel = ih.midiChannel; ins->midiProgram = ih.midiProgram; ins->midiBend = ih.midiBend; diff --git a/src/ft2_mouse.c b/src/ft2_mouse.c @@ -59,16 +59,18 @@ bool createMouseCursors(void) // creates scaled SDL surfaces for current mouse p const uint8_t *cursorsSrc = bmp.mouseCursors; switch (config.mouseType) { - case MOUSE_IDLE_SHAPE_NICE: cursorsSrc += 0 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; - case MOUSE_IDLE_SHAPE_UGLY: cursorsSrc += 1 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; - case MOUSE_IDLE_SHAPE_AWFUL: cursorsSrc += 2 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_NICE: cursorsSrc += 0 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_UGLY: cursorsSrc += 1 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_AWFUL: cursorsSrc += 2 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; case MOUSE_IDLE_SHAPE_USABLE: cursorsSrc += 3 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; default: break; } for (uint32_t i = 0; i < NUM_CURSORS; i++) { - SDL_Surface *surface = SDL_CreateRGBSurface(0, MOUSE_CURSOR_W*video.yScale, MOUSE_CURSOR_H*video.yScale, 32, 0, 0, 0, 0); + int32_t scaleFactor = video.yScale; + + SDL_Surface *surface = SDL_CreateRGBSurface(0, MOUSE_CURSOR_W*scaleFactor, MOUSE_CURSOR_H*scaleFactor, 32, 0, 0, 0, 0); if (surface == NULL) { freeMouseCursors(); @@ -104,10 +106,10 @@ bool createMouseCursors(void) // creates scaled SDL surfaces for current mouse p // blit upscaled cursor to surface for (uint32_t y = 0; y < MOUSE_CURSOR_H; y++) { - uint32_t *outX = &dstPixels32[(y * video.yScale) * surface->w]; - for (uint32_t yScale = 0; yScale < video.yScale; yScale++) + uint32_t *outX = &dstPixels32[(y * scaleFactor) * surface->w]; + for (int32_t yScale = 0; yScale < scaleFactor; yScale++) { - for (uint32_t x = 0; x < MOUSE_CURSOR_W; x++) + for (int32_t x = 0; x < MOUSE_CURSOR_W; x++) { uint8_t srcPix = srcPixels8[(y * MOUSE_CURSOR_W) + x]; if (srcPix != PAL_TRANSPR) @@ -118,7 +120,7 @@ bool createMouseCursors(void) // creates scaled SDL surfaces for current mouse p else if (srcPix == PAL_BCKGRND) pixel = border; - for (uint32_t xScale = 0; xScale < video.yScale; xScale++) + for (int32_t xScale = 0; xScale < scaleFactor; xScale++) outX[xScale] = pixel; } @@ -163,13 +165,13 @@ void setMousePosToCenter(void) { if (video.fullscreen) { - mouse.setPosX = video.displayW / 2; - mouse.setPosY = video.displayH / 2; + mouse.setPosX = video.displayW >> 1; + mouse.setPosY = video.displayH >> 1; } else { - mouse.setPosX = video.renderW / 2; - mouse.setPosY = video.renderH / 2; + mouse.setPosX = video.renderW >> 1; + mouse.setPosY = video.renderH >> 1; } mouse.setPosFlag = true; @@ -242,11 +244,11 @@ void setMouseShape(int16_t shape) gfxPtr = &bmp.mouseCursors[mouseModeGfxOffs]; switch (shape) { - case MOUSE_IDLE_SHAPE_NICE: gfxPtr += 0 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; - case MOUSE_IDLE_SHAPE_UGLY: gfxPtr += 1 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; - case MOUSE_IDLE_SHAPE_AWFUL: gfxPtr += 2 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; - case MOUSE_IDLE_SHAPE_USABLE: gfxPtr += 3 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; - case MOUSE_IDLE_TEXT_EDIT: gfxPtr += 12 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_NICE: gfxPtr += 0 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_UGLY: gfxPtr += 1 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_AWFUL: gfxPtr += 2 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_USABLE: gfxPtr += 3 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_TEXT_EDIT: gfxPtr += 12 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; default: return; } } @@ -284,7 +286,7 @@ static void clearTextEditMouse(void) static void changeCursorIfOverTextBoxes(void) { - int16_t i, mx, my; + int32_t i, mx, my; textBox_t *t; mouse.mouseOverTextBox = false; @@ -542,11 +544,6 @@ static bool testPatternDataMouseDown(void) void mouseButtonUpHandler(uint8_t mouseButton) { -#ifndef __APPLE__ - if (!video.fullscreen) // release mouse button trap - SDL_SetWindowGrab(video.window, SDL_FALSE); -#endif - if (mouseButton == SDL_BUTTON_LEFT) { mouse.leftButtonPressed = false; @@ -569,7 +566,6 @@ void mouseButtonUpHandler(uint8_t mouseButton) mouse.rightButtonPressed = false; mouse.rightButtonReleased = true; - if (editor.editSampleFlag) { // right mouse button released after hand-editing sample data @@ -582,9 +578,9 @@ void mouseButtonUpHandler(uint8_t mouseButton) writeSample(true); setSongModifiedFlag(); + editor.editSampleFlag = false; } - } mouse.firstTimePressingButton = false; @@ -611,9 +607,9 @@ void mouseButtonUpHandler(uint8_t mouseButton) testRadioButtonMouseRelease(); // revert "delete/rename" mouse modes (disk op.) - if (mouse.lastUsedObjectID != PB_DISKOP_DELETE && mouse.lastUsedObjectID != PB_DISKOP_RENAME) + if (mouse.mode != MOUSE_MODE_NORMAL) { - if (mouse.mode != MOUSE_MODE_NORMAL) + if (mouse.lastUsedObjectID != PB_DISKOP_DELETE && mouse.lastUsedObjectID != PB_DISKOP_RENAME) setMouseMode(MOUSE_MODE_NORMAL); } @@ -623,11 +619,6 @@ void mouseButtonUpHandler(uint8_t mouseButton) void mouseButtonDownHandler(uint8_t mouseButton) { -#ifndef __APPLE__ - if (!video.fullscreen) // trap mouse pointer while holding down left and/or right button - SDL_SetWindowGrab(video.window, SDL_TRUE); -#endif - // if already holding left button and clicking right, don't do mouse down handling if (mouseButton == SDL_BUTTON_RIGHT && mouse.leftButtonPressed) { @@ -691,34 +682,35 @@ void mouseButtonDownHandler(uint8_t mouseButton) if (mouse.lastUsedObjectType != OBJECT_PUSHBUTTON && mouse.lastUsedObjectID != OBJECT_ID_NONE) return; - // kludge #3 + // kludge #3 :( if (!mouse.rightButtonPressed) mouse.lastUsedObjectID = OBJECT_ID_NONE; // check if we pressed a GUI object /* test objects like this - clickable things *never* overlap, so no need to test all - ** other objects if we clicked on one already */ + ** other objects if we clicked on one already + */ testInstrSwitcherMouseDown(); // kludge: allow right click to both change ins. and edit text - if (testTextBoxMouseDown()) return; - if (testPushButtonMouseDown()) return; - if (testCheckBoxMouseDown()) return; - if (testScrollBarMouseDown()) return; + if (testTextBoxMouseDown()) return; + if (testPushButtonMouseDown()) return; + if (testCheckBoxMouseDown()) return; + if (testScrollBarMouseDown()) return; if (testRadioButtonMouseDown()) return; - // from this point, we don't need to test more widgets if a system request box is shown + // at this point, we don't need to test more widgets if a system request is shown if (editor.ui.sysReqShown) return; if (testInstrVolEnvMouseDown(false)) return; if (testInstrPanEnvMouseDown(false)) return; - if (testDiskOpMouseDown(false)) return; - if (testPianoKeysMouseDown(false)) return; - if (testSamplerDataMouseDown()) return; - if (testPatternDataMouseDown()) return; - if (testScopesMouseDown()) return; + if (testDiskOpMouseDown(false)) return; + if (testPianoKeysMouseDown(false)) return; + if (testSamplerDataMouseDown()) return; + if (testPatternDataMouseDown()) return; + if (testScopesMouseDown()) return; if (testAudioDeviceListsMouseDown()) return; #ifdef HAS_MIDI @@ -737,11 +729,11 @@ void handleLastGUIObjectDown(void) { switch (mouse.lastUsedObjectType) { - case OBJECT_PUSHBUTTON: handlePushButtonsWhileMouseDown(); break; + case OBJECT_PUSHBUTTON: handlePushButtonsWhileMouseDown(); break; case OBJECT_RADIOBUTTON: handleRadioButtonsWhileMouseDown(); break; - case OBJECT_CHECKBOX: handleCheckBoxesWhileMouseDown(); break; - case OBJECT_SCROLLBAR: handleScrollBarsWhileMouseDown(); break; - case OBJECT_TEXTBOX: handleTextBoxWhileMouseDown(); break; + case OBJECT_CHECKBOX: handleCheckBoxesWhileMouseDown(); break; + case OBJECT_SCROLLBAR: handleScrollBarsWhileMouseDown(); break; + case OBJECT_TEXTBOX: handleTextBoxWhileMouseDown(); break; default: break; } } @@ -750,16 +742,28 @@ void handleLastGUIObjectDown(void) // test non-standard GUI elements switch (mouse.lastUsedObjectType) { - case OBJECT_INSTRSWITCH: testInstrSwitcherMouseDown(); break; - case OBJECT_PATTERNMARK: handlePatternDataMouseDown(true); break; - case OBJECT_DISKOPLIST: testDiskOpMouseDown(true); break; - case OBJECT_SMPDATA: handleSampleDataMouseDown(true); break; - case OBJECT_PIANO: testPianoKeysMouseDown(true); break; - case OBJECT_INSVOLENV: testInstrVolEnvMouseDown(true); break; - case OBJECT_INSPANENV: testInstrPanEnvMouseDown(true); break; + case OBJECT_INSTRSWITCH: testInstrSwitcherMouseDown(); break; + case OBJECT_PATTERNMARK: handlePatternDataMouseDown(true); break; + case OBJECT_DISKOPLIST: testDiskOpMouseDown(true); break; + case OBJECT_SMPDATA: handleSampleDataMouseDown(true); break; + case OBJECT_PIANO: testPianoKeysMouseDown(true); break; + case OBJECT_INSVOLENV: testInstrVolEnvMouseDown(true); break; + case OBJECT_INSPANENV: testInstrPanEnvMouseDown(true); break; default: break; } } + + /* Hack to send "mouse button up" events if we released the mouse button(s) + ** outside of the window... + */ + if (mouse.x < 0 || mouse.x >= SCREEN_W || mouse.y < 0 || mouse.y >= SCREEN_H) + { + if (mouse.leftButtonPressed && !(mouse.buttonState & SDL_BUTTON_LMASK)) + sendMouseButtonUpEvent(SDL_BUTTON_LEFT); + + if (mouse.rightButtonPressed && !(mouse.buttonState & SDL_BUTTON_RMASK)) + sendMouseButtonUpEvent(SDL_BUTTON_RIGHT); + } } } @@ -769,88 +773,102 @@ void updateMouseScaling(void) if (video.renderH > 0.0) video.dMouseYMul = (double)SCREEN_H / video.renderH; } +void sendMouseButtonUpEvent(uint8_t button) +{ + SDL_Event event; + + memset(&event, 0, sizeof (event)); + + event.type = SDL_MOUSEBUTTONUP; + event.button.button = button; + + SDL_PushEvent(&event); +} + void readMouseXY(void) { - int16_t x, y; - int32_t mx, my; + int32_t mx, my, windowX, windowY; if (mouse.setPosFlag) { - mouse.setPosFlag = false; - - if (SDL_GetWindowFlags(video.window) & SDL_WINDOW_SHOWN) + if (!video.windowHidden) SDL_WarpMouseInWindow(video.window, mouse.setPosX, mouse.setPosY); + mouse.setPosFlag = false; return; } - SDL_PumpEvents(); // gathers all pending input from devices into the event queue (less mouse lag) - SDL_GetMouseState(&mx, &my); + mouse.buttonState = SDL_GetGlobalMouseState(&mx, &my); - /* in centered fullscreen mode, trap the mouse inside the framed image - ** and subtract the coords to match the OS mouse position (fixes touch from touchscreens) */ - if (video.fullscreen && !(config.windowFlags & FILTERING)) + if (video.fullscreen) { - if (mx < video.renderX) - { - mx = video.renderX; - SDL_WarpMouseInWindow(video.window, mx, my); - } - else if (mx >= video.renderX+video.renderW) - { - mx = (video.renderX + video.renderW) - 1; - SDL_WarpMouseInWindow(video.window, mx, my); - } + /* If fullscreen without filtering mode, translate coords and warp mouse + ** inside the render space. + ** Fullscreen + filtering mode takes up 100% of the screen area used, so no + ** need to translate coords in that mode. + */ - if (my < video.renderY) + if (!(config.windowFlags & FILTERING)) { - my = video.renderY; - SDL_WarpMouseInWindow(video.window, mx, my); - } - else if (my >= video.renderY+video.renderH) - { - my = (video.renderY + video.renderH) - 1; - SDL_WarpMouseInWindow(video.window, mx, my); - } + if (!(config.specialFlags2 & HARDWARE_MOUSE)) + { + bool warpMouse = false; + + if (mx < video.renderX) + { + mx = video.renderX; + warpMouse = true; + } + else if (mx >= video.renderX+video.renderW) + { + mx = (video.renderX + video.renderW) - 1; + warpMouse = true; + } + + if (my < video.renderY) + { + my = video.renderY; + warpMouse = true; + } + else if (my >= video.renderY+video.renderH) + { + my = (video.renderY + video.renderH) - 1; + warpMouse = true; + } - mx -= video.renderX; - my -= video.renderY; + if (warpMouse) + SDL_WarpMouseInWindow(video.window, mx, my); + } + + // convert fullscreen coords to window (centered image) coords + mx -= video.renderX; + my -= video.renderY; + } } + else + { + // convert desktop coords to window coords - if (mx < 0) mx = 0; - if (my < 0) mx = 0; + // (a call to this function is really fast in windowed mode) + SDL_GetWindowPosition(video.window, &windowX, &windowY); - // multiply coords by video upscaling factors (don't round) - mx = (int32_t)(mx * video.dMouseXMul); - my = (int32_t)(my * video.dMouseYMul); + mx -= windowX; + my -= windowY; + } - if (mx >= SCREEN_W) mx = SCREEN_W - 1; - if (my >= SCREEN_H) my = SCREEN_H - 1; + // multiply coords by video upscaling factors (don't round) + mouse.x = (int32_t)(mx * video.dMouseXMul); + mouse.y = (int32_t)(my * video.dMouseYMul); if (config.specialFlags2 & HARDWARE_MOUSE) { - // hardware mouse (OS) - mouse.x = (int16_t)mx; - mouse.y = (int16_t)my; + // hardware mouse mode (OS) hideSprite(SPRITE_MOUSE_POINTER); } else { - // software mouse (FT2 mouse) - x = (int16_t)mx; - y = (int16_t)my; - - mouse.x = x; - mouse.y = y; - - // for text editing cursor (do this after clamp) - x += mouse.xBias; - y += mouse.yBias; - - if (x < 0) x = 0; - if (y < 0) y = 0; - - setSpritePos(SPRITE_MOUSE_POINTER, x, y); + // software mouse mode (FT2 mouse) + setSpritePos(SPRITE_MOUSE_POINTER, mouse.x + mouse.xBias, mouse.y + mouse.yBias); } changeCursorIfOverTextBoxes(); diff --git a/src/ft2_mouse.h b/src/ft2_mouse.h @@ -19,9 +19,10 @@ struct mouse_t bool leftButtonPressed, rightButtonPressed, leftButtonReleased, rightButtonReleased; bool firstTimePressingButton, mouseOverTextBox; int8_t buttonCounter, mode; - int16_t lastUsedObjectID, lastUsedObjectType, lastEditBox, x, y, lastX, lastY, xBias, yBias; - int16_t lastScrollX, lastScrollXTmp, lastScrollY, saveMouseX, saveMouseY; - int32_t setPosX, setPosY; + int16_t lastUsedObjectID, lastUsedObjectType, lastEditBox; + int32_t x, y, lastX, lastY, xBias, yBias, setPosX, setPosY; + int32_t lastScrollX, lastScrollXTmp, lastScrollY, saveMouseX, saveMouseY; + uint32_t buttonState; } mouse; // do not change these! @@ -30,6 +31,7 @@ struct mouse_t #define MOUSE_GLASS_ANI_FRAMES 22 #define MOUSE_CLOCK_ANI_FRAMES 5 +void sendMouseButtonUpEvent(uint8_t button); void freeMouseCursors(void); bool createMouseCursors(void); void setMousePosToCenter(void); diff --git a/src/ft2_sample_ed.c b/src/ft2_sample_ed.c @@ -1440,7 +1440,7 @@ void rangeAll(void) smpEd_Rx2 = smpEd_ScrPos + smpEd_ViewSize; } -static void zoomSampleDataIn(int32_t step, int16_t x) +static void zoomSampleDataIn(int32_t step, int32_t x) { int32_t tmp32, minViewSize; int64_t newScrPos64; @@ -1482,7 +1482,7 @@ static void zoomSampleDataIn(int32_t step, int16_t x) updateScrPos(); } -static void zoomSampleDataOut(int32_t step, int16_t x) +static void zoomSampleDataOut(int32_t step, int32_t x) { int32_t tmp32; int64_t newViewSize64; diff --git a/src/ft2_sample_ed_features.c b/src/ft2_sample_ed_features.c @@ -43,11 +43,6 @@ static void windowOpen(void) editor.ui.sysReqShown = true; editor.ui.sysReqEnterPressed = false; -#ifndef __APPLE__ - if (!video.fullscreen) // release mouse button trap - SDL_SetWindowGrab(video.window, SDL_FALSE); -#endif - unstuckLastUsedGUIElement(); SDL_EventState(SDL_DROPFILE, SDL_DISABLE); } diff --git a/src/ft2_scrollbars.c b/src/ft2_scrollbars.c @@ -20,6 +20,12 @@ #include "ft2_video.h" #include "ft2_palette.h" +/* Prevent the scrollbar thumbs from being so small that +** it's difficult to use them. In units of pixels. +** Shouldn't be higher than 9! +*/ +#define MIN_THUMB_LENGTH 9 + scrollBar_t scrollBars[NUM_SCROLLBARS] = { // ------ RESERVED SCROLLBARS ------ @@ -168,7 +174,9 @@ void hideScrollBar(uint16_t scrollBarID) static void setScrollBarThumbCoords(uint16_t scrollBarID) { - int16_t tmp16, thumbX, thumbY, thumbW, thumbH, scrollEnd; + int16_t thumbX, thumbY, thumbW, thumbH, scrollEnd, realThumbLength; + int32_t tmp32, length, end; + double dTmp; scrollBar_t *scrollBar; assert(scrollBarID < NUM_SCROLLBARS); @@ -196,11 +204,18 @@ static void setScrollBarThumbCoords(uint16_t scrollBarID) if (scrollBar->thumbType == SCROLLBAR_THUMB_NOFLAT) { - thumbW = 15; + realThumbLength = 15; + + thumbW = realThumbLength; + if (thumbW < MIN_THUMB_LENGTH) + thumbW = MIN_THUMB_LENGTH; + if (scrollBar->end > 0) { - tmp16 = (int16_t)round(((scrollBar->w - thumbW) / (double)scrollBar->end) * scrollBar->pos); - thumbX = scrollBar->x + tmp16; + length = scrollBar->w - realThumbLength; + dTmp = (length / (double)scrollBar->end) * scrollBar->pos; + tmp32 = (int32_t)(dTmp + 0.5); + thumbX = (int16_t)(scrollBar->x + tmp32); } else { @@ -211,18 +226,27 @@ static void setScrollBarThumbCoords(uint16_t scrollBarID) { if (scrollBar->end > 0) { - tmp16 = (int16_t)round((scrollBar->w / (double)scrollBar->end) * scrollBar->page); - thumbW = CLAMP(tmp16, 1, scrollBar->w); + dTmp = (scrollBar->w / (double)scrollBar->end) * scrollBar->page; + tmp32 = (int32_t)(dTmp + 0.5); + realThumbLength = (int16_t)CLAMP(tmp32, 1, scrollBar->w); } else { - thumbW = 1; + realThumbLength = 1; } + thumbW = realThumbLength; + if (thumbW < MIN_THUMB_LENGTH) + thumbW = MIN_THUMB_LENGTH; + if (scrollBar->end > scrollBar->page) { - tmp16 = (int16_t)round(((scrollBar->w - thumbW) / (double)(scrollBar->end - scrollBar->page)) * scrollBar->pos); - thumbX = scrollBar->x + tmp16; + length = scrollBar->w - thumbW; + end = scrollBar->end - scrollBar->page; + + dTmp = (length / (double)end) * scrollBar->pos; + tmp32 = (int32_t)(dTmp + 0.5); + thumbX = (int16_t)(scrollBar->x + tmp32); } else { @@ -231,7 +255,7 @@ static void setScrollBarThumbCoords(uint16_t scrollBarID) } // prevent scrollbar thumb coords from being outside of the scrollbar area - thumbX = CLAMP(thumbX, scrollBar->x, scrollEnd - 1); + thumbX = CLAMP(thumbX, scrollBar->x, scrollEnd-1); if (thumbX+thumbW > scrollEnd) thumbW = scrollEnd - thumbX; } @@ -245,18 +269,27 @@ static void setScrollBarThumbCoords(uint16_t scrollBarID) if (scrollBar->end > 0) { - tmp16 = (int16_t)round((scrollBar->h / (double)scrollBar->end) * scrollBar->page); - thumbH = CLAMP(tmp16, 1, scrollBar->h); + dTmp = (scrollBar->h / (double)scrollBar->end) * scrollBar->page; + tmp32 = (int32_t)(dTmp + 0.5); + realThumbLength = (int16_t)CLAMP(tmp32, MIN_THUMB_LENGTH, scrollBar->h); } else { - thumbH = 1; + realThumbLength = 1; } + thumbH = realThumbLength; + if (thumbW < MIN_THUMB_LENGTH) + thumbW = MIN_THUMB_LENGTH; + if (scrollBar->end > scrollBar->page) { - tmp16 = (int16_t)round(((scrollBar->h - thumbH) / (double)(scrollBar->end - scrollBar->page)) * scrollBar->pos); - thumbY = scrollBar->y + tmp16; + length = scrollBar->h - thumbH; + end = scrollBar->end - scrollBar->page; + + dTmp = (length / (double)end) * scrollBar->pos; + tmp32 = (int32_t)(dTmp + 0.5); + thumbY = (int16_t)(scrollBar->y + tmp32); } else { @@ -270,6 +303,7 @@ static void setScrollBarThumbCoords(uint16_t scrollBarID) } // set values now + scrollBar->realThumbLength = realThumbLength; scrollBar->thumbX = thumbX; scrollBar->thumbY = thumbY; scrollBar->thumbW = thumbW; @@ -463,7 +497,7 @@ void setScrollBarPageLength(uint16_t scrollBarID, uint32_t pageLength) bool testScrollBarMouseDown(void) { uint16_t start, end; - int32_t scrollPos; + int32_t scrollPos, length; double dTmp; scrollBar_t *scrollBar; @@ -506,18 +540,26 @@ bool testScrollBarMouseDown(void) } else { - mouse.saveMouseX = (int16_t)round(scrollBar->thumbW / 2.0); + mouse.saveMouseX = scrollBar->thumbW >> 1; scrollPos = mouse.lastScrollX - scrollBar->x - mouse.saveMouseX; if (scrollBar->thumbType == SCROLLBAR_THUMB_NOFLAT) - scrollPos = (int32_t)round(scrollPos * (scrollBar->w / (double)(scrollBar->w - 15))); + { + dTmp = scrollPos * (scrollBar->w / (double)(scrollBar->w - scrollBar->thumbW)); + scrollPos = (int32_t)(dTmp + 0.5); + } + assert(scrollBar->w > 0); scrollPos = CLAMP(scrollPos, 0, scrollBar->w); - assert(scrollBar->w > 0); + length = scrollBar->w + (scrollBar->realThumbLength - scrollBar->thumbW); + if (length < 1) + length = 1; - dTmp = round(((double)scrollPos * scrollBar->end) / scrollBar->w); - setScrollBarPos(mouse.lastUsedObjectID, (int32_t)dTmp, true); + dTmp = ((double)scrollPos * scrollBar->end) / length; + scrollPos = (int32_t)(dTmp + 0.5); + + setScrollBarPos(mouse.lastUsedObjectID, scrollPos, true); } } else @@ -529,15 +571,21 @@ bool testScrollBarMouseDown(void) } else { - mouse.saveMouseY = (int16_t)(scrollBar->thumbH / 2.0); // truncate here + mouse.saveMouseY = scrollBar->thumbH >> 1; scrollPos = mouse.lastScrollY - scrollBar->y - mouse.saveMouseY; - scrollPos = CLAMP(scrollPos, 0, scrollBar->h); assert(scrollBar->h > 0); + scrollPos = CLAMP(scrollPos, 0, scrollBar->h); + + length = scrollBar->h + (scrollBar->realThumbLength - scrollBar->thumbW); + if (length < 1) + length = 1; + + dTmp = ((double)scrollPos * scrollBar->end) / length; + scrollPos = (int32_t)(dTmp + 0.5); - dTmp = round(((double)scrollPos * scrollBar->end) / scrollBar->h); - setScrollBarPos(mouse.lastUsedObjectID, (int32_t)dTmp, true); + setScrollBarPos(mouse.lastUsedObjectID, scrollPos, true); } } @@ -575,7 +623,7 @@ void testScrollBarMouseRelease(void) void handleScrollBarsWhileMouseDown(void) { - int16_t scrollX, scrollY; + int32_t scrollX, scrollY, length; double dTmp; scrollBar_t *scrollBar; @@ -594,15 +642,21 @@ void handleScrollBarsWhileMouseDown(void) if (scrollBar->thumbType == SCROLLBAR_THUMB_NOFLAT) { assert(scrollBar->w >= 16); - scrollX = (int16_t)round(scrollX * (scrollBar->w / (double)(scrollBar->w - 15))); + dTmp = scrollX * (scrollBar->w / (double)(scrollBar->w - scrollBar->thumbW)); + scrollX = (int32_t)(dTmp + 0.5); } + assert(scrollBar->w > 0); scrollX = CLAMP(scrollX, 0, scrollBar->w); - assert(scrollBar->w > 0); + length = scrollBar->w + (scrollBar->realThumbLength - scrollBar->thumbW); + if (length < 1) + length = 1; - dTmp = round(((double)scrollX * scrollBar->end) / scrollBar->w); - setScrollBarPos(mouse.lastUsedObjectID, (int32_t)dTmp, true); + dTmp = ((double)scrollX * scrollBar->end) / length; + scrollX = (int32_t)(dTmp + 0.5); + + setScrollBarPos(mouse.lastUsedObjectID, scrollX, true); if (mouse.lastUsedObjectID != OBJECT_ID_NONE) // this can change in the callback in setScrollBarPos() drawScrollBar(mouse.lastUsedObjectID); @@ -617,10 +671,14 @@ void handleScrollBarsWhileMouseDown(void) scrollY = mouse.lastScrollY - mouse.saveMouseY - scrollBar->y; scrollY = CLAMP(scrollY, 0, scrollBar->h); - assert(scrollBar->h > 0); + length = scrollBar->h + (scrollBar->realThumbLength - scrollBar->thumbH); + if (length < 1) + length = 1; + + dTmp = ((double)scrollY * scrollBar->end) / length; + scrollY = (int32_t)(dTmp + 0.5); - dTmp = round(((double)scrollY * scrollBar->end) / scrollBar->h); - setScrollBarPos(mouse.lastUsedObjectID, (int32_t)dTmp, true); + setScrollBarPos(mouse.lastUsedObjectID, scrollY, true); if (mouse.lastUsedObjectID != OBJECT_ID_NONE) // this can change in the callback in setScrollBarPos() drawScrollBar(mouse.lastUsedObjectID); diff --git a/src/ft2_scrollbars.h b/src/ft2_scrollbars.h @@ -76,7 +76,7 @@ typedef struct scrollBar_t // DO NOT TOUCH! uint8_t state; uint32_t pos, page, end; uint32_t oldPos, oldPage, oldEnd; - uint16_t thumbX, thumbY, thumbW, thumbH; + uint16_t thumbX, thumbY, thumbW, thumbH, realThumbLength; } scrollBar_t; void drawScrollBar(uint16_t scrollBarID); diff --git a/src/ft2_sysreqs.c b/src/ft2_sysreqs.c @@ -161,11 +161,6 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) pushButton_t *p; checkBox_t *c; -#ifndef __APPLE__ - if (!video.fullscreen) // release mouse button trap - SDL_SetWindowGrab(video.window, SDL_FALSE); -#endif - if (editor.editTextFlag) exitTextEditing(); @@ -411,11 +406,6 @@ int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxSt return 0; } -#ifndef __APPLE__ - if (!video.fullscreen) // release mouse button trap - SDL_SetWindowGrab(video.window, SDL_FALSE); -#endif - SDL_EventState(SDL_DROPFILE, SDL_DISABLE); editor.ui.sysReqShown = true; diff --git a/src/ft2_textboxes.c b/src/ft2_textboxes.c @@ -63,7 +63,8 @@ textBox_t textBoxes[NUM_TEXTBOXES] = }; static int16_t markX1, markX2; -static uint16_t oldCursorPos, oldMouseX; +static uint16_t oldCursorPos; +static int32_t oldMouseX; static void moveTextCursorLeft(int16_t i, bool updateTextBox); static void moveTextCursorRight(int16_t i, bool updateTextBox); diff --git a/src/ft2_video.c b/src/ft2_video.c @@ -376,7 +376,7 @@ bool setupSprites(void) return true; } -void changeSpriteData(uint8_t sprite, const uint8_t *data) +void changeSpriteData(int32_t sprite, const uint8_t *data) { sprites[sprite].data = data; memset(sprites[sprite].refreshBuffer, 0, sprites[sprite].w * sprites[sprite].h * sizeof (int32_t)); @@ -384,7 +384,7 @@ void changeSpriteData(uint8_t sprite, const uint8_t *data) void freeSprites(void) { - for (uint32_t i = 0; i < SPRITE_NUM; i++) + for (int32_t i = 0; i < SPRITE_NUM; i++) { if (sprites[i].refreshBuffer != NULL) { @@ -404,18 +404,18 @@ void setRightLoopPinState(bool clicked) changeSpriteData(SPRITE_RIGHT_LOOP_PIN, clicked ? &bmp.loopPins[3*(154*16)] : &bmp.loopPins[2*(154*16)]); } -int32_t getSpritePosX(uint8_t sprite) +int32_t getSpritePosX(int32_t sprite) { return sprites[sprite].x; } -void setSpritePos(uint8_t sprite, int16_t x, int16_t y) +void setSpritePos(int32_t sprite, int32_t x, int32_t y) { - sprites[sprite].newX = x; - sprites[sprite].newY = y; + sprites[sprite].newX = (int16_t)x; + sprites[sprite].newY = (int16_t)y; } -void hideSprite(uint8_t sprite) +void hideSprite(int32_t sprite) { sprites[sprite].newX = SCREEN_W; } @@ -423,7 +423,7 @@ void hideSprite(uint8_t sprite) void eraseSprites(void) { int8_t i; - register int32_t x, y, sw, sh, srcPitch, dstPitch; + int32_t sx, sy, x, y, sw, sh, srcPitch, dstPitch; const uint32_t *src32; uint32_t *dst32; sprite_t *s; @@ -431,28 +431,36 @@ void eraseSprites(void) for (i = SPRITE_NUM-1; i >= 0; i--) // erasing must be done in reverse order { s = &sprites[i]; - if (s->x >= SCREEN_W) // sprite is hidden, don't erase + if (s->x >= SCREEN_W || s->y >= SCREEN_H) // sprite is hidden, don't draw nor fill clear buffer continue; - assert(s->y >= 0 && s->refreshBuffer != NULL); + assert(s->refreshBuffer != NULL); sw = s->w; sh = s->h; - x = s->x; - y = s->y; + sx = s->x; + sy = s->y; + + // if x is negative, adjust variables + if (sx < 0) + { + sw += sx; // subtraction + sx = 0; + } - // if x is negative, adjust variables (can only happen on loop pins in smp. ed.) - if (x < 0) + // if y is negative, adjust variables + if (sy < 0) { - sw += x; // subtraction - x = 0; + sh += sy; // subtraction + sy = 0; } src32 = s->refreshBuffer; - dst32 = &video.frameBuffer[(y * SCREEN_W) + x]; + dst32 = &video.frameBuffer[(sy * SCREEN_W) + sx]; - if (y+sh >= SCREEN_H) sh = SCREEN_H - y; - if (x+sw >= SCREEN_W) sw = SCREEN_W - x; + // handle x/y clipping + if (sx+sw >= SCREEN_W) sw = SCREEN_W - sx; + if (sy+sh >= SCREEN_H) sh = SCREEN_H - sy; srcPitch = s->w - sw; dstPitch = SCREEN_W - sw; @@ -471,7 +479,7 @@ void eraseSprites(void) void renderSprites(void) { const uint8_t *src8; - register int32_t x, y, sw, sh, srcPitch, dstPitch; + int32_t sx, sy, x, y, sw, sh, srcPitch, dstPitch; uint32_t i, *clr32, *dst32, windowFlags; sprite_t *s; @@ -495,20 +503,42 @@ void renderSprites(void) s->x = s->newX; s->y = s->newY; - if (s->x >= SCREEN_W) // sprite is hidden, don't draw nor fill clear buffer + if (s->x >= SCREEN_W || s->y >= SCREEN_H) // sprite is hidden, don't draw nor fill clear buffer continue; - assert(s->x >= 0 && s->y >= 0 && s->data != NULL && s->refreshBuffer != NULL); + assert(s->data != NULL && s->refreshBuffer != NULL); sw = s->w; sh = s->h; + sx = s->x; + sy = s->y; src8 = s->data; - dst32 = &video.frameBuffer[(s->y * SCREEN_W) + s->x]; + + // if x is negative, adjust variables + if (sx < 0) + { + sw += sx; // subtraction + src8 -= sx; // addition + sx = 0; + } + + // if y is negative, adjust variables + if (sy < 0) + { + sh += sy; // subtraction + src8 += (-sy * s->w); // addition + sy = 0; + } + + if (sw <= 0 || sh <= 0) // sprite is hidden, don't draw nor fill clear buffer + continue; + + dst32 = &video.frameBuffer[(sy * SCREEN_W) + sx]; clr32 = s->refreshBuffer; - // handle xy clipping - if (s->y+sh >= SCREEN_H) sh = SCREEN_H - s->y; - if (s->x+sw >= SCREEN_W) sw = SCREEN_W - s->x; + // handle x/y clipping + if (sx+sw >= SCREEN_W) sw = SCREEN_W - sx; + if (sy+sh >= SCREEN_H) sh = SCREEN_H - sy; srcPitch = s->w - sw; dstPitch = SCREEN_W - sw; @@ -570,8 +600,7 @@ void renderLoopPins(void) { uint8_t pal; const uint8_t *src8; - int32_t sx; - register int32_t x, y, sw, sh, srcPitch, dstPitch; + int32_t sx, x, y, sw, sh, srcPitch, dstPitch; uint32_t *clr32, *dst32; sprite_t *s; @@ -604,7 +633,7 @@ void renderLoopPins(void) dst32 = &video.frameBuffer[(s->y * SCREEN_W) + sx]; // handle x clipping - if (s->x+sw >= SCREEN_W) sw = SCREEN_W - s->x; + if (sx+sw >= SCREEN_W) sw = SCREEN_W - sx; srcPitch = s->w - sw; dstPitch = SCREEN_W - sw; @@ -663,7 +692,7 @@ void renderLoopPins(void) dst32 = &video.frameBuffer[(s->y * SCREEN_W) + sx]; // handle x clipping - if (s->x+sw >= SCREEN_W) sw = SCREEN_W - s->x; + if (sx+sw >= SCREEN_W) sw = SCREEN_W - sx; srcPitch = s->w - sw; dstPitch = SCREEN_W - sw; diff --git a/src/ft2_video.h b/src/ft2_video.h @@ -27,7 +27,7 @@ struct video_t SDL_Window *window; double dMonitorRefreshRate, dMouseXMul, dMouseYMul; uint8_t upscaleFactor; - bool vsync60HzPresent; + bool vsync60HzPresent, windowHidden; int32_t renderX, renderY, renderW, renderH, displayW, displayH; uint32_t *frameBufferUnaligned; SDL_Renderer *renderer; @@ -56,15 +56,15 @@ bool setupRenderer(void); void closeVideo(void); void setLeftLoopPinState(bool clicked); void setRightLoopPinState(bool clicked); -int32_t getSpritePosX(uint8_t sprite); +int32_t getSpritePosX(int32_t sprite); void eraseSprites(void); void renderLoopPins(void); void renderSprites(void); bool setupSprites(void); void freeSprites(void); -void setSpritePos(uint8_t sprite, int16_t x, int16_t y); -void changeSpriteData(uint8_t sprite, const uint8_t *data); -void hideSprite(uint8_t sprite); +void setSpritePos(int32_t sprite, int32_t x, int32_t y); +void changeSpriteData(int32_t sprite, const uint8_t *data); +void hideSprite(int32_t sprite); void handleRedrawing(void); void enterFullscreen(void); void leaveFullScreen(void);