commit d077fbf833ed0ffb79f43ffe53bb198cad65d9ad Author: Igor Kolokolnikov Date: Tue Sep 15 20:39:55 2026 -0700 Initial commit. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..edfd2ef --- /dev/null +++ b/Makefile @@ -0,0 +1,89 @@ +UNAME_M := $(shell uname -m) + +ifeq ($(UNAME_M),x86_64) + ARCH := ARCHITECTURE_X64 +else ifneq ($(filter $(UNAME_M),i686,i386),) + ARCH := ARCHITECTURE_X86 +else ifneq ($(filter $(UNAME_M),aarch64 arm64),) + ARCH := ARCHITECTURE_ARM64 +else ifneq ($(filter $(UNAME_M),armv7l armv6l),) + ARCH := ARCHITECTURE_ARM32 +endif + +UNAME_E := $(shell [ $$(printf '\01\00' | od -An -tu2) -eq 1 ] && \ + echo "little" || echo "big") + +ifeq ($(UNAME_E),little) + ENDIANESS := LITTLE_ENDIAN +else ifeq ($(UNAME_E),little) + ENDIANESS := BIG_ENDIAN +endif + +UNAME_S := $(shell uname -s) + +ifeq ($(UNAME_S),Darwin) + OS := OS_MACOS +else ifeq ($(UNAME_S),Linux) + OS := OS_LINUX +endif + +CC = gcc + +ifeq ($(CC),clang) + COMP := COMPILER_CLANG +else ifeq ($(CC),gcc) + COMP := COMPILER_GCC +endif + +# -ansi -pedantic -pedantic-errors +CFLAGS = -Wno-long-long -Wall -Wextra \ + -Wincompatible-pointer-types \ + -Wint-conversion -Wformat \ + -Wno-strict-prototypes -Wno-missing-field-initializers \ + -Wno-unused-function -Wno-unused-parameter -Wno-unused-variable \ + -Wno-unused-but-set-variable \ + -fno-show-column + +ifeq ($(COMP),COMPILER_CLANG) + CFLAGS += -fno-caret-diagnostics +else ifeq ($(COMP),COMPILER_GCC) + CFLAGS += -fno-diagnostics-show-caret +endif + +ifeq ($(OS),OS_MACOS) + FLAGS = -glldb + INCLUDE = + LIBS = + FRAMEWORKS = +else ifeq ($(OS),OS_LINUX) + FLAGS = -O3 #-ggdb -DDEBUG=1 -DINTERNAL=1 + INCLUDE = + LIBS = + FRAMEWORKS = +endif + +EXE = hid_apple_fn_lock + +default: $(EXE) + +$(EXE): $(EXE).c + $(CC) $(CFLAGS) $(FLAGS) \ + -D$(ARCH)=1 -D$(ENDIANESS)=1 -D$(OS)=1 -D$(COMP)=1 \ + -D_DEFAULT_SOURCE -DHAVE_SHADOW_H \ + $(INCLUDE) $^ $(LIBS) \ + $(FRAMEWORKS) -o $@ $(ifneq $(filter $(OS), OS_MACOS OS_LINUX), \ + /link /FORCE:MULTIPLE) + +watch: + @ while true; do \ + inotifywait -q -q -e 'modify' . && clear && make -B; \ + done + +perf: + perf record --call-graph dwarf ./$(EXE) + +run: + @ clear; ./$(EXE) + +clean: + @ rm -rf $(EXE) $(EXE).dSYM diff --git a/hid_apple_fn_lock.c b/hid_apple_fn_lock.c new file mode 100644 index 0000000..b129ee1 --- /dev/null +++ b/hid_apple_fn_lock.c @@ -0,0 +1,149 @@ +#include /* access */ +#include /* exit */ +#include +#include +#include +#include +#include /* strcmp */ + +/* Kernel module entry for the Apple Human Interface Device driver. */ +#define APPLE_HID_PATH "/sys/module/hid_apple" + +char file_exists(const char *filename) +{ + return access(filename, F_OK) != -1; +} + +int init_apple_hid_module() +{ + int fd; + + if(!file_exists(APPLE_HID_PATH)) { + syslog(LOG_WARNING, "Apple hid module is unloaded."); + return -1; + } + + fd = open(APPLE_HID_PATH"/parameters/fnmode", O_RDWR); + if(fd == -1) { + syslog(LOG_WARNING, "Failed to open %s.", + APPLE_HID_PATH"/parameters/fnmode"); + return -1; + } + + return fd; +} + +int get_fn_lock() +{ + int fd; + char state; + + fd = init_apple_hid_module(); + if(fd != -1) { + if(read(fd, &state, 1) != 1) + state = '0'; + close(fd); + } + + return state == '2'; +} + +void set_fn_lock_on() +{ + int fd; + + fd = init_apple_hid_module(); + if(fd != -1) { + if(write(fd, "2", 1) != 1) + syslog(LOG_WARNING, "Failed to write to Apple HID driver."); + else + syslog(LOG_INFO, "Fn lock is on."); + close(fd); + } +} + +void set_fn_lock_off() +{ + int fd = init_apple_hid_module(); + if(fd != -1) { + if(write(fd, "1", 1) != 1) + syslog(LOG_WARNING, "Failed to write Apple HID driver."); + else + syslog(LOG_INFO, "Fn lock is off."); + close(fd); + } +} + +void daemonize() +{ + close(0); + close(1); + close(2); + open("/dev/null", O_RDONLY); /* stdin */ + open("/dev/null", O_WRONLY); /* stdout */ + open("/dev/null", O_WRONLY); /* stderr */ + chdir("/"); + int pid = fork(); + if(pid > 0) + exit(0); + setsid(); + pid = fork(); + if(pid > 0) + exit(0); +} + +#if 1 +static volatile sig_atomic_t n = 0; + +void usr1hdl(int sig) +{ + int save_errno = errno; + signal(SIGUSR1, usr1hdl); + n = 0; + errno = save_errno; +} + +void termhdl(int sig) +{ + int save_errno = errno; + signal(SIGTERM, termhdl); + n = 1; + errno = save_errno; +} +#endif + +int main(int argc, char *argv[]) +{ + /*daemonize();*/ + + signal(SIGUSR1, usr1hdl); + signal(SIGTERM, termhdl); + + openlog("hid_apple_fn_lock", 0, LOG_USER); + syslog(LOG_INFO, "Daemon started, pid == %d", getpid()); + + uid_t euid = geteuid(); + if(euid != 0) { + syslog(LOG_WARNING, "Must have root priviliges. Quitting..."); + return 1; + } + + for(;;) { + pause(); + if(n == 0) { + if(get_fn_lock()) + set_fn_lock_off(); + else + set_fn_lock_on(); + } else if(n == 1) { + syslog(LOG_INFO, "Shutting down..."); + break; + } else { + /* ignore */ + } + } + + closelog(); + + return 0; +} diff --git a/sv/hid_apple_fn_lock/log/run b/sv/hid_apple_fn_lock/log/run new file mode 100755 index 0000000..661f997 --- /dev/null +++ b/sv/hid_apple_fn_lock/log/run @@ -0,0 +1,3 @@ +#!/bin/sh + +exec chpst -u igor svlogd -tt /opt/hid_apple_fn_lock/logs diff --git a/sv/hid_apple_fn_lock/run b/sv/hid_apple_fn_lock/run new file mode 100755 index 0000000..420d9f2 --- /dev/null +++ b/sv/hid_apple_fn_lock/run @@ -0,0 +1,4 @@ +#!/bin/sh -e + +exec 2>&1 +exec chpst -u igor /usr/local/bin/hid_apple_fn_lock