]> git.sur5r.net Git - i3/i3/blob - src/util.c
Update TODO and header-comments
[i3/i3] / src / util.c
1 /*
2  * vim:ts=8:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  *
6  * © 2009 Michael Stapelberg and contributors
7  *
8  * See file LICENSE for license information.
9  *
10  * util.c: Utility functions, which can be useful everywhere.
11  *
12  */
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <sys/wait.h>
18 #include <stdarg.h>
19
20 #include "i3.h"
21 #include "data.h"
22 #include "table.h"
23 #include "layout.h"
24
25 int min(int a, int b) {
26         return (a < b ? a : b);
27 }
28
29 int max(int a, int b) {
30         return (a > b ? a : b);
31 }
32
33 /*
34  * Checks if pointer is NULL and exits the whole program, printing a message to stdout
35  * before with the given format (see printf())
36  *
37  */
38 void exit_if_null(void *pointer, char *fmt, ...) {
39         va_list args;
40
41         if (pointer != NULL)
42                 return;
43
44         va_start(args, fmt);
45         vfprintf(stderr, fmt, args);
46         va_end(args);
47
48         exit(EXIT_FAILURE);
49 }
50
51 /*
52  * Prints the message (see printf()) to stderr, then exits the program.
53  *
54  */
55 void die(char *fmt, ...) {
56         va_list args;
57
58         va_start(args, fmt);
59         vfprintf(stderr, fmt, args);
60         va_end(args);
61
62         exit(EXIT_FAILURE);
63 }
64
65 /*
66  * The s* functions (safe) are wrappers around malloc, strdup, …, which exits if one of
67  * the called functions returns NULL, meaning that there is no more memory available
68  *
69  */
70 void *smalloc(size_t size) {
71         void *result = malloc(size);
72         exit_if_null(result, "Too less memory for malloc(%d)\n", size);
73         return result;
74 }
75
76 char *sstrdup(const char *str) {
77         char *result = strdup(str);
78         exit_if_null(result, "Too less memory for strdup()\n");
79         return result;
80 }
81
82 /*
83  * Starts the given application by passing it through a shell. We use double fork
84  * to avoid zombie processes. As the started application’s parent exits (immediately),
85  * the application is reparented to init (process-id 1), which correctly handles
86  * childs, so we don’t have to do it :-).
87  *
88  * The shell is determined by looking for the SHELL environment variable. If it
89  * does not exist, /bin/sh is used.
90  *
91  */
92 void start_application(const char *command) {
93         if (fork() == 0) {
94                 /* Child process */
95                 if (fork() == 0) {
96                         /* Stores the path of the shell */
97                         static const char *shell = NULL;
98
99                         if (shell == NULL)
100                                 if ((shell = getenv("SHELL")) == NULL)
101                                         shell = "/bin/sh";
102
103                         /* This is the child */
104                         execl(shell, shell, "-c", command, NULL);
105                         /* not reached */
106                 }
107                 exit(0);
108         }
109         wait(0);
110 }
111
112 /*
113  * Checks a generic cookie for errors and quits with the given message if there
114  * was an error.
115  *
116  */
117 void check_error(xcb_connection_t *connection, xcb_void_cookie_t cookie, char *err_message) {
118         xcb_generic_error_t *error = xcb_request_check(connection, cookie);
119         if (error != NULL) {
120                 fprintf(stderr, "ERROR: %s : %d\n", err_message , error->error_code);
121                 xcb_disconnect(connection);
122                 exit(-1);
123         }
124 }
125
126 /*
127  * Sets the given client as focused by updating the data structures correctly,
128  * updating the X input focus and finally re-decorating both windows (to signalize
129  * the user the new focus situation)
130  *
131  */
132 void set_focus(xcb_connection_t *conn, Client *client) {
133         /* TODO: check if the focus needs to be changed at all */
134         /* Store current_row/current_col */
135         c_ws->current_row = current_row;
136         c_ws->current_col = current_col;
137         c_ws = client->container->workspace;
138
139         /* Update container */
140         Client *old_client = client->container->currently_focused;
141         client->container->currently_focused = client;
142
143         current_col = client->container->col;
144         current_row = client->container->row;
145
146         /* Set focus to the entered window, and flush xcb buffer immediately */
147         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, client->child, XCB_CURRENT_TIME);
148         //xcb_warp_pointer(conn, XCB_NONE, client->child, 0, 0, 0, 0, 10, 10);
149         /* Update last/current client’s titlebar */
150         if (old_client != NULL)
151                 decorate_window(conn, old_client);
152         decorate_window(conn, client);
153         xcb_flush(conn);
154 }
155
156 /*
157  * Warps the pointer into the given client (in the middle of it, to be specific), therefore
158  * selecting it
159  *
160  */
161 void warp_pointer_into(xcb_connection_t *connection, Client *client) {
162         int mid_x = client->rect.width / 2,
163             mid_y = client->rect.height / 2;
164         xcb_warp_pointer(connection, XCB_NONE, client->child, 0, 0, 0, 0, mid_x, mid_y);
165 }
166
167 /*
168  * Toggles fullscreen mode for the given client. It updates the data structures and
169  * reconfigures (= resizes/moves) the client and its frame to the full size of the
170  * screen. When leaving fullscreen, re-rendering the layout is forced.
171  *
172  */
173 void toggle_fullscreen(xcb_connection_t *conn, Client *client) {
174         Workspace *workspace = client->container->workspace;
175
176         workspace->fullscreen_client = (client->fullscreen ? NULL : client);
177
178         client->fullscreen = !client->fullscreen;
179
180         if (client->fullscreen) {
181                 printf("Entering fullscreen mode...\n");
182                 /* We just entered fullscreen mode, let’s configure the window */
183                  uint32_t mask = XCB_CONFIG_WINDOW_X |
184                                  XCB_CONFIG_WINDOW_Y |
185                                  XCB_CONFIG_WINDOW_WIDTH |
186                                  XCB_CONFIG_WINDOW_HEIGHT;
187                 uint32_t values[4] = {workspace->rect.x,
188                                       workspace->rect.y,
189                                       workspace->rect.width,
190                                       workspace->rect.height};
191
192                 printf("child itself will be at %dx%d with size %dx%d\n",
193                                 values[0], values[1], values[2], values[3]);
194
195                 /* Raise the window */
196                 xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, client->frame);
197
198                 xcb_configure_window(conn, client->frame, mask, values);
199                 xcb_configure_window(conn, client->child, mask, values);
200
201                 xcb_flush(conn);
202         } else {
203                 printf("left fullscreen\n");
204                 /* Because the coordinates of the window haven’t changed, it would not be
205                    re-configured if we don’t set the following flag */
206                 client->force_reconfigure = true;
207                 /* We left fullscreen mode, redraw the layout */
208                 render_layout(conn);
209         }
210 }