← KD100

Refactor to modular architecture - Version 1.5.0

b7705d91b64c43d6a731b4e78643dcf3e283858c · Claude · Jan 22, 2026 06:37

MAJOR REFACTORING: Complete modular restructure of the codebase

This commit transforms the monolithic 1,520-line KD100.c file into a
clean, modular architecture across 7 focused modules.

Module Breakdown:
- src/main.c (269 lines) - Application entry point and orchestration
- src/config.c/h (352 lines) - Configuration file parsing and management
- src/device.c/h (402 lines) - USB device discovery and event loop
- src/leader.c/h (198 lines) - Leader key system implementation
- src/handler.c/h (65 lines) - Event handling and key execution
- src/utils.c/h (122 lines) - Utility functions (time, string, parsing)
- src/compat.c/h (66 lines) - Hardware compatibility layer

Benefits:
✓ Improved maintainability - each module has single responsibility
✓ Better testability - modules can be tested independently
✓ Easier debugging - smaller, focused code units
✓ Enhanced readability - clear separation of concerns
✓ Future extensibility - new features easier to add

Technical Changes:
- Created src/ directory for all source files
- Updated Makefile to support modular build
- Added 'make legacy' target to build original monolithic version
- Preserved all v1.4.9 features (leader modes, per-button eligibility)
- Updated version to 1.5.0
- Maintained backward compatibility with existing configs

Build Changes:
- Default 'make' now builds modular version
- 'make legacy' builds original KD100.c
- All existing targets (debug, release, asan, ubsan) updated
- Added -Isrc to include path for headers

No functional changes - this is a pure refactoring commit that maintains
100% feature parity with v1.4.9 while dramatically improving code structure.

+1638 −22 · 14 files changed

  • Makefile +36 −22
  • src/compat.c +66 −0
  • src/compat.h +15 −0
  • src/config.c +352 −0
  • src/config.h +28 −0
  • src/device.c +402 −0
  • src/device.h +14 −0
  • src/handler.c +65 −0
  • src/handler.h +8 −0
  • src/leader.c +198 −0
  • src/leader.h +34 −0
  • src/main.c +269 −0
  • src/utils.c +122 −0
  • src/utils.h +29 −0
diff --git a/Makefile b/Makefile
index 7e83cbd..970192f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,21 @@
 # Compiler and flags
 CC = gcc
-CFAGS = -Wall -Wextra -Wpedantic
+CFLAGS = -Wall -Wextra -Wpedantic -Isrc
 LDFLAGS = -lusb-1.0 -ldl
 USER = $(shell id -u)
 DIR = $(shell pwd)
 HOME = "/home/"$(shell logname)
 TARGET = KD100
 DEBUG_TARGET = KD100-debug
-VERSION = 1.4.3
+VERSION = 1.5.0
+
+# Source files
+SRC_DIR = src
+SOURCES = $(SRC_DIR)/main.c $(SRC_DIR)/config.c $(SRC_DIR)/device.c \
+          $(SRC_DIR)/handler.c $(SRC_DIR)/leader.c $(SRC_DIR)/utils.c \
+          $(SRC_DIR)/compat.c
+OBJECTS = $(SOURCES:.c=.o)
+LEGACY_SOURCE = KD100.c
 
 # Debug flags
 DEBUG_FLAGS = -g -ggdb3 -O0 -fno-omit-frame-pointer -fno-inline \
@@ -19,19 +27,24 @@ RELEASE_FLAGS = -O2 -DNDEBUG
 # Default target
 all: $(TARGET)
 
-# Standard build
-$(TARGET): KD100.c
-	$(CC) $(CFLAGS) KD100.c $(LDFLAGS) -o $(TARGET)
-	@echo "Build complete: $(TARGET)"
+# Standard build (modular)
+$(TARGET): $(SOURCES)
+	$(CC) $(CFLAGS) $(SOURCES) $(LDFLAGS) -o $(TARGET)
+	@echo "Build complete: $(TARGET) (v$(VERSION) - Modular)"
 	@echo "Run './$(TARGET)' to execute"
 
+# Legacy build (monolithic)
+legacy: $(LEGACY_SOURCE)
+	$(CC) $(CFLAGS) $(LEGACY_SOURCE) $(LDFLAGS) -o $(TARGET)-legacy
+	@echo "Legacy build complete: $(TARGET)-legacy (v1.4.9)"
+
 # Debug build with maximum debugability
 debug: CFLAGS += $(DEBUG_FLAGS)
 debug: $(DEBUG_TARGET)
 
-$(DEBUG_TARGET): KD100.c
-	$(CC) $(CFLAGS) KD100.c $(LDFLAGS) -o $(DEBUG_TARGET)
-	@echo "✓ Debug build complete: $(DEBUG_TARGET)"
+$(DEBUG_TARGET): $(SOURCES)
+	$(CC) $(CFLAGS) $(SOURCES) $(LDFLAGS) -o $(DEBUG_TARGET)
+	@echo "✓ Debug build complete: $(DEBUG_TARGET) (v$(VERSION) - Modular)"
 	@echo "✓ Debug symbols: $(shell readelf -S $(DEBUG_TARGET) 2>/dev/null | grep -c "\.debug" || echo 0) sections"
 	@echo "✓ Size: $(shell stat -c%s $(DEBUG_TARGET) 2>/dev/null || echo 0) bytes"
 	@echo ""
@@ -44,10 +57,10 @@ $(DEBUG_TARGET): KD100.c
 release: CFLAGS += $(RELEASE_FLAGS)
 release: $(TARGET)-release
 
-$(TARGET)-release: KD100.c
-	$(CC) $(CFLAGS) KD100.c $(LDFLAGS) -o $(TARGET)-release
+$(TARGET)-release: $(SOURCES)
+	$(CC) $(CFLAGS) $(SOURCES) $(LDFLAGS) -o $(TARGET)-release
 	@strip $(TARGET)-release 2>/dev/null || true
-	@echo "Release build complete: $(TARGET)-release"
+	@echo "Release build complete: $(TARGET)-release (v$(VERSION) - Modular)"
 	@echo "Size: $(shell stat -c%s $(TARGET)-release) bytes"
 
 # Install target (keeps your original logic)
@@ -101,9 +114,9 @@ asan: CFLAGS += -fsanitize=address -fno-omit-frame-pointer
 asan: LDFLAGS += -fsanitize=address
 asan: $(TARGET)-asan
 
-$(TARGET)-asan: KD100.c
-	$(CC) $(CFLAGS) KD100.c $(LDFLAGS) -o $(TARGET)-asan
-	@echo "AddressSanitizer build complete: $(TARGET)-asan"
+$(TARGET)-asan: $(SOURCES)
+	$(CC) $(CFLAGS) $(SOURCES) $(LDFLAGS) -o $(TARGET)-asan
+	@echo "AddressSanitizer build complete: $(TARGET)-asan (v$(VERSION))"
 	@echo "Run with: ./$(TARGET)-asan"
 
 # Build with undefined behavior sanitizer
@@ -111,20 +124,21 @@ ubsan: CFLAGS += -fsanitize=undefined
 ubsan: LDFLAGS += -fsanitize=undefined
 ubsan: $(TARGET)-ubsan
 
-$(TARGET)-ubsan: KD100.c
-	$(CC) $(CFLAGS) KD100.c $(LDFLAGS) -o $(TARGET)-ubsan
-	@echo "UndefinedBehaviorSanitizer build complete: $(TARGET)-ubsan"
+$(TARGET)-ubsan: $(SOURCES)
+	$(CC) $(CFLAGS) $(SOURCES) $(LDFLAGS) -o $(TARGET)-ubsan
+	@echo "UndefinedBehaviorSanitizer build complete: $(TARGET)-ubsan (v$(VERSION))"
 
 # Clean targets
 clean:
-	rm -f $(TARGET) $(DEBUG_TARGET) $(TARGET)-release $(TARGET)-asan $(TARGET)-ubsan KD100-1421
-	rm -f *.o *.log core core.*
+	rm -f $(TARGET) $(DEBUG_TARGET) $(TARGET)-release $(TARGET)-asan $(TARGET)-ubsan $(TARGET)-legacy KD100-1421
+	rm -f $(OBJECTS) *.o *.log core core.*
 	@echo "Cleaned build artifacts"
 
 # Help target
 help:
 	@echo "Available targets:"
-	@echo "  make              - Build standard version"
+	@echo "  make              - Build standard version (v$(VERSION) - Modular)"
+	@echo "  make legacy       - Build legacy monolithic version (v1.4.9)"
 	@echo "  make debug        - Build with full debug symbols and crash handler"
 	@echo "  make release      - Build optimized release version"
 	@echo "  make install      - Install system-wide (requires root)"
@@ -143,5 +157,5 @@ version:
 	@echo "KD100 Makefile v$(VERSION)"
 	@echo "Compiler: $(shell $(CC) --version | head -1)"
 
-.PHONY: all debug release install 1.4 gdb valgrind strace analyze asan ubsan clean help version
+.PHONY: all legacy debug release install 1.4 gdb valgrind strace analyze asan ubsan clean help version
 
diff --git a/src/compat.c b/src/compat.c
new file mode 100644
index 0000000..35fffb4
--- /dev/null
+++ b/src/compat.c
@@ -0,0 +1,66 @@
+#include "compat.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+// Check if a kernel module is loaded
+int is_module_loaded(const char* module_name) {
+    char path[256];
+    snprintf(path, sizeof(path), "/sys/module/%s", module_name);
+    return (access(path, F_OK) == 0);
+}
+
+// Try to access device via hidraw (alternative to libusb)
+int try_hidraw_access(void) {
+    DIR *dir;
+    struct dirent *ent;
+    char path[256];
+    char buf[256];
+
+    dir = opendir("/dev");
+    if (dir == NULL) return -1;
+
+    while ((ent = readdir(dir)) != NULL) {
+        if (strncmp(ent->d_name, "hidraw", 6) == 0) {
+            snprintf(path, sizeof(path), "/sys/class/hidraw/%s/device/uevent",
+                     ent->d_name);
+            FILE *f = fopen(path, "r");
+            if (f) {
+                while (fgets(buf, sizeof(buf), f)) {
+                    if (strstr(buf, "VID=256C") && strstr(buf, "PID=006D")) {
+                        snprintf(path, sizeof(path), "/dev/%s", ent->d_name);
+                        fclose(f);
+                        closedir(dir);
+                        int fd = open(path, O_RDWR);
+                        if (fd >= 0) {
+                            printf("Successfully opened device via hidraw\n");
+                            return fd;
+                        }
+                    }
+                }
+                fclose(f);
+            }
+        }
+    }
+    closedir(dir);
+    return -1;
+}
+
+// Print compatibility warning
+void print_compatibility_warning(void) {
+    printf("\n=========================================================\n");
+    printf("COMPATIBILITY WARNING\n");
+    printf("=========================================================\n");
+    printf("The hid_uclogic kernel module may interfere with this driver.\n");
+    printf("\nIf you're using OpenTabletDriver:\n");
+    printf("  • Keep enable_uclogic: false (default)\n");
+    printf("  • Unload hid_uclogic: sudo rmmod hid_uclogic\n");
+    printf("  • Blacklist it: echo 'blacklist hid_uclogic' | sudo tee /etc/modprobe.d/kd100-blacklist.conf\n");
+    printf("\nIf you need hid_uclogic for other tablet functions:\n");
+    printf("  • Set enable_uclogic: true in config\n");
+    printf("  • Driver will attempt to work around the module\n");
+    printf("=========================================================\n\n");
+}
diff --git a/src/compat.h b/src/compat.h
new file mode 100644
index 0000000..787ac57
--- /dev/null
+++ b/src/compat.h
@@ -0,0 +1,15 @@
+#ifndef COMPAT_H
+#define COMPAT_H
+
+// Compatibility functions for hid_uclogic and device access
+
+// Check if a kernel module is loaded
+int is_module_loaded(const char* module_name);
+
+// Try to access device via hidraw (alternative to libusb)
+int try_hidraw_access(void);
+
+// Print compatibility warning
+void print_compatibility_warning(void);
+
+#endif // COMPAT_H
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..70a1214
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,352 @@
+#include "config.h"
+#include "utils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <unistd.h>
+
+// Create a new configuration structure
+config_t* config_create(void) {
+    config_t* config = malloc(sizeof(config_t));
+    if (config == NULL) {
+        return NULL;
+    }
+
+    // Initialize events
+    config->events = malloc(1 * sizeof(event));
+    if (config->events == NULL) {
+        free(config);
+        return NULL;
+    }
+
+    // Initialize wheel events
+    config->wheelEvents = malloc(1 * sizeof(wheel));
+    if (config->wheelEvents == NULL) {
+        free(config->events);
+        free(config);
+        return NULL;
+    }
+
+    config->totalButtons = 0;
+    config->totalWheels = 0;
+    config->enable_uclogic = 0;
+
+    // Initialize leader state
+    config->leader.leader_button = -1;
+    config->leader.leader_active = 0;
+    config->leader.last_button = -1;
+    config->leader.leader_press_time.tv_sec = 0;
+    config->leader.leader_press_time.tv_usec = 0;
+    config->leader.leader_function = NULL;
+    config->leader.timeout_ms = 1000;  // 1 second default timeout
+    config->leader.mode = LEADER_MODE_ONE_SHOT;  // Default mode
+    config->leader.toggle_state = 0;
+
+    config->wheelEvents[0].right = NULL;
+    config->wheelEvents[0].left = NULL;
+
+    return config;
+}
+
+// Destroy configuration and free memory
+void config_destroy(config_t* config) {
+    if (config == NULL) return;
+
+    // Free button events
+    for (int i = 0; i < config->totalButtons; i++) {
+        if (config->events[i].function != NULL) {
+            free(config->events[i].function);
+        }
+    }
+    free(config->events);
+
+    // Free wheel events
+    for (int i = 0; i < config->totalWheels; i++) {
+        if (config->wheelEvents[i].right != NULL) {
+            free(config->wheelEvents[i].right);
+        }
+        if (config->wheelEvents[i].left != NULL) {
+            free(config->wheelEvents[i].left);
+        }
+    }
+    free(config->wheelEvents);
+
+    // Free leader function
+    if (config->leader.leader_function != NULL) {
+        free(config->leader.leader_function);
+    }
+
+    free(config);
+}
+
+// Load configuration from file
+int config_load(config_t* config, const char* filename, int debug) {
+    if (config == NULL || filename == NULL) {
+        return -1;
+    }
+
+    FILE* f = NULL;
+    int button = -1;
+    int wheelType = 0;
+    int leftWheels = 0;
+    int rightWheels = 0;
+    char data[512];
+
+    // Try to open config file
+    f = fopen(filename, "r");
+    if (f == NULL) {
+        // Try home directory
+        char* home = getpwuid(getuid())->pw_dir;
+        char* config_dir = "/.config/KD100/";
+        char temp[strlen(home) + strlen(config_dir) + strlen(filename) + 1];
+
+        strcpy(temp, home);
+        strcat(temp, config_dir);
+        strcat(temp, filename);
+
+        f = fopen(temp, "r");
+        if (f == NULL) {
+            printf("CONFIG FILE NOT FOUND: %s\n", filename);
+            return -1;
+        }
+    }
+
+    // Parse config file
+    while (fgets(data, sizeof(data), f) != NULL) {
+        data[strcspn(data, "\n")] = 0;
+
+        // Skip comments and empty lines
+        if (strstr(data, "//") == data || strlen(data) == 0) {
+            continue;
+        }
+
+        // Skip leading whitespace
+        char* line = data;
+        while (*line == ' ' || *line == '\t') line++;
+
+        // Parse enable_uclogic
+        if (strncasecmp(line, "enable_uclogic:", 15) == 0) {
+            char* value = line + 15;
+            while (*value == ' ') value++;
+            if (strncasecmp(value, "true", 4) == 0) {
+                config->enable_uclogic = 1;
+                if (debug) printf("Config: enable_uclogic = true\n");
+            } else if (strncasecmp(value, "false", 5) == 0) {
+                config->enable_uclogic = 0;
+                if (debug) printf("Config: enable_uclogic = false\n");
+            }
+            continue;
+        }
+
+        // Parse leader_button
+        if (strncasecmp(line, "leader_button:", 14) == 0) {
+            char* value = line + 14;
+            while (*value == ' ') value++;
+            config->leader.leader_button = atoi(value);
+            if (debug) printf("Config: leader_button = %d\n", config->leader.leader_button);
+            continue;
+        }
+
+        // Parse leader_function
+        if (strncasecmp(line, "leader_function:", 16) == 0) {
+            char* value = line + 16;
+            while (*value == ' ') value++;
+            config->leader.leader_function = strdup(value);
+            if (config->leader.leader_function != NULL) {
+                trim_trailing_spaces(config->leader.leader_function);
+            }
+            if (debug) printf("Config: leader_function = '%s'\n", config->leader.leader_function);
+            continue;
+        }
+
+        // Parse leader_timeout
+        if (strncasecmp(line, "leader_timeout:", 15) == 0) {
+            char* value = line + 15;
+            while (*value == ' ') value++;
+            config->leader.timeout_ms = atoi(value);
+            if (debug) printf("Config: leader_timeout = %d ms\n", config->leader.timeout_ms);
+            continue;
+        }
+
+        // Parse leader_mode
+        if (strncasecmp(line, "leader_mode:", 12) == 0) {
+            char* value = line + 12;
+            while (*value == ' ') value++;
+            config->leader.mode = parse_leader_mode(value);
+            if (debug) printf("Config: leader_mode = %s\n", leader_mode_to_string(config->leader.mode));
+            continue;
+        }
+
+        // Parse button
+        if (strncasecmp(line, "button ", 7) == 0) {
+            char* num_str = line + 7;
+            while (*num_str == ' ') num_str++;
+            button = atoi(num_str);
+
+            if (button >= config->totalButtons) {
+                event* temp = realloc(config->events, (button + 1) * sizeof(*config->events));
+                if (temp == NULL) {
+                    printf("Memory allocation failed!\n");
+                    fclose(f);
+                    return -1;
+                }
+                config->events = temp;
+                for (int j = config->totalButtons; j <= button; j++) {
+                    config->events[j].function = NULL;
+                    config->events[j].type = 0;
+                    config->events[j].leader_eligible = -1;  // Default: not set
+                }
+                config->totalButtons = button + 1;
+            }
+            continue;
+        }
+
+        // Parse type
+        if (strncasecmp(line, "type:", 5) == 0 && button != -1) {
+            char* type_str = line + 5;
+            while (*type_str == ' ') type_str++;
+            config->events[button].type = atoi(type_str);
+            continue;
+        }
+
+        // Parse leader_eligible
+        if (strncasecmp(line, "leader_eligible:", 16) == 0 && button != -1) {
+            char* value = line + 16;
+            while (*value == ' ') value++;
+            if (strcasecmp(value, "false") == 0) {
+                config->events[button].leader_eligible = 0;
+            } else if (strcasecmp(value, "true") == 0) {
+                config->events[button].leader_eligible = 1;
+            } else {
+                config->events[button].leader_eligible = atoi(value);
+            }
+            if (debug) printf("Config: button %d leader_eligible = %d\n", button, config->events[button].leader_eligible);
+            continue;
+        }
+
+        // Parse function
+        if (strncasecmp(line, "function:", 9) == 0) {
+            char* func_str = line + 9;
+            while (*func_str == ' ') func_str++;
+
+            char* func_copy = strdup(func_str);
+            if (func_copy == NULL) {
+                printf("Memory allocation failed!\n");
+                fclose(f);
+                return -1;
+            }
+
+            if (!wheelType) {
+                if (button >= 0 && button < config->totalButtons) {
+                    if (config->events[button].function != NULL) {
+                        free(config->events[button].function);
+                    }
+                    config->events[button].function = func_copy;
+                } else {
+                    free(func_copy);
+                    if (debug) printf("Warning: function without valid button definition\n");
+                }
+            } else if (wheelType == 1) {
+                if (rightWheels != 0) {
+                    wheel* temp = realloc(config->wheelEvents, (rightWheels + 1) * sizeof(*config->wheelEvents));
+                    if (temp == NULL) {
+                        printf("Memory allocation failed!\n");
+                        free(func_copy);
+                        fclose(f);
+                        return -1;
+                    }
+                    config->wheelEvents = temp;
+                    config->wheelEvents[rightWheels].right = func_copy;
+                    config->wheelEvents[rightWheels].left = NULL;
+                } else {
+                    config->wheelEvents[0].right = func_copy;
+                    config->wheelEvents[0].left = NULL;
+                }
+                rightWheels++;
+            } else {
+                if (leftWheels < rightWheels) {
+                    if (config->wheelEvents[leftWheels].left != NULL) {
+                        free(config->wheelEvents[leftWheels].left);
+                    }
+                    config->wheelEvents[leftWheels].left = func_copy;
+                } else {
+                    wheel* temp = realloc(config->wheelEvents, (leftWheels + 1) * sizeof(*config->wheelEvents));
+                    if (temp == NULL) {
+                        printf("Memory allocation failed!\n");
+                        free(func_copy);
+                        fclose(f);
+                        return -1;
+                    }
+                    config->wheelEvents = temp;
+                    config->wheelEvents[leftWheels].left = func_copy;
+                    config->wheelEvents[leftWheels].right = NULL;
+                }
+                leftWheels++;
+            }
+            continue;
+        }
+
+        // Parse wheel
+        if (strncasecmp(line, "wheel", 5) == 0) {
+            wheelType++;
+            continue;
+        }
+
+        if (debug > 1) {
+            printf("Skipping unrecognized line: %s\n", line);
+        }
+    }
+
+    fclose(f);
+
+    // Set default eligibility for buttons that weren't explicitly configured
+    for (int i = 0; i < config->totalButtons; i++) {
+        if (config->events[i].leader_eligible == -1) {
+            // Default: eligible for all buttons except wheel button (18)
+            config->events[i].leader_eligible = (i == 18) ? 0 : 1;
+        }
+    }
+
+    // Calculate total wheels
+    if (rightWheels > leftWheels)
+        config->totalWheels = rightWheels;
+    else
+        config->totalWheels = leftWheels;
+
+    return 0;
+}
+
+// Print configuration (for debugging)
+void config_print(const config_t* config, int debug) {
+    if (config == NULL || debug == 0) return;
+
+    printf("\n=== Button Configuration ===\n");
+    for (int i = 0; i < config->totalButtons; i++) {
+        if (config->events[i].function != NULL) {
+            printf("Button %2d: Type: %d | Function: '%s' | Leader Eligible: %s\n",
+                   i, config->events[i].type, config->events[i].function,
+                   config->events[i].leader_eligible == 1 ? "YES" :
+                   config->events[i].leader_eligible == 0 ? "NO" : "DEFAULT");
+        } else {
+            printf("Button %2d: Type: %d | Function: (not set) | Leader Eligible: %s\n",
+                   i, config->events[i].type,
+                   config->events[i].leader_eligible == 1 ? "YES" :
+                   config->events[i].leader_eligible == 0 ? "NO" : "DEFAULT");
+        }
+    }
+
+    printf("\n=== Wheel Configuration ===\n");
+    for (int i = 0; i < config->totalWheels; i++) {
+        printf("Wheel %d: Right: %s | Left: %s\n", i,
+               config->wheelEvents[i].right ? config->wheelEvents[i].right : "(null)",
+               config->wheelEvents[i].left ? config->wheelEvents[i].left : "(null)");
+    }
+
+    printf("\n=== Leader Configuration ===\n");
+    printf("Leader button: %d\n", config->leader.leader_button);
+    printf("Leader function: '%s'\n", config->leader.leader_function ? config->leader.leader_function : "(null)");
+    printf("Leader timeout: %d ms\n", config->leader.timeout_ms);
+    printf("Leader mode: %s\n", leader_mode_to_string(config->leader.mode));
+    printf("\n");
+}
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..4518373
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,28 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include "leader.h"
+
+// Wheel event structure
+typedef struct {
+    char* right;
+    char* left;
+} wheel;
+
+// Configuration structure
+typedef struct {
+    event* events;
+    int totalButtons;
+    wheel* wheelEvents;
+    int totalWheels;
+    leader_state leader;
+    int enable_uclogic;
+} config_t;
+
+// Configuration functions
+config_t* config_create(void);
+void config_destroy(config_t* config);
+int config_load(config_t* config, const char* filename, int debug);
+void config_print(const config_t* config, int debug);
+
+#endif // CONFIG_H
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..22cd763
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,402 @@
+#include "device.h"
+#include "config.h"
+#include "leader.h"
+#include "handler.h"
+#include "utils.h"
+#include "compat.h"
+#include <libusb-1.0/libusb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+void device_run(libusb_context* ctx, config_t* config, int debug, int accept, int dry) {
+    int err = 0;
+    int wheelFunction = 0;
+    int c = 0;
+    char indi[] = "|/-\\";
+    event prevEvent;
+    prevEvent.function = "";
+    prevEvent.type = 0;
+
+    system("clear");
+
+    if (debug > 0) {
+        if (debug > 2)
+            debug = 2;
+        printf("Version 1.5.0 - Modular Architecture\n");
+        printf("Debug level: %d\n", debug);
+    }
+
+    // Print configuration if debug
+    config_print(config, debug);
+
+    // Check module state
+    int uclogic_loaded = is_module_loaded("hid_uclogic");
+
+    if (debug) {
+        printf("Module status: hid_uclogic=%s\n",
+               uclogic_loaded ? "loaded" : "not loaded");
+        printf("Config: enable_uclogic=%s\n", config->enable_uclogic ? "true" : "false");
+    }
+
+    if (uclogic_loaded && !config->enable_uclogic) {
+        print_compatibility_warning();
+        printf("hid_uclogic is loaded but enable_uclogic is false.\n");
+        printf("Attempting alternative access methods...\n");
+    }
+
+    int devI = 0;
+    int use_hidraw_fallback = 0;
+
+    while (err == 0 || err == LIBUSB_ERROR_NO_DEVICE) {
+        libusb_device **devs;
+        libusb_device *dev;
+        struct libusb_config_descriptor *desc;
+        libusb_device_handle *handle = NULL;
+        int hidraw_fd = -1;
+
+        if (!config->enable_uclogic && uclogic_loaded && use_hidraw_fallback == 0) {
+            hidraw_fd = try_hidraw_access();
+            if (hidraw_fd >= 0) {
+                printf("Using hidraw interface (bypassing hid_uclogic)\n");
+                use_hidraw_fallback = 1;
+            } else {
+                printf("hidraw access failed, trying libusb with workarounds...\n");
+            }
+        }
+
+        err = libusb_get_device_list(ctx, &devs);
+        if (err < 0) {
+            printf("Unable to retrieve USB devices. Exiting...\n");
+            return;
+        }
+
+        int d = 0, found = 0;
+        devI = 0;
+        libusb_device *savedDevs[sizeof(devs)];
+
+        while ((dev = devs[d++]) != NULL) {
+            struct libusb_device_descriptor devDesc;
+            unsigned char info[200] = "";
+            err = libusb_get_device_descriptor(dev, &devDesc);
+
+            if (err < 0) {
+                if (debug > 0) {
+                    printf("Unable to retrieve info from device #%d. Ignoring...\n", d);
+                }
+            } else if (devDesc.idVendor == DEVICE_VID && devDesc.idProduct == DEVICE_PID) {
+                if (accept == 1) {
+                    if (getuid() != 0) {
+                        err = libusb_open(dev, &handle);
+                        if (err < 0) {
+                            if (err == LIBUSB_ERROR_ACCESS && !config->enable_uclogic) {
+                                printf("\nPermission denied - hid_uclogic may be claiming the device.\n");
+                                printf("Try: sudo rmmod hid_uclogic\n");
+                                printf("Or set enable_uclogic: true in config\n");
+                            }
+                            handle = NULL;
+                            if (err == LIBUSB_ERROR_ACCESS) {
+                                printf("Error: Permission denied\n");
+                                libusb_free_device_list(devs, 1);
+                                return;
+                            }
+                        }
+                        if (debug > 0) {
+                            printf("\nUsing: %04x:%04x (Bus: %03d Device: %03d)\n",
+                                   DEVICE_VID, DEVICE_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) {
+                            printf("\n#%d | %04x:%04x : %s\n", d, DEVICE_VID, DEVICE_PID, info);
+                        }
+                        if (strlen(info) == 0 || strcmp("Huion Tablet_KD100", info) == 0) {
+                            break;
+                        } else {
+                            libusb_close(handle);
+                            handle = NULL;
+                            found++;
+                        }
+                    }
+                } else {
+                    savedDevs[devI] = dev;
+                    devI++;
+                }
+            }
+        }
+
+        if (accept == 0) {
+            int in = -1;
+            while (in == -1) {
+                char buf[64];
+                printf("\n");
+                system("lsusb");
+                printf("\n");
+                for (d = 0; d < devI; d++) {
+                    printf("%d) %04x:%04x (Bus: %03d Device: %03d)\n", d,
+                           DEVICE_VID, DEVICE_PID,
+                           libusb_get_bus_number(savedDevs[d]),
+                           libusb_get_device_address(savedDevs[d]));
+                }
+                printf("Select a device to use: ");
+                fflush(stdout);
+                fgets(buf, 10, stdin);
+                in = atoi(buf);
+                if (in >= devI || in < 0) {
+                    in = -1;
+                }
+                system("clear");
+            }
+            err = libusb_open(savedDevs[in], &handle);
+            if (err < 0) {
+                printf("Unable to open device. Error: %d\n", err);
+                handle = NULL;
+                if (err == LIBUSB_ERROR_ACCESS) {
+                    printf("Error: Permission denied\n");
+                    if (!config->enable_uclogic) {
+                        printf("hid_uclogic may be claiming the device.\n");
+                        printf("Solutions:\n");
+                        printf("  1. Unload: sudo rmmod hid_uclogic\n");
+                        printf("  2. Set enable_uclogic: true in config\n");
+                        printf("  3. Run driver as root (not recommended)\n");
+                    }
+                    libusb_free_device_list(devs, 1);
+                    return;
+                }
+            }
+        } else if (found > 0) {
+            printf("Error: Found device does not appear to be the keydial\n");
+            printf("Try running without the -a flag\n");
+            libusb_free_device_list(devs, 1);
+            return;
+        }
+
+        int interfaces = 0;
+        if (handle == NULL && hidraw_fd < 0) {
+            printf("\rWaiting for a device %c", indi[c]);
+            fflush(stdout);
+            usleep(250000);
+            c++;
+            if (c == 4) {
+                c = 0;
+            }
+            err = LIBUSB_ERROR_NO_DEVICE;
+        } else {
+            if (debug == 0) {
+                system("clear");
+            }
+
+            if (hidraw_fd >= 0) {
+                printf("Starting driver via hidraw...\n");
+                printf("Driver is running!\n");
+
+                while (1) {
+                    unsigned char data[64];
+                    ssize_t bytes_read = read(hidraw_fd, data, sizeof(data));
+
+                    if (bytes_read < 0) {
+                        printf("Error reading from hidraw\n");
+                        break;
+                    }
+
+                    if (bytes_read > 0) {
+                        if (debug == 2 || dry) {
+                            printf("HIDRAW DATA: [%d", data[0]);
+                            for (int i = 1; i < bytes_read; i++) {
+                                printf(", %d", data[i]);
+                            }
+                            printf("]\n");
+                        }
+                    }
+
+                    usleep(10000);
+                }
+
+                close(hidraw_fd);
+                return;
+            } else {
+                interfaces = 0;
+                printf("Starting driver via libusb...\n");
+
+                dev = libusb_get_device(handle);
+                libusb_get_config_descriptor(dev, 0, &desc);
+                interfaces = desc->bNumInterfaces;
+                libusb_free_config_descriptor(desc);
+
+                if (config->enable_uclogic) {
+                    libusb_set_auto_detach_kernel_driver(handle, 1);
+                    if (debug == 1)
+                        printf("Using auto-detach (hid_uclogic compatible mode)\n");
+                } else {
+                    for (int x = 0; x < interfaces; x++) {
+                        if (libusb_kernel_driver_active(handle, x) == 1) {
+                            printf("Detaching kernel driver from interface %d...\n", x);
+                            err = libusb_detach_kernel_driver(handle, x);
+                            if (err != 0 && debug == 1) {
+                                printf("Failed to detach kernel driver: %s\n", libusb_error_name(err));
+                            }
+                        }
+                    }
+                }
+
+                if (debug == 1)
+                    printf("Claiming interfaces... \n");
+
+                for (int x = 0; x < interfaces; x++) {
+                    int err = libusb_claim_interface(handle, x);
+                    if (err != LIBUSB_SUCCESS && debug == 1)
+                        printf("Failed to claim interface %d: %s\n", x, libusb_error_name(err));
+                }
+
+                printf("Driver is running!\n");
+                printf("Enhanced Leader Key System v1.5.0\n");
+                printf("Mode: %s | Timeout: %d ms\n",
+                       leader_mode_to_string(config->leader.mode), config->leader.timeout_ms);
+                printf("Press leader button first, then eligible buttons for combinations.\n");
+
+                err = 0;
+
+                while (err >= 0) {
+                    unsigned char data[40];
+                    int keycode = 0;
+                    err = libusb_interrupt_transfer(handle, 0x81, data, sizeof(data), NULL, 0);
+
+                    if (err == LIBUSB_ERROR_TIMEOUT)
+                        printf("\nTIMEDOUT\n");
+                    if (err == LIBUSB_ERROR_PIPE)
+                        printf("\nPIPE ERROR\n");
+                    if (err == LIBUSB_ERROR_NO_DEVICE)
+                        printf("\nDEVICE DISCONNECTED\n");
+                    if (err == LIBUSB_ERROR_OVERFLOW)
+                        printf("\nOVERFLOW ERROR\n");
+                    if (err == LIBUSB_ERROR_INVALID_PARAM)
+                        printf("\nINVALID PARAMETERS\n");
+                    if (err == -1)
+                        printf("\nDEVICE IS ALREADY IN USE\n");
+                    if (err < 0) {
+                        if (debug == 1) {
+                            printf("Unable to retrieve data: %d\n", err);
+                        }
+                        break;
+                    }
+
+                    // Convert data to keycodes
+                    if (data[4] != 0)
+                        keycode = data[4];
+                    else if (data[5] != 0)
+                        keycode = data[5] + 128;
+                    else if (data[6] != 0)
+                        keycode = data[6] + 256;
+                    if (data[1] == 241)
+                        keycode += 512;
+                    if (dry)
+                        keycode = 0;
+
+                    if (debug == 1 && keycode != 0) {
+                        printf("Keycode: %d\n", keycode);
+                    }
+
+                    // Handle wheel events
+                    if (keycode == 641) {
+                        if (wheelFunction >= 0 && wheelFunction < config->totalWheels &&
+                            config->wheelEvents[wheelFunction].right != NULL) {
+                            Handler(config->wheelEvents[wheelFunction].right, -1, debug);
+                        }
+                    } else if (keycode == 642) {
+                        if (wheelFunction >= 0 && wheelFunction < config->totalWheels &&
+                            config->wheelEvents[wheelFunction].left != NULL) {
+                            Handler(config->wheelEvents[wheelFunction].left, -1, debug);
+                        }
+                    } else {
+                        int button_index = find_button_index(keycode);
+
+                        if (button_index != -1) {
+                            // Process button press with leader system
+                            process_leader_combination(&config->leader, config->events, button_index, debug);
+
+                            // Also handle legacy single-button events for compatibility
+                            if (config->events[button_index].function != NULL) {
+                                if (strcmp(config->events[button_index].function, "NULL") == 0) {
+                                    if (prevEvent.type != 0) {
+                                        Handler(prevEvent.function, prevEvent.type, debug);
+                                        prevEvent.type = 0;
+                                        prevEvent.function = "";
+                                    }
+                                } else if (strcmp(config->events[button_index].function, "swap") == 0) {
+                                    if (wheelFunction != config->totalWheels - 1) {
+                                        wheelFunction++;
+                                    } else
+                                        wheelFunction = 0;
+                                    if (debug == 1) {
+                                        printf("Function: %s | %s\n",
+                                               config->wheelEvents[wheelFunction].left ? config->wheelEvents[wheelFunction].left : "(null)",
+                                               config->wheelEvents[wheelFunction].right ? config->wheelEvents[wheelFunction].right : "(null)");
+                                    }
+                                } else if (strcmp(config->events[button_index].function, "mouse1") == 0 ||
+                                           strcmp(config->events[button_index].function, "mouse2") == 0 ||
+                                           strcmp(config->events[button_index].function, "mouse3") == 0 ||
+                                           strcmp(config->events[button_index].function, "mouse4") == 0 ||
+                                           strcmp(config->events[button_index].function, "mouse5") == 0) {
+                                    if (strcmp(config->events[button_index].function, prevEvent.function)) {
+                                        if (prevEvent.type != 0) {
+                                            Handler(prevEvent.function, prevEvent.type, debug);
+                                        }
+                                        prevEvent.function = config->events[button_index].function;
+                                        prevEvent.type = 3;
+                                    }
+                                    Handler(config->events[button_index].function, 2, debug);
+                                }
+                            }
+                        }
+                    }
+
+                    if (debug == 2 || dry) {
+                        printf("DATA: [%d", data[0]);
+                        for (int i = 1; i < sizeof(data); i++) {
+                            printf(", %d", data[i]);
+                        }
+                        printf("]\n");
+
+                        if (config->leader.toggle_state) {
+                            printf("Leader toggle: ON (mode: %s)\n", leader_mode_to_string(config->leader.mode));
+                        } else if (config->leader.leader_active) {
+                            struct timeval now;
+                            gettimeofday(&now, NULL);
+                            long elapsed = time_diff_ms(config->leader.leader_press_time, now);
+                            printf("Leader active: YES (%ld ms elapsed, mode: %s)\n",
+                                   elapsed, leader_mode_to_string(config->leader.mode));
+                        } else {
+                            printf("Leader active: NO\n");
+                        }
+                    }
+
+                    // Small delay to prevent CPU hogging
+                    usleep(10000);
+                }
+
+                // Cleanup
+                for (int x = 0; x < interfaces; x++) {
+                    if (debug == 1) {
+                        printf("Releasing interface %d...\n", x);
+                    }
+                    libusb_release_interface(handle, x);
+                }
+                printf("Closing device...\n");
+                libusb_close(handle);
+                interfaces = 0;
+                sleep(1);
+            }
+        }
+        libusb_free_device_list(devs, 1);
+    }
+}
diff --git a/src/device.h b/src/device.h
new file mode 100644
index 0000000..385e6f7
--- /dev/null
+++ b/src/device.h
@@ -0,0 +1,14 @@
+#ifndef DEVICE_H
+#define DEVICE_H
+
+#include <libusb-1.0/libusb.h>
+#include "config.h"
+
+// Device identifiers
+#define DEVICE_VID 0x256c
+#define DEVICE_PID 0x006d
+
+// Device management functions
+void device_run(libusb_context* ctx, config_t* config, int debug, int accept, int dry);
+
+#endif // DEVICE_H
diff --git a/src/handler.c b/src/handler.c
new file mode 100644
index 0000000..71a9ca6
--- /dev/null
+++ b/src/handler.c
@@ -0,0 +1,65 @@
+#include "handler.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void Handler(char* key, int type, int debug) {
+    if (key == NULL) {
+        if (debug == 1) printf("Handler called with NULL key\n");
+        return;
+    }
+
+    if (strcmp(key, "NULL") == 0) {
+        return;
+    }
+
+    char* cmd = NULL;
+    int is_mouse = 0;
+    char mouse_char = '1';
+
+    switch(type) {
+        case -1:
+            cmd = "xdotool key ";
+            break;
+        case 0:
+            cmd = "xdotool keydown ";
+            break;
+        case 1:
+            cmd = "xdotool keyup ";
+            break;
+        case 2:
+            is_mouse = 1;
+            cmd = "xdotool mousedown ";
+            break;
+        case 3:
+            is_mouse = 1;
+            cmd = "xdotool mouseup ";
+            break;
+        default:
+            if (debug == 1) printf("Handler called with unknown type: %d\n", type);
+            return;
+    }
+
+    if (cmd == NULL) {
+        return;
+    }
+
+    if (is_mouse) {
+        if (strlen(key) >= 6 && strncmp(key, "mouse", 5) == 0) {
+            mouse_char = key[5];
+            if (mouse_char < '1' || mouse_char > '5') {
+                mouse_char = '1';
+            }
+        }
+
+        char temp[strlen(cmd) + 3];
+        snprintf(temp, sizeof(temp), "%s %c", cmd, mouse_char);
+        if (debug == 1) printf("Executing: %s\n", temp);
+        system(temp);
+    } else {
+        char temp[strlen(cmd) + strlen(key) + 1];
+        snprintf(temp, sizeof(temp), "%s%s", cmd, key);
+        if (debug == 1) printf("Executing: %s\n", temp);
+        system(temp);
+    }
+}
diff --git a/src/handler.h b/src/handler.h
new file mode 100644
index 0000000..4f42473
--- /dev/null
+++ b/src/handler.h
@@ -0,0 +1,8 @@
+#ifndef HANDLER_H
+#define HANDLER_H
+
+// Event handler function
+// type: -1 = full key press, 0 = key down, 1 = key up, 2 = mouse down, 3 = mouse up
+void Handler(char* key, int type, int debug);
+
+#endif // HANDLER_H
diff --git a/src/leader.c b/src/leader.c
new file mode 100644
index 0000000..9dde16d
--- /dev/null
+++ b/src/leader.c
@@ -0,0 +1,198 @@
+#include "leader.h"
+#include "utils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+// Forward declaration of Handler (implemented in handler module)
+void Handler(char* key, int type, int debug);
+
+// 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;
+    // Don't reset toggle_state here - it's managed separately
+}
+
+// Send leader combination
+void send_leader_combination(leader_state* state, char* combination, int debug) {
+    if (combination == NULL || strlen(combination) == 0) {
+        return;
+    }
+
+    if (debug == 1) {
+        printf("Sending leader combination: %s\n", combination);
+    }
+
+    // Send the combination
+    char cmd[512];
+    snprintf(cmd, sizeof(cmd), "xdotool key %s", combination);
+    system(cmd);
+
+    // 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
+void process_leader_combination(leader_state* state, event* events, int button_index, int debug) {
+    if (button_index < 0 || button_index >= 19) {
+        return;
+    }
+
+    // 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 &&
+            strcmp(events[button_index].function, "swap") == 0) {
+            // Handle swap function
+            // (This would need wheel state tracking - implemented elsewhere)
+        }
+        // Don't reset leader state for wheel button in toggle mode
+        if (state->mode != LEADER_MODE_TOGGLE) {
+            reset_leader_state(state);
+        }
+        return;
+    }
+
+    if (events[button_index].function == NULL) {
+        return;
+    }
+
+    char* button_func = events[button_index].function;
+
+    // Check if this button is configured as a leader
+    if (strcmp(button_func, "leader") == 0) {
+        // This button is the leader itself
+        if (state->mode == LEADER_MODE_TOGGLE) {
+            // Toggle mode: press to enable, press again to disable
+            if (!state->toggle_state) {
+                // Enable toggle mode
+                state->toggle_state = 1;
+                state->leader_active = 1;
+                gettimeofday(&state->leader_press_time, NULL);
+                state->last_button = button_index;
+
+                if (debug == 1) {
+                    printf("Leader toggle mode ENABLED by button %d\n", button_index);
+                }
+            } else {
+                // 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);
+                }
+            }
+        } else {
+            // One-shot or sticky mode
+            if (!state->leader_active) {
+                // Activate leader mode
+                state->leader_active = 1;
+                gettimeofday(&state->leader_press_time, NULL);
+                state->last_button = button_index;
+
+                if (debug == 1) {
+                    printf("Leader mode activated by button %d\n", button_index);
+                }
+            } else {
+                // Leader pressed again - cancel leader mode
+                reset_leader_state(state);
+                if (debug == 1) {
+                    printf("Leader mode cancelled\n");
+                }
+            }
+        }
+        return;
+    }
+
+    // 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
+            if (debug == 1) {
+                printf("Button %d not eligible for leader - handling normally\n", button_index);
+            }
+
+            // For sticky mode, we might want to keep leader active
+            if (state->mode != LEADER_MODE_STICKY && state->mode != LEADER_MODE_TOGGLE) {
+                reset_leader_state(state);
+            }
+
+            // Fall through to normal button handling
+        } else {
+            // Button is eligible for leader modifications
+            // Calculate time since leader was pressed (for timeout)
+            struct timeval now;
+            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!
+                // Format: leader_function + button_function
+                char combination[256] = "";
+
+                if (state->leader_function != NULL && strlen(state->leader_function) > 0) {
+                    strcpy(combination, state->leader_function);
+                    strcat(combination, "+");
+                }
+
+                strcat(combination, button_func);
+
+                // Send the combination
+                send_leader_combination(state, combination, debug);
+
+                // For sticky mode, update the timer to extend timeout
+                if (state->mode == LEADER_MODE_STICKY) {
+                    gettimeofday(&state->leader_press_time, NULL);
+                }
+
+                return; // Don't also handle as normal button
+            }
+        }
+    }
+
+    // Not in leader mode, leader timed out, or button not eligible - handle normal button press
+    if (strcmp(button_func, "NULL") != 0 && strcmp(button_func, "swap") != 0 &&
+        strcmp(button_func, "mouse1") != 0 && strcmp(button_func, "mouse2") != 0 &&
+        strcmp(button_func, "mouse3") != 0 && strcmp(button_func, "mouse4") != 0 &&
+        strcmp(button_func, "mouse5") != 0) {
+
+        if (events[button_index].type == 0) {
+            // Type 0: Key press
+            Handler(button_func, 0, debug);
+            usleep(10000); // Small delay
+            Handler(button_func, 1, debug);
+        } else if (events[button_index].type == 1) {
+            // Type 1: Run program/script
+            system(button_func);
+        }
+    }
+}
diff --git a/src/leader.h b/src/leader.h
new file mode 100644
index 0000000..f8fefc0
--- /dev/null
+++ b/src/leader.h
@@ -0,0 +1,34 @@
+#ifndef LEADER_H
+#define LEADER_H
+
+#include <sys/time.h>
+#include "utils.h"
+
+// Forward declaration
+typedef struct event event;
+
+// Leader state structure
+typedef struct {
+    int leader_button;            // Which button is the leader (e.g., Button 16 for shift)
+    int leader_active;            // Is leader mode active?
+    int last_button;              // Last button pressed (for timing)
+    struct timeval leader_press_time; // When was leader pressed?
+    char* leader_function;        // What function does the leader have?
+    int timeout_ms;               // Leader timeout in milliseconds
+    leader_mode_t mode;           // Leader mode (one_shot, sticky, toggle)
+    int toggle_state;             // For toggle mode: 0 = off, 1 = on
+} leader_state;
+
+// Event structure (button configuration)
+struct event {
+    int type;
+    char* function;
+    int leader_eligible;  // 0 = not eligible, 1 = eligible, -1 = not set (default eligible)
+};
+
+// Leader key functions
+void reset_leader_state(leader_state* state);
+void send_leader_combination(leader_state* state, char* combination, int debug);
+void process_leader_combination(leader_state* state, event* events, int button_index, int debug);
+
+#endif // LEADER_H
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..513d430
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,269 @@
+/*
+	V1.5.0 - Modular Architecture
+	https://github.com/mckset/KD100.git
+	KD100 Linux driver for X11 desktops
+	Features:
+	- Modular code structure for better maintainability
+	- Configurable leader modes (one_shot, sticky, toggle)
+	- Per-button leader eligibility
+	- Fixed timing system
+*/
+
+#include <libusb-1.0/libusb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "device.h"
+#include "compat.h"
+
+/* ===== CRASH HANDLER ===== */
+#ifdef DEBUG
+#include <execinfo.h>
+#include <signal.h>
+#include <dlfcn.h>
+
+static void* get_base_address(void) {
+    FILE *maps = fopen("/proc/self/maps", "r");
+    if (!maps) return NULL;
+
+    char line[256];
+    void *base_addr = NULL;
+
+    while (fgets(line, sizeof(line), maps)) {
+        if (strstr(line, "r-xp") && strstr(line, "KD100-debug")) {
+            unsigned long start;
+            sscanf(line, "%lx", &start);
+            base_addr = (void*)start;
+            break;
+        }
+    }
+
+    fclose(maps);
+    return base_addr;
+}
+
+void crash_handler(int sig) {
+    void *array[50];
+    size_t size;
+    char **strings;
+
+    fprintf(stderr, "\n⚠️  PROGRAM CRASHED! Signal: %d\n", sig);
+    fprintf(stderr, "════════════════════════════════════════\n");
+
+    size = backtrace(array, 50);
+    strings = backtrace_symbols(array, size);
+
+    if (strings != NULL) {
+        fprintf(stderr, "Stack trace (%zu frames):\n", size);
+        for (size_t i = 0; i < size; i++) {
+            fprintf(stderr, "  #%zu: %s\n", i, strings[i]);
+        }
+        free(strings);
+    }
+
+    char exe_path[1024];
+    ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path)-1);
+    if (len == -1) {
+        strcpy(exe_path, "./KD100-debug");
+    } else {
+        exe_path[len] = '\0';
+    }
+
+    void *base_addr = get_base_address();
+
+    fprintf(stderr, "\n=== Resolving line numbers ===\n");
+    fprintf(stderr, "Executable: %s\n", exe_path);
+    fprintf(stderr, "Base address: %p\n", base_addr);
+
+    for (size_t i = 0; i < size; i++) {
+        void *file_offset = array[i];
+        if (base_addr) {
+            file_offset = (void*)((unsigned long)array[i] - (unsigned long)base_addr);
+        }
+
+        fprintf(stderr, "\nFrame #%zu:\n", i);
+        fprintf(stderr, "  Runtime address: %p\n", array[i]);
+        fprintf(stderr, "  File offset:     %p\n", file_offset);
+
+        char cmd[512];
+        snprintf(cmd, sizeof(cmd), "addr2line -e '%s' -f -C -p %p 2>&1",
+                exe_path, file_offset);
+
+        FILE *fp = popen(cmd, "r");
+        if (fp) {
+            char line[256];
+            int has_output = 0;
+            while (fgets(line, sizeof(line), fp)) {
+                line[strcspn(line, "\n")] = 0;
+                if (strstr(line, "??") == NULL) {
+                    fprintf(stderr, "  %s\n", line);
+                    has_output = 1;
+                }
+            }
+            pclose(fp);
+
+            if (!has_output) {
+                fprintf(stderr, "  Could not resolve (trying raw address)\n");
+                snprintf(cmd, sizeof(cmd), "addr2line -e '%s' -f -C -p %p 2>&1",
+                        exe_path, array[i]);
+                system(cmd);
+            }
+        }
+    }
+
+    fprintf(stderr, "\n=== Using gdb to get line info ===\n");
+    char gdb_cmd[512];
+    snprintf(gdb_cmd, sizeof(gdb_cmd),
+            "gdb -q '%s' -ex 'info line *%p' -ex 'quit' 2>/dev/null | grep -E \"Line|at\"",
+            exe_path, array[3]);
+    system(gdb_cmd);
+
+    fprintf(stderr, "\n════════════════════════════════════════\n");
+    exit(1);
+}
+
+void setup_crash_handler(void) {
+    signal(SIGSEGV, crash_handler);
+    signal(SIGABRT, crash_handler);
+    signal(SIGBUS, crash_handler);
+    signal(SIGILL, crash_handler);
+    signal(SIGFPE, crash_handler);
+    signal(SIGTERM, crash_handler);
+}
+#endif
+
+// Global keycodes array (used by utils module)
+int keycodes[] = {1, 2, 4, 8, 16, 32, 64, 128, 129, 130, 132, 136, 144, 160, 192, 256, 257, 258, 260, 641, 642};
+
+int main(int args, char *in[]) {
+#ifdef DEBUG
+    setup_crash_handler();
+    printf("Debug mode enabled. Crash handler active.\n");
+#endif
+
+    int debug = 0, accept = 0, dry = 0, err;
+    char* file = "default.cfg";
+    int enable_uclogic = 0;
+
+    // Check for xdotool
+    err = system("xdotool sleep 0.01");
+    if (err != 0) {
+        printf("xdotool not found. Please install xdotool for key simulation.\n");
+        printf("Exiting...\n");
+        return -9;
+    }
+
+    // Parse command-line arguments
+    for (int arg = 1; arg < args; arg++) {
+        if (strcmp(in[arg], "-h") == 0 || strcmp(in[arg], "--help") == 0) {
+            printf("Usage: KD100 [option]...\n");
+            printf("\t-a\t\tAssume the first device that matches %04x:%04x is the Keydial\n", DEVICE_VID, DEVICE_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");
+            printf("\nNew in v1.5.0 - MODULAR ARCHITECTURE:\n");
+            printf("\t• Separated code into focused modules for better maintainability\n");
+            printf("\t• Improved code organization and testability\n");
+            printf("\t• Retained all v1.4.9 features:\n");
+            printf("\t  - Configurable leader modes (one_shot, sticky, toggle)\n");
+            printf("\t  - Per-button eligibility control\n");
+            printf("\t  - Enhanced timing system\n");
+            printf("\t• Example config in default.cfg:\n");
+            printf("\t  leader_button: 16\n");
+            printf("\t  leader_function: shift\n");
+            printf("\t  leader_timeout: 1000\n");
+            printf("\t  leader_mode: sticky\n");
+            printf("\t  button 0\n");
+            printf("\t  type: 0\n");
+            printf("\t  function: b\n");
+            printf("\t  leader_eligible: true\n");
+            printf("\t  button 18\n");
+            printf("\t  type: 1\n");
+            printf("\t  function: swap\n");
+            printf("\t  leader_eligible: false\n");
+            printf("\nConfiguration:\n");
+            printf("\tAdd 'enable_uclogic: true' to config to work with hid_uclogic loaded\n");
+            printf("\tDefault: enable_uclogic: false (compatible with OpenTabletDriver)\n\n");
+            return 0;
+        }
+        if (strcmp(in[arg], "-d") == 0) {
+            debug++;
+        }
+        if (strcmp(in[arg], "-dry") == 0) {
+            dry = 1;
+        }
+        if (strcmp(in[arg], "-a") == 0) {
+            accept = 1;
+        }
+        if (strcmp(in[arg], "-c") == 0) {
+            if (in[arg + 1]) {
+                file = in[arg + 1];
+                arg++;
+            } else {
+                printf("No config file specified. Exiting...\n");
+                return -8;
+            }
+        }
+        if (strcmp(in[arg], "--uclogic") == 0) {
+            enable_uclogic = 1;
+            printf("Forcing hid_uclogic compatibility mode\n");
+        }
+        if (strcmp(in[arg], "--no-uclogic") == 0) {
+            enable_uclogic = 0;
+            printf("Disabling hid_uclogic compatibility (OpenTabletDriver mode)\n");
+        }
+    }
+
+    // Initialize libusb
+    libusb_context *ctx = NULL;
+    err = libusb_init(&ctx);
+    if (err < 0) {
+        printf("Error initializing libusb: %d\n", err);
+        return err;
+    }
+
+    // Load configuration
+    config_t* config = config_create();
+    if (config == NULL) {
+        printf("Failed to create configuration\n");
+        libusb_exit(ctx);
+        return -1;
+    }
+
+    if (config_load(config, file, debug) < 0) {
+        printf("Failed to load configuration from %s\n", file);
+        config_destroy(config);
+        libusb_exit(ctx);
+        return -1;
+    }
+
+    // Override enable_uclogic from command line if specified
+    if (enable_uclogic) {
+        config->enable_uclogic = 1;
+    }
+
+    // Print startup information
+    if (config->enable_uclogic) {
+        printf("Mode: hid_uclogic compatibility enabled\n");
+    } else {
+        printf("Mode: OpenTabletDriver compatible (hid_uclogic disabled)\n");
+        if (is_module_loaded("hid_uclogic")) {
+            print_compatibility_warning();
+        }
+    }
+
+    printf("\nKD100 Driver v1.5.0 - Modular Architecture\n");
+    printf("Features: Configurable modes | Per-button eligibility | Modular design\n\n");
+
+    // Run device handler
+    device_run(ctx, config, debug, accept, dry);
+
+    // Cleanup
+    config_destroy(config);
+    libusb_exit(ctx);
+    return 0;
+}
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..8f7eeb0
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,122 @@
+#include "utils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+// External keycode array (defined in main)
+extern int keycodes[];
+
+// Get current time in milliseconds
+long get_time_ms(void) {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+}
+
+// Calculate time difference in milliseconds
+long time_diff_ms(struct timeval start, struct timeval end) {
+    long sec_diff = end.tv_sec - start.tv_sec;
+    long usec_diff = end.tv_usec - start.tv_usec;
+    return (sec_diff * 1000) + (usec_diff / 1000);
+}
+
+// Trim trailing spaces from a string
+void trim_trailing_spaces(char* str) {
+    if (str == NULL) return;
+
+    int len = strlen(str);
+    while (len > 0 && isspace((unsigned char)str[len - 1])) {
+        str[len - 1] = '\0';
+        len--;
+    }
+}
+
+// Parse leader mode string
+leader_mode_t parse_leader_mode(const char* mode_str) {
+    if (mode_str == NULL) return LEADER_MODE_ONE_SHOT;
+
+    if (strcasecmp(mode_str, "sticky") == 0) {
+        return LEADER_MODE_STICKY;
+    } else if (strcasecmp(mode_str, "toggle") == 0) {
+        return LEADER_MODE_TOGGLE;
+    } else if (strcasecmp(mode_str, "one_shot") == 0 || strcasecmp(mode_str, "oneshot") == 0) {
+        return LEADER_MODE_ONE_SHOT;
+    }
+
+    return LEADER_MODE_ONE_SHOT; // Default
+}
+
+// Convert leader mode to string
+const char* leader_mode_to_string(leader_mode_t mode) {
+    switch (mode) {
+        case LEADER_MODE_ONE_SHOT: return "one_shot";
+        case LEADER_MODE_STICKY: return "sticky";
+        case LEADER_MODE_TOGGLE: return "toggle";
+        default: return "unknown";
+    }
+}
+
+// Find button index from keycode
+int find_button_index(int keycode) {
+    for (int k = 0; k < 19; k++) {  // Only 0-17 for buttons, 18 is wheel button
+        if (keycodes[k] == keycode) {
+            return k;
+        }
+    }
+    return -1;
+}
+
+// Check if a key is a modifier
+int is_modifier_key(const char* key) {
+    if (key == NULL) return 0;
+
+    if (strcmp(key, "ctrl") == 0 || strcmp(key, "control") == 0 ||
+        strcmp(key, "shift") == 0 ||
+        strcmp(key, "alt") == 0 ||
+        strcmp(key, "super") == 0 || strcmp(key, "meta") == 0) {
+        return 1;
+    }
+    return 0;
+}
+
+// Substring function
+char* Substring(const char* in, int start, int end) {
+    if (in == NULL) {
+        char* out = malloc(1);
+        if (out) out[0] = '\0';
+        return out;
+    }
+
+    int len = strlen(in);
+
+    if (start < 0) start = 0;
+    if (start >= len) {
+        char* out = malloc(1);
+        if (out) out[0] = '\0';
+        return out;
+    }
+
+    if (end <= 0) {
+        char* out = malloc(1);
+        if (out) out[0] = '\0';
+        return out;
+    }
+
+    if (start + end > len) {
+        end = len - start;
+    }
+
+    char* out = malloc(end + 1);
+    if (out == NULL) {
+        return NULL;
+    }
+
+    for (int i = 0; i < end; i++) {
+        out[i] = in[start + i];
+    }
+    out[end] = '\0';
+
+    return out;
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..c0606fa
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,29 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <sys/time.h>
+
+// Leader mode enumeration
+typedef enum {
+    LEADER_MODE_ONE_SHOT,    // Leader + 1 key = combination, then reset (default)
+    LEADER_MODE_STICKY,      // Leader stays active for multiple keys until timeout
+    LEADER_MODE_TOGGLE       // Leader toggles on/off (press to enable, press again to disable)
+} leader_mode_t;
+
+// Time utilities
+long get_time_ms(void);
+long time_diff_ms(struct timeval start, struct timeval end);
+
+// String utilities
+void trim_trailing_spaces(char* str);
+char* Substring(const char* in, int start, int end);
+
+// Leader mode utilities
+leader_mode_t parse_leader_mode(const char* mode_str);
+const char* leader_mode_to_string(leader_mode_t mode);
+
+// Button utilities
+int find_button_index(int keycode);
+int is_modifier_key(const char* key);
+
+#endif // UTILS_H