dwm-ipc-20201106-f04cac6.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) 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