donniebreve / touchcursor-linux

TouchCursor style keyboard remapping for Linux.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Detect configuration file changes

donniebreve opened this issue · comments

Detect configuration file changes and restart the service when something is changed.

Actually this is being handled by #52

Doesn't #52 only restart when keyboards are plugged or unplugged?

I don't know if the binary will detect config file changes or if systemd will do it, but if not systemd, could it be made optional? I use openrc and a script to manually restart touchcursor after config changes because key rate and other keyboard settings are lost when the process terminates. Having the process automatically restart if the config was "accidentally" touched would be unwanted.

#52 also includes a PathChanged watch on the configuration file. The PR aims to make systemd detect device changes and configuration file changes, which means the binary wouldn't be modified.

Would you be willing to share your script that restarts touchcursor after a config file change? I'm interested and I think it may give me some ideas. If I decide to introduce config file monitoring into the binary, we can discuss making it configurable. I'm not sure if you're familiar with alacritty, but I appreciate how it handles configuration file changes.

key rate and other keyboard settings are lost when the process terminates

Is this something related to touchcursor-linux or your current setup?

Having the process automatically restart if the config was "accidentally" touched would be unwanted.

Could you elaborate? In what kind of scenario do you envision this happening?

Would you be willing to share your script that restarts touchcursor after a config file change?

It doesn't detect config file changes, it is something I run manually after making changes. It basically calls /etc/init.d/touchcursor restart and then loads various setting files in the HOMEs of each X server user.

I'm not sure if you're familiar with alacritty, but I appreciate how it handles configuration file changes.

I'm not.

key rate and other keyboard settings are lost when the process terminates
Is this something related to touchcursor-linux or your current setup?

It would be on the xorg side because it removes the keyboard and its settings when the device goes away. I don't know if it happens because touchcursor restarts on a different device and if #55 would fix the issue.

If touchcursor could internally restart, as I mentioned in #55, it could restart when receiving SIGHUP or when it detects a change to the config file. libinotifytools from inotify-tools can be used to monitor MODIFY events from a file. This would be a soft restart because neither the process nor device would go away, and xorg wouldn't lose its settings.

Could you elaborate? In what kind of scenario do you envision this happening?

Changing a comment or touching the file, things you wouldn't restart the service for because nothing important was modified. This only matters if the process is killed and restarted, an interal restart would be fine.

Or the inotify kernel API could be used to avoid the dependency on inotify-tools. The fanotify API is only available in 5.9+ and not usable by anyone on older LTS kernels.

The following program watches for modifications to a file called the-config-file. The input variable would be touchcursor's input device (stdin here) and the keypress line would handle input from the device. The only thing missing is code to actually reload the config.

#include <stdio.h>
#include <sys/inotify.h>
#include <sys/time.h>
#include <unistd.h>

int pending( int fd ){
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
    fd_set set;
    FD_ZERO(&set);
    FD_SET(fd, &set);
    return select(fd+1, &set, NULL, NULL, &timeout);
}

int main( const int argc, const char *argv[] ){
    int id = inotify_init();
    if(id == -1){ printf("FAILED TO INIT\n"); return 1; }

    const char *filename = "the-config-file";

    int wd = inotify_add_watch(id, filename, IN_MODIFY|IN_DELETE_SELF);
    if(wd < 0){ printf("FAILED TO WATCH %s\n", filename); close(id); return 1; }

    while(1){
        struct timeval timeout;
        timeout.tv_sec = 0;
        timeout.tv_usec = 0;
        fd_set set;
        FD_ZERO(&set);
        int input = 0; // stdin
        FD_SET(input, &set);
        FD_SET(id, &set);
        int n = select((input>id?input:id)+1, &set, NULL, NULL, &timeout);
        if(n > 0){
            if(FD_ISSET(0, &set)) break; // keypress

            if(FD_ISSET(id, &set)){
                int modified = 0;
                do {
                    struct inotify_event event;
                    ssize_t bytes = read(id, &event, sizeof(event));
                    if(bytes == sizeof(event)){
                        // IN_IGNORE is sent after IN_DELETE_SELF

                        // other editors directly modify the file
                        if(event.mask & IN_MODIFY) modified = 1;

                        // vim moves the file and creates a new one in its place
                        if(event.mask & IN_DELETE_SELF){
                            inotify_rm_watch(id, wd);
                            wd = inotify_add_watch(id, filename, IN_MODIFY|IN_DELETE_SELF);
                            if(wd < 0){ printf("FAILED TO WATCH AGAIN\n"); close(id); return 1; }
                            modified = 1;
                        }
                    } else
                        printf("got %d bytes in event, expected %d\n", bytes, sizeof(event));
                } while(pending(id));
                if(modified) printf("MODIFIED\n"); // reload config
            }
        }
    }

    inotify_rm_watch(id, wd);
    close(id);
    return 0;
}

The three timeout lines in the while loop shouldn't be there, and the timeout parameter for select() should be NULL. Otherwise it would immediately return and spin in the while loop.