diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | src/comet.c | 4 | ||||
| -rw-r--r-- | src/dwm.c | 285 | ||||
| -rw-r--r-- | src/dwm.h | 14 |
4 files changed, 305 insertions, 0 deletions
@@ -11,6 +11,8 @@ PCDEP = xcb \ xcb-errors \ "xcb-randr >= 1.5" \ "glib-2.0 >= 2.44" \ + gio-2.0 \ + json-glib-1.0 \ pangocairo CFLAGS := $(shell pkg-config --cflags $(PCDEP)) -Werror diff --git a/src/comet.c b/src/comet.c index b6b36c5..5feab31 100644 --- a/src/comet.c +++ b/src/comet.c @@ -11,6 +11,7 @@ #include "draw.h" #include "connect.h" #include "state.h" +#include "dwm.h" #define EVEN(n) ((int)(n) - ((int)(n) % 2 != 0)) @@ -364,6 +365,8 @@ int main(int argc, char **argv) button_group_append(group, menu); state_add_button(state, group); + DwmIpc *dwm = dwm_create(state, "/tmp/dwm.sock"); + connect_attach_state(con, state); connect_attach_source(con); @@ -387,6 +390,7 @@ int main(int argc, char **argv) g_list_free(menu_ctx.toggled); // Buttons are freed by state_destroy + dwm_destroy(dwm); state_destroy(state); draw_destroy(draw); window_destroy(win); diff --git a/src/dwm.c b/src/dwm.c new file mode 100644 index 0000000..513f08a --- /dev/null +++ b/src/dwm.c @@ -0,0 +1,285 @@ +#include <stdbool.h> +#include <stdint.h> +#include <glib.h> +#include <gio/gio.h> +#include <json-glib/json-glib.h> +#include <json-glib/json-builder.h> + +#include "dwm.h" +#include "log.h" + +#define IPC_MAGIC "DWM-IPC" +#define IPC_MAGIC_LEN 7 + +typedef struct { + uint8_t magic[IPC_MAGIC_LEN]; + uint32_t size; + uint8_t type; +} __attribute__((packed)) DwmIpcHeader; + +struct DwmIpc { + GSource source; + GSocket *socket; + State *state; +}; + +typedef enum { + IPC_TYPE_RUN_COMMAND = 0, + IPC_TYPE_GET_MONITORS = 1, + IPC_TYPE_GET_TAGS = 2, + IPC_TYPE_GET_LAYOUTS = 3, + IPC_TYPE_GET_DWM_CLIENT = 4, + IPC_TYPE_SUBSCRIBE = 5, + IPC_TYPE_EVENT = 6 +} DwmIpcMessage; + +#define IPC_EVENT_TAG_CHANGE "tag_change_event" +#define IPC_EVENT_CLIENT_FOCUS_CHANGE "client_focus_change_event" +#define IPC_EVENT_LAYOUT_CHANGE "layout_change_event" +#define IPC_EVENT_MONITOR_FOCUS_CHANGE "monitor_focus_change_event" +#define IPC_EVENT_FOCUSED_TITLE_CHANGE "focused_title_change_event" +#define IPC_EVENT_FOCUSED_STATE_CHANGE "focused_state_change_event" + +static void ipc_send(DwmIpc *dwm, DwmIpcMessage msg_type, uint8_t *msg, uint32_t msg_size) +{ + DwmIpcHeader header; + memcpy(header.magic, IPC_MAGIC, IPC_MAGIC_LEN); + header.size = msg_size; + header.type = msg_type; + + GError *error = NULL; + + gssize size = 0; + while (size < sizeof(DwmIpcHeader)) { + gssize sent = g_socket_send(dwm->socket, (void *)&header + size, sizeof(DwmIpcHeader) - size, NULL, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_error_free(error); + continue; + } + g_assert(sent != -1); + size += sent; + } + + size = 0; + while (size < msg_size) { + gssize sent = g_socket_send(dwm->socket, msg + size, msg_size - size, NULL, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_error_free(error); + continue; + } + g_assert(sent != -1); + size += sent; + } + + log_debug("Sent ipc message [type=%d, size=%d]", msg_type, msg_size); +} + +static void ipc_subscribe(DwmIpc *dwm, const char *event) +{ + // { + // "event": "<event>", + // "action": "subscribe" + // } + + JsonBuilder *builder = json_builder_new(); + + json_builder_begin_object(builder); + + json_builder_set_member_name(builder, "event"); + json_builder_add_string_value(builder, event); + + json_builder_set_member_name(builder, "action"); + json_builder_add_string_value(builder, "subscribe"); + + json_builder_end_object(builder); + + JsonNode *root = json_builder_get_root(builder); + + JsonGenerator *gen = json_generator_new(); + json_generator_set_root(gen, root); + + gsize msg_len = 0; + gchar *msg = json_generator_to_data(gen, &msg_len); + + log_info("Subscribing to dwm ipc: %s", msg); + ipc_send(dwm, IPC_TYPE_SUBSCRIBE, msg, msg_len); + + json_node_free(root); + g_object_unref(gen); + g_object_unref(builder); + g_free(msg); +} + +static void ipc_handle(DwmIpc *dwm, DwmIpcMessage type, char *reply, uint32_t reply_size) +{ + GError *error = NULL; + JsonParser *parser = json_parser_new(); + if (!json_parser_load_from_data(parser, reply, reply_size - 1, &error)) { + log_warning("Failed to parse dwm ipc reply: %s", error->message); + g_error_free(error); + return; + } + + JsonReader *reader = json_reader_new(json_parser_get_root(parser)); + + if (type == IPC_TYPE_EVENT) { + if (json_reader_read_member(reader, IPC_EVENT_TAG_CHANGE)) { + log_debug("Reading dwm ipc event [event=%s]", IPC_EVENT_TAG_CHANGE); + + json_reader_read_member(reader, "new_state"); + json_reader_read_member(reader, "selected"); + long selected = json_reader_get_int_value(reader); + json_reader_end_member (reader); + json_reader_end_member (reader); + json_reader_end_member (reader); + + log_debug("Dwm tag mask: %ld", selected); + + for (int i = 0; i < 9; i++) { + Button *btn = g_list_nth_data(dwm->state->btns, i); + g_assert_nonnull(btn); + + Color color = { 0.4, 0.4, 0.4, 1 }; + Color sel_color = { 0.3, 0.5, 0.2, 1 }; + + if (selected & (1 << i)) + btn->color = sel_color; + else + btn->color = color; + } + + state_redraw(dwm->state, false); + } else if (json_reader_read_member(reader, IPC_EVENT_CLIENT_FOCUS_CHANGE)) { + log_info("Ignoring dwm ipc event [event=%s]", IPC_EVENT_CLIENT_FOCUS_CHANGE); + // TODO + + } else if (json_reader_read_member(reader, IPC_EVENT_LAYOUT_CHANGE)) { + log_info("Ignoring dwm ipc event [event=%s]", IPC_EVENT_LAYOUT_CHANGE); + // TODO + + } else if (json_reader_read_member(reader, IPC_EVENT_MONITOR_FOCUS_CHANGE)) { + log_info("Ignoring dwm ipc event [event=%s]", IPC_EVENT_MONITOR_FOCUS_CHANGE); + // TODO + + } else if (json_reader_read_member(reader, IPC_EVENT_FOCUSED_TITLE_CHANGE)) { + log_info("Ignoring dwm ipc event [event=%s]", IPC_EVENT_FOCUSED_TITLE_CHANGE); + // TODO + + } else if (json_reader_read_member(reader, IPC_EVENT_FOCUSED_STATE_CHANGE)) { + log_info("Ignoring dwm ipc event [event=%s]", IPC_EVENT_FOCUSED_STATE_CHANGE); + // TODO + + } else { + log_warning("Unrecognized dwm ipc event"); + } + } else { + log_debug("Ignoring dwm ipc message [type=%d]", type); + } + + g_object_unref (reader); + g_object_unref (parser); +} + +static gboolean source_check(GSource *source) +{ + DwmIpc *dwm = (DwmIpc *)source; + GIOCondition flags = g_socket_condition_check(dwm->socket, G_IO_IN); + return flags & G_IO_IN; +} + +static gboolean source_dispatch(GSource *source, GSourceFunc callback, gpointer data) +{ + DwmIpc *dwm = (DwmIpc *)source; + DwmIpcHeader header; + GError *error = NULL; + + gssize size = 0; + while (size < sizeof(DwmIpcHeader)) { + gssize read = g_socket_receive(dwm->socket, (void *)&header + size, sizeof(DwmIpcHeader) - size, NULL, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_error_free(error); + continue; + } + g_assert(read != -1); + size += read; + } + + if (memcmp(&header, IPC_MAGIC, IPC_MAGIC_LEN) != 0) { + log_warning("Erroneous ipc message"); + return G_SOURCE_CONTINUE;; + } + + uint32_t reply_size; + memcpy(&reply_size, (void *)&header + IPC_MAGIC_LEN, sizeof(uint32_t)); + + uint8_t msg_type; + memcpy(&msg_type, (void *)&header + IPC_MAGIC_LEN + sizeof(uint32_t), sizeof(uint8_t)); + + char *reply = g_malloc(reply_size); + + size = 0; + while (size < reply_size) { + gssize read = g_socket_receive(dwm->socket, reply + size, reply_size - size, NULL, NULL); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_error_free(error); + continue; + } + g_assert(read != -1); + size += read; + } + + //log_debug("Received dwm ipc response: %s", reply); + ipc_handle(dwm, msg_type, reply, reply_size); + + return G_SOURCE_CONTINUE; +} + +static void source_finalize(GSource *source) +{ + g_object_unref(((DwmIpc *)source)->socket); +} + +static GSourceFuncs source_fns = { + NULL, + source_check, + source_dispatch, + source_finalize, +}; + +DwmIpc *dwm_create(State *state, const char *socket) +{ + DwmIpc *dwm = (DwmIpc *)g_source_new(&source_fns, sizeof(DwmIpc)); + g_source_set_static_name((GSource *)dwm, "DwmIpc"); + + GError *error = NULL; + dwm->socket = g_socket_new(G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, 0, &error); + g_assert_nonnull(dwm->socket); + + GSocketAddress *addr = g_unix_socket_address_new(socket); + + if (!g_socket_connect(dwm->socket, addr, NULL, &error)) { + g_assert_nonnull(error); + log_error("Failed to connect to %s: %s", socket, error->message); + } + + g_source_attach((GSource *)dwm, NULL); + + dwm->state = state; + + const char *events[] = { + IPC_EVENT_TAG_CHANGE, + IPC_EVENT_FOCUSED_STATE_CHANGE, + }; + for (int i = 0; i < G_N_ELEMENTS(events); ++i) + ipc_subscribe(dwm, events[i]); + + log_debug("Attached dwm ipc source"); + + return dwm; +} + +void dwm_destroy(DwmIpc *dwm) +{ + g_source_destroy((GSource *)dwm); + g_source_unref((GSource *)dwm); +} diff --git a/src/dwm.h b/src/dwm.h new file mode 100644 index 0000000..eab8ddb --- /dev/null +++ b/src/dwm.h @@ -0,0 +1,14 @@ +#ifndef COMET_DWM_H +#define COMET_DWM_H + +#include "state.h" + +typedef struct DwmIpc DwmIpc; + +DwmIpc *dwm_create(State *state, const char *socket); + +void dwm_destroy(DwmIpc *dwm); + +#endif + +// vim: ts=4 sw=4 et |
