← KD100

Before: send_leader_combination() was resetting leader state for all modes After: Only resets for one_shot mode, preserves state for toggle and sticky Added proper separation between leader_active (temporary) and toggle_state (persistent) Shows toggle state separately from leader active state Clearer mode indication in debug output Button 18 was getting the last parsed leader_eligible value

e49c7fb6177cfc6b110c2003141b9f0ffc160028 · ovidiucs · Jan 22, 2026 07:51

+69 −55 · 2 files changed

  • KD100.c +32 −18
  • default.cfg +37 −37
diff --git a/KD100.c b/KD100.c
index 2097645..c06edf7 100644
--- a/KD100.c
+++ b/KD100.c
@@ -139,6 +139,10 @@ void setup_crash_handler(void) {
 }
 #endif
 
+// Device identifiers
+const int vid = 0x256c;
+const int pid = 0x006d;
+
 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";
 int enable_uclogic = 0;
@@ -193,9 +197,6 @@ void trim_trailing_spaces(char* str);
 leader_mode_t parse_leader_mode(const char* mode_str);
 const char* leader_mode_to_string(leader_mode_t mode);
 
-const int vid = 0x256c;
-const int pid = 0x006d;
-
 // Get current time in milliseconds
 long get_time_ms() {
     struct timeval tv;
@@ -269,13 +270,13 @@ int is_modifier_key(const char* key) {
     return 0;
 }
 
-// Reset leader state
+// Reset leader state (but preserve toggle state for toggle mode)
 void reset_leader_state(leader_state* state) {
     state->leader_active = 0;
     state->last_button = -1;
     state->leader_press_time.tv_sec = 0;
     state->leader_press_time.tv_usec = 0;
-    state->toggle_state = 0; // Also reset toggle state
+    // Don't reset toggle_state here - it's managed separately
 }
 
 // Send leader combination
@@ -293,10 +294,15 @@ void send_leader_combination(leader_state* state, char* combination, int debug)
     snprintf(cmd, sizeof(cmd), "xdotool key %s", combination);
     system(cmd);
     
-    // Reset leader state after sending (except for sticky mode)
-    if (state->mode != LEADER_MODE_STICKY) {
+    // Reset leader state after sending (except for sticky and toggle modes)
+    if (state->mode == LEADER_MODE_ONE_SHOT) {
         reset_leader_state(state);
+        state->toggle_state = 0; // Also reset toggle for one_shot
+    } else if (state->mode == LEADER_MODE_STICKY) {
+        // Update timer for sticky mode
+        gettimeofday(&state->leader_press_time, NULL);
     }
+    // For toggle mode, don't reset anything - stay active
 }
 
 // Process leader key combinations
@@ -305,7 +311,7 @@ void process_leader_combination(leader_state* state, event* events, int button_i
         return;
     }
     
-    // Check if this is the wheel button (button 18) - should not be modified
+    // Check if this is the wheel button (button 18) - handle specially
     if (button_index == 18) {
         // Wheel button - handle normally without leader
         if (events[button_index].function != NULL && 
@@ -313,7 +319,10 @@ void process_leader_combination(leader_state* state, event* events, int button_i
             // Handle swap function
             // (This would need wheel state tracking - implemented elsewhere)
         }
-        reset_leader_state(state);
+        // Don't reset leader state for wheel button in toggle mode
+        if (state->mode != LEADER_MODE_TOGGLE) {
+            reset_leader_state(state);
+        }
         return;
     }
     
@@ -342,6 +351,7 @@ void process_leader_combination(leader_state* state, event* events, int button_i
                 // Disable toggle mode
                 state->toggle_state = 0;
                 state->leader_active = 0;
+                reset_leader_state(state);
                 
                 if (debug == 1) {
                     printf("Leader toggle mode DISABLED by button %d\n", button_index);
@@ -369,8 +379,13 @@ void process_leader_combination(leader_state* state, event* events, int button_i
         return;
     }
     
-    // Check if we're in leader mode
-    if (state->leader_active) {
+    // Check if we're in leader mode (either active or toggle mode is on)
+    int in_leader_mode = state->leader_active;
+    if (state->mode == LEADER_MODE_TOGGLE) {
+        in_leader_mode = state->toggle_state;
+    }
+    
+    if (in_leader_mode) {
         // Check eligibility first
         if (events[button_index].leader_eligible == 0) {
             // Button is not eligible for leader modifications
@@ -379,7 +394,7 @@ void process_leader_combination(leader_state* state, event* events, int button_i
             }
             
             // For sticky mode, we might want to keep leader active
-            if (state->mode != LEADER_MODE_STICKY) {
+            if (state->mode != LEADER_MODE_STICKY && state->mode != LEADER_MODE_TOGGLE) {
                 reset_leader_state(state);
             }
             
@@ -391,12 +406,14 @@ void process_leader_combination(leader_state* state, event* events, int button_i
             gettimeofday(&now, NULL);
             long elapsed = time_diff_ms(state->leader_press_time, now);
             
+            // Check timeout (skip for toggle mode)
             if (elapsed > state->timeout_ms && state->mode != LEADER_MODE_TOGGLE) {
                 // Leader timeout - reset (except for toggle mode)
                 if (debug == 1) {
                     printf("Leader timeout (%ld ms > %d ms)\n", elapsed, state->timeout_ms);
                 }
                 reset_leader_state(state);
+                state->toggle_state = 0;
                 // Fall through to normal button handling
             } else {
                 // We have a leader combination!
@@ -418,11 +435,6 @@ void process_leader_combination(leader_state* state, event* events, int button_i
                     gettimeofday(&state->leader_press_time, NULL);
                 }
                 
-                // For one-shot mode, reset after sending
-                if (state->mode == LEADER_MODE_ONE_SHOT) {
-                    reset_leader_state(state);
-                }
-                
                 return; // Don't also handle as normal button
             }
         }
@@ -1252,7 +1264,9 @@ void GetDevice(libusb_context *ctx, int debug, int accept, int dry){
 						}
 						printf("]\n");
 						
-						if (leader.leader_active) {
+						if (leader.toggle_state) {
+							printf("Leader toggle: ON (mode: %s)\n", leader_mode_to_string(leader.mode));
+						} else if (leader.leader_active) {
 							struct timeval now;
 							gettimeofday(&now, NULL);
 							long elapsed = time_diff_ms(leader.leader_press_time, now);
diff --git a/default.cfg b/default.cfg
index 2fd9734..d1d27b5 100644
--- a/default.cfg
+++ b/default.cfg
@@ -47,52 +47,52 @@
 //
 
 // 
-// Leader configuration
-// Options:
-// Leader Button:    Specify which key should take role as modifier for the keys.
-// Leader Functions: Specify which key press will be used. 
-// Leader Timeout:   Ammount of time that leader remains active in miliseconds.
-// Leader Mode:      One of the three modes that the leader will act.
-// 
-// One shot:         The leader keypress will act one time in the configured time window.
-// Example:          Press Button 16 (leader) -> Press Button 0 (b) -> Result: shift+b -> Leader resets
-// 
-// Sticky:           Leader stays active until timeout
-// Example:          Press Button 16 (leader) -> Press Button 0 (b) -> Result: shift+b
-//                   Press Button 1 (p) -> Result: shift+p
-//                   Press Button 2 (t) -> Result: shift+t
-//                   Timeout after 1000ms -> Leader resets
-// 
-// Toggle:           Leader toggles on/off
-// Example:          Press Button 16 (leader) -> Leader enabled
-//                   Press Button 0 (b) -> Result: shift+b
-//                   Press Button 1 (p) -> Result: shift+p
-//                   Press Button 16 (leader) -> Leader disabled
-//                   Press Button 0 (b) -> Result: b (normal)
-// Default:          one_shot
-//
+//      Leader configuration
+//      Options:
+//      Leader Button:    Specify which key should take role as modifier for the keys.
+//      Leader Functions: Specify which key press will be used. 
+//      Leader Timeout:   Ammount of time that leader remains active in miliseconds.
+//      Leader Mode:      One of the three modes that the leader will act.
+//      
+//      one_shot:         The leader keypress will act one time in the configured time window.
+//      Example:          Press Button 16 (leader) -> Press Button 0 (b) -> Result: shift+b -> Leader resets
+//      
+//      sticky:           Leader stays active until timeout
+//      Example:          Press Button 16 (leader) -> Press Button 0 (b) -> Result: shift+b
+//                        Press Button 1 (p) -> Result: shift+p
+//                        Press Button 2 (t) -> Result: shift+t
+//                        Timeout after 1000ms -> Leader resets
+//      
+//      toggle:           Leader toggles on/off
+//      Example:          Press Button 16 (leader) -> Leader enabled
+//                        Press Button 0 (b) -> Result: shift+b
+//                        Press Button 1 (p) -> Result: shift+p
+//                        Press Button 16 (leader) -> Leader disabled
+//                        Press Button 0 (b) -> Result: b (normal)
+//      Default:          one_shot
+//     
 leader_button: 16
 leader_function: shift
 leader_timeout: 1000
-leader_mode: one_shot
+leader_mode: toggle
 
 // 
-// hid_uclogic compatibility configuration
-// enabe_uclogic: [true|false] - OpenTabletDriver requires hid_uclogic to be unloaded from kernel.
-// Default:       false
-// enable_uclogic: false
-//
+//     hid_uclogic compatibility configuration
+//     enabe_uclogic: [true|false] - OpenTabletDriver requires hid_uclogic to be unloaded from kernel.
+//     Default:       false
+//     enable_uclogic: false
+//    
 
-// 
-// Brush Tool
-//
+//     
+//     Brush Tool
+//    
 Button 0
 type: 0
 function: b
 // 
-// Optional: leader_eligible: [true|false]
-// Declare button as eligble to be used or not alongside with the leader key
-// Default: all buttons (with the exception of Button 18, the wheel toggle button, are eligible.
+//     Optional: leader_eligible: [true|false]
+//     Declare button as eligble to be used or not alongside with the leader key
+//     Default: all buttons (with the exception of Button 18, the wheel toggle button, are eligible.
 //
 leader_eligible: true
 // 
@@ -101,7 +101,7 @@ leader_eligible: true
 Button 1
 type: 0
 function: p
-leader_eligible: true
+leader_eligible: false
 // 
 // Transform Tool
 //