]> git.sur5r.net Git - i3/i3/blob - src/layout.c
a3b87f176ef15ad695c0cbc92b7d0f578d917b99
[i3/i3] / src / layout.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  * layout.c: Functions handling layout/drawing of window decorations
11  *
12  */
13 #define _GNU_SOURCE
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <xcb/xcb.h>
18
19 #include "font.h"
20 #include "i3.h"
21 #include "xcb.h"
22 #include "table.h"
23 #include "util.h"
24 #include "xinerama.h"
25
26 /*
27  * For resizing containers (= cells), we need to know the space which is unoccupied by "default"
28  * windows. The resized containers will be rendered relatively to this space, meaning that newly
29  * created columns/rows after a container was resized will start with their normal size.
30  *
31  */
32 Rect get_unoccupied_space(Workspace *workspace) {
33         printf("getting unoccupied space\n");
34         float default_factor_w = ((float)workspace->rect.width / (float)workspace->cols) / (float)workspace->rect.width;
35         float default_factor_h = (workspace->rect.height / workspace->rows) / workspace->rect.height;
36         Rect result = {0, 0, workspace->rect.width, workspace->rect.height};
37
38         printf("default factor is %f and %f\n", default_factor_w, default_factor_h);
39         printf("start w = %d, h = %d\n", result.width, result.height);
40         /* TODO: colspan/rowspan*/
41
42         for (int cols = 0; cols < workspace->cols; cols++)
43                 for (int rows = 0; rows < workspace->rows; rows++) {
44                         printf("oh hai. wf[%d][%d] = %f\n", cols, rows, workspace->table[cols][rows]->width_factor);
45                         if (workspace->table[cols][rows]->width_factor == 0)
46                                 result.width -= workspace->rect.width * default_factor_w;
47                         if (workspace->table[cols][rows]->height_factor == 0)
48                                 result.height -= workspace->rect.height * default_factor_h;
49                 }
50
51         /* If every container is using the default factor, we have the whole space available */
52         if (result.width == 0)
53                 result.width = workspace->rect.width;
54
55         if (result.height == 0)
56                 result.height = workspace->rect.height;
57
58         printf("unoccupied x = %d, unoccupied y = %d\n", result.width, result.height);
59
60         return result;
61 }
62
63 /*
64  * (Re-)draws window decorations for a given Client
65  *
66  */
67 void decorate_window(xcb_connection_t *conn, Client *client) {
68         uint32_t mask = 0;
69         uint32_t values[3];
70         i3Font *font = load_font(conn, pattern);
71         uint32_t background_color,
72                  text_color,
73                  border_color;
74
75         if (client->container->currently_focused == client) {
76                 background_color = get_colorpixel(conn, client->frame, "#285577");
77                 text_color = get_colorpixel(conn, client->frame, "#ffffff");
78                 border_color = get_colorpixel(conn, client->frame, "#4c7899");
79         } else {
80                 background_color = get_colorpixel(conn, client->frame, "#222222");
81                 text_color = get_colorpixel(conn, client->frame, "#888888");
82                 border_color = get_colorpixel(conn, client->frame, "#333333");
83         }
84
85         /* Our plan is the following:
86            - Draw a rect around the whole client in background_color
87            - Draw two lines in a lighter color
88            - Draw the window’s title
89
90            Note that xcb_image_text apparently adds 1xp border around the font? Can anyone confirm this?
91          */
92
93         /* Draw a green rectangle around the window */
94         mask = XCB_GC_FOREGROUND;
95         values[0] = background_color;
96         xcb_change_gc(conn, client->titlegc, mask, values);
97
98         xcb_rectangle_t rect = {0, 0, client->rect.width, client->rect.height};
99         xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &rect);
100
101         /* Draw the lines */
102         /* TODO: this needs to be more beautiful somewhen. maybe stdarg + change_gc(gc, ...) ? */
103 #define DRAW_LINE(colorpixel, x, y, to_x, to_y) { \
104                 uint32_t draw_values[1]; \
105                 draw_values[0] = colorpixel; \
106                 xcb_change_gc(conn, client->titlegc, XCB_GC_FOREGROUND, draw_values); \
107                 xcb_point_t points[] = {{x, y}, {to_x, to_y}}; \
108                 xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, client->frame, client->titlegc, 2, points); \
109         }
110
111         DRAW_LINE(border_color, 2, 0, client->rect.width, 0);
112         DRAW_LINE(border_color, 2, font->height + 3, 2 + client->rect.width, font->height + 3);
113
114         /* Draw the font */
115         mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
116
117         values[0] = text_color;
118         values[1] = background_color;
119         values[2] = font->id;
120
121         xcb_change_gc(conn, client->titlegc, mask, values);
122
123         /* TODO: utf8? */
124         char *label;
125         asprintf(&label, "(%08x) %.*s", client->frame, client->name_len, client->name);
126         xcb_void_cookie_t text_cookie = xcb_image_text_8_checked(conn, strlen(label), client->frame,
127                                         client->titlegc, 3 /* X */, font->height /* Y = baseline of font */, label);
128         check_error(conn, text_cookie, "Could not draw client's title");
129         free(label);
130 }
131
132 static void render_container(xcb_connection_t *connection, Container *container) {
133         Client *client;
134         i3Font *font = load_font(connection, pattern);
135
136         if (container->mode == MODE_DEFAULT) {
137                 int num_clients = 0;
138                 CIRCLEQ_FOREACH(client, &(container->clients), clients)
139                         num_clients++;
140                 printf("got %d clients in this default container.\n", num_clients);
141
142                 int current_client = 0;
143                 CIRCLEQ_FOREACH(client, &(container->clients), clients) {
144                         /* TODO: at the moment, every column/row is screen / num_cols. This
145                          * needs to be changed to "percentage of the screen" by
146                          * default and adjustable by the user if necessary.
147                          */
148
149                         /* Check if we changed client->x or client->y by updating it…
150                          * Note the bitwise OR instead of logical OR to force evaluation of both statements */
151                         if (client->force_reconfigure |
152                             (client->rect.x != (client->rect.x = container->x)) |
153                             (client->rect.y != (client->rect.y = container->y +
154                                           (container->height / num_clients) * current_client))) {
155                                 printf("frame needs to be pushed to %dx%d\n", client->rect.x, client->rect.y);
156                                 /* Note: We can use a pointer to client->x like an array of uint32_ts
157                                    because it is followed by client->y by definition */
158                                 xcb_configure_window(connection, client->frame,
159                                                 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &(client->rect.x));
160                         }
161
162                         /* TODO: vertical default layout */
163                         if (client->force_reconfigure |
164                             (client->rect.width != (client->rect.width = container->width)) |
165                             (client->rect.height != (client->rect.height = container->height / num_clients))) {
166                                 printf("resizing client to %d x %d\n", client->rect.width, client->rect.height);
167                                 xcb_configure_window(connection, client->frame,
168                                                 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
169                                                 &(client->rect.width));
170
171                                 /* Adjust the position of the child inside its frame.
172                                  * The coordinates of the child are relative to its frame, we
173                                  * add a border of 2 pixel to each value */
174                                 uint32_t mask = XCB_CONFIG_WINDOW_X |
175                                                 XCB_CONFIG_WINDOW_Y |
176                                                 XCB_CONFIG_WINDOW_WIDTH |
177                                                 XCB_CONFIG_WINDOW_HEIGHT;
178                                 uint32_t values[4] = {2,                                                   /* x */
179                                                       font->height + 2 + 2,                                /* y */
180                                                       client->rect.width - (2 + 2),                        /* width */
181                                                       client->rect.height - ((font->height + 2 + 2) + 2)}; /* height */
182
183                                 printf("child will be at %dx%d with size %dx%d\n",
184                                                 values[0], values[1], values[2], values[3]);
185
186                                 xcb_configure_window(connection, client->child, mask, values);
187                         }
188
189                         if (client->force_reconfigure)
190                                 client->force_reconfigure = false;
191
192                         current_client++;
193                 }
194         } else {
195                 /* TODO: Implement stacking */
196         }
197 }
198
199 void render_layout(xcb_connection_t *connection) {
200         i3Screen *screen;
201
202         TAILQ_FOREACH(screen, &virtual_screens, screens) {
203                 /* r_ws (rendering workspace) is just a shortcut to the Workspace being currently rendered */
204                 Workspace *r_ws = &(workspaces[screen->current_workspace]);
205
206                 printf("Rendering screen %d\n", screen->num);
207                 if (r_ws->fullscreen_client != NULL)
208                         /* This is easy: A client has entered fullscreen mode, so we don’t render at all */
209                         continue;
210                 int width = r_ws->rect.width;
211                 int height = r_ws->rect.height;
212                 int x = r_ws->rect.x;
213                 int y = r_ws->rect.y;
214
215                 printf("got %d rows and %d cols\n", r_ws->rows, r_ws->cols);
216                 printf("each of them therefore is %d px width and %d px height\n",
217                                 width / r_ws->cols, height / r_ws->rows);
218
219                 Rect space = get_unoccupied_space(r_ws);
220                 printf("got %d / %d unoc space\n", space.width, space.height);
221
222                 /* Go through the whole table and render what’s necessary */
223                 for (int cols = 0; cols < r_ws->cols; cols++)
224                         for (int rows = 0; rows < r_ws->rows; rows++) {
225                                 Container *container = r_ws->table[cols][rows];
226                                 printf("container has %d colspan, %d rowspan\n",
227                                                 container->colspan, container->rowspan);
228                                 printf("container at %d, %d\n", x, y);
229                                 /* Update position of the container */
230                                 container->row = rows;
231                                 container->col = cols;
232                                 container->x = x;
233                                 container->y = y;
234                                 if (container->width_factor == 0)
235                                         container->width = (width / r_ws->cols) * container->colspan;
236                                 else container->width = space.width * container->width_factor;
237                                 container->height = (height / r_ws->rows) * container->rowspan;
238
239                                 /* Render it */
240                                 render_container(connection, container);
241
242                                 x += container->width;
243                         }
244         }
245
246         xcb_flush(connection);
247 }