diff options
author | B. Stack <bgstack15@gmail.com> | 2022-09-30 16:38:40 -0400 |
---|---|---|
committer | B. Stack <bgstack15@gmail.com> | 2022-09-30 16:40:46 -0400 |
commit | 29fa73d2f5013c9502934bc8252014a211d7f7a8 (patch) | |
tree | 33a0854ed05537ca4515f784f84889c5e0ee77b6 | |
parent | WIP: minimum viable product for status.c (diff) | |
download | keyboard-leds-trayicons-29fa73d2f5013c9502934bc8252014a211d7f7a8.tar.gz keyboard-leds-trayicons-29fa73d2f5013c9502934bc8252014a211d7f7a8.tar.bz2 keyboard-leds-trayicons-29fa73d2f5013c9502934bc8252014a211d7f7a8.zip |
added major C improvements
* use libinih to read config file
* handle SIGPIPE gracefully (when a trayicon exits)
* exit when both trayicons have exited
I wanted to try using libini-config5 but it was not documented at all,
and I need useful examples.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | experimental/Makefile | 36 | ||||
-rw-r--r-- | experimental/keyboard-leds-trayicons.c (renamed from experimental/status.c) | 264 |
3 files changed, 204 insertions, 97 deletions
@@ -10,3 +10,4 @@ debian status *.o old/ +keyboard-leds-trayicons diff --git a/experimental/Makefile b/experimental/Makefile index 2bb7fca..17c9660 100644 --- a/experimental/Makefile +++ b/experimental/Makefile @@ -20,39 +20,63 @@ CC = gcc CXXFLAGS = -g -std=c++17 -Wall -Weffc++ -Wextra -Wsign-conversion -Werror CCFLAGS = -g -Wno-deprecated-declarations \ `pkg-config --cflags x11` \ - `pkg-config --cflags gtk+-3.0` + `pkg-config --cflags gtk+-3.0` \ + `pkg-config --cflags inih` \ + $(DEBUGFLAGS) +CCFLAGS2 = -g -Wsign-conversion -Werror \ + `pkg-config --cflags inih` # to remove all debug symbols: -s # to add full debug symbols: -g LDFLAGS = -g \ `pkg-config --libs x11` \ - `pkg-config --libs gtk+-3.0` + `pkg-config --libs gtk+-3.0` \ + `pkg-config --libs inih` +LDFLAGS2 = -g \ + `pkg-config --libs inih` + +ifneq (,$(DEBUG)) +CCFLAGS+=-DDEBUG=1 +endif src = $(wildcard *.c) +src := $(filter-out readconf.c, $(src)) +#src2 = $(wildcard read*.c) obj = $(src:.c=.o) +#obj2 = $(src2:.c=.o) BUILD_DIR ?= . -OUTEXE = status +OUTEXE = keyboard-leds-trayicons +#OUTEXE2 = readconf -all: $(OUTEXE) +all: $(OUTEXE) $(OUTEXE2) +#all: $(OUTEXE) $(obj): +#$(obj2): + $(BUILD_DIR)/%.o: %.c $(CC) -c $(CCFLAGS) -o $@ $< +#$(BUILD_DIR)/readconf.o: readconf.c +# $(CC) -c $(CCFLAGS2) -o $@ $< + # link $(OUTEXE): $(obj) $(CC) -o $@ $^ $(LDFLAGS) +#$(OUTEXE2): $(obj2) +# $(CC) -o $@ $^ $(LDFLAGS2) + .PHONY: clean cleanall list list: @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | ${awkbin} -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | ${sortbin} | ${grepbin} -E -v -e '^[^[:alnum:]]' -e '^$@$$' -e '\.(cp*|o)' clean: - rm -f $(obj) + rm -f $(obj) $(obj2) cleanall: - rm -f $(obj) $(OUTEXE) + rm -f $(obj) $(OUTEXE) $(obj2) $(OUTEXE2) diff --git a/experimental/status.c b/experimental/keyboard-leds-trayicons.c index f968af1..f2c6a4a 100644 --- a/experimental/status.c +++ b/experimental/keyboard-leds-trayicons.c @@ -1,5 +1,5 @@ /* - * File: status.c + * File: keyboard-leds-trayicons.c * Location: https://bgstack15.ddns.net/cgit/keyboard-leds-trayicons/ * Author: bgstack15 * Startdate: 2022-09-28-4 13:30 @@ -7,7 +7,6 @@ * Title: Proof of Concept C utility for polling capslock and numlock * Purpose: Demonstrate these can be done in C, with the eventual goal of rewriting keyboard-leds-trayicons entirely in C to avoid the "sleep 0.75" in ps output * History: - * minimum viable product * Usage: * References: * https://github.com/Cairo-Dock/cairo-dock-plug-ins/blob/master/keyboard-indicator/src/applet-xklavier.c#L124 @@ -18,15 +17,24 @@ * https://stackoverflow.com/questions/43326857/multiple-fork-in-c-program * https://stackoverflow.com/questions/14173268/fifo-files-and-read-write * https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_chapter/libc_15.html - * Improvements: - * Write main keyboard-led-trayicons logic, which includes parsing config file (libconfig?) - * Write pipe connectivity to mktrayicon or integrate it entirely * https://qnaplus.com/c-program-to-sleep-in-milliseconds/ - * Dependencies: libx11-dev + * https://stackoverflow.com/questions/47107311/sending-signal-from-child-to-parent + * https://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly + * Improvements: + * Use getopts, -h, -c conffile, -d (debug) + * fix all return values at end + * Dependencies: libx11-dev, libinih-dev, libgtk-3-dev + * Documentation: + * features include: + * operate when only one trayicon has exited + * exit when both trayicons have exited */ + #include<ctype.h> +#include<fcntl.h> #include<glib.h> #include<gtk/gtk.h> +#include<ini.h> #include<stdio.h> #include<stdlib.h> #include<string.h> @@ -34,8 +42,9 @@ #include<unistd.h> #include<X11/XKBlib.h> #include<X11/Xlib.h> -#include<fcntl.h> +#include<signal.h> +/* START inclusion of mktrayicon.c, 536 lines */ /* * This function is made because it's needed to escape the '\' character * The basic code has been taken from the man pages of the strncpy function @@ -620,6 +629,7 @@ int fmain(int argc, char **argv) { gtk_main(); return 0; } +/* STOP inclusion of mktrayicon.c */ Display* dpy; @@ -631,121 +641,193 @@ int get_indicator(Display* dpy, char* indicator) { return st; } +typedef struct { + const char* caps_on; + const char* caps_off; + const char* num_on; + const char* num_off; + int sleep_microseconds; + const char* caps_fifo; + const char* num_fifo; +} configuration; + +// this should be configurable with -c parameter (getopts?) +char* conffile = "/home/bgstack15/keyboard-leds-trayicons.conf"; + +// slightly modified from example https://github.com/benhoyt/inih +static int handler(void* user, const char* section, const char* name, const char* value) { + // KLT does not need section information at all. + configuration *pconfig = (configuration*)user; + #define MATCH(n, m, func) if (strcmp(name, n) == 0) { pconfig->m = func(value); } + MATCH("KLT_CAPS_ON_ICON", caps_on, strdup) + else MATCH("KLT_CAPS_OFF_ICON", caps_off, strdup) + else MATCH("KLT_NUM_ON_ICON", num_on, strdup) + else MATCH("KLT_NUM_OFF_ICON", num_off, strdup) + else MATCH("KLT_SLEEP_MICROSECONDS", sleep_microseconds, atoi) + else MATCH("KLT_CAPS_FIFO", caps_fifo, strdup) + else MATCH("KLT_NUM_FIFO", num_fifo, strdup) + else { + return 0; /* unknown section/name, error */ + } + return 1; +} + +char msg[1000]; + +void *send_fifo_message(FILE *stream, const char *message) { +#ifdef DEBUG + printf("Msg: %s", message); +#endif + int response = fprintf(stream, message); +#ifdef DEBUG + printf("response: %d\n", response); +#endif + fflush(stream); +} + +void *send_fifo_icon_message(FILE *stream, const char *iconfile) { + // msg is global buffer for messages to dump into a pipe + strcpy(msg, "i "); + strcat(msg, iconfile); + strcat(msg, "\n"); // very important for mktrayicon + send_fifo_message(stream, msg); +} + +int closed_icons = 0; +void sig_usr(int signo) { + if(signo == SIGUSR1) { + printf("an icon has exited!\n"); + closed_icons++; + }; + return; +} + int main(int argc, char **argv) { - FILE *stream; + + // Step 1: load config + configuration config; + if (ini_parse(conffile, handler, &config) < 0) { + printf("Error: cannot load %s\n",conffile); + return 1; + } + #ifdef DEBUG + printf("Config loaded from %s.\n", conffile); + printf("caps_on=%s\n", config.caps_on); + printf("caps_off=%s\n", config.caps_off); + printf("num_on=%s\n", config.num_on); + printf("num_off=%s\n", config.num_off); + printf("sleep_us=%d\n", config.sleep_microseconds); + printf("caps_fifo=%s\n", config.caps_fifo); + printf("num_fifo=%s\n", config.num_fifo); + #endif + + // Step 2: validate config + // hardcoded min/max for microseconds, 0 and 10 seconds + if (config.sleep_microseconds < 0 || config.sleep_microseconds >= 10000000) { + printf("Warning: invalid sleep_microseconds: %d\n", config.sleep_microseconds); + }; + + // Step 3: initialize variables + FILE *stream_N, *stream_C; dpy = XOpenDisplay( NULL ); int status_capslock = get_indicator(dpy, "Caps Lock"); int status_numlock = get_indicator(dpy, "Num Lock"); - printf("Capslock: %d\tNumlock: %d\n",status_capslock,status_numlock); - char *fifo_name_C = "/tmp/fifo1"; - char *fifo_name_N = "/tmp/fifo2"; - printf("done\n"); - char *_argv_C[] = { "fmain", fifo_name_C, NULL }; - char *_argv_N[] = { "fmain", fifo_name_N, NULL }; + char *_argv_C[] = { "fmain", (char*)config.caps_fifo, NULL }; + char *_argv_N[] = { "fmain", (char*)config.num_fifo, NULL }; int count_C = 0; int count_N = 0; - int fd_C, fd_N; - struct stat st_C, st_N; - char *msg; - if (stat(fifo_name_C, &st_C) != 0) - mkfifo(fifo_name_C, 0666); - if (stat(fifo_name_N, &st_N) != 0) - mkfifo(fifo_name_N, 0666); while (_argv_C[++count_C]); while (_argv_N[++count_N]); - //fd_C = open(fifo_name_C, O_WRONLY); - if (fork() == 0) { + int fd_C, fd_N; + struct stat st_C, st_N; + pid_t pid, pid_C, pid_N; + printf("Capslock: %d\tNumlock: %d\n",status_capslock,status_numlock); + + // Step 4: initiate fifo files + if (stat(config.caps_fifo, &st_C) != 0) + mkfifo(config.caps_fifo, 0666); + if (stat(config.num_fifo, &st_N) != 0) + mkfifo(config.num_fifo, 0666); + + // Step 5: start child proceses (tray icons) + // parent 1 + pid = getpid(); + if ((pid_C = fork()) == 0) { // parent1->child 1 - if (fork() == 0) { - // parent1->child1->child1 - fmain(count_C,_argv_C); - } - else { - // parent1->child1 - fd_C = open(fifo_name_C, O_WRONLY); - stream = fdopen(fd_C,"w"); - msg = "m quit,echo 'q' > /tmp/fifo1\n"; - printf("sending to fd_C %d message %s",fd_C,msg); - fprintf(stream,msg); - if (status_capslock) - msg = "i /home/bgstack15/dev/keyboard-leds-trayicons/src/usr/share/icons/hicolor/scalable/status/capslock-on.svg\n"; - else - msg = "i /home/bgstack15/dev/keyboard-leds-trayicons/src/usr/share/icons/hicolor/scalable/status/capslock-off.svg\n"; - printf("sending to fd_C %d message %s",fd_C,msg); - fprintf(stream,msg); - fclose(stream); - }; + fmain(count_C,_argv_C); + kill(pid, SIGUSR1); + _exit(0); } else { // parent 1 - if (fork() == 0) { + if ((pid_N = fork()) == 0) { // parent1->child2 fmain(count_N,_argv_N); + kill(pid, SIGUSR1); + _exit(0); } else { // parent 1 - fd_N = open(fifo_name_N, O_WRONLY); - stream = fdopen(fd_N,"w"); - msg = "i battery\n"; - printf("sending to fd_N %d message %s",fd_N,msg); - fprintf(stream,msg); - msg = "m say hello,yad 'hello'|quit,echo 'q' > /tmp/fifo2\n"; - printf("sending to fd_N %d message %s",fd_N,msg); - fprintf(stream,msg); - fclose(stream); - }; - // still parent 1 - // main logic loop happens now - //while (access("/tmp/stop-keyboard-leds",F_OK) != 0) { - fd_C = open(fifo_name_C, O_WRONLY); - fd_N = open(fifo_name_N, O_WRONLY); - int status_caps_old = status_capslock; - int status_num_old = status_numlock; - while (1) { - //ueep(1000000); 1 second - //ueep( 1000); 1 millisecond - usleep( 100000); // WORKHERE: should be configurable - status_capslock = get_indicator(dpy, "Caps Lock"); - status_numlock = get_indicator(dpy, "Num Lock"); - //printf("Capslock: %d\tNumlock: %d\n",status_capslock,status_numlock); - if(status_capslock != status_caps_old) { - stream = fdopen(fd_C,"w"); - if (stream > 0) { + signal(SIGPIPE, SIG_IGN); + /* prevents breakage when C or N tray icon is closed. + However, now this daemon will never quit without the closed_icon counter! + */ + fd_C = open(config.caps_fifo, O_WRONLY); + stream_C = fdopen(fd_C,"w"); + fd_N = open(config.num_fifo, O_WRONLY); + stream_N = fdopen(fd_N,"w"); + strcpy(msg, "m quit,echo 'q' > "); + strcat(msg, config.caps_fifo); + strcat(msg, "\n"); + send_fifo_message(stream_C, msg); + strcpy(msg, "m say hello,yad 'hello'|quit,echo 'q' > "); + strcat(msg, config.num_fifo); + strcat(msg, "\n"); + send_fifo_message(stream_N, msg); + // still parent 1 + // Step 6: loop monitoring logic + int status_caps_old = -1; + int status_num_old = -1; + int kill_result = 500; + while (closed_icons < 2) { + // (1000000); 1 second + // ( 1000); 1 millisecond + // ( 100000); 0.1 seconds is a good number + usleep(config.sleep_microseconds); + status_capslock = get_indicator(dpy, "Caps Lock"); + status_numlock = get_indicator(dpy, "Num Lock"); + //printf("Capslock: %d\tNumlock: %d\n",status_capslock,status_numlock); + if(signal(SIGUSR1,sig_usr) == SIG_ERR) + printf("Signal processed."); + if(status_capslock != status_caps_old) { + kill_result = kill(pid_C, 0); + printf("Checking for capslock change, pid %d, result %d\n", pid_C, kill_result); if(0 == status_capslock) { - msg = "i /home/bgstack15/dev/keyboard-leds-trayicons/src/usr/share/icons/hicolor/scalable/status/capslock-off.svg\n"; - //printf("sending to fd_C %d message %s",fd_C,msg); + send_fifo_icon_message(stream_C, config.caps_off); printf("capslock off\n"); - fprintf(stream,msg); } else { - msg = "i /home/bgstack15/dev/keyboard-leds-trayicons/src/usr/share/icons/hicolor/scalable/status/capslock-on.svg\n"; + send_fifo_icon_message(stream_C, config.caps_on); printf("capslock on\n"); - fprintf(stream,msg); }; status_caps_old = status_capslock; - fflush(stream); - //fclose(stream); }; - }; - if(status_numlock != status_num_old) { - stream = fdopen(fd_N,"w"); - if (stream > 0) { + if(status_numlock != status_num_old) { if(0 == status_numlock) { - msg = "i /home/bgstack15/dev/keyboard-leds-trayicons/src/usr/share/icons/hicolor/scalable/status/numlock-off.svg\n"; - //printf("sending to fd_N %d message %s",fd_N,msg); + send_fifo_icon_message(stream_N, config.num_off); printf("numlock off\n"); - fprintf(stream,msg); } else { - msg = "i /home/bgstack15/dev/keyboard-leds-trayicons/src/usr/share/icons/hicolor/scalable/status/numlock-on.svg\n"; + send_fifo_icon_message(stream_N, config.num_on); printf("numlock on\n"); - fprintf(stream,msg); }; status_num_old = status_numlock; - fflush(stream); }; - }; - }; - }; - return 0; + }; // end while(1) + // Either CTRL+C was run twice, or both icons closed + return 15; + }; // end parent 1 loop + return 26; + }; // end parent 1 loop + return 37; } |