aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Stack <bgstack15@gmail.com>2022-09-30 16:38:40 -0400
committerB. Stack <bgstack15@gmail.com>2022-09-30 16:40:46 -0400
commit29fa73d2f5013c9502934bc8252014a211d7f7a8 (patch)
tree33a0854ed05537ca4515f784f84889c5e0ee77b6
parentWIP: minimum viable product for status.c (diff)
downloadkeyboard-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--.gitignore1
-rw-r--r--experimental/Makefile36
-rw-r--r--experimental/keyboard-leds-trayicons.c (renamed from experimental/status.c)264
3 files changed, 204 insertions, 97 deletions
diff --git a/.gitignore b/.gitignore
index 1c3ea63..f63ec58 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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;
}
bgstack15