diff --git a/CHANGELOG.md b/CHANGELOG.md index b2863f3c..c392dbcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. - Allow RtAudio device block size to be as low as 16. - Copy cable color when cloning cables with Ctrl+click. - Fix key commands on AZERTY, Dvorak, and all other keyboard layouts. +- Add Mouse device to Computer keyboard/mouse MIDI driver. - Core - Add Audio-2 module with stereo input/output, a level knob, and VU meters. diff --git a/include/keyboard.hpp b/include/keyboard.hpp index 400b3b3d..49f8bb42 100644 --- a/include/keyboard.hpp +++ b/include/keyboard.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include namespace rack { @@ -13,6 +14,8 @@ namespace keyboard { void init(); void press(int key); void release(int key); +/** pos is in the unit box. */ +void mouseMove(math::Vec pos); } // namespace keyboard diff --git a/src/keyboard.cpp b/src/keyboard.cpp index 4ee8fa27..2016e18e 100644 --- a/src/keyboard.cpp +++ b/src/keyboard.cpp @@ -14,6 +14,7 @@ struct Driver; static const int DRIVER = -11; static Driver* driver = NULL; +static const int MOUSE_DEVICE_ID = 1000; enum { CMD_OCTAVE_DOWN = -1, @@ -106,6 +107,14 @@ struct InputDevice : midi::InputDevice { int octave = 5; std::map pressedNotes; + void setDeviceId(int deviceId) { + this->deviceId = deviceId; + // Default lowest key of numpad is C1. + if (deviceId == 1) { + octave = 3; + } + } + std::string getName() override { return deviceInfos[deviceId].name; } @@ -114,7 +123,7 @@ struct InputDevice : midi::InputDevice { // Do nothing if no ports are subscribed if (subscribed.empty()) return; - auto keyMap = deviceInfos[deviceId].keyMap; + const auto& keyMap = deviceInfos[deviceId].keyMap; auto it = keyMap.find(key); if (it == keyMap.end()) return; @@ -164,18 +173,52 @@ struct InputDevice : midi::InputDevice { }; +struct MouseInputDevice : midi::InputDevice { + int16_t lastValues[2] = {}; + + std::string getName() override { + return "Mouse"; + } + + void onMouseMove(math::Vec pos) { + int16_t values[2]; + values[0] = math::clamp((int) std::round(pos.x * 0x3f80), 0, 0x3f80); + // Flip Y values + values[1] = math::clamp((int) std::round((1.f - pos.y) * 0x3f80), 0, 0x3f80); + + for (int id = 0; id < 2; id++) { + if (values[id] != lastValues[id]) { + // Continuous controller MSB + midi::Message m; + m.setStatus(0xb); + m.setNote(id); + m.setValue(values[id] >> 7); + onMessage(m); + // Continuous controller LSB + midi::Message m2; + m2.setStatus(0xb); + m2.setNote(id + 32); + m2.setValue(values[id] & 0x7f); + onMessage(m2); + lastValues[id] = values[id]; + } + } + } +}; + + struct Driver : midi::Driver { InputDevice devices[deviceCount]; + MouseInputDevice mouseDevice; Driver() { for (int deviceId = 0; deviceId < deviceCount; deviceId++) { - devices[deviceId].deviceId = deviceId; + devices[deviceId].setDeviceId(deviceId); } - devices[1].octave = 3; } std::string getName() override { - return "Computer keyboard"; + return "Computer keyboard/mouse"; } std::vector getInputDeviceIds() override { @@ -183,26 +226,42 @@ struct Driver : midi::Driver { for (int deviceId = 0; deviceId < deviceCount; deviceId++) { deviceIds.push_back(deviceId); } + deviceIds.push_back(MOUSE_DEVICE_ID); return deviceIds; } + midi::InputDevice* getInputDevice(int deviceId) { + if (deviceId == MOUSE_DEVICE_ID) + return &mouseDevice; + if (0 <= deviceId && deviceId < deviceCount) + return &devices[deviceId]; + return NULL; + } + std::string getInputDeviceName(int deviceId) override { - return deviceInfos[deviceId].name; + midi::InputDevice* inputDevice = getInputDevice(deviceId); + if (!inputDevice) + return ""; + return inputDevice->getName(); } midi::InputDevice* subscribeInput(int deviceId, midi::Input* input) override { - if (!(0 <= deviceId && deviceId < deviceCount)) + midi::InputDevice* inputDevice = getInputDevice(deviceId); + if (!inputDevice) return NULL; - devices[deviceId].subscribe(input); - return &devices[deviceId]; + inputDevice->subscribe(input); + return inputDevice; } void unsubscribeInput(int deviceId, midi::Input* input) override { - if (!(0 <= deviceId && deviceId < deviceCount)) + midi::InputDevice* inputDevice = getInputDevice(deviceId); + if (!inputDevice) return; - devices[deviceId].unsubscribe(input); + inputDevice->unsubscribe(input); } + // Events that forward to InputDevices + void onKeyPress(int key) { for (int deviceId = 0; deviceId < deviceCount; deviceId++) { devices[deviceId].onKeyPress(key); @@ -214,6 +273,10 @@ struct Driver : midi::Driver { devices[deviceId].onKeyRelease(key); } } + + void onMouseMove(math::Vec pos) { + mouseDevice.onMouseMove(pos); + } }; @@ -235,5 +298,12 @@ void release(int key) { } +void mouseMove(math::Vec pos) { + if (!driver) + return; + driver->onMouseMove(pos); +} + + } // namespace keyboard } // namespace rack diff --git a/src/window.cpp b/src/window.cpp index 11cb9339..789c9614 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -155,6 +155,12 @@ static void cursorPosCallback(GLFWwindow* win, double xpos, double ypos) { window->internal->lastMousePos = mousePos; APP->event->handleHover(mousePos, mouseDelta); + + // Keyboard/mouse MIDI driver + int width, height; + glfwGetWindowSize(win, &width, &height); + math::Vec scaledPos(xpos / width, ypos / height); + keyboard::mouseMove(scaledPos); } static void cursorEnterCallback(GLFWwindow* win, int entered) { @@ -186,7 +192,7 @@ static void keyCallback(GLFWwindow* win, int key, int scancode, int action, int if (APP->event->handleKey(window->internal->lastMousePos, key, scancode, action, mods)) return; - // Keyboard MIDI driver + // Keyboard/mouse MIDI driver if (action == GLFW_PRESS && (mods & RACK_MOD_MASK) == 0) { keyboard::press(key); }