From 2fa65fd776d3460a1d63a3f32b4f6b5b1d3c124a Mon Sep 17 00:00:00 2001 From: mckset <99543950+mckset@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:19:05 -0400 Subject: [PATCH 01/10] Changed Config Handling - Config files no longer need to maintain the same line count - Functions and file paths no longer need to be a specific length - Added the dry flag to display received data without handling it --- KD100.c | 292 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 177 insertions(+), 115 deletions(-) diff --git a/KD100.c b/KD100.c index 69e83ef..7b33d37 100644 --- a/KD100.c +++ b/KD100.c @@ -1,5 +1,5 @@ /* - V1.3 + V1.4 https://github.com/mckset/KD100.git KD 100 Linux driver for X11 desktops Other devices can be supported by modifying the code to read data received by the device @@ -13,25 +13,35 @@ #include #include -int keycodes[] = {1, 2, 4, 8, 16, 32, 64, 128, 129, 130, 132, 136, 144, 160, 192, 256, 257, 258, 260, 641, 642}; -char file[4096] = "default.cfg"; +typedef struct event event; +typedef struct wheel wheel; +struct event{ + int type; + char* function; +}; -void GetDevice(int, int); +struct wheel { + char* right; + char* left; +}; + +const int keycodes[] = {1, 2, 4, 8, 16, 32, 64, 128, 129, 130, 132, 136, 144, 160, 192, 256, 257, 258, 260, 641, 642}; +char* file = "default.cfg"; + +void GetDevice(int, int, int); void Handler(char*, int); +char* Substring(char*, int, int); const int vid = 0x256c; const int pid = 0x006d; - -void GetDevice(int debug, int accept){ - int i=0, err=0, l=0, subL=0, wheelFunction=0, c=0; - char data[512]; // Data received from the config file and the USB - int type[19]; // Stores the button type - char events[19][256]; // Stores the button event key/function - char wheelEvents[6][256]; // Stores the wheel keys - int prevType = 0; // prevKey function type - char prevKey[256]; // Stores the previous event to release held keys +void GetDevice(int debug, int accept, int dry){ + int err=0, wheelFunction=0, c=0, b=-1, tButtons=0, wheelType=0, lWheels=0, rWheels=0, tWheels=0; + char* data = malloc(sizeof(data)); // Data received from the config file and the USB + event* events = malloc(1*sizeof(*events)); // Stores key events and functions + wheel* wheelEvents = malloc(1*sizeof(*wheelEvents)); // Stores wheel functions + event prevEvent; uid_t uid=getuid(); // Used to check if the driver was ran as root system("clear"); @@ -39,13 +49,14 @@ void GetDevice(int debug, int accept){ if (debug > 0){ if (debug > 2) debug=2; - printf("Version 1.3\nDebug level: %d\n", debug); - } + printf("Version 1.4\nDebug level: %d\n", debug); + } // Load config file if (debug == 1){ printf("Loading config...\n"); } + FILE *f; if (strcmp(file, "default.cfg")){ f = fopen(file, "r"); @@ -67,44 +78,62 @@ void GetDevice(int debug, int accept){ } } while (fscanf(f, "%[^\n] ", data) == 1){ - if (l > 19 && l <= 76){ // Button functions - if (subL == 0){ - type[i] = atoi(&data[5]); - subL = 1; - }else if (subL == 1){ - char func[256]; - if (strlen(data) < 256){ - for (int d = 10; d < sizeof(data); d++){ - func[d-10] = data[d]; - } - }else{ - strcpy(func, "NULL"); - printf("Line: %d - Function length exceeded 256 characters. Ignoring %s\n", l, data); + for (int i = 0; i < strlen(data) && strlen(data)-6 > 0; i++){ + if (strcmp(Substring(data, i, 5), "type:") == 0 && b != -1){ + events[b].type = atoi(Substring(data, i+6, strlen(data)-(i+6))); + break; + }else if (strcmp(Substring(data, i, 6), "Button") == 0){ + b = atoi(Substring(data, i+7, strlen(data)-(i+7))); + if (b >= tButtons){ + event* temp = realloc(events, (b+1)*sizeof(*events)); + events = temp; + tButtons = b+1; } - strcpy(events[i], func); - subL = -1; - i++; - }else{ - subL+=1; - } - }else if (l > 76 && l < 85){ // Wheel functions - if (subL < 3){ - char func[256]; - for (int d = 12; d < sizeof(data); d++){ - func[d-12] = data[d]; + break; + }else if (strcmp(Substring(data, i, 9), "function:") == 0 && b != -1){ + if (!wheelType) + events[b].function = Substring(data, i+10, strlen(data)-(i+10)); + else if (wheelType == 1){ + wheel* temp = realloc(wheelEvents, (rWheels+1)*sizeof(*wheelEvents)); + wheelEvents = temp; + wheelEvents[rWheels].right = Substring(data, i+10, strlen(data)-(i+10)); + wheelEvents[rWheels].left = "NULL"; + rWheels++; + }else{ + if (lWheels < rWheels) + wheelEvents[lWheels].left = Substring(data, i+10, strlen(data)-(i+10)); + else{ + wheel* temp = realloc(wheelEvents, (lWheels+1)*sizeof(*wheelEvents)); + wheelEvents = temp; + wheelEvents[lWheels].left = Substring(data, i+10, strlen(data)-(i+10)); + wheelEvents[lWheels].right = "NULL"; + } + lWheels++; } - strcpy(wheelEvents[wheelFunction], func); - wheelFunction++; - subL++; - }else{ - subL=0; + + + break; + }else if (strcmp(Substring(data, i, 6), "Wheel ") == 0){ + wheelType++; } } - l++; } wheelFunction=0; + if (rWheels > lWheels) + tWheels = rWheels; + else + tWheels = lWheels; + + if (debug > 0){ + for (int i = 0; i < tButtons; i++) + printf("Button: %d | Type: %d | Function: %s\n", i, events[i].type, events[i].function); + printf("\n"); + for (int i = 0; i < tWheels; i++) + printf("Wheel Right: %s | Wheel Left: %s\n", wheelEvents[i].right, wheelEvents[i].left); + printf("\n"); + } - i = 0; + int i = 0; char indi[] = "|/-\\"; while (err == 0 || err == LIBUSB_ERROR_NO_DEVICE){ libusb_device **devs; // List of USB devices @@ -200,7 +229,7 @@ void GetDevice(int debug, int accept){ } } - + i=0; if (handle == NULL){ @@ -240,7 +269,8 @@ void GetDevice(int debug, int accept){ err = 0; // Listen for events printf("Driver is running!\n"); - + prevEvent.function = ""; + prevEvent.type = 0; while (err >=0){ unsigned char data[40]; // Stores device input int keycode = 0; // Keycode read from the device @@ -275,69 +305,70 @@ void GetDevice(int debug, int accept){ keycode = data[6] + 256; if (data[1] == 241) keycode+=512; + if (dry) + keycode = 0; // Compare keycodes to data and trigger events if (debug == 1 && keycode != 0){ printf("Keycode: %d\n", keycode); } - if (keycode == 0 && prevType != 0){ // Reset key held - Handler(prevKey, prevType); - strcpy(prevKey, ""); - prevType=0; + if (keycode == 0 && prevEvent.type != 0){ // Reset key held + Handler(prevEvent.function, prevEvent.type); + prevEvent.function = ""; + prevEvent.type = 0; } - if (keycode == 641){ // Wheel Clockwise - Handler(wheelEvents[wheelFunction], -1); - }else if (keycode == 642){ - Handler(wheelEvents[wheelFunction + 3], -1); + if (keycode == 641){ // Wheel clockwise + Handler(wheelEvents[wheelFunction].right, -1); + }else if (keycode == 642){ // Counter clockwise + Handler(wheelEvents[wheelFunction].left, -1); }else{ for (int k = 0; k < 19; k++){ if (keycodes[k] == keycode){ - if (strcmp(events[k], "NULL") == 0){ - if (prevType != 0){ - Handler(prevKey, prevType); - prevType = 0; - strcpy(prevKey, ""); - } - break; - } - if (type[k] == 0){ - if (strcmp(events[k], prevKey)){ - if (prevType != 0){ - Handler(prevKey, prevType); + if (events[k].function){ + if (strcmp(events[k].function, "NULL") == 0){ + if (prevEvent.type != 0){ + Handler(prevEvent.function, prevEvent.type); + prevEvent.type = 0; + prevEvent.function = ""; } - strcpy(prevKey, events[k]); - prevType=1; + break; } - Handler(events[k], 0); - }else if (strcmp(events[k], "swap") == 0){ - if (wheelFunction != 2){ - wheelFunction++; - if (strcmp(wheelEvents[wheelFunction], "NULL") == 0){ - wheelFunction = 0; + if (events[k].type == 0){ + if (strcmp(events[k].function, prevEvent.function)){ + if (prevEvent.type != 0){ + Handler(prevEvent.function, prevEvent.type); + } + prevEvent.function = events[k].function; + prevEvent.type=1; } + Handler(events[k].function, 0); + }else if (strcmp(events[k].function, "swap") == 0){ + if (wheelFunction != tWheels-1){ + wheelFunction++; + }else + wheelFunction=0; if (debug == 1){ - printf("Function: %s\n", wheelEvents[wheelFunction]); + printf("Function: %s | %s\n", wheelEvents[wheelFunction].left, wheelEvents[wheelFunction].right); } - }else - wheelFunction=0; - }else if (strcmp(events[k], "mouse1") == 0 || strcmp(events[k], "mouse2") == 0 || strcmp(events[k], "mouse3") == 0 || strcmp(events[k], "mouse4") == 0 || strcmp(events[k], "mouse5") == 0){ - if (strcmp(events[k], prevKey)){ - if (prevType != 0){ - Handler(prevKey, prevType); + }else if (strcmp(events[k].function, "mouse1") == 0 || strcmp(events[k].function, "mouse2") == 0 || strcmp(events[k].function, "mouse3") == 0 || strcmp(events[k].function, "mouse4") == 0 || strcmp(events[k].function, "mouse5") == 0){ + if (strcmp(events[k].function, prevEvent.function)){ + if (prevEvent.type != 0){ + Handler(prevEvent.function, prevEvent.type); + } + prevEvent.function = events[k].function; + prevEvent.type=3; } - strcpy(prevKey, events[k]); - prevType=3; + Handler(events[k].function, 2); + }else{ + system(events[k].function); } - Handler(events[k], 2); - }else{ - system(events[k]); + break; } - break; } } } - if(debug == 2){ + if(debug == 2 || dry){ printf("DATA: [%d", data[0]); for (int i = 1; i < sizeof(data); i++){ printf(", %d", data[i]); @@ -347,8 +378,7 @@ void GetDevice(int debug, int accept){ } - // Cleanup - + // Cleanup for (int x = 0; x strlen(in) || end + start > strlen(in)){ + return in; } - system(cmd); + char* out = malloc(end+1); + for (int i = 0; i < end; i++) + out[i] = in[i+start]; + out[end] = '\0'; + return out; } + int main(int args, char *in[]) { - int d=0, a=0, err; + int debug=0, accept=0, dry=0, err; err = system("xdotool sleep 0.01"); if (err != 0){ printf("Exitting...\n"); - return -1; + return -9; } for (int arg = 1; arg < args; arg++){ @@ -402,20 +457,26 @@ int main(int args, char *in[]) printf("\t-a\t\tAssume the first device that matches %04x:%04x is the Keydial\n", vid, pid); printf("\t-c [path]\tSpecifies a config file to use\n"); printf("\t-d [-d]\t\tEnable debug outputs (use twice to view data sent by the device)\n"); + printf("\t-dry \t\tDisplay data sent by the device without sending events\n"); printf("\t-h\t\tDisplays this message\n\n"); return 0; } if (strcmp(in[arg],"-d") == 0){ - d++; + debug++; + } + if (strcmp(in[arg],"-dry") == 0){ + dry=1; } if (strcmp(in[arg],"-a") == 0){ - a=1; + accept=1; } if (strcmp(in[arg], "-c") == 0){ if (strlen(in[arg+1]) > 0){ strcpy(file, in[arg+1]); arg++; - } + }else + printf("No config file specified. Exiting...\n"); + return -8; } } @@ -426,8 +487,9 @@ int main(int args, char *in[]) printf("Error: %d\n", err); return err; } - libusb_set_option(*ctx, LIBUSB_OPTION_LOG_LEVEL, 0); - GetDevice(d, a); + // Uncomment for more detailed debugging (might cause segmentation errors on some systems) + //libusb_set_option(*ctx, LIBUSB_OPTION_LOG_LEVEL, 0); + GetDevice(debug, accept, dry); libusb_exit(*ctx); return 0; } From 44a1751334437951a8c6337b5f0c54e85fba3474 Mon Sep 17 00:00:00 2001 From: mckset <99543950+mckset@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:21:52 -0400 Subject: [PATCH 02/10] Updated Layout - Swap button and wheel buttons now use a different method of being defined - Added further explanation on how the config file works and it other functions --- default.cfg | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/default.cfg b/default.cfg index 9bbf6c3..ceeb205 100644 --- a/default.cfg +++ b/default.cfg @@ -1,11 +1,33 @@ // KD100 config file -// The driver supports two key functions +// +// Assumptions: +// - (B)utton defines a new button and the following number specifies which button it is. Without the paranthesis, the previous sentence would define a new button +// - (B)uttons do not need to be listed in order but their type and function must be listed on separate lines afterwards +// - (B)utton definitions can be skipped if you don't want all the buttons to work on the keydial +// - (B)utton, type, and function definitions can contain numbers, symbols, or letters before hand but must have a number or function after the ':' +// - Ex) @#$!123ttype: 1 - is valid +// - type: f02r - will default to type 0 +// - type and function definitions must be lower case but can be in any order +// - The program defaults the type to 0 and the function to null +// - The (W)heel works in two parts. The first definition assigns all functions for turning it clockwise while the second definition assigns counter clockwise functions +// - The wheel functions must be defined at the end of the config file +// - If one wheel side has more functions than the other, it will set the function of the other wheel to NULL and do nothing when turning the wheel that way +// - Three wheel functions are provided but there is no limit to the amount you can add +// - Both button and wheel definitions do not need '//' before them and the wheel definition doesn't need the text after it to work +// - '//' is not required to add comments to the file but anything after a function, type, or button will be included in the program +// - All other text is skipped by the program +// - Wheel functions cannot run programs or act as mouse input +// +// (B)utton Types: // 0: Key - The pressed button acts as a key or a combination of keys // ex) a = key 'A' | ctrl+a = control and 'A' at the same time // 1: function - The pressed button runs a bash command/script // ex) krita | echo Hello world | gpio www.example.com -// NOTE: "swap" changes the wheel buttons function and "mouse1/2/3/4/5" activates mouse buttons (left/middle/right/scroll up/ scroll down) -// Each key is numbered from the top left to the bottom right and keeps the wheel and button separate +// NOTE: "swap" changes the wheel buttons function +// 2: Mouse buttons - Specify mouse1, 2, 3, 4, or 5 activates mouse buttons (left/middle/right/scroll up/ scroll down) +// ex) type: 2 function: mouse1 +// +// Each key is numbered from the top left to the bottom right and keeps the wheel and button separate. The wheel button is button 18 // |---------------| // | 0 | 1 | 2 | 3 | // |---|---|---|---| @@ -72,14 +94,14 @@ function: ctrl+s // Button 17 type: 0 function: Insert -// Button Wheel Button +// Dial Button 18 type: 1 function: swap -// Button Wheel Clockwise (MUST BE A KEY COMMAND or NULL to return to the first function) -function 1: ctrl+KP_Add -function 2: bracketright -function 3: NULL -// Button Wheel Counter-Clockwise (MUST BE A KEY COMMAND) -function 1: ctrl+minus -function 2: bracketleft -function 3: NULL +// Wheel Clockwise +function: ctrl+KP_Add +function: bracketright +function: NULL +// Wheel Counter-Clockwise +function: ctrl+minus +function: bracketleft +function: NULL From fd90de916ca3833f5ee1d41c3bf78156bb7b96c3 Mon Sep 17 00:00:00 2001 From: mckset <99543950+mckset@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:47:18 -0400 Subject: [PATCH 03/10] Fixed File Loading --- KD100.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/KD100.c b/KD100.c index 7b33d37..68600de 100644 --- a/KD100.c +++ b/KD100.c @@ -67,12 +67,19 @@ void GetDevice(int debug, int accept, int dry){ }else{ f = fopen(file, "r"); if (f == NULL){ - strcpy(file, getpwuid(getuid())->pw_dir); - strcat(file, "/.config/KD100/default.cfg"); - f = fopen(file, "r"); + char* home = getpwuid(getuid())->pw_dir; + file = "/.config/KD100/default.cfg"; + char temp[strlen(file)+strlen(home)+1]; + for (int i = 0; i < strlen(home); i++) + temp[i] = home[i]; + for (int i = 0; i < strlen(file); i++) + temp[i+strlen(home)] = file[i]; + temp[strlen(temp)] = '\0'; + + f = fopen(temp, "r"); if (f == NULL){ printf("DEFAULT CONFIGS ARE MISSING!\n"); - printf("Please add default.cfg to %s/.config/KD100/ or specify a file to use with -c\n", getpwuid(getuid())->pw_dir); + printf("Please add default.cfg to %s/.config/KD100/ or specify a file to use with -c\n", home); return; } } @@ -419,7 +426,7 @@ void Handler(char* key, int type){ if (mouse == 'a'){ for (int i = 0; i < strlen(key); i++) temp[i+strlen(cmd)] = key[i]; - temp[strlen(cmd) + strlen(key)] = '\0'; + temp[strlen(cmd)+strlen(key)] = '\0'; }else{ temp[strlen(cmd)] = ' '; temp[strlen(cmd)+1] = mouse; @@ -471,12 +478,14 @@ int main(int args, char *in[]) accept=1; } if (strcmp(in[arg], "-c") == 0){ - if (strlen(in[arg+1]) > 0){ - strcpy(file, in[arg+1]); + if (in[arg+1]){ + file = in[arg+1]; + printf("%s\n", file); arg++; - }else + }else{ printf("No config file specified. Exiting...\n"); return -8; + } } } From f7c5c7f5ff18cb57d3f0fd18ae8e4e37f21ffc62 Mon Sep 17 00:00:00 2001 From: mckset <99543950+mckset@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:47:51 -0400 Subject: [PATCH 04/10] v1.4 --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d6e25ee..a52aa2c 100644 --- a/README.md +++ b/README.md @@ -33,16 +33,18 @@ sudo ./KD100 [options] **-d** Enable debug output (can be used twice to output the full packet of data recieved from the device) +**-dry** Displays data sent from the keydial without send events to the program + **-h** Displays a help message Configuring ---------- Edit or copy **default.cfg** to add your own keys/commands and use the '-c' flag to specify the location of the config file -> **_NOTE:_** New config files must have the same format and line count as the default file +> **_NOTE:_** Config files from v1.31 and below need to be updated. See the example config file for the changes to the wheel function and wheel button Caveats ------- -- This only works on X11 based desktops (because of xdotool) +- This only works on X11 based desktops (because it relies on xdotool) - You do not need to run this with sudo if you set a udev rule for the device. Create/edit a rule file in /etc/udev/rules.d/ and add the following, then save and reboot or reload your udev rules ``` SUBSYSTEM=="usb",ATTRS{idVendor}=="256c",ATTRS{idProduct}=="006d",MODE="0666",GROUP="plugdev" From eeb351c12b11c72f9e0e4a9e2641b33beb6a1014 Mon Sep 17 00:00:00 2001 From: mckset <99543950+mckset@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:48:47 -0400 Subject: [PATCH 05/10] Fixed Spelling --- default.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/default.cfg b/default.cfg index ceeb205..f24e0c9 100644 --- a/default.cfg +++ b/default.cfg @@ -16,7 +16,7 @@ // - Both button and wheel definitions do not need '//' before them and the wheel definition doesn't need the text after it to work // - '//' is not required to add comments to the file but anything after a function, type, or button will be included in the program // - All other text is skipped by the program -// - Wheel functions cannot run programs or act as mouse input +// - (W)heel functions cannot run programs or act as mouse input // // (B)utton Types: // 0: Key - The pressed button acts as a key or a combination of keys From 7c7fe8b2b04dc019f9f18ae567395f3223b091a5 Mon Sep 17 00:00:00 2001 From: mckset <99543950+mckset@users.noreply.github.com> Date: Wed, 19 Jul 2023 14:37:39 -0400 Subject: [PATCH 06/10] Fixed Config Issues - Fixed issue when reading the config file on Ubuntu 20.04 - Added an additional error code report --- KD100.c | 73 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/KD100.c b/KD100.c index 68600de..65b9c68 100644 --- a/KD100.c +++ b/KD100.c @@ -101,19 +101,31 @@ void GetDevice(int debug, int accept, int dry){ if (!wheelType) events[b].function = Substring(data, i+10, strlen(data)-(i+10)); else if (wheelType == 1){ - wheel* temp = realloc(wheelEvents, (rWheels+1)*sizeof(*wheelEvents)); - wheelEvents = temp; - wheelEvents[rWheels].right = Substring(data, i+10, strlen(data)-(i+10)); - wheelEvents[rWheels].left = "NULL"; + if (rWheels != 0){ + wheel* temp = realloc(wheelEvents, (rWheels+1)*sizeof(*wheelEvents)); + wheelEvents = temp; + wheelEvents[rWheels].right = Substring(data, i+10, strlen(data)-(i+10)); + wheelEvents[rWheels].left = "NULL"; + }else{ + wheelEvents = malloc(sizeof(wheel)); + wheelEvents[0].right = Substring(data, i+10, strlen(data)-(i+10)); + wheelEvents[0].left = "NULL"; + } rWheels++; }else{ if (lWheels < rWheels) wheelEvents[lWheels].left = Substring(data, i+10, strlen(data)-(i+10)); else{ - wheel* temp = realloc(wheelEvents, (lWheels+1)*sizeof(*wheelEvents)); - wheelEvents = temp; - wheelEvents[lWheels].left = Substring(data, i+10, strlen(data)-(i+10)); - wheelEvents[lWheels].right = "NULL"; + if (lWheels != 0){ + wheel* temp = realloc(wheelEvents, (lWheels+1)*sizeof(*wheelEvents)); + wheelEvents = temp; + wheelEvents[lWheels].left = Substring(data, i+10, strlen(data)-(i+10)); + wheelEvents[lWheels].right = "NULL"; + }else{ + wheelEvents = malloc(sizeof(wheel)); + wheelEvents[0].left = Substring(data, i+10, strlen(data)-(i+10)); + wheelEvents[0].right = "NULL"; + } } lWheels++; } @@ -168,40 +180,38 @@ void GetDevice(int debug, int accept, int dry){ } }else if (devDesc.idVendor == vid && devDesc.idProduct == pid){ if (accept == 1){ - err=libusb_open(dev, &handle); - if (err < 0){ - printf("\nUnable to open device. Error: %d\n", err); - handle=NULL; - if (err == LIBUSB_ERROR_ACCESS){ - printf("Error: Permission denied\n"); - return; + if (uid != 0){ + err=libusb_open(dev, &handle); + if (err < 0){ + printf("\nUnable to open device. Error: %d\n", err); + handle=NULL; + if (err == LIBUSB_ERROR_ACCESS){ + printf("Error: Permission denied\n"); + return; + } } - } - if (debug > 0){ - printf("\nUsing: %04x:%04x (Bus: %03d Device: %03d)\n", vid, pid, libusb_get_bus_number(dev), libusb_get_device_address(dev)); - } - break; - }else{ - if (uid == 0){ // If the driver is ran as root, it can safely execute the following + if (debug > 0) + printf("\nUsing: %04x:%04x (Bus: %03d Device: %03d)\n", vid, pid, libusb_get_bus_number(dev), libusb_get_device_address(dev)); + break; + }else{ err = libusb_open(dev, &handle); if (err < 0){ printf("\nUnable to open device. Error: %d\n", err); handle=NULL; } err = libusb_get_string_descriptor_ascii(handle, devDesc.iProduct, info, 200); - if (debug > 0){ + if (debug > 0) printf("\n#%d | %04x:%04x : %s\n", d, vid, pid, info); - } - if (strlen(info) == 0){ + if (strlen(info) == 0) break; - }else{ + else{ libusb_close(handle); handle = NULL; } - }else{ - savedDevs[i] = dev; - i++; } + }else{ + savedDevs[i] = dev; + i++; } } } @@ -230,7 +240,10 @@ void GetDevice(int debug, int accept, int dry){ printf("Unable to open device. Error: %d\n", err); handle=NULL; if (err == LIBUSB_ERROR_ACCESS){ - printf("Error: Permission denied\n"); + printf("Permission denied\n"); + return; + }else if (err == LIBUSB_ERROR_IO){ + printf("I/O error. Try disconnecting and reconnecting the device."); return; } } From f4e5185ab22eb71ca3720b98f5453d2a1dffa599 Mon Sep 17 00:00:00 2001 From: mckset <99543950+mckset@users.noreply.github.com> Date: Wed, 19 Jul 2023 16:14:49 -0400 Subject: [PATCH 07/10] Update KD100.c --- KD100.c | 76 ++++++++++++++++++++++++++------------------------------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/KD100.c b/KD100.c index 65b9c68..b2c4016 100644 --- a/KD100.c +++ b/KD100.c @@ -13,6 +13,9 @@ #include #include +int keycodes[] = {1, 2, 4, 8, 16, 32, 64, 128, 129, 130, 132, 136, 144, 160, 192, 256, 257, 258, 260, 641, 642}; +char* file = "default.cfg"; + typedef struct event event; typedef struct wheel wheel; @@ -26,9 +29,6 @@ struct wheel { char* left; }; -const int keycodes[] = {1, 2, 4, 8, 16, 32, 64, 128, 129, 130, 132, 136, 144, 160, 192, 256, 257, 258, 260, 641, 642}; -char* file = "default.cfg"; - void GetDevice(int, int, int); void Handler(char*, int); char* Substring(char*, int, int); @@ -40,7 +40,7 @@ void GetDevice(int debug, int accept, int dry){ int err=0, wheelFunction=0, c=0, b=-1, tButtons=0, wheelType=0, lWheels=0, rWheels=0, tWheels=0; char* data = malloc(sizeof(data)); // Data received from the config file and the USB event* events = malloc(1*sizeof(*events)); // Stores key events and functions - wheel* wheelEvents = malloc(1*sizeof(*wheelEvents)); // Stores wheel functions + wheel* wheelEvents = malloc(1*sizeof(wheel)); // Stores wheel functions event prevEvent; uid_t uid=getuid(); // Used to check if the driver was ran as root @@ -107,7 +107,6 @@ void GetDevice(int debug, int accept, int dry){ wheelEvents[rWheels].right = Substring(data, i+10, strlen(data)-(i+10)); wheelEvents[rWheels].left = "NULL"; }else{ - wheelEvents = malloc(sizeof(wheel)); wheelEvents[0].right = Substring(data, i+10, strlen(data)-(i+10)); wheelEvents[0].left = "NULL"; } @@ -116,16 +115,10 @@ void GetDevice(int debug, int accept, int dry){ if (lWheels < rWheels) wheelEvents[lWheels].left = Substring(data, i+10, strlen(data)-(i+10)); else{ - if (lWheels != 0){ - wheel* temp = realloc(wheelEvents, (lWheels+1)*sizeof(*wheelEvents)); - wheelEvents = temp; - wheelEvents[lWheels].left = Substring(data, i+10, strlen(data)-(i+10)); - wheelEvents[lWheels].right = "NULL"; - }else{ - wheelEvents = malloc(sizeof(wheel)); - wheelEvents[0].left = Substring(data, i+10, strlen(data)-(i+10)); - wheelEvents[0].right = "NULL"; - } + wheel* temp = realloc(wheelEvents, (lWheels+1)*sizeof(*wheelEvents)); + wheelEvents = temp; + wheelEvents[lWheels].left = Substring(data, i+10, strlen(data)-(i+10)); + wheelEvents[lWheels].right = "NULL"; } lWheels++; } @@ -152,6 +145,7 @@ void GetDevice(int debug, int accept, int dry){ printf("\n"); } + int i = 0; char indi[] = "|/-\\"; while (err == 0 || err == LIBUSB_ERROR_NO_DEVICE){ @@ -180,38 +174,40 @@ void GetDevice(int debug, int accept, int dry){ } }else if (devDesc.idVendor == vid && devDesc.idProduct == pid){ if (accept == 1){ - if (uid != 0){ - err=libusb_open(dev, &handle); - if (err < 0){ - printf("\nUnable to open device. Error: %d\n", err); - handle=NULL; - if (err == LIBUSB_ERROR_ACCESS){ - printf("Error: Permission denied\n"); - return; - } + err=libusb_open(dev, &handle); + if (err < 0){ + printf("\nUnable to open device. Error: %d\n", err); + handle=NULL; + if (err == LIBUSB_ERROR_ACCESS){ + printf("Error: Permission denied\n"); + return; } - if (debug > 0) - printf("\nUsing: %04x:%04x (Bus: %03d Device: %03d)\n", vid, pid, libusb_get_bus_number(dev), libusb_get_device_address(dev)); - break; - }else{ + } + if (debug > 0){ + printf("\nUsing: %04x:%04x (Bus: %03d Device: %03d)\n", vid, pid, libusb_get_bus_number(dev), libusb_get_device_address(dev)); + } + break; + }else{ + if (uid == 0){ // If the driver is ran as root, it can safely execute the following err = libusb_open(dev, &handle); if (err < 0){ printf("\nUnable to open device. Error: %d\n", err); handle=NULL; } err = libusb_get_string_descriptor_ascii(handle, devDesc.iProduct, info, 200); - if (debug > 0) + if (debug > 0){ printf("\n#%d | %04x:%04x : %s\n", d, vid, pid, info); - if (strlen(info) == 0) + } + if (strlen(info) == 0){ break; - else{ + }else{ libusb_close(handle); handle = NULL; } + }else{ + savedDevs[i] = dev; + i++; } - }else{ - savedDevs[i] = dev; - i++; } } } @@ -240,16 +236,13 @@ void GetDevice(int debug, int accept, int dry){ printf("Unable to open device. Error: %d\n", err); handle=NULL; if (err == LIBUSB_ERROR_ACCESS){ - printf("Permission denied\n"); - return; - }else if (err == LIBUSB_ERROR_IO){ - printf("I/O error. Try disconnecting and reconnecting the device."); + printf("Error: Permission denied\n"); return; } } } - + i=0; if (handle == NULL){ @@ -289,6 +282,7 @@ void GetDevice(int debug, int accept, int dry){ err = 0; // Listen for events printf("Driver is running!\n"); + prevEvent.function = ""; prevEvent.type = 0; while (err >=0){ @@ -509,8 +503,8 @@ int main(int args, char *in[]) printf("Error: %d\n", err); return err; } - // Uncomment for more detailed debugging (might cause segmentation errors on some systems) - //libusb_set_option(*ctx, LIBUSB_OPTION_LOG_LEVEL, 0); + // Uncomment to enable libusb debug messages (might not work with older versions of libusb) + // libusb_set_option(*ctx, LIBUSB_OPTION_LOG_LEVEL, 1); GetDevice(debug, accept, dry); libusb_exit(*ctx); return 0; From 68c84cc73ada07de643364c2b6186d8345fa7662 Mon Sep 17 00:00:00 2001 From: mckset <99543950+mckset@users.noreply.github.com> Date: Wed, 19 Jul 2023 20:03:16 -0400 Subject: [PATCH 08/10] Updated Config Loading - Updated the way that the driver loads config files - Removed limit for config line lengths - Removed need for the config file to be exactly the same as the default - Cleaned up messy code and fixed some minor spelling errors - Fixed segmentation faults caused by initializing libusb - Fixed segmentation faults when closing libusb --- KD100.c | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/KD100.c b/KD100.c index b2c4016..fdb75db 100644 --- a/KD100.c +++ b/KD100.c @@ -38,7 +38,7 @@ const int pid = 0x006d; void GetDevice(int debug, int accept, int dry){ int err=0, wheelFunction=0, c=0, b=-1, tButtons=0, wheelType=0, lWheels=0, rWheels=0, tWheels=0; - char* data = malloc(sizeof(data)); // Data received from the config file and the USB + char* data = malloc(512*sizeof(char)); // Data received from the config file and the USB event* events = malloc(1*sizeof(*events)); // Stores key events and functions wheel* wheelEvents = malloc(1*sizeof(wheel)); // Stores wheel functions event prevEvent; @@ -144,8 +144,7 @@ void GetDevice(int debug, int accept, int dry){ printf("Wheel Right: %s | Wheel Left: %s\n", wheelEvents[i].right, wheelEvents[i].left); printf("\n"); } - - + free(data); int i = 0; char indi[] = "|/-\\"; while (err == 0 || err == LIBUSB_ERROR_NO_DEVICE){ @@ -174,21 +173,21 @@ void GetDevice(int debug, int accept, int dry){ } }else if (devDesc.idVendor == vid && devDesc.idProduct == pid){ if (accept == 1){ - err=libusb_open(dev, &handle); - if (err < 0){ - printf("\nUnable to open device. Error: %d\n", err); - handle=NULL; - if (err == LIBUSB_ERROR_ACCESS){ - printf("Error: Permission denied\n"); - return; + if (uid != 0){ + err=libusb_open(dev, &handle); + if (err < 0){ + printf("\nUnable to open device. Error: %d\n", err); + handle=NULL; + if (err == LIBUSB_ERROR_ACCESS){ + printf("Error: Permission denied\n"); + return; + } } - } - if (debug > 0){ - printf("\nUsing: %04x:%04x (Bus: %03d Device: %03d)\n", vid, pid, libusb_get_bus_number(dev), libusb_get_device_address(dev)); - } - break; - }else{ - if (uid == 0){ // If the driver is ran as root, it can safely execute the following + if (debug > 0){ + printf("\nUsing: %04x:%04x (Bus: %03d Device: %03d)\n", vid, pid, libusb_get_bus_number(dev), libusb_get_device_address(dev)); + } + break; + }else{ // If the driver is ran as root, it can safely execute the following err = libusb_open(dev, &handle); if (err < 0){ printf("\nUnable to open device. Error: %d\n", err); @@ -204,15 +203,15 @@ void GetDevice(int debug, int accept, int dry){ libusb_close(handle); handle = NULL; } - }else{ - savedDevs[i] = dev; - i++; } + }else{ + savedDevs[i] = dev; + i++; } } } - if (i > 0){ + if (accept == 0){ int in=-1; while(in == -1){ char buf[64]; @@ -409,6 +408,9 @@ void GetDevice(int debug, int accept, int dry){ void Handler(char* key, int type){ + if (strcmp(key, "NULL") == 0) + return 0; + char* cmd = ""; char mouse = 'a'; @@ -498,7 +500,7 @@ int main(int args, char *in[]) libusb_context **ctx; - err = libusb_init(ctx); + err = libusb_init(&ctx); if (err < 0){ printf("Error: %d\n", err); return err; @@ -506,6 +508,6 @@ int main(int args, char *in[]) // Uncomment to enable libusb debug messages (might not work with older versions of libusb) // libusb_set_option(*ctx, LIBUSB_OPTION_LOG_LEVEL, 1); GetDevice(debug, accept, dry); - libusb_exit(*ctx); + libusb_exit(ctx); return 0; } From ad587d91feb760fed2628fc7b623eb507e8a4603 Mon Sep 17 00:00:00 2001 From: mckset <99543950+mckset@users.noreply.github.com> Date: Wed, 19 Jul 2023 20:19:00 -0400 Subject: [PATCH 09/10] v1.4 --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a52aa2c..34eeb37 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ # Huion KD100 Linux Driver A simple driver for the Huion KD100 mini Keydial written in C to give the device some usability while waiting for Huion to fix their Linux drivers. Each button can be configured to either act as a key/multiple keys or to execute a program/command +> **_NOTICE:_** When updating from **v1.31** or below, make sure you updated your config file to follow the new format shown in the default config file + Pre-Installation ------------ -Arch Linux: +Arch Linux/Manjaro: ``` sudo pacman -S libusb-1.0 xdotool ``` -Ubuntu/Debian: +Ubuntu/Debian/Pop OS: ``` -sudo apt-get install libusb-1.0 xdotool +sudo apt-get install libusb-1.0-0-dev xdotool ``` > **_NOTE:_** Some distros label libusb as "libusb-1.0-0" and others might require the separate "libusb-1.0-dev" package @@ -22,6 +24,8 @@ cd KD100 make ``` +> Running make as root will install the driver as a command and create a folder in ~/.config to store config files + Usage ----- ``` @@ -33,23 +37,21 @@ sudo ./KD100 [options] **-d** Enable debug output (can be used twice to output the full packet of data recieved from the device) -**-dry** Displays data sent from the keydial without send events to the program +**-dry** Display data sent from the keydial and ignore events **-h** Displays a help message Configuring ---------- -Edit or copy **default.cfg** to add your own keys/commands and use the '-c' flag to specify the location of the config file -> **_NOTE:_** Config files from v1.31 and below need to be updated. See the example config file for the changes to the wheel function and wheel button +Edit or copy **default.cfg** to add your own keys/commands and use the '-c' flag to specify the location of the config file. New config files do not need to end in ".cfg". Caveats ------- -- This only works on X11 based desktops (because it relies on xdotool) +- This only works on X11 based desktops (because it relies on xdotool) but can be patched for wayland desktops by altering the "handler" function - You do not need to run this with sudo if you set a udev rule for the device. Create/edit a rule file in /etc/udev/rules.d/ and add the following, then save and reboot or reload your udev rules ``` SUBSYSTEM=="usb",ATTRS{idVendor}=="256c",ATTRS{idProduct}=="006d",MODE="0666",GROUP="plugdev" ``` -- If the driver is ran as a user and the '-a' flag is not used, you will need to select the device to use during startup - Technically speaking, this can support other devices, especially if they send the same type of byte information, otherwise the code should be easy enough to edit and add support for other usb devices. If you want to see the information sent by different devices, change the vid and pid in the program and run it with two debug flags Tested Distros @@ -57,7 +59,7 @@ Tested Distros - Arch linux - Manjaro - Ubuntu -- Kali Linux +- Pop OS Known Issues ------------ From 0a07126bffe9840599a161a38f0c5c1bcc1ccf41 Mon Sep 17 00:00:00 2001 From: mckset <99543950+mckset@users.noreply.github.com> Date: Wed, 19 Jul 2023 20:39:36 -0400 Subject: [PATCH 10/10] Updated Config Loading Also fixed minor spelling mistakes and fixed the libusb_set_option call --- KD100.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/KD100.c b/KD100.c index fdb75db..574e003 100644 --- a/KD100.c +++ b/KD100.c @@ -1,7 +1,7 @@ /* V1.4 https://github.com/mckset/KD100.git - KD 100 Linux driver for X11 desktops + KD100 Linux driver for X11 desktops Other devices can be supported by modifying the code to read data received by the device At the moment, only the KD100 mini keypad is supported by this code officially */ @@ -150,7 +150,7 @@ void GetDevice(int debug, int accept, int dry){ while (err == 0 || err == LIBUSB_ERROR_NO_DEVICE){ libusb_device **devs; // List of USB devices libusb_device *dev; // Selected USB device - struct libusb_config_descriptor *desc; // USB descrition (For claiming interfaces) + struct libusb_config_descriptor *desc; // USB description (For claiming interfaces) libusb_device_handle *handle = NULL; // USB handle err = libusb_get_device_list(NULL, &devs); @@ -505,8 +505,8 @@ int main(int args, char *in[]) printf("Error: %d\n", err); return err; } - // Uncomment to enable libusb debug messages (might not work with older versions of libusb) - // libusb_set_option(*ctx, LIBUSB_OPTION_LOG_LEVEL, 1); + // Uncomment to enable libusb debug messages + // libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, 1); GetDevice(debug, accept, dry); libusb_exit(ctx); return 0;