]> git.sur5r.net Git - i3/i3/blob - i3bar/src/workspaces.c
Merge branch 'master' into next
[i3/i3] / i3bar / src / workspaces.c
1 /*
2  * i3bar - an xcb-based status- and ws-bar for i3
3  *
4  * © 2010-2011 Axel Wagner and contributors
5  *
6  * See file LICNSE for license information
7  *
8  * src/workspaces.c: Maintaining the workspace-lists
9  *
10  */
11 #include <string.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include <yajl/yajl_parse.h>
16 #include <yajl/yajl_version.h>
17
18 #include "common.h"
19
20 /* A datatype to pass through the callbacks to save the state */
21 struct workspaces_json_params {
22     struct ws_head *workspaces;
23     i3_ws          *workspaces_walk;
24     char           *cur_key;
25     char           *json;
26 };
27
28 /*
29  * Parse a boolean value (visible, focused, urgent)
30  *
31  */
32 static int workspaces_boolean_cb(void *params_, int val) {
33     struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
34
35     if (!strcmp(params->cur_key, "visible")) {
36         params->workspaces_walk->visible = val;
37         FREE(params->cur_key);
38         return 1;
39     }
40
41     if (!strcmp(params->cur_key, "focused")) {
42         params->workspaces_walk->focused = val;
43         FREE(params->cur_key);
44         return 1;
45     }
46
47     if (!strcmp(params->cur_key, "urgent")) {
48         params->workspaces_walk->urgent = val;
49         FREE(params->cur_key);
50         return 1;
51     }
52
53     FREE(params->cur_key);
54
55     return 0;
56 }
57
58 /*
59  * Parse an integer (num or the rect)
60  *
61  */
62 #if YAJL_MAJOR >= 2
63 static int workspaces_integer_cb(void *params_, long long val) {
64 #else
65 static int workspaces_integer_cb(void *params_, long val) {
66 #endif
67     struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
68
69     if (!strcmp(params->cur_key, "num")) {
70         params->workspaces_walk->num = (int) val;
71         FREE(params->cur_key);
72         return 1;
73     }
74
75     if (!strcmp(params->cur_key, "x")) {
76         params->workspaces_walk->rect.x = (int) val;
77         FREE(params->cur_key);
78         return 1;
79     }
80
81     if (!strcmp(params->cur_key, "y")) {
82         params->workspaces_walk->rect.y = (int) val;
83         FREE(params->cur_key);
84         return 1;
85     }
86
87     if (!strcmp(params->cur_key, "width")) {
88         params->workspaces_walk->rect.w = (int) val;
89         FREE(params->cur_key);
90         return 1;
91     }
92
93     if (!strcmp(params->cur_key, "height")) {
94         params->workspaces_walk->rect.h = (int) val;
95         FREE(params->cur_key);
96         return 1;
97     }
98
99     FREE(params->cur_key);
100     return 0;
101 }
102
103 /*
104  * Parse a string (name, output)
105  *
106  */
107 #if YAJL_MAJOR >= 2
108 static int workspaces_string_cb(void *params_, const unsigned char *val, size_t len) {
109 #else
110 static int workspaces_string_cb(void *params_, const unsigned char *val, unsigned int len) {
111 #endif
112         struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
113
114         char *output_name;
115
116         if (!strcmp(params->cur_key, "name")) {
117             /* Save the name */
118             params->workspaces_walk->name = malloc(sizeof(const unsigned char) * (len + 1));
119             strncpy(params->workspaces_walk->name, (const char*) val, len);
120             params->workspaces_walk->name[len] = '\0';
121
122             /* Convert the name to ucs2, save its length in glyphs and calculate its rendered width */
123             int ucs2_len;
124             xcb_char2b_t *ucs2_name = (xcb_char2b_t*) convert_utf8_to_ucs2(params->workspaces_walk->name, &ucs2_len);
125             params->workspaces_walk->ucs2_name = ucs2_name;
126             params->workspaces_walk->name_glyphs = ucs2_len;
127             params->workspaces_walk->name_width =
128                 predict_text_extents(params->workspaces_walk->ucs2_name,
129                 params->workspaces_walk->name_glyphs);
130
131             DLOG("Got Workspace %s, name_width: %d, glyphs: %d\n",
132                  params->workspaces_walk->name,
133                  params->workspaces_walk->name_width,
134                  params->workspaces_walk->name_glyphs);
135             FREE(params->cur_key);
136
137             return 1;
138         }
139
140         if (!strcmp(params->cur_key, "output")) {
141             /* We add the ws to the TAILQ of the output, it belongs to */
142             output_name = malloc(sizeof(const unsigned char) * (len + 1));
143             strncpy(output_name, (const char*) val, len);
144             output_name[len] = '\0';
145             params->workspaces_walk->output = get_output_by_name(output_name);
146
147             TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces,
148                               params->workspaces_walk,
149                               tailq);
150
151             FREE(output_name);
152             return 1;
153         }
154
155         return 0;
156 }
157
158 /*
159  * We hit the start of a json-map (rect or a new output)
160  *
161  */
162 static int workspaces_start_map_cb(void *params_) {
163     struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
164
165     i3_ws *new_workspace = NULL;
166
167     if (params->cur_key == NULL) {
168         new_workspace = malloc(sizeof(i3_ws));
169         new_workspace->num = -1;
170         new_workspace->name = NULL;
171         new_workspace->visible = 0;
172         new_workspace->focused = 0;
173         new_workspace->urgent = 0;
174         memset(&new_workspace->rect, 0, sizeof(rect));
175         new_workspace->output = NULL;
176
177         params->workspaces_walk = new_workspace;
178         return 1;
179     }
180
181     return 1;
182 }
183
184 /*
185  * Parse a key.
186  *
187  * Essentially we just save it in the parsing-state
188  *
189  */
190 #if YAJL_MAJOR >= 2
191 static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
192 #else
193 static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, unsigned int keyLen) {
194 #endif
195     struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
196     FREE(params->cur_key);
197
198     params->cur_key = malloc(sizeof(unsigned char) * (keyLen + 1));
199     if (params->cur_key == NULL) {
200         ELOG("Could not allocate memory: %s\n", strerror(errno));
201         exit(EXIT_FAILURE);
202     }
203     strncpy(params->cur_key, (const char*) keyVal, keyLen);
204     params->cur_key[keyLen] = '\0';
205
206     return 1;
207 }
208
209 /* A datastructure to pass all these callbacks to yajl */
210 yajl_callbacks workspaces_callbacks = {
211     NULL,
212     &workspaces_boolean_cb,
213     &workspaces_integer_cb,
214     NULL,
215     NULL,
216     &workspaces_string_cb,
217     &workspaces_start_map_cb,
218     &workspaces_map_key_cb,
219     NULL,
220     NULL,
221     NULL
222 };
223
224 /*
225  * Start parsing the received json-string
226  *
227  */
228 void parse_workspaces_json(char *json) {
229     /* FIXME: Fasciliate stream-processing, i.e. allow starting to interpret
230      * JSON in chunks */
231     struct workspaces_json_params params;
232
233     free_workspaces();
234
235     params.workspaces_walk = NULL;
236     params.cur_key = NULL;
237     params.json = json;
238
239     yajl_handle handle;
240     yajl_status state;
241 #if YAJL_MAJOR < 2
242     yajl_parser_config parse_conf = { 0, 0 };
243
244     handle = yajl_alloc(&workspaces_callbacks, &parse_conf, NULL, (void*) &params);
245 #else
246     handle = yajl_alloc(&workspaces_callbacks, NULL, (void*) &params);
247 #endif
248
249     state = yajl_parse(handle, (const unsigned char*) json, strlen(json));
250
251     /* FIXME: Propper errorhandling for JSON-parsing */
252     switch (state) {
253         case yajl_status_ok:
254             break;
255         case yajl_status_client_canceled:
256 #if YAJL_MAJOR < 2
257         case yajl_status_insufficient_data:
258 #endif
259         case yajl_status_error:
260             ELOG("Could not parse workspaces-reply!\n");
261             exit(EXIT_FAILURE);
262             break;
263     }
264
265     yajl_free(handle);
266
267     FREE(params.cur_key);
268 }
269
270 /*
271  * free() all workspace data-structures. Does not free() the heads of the tailqueues.
272  *
273  */
274 void free_workspaces() {
275     i3_output *outputs_walk;
276     if (outputs == NULL) {
277         return;
278     }
279     i3_ws     *ws_walk;
280
281     SLIST_FOREACH(outputs_walk, outputs, slist) {
282         if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
283             TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
284                 FREE(ws_walk->name);
285                 FREE(ws_walk->ucs2_name);
286             }
287             FREE_TAILQ(outputs_walk->workspaces, i3_ws);
288         }
289     }
290 }