i3
util.c
Go to the documentation of this file.
1/*
2 * vim:ts=4:sw=4:expandtab
3 *
4 * i3 - an improved tiling window manager
5 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
6 *
7 * util.c: Utility functions, which can be useful everywhere within i3 (see
8 * also libi3).
9 *
10 */
11#include "all.h"
12
13#include <ctype.h>
14#include <fcntl.h>
15#include <inttypes.h>
16#include <libgen.h>
17#include <locale.h>
18#include <sys/wait.h>
19#include <unistd.h>
20#if defined(__OpenBSD__)
21#include <sys/cdefs.h>
22#endif
23
24int min(int a, int b) {
25 return (a < b ? a : b);
26}
27
28int max(int a, int b) {
29 return (a > b ? a : b);
30}
31
32bool rect_contains(Rect rect, uint32_t x, uint32_t y) {
33 return (x >= rect.x &&
34 x <= (rect.x + rect.width) &&
35 y >= rect.y &&
36 y <= (rect.y + rect.height));
37}
38
40 return (Rect){a.x + b.x,
41 a.y + b.y,
42 a.width + b.width,
43 a.height + b.height};
44}
45
47 return (Rect){a.x - b.x,
48 a.y - b.y,
49 a.width - b.width,
50 a.height - b.height};
51}
52
54 rect.width = (int32_t)rect.width <= 0 ? 1 : rect.width;
55 rect.height = (int32_t)rect.height <= 0 ? 1 : rect.height;
56 return rect;
57}
58
59bool rect_equals(Rect a, Rect b) {
60 return a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height;
61}
62
63/*
64 * Returns true if the name consists of only digits.
65 *
66 */
67__attribute__((pure)) bool name_is_digits(const char *name) {
68 /* positive integers and zero are interpreted as numbers */
69 for (size_t i = 0; i < strlen(name); i++) {
70 if (!isdigit(name[i])) {
71 return false;
72 }
73 }
74
75 return true;
76}
77
78/*
79 * Set 'out' to the layout_t value for the given layout. The function
80 * returns true on success or false if the passed string is not a valid
81 * layout name.
82 *
83 */
84bool layout_from_name(const char *layout_str, layout_t *out) {
85 if (strcmp(layout_str, "default") == 0) {
86 *out = L_DEFAULT;
87 return true;
88 } else if (strcasecmp(layout_str, "stacked") == 0 ||
89 strcasecmp(layout_str, "stacking") == 0) {
90 *out = L_STACKED;
91 return true;
92 } else if (strcasecmp(layout_str, "tabbed") == 0) {
93 *out = L_TABBED;
94 return true;
95 } else if (strcasecmp(layout_str, "splitv") == 0) {
96 *out = L_SPLITV;
97 return true;
98 } else if (strcasecmp(layout_str, "splith") == 0) {
99 *out = L_SPLITH;
100 return true;
101 }
102
103 return false;
104}
105
106/*
107 * Parses the workspace name as a number. Returns -1 if the workspace should be
108 * interpreted as a "named workspace".
109 *
110 */
111int ws_name_to_number(const char *name) {
112 /* positive integers and zero are interpreted as numbers */
113 char *endptr = NULL;
114 errno = 0;
115 long long parsed_num = strtoll(name, &endptr, 10);
116 if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == name) {
117 parsed_num = -1;
118 }
119
120 return parsed_num;
121}
122
123/*
124 * Updates *destination with new_value and returns true if it was changed or false
125 * if it was the same
126 *
127 */
128bool update_if_necessary(uint32_t *destination, const uint32_t new_value) {
129 uint32_t old_value = *destination;
130
131 return ((*destination = new_value) != old_value);
132}
133
134/*
135 * exec()s an i3 utility, for example the config file migration script or
136 * i3-nagbar. This function first searches $PATH for the given utility named,
137 * then falls back to the dirname() of the i3 executable path and then falls
138 * back to the dirname() of the target of /proc/self/exe (on linux).
139 *
140 * This function should be called after fork()ing.
141 *
142 * The first argument of the given argv vector will be overwritten with the
143 * executable name, so pass NULL.
144 *
145 * If the utility cannot be found in any of these locations, it exits with
146 * return code 2.
147 *
148 */
149void exec_i3_utility(char *name, char *argv[]) {
150 /* start the utility, search PATH first */
151 char *binary = name;
152 argv[0] = binary;
153 execvp(binary, argv);
154
155 /* if the utility is not in path, maybe the user installed to a strange
156 * location and runs the i3 binary with an absolute path. We use
157 * argv[0]’s dirname */
158 char *pathbuf = sstrdup(start_argv[0]);
159 char *dir = dirname(pathbuf);
160 sasprintf(&binary, "%s/%s", dir, name);
161 argv[0] = binary;
162 execvp(binary, argv);
163
164#if defined(__linux__)
165 /* on linux, we have one more fall-back: dirname(/proc/self/exe) */
166 char buffer[BUFSIZ];
167 if (readlink("/proc/self/exe", buffer, BUFSIZ) == -1) {
168 warn("could not read /proc/self/exe");
169 _exit(EXIT_FAILURE);
170 }
171 dir = dirname(buffer);
172 sasprintf(&binary, "%s/%s", dir, name);
173 argv[0] = binary;
174 execvp(binary, argv);
175#endif
176
177 warn("Could not start %s", name);
178 _exit(2);
179}
180
181/*
182 * Goes through the list of arguments (for exec()) and add/replace the given option,
183 * including the option name, its argument, and the option character.
184 */
185static char **add_argument(char **original, char *opt_char, char *opt_arg, char *opt_name) {
186 int num_args;
187 for (num_args = 0; original[num_args] != NULL; num_args++) {
188 }
189 char **result = scalloc(num_args + 3, sizeof(char *));
190
191 /* copy the arguments, but skip the ones we'll replace */
192 int write_index = 0;
193 bool skip_next = false;
194 for (int i = 0; i < num_args; ++i) {
195 if (skip_next) {
196 skip_next = false;
197 continue;
198 }
199 if (!strcmp(original[i], opt_char) ||
200 (opt_name && !strcmp(original[i], opt_name))) {
201 if (opt_arg) {
202 skip_next = true;
203 }
204 continue;
205 }
206 result[write_index++] = original[i];
207 }
208
209 /* add the arguments we'll replace */
210 result[write_index++] = opt_char;
211 result[write_index] = opt_arg;
212
213 return result;
214}
215
216#define y(x, ...) yajl_gen_##x(gen, ##__VA_ARGS__)
217#define ystr(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
218
219static char *store_restart_layout(void) {
220 setlocale(LC_NUMERIC, "C");
221 yajl_gen gen = yajl_gen_alloc(NULL);
222
223 dump_node(gen, croot, true);
224
225 setlocale(LC_NUMERIC, "");
226
227 const unsigned char *payload;
228 size_t length;
229 y(get_buf, &payload, &length);
230
231 /* create a temporary file if one hasn't been specified, or just
232 * resolve the tildes in the specified path */
233 char *filename;
234 if (config.restart_state_path == NULL) {
235 filename = get_process_filename("restart-state");
236 if (!filename) {
237 return NULL;
238 }
239 } else {
241 }
242
243 /* create the directory, it could have been cleaned up before restarting or
244 * may not exist at all in case it was user-specified. */
245 char *filenamecopy = sstrdup(filename);
246 char *base = dirname(filenamecopy);
247 DLOG("Creating \"%s\" for storing the restart layout\n", base);
248 if (mkdirp(base, DEFAULT_DIR_MODE) != 0) {
249 ELOG("Could not create \"%s\" for storing the restart layout, layout will be lost.\n", base);
250 }
251 free(filenamecopy);
252
253 int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
254 if (fd == -1) {
255 perror("open()");
256 free(filename);
257 return NULL;
258 }
259
260 if (writeall(fd, payload, length) == -1) {
261 ELOG("Could not write restart layout to \"%s\", layout will be lost: %s\n", filename, strerror(errno));
262 free(filename);
263 close(fd);
264 return NULL;
265 }
266
267 close(fd);
268
269 if (length > 0) {
270 DLOG("layout: %.*s\n", (int)length, payload);
271 }
272
273 y(free);
274
275 return filename;
276}
277
278/*
279 * Restart i3 in-place
280 * appends -a to argument list to disable autostart
281 *
282 */
283void i3_restart(bool forget_layout) {
284 char *restart_filename = forget_layout ? NULL : store_restart_layout();
285
288
290
292
293 LOG("restarting \"%s\"...\n", start_argv[0]);
294 /* make sure -a is in the argument list or add it */
295 start_argv = add_argument(start_argv, "-a", NULL, NULL);
296
297 /* make debuglog-on persist */
298 if (get_debug_logging()) {
299 start_argv = add_argument(start_argv, "-d", "all", NULL);
300 }
301
302 /* replace -r <file> so that the layout is restored */
303 if (restart_filename != NULL) {
304 start_argv = add_argument(start_argv, "--restart", restart_filename, "-r");
305 }
306
307 execvp(start_argv[0], start_argv);
308
309 /* not reached */
310}
311
312/*
313 * Escapes the given string if a pango font is currently used.
314 * If the string has to be escaped, the input string will be free'd.
315 *
316 */
317char *pango_escape_markup(char *input) {
318 if (!font_is_pango()) {
319 return input;
320 }
321
322 char *escaped = g_markup_escape_text(input, -1);
323 FREE(input);
324
325 return escaped;
326}
327
328/*
329 * Handler which will be called when we get a SIGCHLD for the nagbar, meaning
330 * it exited (or could not be started, depending on the exit code).
331 *
332 */
333static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
334 ev_child_stop(EV_A_ watcher);
335
336 int exitcode = WEXITSTATUS(watcher->rstatus);
337 if (!WIFEXITED(watcher->rstatus)) {
338 ELOG("i3-nagbar (%d) did not exit normally. This is not an error if the config was reloaded while a nagbar was active.\n", watcher->pid);
339 } else if (exitcode != 0) {
340 ELOG("i3-nagbar (%d) process exited with status %d\n", watcher->pid, exitcode);
341 } else {
342 DLOG("i3-nagbar (%d) process exited with status %d\n", watcher->pid, exitcode);
343 }
344
345 pid_t *nagbar_pid = watcher->data;
346 if (*nagbar_pid == watcher->pid) {
347 /* Only reset if the watched nagbar is the active nagbar */
348 *nagbar_pid = -1;
349 }
350}
351
352/*
353 * Starts an i3-nagbar instance with the given parameters. Takes care of
354 * handling SIGCHLD and killing i3-nagbar when i3 exits.
355 *
356 * The resulting PID will be stored in *nagbar_pid and can be used with
357 * kill_nagbar() to kill the bar later on.
358 *
359 */
360void start_nagbar(pid_t *nagbar_pid, char *argv[]) {
361 if (*nagbar_pid != -1) {
362 DLOG("i3-nagbar already running (PID %d), not starting again.\n", *nagbar_pid);
363 return;
364 }
365
366 *nagbar_pid = fork();
367 if (*nagbar_pid == -1) {
368 warn("Could not fork()");
369 return;
370 }
371
372 /* child */
373 if (*nagbar_pid == 0) {
374 exec_i3_utility("i3-nagbar", argv);
375 }
376
377 DLOG("Starting i3-nagbar with PID %d\n", *nagbar_pid);
378
379 /* parent */
380 /* install a child watcher */
381 ev_child *child = smalloc(sizeof(ev_child));
382 ev_child_init(child, &nagbar_exited, *nagbar_pid, 0);
383 child->data = nagbar_pid;
384 ev_child_start(main_loop, child);
385}
386
387/*
388 * Kills the i3-nagbar process, if nagbar_pid != -1.
389 *
390 * If wait_for_it is set (restarting i3), this function will waitpid(),
391 * otherwise, ev is assumed to handle it (reloading).
392 *
393 */
394void kill_nagbar(pid_t nagbar_pid, bool wait_for_it) {
395 if (nagbar_pid == -1) {
396 return;
397 }
398
399 if (kill(nagbar_pid, SIGTERM) == -1) {
400 warn("kill(configerror_nagbar) failed");
401 }
402
403 if (!wait_for_it) {
404 return;
405 }
406
407 /* When restarting, we don’t enter the ev main loop anymore and after the
408 * exec(), our old pid is no longer watched. So, ev won’t handle SIGCHLD
409 * for us and we would end up with a <defunct> process. Therefore we
410 * waitpid() here. */
411 waitpid(nagbar_pid, NULL, 0);
412}
413
414/*
415 * Converts a string into a long using strtol().
416 * This is a convenience wrapper checking the parsing result. It returns true
417 * if the number could be parsed.
418 */
419bool parse_long(const char *str, long *out, int base) {
420 char *end = NULL;
421 long result = strtol(str, &end, base);
422 if (result == LONG_MIN || result == LONG_MAX || result < 0 || (end != NULL && *end != '\0')) {
423 *out = result;
424 return false;
425 }
426
427 *out = result;
428 return true;
429}
430
431/*
432 * Slurp reads path in its entirety into buf, returning the length of the file
433 * or -1 if the file could not be read. buf is set to a buffer of appropriate
434 * size, or NULL if -1 is returned.
435 *
436 */
437ssize_t slurp(const char *path, char **buf) {
438 FILE *f;
439 if ((f = fopen(path, "r")) == NULL) {
440 ELOG("Cannot open file \"%s\": %s\n", path, strerror(errno));
441 return -1;
442 }
443 struct stat stbuf;
444 if (fstat(fileno(f), &stbuf) != 0) {
445 ELOG("Cannot fstat() \"%s\": %s\n", path, strerror(errno));
446 fclose(f);
447 return -1;
448 }
449 /* Allocate one extra NUL byte to make the buffer usable with C string
450 * functions. yajl doesn’t need this, but this makes slurp safer. */
451 *buf = scalloc(stbuf.st_size + 1, 1);
452 size_t n = fread(*buf, 1, stbuf.st_size, f);
453 fclose(f);
454 if ((ssize_t)n != stbuf.st_size) {
455 ELOG("File \"%s\" could not be read entirely: got %zd, want %" PRIi64 "\n", path, n, (int64_t)stbuf.st_size);
456 FREE(*buf);
457 return -1;
458 }
459 return (ssize_t)n;
460}
461
462/*
463 * Convert a direction to its corresponding orientation.
464 *
465 */
467 return (direction == D_LEFT || direction == D_RIGHT) ? HORIZ : VERT;
468}
469
470/*
471 * Convert a direction to its corresponding position.
472 *
473 */
475 return (direction == D_LEFT || direction == D_UP) ? BEFORE : AFTER;
476}
477
478/*
479 * Convert orientation and position to the corresponding direction.
480 *
481 */
483 if (orientation == HORIZ) {
484 return position == BEFORE ? D_LEFT : D_RIGHT;
485 } else {
486 return position == BEFORE ? D_UP : D_DOWN;
487 }
488}
489
490/*
491 * Converts direction to a string representation.
492 *
493 */
494const char *direction_to_string(direction_t direction) {
495 switch (direction) {
496 case D_LEFT:
497 return "left";
498 case D_RIGHT:
499 return "right";
500 case D_UP:
501 return "up";
502 case D_DOWN:
503 return "down";
504 }
505 return "invalid";
506}
507
508/*
509 * Converts position to a string representation.
510 *
511 */
512const char *position_to_string(position_t position) {
513 switch (position) {
514 case BEFORE:
515 return "before";
516 case AFTER:
517 return "after";
518 }
519 return "invalid";
520}
pid_t command_error_nagbar_pid
Definition bindings.c:19
Config config
Definition config.c:19
pid_t config_error_nagbar_pid
void restore_geometry(void)
Restores the geometry of each window by reparenting it to the root window at the position of its fram...
Definition manage.c:81
struct Con * croot
Definition tree.c:12
orientation_t orientation_from_direction(direction_t direction)
Convert a direction to its corresponding orientation.
Definition util.c:466
__attribute__((pure))
Definition util.c:67
position_t position_from_direction(direction_t direction)
Convert a direction to its corresponding position.
Definition util.c:474
int ws_name_to_number(const char *name)
Parses the workspace name as a number.
Definition util.c:111
void exec_i3_utility(char *name, char *argv[])
exec()s an i3 utility, for example the config file migration script or i3-nagbar.
Definition util.c:149
#define y(x,...)
Definition util.c:216
bool rect_contains(Rect rect, uint32_t x, uint32_t y)
Definition util.c:32
static char * store_restart_layout(void)
Definition util.c:219
void start_nagbar(pid_t *nagbar_pid, char *argv[])
Starts an i3-nagbar instance with the given parameters.
Definition util.c:360
bool update_if_necessary(uint32_t *destination, const uint32_t new_value)
Updates *destination with new_value and returns true if it was changed or false if it was the same.
Definition util.c:128
void kill_nagbar(pid_t nagbar_pid, bool wait_for_it)
Kills the i3-nagbar process, if nagbar_pid != -1.
Definition util.c:394
static char ** add_argument(char **original, char *opt_char, char *opt_arg, char *opt_name)
Definition util.c:185
void i3_restart(bool forget_layout)
Restart i3 in-place appends -a to argument list to disable autostart.
Definition util.c:283
Rect rect_add(Rect a, Rect b)
Definition util.c:39
char * pango_escape_markup(char *input)
Escapes the given string if a pango font is currently used.
Definition util.c:317
bool parse_long(const char *str, long *out, int base)
Converts a string into a long using strtol().
Definition util.c:419
bool rect_equals(Rect a, Rect b)
Definition util.c:59
const char * position_to_string(position_t position)
Converts position to a string representation.
Definition util.c:512
const char * direction_to_string(direction_t direction)
Converts direction to a string representation.
Definition util.c:494
static void nagbar_exited(EV_P_ ev_child *watcher, int revents)
Definition util.c:333
ssize_t slurp(const char *path, char **buf)
Slurp reads path in its entirety into buf, returning the length of the file or -1 if the file could n...
Definition util.c:437
int min(int a, int b)
Definition util.c:24
bool layout_from_name(const char *layout_str, layout_t *out)
Set 'out' to the layout_t value for the given layout.
Definition util.c:84
direction_t direction_from_orientation_position(orientation_t orientation, position_t position)
Convert orientation and position to the corresponding direction.
Definition util.c:482
Rect rect_sub(Rect a, Rect b)
Definition util.c:46
Rect rect_sanitize_dimensions(Rect rect)
Definition util.c:53
int max(int a, int b)
Definition util.c:28
void ipc_shutdown(shutdown_reason_t reason, int exempt_fd)
Calls shutdown() on each socket and closes it.
Definition ipc.c:192
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart)
Definition ipc.c:362
bool get_debug_logging(void)
Checks if debug logging is active.
Definition log.c:213
char ** start_argv
Definition main.c:52
struct ev_loop * main_loop
Definition main.c:79
position_t
Definition data.h:63
@ AFTER
Definition data.h:64
@ BEFORE
Definition data.h:63
layout_t
Container layouts.
Definition data.h:101
@ L_STACKED
Definition data.h:103
@ L_TABBED
Definition data.h:104
@ L_SPLITH
Definition data.h:108
@ L_SPLITV
Definition data.h:107
@ L_DEFAULT
Definition data.h:102
orientation_t
Definition data.h:60
@ VERT
Definition data.h:62
@ HORIZ
Definition data.h:61
direction_t
Definition data.h:56
@ D_RIGHT
Definition data.h:57
@ D_LEFT
Definition data.h:56
@ D_UP
Definition data.h:58
@ D_DOWN
Definition data.h:59
char * resolve_tilde(const char *path)
This function resolves ~ in pathnames.
#define DLOG(fmt,...)
Definition libi3.h:105
#define DEFAULT_DIR_MODE
Definition libi3.h:26
#define LOG(fmt,...)
Definition libi3.h:95
ssize_t writeall(int fd, const void *buf, size_t count)
Wrapper around correct write which returns -1 (meaning that write failed) or count (meaning that all ...
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
#define ELOG(fmt,...)
Definition libi3.h:100
void * scalloc(size_t num, size_t size)
Safe-wrapper around calloc which exits if malloc returns NULL (meaning that there is no more memory a...
int sasprintf(char **strp, const char *fmt,...)
Safe-wrapper around asprintf which exits if it returns -1 (meaning that there is no more memory avail...
char * get_process_filename(const char *prefix)
Returns the name of a temporary file with the specified prefix.
int mkdirp(const char *path, mode_t mode)
Emulates mkdir -p (creates any missing folders)
bool font_is_pango(void)
Returns true if and only if the current font is a pango font.
void * smalloc(size_t size)
Safe-wrapper around malloc which exits if malloc returns NULL (meaning that there is no more memory a...
#define FREE(pointer)
Definition util.h:47
@ SHUTDOWN_REASON_RESTART
Definition ipc.h:94
char * restart_state_path
Stores a rectangle, for example the size of a window, the child window etc.
Definition data.h:185
uint32_t height
Definition data.h:189
uint32_t x
Definition data.h:186
uint32_t y
Definition data.h:187
uint32_t width
Definition data.h:188