diff -Nru ckb-next-0.4.3-28/CHANGELOG.md ckb-next-0.4.4-8/CHANGELOG.md --- ckb-next-0.4.3-28/CHANGELOG.md 2020-12-18 05:15:15.000000000 +0000 +++ ckb-next-0.4.4-8/CHANGELOG.md 2021-06-20 12:34:50.000000000 +0000 @@ -1,5 +1,15 @@ # Change Log +## [v0.4.4](https://github.com/ckb-next/ckb-next/tree/v0.4.4) (2021-03-18) +[Full Changelog](https://github.com/ckb-next/ckb-next/compare/v0.4.3...v0.4.4) + +Important bugfixes: +- Fixed a crash when creating a new profile +- Fixed lights turning off unexpectedly or not being restored correctly +- Macros can now be repeated quickly by repeatedly pressing the keys +- Fixed various deadlocks when resuming from suspend or during device initialisation +- Fixed devices not functioning during system boot requiring a daemon restart + ## [v0.4.3](https://github.com/ckb-next/ckb-next/tree/v0.4.3) (2020-12-18) [Full Changelog](https://github.com/ckb-next/ckb-next/compare/v0.4.2...v0.4.3) diff -Nru ckb-next-0.4.3-28/cmake/modules/CkbNextCompileFlags.cmake ckb-next-0.4.4-8/cmake/modules/CkbNextCompileFlags.cmake --- ckb-next-0.4.3-28/cmake/modules/CkbNextCompileFlags.cmake 2020-08-28 17:08:47.000000000 +0000 +++ ckb-next-0.4.4-8/cmake/modules/CkbNextCompileFlags.cmake 2021-06-20 12:34:50.000000000 +0000 @@ -44,6 +44,7 @@ -Werror=return-type -Wshadow $<$:${opt_lvl}> + $<$:-Wvla> # $<$:-Wfloat-equal> # $<$:-Wundef> # $<$:-Wshadow> diff -Nru ckb-next-0.4.3-28/CMakeLists.txt ckb-next-0.4.4-8/CMakeLists.txt --- ckb-next-0.4.3-28/CMakeLists.txt 2021-03-18 11:40:00.000000000 +0000 +++ ckb-next-0.4.4-8/CMakeLists.txt 2021-06-20 12:34:50.000000000 +0000 @@ -63,7 +63,7 @@ # Get more precise version from git, fallback on release include(CkbNextDetermineVersion) find_package(Git) -set(ckb-next_VERSION "0.4.3-28-geea21d4") +set(ckb-next_VERSION "0.4.4-8-g8788ef2") find_package(Sanitizers) diff -Nru ckb-next-0.4.3-28/debian/changelog ckb-next-0.4.4-8/debian/changelog --- ckb-next-0.4.3-28/debian/changelog 2021-03-18 11:40:28.000000000 +0000 +++ ckb-next-0.4.4-8/debian/changelog 2021-06-20 12:35:22.000000000 +0000 @@ -1,3 +1,9 @@ +ckb-next (0.4.4-8-g8788ef2~hirsute) hirsute; urgency=low + + * 0.4.4-8-g8788ef2 git upstream + + -- Tasos Sahanidis Sun, 20 Jun 2021 15:35:22 +0300 + ckb-next (0.4.3-28-geea21d4~hirsute) hirsute; urgency=low * 0.4.3-28-geea21d4 git upstream diff -Nru ckb-next-0.4.3-28/libera.svg ckb-next-0.4.4-8/libera.svg --- ckb-next-0.4.3-28/libera.svg 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.4-8/libera.svg 2021-06-20 12:34:50.000000000 +0000 @@ -0,0 +1 @@ +#irc: libera.chat#irclibera.chat \ No newline at end of file diff -Nru ckb-next-0.4.3-28/README.md ckb-next-0.4.4-8/README.md --- ckb-next-0.4.3-28/README.md 2020-11-13 13:29:22.000000000 +0000 +++ ckb-next-0.4.4-8/README.md 2021-06-20 12:34:50.000000000 +0000 @@ -1,6 +1,6 @@ # ckb-next: RGB Driver for Linux and macOS - +irc.libera.chat #ckb-next **ckb-next** is an open-source driver for Corsair keyboards and mice. It aims to bring the features of Corsair's proprietary CUE software to Linux operating systems. This project is currently a work in progress, but it already supports much of the same functionality, including full RGB animations. More features are coming soon. Testing and bug reports are appreciated! @@ -40,6 +40,6 @@ Maintainers reserve the rights to modify and remove issues, pull requests and comments therein, that are denunciating, off-topic, harmful, hateful and overall inappropriate. Please be appreciative, humble and kind to each other. -* IRC chat: `#ckb-next` channel at [chat.freenode.net](http://webchat.freenode.net?channels=%23ckb-next&uio=d4) +* IRC chat: `#ckb-next` channel at [irc.libera.chat](https://web.libera.chat/?channels=#ckb-next) * Mailing list: [ckb-next@googlegroups.com](https://groups.google.com/forum/#!forum/ckb-next) * [GitHub Issues](https://github.com/ckb-next/ckb-next/issues) diff -Nru ckb-next-0.4.3-28/src/ckbnextconfig.h.in ckb-next-0.4.4-8/src/ckbnextconfig.h.in --- ckb-next-0.4.3-28/src/ckbnextconfig.h.in 2021-03-18 11:40:00.000000000 +0000 +++ ckb-next-0.4.4-8/src/ckbnextconfig.h.in 2021-06-20 12:34:50.000000000 +0000 @@ -11,3 +11,5 @@ #cmakedefine DEBUG_USB_INPUT #cmakedefine DEBUG_MUTEX #cmakedefine NO_FAIR_MUTEX_QUEUEING + +#define CKB_NEXT_COPYRIGHT_YEAR "${ckb-next_COPYRIGHT_YEAR}" diff -Nru ckb-next-0.4.3-28/src/daemon/CMakeLists.txt ckb-next-0.4.4-8/src/daemon/CMakeLists.txt --- ckb-next-0.4.3-28/src/daemon/CMakeLists.txt 2020-12-25 14:26:10.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/CMakeLists.txt 2021-06-20 12:34:50.000000000 +0000 @@ -83,6 +83,8 @@ profile_keyboard.c profile_mouse.c usb.c + usb_nxp.c + usb_legacy.c command.h device.h devnode.h @@ -101,7 +103,10 @@ protocol.h request_hid_mac.h structures.h - usb.h) + usb.h + usb_nxp.h + usb_legacy.h + ) endif () if (MACOS) target_sources( diff -Nru ckb-next-0.4.3-28/src/daemon/command.h ckb-next-0.4.4-8/src/daemon/command.h --- ckb-next-0.4.3-28/src/daemon/command.h 2020-10-10 07:11:19.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/command.h 2021-06-20 12:34:50.000000000 +0000 @@ -71,6 +71,7 @@ typedef void (*cmdhandler)(usbdevice* kb, usbmode* modeidx, int notifyidx, int keyindex, const char* parameter); // Normal command typedef int (*cmdhandler_io)(usbdevice* kb, usbmode* modeidx, int notifyidx, int keyindex, const char* parameter); // Command with hardware I/O - returns zero on success typedef void (*cmdhandler_mac)(usbdevice* kb, usbmode* modeidx, int notifyidx, const char* keys, const char* assignment); // Macro command has a different left-side handler +typedef int (*device_io)(usbdevice* kb, void* ptr, int len, int is_recv, const char* file, int line); typedef union devcmd { // Commands can be accessed by name or by position cmdhandler do_cmd[CMD_DEV_COUNT]; @@ -136,6 +137,12 @@ int (*updatedpi)(usbdevice* kb, int force); cmdhandler reset; + + // Everything below this line should not be exposed through the command interface + void (*fill_input_eps)(usbdevice* kb); + + device_io write; + device_io read; }; } devcmd; diff -Nru ckb-next-0.4.3-28/src/daemon/device.c ckb-next-0.4.4-8/src/daemon/device.c --- ckb-next-0.4.3-28/src/daemon/device.c 2021-03-18 11:40:00.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/device.c 2021-06-20 12:34:50.000000000 +0000 @@ -259,7 +259,7 @@ } if(pkt[2] != 0xff){ - if(!usbsend(kb, pkt, 1)) + if(!usbsend(kb, pkt, sizeof(pkt), 1)) ckb_err("%s reset failed", type); } } diff -Nru ckb-next-0.4.3-28/src/daemon/device_keyboard.c ckb-next-0.4.4-8/src/daemon/device_keyboard.c --- ckb-next-0.4.3-28/src/daemon/device_keyboard.c 2020-09-03 16:26:45.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/device_keyboard.c 2021-06-20 12:34:50.000000000 +0000 @@ -37,7 +37,7 @@ if(active){ // Put the M-keys (K95) as well as the Brightness/Lock keys into software-controlled mode. msg[0][2] = MODE_SOFTWARE; - if(!usbsend(kb, msg[0], 1)) + if(!usbsend(kb, msg[0], MSG_SIZE, 1)) return -1; DELAY_MEDIUM(kb); // Set input mode on the keys. They must be grouped into packets of 60 bytes (+ 4 bytes header) @@ -55,20 +55,20 @@ } // Byte 2 = pair count (usually 30, less on final message) msg[1][2] = pair; - if(!usbsend(kb, msg[1], 1)) + if(!usbsend(kb, msg[1], MSG_SIZE, 1)) return -1; } // Commit new input settings - if(!usbsend(kb, msg[2], 1)) + if(!usbsend(kb, msg[2], MSG_SIZE, 1)) return -1; DELAY_MEDIUM(kb); } else { // Set the M-keys back into hardware mode, restore hardware RGB profile. It has to be sent twice for some reason. msg[0][2] = MODE_HARDWARE; - if(!usbsend(kb, msg[0], 1)) + if(!usbsend(kb, msg[0], MSG_SIZE, 1)) return -1; DELAY_MEDIUM(kb); - if(!usbsend(kb, msg[0], 1)) + if(!usbsend(kb, msg[0], MSG_SIZE, 1)) return -1; DELAY_MEDIUM(kb); #ifdef OS_LINUX @@ -97,11 +97,11 @@ } // Byte 2 = pair count (usually 30, less on final message) msg[1][2] = pair; - if(!usbsend(kb, msg[1], 1)) + if(!usbsend(kb, msg[1], MSG_SIZE, 1)) return -1; } // Commit new input settings - if(!usbsend(kb, msg[2], 1)) + if(!usbsend(kb, msg[2], MSG_SIZE, 1)) return -1; DELAY_MEDIUM(kb); #endif diff -Nru ckb-next-0.4.3-28/src/daemon/device_mouse.c ckb-next-0.4.4-8/src/daemon/device_mouse.c --- ckb-next-0.4.3-28/src/daemon/device_mouse.c 2020-08-05 20:40:56.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/device_mouse.c 2021-06-20 12:34:50.000000000 +0000 @@ -8,20 +8,18 @@ int start_mouse_legacy(usbdevice* kb, int makeactive){ (void)makeactive; - - // kb, ptr, len, bRequest, wValue, wIndex // 0x00002 == HW Playback off // 0x00001 == HW Playback on - usbsend_control(kb, NULL, 0, 2, 0x0002, 0); + usbsend(kb, (&(ctrltransfer) { .bRequestType = 0x40, .bRequest = 2, .wValue = 0x0002, .wIndex = 0, .wLength = 0, .timeout = 5000, .data = NULL }), 0, 1); // Send initial DPI settings unsigned char pkt1[] = {0x04, 0x02, 0x10, 0x10, 0x30, 0x30, 0x78, 0x78, 0x08, 0x08}; - usbsend_control(kb, pkt1, 10, 174, 0x0000, 0); + usbsend(kb, (&(ctrltransfer) { .bRequestType = 0x40, .bRequest = 174, .wValue = 0x0000, .wIndex = 0, .wLength = sizeof(pkt1), .timeout = 5000, .data = pkt1 }), 0, 1); // ???? unsigned char pkt2[] = {0x02, 0x02, 0x03, 0x02, 0xff, 0x3c, 0x00, 0x00, 0x00, 0x06}; - usbsend_control(kb, pkt2, 10, 3, 0x0000, 0); + usbsend(kb, (&(ctrltransfer) { .bRequestType = 0x40, .bRequest = 3, .wValue = 0x0000, .wIndex = 0, .wLength = sizeof(pkt2), .timeout = 5000, .data = pkt2 }), 0, 1); // Angle snap ON unsigned char pkt3[] = {0x01}; - usbsend_control(kb, pkt3, 1, 100, 0x0000, 0); + usbsend(kb, (&(ctrltransfer) { .bRequestType = 0x40, .bRequest = 100, .wValue = 0x0000, .wIndex = 0, .wLength = sizeof(pkt3), .timeout = 5000, .data = pkt3 }), 0, 1); kb->active = 1; kb->pollrate = -1; @@ -33,7 +31,7 @@ (void)dummy2; (void)dummy3; - usbsend_control(kb, NULL, 0, 10, rate, 0); + usbsend(kb, (&(ctrltransfer) { .bRequestType = 0x40, .bRequest = 10, .wValue = rate, .wIndex = 0, .wLength = 0, .timeout = 5000, .data = NULL }), 0, 1); // Device should disconnect+reconnect, but update the poll rate field in case it doesn't kb->pollrate = rate; @@ -61,11 +59,11 @@ memset(&kb->input.keys, 0, sizeof(kb->input.keys)); inputupdate(kb); queued_mutex_unlock(imutex(kb)); - if(!usbsend(kb, msg[0], 1)) + if(!usbsend(kb, msg[0], MSG_SIZE, 1)) return -1; if(active){ // Set up key input - if(!usbsend(kb, msg[1], 1)) + if(!usbsend(kb, msg[1], MSG_SIZE, 1)) return -1; for(int i = 0; i < keycount; i++){ msg[1][i * 2 + 4] = i + 1; @@ -101,7 +99,7 @@ uchar msg[MSG_SIZE] = { CMD_SET, FIELD_POLLRATE, 0, 0, (uchar)rate }; - if(!usbsend(kb, msg, 1)) + if(!usbsend(kb, msg, sizeof(msg), 1)) return -1; // Device should disconnect+reconnect, but update the poll rate field in case it doesn't kb->pollrate = rate; diff -Nru ckb-next-0.4.3-28/src/daemon/device_vtable.c ckb-next-0.4.4-8/src/daemon/device_vtable.c --- ckb-next-0.4.3-28/src/daemon/device_vtable.c 2020-10-10 07:11:19.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/device_vtable.c 2021-06-20 12:34:50.000000000 +0000 @@ -6,6 +6,8 @@ #include "led.h" #include "notify.h" #include "profile.h" +#include "usb_legacy.h" +#include "usb_nxp.h" // Do-nothing functions static void cmd_none(usbdevice* kb, usbmode* dummy1, int dummy2, int dummy3, const char* dummy4){ @@ -47,6 +49,13 @@ return 0; } +#if 0 +static int cmd_io_stub(usbdevice* kb, void* ptr, int len, int is_recv, const char* file, int line){ + ckb_fatal_fn("This should never be called", file, line); + return -1; +} +#endif + /// \brief RGB keyboard vtable holds functions for each device type. /// const devcmd vtable_keyboard = { @@ -85,14 +94,17 @@ .get = cmd_get, .start = start_dev, - .setmodeindex = int1_void_none, ///< is just for non rgb keyboards + .setmodeindex = int1_void_none, .allocprofile = allocprofile, .loadprofile = loadprofile, .freeprofile = freeprofile, .updatergb = updatergb_kb, .updateindicators = updateindicators_kb, - .updatedpi = int1_int_none, ///< This is for mice only + .updatedpi = int1_int_none, .reset = nxp_reset, + .fill_input_eps = nxp_fill_input_eps, + .write = nxp_usb_write, + .read = nxp_usb_read, }; // Legacy keyboard vtable (K70) @@ -132,14 +144,17 @@ .get = cmd_get, .start = start_kb_legacy, - .setmodeindex = setmodeindex_legacy, ///< This is special for legacy keyboards + .setmodeindex = setmodeindex_legacy, .allocprofile = allocprofile, .loadprofile = loadprofile_none, .freeprofile = freeprofile, - .updatergb = int1_int_none, ///< Legacy keyboards do not have an rgb update function + .updatergb = int1_int_none, .updateindicators = updateindicators_kb, - .updatedpi = int1_int_none, ///< This is for mice only + .updatedpi = int1_int_none, .reset = cmd_none, + .fill_input_eps = legacy_fill_input_eps, + .write = legacy_dev_io, + .read = legacy_dev_io, }; // RGB mouse vtable @@ -179,14 +194,17 @@ .get = cmd_get, .start = start_dev, - .setmodeindex = int1_void_none, ///< Mice do not have different modes - .allocprofile = allocprofile, ///< same for all keyboards and mice - .loadprofile = loadprofile, ///< same for all keyboards and mice - .freeprofile = freeprofile, ///< same for all keyboards and mice - .updatergb = updatergb_mouse, ///< special for mice - .updateindicators = int1_void_none, ///< Mice do not have keyboard indicators like num - .updatedpi = updatedpi, ///< special for mice + .setmodeindex = int1_void_none, + .allocprofile = allocprofile, + .loadprofile = loadprofile, + .freeprofile = freeprofile, + .updatergb = updatergb_mouse, + .updateindicators = int1_void_none, + .updatedpi = updatedpi, .reset = nxp_reset, + .fill_input_eps = nxp_fill_input_eps, + .write = nxp_usb_write, + .read = nxp_usb_read, }; // RGB Mousepad vtable @@ -234,6 +252,9 @@ .updateindicators = int1_void_none, .updatedpi = int1_int_none, .reset = nxp_reset, + .fill_input_eps = nxp_fill_input_eps, + .write = nxp_usb_write, + .read = nxp_usb_read, }; // Legacy mouse vtable @@ -281,4 +302,7 @@ .updateindicators = int1_void_none, .updatedpi = updatedpi_legacy, .reset = cmd_none, + .fill_input_eps = legacy_fill_input_eps, + .write = legacy_dev_io, + .read = legacy_dev_io, }; diff -Nru ckb-next-0.4.3-28/src/daemon/dpi.c ckb-next-0.4.4-8/src/daemon/dpi.c --- ckb-next-0.4.3-28/src/daemon/dpi.c 2020-09-03 16:26:45.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/dpi.c 2021-06-20 12:34:50.000000000 +0000 @@ -132,7 +132,7 @@ newenabled = lastdpi->enabled | 1 << newdpi->current; } uchar data_pkt[MSG_SIZE] = { CMD_SET, FIELD_MOUSE, MOUSE_DPIMASK, 0, newenabled }; - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -2; // Cache the flags we wrote. lastdpi->enabled = newenabled; @@ -149,7 +149,7 @@ data_pkt[6] = (newdpi->x[newdpi->current] >> 8) & 0xFF; data_pkt[7] = newdpi->y[newdpi->current] & 0xFF; data_pkt[8] = (newdpi->y[newdpi->current] >> 8) & 0xFF; - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -1; // Set these values in the cache so we don't rewrite them. lastdpi->x[newdpi->current] = newdpi->x[newdpi->current]; @@ -157,7 +157,7 @@ } // Set current DPI stage. uchar data_pkt[MSG_SIZE] = { CMD_SET, FIELD_MOUSE, MOUSE_DPI, 0, newdpi->current }; - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -2; } @@ -175,24 +175,24 @@ data_pkt[6] = (newdpi->x[i] >> 8) & 0xFF; data_pkt[7] = newdpi->y[i] & 0xFF; data_pkt[8] = (newdpi->y[i] >> 8) & 0xFF; - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -1; } // Send settings if (newdpi->enabled != lastdpi->enabled || force) { uchar data_pkt[MSG_SIZE] = { CMD_SET, FIELD_MOUSE, MOUSE_DPIMASK, 0, newdpi->enabled }; - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -2; } if (newdpi->lift != lastdpi->lift || force) { uchar data_pkt[MSG_SIZE] = { CMD_SET, FIELD_MOUSE, MOUSE_LIFT, 0, newdpi->lift }; - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -2; } if (newdpi->snap != lastdpi->snap || force) { uchar data_pkt[MSG_SIZE] = { CMD_SET, FIELD_MOUSE, MOUSE_SNAP, 0, newdpi->snap, 0x05 }; - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -2; } @@ -214,7 +214,7 @@ data_pkt[9] = light->r[LED_MOUSE + N_MOUSE_ZONES + i]; data_pkt[10] = light->g[LED_MOUSE + N_MOUSE_ZONES + i]; data_pkt[11] = light->b[LED_MOUSE + N_MOUSE_ZONES + i]; - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -1; } @@ -225,7 +225,7 @@ { CMD_SET, FIELD_MOUSE, MOUSE_LIFT, 1, dpi->lift }, { CMD_SET, FIELD_MOUSE, MOUSE_SNAP, 1, dpi->snap, 0x05 } }; - if(!usbsend(kb, data_pkt[0], 4)) + if(!usbsend(kb, data_pkt[0], MSG_SIZE, 4)) return -2; // Finished return 0; @@ -241,7 +241,7 @@ }; uchar in_pkt[4][MSG_SIZE]; for(int i = 0; i < 4; i++){ - if(!usbrecv(kb, data_pkt[i], in_pkt[i])) + if(!usbrecv(kb, data_pkt[i], MSG_SIZE, in_pkt[i])) return -2; if(memcmp(in_pkt[i], data_pkt[i], 4)){ ckb_err("Bad input header"); @@ -264,7 +264,7 @@ uchar dpi_pkt[MSG_SIZE] = { CMD_GET, FIELD_MOUSE, MOUSE_DPIPROF, 1 }; uchar dpi_in_pkt[MSG_SIZE]; dpi_pkt[2] |= i; - if(!usbrecv(kb, dpi_pkt, dpi_in_pkt)) + if(!usbrecv(kb, dpi_pkt, sizeof(dpi_pkt), dpi_in_pkt)) return -2; if(memcmp(dpi_in_pkt, dpi_pkt, 4)){ ckb_err("Bad input header"); diff -Nru ckb-next-0.4.3-28/src/daemon/dpi_legacy.c ckb-next-0.4.4-8/src/daemon/dpi_legacy.c --- ckb-next-0.4.3-28/src/daemon/dpi_legacy.c 2019-11-06 00:55:23.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/dpi_legacy.c 2021-06-20 12:34:50.000000000 +0000 @@ -13,7 +13,7 @@ return; mode->dpi.lift = heightnum; - usbsend_control(kb, NULL, 0, 13, heightnum - 1, 0); + usbsend(kb, (&(ctrltransfer) { .bRequestType = 0x40, .bRequest = 13, .wValue = heightnum - 1, .wIndex = 0, .wLength = 0, .timeout = 5000, .data = NULL }), 0, 1); } void cmd_snap_legacy(usbdevice* kb, usbmode* mode, int dummy1, int dummy2, const char* enable){ @@ -23,11 +23,10 @@ if(!strcmp(enable, "on")) { mode->dpi.snap = 1; - usbsend_control(kb, &mode->dpi.snap, 1, 100, 0, 0); - } - if(!strcmp(enable, "off")) { + usbsend(kb, (&(ctrltransfer) { .bRequestType = 0x40, .bRequest = 100, .wValue = 0x0000, .wIndex = 0, .wLength = 1, .timeout = 5000, .data = &mode->dpi.snap }), 0, 1); + } else if(!strcmp(enable, "off")) { mode->dpi.snap = 0; - usbsend_control(kb, NULL, 0, 100, 0, 0); + usbsend(kb, (&(ctrltransfer) { .bRequestType = 0x40, .bRequest = 100, .wValue = 0x0000, .wIndex = 0, .wLength = 0, .timeout = 5000, .data = NULL }), 0, 1); } } @@ -60,7 +59,7 @@ dpi_pkt[8] = newdpi->y[0] / 50; dpi_pkt[9] = newdpi->x[0] / 50; - usbsend_control(kb, dpi_pkt, 10, 174, 0x0000, 0); + usbsend(kb, (&(ctrltransfer) { .bRequestType = 0x40, .bRequest = 174, .wValue = 0x0000, .wIndex = 0, .wLength = sizeof(dpi_pkt), .timeout = 5000, .data = dpi_pkt }), 0, 1); memcpy(lastdpi, newdpi, sizeof(dpiset)); return 0; diff -Nru ckb-next-0.4.3-28/src/daemon/firmware.c ckb-next-0.4.4-8/src/daemon/firmware.c --- ckb-next-0.4.3-28/src/daemon/firmware.c 2020-08-30 18:45:18.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/firmware.c 2021-06-20 12:34:50.000000000 +0000 @@ -21,7 +21,7 @@ kb->layout = LAYOUT_UNKNOWN; } - if(!usbrecv(kb, data_pkt, in_pkt)) + if(!usbrecv(kb, data_pkt, sizeof(data_pkt), in_pkt)) return -1; if(in_pkt[0] != CMD_GET || in_pkt[1] != FIELD_IDENT){ ckb_err("Bad input header"); @@ -140,14 +140,14 @@ } } if(index == 1){ - if(!usbsend(kb, data_pkt[0], 1)){ + if(!usbsend(kb, data_pkt[0], MSG_SIZE, 1)){ ckb_err("Firmware update failed"); free(fwdata); return FW_USBFAIL; } // The above packet can take a lot longer to process, so wait for a while sleep(3); - if(!usbsend(kb, data_pkt[2], npackets - 1)){ + if(!usbsend(kb, data_pkt[2], MSG_SIZE, npackets - 1)){ ckb_err("Firmware update failed"); free(fwdata); return FW_USBFAIL; @@ -156,7 +156,7 @@ // If the output ends here, set the length byte appropriately if(output >= length) data_pkt[npackets][2] = length - last; - if(!usbsend(kb, data_pkt[1], npackets)){ + if(!usbsend(kb, data_pkt[1], MSG_SIZE, npackets)){ ckb_err("Firmware update failed"); free(fwdata); return FW_USBFAIL; @@ -169,7 +169,7 @@ { CMD_SET, FIELD_FW_DATA, 0xf0, 0x00, 0x00, 0x00, index }, { CMD_SET, FIELD_RESET, RESET_SLOW, 0 } }; - if(!usbsend(kb, data_pkt2[0], 2)){ + if(!usbsend(kb, data_pkt2[0], MSG_SIZE, 2)){ ckb_err("Firmware update failed"); free(fwdata); return FW_USBFAIL; diff -Nru ckb-next-0.4.3-28/src/daemon/input.c ckb-next-0.4.4-8/src/daemon/input.c --- ckb-next-0.4.3-28/src/daemon/input.c 2021-03-07 17:27:13.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/input.c 2021-06-20 12:34:50.000000000 +0000 @@ -387,7 +387,17 @@ kb->hw_ileds_old = hw_new; if(old != new || force){ DELAY_SHORT(kb); - os_sendindicators(kb); + + ushort leds = kb->ileds; + int len = 1; + if(kb->fwversion >= 0x300 || IS_V3_OVERRIDE(kb)) { + leds = (kb->ileds << 8) | 0x0001; + len = 2; + } + queued_mutex_unlock(dmutex(kb)); + ctrltransfer transfer = { .bRequestType = 0x21, .bRequest = 0x09, .wValue = 0x0200, .wIndex = 0, .wLength = len, .timeout = 5000, .data = &leds }; + os_usb_control(kb, &transfer, __FILE_NOPATH__, __LINE__); + queued_mutex_lock(dmutex(kb)); } // Print notifications if desired if(!kb->active) diff -Nru ckb-next-0.4.3-28/src/daemon/led_keyboard.c ckb-next-0.4.4-8/src/daemon/led_keyboard.c --- ckb-next-0.4.3-28/src/daemon/led_keyboard.c 2020-08-30 18:45:18.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/led_keyboard.c 2021-06-20 12:34:50.000000000 +0000 @@ -40,7 +40,7 @@ uchar data_pkt[MSG_SIZE] = { CMD_SET, FIELD_LIGHTING, MODE_SIDELIGHT, 0, !!newlight->sidelight, 0x00 }; - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -1; return 0; } @@ -116,7 +116,7 @@ uchar winlock_pkt[MSG_SIZE] = { CMD_SET, FIELD_LIGHTING, MODE_WINLOCK, 0, winlock_status, 0 }; - if (!usbsend(kb, winlock_pkt, 1)) + if (!usbsend(kb, winlock_pkt, sizeof(winlock_pkt), 1)) return -1; } @@ -128,7 +128,7 @@ // The K55 has its own packet type, because it only has three lighting zones. uchar data_pkt[MSG_SIZE] = { 0x07, 0x25, 0x00 }; makergb_k55(newlight, data_pkt); - if (!usbsend(kb, data_pkt, 1)) + if (!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -1; } else if(IS_FULLRANGE(kb)) { // Update strafe sidelights if necessary @@ -160,7 +160,7 @@ if(IS_MONOCHROME_DEV(kb)) data_pkt_count = 4; - if(!usbsend(kb, data_pkt[0], data_pkt_count)) + if(!usbsend(kb, data_pkt[0], MSG_SIZE, data_pkt_count)) return -1; } else { // Update strafe sidelights if necessary @@ -175,7 +175,7 @@ { CMD_SET, FIELD_KB_9BCLR, 0x00, 0x00, 0xD8 } }; makergb_512(newlight, data_pkt, kb->dither ? ordered8to3 : quantize8to3); - if(!usbsend(kb, data_pkt[0], 5)) + if(!usbsend(kb, data_pkt[0], MSG_SIZE, 5)) return -1; } @@ -203,11 +203,11 @@ { CMD_SET, FIELD_KB_HWCLR, 0x03, 0x01, 0x01, mode + 1, COLOR_BLUE } }; makergb_full(light, data_pkt); - if(!usbsend(kb, data_pkt[0], 12)) + if(!usbsend(kb, data_pkt[0], MSG_SIZE, 12)) return -1; if (IS_STRAFE(kb)){ // end save uchar save_end_pkt[MSG_SIZE] = { CMD_SET, FIELD_KB_HWCLR, 0x04, 0x01, 0x01 }; - if(!usbsend(kb, save_end_pkt, 1)) + if(!usbsend(kb, save_end_pkt, sizeof(save_end_pkt), 1)) return -1; } } else { @@ -219,7 +219,7 @@ { CMD_SET, FIELD_KB_HWCLR, 0x02, 0x00, 0x01, mode + 1 } }; makergb_512(light, data_pkt, kb->dither ? ordered8to3 : quantize8to3); - if(!usbsend(kb, data_pkt[0], 5)) + if(!usbsend(kb, data_pkt[0], MSG_SIZE, 5)) return -1; } return 0; @@ -264,7 +264,7 @@ uchar* colors[3] = { light->r, light->g, light->b }; for(int clr = 0; clr < 3; clr++){ for(int i = 0; i < 4; i++){ - if(!usbrecv(kb, data_pkt[i + clr * 4], in_pkt[i])) + if(!usbrecv(kb, data_pkt[i + clr * 4], MSG_SIZE, in_pkt[i])) return -1; uchar* comparePacket = data_pkt[i + clr * 4]; ///< That is the old comparison method: you get back what you sent. @@ -286,7 +286,7 @@ in_pkt[2][1] = 0x99; in_pkt[2][2] = 0x99; in_pkt[2][3] = 0x99; - usbrecv(kb, in_pkt[2], in_pkt[2]); // just to find it in the wireshark log + usbrecv(kb, in_pkt[2], MSG_SIZE, in_pkt[2]); // just to find it in the wireshark log return -1; } } @@ -310,11 +310,11 @@ { CMD_READ_BULK, 0x04, 36, 0 }, }; // Write initial packet - if(!usbsend(kb, data_pkt[0], 1)) + if(!usbsend(kb, data_pkt[0], MSG_SIZE, 1)) return -1; // Read colors for(int i = 1; i < 5; i++){ - if(!usbrecv(kb, data_pkt[i],in_pkt[i - 1])) + if(!usbrecv(kb, data_pkt[i], MSG_SIZE, in_pkt[i - 1])) return -1; if(memcmp(in_pkt[i - 1], data_pkt[i], 4)){ ckb_err("Bad input header"); diff -Nru ckb-next-0.4.3-28/src/daemon/led_mouse.c ckb-next-0.4.4-8/src/daemon/led_mouse.c --- ckb-next-0.4.3-28/src/daemon/led_mouse.c 2020-08-30 18:45:18.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/led_mouse.c 2021-06-20 12:34:50.000000000 +0000 @@ -33,7 +33,7 @@ *rgb_data++ = newlight->b[LED_MOUSE + i]; } // Send RGB data - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -1; memcpy(lastlight, newlight, sizeof(lighting)); @@ -53,7 +53,7 @@ data_pkt[4] = light->r[led]; data_pkt[5] = light->g[led]; data_pkt[6] = light->b[led]; - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -1; // Set packet for next zone data_pkt[2]++; @@ -69,7 +69,7 @@ // Load each RGB zone int zonecount = IS_SCIMITAR(kb) ? 4 : IS_SABRE(kb) ? 3 : 2; for(int i = 0; i < zonecount; i++){ - if(!usbrecv(kb, data_pkt, in_pkt)) + if(!usbrecv(kb, data_pkt, sizeof(data_pkt), in_pkt)) return -1; if(memcmp(in_pkt, data_pkt, 4)){ ckb_err("Bad input header"); @@ -108,6 +108,6 @@ lastlight->g[MOUSE_BACK_LED] = newlight->g[MOUSE_BACK_LED]; lastlight->b[MOUSE_BACK_LED] = newlight->b[MOUSE_BACK_LED]; - usbsend_control(kb, NULL, 0, 49, newwValue, 0); + usbsend(kb, (&(ctrltransfer) { .bRequestType = 0x40, .bRequest = 49, .wValue = newwValue, .wIndex = 0, .wLength = 0, .timeout = 5000, .data = NULL }), 0, 1); return 0; } diff -Nru ckb-next-0.4.3-28/src/daemon/led_mousepad.c ckb-next-0.4.4-8/src/daemon/led_mousepad.c --- ckb-next-0.4.3-28/src/daemon/led_mousepad.c 2019-11-06 00:55:23.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/led_mousepad.c 2021-06-20 12:34:50.000000000 +0000 @@ -30,7 +30,7 @@ *rgb_data++ = newlight->b[i]; } // Send RGB data - if(!usbsend(kb, data_pkt, 1)) + if(!usbsend(kb, data_pkt, sizeof(data_pkt), 1)) return -1; memcpy(lastlight, newlight, sizeof(lighting)); return 0; diff -Nru ckb-next-0.4.3-28/src/daemon/main.c ckb-next-0.4.4-8/src/daemon/main.c --- ckb-next-0.4.3-28/src/daemon/main.c 2021-03-07 17:27:13.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/main.c 2021-06-20 12:34:50.000000000 +0000 @@ -8,10 +8,12 @@ #include #include #include +#include "keymap_patch.h" // usb.c extern _Atomic int reset_stop; extern int features_mask; +extern int enable_experimental; // device.c extern int hwload_mode; @@ -245,6 +247,35 @@ ignored_devices[j].idProduct = pid; break; } + } else if(!strncmp(argument, "--search=", 9)) { + char* searchstr = argument + 9; + usbdevice dev = {0}; + + int count; + if(sscanf(searchstr, "%hx:%hx%n", &dev.vendor, &dev.product, &count) == 2) { + searchstr += count; + if(*searchstr != '\0') + searchstr++; + } + + patchkeys(&dev); + + // Search through the patched keymap + for (int j = 0; j < N_KEYS_EXTENDED; j++) { + if (!dev.keymap[j].name) + continue; + + if (!strcasecmp(searchstr, dev.keymap[j].name)) { + printf("Key %s has id %d\n", dev.keymap[j].name, j); + free(dev.keymap); + return 0; + } + } + printf("Key %s was not found\n", searchstr); + free(dev.keymap); + return 1; + } else if(!strcmp(argument, "--enable-experimental")) { + enable_experimental = 1; } #ifdef OS_MAC_LEGACY else if(!strcmp(argument, "--nomouseaccel")){ diff -Nru ckb-next-0.4.3-28/src/daemon/profile_keyboard.c ckb-next-0.4.4-8/src/daemon/profile_keyboard.c --- ckb-next-0.4.3-28/src/daemon/profile_keyboard.c 2019-11-06 00:55:23.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/profile_keyboard.c 2021-06-20 12:34:50.000000000 +0000 @@ -6,7 +6,7 @@ // Ask for mode's name uchar data_pkt[MSG_SIZE] = { 0x0e, 0x16, 0x01, mode + 1, 0 }; uchar in_pkt[MSG_SIZE]; - if(!usbrecv(kb, data_pkt, in_pkt)) + if(!usbrecv(kb, data_pkt, sizeof(data_pkt), in_pkt)) return -1; memcpy(hw->name[mode + 1], in_pkt + 4, MD_NAME_LEN * 2); // Load the RGB setting @@ -29,14 +29,14 @@ int modes = (IS_K95(kb) ? HWMODE_K95 : HWMODE_K70); for(int i = 0; i <= modes; i++){ data_pkt[0][3] = i; - if(!usbrecv(kb, data_pkt[0], in_pkt)){ + if(!usbrecv(kb, data_pkt[0], MSG_SIZE, in_pkt)){ free(hw); return -1; } memcpy(hw->id + i, in_pkt + 4, sizeof(usbid)); } // Ask for profile name - if(!usbrecv(kb, data_pkt[1], in_pkt)){ + if(!usbrecv(kb, data_pkt[1], MSG_SIZE, in_pkt)){ free(hw); return -1; } @@ -79,14 +79,14 @@ for(int i = 0; i <= modes; i++){ data_pkt[0][3] = i; memcpy(data_pkt[0] + 4, hw->name[i], MD_NAME_LEN * 2); - if(!usbsend(kb, data_pkt[0], 1)) + if(!usbsend(kb, data_pkt[0], MSG_SIZE, 1)) return -1; } // Save the IDs for(int i = 0; i <= modes; i++){ data_pkt[1][3] = i; memcpy(data_pkt[1] + 4, hw->id + i, sizeof(usbid)); - if(!usbsend(kb, data_pkt[1], 1)) + if(!usbsend(kb, data_pkt[1], MSG_SIZE, 1)) return -1; } // Save the RGB data diff -Nru ckb-next-0.4.3-28/src/daemon/profile_mouse.c ckb-next-0.4.4-8/src/daemon/profile_mouse.c --- ckb-next-0.4.3-28/src/daemon/profile_mouse.c 2019-11-06 00:55:23.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/profile_mouse.c 2021-06-20 12:34:50.000000000 +0000 @@ -18,7 +18,7 @@ uchar in_pkt[MSG_SIZE]; for(int i = 0; i <= 1; i++){ data_pkt[0][3] = i; - if(!usbrecv(kb, data_pkt[0], in_pkt)){ + if(!usbrecv(kb, data_pkt[0], MSG_SIZE, in_pkt)){ free(hw); return -1; } @@ -27,7 +27,7 @@ // Ask for profile and mode names for(int i = 0; i <= 1; i++){ data_pkt[1][3] = i; - if(!usbrecv(kb, data_pkt[1],in_pkt)){ + if(!usbrecv(kb, data_pkt[1], MSG_SIZE, in_pkt)){ free(hw); return -1; } @@ -70,14 +70,14 @@ for(int i = 0; i <= 1; i++){ data_pkt[0][3] = i; memcpy(data_pkt[0] + 4, hw->name[i], MD_NAME_LEN * 2); - if(!usbsend(kb, data_pkt[0], 1)) + if(!usbsend(kb, data_pkt[0], MSG_SIZE, 1)) return -1; } // Save the IDs for(int i = 0; i <= 1; i++){ data_pkt[1][3] = i; memcpy(data_pkt[1] + 4, hw->id + i, sizeof(usbid)); - if(!usbsend(kb, data_pkt[1], 1)) + if(!usbsend(kb, data_pkt[1], MSG_SIZE, 1)) return -1; } // Save the RGB data for the non-DPI zones diff -Nru ckb-next-0.4.3-28/src/daemon/structures.h ckb-next-0.4.4-8/src/daemon/structures.h --- ckb-next-0.4.3-28/src/daemon/structures.h 2021-03-18 11:40:00.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/structures.h 2021-06-20 12:34:50.000000000 +0000 @@ -202,7 +202,9 @@ #define KB_NAME_LEN 50 #define SERIAL_LEN 34 #define MSG_SIZE 64 +#define MAX_MSG_SIZE 1024 #define IFACE_MAX 4 +#define USB_EP_MAX 16 typedef struct { // Function table (see command.h) const union devcmd* vtable; @@ -298,6 +300,24 @@ key* keymap; // Buffer used to store non-HID interrupt reads from the input thread. uchar* interruptbuf; + // Endpoints the main input thread should listen to + // Must always end with 0, and endpoints should be 0x80 | i + uchar input_endpoints[USB_EP_MAX+1]; } usbdevice; +#ifdef OS_LINUX +typedef struct usbdevfs_ctrltransfer ctrltransfer; +#else +// Used to request an explicit URB Control transfer for backends that do controls only, or require values that change +typedef struct { + uint8_t bRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + uint32_t timeout; + void* data; +} ctrltransfer; +#endif + #endif // STRUCTURES_H diff -Nru ckb-next-0.4.3-28/src/daemon/usb.c ckb-next-0.4.4-8/src/daemon/usb.c --- ckb-next-0.4.3-28/src/daemon/usb.c 2021-02-16 02:53:42.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/usb.c 2021-06-20 12:34:50.000000000 +0000 @@ -108,6 +108,9 @@ /// \n Have a look at \a main() in main.c for details. int features_mask = -1; +// Enable experimental support for not fully supported devices +int enable_experimental = 0; + /// brief . /// /// vendor_str returns "corsair" if the given \a vendor argument is equal to \a V_CORSAIR \c (0x1bc) @@ -357,7 +360,7 @@ kb->usbdelay = USB_DELAY_DEFAULT; /// Allocate memory for the os_usbrecv() buffer - kb->interruptbuf = malloc(MSG_SIZE * sizeof(uchar)); + kb->interruptbuf = calloc(MAX_MSG_SIZE, sizeof(uchar)); if(!kb->interruptbuf) ckb_fatal("Error allocating memory for usb_recv() %s", strerror(errno)); @@ -389,6 +392,9 @@ if(!kb->name[0]) snprintf(kb->name, KB_NAME_LEN, "%s %s", vendor_str(kb->vendor), product_str(kb->product)); + // Ask the protocol handler to set the endpoints required for the main input thread + kb->vtable->fill_input_eps(kb); + // Set up an input device for key events /// /// - Then the user level input subsystem is activated via os_openinput(). @@ -551,6 +557,8 @@ int revertusb(usbdevice* kb){ if(NEEDS_FW_UPDATE(kb)) return 0; + + // FIXME: This should be moved to a device-specific function if(!HAS_FEATURES(kb, FEAT_RGB)){ nk95cmd(kb, NK95_HWON); return 0; @@ -628,59 +636,15 @@ /// extern int hwload_mode; -/// \brief . -/// -/// \todo A lot of different conditions are combined in this code. Don't think, it is good in every combination... -/// -/// The main task of _usbsend () is to transfer the complete logical message from the buffer beginning with \a messages to count * MSG_SIZE. -/// \n According to usb 2.0 specification, a USB transmits a maximum of 64 byte user data packets. -/// For the transmission of longer messages we need a segmentation. -/// And that is exactly what happens here. -/// -/// The message is given one by one to os_usbsend() in MSG_SIZE (= 64) byte large bites. -/// \attention This means that the buffer given as argument must be n * MSG_SIZE Byte long. -/// -/// An essential constant parameter which is relevant for os_usbsend() only is is_recv = 0, which means sending. -/// -/// Now it gets a little complicated again: -/// - If os_usbsend() returns 0, only zero bytes could be sent in one of the packets, -/// or it was an error (-1 from the systemcall), but not a timeout. -/// How many Bytes were sent in total from earlier calls does not seem to matter, -/// _usbsend() returns a total of 0. -/// - Returns os_usbsend() -1, first check if \b reset_stop is set globally -/// or (incomprehensible) hwload_mode is not set to "always". -/// In either case, _usbsend() returns 0, -/// otherwise it is assumed to be a temporary transfer error and it simply retransmits the physical packet after a long delay. -/// - If the return value of os_usbsend() was neither 0 nor -1, -/// it specifies the numer of bytes transferred. -/// \n Here is an information hiding conflict with os_usbsend() (at least in the Linux version): -/// \n If os_usbsend() can not transfer the entire packet, -/// errors are thrown and the number of bytes sent is returned. -/// _usbsend() interprets this as well -/// and remembers the total number of bytes transferred in the local variable \b total_sent. -/// Subsequently, however, transmission is continued with the next complete MSG_SIZE block -/// and not with the first of the possibly missing bytes. -/// \todo Check whether this is the same in the macOS variant. It is not dramatic, but if errors occur, it can certainly irritate the devices completely if they receive incomplete data streams. Do we have errors with the messages "Wrote YY bytes (expected 64)" in the system logs? If not, we do not need to look any further. -/// -/// When the last packet is transferred, -/// _usbsend() returns the effectively counted set of bytes (from \b total_sent). -/// This at least gives the caller the opportunity to check whether something has been lost in the middle. -/// -/// A bit strange is the structure of the program: -/// Handling the \b count MSG_SIZE blocks to be transferred is done -/// in the outer for (...) loop. -/// Repeating the transfer with a treatable error is managed by the inner while(1) loop. -/// \n This must be considered when reading the code; -/// The "break" on successful block transfer leaves the inner while, not the for (...). -/// -int _usbsend(usbdevice* kb, const uchar* messages, int count, const char* file, int line){ +// Wrapper around the vtable write() function for error handling and recovery +int _usbsend(usbdevice* kb, void* messages, size_t msg_len, int count, const char* file, int line){ int total_sent = 0; for(int i = 0; i < count; i++){ // Send each message via the OS function while(1){ DELAY_SHORT(kb); queued_mutex_lock(mmutex(kb)); ///< Synchonization between macro and color information - int res = os_usbsend(kb, messages + i * MSG_SIZE, 0, file, line); + int res = kb->vtable->write(kb, messages + i * msg_len, msg_len, 0, file, line); queued_mutex_unlock(mmutex(kb)); if(res == 0) return 0; @@ -698,76 +662,16 @@ return total_sent; } -int _usbsend_control(usbdevice* kb, uchar* data, ushort len, uchar bRequest, ushort wValue, ushort wIndex, const char* file, int line){ - while(1){ - DELAY_SHORT(kb); - queued_mutex_lock(mmutex(kb)); ///< Synchonization between macro and color information - int res = os_usbsend_control(kb, data, len, bRequest, wValue, wIndex, file, line); - queued_mutex_unlock(mmutex(kb)); +// Wrapper around the vtable write() and read() functions for error handling and recovery +int _usbrecv(usbdevice* kb, void* out_msg, size_t msg_len, uchar* in_msg, const char* file, int line){ + // For now assume that msg_len is for both out_msg and in_msg - if(res != -1) - return res; - - // Stop immediately if the program is shutting down or hardware load is set to tryonce - if(reset_stop || hwload_mode != 2) - return 0; - - // Retry as long as the result is temporary failure - DELAY_LONG(kb); - } -} - -/// \brief . -/// -/// To fully understand this, you need to know about usb: -/// All control is at the usb host (the CPU). -/// If the device wants to communicate something to the host, -/// it must wait for the host to ask. -/// The usb protocol defines the cycles and periods in which actions are to be taken. -/// -/// So in order to receive a data packet from the device, -/// the host must first send a send request. -/// \n This is done by _usbrecv() in the first block -/// by sending the MSG_SIZE large data block from \b out_msg via os_usbsend() as it is a machine depending implementation. -/// The usb target device is as always determined over kb. -/// -/// For os_usbsend() to know that it is a receive request, -/// the \b is_recv parameter is set to true (1). -/// With this, os_usbsend () generates a control package for the hardware, not a data packet. -/// -/// If sending of the control package is not successful, -/// a maximum of 5 times the transmission is repeated (including the first attempt). -/// If a non-cancelable error is signaled or the drive is stopped via reset_stop, -/// _usbrecv() immediately returns 0. -/// -/// After this, the function waits for the requested response from the device using os_usbrecv (). -/// -/// os_usbrecv() returns 0, -1 or something else. -/// \n Zero signals a serious error which is not treatable and _usbrecv() also returns 0. -/// \n -1 means that it is a treatable error - a timeout for example - -/// and therefore the next transfer attempt is started after a long pause (DELAY_LONG) -/// if not reset_stop or the wrong hwload_mode require a termination with a return value of 0. -/// -/// After 5 attempts, _usbrecv () returns and returns 0 as well as an error message. -/// -/// When data is received, the number of received bytes is returned. -/// This should always be MSG_SIZE, -/// but os_usbrecv() can also return less. -/// It should not be more, -/// because then there would be an unhandled buffer overflow, -/// but it could be less. -/// This would be signaled in os_usbrecv () with a message. -/// -/// The buffers behind \b out_msg and \b in_msg are MSG_SIZE at least (currently 64 Bytes). -/// More is ok but useless, less brings unpredictable behavior. -/// -int _usbrecv(usbdevice* kb, const uchar* out_msg, uchar* in_msg, const char* file, int line){ // Try a maximum of 5 times for (int try = 0; try < 5; try++) { // Send the output message queued_mutex_lock(mmutex(kb)); ///< Synchonization between macro and color information DELAY_SHORT(kb); - int res = os_usbsend(kb, out_msg, 1, file, line); + int res = kb->vtable->write(kb, out_msg, msg_len, 1, file, line); queued_mutex_unlock(mmutex(kb)); if (res == 0) return 0; @@ -781,7 +685,7 @@ // Wait for the response if(!(kb->fwversion >= 0x120 || IS_V2_OVERRIDE(kb))) DELAY_MEDIUM(kb); - res = os_usbrecv(kb, in_msg, file, line); + res = kb->vtable->read(kb, in_msg, msg_len, 0, file, line); if(res == 0) return 0; else if(res != -1) diff -Nru ckb-next-0.4.3-28/src/daemon/usb.h ckb-next-0.4.4-8/src/daemon/usb.h --- ckb-next-0.4.3-28/src/daemon/usb.h 2020-12-18 05:15:15.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/usb.h 2021-06-20 12:34:50.000000000 +0000 @@ -146,7 +146,7 @@ // #define DEBUG_USB_RECV /// -/// Uncomment to see USB packets received from the device throug the input thread +/// Uncomment to see USB packets received from the device through the input thread // #define DEBUG_USB_INPUT /// @@ -192,6 +192,13 @@ /// Used to apply quirks and features to the PLATINUM devices. #define IS_PLATINUM(kb) ((kb)->vendor == V_CORSAIR && ((kb)->product == P_K95_PLATINUM)) +// Mousepad test +#define IS_MOUSEPAD(vendor, product) ((vendor) == (V_CORSAIR) && (product) == (P_POLARIS)) +#define IS_MOUSEPAD_DEV(kb) IS_MOUSEPAD((kb)->vendor, (kb)->product) + +// Devices that are considered experimental and are either not fully tested, or aren't fully implemented +#define IS_EXPERIMENTAL(vendor, product) ((vendor) == V_CORSAIR && (0)) + /// Some devices cause usbhid to spend a long time initialising it. To work around this, we intentionally uncleanly /// deinitialise the device, skipping the usbhid handover. #define NEEDS_UNCLEAN_EXIT(kb) ((kb)->product == P_K65_RFIRE || (kb)->product == P_K70_RFIRE || (kb)->product == P_K70_RFIRE_NRGB || (kb)->product == P_K95) @@ -230,12 +237,6 @@ /// This constant is used to initialize \b kb->usbdelay. /// It is used in many places (see macros above) but often also overwritten to the fixed value of 10. -/// Pure Hacker code. - -// Mousepad test -#define IS_MOUSEPAD(vendor, product) ((vendor) == (V_CORSAIR) && (product) == (P_POLARIS)) -#define IS_MOUSEPAD_DEV(kb) IS_MOUSEPAD((kb)->vendor, (kb)->product) - #define USB_DELAY_DEFAULT 5 /// Start the USB main loop. Returns program exit code when finished @@ -309,13 +310,13 @@ /// \param[IN] line for debugging /// \param[in] reset_stop global variable is read /// \return number of Bytes sent (ideal == count * MSG_SIZE);\n 0 if a block could not be sent and it was not a timeout OR \b reset_stop was required or \b hwload_mode is not set to "always" -int _usbsend(usbdevice* kb, const uchar* messages, int count, const char* file, int line); +int _usbsend(usbdevice* kb, void* messages, size_t msg_len, int count, const char* file, int line); /// \brief usbsend macro is used to wrap _usbsend() with debugging information (file and lineno) /// \param kb THE usbdevice* /// \param[IN] messages a Pointer to the first byte of the logical message /// \param[IN] count how many MSG_SIZE buffers is the logical message long? -#define usbsend(kb, messages, count) _usbsend(kb, messages, count, __FILE_NOPATH__, __LINE__) +#define usbsend(kb, messages, msg_len, count) _usbsend(kb, messages, msg_len, count, __FILE_NOPATH__, __LINE__) /// /// \brief _usbrecv Request data from a USB device by first sending an output packet and then reading the response. @@ -326,53 +327,19 @@ /// \param[IN] line for debugging /// \param[IN] reset_stop global variable is read /// \return number of bytes read or zero on failure. -int _usbrecv(usbdevice* kb, const uchar* out_msg, uchar* in_msg, const char* file, int line); +int _usbrecv(usbdevice* kb, void* out_msg, size_t msg_len, uchar* in_msg, const char* file, int line); /// \brief usbrecv macro is used to wrap _usbrecv() with debugging information (file and lineno) /// \param kb THE usbdevice* /// \param[IN] out_msg What information does the caller want from the device? /// \param[OUT] in_msg Here comes the answer; The names represent the usb view, not the view of this function! So INput from usb is OUTput of this function. -#define usbrecv(kb, out_msg, in_msg) _usbrecv(kb, out_msg, in_msg, __FILE_NOPATH__, __LINE__) - -/// \details -/// \brief os_usbsend sends a data packet (MSG_SIZE = 64) Bytes long -/// \param kb THE usbdevice* -/// \param out_msg the MSGSIZE char long buffer to send -/// \param is_recv if true, just send an ioctl for further reading packets. If false, send the data at \b out_msg. -/// \param file for debugging -/// \param line for debugging -/// \return -1 on timeout (try again), 0 on hard error, numer of bytes sent otherwise -int os_usbsend(usbdevice* kb, const uchar* out_msg, int is_recv, const char* file, int line); - -/// -/// \brief os_usbrecv receives a max MSGSIZE long buffer from usb device -/// \param kb THE usbdevice* -/// \param in_msg the buffer to fill with the message received -/// \param file for debugging -/// \param line for debugging -/// \return -1 on timeout, 0 on hard error, numer of bytes received otherwise -int os_usbrecv(usbdevice* kb, uchar* in_msg, const char* file, int line); +#define usbrecv(kb, out_msg, msg_len, in_msg) _usbrecv(kb, out_msg, msg_len, in_msg, __FILE_NOPATH__, __LINE__) -/// -/// \brief os_sendindicators update the indicators for the special keys (Numlock, Capslock and what else?) -/// \param kb THE usbdevice* -void os_sendindicators(usbdevice* kb); - -/// -/// \brief _nk95cmd If we control a non RGB keyboard, set the keyboard via ioctl with usbdevfs_ctrltransfer -/// \param kb THE usbdevice* -/// \param bRequest the byte array with the usb request -/// \param wValue a usb wValue -/// \param file for error message -/// \param line for error message -/// \return 1 (true) on failure, 0 (false) on success. -int _nk95cmd(usbdevice* kb, uchar bRequest, ushort wValue, const char* file, int line); - -/// \brief nk95cmd() macro is used to wrap _nk95cmd() with debugging information (file and lineno). +/// \brief nk95cmd() macro is used to send legacy keyboard commands /// the command structure is different: /// \n Just the bits 23..16 are used as bits 7..0 for bRequest /// \n Bits 15..0 are used as wValue -#define nk95cmd(kb, command) _nk95cmd(kb, (command) >> 16 & 0xFF, (command) & 0xFFFF, __FILE_NOPATH__, __LINE__) +#define nk95cmd(kb, command) kb->vtable->write(kb, &(ctrltransfer) { .bRequestType = 0x40, .bRequest = (command) >> 16 & 0xFF, .wValue = (command) & 0xFFFF, .wIndex = 0, .wLength = 0, .timeout = 5000, .data = NULL}, 0, 0, __FILE_NOPATH__, __LINE__) /// Hardware-specific commands for the K95 nonRGB, /// \see [usb2.0 documentation for details](http://www.usb.org/developers/docs/usb_20.zip). @@ -400,9 +367,7 @@ void print_urb_buffer(const char* prefix, const unsigned char* buffer, int actual_length, const char* file, int line, const char* function, int devnum); -int _usbsend_control(usbdevice* kb, uchar* data, ushort len, uchar bRequest, ushort wValue, ushort wIndex, const char* file, int line); -#define usbsend_control(kb, message, len, bRequest, wValue, wIndex) _usbsend_control(kb, message, len, bRequest, wValue, wIndex, __FILE_NOPATH__, __LINE__) -int os_usbsend_control(usbdevice* kb, uchar* data, ushort len, uchar bRequest, ushort wValue, ushort wIndex, const char* file, int line); +int os_usb_control(usbdevice* kb, ctrltransfer* transfer, const char* file, int line); // receive message from initial sighandler socketpair communication extern int sighandler_pipe[2]; @@ -417,4 +382,6 @@ extern dpi_list mouse_dpi_list[]; +int os_usb_interrupt_out(usbdevice* kb, unsigned int ep, unsigned int len, uchar* data, const char* file, int line); + #endif // USB_H diff -Nru ckb-next-0.4.3-28/src/daemon/usb_legacy.c ckb-next-0.4.4-8/src/daemon/usb_legacy.c --- ckb-next-0.4.3-28/src/daemon/usb_legacy.c 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/usb_legacy.c 2021-06-20 12:34:50.000000000 +0000 @@ -0,0 +1,20 @@ +#include "structures.h" +#include "usb.h" + +void legacy_fill_input_eps(usbdevice* kb) +{ + for(int i = 0; i < kb->epcount; i++) + kb->input_endpoints[i] = (i + 1) | 0x80; +} + +int legacy_dev_io(usbdevice* kb, void* out, int len, int is_recv, const char* file, int line) +{ + ctrltransfer* transfer = out; + int res = os_usb_control(kb, transfer, file, line); + + // This is needed because we send controls without any data, and usbsend/usbrecv assume 0 is failure + if(res == 0 && transfer->wLength == 0) + return 1; + + return res; +} diff -Nru ckb-next-0.4.3-28/src/daemon/usb_legacy.h ckb-next-0.4.4-8/src/daemon/usb_legacy.h --- ckb-next-0.4.3-28/src/daemon/usb_legacy.h 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/usb_legacy.h 2021-06-20 12:34:50.000000000 +0000 @@ -0,0 +1,3 @@ +#include "structures.h" +void legacy_fill_input_eps(usbdevice* kb); +int legacy_dev_io(usbdevice* kb, void* out, int len, int is_recv, const char* file, int line); diff -Nru ckb-next-0.4.3-28/src/daemon/usb_linux.c ckb-next-0.4.4-8/src/daemon/usb_linux.c --- ckb-next-0.4.3-28/src/daemon/usb_linux.c 2021-03-18 11:40:00.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/usb_linux.c 2021-06-20 12:34:50.000000000 +0000 @@ -9,22 +9,22 @@ // usb.c extern _Atomic int reset_stop; +extern int enable_experimental; /// \details /// \brief all open usb devices have their system path names here in this array. - static char kbsyspath[DEV_MAX][FILENAME_MAX]; -int os_usbsend_control(usbdevice* kb, uchar* data, ushort len, uchar bRequest, ushort wValue, ushort wIndex, const char* file, int line) { +// USB IO functions +int os_usb_control(usbdevice* kb, ctrltransfer* transfer, const char* file, int line) { #ifdef DEBUG_USB_SEND - int ckb = INDEX_OF(kb, keyboard); - ckb_info("ckb%d Control (%s:%d): bmRequestType: 0x%02hhx, bRequest: %hhu, wValue: 0x%04hx, wIndex: %04hx, wLength: %hu", ckb, file, line, 0x40, bRequest, wValue, wIndex, len); - if(len) - print_urb_buffer("Control buffer:", data, len, file, line, __func__, ckb); + const int ckb = INDEX_OF(kb, keyboard); + ckb_info("ckb%d Control (%s:%d): bmRequestType: 0x%02hhx, bRequest: %hhu, wValue: 0x%04hx, wIndex: %04hx, wLength: %hu", ckb, file, line, transfer->bRequestType, transfer->bRequest, transfer->wValue, transfer->wIndex, transfer->wLength); + if(transfer->wLength) + print_urb_buffer("Control buffer:", transfer->data, transfer->wLength, file, line, __func__, ckb); #endif - struct usbdevfs_ctrltransfer transfer = { 0x40, bRequest, wValue, wIndex, len, 5000, data }; - int res = ioctl(kb->handle - 1, USBDEVFS_CONTROL, &transfer); + const int res = ioctl(kb->handle - 1, USBDEVFS_CONTROL, transfer); if (res == -1){ int ioctlerrno = errno; ckb_err_fn(" %s, res = 0x%x", file, line, strerror(ioctlerrno), res); @@ -33,256 +33,33 @@ else return 0; - } else if (res != len) + } else if (res != transfer->wLength) ckb_warn_fn("Wrote %d bytes (expected %d)", file, line, res, MSG_SIZE); return res; } -//// -/// \brief os_usbsend sends a data packet (MSG_SIZE = 64) Bytes long -/// -/// os_usbsend has two functions: -/// - if is_recv == false, it tries to send a given MSG_SIZE buffer via the usb interface given with kb. -/// - otherwise a request is sent via the usb device to initiate the receiving of a message from the remote device. -/// -/// The functionality for sending distinguishes two cases, -/// depending on the version number of the firmware of the connected device: -/// \n If the firmware is less or equal 1.2, the transmission is done via an ioctl(). -/// The ioctl() is given a struct usbdevfs_ctrltransfer, in which the relevant parameters are entered: -/// -/// bRequestType | bRequest | wValue | EP | size | Timeout | data -/// ------------ | -------- | ------ | -- | ---- | ------- | ---- -/// 0x21 | 0x09 | 0x0200 | endpoint / IF to be addressed from epcount-1 | MSG_SIZE | 5000 (=5ms) | the message buffer pointer -/// Host to Device, Type=Class, Recipient=Interface | 9 = Send data? | specific | last or pre-last device # | 64 | 5000 | out_msg -/// -/// \n The ioctl command is USBDEVFS_CONTROL. -/// -/// The same constellation is used if the device is requested to send its data (is_recv = true). -/// -/// For a more recent firmware and is_recv = false, -/// the ioctl command USBDEVFS_CONTROL is not used -/// (this tells the bus to enter the control mode), -/// but the bulk method is used: USBDEVFS_BULK. -/// This is astonishing, because all of the endpoints are type Interrupt, not bulk. -/// -/// Anyhow, forthis purpose a different structure is used for the ioctl() (struct \b usbdevfs_bulktransfer) -/// and this is also initialized differently: -/// \n The length and timeout parameters are given the same values as above. -/// The formal parameter out_msg is also passed as a buffer pointer. -/// For the endpoints, the firmware version is differentiated again: -/// \n For a firmware version between 1.3 and <2.0 endpoint 4 is used, -/// otherwise (it can only be >=2.0) endpoint 3 is used. -/// -/// \todo Since the handling of endpoints has already led to problems elsewhere, this implementation is extremely hardware-dependent and critical! -/// \n Eg. the new keyboard K95PLATINUMRGB has a version number significantly less than 2.0 - will it run with this implementation? -/// -/// The ioctl() - no matter what type - -/// returns the number of bytes sent. -/// Now comes the usual check: -/// - If the return value is -1 AND the error is a timeout (ETIMEOUT), -/// os_usbsend() will return -1 to indicate that it is probably a recoverable problem and a retry is recommended. -/// - For another negative value or other error identifier OR 0 bytes sent, 0 is returned as a heavy error identifier. -/// - In all other cases, the function returns the number of bytes sent. -/// -/// If this is not the entire blocksize (MSG_SIZE bytes), -/// an error message is issued on the standard error channel -/// [warning "Wrote YY bytes (expected 64)"]. -/// -/// If DEBUG_USB_SEND is set during compilation, -/// the number of bytes sent and their representation are logged to the error channel. -/// -int os_usbsend(usbdevice* kb, const uchar* out_msg, int is_recv, const char* file, int line) { - int res; - if (kb->fwversion >= 0x120 || IS_V2_OVERRIDE(kb)){ - // If we need to read a response, lock the interrupt mutex - if(is_recv) - if(pthread_mutex_lock(intmutex(kb))) - ckb_fatal("Error locking interrupt mutex in os_usbsend()"); - - struct usbdevfs_bulktransfer transfer = {0}; - // All firmware versions for normal HID devices have the OUT endpoint at the end - // Devices with no input, such as the Polaris, have it at the start. - transfer.ep = (IS_SINGLE_EP(kb) ? 1 : kb->epcount); - transfer.len = MSG_SIZE; - transfer.timeout = 5000; - transfer.data = (void*)out_msg; - res = ioctl(kb->handle - 1, USBDEVFS_BULK, &transfer); - } else { - // Note, Ctrl Transfers require an index, not an endpoint, which is why kb->epcount - 1 works - struct usbdevfs_ctrltransfer transfer = { 0x21, 0x09, 0x0200, kb->epcount - 1, MSG_SIZE, 5000, (void*)out_msg }; - res = ioctl(kb->handle - 1, USBDEVFS_CONTROL, &transfer); - } - +int os_usb_interrupt_out(usbdevice* kb, unsigned int ep, unsigned int len, uchar* data, const char* file, int line) +{ +#ifdef DEBUG_USB_SEND + print_urb_buffer("Sending:", data, MSG_SIZE, file, line, __func__, INDEX_OF(kb, keyboard)); +#endif + const struct usbdevfs_bulktransfer transfer = { .ep = ep, .len = len, .timeout = 5000, .data = data, }; + int res = ioctl(kb->handle - 1, USBDEVFS_BULK, &transfer); if (res <= 0){ int ioctlerrno = errno; ckb_err_fn("%s, res = 0x%x", file, line, res ? strerror(ioctlerrno) : "No data written", res); - if(res == -1 && ioctlerrno == ETIMEDOUT){ - if(is_recv) - pthread_mutex_unlock(intmutex(kb)); + if(res == -1 && ioctlerrno == ETIMEDOUT) return -1; - } else { - if(is_recv) - pthread_mutex_unlock(intmutex(kb)); + else return 0; - } - } else if (res != MSG_SIZE) - ckb_warn_fn("Wrote %d bytes (expected %d)", file, line, res, MSG_SIZE); -#ifdef DEBUG_USB_SEND - print_urb_buffer("Sent:", out_msg, MSG_SIZE, file, line, __func__, INDEX_OF(kb, keyboard)); -#endif - - return res; -} - -/// -/// \brief os_usbrecv receives a max MSGSIZE long buffer from usb device - -/// os_usbrecv does what its name says: -/// -/// The comment at the beginning of the procedure -/// causes the suspicion that the firmware versionspecific distinction -/// is missing for receiving from usb endpoint 3 or 4. -/// The commented code contains only the reception from EP4, -/// but this may be wrong for a software version 2.0 or higher (see the code for os-usbsend ()). -/// -/// \n So all the receiving is done via an ioctl() like in os_usbsend. -/// The ioctl() is given a struct usbdevfs_ctrltransfer, in which the relevant parameters are entered: -/// -/// bRequestType | bRequest | wValue | EP | size | Timeout | data -/// ------------ | -------- | ------ | -- | ---- | ------- | ---- -/// 0xA1 | 0x01 | 0x0200 | endpoint to be addressed from epcount - 1 | MSG_SIZE | 5ms | the message buffer pointer -/// Device to Host, Type=Class, Recipient=Interface | 1 = RECEIVE? | specific | Interface # | 64 | 5000 | in_msg -/// -/// The ioctl() returns the number of bytes received. -/// Here is the usual check again: -/// - If the return value is -1 AND the error is a timeout (ETIMEOUT), -/// os_usbrecv() will return -1 to indicate that it is probably a recoverable problem and a retry is recommended. -/// - For another negative value or other error identifier OR 0 bytes are received, 0 is returned as an identifier for a heavy error. -/// - In all other cases, the function returns the number of bytes received. -/// -/// If this is not the entire blocksize (MSG_SIZE bytes), -/// an error message is issued on the standard error channel -/// [warning "Read YY bytes (expected 64)"]. -/// -int os_usbrecv(usbdevice* kb, uchar* in_msg, const char* file, int line){ - int res; - if(kb->fwversion >= 0x120 || IS_V2_OVERRIDE(kb)){ - // Wait for 2s - int condret = cond_nanosleep(intcond(kb), intmutex(kb), 2000000000); - if(condret != 0){ - if(pthread_mutex_unlock(intmutex(kb))) - ckb_fatal("Error unlocking interrupt mutex in os_usbrecv()"); - if(condret == ETIMEDOUT) - ckb_warn_fn("ckb%d: Timeout while waiting for response", file, line, INDEX_OF(kb, keyboard)); - else - ckb_warn_fn("Interrupt cond error %i", file, line, condret); - return -1; - } - memcpy(in_msg, kb->interruptbuf, MSG_SIZE); - memset(kb->interruptbuf, 0, MSG_SIZE); - res = MSG_SIZE; - if(pthread_mutex_unlock(intmutex(kb))) - ckb_fatal("Error unlocking interrupt mutex in os_usbrecv()"); - } else { - struct usbdevfs_ctrltransfer transfer = { 0xa1, 0x01, 0x0300, kb->epcount - 1, MSG_SIZE, 5000, in_msg }; - res = ioctl(kb->handle - 1, USBDEVFS_CONTROL, &transfer); - if(res <= 0){ - // This is done because ckb_err_fn can set errno itself - int ioctlerrno = errno; - ckb_err_fn("%s", file, line, res ? strerror(ioctlerrno) : "No data read"); - if(res == -1 && ioctlerrno == ETIMEDOUT) - return -1; - else - return 0; - } else if(res != MSG_SIZE) - ckb_warn_fn("Read %d bytes (expected %d)", file, line, res, MSG_SIZE); + } else if ((unsigned int)res != len) { + ckb_warn_fn("Wrote %d bytes (expected %d)", file, line, res, len); } - -#ifdef DEBUG_USB_RECV - print_urb_buffer("Recv:", in_msg, MSG_SIZE, file, line, __func__, INDEX_OF(kb, keyboard)); -#endif - return res; } /// -/// \brief _nk95cmd If we control a non RGB keyboard, set the keyboard via ioctl with usbdevfs_ctrltransfer -/// -/// To send control packets to a non RGB non color K95 Keyboard, -/// use this function. Normally it is called via the nk95cmd() macro. -/// -/// If it is the wrong device for which the function is called, 0 is returned and nothing done. -/// Otherwise a usbdevfs_ctrltransfer structure is filled and an USBDEVFS_CONTROL ioctl() called. -/// -/// bRequestType | bRequest | wValue | EP | size | Timeout | data -/// ------------ | -------- | ------ | -- | ---- | ------- | ---- -/// 0x40 | see table below to switch hardware-modus at Keyboard | wValue | device | MSG_SIZE | 5ms | the message buffer pointer -/// Host to Device, Type=Vendor, Recipient=Device | bRequest parameter | given wValue Parameter | device 0 | 0 data to write | 5000 | null -/// -/// If a 0 or a negative error number is returned by the ioctl, an error message is shown depending on the errno or "No data written" if retval was 0. -/// In either case 1 is returned to indicate the error. -/// If the ioctl returned a value > 0, 0 is returned to indicate no error. -/// -/// Currently the following combinations for bRequest and wValue are used: -/// Device | what it might to do | constant | bRequest | wValue -/// ------ | ------------------- | -------- | -------- | ------ -/// non RGB Keyboard | set HW-modus on (leave the ckb driver) | HWON | 0x0002 | 0x0030 -/// non RGB Keyboard | set HW-modus off (initialize the ckb driver) | HWOFF | 0x0002 | 0x0001 -/// non RGB Keyboard | set light modus M1 in single-color keyboards | NK95_M1 | 0x0014 | 0x0001 -/// non RGB Keyboard | set light modus M2 in single-color keyboards | NK95_M2 | 0x0014 | 0x0002 -/// non RGB Keyboard | set light modus M3 in single-color keyboards | NK95_M3 | 0x0014 | 0x0003 -/// \see usb.h -/// -int _nk95cmd(usbdevice* kb, uchar bRequest, ushort wValue, const char* file, int line){ - if(kb->product != P_K95_LEGACY) - return 0; - struct usbdevfs_ctrltransfer transfer = { 0x40, bRequest, wValue, 0, 0, 5000, 0 }; - int res = ioctl(kb->handle - 1, USBDEVFS_CONTROL, &transfer); - if(res <= 0){ - ckb_err_fn("%s", file, line, res ? strerror(errno) : "No data written"); - return 1; - } - return 0; -} - -/// \brief . -/// -/// \brief os_sendindicators update the indicators for the special keys (Numlock, Capslock and what else?) -/// -/// Read the data from kb->ileds ans send them via ioctl() to the keyboard. -/// -/// bRequestType | bRequest | wValue | EP | size | Timeout | data -/// ------------ | -------- | ------ | -- | ---- | ------- | ---- -/// 0x21 | 0x09 | 0x0200 | Interface 0 | MSG_SIZE 1 Byte | timeout 0,5ms | the message buffer pointer -/// Host to Device, Type=Class, Recipient=Interface (why not endpoint?) | 9 = SEND? | specific | 0 | 1 | 500 | struct* kb->ileds -/// -/// \n The ioctl command is USBDEVFS_CONTROL. -/// -void os_sendindicators(usbdevice* kb) { - static int countForReset = 0; - void *ileds; - ushort leds; - if(kb->fwversion >= 0x300 || IS_V3_OVERRIDE(kb)) { - leds = (kb->ileds << 8) | 0x0001; - ileds = &leds; - } - else { - ileds = &kb->ileds; - } - struct usbdevfs_ctrltransfer transfer = { 0x21, 0x09, 0x0200, 0x00, ((kb->fwversion >= 0x300 || IS_V3_OVERRIDE(kb)) ? 2 : 1), 500, ileds }; - queued_mutex_unlock(dmutex(kb)); - int res = ioctl(kb->handle - 1, USBDEVFS_CONTROL, &transfer); - queued_mutex_lock(dmutex(kb)); - if(res <= 0) { - ckb_err("%s", res ? strerror(errno) : "No data written"); - if (usb_tryreset(kb) == 0 && countForReset++ < 3) { - os_sendindicators(kb); - } - } -} - -/// /// \brief os_inputmain This function is run in a separate thread and will be detached from the main thread, so it needs to clean up its own resources. /// \todo This function is a collection of many tasks. It should be divided into several sub-functions for the sake of greater convenience: /// @@ -298,23 +75,16 @@ void* os_inputmain(void* context){ usbdevice* kb = context; int fd = kb->handle - 1; - ushort vendor = kb->vendor, product = kb->product; int index = INDEX_OF(kb, keyboard); ckb_info("Starting input thread for %s%d", devpath, index); - /// Here the actions in detail: - /// - /// Monitor input transfers on all endpoints for non-RGB devices - /// For RGB, monitor all but the last, as it's used for input/output - int urbcount = ((IS_LEGACY(vendor, product) || product == P_ST100) ? kb->epcount : (kb->epcount - 1)); - if (urbcount == 0) { - ckb_err("urbcount = 0, so there is nothing to claim in os_inputmain()"); + if (kb->input_endpoints[0] == 0) { + ckb_err("No endpoints claimed in inputmain"); return 0; } /// Get an usbdevfs_urb data structure and clear it via memset() - struct usbdevfs_urb urbs[urbcount]; - memset(urbs, 0, sizeof(urbs)); + struct usbdevfs_urb urbs[USB_EP_MAX] = {0}; /// Query udev for wMaxPacketSize on each endpoint, due to certain devices sending more data than the max defined, causing all sorts of issues. /// A syspath example would be: @@ -334,53 +104,70 @@ // Create a list containing them struct udev_list_entry* udeventry = udev_enumerate_get_list_entry(enumerate); - for(int i = 0; i < urbcount; i++){ - ushort ep = 0x80 | (i + 1); - + int ifcount = 0; + do { // Move to the next entry in the udev list (skipping the first one). struct udev_list_entry* nextentry = udev_list_entry_get_next(udeventry); const char* path = udev_list_entry_get_name(nextentry); // If there's an underscore, that means we are dealing with udev iterating through endpoints // usbX/X-X/X-X:1.0/ep_80 // ~~~~~~~~~~~~~~~~~~~^ - if(path[strlen(path) - 3] == '_'){ + // We only want to iterate through the interfaces + size_t pathlen = strlen(path); + if(path[pathlen - 3] == '_'){ ckb_info("Applying udev endpoint workaround for %s", path); // Skip the current entry udeventry = nextentry; nextentry = udev_list_entry_get_next(udeventry); path = udev_list_entry_get_name(nextentry); + pathlen = strlen(path); } // Create the path to the endpoint - char finalpath[strlen(path)+7]; - // Copy the base path - strcpy(finalpath, path); - // Append the endpoint - char epstr[7]; - snprintf(epstr, 7, "/ep_%02x", ep & 0xFF); - strcat(finalpath, epstr); - // Access it - struct udev_device* child = udev_device_new_from_syspath(dev_udev, finalpath); - const char* sizehex = udev_device_get_sysattr_value(child, "wMaxPacketSize"); - // Read its wMaxPacketSize + size_t finalpathlen = pathlen + 7; + char* finalpath = malloc(finalpathlen); + // Try to find any of the wanted endpoints in the current interface + // We'll assume that each interface has at most one IN endpoint ushort size = 64; - if(sizehex) - sscanf(sizehex, "%hx", &size); - else - ckb_warn("Unable to read wMaxPacketSize for %s, assuming 64", epstr); + ushort ep = 0; + for(int i = 0; (ep = kb->input_endpoints[i]); i++){ + // Build the path + snprintf(finalpath, finalpathlen, "%s/ep_%02hhx", path, ep); + // Access it + struct udev_device* child = udev_device_new_from_syspath(dev_udev, finalpath); + const char* sizehex = udev_device_get_sysattr_value(child, "wMaxPacketSize"); + // Read its wMaxPacketSize + if(sizehex && sscanf(sizehex, "%hx", &size) == 1) + { + //#ifdef DEBUG + ckb_info("Found EP 0x%hx at %s", ep, finalpath); + //#endif + udev_device_unref(child); + break; + } + udev_device_unref(child); + } + if(!ep) + { + ckb_warn("Unable to read wMaxPacketSize for %s, ignoring", finalpath); + free(finalpath); + continue; + } + #ifdef DEBUG ckb_info("Endpoint path %s has wMaxPacketSize %i", epstr, size); #endif // Increment the udev list pointer udeventry = nextentry; // Set the URB parameters - urbs[i].buffer_length = size; - urbs[i].type = USBDEVFS_URB_TYPE_INTERRUPT; - urbs[i].endpoint = ep; - urbs[i].buffer = malloc(urbs[i].buffer_length); - ioctl(fd, USBDEVFS_SUBMITURB, urbs + i); + urbs[ifcount].buffer_length = size; + urbs[ifcount].type = USBDEVFS_URB_TYPE_INTERRUPT; + urbs[ifcount].endpoint = ep; + urbs[ifcount].buffer = malloc(size); + ioctl(fd, USBDEVFS_SUBMITURB, urbs + ifcount); // Clean up - udev_device_unref(child); - } + free(finalpath); + ifcount++; + } while (*(kb->input_endpoints + ifcount)); udev_enumerate_unref(enumerate); /// The userSpaceFS knows the URBs now, so start monitoring input @@ -424,7 +211,7 @@ /// If the endless loop is terminated, clean up by discarding the URBs via ioctl(USBDEVFS_DISCARDURB), /// free the URB buffers and return a null pointer as thread exit code. ckb_info("Stopping input thread for %s%d", devpath, index); - for(int i = 0; i < urbcount; i++){ + for(int i = 0; i < ifcount; i++){ ioctl(fd, USBDEVFS_DISCARDURB, urbs + i); free(urbs[i].buffer); } @@ -752,8 +539,11 @@ return 1; for(size_t c = 0; c < N_MODELS; c++){ - if(models[c].idVendor == vid && models[c].idProduct == pid) + if(models[c].idVendor == vid && models[c].idProduct == pid){ + if(IS_EXPERIMENTAL(vid, pid) && !enable_experimental) + continue; return usbadd(dev, models[c].idVendor, models[c].idProduct); + } } return 1; diff -Nru ckb-next-0.4.3-28/src/daemon/usb_nxp.c ckb-next-0.4.4-8/src/daemon/usb_nxp.c --- ckb-next-0.4.3-28/src/daemon/usb_nxp.c 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/usb_nxp.c 2021-06-20 12:34:50.000000000 +0000 @@ -0,0 +1,70 @@ +#include "structures.h" +#include "usb.h" +#include "device.h" + +void nxp_fill_input_eps(usbdevice* kb) +{ + int epcount = (kb->product == P_ST100 ? kb->epcount : (kb->epcount - 1)); + for(int i = 0; i < epcount; i++) + kb->input_endpoints[i] = (i + 1) | 0x80; +} + +int nxp_usb_write(usbdevice* kb, void* out, int len, int is_recv, const char* file, int line) +{ + if(len != 64) + { + ckb_fatal_fn("len != 64 not supported in NXP backend", file, line); + return -1; + } + + if (kb->fwversion >= 0x120 || IS_V2_OVERRIDE(kb)){ + // If we need to read a response, lock the interrupt mutex + if(is_recv) + if(pthread_mutex_lock(intmutex(kb))) + ckb_fatal("Error locking interrupt mutex in os_usbsend()"); + + // All firmware versions for normal HID devices have the OUT endpoint at the end + // Devices with no input, such as the Polaris, have it at the start. + unsigned int ep = (IS_SINGLE_EP(kb) ? 1 : kb->epcount); + int res = os_usb_interrupt_out(kb, ep, len, out, file, line); + // Unlock on failure + if(is_recv && res < 1) + pthread_mutex_unlock(intmutex(kb)); + return res; + } else { + // Note, Ctrl Transfers require an index, not an endpoint, which is why kb->epcount - 1 works + ctrltransfer transfer = { 0x21, 0x09, 0x0200, kb->epcount - 1, len, 5000, out }; + return os_usb_control(kb, &transfer, file, line); + } +} + +int nxp_usb_read(usbdevice* kb, void* in, int len, int dummy, const char* file, int line) +{ + if(len != 64) + { + ckb_fatal_fn("len != 64 not supported in NXP backend", file, line); + return -1; + } + + if(kb->fwversion >= 0x120 || IS_V2_OVERRIDE(kb)){ + // Wait for max 2s + int condret = cond_nanosleep(intcond(kb), intmutex(kb), 2000000000); + if(condret != 0){ + if(pthread_mutex_unlock(intmutex(kb))) + ckb_fatal("Error unlocking interrupt mutex in os_usbrecv()"); + if(condret == ETIMEDOUT) + ckb_warn_fn("ckb%d: Timeout while waiting for response", file, line, INDEX_OF(kb, keyboard)); + else + ckb_warn_fn("Interrupt cond error %i", file, line, condret); + return -1; + } + memcpy(in, kb->interruptbuf, len); + memset(kb->interruptbuf, 0, len); + if(pthread_mutex_unlock(intmutex(kb))) + ckb_fatal("Error unlocking interrupt mutex in os_usbrecv()"); + return len; + } else { + ctrltransfer transfer = { 0xa1, 0x01, 0x0300, kb->epcount - 1, len, 5000, in }; + return os_usb_control(kb, &transfer, file, line); + } +} diff -Nru ckb-next-0.4.3-28/src/daemon/usb_nxp.h ckb-next-0.4.4-8/src/daemon/usb_nxp.h --- ckb-next-0.4.3-28/src/daemon/usb_nxp.h 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.4-8/src/daemon/usb_nxp.h 2021-06-20 12:34:50.000000000 +0000 @@ -0,0 +1,4 @@ +#include "structures.h" +void nxp_fill_input_eps(usbdevice* kb); +int nxp_usb_write(usbdevice* kb, void* out, int len, int is_recv, const char* file, int line); +int nxp_usb_read(usbdevice* kb, void* in, int len, int dummy, const char* file, int line); diff -Nru ckb-next-0.4.3-28/src/gui/resources/translations/el.ts ckb-next-0.4.4-8/src/gui/resources/translations/el.ts --- ckb-next-0.4.3-28/src/gui/resources/translations/el.ts 2021-03-18 11:40:00.000000000 +0000 +++ ckb-next-0.4.4-8/src/gui/resources/translations/el.ts 2021-06-20 12:34:50.000000000 +0000 @@ -1071,7 +1071,7 @@ - + Click to select Κλικ για επιλογή @@ -1115,22 +1115,22 @@ πλήκτρα - + 1 zone selected 1 ζώνη επιλεγμένη - + 1 key selected 1 πλήκτρο επιλεγμένο - + %1 zones selected %1 ζώνες επιλεγμένες - + %1 keys selected %1 πλήκτρα επιλεγμένα @@ -2009,63 +2009,63 @@ Το ckb-next-daemon δεν τρέχει. - + Driver inactive Ο driver είναι αδρανής - + <br /><br /><b>Warning:</b> Driver version mismatch ( <br /><br /><b>Προσοχή:</b> Ασυμφωνία έκδοσης του driver ( - + ). Please upgrade ckb-next ). Παρακαλώ ενημερώστε το ckb-next - + . If the problem persists, try rebooting. .<br />Αν το πρόβλημα παραμένει, επανεκκινήστε το σύστημά σας. - + <br /><b>Warning:</b> System Extension by "Fumihiko Takayama" is not allowed in Security & Privacy. Please allow it and then unplug and replug your devices. <br /><b>Ποσοχή:</b> Η επέκταση από "Fumihiko Takayama" δεν επιτρέπεται στο Ασφάλεια και απόρρητο. Επιτρέψτε το και επανασυνδέστε τις συσκευές. - + <br /><b>Warning:</b> Make sure ckb-next-daemon is allowed in Security & Privacy -> Input monitoring.<br />Please allow for up to 10 seconds for the daemon restart prompt to show up after allowing input monitoring. <br /><b>Προσοχή:</b> Σιγουρευτείτε ότι το ckb-next-daemon επιτρέπεται στο Ασφάλεια και απόρρητο -> Παρακολούθησή εισόδου.</br />Παρακαλώ επιτρέψτε μέχρι 10 δευτερόλεπτα μέχρι να εμφανιστεί η ειδοποίηση ενεργοποιήσης του daemon. - + <br /><b>Warning:</b> The uinput module could not be loaded. If this issue persists after rebooting, compile a kernel with CONFIG_INPUT_UINPUT=y. <br /><b>Προσοχή:</b> Δεν ήταν δυνατό να φορτωθεί το άρθρωμα πυρήνα uinput. Αν αυτο το πρόβλημα παραμένει μετά από επανεκκίνηση, μεταγλωττίστε πυρήνα με CONFIG_INPUT_UINPUT=y. - + No devices connected Δεν υπάρχουν συνδεδεμένες συσκευές - + 1 device connected 1 συσκευή συνδεδεμένη - + %1 devices connected %1 συσκευές συνδεδεμένες - + A new firmware is available for your %1 (v%2) Would you like to install it now? Υπάρχει νέο firmware για το %1 σας (v%2) Θα θέλατε να το εγκαταστήσετε τώρα; - + ckb-next will still run in the background. To close it, choose Quit from the tray menu or click "Quit" on the Settings screen. @@ -2074,12 +2074,12 @@ ή κάντε κλικ στο "Έξοδος" στο παράθυρο ρυθμίσεων. - + Update to v Ενημέρωση στο v - + Up to date Ενημερωμένο @@ -2722,7 +2722,7 @@ - + Generate report Δημιουργία αναφοράς @@ -2733,14 +2733,13 @@ - + Uninstall ckb-next Απεγκατάσταση του ckb-next - © 2014-2016 <a href="https://github.com/ccMSC/" style="text-decoration:none;">ccMSC</a>.<br/>© 2017-2019 <a href="https://github.com/ckb-next/ckb-next/graphs/contributors" style="text-decoration:none;">The ckb-next development team</a>. - © 2014-2016 <a href="https://github.com/ccMSC/" style="text-decoration:none;">ccMSC</a>.<br/>© 2017-2019 <a href="https://github.com/ckb-next/ckb-next/graphs/contributors" style="text-decoration:none;">Η ομάδα ανάπτυξης του ckb-next</a>. + © 2014-2016 <a href="https://github.com/ccMSC/" style="text-decoration:none;">ccMSC</a>.<br/>© 2017-2019 <a href="https://github.com/ckb-next/ckb-next/graphs/contributors" style="text-decoration:none;">Η ομάδα ανάπτυξης του ckb-next</a>. @@ -2748,12 +2747,17 @@ Έξοδος - + + The ckb-next development team + Η ομάδα ανάπτυξης του ckb-next + + + <br/>Special thanks to <a href="https://github.com/tekezo" style="text-decoration:none;">tekezo</a> for <a href="https://github.com/tekezo/Karabiner-VirtualHIDDevice" style="text-decoration:none;">VirtualHIDDevice</a>. <br/>Ιδιαίτερες ευχαριστίες στον <a href="https://github.com/tekezo" style="text-decoration:none;">tekezo</a> για το <a href="https://github.com/tekezo/Karabiner-VirtualHIDDevice" style="text-decoration:none;">VirtualHIDDevice</a>. - + This will collect software logs, as well as information about the Corsair devices in your system. Make sure they are plugged in and click OK. @@ -2762,20 +2766,25 @@ Σιγουρευτείτε ότι είναι συνδεδεμένες και κάντε κλικ στο OK. - - + + Error executing ckb-next-dev-detect Σφάλμα εκτέλεσης του ckb-next-dev-detect - + An error occurred while trying to execute ckb-next-dev-detect. File not found or not executable. Παρουσιάστηκε σφάλμα κατά την εκτέλεση του ckb-next-dev-detect. Το αρχείο δεν βρέθηκε ή δεν είναι εκτελέσιμο. - + + Generating Report + Γίνεται δημιουργία αναφοράς + + + An error occurred while trying to execute ckb-next-dev-detect. @@ -2784,44 +2793,44 @@ - + Return code %1 Κωδικός επιστροφής %1 - + Select output directory Επιλέξτε φακέλου εξόδου - + Report generated successfully Επιτυχής δημιουργία αναφοράς - + The report has been generated successfully. Η αναφορά δημιουργήθηκε με επιτυχία. - + Error writing report Σφάλμα εγγραφής αναφοράς - + Could not write report to the selected directory. Please pick a different one and try again. Δεν ήταν δυνατή η δημιουργία αναφοράς στον επιλεγμένο φάκελο. Παρακαλώ διαλέξτε έναν άλλο φάκελο και δοκιμάστε ξανά. - + Checking... Γίνεται έλεγχος... - + WARNING: Clicking OK will uninstall ckb-next and any older versions of the software from your system. Your settings and lighting profiles will be preserved. @@ -2830,12 +2839,12 @@ Οι ρυθμίσεις και τα προφίλ σας θα διατηρηθούν. - + Please restart ckb-next Παρακαλώ επανεκκινήστε το ckb-next - + Please click the Quit button and restart ckb-next for the changes to take effect. Κάντε κλικ στο κουμπί Έξοδος και επανεκκινήστε το ckb-next για να εφαρμοστούν οι αλλαγές. diff -Nru ckb-next-0.4.3-28/src/gui/resources/translations/en_GB.ts ckb-next-0.4.4-8/src/gui/resources/translations/en_GB.ts --- ckb-next-0.4.3-28/src/gui/resources/translations/en_GB.ts 2020-12-18 05:15:15.000000000 +0000 +++ ckb-next-0.4.4-8/src/gui/resources/translations/en_GB.ts 2021-06-20 12:34:50.000000000 +0000 @@ -1053,7 +1053,7 @@ - + Click to select @@ -1073,22 +1073,22 @@ - + 1 zone selected - + 1 key selected - + %1 zones selected - + %1 keys selected @@ -1946,75 +1946,75 @@ - + Driver inactive - + <br /><br /><b>Warning:</b> Driver version mismatch ( - + ). Please upgrade ckb-next - + . If the problem persists, try rebooting. - + <br /><b>Warning:</b> System Extension by "Fumihiko Takayama" is not allowed in Security & Privacy. Please allow it and then unplug and replug your devices. - + <br /><b>Warning:</b> Make sure ckb-next-daemon is allowed in Security & Privacy -> Input monitoring.<br />Please allow for up to 10 seconds for the daemon restart prompt to show up after allowing input monitoring. - + <br /><b>Warning:</b> The uinput module could not be loaded. If this issue persists after rebooting, compile a kernel with CONFIG_INPUT_UINPUT=y. - + No devices connected - + 1 device connected - + %1 devices connected - + A new firmware is available for your %1 (v%2) Would you like to install it now? - + ckb-next will still run in the background. To close it, choose Quit from the tray menu or click "Quit" on the Settings screen. - + Update to v - + Up to date @@ -2561,7 +2561,7 @@ - + Generate report @@ -2572,16 +2572,11 @@ - + Uninstall ckb-next - - © 2014-2016 <a href="https://github.com/ccMSC/" style="text-decoration:none;">ccMSC</a>.<br/>© 2017-2019 <a href="https://github.com/ckb-next/ckb-next/graphs/contributors" style="text-decoration:none;">The ckb-next development team</a>. - - - About Qt @@ -2592,86 +2587,96 @@ - + + The ckb-next development team + + + + <br/>Special thanks to <a href="https://github.com/tekezo" style="text-decoration:none;">tekezo</a> for <a href="https://github.com/tekezo/Karabiner-VirtualHIDDevice" style="text-decoration:none;">VirtualHIDDevice</a>. - + This will collect software logs, as well as information about the Corsair devices in your system. Make sure they are plugged in and click OK. - - + + Error executing ckb-next-dev-detect - + An error occurred while trying to execute ckb-next-dev-detect. File not found or not executable. - + + Generating Report + + + + An error occurred while trying to execute ckb-next-dev-detect. - + Return code %1 - + Select output directory - + Report generated successfully - + The report has been generated successfully. - + Error writing report - + Could not write report to the selected directory. Please pick a different one and try again. - + Checking... - + WARNING: Clicking OK will uninstall ckb-next and any older versions of the software from your system. Your settings and lighting profiles will be preserved. - + Please restart ckb-next - + Please click the Quit button and restart ckb-next for the changes to take effect. diff -Nru ckb-next-0.4.3-28/src/gui/settingswidget.cpp ckb-next-0.4.4-8/src/gui/settingswidget.cpp --- ckb-next-0.4.3-28/src/gui/settingswidget.cpp 2021-03-18 11:40:00.000000000 +0000 +++ ckb-next-0.4.4-8/src/gui/settingswidget.cpp 2021-06-20 12:34:50.000000000 +0000 @@ -89,14 +89,16 @@ ui->loginItemBox->setChecked(AutoRun::isEnabled()); } + QString copyrightText = QString("© 2014-2016 ccMSC.
© 2017-%1 %2.").arg(CKB_NEXT_COPYRIGHT_YEAR, tr("The ckb-next development team")); + #ifndef OS_MAC_LEGACY #ifdef Q_OS_MACOS - QString labelText = ui->label_2->text(); - labelText.append(tr("
Special thanks to tekezo for VirtualHIDDevice.")); - ui->label_2->setText(labelText); + copyrightText.append(tr("
Special thanks to tekezo for VirtualHIDDevice.")); #endif #endif + ui->label_2->setText(copyrightText); + #ifdef DISABLE_UPDATER ui->autoUpdBox->hide(); ui->pushButton_2->hide(); diff -Nru ckb-next-0.4.3-28/src/gui/settingswidget.ui ckb-next-0.4.4-8/src/gui/settingswidget.ui --- ckb-next-0.4.3-28/src/gui/settingswidget.ui 2019-11-24 13:17:44.000000000 +0000 +++ ckb-next-0.4.4-8/src/gui/settingswidget.ui 2021-06-20 12:34:50.000000000 +0000 @@ -491,7 +491,7 @@ - © 2014-2016 <a href="https://github.com/ccMSC/" style="text-decoration:none;">ccMSC</a>.<br/>© 2017-2019 <a href="https://github.com/ckb-next/ckb-next/graphs/contributors" style="text-decoration:none;">The ckb-next development team</a>. + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter diff -Nru ckb-next-0.4.3-28/VERSION.cmake ckb-next-0.4.4-8/VERSION.cmake --- ckb-next-0.4.3-28/VERSION.cmake 2020-12-18 05:15:15.000000000 +0000 +++ ckb-next-0.4.4-8/VERSION.cmake 2021-06-20 12:34:50.000000000 +0000 @@ -1,7 +1,7 @@ -set(ckb-next_VERSION "0.4.3") +set(ckb-next_VERSION "0.4.4") set(ckb-next_VERSION_MAJOR 0) set(ckb-next_VERSION_MINOR 4) -set(ckb-next_VERSION_PATCH 3) +set(ckb-next_VERSION_PATCH 4) # This should be set to TRUE _only_ in archive/tarball releases set(ckb-next_VERSION_IS_RELEASE FALSE) @@ -10,3 +10,6 @@ # Settings version set(ckb-next_SETTINGS_VER 1) + +# Copyright year string +set(ckb-next_COPYRIGHT_YEAR "2021") Binary files /tmp/tmp8xvz47lf/mizaGJYLDC/ckb-next-0.4.3-28/zMK9jOP.png and /tmp/tmp8xvz47lf/OwZRhE3DvD/ckb-next-0.4.4-8/zMK9jOP.png differ