ref: f4ba52e6835e5935ad294b7dbbfbce3fc36d40c1
dir: /src/pt2_keyboard.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 <stdint.h> #include <stdbool.h> #include <ctype.h> // tolower() #ifdef _WIN32 #include <windows.h> #else #include <unistd.h> #endif #include "pt2_textout.h" #include "pt2_helpers.h" #include "pt2_visuals.h" #include "pt2_diskop.h" #include "pt2_edit.h" #include "pt2_sampler.h" #include "pt2_audio.h" #include "pt2_tables.h" #include "pt2_module_saver.h" #include "pt2_sample_saver.h" #include "pt2_config.h" #include "pt2_sampling.h" #include "pt2_chordmaker.h" #include "pt2_askbox.h" #include "pt2_replayer.h" #if defined _WIN32 && !defined _DEBUG extern bool windowsKeyIsDown; extern HHOOK g_hKeyboardHook; #endif static bool handleGeneralModes(SDL_Scancode scancode); static void movePatCurPrevCh(void); static void movePatCurNextCh(void); static void movePatCurRight(void); static void movePatCurLeft(void); bool handleTextEditMode(SDL_Scancode scancode); void gotoNextMulti(void) { cursor.channel = (editor.multiModeNext[cursor.channel] - 1) & 3; cursor.pos = cursor.channel * 6; updateCursorPos(); } void readKeyModifiers(void) { const SDL_Keymod modState = SDL_GetModState(); keyb.leftCtrlPressed = (modState & KMOD_LCTRL) ? true : false; keyb.leftAltPressed = (modState & KMOD_LALT) ? true : false; keyb.shiftPressed = (modState & (KMOD_LSHIFT + KMOD_RSHIFT)) ? true : false; #ifdef __APPLE__ keyb.leftCommandPressed = (modState & KMOD_LGUI) ? true : false; #endif #if defined _WIN32 && !defined _DEBUG keyb.leftAmigaPressed = windowsKeyIsDown; // Windows: handled in lowLevelKeyboardProc #else keyb.leftAmigaPressed = (modState & KMOD_LGUI) ? true : false; #endif } #if defined _WIN32 && !defined _DEBUG /* For taking control over the windows key if the program has focus. ** Warning: Don't do this in debug mode, it will completely ruin the keyboard input ** latency (in the OS in general) when the debugger is breaking. */ LRESULT CALLBACK lowLevelKeyboardProc(int32_t nCode, WPARAM wParam, LPARAM lParam) { SDL_Window *window = video.window; if (nCode == HC_ACTION && window != NULL) { switch (wParam) { case WM_KEYUP: case WM_KEYDOWN: case WM_SYSKEYUP: // needed to prevent stuck Windows key if used with ALT { const bool windowHasFocus = SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS; if (!windowHasFocus) { windowsKeyIsDown = false; break; } if (((KBDLLHOOKSTRUCT *)lParam)->vkCode != VK_LWIN) break; windowsKeyIsDown = (wParam == WM_KEYDOWN); return 1; // eat keystroke } break; default: break; } } return CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam); } #endif // these four functions are for the text edit cursor void textMarkerMoveLeft(void) { if (ui.dstPos > 0) { removeTextEditMarker(); ui.dstPos--; ui.lineCurX -= FONT_CHAR_W; renderTextEditMarker(); } else { if (ui.dstOffset != NULL) { (*ui.dstOffset)--; if (ui.editObject == PTB_DO_DATAPATH) ui.updateDiskOpPathText = true; } } } void textMarkerMoveRight(void) { if (ui.editTextType == TEXT_EDIT_STRING) { if (ui.dstPos < ui.textLength-1) { removeTextEditMarker(); ui.dstPos++; ui.lineCurX += FONT_CHAR_W; renderTextEditMarker(); } else { if (ui.dstOffset != NULL) { (*ui.dstOffset)++; if (ui.editObject == PTB_DO_DATAPATH) ui.updateDiskOpPathText = true; } } } else { // we end up here when entering a number/hex digit if (ui.dstPos < ui.numLen) removeTextEditMarker(); ui.dstPos++; ui.lineCurX += FONT_CHAR_W; if (ui.dstPos < ui.numLen) renderTextEditMarker(); // don't clamp, dstPos is tested elsewhere to check if done editing a number } } void textCharPrevious(void) { if (ui.editTextType != TEXT_EDIT_STRING) { if (ui.dstPos > 0) { removeTextEditMarker(); ui.dstPos--; ui.lineCurX -= FONT_CHAR_W; renderTextEditMarker(); } return; } if (editor.mixFlag && ui.dstPos <= 4) return; if (ui.editPos > ui.showTextPtr) { removeTextEditMarker(); ui.editPos--; textMarkerMoveLeft(); if (editor.mixFlag) // special case for "Mix" input field in Edit. Op. { if (ui.dstPos == 12) { ui.editPos--; textMarkerMoveLeft(); ui.editPos--; textMarkerMoveLeft(); ui.editPos--; textMarkerMoveLeft(); ui.editPos--; textMarkerMoveLeft(); } else if (ui.dstPos == 6) { ui.editPos--; textMarkerMoveLeft(); } } renderTextEditMarker(); } ui.dstOffsetEnd = false; } void textCharNext(void) { if (ui.editTextType != TEXT_EDIT_STRING) { if (ui.dstPos < ui.numLen-1) { removeTextEditMarker(); ui.dstPos++; ui.lineCurX += FONT_CHAR_W; renderTextEditMarker(); } return; } if (editor.mixFlag && ui.dstPos >= 14) return; if (ui.editPos < ui.textEndPtr) { if (*ui.editPos != '\0') { removeTextEditMarker(); ui.editPos++; textMarkerMoveRight(); if (editor.mixFlag) // special case for "Mix" input field in Edit. Op. { if (ui.dstPos == 9) { ui.editPos++; textMarkerMoveRight(); ui.editPos++; textMarkerMoveRight(); ui.editPos++; textMarkerMoveRight(); ui.editPos++; textMarkerMoveRight(); } else if (ui.dstPos == 6) { ui.editPos++; textMarkerMoveRight(); } } renderTextEditMarker(); } else { ui.dstOffsetEnd = true; } } else { ui.dstOffsetEnd = true; } } // -------------------------------- void keyUpHandler(SDL_Scancode scancode) { if (scancode == SDL_SCANCODE_KP_PLUS) keyb.keypadEnterPressed = false; if (scancode == keyb.lastRepKey) keyb.lastRepKey = SDL_SCANCODE_UNKNOWN; switch (scancode) { // modifiers shouldn't reset keyb repeat/delay flags & counters case SDL_SCANCODE_LCTRL: case SDL_SCANCODE_RCTRL: case SDL_SCANCODE_LSHIFT: case SDL_SCANCODE_RSHIFT: case SDL_SCANCODE_LALT: case SDL_SCANCODE_RALT: case SDL_SCANCODE_LGUI: case SDL_SCANCODE_RGUI: case SDL_SCANCODE_MENU: case SDL_SCANCODE_MODE: case SDL_SCANCODE_CAPSLOCK: break; default: { keyb.repeatKey = false; keyb.delayKey = false; keyb.repeatFrac = 0; keyb.delayCounter = 0; } break; } } static void incMulti(uint8_t slot) { char str[32]; assert(slot < 4); if (editor.multiModeNext[slot] == 4) editor.multiModeNext[slot] = 1; else editor.multiModeNext[slot]++; sprintf(str, "MULTI=%d-%d-%d-%d", editor.multiModeNext[0], editor.multiModeNext[1], editor.multiModeNext[2], editor.multiModeNext[3]); displayMsg(str); } void keyDownHandler(SDL_Scancode scancode, SDL_Keycode keycode) { if (scancode == SDL_SCANCODE_CAPSLOCK) { editor.repeatKeyFlag ^= 1; return; } // kludge: allow certain specific key combos to be repeated with the ctrl key const bool nonRepeatAltKeys = keyb.leftAltPressed && scancode != SDL_SCANCODE_DELETE && scancode != SDL_SCANCODE_RETURN && scancode != SDL_SCANCODE_BACKSPACE && scancode != SDL_SCANCODE_BACKSLASH && scancode != SDL_SCANCODE_EQUALS && scancode != SDL_SCANCODE_MINUS && scancode < SDL_SCANCODE_1 && scancode > SDL_SCANCODE_0; // kludge: allow certain specific key combos to be repeated with the alt key const bool nonRepeatCtrlKeys = keyb.leftCtrlPressed && scancode != SDL_SCANCODE_DELETE && scancode != SDL_SCANCODE_RETURN && scancode != SDL_SCANCODE_BACKSPACE; // these keys should not allow to be repeated in keyrepeat mode (caps lock) const bool nonRepeatKeys = keyb.leftAmigaPressed || nonRepeatAltKeys || nonRepeatCtrlKeys || scancode == SDL_SCANCODE_LEFT || scancode == SDL_SCANCODE_RIGHT || scancode == SDL_SCANCODE_UP || scancode == SDL_SCANCODE_DOWN; if (editor.repeatKeyFlag && keyb.repeatKey && scancode == keyb.lastRepKey && nonRepeatKeys) return; if (scancode == SDL_SCANCODE_KP_PLUS) keyb.keypadEnterPressed = true; // TOGGLE FULLSCREEN (should always react) if (scancode == SDL_SCANCODE_F11 && !keyb.leftAltPressed) { toggleFullscreen(); // prevent fullscreen toggle from firing twice on certain SDL2 Linux ports #ifdef __unix__ SDL_Delay(100); #endif return; } // don't handle input if an error message wait is active or if an unknown key is passed if ((editor.errorMsgActive && editor.errorMsgBlock) || scancode == SDL_SCANCODE_UNKNOWN) return; // if no ALT/SHIFT/CTRL/AMIGA, update last key for repeat routine if (scancode != SDL_SCANCODE_LALT && scancode != SDL_SCANCODE_RALT && scancode != SDL_SCANCODE_LCTRL && scancode != SDL_SCANCODE_RCTRL && scancode != SDL_SCANCODE_LSHIFT && scancode != SDL_SCANCODE_RSHIFT && scancode != SDL_SCANCODE_LGUI && scancode != SDL_SCANCODE_RGUI && scancode != SDL_SCANCODE_MENU && scancode != SDL_SCANCODE_MODE && scancode != SDL_SCANCODE_CAPSLOCK && scancode != SDL_SCANCODE_ESCAPE) { if (editor.repeatKeyFlag) { // if Repeat Flag, repeat all keys if (!keyb.repeatKey) { keyb.repeatCounter = 0; keyb.repeatFrac = 0; } keyb.repeatKey = true; keyb.delayKey = true; } keyb.repeatCounter = 0; keyb.repeatFrac = 0; keyb.lastRepKey = scancode; } // ENTRY JUMPING IN DISK OP. FILELIST if (ui.diskOpScreenShown && keyb.shiftPressed && !ui.editTextFlag) { if (keycode >= 32 && keycode <= 126) { handleEntryJumping(keycode); return; } } // XXX: This really needs some refactoring, it's messy and not logical if (!handleGeneralModes(scancode)) return; if (!handleTextEditMode(scancode)) return; if (ui.samplerVolBoxShown || ui.samplingBoxShown) return; if (ui.samplerFiltersBoxShown) { handleEditKeys(scancode, EDIT_NORMAL); return; } // GENERAL KEYS switch (scancode) { case SDL_SCANCODE_NONUSBACKSLASH: turnOffVoices(); break; // magic "kill all voices" button case SDL_SCANCODE_APOSTROPHE: { if (editor.autoInsFlag) { if (keyb.shiftPressed) editor.autoInsSlot -= 4; else editor.autoInsSlot--; if (editor.autoInsSlot < 0) editor.autoInsSlot = 0; ui.updateTrackerFlags = true; } } break; case SDL_SCANCODE_BACKSLASH: { if (keyb.leftAltPressed) { if (handleSpecialKeys(scancode) && editor.currMode != MODE_RECORD) modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 63); } else { if (editor.autoInsFlag) { if (keyb.shiftPressed) editor.autoInsSlot += 4; else editor.autoInsSlot++; if (editor.autoInsSlot > 9) editor.autoInsSlot = 9; } else { editor.pNoteFlag = (editor.pNoteFlag + 1) % 3; } ui.updateTrackerFlags = true; } } break; #ifdef __APPLE__ case SDL_SCANCODE_RGUI: #else case SDL_SCANCODE_RALT: #endif { // right Amiga key on Amiga keyb if (!ui.askBoxShown) { editor.playMode = PLAY_MODE_NORMAL; modPlay(DONT_SET_PATTERN, song->currOrder, DONT_SET_ROW); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } } break; #ifdef __APPLE__ case SDL_SCANCODE_RALT: #else case SDL_SCANCODE_RCTRL: #endif { // right alt on Amiga keyb if (!ui.askBoxShown) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, DONT_SET_ROW); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } } break; case SDL_SCANCODE_RSHIFT: { // right shift on Amiga keyb if (!ui.samplerScreenShown && !ui.askBoxShown) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, DONT_SET_ROW); editor.currMode = MODE_RECORD; pointerSetMode(POINTER_MODE_EDIT, DO_CARRY); statusAllRight(); } } break; case SDL_SCANCODE_ESCAPE: { if (ui.posEdScreenShown) { ui.posEdScreenShown = false; displayMainScreen(); } else if (ui.diskOpScreenShown) { ui.diskOpScreenShown = false; displayMainScreen(); } else if (ui.samplerScreenShown) { exitFromSam(); } else if (ui.editOpScreenShown) { ui.editOpScreenShown = false; displayMainScreen(); } else { if (askBox(ASKBOX_YES_NO, "REALLY QUIT ?")) { ui.throwExit = true; return; } } pointerSetPreviousMode(); setPrevStatusMessage(); } break; case SDL_SCANCODE_INSERT: { if (ui.samplerScreenShown) { samplerSamPaste(); return; } } break; case SDL_SCANCODE_PAGEUP: { if (ui.posEdScreenShown) { if (song->currOrder > 0) { if (song->currOrder-(POSED_LIST_SIZE-1) > 0) modSetPos(song->currOrder-(POSED_LIST_SIZE-1), DONT_SET_ROW); else modSetPos(0, DONT_SET_ROW); } } else if (ui.diskOpScreenShown) { diskop.scrollOffset -= DISKOP_LINES - 1; if (diskop.scrollOffset < 0) diskop.scrollOffset = 0; ui.updateDiskOpFileList = true; } else { if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT) { if (song->currRow == 63) modSetPos(DONT_SET_ORDER, song->currRow - 15); else if (song->currRow == 15) modSetPos(DONT_SET_ORDER, 0); // 15-16 would turn into -1, which is "DON'T SET ROW" flag else modSetPos(DONT_SET_ORDER, song->currRow - 16); } } if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = true; } break; case SDL_SCANCODE_PAGEDOWN: { if (ui.posEdScreenShown) { if (song->currOrder != song->header.numOrders-1) { if (song->currOrder+(POSED_LIST_SIZE-1) <= song->header.numOrders-1) modSetPos(song->currOrder+(POSED_LIST_SIZE-1), DONT_SET_ROW); else modSetPos(song->header.numOrders - 1, DONT_SET_ROW); } } else if (ui.diskOpScreenShown) { if (diskop.numEntries > DISKOP_LINES) { diskop.scrollOffset += DISKOP_LINES-1; if (diskop.scrollOffset > diskop.numEntries-DISKOP_LINES) diskop.scrollOffset = diskop.numEntries-DISKOP_LINES; ui.updateDiskOpFileList = true; } } else { if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT) modSetPos(DONT_SET_ORDER, song->currRow + 16); } if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = true; } break; case SDL_SCANCODE_HOME: { if (ui.posEdScreenShown) { if (song->currOrder > 0) modSetPos(0, DONT_SET_ROW); } else if (ui.diskOpScreenShown) { if (diskop.scrollOffset != 0) { diskop.scrollOffset = 0; ui.updateDiskOpFileList = true; } } else { if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT) modSetPos(DONT_SET_ORDER, 0); } } break; case SDL_SCANCODE_END: { if (ui.posEdScreenShown) { modSetPos(song->header.numOrders - 1, DONT_SET_ROW); } else if (ui.diskOpScreenShown) { if (diskop.numEntries > DISKOP_LINES) { diskop.scrollOffset = diskop.numEntries - DISKOP_LINES; ui.updateDiskOpFileList = true; } } else { if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT) modSetPos(DONT_SET_ORDER, 63); } } break; case SDL_SCANCODE_DELETE: { if (ui.samplerScreenShown) samplerSamDelete(NO_SAMPLE_CUT); else handleEditKeys(scancode, EDIT_NORMAL); } break; case SDL_SCANCODE_F12: { if (keyb.leftCtrlPressed) { const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); editor.timingMode ^= 1; updateReplayerTimingMode(); if (editor.timingMode == TEMPO_MODE_VBLANK) { editor.oldTempo = song->currBPM; modSetTempo(125, false); } else { modSetTempo(editor.oldTempo, false); } if (audioWasntLocked) unlockAudio(); ui.updateSongTiming = true; } else if (keyb.shiftPressed) { toggleAmigaPanMode(); } else { toggleAmigaFilterModel(); } } break; case SDL_SCANCODE_RETURN: { if (keyb.shiftPressed || keyb.leftAltPressed || keyb.leftCtrlPressed) { saveUndo(); if (keyb.leftAltPressed && !keyb.leftCtrlPressed) { if (song->currRow < 63) { for (int32_t i = 0; i < PAULA_VOICES; i++) { int32_t j; for (j = 62; j >= song->currRow; j--) { note_t *noteSrc = &song->patterns[song->currPattern][(j * PAULA_VOICES) + i]; song->patterns[song->currPattern][((j + 1) * PAULA_VOICES) + i] = *noteSrc; } note_t *noteDst = &song->patterns[song->currPattern][((j + 1) * PAULA_VOICES) + i]; noteDst->period = 0; noteDst->sample = 0; noteDst->command = 0; noteDst->param = 0; } song->currRow++; updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } } else { if (song->currRow < 63) { int32_t i; for (i = 62; i >= song->currRow; i--) { note_t *noteSrc = &song->patterns[song->currPattern][((i + 0) * PAULA_VOICES) + cursor.channel]; note_t *noteDst = &song->patterns[song->currPattern][((i + 1) * PAULA_VOICES) + cursor.channel]; if (keyb.leftCtrlPressed) { noteDst->command = noteSrc->command; noteDst->param = noteSrc->param; } else { *noteDst = *noteSrc; } } note_t *noteDst = &song->patterns[song->currPattern][((i + 1) * PAULA_VOICES) + cursor.channel]; if (!keyb.leftCtrlPressed) { noteDst->period = 0; noteDst->sample = 0; } noteDst->command = 0; noteDst->param = 0; song->currRow++; updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } } } else { editor.stepPlayEnabled = true; editor.stepPlayBackwards = false; editor.stepPlayLastMode = editor.currMode; if (config.keepEditModeAfterStepPlay && editor.stepPlayLastMode == MODE_EDIT) doStopIt(false); else doStopIt(true); playPattern(song->currRow); if (config.keepEditModeAfterStepPlay && editor.stepPlayLastMode == MODE_EDIT) { pointerSetMode(POINTER_MODE_EDIT, DO_CARRY); editor.playMode = PLAY_MODE_NORMAL; editor.currMode = MODE_EDIT; } } } break; // toggle between IDLE and EDIT (IDLE if PLAY) case SDL_SCANCODE_SPACE: { if (editor.currMode == MODE_PLAY) { modStop(); editor.currMode = MODE_IDLE; pointerSetMode(POINTER_MODE_IDLE, DO_CARRY); statusAllRight(); } else if (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD) { if (!ui.samplerScreenShown) { modStop(); editor.currMode = MODE_IDLE; pointerSetMode(POINTER_MODE_IDLE, DO_CARRY); statusAllRight(); } } else { if (!ui.samplerScreenShown) { modStop(); editor.currMode = MODE_EDIT; pointerSetMode(POINTER_MODE_EDIT, DO_CARRY); statusAllRight(); } } } break; case SDL_SCANCODE_F1: editor.keyOctave = OCTAVE_LOW; break; case SDL_SCANCODE_F2: editor.keyOctave = OCTAVE_HIGH; break; case SDL_SCANCODE_F3: { if (ui.samplerScreenShown) { samplerSamDelete(SAMPLE_CUT); } else { if (keyb.shiftPressed) { // cut channel and put in buffer saveUndo(); note_t *noteDst = editor.trackBuffer; for (int32_t i = 0; i < MOD_ROWS; i++) { note_t *noteSrc = &song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; *noteDst++ = *noteSrc; noteSrc->period = 0; noteSrc->sample = 0; noteSrc->command = 0; noteSrc->param = 0; } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else if (keyb.leftAltPressed) { // cut pattern and put in buffer saveUndo(); memcpy(editor.patternBuffer, song->patterns[song->currPattern], sizeof (note_t) * (PAULA_VOICES * MOD_ROWS)); memset(song->patterns[song->currPattern], 0, sizeof (note_t) * (PAULA_VOICES * MOD_ROWS)); updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else if (keyb.leftCtrlPressed) { // cut channel commands and put in buffer saveUndo(); note_t *noteDst = editor.cmdsBuffer; for (int32_t i = 0; i < MOD_ROWS; i++) { note_t *noteSrc = &song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; *noteDst++ = *noteSrc; noteSrc->command = 0; noteSrc->param = 0; } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } } } break; case SDL_SCANCODE_F4: { if (ui.samplerScreenShown) { samplerSamCopy(); } else { if (keyb.shiftPressed) { // copy channel to buffer note_t *noteDst = editor.trackBuffer; for (int32_t i = 0; i < MOD_ROWS; i++) *noteDst++ = song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; } else if (keyb.leftAltPressed) { // copy pattern to buffer memcpy(editor.patternBuffer, song->patterns[song->currPattern], sizeof (note_t) * (PAULA_VOICES * MOD_ROWS)); } else if (keyb.leftCtrlPressed) { // copy channel commands to buffer note_t *noteDst = editor.cmdsBuffer; for (int32_t i = 0; i < MOD_ROWS; i++) { note_t *noteSrc = &song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; noteDst->command = noteSrc->command; noteDst->param = noteSrc->param; noteDst++; } } } } break; case SDL_SCANCODE_F5: { if (ui.samplerScreenShown) { samplerSamPaste(); } else { if (keyb.shiftPressed) { // paste channel buffer to channel saveUndo(); note_t *noteSrc = editor.trackBuffer; for (int32_t i = 0; i < MOD_ROWS; i++) song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel] = *noteSrc++; updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else if (keyb.leftAltPressed) { // paste pattern buffer to pattern saveUndo(); memcpy(song->patterns[song->currPattern], editor.patternBuffer, sizeof (note_t) * (PAULA_VOICES * MOD_ROWS)); updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else if (keyb.leftCtrlPressed) { // paste channel commands buffer to channel saveUndo(); note_t *noteSrc = editor.cmdsBuffer; for (int32_t i = 0; i < MOD_ROWS; i++) { note_t *noteDst = &song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; noteDst->command = noteSrc->command; noteDst->param = noteSrc->param; noteSrc++; } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } } } break; case SDL_SCANCODE_F6: { if (keyb.shiftPressed) { editor.f6Pos = song->currRow; displayMsg("POSITION SET"); } else { if (keyb.leftAltPressed) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, editor.f6Pos); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } else if (keyb.leftCtrlPressed) { if (!ui.samplerScreenShown) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, editor.f6Pos); editor.currMode = MODE_RECORD; pointerSetMode(POINTER_MODE_EDIT, DO_CARRY); statusAllRight(); } } else if (keyb.leftAmigaPressed) { editor.playMode = PLAY_MODE_NORMAL; modPlay(DONT_SET_PATTERN, song->currOrder, editor.f6Pos); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } else if (editor.currMode != MODE_PLAY && editor.currMode != MODE_RECORD) { modSetPos(DONT_SET_ORDER, editor.f6Pos); } } } break; case SDL_SCANCODE_F7: { if (keyb.shiftPressed) { editor.f7Pos = song->currRow; displayMsg("POSITION SET"); } else { if (keyb.leftAltPressed) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, editor.f7Pos); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } else if (keyb.leftCtrlPressed) { if (!ui.samplerScreenShown) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, editor.f7Pos); editor.currMode = MODE_RECORD; pointerSetMode(POINTER_MODE_EDIT, DO_CARRY); statusAllRight(); } } else if (keyb.leftAmigaPressed) { editor.playMode = PLAY_MODE_NORMAL; modPlay(DONT_SET_PATTERN, song->currOrder, editor.f7Pos); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } else if (editor.currMode != MODE_PLAY && editor.currMode != MODE_RECORD) { modSetPos(DONT_SET_ORDER, editor.f7Pos); } } } break; case SDL_SCANCODE_F8: { if (keyb.shiftPressed) { editor.f8Pos = song->currRow; displayMsg("POSITION SET"); } else { if (keyb.leftAltPressed) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, editor.f8Pos); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } else if (keyb.leftCtrlPressed) { if (!ui.samplerScreenShown) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, editor.f8Pos); editor.currMode = MODE_RECORD; pointerSetMode(POINTER_MODE_EDIT, DO_CARRY); statusAllRight(); } } else if (keyb.leftAmigaPressed) { editor.playMode = PLAY_MODE_NORMAL; modPlay(DONT_SET_PATTERN, song->currOrder, editor.f8Pos); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } else if (editor.currMode != MODE_PLAY && editor.currMode != MODE_RECORD) { modSetPos(DONT_SET_ORDER, editor.f8Pos); } } } break; case SDL_SCANCODE_F9: { if (keyb.shiftPressed) { editor.f9Pos = song->currRow; displayMsg("POSITION SET"); } else { if (keyb.leftAltPressed) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, editor.f9Pos); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } else if (keyb.leftCtrlPressed) { if (!ui.samplerScreenShown) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, editor.f9Pos); editor.currMode = MODE_RECORD; pointerSetMode(POINTER_MODE_EDIT, DO_CARRY); statusAllRight(); } } else if (keyb.leftAmigaPressed) { editor.playMode = PLAY_MODE_NORMAL; modPlay(DONT_SET_PATTERN, song->currOrder, editor.f9Pos); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } else if (editor.currMode != MODE_PLAY && editor.currMode != MODE_RECORD) { modSetPos(DONT_SET_ORDER, editor.f9Pos); } } } break; case SDL_SCANCODE_F10: { if (keyb.shiftPressed) { editor.f10Pos = song->currRow; displayMsg("POSITION SET"); } else { if (keyb.leftAltPressed) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, editor.f10Pos); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } else if (keyb.leftCtrlPressed) { if (!ui.samplerScreenShown) { editor.playMode = PLAY_MODE_PATTERN; modPlay(song->currPattern, DONT_SET_ORDER, editor.f10Pos); editor.currMode = MODE_RECORD; pointerSetMode(POINTER_MODE_EDIT, DO_CARRY); statusAllRight(); } } else if (keyb.leftAmigaPressed) { editor.playMode = PLAY_MODE_NORMAL; modPlay(DONT_SET_PATTERN, song->currOrder, editor.f10Pos); editor.currMode = MODE_PLAY; pointerSetMode(POINTER_MODE_PLAY, DO_CARRY); statusAllRight(); } else if (editor.currMode != MODE_PLAY && editor.currMode != MODE_RECORD) { modSetPos(DONT_SET_ORDER, editor.f10Pos); } } } break; case SDL_SCANCODE_F11: { if (keyb.leftAltPressed) { config.realVuMeters ^= 1; displayMsg(config.realVuMeters ? "VU-METERS: REAL" : "VU-METERS: FAKE"); } } break; case SDL_SCANCODE_TAB: { if (keyb.shiftPressed) movePatCurPrevCh(); else movePatCurNextCh(); } break; case SDL_SCANCODE_0: { if (keyb.leftCtrlPressed) { editor.editMoveAdd = 0; displayMsg("EDITSKIP = 0"); ui.updateTrackerFlags = true; } else if (keyb.shiftPressed) { note_t *noteSrc = &song->patterns[song->currPattern][(song->currRow * PAULA_VOICES) + cursor.channel]; editor.effectMacros[9] = (noteSrc->command << 8) | noteSrc->param; displayMsg("COMMAND STORED!"); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_1: { if (keyb.leftAmigaPressed) { trackNoteUp(TRANSPOSE_ALL, 0, MOD_ROWS - 1); } else if (keyb.leftCtrlPressed) { editor.editMoveAdd = 1; displayMsg("EDITSKIP = 1"); ui.updateTrackerFlags = true; } else if (keyb.shiftPressed) { note_t *noteSrc = &song->patterns[song->currPattern][(song->currRow * PAULA_VOICES) + cursor.channel]; editor.effectMacros[0] = (noteSrc->command << 8) | noteSrc->param; displayMsg("COMMAND STORED!"); } else if (editor.currMode == MODE_IDLE && keyb.leftAltPressed) { incMulti(0); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_2: { if (keyb.leftAmigaPressed) { pattNoteUp(TRANSPOSE_ALL); } else if (keyb.leftCtrlPressed) { editor.editMoveAdd = 2; displayMsg("EDITSKIP = 2"); ui.updateTrackerFlags = true; } else if (keyb.shiftPressed) { note_t *noteSrc = &song->patterns[song->currPattern][(song->currRow * PAULA_VOICES) + cursor.channel]; editor.effectMacros[1] = (noteSrc->command << 8) | noteSrc->param; displayMsg("COMMAND STORED!"); } else if (editor.currMode == MODE_IDLE && keyb.leftAltPressed) { incMulti(1); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_3: { if (keyb.leftAmigaPressed) { trackNoteUp(TRANSPOSE_ALL, 0, MOD_ROWS - 1); } else if (keyb.leftCtrlPressed) { editor.editMoveAdd = 3; displayMsg("EDITSKIP = 3"); ui.updateTrackerFlags = true; } else if (keyb.shiftPressed) { note_t *noteSrc = &song->patterns[song->currPattern][(song->currRow * PAULA_VOICES) + cursor.channel]; editor.effectMacros[2] = (noteSrc->command << 8) | noteSrc->param; displayMsg("COMMAND STORED!"); } else if (editor.currMode == MODE_IDLE && keyb.leftAltPressed) { incMulti(2); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_4: { if (keyb.leftAmigaPressed) { pattNoteUp(TRANSPOSE_ALL); } else if (keyb.leftCtrlPressed) { editor.editMoveAdd = 4; displayMsg("EDITSKIP = 4"); ui.updateTrackerFlags = true; } else if (keyb.shiftPressed) { note_t *noteSrc = &song->patterns[song->currPattern][(song->currRow * PAULA_VOICES) + cursor.channel]; editor.effectMacros[3] = (noteSrc->command << 8) | noteSrc->param; displayMsg("COMMAND STORED!"); } else if (editor.currMode == MODE_IDLE && keyb.leftAltPressed) { incMulti(3); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_5: { if (keyb.leftCtrlPressed) { editor.editMoveAdd = 5; displayMsg("EDITSKIP = 5"); ui.updateTrackerFlags = true; } else if (keyb.shiftPressed) { note_t *noteSrc = &song->patterns[song->currPattern][(song->currRow * PAULA_VOICES) + cursor.channel]; editor.effectMacros[4] = (noteSrc->command << 8) | noteSrc->param; displayMsg("COMMAND STORED!"); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_6: { if (keyb.leftCtrlPressed) { editor.editMoveAdd = 6; displayMsg("EDITSKIP = 6"); ui.updateTrackerFlags = true; } else if (keyb.shiftPressed) { note_t *noteSrc = &song->patterns[song->currPattern][(song->currRow * PAULA_VOICES) + cursor.channel]; editor.effectMacros[5] = (noteSrc->command << 8) | noteSrc->param; displayMsg("COMMAND STORED!"); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_7: { if (keyb.leftCtrlPressed) { editor.editMoveAdd = 7; displayMsg("EDITSKIP = 7"); ui.updateTrackerFlags = true; } else if (keyb.shiftPressed) { note_t *noteSrc = &song->patterns[song->currPattern][(song->currRow * PAULA_VOICES) + cursor.channel]; editor.effectMacros[6] = (noteSrc->command << 8) | noteSrc->param; displayMsg("COMMAND STORED!"); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_8: { if (keyb.leftCtrlPressed) { editor.editMoveAdd = 8; displayMsg("EDITSKIP = 8"); ui.updateTrackerFlags = true; } else if (keyb.shiftPressed) { note_t *noteSrc = &song->patterns[song->currPattern][(song->currRow * PAULA_VOICES) + cursor.channel]; editor.effectMacros[7] = (noteSrc->command << 8) | noteSrc->param; displayMsg("COMMAND STORED!"); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_9: { if (keyb.leftCtrlPressed) { editor.editMoveAdd = 9; displayMsg("EDITSKIP = 9"); ui.updateTrackerFlags = true; } else if (keyb.shiftPressed) { note_t *noteSrc = &song->patterns[song->currPattern][(song->currRow * PAULA_VOICES) + cursor.channel]; editor.effectMacros[8] = (noteSrc->command << 8) | noteSrc->param; displayMsg("COMMAND STORED!"); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_KP_0: { if (editor.hiLowInstr >= 0x10) { editor.sampleZero = false; editor.currSample = 0x10-1; } else { editor.sampleZero = true; editor.currSample = 0x00; } updateCurrSample(); } break; case SDL_SCANCODE_KP_1: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 12; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_2: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 13; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_3: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 14; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_4: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 8; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_5: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 9; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_6: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 10; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_7: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 4; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_8: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 5; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_9: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 6; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_ENTER: { editor.hiLowInstr ^= 0x10; if (editor.sampleZero) { editor.currSample = 15; editor.sampleZero = false; } else { editor.currSample ^= 0x10; } if (editor.currSample == 31) // kludge if sample was 15 (0010 in UI) before key press { editor.currSample = 15; editor.sampleZero ^= 1; } updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_PLUS: { editor.sampleZero = false; // the Amiga numpad has one more key, so we need to use this key for two sample numbers... if (editor.keypadToggle8CFlag) editor.currSample = editor.hiLowInstr + (0x0C - 1); else editor.currSample = editor.hiLowInstr + (0x08 - 1); updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) displayErrorMsg("INVALID PAD KEY !"); editor.keypadToggle8CFlag ^= 1; } break; case SDL_SCANCODE_KP_MINUS: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 3; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_MULTIPLY: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 2; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_DIVIDE: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 1; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_NUMLOCKCLEAR: { editor.sampleZero = false; editor.currSample = editor.hiLowInstr + 0; updateCurrSample(); if (keyb.leftAltPressed && editor.pNoteFlag > 0) { ui.changingDrumPadNote = true; setStatusMessage("SELECT NOTE", NO_CARRY); pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); break; } if (editor.pNoteFlag > 0) handleEditKeys(scancode, EDIT_SPECIAL); } break; case SDL_SCANCODE_KP_PERIOD: { if (askBox(ASKBOX_YES_NO, "KILL SAMPLE ?")) killSample(); } break; case SDL_SCANCODE_DOWN: { keyb.delayKey = false; keyb.repeatKey = false; if (ui.diskOpScreenShown) { if (diskop.numEntries > DISKOP_LINES) { diskop.scrollOffset++; if (mouse.rightButtonPressed) // PT quirk: right mouse button speeds up scrolling even on keyb UP/DOWN diskop.scrollOffset += 3; if (diskop.scrollOffset > diskop.numEntries-DISKOP_LINES) diskop.scrollOffset = diskop.numEntries-DISKOP_LINES; ui.updateDiskOpFileList = true; } if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = false; } else if (ui.posEdScreenShown) { if (song->currOrder != song->header.numOrders-1) { if (++song->currOrder > song->header.numOrders-1) song->currOrder = song->header.numOrders-1; modSetPos(song->currOrder, DONT_SET_ROW); ui.updatePosEd = true; } if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = true; } else if (!ui.samplerScreenShown) { if (editor.currMode != MODE_PLAY && editor.currMode != MODE_RECORD) modSetPos(DONT_SET_ORDER, (song->currRow + 1) & 63); keyb.repeatKey = true; } } break; case SDL_SCANCODE_UP: { keyb.delayKey = false; keyb.repeatKey = false; if (ui.diskOpScreenShown) { diskop.scrollOffset--; if (mouse.rightButtonPressed) // PT quirk: right mouse button speeds up scrolling even on keyb UP/DOWN diskop.scrollOffset -= 3; if (diskop.scrollOffset < 0) diskop.scrollOffset = 0; ui.updateDiskOpFileList = true; if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = false; } else if (ui.posEdScreenShown) { if (song->currOrder > 0) { modSetPos(song->currOrder - 1, DONT_SET_ROW); ui.updatePosEd = true; } if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = true; } else if (!ui.samplerScreenShown) { if ((editor.currMode != MODE_PLAY) && (editor.currMode != MODE_RECORD)) modSetPos(DONT_SET_ORDER, (song->currRow - 1) & 63); keyb.repeatKey = true; } } break; case SDL_SCANCODE_LEFT: { keyb.delayKey = false; keyb.repeatKey = false; if (keyb.leftCtrlPressed) { sampleDownButton(); if (editor.repeatKeyFlag) { keyb.delayKey = true; keyb.repeatKey = true; } } else if (keyb.shiftPressed) { if (song->currOrder > 0) { modSetPos(song->currOrder - 1, DONT_SET_ROW); if (editor.repeatKeyFlag) { keyb.delayKey = true; keyb.repeatKey = true; } } } else if (keyb.leftAltPressed) { decPatt(); if (editor.repeatKeyFlag) { keyb.delayKey = true; keyb.repeatKey = true; } } else { movePatCurLeft(); keyb.repeatKey = true; } } break; case SDL_SCANCODE_RIGHT: { keyb.delayKey = false; keyb.repeatKey = false; if (keyb.leftCtrlPressed) { sampleUpButton(); if (editor.repeatKeyFlag) { keyb.delayKey = true; keyb.repeatKey = true; } } else if (keyb.shiftPressed) { if (song->currOrder < 126) { modSetPos(song->currOrder + 1, DONT_SET_ROW); if (editor.repeatKeyFlag) { keyb.delayKey = true; keyb.repeatKey = true; } } } else if (keyb.leftAltPressed) { incPatt(); if (editor.repeatKeyFlag) { keyb.delayKey = true; keyb.repeatKey = true; } } else { movePatCurRight(); keyb.repeatKey = true; } } break; case SDL_SCANCODE_A: { if (keyb.leftAmigaPressed) { trackOctaUp(TRANSPOSE_ALL, 0, MOD_ROWS - 1); } else if (keyb.leftCtrlPressed) { if (ui.samplerScreenShown) { samplerRangeAll(); } else { if (keyb.shiftPressed) { editor.muted[0] = true; editor.muted[1] = true; editor.muted[2] = true; editor.muted[3] = true; editor.muted[cursor.channel] = false; renderMuteButtons(); break; } editor.muted[cursor.channel] ^= 1; renderMuteButtons(); } } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_B: { if (keyb.leftCtrlPressed) { // CTRL+B doesn't change the status message back, so do this: if (ui.introTextShown) { ui.introTextShown = false; statusAllRight(); } if (editor.blockMarkFlag) { editor.blockMarkFlag = false; } else { editor.blockMarkFlag = true; editor.blockFromPos = song->currRow; editor.blockToPos = song->currRow; } ui.updateStatusText = true; } else if (keyb.leftAltPressed) { moduleSample_t *s = &song->samples[editor.currSample]; if (s->length == 0) { statusSampleIsEmpty(); break; } boostSample(editor.currSample, true); if (ui.samplerScreenShown) displaySample(); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_C: { if (keyb.leftAmigaPressed) { trackOctaDown(TRANSPOSE_ALL, 0, MOD_ROWS - 1); } else if (keyb.leftCtrlPressed) { if (ui.samplerScreenShown) { samplerSamCopy(); return; } if (!editor.blockMarkFlag) { displayErrorMsg("NO BLOCK MARKED !"); return; } editor.blockMarkFlag = false; editor.blockBufferFlag = true; for (int32_t i = 0; i < MOD_ROWS; i++) editor.blockBuffer[i] = song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; if (editor.blockFromPos > editor.blockToPos) { editor.buffFromPos = editor.blockToPos; editor.buffToPos = editor.blockFromPos; } else { editor.buffFromPos = editor.blockFromPos; editor.buffToPos = editor.blockToPos; } statusAllRight(); } else { if (keyb.leftAltPressed) { editor.muted[2] ^= 1; // toggle channel 3 renderMuteButtons(); } else { handleEditKeys(scancode, EDIT_NORMAL); } } } break; case SDL_SCANCODE_D: { if (keyb.leftAmigaPressed) { trackOctaUp(TRANSPOSE_ALL, 0, MOD_ROWS - 1); } else if (keyb.leftCtrlPressed) { saveUndo(); } else { if (keyb.leftAltPressed) { if (!ui.posEdScreenShown) { editor.blockMarkFlag = false; ui.diskOpScreenShown ^= 1; if (!ui.diskOpScreenShown) { pointerSetPreviousMode(); setPrevStatusMessage(); displayMainScreen(); } else { ui.diskOpScreenShown = true; renderDiskOpScreen(); } } } else { handleEditKeys(scancode, EDIT_NORMAL); } } } break; case SDL_SCANCODE_E: { if (keyb.leftAmigaPressed) { trackNoteDown(TRANSPOSE_ALL, 0, MOD_ROWS - 1); } else if (keyb.leftAltPressed) { if (!ui.diskOpScreenShown && !ui.posEdScreenShown) { if (ui.editOpScreenShown) ui.editOpScreen = (ui.editOpScreen + 1) % 3; else ui.editOpScreenShown = true; renderEditOpScreen(); } } else if (keyb.leftCtrlPressed) { saveUndo(); int32_t j = song->currRow + 1; while (j < MOD_ROWS) { int32_t i; for (i = 62; i >= j; i--) { note_t *noteSrc = &song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; song->patterns[song->currPattern][((i + 1) * PAULA_VOICES) + cursor.channel] = *noteSrc; } note_t *noteDst = &song->patterns[song->currPattern][((i + 1) * PAULA_VOICES) + cursor.channel]; noteDst->period = 0; noteDst->sample = 0; noteDst->command = 0; noteDst->param = 0; j += 2; } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_F: { #ifdef __APPLE__ if (keyb.leftCommandPressed && keyb.leftCtrlPressed) { toggleFullscreen(); } else #endif if (keyb.leftAmigaPressed) { pattOctaUp(TRANSPOSE_ALL); } else if (keyb.leftCtrlPressed) { toggleLEDFilter(); if (audio.ledFilterEnabled) displayMsg("LED FILTER ON"); else displayMsg("LED FILTER OFF"); } else if (keyb.leftAltPressed) { moduleSample_t *s = &song->samples[editor.currSample]; if (s->length == 0) { statusSampleIsEmpty(); break; } filterSample(editor.currSample, true); if (ui.samplerScreenShown) displaySample(); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_G: { if (keyb.leftCtrlPressed) { if (askBox(ASKBOX_YES_NO, "BOOST ALL SAMPLES")) { for (int32_t i = 0; i < MOD_SAMPLES; i++) boostSample(i, true); if (ui.samplerScreenShown) redrawSample(); updateWindowTitle(MOD_IS_MODIFIED); } } else if (keyb.leftAltPressed) // toggle record mode (PT clone and PT2.3E only) { editor.recordMode ^= 1; if (editor.recordMode == 0) displayMsg("REC MODE: PATT"); else displayMsg("REC MODE: SONG"); if (ui.editOpScreenShown && ui.editOpScreen == 1) ui.updateRecordText = true; } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_H: { if (keyb.leftCtrlPressed) { if (!editor.blockMarkFlag) { displayErrorMsg("NO BLOCK MARKED !"); return; } trackNoteUp(TRANSPOSE_ALL, editor.blockFromPos, editor.blockToPos); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_I: { if (keyb.leftCtrlPressed) { if (!editor.blockBufferFlag) { displayErrorMsg("BUFFER IS EMPTY !"); return; } if (song->currRow < 63) { for (int32_t i = 0; i <= editor.buffToPos-editor.buffFromPos; i++) { for (int32_t j = 62; j >= song->currRow; j--) { note_t *noteSrc = &song->patterns[song->currPattern][(j * PAULA_VOICES) + cursor.channel]; song->patterns[song->currPattern][((j + 1) * PAULA_VOICES) + cursor.channel] = *noteSrc; } } } saveUndo(); int32_t i; for (i = 0; i <= editor.buffToPos-editor.buffFromPos; i++) { if (song->currRow+i > 63) break; song->patterns[song->currPattern][((song->currRow + i) * PAULA_VOICES) + cursor.channel] = editor.blockBuffer[editor.buffFromPos + i]; } if (!keyb.shiftPressed) { song->currRow += i & 0xFF; if (song->currRow > 63) song->currRow = 0; } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else if (keyb.leftAltPressed) { editor.autoInsFlag ^= 1; ui.updateTrackerFlags = true; } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_J: { if (keyb.leftCtrlPressed) { if (!editor.blockBufferFlag) { displayErrorMsg("BUFFER IS EMPTY !"); return; } saveUndo(); int32_t i = editor.buffFromPos; int32_t j = song->currRow; note_t *patt = song->patterns[song->currPattern]; while (true) { note_t *noteDst = &patt[(j * PAULA_VOICES) + cursor.channel]; if (editor.blockBuffer[i].period == 0 && editor.blockBuffer[i].sample == 0) { noteDst->command = editor.blockBuffer[i].command; noteDst->param = editor.blockBuffer[i].param; } else { *noteDst = editor.blockBuffer[i]; } if (i == editor.buffToPos || i == 63 || j == 63) break; i++; j++; } if (!keyb.shiftPressed) { song->currRow += (editor.buffToPos-editor.buffFromPos) + 1; if (song->currRow > 63) song->currRow = 0; } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_K: { if (keyb.leftAltPressed) { for (int32_t i = 0; i < MOD_ROWS; i++) { note_t *noteSrc = &song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; if (noteSrc->sample == editor.currSample+1) { noteSrc->period = 0; noteSrc->sample = 0; noteSrc->command = 0; noteSrc->param = 0; } } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else if (keyb.leftCtrlPressed) { saveUndo(); int32_t i = song->currRow; if (keyb.shiftPressed) { // kill to start while (i >= 0) { note_t *noteDst = &song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; noteDst->period = 0; noteDst->sample = 0; noteDst->command = 0; noteDst->param = 0; i--; } } else { // kill to end while (i < MOD_ROWS) { note_t *noteDst = &song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; noteDst->period = 0; noteDst->sample = 0; noteDst->command = 0; noteDst->param = 0; i++; } } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_L: { if (keyb.leftCtrlPressed) { if (!editor.blockMarkFlag) { displayErrorMsg("NO BLOCK MARKED !"); return; } trackNoteDown(TRANSPOSE_ALL, editor.blockFromPos, editor.blockToPos); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_M: { if (keyb.leftCtrlPressed) { editor.multiFlag ^= 1; ui.updateTrackerFlags = true; ui.updateKeysText = true; } else if (keyb.leftAltPressed) { if (keyb.shiftPressed) editor.metroChannel = cursor.channel + 1; else editor.metroFlag ^= 1; ui.updateTrackerFlags = true; } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_N: { if (keyb.leftCtrlPressed) { editor.blockMarkFlag = true; song->currRow = editor.blockToPos; } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_O: { if (keyb.leftCtrlPressed) { // fun fact: this function is broken in PT but I fixed it in my clone saveUndo(); int32_t j = song->currRow + 1; while (j < MOD_ROWS) { for (int32_t i = j; i < MOD_ROWS-1; i++) { note_t *noteSrc = &song->patterns[song->currPattern][((i + 1) * PAULA_VOICES) + cursor.channel]; song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel] = *noteSrc; } // clear newly made row on very bottom note_t *noteDst = &song->patterns[song->currPattern][(63 * PAULA_VOICES) + cursor.channel]; noteDst->period = 0; noteDst->sample = 0; noteDst->command = 0; noteDst->param = 0; j++; } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_P: { if (keyb.leftCtrlPressed) { if (!editor.blockBufferFlag) { displayErrorMsg("BUFFER IS EMPTY !"); return; } saveUndo(); int32_t i = editor.buffFromPos; int32_t j = song->currRow; note_t *patt = song->patterns[song->currPattern]; while (true) { note_t *noteDst = &patt[(j * PAULA_VOICES) + cursor.channel]; *noteDst = editor.blockBuffer[i]; if (i == editor.buffToPos || i == 63 || j == 63) break; i++; j++; } if (!keyb.shiftPressed) { song->currRow += (editor.buffToPos-editor.buffFromPos) + 1; if (song->currRow > 63) song->currRow = 0; } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else if (keyb.leftAltPressed) { if (!ui.diskOpScreenShown) { ui.posEdScreenShown ^= 1; if (ui.posEdScreenShown) { renderPosEdScreen(); ui.updatePosEd = true; } else { displayMainScreen(); } } } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_Q: { if (keyb.leftAmigaPressed) { trackNoteDown(TRANSPOSE_ALL, 0, MOD_ROWS - 1); #ifdef __APPLE__ /* On Mac, command+Q sends a quit signal to the program. ** However, command+Q is also one of the transpose keys in this ProTracker port. ** Ignore the signal if command+Q was pressed. */ editor.macCmdQIssued = true; #endif } else if (keyb.leftCtrlPressed) { editor.muted[0] = false; editor.muted[1] = false; editor.muted[2] = false; editor.muted[3] = false; renderMuteButtons(); } else if (keyb.leftAltPressed) { if (askBox(ASKBOX_YES_NO, "REALLY QUIT ?")) ui.throwExit = true; } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_R: { if (keyb.leftAmigaPressed) { pattNoteDown(TRANSPOSE_ALL); } else if (keyb.leftCtrlPressed) { editor.f6Pos = 0; editor.f7Pos = 16; editor.f8Pos = 32; editor.f9Pos = 48; editor.f10Pos = 63; displayMsg("POS RESTORED !"); } else if (keyb.leftAltPressed) { if (askBox(ASKBOX_YES_NO, "RESAMPLE?")) samplerResample(); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_S: { if (keyb.leftCtrlPressed) { // if we're in sample load/save mode, set current dir to modules path if (diskop.mode == DISKOP_MODE_SMP) UNICHAR_CHDIR(editor.modulesPathU); saveModule(DONT_CHECK_IF_FILE_EXIST, DONT_GIVE_NEW_FILENAME); // set current dir to samples path if (diskop.mode == DISKOP_MODE_SMP) UNICHAR_CHDIR(editor.samplesPathU); } else if (keyb.leftAmigaPressed) { pattOctaUp(TRANSPOSE_ALL); } else if (keyb.leftAltPressed) { samplerScreen(); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_T: { if (keyb.leftCtrlPressed) { editor.swapChannelFlag = true; pointerSetMode(POINTER_MODE_MSG1, NO_CARRY); setStatusMessage("SWAP (1/2/3/4) ?", NO_CARRY); } else if (keyb.leftAltPressed) { toggleTuningTone(); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_U: { if (keyb.leftCtrlPressed) undoLastChange(); else handleEditKeys(scancode, EDIT_NORMAL); } break; case SDL_SCANCODE_V: { if (keyb.leftAmigaPressed) { pattOctaDown(TRANSPOSE_ALL); } else if (keyb.leftCtrlPressed) { if (ui.samplerScreenShown) { samplerSamPaste(); } else { if (askBox(ASKBOX_YES_NO, "FILTER ALL SAMPLS")) { for (int32_t i = 0; i < MOD_SAMPLES; i++) filterSample(i, true); if (ui.samplerScreenShown) redrawSample(); updateWindowTitle(MOD_IS_MODIFIED); } } } else if (keyb.leftAltPressed) { editor.muted[3] ^= 1; // toggle channel 4 renderMuteButtons(); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_W: { if (keyb.leftAmigaPressed) { pattNoteDown(TRANSPOSE_ALL); } else if (keyb.leftCtrlPressed) { // Polyphonize Block if (!editor.blockBufferFlag) { displayErrorMsg("BUFFER IS EMPTY !"); return; } saveUndo(); int32_t i = editor.buffFromPos; int32_t j = song->currRow; note_t *patt = song->patterns[song->currPattern]; while (true) { note_t *noteDst = &patt[(j * PAULA_VOICES) + cursor.channel]; if (editor.blockBuffer[i].period == 0 && editor.blockBuffer[i].sample == 0) { noteDst->command = editor.blockBuffer[i].command; noteDst->param = editor.blockBuffer[i].param; } else { *noteDst = editor.blockBuffer[i]; } if (i == editor.buffToPos || i == 63 || j == 63) break; i++; j++; gotoNextMulti(); } if (!keyb.shiftPressed) { song->currRow += (editor.buffToPos-editor.buffFromPos) + 1; if (song->currRow > 63) song->currRow = 0; } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_X: { if (keyb.leftAmigaPressed) { pattOctaDown(TRANSPOSE_ALL); } else if (keyb.leftCtrlPressed) { if (ui.samplerScreenShown) { samplerSamDelete(SAMPLE_CUT); return; } if (!editor.blockMarkFlag) { displayErrorMsg("NO BLOCK MARKED !"); return; } editor.blockMarkFlag = false; saveUndo(); editor.blockBufferFlag = true; for (int32_t i = 0; i < MOD_ROWS; i++) editor.blockBuffer[i] = song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; if (editor.blockFromPos > editor.blockToPos) { editor.buffFromPos = editor.blockToPos; editor.buffToPos = editor.blockFromPos; } else { editor.buffFromPos = editor.blockFromPos; editor.buffToPos = editor.blockToPos; } for (int32_t i = editor.buffFromPos; i <= editor.buffToPos; i++) { note_t *noteDst = &song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; noteDst->period = 0; noteDst->sample = 0; noteDst->command = 0; noteDst->param = 0; } statusAllRight(); updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else { if (keyb.leftAltPressed) { editor.muted[1] ^= 1; // toggle channel 2 renderMuteButtons(); } else { handleEditKeys(scancode, EDIT_NORMAL); } } } break; case SDL_SCANCODE_Y: { uint8_t blockFrom, blockTo; if (keyb.leftCtrlPressed) { if (!editor.blockMarkFlag) { displayErrorMsg("NO BLOCK MARKED !"); return; } editor.blockMarkFlag = false; saveUndo(); if (editor.blockFromPos >= editor.blockToPos) { blockFrom = editor.blockToPos; blockTo = editor.blockFromPos; } else { blockFrom = editor.blockFromPos; blockTo = editor.blockToPos; } while (blockFrom < blockTo) { note_t *noteDst = &song->patterns[song->currPattern][(blockFrom * PAULA_VOICES) + cursor.channel]; note_t *noteSrc = &song->patterns[song->currPattern][(blockTo * PAULA_VOICES) + cursor.channel]; note_t noteTmp = *noteDst; *noteDst = *noteSrc; *noteSrc = noteTmp; blockFrom += 1; blockTo -= 1; } statusAllRight(); updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } else if (keyb.leftAltPressed) { if (askBox(ASKBOX_YES_NO, "SAVE ALL SAMPLES?")) { int8_t oldSample = editor.currSample; for (int32_t i = 0; i < MOD_SAMPLES; i++) { editor.currSample = (int8_t)i; if (song->samples[i].length > 2) saveSample(DONT_CHECK_IF_FILE_EXIST, GIVE_NEW_FILENAME); } editor.currSample = oldSample; displayMsg("SAMPLES SAVED !"); setMsgPointer(); } } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; case SDL_SCANCODE_Z: { if (keyb.leftAmigaPressed) { trackOctaDown(TRANSPOSE_ALL, 0, MOD_ROWS - 1); } else if (keyb.leftCtrlPressed) { if (ui.samplerScreenShown) { if (askBox(ASKBOX_YES_NO, "RESTORE SAMPLE?")) redoSampleData(editor.currSample); } else { modSetTempo(125, true); modSetSpeed(6); moduleChannel_t *ch = song->channels; for (int32_t i = 0; i < PAULA_VOICES; i++, ch++) { ch->n_wavecontrol = 0; ch->n_glissfunk = 0; ch->n_finetune = 0; ch->n_loopcount = 0; } displayMsg("EFX RESTORED !"); } } else if (keyb.leftAltPressed) { editor.muted[0] ^= 1; // toggle channel 1 renderMuteButtons(); } else { handleEditKeys(scancode, EDIT_NORMAL); } } break; default: handleEditKeys(scancode, EDIT_NORMAL); break; } } static void movePatCurPrevCh(void) { int8_t pos = ((cursor.pos + 5) / 6) - 1; cursor.pos = (pos < 0) ? (3 * 6) : (pos * 6); cursor.mode = CURSOR_NOTE; if (cursor.pos < 6) cursor.channel = 0; else if (cursor.pos < 12) cursor.channel = 1; else if (cursor.pos < 18) cursor.channel = 2; else if (cursor.pos < 24) cursor.channel = 3; updateCursorPos(); } static void movePatCurNextCh(void) { int8_t pos = (cursor.pos / 6) + 1; cursor.pos = (pos == 4) ? 0 : (pos * 6); cursor.mode = CURSOR_NOTE; if (cursor.pos < 6) cursor.channel = 0; else if (cursor.pos < 12) cursor.channel = 1; else if (cursor.pos < 18) cursor.channel = 2; else if (cursor.pos < 24) cursor.channel = 3; updateCursorPos(); } static void movePatCurRight(void) { cursor.pos = (cursor.pos == 23) ? 0 : (cursor.pos + 1); if (cursor.pos < 6) cursor.channel = 0; else if (cursor.pos < 12) cursor.channel = 1; else if (cursor.pos < 18) cursor.channel = 2; else if (cursor.pos < 24) cursor.channel = 3; cursor.mode = cursor.pos % 6; updateCursorPos(); } static void movePatCurLeft(void) { cursor.pos = (cursor.pos == 0) ? 23 : (cursor.pos - 1); if (cursor.pos < 6) cursor.channel = 0; else if (cursor.pos < 12) cursor.channel = 1; else if (cursor.pos < 18) cursor.channel = 2; else if (cursor.pos < 24) cursor.channel = 3; cursor.mode = cursor.pos % 6; updateCursorPos(); } void handleKeyRepeat(SDL_Scancode scancode) { if (!keyb.repeatKey || ui.askBoxShown) { keyb.repeatFrac = 0; keyb.repeatCounter = 0; return; } if (keyb.delayKey && keyb.delayCounter < KEYB_REPEAT_DELAY) { keyb.delayCounter++; return; } switch (scancode) // only some buttons have repeat { case SDL_SCANCODE_PAGEUP: { if (keyb.repeatCounter >= 3) { keyb.repeatCounter = 0; if (ui.posEdScreenShown) { if (song->currOrder-(POSED_LIST_SIZE-1) > 0) modSetPos(song->currOrder-(POSED_LIST_SIZE-1), DONT_SET_ROW); else modSetPos(0, DONT_SET_ROW); } else if (ui.diskOpScreenShown) { if (ui.diskOpScreenShown) { diskop.scrollOffset -= DISKOP_LINES-1; if (diskop.scrollOffset < 0) diskop.scrollOffset = 0; ui.updateDiskOpFileList = true; } } else if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT) { if (song->currRow == 63) modSetPos(DONT_SET_ORDER, song->currRow - 15); else if (song->currRow == 15) modSetPos(DONT_SET_ORDER, 0); // 15-16 would turn into -1, which is "DON'T SET ROW" flag else modSetPos(DONT_SET_ORDER, song->currRow - 16); } } } break; case SDL_SCANCODE_PAGEDOWN: { if (keyb.repeatCounter >= 3) { keyb.repeatCounter = 0; if (ui.posEdScreenShown) { if (song->currOrder+(POSED_LIST_SIZE-1) <= song->header.numOrders-1) modSetPos(song->currOrder+(POSED_LIST_SIZE-1), DONT_SET_ROW); else modSetPos(song->header.numOrders - 1, DONT_SET_ROW); } else if (ui.diskOpScreenShown) { if (diskop.numEntries > DISKOP_LINES) { diskop.scrollOffset += DISKOP_LINES-1; if (diskop.scrollOffset > diskop.numEntries-DISKOP_LINES) diskop.scrollOffset = diskop.numEntries-DISKOP_LINES; ui.updateDiskOpFileList = true; } } else if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT) { modSetPos(DONT_SET_ORDER, song->currRow + 16); } } } break; case SDL_SCANCODE_LEFT: { if (ui.editTextFlag) { if (keyb.delayCounter >= KEYB_REPEAT_DELAY) { if (keyb.repeatCounter >= 3) { keyb.repeatCounter = 0; textCharPrevious(); } } else { keyb.delayCounter++; } } else { if (keyb.leftCtrlPressed) { if (keyb.repeatCounter >= 6) { keyb.repeatCounter = 0; sampleDownButton(); } } else if (keyb.shiftPressed) { if (keyb.repeatCounter >= 6) { keyb.repeatCounter = 0; if (song->currOrder > 0) modSetPos(song->currOrder - 1, DONT_SET_ROW); } } else if (keyb.leftAltPressed) { if (keyb.repeatCounter >= 4) { keyb.repeatCounter = 0; decPatt(); } } else { if (keyb.repeatCounter >= 6) { keyb.repeatCounter = 0; if (!keyb.shiftPressed && !keyb.leftAltPressed && !keyb.leftCtrlPressed) movePatCurLeft(); } } } } break; case SDL_SCANCODE_RIGHT: { if (ui.editTextFlag) { if (keyb.delayCounter >= KEYB_REPEAT_DELAY) { if (keyb.repeatCounter >= 3) { keyb.repeatCounter = 0; textCharNext(); } } else { keyb.delayCounter++; } } else { if (keyb.leftCtrlPressed) { if (keyb.repeatCounter >= 6) { keyb.repeatCounter = 0; sampleUpButton(); } } else if (keyb.shiftPressed) { if (keyb.repeatCounter >= 6) { keyb.repeatCounter = 0; if (song->currOrder < 126) modSetPos(song->currOrder + 1, DONT_SET_ROW); } } else if (keyb.leftAltPressed) { if (keyb.repeatCounter >= 4) { keyb.repeatCounter = 0; incPatt(); } } else { if (keyb.repeatCounter >= 6) { keyb.repeatCounter = 0; if (!keyb.shiftPressed && !keyb.leftAltPressed && !keyb.leftCtrlPressed) movePatCurRight(); } } } } break; case SDL_SCANCODE_UP: { if (ui.diskOpScreenShown) { if (keyb.repeatCounter >= 1) { keyb.repeatCounter = 0; diskop.scrollOffset--; if (mouse.rightButtonPressed) // PT quirk: right mouse button speeds up scrolling even on keyb UP/DOWN diskop.scrollOffset -= 3; if (diskop.scrollOffset < 0) diskop.scrollOffset = 0; ui.updateDiskOpFileList = true; } } else if (ui.posEdScreenShown) { if (keyb.repeatCounter >= 3) { keyb.repeatCounter = 0; if (song->currOrder > 0) { modSetPos(song->currOrder - 1, DONT_SET_ROW); ui.updatePosEd = true; } } } else if (!ui.samplerScreenShown) { if (editor.currMode != MODE_PLAY && editor.currMode != MODE_RECORD) { uint8_t repeatNum = 6; if (keyb.leftAltPressed) repeatNum = 1; else if (keyb.shiftPressed) repeatNum = 3; if (keyb.repeatCounter >= repeatNum) { keyb.repeatCounter = 0; modSetPos(DONT_SET_ORDER, (song->currRow - 1) & 63); } } } } break; case SDL_SCANCODE_DOWN: { if (ui.diskOpScreenShown) { if (keyb.repeatCounter >= 1) { keyb.repeatCounter = 0; if (diskop.numEntries > DISKOP_LINES) { diskop.scrollOffset++; if (mouse.rightButtonPressed) // PT quirk: right mouse button speeds up scrolling even on keyb UP/DOWN diskop.scrollOffset += 3; if (diskop.scrollOffset > diskop.numEntries-DISKOP_LINES) diskop.scrollOffset = diskop.numEntries-DISKOP_LINES; ui.updateDiskOpFileList = true; } } } else if (ui.posEdScreenShown) { if (keyb.repeatCounter >= 3) { keyb.repeatCounter = 0; if (song->currOrder != song->header.numOrders-1) { if (++song->currOrder > song->header.numOrders-1) song->currOrder = song->header.numOrders-1; modSetPos(song->currOrder, DONT_SET_ROW); ui.updatePosEd = true; } } } else if (!ui.samplerScreenShown) { if (editor.currMode != MODE_PLAY && editor.currMode != MODE_RECORD) { uint8_t repeatNum = 6; if (keyb.leftAltPressed) repeatNum = 1; else if (keyb.shiftPressed) repeatNum = 3; if (keyb.repeatCounter >= repeatNum) { keyb.repeatCounter = 0; modSetPos(DONT_SET_ORDER, (song->currRow + 1) & 63); } } } } break; case SDL_SCANCODE_BACKSPACE: { if (ui.editTextFlag) { // only repeat backspace while editing texts if (keyb.delayCounter >= KEYB_REPEAT_DELAY) { if (keyb.repeatCounter >= 3) { keyb.repeatCounter = 0; keyDownHandler(scancode, 0); } } else { keyb.delayCounter++; } } } break; case SDL_SCANCODE_KP_ENTER: case SDL_SCANCODE_RETURN: break; // do NOT repeat enter! default: { if (keyb.repeatCounter >= 3) { keyb.repeatCounter = 0; keyDownHandler(scancode, 0); } } break; } keyb.repeatFrac += keyb.repeatDelta; // 32.32 fixed-point counter if (keyb.repeatFrac > UINT32_MAX) { keyb.repeatFrac &= UINT32_MAX; keyb.repeatCounter++; } } static void swapChannel(uint8_t srcCh, uint8_t dstCh) { if (srcCh == dstCh) return; for (int32_t i = 0; i < MOD_ROWS; i++) { note_t *noteSrc = &song->patterns[song->currPattern][(i * PAULA_VOICES) + dstCh]; note_t noteTmp = song->patterns[song->currPattern][(i * PAULA_VOICES) + srcCh]; song->patterns[song->currPattern][(i * PAULA_VOICES) + srcCh] = *noteSrc; *noteSrc = noteTmp; } updateWindowTitle(MOD_IS_MODIFIED); ui.updatePatternData = true; } static bool handleGeneralModes(SDL_Scancode scancode) { // if MOD2WAV is ongoing, only check for ESC key if (editor.mod2WavOngoing) { if (scancode == SDL_SCANCODE_ESCAPE) editor.abortMod2Wav = true; return false; // don't handle other keys } // SAMPLER SCREEN (volume box) if (ui.samplerVolBoxShown && !ui.editTextFlag && scancode == SDL_SCANCODE_ESCAPE) { ui.samplerVolBoxShown = false; removeSamplerVolBox(); return false; } // SAMPLER SCREEN (filters box) if (ui.samplerFiltersBoxShown && !ui.editTextFlag && scancode == SDL_SCANCODE_ESCAPE) { ui.samplerFiltersBoxShown = false; removeSamplerFiltersBox(); return false; } // SAMPLER SCREEN (sampling box) if (ui.samplingBoxShown) { if (audio.isSampling) { stopSampling(); return false; } if (scancode == SDL_SCANCODE_F1) editor.keyOctave = OCTAVE_LOW; else if (scancode == SDL_SCANCODE_F2) editor.keyOctave = OCTAVE_HIGH; if (ui.changingSamplingNote) { if (scancode == SDL_SCANCODE_ESCAPE) { ui.changingSamplingNote = false; setPrevStatusMessage(); pointerSetPreviousMode(); } int8_t rawKey = keyToNote(scancode); if (rawKey >= 0) { ui.changingSamplingNote = false; setSamplingNote(rawKey); setPrevStatusMessage(); pointerSetPreviousMode(); } return false; } else { if (keyb.leftCtrlPressed) { if (scancode == SDL_SCANCODE_LEFT) samplingSampleNumDown(); else if (scancode == SDL_SCANCODE_RIGHT) samplingSampleNumUp(); } else { if (scancode == SDL_SCANCODE_SPACE) turnOffVoices(); else handleSampleJamming(scancode); } } if (!ui.editTextFlag && scancode == SDL_SCANCODE_ESCAPE) { ui.samplingBoxShown = false; removeSamplingBox(); } return false; } // EDIT OP. SCREEN #3 if (editor.mixFlag && scancode == SDL_SCANCODE_ESCAPE) { exitGetTextLine(EDIT_TEXT_UPDATE); editor.mixFlag = false; ui.updateMixText = true; return false; } // EDIT OP. SCREEN #4 if (ui.changingChordNote) { if (scancode == SDL_SCANCODE_ESCAPE) { ui.changingChordNote = false; setPrevStatusMessage(); pointerSetPreviousMode(); return false; } if (scancode == SDL_SCANCODE_F1) editor.keyOctave = OCTAVE_LOW; else if (scancode == SDL_SCANCODE_F2) editor.keyOctave = OCTAVE_HIGH; int8_t rawKey = keyToNote(scancode); if (rawKey >= 0) { if (ui.changingChordNote == 1) { editor.note1 = rawKey; ui.updateChordNote1Text = true; } else if (ui.changingChordNote == 2) { editor.note2 = rawKey; ui.updateChordNote2Text = true; } else if (ui.changingChordNote == 3) { editor.note3 = rawKey; ui.updateChordNote3Text = true; } else if (ui.changingChordNote == 4) { editor.note4 = rawKey; ui.updateChordNote4Text = true; } ui.changingChordNote = false; recalcChordLength(); setPrevStatusMessage(); pointerSetPreviousMode(); } return false; } // CHANGE DRUMPAD NOTE if (ui.changingDrumPadNote) { if (scancode == SDL_SCANCODE_ESCAPE) { ui.changingDrumPadNote = false; setPrevStatusMessage(); pointerSetPreviousMode(); return false; } if (scancode == SDL_SCANCODE_F1) editor.keyOctave = OCTAVE_LOW; else if (scancode == SDL_SCANCODE_F2) editor.keyOctave = OCTAVE_HIGH; int8_t rawKey = keyToNote(scancode); if (rawKey >= 0) { pNoteTable[editor.currSample] = rawKey; ui.changingDrumPadNote = false; setPrevStatusMessage(); pointerSetPreviousMode(); } return false; } // SAMPLER SCREEN if (ui.changingSmpResample) { if (scancode == SDL_SCANCODE_ESCAPE) { ui.changingSmpResample = false; ui.updateResampleNote = true; setPrevStatusMessage(); pointerSetPreviousMode(); return false; } if (scancode == SDL_SCANCODE_F1) editor.keyOctave = OCTAVE_LOW; else if (scancode == SDL_SCANCODE_F2) editor.keyOctave = OCTAVE_HIGH; int8_t rawKey = keyToNote(scancode); if (rawKey >= 0) { editor.resampleNote = rawKey; ui.changingSmpResample = false; ui.updateResampleNote = true; setPrevStatusMessage(); pointerSetPreviousMode(); } return false; } // SWAP CHANNEL (CTRL+T) if (editor.swapChannelFlag) { switch (scancode) { case SDL_SCANCODE_ESCAPE: { editor.swapChannelFlag = false; pointerSetPreviousMode(); setPrevStatusMessage(); } break; case SDL_SCANCODE_1: { swapChannel(0, cursor.channel); editor.swapChannelFlag = false; pointerSetPreviousMode(); setPrevStatusMessage(); } break; case SDL_SCANCODE_2: { swapChannel(1, cursor.channel); editor.swapChannelFlag = false; pointerSetPreviousMode(); setPrevStatusMessage(); } break; case SDL_SCANCODE_3: { swapChannel(2, cursor.channel); editor.swapChannelFlag = false; pointerSetPreviousMode(); setPrevStatusMessage(); } break; case SDL_SCANCODE_4: { swapChannel(3, cursor.channel); editor.swapChannelFlag = false; pointerSetPreviousMode(); setPrevStatusMessage(); } break; default: break; } return false; } return true; } void handleTextEditInputChar(char textChar) { // we only want certain keys if (textChar < ' ' || textChar > '~') return; // A..Z -> a..z if (textChar >= 'A' && textChar <= 'Z') textChar = (char)tolower(textChar); if (ui.editTextType == TEXT_EDIT_STRING) { if (ui.editPos < ui.textEndPtr) { if (!editor.mixFlag) { char *readTmp = ui.textEndPtr; while (readTmp > ui.editPos) { int8_t readTmpPrev = *--readTmp; *(readTmp + 1) = readTmpPrev; } *ui.textEndPtr = '\0'; *ui.editPos++ = textChar; textMarkerMoveRight(); } else if ((textChar >= '0' && textChar <= '9') || (textChar >= 'a' && textChar <= 'f')) { if (ui.dstPos == 14) // hack for sample mix text { *ui.editPos = textChar; } else { *ui.editPos++ = textChar; textMarkerMoveRight(); if (ui.dstPos == 9) // hack for sample mix text { for (int32_t i = 0; i < 4; i++) { ui.editPos++; textMarkerMoveRight(); } } else if (ui.dstPos == 6) // hack for sample mix text { ui.editPos++; textMarkerMoveRight(); } } } } } else { if (ui.editTextType == TEXT_EDIT_DECIMAL) { if (textChar >= '0' && textChar <= '9') { uint8_t digit1, digit2, digit3, digit4; uint32_t number; textChar -= '0'; if (ui.numLen == 4) { number = *ui.numPtr16; digit4 = number % 10; number /= 10; digit3 = number % 10; number /= 10; digit2 = number % 10; number /= 10; digit1 = (uint8_t)number; if (ui.dstPos == 0) *ui.numPtr16 = (textChar * 1000) + (digit2 * 100) + (digit3 * 10) + digit4; else if (ui.dstPos == 1) *ui.numPtr16 = (digit1 * 1000) + (textChar * 100) + (digit3 * 10) + digit4; else if (ui.dstPos == 2) *ui.numPtr16 = (digit1 * 1000) + (digit2 * 100) + (textChar * 10) + digit4; else if (ui.dstPos == 3) *ui.numPtr16 = (digit1 * 1000) + (digit2 * 100) + (digit3 * 10) + textChar; } else if (ui.numLen == 3) { number = *ui.numPtr16; digit3 = number % 10; number /= 10; digit2 = number % 10; number /= 10; digit1 = (uint8_t)number; if (ui.dstPos == 0) *ui.numPtr16 = (textChar * 100) + (digit2 * 10) + digit3; else if (ui.dstPos == 1) *ui.numPtr16 = (digit1 * 100) + (textChar * 10) + digit3; else if (ui.dstPos == 2) *ui.numPtr16 = (digit1 * 100) + (digit2 * 10) + textChar; } else if (ui.numLen == 2) { number = *ui.numPtr16; digit2 = number % 10; number /= 10; digit1 = (uint8_t)number; if (ui.dstPos == 0) *ui.numPtr16 = (textChar * 10) + digit2; else if (ui.dstPos == 1) *ui.numPtr16 = (digit1 * 10) + textChar; } textMarkerMoveRight(); if (ui.dstPos >= ui.numLen) exitGetTextLine(EDIT_TEXT_UPDATE); } } else { if ((textChar >= '0' && textChar <= '9') || (textChar >= 'a' && textChar <= 'f')) { if (textChar <= '9') textChar -= '0'; else if (textChar <= 'f') textChar -= 'a'-10; if (ui.numBits == 17) { *ui.numPtr32 &= ~(0xF0000 >> (ui.dstPos << 2)); *ui.numPtr32 |= textChar << (16 - (ui.dstPos << 2)); } else if (ui.numBits == 16) { if (ui.force32BitNumPtr) { *ui.numPtr32 &= ~(0xF000 >> (ui.dstPos << 2)); *ui.numPtr32 |= textChar << (12 - (ui.dstPos << 2)); } else { *ui.numPtr16 &= ~(0xF000 >> (ui.dstPos << 2)); *ui.numPtr16 |= textChar << (12 - (ui.dstPos << 2)); } } else if (ui.numBits == 8) { *ui.numPtr8 &= ~(0xF0 >> (ui.dstPos << 2)); *ui.numPtr8 |= textChar << (4 - (ui.dstPos << 2)); } textMarkerMoveRight(); if (ui.dstPos >= ui.numLen) exitGetTextLine(EDIT_TEXT_UPDATE); } } } updateTextObject(ui.editObject); if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = true; } bool handleTextEditMode(SDL_Scancode scancode) { switch (scancode) { case SDL_SCANCODE_ESCAPE: { editor.blockMarkFlag = false; if (ui.editTextFlag) { exitGetTextLine(EDIT_TEXT_NO_UPDATE); return false; } } break; case SDL_SCANCODE_HOME: { if (ui.editTextFlag && !editor.mixFlag) { while (ui.editPos > ui.showTextPtr) textCharPrevious(); } } break; case SDL_SCANCODE_END: { if (ui.editTextFlag && !editor.mixFlag) { if (ui.editTextType != TEXT_EDIT_STRING) break; while (!ui.dstOffsetEnd) textCharNext(); } } break; case SDL_SCANCODE_LEFT: { if (ui.editTextFlag) { textCharPrevious(); if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = false; } else { keyb.delayKey = false; keyb.repeatKey = true; } } break; case SDL_SCANCODE_RIGHT: { if (ui.editTextFlag) { textCharNext(); if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = false; } else { keyb.delayKey = false; keyb.repeatKey = true; } } break; case SDL_SCANCODE_DELETE: { if (ui.editTextFlag) { if (editor.mixFlag || ui.editTextType != TEXT_EDIT_STRING) break; char *readTmp = ui.editPos; while (readTmp < ui.textEndPtr) { int8_t readTmpNext = *(readTmp + 1); *readTmp++ = readTmpNext; } // kludge to prevent cloning last character if the song/sample name has one character too much if (ui.editObject == PTB_SONGNAME || ui.editObject == PTB_SAMPLENAME) *ui.textEndPtr = '\0'; if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = true; updateTextObject(ui.editObject); } } break; case SDL_SCANCODE_BACKSPACE: { if (ui.editTextFlag) { if (editor.mixFlag || ui.editTextType != TEXT_EDIT_STRING) break; if (ui.editPos > ui.dstPtr) { ui.editPos--; char *readTmp = ui.editPos; while (readTmp < ui.textEndPtr) { int8_t readTmpNext = *(readTmp + 1); *readTmp++ = readTmpNext; } // kludge to prevent cloning last character if the song/sample name has one character too much if (ui.editObject == PTB_SONGNAME || ui.editObject == PTB_SAMPLENAME) *ui.textEndPtr = '\0'; textMarkerMoveLeft(); updateTextObject(ui.editObject); } if (!keyb.repeatKey) keyb.delayCounter = 0; keyb.repeatKey = true; keyb.delayKey = false; } else { if (ui.diskOpScreenShown) { #ifdef _WIN32 diskOpSetPath(L"..", DISKOP_CACHE); #else diskOpSetPath("..", DISKOP_CACHE); #endif } else if (keyb.shiftPressed || keyb.leftAltPressed || keyb.leftCtrlPressed) { saveUndo(); if (keyb.leftAltPressed && !keyb.leftCtrlPressed) { if (song->currRow > 0) { for (int32_t i = 0; i < PAULA_VOICES; i++) { for (int32_t j = (song->currRow - 1); j < MOD_ROWS; j++) { note_t *noteSrc = &song->patterns[song->currPattern][((j + 1) * PAULA_VOICES) + i]; song->patterns[song->currPattern][(j * PAULA_VOICES) + i] = *noteSrc; } // clear newly made row on very bottom note_t *noteDst = &song->patterns[song->currPattern][(63 * PAULA_VOICES) + i]; noteDst->period = 0; noteDst->sample = 0; noteDst->command = 0; noteDst->param = 0; } song->currRow--; ui.updatePatternData = true; } } else { if (song->currRow > 0) { for (int32_t i = song->currRow-1; i < MOD_ROWS-1; i++) { note_t *noteSrc = &song->patterns[song->currPattern][((i + 1) * PAULA_VOICES) + cursor.channel]; note_t *noteDst = &song->patterns[song->currPattern][(i * PAULA_VOICES) + cursor.channel]; if (keyb.leftCtrlPressed) { noteDst->command = noteSrc->command; noteDst->param = noteSrc->param; } else { *noteDst = *noteSrc; } } // clear newly made row on very bottom note_t *noteDst = &song->patterns[song->currPattern][(63 * PAULA_VOICES) + cursor.channel]; noteDst->period = 0; noteDst->sample = 0; noteDst->command = 0; noteDst->param = 0; song->currRow--; ui.updatePatternData = true; } } } else { editor.stepPlayEnabled = true; editor.stepPlayBackwards = true; editor.stepPlayLastMode = editor.currMode; if (config.keepEditModeAfterStepPlay && editor.stepPlayLastMode == MODE_EDIT) doStopIt(false); else doStopIt(true); playPattern((song->currRow - 1) & 63); if (config.keepEditModeAfterStepPlay && editor.stepPlayLastMode == MODE_EDIT) { pointerSetMode(POINTER_MODE_EDIT, DO_CARRY); editor.playMode = PLAY_MODE_NORMAL; editor.currMode = MODE_EDIT; } } } } break; default: break; } if (ui.editTextFlag) { if (scancode == SDL_SCANCODE_RETURN || scancode == SDL_SCANCODE_KP_ENTER) { // dirty hack if (ui.editObject == PTB_SAMPLES) ui.tmpDisp8++; exitGetTextLine(EDIT_TEXT_UPDATE); if (editor.mixFlag) { editor.mixFlag = false; ui.updateMixText = true; doMix(); } } return false; // don't continue further key handling } return true; // continue further key handling (we're not editing text) }