@code_type c .c @comment_type /* %s */ @title wmaffle @s Introduction This is the source code for wmaffle. It parses it's arguments and prints either help / info text, or runs the window manager. Here's an overview: --- wmaffle.c @{copyright notice} @{c headers} @{posix headers} @{defines} char *argv0; @{function definitions} int main(int argc, char *argv[]) { argv0 = argv[0]; @{variables local to main} @{parse arguments} @{open ipc connection} } --- Note that this is version NaV (not a version). --- defines #define VERSION_STRING "NaV" --- @s Copyright This project is licensed under the Mozilla Public License Version 2.0. Please read LICENSE for more details. --- copyright notice /* Copyright (c), Niclas Meyer * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at https://mozilla.org/MPL/2.0/. */ --- @s Helper function `die` prints an error message and then exist unsuccessfully. --- function definitions static void die(char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); exit(EXIT_FAILURE); } --- Note that this requires the `stdarg.h` header. --- c headers #include --- @s Parse arguments There are only two arguments and two of them are rather straight forward. I use getopt for this, since it's quite good and allows us to extract the config argument easily. This requires `getopt.h`. --- posix headers #include #include --- That one we save in a local variable in main. --- variables local to main char config_path[PATH_MAX]; int option; --- --- parse arguments config_path[0] = '\0'; // Used to check if config was already set while ((option = getopt(argc, argv, "hvc:")) != -1) { switch (option) { case 'h': printf("Usage: %s [-h|-v|-c ]\n", argv0); exit(EXIT_SUCCESS); break; case 'v': printf("v" VERSION_STRING "\n"); exit(EXIT_SUCCESS); break; case 'c': strncpy(config_path, optarg, sizeof(config_path)); break; } } if (!config_path[0]) { @{get default config path} } --- We also need `string.h`, `stdio.h`, and `stdlib.h`. --- c headers += #include #include #include --- @s Config default path I can clearly see how a user doesn't specify a config path. This was a design decision to make, whether to load a default config. I decided to go with it. It loads `"${XDG_CONFIG_HOME:-$HOME/.config}/wmaffle/wmafflerc"`. ---get default config path char *config_home = getenv("XDG_CONFIG_HOME"); if (config_home) { snprintf( config_path, sizeof(config_path), "%s/%s", config_home, "wmafflw/wmafflerc" ); } else { snprintf( config_path, sizeof(config_path), "%s/%s", getenv("HOME"), ".config/wmaffle/wmafflerc" ); } --- @s Open IPC connection Pretty straight forward, we open a `AF_UNIX` socket and that's it. We need both a sockaddr and a file descriptor. --- variables local to main += struct sockaddr_un sock_addr; int sock_fd; char *sock_path; --- This requires the following headers. --- posix headers += #include #include --- Opening the server is pretty straight forward. --- open ipc connection memset(&sock_addr, 0, sizeof(sock_addr)); @{get socket path} unlink(sock_addr.sun_path); sock_addr.sun_family = AF_UNIX; if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { die("%s: failed to create socket\n", argv0); } if (bind(sock_fd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { die("%s: failed to bind socket\n", argv0); } if (listen(sock_fd, 1) < 0) { die("%s: failed to listen to socket\n", argv0); } --- This requires this header file. --- posix headers += #include --- @s Get socket path We default to `/tmp/wmaffle.sock` if `WMAFFLE_SOCKET` isn't set. --- get socket path sock_path = getenv("WMAFFLE_SOCKET"); if (sock_path) { strncpy(sock_addr.sun_path, sock_path, sizeof(sock_addr.sun_path)); } else { strncpy(sock_addr.sun_path, "/tmp/wmaffle.sock", sizeof(sock_addr.sun_path)); } ---