docs/features/os_detection.md
This feature makes a best guess at the host OS based on OS specific behavior during USB setup. It may not always get the correct OS, and shouldn't be relied on as for critical functionality.
Using it you can have OS specific key mappings or combos which work differently on different devices.
It is available for keyboards which use ChibiOS, LUFA and V-USB.
In your rules.mk add:
OS_DETECTION_ENABLE = yes
It will automatically include the required headers file.
It declares os_variant_t detected_host_os(void); which you can call to get detected OS.
It returns one of the following values:
enum {
OS_UNSURE,
OS_LINUX,
OS_WINDOWS,
OS_MACOS,
OS_IOS,
} os_variant_t;
::: tip Note that it takes some time after firmware is booted to detect the OS. ::: This time is quite short, probably hundreds of milliseconds, but this data may be not ready in keyboard and layout setup functions which run very early during firmware startup.
If you want to perform custom actions when the OS is detected, then you can use the process_detected_host_os_kb function on the keyboard level source file, or process_detected_host_os_user function in the user keymap.c.
bool process_detected_host_os_kb(os_variant_t detected_os) {
if (!process_detected_host_os_user(detected_os)) {
return false;
}
switch (detected_os) {
case OS_MACOS:
case OS_IOS:
rgb_matrix_set_color_all(RGB_WHITE);
break;
case OS_WINDOWS:
rgb_matrix_set_color_all(RGB_BLUE);
break;
case OS_LINUX:
rgb_matrix_set_color_all(RGB_ORANGE);
break;
case OS_UNSURE:
rgb_matrix_set_color_all(RGB_RED);
break;
}
return true;
}
The OS detection is currently handled while the USB device descriptor is being assembled. The process is done in steps, generating a number of intermediate results until it stabilizes. We therefore resort to debouncing the result until it has been stable for a given amount of milliseconds. This amount can be configured, in case your board is not stable within the default debouncing time of 200ms.
#define OS_DETECTION_DEBOUNCE 250
#define OS_DETECTION_KEYBOARD_RESET
#define OS_DETECTION_SINGLE_REPORT
Some KVMs and USB switches may cause issues when the OS detection is turned on. Here is a list of common issues and how to fix them:
OS_DETECTION_KEYBOARD_RESET to force the keyboard to reset upon switching.OS_DETECTION_SINGLE_REPORT to suppress repeated callback invocations.If OS is guessed incorrectly, you may want to collect data about USB setup packets to refine the detection logic.
To do so in your config.h add:
#define OS_DETECTION_DEBUG_ENABLE
And in your rules.mk add:
CONSOLE_ENABLE = yes
And also include "os_detection.h" in your keymap.c.
Then you can define custom keycodes to store data about USB setup packets in EEPROM (persistent memory) and to print it later on host where you can run qmk console:
enum custom_keycodes {
STORE_SETUPS = SAFE_RANGE,
PRINT_SETUPS,
};
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case STORE_SETUPS:
if (record->event.pressed) {
store_setups_in_eeprom();
}
return false;
case PRINT_SETUPS:
if (record->event.pressed) {
print_stored_setups();
}
return false;
default:
return true; // Process all other keycodes normally
}
}
Add both STORE_SETUPS and PRINT_SETUPS to your keyboard's keymap. Connect the keyboard to the device where the OS was not recognised, and press the STORE_SETUPS key to capture and store the fingerprint. On your development computer, run one of the suggested console debugging tools, connect the keyboard, and press the PRINT_SETUPS key. The console should display multiple lines of data from the most recent STORE_SETUPS run.
Open an issue on GitHub and paste the console output into the issue. Also tell us which OS (including the version, if possible) was not detected correctly and whether any intermediate devices, such as a USB hub, were used between the keyboard and the target device.
::: tip
If STORE_SETUPS has not been used previously, PRINT_SETUPS will report whatever values are already present in the controller's EEPROM. These may appear as random numbers.
:::
Original idea is coming from FingerprintUSBHost project.