diff --git a/src/components/panes/configure-panes/submenus/macros/macro-detail.tsx b/src/components/panes/configure-panes/submenus/macros/macro-detail.tsx index 4be4fe7d..61e6080e 100644 --- a/src/components/panes/configure-panes/submenus/macros/macro-detail.tsx +++ b/src/components/panes/configure-panes/submenus/macros/macro-detail.tsx @@ -113,8 +113,8 @@ const BufferSizeUsage = () => { if (!connectedDevice || !api) { return null; } - const {protocol} = connectedDevice; - const macroApi = getMacroAPI(protocol, api); + const {protocol, keycodeVersion} = connectedDevice; + const macroApi = getMacroAPI(protocol, keycodeVersion, api); const bytesUsed = macroApi.rawKeycodeSequencesToMacroBytes(ast).length; return ( diff --git a/src/components/panes/settings.tsx b/src/components/panes/settings.tsx index 119f4587..5143316c 100644 --- a/src/components/panes/settings.tsx +++ b/src/components/panes/settings.tsx @@ -176,6 +176,10 @@ export const Settings = () => { {selectedDevice.protocol} + + + {selectedDevice.keycodeVersion} + ) : null} diff --git a/src/store/definitionsSlice.ts b/src/store/definitionsSlice.ts index dfd0adbe..5ce609ed 100644 --- a/src/store/definitionsSlice.ts +++ b/src/store/definitionsSlice.ts @@ -155,6 +155,7 @@ export const getBasicKeyToByte = createSelector( (connectedDevice) => { const basicKeyToByte = getBasicKeyDict( connectedDevice ? connectedDevice.protocol : 0, + connectedDevice ? connectedDevice.keycodeVersion : 0, ); return {basicKeyToByte, byteToKey: getByteToKey(basicKeyToByte)}; }, diff --git a/src/store/devicesThunks.ts b/src/store/devicesThunks.ts index 565ca3ab..0707a529 100644 --- a/src/store/devicesThunks.ts +++ b/src/store/devicesThunks.ts @@ -151,6 +151,29 @@ export const reloadConnectedDevices = }); } + const keycodeVersions = await Promise.all( + recognisedDevices.map((device) => + new KeyboardAPI(device.path).getKeycodeVersion(), + ), + ); + + const recognisedDevicesWithBadKeycodeVersion = recognisedDevices.filter( + (_, i) => keycodeVersions[i] === -1, + ); + + if (recognisedDevicesWithBadKeycodeVersion.length) { + // Should we exit early?? + recognisedDevicesWithBadKeycodeVersion.forEach((device: WebVIADevice) => { + const deviceInfo = extractDeviceInfo(device); + dispatch( + logAppError({ + message: 'Received invalid keycode version from device', + deviceInfo, + }), + ); + }); + } + const authorizedDevices: AuthorizedDevice[] = recognisedDevices .filter((_, i) => protocolVersions[i] !== -1) .map((device, idx) => { @@ -161,6 +184,7 @@ export const reloadConnectedDevices = productId, vendorId, protocol, + keycodeVersion: protocol >= 13 ? keycodeVersions[idx] : 0, productName, hasResolvedDefinition: false, requiredDefinitionVersion: protocol >= 11 ? 'v3' : 'v2', @@ -173,6 +197,8 @@ export const reloadConnectedDevices = await dispatch(reloadDefinitions(authorizedDevices)); + + const newDefinitions = getDefinitions(getState()); const connectedDevices = authorizedDevices .filter((device, i) => diff --git a/src/store/errorsSlice.ts b/src/store/errorsSlice.ts index c202c91e..aaf457f0 100644 --- a/src/store/errorsSlice.ts +++ b/src/store/errorsSlice.ts @@ -20,6 +20,7 @@ export const extractDeviceInfo = (device: DeviceInfo): DeviceInfo => ({ vendorId: device.vendorId, productName: device.productName, protocol: device.protocol, + keycodeVersion: device.keycodeVersion, }); type ErrorsState = { diff --git a/src/store/macrosSlice.ts b/src/store/macrosSlice.ts index 6cb4f814..8d5d67cb 100644 --- a/src/store/macrosSlice.ts +++ b/src/store/macrosSlice.ts @@ -65,14 +65,14 @@ export default macrosSlice.reducer; export const loadMacros = (connectedDevice: ConnectedDevice): AppThunk => async (dispatch, getState) => { - const {protocol} = connectedDevice; + const {protocol, keycodeVersion} = connectedDevice; if (protocol < 8) { dispatch(setMacrosNotSupported()); } else { try { const state = getState(); const api = getSelectedKeyboardAPI(state) as KeyboardAPI; - const macroApi = getMacroAPI(protocol, api); + const macroApi = getMacroAPI(protocol, keycodeVersion, api); if (macroApi) { const sequences = await macroApi.readRawKeycodeSequences(); const macroBufferSize = await api.getMacroBufferSize(); @@ -92,8 +92,8 @@ export const saveMacros = async (dispatch, getState) => { const state = getState(); const api = getSelectedKeyboardAPI(state) as KeyboardAPI; - const {protocol} = connectedDevice; - const macroApi = getMacroAPI(protocol, api); + const {protocol, keycodeVersion} = connectedDevice; + const macroApi = getMacroAPI(protocol, keycodeVersion, api); if (macroApi) { const sequences = macros.map((expression) => { const optimizedSequence = expressionToSequence(expression); diff --git a/src/types/types.ts b/src/types/types.ts index 3b86fb2f..6b7fe0a5 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -27,6 +27,7 @@ export type DeviceInfo = { productId: number; productName: string; protocol?: number; + keycodeVersion?: number; }; export type Device = DeviceInfo & { @@ -52,6 +53,7 @@ export type AuthorizedDevice = DeviceInfo & { path: string; vendorProductId: number; protocol: number; + keycodeVersion: number; requiredDefinitionVersion: DefinitionVersion; hasResolvedDefinition: false; }; @@ -60,6 +62,7 @@ export type ConnectedDevice = DeviceInfo & { path: string; vendorProductId: number; protocol: number; + keycodeVersion: number; requiredDefinitionVersion: DefinitionVersion; hasResolvedDefinition: true; }; diff --git a/src/utils/key-to-byte/dictionary-store.ts b/src/utils/key-to-byte/dictionary-store.ts index 96540853..13febe7b 100644 --- a/src/utils/key-to-byte/dictionary-store.ts +++ b/src/utils/key-to-byte/dictionary-store.ts @@ -2,20 +2,34 @@ import basicKeyToByte from './default'; import v10BasicKeyToByte from './v10'; import v11BasicKeyToByte from './v11'; import v12BasicKeyToByte from './v12'; -export function getBasicKeyDict(version: number) { - switch (version) { - case 13: - case 12: { - return v12BasicKeyToByte; +export function getBasicKeyDict(protocol: number, keycodeVersion: number) { + if (protocol <= 12) { + switch (protocol) { + case 12: { + return v12BasicKeyToByte; + } + case 11: { + return v11BasicKeyToByte; + } + case 10: { + return v10BasicKeyToByte; + } + default: { + return basicKeyToByte; + } } - case 11: { - return v11BasicKeyToByte; - } - case 10: { - return v10BasicKeyToByte; - } - default: { - return basicKeyToByte; + } + else + { + switch(keycodeVersion) { + // dummy to check the keycode ver too + case 2: + case 1: { + return v12BasicKeyToByte; + } + default: { + return v12BasicKeyToByte; + } } } } diff --git a/src/utils/keyboard-api.ts b/src/utils/keyboard-api.ts index 56118378..24e9e168 100644 --- a/src/utils/keyboard-api.ts +++ b/src/utils/keyboard-api.ts @@ -39,6 +39,8 @@ enum APICommand { DYNAMIC_KEYMAP_SET_BUFFER = 0x13, DYNAMIC_KEYMAP_GET_ENCODER = 0x14, DYNAMIC_KEYMAP_SET_ENCODER = 0x15, + + GET_KEYCODE_VERSION = 0x15, // DEPRECATED: BACKLIGHT_CONFIG_SET_VALUE = 0x07, @@ -105,6 +107,16 @@ export const shiftFrom16Bit = (value: number): [number, number] => [ value & 255, ]; +export const shiftTo32Bit = ([msb, hi, lo, lsb]: [number, number, number, number]): number => + (msb << 24) | (hi << 16) | (lo << 8) | lsb; + +export const shiftFrom32Bit = (value: number): [number, number, number, number] => [ + value >> 24, + value >> 16, + value >> 8, + value & 255, +]; + const shiftBufferTo16Bit = (buffer: number[]): number[] => { const shiftedBuffer = []; for (let i = 0; i < buffer.length; i += 2) { @@ -174,6 +186,15 @@ export class KeyboardAPI { return -1; } } + + async getKeycodeVersion() { + try { + const [, msb, hi, lo, lsb] = await this.hidCommand(APICommand.GET_KEYCODE_VERSION); + return shiftTo32Bit([msb, hi, lo, lsb]); + } catch (e) { + return -1; + } + } async getKey(layer: Layer, row: Row, col: Column) { const buffer = await this.hidCommand( diff --git a/src/utils/macro-api/index.ts b/src/utils/macro-api/index.ts index f828e9a9..59b071c1 100644 --- a/src/utils/macro-api/index.ts +++ b/src/utils/macro-api/index.ts @@ -4,9 +4,9 @@ import type {KeyboardAPI} from '../keyboard-api'; import {MacroAPI, validateMacroExpression} from './macro-api'; import {MacroAPIV11, validateMacroExpressionV11} from './macro-api.v11'; -export const getMacroAPI = (protocol: number, keyboardApi: KeyboardAPI) => { - const basicKeyToByte = getBasicKeyDict(protocol); - const byteToKey = getByteToKey(getBasicKeyDict(protocol)); +export const getMacroAPI = (protocol: number, keycodeVersion: number, keyboardApi: KeyboardAPI) => { + const basicKeyToByte = getBasicKeyDict(protocol, keycodeVersion); + const byteToKey = getByteToKey(getBasicKeyDict(protocol, keycodeVersion)); return protocol >= 11 ? new MacroAPIV11(keyboardApi, basicKeyToByte, byteToKey) : new MacroAPI(keyboardApi, basicKeyToByte, byteToKey);