00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <stdio.h>
00012 #include <assert.h>
00013 #include <string.h>
00014 #include <stdlib.h>
00015 #include <time.h>
00016
00017 #include <xcb/xcb.h>
00018 #include <xcb/xcb_atom.h>
00019 #include <xcb/xcb_icccm.h>
00020 #include <xcb/randr.h>
00021
00022 #include <X11/XKBlib.h>
00023
00024 #include "i3.h"
00025 #include "debug.h"
00026 #include "table.h"
00027 #include "layout.h"
00028 #include "commands.h"
00029 #include "data.h"
00030 #include "xcb.h"
00031 #include "util.h"
00032 #include "randr.h"
00033 #include "config.h"
00034 #include "queue.h"
00035 #include "resize.h"
00036 #include "client.h"
00037 #include "manage.h"
00038 #include "floating.h"
00039 #include "workspace.h"
00040 #include "log.h"
00041 #include "container.h"
00042 #include "ipc.h"
00043
00044
00045
00046
00047 static SLIST_HEAD(ignore_head, Ignore_Event) ignore_events;
00048
00049 static void add_ignore_event(const int sequence) {
00050 struct Ignore_Event *event = smalloc(sizeof(struct Ignore_Event));
00051
00052 event->sequence = sequence;
00053 event->added = time(NULL);
00054
00055 SLIST_INSERT_HEAD(&ignore_events, event, ignore_events);
00056 }
00057
00058
00059
00060
00061
00062 static bool event_is_ignored(const int sequence) {
00063 struct Ignore_Event *event;
00064 time_t now = time(NULL);
00065 for (event = SLIST_FIRST(&ignore_events); event != SLIST_END(&ignore_events);) {
00066 if ((now - event->added) > 5) {
00067 struct Ignore_Event *save = event;
00068 event = SLIST_NEXT(event, ignore_events);
00069 SLIST_REMOVE(&ignore_events, save, Ignore_Event, ignore_events);
00070 free(save);
00071 } else event = SLIST_NEXT(event, ignore_events);
00072 }
00073
00074 SLIST_FOREACH(event, &ignore_events, ignore_events) {
00075 if (event->sequence == sequence) {
00076 SLIST_REMOVE(&ignore_events, event, Ignore_Event, ignore_events);
00077 free(event);
00078 return true;
00079 }
00080 }
00081
00082 return false;
00083 }
00084
00085
00086
00087
00088
00089
00090 int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
00091 DLOG("Keypress %d, state raw = %d\n", event->detail, event->state);
00092
00093
00094 uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK);
00095 DLOG("(removed numlock, state = %d)\n", state_filtered);
00096
00097
00098 state_filtered &= 0xFF;
00099 DLOG("(removed upper 8 bits, state = %d)\n", state_filtered);
00100
00101 if (xkb_current_group == XkbGroup2Index)
00102 state_filtered |= BIND_MODE_SWITCH;
00103
00104 DLOG("(checked mode_switch, state %d)\n", state_filtered);
00105
00106
00107 Binding *bind = get_binding(state_filtered, event->detail);
00108
00109
00110
00111
00112
00113 if (bind == NULL) {
00114 state_filtered &= ~(BIND_MODE_SWITCH);
00115 DLOG("no match, new state_filtered = %d\n", state_filtered);
00116 if ((bind = get_binding(state_filtered, event->detail)) == NULL) {
00117 ELOG("Could not lookup key binding (modifiers %d, keycode %d)\n",
00118 state_filtered, event->detail);
00119 return 1;
00120 }
00121 }
00122
00123 parse_command(conn, bind->command);
00124 return 1;
00125 }
00126
00127
00128
00129
00130
00131
00132
00133 static void check_crossing_screen_boundary(uint32_t x, uint32_t y) {
00134 Output *output;
00135
00136 if ((output = get_output_containing(x, y)) == NULL) {
00137 ELOG("ERROR: No such screen\n");
00138 return;
00139 }
00140 if (output == c_ws->output)
00141 return;
00142
00143 c_ws->current_row = current_row;
00144 c_ws->current_col = current_col;
00145 c_ws = output->current_workspace;
00146 current_row = c_ws->current_row;
00147 current_col = c_ws->current_col;
00148 DLOG("We're now on output %p\n", output);
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164 Client *first_client = SLIST_FIRST(&(c_ws->focus_stack));
00165 if (first_client != NULL)
00166 set_focus(global_conn, first_client, true);
00167 }
00168
00169
00170
00171
00172
00173 int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_event_t *event) {
00174 DLOG("enter_notify for %08x, mode = %d, detail %d, serial %d\n", event->event, event->mode, event->detail, event->sequence);
00175 if (event->mode != XCB_NOTIFY_MODE_NORMAL) {
00176 DLOG("This was not a normal notify, ignoring\n");
00177 return 1;
00178 }
00179
00180
00181 if (event_is_ignored(event->sequence))
00182 return 1;
00183
00184
00185 Client *client = table_get(&by_parent, event->event);
00186
00187 if (client == NULL)
00188 client = table_get(&by_child, event->event);
00189
00190
00191 if (client == NULL) {
00192 struct Stack_Window *stack_win;
00193 SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
00194 if (stack_win->window == event->event) {
00195 client = stack_win->container->currently_focused;
00196 break;
00197 }
00198 }
00199
00200
00201
00202 if (client == NULL) {
00203 DLOG("Getting screen at %d x %d\n", event->root_x, event->root_y);
00204 check_crossing_screen_boundary(event->root_x, event->root_y);
00205 return 1;
00206 }
00207
00208
00209
00210 if (client->container != NULL &&
00211 client->container->mode == MODE_STACK &&
00212 client->container->currently_focused != client) {
00213 DLOG("Plausibility check says: no\n");
00214 return 1;
00215 }
00216
00217 if (client->workspace != c_ws && client->workspace->output == c_ws->output) {
00218
00219
00220
00221 DLOG("enter_notify for a client on a different workspace but the same screen, ignoring\n");
00222 return 1;
00223 }
00224
00225 if (!config.disable_focus_follows_mouse)
00226 set_focus(conn, client, false);
00227
00228 return 1;
00229 }
00230
00231
00232
00233
00234
00235
00236
00237 int handle_motion_notify(void *ignored, xcb_connection_t *conn, xcb_motion_notify_event_t *event) {
00238
00239
00240 if (event->child != 0)
00241 return 1;
00242
00243 check_crossing_screen_boundary(event->root_x, event->root_y);
00244
00245 return 1;
00246 }
00247
00248
00249
00250
00251
00252
00253 int handle_mapping_notify(void *ignored, xcb_connection_t *conn, xcb_mapping_notify_event_t *event) {
00254 if (event->request != XCB_MAPPING_KEYBOARD &&
00255 event->request != XCB_MAPPING_MODIFIER)
00256 return 0;
00257
00258 DLOG("Received mapping_notify for keyboard or modifier mapping, re-grabbing keys\n");
00259 xcb_refresh_keyboard_mapping(keysyms, event);
00260
00261 xcb_get_numlock_mask(conn);
00262
00263 ungrab_all_keys(conn);
00264 translate_keysyms();
00265 grab_all_keys(conn, false);
00266
00267 return 0;
00268 }
00269
00270
00271
00272
00273
00274 int handle_map_request(void *prophs, xcb_connection_t *conn, xcb_map_request_event_t *event) {
00275 xcb_get_window_attributes_cookie_t cookie;
00276
00277 cookie = xcb_get_window_attributes_unchecked(conn, event->window);
00278
00279 DLOG("window = 0x%08x, serial is %d.\n", event->window, event->sequence);
00280 add_ignore_event(event->sequence);
00281
00282 manage_window(prophs, conn, event->window, cookie, false);
00283 return 1;
00284 }
00285
00286
00287
00288
00289
00290
00291
00292 int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure_request_event_t *event) {
00293 DLOG("window 0x%08x wants to be at %dx%d with %dx%d\n",
00294 event->window, event->x, event->y, event->width, event->height);
00295
00296 Client *client = table_get(&by_child, event->window);
00297 if (client == NULL) {
00298 uint32_t mask = 0;
00299 uint32_t values[7];
00300 int c = 0;
00301 #define COPY_MASK_MEMBER(mask_member, event_member) do { \
00302 if (event->value_mask & mask_member) { \
00303 mask |= mask_member; \
00304 values[c++] = event->event_member; \
00305 } \
00306 } while (0)
00307
00308 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_X, x);
00309 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_Y, y);
00310 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_WIDTH, width);
00311 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_HEIGHT, height);
00312 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_BORDER_WIDTH, border_width);
00313 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_SIBLING, sibling);
00314 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_STACK_MODE, stack_mode);
00315
00316 xcb_configure_window(conn, event->window, mask, values);
00317 xcb_flush(conn);
00318
00319 return 1;
00320 }
00321
00322 if (client->fullscreen) {
00323 DLOG("Client is in fullscreen mode\n");
00324
00325 Rect child_rect = client->workspace->rect;
00326 child_rect.x = child_rect.y = 0;
00327 fake_configure_notify(conn, child_rect, client->child);
00328
00329 return 1;
00330 }
00331
00332
00333 if (client_is_floating(client)) {
00334 i3Font *font = load_font(conn, config.font);
00335 int mode = (client->container != NULL ? client->container->mode : MODE_DEFAULT);
00336
00337
00338
00339 if (event->value_mask & XCB_CONFIG_WINDOW_X) {
00340 if (mode == MODE_STACK || mode == MODE_TABBED) {
00341 client->rect.x = event->x - 2;
00342 } else {
00343 if (client->titlebar_position == TITLEBAR_OFF && client->borderless)
00344 client->rect.x = event->x;
00345 else if (client->titlebar_position == TITLEBAR_OFF && !client->borderless)
00346 client->rect.x = event->x - 1;
00347 else client->rect.x = event->x - 2;
00348 }
00349 }
00350 if (event->value_mask & XCB_CONFIG_WINDOW_Y) {
00351 if (mode == MODE_STACK || mode == MODE_TABBED) {
00352 client->rect.y = event->y - 2;
00353 } else {
00354 if (client->titlebar_position == TITLEBAR_OFF && client->borderless)
00355 client->rect.y = event->y;
00356 else if (client->titlebar_position == TITLEBAR_OFF && !client->borderless)
00357 client->rect.y = event->y - 1;
00358 else client->rect.y = event->y - font->height - 2 - 2;
00359 }
00360 }
00361 if (event->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
00362 if (mode == MODE_STACK || mode == MODE_TABBED) {
00363 client->rect.width = event->width + 2 + 2;
00364 } else {
00365 if (client->titlebar_position == TITLEBAR_OFF && client->borderless)
00366 client->rect.width = event->width;
00367 else if (client->titlebar_position == TITLEBAR_OFF && !client->borderless)
00368 client->rect.width = event->width + (1 + 1);
00369 else client->rect.width = event->width + (2 + 2);
00370 }
00371 }
00372 if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
00373 if (mode == MODE_STACK || mode == MODE_TABBED) {
00374 client->rect.height = event->height + 2;
00375 } else {
00376 if (client->titlebar_position == TITLEBAR_OFF && client->borderless)
00377 client->rect.height = event->height;
00378 else if (client->titlebar_position == TITLEBAR_OFF && !client->borderless)
00379 client->rect.height = event->height + (1 + 1);
00380 else client->rect.height = event->height + (font->height + 2 + 2) + 2;
00381 }
00382 }
00383
00384 DLOG("Accepted new position/size for floating client: (%d, %d) size %d x %d\n",
00385 client->rect.x, client->rect.y, client->rect.width, client->rect.height);
00386
00387
00388 reposition_client(conn, client);
00389 resize_client(conn, client);
00390 xcb_flush(conn);
00391
00392 return 1;
00393 }
00394
00395
00396 if (client->dock) {
00397 DLOG("Reconfiguring height of this dock client\n");
00398
00399 if (!(event->value_mask & XCB_CONFIG_WINDOW_HEIGHT)) {
00400 DLOG("Ignoring configure request, no height given\n");
00401 return 1;
00402 }
00403
00404 client->desired_height = event->height;
00405 render_workspace(conn, c_ws->output, c_ws);
00406 xcb_flush(conn);
00407
00408 return 1;
00409 }
00410
00411 if (client->fullscreen) {
00412 DLOG("Client is in fullscreen mode\n");
00413
00414 Rect child_rect = client->container->workspace->rect;
00415 child_rect.x = child_rect.y = 0;
00416 fake_configure_notify(conn, child_rect, client->child);
00417
00418 return 1;
00419 }
00420
00421 fake_absolute_configure_notify(conn, client);
00422
00423 return 1;
00424 }
00425
00426
00427
00428
00429
00430
00431 int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event) {
00432
00433 add_ignore_event(event->sequence);
00434 add_ignore_event(event->sequence);
00435
00436 return 1;
00437 }
00438
00439
00440
00441
00442
00443
00444 int handle_screen_change(void *prophs, xcb_connection_t *conn,
00445 xcb_generic_event_t *e) {
00446 DLOG("RandR screen change\n");
00447
00448 randr_query_outputs(conn);
00449
00450 ipc_send_event("output", I3_IPC_EVENT_OUTPUT, "{\"change\":\"unspecified\"}");
00451
00452 return 1;
00453 }
00454
00455
00456
00457
00458
00459
00460 int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_notify_event_t *event) {
00461 xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
00462
00463 add_ignore_event(event->sequence);
00464
00465 Client *client = table_get(&by_child, event->window);
00466
00467
00468 if (client != NULL && client->awaiting_useless_unmap) {
00469 client->awaiting_useless_unmap = false;
00470 return 1;
00471 }
00472
00473 DLOG("event->window = %08x, event->event = %08x\n", event->window, event->event);
00474 DLOG("UnmapNotify for 0x%08x (received from 0x%08x)\n", event->window, event->event);
00475 if (client == NULL) {
00476 DLOG("not a managed window. Ignoring.\n");
00477
00478
00479
00480
00481
00482 add_ignore_event(event->sequence);
00483
00484 return 0;
00485 }
00486
00487 client = table_remove(&by_child, event->window);
00488
00489
00490
00491 if (client->fullscreen) {
00492 Workspace *ws;
00493 TAILQ_FOREACH(ws, workspaces, workspaces)
00494 if (ws->fullscreen_client == client)
00495 ws->fullscreen_client = NULL;
00496 }
00497
00498
00499 if (client->container != NULL) {
00500 Container *con = client->container;
00501
00502
00503 client_remove_from_container(conn, client, con, true);
00504
00505
00506 con->currently_focused = get_last_focused_client(conn, con, NULL);
00507
00508
00509 if ((con->currently_focused != NULL) && ((con == CUR_CELL) || client->fullscreen))
00510 set_focus(conn, con->currently_focused, true);
00511 } else if (client_is_floating(client)) {
00512 DLOG("Removing from floating clients\n");
00513 TAILQ_REMOVE(&(client->workspace->floating_clients), client, floating_clients);
00514 SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients);
00515 }
00516
00517 if (client->dock) {
00518 DLOG("Removing from dock clients\n");
00519 SLIST_REMOVE(&(client->workspace->output->dock_clients), client, Client, dock_clients);
00520 }
00521
00522 DLOG("child of 0x%08x.\n", client->frame);
00523 xcb_reparent_window(conn, client->child, root, 0, 0);
00524
00525 client_unmap(conn, client);
00526
00527 xcb_destroy_window(conn, client->frame);
00528 xcb_flush(conn);
00529 table_remove(&by_parent, client->frame);
00530
00531 if (client->container != NULL) {
00532 Workspace *workspace = client->container->workspace;
00533 cleanup_table(conn, workspace);
00534 fix_colrowspan(conn, workspace);
00535 }
00536
00537
00538 bool workspace_empty = SLIST_EMPTY(&(client->workspace->focus_stack));
00539 bool workspace_focused = (c_ws == client->workspace);
00540 Client *to_focus = (!workspace_empty ? SLIST_FIRST(&(client->workspace->focus_stack)) : NULL);
00541
00542
00543 if (workspace_is_visible(client->workspace))
00544 workspace_empty = false;
00545
00546 if (workspace_empty) {
00547 client->workspace->output = NULL;
00548 ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
00549 }
00550
00551
00552 client->urgent = false;
00553 workspace_update_urgent_flag(client->workspace);
00554
00555 FREE(client->window_class_instance);
00556 FREE(client->window_class_class);
00557 FREE(client->name);
00558 free(client);
00559
00560 render_layout(conn);
00561
00562
00563
00564
00565
00566 if (workspace_focused) {
00567 if (to_focus != NULL)
00568 set_focus(conn, to_focus, true);
00569 else {
00570 DLOG("Restoring focus to root screen\n");
00571 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
00572 xcb_flush(conn);
00573 }
00574 }
00575
00576 return 1;
00577 }
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588 int handle_destroy_notify_event(void *data, xcb_connection_t *conn, xcb_destroy_notify_event_t *event) {
00589 DLOG("destroy notify for 0x%08x, 0x%08x\n", event->event, event->window);
00590
00591 xcb_unmap_notify_event_t unmap;
00592 unmap.sequence = event->sequence;
00593 unmap.event = event->event;
00594 unmap.window = event->window;
00595
00596 return handle_unmap_notify_event(NULL, conn, &unmap);
00597 }
00598
00599
00600
00601
00602
00603 int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state,
00604 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
00605 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
00606 DLOG("_NET_WM_NAME not specified, not changing\n");
00607 return 1;
00608 }
00609 Client *client = table_get(&by_child, window);
00610 if (client == NULL)
00611 return 1;
00612
00613
00614 char *new_name;
00615 int new_len;
00616 asprintf(&new_name, "%.*s", xcb_get_property_value_length(prop), (char*)xcb_get_property_value(prop));
00617
00618 char *ucs2_name = convert_utf8_to_ucs2(new_name, &new_len);
00619 LOG("_NET_WM_NAME changed to \"%s\"\n", new_name);
00620 free(new_name);
00621
00622
00623
00624
00625
00626 if ((new_len == client->name_len) &&
00627 (client->name != NULL) &&
00628 (memcmp(client->name, ucs2_name, new_len * 2) == 0)) {
00629 free(ucs2_name);
00630 return 1;
00631 }
00632
00633 char *old_name = client->name;
00634 client->name = ucs2_name;
00635 client->name_len = new_len;
00636 client->uses_net_wm_name = true;
00637
00638 FREE(old_name);
00639
00640
00641 if (client->dock)
00642 return 1;
00643
00644 if (!workspace_is_visible(client->workspace))
00645 return 1;
00646
00647 int mode = container_mode(client->container, true);
00648 if (mode == MODE_STACK || mode == MODE_TABBED)
00649 render_container(conn, client->container);
00650 else decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
00651 xcb_flush(conn);
00652
00653 return 1;
00654 }
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667 int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t state,
00668 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
00669 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
00670 DLOG("prop == NULL\n");
00671 return 1;
00672 }
00673 Client *client = table_get(&by_child, window);
00674 if (client == NULL)
00675 return 1;
00676
00677
00678 if (client->uses_net_wm_name)
00679 return 1;
00680
00681
00682 char *new_name;
00683 if (asprintf(&new_name, "%.*s", xcb_get_property_value_length(prop), (char*)xcb_get_property_value(prop)) == -1) {
00684 perror("Could not get old name");
00685 DLOG("Could not get old name\n");
00686 return 1;
00687 }
00688
00689 LOG("WM_NAME changed to \"%s\"\n", new_name);
00690
00691
00692 if (client->name != NULL &&
00693 strlen(new_name) == strlen(client->name) &&
00694 strcmp(client->name, new_name) == 0) {
00695 free(new_name);
00696 return 1;
00697 }
00698
00699 LOG("Using legacy window title. Note that in order to get Unicode window titles in i3, "
00700 "the application has to set _NET_WM_NAME which is in UTF-8 encoding.\n");
00701
00702 char *old_name = client->name;
00703 client->name = new_name;
00704 client->name_len = -1;
00705
00706 if (old_name != NULL)
00707 free(old_name);
00708
00709
00710 if (client->dock)
00711 return 1;
00712
00713 if (!workspace_is_visible(client->workspace))
00714 return 1;
00715
00716 if (client->container != NULL &&
00717 (client->container->mode == MODE_STACK ||
00718 client->container->mode == MODE_TABBED))
00719 render_container(conn, client->container);
00720 else decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
00721 xcb_flush(conn);
00722
00723 return 1;
00724 }
00725
00726
00727
00728
00729
00730 int handle_windowclass_change(void *data, xcb_connection_t *conn, uint8_t state,
00731 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
00732 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
00733 DLOG("prop == NULL\n");
00734 return 1;
00735 }
00736 Client *client = table_get(&by_child, window);
00737 if (client == NULL)
00738 return 1;
00739
00740
00741
00742
00743 char *new_class = xcb_get_property_value(prop);
00744
00745 FREE(client->window_class_instance);
00746 FREE(client->window_class_class);
00747
00748 client->window_class_instance = strdup(new_class);
00749 if ((strlen(new_class) + 1) < xcb_get_property_value_length(prop))
00750 client->window_class_class = strdup(new_class + strlen(new_class) + 1);
00751 else client->window_class_class = NULL;
00752 LOG("WM_CLASS changed to %s (instance), %s (class)\n",
00753 client->window_class_instance, client->window_class_class);
00754
00755 return 0;
00756 }
00757
00758
00759
00760
00761
00762 int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *event) {
00763
00764
00765 if (event->count != 0)
00766 return 1;
00767 DLOG("window = %08x\n", event->window);
00768
00769 Client *client = table_get(&by_parent, event->window);
00770 if (client == NULL) {
00771
00772
00773 struct Stack_Window *stack_win;
00774 SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
00775 if (stack_win->window == event->window) {
00776 render_container(conn, stack_win->container);
00777 return 1;
00778 }
00779
00780
00781 Output *output;
00782 TAILQ_FOREACH(output, &outputs, outputs)
00783 if (output->bar == event->window)
00784 render_layout(conn);
00785 return 1;
00786 }
00787
00788 if (client->dock)
00789 return 1;
00790
00791 if (container_mode(client->container, true) == MODE_DEFAULT)
00792 decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
00793 else {
00794 uint32_t background_color;
00795 if (client->urgent)
00796 background_color = config.client.urgent.background;
00797
00798 else if (CUR_CELL != NULL && CUR_CELL->currently_focused == client)
00799 background_color = config.client.focused.background;
00800
00801 else background_color = config.client.focused_inactive.background;
00802
00803
00804 uint32_t values[] = {background_color, 2};
00805 xcb_change_gc(conn, client->titlegc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
00806
00807
00808 xcb_point_t points[] = {{1, 0},
00809 {1, client->rect.height-1},
00810 {client->rect.width-1, client->rect.height-1},
00811 {client->rect.width-1, 0}};
00812 xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, client->frame, client->titlegc, 4, points);
00813
00814
00815 xcb_change_gc_single(conn, client->titlegc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
00816 if (client->titlebar_position == TITLEBAR_OFF && !client->borderless) {
00817 xcb_rectangle_t crect = {1, 0, client->rect.width - (1 + 1), client->rect.height - 1};
00818 xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
00819 } else {
00820 xcb_rectangle_t crect = {2, 0, client->rect.width - (2 + 2), client->rect.height - 2};
00821 xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
00822 }
00823 }
00824 xcb_flush(conn);
00825 return 1;
00826 }
00827
00828
00829
00830
00831
00832 int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message_event_t *event) {
00833 if (event->type == atoms[_NET_WM_STATE]) {
00834 if (event->format != 32 || event->data.data32[1] != atoms[_NET_WM_STATE_FULLSCREEN])
00835 return 0;
00836
00837 Client *client = table_get(&by_child, event->window);
00838 if (client == NULL)
00839 return 0;
00840
00841
00842 if ((client->fullscreen &&
00843 (event->data.data32[0] == _NET_WM_STATE_REMOVE ||
00844 event->data.data32[0] == _NET_WM_STATE_TOGGLE)) ||
00845 (!client->fullscreen &&
00846 (event->data.data32[0] == _NET_WM_STATE_ADD ||
00847 event->data.data32[0] == _NET_WM_STATE_TOGGLE)))
00848 client_toggle_fullscreen(conn, client);
00849 } else {
00850 ELOG("unhandled clientmessage\n");
00851 return 0;
00852 }
00853
00854 return 1;
00855 }
00856
00857 int handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
00858 xcb_atom_t atom, xcb_get_property_reply_t *property) {
00859
00860
00861 ELOG("_NET_WM_WINDOW_TYPE changed, this is not yet implemented.\n");
00862 return 0;
00863 }
00864
00865
00866
00867
00868
00869
00870
00871
00872 int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
00873 xcb_atom_t name, xcb_get_property_reply_t *reply) {
00874 Client *client = table_get(&by_child, window);
00875 if (client == NULL) {
00876 DLOG("Received WM_SIZE_HINTS for unknown client\n");
00877 return 1;
00878 }
00879 xcb_size_hints_t size_hints;
00880
00881 CLIENT_LOG(client);
00882
00883
00884 if (reply != NULL)
00885 xcb_get_wm_size_hints_from_reply(&size_hints, reply);
00886 else
00887 xcb_get_wm_normal_hints_reply(conn, xcb_get_wm_normal_hints_unchecked(conn, client->child), &size_hints, NULL);
00888
00889 if ((size_hints.flags & XCB_SIZE_HINT_P_MIN_SIZE)) {
00890
00891 DLOG("Minimum size: %d (width) x %d (height)\n", size_hints.min_width, size_hints.min_height);
00892 }
00893
00894 bool changed = false;
00895 if ((size_hints.flags & XCB_SIZE_HINT_P_RESIZE_INC)) {
00896 if (size_hints.width_inc > 0 && size_hints.width_inc < 0xFFFF)
00897 if (client->width_increment != size_hints.width_inc) {
00898 client->width_increment = size_hints.width_inc;
00899 changed = true;
00900 }
00901 if (size_hints.height_inc > 0 && size_hints.height_inc < 0xFFFF)
00902 if (client->height_increment != size_hints.height_inc) {
00903 client->height_increment = size_hints.height_inc;
00904 changed = true;
00905 }
00906
00907 if (changed)
00908 DLOG("resize increments changed\n");
00909 }
00910
00911 int base_width = 0, base_height = 0;
00912
00913
00914
00915
00916 if (size_hints.flags & XCB_SIZE_HINT_BASE_SIZE) {
00917 base_width = size_hints.base_width;
00918 base_height = size_hints.base_height;
00919 } else if (size_hints.flags & XCB_SIZE_HINT_P_MIN_SIZE) {
00920
00921 base_width = size_hints.min_width;
00922 base_height = size_hints.min_height;
00923 }
00924
00925 if (base_width != client->base_width ||
00926 base_height != client->base_height) {
00927 client->base_width = base_width;
00928 client->base_height = base_height;
00929 DLOG("client's base_height changed to %d\n", base_height);
00930 DLOG("client's base_width changed to %d\n", base_width);
00931 changed = true;
00932 }
00933
00934 if (changed) {
00935 if (client->fullscreen)
00936 DLOG("Not resizing client, it is in fullscreen mode\n");
00937 else {
00938 resize_client(conn, client);
00939 xcb_flush(conn);
00940 }
00941 }
00942
00943
00944 if (!(size_hints.flags & XCB_SIZE_HINT_P_ASPECT) ||
00945 (size_hints.min_aspect_num <= 0) ||
00946 (size_hints.min_aspect_den <= 0)) {
00947 return 1;
00948 }
00949
00950 double width = client->rect.width - base_width;
00951 double height = client->rect.height - base_height;
00952
00953 double min_aspect = (double)size_hints.min_aspect_num / size_hints.min_aspect_den;
00954 double max_aspect = (double)size_hints.max_aspect_num / size_hints.min_aspect_den;
00955
00956 DLOG("Aspect ratio set: minimum %f, maximum %f\n", min_aspect, max_aspect);
00957 DLOG("width = %f, height = %f\n", width, height);
00958
00959
00960 if (max_aspect <= 0 || min_aspect <= 0 || height == 0 || (width / height) <= 0)
00961 return 1;
00962
00963
00964 if ((width / height) < min_aspect) {
00965 client->proportional_width = width;
00966 client->proportional_height = width / min_aspect;
00967 } else if ((width / height) > max_aspect) {
00968 client->proportional_width = width;
00969 client->proportional_height = width / max_aspect;
00970 } else return 1;
00971
00972 client->force_reconfigure = true;
00973
00974 if (client->container != NULL && workspace_is_visible(client->workspace)) {
00975 render_container(conn, client->container);
00976 xcb_flush(conn);
00977 }
00978
00979 return 1;
00980 }
00981
00982
00983
00984
00985
00986 int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
00987 xcb_atom_t name, xcb_get_property_reply_t *reply) {
00988 Client *client = table_get(&by_child, window);
00989 if (client == NULL) {
00990 DLOG("Received WM_HINTS for unknown client\n");
00991 return 1;
00992 }
00993 xcb_wm_hints_t hints;
00994
00995 if (reply != NULL) {
00996 if (!xcb_get_wm_hints_from_reply(&hints, reply))
00997 return 1;
00998 } else {
00999 if (!xcb_get_wm_hints_reply(conn, xcb_get_wm_hints_unchecked(conn, client->child), &hints, NULL))
01000 return 1;
01001 }
01002
01003 Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
01004 if (!client->urgent && client == last_focused) {
01005 DLOG("Ignoring urgency flag for current client\n");
01006 return 1;
01007 }
01008
01009
01010 client->urgent = (xcb_wm_hints_get_urgency(&hints) != 0);
01011 CLIENT_LOG(client);
01012 LOG("Urgency flag changed to %d\n", client->urgent);
01013
01014 workspace_update_urgent_flag(client->workspace);
01015
01016
01017
01018 if (!workspace_is_visible(client->workspace)) {
01019 Output *output = client->workspace->output;
01020 render_workspace(conn, output, output->current_workspace);
01021 xcb_flush(conn);
01022 } else {
01023 redecorate_window(conn, client);
01024 }
01025
01026 return 1;
01027 }
01028
01029
01030
01031
01032
01033
01034
01035
01036 int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
01037 xcb_atom_t name, xcb_get_property_reply_t *reply) {
01038 Client *client = table_get(&by_child, window);
01039 if (client == NULL) {
01040 DLOG("No such client\n");
01041 return 1;
01042 }
01043
01044 xcb_window_t transient_for;
01045
01046 if (reply != NULL) {
01047 if (!xcb_get_wm_transient_for_from_reply(&transient_for, reply))
01048 return 1;
01049 } else {
01050 if (!xcb_get_wm_transient_for_reply(conn, xcb_get_wm_transient_for_unchecked(conn, window),
01051 &transient_for, NULL))
01052 return 1;
01053 }
01054
01055 if (client->floating == FLOATING_AUTO_OFF) {
01056 DLOG("This is a popup window, putting into floating\n");
01057 toggle_floating_mode(conn, client, true);
01058 }
01059
01060 return 1;
01061 }
01062
01063
01064
01065
01066
01067
01068 int handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
01069 xcb_atom_t name, xcb_get_property_reply_t *prop) {
01070 if (prop == NULL) {
01071 prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
01072 false, window, WM_CLIENT_LEADER, WINDOW, 0, 32), NULL);
01073 if (prop == NULL)
01074 return 1;
01075 }
01076
01077 Client *client = table_get(&by_child, window);
01078 if (client == NULL)
01079 return 1;
01080
01081 xcb_window_t *leader = xcb_get_property_value(prop);
01082 if (leader == NULL)
01083 return 1;
01084
01085 DLOG("Client leader changed to %08x\n", *leader);
01086
01087 client->leader = *leader;
01088
01089 return 1;
01090 }