Skip to content

Commit 4fda4fc

Browse files
authored
Fix broken feedback for the arrow keys in the virtual MIDI keyboard if it opens when REAPER starts. (#1217)
Previously, if the virtual MIDI keyboard was configured to open when REAPER starts, this feedback would fail to work until REAPER was restarted with the keyboard closed. To fix this, always register the hook as soon as OSARA loads.
1 parent bf3e194 commit 4fda4fc

File tree

1 file changed

+95
-87
lines changed

1 file changed

+95
-87
lines changed

Diff for: src/reaper_osara.cpp

+95-87
Original file line numberDiff line numberDiff line change
@@ -2459,6 +2459,85 @@ void postMidiResets(int command) {
24592459
}
24602460
}
24612461

2462+
// The virtual MIDI keyboard dialog HWND.
2463+
HWND vkbHwnd = nullptr;
2464+
// This is called from translateAccel. This is necessary because REAPER
2465+
// registers its own accelerator hook for the virtual MIDI keyboard when the
2466+
// keyboard first opens and we need to be ahead of that hook in order to be
2467+
// called. This might happen right at startup if the virtual keyboard is
2468+
// configured to open then. REAPER never unregisters its hook once registered.
2469+
int vkbTranslateAccel(MSG* msg, accelerator_register_t* accelReg) {
2470+
if (!vkbHwnd) {
2471+
// The window probably isn't open.
2472+
return 0; // Normal handling.
2473+
}
2474+
if (!IsWindow(vkbHwnd)) {
2475+
vkbHwnd = nullptr;
2476+
return 0;
2477+
}
2478+
if (msg->message != WM_KEYDOWN ||
2479+
(msg->hwnd != vkbHwnd && GetParent(msg->hwnd) != vkbHwnd)) {
2480+
// This key isn't for us.
2481+
return 0;
2482+
}
2483+
if (isClassName(msg->hwnd, "Edit") || isClassName(msg->hwnd, "ComboBox")) {
2484+
// Don't trap arrow keys in these controls.
2485+
return 0;
2486+
}
2487+
if (isShortcutHelpEnabled) {
2488+
switch (msg->wParam) {
2489+
case VK_RIGHT:
2490+
outputMessage(translate("Octave up"));
2491+
return 1; // Eat the key.
2492+
case VK_LEFT:
2493+
outputMessage(translate("Octave down"));
2494+
return 1;
2495+
case VK_UP:
2496+
outputMessage(translate("Increase MIDI channel"));
2497+
return 1;
2498+
case VK_DOWN:
2499+
outputMessage(translate("Decrease MIDI channel"));
2500+
return 1;
2501+
default:
2502+
break;
2503+
}
2504+
return 0;
2505+
}
2506+
switch (msg->wParam) {
2507+
case VK_RIGHT:
2508+
case VK_LEFT:
2509+
// We need to wait until this executes before we can report the new value.
2510+
CallLater([] {
2511+
constexpr long WCID_CENTER_NOTE = 1239;
2512+
char note[10];
2513+
if (GetDlgItemText(vkbHwnd, WCID_CENTER_NOTE, note, sizeof(note)) != 0) {
2514+
outputMessage(note);
2515+
}
2516+
}, 0);
2517+
return 0;
2518+
case VK_UP:
2519+
case VK_DOWN:
2520+
CallLater([] {
2521+
constexpr long WCID_CHANNEL = 1377;
2522+
char channel[10];
2523+
if (GetDlgItemText(vkbHwnd, WCID_CHANNEL, channel, sizeof(channel)) != 0) {
2524+
outputMessage(channel);
2525+
}
2526+
}, 0);
2527+
return 0;
2528+
default:
2529+
break;
2530+
}
2531+
return 0;
2532+
}
2533+
2534+
void postVirtualMidiKeyboard(int command) {
2535+
if (GetToggleCommandState(command)) {
2536+
// The window has just been shown. Store its HWND for vkbTranslateAccel.
2537+
vkbHwnd = GetForegroundWindow();
2538+
}
2539+
}
2540+
24622541
void postMExplorerChangeVolume(int cmd, HWND hwnd) {
24632542
HWND w = GetDlgItem(hwnd, 997);
24642543
if(!w) {// support Reaper versions before 6.65
@@ -2710,9 +2789,10 @@ PostCommand POST_COMMANDS[] = {
27102789
{24866, postMomentarilySetOverrideToAltN}, // Main action section: Momentarily set override to alt-14
27112790
{24867, postMomentarilySetOverrideToAltN}, // Main action section: Momentarily set override to alt-15
27122791
{24868, postMomentarilySetOverrideToAltN}, // Main action section: Momentarily set override to alt-16
2713-
{41175, postMidiResets}, // Reset all MIDI devices
2714-
{42348, postMidiResets}, // Reset all MIDI control surface devices
2715-
{40345, postMidiResets}, // Send all-notes-off and all-sounds-off to all MIDI outputs/plug-ins
2792+
{41175, postMidiResets}, // Reset all MIDI devices
2793+
{42348, postMidiResets}, // Reset all MIDI control surface devices
2794+
{40345, postMidiResets}, // Send all-notes-off and all-sounds-off to all MIDI outputs/plug-ins
2795+
{40377, postVirtualMidiKeyboard}, // View: Show virtual MIDI keyboard
27162796
{0},
27172797
};
27182798
MidiPostCommand MIDI_POST_COMMANDS[] = {
@@ -5213,89 +5293,6 @@ void cmdJumpToTime(Command* command) {
52135293
}, 50);
52145294
}
52155295

5216-
void cmdVirtualMidiKeyboard(Command* command) {
5217-
static accelerator_register_t accelReg = {0};
5218-
if (GetToggleCommandState(command->gaccel.accel.cmd)) {
5219-
// The virtual keyboard is showing. Run the command to hide it.
5220-
Main_OnCommand(command->gaccel.accel.cmd, 0);
5221-
return;
5222-
}
5223-
const bool isRegistered = !!accelReg.translateAccel;
5224-
accelReg.translateAccel = [](MSG* msg, accelerator_register_t* accelReg) -> int {
5225-
HWND dialog = (HWND)accelReg->user;
5226-
if (dialog && !IsWindow(dialog)) {
5227-
// The dialog is dead. Ideally, we would unregister. However, if we do,
5228-
// subsequent registrations of our hook never intercept key presses in the
5229-
// virtual keyboard. So, we just have to leave this registered.
5230-
accelReg->user = nullptr;
5231-
return 0; // Normal handling.
5232-
}
5233-
if (msg->message != WM_KEYDOWN ||
5234-
(msg->hwnd != dialog && GetParent(msg->hwnd) != dialog)) {
5235-
// This key isn't for us.
5236-
return 0;
5237-
}
5238-
if (isClassName(msg->hwnd, "Edit") || isClassName(msg->hwnd, "ComboBox")) {
5239-
// Don't trap arrow keys in these controls.
5240-
return 0;
5241-
}
5242-
if (isShortcutHelpEnabled) {
5243-
switch (msg->wParam) {
5244-
case VK_RIGHT:
5245-
outputMessage(translate("Octave up"));
5246-
return 1; // Eat the key.
5247-
case VK_LEFT:
5248-
outputMessage(translate("Octave down"));
5249-
return 1;
5250-
case VK_UP:
5251-
outputMessage(translate("Increase MIDI channel"));
5252-
return 1;
5253-
case VK_DOWN:
5254-
outputMessage(translate("Decrease MIDI channel"));
5255-
return 1;
5256-
default:
5257-
break;
5258-
}
5259-
return 0;
5260-
}
5261-
switch (msg->wParam) {
5262-
case VK_RIGHT:
5263-
case VK_LEFT:
5264-
// We need to wait until this executes before we can report the new value.
5265-
CallLater([dialog] {
5266-
constexpr long WCID_CENTER_NOTE = 1239;
5267-
char note[10];
5268-
if (GetDlgItemText(dialog, WCID_CENTER_NOTE, note, sizeof(note)) != 0) {
5269-
outputMessage(note);
5270-
}
5271-
}, 0);
5272-
return 0;
5273-
case VK_UP:
5274-
case VK_DOWN:
5275-
CallLater([dialog] {
5276-
constexpr long WCID_CHANNEL = 1377;
5277-
char channel[10];
5278-
if (GetDlgItemText(dialog, WCID_CHANNEL, channel, sizeof(channel)) != 0) {
5279-
outputMessage(channel);
5280-
}
5281-
}, 0);
5282-
return 0;
5283-
default:
5284-
break;
5285-
}
5286-
return 0;
5287-
};
5288-
accelReg.isLocal = true;
5289-
accelReg.user = nullptr;
5290-
if (!isRegistered) {
5291-
// We must register the hook before the window appears or it won't work.
5292-
plugin_register("accelerator", &accelReg);
5293-
}
5294-
// Show the window.
5295-
Main_OnCommand(command->gaccel.accel.cmd, 0);
5296-
accelReg.user = GetForegroundWindow(); // The dialog.
5297-
}
5298-
52995296
#define DEFACCEL {0, 0, 0}
53005297

53015298
Command COMMANDS[] = {
@@ -5399,7 +5396,6 @@ Command COMMANDS[] = {
53995396
{MAIN_SECTION, {{0, 0, 42207}, nullptr}, nullptr, cmdAddAutoItems}, // Envelope: Convert all project automation to automation items
54005397
{MAIN_SECTION, {{0, 0, 42089}, nullptr}, nullptr, cmdGlueAutoItems}, // Envelope: Glue automation items
54015398
{MAIN_SECTION, {{0, 0, 40069}, nullptr}, nullptr, cmdJumpToTime}, // View: Jump (go) to time window
5402-
{MAIN_SECTION, {{0, 0, 40377}, nullptr}, nullptr, cmdVirtualMidiKeyboard}, // View: Show virtual MIDI keyboard
54035399
{MIDI_EDITOR_SECTION, {{0, 0, 40036}, nullptr}, nullptr, cmdMidiMoveCursor}, // View: Go to start of file
54045400
{MIDI_EVENT_LIST_SECTION, {{0, 0, 40036}, nullptr}, nullptr, cmdMidiMoveCursor}, // View: Go to start of file
54055401
{MIDI_EDITOR_SECTION, {{0, 0, 40037}, nullptr}, nullptr, cmdMidiMoveCursor}, // View: Go to end of file
@@ -5968,6 +5964,12 @@ HWINEVENTHOOK winEventHook = nullptr;
59685964

59695965
#endif // _WIN32
59705966

5967+
// This accelerator hook is registered at startup and remains registered until
5968+
// REAPER exits.
5969+
int translateAccel(MSG* msg, accelerator_register_t* accelReg) {
5970+
return vkbTranslateAccel(msg, accelReg);
5971+
}
5972+
59715973
extern "C" {
59725974

59735975
REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(REAPER_PLUGIN_HINSTANCE hInstance, reaper_plugin_info_t* rec) {
@@ -6021,6 +6023,12 @@ REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(REAPER_PLUGIN_HINSTANCE hI
60216023
#ifdef _WIN32
60226024
keyboardHook = SetWindowsHookEx(WH_KEYBOARD, keyboardHookProc, nullptr, guiThread);
60236025
#endif
6026+
6027+
static accelerator_register_t accelReg;
6028+
accelReg.translateAccel = translateAccel;
6029+
accelReg.isLocal = true;
6030+
accelReg.user = nullptr;
6031+
plugin_register("accelerator", &accelReg);
60246032
return 1;
60256033

60266034
} else {

0 commit comments

Comments
 (0)