← KD100

Add leader key descriptions and bump to v1.7.1

57392f708d78aee977b33f35dd22799140f79b74 · Claude · Feb 7, 2026 12:31

Implement leader_description_N config fields that provide per-button
labels shown in the OSD when the leader key is active. Eligible buttons
swap their description to the leader variant (purple-tinted) in the
expanded keyboard layout. Input validation enforced (max 64 chars,
printable ASCII).

https://claude.ai/code/session_019MPUMMqDBcEYr7qFBLzJd6

+129 −16 · 9 files changed

  • Makefile +1 −1
  • README.md +31 −3
  • default.cfg +32 −2
  • src/config.c +35 −0
  • src/config.h +2 −1
  • src/device.c +5 −2
  • src/main.c +4 −4
  • src/osd.c +16 −2
  • src/osd.h +3 −1
diff --git a/Makefile b/Makefile
index 2c8d590..a193d47 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ DIR = $(shell pwd)
 HOME = "/home/"$(shell logname)
 TARGET = KD100
 DEBUG_TARGET = KD100-debug
-VERSION = 1.7.0
+VERSION = 1.7.1
 
 # Source files
 SRC_DIR = src
diff --git a/README.md b/README.md
index acba47d..4ed4789 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,12 @@ Originally forked from [mckset/KD100](https://github.com/mckset/KD100), now inde
 
 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.
 
-**Version 1.7.0** enhances the OSD with active button highlighting, wheel function descriptions, wheel set indicators, leader key visual feedback, and action aggregation for wheel events.
+**Version 1.7.1** adds leader key descriptions — per-button labels that swap in when the leader key is active, shown in the expanded OSD keyboard layout. Also includes all v1.7.0 enhancements: active button highlighting, wheel function descriptions, wheel set indicators, leader key visual feedback, and action aggregation.
 
 > **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.
 
 ## Features
+- **Leader Key Descriptions (v1.7.1)**: Per-button descriptions that swap in when leader is active
 - **Wheel Function Descriptions (v1.7.0)**: Human-readable names for wheel functions shown in OSD
 - **Active Button Highlighting (v1.7.0)**: Pressed buttons flash green in expanded keyboard layout
 - **Leader Key Visual Feedback (v1.7.0)**: Orange highlight for active leader, purple tint for eligible keys
@@ -33,7 +34,7 @@ A simple driver for the Huion KD100 mini Keydial written in C to give the device
 - **Clean Compilation**: Zero warnings with strict compiler flags
 - **Better Code Organization**: Easy to extend and maintain
 
-## Architecture (v1.7.0)
+## Architecture (v1.7.1)
 
 The codebase is organized into focused modules:
 
@@ -149,6 +150,21 @@ function: p
 leader_eligible: false # Cannot be modified by leader
 ```
 
+### Leader Key Descriptions (v1.7.1)
+Each button can have a separate description shown when the leader key is active. This lets you display the modified action in the OSD expanded view.
+
+```bash
+# Format: leader_description_<button_number>: <description>
+# Maximum 64 characters, printable ASCII only
+# Only define for leader-eligible buttons
+
+leader_description_0: Shift+Brush
+leader_description_2: Shift+Transform
+leader_description_3: Shift+Lasso
+```
+
+When the leader key is active, eligible buttons in the OSD keyboard layout swap their label to the leader description (shown with a purple tint). Non-eligible buttons keep their normal description.
+
 ### Configuration Example
 ```bash
 # Leader configuration
@@ -167,6 +183,10 @@ Button 18                  # Wheel toggle button
 type: 1
 function: swap
 leader_eligible: false    # Typically not eligible
+
+# Leader descriptions (shown in OSD when leader is active)
+leader_description_0: Shift+Brush
+leader_description_2: Shift+Transform
 ```
 
 ## On-Screen Display (OSD)
@@ -486,7 +506,15 @@ If you encounter permission errors or device conflicts:
 
 ## Version History
 
-### v1.7.0 (Current) - Enhanced OSD Feedback & Wheel Descriptions
+### v1.7.1 (Current) - Leader Key Descriptions
+- **Leader Key Descriptions**: New `leader_description_N` config fields for per-button labels when leader is active
+  - Descriptions swap in on eligible buttons in the expanded OSD keyboard layout
+  - Purple-tinted buttons show the leader description instead of the normal one
+  - Input validation: max 64 chars, printable ASCII only
+- **Configuration**: `leader_description_<button_number>: <description>` in config file
+- **Version Bump**: Updated version strings across all modules
+
+### v1.7.0 - Enhanced OSD Feedback & Wheel Descriptions
 - **Wheel Function Descriptions**: New `wheel_description_N` config fields for human-readable names
   - Shown in OSD set indicator, function pair display, and action messages
   - Replaces inline comments after wheel function lines in config
diff --git a/default.cfg b/default.cfg
index 555d94b..52e0cb8 100644
--- a/default.cfg
+++ b/default.cfg
@@ -1,4 +1,4 @@
-// KD100 config file - Version 1.7.0
+// KD100 config file - Version 1.7.1
 //
 //      Assumptions:
 //              - (B)utton defines a new button and the following number specifies which button it is.
@@ -141,7 +141,37 @@ description_16: SHIFT
 description_17: Redo
 description_18: Wheel Fn
 
-// 
+//
+//      ==========================================
+//      LEADER KEY DESCRIPTIONS
+//      ==========================================
+//
+//      Descriptions shown in the OSD when the leader key is active.
+//      When leader is on, eligible buttons show these descriptions
+//      instead of their normal ones.
+//
+//      Format: leader_description_<button_number>: <description>
+//      Maximum 64 characters, printable ASCII only.
+//      Only define for leader-eligible buttons (not button 1 or 18).
+//
+leader_description_0: Shift+Brush
+leader_description_2: Shift+Transform
+leader_description_3: Shift+Lasso
+leader_description_4: Shift+New Layer
+leader_description_5: Shift+Duplicate
+leader_description_6: Shift+Merge
+leader_description_7: Shift+Eraser
+leader_description_8: Shift+Deselect
+leader_description_9: Shift+Delete
+leader_description_10: Shift+Undo
+leader_description_11: Shift+Inv Sel
+leader_description_12: Shift+Pan
+leader_description_13: Shift+Zoom
+leader_description_14: Shift+Rotate
+leader_description_15: Shift+Mirror
+leader_description_17: Shift+Redo
+
+//
 //      Leader configuration
 //      Options:
 //      Leader Button:    Specify which key should take role as modifier for the keys.
diff --git a/src/config.c b/src/config.c
index b35aaa5..96a72d9 100644
--- a/src/config.c
+++ b/src/config.c
@@ -148,6 +148,7 @@ config_t* config_create(void) {
     // Initialize key descriptions
     for (int i = 0; i < 19; i++) {
         config->key_descriptions[i] = NULL;
+        config->leader_descriptions[i] = NULL;
     }
 
     return config;
@@ -194,6 +195,9 @@ void config_destroy(config_t* config) {
         if (config->key_descriptions[i] != NULL) {
             free(config->key_descriptions[i]);
         }
+        if (config->leader_descriptions[i] != NULL) {
+            free(config->leader_descriptions[i]);
+        }
     }
 
     free(config);
@@ -453,6 +457,25 @@ int config_load(config_t* config, const char* filename, int debug) {
             continue;
         }
 
+        // Parse leader key descriptions (leader_description_0, leader_description_1, etc.)
+        if (strncasecmp(line, "leader_description_", 19) == 0) {
+            char* num_str = line + 19;
+            char* colon = strchr(num_str, ':');
+            if (colon) {
+                *colon = '\0';
+                int btn = atoi(num_str);
+                if (btn >= 0 && btn <= 18) {
+                    char* value = colon + 1;
+                    while (*value == ' ') value++;
+                    if (config->leader_descriptions[btn]) free(config->leader_descriptions[btn]);
+                    config->leader_descriptions[btn] = sanitize_description(value);
+                    if (debug) printf("Config: leader_description_%d = %s\n", btn,
+                                      config->leader_descriptions[btn] ? config->leader_descriptions[btn] : "(empty)");
+                }
+            }
+            continue;
+        }
+
         // Parse wheel descriptions (wheel_description_0, wheel_description_1, etc.)
         if (strncasecmp(line, "wheel_description_", 18) == 0) {
             char* num_str = line + 18;
@@ -695,5 +718,17 @@ void config_print(const config_t* config, int debug) {
         }
     }
 
+    // Print leader key descriptions if any are set
+    int has_leader_descriptions = 0;
+    for (int i = 0; i < 19; i++) {
+        if (config->leader_descriptions[i]) {
+            if (!has_leader_descriptions) {
+                printf("\n=== Leader Key Descriptions ===\n");
+                has_leader_descriptions = 1;
+            }
+            printf("Button %d (leader): %s\n", i, config->leader_descriptions[i]);
+        }
+    }
+
     printf("\n");
 }
diff --git a/src/config.h b/src/config.h
index dd1cb75..68a281a 100644
--- a/src/config.h
+++ b/src/config.h
@@ -55,7 +55,8 @@ typedef struct {
     wheel_mode_t wheel_mode;     // Wheel toggle mode (sequential or sets)
     osd_config_t osd;            // OSD settings
     profile_config_t profile;    // Profile settings
-    char* key_descriptions[19];  // Per-button descriptions (for default profile)
+    char* key_descriptions[19];         // Per-button descriptions (for default profile)
+    char* leader_descriptions[19];      // Per-button descriptions when leader is active
 } config_t;
 
 // Configuration functions
diff --git a/src/device.c b/src/device.c
index 043df36..f7bfd0a 100644
--- a/src/device.c
+++ b/src/device.c
@@ -40,7 +40,7 @@ void device_run(libusb_context* ctx, config_t* config, int debug, int accept, in
     if (debug > 0) {
         if (debug > 2)
             debug = 2;
-        printf("Version 1.7.0 - Enhanced OSD Feedback & Wheel Descriptions\n");
+        printf("Version 1.7.1 - Leader Key Descriptions\n");
         printf("Debug level: %d\n", debug);
     }
 
@@ -70,6 +70,9 @@ void device_run(libusb_context* ctx, config_t* config, int debug, int accept, in
                     if (config->key_descriptions[i]) {
                         osd_set_key_description(osd, i, config->key_descriptions[i]);
                     }
+                    if (config->leader_descriptions[i]) {
+                        osd_set_leader_description(osd, i, config->leader_descriptions[i]);
+                    }
                 }
 
                 // Load wheel descriptions from config
@@ -355,7 +358,7 @@ void device_run(libusb_context* ctx, config_t* config, int debug, int accept, in
                 }
 
                 printf("Driver is running!\n");
-                printf("Enhanced Leader Key System v1.7.0\n");
+                printf("Enhanced Leader Key System v1.7.1\n");
                 printf("Mode: %s | Timeout: %d ms\n",
                        leader_mode_to_string(config->leader.mode), config->leader.timeout_ms);
                 printf("Wheel Mode: %s", config->wheel_mode == WHEEL_MODE_SEQUENTIAL ? "sequential" : "sets");
diff --git a/src/main.c b/src/main.c
index 1e312f1..0151cf0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,5 +1,5 @@
 /*
-	V1.7.0 - Enhanced OSD Feedback & Wheel Descriptions
+	V1.7.1 - Leader Key Descriptions
 	https://github.com/mckset/KD100.git
 	KD100 Linux driver for X11 desktops
 	Features:
@@ -169,7 +169,7 @@ int main(int args, char *in[]) {
             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");
-            printf("\nNew in v1.7.0 - ENHANCED OSD FEEDBACK:\n");
+            printf("\nNew in v1.7.1 - LEADER KEY DESCRIPTIONS:\n");
             printf("\t• Wheel function descriptions (wheel_description_N: name)\n");
             printf("\t• Active button highlighting in expanded keyboard layout\n");
             printf("\t• Leader key visual feedback (orange/purple indicators)\n");
@@ -249,8 +249,8 @@ int main(int args, char *in[]) {
         }
     }
 
-    printf("\nKD100 Driver v1.7.0 - Enhanced OSD Feedback & Wheel Descriptions\n");
-    printf("Features: OSD overlay | Profile switching | Wheel descriptions | Button highlighting\n\n");
+    printf("\nKD100 Driver v1.7.1 - Leader Key Descriptions\n");
+    printf("Features: OSD overlay | Profile switching | Wheel descriptions | Leader descriptions | Button highlighting\n\n");
 
     // Run device handler
     device_run(ctx, config, debug, accept, dry);
diff --git a/src/osd.c b/src/osd.c
index 1f549f6..1a03e0b 100644
--- a/src/osd.c
+++ b/src/osd.c
@@ -97,6 +97,7 @@ osd_state_t* osd_create(config_t* config) {
     // Initialize key descriptions to NULL
     for (int i = 0; i < 19; i++) {
         osd->key_descriptions[i] = NULL;
+        osd->leader_descriptions[i] = NULL;
     }
 
     // Initialize active button state
@@ -136,6 +137,7 @@ void osd_destroy(osd_state_t* osd) {
     // Free key descriptions
     for (int i = 0; i < 19; i++) {
         if (osd->key_descriptions[i]) free(osd->key_descriptions[i]);
+        if (osd->leader_descriptions[i]) free(osd->leader_descriptions[i]);
     }
 
     // Free wheel descriptions
@@ -439,9 +441,11 @@ static void draw_button_key(Display* dpy, Window win, GC gc, osd_state_t* osd,
     snprintf(num, sizeof(num), "%d", btn);
     XDrawString(dpy, win, gc, x + text_offset_x, y + text_offset_y1, num, strlen(num));
 
-    // Draw description or function
+    // Draw description or function (swap to leader description when leader is active)
     const char* desc = NULL;
-    if (osd->key_descriptions[btn]) {
+    if (is_leader_modified && osd->leader_descriptions[btn]) {
+        desc = osd->leader_descriptions[btn];
+    } else if (osd->key_descriptions[btn]) {
         desc = osd->key_descriptions[btn];
     } else if (osd->config && btn < osd->config->totalButtons &&
                osd->config->events[btn].function) {
@@ -907,6 +911,16 @@ void osd_set_key_description(osd_state_t* osd, int button_index, const char* des
     osd->key_descriptions[button_index] = description ? strdup(description) : NULL;
 }
 
+// Set leader key description
+void osd_set_leader_description(osd_state_t* osd, int button_index, const char* description) {
+    if (osd == NULL || button_index < 0 || button_index > 18) return;
+
+    if (osd->leader_descriptions[button_index]) {
+        free(osd->leader_descriptions[button_index]);
+    }
+    osd->leader_descriptions[button_index] = description ? strdup(description) : NULL;
+}
+
 // Get key description
 const char* osd_get_key_description(osd_state_t* osd, int button_index) {
     if (osd == NULL || button_index < 0 || button_index > 18) return NULL;
diff --git a/src/osd.h b/src/osd.h
index 62a1467..d6d8394 100644
--- a/src/osd.h
+++ b/src/osd.h
@@ -73,7 +73,8 @@ typedef struct {
     config_t* config;
 
     // Key descriptions for the current profile
-    char* key_descriptions[19];   // Descriptions for buttons 0-18
+    char* key_descriptions[19];    // Descriptions for buttons 0-18
+    char* leader_descriptions[19]; // Descriptions when leader is active
 
     // Active button highlighting
     int active_button;            // Currently pressed button (-1 = none)
@@ -110,6 +111,7 @@ void osd_move(osd_state_t* osd, int dx, int dy);
 
 // Key description functions
 void osd_set_key_description(osd_state_t* osd, int button_index, const char* description);
+void osd_set_leader_description(osd_state_t* osd, int button_index, const char* description);
 const char* osd_get_key_description(osd_state_t* osd, int button_index);
 void osd_clear_descriptions(osd_state_t* osd);