sites

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

dwm-ipc-20200724-f04cac6.diff (89873B)


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