00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include <stdio.h>
00014 #include <string.h>
00015 #include <stdlib.h>
00016 #include <xcb/xcb.h>
00017 #include <assert.h>
00018 #include <math.h>
00019
00020 #include "config.h"
00021 #include "i3.h"
00022 #include "xcb.h"
00023 #include "table.h"
00024 #include "util.h"
00025 #include "randr.h"
00026 #include "layout.h"
00027 #include "client.h"
00028 #include "floating.h"
00029 #include "handlers.h"
00030 #include "workspace.h"
00031 #include "log.h"
00032 #include "container.h"
00033
00034
00035
00036
00037
00038
00039
00040 int get_unoccupied_x(Workspace *workspace) {
00041 double unoccupied = workspace->rect.width;
00042 double default_factor = 1.0 / workspace->cols;
00043
00044 DLOG("get_unoccupied_x(), starting with %f, default_factor = %f\n", unoccupied, default_factor);
00045
00046 for (int cols = 0; cols < workspace->cols; cols++) {
00047 DLOG("width_factor[%d] = %f, unoccupied = %f\n", cols, workspace->width_factor[cols], unoccupied);
00048 if (workspace->width_factor[cols] == 0)
00049 unoccupied -= workspace->rect.width * default_factor;
00050 }
00051
00052 DLOG("unoccupied space: %f\n", unoccupied);
00053 return unoccupied;
00054 }
00055
00056
00057 int get_unoccupied_y(Workspace *workspace) {
00058 int height = workspace_height(workspace);
00059 double unoccupied = height;
00060 double default_factor = 1.0 / workspace->rows;
00061
00062 DLOG("get_unoccupied_y(), starting with %f, default_factor = %f\n", unoccupied, default_factor);
00063
00064 for (int rows = 0; rows < workspace->rows; rows++) {
00065 DLOG("height_factor[%d] = %f, unoccupied = %f\n", rows, workspace->height_factor[rows], unoccupied);
00066 if (workspace->height_factor[rows] == 0)
00067 unoccupied -= height * default_factor;
00068 }
00069
00070 DLOG("unoccupied space: %f\n", unoccupied);
00071 return unoccupied;
00072 }
00073
00074
00075
00076
00077
00078
00079
00080 void redecorate_window(xcb_connection_t *conn, Client *client) {
00081 if (client->container != NULL &&
00082 (client->container->mode == MODE_STACK ||
00083 client->container->mode == MODE_TABBED)) {
00084 render_container(conn, client->container);
00085
00086
00087 xcb_clear_area(conn, true, client->frame, 0, 0, client->rect.width, client->rect.height);
00088 } else decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
00089 xcb_flush(conn);
00090 }
00091
00092
00093
00094
00095
00096
00097 void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t drawable,
00098 xcb_gcontext_t gc, int offset_x, int offset_y) {
00099 i3Font *font = load_font(conn, config.font);
00100 int decoration_height = font->height + 2 + 2;
00101 struct Colortriple *color;
00102 Client *last_focused;
00103
00104
00105 if (client->dock)
00106 return;
00107
00108 last_focused = SLIST_FIRST(&(client->workspace->focus_stack));
00109
00110 if (client->urgent)
00111 color = &(config.client.urgent);
00112 else {
00113 if (client_is_floating(client)) {
00114 if (last_focused == client)
00115 color = &(config.client.focused);
00116 else color = &(config.client.unfocused);
00117 } else {
00118 if (client->container->currently_focused == client) {
00119
00120 if (last_focused == client && c_ws == client->workspace)
00121 color = &(config.client.focused);
00122
00123 else color = &(config.client.focused_inactive);
00124 } else color = &(config.client.unfocused);
00125 }
00126 }
00127
00128
00129
00130
00131
00132
00133 int mode = container_mode(client->container, true);
00134
00135
00136 if (client->borderless && mode == MODE_DEFAULT)
00137 xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, config.client.background);
00138 else xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, color->background);
00139
00140
00141 if (mode == MODE_STACK || mode == MODE_TABBED) {
00142
00143
00144
00145 xcb_rectangle_t rect = {offset_x, offset_y,
00146 offset_x + client->container->width,
00147 offset_y + decoration_height };
00148 xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect);
00149 } else {
00150 xcb_rectangle_t rect = {0, 0, client->rect.width, client->rect.height};
00151 xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect);
00152
00153
00154
00155 xcb_change_gc_single(conn, client->titlegc, XCB_GC_FOREGROUND, config.client.background);
00156 if (client->titlebar_position == TITLEBAR_OFF && client->borderless) {
00157 xcb_rectangle_t crect = {0, 0, client->rect.width, client->rect.height};
00158 xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
00159 } else if (client->titlebar_position == TITLEBAR_OFF && !client->borderless) {
00160 xcb_rectangle_t crect = {1, 1, client->rect.width - (1 + 1), client->rect.height - (1 + 1)};
00161 xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
00162 } else {
00163 xcb_rectangle_t crect = {2, decoration_height,
00164 client->rect.width - (2 + 2), client->rect.height - 2 - decoration_height};
00165 xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
00166 }
00167 }
00168
00169 mode = container_mode(client->container, false);
00170
00171 if (client->titlebar_position != TITLEBAR_OFF) {
00172
00173 xcb_draw_line(conn, drawable, gc, color->border, offset_x, offset_y, offset_x + client->rect.width, offset_y);
00174 xcb_draw_line(conn, drawable, gc, color->border,
00175 offset_x + 2,
00176 offset_y + font->height + 3,
00177 offset_x + client->rect.width - 3,
00178 offset_y + font->height + 3 );
00179 }
00180
00181
00182 if (client->name != NULL &&
00183 (mode != MODE_DEFAULT || client->titlebar_position != TITLEBAR_OFF)) {
00184
00185 uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
00186 uint32_t values[] = { color->text, color->background, font->id };
00187 xcb_change_gc(conn, gc, mask, values);
00188
00189
00190
00191
00192 if (client->name_len == -1)
00193 xcb_image_text_8(conn, strlen(client->name), drawable, gc, offset_x + 3 ,
00194 offset_y + font->height , client->name);
00195 else
00196 xcb_image_text_16(conn, client->name_len, drawable, gc, offset_x + 3 ,
00197 offset_y + font->height , (xcb_char2b_t*)client->name);
00198 }
00199 }
00200
00201
00202
00203
00204
00205 void reposition_client(xcb_connection_t *conn, Client *client) {
00206 Output *output;
00207
00208 DLOG("frame 0x%08x needs to be pushed to %dx%d\n", client->frame, client->rect.x, client->rect.y);
00209
00210
00211 xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &(client->rect.x));
00212
00213 if (!client_is_floating(client))
00214 return;
00215
00216
00217 output = get_output_containing(client->rect.x + (client->rect.width / 2),
00218 client->rect.y + (client->rect.height / 2));
00219 if (client->workspace->output == output)
00220 return;
00221
00222 if (output == NULL) {
00223 DLOG("Boundary checking disabled, no output found for (%d, %d)\n", client->rect.x, client->rect.y);
00224 return;
00225 }
00226
00227 if (output->current_workspace == NULL) {
00228 DLOG("Boundary checking deferred, no current workspace on output\n");
00229 client->force_reconfigure = true;
00230 return;
00231 }
00232
00233 DLOG("Client is on workspace %p with output %p\n", client->workspace, client->workspace->output);
00234 DLOG("but output at %d, %d is %p\n", client->rect.x, client->rect.y, output);
00235 floating_assign_to_workspace(client, output->current_workspace);
00236
00237 set_focus(conn, client, true);
00238 }
00239
00240
00241
00242
00243
00244
00245
00246
00247 void resize_client(xcb_connection_t *conn, Client *client) {
00248 i3Font *font = load_font(conn, config.font);
00249
00250 DLOG("frame 0x%08x needs to be pushed to %dx%d\n", client->frame, client->rect.x, client->rect.y);
00251 DLOG("resizing client 0x%08x to %d x %d\n", client->frame, client->rect.width, client->rect.height);
00252 xcb_set_window_rect(conn, client->frame, client->rect);
00253
00254
00255
00256
00257 Rect *rect = &(client->child_rect);
00258 switch (container_mode(client->container, true)) {
00259 case MODE_STACK:
00260 case MODE_TABBED:
00261 rect->x = 2;
00262 rect->y = 0;
00263 rect->width = client->rect.width - (2 + 2);
00264 rect->height = client->rect.height - 2;
00265 break;
00266 default:
00267 if (client->titlebar_position == TITLEBAR_OFF && client->borderless) {
00268 rect->x = 0;
00269 rect->y = 0;
00270 rect->width = client->rect.width;
00271 rect->height = client->rect.height;
00272 } else if (client->titlebar_position == TITLEBAR_OFF && !client->borderless) {
00273 rect->x = 1;
00274 rect->y = 1;
00275 rect->width = client->rect.width - 1 - 1;
00276 rect->height = client->rect.height - 1 - 1;
00277 } else {
00278 rect->x = 2;
00279 rect->y = font->height + 2 + 2;
00280 rect->width = client->rect.width - (2 + 2);
00281 rect->height = client->rect.height - ((font->height + 2 + 2) + 2);
00282 }
00283 break;
00284 }
00285
00286 rect->width -= (2 * client->border_width);
00287 rect->height -= (2 * client->border_width);
00288
00289
00290 if (client->proportional_height != 0 &&
00291 client->proportional_width != 0) {
00292 DLOG("proportional height = %d, width = %d\n", client->proportional_height, client->proportional_width);
00293 double new_height = rect->height + 1;
00294 int new_width = rect->width;
00295
00296 while (new_height > rect->height) {
00297 new_height = ((double)client->proportional_height / client->proportional_width) * new_width;
00298
00299 if (new_height > rect->height)
00300 new_width--;
00301 }
00302
00303 rect->y += ceil(rect->height / 2) - floor(new_height / 2);
00304 rect->x += ceil(rect->width / 2) - floor(new_width / 2);
00305
00306 rect->height = new_height;
00307 rect->width = new_width;
00308 DLOG("new_height = %f, new_width = %d\n", new_height, new_width);
00309 }
00310
00311 if (client->height_increment > 1) {
00312 int old_height = rect->height;
00313 rect->height -= (rect->height - client->base_height) % client->height_increment;
00314 DLOG("Lost %d pixel due to client's height_increment (%d px, base_height = %d)\n",
00315 old_height - rect->height, client->height_increment, client->base_height);
00316 }
00317
00318 if (client->width_increment > 1) {
00319 int old_width = rect->width;
00320 rect->width -= (rect->width - client->base_width) % client->width_increment;
00321 DLOG("Lost %d pixel due to client's width_increment (%d px, base_width = %d)\n",
00322 old_width - rect->width, client->width_increment, client->base_width);
00323 }
00324
00325 DLOG("child will be at %dx%d with size %dx%d\n", rect->x, rect->y, rect->width, rect->height);
00326
00327 xcb_set_window_rect(conn, client->child, *rect);
00328
00329
00330
00331
00332 fake_absolute_configure_notify(conn, client);
00333
00334
00335
00336 xcb_expose_event_t generated;
00337 generated.window = client->frame;
00338 generated.count = 0;
00339 handle_expose_event(NULL, conn, &generated);
00340 }
00341
00342
00343
00344
00345
00346
00347 void render_container(xcb_connection_t *conn, Container *container) {
00348 Client *client;
00349 int num_clients = 0, current_client = 0;
00350
00351 CIRCLEQ_FOREACH(client, &(container->clients), clients)
00352 num_clients++;
00353
00354 if (container->mode == MODE_DEFAULT) {
00355 int height = (container->height / max(1, num_clients));
00356 int rest_pixels = (container->height % max(1, num_clients));
00357 DLOG("height per client = %d, rest = %d\n", height, rest_pixels);
00358
00359 CIRCLEQ_FOREACH(client, &(container->clients), clients) {
00360
00361 if (container->workspace->fullscreen_client == client) {
00362 current_client++;
00363 continue;
00364 }
00365
00366
00367
00368 int this_height = height;
00369 if (rest_pixels > 0) {
00370 height++;
00371 rest_pixels--;
00372 }
00373
00374
00375 if (client->force_reconfigure |
00376 update_if_necessary(&(client->rect.x), container->x) |
00377 update_if_necessary(&(client->rect.y), container->y +
00378 (container->height / num_clients) * current_client) |
00379 update_if_necessary(&(client->rect.width), container->width) |
00380 update_if_necessary(&(client->rect.height), this_height))
00381 resize_client(conn, client);
00382
00383
00384
00385 client->force_reconfigure = false;
00386
00387 current_client++;
00388 }
00389 } else {
00390 i3Font *font = load_font(conn, config.font);
00391 int decoration_height = (font->height + 2 + 2);
00392 struct Stack_Window *stack_win = &(container->stack_win);
00393
00394
00395 int size_each = (num_clients == 0 ? container->width : container->width / num_clients);
00396 int stack_lines = num_clients;
00397
00398
00399
00400 if (stack_win->rect.height == 0 && num_clients > 1) {
00401 DLOG("remapping stack win\n");
00402 xcb_map_window(conn, stack_win->window);
00403 } else DLOG("not remapping stackwin, height = %d, num_clients = %d\n",
00404 stack_win->rect.height, num_clients);
00405
00406 if (container->mode == MODE_TABBED) {
00407
00408
00409 DLOG("tabbed mode, setting num_clients = 1\n");
00410 if (stack_lines > 1)
00411 stack_lines = 1;
00412 }
00413
00414 if (container->stack_limit == STACK_LIMIT_COLS) {
00415 stack_lines = ceil((float)num_clients / container->stack_limit_value);
00416 } else if (container->stack_limit == STACK_LIMIT_ROWS) {
00417 stack_lines = min(num_clients, container->stack_limit_value);
00418 }
00419
00420 int height = decoration_height * stack_lines;
00421 if (num_clients == 1) {
00422 height = 0;
00423 stack_win->rect.height = 0;
00424 xcb_unmap_window(conn, stack_win->window);
00425
00426 DLOG("Just one client, setting height to %d\n", height);
00427 }
00428
00429
00430 if (height > 0 && (
00431 update_if_necessary(&(stack_win->rect.x), container->x) |
00432 update_if_necessary(&(stack_win->rect.y), container->y) |
00433 update_if_necessary(&(stack_win->rect.width), container->width) |
00434 update_if_necessary(&(stack_win->rect.height), height))) {
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446 uint32_t values[] = { stack_win->rect.x, stack_win->rect.y,
00447 stack_win->rect.width, stack_win->rect.height,
00448 XCB_STACK_MODE_ABOVE, XCB_STACK_MODE_BELOW };
00449 uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
00450 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
00451 XCB_CONFIG_WINDOW_STACK_MODE;
00452
00453
00454
00455 Client *first_floating = TAILQ_FIRST(&(container->workspace->floating_clients));
00456 if (container->workspace->fullscreen_client != NULL) {
00457 mask |= XCB_CONFIG_WINDOW_SIBLING;
00458 values[4] = container->workspace->fullscreen_client->frame;
00459 } else if (first_floating != TAILQ_END(&(container->workspace->floating_clients))) {
00460 mask |= XCB_CONFIG_WINDOW_SIBLING;
00461 values[4] = first_floating->frame;
00462 }
00463
00464 xcb_configure_window(conn, stack_win->window, mask, values);
00465 }
00466
00467
00468 if (num_clients > 1)
00469 cached_pixmap_prepare(conn, &(stack_win->pixmap));
00470
00471 int current_row = 0, current_col = 0;
00472 int wrap = 0;
00473
00474 if (container->stack_limit == STACK_LIMIT_COLS) {
00475
00476
00477 wrap = ceil((float)num_clients / container->stack_limit_value);
00478 } else if (container->stack_limit == STACK_LIMIT_ROWS) {
00479
00480
00481
00482
00483
00484 wrap = (stack_win->rect.width / ceil((float)num_clients / container->stack_limit_value));
00485 }
00486
00487
00488 CIRCLEQ_FOREACH(client, &(container->clients), clients) {
00489
00490 if (container->workspace->fullscreen_client == client) {
00491 current_client++;
00492 continue;
00493 }
00494
00495
00496
00497 if (client->force_reconfigure |
00498 update_if_necessary(&(client->rect.x), container->x) |
00499 update_if_necessary(&(client->rect.y), container->y + height) |
00500 update_if_necessary(&(client->rect.width), container->width) |
00501 update_if_necessary(&(client->rect.height), container->height - height))
00502 resize_client(conn, client);
00503
00504 client->force_reconfigure = false;
00505
00506 int offset_x = 0;
00507 int offset_y = 0;
00508 if (container->mode == MODE_STACK ||
00509 (container->mode == MODE_TABBED &&
00510 container->stack_limit == STACK_LIMIT_COLS)) {
00511 if (container->stack_limit == STACK_LIMIT_COLS) {
00512 offset_x = current_col * (stack_win->rect.width / container->stack_limit_value);
00513 offset_y = current_row * decoration_height;
00514 current_row++;
00515 if ((current_row % wrap) == 0) {
00516 current_col++;
00517 current_row = 0;
00518 }
00519 } else if (container->stack_limit == STACK_LIMIT_ROWS) {
00520 offset_x = current_col * wrap;
00521 offset_y = current_row * decoration_height;
00522 current_row++;
00523 if ((current_row % container->stack_limit_value) == 0) {
00524 current_col++;
00525 current_row = 0;
00526 }
00527 } else {
00528 offset_y = current_client * decoration_height;
00529 }
00530 current_client++;
00531 } else if (container->mode == MODE_TABBED) {
00532 if (container->stack_limit == STACK_LIMIT_ROWS) {
00533 LOG("You limited a tabbed container in its rows. "
00534 "This makes no sense in tabbing mode.\n");
00535 }
00536 offset_x = current_client++ * size_each;
00537 }
00538 if (stack_win->pixmap.id != XCB_NONE)
00539 decorate_window(conn, client, stack_win->pixmap.id,
00540 stack_win->pixmap.gc, offset_x, offset_y);
00541 else
00542 decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
00543 }
00544
00545
00546
00547 if (container->mode == MODE_STACK) {
00548 if (container->stack_limit == STACK_LIMIT_COLS && (current_col % 2) != 0) {
00549 xcb_change_gc_single(conn, stack_win->pixmap.gc, XCB_GC_FOREGROUND, config.client.background);
00550
00551 int offset_x = current_col * (stack_win->rect.width / container->stack_limit_value);
00552 int offset_y = current_row * decoration_height;
00553 xcb_rectangle_t rect = {offset_x, offset_y,
00554 offset_x + container->width,
00555 offset_y + decoration_height };
00556 xcb_poly_fill_rectangle(conn, stack_win->pixmap.id, stack_win->pixmap.gc, 1, &rect);
00557 } else if (container->stack_limit == STACK_LIMIT_ROWS && (current_row % 2) != 0) {
00558 xcb_change_gc_single(conn, stack_win->pixmap.gc, XCB_GC_FOREGROUND, config.client.background);
00559
00560 int offset_x = current_col * wrap;
00561 int offset_y = current_row * decoration_height;
00562 xcb_rectangle_t rect = {offset_x, offset_y,
00563 offset_x + container->width,
00564 offset_y + decoration_height };
00565 xcb_poly_fill_rectangle(conn, stack_win->pixmap.id, stack_win->pixmap.gc, 1, &rect);
00566 }
00567 }
00568
00569 if (stack_win->pixmap.id == XCB_NONE)
00570 return;
00571 xcb_copy_area(conn, stack_win->pixmap.id, stack_win->window, stack_win->pixmap.gc,
00572 0, 0, 0, 0, stack_win->rect.width, stack_win->rect.height);
00573 }
00574 }
00575
00576 static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int *height) {
00577 Client *client;
00578 SLIST_FOREACH(client, &(r_ws->output->dock_clients), dock_clients) {
00579 DLOG("client is at %d, should be at %d\n", client->rect.y, *height);
00580 if (client->force_reconfigure |
00581 update_if_necessary(&(client->rect.x), r_ws->rect.x) |
00582 update_if_necessary(&(client->rect.y), *height))
00583 reposition_client(conn, client);
00584
00585 if (client->force_reconfigure |
00586 update_if_necessary(&(client->rect.width), width) |
00587 update_if_necessary(&(client->rect.height), client->desired_height))
00588 resize_client(conn, client);
00589
00590 client->force_reconfigure = false;
00591 DLOG("desired_height = %d\n", client->desired_height);
00592 *height += client->desired_height;
00593 }
00594 }
00595
00596 static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int width, int height) {
00597 i3Font *font = load_font(conn, config.font);
00598 Output *output = r_ws->output;
00599 enum { SET_NORMAL = 0, SET_FOCUSED = 1 };
00600
00601
00602 xcb_change_gc_single(conn, output->bargc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
00603 xcb_rectangle_t rect = {0, 0, width, height};
00604 xcb_poly_fill_rectangle(conn, output->bar, output->bargc, 1, &rect);
00605
00606
00607 xcb_change_gc_single(conn, output->bargc, XCB_GC_FONT, font->id);
00608
00609 int drawn = 0;
00610 Workspace *ws;
00611 TAILQ_FOREACH(ws, workspaces, workspaces) {
00612 if (ws->output != output)
00613 continue;
00614
00615 struct Colortriple *color;
00616
00617 if (output->current_workspace == ws)
00618 color = &(config.bar.focused);
00619 else if (ws->urgent)
00620 color = &(config.bar.urgent);
00621 else color = &(config.bar.unfocused);
00622
00623
00624 xcb_draw_rect(conn, output->bar, output->bargc, color->border,
00625 drawn,
00626 1,
00627 ws->text_width + 5 + 5,
00628 height - 2 );
00629
00630
00631 xcb_draw_rect(conn, output->bar, output->bargc, color->background,
00632 drawn + 1,
00633 2,
00634 ws->text_width + 4 + 4,
00635 height - 4);
00636
00637 xcb_change_gc_single(conn, output->bargc, XCB_GC_FOREGROUND, color->text);
00638 xcb_change_gc_single(conn, output->bargc, XCB_GC_BACKGROUND, color->background);
00639 xcb_image_text_16(conn, ws->name_len, output->bar, output->bargc, drawn + 5 ,
00640 font->height + 1 ,
00641 (xcb_char2b_t*)ws->name);
00642 drawn += ws->text_width + 12;
00643 }
00644 }
00645
00646
00647
00648
00649
00650
00651
00652 void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bool ignore_enter_notify) {
00653 Client *client;
00654 uint32_t values[1];
00655
00656 FOR_TABLE(workspace) {
00657 if (workspace->table[cols][rows] == NULL)
00658 continue;
00659
00660 CIRCLEQ_FOREACH(client, &(workspace->table[cols][rows]->clients), clients) {
00661
00662 values[0] = FRAME_EVENT_MASK;
00663 if (ignore_enter_notify)
00664 values[0] &= ~(XCB_EVENT_MASK_ENTER_WINDOW);
00665 xcb_change_window_attributes(conn, client->frame, XCB_CW_EVENT_MASK, values);
00666
00667
00668 values[0] = CHILD_EVENT_MASK;
00669 if (ignore_enter_notify)
00670 values[0] &= ~(XCB_EVENT_MASK_ENTER_WINDOW);
00671 xcb_change_window_attributes(conn, client->child, XCB_CW_EVENT_MASK, values);
00672 }
00673 }
00674 }
00675
00676
00677
00678
00679
00680 void render_workspace(xcb_connection_t *conn, Output *output, Workspace *r_ws) {
00681 i3Font *font = load_font(conn, config.font);
00682 int width = r_ws->rect.width;
00683 int height = r_ws->rect.height;
00684
00685
00686 Client *client;
00687 SLIST_FOREACH(client, &(output->dock_clients), dock_clients)
00688 height -= client->desired_height;
00689
00690
00691 if (!config.disable_workspace_bar)
00692 height -= (font->height + 6);
00693
00694 int xoffset[r_ws->rows];
00695 int yoffset[r_ws->cols];
00696
00697 for (int cols = 0; cols < r_ws->cols; cols++)
00698 yoffset[cols] = r_ws->rect.y;
00699 for (int rows = 0; rows < r_ws->rows; rows++)
00700 xoffset[rows] = r_ws->rect.x;
00701
00702 ignore_enter_notify_forall(conn, r_ws, true);
00703
00704
00705 int col_width[r_ws->cols];
00706 int unoccupied_x = get_unoccupied_x(r_ws);
00707 int default_col_width = unoccupied_x / r_ws->cols;
00708 int total_col_width = 0;
00709 for (int i = 0; i < r_ws->cols; ++i) {
00710 col_width[i] = r_ws->width_factor[i] == 0 ? default_col_width : unoccupied_x * r_ws->width_factor[i];
00711 total_col_width += col_width[i];
00712 }
00713
00714
00715 int error = r_ws->rect.width - total_col_width, error_index = r_ws->cols - 1;
00716 while (error) {
00717 ++col_width[error_index];
00718 --error;
00719 error_index = error_index == 0 ? r_ws->cols - 1 : error_index - 1;
00720 }
00721
00722
00723 int row_height[r_ws->rows];
00724 int unoccupied_y = get_unoccupied_y(r_ws);
00725 int default_row_height = unoccupied_y / r_ws->rows;
00726 int total_row_height = 0;
00727 for (int i = 0; i < r_ws->rows; ++i) {
00728 row_height[i] = r_ws->height_factor[i] == 0 ? default_row_height : unoccupied_y * r_ws->height_factor[i];
00729 total_row_height += row_height[i];
00730 }
00731
00732
00733 error = workspace_height(r_ws) - total_row_height;
00734 error_index = r_ws->rows - 1;
00735 while (error) {
00736 ++row_height[error_index];
00737 --error;
00738 error_index = error_index == 0 ? r_ws->rows - 1 : error_index - 1;
00739 }
00740
00741
00742 FOR_TABLE(r_ws) {
00743 Container *container = r_ws->table[cols][rows];
00744 if (container == NULL)
00745 continue;
00746 int single_width = -1, single_height = -1;
00747
00748 container->row = rows;
00749 container->col = cols;
00750 container->x = xoffset[rows];
00751 container->y = yoffset[cols];
00752 container->width = 0;
00753
00754 for (int c = 0; c < container->colspan; c++) {
00755 container->width += col_width[cols + c];
00756 if (single_width == -1)
00757 single_width = container->width;
00758 }
00759
00760 DLOG("height is %d\n", height);
00761
00762 container->height = 0;
00763
00764 for (int c = 0; c < container->rowspan; c++) {
00765 container->height += row_height[rows + c];
00766 if (single_height == -1)
00767 single_height = container->height;
00768 }
00769
00770
00771 render_container(conn, container);
00772
00773 xoffset[rows] += single_width;
00774 yoffset[cols] += single_height;
00775 }
00776
00777
00778 TAILQ_FOREACH(client, &(r_ws->floating_clients), floating_clients) {
00779 if (!client->force_reconfigure)
00780 continue;
00781
00782 client->force_reconfigure = false;
00783 reposition_client(conn, client);
00784 resize_client(conn, client);
00785 }
00786
00787 ignore_enter_notify_forall(conn, r_ws, false);
00788
00789 render_bars(conn, r_ws, width, &height);
00790 if (!config.disable_workspace_bar)
00791 render_internal_bar(conn, r_ws, width, font->height + 6);
00792 }
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802 void render_layout(xcb_connection_t *conn) {
00803 Output *output;
00804
00805 TAILQ_FOREACH(output, &outputs, outputs)
00806 if (output->current_workspace != NULL)
00807 render_workspace(conn, output, output->current_workspace);
00808
00809 xcb_flush(conn);
00810 }