]> git.sur5r.net Git - i3/i3/blob - i3bar/src/ipc.c
86c61f863a3d255e4e52d3242d2895745670e16f
[i3/i3] / i3bar / src / ipc.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <stdint.h>
5 #include <sys/socket.h>
6 #include <sys/un.h>
7 #include <i3/ipc.h>
8 #include <ev.h>
9
10 #include "common.h"
11
12 ev_io *i3_connection;
13
14 typedef void(*handler_t)(char*);
15
16 /*
17  * Get a connect to the IPC-interface of i3 and return a filedescriptor
18  *
19  */
20 int get_ipc_fd(const char *socket_path) {
21     int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
22     if (sockfd == -1) {
23         printf("ERROR: Could not create Socket!\n");
24         exit(EXIT_FAILURE);
25     }
26
27     struct sockaddr_un addr;
28     memset(&addr, 0, sizeof(struct sockaddr_un));
29     addr.sun_family = AF_LOCAL;
30     strcpy(addr.sun_path, socket_path);
31     if (connect(sockfd, (const struct sockaddr*) &addr, sizeof(struct sockaddr_un)) < 0) {
32         printf("ERROR: Could not connct to i3\n");
33         exit(EXIT_FAILURE);
34     }
35     return sockfd;
36 }
37
38 /*
39  * Called, when we get a reply to a command from i3.
40  * Since i3 does not give us much feedback on commands, we do not much
41  *
42  */
43 void got_command_reply(char *reply) {
44     /* FIXME: Error handling for command-replies */
45 }
46
47 /*
48  * Called, when we get a reply with workspaces-data
49  *
50  */
51 void got_workspace_reply(char *reply) {
52     printf("Got Workspace-Data!\n");
53     parse_workspaces_json(reply);
54     draw_bars();
55 }
56
57 /*
58  * Called, when we get a reply for a subscription.
59  * Since i3 does not give us much feedback on commands, we do not much
60  *
61  */
62 void got_subscribe_reply(char *reply) {
63     printf("Got Subscribe Reply: %s\n", reply);
64     /* FIXME: Error handling for subscribe-commands */
65 }
66
67 /*
68  * Called, when we get a reply with outputs-data
69  *
70  */
71 void got_output_reply(char *reply) {
72     printf("Parsing Outputs-JSON...\n");
73     parse_outputs_json(reply);
74     printf("Reconfiguring Windows...\n");
75     reconfig_windows();
76 }
77
78 /* Data-structure to easily call the reply-handlers later */
79 handler_t reply_handlers[] = {
80     &got_command_reply,
81     &got_workspace_reply,
82     &got_subscribe_reply,
83     &got_output_reply,
84 };
85
86 /*
87  * Called, when a workspace-event arrives (i.e. the user changed the workspace)
88  *
89  */
90 void got_workspace_event(char *event) {
91     printf("Got Workspace Event!\n");
92     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
93 }
94
95 /*
96  * Called, when an output-event arrives (i.e. the screen-configuration changed)
97  *
98  */
99 void got_output_event(char *event) {
100     printf("Got Output Event!\n");
101     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
102     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
103 }
104
105 /* Data-structure to easily call the reply-handlers later */
106 handler_t event_handlers[] = {
107     &got_workspace_event,
108     &got_output_event
109 };
110
111 /*
112  * Called, when we get a message from i3
113  *
114  */
115 void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
116     printf("Got data!\n");
117     int fd = watcher->fd;
118
119     /* First we only read the header, because we know it's length */
120     uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t)*2;
121     char *header = malloc(header_len);
122     if (header == NULL) {
123         printf("ERROR: Could not allocate memory!\n");
124         exit(EXIT_FAILURE);
125     }
126
127     uint32_t rec = 0;
128     while (rec < header_len) {
129         int n = read(fd, header + rec, header_len - rec);
130         if (n == -1) {
131             printf("ERROR: read() failed!\n");
132             exit(EXIT_FAILURE);
133         }
134         if (n == 0) {
135             printf("ERROR: Nothing to read!\n");
136             exit(EXIT_FAILURE);
137         }
138         rec += n;
139     }
140
141     if (strncmp(header, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC))) {
142         printf("ERROR: Wrong magic code: %.*s\n Expected: %s\n",
143                (int) strlen(I3_IPC_MAGIC),
144                header,
145                I3_IPC_MAGIC);
146         exit(EXIT_FAILURE);
147     }
148
149     /* Know we read the rest of the message */
150     char *walk = header + strlen(I3_IPC_MAGIC);
151     uint32_t size = *((uint32_t*) walk);
152     walk += sizeof(uint32_t);
153     uint32_t type = *((uint32_t*) walk);
154     char *buffer = malloc(size + 1);
155     if (buffer == NULL) {
156         printf("ERROR: Could not allocate memory!\n");
157         exit(EXIT_FAILURE);
158     }
159     rec = 0;
160
161     while (rec < size) {
162         int n = read(fd, buffer + rec, size - rec);
163         if (n == -1) {
164             printf("ERROR: read() failed!\n");
165             exit(EXIT_FAILURE);
166         }
167         if (n == 0) {
168             printf("ERROR: Nothing to read!\n");
169             exit(EXIT_FAILURE);
170         }
171         rec += n;
172     }
173     buffer[size] = '\0';
174
175     /* And call the callback (indexed by the type) */
176     if (type & (1 << 31)) {
177         type ^= 1 << 31;
178         event_handlers[type](buffer);
179     } else {
180         reply_handlers[type](buffer);
181     }
182
183     FREE(header);
184     FREE(buffer);
185 }
186
187 /*
188  * Sends a Message to i3.
189  * type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
190  *
191  */
192 int i3_send_msg(uint32_t type, const char *payload) {
193     uint32_t len = 0;
194     if (payload != NULL) {
195         len = strlen(payload);
196     }
197
198     uint32_t to_write = strlen (I3_IPC_MAGIC) + sizeof(uint32_t)*2 + len;
199     char *buffer = malloc(to_write);
200     if (buffer == NULL) {
201         printf("ERROR: Could not allocate memory\n");
202         exit(EXIT_FAILURE);
203     }
204
205     char *walk = buffer;
206
207     strncpy(buffer, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC));
208     walk += strlen(I3_IPC_MAGIC);
209     memcpy(walk, &len, sizeof(uint32_t));
210     walk += sizeof(uint32_t);
211     memcpy(walk, &type, sizeof(uint32_t));
212     walk += sizeof(uint32_t);
213
214     strncpy(walk, payload, len);
215
216     uint32_t written = 0;
217
218     while (to_write > 0) {
219         int n = write(i3_connection->fd, buffer + written, to_write);
220         if (n == -1) {
221             printf("ERROR: write() failed!\n");
222             exit(EXIT_FAILURE);
223         }
224
225         to_write -= n;
226         written += n;
227     }
228
229     FREE(buffer);
230
231     return 1;
232 }
233
234 /*
235  * Initiate a connection to i3.
236  * socket-path must be a valid path to the ipc_socket of i3
237  *
238  */
239 int init_connection(const char *socket_path) {
240     int sockfd = get_ipc_fd(socket_path);
241
242     i3_connection = malloc(sizeof(ev_io));
243     ev_io_init(i3_connection, &got_data, sockfd, EV_READ);
244     ev_io_start(main_loop, i3_connection);
245
246     return 1;
247 }
248
249 /*
250  * Subscribe to all the i3-events, we need
251  *
252  */
253 void subscribe_events() {
254     i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\" ]");
255 }