1 /*
2 * vim:ts=4:sw=4:expandtab
3 *
4 * i3bar - an xcb-based status- and ws-bar for i3
5 * © 2010 Axel Wagner and contributors (see also: LICENSE)
6 *
7 * outputs.c: Maintaining the outputs list
8 *
9 */
10 #include "common.h"
11
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #include <yajl/yajl_parse.h>
18
19 /* A datatype to pass through the callbacks to save the state */
20 struct outputs_json_params {
21 struct outputs_head *outputs;
22 i3_output *outputs_walk;
23 char *cur_key;
24 char *json;
25 bool in_rect;
26 };
27
28 /*
29 * Parse a null value (current_workspace)
30 *
31 */
outputs_null_cb(void * params_)32 static int outputs_null_cb(void *params_) {
33 struct outputs_json_params *params = (struct outputs_json_params *)params_;
34
35 FREE(params->cur_key);
36
37 return 1;
38 }
39
40 /*
41 * Parse a boolean value (active)
42 *
43 */
outputs_boolean_cb(void * params_,int val)44 static int outputs_boolean_cb(void *params_, int val) {
45 struct outputs_json_params *params = (struct outputs_json_params *)params_;
46
47 if (!strcmp(params->cur_key, "active")) {
48 params->outputs_walk->active = val;
49 FREE(params->cur_key);
50 return 1;
51 }
52
53 if (!strcmp(params->cur_key, "primary")) {
54 params->outputs_walk->primary = val;
55 FREE(params->cur_key);
56 return 1;
57 }
58
59 return 0;
60 }
61
62 /*
63 * Parse an integer (current_workspace or the rect)
64 *
65 */
outputs_integer_cb(void * params_,long long val)66 static int outputs_integer_cb(void *params_, long long val) {
67 struct outputs_json_params *params = (struct outputs_json_params *)params_;
68
69 if (!strcmp(params->cur_key, "current_workspace")) {
70 params->outputs_walk->ws = (int)val;
71 FREE(params->cur_key);
72 return 1;
73 }
74
75 if (!strcmp(params->cur_key, "x")) {
76 params->outputs_walk->rect.x = (int)val;
77 FREE(params->cur_key);
78 return 1;
79 }
80
81 if (!strcmp(params->cur_key, "y")) {
82 params->outputs_walk->rect.y = (int)val;
83 FREE(params->cur_key);
84 return 1;
85 }
86
87 if (!strcmp(params->cur_key, "width")) {
88 params->outputs_walk->rect.w = (int)val;
89 FREE(params->cur_key);
90 return 1;
91 }
92
93 if (!strcmp(params->cur_key, "height")) {
94 params->outputs_walk->rect.h = (int)val;
95 FREE(params->cur_key);
96 return 1;
97 }
98
99 return 0;
100 }
101
102 /*
103 * Parse a string (name)
104 *
105 */
outputs_string_cb(void * params_,const unsigned char * val,size_t len)106 static int outputs_string_cb(void *params_, const unsigned char *val, size_t len) {
107 struct outputs_json_params *params = (struct outputs_json_params *)params_;
108
109 if (!strcmp(params->cur_key, "current_workspace")) {
110 char *copy = NULL;
111 sasprintf(©, "%.*s", len, val);
112
113 char *end;
114 errno = 0;
115 long parsed_num = strtol(copy, &end, 10);
116 if (errno == 0 &&
117 (end && *end == '\0'))
118 params->outputs_walk->ws = parsed_num;
119
120 FREE(copy);
121 FREE(params->cur_key);
122 return 1;
123 }
124
125 if (strcmp(params->cur_key, "name")) {
126 return 0;
127 }
128
129 sasprintf(&(params->outputs_walk->name), "%.*s", len, val);
130
131 FREE(params->cur_key);
132 return 1;
133 }
134
135 /*
136 * We hit the start of a JSON map (rect or a new output)
137 *
138 */
outputs_start_map_cb(void * params_)139 static int outputs_start_map_cb(void *params_) {
140 struct outputs_json_params *params = (struct outputs_json_params *)params_;
141 i3_output *new_output = NULL;
142
143 if (params->cur_key == NULL) {
144 new_output = smalloc(sizeof(i3_output));
145 new_output->name = NULL;
146 new_output->active = false;
147 new_output->primary = false;
148 new_output->visible = false;
149 new_output->ws = 0,
150 new_output->statusline_width = 0;
151 new_output->statusline_short_text = false;
152 memset(&new_output->rect, 0, sizeof(rect));
153 memset(&new_output->bar, 0, sizeof(surface_t));
154 memset(&new_output->buffer, 0, sizeof(surface_t));
155 memset(&new_output->statusline_buffer, 0, sizeof(surface_t));
156
157 new_output->workspaces = smalloc(sizeof(struct ws_head));
158 TAILQ_INIT(new_output->workspaces);
159
160 new_output->trayclients = smalloc(sizeof(struct tc_head));
161 TAILQ_INIT(new_output->trayclients);
162
163 params->outputs_walk = new_output;
164
165 return 1;
166 }
167
168 if (!strcmp(params->cur_key, "rect")) {
169 params->in_rect = true;
170 }
171
172 return 1;
173 }
174
clear_output(i3_output * output)175 static void clear_output(i3_output *output) {
176 FREE(output->name);
177 FREE(output->workspaces);
178 FREE(output->trayclients);
179 }
180
181 /*
182 * We hit the end of a map (rect or a new output)
183 *
184 */
outputs_end_map_cb(void * params_)185 static int outputs_end_map_cb(void *params_) {
186 struct outputs_json_params *params = (struct outputs_json_params *)params_;
187 if (params->in_rect) {
188 params->in_rect = false;
189 /* Ignore the end of a rect */
190 return 1;
191 }
192
193 /* See if we actually handle that output */
194 if (config.num_outputs > 0) {
195 const bool is_primary = params->outputs_walk->primary;
196 bool handle_output = false;
197 for (int c = 0; c < config.num_outputs; c++) {
198 if ((strcasecmp(params->outputs_walk->name, config.outputs[c]) == 0) ||
199 (strcasecmp(config.outputs[c], "primary") == 0 && is_primary) ||
200 (strcasecmp(config.outputs[c], "nonprimary") == 0 && !is_primary)) {
201 handle_output = true;
202 break;
203 }
204 }
205 if (!handle_output) {
206 DLOG("Ignoring output \"%s\", not configured to handle it.\n",
207 params->outputs_walk->name);
208 clear_output(params->outputs_walk);
209 FREE(params->outputs_walk);
210 FREE(params->cur_key);
211 return 1;
212 }
213 }
214
215 i3_output *target = get_output_by_name(params->outputs_walk->name);
216
217 if (target == NULL) {
218 SLIST_INSERT_HEAD(outputs, params->outputs_walk, slist);
219 } else {
220 target->active = params->outputs_walk->active;
221 target->primary = params->outputs_walk->primary;
222 target->ws = params->outputs_walk->ws;
223 target->rect = params->outputs_walk->rect;
224
225 clear_output(params->outputs_walk);
226 FREE(params->outputs_walk);
227 }
228 return 1;
229 }
230
231 /*
232 * Parse a key.
233 *
234 * Essentially we just save it in the parsing state
235 *
236 */
outputs_map_key_cb(void * params_,const unsigned char * keyVal,size_t keyLen)237 static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
238 struct outputs_json_params *params = (struct outputs_json_params *)params_;
239 FREE(params->cur_key);
240 sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
241 return 1;
242 }
243
244 /* A datastructure to pass all these callbacks to yajl */
245 static yajl_callbacks outputs_callbacks = {
246 .yajl_null = outputs_null_cb,
247 .yajl_boolean = outputs_boolean_cb,
248 .yajl_integer = outputs_integer_cb,
249 .yajl_string = outputs_string_cb,
250 .yajl_start_map = outputs_start_map_cb,
251 .yajl_map_key = outputs_map_key_cb,
252 .yajl_end_map = outputs_end_map_cb,
253 };
254
255 struct outputs_head *outputs;
256 /*
257 * Initiate the outputs list
258 *
259 */
init_outputs(void)260 void init_outputs(void) {
261 outputs = smalloc(sizeof(struct outputs_head));
262 SLIST_INIT(outputs);
263 }
264
265 /*
266 * Start parsing the received JSON string
267 *
268 */
parse_outputs_json(char * json)269 void parse_outputs_json(char *json) {
270 struct outputs_json_params params;
271 params.outputs_walk = NULL;
272 params.cur_key = NULL;
273 params.json = json;
274 params.in_rect = false;
275
276 yajl_handle handle;
277 yajl_status state;
278 handle = yajl_alloc(&outputs_callbacks, NULL, (void *)¶ms);
279
280 state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
281
282 /* FIXME: Proper errorhandling for JSON-parsing */
283 switch (state) {
284 case yajl_status_ok:
285 break;
286 case yajl_status_client_canceled:
287 case yajl_status_error:
288 ELOG("Could not parse outputs reply!\n");
289 exit(EXIT_FAILURE);
290 break;
291 }
292
293 yajl_free(handle);
294 }
295
296 /*
297 * free() all outputs data structures.
298 *
299 */
free_outputs(void)300 void free_outputs(void) {
301 free_workspaces();
302
303 i3_output *outputs_walk;
304 if (outputs == NULL) {
305 return;
306 }
307 SLIST_FOREACH (outputs_walk, outputs, slist) {
308 destroy_window(outputs_walk);
309 if (outputs_walk->trayclients != NULL && !TAILQ_EMPTY(outputs_walk->trayclients)) {
310 FREE_TAILQ(outputs_walk->trayclients, trayclient);
311 }
312 clear_output(outputs_walk);
313 }
314 FREE_SLIST(outputs, i3_output);
315 }
316
317 /*
318 * Returns the output with the given name
319 *
320 */
get_output_by_name(char * name)321 i3_output *get_output_by_name(char *name) {
322 i3_output *walk;
323 if (name == NULL) {
324 return NULL;
325 }
326 SLIST_FOREACH (walk, outputs, slist) {
327 if (!strcmp(walk->name, name)) {
328 break;
329 }
330 }
331
332 return walk;
333 }
334
335 /*
336 * Returns true if the output has the currently focused workspace
337 *
338 */
output_has_focus(i3_output * output)339 bool output_has_focus(i3_output *output) {
340 i3_ws *ws_walk;
341 TAILQ_FOREACH (ws_walk, output->workspaces, tailq) {
342 if (ws_walk->focused) {
343 return true;
344 }
345 }
346 return false;
347 }
348