mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
47d886306e
## Link to GitHub Issue or related Pull Request, if one exists Fixes #603 ## Description of change Scanning for midi devices can take a while on Windows 11 with MIDI 2.0 service. Sometimes it'll take a couple seconds. Sometimes, on a PC with zero MIDI devices, it takes 10-11 seconds. This was causing two issues: 1. slow startup time 2. crash on invalid memory access due to a race condition Address both. Also fix a bug: when a device change event fires, we do a MIDI scan, and invalidated all existing MIDI devices in favor of creating new handles. Stop doing this, and instead check for duplicates by matching the ID and keep existing device handles alone. Properly clean up devices on unplug. Also: fix MIDI buttons being stuck on when unplugged while holding a key. ## Testing Tested with Nostroller in MIDI mode. rtpMIDI works too.
139 lines
5.3 KiB
C++
139 lines
5.3 KiB
C++
#include <initguid.h>
|
|
|
|
#include "hotplug.h"
|
|
|
|
#include <dbt.h>
|
|
#include <devguid.h>
|
|
|
|
#include "misc/eamuse.h"
|
|
#include "rawinput.h"
|
|
#include "util/fileutils.h"
|
|
#include "util/logging.h"
|
|
|
|
namespace rawinput {
|
|
|
|
DEFINE_GUID(GUID_HID, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
|
|
|
|
HotplugManager::HotplugManager(RawInputManager *ri_mgr, HWND hWnd) : ri_mgr(ri_mgr) {
|
|
|
|
// init settings
|
|
DEV_BROADCAST_DEVICEINTERFACE settings_hid {
|
|
.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE),
|
|
.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE,
|
|
.dbcc_reserved = 0,
|
|
.dbcc_classguid = GUID_HID,
|
|
.dbcc_name = {0},
|
|
};
|
|
|
|
// register notifications
|
|
this->hotplug_hid = RegisterDeviceNotification(hWnd, &settings_hid, DEVICE_NOTIFY_WINDOW_HANDLE);
|
|
if (this->hotplug_hid == nullptr) {
|
|
log_warning("hotplug", "failed to register HID notifications: {}", GetLastError());
|
|
}
|
|
}
|
|
|
|
HotplugManager::~HotplugManager() {
|
|
|
|
// unregister notifications
|
|
if (this->hotplug_hid != nullptr) {
|
|
UnregisterDeviceNotification(this->hotplug_hid);
|
|
}
|
|
}
|
|
|
|
LRESULT CALLBACK HotplugManager::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|
|
|
// check for device change
|
|
if (msg == WM_DEVICECHANGE) {
|
|
|
|
// check type
|
|
switch (wParam) {
|
|
case DBT_DEVICEARRIVAL: {
|
|
|
|
// check arrived device type
|
|
auto hdr = (DEV_BROADCAST_HDR*) lParam;
|
|
switch (hdr->dbch_devicetype) {
|
|
case DBT_DEVTYP_DEVICEINTERFACE: {
|
|
auto dev = (const DEV_BROADCAST_DEVICEINTERFACE *) hdr;
|
|
this->ri_mgr->midi_scan_start();
|
|
|
|
// check if class is not HID
|
|
if (memcmp(&dev->dbcc_classguid, &GUID_HID, sizeof(GUID_HID)) != 0)
|
|
break;
|
|
|
|
// hotplug
|
|
std::string name(dev->dbcc_name);
|
|
this->ri_mgr->devices_scan_rawinput(name);
|
|
this->ri_mgr->devices_register();
|
|
|
|
break;
|
|
}
|
|
case DBT_DEVTYP_VOLUME: {
|
|
auto dev = (const DEV_BROADCAST_VOLUME *) hdr;
|
|
auto unitmask = dev->dbcv_unitmask;
|
|
|
|
// check drive
|
|
unsigned long bit;
|
|
while (_BitScanForward(&bit, unitmask)) {
|
|
|
|
// remove drive from unitmask so it is not scanned again
|
|
unitmask &= ~(1 << bit);
|
|
|
|
// convert bit to drive letter char
|
|
char drive = 'A' + bit;
|
|
log_info("hotplug", "detected volume arrival: {}", drive);
|
|
|
|
#ifndef SPICETOOLS_SPICECFG_STANDALONE
|
|
// auto insert cards
|
|
for (int player = 0; player <= 1; player++) {
|
|
std::string path = to_string(drive) + ":\\card" + to_string(player) + ".txt";
|
|
if (fileutils::file_exists(path)) {
|
|
uint8_t card_data[8];
|
|
if (eamuse_get_card_from_file(path, card_data, player)) {
|
|
eamuse_card_insert(player, card_data);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// success
|
|
return TRUE;
|
|
}
|
|
case DBT_DEVICEREMOVECOMPLETE: {
|
|
|
|
// check arrived device type
|
|
auto hdr = (DEV_BROADCAST_HDR*) lParam;
|
|
switch (hdr->dbch_devicetype) {
|
|
case DBT_DEVTYP_DEVICEINTERFACE: {
|
|
auto dev = (DEV_BROADCAST_DEVICEINTERFACE*) hdr;
|
|
std::string name(dev->dbcc_name);
|
|
|
|
// destruct device
|
|
log_misc("hotplug", "device interface removal: {}", name);
|
|
this->ri_mgr->devices_remove(name);
|
|
}
|
|
}
|
|
|
|
// success
|
|
return TRUE;
|
|
}
|
|
case DBT_DEVNODES_CHANGED: {
|
|
// catch-all device-tree change: MIDI and XInput have no targeted
|
|
// arrival/removal notification, so this is our only hook to detect them
|
|
// can be a little noisy as it gets called on every device
|
|
log_misc("hotplug", "device tree changed, rescanning MIDI + XInput");
|
|
this->ri_mgr->midi_scan_start();
|
|
this->ri_mgr->devices_scan_xinput();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// default
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
}
|