]> git.sur5r.net Git - i3/i3/blob - src/layout.c
Various code improvements
[i3/i3] / src / layout.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <xcb/xcb.h>
6
7 #include "font.h"
8 #include "i3.h"
9 #include "xcb.h"
10 #include "table.h"
11 #include "util.h"
12
13 /* All functions handling layout/drawing of window decorations */
14
15 /*
16  * (Re-)draws window decorations for a given Client
17  *
18  */
19 void decorate_window(xcb_connection_t *conn, Client *client) {
20         uint32_t mask = 0;
21         uint32_t values[3];
22         i3Font *font = load_font(conn, pattern);
23         uint32_t background_color,
24                  text_color,
25                  border_color;
26
27         if (client->container->currently_focused == client) {
28                 background_color = get_colorpixel(conn, client->frame, "#285577");
29                 text_color = get_colorpixel(conn, client->frame, "#ffffff");
30                 border_color = get_colorpixel(conn, client->frame, "#4c7899");
31         } else {
32                 background_color = get_colorpixel(conn, client->frame, "#222222");
33                 text_color = get_colorpixel(conn, client->frame, "#888888");
34                 border_color = get_colorpixel(conn, client->frame, "#333333");
35         }
36
37         /* Our plan is the following:
38            - Draw a rect around the whole client in background_color
39            - Draw two lines in a lighter color
40            - Draw the window’s title
41
42            Note that xcb_image_text apparently adds 1xp border around the font? Can anyone confirm this?
43          */
44
45         /* Draw a green rectangle around the window */
46         mask = XCB_GC_FOREGROUND;
47         values[0] = background_color;
48         xcb_change_gc(conn, client->titlegc, mask, values);
49
50         xcb_rectangle_t rect = {0, 0, client->width, client->height};
51         xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &rect);
52
53         /* Draw the lines */
54         /* TODO: this needs to be more beautiful somewhen. maybe stdarg + change_gc(gc, ...) ? */
55 #define DRAW_LINE(colorpixel, x, y, to_x, to_y) { \
56                 uint32_t draw_values[1]; \
57                 draw_values[0] = colorpixel; \
58                 xcb_change_gc(conn, client->titlegc, XCB_GC_FOREGROUND, draw_values); \
59                 xcb_point_t points[] = {{x, y}, {to_x, to_y}}; \
60                 xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, client->frame, client->titlegc, 2, points); \
61         }
62
63         DRAW_LINE(border_color, 2, 0, client->width, 0);
64         DRAW_LINE(border_color, 2, font->height + 3, 2 + client->width, font->height + 3);
65
66         /* Draw the font */
67         mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
68
69         values[0] = text_color;
70         values[1] = background_color;
71         values[2] = font->id;
72
73         xcb_change_gc(conn, client->titlegc, mask, values);
74
75         /* TODO: utf8? */
76         char *label;
77         asprintf(&label, "(%08x) %.*s", client->frame, client->name_len, client->name);
78         xcb_void_cookie_t text_cookie = xcb_image_text_8_checked(conn, strlen(label), client->frame,
79                                         client->titlegc, 3 /* X */, font->height /* Y = baseline of font */, label);
80         check_error(conn, text_cookie, "Could not draw client's title");
81         free(label);
82 }
83
84 static void render_container(xcb_connection_t *connection, Container *container) {
85         Client *client;
86         i3Font *font = load_font(connection, pattern);
87
88         if (container->mode == MODE_DEFAULT) {
89                 int num_clients = 0;
90                 CIRCLEQ_FOREACH(client, &(container->clients), clients)
91                         num_clients++;
92                 printf("got %d clients in this default container.\n", num_clients);
93
94                 int current_client = 0;
95                 CIRCLEQ_FOREACH(client, &(container->clients), clients) {
96                         /* TODO: at the moment, every column/row is screen / num_cols. This
97                          * needs to be changed to "percentage of the screen" by
98                          * default and adjustable by the user if necessary.
99                          */
100
101                         /* Check if we changed client->x or client->y by updating it…
102                          * Note the bitwise OR instead of logical OR to force evaluation of both statements */
103                         if ((client->x != (client->x = container->x + (container->col * container->width))) |
104                             (client->y != (client->y = container->y + (container->row * container->height +
105                                           (container->height / num_clients) * current_client)))) {
106                                 printf("frame needs to be pushed to %dx%d\n", client->x, client->y);
107                                 /* Note: We can use a pointer to client->x like an array of uint32_ts
108                                    because it is followed by client->y by definition */
109                                 xcb_configure_window(connection, client->frame,
110                                                 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &(client->x));
111                         }
112
113                         /* TODO: vertical default layout */
114                         if ((client->width != (client->width = container->width)) |
115                             (client->height != (client->height = container->height / num_clients))) {
116                                 xcb_configure_window(connection, client->frame,
117                                                 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
118                                                 &(client->width));
119
120                                 /* Adjust the position of the child inside its frame.
121                                  * The coordinates of the child are relative to its frame, we
122                                  * add a border of 2 pixel to each value */
123                                 uint32_t mask = XCB_CONFIG_WINDOW_X |
124                                                 XCB_CONFIG_WINDOW_Y |
125                                                 XCB_CONFIG_WINDOW_WIDTH |
126                                                 XCB_CONFIG_WINDOW_HEIGHT;
127                                 uint32_t values[4] = {2,                                              /* x */
128                                                       font->height + 2 + 2,                           /* y */
129                                                       client->width - (2 + 2),                        /* width */
130                                                       client->height - ((font->height + 2 + 2) + 2)}; /* height */
131
132                                 printf("child itself will be at %dx%d with size %dx%d\n",
133                                                 values[0], values[1], values[2], values[3]);
134
135                                 xcb_configure_window(connection, client->child, mask, values);
136                         }
137
138                         decorate_window(connection, client);
139                         current_client++;
140                 }
141         } else {
142                 /* TODO: Implement stacking */
143         }
144 }
145
146 void render_layout(xcb_connection_t *conn) {
147         int cols, rows;
148         int screen;
149         for (screen = 0; screen < num_screens; screen++) {
150                 printf("Rendering screen %d\n", screen);
151                 /* TODO: get the workspace which is active on the screen */
152                 int width = workspaces[screen].width;
153                 int height = workspaces[screen].height;
154
155                 printf("got %d rows and %d cols\n", c_ws->rows, c_ws->cols);
156                 printf("each of them therefore is %d px width and %d px height\n",
157                                 width / c_ws->cols, height / c_ws->rows);
158
159                 /* Go through the whole table and render what’s necessary */
160                 for (cols = 0; cols < c_ws->cols; cols++)
161                         for (rows = 0; rows < c_ws->rows; rows++) {
162                                 Container *con = CUR_TABLE[cols][rows];
163                                 printf("container has %d colspan, %d rowspan\n",
164                                                 con->colspan, con->rowspan);
165                                 /* Update position of the container */
166                                 con->row = rows;
167                                 con->col = cols;
168                                 con->x = workspaces[screen].x;
169                                 con->y = workspaces[screen].y;
170                                 con->width = (width / c_ws->cols) * con->colspan;
171                                 con->height = (height / c_ws->rows) * con->rowspan;
172
173                                 /* Render it */
174                                 render_container(conn, CUR_TABLE[cols][rows]);
175                         }
176         }
177
178         xcb_flush(conn);
179 }