sites

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

dwm-ipc-20201106-f04cac6.diff (90493B)


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