sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

dwm-ipc-20200729-f04cac6.diff (90361B)


      1 From 1db4901625c20669b8e448b5c78b642fb792c844 Mon Sep 17 00:00:00 2001
      2 From: mihirlad55 <mihirlad55@gmail.com>
      3 Date: Wed, 29 Jul 2020 23:56:25 +0000
      4 Subject: [PATCH] Add IPC support through a unix socket
      5 
      6 This patch currently supports the following requests:
      7 * Run custom commands with arguments (similar to key bind functions)
      8 * Get monitor properties
      9 * Get all available layouts
     10 * Get available tags
     11 * Get client properties
     12 * Subscribe to tag change, client focus change, and layout change,
     13   monitor focus change, focused title change, and client state change
     14   events
     15 
     16 This patch includes a dwm-msg cli program that supports all of the
     17 above requests for easy integration into shell scripts.
     18 
     19 The messages are sent in a JSON format to promote integration to
     20 increase scriptability in languages like Python/JavaScript.
     21 
     22 The patch requires YAJL for JSON parsing and a system with epoll
     23 support. Portability is planned to be increased in the future.
     24 
     25 This patch is best applied after all other patches to avoid merge
     26 conflicts.
     27 
     28 For more info on the IPC implementation and how to send/receive
     29 messages, documentation can be found at
     30 https://github.com/mihirlad55/dwm-ipc
     31 ---
     32  IPCClient.c  |   66 +++
     33  IPCClient.h  |   61 +++
     34  Makefile     |   10 +-
     35  config.def.h |   19 +
     36  config.mk    |    8 +-
     37  dwm-msg.c    |  543 +++++++++++++++++++++++
     38  dwm.c        |  150 ++++++-
     39  ipc.c        | 1202 ++++++++++++++++++++++++++++++++++++++++++++++++++
     40  ipc.h        |  320 ++++++++++++++
     41  util.c       |  135 ++++++
     42  util.h       |   10 +
     43  yajl_dumps.c |  351 +++++++++++++++
     44  yajl_dumps.h |   65 +++
     45  13 files changed, 2927 insertions(+), 13 deletions(-)
     46  create mode 100644 IPCClient.c
     47  create mode 100644 IPCClient.h
     48  create mode 100644 dwm-msg.c
     49  create mode 100644 ipc.c
     50  create mode 100644 ipc.h
     51  create mode 100644 yajl_dumps.c
     52  create mode 100644 yajl_dumps.h
     53 
     54 diff --git a/IPCClient.c b/IPCClient.c
     55 new file mode 100644
     56 index 0000000..0d3eefb
     57 --- /dev/null
     58 +++ b/IPCClient.c
     59 @@ -0,0 +1,66 @@
     60 +#include "IPCClient.h"
     61 +
     62 +#include <string.h>
     63 +#include <sys/epoll.h>
     64 +
     65 +#include "util.h"
     66 +
     67 +IPCClient *
     68 +ipc_client_new(int fd)
     69 +{
     70 +  IPCClient *c = (IPCClient *)malloc(sizeof(IPCClient));
     71 +
     72 +  if (c == NULL) return NULL;
     73 +
     74 +  // Initialize struct
     75 +  memset(&c->event, 0, sizeof(struct epoll_event));
     76 +
     77 +  c->buffer_size = 0;
     78 +  c->buffer = NULL;
     79 +  c->fd = fd;
     80 +  c->event.data.fd = fd;
     81 +  c->next = NULL;
     82 +  c->prev = NULL;
     83 +  c->subscriptions = 0;
     84 +
     85 +  return c;
     86 +}
     87 +
     88 +void
     89 +ipc_list_add_client(IPCClientList *list, IPCClient *nc)
     90 +{
     91 +  DEBUG("Adding client with fd %d to list\n", nc->fd);
     92 +
     93 +  if (*list == NULL) {
     94 +    // List is empty, point list at first client
     95 +    *list = nc;
     96 +  } else {
     97 +    IPCClient *c;
     98 +    // Go to last client in list
     99 +    for (c = *list; c && c->next; c = c->next)
    100 +      ;
    101 +    c->next = nc;
    102 +    nc->prev = c;
    103 +  }
    104 +}
    105 +
    106 +void
    107 +ipc_list_remove_client(IPCClientList *list, IPCClient *c)
    108 +{
    109 +  IPCClient *cprev = c->prev;
    110 +  IPCClient *cnext = c->next;
    111 +
    112 +  if (cprev != NULL) cprev->next = c->next;
    113 +  if (cnext != NULL) cnext->prev = c->prev;
    114 +  if (c == *list) *list = c->next;
    115 +}
    116 +
    117 +IPCClient *
    118 +ipc_list_get_client(IPCClientList list, int fd)
    119 +{
    120 +  for (IPCClient *c = list; c; c = c->next) {
    121 +    if (c->fd == fd) return c;
    122 +  }
    123 +
    124 +  return NULL;
    125 +}
    126 diff --git a/IPCClient.h b/IPCClient.h
    127 new file mode 100644
    128 index 0000000..307dfba
    129 --- /dev/null
    130 +++ b/IPCClient.h
    131 @@ -0,0 +1,61 @@
    132 +#ifndef IPC_CLIENT_H_
    133 +#define IPC_CLIENT_H_
    134 +
    135 +#include <stdio.h>
    136 +#include <stdlib.h>
    137 +#include <sys/epoll.h>
    138 +
    139 +typedef struct IPCClient IPCClient;
    140 +/**
    141 + * This structure contains the details of an IPC Client and pointers for a
    142 + * linked list
    143 + */
    144 +struct IPCClient {
    145 +  int fd;
    146 +  int subscriptions;
    147 +
    148 +  char *buffer;
    149 +  uint32_t buffer_size;
    150 +
    151 +  struct epoll_event event;
    152 +  IPCClient *next;
    153 +  IPCClient *prev;
    154 +};
    155 +
    156 +typedef IPCClient *IPCClientList;
    157 +
    158 +/**
    159 + * Allocate memory for new IPCClient with the specified file descriptor and
    160 + * initialize struct.
    161 + *
    162 + * @param fd File descriptor of IPC client
    163 + *
    164 + * @return Address to allocated IPCClient struct
    165 + */
    166 +IPCClient *ipc_client_new(int fd);
    167 +
    168 +/**
    169 + * Add an IPC Client to the specified list
    170 + *
    171 + * @param list Address of the list to add the client to
    172 + * @param nc Address of the IPCClient
    173 + */
    174 +void ipc_list_add_client(IPCClientList *list, IPCClient *nc);
    175 +
    176 +/**
    177 + * Remove an IPCClient from the specified list
    178 + *
    179 + * @param list Address of the list to remove the client from
    180 + * @param c Address of the IPCClient
    181 + */
    182 +void ipc_list_remove_client(IPCClientList *list, IPCClient *c);
    183 +
    184 +/**
    185 + * Get an IPCClient from the specified IPCClient list
    186 + *
    187 + * @param list List to remove the client from
    188 + * @param fd File descriptor of the IPCClient
    189 + */
    190 +IPCClient *ipc_list_get_client(IPCClientList list, int fd);
    191 +
    192 +#endif  // IPC_CLIENT_H_
    193 diff --git a/Makefile b/Makefile
    194 index 77bcbc0..0456754 100644
    195 --- a/Makefile
    196 +++ b/Makefile
    197 @@ -6,7 +6,7 @@ include config.mk
    198  SRC = drw.c dwm.c util.c
    199  OBJ = ${SRC:.c=.o}
    200  
    201 -all: options dwm
    202 +all: options dwm dwm-msg
    203  
    204  options:
    205  	@echo dwm build options:
    206 @@ -25,8 +25,11 @@ config.h:
    207  dwm: ${OBJ}
    208  	${CC} -o $@ ${OBJ} ${LDFLAGS}
    209  
    210 +dwm-msg: dwm-msg.o
    211 +	${CC} -o $@ $< ${LDFLAGS}
    212 +
    213  clean:
    214 -	rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
    215 +	rm -f dwm dwm-msg ${OBJ} dwm-${VERSION}.tar.gz
    216  
    217  dist: clean
    218  	mkdir -p dwm-${VERSION}
    219 @@ -38,8 +41,9 @@ dist: clean
    220  
    221  install: all
    222  	mkdir -p ${DESTDIR}${PREFIX}/bin
    223 -	cp -f dwm ${DESTDIR}${PREFIX}/bin
    224 +	cp -f dwm dwm-msg ${DESTDIR}${PREFIX}/bin
    225  	chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
    226 +	chmod 755 ${DESTDIR}${PREFIX}/bin/dwm-msg
    227  	mkdir -p ${DESTDIR}${MANPREFIX}/man1
    228  	sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
    229  	chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
    230 diff --git a/config.def.h b/config.def.h
    231 index 1c0b587..3ad9785 100644
    232 --- a/config.def.h
    233 +++ b/config.def.h
    234 @@ -113,3 +113,22 @@ static Button buttons[] = {
    235  	{ ClkTagBar,            MODKEY,         Button3,        toggletag,      {0} },
    236  };
    237  
    238 +static const char *ipcsockpath = "/tmp/dwm.sock";
    239 +static IPCCommand ipccommands[] = {
    240 +  IPCCOMMAND(  view,                1,      {ARG_TYPE_UINT}   ),
    241 +  IPCCOMMAND(  toggleview,          1,      {ARG_TYPE_UINT}   ),
    242 +  IPCCOMMAND(  tag,                 1,      {ARG_TYPE_UINT}   ),
    243 +  IPCCOMMAND(  toggletag,           1,      {ARG_TYPE_UINT}   ),
    244 +  IPCCOMMAND(  tagmon,              1,      {ARG_TYPE_UINT}   ),
    245 +  IPCCOMMAND(  focusmon,            1,      {ARG_TYPE_SINT}   ),
    246 +  IPCCOMMAND(  focusstack,          1,      {ARG_TYPE_SINT}   ),
    247 +  IPCCOMMAND(  zoom,                1,      {ARG_TYPE_NONE}   ),
    248 +  IPCCOMMAND(  spawn,               1,      {ARG_TYPE_PTR}    ),
    249 +  IPCCOMMAND(  incnmaster,          1,      {ARG_TYPE_SINT}   ),
    250 +  IPCCOMMAND(  killclient,          1,      {ARG_TYPE_SINT}   ),
    251 +  IPCCOMMAND(  togglefloating,      1,      {ARG_TYPE_NONE}   ),
    252 +  IPCCOMMAND(  setmfact,            1,      {ARG_TYPE_FLOAT}  ),
    253 +  IPCCOMMAND(  setlayoutsafe,       1,      {ARG_TYPE_PTR}    ),
    254 +  IPCCOMMAND(  quit,                1,      {ARG_TYPE_NONE}   )
    255 +};
    256 +
    257 diff --git a/config.mk b/config.mk
    258 index 7084c33..8570938 100644
    259 --- a/config.mk
    260 +++ b/config.mk
    261 @@ -20,9 +20,13 @@ FREETYPEINC = /usr/include/freetype2
    262  # OpenBSD (uncomment)
    263  #FREETYPEINC = ${X11INC}/freetype2
    264  
    265 +# yajl
    266 +YAJLLIBS = -lyajl
    267 +YAJLINC = /usr/include/yajl
    268 +
    269  # includes and libs
    270 -INCS = -I${X11INC} -I${FREETYPEINC}
    271 -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
    272 +INCS = -I${X11INC} -I${FREETYPEINC} -I${YAJLINC}
    273 +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${YAJLLIBS}
    274  
    275  # flags
    276  CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
    277 diff --git a/dwm-msg.c b/dwm-msg.c
    278 new file mode 100644
    279 index 0000000..606f6ce
    280 --- /dev/null
    281 +++ b/dwm-msg.c
    282 @@ -0,0 +1,543 @@
    283 +#include <ctype.h>
    284 +#include <errno.h>
    285 +#include <inttypes.h>
    286 +#include <stdarg.h>
    287 +#include <stdint.h>
    288 +#include <stdio.h>
    289 +#include <stdlib.h>
    290 +#include <string.h>
    291 +#include <sys/socket.h>
    292 +#include <sys/un.h>
    293 +#include <unistd.h>
    294 +#include <yajl/yajl_gen.h>
    295 +
    296 +#define IPC_MAGIC "DWM-IPC"
    297 +// clang-format off
    298 +#define IPC_MAGIC_ARR { 'D', 'W', 'M', '-', 'I', 'P', 'C' }
    299 +// clang-format on
    300 +#define IPC_MAGIC_LEN 7  // Not including null char
    301 +
    302 +#define IPC_EVENT_TAG_CHANGE "tag_change_event"
    303 +#define IPC_EVENT_CLIENT_FOCUS_CHANGE "client_focus_change_event"
    304 +#define IPC_EVENT_LAYOUT_CHANGE "layout_change_event"
    305 +#define IPC_EVENT_MONITOR_FOCUS_CHANGE "monitor_focus_change_event"
    306 +#define IPC_EVENT_FOCUSED_TITLE_CHANGE "focused_title_change_event"
    307 +#define IPC_EVENT_FOCUSED_STATE_CHANGE "focused_state_change_event"
    308 +
    309 +#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
    310 +#define YINT(num) yajl_gen_integer(gen, num)
    311 +#define YDOUBLE(num) yajl_gen_double(gen, num)
    312 +#define YBOOL(v) yajl_gen_bool(gen, v)
    313 +#define YNULL() yajl_gen_null(gen)
    314 +#define YARR(body)                                                             \
    315 +  {                                                                            \
    316 +    yajl_gen_array_open(gen);                                                  \
    317 +    body;                                                                      \
    318 +    yajl_gen_array_close(gen);                                                 \
    319 +  }
    320 +#define YMAP(body)                                                             \
    321 +  {                                                                            \
    322 +    yajl_gen_map_open(gen);                                                    \
    323 +    body;                                                                      \
    324 +    yajl_gen_map_close(gen);                                                   \
    325 +  }
    326 +
    327 +typedef unsigned long Window;
    328 +
    329 +const char *DEFAULT_SOCKET_PATH = "/tmp/dwm.sock";
    330 +static int sock_fd = -1;
    331 +static unsigned int ignore_reply = 0;
    332 +
    333 +typedef enum IPCMessageType {
    334 +  IPC_TYPE_RUN_COMMAND = 0,
    335 +  IPC_TYPE_GET_MONITORS = 1,
    336 +  IPC_TYPE_GET_TAGS = 2,
    337 +  IPC_TYPE_GET_LAYOUTS = 3,
    338 +  IPC_TYPE_GET_DWM_CLIENT = 4,
    339 +  IPC_TYPE_SUBSCRIBE = 5,
    340 +  IPC_TYPE_EVENT = 6
    341 +} IPCMessageType;
    342 +
    343 +// Every IPC message must begin with this
    344 +typedef struct dwm_ipc_header {
    345 +  uint8_t magic[IPC_MAGIC_LEN];
    346 +  uint32_t size;
    347 +  uint8_t type;
    348 +} __attribute((packed)) dwm_ipc_header_t;
    349 +
    350 +static int
    351 +recv_message(uint8_t *msg_type, uint32_t *reply_size, uint8_t **reply)
    352 +{
    353 +  uint32_t read_bytes = 0;
    354 +  const int32_t to_read = sizeof(dwm_ipc_header_t);
    355 +  char header[to_read];
    356 +  char *walk = header;
    357 +
    358 +  // Try to read header
    359 +  while (read_bytes < to_read) {
    360 +    ssize_t n = read(sock_fd, header + read_bytes, to_read - read_bytes);
    361 +
    362 +    if (n == 0) {
    363 +      if (read_bytes == 0) {
    364 +        fprintf(stderr, "Unexpectedly reached EOF while reading header.");
    365 +        fprintf(stderr,
    366 +                "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n",
    367 +                read_bytes, to_read);
    368 +        return -2;
    369 +      } else {
    370 +        fprintf(stderr, "Unexpectedly reached EOF while reading header.");
    371 +        fprintf(stderr,
    372 +                "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n",
    373 +                read_bytes, to_read);
    374 +        return -3;
    375 +      }
    376 +    } else if (n == -1) {
    377 +      return -1;
    378 +    }
    379 +
    380 +    read_bytes += n;
    381 +  }
    382 +
    383 +  // Check if magic string in header matches
    384 +  if (memcmp(walk, IPC_MAGIC, IPC_MAGIC_LEN) != 0) {
    385 +    fprintf(stderr, "Invalid magic string. Got '%.*s', expected '%s'\n",
    386 +            IPC_MAGIC_LEN, walk, IPC_MAGIC);
    387 +    return -3;
    388 +  }
    389 +
    390 +  walk += IPC_MAGIC_LEN;
    391 +
    392 +  // Extract reply size
    393 +  memcpy(reply_size, walk, sizeof(uint32_t));
    394 +  walk += sizeof(uint32_t);
    395 +
    396 +  // Extract message type
    397 +  memcpy(msg_type, walk, sizeof(uint8_t));
    398 +  walk += sizeof(uint8_t);
    399 +
    400 +  (*reply) = malloc(*reply_size);
    401 +
    402 +  // Extract payload
    403 +  read_bytes = 0;
    404 +  while (read_bytes < *reply_size) {
    405 +    ssize_t n = read(sock_fd, *reply + read_bytes, *reply_size - read_bytes);
    406 +
    407 +    if (n == 0) {
    408 +      fprintf(stderr, "Unexpectedly reached EOF while reading payload.");
    409 +      fprintf(stderr, "Read %" PRIu32 " bytes, expected %" PRIu32 " bytes.\n",
    410 +              read_bytes, *reply_size);
    411 +      free(*reply);
    412 +      return -2;
    413 +    } else if (n == -1) {
    414 +      if (errno == EINTR || errno == EAGAIN) continue;
    415 +      free(*reply);
    416 +      return -1;
    417 +    }
    418 +
    419 +    read_bytes += n;
    420 +  }
    421 +
    422 +  return 0;
    423 +}
    424 +
    425 +static int
    426 +read_socket(IPCMessageType *msg_type, uint32_t *msg_size, char **msg)
    427 +{
    428 +  int ret = -1;
    429 +
    430 +  while (ret != 0) {
    431 +    ret = recv_message((uint8_t *)msg_type, msg_size, (uint8_t **)msg);
    432 +
    433 +    if (ret < 0) {
    434 +      // Try again (non-fatal error)
    435 +      if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue;
    436 +
    437 +      fprintf(stderr, "Error receiving response from socket. ");
    438 +      fprintf(stderr, "The connection might have been lost.\n");
    439 +      exit(2);
    440 +    }
    441 +  }
    442 +
    443 +  return 0;
    444 +}
    445 +
    446 +static ssize_t
    447 +write_socket(const void *buf, size_t count)
    448 +{
    449 +  size_t written = 0;
    450 +
    451 +  while (written < count) {
    452 +    const ssize_t n =
    453 +        write(sock_fd, ((uint8_t *)buf) + written, count - written);
    454 +
    455 +    if (n == -1) {
    456 +      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
    457 +        continue;
    458 +      else
    459 +        return n;
    460 +    }
    461 +    written += n;
    462 +  }
    463 +  return written;
    464 +}
    465 +
    466 +static void
    467 +connect_to_socket()
    468 +{
    469 +  struct sockaddr_un addr;
    470 +
    471 +  int sock = socket(AF_UNIX, SOCK_STREAM, 0);
    472 +
    473 +  // Initialize struct to 0
    474 +  memset(&addr, 0, sizeof(struct sockaddr_un));
    475 +
    476 +  addr.sun_family = AF_UNIX;
    477 +  strcpy(addr.sun_path, DEFAULT_SOCKET_PATH);
    478 +
    479 +  connect(sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un));
    480 +
    481 +  sock_fd = sock;
    482 +}
    483 +
    484 +static int
    485 +send_message(IPCMessageType msg_type, uint32_t msg_size, uint8_t *msg)
    486 +{
    487 +  dwm_ipc_header_t header = {
    488 +      .magic = IPC_MAGIC_ARR, .size = msg_size, .type = msg_type};
    489 +
    490 +  size_t header_size = sizeof(dwm_ipc_header_t);
    491 +  size_t total_size = header_size + msg_size;
    492 +
    493 +  uint8_t buffer[total_size];
    494 +
    495 +  // Copy header to buffer
    496 +  memcpy(buffer, &header, header_size);
    497 +  // Copy message to buffer
    498 +  memcpy(buffer + header_size, msg, header.size);
    499 +
    500 +  write_socket(buffer, total_size);
    501 +
    502 +  return 0;
    503 +}
    504 +
    505 +static int
    506 +is_float(const char *s)
    507 +{
    508 +  size_t len = strlen(s);
    509 +  int is_dot_used = 0;
    510 +
    511 +  // Floats can only have one decimal point in between or digits
    512 +  for (int i = 0; i < len; i++) {
    513 +    if (isdigit(s[i]))
    514 +      continue;
    515 +    else if (!is_dot_used && s[i] == '.' && i != 0 && i != len - 1) {
    516 +      is_dot_used = 1;
    517 +      continue;
    518 +    } else
    519 +      return 0;
    520 +  }
    521 +
    522 +  return 1;
    523 +}
    524 +
    525 +static int
    526 +is_unsigned_int(const char *s)
    527 +{
    528 +  size_t len = strlen(s);
    529 +
    530 +  // Unsigned int can only have digits
    531 +  for (int i = 0; i < len; i++) {
    532 +    if (isdigit(s[i]))
    533 +      continue;
    534 +    else
    535 +      return 0;
    536 +  }
    537 +
    538 +  return 1;
    539 +}
    540 +
    541 +static int
    542 +is_signed_int(const char *s)
    543 +{
    544 +  size_t len = strlen(s);
    545 +
    546 +  // Signed int can only have digits and a negative sign at the start
    547 +  for (int i = 0; i < len; i++) {
    548 +    if (isdigit(s[i]))
    549 +      continue;
    550 +    else if (i == 0 && s[i] == '-') {
    551 +      continue;
    552 +    } else
    553 +      return 0;
    554 +  }
    555 +
    556 +  return 1;
    557 +}
    558 +
    559 +static void
    560 +flush_socket_reply()
    561 +{
    562 +  IPCMessageType reply_type;
    563 +  uint32_t reply_size;
    564 +  char *reply;
    565 +
    566 +  read_socket(&reply_type, &reply_size, &reply);
    567 +
    568 +  free(reply);
    569 +}
    570 +
    571 +static void
    572 +print_socket_reply()
    573 +{
    574 +  IPCMessageType reply_type;
    575 +  uint32_t reply_size;
    576 +  char *reply;
    577 +
    578 +  read_socket(&reply_type, &reply_size, &reply);
    579 +
    580 +  printf("%.*s\n", reply_size, reply);
    581 +  fflush(stdout);
    582 +  free(reply);
    583 +}
    584 +
    585 +static int
    586 +run_command(const char *name, char *args[], int argc)
    587 +{
    588 +  const unsigned char *msg;
    589 +  size_t msg_size;
    590 +
    591 +  yajl_gen gen = yajl_gen_alloc(NULL);
    592 +
    593 +  // Message format:
    594 +  // {
    595 +  //   "command": "<name>",
    596 +  //   "args": [ ... ]
    597 +  // }
    598 +  // clang-format off
    599 +  YMAP(
    600 +    YSTR("command"); YSTR(name);
    601 +    YSTR("args"); YARR(
    602 +      for (int i = 0; i < argc; i++) {
    603 +        if (is_signed_int(args[i])) {
    604 +          long long num = atoll(args[i]);
    605 +          YINT(num);
    606 +        } else if (is_float(args[i])) {
    607 +          float num = atof(args[i]);
    608 +          YDOUBLE(num);
    609 +        } else {
    610 +          YSTR(args[i]);
    611 +        }
    612 +      }
    613 +    )
    614 +  )
    615 +  // clang-format on
    616 +
    617 +  yajl_gen_get_buf(gen, &msg, &msg_size);
    618 +
    619 +  send_message(IPC_TYPE_RUN_COMMAND, msg_size, (uint8_t *)msg);
    620 +
    621 +  if (!ignore_reply)
    622 +    print_socket_reply();
    623 +  else
    624 +    flush_socket_reply();
    625 +
    626 +  yajl_gen_free(gen);
    627 +
    628 +  return 0;
    629 +}
    630 +
    631 +static int
    632 +get_monitors()
    633 +{
    634 +  send_message(IPC_TYPE_GET_MONITORS, 1, (uint8_t *)"");
    635 +  print_socket_reply();
    636 +  return 0;
    637 +}
    638 +
    639 +static int
    640 +get_tags()
    641 +{
    642 +  send_message(IPC_TYPE_GET_TAGS, 1, (uint8_t *)"");
    643 +  print_socket_reply();
    644 +
    645 +  return 0;
    646 +}
    647 +
    648 +static int
    649 +get_layouts()
    650 +{
    651 +  send_message(IPC_TYPE_GET_LAYOUTS, 1, (uint8_t *)"");
    652 +  print_socket_reply();
    653 +
    654 +  return 0;
    655 +}
    656 +
    657 +static int
    658 +get_dwm_client(Window win)
    659 +{
    660 +  const unsigned char *msg;
    661 +  size_t msg_size;
    662 +
    663 +  yajl_gen gen = yajl_gen_alloc(NULL);
    664 +
    665 +  // Message format:
    666 +  // {
    667 +  //   "client_window_id": "<win>"
    668 +  // }
    669 +  // clang-format off
    670 +  YMAP(
    671 +    YSTR("client_window_id"); YINT(win);
    672 +  )
    673 +  // clang-format on
    674 +
    675 +  yajl_gen_get_buf(gen, &msg, &msg_size);
    676 +
    677 +  send_message(IPC_TYPE_GET_DWM_CLIENT, msg_size, (uint8_t *)msg);
    678 +
    679 +  print_socket_reply();
    680 +
    681 +  yajl_gen_free(gen);
    682 +
    683 +  return 0;
    684 +}
    685 +
    686 +static int
    687 +subscribe(const char *event)
    688 +{
    689 +  const unsigned char *msg;
    690 +  size_t msg_size;
    691 +
    692 +  yajl_gen gen = yajl_gen_alloc(NULL);
    693 +
    694 +  // Message format:
    695 +  // {
    696 +  //   "event": "<event>",
    697 +  //   "action": "subscribe"
    698 +  // }
    699 +  // clang-format off
    700 +  YMAP(
    701 +    YSTR("event"); YSTR(event);
    702 +    YSTR("action"); YSTR("subscribe");
    703 +  )
    704 +  // clang-format on
    705 +
    706 +  yajl_gen_get_buf(gen, &msg, &msg_size);
    707 +
    708 +  send_message(IPC_TYPE_SUBSCRIBE, msg_size, (uint8_t *)msg);
    709 +
    710 +  if (!ignore_reply)
    711 +    print_socket_reply();
    712 +  else
    713 +    flush_socket_reply();
    714 +
    715 +  yajl_gen_free(gen);
    716 +
    717 +  return 0;
    718 +}
    719 +
    720 +static void
    721 +usage_error(const char *prog_name, const char *format, ...)
    722 +{
    723 +  va_list args;
    724 +  va_start(args, format);
    725 +
    726 +  fprintf(stderr, "Error: ");
    727 +  vfprintf(stderr, format, args);
    728 +  fprintf(stderr, "\nusage: %s <command> [...]\n", prog_name);
    729 +  fprintf(stderr, "Try '%s help'\n", prog_name);
    730 +
    731 +  va_end(args);
    732 +  exit(1);
    733 +}
    734 +
    735 +static void
    736 +print_usage(const char *name)
    737 +{
    738 +  printf("usage: %s [options] <command> [...]\n", name);
    739 +  puts("");
    740 +  puts("Commands:");
    741 +  puts("  run_command <name> [args...]    Run an IPC command");
    742 +  puts("");
    743 +  puts("  get_monitors                    Get monitor properties");
    744 +  puts("");
    745 +  puts("  get_tags                        Get list of tags");
    746 +  puts("");
    747 +  puts("  get_layouts                     Get list of layouts");
    748 +  puts("");
    749 +  puts("  get_dwm_client <window_id>      Get dwm client proprties");
    750 +  puts("");
    751 +  puts("  subscribe [events...]           Subscribe to specified events");
    752 +  puts("                                  Options: " IPC_EVENT_TAG_CHANGE ",");
    753 +  puts("                                  " IPC_EVENT_LAYOUT_CHANGE ",");
    754 +  puts("                                  " IPC_EVENT_CLIENT_FOCUS_CHANGE ",");
    755 +  puts("                                  " IPC_EVENT_MONITOR_FOCUS_CHANGE ",");
    756 +  puts("                                  " IPC_EVENT_FOCUSED_TITLE_CHANGE ",");
    757 +  puts("                                  " IPC_EVENT_FOCUSED_STATE_CHANGE);
    758 +  puts("");
    759 +  puts("  help                            Display this message");
    760 +  puts("");
    761 +  puts("Options:");
    762 +  puts("  --ignore-reply                  Don't print reply messages from");
    763 +  puts("                                  run_command and subscribe.");
    764 +  puts("");
    765 +}
    766 +
    767 +int
    768 +main(int argc, char *argv[])
    769 +{
    770 +  const char *prog_name = argv[0];
    771 +
    772 +  connect_to_socket();
    773 +  if (sock_fd == -1) {
    774 +    fprintf(stderr, "Failed to connect to socket\n");
    775 +    return 1;
    776 +  }
    777 +
    778 +  int i = 1;
    779 +  if (strcmp(argv[i], "--ignore-reply") == 0) {
    780 +    ignore_reply = 1;
    781 +    i++;
    782 +  }
    783 +
    784 +  if (i >= argc) usage_error(prog_name, "Expected an argument, got none");
    785 +
    786 +  if (strcmp(argv[i], "help") == 0)
    787 +    print_usage(prog_name);
    788 +  else if (strcmp(argv[i], "run_command") == 0) {
    789 +    if (++i >= argc) usage_error(prog_name, "No command specified");
    790 +    // Command name
    791 +    char *command = argv[i];
    792 +    // Command arguments are everything after command name
    793 +    char **command_args = argv + ++i;
    794 +    // Number of command arguments
    795 +    int command_argc = argc - i;
    796 +    run_command(command, command_args, command_argc);
    797 +  } else if (strcmp(argv[i], "get_monitors") == 0) {
    798 +    get_monitors();
    799 +  } else if (strcmp(argv[i], "get_tags") == 0) {
    800 +    get_tags();
    801 +  } else if (strcmp(argv[i], "get_layouts") == 0) {
    802 +    get_layouts();
    803 +  } else if (strcmp(argv[i], "get_dwm_client") == 0) {
    804 +    if (++i < argc) {
    805 +      if (is_unsigned_int(argv[i])) {
    806 +        Window win = atol(argv[i]);
    807 +        get_dwm_client(win);
    808 +      } else
    809 +        usage_error(prog_name, "Expected unsigned integer argument");
    810 +    } else
    811 +      usage_error(prog_name, "Expected the window id");
    812 +  } else if (strcmp(argv[i], "subscribe") == 0) {
    813 +    if (++i < argc) {
    814 +      for (int j = i; j < argc; j++) subscribe(argv[j]);
    815 +    } else
    816 +      usage_error(prog_name, "Expected event name");
    817 +    // Keep listening for events forever
    818 +    while (1) {
    819 +      print_socket_reply();
    820 +    }
    821 +  } else
    822 +    usage_error(prog_name, "Invalid argument '%s'", argv[i]);
    823 +
    824 +  return 0;
    825 +}
    826 diff --git a/dwm.c b/dwm.c
    827 index 9fd0286..c90c61a 100644
    828 --- a/dwm.c
    829 +++ b/dwm.c
    830 @@ -30,6 +30,7 @@
    831  #include <unistd.h>
    832  #include <sys/types.h>
    833  #include <sys/wait.h>
    834 +#include <sys/epoll.h>
    835  #include <X11/cursorfont.h>
    836  #include <X11/keysym.h>
    837  #include <X11/Xatom.h>
    838 @@ -67,9 +68,21 @@ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms *
    839  enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
    840         ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
    841  
    842 +typedef struct TagState TagState;
    843 +struct TagState {
    844 +	int selected;
    845 +	int occupied;
    846 +	int urgent;
    847 +};
    848 +
    849 +typedef struct ClientState ClientState;
    850 +struct ClientState {
    851 +	int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
    852 +};
    853 +
    854  typedef union {
    855 -	int i;
    856 -	unsigned int ui;
    857 +	long i;
    858 +	unsigned long ui;
    859  	float f;
    860  	const void *v;
    861  } Arg;
    862 @@ -97,6 +110,7 @@ struct Client {
    863  	Client *snext;
    864  	Monitor *mon;
    865  	Window win;
    866 +	ClientState prevstate;
    867  };
    868  
    869  typedef struct {
    870 @@ -111,8 +125,10 @@ typedef struct {
    871  	void (*arrange)(Monitor *);
    872  } Layout;
    873  
    874 +
    875  struct Monitor {
    876  	char ltsymbol[16];
    877 +	char lastltsymbol[16];
    878  	float mfact;
    879  	int nmaster;
    880  	int num;
    881 @@ -122,14 +138,17 @@ struct Monitor {
    882  	unsigned int seltags;
    883  	unsigned int sellt;
    884  	unsigned int tagset[2];
    885 +	TagState tagstate;
    886  	int showbar;
    887  	int topbar;
    888  	Client *clients;
    889  	Client *sel;
    890 +	Client *lastsel;
    891  	Client *stack;
    892  	Monitor *next;
    893  	Window barwin;
    894  	const Layout *lt[2];
    895 +	const Layout *lastlt;
    896  };
    897  
    898  typedef struct {
    899 @@ -175,6 +194,7 @@ static long getstate(Window w);
    900  static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
    901  static void grabbuttons(Client *c, int focused);
    902  static void grabkeys(void);
    903 +static int handlexevent(struct epoll_event *ev);
    904  static void incnmaster(const Arg *arg);
    905  static void keypress(XEvent *e);
    906  static void killclient(const Arg *arg);
    907 @@ -201,8 +221,10 @@ static void setclientstate(Client *c, long state);
    908  static void setfocus(Client *c);
    909  static void setfullscreen(Client *c, int fullscreen);
    910  static void setlayout(const Arg *arg);
    911 +static void setlayoutsafe(const Arg *arg);
    912  static void setmfact(const Arg *arg);
    913  static void setup(void);
    914 +static void setupepoll(void);
    915  static void seturgent(Client *c, int urg);
    916  static void showhide(Client *c);
    917  static void sigchld(int unused);
    918 @@ -261,17 +283,27 @@ static void (*handler[LASTEvent]) (XEvent *) = {
    919  	[UnmapNotify] = unmapnotify
    920  };
    921  static Atom wmatom[WMLast], netatom[NetLast];
    922 +static int epoll_fd;
    923 +static int dpy_fd;
    924  static int running = 1;
    925  static Cur *cursor[CurLast];
    926  static Clr **scheme;
    927  static Display *dpy;
    928  static Drw *drw;
    929 -static Monitor *mons, *selmon;
    930 +static Monitor *mons, *selmon, *lastselmon;
    931  static Window root, wmcheckwin;
    932  
    933 +#include "ipc.h"
    934 +
    935  /* configuration, allows nested code to access above variables */
    936  #include "config.h"
    937  
    938 +#ifdef VERSION
    939 +#include "IPCClient.c"
    940 +#include "yajl_dumps.c"
    941 +#include "ipc.c"
    942 +#endif
    943 +
    944  /* compile-time check if all tags fit into an unsigned int bit array. */
    945  struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
    946  
    947 @@ -492,6 +524,12 @@ cleanup(void)
    948  	XSync(dpy, False);
    949  	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
    950  	XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
    951 +
    952 +	ipc_cleanup();
    953 +
    954 +	if (close(epoll_fd) < 0) {
    955 +			fprintf(stderr, "Failed to close epoll file descriptor\n");
    956 +	}
    957  }
    958  
    959  void
    960 @@ -964,6 +1002,25 @@ grabkeys(void)
    961  	}
    962  }
    963  
    964 +int
    965 +handlexevent(struct epoll_event *ev)
    966 +{
    967 +	if (ev->events & EPOLLIN) {
    968 +		XEvent ev;
    969 +		while (running && XPending(dpy)) {
    970 +			XNextEvent(dpy, &ev);
    971 +			if (handler[ev.type]) {
    972 +				handler[ev.type](&ev); /* call handler */
    973 +				ipc_send_events(mons, &lastselmon, selmon);
    974 +			}
    975 +		}
    976 +	} else if (ev-> events & EPOLLHUP) {
    977 +		return -1;
    978 +	}
    979 +
    980 +	return 0;
    981 +}
    982 +
    983  void
    984  incnmaster(const Arg *arg)
    985  {
    986 @@ -1373,12 +1430,40 @@ restack(Monitor *m)
    987  void
    988  run(void)
    989  {
    990 -	XEvent ev;
    991 -	/* main event loop */
    992 +	int event_count = 0;
    993 +	const int MAX_EVENTS = 10;
    994 +	struct epoll_event events[MAX_EVENTS];
    995 +
    996  	XSync(dpy, False);
    997 -	while (running && !XNextEvent(dpy, &ev))
    998 -		if (handler[ev.type])
    999 -			handler[ev.type](&ev); /* call handler */
   1000 +
   1001 +	/* main event loop */
   1002 +	while (running) {
   1003 +		event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
   1004 +
   1005 +		for (int i = 0; i < event_count; i++) {
   1006 +			int event_fd = events[i].data.fd;
   1007 +			DEBUG("Got event from fd %d\n", event_fd);
   1008 +
   1009 +			if (event_fd == dpy_fd) {
   1010 +				// -1 means EPOLLHUP
   1011 +				if (handlexevent(events + i) == -1)
   1012 +					return;
   1013 +			} else if (event_fd == ipc_get_sock_fd()) {
   1014 +				ipc_handle_socket_epoll_event(events + i);
   1015 +			} else if (ipc_is_client_registered(event_fd)){
   1016 +				if (ipc_handle_client_epoll_event(events + i, mons, &lastselmon, selmon,
   1017 +							tags, LENGTH(tags), layouts, LENGTH(layouts)) < 0) {
   1018 +					fprintf(stderr, "Error handling IPC event on fd %d\n", event_fd);
   1019 +				}
   1020 +			} else {
   1021 +				fprintf(stderr, "Got event from unknown fd %d, ptr %p, u32 %d, u64 %lu",
   1022 +						event_fd, events[i].data.ptr, events[i].data.u32,
   1023 +						events[i].data.u64);
   1024 +				fprintf(stderr, " with events %d\n", events[i].events);
   1025 +				return;
   1026 +			}
   1027 +		}
   1028 +	}
   1029  }
   1030  
   1031  void
   1032 @@ -1512,6 +1597,18 @@ setlayout(const Arg *arg)
   1033  		drawbar(selmon);
   1034  }
   1035  
   1036 +void
   1037 +setlayoutsafe(const Arg *arg)
   1038 +{
   1039 +	const Layout *ltptr = (Layout *)arg->v;
   1040 +	if (ltptr == 0)
   1041 +			setlayout(arg);
   1042 +	for (int i = 0; i < LENGTH(layouts); i++) {
   1043 +		if (ltptr == &layouts[i])
   1044 +			setlayout(arg);
   1045 +	}
   1046 +}
   1047 +
   1048  /* arg > 1.0 will set mfact absolutely */
   1049  void
   1050  setmfact(const Arg *arg)
   1051 @@ -1595,8 +1692,37 @@ setup(void)
   1052  	XSelectInput(dpy, root, wa.event_mask);
   1053  	grabkeys();
   1054  	focus(NULL);
   1055 +	setupepoll();
   1056  }
   1057  
   1058 +void
   1059 +setupepoll(void)
   1060 +{
   1061 +	epoll_fd = epoll_create1(0);
   1062 +	dpy_fd = ConnectionNumber(dpy);
   1063 +	struct epoll_event dpy_event;
   1064 +
   1065 +	// Initialize struct to 0
   1066 +	memset(&dpy_event, 0, sizeof(dpy_event));
   1067 +
   1068 +	DEBUG("Display socket is fd %d\n", dpy_fd);
   1069 +
   1070 +	if (epoll_fd == -1) {
   1071 +		fputs("Failed to create epoll file descriptor", stderr);
   1072 +	}
   1073 +
   1074 +	dpy_event.events = EPOLLIN;
   1075 +	dpy_event.data.fd = dpy_fd;
   1076 +	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dpy_fd, &dpy_event)) {
   1077 +		fputs("Failed to add display file descriptor to epoll", stderr);
   1078 +		close(epoll_fd);
   1079 +		exit(1);
   1080 +	}
   1081 +
   1082 +	if (ipc_init(ipcsockpath, epoll_fd, ipccommands, LENGTH(ipccommands)) < 0) {
   1083 +		fputs("Failed to initialize IPC\n", stderr);
   1084 +	}
   1085 +}
   1086  
   1087  void
   1088  seturgent(Client *c, int urg)
   1089 @@ -1998,10 +2124,18 @@ updatestatus(void)
   1090  void
   1091  updatetitle(Client *c)
   1092  {
   1093 +	char oldname[sizeof(c->name)];
   1094 +	strcpy(oldname, c->name);
   1095 +
   1096  	if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
   1097  		gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
   1098  	if (c->name[0] == '\0') /* hack to mark broken clients */
   1099  		strcpy(c->name, broken);
   1100 +
   1101 +	for (Monitor *m = mons; m; m = m->next) {
   1102 +		if (m->sel == c && strcmp(oldname, c->name) != 0)
   1103 +			ipc_focused_title_change_event(m->num, c->win, oldname, c->name);
   1104 +	}
   1105  }
   1106  
   1107  void
   1108 diff --git a/ipc.c b/ipc.c
   1109 new file mode 100644
   1110 index 0000000..e527e23
   1111 --- /dev/null
   1112 +++ b/ipc.c
   1113 @@ -0,0 +1,1202 @@
   1114 +#include "ipc.h"
   1115 +
   1116 +#include <errno.h>
   1117 +#include <fcntl.h>
   1118 +#include <inttypes.h>
   1119 +#include <stdarg.h>
   1120 +#include <stdio.h>
   1121 +#include <stdlib.h>
   1122 +#include <sys/epoll.h>
   1123 +#include <sys/socket.h>
   1124 +#include <sys/un.h>
   1125 +#include <unistd.h>
   1126 +#include <yajl/yajl_gen.h>
   1127 +#include <yajl/yajl_tree.h>
   1128 +
   1129 +#include "util.h"
   1130 +#include "yajl_dumps.h"
   1131 +
   1132 +static struct sockaddr_un sockaddr;
   1133 +static struct epoll_event sock_epoll_event;
   1134 +static IPCClientList ipc_clients = NULL;
   1135 +static int epoll_fd = -1;
   1136 +static int sock_fd = -1;
   1137 +static IPCCommand *ipc_commands;
   1138 +static unsigned int ipc_commands_len;
   1139 +// Max size is 1 MB
   1140 +static const uint32_t MAX_MESSAGE_SIZE = 1000000;
   1141 +static const int IPC_SOCKET_BACKLOG = 5;
   1142 +
   1143 +/**
   1144 + * Create IPC socket at specified path and return file descriptor to socket.
   1145 + * This initializes the static variable sockaddr.
   1146 + */
   1147 +static int
   1148 +ipc_create_socket(const char *filename)
   1149 +{
   1150 +  char *normal_filename;
   1151 +  char *parent;
   1152 +  const size_t addr_size = sizeof(struct sockaddr_un);
   1153 +  const int sock_type = SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC;
   1154 +
   1155 +  normalizepath(filename, &normal_filename);
   1156 +
   1157 +  // In case socket file exists
   1158 +  unlink(normal_filename);
   1159 +
   1160 +  // For portability clear the addr structure, since some implementations have
   1161 +  // nonstandard fields in the structure
   1162 +  memset(&sockaddr, 0, addr_size);
   1163 +
   1164 +  parentdir(normal_filename, &parent);
   1165 +  // Create parent directories
   1166 +  mkdirp(parent);
   1167 +  free(parent);
   1168 +
   1169 +  sockaddr.sun_family = AF_LOCAL;
   1170 +  strcpy(sockaddr.sun_path, normal_filename);
   1171 +  free(normal_filename);
   1172 +
   1173 +  sock_fd = socket(AF_LOCAL, sock_type, 0);
   1174 +  if (sock_fd == -1) {
   1175 +    fputs("Failed to create socket\n", stderr);
   1176 +    return -1;
   1177 +  }
   1178 +
   1179 +  DEBUG("Created socket at %s\n", sockaddr.sun_path);
   1180 +
   1181 +  if (bind(sock_fd, (const struct sockaddr *)&sockaddr, addr_size) == -1) {
   1182 +    fputs("Failed to bind socket\n", stderr);
   1183 +    return -1;
   1184 +  }
   1185 +
   1186 +  DEBUG("Socket binded\n");
   1187 +
   1188 +  if (listen(sock_fd, IPC_SOCKET_BACKLOG) < 0) {
   1189 +    fputs("Failed to listen for connections on socket\n", stderr);
   1190 +    return -1;
   1191 +  }
   1192 +
   1193 +  DEBUG("Now listening for connections on socket\n");
   1194 +
   1195 +  return sock_fd;
   1196 +}
   1197 +
   1198 +/**
   1199 + * Internal function used to receive IPC messages from a given file descriptor.
   1200 + *
   1201 + * Returns -1 on error reading (could be EAGAIN or EINTR)
   1202 + * Returns -2 if EOF before header could be read
   1203 + * Returns -3 if invalid IPC header
   1204 + * Returns -4 if message length exceeds MAX_MESSAGE_SIZE
   1205 + */
   1206 +static int
   1207 +ipc_recv_message(int fd, uint8_t *msg_type, uint32_t *reply_size,
   1208 +                 uint8_t **reply)
   1209 +{
   1210 +  uint32_t read_bytes = 0;
   1211 +  const int32_t to_read = sizeof(dwm_ipc_header_t);
   1212 +  char header[to_read];
   1213 +  char *walk = header;
   1214 +
   1215 +  // Try to read header
   1216 +  while (read_bytes < to_read) {
   1217 +    const ssize_t n = read(fd, header + read_bytes, to_read - read_bytes);
   1218 +
   1219 +    if (n == 0) {
   1220 +      if (read_bytes == 0) {
   1221 +        fprintf(stderr, "Unexpectedly reached EOF while reading header.");
   1222 +        fprintf(stderr,
   1223 +                "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n",
   1224 +                read_bytes, to_read);
   1225 +        return -2;
   1226 +      } else {
   1227 +        fprintf(stderr, "Unexpectedly reached EOF while reading header.");
   1228 +        fprintf(stderr,
   1229 +                "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n",
   1230 +                read_bytes, to_read);
   1231 +        return -3;
   1232 +      }
   1233 +    } else if (n == -1) {
   1234 +      // errno will still be set
   1235 +      return -1;
   1236 +    }
   1237 +
   1238 +    read_bytes += n;
   1239 +  }
   1240 +
   1241 +  // Check if magic string in header matches
   1242 +  if (memcmp(walk, IPC_MAGIC, IPC_MAGIC_LEN) != 0) {
   1243 +    fprintf(stderr, "Invalid magic string. Got '%.*s', expected '%s'\n",
   1244 +            IPC_MAGIC_LEN, walk, IPC_MAGIC);
   1245 +    return -3;
   1246 +  }
   1247 +
   1248 +  walk += IPC_MAGIC_LEN;
   1249 +
   1250 +  // Extract reply size
   1251 +  memcpy(reply_size, walk, sizeof(uint32_t));
   1252 +  walk += sizeof(uint32_t);
   1253 +
   1254 +  if (*reply_size > MAX_MESSAGE_SIZE) {
   1255 +    fprintf(stderr, "Message too long: %" PRIu32 " bytes. ", *reply_size);
   1256 +    fprintf(stderr, "Maximum message size is: %d\n", MAX_MESSAGE_SIZE);
   1257 +    return -4;
   1258 +  }
   1259 +
   1260 +  // Extract message type
   1261 +  memcpy(msg_type, walk, sizeof(uint8_t));
   1262 +  walk += sizeof(uint8_t);
   1263 +
   1264 +  if (*reply_size > 0)
   1265 +    (*reply) = malloc(*reply_size);
   1266 +  else
   1267 +    return 0;
   1268 +
   1269 +  read_bytes = 0;
   1270 +  while (read_bytes < *reply_size) {
   1271 +    const ssize_t n = read(fd, *reply + read_bytes, *reply_size - read_bytes);
   1272 +
   1273 +    if (n == 0) {
   1274 +      fprintf(stderr, "Unexpectedly reached EOF while reading payload.");
   1275 +      fprintf(stderr, "Read %" PRIu32 " bytes, expected %" PRIu32 " bytes.\n",
   1276 +              read_bytes, *reply_size);
   1277 +      free(*reply);
   1278 +      return -2;
   1279 +    } else if (n == -1) {
   1280 +      // TODO: Should we return and wait for another epoll event?
   1281 +      // This would require saving the partial read in some way.
   1282 +      if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;
   1283 +
   1284 +      free(*reply);
   1285 +      return -1;
   1286 +    }
   1287 +
   1288 +    read_bytes += n;
   1289 +  }
   1290 +
   1291 +  return 0;
   1292 +}
   1293 +
   1294 +/**
   1295 + * Internal function used to write a buffer to a file descriptor
   1296 + *
   1297 + * Returns number of bytes written if successful write
   1298 + * Returns 0 if no bytes were written due to EAGAIN or EWOULDBLOCK
   1299 + * Returns -1 on unknown error trying to write, errno will carry over from
   1300 + *   write() call
   1301 + */
   1302 +static ssize_t
   1303 +ipc_write_message(int fd, const void *buf, size_t count)
   1304 +{
   1305 +  size_t written = 0;
   1306 +
   1307 +  while (written < count) {
   1308 +    const ssize_t n = write(fd, (uint8_t *)buf + written, count - written);
   1309 +
   1310 +    if (n == -1) {
   1311 +      if (errno == EAGAIN || errno == EWOULDBLOCK)
   1312 +        return written;
   1313 +      else if (errno == EINTR)
   1314 +        continue;
   1315 +      else
   1316 +        return n;
   1317 +    }
   1318 +
   1319 +    written += n;
   1320 +    DEBUG("Wrote %zu/%zu to client at fd %d\n", written, count, fd);
   1321 +  }
   1322 +
   1323 +  return written;
   1324 +}
   1325 +
   1326 +/**
   1327 + * Initialization for generic event message. This is used to allocate the yajl
   1328 + * handle, set yajl options, and in the future any other initialization that
   1329 + * should occur for event messages.
   1330 + */
   1331 +static void
   1332 +ipc_event_init_message(yajl_gen *gen)
   1333 +{
   1334 +  *gen = yajl_gen_alloc(NULL);
   1335 +  yajl_gen_config(*gen, yajl_gen_beautify, 1);
   1336 +}
   1337 +
   1338 +/**
   1339 + * Prepares buffers of IPC subscribers of specified event using buffer from yajl
   1340 + * handle.
   1341 + */
   1342 +static void
   1343 +ipc_event_prepare_send_message(yajl_gen gen, IPCEvent event)
   1344 +{
   1345 +  const unsigned char *buffer;
   1346 +  size_t len = 0;
   1347 +
   1348 +  yajl_gen_get_buf(gen, &buffer, &len);
   1349 +  len++;  // For null char
   1350 +
   1351 +  for (IPCClient *c = ipc_clients; c; c = c->next) {
   1352 +    if (c->subscriptions & event) {
   1353 +      DEBUG("Sending selected client change event to fd %d\n", c->fd);
   1354 +      ipc_prepare_send_message(c, IPC_TYPE_EVENT, len, (char *)buffer);
   1355 +    }
   1356 +  }
   1357 +
   1358 +  // Not documented, but this frees temp_buffer
   1359 +  yajl_gen_free(gen);
   1360 +}
   1361 +
   1362 +/**
   1363 + * Initialization for generic reply message. This is used to allocate the yajl
   1364 + * handle, set yajl options, and in the future any other initialization that
   1365 + * should occur for reply messages.
   1366 + */
   1367 +static void
   1368 +ipc_reply_init_message(yajl_gen *gen)
   1369 +{
   1370 +  *gen = yajl_gen_alloc(NULL);
   1371 +  yajl_gen_config(*gen, yajl_gen_beautify, 1);
   1372 +}
   1373 +
   1374 +/**
   1375 + * Prepares the IPC client's buffer with a message using the buffer of the yajl
   1376 + * handle.
   1377 + */
   1378 +static void
   1379 +ipc_reply_prepare_send_message(yajl_gen gen, IPCClient *c,
   1380 +                               IPCMessageType msg_type)
   1381 +{
   1382 +  const unsigned char *buffer;
   1383 +  size_t len = 0;
   1384 +
   1385 +  yajl_gen_get_buf(gen, &buffer, &len);
   1386 +  len++;  // For null char
   1387 +
   1388 +  ipc_prepare_send_message(c, msg_type, len, (const char *)buffer);
   1389 +
   1390 +  // Not documented, but this frees temp_buffer
   1391 +  yajl_gen_free(gen);
   1392 +}
   1393 +
   1394 +/**
   1395 + * Find the IPCCommand with the specified name
   1396 + *
   1397 + * Returns 0 if a command with the specified name was found
   1398 + * Returns -1 if a command with the specified name could not be found
   1399 + */
   1400 +static int
   1401 +ipc_get_ipc_command(const char *name, IPCCommand *ipc_command)
   1402 +{
   1403 +  for (int i = 0; i < ipc_commands_len; i++) {
   1404 +    if (strcmp(ipc_commands[i].name, name) == 0) {
   1405 +      *ipc_command = ipc_commands[i];
   1406 +      return 0;
   1407 +    }
   1408 +  }
   1409 +
   1410 +  return -1;
   1411 +}
   1412 +
   1413 +/**
   1414 + * Parse a IPC_TYPE_RUN_COMMAND message from a client. This function extracts
   1415 + * the arguments, argument count, argument types, and command name and returns
   1416 + * the parsed information as an IPCParsedCommand. If this function returns
   1417 + * successfully, the parsed_command must be freed using
   1418 + * ipc_free_parsed_command_members.
   1419 + *
   1420 + * Returns 0 if the message was successfully parsed
   1421 + * Returns -1 otherwise
   1422 + */
   1423 +static int
   1424 +ipc_parse_run_command(char *msg, IPCParsedCommand *parsed_command)
   1425 +{
   1426 +  char error_buffer[1000];
   1427 +  yajl_val parent = yajl_tree_parse(msg, error_buffer, 1000);
   1428 +
   1429 +  if (parent == NULL) {
   1430 +    fputs("Failed to parse command from client\n", stderr);
   1431 +    fprintf(stderr, "%s\n", error_buffer);
   1432 +    fprintf(stderr, "Tried to parse: %s\n", msg);
   1433 +    return -1;
   1434 +  }
   1435 +
   1436 +  // Format:
   1437 +  // {
   1438 +  //   "command": "<command name>"
   1439 +  //   "args": [ "arg1", "arg2", ... ]
   1440 +  // }
   1441 +  const char *command_path[] = {"command", 0};
   1442 +  yajl_val command_val = yajl_tree_get(parent, command_path, yajl_t_string);
   1443 +
   1444 +  if (command_val == NULL) {
   1445 +    fputs("No command key found in client message\n", stderr);
   1446 +    yajl_tree_free(parent);
   1447 +    return -1;
   1448 +  }
   1449 +
   1450 +  const char *command_name = YAJL_GET_STRING(command_val);
   1451 +  size_t command_name_len = strlen(command_name);
   1452 +  parsed_command->name = (char *)malloc((command_name_len + 1) * sizeof(char));
   1453 +  strcpy(parsed_command->name, command_name);
   1454 +
   1455 +  DEBUG("Received command: %s\n", parsed_command->name);
   1456 +
   1457 +  const char *args_path[] = {"args", 0};
   1458 +  yajl_val args_val = yajl_tree_get(parent, args_path, yajl_t_array);
   1459 +
   1460 +  if (args_val == NULL) {
   1461 +    fputs("No args key found in client message\n", stderr);
   1462 +    yajl_tree_free(parent);
   1463 +    return -1;
   1464 +  }
   1465 +
   1466 +  unsigned int *argc = &parsed_command->argc;
   1467 +  Arg **args = &parsed_command->args;
   1468 +  ArgType **arg_types = &parsed_command->arg_types;
   1469 +
   1470 +  *argc = args_val->u.array.len;
   1471 +
   1472 +  // If no arguments are specified, make a dummy argument to pass to the
   1473 +  // function. This is just the way dwm's void(Arg*) functions are setup.
   1474 +  if (*argc == 0) {
   1475 +    *args = (Arg *)malloc(sizeof(Arg));
   1476 +    *arg_types = (ArgType *)malloc(sizeof(ArgType));
   1477 +    (*arg_types)[0] = ARG_TYPE_NONE;
   1478 +    (*args)[0].f = 0;
   1479 +    (*argc)++;
   1480 +  } else if (*argc > 0) {
   1481 +    *args = (Arg *)calloc(*argc, sizeof(Arg));
   1482 +    *arg_types = (ArgType *)malloc(*argc * sizeof(ArgType));
   1483 +
   1484 +    for (int i = 0; i < *argc; i++) {
   1485 +      yajl_val arg_val = args_val->u.array.values[i];
   1486 +
   1487 +      if (YAJL_IS_NUMBER(arg_val)) {
   1488 +        if (YAJL_IS_INTEGER(arg_val)) {
   1489 +          // Any values below 0 must be a signed int
   1490 +          if (YAJL_GET_INTEGER(arg_val) < 0) {
   1491 +            (*args)[i].i = YAJL_GET_INTEGER(arg_val);
   1492 +            (*arg_types)[i] = ARG_TYPE_SINT;
   1493 +            DEBUG("i=%ld\n", (*args)[i].i);
   1494 +            // Any values above 0 should be an unsigned int
   1495 +          } else if (YAJL_GET_INTEGER(arg_val) >= 0) {
   1496 +            (*args)[i].ui = YAJL_GET_INTEGER(arg_val);
   1497 +            (*arg_types)[i] = ARG_TYPE_UINT;
   1498 +            DEBUG("ui=%ld\n", (*args)[i].i);
   1499 +          }
   1500 +          // If the number is not an integer, it must be a float
   1501 +        } else {
   1502 +          (*args)[i].f = (float)YAJL_GET_DOUBLE(arg_val);
   1503 +          (*arg_types)[i] = ARG_TYPE_FLOAT;
   1504 +          DEBUG("f=%f\n", (*args)[i].f);
   1505 +          // If argument is not a number, it must be a string
   1506 +        }
   1507 +      } else if (YAJL_IS_STRING(arg_val)) {
   1508 +        char *arg_s = YAJL_GET_STRING(arg_val);
   1509 +        size_t arg_s_size = (strlen(arg_s) + 1) * sizeof(char);
   1510 +        (*args)[i].v = (char *)malloc(arg_s_size);
   1511 +        (*arg_types)[i] = ARG_TYPE_STR;
   1512 +        strcpy((char *)(*args)[i].v, arg_s);
   1513 +      }
   1514 +    }
   1515 +  }
   1516 +
   1517 +  yajl_tree_free(parent);
   1518 +
   1519 +  return 0;
   1520 +}
   1521 +
   1522 +/**
   1523 + * Free the members of a IPCParsedCommand struct
   1524 + */
   1525 +static void
   1526 +ipc_free_parsed_command_members(IPCParsedCommand *command)
   1527 +{
   1528 +  for (int i = 0; i < command->argc; i++) {
   1529 +    if (command->arg_types[i] == ARG_TYPE_STR) free((void *)command->args[i].v);
   1530 +  }
   1531 +  free(command->args);
   1532 +  free(command->arg_types);
   1533 +  free(command->name);
   1534 +}
   1535 +
   1536 +/**
   1537 + * Check if the given arguments are the correct length and type. Also do any
   1538 + * casting to correct the types.
   1539 + *
   1540 + * Returns 0 if the arguments were the correct length and types
   1541 + * Returns -1 if the argument count doesn't match
   1542 + * Returns -2 if the argument types don't match
   1543 + */
   1544 +static int
   1545 +ipc_validate_run_command(IPCParsedCommand *parsed, const IPCCommand actual)
   1546 +{
   1547 +  if (actual.argc != parsed->argc) return -1;
   1548 +
   1549 +  for (int i = 0; i < parsed->argc; i++) {
   1550 +    ArgType ptype = parsed->arg_types[i];
   1551 +    ArgType atype = actual.arg_types[i];
   1552 +
   1553 +    if (ptype != atype) {
   1554 +      if (ptype == ARG_TYPE_UINT && atype == ARG_TYPE_PTR)
   1555 +        // If this argument is supposed to be a void pointer, cast it
   1556 +        parsed->args[i].v = (void *)parsed->args[i].ui;
   1557 +      else if (ptype == ARG_TYPE_UINT && atype == ARG_TYPE_SINT)
   1558 +        // If this argument is supposed to be a signed int, cast it
   1559 +        parsed->args[i].i = parsed->args[i].ui;
   1560 +      else
   1561 +        return -2;
   1562 +    }
   1563 +  }
   1564 +
   1565 +  return 0;
   1566 +}
   1567 +
   1568 +/**
   1569 + * Convert event name to their IPCEvent equivalent enum value
   1570 + *
   1571 + * Returns 0 if a valid event name was given
   1572 + * Returns -1 otherwise
   1573 + */
   1574 +static int
   1575 +ipc_event_stoi(const char *subscription, IPCEvent *event)
   1576 +{
   1577 +  if (strcmp(subscription, "tag_change_event") == 0)
   1578 +    *event = IPC_EVENT_TAG_CHANGE;
   1579 +  else if (strcmp(subscription, "client_focus_change_event") == 0)
   1580 +    *event = IPC_EVENT_CLIENT_FOCUS_CHANGE;
   1581 +  else if (strcmp(subscription, "layout_change_event") == 0)
   1582 +    *event = IPC_EVENT_LAYOUT_CHANGE;
   1583 +  else if (strcmp(subscription, "monitor_focus_change_event") == 0)
   1584 +    *event = IPC_EVENT_MONITOR_FOCUS_CHANGE;
   1585 +  else if (strcmp(subscription, "focused_title_change_event") == 0)
   1586 +    *event = IPC_EVENT_FOCUSED_TITLE_CHANGE;
   1587 +  else if (strcmp(subscription, "focused_state_change_event") == 0)
   1588 +    *event = IPC_EVENT_FOCUSED_STATE_CHANGE;
   1589 +  else
   1590 +    return -1;
   1591 +  return 0;
   1592 +}
   1593 +
   1594 +/**
   1595 + * Parse a IPC_TYPE_SUBSCRIBE message from a client. This function extracts the
   1596 + * event name and the subscription action from the message.
   1597 + *
   1598 + * Returns 0 if message was successfully parsed
   1599 + * Returns -1 otherwise
   1600 + */
   1601 +static int
   1602 +ipc_parse_subscribe(const char *msg, IPCSubscriptionAction *subscribe,
   1603 +                    IPCEvent *event)
   1604 +{
   1605 +  char error_buffer[100];
   1606 +  yajl_val parent = yajl_tree_parse((char *)msg, error_buffer, 100);
   1607 +
   1608 +  if (parent == NULL) {
   1609 +    fputs("Failed to parse command from client\n", stderr);
   1610 +    fprintf(stderr, "%s\n", error_buffer);
   1611 +    return -1;
   1612 +  }
   1613 +
   1614 +  // Format:
   1615 +  // {
   1616 +  //   "event": "<event name>"
   1617 +  //   "action": "<subscribe|unsubscribe>"
   1618 +  // }
   1619 +  const char *event_path[] = {"event", 0};
   1620 +  yajl_val event_val = yajl_tree_get(parent, event_path, yajl_t_string);
   1621 +
   1622 +  if (event_val == NULL) {
   1623 +    fputs("No 'event' key found in client message\n", stderr);
   1624 +    return -1;
   1625 +  }
   1626 +
   1627 +  const char *event_str = YAJL_GET_STRING(event_val);
   1628 +  DEBUG("Received event: %s\n", event_str);
   1629 +
   1630 +  if (ipc_event_stoi(event_str, event) < 0) return -1;
   1631 +
   1632 +  const char *action_path[] = {"action", 0};
   1633 +  yajl_val action_val = yajl_tree_get(parent, action_path, yajl_t_string);
   1634 +
   1635 +  if (action_val == NULL) {
   1636 +    fputs("No 'action' key found in client message\n", stderr);
   1637 +    return -1;
   1638 +  }
   1639 +
   1640 +  const char *action = YAJL_GET_STRING(action_val);
   1641 +
   1642 +  if (strcmp(action, "subscribe") == 0)
   1643 +    *subscribe = IPC_ACTION_SUBSCRIBE;
   1644 +  else if (strcmp(action, "unsubscribe") == 0)
   1645 +    *subscribe = IPC_ACTION_UNSUBSCRIBE;
   1646 +  else {
   1647 +    fputs("Invalid action specified for subscription\n", stderr);
   1648 +    return -1;
   1649 +  }
   1650 +
   1651 +  yajl_tree_free(parent);
   1652 +
   1653 +  return 0;
   1654 +}
   1655 +
   1656 +/**
   1657 + * Parse an IPC_TYPE_GET_DWM_CLIENT message from a client. This function
   1658 + * extracts the window id from the message.
   1659 + *
   1660 + * Returns 0 if message was successfully parsed
   1661 + * Returns -1 otherwise
   1662 + */
   1663 +static int
   1664 +ipc_parse_get_dwm_client(const char *msg, Window *win)
   1665 +{
   1666 +  char error_buffer[100];
   1667 +
   1668 +  yajl_val parent = yajl_tree_parse(msg, error_buffer, 100);
   1669 +
   1670 +  if (parent == NULL) {
   1671 +    fputs("Failed to parse message from client\n", stderr);
   1672 +    fprintf(stderr, "%s\n", error_buffer);
   1673 +    return -1;
   1674 +  }
   1675 +
   1676 +  // Format:
   1677 +  // {
   1678 +  //   "client_window_id": <client window id>
   1679 +  // }
   1680 +  const char *win_path[] = {"client_window_id", 0};
   1681 +  yajl_val win_val = yajl_tree_get(parent, win_path, yajl_t_number);
   1682 +
   1683 +  if (win_val == NULL) {
   1684 +    fputs("No client window id found in client message\n", stderr);
   1685 +    return -1;
   1686 +  }
   1687 +
   1688 +  *win = YAJL_GET_INTEGER(win_val);
   1689 +
   1690 +  yajl_tree_free(parent);
   1691 +
   1692 +  return 0;
   1693 +}
   1694 +
   1695 +/**
   1696 + * Called when an IPC_TYPE_RUN_COMMAND message is received from a client. This
   1697 + * function parses, executes the given command, and prepares a reply message to
   1698 + * the client indicating success/failure.
   1699 + *
   1700 + * NOTE: There is currently no check for argument validity beyond the number of
   1701 + * arguments given and types of arguments. There is also no way to check if the
   1702 + * function succeeded based on dwm's void(const Arg*) function types. Pointer
   1703 + * arguments can cause crashes if they are not validated in the function itself.
   1704 + *
   1705 + * Returns 0 if message was successfully parsed
   1706 + * Returns -1 on failure parsing message
   1707 + */
   1708 +static int
   1709 +ipc_run_command(IPCClient *ipc_client, char *msg)
   1710 +{
   1711 +  IPCParsedCommand parsed_command;
   1712 +  IPCCommand ipc_command;
   1713 +
   1714 +  // Initialize struct
   1715 +  memset(&parsed_command, 0, sizeof(IPCParsedCommand));
   1716 +
   1717 +  if (ipc_parse_run_command(msg, &parsed_command) < 0) {
   1718 +    ipc_prepare_reply_failure(ipc_client, IPC_TYPE_RUN_COMMAND,
   1719 +                              "Failed to parse run command");
   1720 +    return -1;
   1721 +  }
   1722 +
   1723 +  if (ipc_get_ipc_command(parsed_command.name, &ipc_command) < 0) {
   1724 +    ipc_prepare_reply_failure(ipc_client, IPC_TYPE_RUN_COMMAND,
   1725 +                              "Command %s not found", parsed_command.name);
   1726 +    ipc_free_parsed_command_members(&parsed_command);
   1727 +    return -1;
   1728 +  }
   1729 +
   1730 +  int res = ipc_validate_run_command(&parsed_command, ipc_command);
   1731 +  if (res < 0) {
   1732 +    if (res == -1)
   1733 +      ipc_prepare_reply_failure(ipc_client, IPC_TYPE_RUN_COMMAND,
   1734 +                                "%u arguments provided, %u expected",
   1735 +                                parsed_command.argc, ipc_command.argc);
   1736 +    else if (res == -2)
   1737 +      ipc_prepare_reply_failure(ipc_client, IPC_TYPE_RUN_COMMAND,
   1738 +                                "Type mismatch");
   1739 +    ipc_free_parsed_command_members(&parsed_command);
   1740 +    return -1;
   1741 +  }
   1742 +
   1743 +  if (parsed_command.argc == 1)
   1744 +    ipc_command.func.single_param(parsed_command.args);
   1745 +  else if (parsed_command.argc > 1)
   1746 +    ipc_command.func.array_param(parsed_command.args, parsed_command.argc);
   1747 +
   1748 +  DEBUG("Called function for command %s\n", parsed_command.name);
   1749 +
   1750 +  ipc_free_parsed_command_members(&parsed_command);
   1751 +
   1752 +  ipc_prepare_reply_success(ipc_client, IPC_TYPE_RUN_COMMAND);
   1753 +  return 0;
   1754 +}
   1755 +
   1756 +/**
   1757 + * Called when an IPC_TYPE_GET_MONITORS message is received from a client. It
   1758 + * prepares a reply with the properties of all of the monitors in JSON.
   1759 + */
   1760 +static void
   1761 +ipc_get_monitors(IPCClient *c, Monitor *mons, Monitor *selmon)
   1762 +{
   1763 +  yajl_gen gen;
   1764 +  ipc_reply_init_message(&gen);
   1765 +  dump_monitors(gen, mons, selmon);
   1766 +
   1767 +  ipc_reply_prepare_send_message(gen, c, IPC_TYPE_GET_MONITORS);
   1768 +}
   1769 +
   1770 +/**
   1771 + * Called when an IPC_TYPE_GET_TAGS message is received from a client. It
   1772 + * prepares a reply with info about all the tags in JSON.
   1773 + */
   1774 +static void
   1775 +ipc_get_tags(IPCClient *c, const char *tags[], const int tags_len)
   1776 +{
   1777 +  yajl_gen gen;
   1778 +  ipc_reply_init_message(&gen);
   1779 +
   1780 +  dump_tags(gen, tags, tags_len);
   1781 +
   1782 +  ipc_reply_prepare_send_message(gen, c, IPC_TYPE_GET_TAGS);
   1783 +}
   1784 +
   1785 +/**
   1786 + * Called when an IPC_TYPE_GET_LAYOUTS message is received from a client. It
   1787 + * prepares a reply with a JSON array of available layouts
   1788 + */
   1789 +static void
   1790 +ipc_get_layouts(IPCClient *c, const Layout layouts[], const int layouts_len)
   1791 +{
   1792 +  yajl_gen gen;
   1793 +  ipc_reply_init_message(&gen);
   1794 +
   1795 +  dump_layouts(gen, layouts, layouts_len);
   1796 +
   1797 +  ipc_reply_prepare_send_message(gen, c, IPC_TYPE_GET_LAYOUTS);
   1798 +}
   1799 +
   1800 +/**
   1801 + * Called when an IPC_TYPE_GET_DWM_CLIENT message is received from a client. It
   1802 + * prepares a JSON reply with the properties of the client with the specified
   1803 + * window XID.
   1804 + *
   1805 + * Returns 0 if the message was successfully parsed and if the client with the
   1806 + *   specified window XID was found
   1807 + * Returns -1 if the message could not be parsed
   1808 + */
   1809 +static int
   1810 +ipc_get_dwm_client(IPCClient *ipc_client, const char *msg, const Monitor *mons)
   1811 +{
   1812 +  Window win;
   1813 +
   1814 +  if (ipc_parse_get_dwm_client(msg, &win) < 0) return -1;
   1815 +
   1816 +  // Find client with specified window XID
   1817 +  for (const Monitor *m = mons; m; m = m->next)
   1818 +    for (Client *c = m->clients; c; c = c->next)
   1819 +      if (c->win == win) {
   1820 +        yajl_gen gen;
   1821 +        ipc_reply_init_message(&gen);
   1822 +
   1823 +        dump_client(gen, c);
   1824 +
   1825 +        ipc_reply_prepare_send_message(gen, ipc_client,
   1826 +                                       IPC_TYPE_GET_DWM_CLIENT);
   1827 +
   1828 +        return 0;
   1829 +      }
   1830 +
   1831 +  ipc_prepare_reply_failure(ipc_client, IPC_TYPE_GET_DWM_CLIENT,
   1832 +                            "Client with window id %d not found", win);
   1833 +  return -1;
   1834 +}
   1835 +
   1836 +/**
   1837 + * Called when an IPC_TYPE_SUBSCRIBE message is received from a client. It
   1838 + * subscribes/unsubscribes the client from the specified event and replies with
   1839 + * the result.
   1840 + *
   1841 + * Returns 0 if the message was successfully parsed.
   1842 + * Returns -1 if the message could not be parsed
   1843 + */
   1844 +static int
   1845 +ipc_subscribe(IPCClient *c, const char *msg)
   1846 +{
   1847 +  IPCSubscriptionAction action = IPC_ACTION_SUBSCRIBE;
   1848 +  IPCEvent event = 0;
   1849 +
   1850 +  if (ipc_parse_subscribe(msg, &action, &event)) {
   1851 +    ipc_prepare_reply_failure(c, IPC_TYPE_SUBSCRIBE, "Event does not exist");
   1852 +    return -1;
   1853 +  }
   1854 +
   1855 +  if (action == IPC_ACTION_SUBSCRIBE) {
   1856 +    DEBUG("Subscribing client on fd %d to %d\n", c->fd, event);
   1857 +    c->subscriptions |= event;
   1858 +  } else if (action == IPC_ACTION_UNSUBSCRIBE) {
   1859 +    DEBUG("Unsubscribing client on fd %d to %d\n", c->fd, event);
   1860 +    c->subscriptions ^= event;
   1861 +  } else {
   1862 +    ipc_prepare_reply_failure(c, IPC_TYPE_SUBSCRIBE,
   1863 +                              "Invalid subscription action");
   1864 +    return -1;
   1865 +  }
   1866 +
   1867 +  ipc_prepare_reply_success(c, IPC_TYPE_SUBSCRIBE);
   1868 +  return 0;
   1869 +}
   1870 +
   1871 +int
   1872 +ipc_init(const char *socket_path, const int p_epoll_fd, IPCCommand commands[],
   1873 +         const int commands_len)
   1874 +{
   1875 +  // Initialize struct to 0
   1876 +  memset(&sock_epoll_event, 0, sizeof(sock_epoll_event));
   1877 +
   1878 +  int socket_fd = ipc_create_socket(socket_path);
   1879 +  if (socket_fd < 0) return -1;
   1880 +
   1881 +  ipc_commands = commands;
   1882 +  ipc_commands_len = commands_len;
   1883 +
   1884 +  epoll_fd = p_epoll_fd;
   1885 +
   1886 +  // Wake up to incoming connection requests
   1887 +  sock_epoll_event.data.fd = socket_fd;
   1888 +  sock_epoll_event.events = EPOLLIN;
   1889 +  if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &sock_epoll_event)) {
   1890 +    fputs("Failed to add sock file descriptor to epoll", stderr);
   1891 +    return -1;
   1892 +  }
   1893 +
   1894 +  return socket_fd;
   1895 +}
   1896 +
   1897 +void
   1898 +ipc_cleanup()
   1899 +{
   1900 +  IPCClient *c = ipc_clients;
   1901 +  // Free clients and their buffers
   1902 +  while (c) {
   1903 +    ipc_drop_client(c);
   1904 +    c = ipc_clients;
   1905 +  }
   1906 +
   1907 +  // Stop waking up for socket events
   1908 +  epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sock_fd, &sock_epoll_event);
   1909 +
   1910 +  // Uninitialize all static variables
   1911 +  epoll_fd = -1;
   1912 +  sock_fd = -1;
   1913 +  ipc_commands = NULL;
   1914 +  ipc_commands_len = 0;
   1915 +  memset(&sock_epoll_event, 0, sizeof(struct epoll_event));
   1916 +  memset(&sockaddr, 0, sizeof(struct sockaddr_un));
   1917 +
   1918 +  // Delete socket
   1919 +  unlink(sockaddr.sun_path);
   1920 +
   1921 +  shutdown(sock_fd, SHUT_RDWR);
   1922 +  close(sock_fd);
   1923 +}
   1924 +
   1925 +int
   1926 +ipc_get_sock_fd()
   1927 +{
   1928 +  return sock_fd;
   1929 +}
   1930 +
   1931 +IPCClient *
   1932 +ipc_get_client(int fd)
   1933 +{
   1934 +  return ipc_list_get_client(ipc_clients, fd);
   1935 +}
   1936 +
   1937 +int
   1938 +ipc_is_client_registered(int fd)
   1939 +{
   1940 +  return (ipc_get_client(fd) != NULL);
   1941 +}
   1942 +
   1943 +int
   1944 +ipc_accept_client()
   1945 +{
   1946 +  int fd = -1;
   1947 +
   1948 +  struct sockaddr_un client_addr;
   1949 +  socklen_t len = 0;
   1950 +
   1951 +  // For portability clear the addr structure, since some implementations
   1952 +  // have nonstandard fields in the structure
   1953 +  memset(&client_addr, 0, sizeof(struct sockaddr_un));
   1954 +
   1955 +  fd = accept(sock_fd, (struct sockaddr *)&client_addr, &len);
   1956 +  if (fd < 0 && errno != EINTR) {
   1957 +    fputs("Failed to accept IPC connection from client", stderr);
   1958 +    return -1;
   1959 +  }
   1960 +
   1961 +  if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
   1962 +    shutdown(fd, SHUT_RDWR);
   1963 +    close(fd);
   1964 +    fputs("Failed to set flags on new client fd", stderr);
   1965 +  }
   1966 +
   1967 +  IPCClient *nc = ipc_client_new(fd);
   1968 +  if (nc == NULL) return -1;
   1969 +
   1970 +  // Wake up to messages from this client
   1971 +  nc->event.data.fd = fd;
   1972 +  nc->event.events = EPOLLIN | EPOLLHUP;
   1973 +  epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &nc->event);
   1974 +
   1975 +  ipc_list_add_client(&ipc_clients, nc);
   1976 +
   1977 +  DEBUG("%s%d\n", "New client at fd: ", fd);
   1978 +
   1979 +  return fd;
   1980 +}
   1981 +
   1982 +int
   1983 +ipc_drop_client(IPCClient *c)
   1984 +{
   1985 +  int fd = c->fd;
   1986 +  shutdown(fd, SHUT_RDWR);
   1987 +  int res = close(fd);
   1988 +
   1989 +  if (res == 0) {
   1990 +    struct epoll_event ev;
   1991 +
   1992 +    // Stop waking up to messages from this client
   1993 +    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev);
   1994 +    ipc_list_remove_client(&ipc_clients, c);
   1995 +
   1996 +    free(c->buffer);
   1997 +    free(c);
   1998 +
   1999 +    DEBUG("Successfully removed client on fd %d\n", fd);
   2000 +  } else if (res < 0 && res != EINTR) {
   2001 +    fprintf(stderr, "Failed to close fd %d\n", fd);
   2002 +  }
   2003 +
   2004 +  return res;
   2005 +}
   2006 +
   2007 +int
   2008 +ipc_read_client(IPCClient *c, IPCMessageType *msg_type, uint32_t *msg_size,
   2009 +                char **msg)
   2010 +{
   2011 +  int fd = c->fd;
   2012 +  int ret =
   2013 +      ipc_recv_message(fd, (uint8_t *)msg_type, msg_size, (uint8_t **)msg);
   2014 +
   2015 +  if (ret < 0) {
   2016 +    // This will happen if these errors occur while reading header
   2017 +    if (ret == -1 &&
   2018 +        (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
   2019 +      return -2;
   2020 +
   2021 +    fprintf(stderr, "Error reading message: dropping client at fd %d\n", fd);
   2022 +    ipc_drop_client(c);
   2023 +
   2024 +    return -1;
   2025 +  }
   2026 +
   2027 +  // Make sure receive message is null terminated to avoid parsing issues
   2028 +  if (*msg_size > 0) {
   2029 +    size_t len = *msg_size;
   2030 +    nullterminate(msg, &len);
   2031 +    *msg_size = len;
   2032 +  }
   2033 +
   2034 +  DEBUG("[fd %d] ", fd);
   2035 +  if (*msg_size > 0)
   2036 +    DEBUG("Received message: '%.*s' ", *msg_size, *msg);
   2037 +  else
   2038 +    DEBUG("Received empty message ");
   2039 +  DEBUG("Message type: %" PRIu8 " ", (uint8_t)*msg_type);
   2040 +  DEBUG("Message size: %" PRIu32 "\n", *msg_size);
   2041 +
   2042 +  return 0;
   2043 +}
   2044 +
   2045 +ssize_t
   2046 +ipc_write_client(IPCClient *c)
   2047 +{
   2048 +  const ssize_t n = ipc_write_message(c->fd, c->buffer, c->buffer_size);
   2049 +
   2050 +  if (n < 0) return n;
   2051 +
   2052 +  // TODO: Deal with client timeouts
   2053 +
   2054 +  if (n == c->buffer_size) {
   2055 +    c->buffer_size = 0;
   2056 +    free(c->buffer);
   2057 +    // No dangling pointers!
   2058 +    c->buffer = NULL;
   2059 +    // Stop waking up when client is ready to receive messages
   2060 +    if (c->event.events & EPOLLOUT) {
   2061 +      c->event.events -= EPOLLOUT;
   2062 +      epoll_ctl(epoll_fd, EPOLL_CTL_MOD, c->fd, &c->event);
   2063 +    }
   2064 +    return n;
   2065 +  }
   2066 +
   2067 +  // Shift unwritten buffer to beginning of buffer and reallocate
   2068 +  c->buffer_size -= n;
   2069 +  memmove(c->buffer, c->buffer + n, c->buffer_size);
   2070 +  c->buffer = (char *)realloc(c->buffer, c->buffer_size);
   2071 +
   2072 +  return n;
   2073 +}
   2074 +
   2075 +void
   2076 +ipc_prepare_send_message(IPCClient *c, const IPCMessageType msg_type,
   2077 +                         const uint32_t msg_size, const char *msg)
   2078 +{
   2079 +  dwm_ipc_header_t header = {
   2080 +      .magic = IPC_MAGIC_ARR, .type = msg_type, .size = msg_size};
   2081 +
   2082 +  uint32_t header_size = sizeof(dwm_ipc_header_t);
   2083 +  uint32_t packet_size = header_size + msg_size;
   2084 +
   2085 +  if (c->buffer == NULL)
   2086 +    c->buffer = (char *)malloc(c->buffer_size + packet_size);
   2087 +  else
   2088 +    c->buffer = (char *)realloc(c->buffer, c->buffer_size + packet_size);
   2089 +
   2090 +  // Copy header to end of client buffer
   2091 +  memcpy(c->buffer + c->buffer_size, &header, header_size);
   2092 +  c->buffer_size += header_size;
   2093 +
   2094 +  // Copy message to end of client buffer
   2095 +  memcpy(c->buffer + c->buffer_size, msg, msg_size);
   2096 +  c->buffer_size += msg_size;
   2097 +
   2098 +  // Wake up when client is ready to receive messages
   2099 +  c->event.events |= EPOLLOUT;
   2100 +  epoll_ctl(epoll_fd, EPOLL_CTL_MOD, c->fd, &c->event);
   2101 +}
   2102 +
   2103 +void
   2104 +ipc_prepare_reply_failure(IPCClient *c, IPCMessageType msg_type,
   2105 +                          const char *format, ...)
   2106 +{
   2107 +  yajl_gen gen;
   2108 +  va_list args;
   2109 +
   2110 +  // Get output size
   2111 +  va_start(args, format);
   2112 +  size_t len = vsnprintf(NULL, 0, format, args);
   2113 +  va_end(args);
   2114 +  char *buffer = (char *)malloc((len + 1) * sizeof(char));
   2115 +
   2116 +  ipc_reply_init_message(&gen);
   2117 +
   2118 +  va_start(args, format);
   2119 +  vsnprintf(buffer, len + 1, format, args);
   2120 +  va_end(args);
   2121 +  dump_error_message(gen, buffer);
   2122 +
   2123 +  ipc_reply_prepare_send_message(gen, c, msg_type);
   2124 +  fprintf(stderr, "[fd %d] Error: %s\n", c->fd, buffer);
   2125 +
   2126 +  free(buffer);
   2127 +}
   2128 +
   2129 +void
   2130 +ipc_prepare_reply_success(IPCClient *c, IPCMessageType msg_type)
   2131 +{
   2132 +  const char *success_msg = "{\"result\":\"success\"}";
   2133 +  const size_t msg_len = strlen(success_msg) + 1;  // +1 for null char
   2134 +
   2135 +  ipc_prepare_send_message(c, msg_type, msg_len, success_msg);
   2136 +}
   2137 +
   2138 +void
   2139 +ipc_tag_change_event(int mon_num, TagState old_state, TagState new_state)
   2140 +{
   2141 +  yajl_gen gen;
   2142 +  ipc_event_init_message(&gen);
   2143 +  dump_tag_event(gen, mon_num, old_state, new_state);
   2144 +  ipc_event_prepare_send_message(gen, IPC_EVENT_TAG_CHANGE);
   2145 +}
   2146 +
   2147 +void
   2148 +ipc_client_focus_change_event(int mon_num, Client *old_client,
   2149 +                              Client *new_client)
   2150 +{
   2151 +  yajl_gen gen;
   2152 +  ipc_event_init_message(&gen);
   2153 +  dump_client_focus_change_event(gen, old_client, new_client, mon_num);
   2154 +  ipc_event_prepare_send_message(gen, IPC_EVENT_CLIENT_FOCUS_CHANGE);
   2155 +}
   2156 +
   2157 +void
   2158 +ipc_layout_change_event(const int mon_num, const char *old_symbol,
   2159 +                        const Layout *old_layout, const char *new_symbol,
   2160 +                        const Layout *new_layout)
   2161 +{
   2162 +  yajl_gen gen;
   2163 +  ipc_event_init_message(&gen);
   2164 +  dump_layout_change_event(gen, mon_num, old_symbol, old_layout, new_symbol,
   2165 +                           new_layout);
   2166 +  ipc_event_prepare_send_message(gen, IPC_EVENT_LAYOUT_CHANGE);
   2167 +}
   2168 +
   2169 +void
   2170 +ipc_monitor_focus_change_event(const int last_mon_num, const int new_mon_num)
   2171 +{
   2172 +  yajl_gen gen;
   2173 +  ipc_event_init_message(&gen);
   2174 +  dump_monitor_focus_change_event(gen, last_mon_num, new_mon_num);
   2175 +  ipc_event_prepare_send_message(gen, IPC_EVENT_MONITOR_FOCUS_CHANGE);
   2176 +}
   2177 +
   2178 +void
   2179 +ipc_focused_title_change_event(const int mon_num, const Window client_id,
   2180 +                               const char *old_name, const char *new_name)
   2181 +{
   2182 +  yajl_gen gen;
   2183 +  ipc_event_init_message(&gen);
   2184 +  dump_focused_title_change_event(gen, mon_num, client_id, old_name, new_name);
   2185 +  ipc_event_prepare_send_message(gen, IPC_EVENT_FOCUSED_TITLE_CHANGE);
   2186 +}
   2187 +
   2188 +void
   2189 +ipc_focused_state_change_event(const int mon_num, const Window client_id,
   2190 +                               const ClientState *old_state,
   2191 +                               const ClientState *new_state)
   2192 +{
   2193 +  yajl_gen gen;
   2194 +  ipc_event_init_message(&gen);
   2195 +  dump_focused_state_change_event(gen, mon_num, client_id, old_state,
   2196 +                                  new_state);
   2197 +  ipc_event_prepare_send_message(gen, IPC_EVENT_FOCUSED_STATE_CHANGE);
   2198 +}
   2199 +
   2200 +void
   2201 +ipc_send_events(Monitor *mons, Monitor **lastselmon, Monitor *selmon)
   2202 +{
   2203 +  for (Monitor *m = mons; m; m = m->next) {
   2204 +    unsigned int urg = 0, occ = 0, tagset = 0;
   2205 +
   2206 +    for (Client *c = m->clients; c; c = c->next) {
   2207 +      occ |= c->tags;
   2208 +
   2209 +      if (c->isurgent) urg |= c->tags;
   2210 +    }
   2211 +    tagset = m->tagset[m->seltags];
   2212 +
   2213 +    TagState new_state = {.selected = tagset, .occupied = occ, .urgent = urg};
   2214 +
   2215 +    if (memcmp(&m->tagstate, &new_state, sizeof(TagState)) != 0) {
   2216 +      ipc_tag_change_event(m->num, m->tagstate, new_state);
   2217 +      m->tagstate = new_state;
   2218 +    }
   2219 +
   2220 +    if (m->lastsel != m->sel) {
   2221 +      ipc_client_focus_change_event(m->num, m->lastsel, m->sel);
   2222 +      m->lastsel = m->sel;
   2223 +    }
   2224 +
   2225 +    if (strcmp(m->ltsymbol, m->lastltsymbol) != 0 ||
   2226 +        m->lastlt != m->lt[m->sellt]) {
   2227 +      ipc_layout_change_event(m->num, m->lastltsymbol, m->lastlt, m->ltsymbol,
   2228 +                              m->lt[m->sellt]);
   2229 +      strcpy(m->lastltsymbol, m->ltsymbol);
   2230 +      m->lastlt = m->lt[m->sellt];
   2231 +    }
   2232 +
   2233 +    if (*lastselmon != selmon) {
   2234 +      if (*lastselmon != NULL)
   2235 +        ipc_monitor_focus_change_event((*lastselmon)->num, selmon->num);
   2236 +      *lastselmon = selmon;
   2237 +    }
   2238 +
   2239 +    Client *sel = m->sel;
   2240 +    if (!sel) continue;
   2241 +    ClientState *o = &m->sel->prevstate;
   2242 +    ClientState n = {.oldstate = sel->oldstate,
   2243 +                     .isfixed = sel->isfixed,
   2244 +                     .isfloating = sel->isfloating,
   2245 +                     .isfullscreen = sel->isfullscreen,
   2246 +                     .isurgent = sel->isurgent,
   2247 +                     .neverfocus = sel->neverfocus};
   2248 +    if (memcmp(o, &n, sizeof(ClientState)) != 0) {
   2249 +      ipc_focused_state_change_event(m->num, m->sel->win, o, &n);
   2250 +      *o = n;
   2251 +    }
   2252 +  }
   2253 +}
   2254 +
   2255 +int
   2256 +ipc_handle_client_epoll_event(struct epoll_event *ev, Monitor *mons,
   2257 +                              Monitor **lastselmon, Monitor *selmon,
   2258 +                              const char *tags[], const int tags_len,
   2259 +                              const Layout *layouts, const int layouts_len)
   2260 +{
   2261 +  int fd = ev->data.fd;
   2262 +  IPCClient *c = ipc_get_client(fd);
   2263 +
   2264 +  if (ev->events & EPOLLHUP) {
   2265 +    DEBUG("EPOLLHUP received from client at fd %d\n", fd);
   2266 +    ipc_drop_client(c);
   2267 +  } else if (ev->events & EPOLLOUT) {
   2268 +    DEBUG("Sending message to client at fd %d...\n", fd);
   2269 +    if (c->buffer_size) ipc_write_client(c);
   2270 +  } else if (ev->events & EPOLLIN) {
   2271 +    IPCMessageType msg_type = 0;
   2272 +    uint32_t msg_size = 0;
   2273 +    char *msg = NULL;
   2274 +
   2275 +    DEBUG("Received message from fd %d\n", fd);
   2276 +    if (ipc_read_client(c, &msg_type, &msg_size, &msg) < 0) return -1;
   2277 +
   2278 +    if (msg_type == IPC_TYPE_GET_MONITORS)
   2279 +      ipc_get_monitors(c, mons, selmon);
   2280 +    else if (msg_type == IPC_TYPE_GET_TAGS)
   2281 +      ipc_get_tags(c, tags, tags_len);
   2282 +    else if (msg_type == IPC_TYPE_GET_LAYOUTS)
   2283 +      ipc_get_layouts(c, layouts, layouts_len);
   2284 +    else if (msg_type == IPC_TYPE_RUN_COMMAND) {
   2285 +      if (ipc_run_command(c, msg) < 0) return -1;
   2286 +      ipc_send_events(mons, lastselmon, selmon);
   2287 +    } else if (msg_type == IPC_TYPE_GET_DWM_CLIENT) {
   2288 +      if (ipc_get_dwm_client(c, msg, mons) < 0) return -1;
   2289 +    } else if (msg_type == IPC_TYPE_SUBSCRIBE) {
   2290 +      if (ipc_subscribe(c, msg) < 0) return -1;
   2291 +    } else {
   2292 +      fprintf(stderr, "Invalid message type received from fd %d", fd);
   2293 +      ipc_prepare_reply_failure(c, msg_type, "Invalid message type: %d",
   2294 +                                msg_type);
   2295 +    }
   2296 +    free(msg);
   2297 +  } else {
   2298 +    fprintf(stderr, "Epoll event returned %d from fd %d\n", ev->events, fd);
   2299 +    return -1;
   2300 +  }
   2301 +
   2302 +  return 0;
   2303 +}
   2304 +
   2305 +int
   2306 +ipc_handle_socket_epoll_event(struct epoll_event *ev)
   2307 +{
   2308 +  if (!(ev->events & EPOLLIN)) return -1;
   2309 +
   2310 +  // EPOLLIN means incoming client connection request
   2311 +  fputs("Received EPOLLIN event on socket\n", stderr);
   2312 +  int new_fd = ipc_accept_client();
   2313 +
   2314 +  return new_fd;
   2315 +}
   2316 diff --git a/ipc.h b/ipc.h
   2317 new file mode 100644
   2318 index 0000000..e3b5bba
   2319 --- /dev/null
   2320 +++ b/ipc.h
   2321 @@ -0,0 +1,320 @@
   2322 +#ifndef IPC_H_
   2323 +#define IPC_H_
   2324 +
   2325 +#include <stdint.h>
   2326 +#include <sys/epoll.h>
   2327 +#include <yajl/yajl_gen.h>
   2328 +
   2329 +#include "IPCClient.h"
   2330 +
   2331 +// clang-format off
   2332 +#define IPC_MAGIC "DWM-IPC"
   2333 +#define IPC_MAGIC_ARR { 'D', 'W', 'M', '-', 'I', 'P', 'C'}
   2334 +#define IPC_MAGIC_LEN 7 // Not including null char
   2335 +
   2336 +#define IPCCOMMAND(FUNC, ARGC, TYPES)                                          \
   2337 +  { #FUNC, {FUNC }, ARGC, (ArgType[ARGC])TYPES }
   2338 +// clang-format on
   2339 +
   2340 +typedef enum IPCMessageType {
   2341 +  IPC_TYPE_RUN_COMMAND = 0,
   2342 +  IPC_TYPE_GET_MONITORS = 1,
   2343 +  IPC_TYPE_GET_TAGS = 2,
   2344 +  IPC_TYPE_GET_LAYOUTS = 3,
   2345 +  IPC_TYPE_GET_DWM_CLIENT = 4,
   2346 +  IPC_TYPE_SUBSCRIBE = 5,
   2347 +  IPC_TYPE_EVENT = 6
   2348 +} IPCMessageType;
   2349 +
   2350 +typedef enum IPCEvent {
   2351 +  IPC_EVENT_TAG_CHANGE = 1 << 0,
   2352 +  IPC_EVENT_CLIENT_FOCUS_CHANGE = 1 << 1,
   2353 +  IPC_EVENT_LAYOUT_CHANGE = 1 << 2,
   2354 +  IPC_EVENT_MONITOR_FOCUS_CHANGE = 1 << 3,
   2355 +  IPC_EVENT_FOCUSED_TITLE_CHANGE = 1 << 4,
   2356 +  IPC_EVENT_FOCUSED_STATE_CHANGE = 1 << 5
   2357 +} IPCEvent;
   2358 +
   2359 +typedef enum IPCSubscriptionAction {
   2360 +  IPC_ACTION_UNSUBSCRIBE = 0,
   2361 +  IPC_ACTION_SUBSCRIBE = 1
   2362 +} IPCSubscriptionAction;
   2363 +
   2364 +/**
   2365 + * Every IPC packet starts with this structure
   2366 + */
   2367 +typedef struct dwm_ipc_header {
   2368 +  uint8_t magic[IPC_MAGIC_LEN];
   2369 +  uint32_t size;
   2370 +  uint8_t type;
   2371 +} __attribute((packed)) dwm_ipc_header_t;
   2372 +
   2373 +typedef enum ArgType {
   2374 +  ARG_TYPE_NONE = 0,
   2375 +  ARG_TYPE_UINT = 1,
   2376 +  ARG_TYPE_SINT = 2,
   2377 +  ARG_TYPE_FLOAT = 3,
   2378 +  ARG_TYPE_PTR = 4,
   2379 +  ARG_TYPE_STR = 5
   2380 +} ArgType;
   2381 +
   2382 +/**
   2383 + * An IPCCommand function can have either of these function signatures
   2384 + */
   2385 +typedef union ArgFunction {
   2386 +  void (*single_param)(const Arg *);
   2387 +  void (*array_param)(const Arg *, int);
   2388 +} ArgFunction;
   2389 +
   2390 +typedef struct IPCCommand {
   2391 +  char *name;
   2392 +  ArgFunction func;
   2393 +  unsigned int argc;
   2394 +  ArgType *arg_types;
   2395 +} IPCCommand;
   2396 +
   2397 +typedef struct IPCParsedCommand {
   2398 +  char *name;
   2399 +  Arg *args;
   2400 +  ArgType *arg_types;
   2401 +  unsigned int argc;
   2402 +} IPCParsedCommand;
   2403 +
   2404 +/**
   2405 + * Initialize the IPC socket and the IPC module
   2406 + *
   2407 + * @param socket_path Path to create the socket at
   2408 + * @param epoll_fd File descriptor for epoll
   2409 + * @param commands Address of IPCCommands array defined in config.h
   2410 + * @param commands_len Length of commands[] array
   2411 + *
   2412 + * @return int The file descriptor of the socket if it was successfully created,
   2413 + *   -1 otherwise
   2414 + */
   2415 +int ipc_init(const char *socket_path, const int p_epoll_fd,
   2416 +             IPCCommand commands[], const int commands_len);
   2417 +
   2418 +/**
   2419 + * Uninitialize the socket and module. Free allocated memory and restore static
   2420 + * variables to their state before ipc_init
   2421 + */
   2422 +void ipc_cleanup();
   2423 +
   2424 +/**
   2425 + * Get the file descriptor of the IPC socket
   2426 + *
   2427 + * @return int File descriptor of IPC socket, -1 if socket not created.
   2428 + */
   2429 +int ipc_get_sock_fd();
   2430 +
   2431 +/**
   2432 + * Get address to IPCClient with specified file descriptor
   2433 + *
   2434 + * @param fd File descriptor of IPC Client
   2435 + *
   2436 + * @return Address to IPCClient with specified file descriptor, -1 otherwise
   2437 + */
   2438 +IPCClient *ipc_get_client(int fd);
   2439 +
   2440 +/**
   2441 + * Check if an IPC client exists with the specified file descriptor
   2442 + *
   2443 + * @param fd File descriptor
   2444 + *
   2445 + * @return int 1 if client exists, 0 otherwise
   2446 + */
   2447 +int ipc_is_client_registered(int fd);
   2448 +
   2449 +/**
   2450 + * Disconnect an IPCClient from the socket and remove the client from the list
   2451 + *   of known connected clients
   2452 + *
   2453 + * @param c Address of IPCClient
   2454 + *
   2455 + * @return 0 if the client's file descriptor was closed successfully, the
   2456 + * result of executing close() on the file descriptor otherwise.
   2457 + */
   2458 +int ipc_drop_client(IPCClient *c);
   2459 +
   2460 +/**
   2461 + * Accept an IPC Client requesting to connect to the socket and add it to the
   2462 + *   list of clients
   2463 + *
   2464 + * @return File descriptor of new client, -1 on error
   2465 + */
   2466 +int ipc_accept_client();
   2467 +
   2468 +/**
   2469 + * Read an incoming message from an accepted IPC client
   2470 + *
   2471 + * @param c Address of IPCClient
   2472 + * @param msg_type Address to IPCMessageType variable which will be assigned
   2473 + *   the message type of the received message
   2474 + * @param msg_size Address to uint32_t variable which will be assigned the size
   2475 + *   of the received message
   2476 + * @param msg Address to char* variable which will be assigned the address of
   2477 + *   the received message. This must be freed using free().
   2478 + *
   2479 + * @return 0 on success, -1 on error reading message, -2 if reading the message
   2480 + * resulted in EAGAIN, EINTR, or EWOULDBLOCK.
   2481 + */
   2482 +int ipc_read_client(IPCClient *c, IPCMessageType *msg_type, uint32_t *msg_size,
   2483 +                    char **msg);
   2484 +
   2485 +/**
   2486 + * Write any pending buffer of the client to the client's socket
   2487 + *
   2488 + * @param c Client whose buffer to write
   2489 + *
   2490 + * @return Number of bytes written >= 0, -1 otherwise. errno will still be set
   2491 + * from the write operation.
   2492 + */
   2493 +ssize_t ipc_write_client(IPCClient *c);
   2494 +
   2495 +/**
   2496 + * Prepare a message in the specified client's buffer.
   2497 + *
   2498 + * @param c Client to prepare message for
   2499 + * @param msg_type Type of message to prepare
   2500 + * @param msg_size Size of the message in bytes. Should not exceed
   2501 + *   MAX_MESSAGE_SIZE
   2502 + * @param msg Message to prepare (not including header). This pointer can be
   2503 + *   freed after the function invocation.
   2504 + */
   2505 +void ipc_prepare_send_message(IPCClient *c, const IPCMessageType msg_type,
   2506 +                              const uint32_t msg_size, const char *msg);
   2507 +
   2508 +/**
   2509 + * Prepare an error message in the specified client's buffer
   2510 + *
   2511 + * @param c Client to prepare message for
   2512 + * @param msg_type Type of message
   2513 + * @param format Format string following vsprintf
   2514 + * @param ... Arguments for format string
   2515 + */
   2516 +void ipc_prepare_reply_failure(IPCClient *c, IPCMessageType msg_type,
   2517 +                               const char *format, ...);
   2518 +
   2519 +/**
   2520 + * Prepare a success message in the specified client's buffer
   2521 + *
   2522 + * @param c Client to prepare message for
   2523 + * @param msg_type Type of message
   2524 + */
   2525 +void ipc_prepare_reply_success(IPCClient *c, IPCMessageType msg_type);
   2526 +
   2527 +/**
   2528 + * Send a tag_change_event to all subscribers. Should be called only when there
   2529 + * has been a tag state change.
   2530 + *
   2531 + * @param mon_num The index of the monitor (Monitor.num property)
   2532 + * @param old_state The old tag state
   2533 + * @param new_state The new (now current) tag state
   2534 + */
   2535 +void ipc_tag_change_event(const int mon_num, TagState old_state,
   2536 +                          TagState new_state);
   2537 +
   2538 +/**
   2539 + * Send a client_focus_change_event to all subscribers. Should be called only
   2540 + * when the client focus changes.
   2541 + *
   2542 + * @param mon_num The index of the monitor (Monitor.num property)
   2543 + * @param old_client The old DWM client selection (Monitor.oldsel)
   2544 + * @param new_client The new (now current) DWM client selection
   2545 + */
   2546 +void ipc_client_focus_change_event(const int mon_num, Client *old_client,
   2547 +                                   Client *new_client);
   2548 +
   2549 +/**
   2550 + * Send a layout_change_event to all subscribers. Should be called only
   2551 + * when there has been a layout change.
   2552 + *
   2553 + * @param mon_num The index of the monitor (Monitor.num property)
   2554 + * @param old_symbol The old layout symbol
   2555 + * @param old_layout Address to the old Layout
   2556 + * @param new_symbol The new (now current) layout symbol
   2557 + * @param new_layout Address to the new Layout
   2558 + */
   2559 +void ipc_layout_change_event(const int mon_num, const char *old_symbol,
   2560 +                             const Layout *old_layout, const char *new_symbol,
   2561 +                             const Layout *new_layout);
   2562 +
   2563 +/**
   2564 + * Send a monitor_focus_change_event to all subscribers. Should be called only
   2565 + * when the monitor focus changes.
   2566 + *
   2567 + * @param last_mon_num The index of the previously selected monitor
   2568 + * @param new_mon_num The index of the newly selected monitor
   2569 + */
   2570 +void ipc_monitor_focus_change_event(const int last_mon_num,
   2571 +                                    const int new_mon_num);
   2572 +
   2573 +/**
   2574 + * Send a focused_title_change_event to all subscribers. Should only be called
   2575 + * if a selected client has a title change.
   2576 + *
   2577 + * @param mon_num Index of the client's monitor
   2578 + * @param client_id Window XID of client
   2579 + * @param old_name Old name of the client window
   2580 + * @param new_name New name of the client window
   2581 + */
   2582 +void ipc_focused_title_change_event(const int mon_num, const Window client_id,
   2583 +                                    const char *old_name, const char *new_name);
   2584 +
   2585 +/**
   2586 + * Send a focused_state_change_event to all subscribers. Should only be called
   2587 + * if a selected client has a state change.
   2588 + *
   2589 + * @param mon_num Index of the client's monitor
   2590 + * @param client_id Window XID of client
   2591 + * @param old_state Old state of the client
   2592 + * @param new_state New state of the client
   2593 + */
   2594 +void ipc_focused_state_change_event(const int mon_num, const Window client_id,
   2595 +                                    const ClientState *old_state,
   2596 +                                    const ClientState *new_state);
   2597 +/**
   2598 + * Check to see if an event has occured and call the *_change_event functions
   2599 + * accordingly
   2600 + *
   2601 + * @param mons Address of Monitor pointing to start of linked list
   2602 + * @param lastselmon Address of pointer to previously selected monitor
   2603 + * @param selmon Address of selected Monitor
   2604 + */
   2605 +void ipc_send_events(Monitor *mons, Monitor **lastselmon, Monitor *selmon);
   2606 +
   2607 +/**
   2608 + * Handle an epoll event caused by a registered IPC client. Read, process, and
   2609 + * handle any received messages from clients. Write pending buffer to client if
   2610 + * the client is ready to receive messages. Drop clients that have sent an
   2611 + * EPOLLHUP.
   2612 + *
   2613 + * @param ev Associated epoll event returned by epoll_wait
   2614 + * @param mons Address of Monitor pointing to start of linked list
   2615 + * @param selmon Address of selected Monitor
   2616 + * @param lastselmon Address of pointer to previously selected monitor
   2617 + * @param tags Array of tag names
   2618 + * @param tags_len Length of tags array
   2619 + * @param layouts Array of available layouts
   2620 + * @param layouts_len Length of layouts array
   2621 + *
   2622 + * @return 0 if event was successfully handled, -1 on any error receiving
   2623 + * or handling incoming messages or unhandled epoll event.
   2624 + */
   2625 +int ipc_handle_client_epoll_event(struct epoll_event *ev, Monitor *mons,
   2626 +                                  Monitor **lastselmon, Monitor *selmon,
   2627 +                                  const char *tags[], const int tags_len,
   2628 +                                  const Layout *layouts, const int layouts_len);
   2629 +
   2630 +/**
   2631 + * Handle an epoll event caused by the IPC socket. This function only handles an
   2632 + * EPOLLIN event indicating a new client requesting to connect to the socket.
   2633 + *
   2634 + * @param ev Associated epoll event returned by epoll_wait
   2635 + *
   2636 + * @return 0, if the event was successfully handled, -1 if not an EPOLLIN event
   2637 + * or if a new IPC client connection request could not be accepted.
   2638 + */
   2639 +int ipc_handle_socket_epoll_event(struct epoll_event *ev);
   2640 +
   2641 +#endif /* IPC_H_ */
   2642 diff --git a/util.c b/util.c
   2643 index fe044fc..dca4794 100644
   2644 --- a/util.c
   2645 +++ b/util.c
   2646 @@ -3,6 +3,8 @@
   2647  #include <stdio.h>
   2648  #include <stdlib.h>
   2649  #include <string.h>
   2650 +#include <errno.h>
   2651 +#include <sys/stat.h>
   2652  
   2653  #include "util.h"
   2654  
   2655 @@ -33,3 +35,136 @@ die(const char *fmt, ...) {
   2656  
   2657  	exit(1);
   2658  }
   2659 +
   2660 +int
   2661 +normalizepath(const char *path, char **normal)
   2662 +{
   2663 +  size_t len = strlen(path);
   2664 +  *normal = (char *)malloc((len + 1) * sizeof(char));
   2665 +  const char *walk = path;
   2666 +  const char *match;
   2667 +  size_t newlen = 0;
   2668 +
   2669 +  while ((match = strchr(walk, '/'))) {
   2670 +    // Copy everything between match and walk
   2671 +    strncpy(*normal + newlen, walk, match - walk);
   2672 +    newlen += match - walk;
   2673 +    walk += match - walk;
   2674 +
   2675 +    // Skip all repeating slashes
   2676 +    while (*walk == '/')
   2677 +      walk++;
   2678 +
   2679 +    // If not last character in path
   2680 +    if (walk != path + len)
   2681 +      (*normal)[newlen++] = '/';
   2682 +  }
   2683 +
   2684 +  (*normal)[newlen++] = '\0';
   2685 +
   2686 +  // Copy remaining path
   2687 +  strcat(*normal, walk);
   2688 +  newlen += strlen(walk);
   2689 +
   2690 +  *normal = (char *)realloc(*normal, newlen * sizeof(char));
   2691 +
   2692 +  return 0;
   2693 +}
   2694 +
   2695 +int
   2696 +parentdir(const char *path, char **parent)
   2697 +{
   2698 +  char *normal;
   2699 +  char *walk;
   2700 +
   2701 +  normalizepath(path, &normal);
   2702 +
   2703 +  // Pointer to last '/'
   2704 +  if (!(walk = strrchr(normal, '/'))) {
   2705 +    free(normal);
   2706 +    return -1;
   2707 +  }
   2708 +
   2709 +  // Get path up to last '/'
   2710 +  size_t len = walk - normal;
   2711 +  *parent = (char *)malloc((len + 1) * sizeof(char));
   2712 +
   2713 +  // Copy path up to last '/'
   2714 +  strncpy(*parent, normal, len);
   2715 +  // Add null char
   2716 +  (*parent)[len] = '\0';
   2717 +
   2718 +  free(normal);
   2719 +
   2720 +  return 0;
   2721 +}
   2722 +
   2723 +int
   2724 +mkdirp(const char *path)
   2725 +{
   2726 +  char *normal;
   2727 +  char *walk;
   2728 +  size_t normallen;
   2729 +
   2730 +  normalizepath(path, &normal);
   2731 +  normallen = strlen(normal);
   2732 +  walk = normal;
   2733 +
   2734 +  while (walk < normal + normallen + 1) {
   2735 +    // Get length from walk to next /
   2736 +    size_t n = strcspn(walk, "/");
   2737 +
   2738 +    // Skip path /
   2739 +    if (n == 0) {
   2740 +      walk++;
   2741 +      continue;
   2742 +    }
   2743 +
   2744 +    // Length of current path segment
   2745 +    size_t curpathlen = walk - normal + n;
   2746 +    char curpath[curpathlen + 1];
   2747 +    struct stat s;
   2748 +
   2749 +    // Copy path segment to stat
   2750 +    strncpy(curpath, normal, curpathlen);
   2751 +    strcpy(curpath + curpathlen, "");
   2752 +    int res = stat(curpath, &s);
   2753 +
   2754 +    if (res < 0) {
   2755 +      if (errno == ENOENT) {
   2756 +        DEBUG("Making directory %s\n", curpath);
   2757 +        if (mkdir(curpath, 0700) < 0) {
   2758 +          fprintf(stderr, "Failed to make directory %s\n", curpath);
   2759 +          perror("");
   2760 +          free(normal);
   2761 +          return -1;
   2762 +        }
   2763 +      } else {
   2764 +        fprintf(stderr, "Error statting directory %s\n", curpath);
   2765 +        perror("");
   2766 +        free(normal);
   2767 +        return -1;
   2768 +      }
   2769 +    }
   2770 +
   2771 +    // Continue to next path segment
   2772 +    walk += n;
   2773 +  }
   2774 +
   2775 +  free(normal);
   2776 +
   2777 +  return 0;
   2778 +}
   2779 +
   2780 +int
   2781 +nullterminate(char **str, size_t *len)
   2782 +{
   2783 +  if ((*str)[*len - 1] == '\0')
   2784 +    return 0;
   2785 +
   2786 +  (*len)++;
   2787 +  *str = (char*)realloc(*str, *len * sizeof(char));
   2788 +  (*str)[*len - 1] = '\0';
   2789 +
   2790 +  return 0;
   2791 +}
   2792 diff --git a/util.h b/util.h
   2793 index f633b51..73a238e 100644
   2794 --- a/util.h
   2795 +++ b/util.h
   2796 @@ -4,5 +4,15 @@
   2797  #define MIN(A, B)               ((A) < (B) ? (A) : (B))
   2798  #define BETWEEN(X, A, B)        ((A) <= (X) && (X) <= (B))
   2799  
   2800 +#ifdef _DEBUG
   2801 +#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
   2802 +#else
   2803 +#define DEBUG(...)
   2804 +#endif
   2805 +
   2806  void die(const char *fmt, ...);
   2807  void *ecalloc(size_t nmemb, size_t size);
   2808 +int normalizepath(const char *path, char **normal);
   2809 +int mkdirp(const char *path);
   2810 +int parentdir(const char *path, char **parent);
   2811 +int nullterminate(char **str, size_t *len);
   2812 diff --git a/yajl_dumps.c b/yajl_dumps.c
   2813 new file mode 100644
   2814 index 0000000..8bf9688
   2815 --- /dev/null
   2816 +++ b/yajl_dumps.c
   2817 @@ -0,0 +1,351 @@
   2818 +#include "yajl_dumps.h"
   2819 +
   2820 +#include <stdint.h>
   2821 +
   2822 +int
   2823 +dump_tag(yajl_gen gen, const char *name, const int tag_mask)
   2824 +{
   2825 +  // clang-format off
   2826 +  YMAP(
   2827 +    YSTR("bit_mask"); YINT(tag_mask);
   2828 +    YSTR("name"); YSTR(name);
   2829 +  )
   2830 +  // clang-format on
   2831 +
   2832 +  return 0;
   2833 +}
   2834 +
   2835 +int
   2836 +dump_tags(yajl_gen gen, const char *tags[], int tags_len)
   2837 +{
   2838 +  // clang-format off
   2839 +  YARR(
   2840 +    for (int i = 0; i < tags_len; i++)
   2841 +      dump_tag(gen, tags[i], 1 << i);
   2842 +  )
   2843 +  // clang-format on
   2844 +
   2845 +  return 0;
   2846 +}
   2847 +
   2848 +int
   2849 +dump_client(yajl_gen gen, Client *c)
   2850 +{
   2851 +  // clang-format off
   2852 +  YMAP(
   2853 +    YSTR("name"); YSTR(c->name);
   2854 +    YSTR("tags"); YINT(c->tags);
   2855 +    YSTR("window_id"); YINT(c->win);
   2856 +    YSTR("monitor_number"); YINT(c->mon->num);
   2857 +
   2858 +    YSTR("geometry"); YMAP(
   2859 +      YSTR("current"); YMAP (
   2860 +        YSTR("x"); YINT(c->x);
   2861 +        YSTR("y"); YINT(c->y);
   2862 +        YSTR("width"); YINT(c->w);
   2863 +        YSTR("height"); YINT(c->h);
   2864 +      )
   2865 +      YSTR("old"); YMAP(
   2866 +        YSTR("x"); YINT(c->oldx);
   2867 +        YSTR("y"); YINT(c->oldy);
   2868 +        YSTR("width"); YINT(c->oldw);
   2869 +        YSTR("height"); YINT(c->oldh);
   2870 +      )
   2871 +    )
   2872 +
   2873 +    YSTR("size_hints"); YMAP(
   2874 +      YSTR("base"); YMAP(
   2875 +        YSTR("width"); YINT(c->basew);
   2876 +        YSTR("height"); YINT(c->baseh);
   2877 +      )
   2878 +      YSTR("step"); YMAP(
   2879 +        YSTR("width"); YINT(c->incw);
   2880 +        YSTR("height"); YINT(c->inch);
   2881 +      )
   2882 +      YSTR("max"); YMAP(
   2883 +        YSTR("width"); YINT(c->maxw);
   2884 +        YSTR("height"); YINT(c->maxh);
   2885 +      )
   2886 +      YSTR("min"); YMAP(
   2887 +        YSTR("width"); YINT(c->minw);
   2888 +        YSTR("height"); YINT(c->minh);
   2889 +      )
   2890 +      YSTR("aspect_ratio"); YMAP(
   2891 +        YSTR("min"); YDOUBLE(c->mina);
   2892 +        YSTR("max"); YDOUBLE(c->maxa);
   2893 +      )
   2894 +    )
   2895 +
   2896 +    YSTR("border_width"); YMAP(
   2897 +      YSTR("current"); YINT(c->bw);
   2898 +      YSTR("old"); YINT(c->oldbw);
   2899 +    )
   2900 +
   2901 +    YSTR("states"); YMAP(
   2902 +      YSTR("is_fixed"); YBOOL(c->isfixed);
   2903 +      YSTR("is_floating"); YBOOL(c->isfloating);
   2904 +      YSTR("is_urgent"); YBOOL(c->isurgent);
   2905 +      YSTR("never_focus"); YBOOL(c->neverfocus);
   2906 +      YSTR("old_state"); YBOOL(c->oldstate);
   2907 +      YSTR("is_fullscreen"); YBOOL(c->isfullscreen);
   2908 +    )
   2909 +  )
   2910 +  // clang-format on
   2911 +
   2912 +  return 0;
   2913 +}
   2914 +
   2915 +int
   2916 +dump_monitor(yajl_gen gen, Monitor *mon, int is_selected)
   2917 +{
   2918 +  // clang-format off
   2919 +  YMAP(
   2920 +    YSTR("master_factor"); YDOUBLE(mon->mfact);
   2921 +    YSTR("num_master"); YINT(mon->nmaster);
   2922 +    YSTR("num"); YINT(mon->num);
   2923 +    YSTR("is_selected"); YBOOL(is_selected);
   2924 +
   2925 +    YSTR("monitor_geometry"); YMAP(
   2926 +      YSTR("x"); YINT(mon->mx);
   2927 +      YSTR("y"); YINT(mon->my);
   2928 +      YSTR("width"); YINT(mon->mw);
   2929 +      YSTR("height"); YINT(mon->mh);
   2930 +    )
   2931 +
   2932 +    YSTR("window_geometry"); YMAP(
   2933 +      YSTR("x"); YINT(mon->wx);
   2934 +      YSTR("y"); YINT(mon->wy);
   2935 +      YSTR("width"); YINT(mon->ww);
   2936 +      YSTR("height"); YINT(mon->wh);
   2937 +    )
   2938 +
   2939 +    YSTR("tagset"); YMAP(
   2940 +      YSTR("current");  YINT(mon->tagset[mon->seltags]);
   2941 +      YSTR("old"); YINT(mon->tagset[mon->seltags ^ 1]);
   2942 +    )
   2943 +
   2944 +    YSTR("tag_state"); dump_tag_state(gen, mon->tagstate);
   2945 +
   2946 +    YSTR("clients"); YMAP(
   2947 +      YSTR("selected"); YINT(mon->sel ? mon->sel->win : 0);
   2948 +      YSTR("stack"); YARR(
   2949 +        for (Client* c = mon->stack; c; c = c->snext)
   2950 +          YINT(c->win);
   2951 +      )
   2952 +      YSTR("all"); YARR(
   2953 +        for (Client* c = mon->clients; c; c = c->next)
   2954 +          YINT(c->win);
   2955 +      )
   2956 +    )
   2957 +
   2958 +    YSTR("layout"); YMAP(
   2959 +      YSTR("symbol"); YMAP(
   2960 +        YSTR("current"); YSTR(mon->ltsymbol);
   2961 +        YSTR("old"); YSTR(mon->lastltsymbol);
   2962 +      )
   2963 +      YSTR("address"); YMAP(
   2964 +        YSTR("current"); YINT((uintptr_t)mon->lt[mon->sellt]);
   2965 +        YSTR("old"); YINT((uintptr_t)mon->lt[mon->sellt ^ 1]);
   2966 +      )
   2967 +    )
   2968 +
   2969 +    YSTR("bar"); YMAP(
   2970 +      YSTR("y"); YINT(mon->by);
   2971 +      YSTR("is_shown"); YBOOL(mon->showbar);
   2972 +      YSTR("is_top"); YBOOL(mon->topbar);
   2973 +      YSTR("window_id"); YINT(mon->barwin);
   2974 +    )
   2975 +  )
   2976 +  // clang-format on
   2977 +
   2978 +  return 0;
   2979 +}
   2980 +
   2981 +int
   2982 +dump_monitors(yajl_gen gen, Monitor *mons, Monitor *selmon)
   2983 +{
   2984 +  // clang-format off
   2985 +  YARR(
   2986 +    for (Monitor *mon = mons; mon; mon = mon->next) {
   2987 +      if (mon == selmon)
   2988 +        dump_monitor(gen, mon, 1);
   2989 +      else
   2990 +        dump_monitor(gen, mon, 0);
   2991 +    }
   2992 +  )
   2993 +  // clang-format on
   2994 +
   2995 +  return 0;
   2996 +}
   2997 +
   2998 +int
   2999 +dump_layouts(yajl_gen gen, const Layout layouts[], const int layouts_len)
   3000 +{
   3001 +  // clang-format off
   3002 +  YARR(
   3003 +    for (int i = 0; i < layouts_len; i++) {
   3004 +      YMAP(
   3005 +        // Check for a NULL pointer. The cycle layouts patch adds an entry at
   3006 +        // the end of the layouts array with a NULL pointer for the symbol
   3007 +        YSTR("symbol"); YSTR((layouts[i].symbol ? layouts[i].symbol : ""));
   3008 +        YSTR("address"); YINT((uintptr_t)(layouts + i));
   3009 +      )
   3010 +    }
   3011 +  )
   3012 +  // clang-format on
   3013 +
   3014 +  return 0;
   3015 +}
   3016 +
   3017 +int
   3018 +dump_tag_state(yajl_gen gen, TagState state)
   3019 +{
   3020 +  // clang-format off
   3021 +  YMAP(
   3022 +    YSTR("selected"); YINT(state.selected);
   3023 +    YSTR("occupied"); YINT(state.occupied);
   3024 +    YSTR("urgent"); YINT(state.urgent);
   3025 +  )
   3026 +  // clang-format on
   3027 +
   3028 +  return 0;
   3029 +}
   3030 +
   3031 +int
   3032 +dump_tag_event(yajl_gen gen, int mon_num, TagState old_state,
   3033 +               TagState new_state)
   3034 +{
   3035 +  // clang-format off
   3036 +  YMAP(
   3037 +    YSTR("tag_change_event"); YMAP(
   3038 +      YSTR("monitor_number"); YINT(mon_num);
   3039 +      YSTR("old_state"); dump_tag_state(gen, old_state);
   3040 +      YSTR("new_state"); dump_tag_state(gen, new_state);
   3041 +    )
   3042 +  )
   3043 +  // clang-format on
   3044 +
   3045 +  return 0;
   3046 +}
   3047 +
   3048 +int
   3049 +dump_client_focus_change_event(yajl_gen gen, Client *old_client,
   3050 +                               Client *new_client, int mon_num)
   3051 +{
   3052 +  // clang-format off
   3053 +  YMAP(
   3054 +    YSTR("client_focus_change_event"); YMAP(
   3055 +      YSTR("monitor_number"); YINT(mon_num);
   3056 +      YSTR("old_win_id"); old_client == NULL ? YNULL() : YINT(old_client->win);
   3057 +      YSTR("new_win_id"); new_client == NULL ? YNULL() : YINT(new_client->win);
   3058 +    )
   3059 +  )
   3060 +  // clang-format on
   3061 +
   3062 +  return 0;
   3063 +}
   3064 +
   3065 +int
   3066 +dump_layout_change_event(yajl_gen gen, const int mon_num,
   3067 +                         const char *old_symbol, const Layout *old_layout,
   3068 +                         const char *new_symbol, const Layout *new_layout)
   3069 +{
   3070 +  // clang-format off
   3071 +  YMAP(
   3072 +    YSTR("layout_change_event"); YMAP(
   3073 +      YSTR("monitor_number"); YINT(mon_num);
   3074 +      YSTR("old_symbol"); YSTR(old_symbol);
   3075 +      YSTR("old_address"); YINT((uintptr_t)old_layout);
   3076 +      YSTR("new_symbol"); YSTR(new_symbol);
   3077 +      YSTR("new_address"); YINT((uintptr_t)new_layout);
   3078 +    )
   3079 +  )
   3080 +  // clang-format on
   3081 +
   3082 +  return 0;
   3083 +}
   3084 +
   3085 +int
   3086 +dump_monitor_focus_change_event(yajl_gen gen, const int last_mon_num,
   3087 +                                const int new_mon_num)
   3088 +{
   3089 +  // clang-format off
   3090 +  YMAP(
   3091 +    YSTR("monitor_focus_change_event"); YMAP(
   3092 +      YSTR("old_monitor_number"); YINT(last_mon_num);
   3093 +      YSTR("new_monitor_number"); YINT(new_mon_num);
   3094 +    )
   3095 +  )
   3096 +  // clang-format on
   3097 +
   3098 +  return 0;
   3099 +}
   3100 +
   3101 +int
   3102 +dump_focused_title_change_event(yajl_gen gen, const int mon_num,
   3103 +                                const Window client_id, const char *old_name,
   3104 +                                const char *new_name)
   3105 +{
   3106 +  // clang-format off
   3107 +  YMAP(
   3108 +    YSTR("focused_title_change_event"); YMAP(
   3109 +      YSTR("monitor_number"); YINT(mon_num);
   3110 +      YSTR("client_window_id"); YINT(client_id);
   3111 +      YSTR("old_name"); YSTR(old_name);
   3112 +      YSTR("new_name"); YSTR(new_name);
   3113 +    )
   3114 +  )
   3115 +  // clang-format on
   3116 +
   3117 +  return 0;
   3118 +}
   3119 +
   3120 +int
   3121 +dump_client_state(yajl_gen gen, const ClientState *state)
   3122 +{
   3123 +  // clang-format off
   3124 +  YMAP(
   3125 +    YSTR("old_state"); YBOOL(state->oldstate);
   3126 +    YSTR("is_fixed"); YBOOL(state->isfixed);
   3127 +    YSTR("is_floating"); YBOOL(state->isfloating);
   3128 +    YSTR("is_fullscreen"); YBOOL(state->isfullscreen);
   3129 +    YSTR("is_urgent"); YBOOL(state->isurgent);
   3130 +    YSTR("never_focus"); YBOOL(state->neverfocus);
   3131 +  )
   3132 +  // clang-format on
   3133 +
   3134 +  return 0;
   3135 +}
   3136 +
   3137 +int
   3138 +dump_focused_state_change_event(yajl_gen gen, const int mon_num,
   3139 +                                const Window client_id,
   3140 +                                const ClientState *old_state,
   3141 +                                const ClientState *new_state)
   3142 +{
   3143 +  // clang-format off
   3144 +  YMAP(
   3145 +    YSTR("focused_state_change_event"); YMAP(
   3146 +      YSTR("monitor_number"); YINT(mon_num);
   3147 +      YSTR("client_window_id"); YINT(client_id);
   3148 +      YSTR("old_state"); dump_client_state(gen, old_state);
   3149 +      YSTR("new_state"); dump_client_state(gen, new_state);
   3150 +    )
   3151 +  )
   3152 +  // clang-format on
   3153 +
   3154 +  return 0;
   3155 +}
   3156 +
   3157 +int
   3158 +dump_error_message(yajl_gen gen, const char *reason)
   3159 +{
   3160 +  // clang-format off
   3161 +  YMAP(
   3162 +    YSTR("result"); YSTR("error");
   3163 +    YSTR("reason"); YSTR(reason);
   3164 +  )
   3165 +  // clang-format on
   3166 +
   3167 +  return 0;
   3168 +}
   3169 diff --git a/yajl_dumps.h b/yajl_dumps.h
   3170 new file mode 100644
   3171 index 0000000..ee9948e
   3172 --- /dev/null
   3173 +++ b/yajl_dumps.h
   3174 @@ -0,0 +1,65 @@
   3175 +#ifndef YAJL_DUMPS_H_
   3176 +#define YAJL_DUMPS_H_
   3177 +
   3178 +#include <string.h>
   3179 +#include <yajl/yajl_gen.h>
   3180 +
   3181 +#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
   3182 +#define YINT(num) yajl_gen_integer(gen, num)
   3183 +#define YDOUBLE(num) yajl_gen_double(gen, num)
   3184 +#define YBOOL(v) yajl_gen_bool(gen, v)
   3185 +#define YNULL() yajl_gen_null(gen)
   3186 +#define YARR(body)                                                             \
   3187 +  {                                                                            \
   3188 +    yajl_gen_array_open(gen);                                                  \
   3189 +    body;                                                                      \
   3190 +    yajl_gen_array_close(gen);                                                 \
   3191 +  }
   3192 +#define YMAP(body)                                                             \
   3193 +  {                                                                            \
   3194 +    yajl_gen_map_open(gen);                                                    \
   3195 +    body;                                                                      \
   3196 +    yajl_gen_map_close(gen);                                                   \
   3197 +  }
   3198 +
   3199 +int dump_tag(yajl_gen gen, const char *name, const int tag_mask);
   3200 +
   3201 +int dump_tags(yajl_gen gen, const char *tags[], int tags_len);
   3202 +
   3203 +int dump_client(yajl_gen gen, Client *c);
   3204 +
   3205 +int dump_monitor(yajl_gen gen, Monitor *mon, int is_selected);
   3206 +
   3207 +int dump_monitors(yajl_gen gen, Monitor *mons, Monitor *selmon);
   3208 +
   3209 +int dump_layouts(yajl_gen gen, const Layout layouts[], const int layouts_len);
   3210 +
   3211 +int dump_tag_state(yajl_gen gen, TagState state);
   3212 +
   3213 +int dump_tag_event(yajl_gen gen, int mon_num, TagState old_state,
   3214 +                   TagState new_state);
   3215 +
   3216 +int dump_client_focus_change_event(yajl_gen gen, Client *old_client,
   3217 +                                   Client *new_client, int mon_num);
   3218 +
   3219 +int dump_layout_change_event(yajl_gen gen, const int mon_num,
   3220 +                             const char *old_symbol, const Layout *old_layout,
   3221 +                             const char *new_symbol, const Layout *new_layout);
   3222 +
   3223 +int dump_monitor_focus_change_event(yajl_gen gen, const int last_mon_num,
   3224 +                                    const int new_mon_num);
   3225 +
   3226 +int dump_focused_title_change_event(yajl_gen gen, const int mon_num,
   3227 +                                    const Window client_id,
   3228 +                                    const char *old_name, const char *new_name);
   3229 +
   3230 +int dump_client_state(yajl_gen gen, const ClientState *state);
   3231 +
   3232 +int dump_focused_state_change_event(yajl_gen gen, const int mon_num,
   3233 +                                    const Window client_id,
   3234 +                                    const ClientState *old_state,
   3235 +                                    const ClientState *new_state);
   3236 +
   3237 +int dump_error_message(yajl_gen gen, const char *reason);
   3238 +
   3239 +#endif  // YAJL_DUMPS_H_
   3240 -- 
   3241 2.27.0
   3242