sites

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

dwm-ipc-20200726-f04cac6.diff (89943B)


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