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 if (asprintf(&new_name, "%.*s", xcb_get_property_value_length(prop), (char*)xcb_get_property_value(prop)) == -1) {
00617 perror("asprintf");
00618 LOG("Could not format _NET_WM_NAME, ignoring new hint\n");
00619 return 1;
00620 }
00621
00622 char *ucs2_name = convert_utf8_to_ucs2(new_name, &new_len);
00623 LOG("_NET_WM_NAME changed to \"%s\"\n", new_name);
00624 free(new_name);
00625 if (ucs2_name == NULL) {
00626 LOG("Could not convert _NET_WM_NAME to UCS-2, ignoring new hint\n");
00627 return 1;
00628 }
00629
00630
00631
00632
00633
00634 if ((new_len == client->name_len) &&
00635 (client->name != NULL) &&
00636 (memcmp(client->name, ucs2_name, new_len * 2) == 0)) {
00637 free(ucs2_name);
00638 return 1;
00639 }
00640
00641 char *old_name = client->name;
00642 client->name = ucs2_name;
00643 client->name_len = new_len;
00644 client->uses_net_wm_name = true;
00645
00646 FREE(old_name);
00647
00648
00649 if (client->dock)
00650 return 1;
00651
00652 if (!workspace_is_visible(client->workspace))
00653 return 1;
00654
00655 int mode = container_mode(client->container, true);
00656 if (mode == MODE_STACK || mode == MODE_TABBED)
00657 render_container(conn, client->container);
00658 else decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
00659 xcb_flush(conn);
00660
00661 return 1;
00662 }
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675 int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t state,
00676 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
00677 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
00678 DLOG("prop == NULL\n");
00679 return 1;
00680 }
00681 Client *client = table_get(&by_child, window);
00682 if (client == NULL)
00683 return 1;
00684
00685
00686 if (client->uses_net_wm_name)
00687 return 1;
00688
00689
00690 char *new_name;
00691 if (asprintf(&new_name, "%.*s", xcb_get_property_value_length(prop), (char*)xcb_get_property_value(prop)) == -1) {
00692 perror("Could not get old name");
00693 DLOG("Could not get old name\n");
00694 return 1;
00695 }
00696
00697 LOG("WM_NAME changed to \"%s\"\n", new_name);
00698
00699
00700 if (client->name != NULL &&
00701 strlen(new_name) == strlen(client->name) &&
00702 strcmp(client->name, new_name) == 0) {
00703 free(new_name);
00704 return 1;
00705 }
00706
00707 LOG("Using legacy window title. Note that in order to get Unicode window titles in i3, "
00708 "the application has to set _NET_WM_NAME which is in UTF-8 encoding.\n");
00709
00710 char *old_name = client->name;
00711 client->name = new_name;
00712 client->name_len = -1;
00713
00714 if (old_name != NULL)
00715 free(old_name);
00716
00717
00718 if (client->dock)
00719 return 1;
00720
00721 if (!workspace_is_visible(client->workspace))
00722 return 1;
00723
00724 if (client->container != NULL &&
00725 (client->container->mode == MODE_STACK ||
00726 client->container->mode == MODE_TABBED))
00727 render_container(conn, client->container);
00728 else decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
00729 xcb_flush(conn);
00730
00731 return 1;
00732 }
00733
00734
00735
00736
00737
00738 int handle_windowclass_change(void *data, xcb_connection_t *conn, uint8_t state,
00739 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
00740 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
00741 DLOG("prop == NULL\n");
00742 return 1;
00743 }
00744 Client *client = table_get(&by_child, window);
00745 if (client == NULL)
00746 return 1;
00747
00748
00749
00750
00751 char *new_class = xcb_get_property_value(prop);
00752
00753 FREE(client->window_class_instance);
00754 FREE(client->window_class_class);
00755
00756 client->window_class_instance = strdup(new_class);
00757 if ((strlen(new_class) + 1) < xcb_get_property_value_length(prop))
00758 client->window_class_class = strdup(new_class + strlen(new_class) + 1);
00759 else client->window_class_class = NULL;
00760 LOG("WM_CLASS changed to %s (instance), %s (class)\n",
00761 client->window_class_instance, client->window_class_class);
00762
00763 return 0;
00764 }
00765
00766
00767
00768
00769
00770 int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *event) {
00771
00772
00773 if (event->count != 0)
00774 return 1;
00775 DLOG("window = %08x\n", event->window);
00776
00777 Client *client = table_get(&by_parent, event->window);
00778 if (client == NULL) {
00779
00780
00781 struct Stack_Window *stack_win;
00782 SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
00783 if (stack_win->window == event->window) {
00784 render_container(conn, stack_win->container);
00785 return 1;
00786 }
00787
00788
00789 Output *output;
00790 TAILQ_FOREACH(output, &outputs, outputs)
00791 if (output->bar == event->window)
00792 render_layout(conn);
00793 return 1;
00794 }
00795
00796 if (client->dock)
00797 return 1;
00798
00799 if (container_mode(client->container, true) == MODE_DEFAULT)
00800 decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
00801 else {
00802 uint32_t background_color;
00803 if (client->urgent)
00804 background_color = config.client.urgent.background;
00805
00806 else if (CUR_CELL != NULL && CUR_CELL->currently_focused == client)
00807 background_color = config.client.focused.background;
00808
00809 else background_color = config.client.focused_inactive.background;
00810
00811
00812 uint32_t values[] = {background_color, 2};
00813 xcb_change_gc(conn, client->titlegc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
00814
00815
00816 xcb_point_t points[] = {{1, 0},
00817 {1, client->rect.height-1},
00818 {client->rect.width-1, client->rect.height-1},
00819 {client->rect.width-1, 0}};
00820 xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, client->frame, client->titlegc, 4, points);
00821
00822
00823 xcb_change_gc_single(conn, client->titlegc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
00824 if (client->titlebar_position == TITLEBAR_OFF && !client->borderless) {
00825 xcb_rectangle_t crect = {1, 0, client->rect.width - (1 + 1), client->rect.height - 1};
00826 xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
00827 } else {
00828 xcb_rectangle_t crect = {2, 0, client->rect.width - (2 + 2), client->rect.height - 2};
00829 xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
00830 }
00831 }
00832 xcb_flush(conn);
00833 return 1;
00834 }
00835
00836
00837
00838
00839
00840 int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message_event_t *event) {
00841 if (event->type == atoms[_NET_WM_STATE]) {
00842 if (event->format != 32 || event->data.data32[1] != atoms[_NET_WM_STATE_FULLSCREEN])
00843 return 0;
00844
00845 Client *client = table_get(&by_child, event->window);
00846 if (client == NULL)
00847 return 0;
00848
00849
00850 if ((client->fullscreen &&
00851 (event->data.data32[0] == _NET_WM_STATE_REMOVE ||
00852 event->data.data32[0] == _NET_WM_STATE_TOGGLE)) ||
00853 (!client->fullscreen &&
00854 (event->data.data32[0] == _NET_WM_STATE_ADD ||
00855 event->data.data32[0] == _NET_WM_STATE_TOGGLE)))
00856 client_toggle_fullscreen(conn, client);
00857 } else {
00858 ELOG("unhandled clientmessage\n");
00859 return 0;
00860 }
00861
00862 return 1;
00863 }
00864
00865 int handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
00866 xcb_atom_t atom, xcb_get_property_reply_t *property) {
00867
00868
00869 ELOG("_NET_WM_WINDOW_TYPE changed, this is not yet implemented.\n");
00870 return 0;
00871 }
00872
00873
00874
00875
00876
00877
00878
00879
00880 int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
00881 xcb_atom_t name, xcb_get_property_reply_t *reply) {
00882 Client *client = table_get(&by_child, window);
00883 if (client == NULL) {
00884 DLOG("Received WM_SIZE_HINTS for unknown client\n");
00885 return 1;
00886 }
00887 xcb_size_hints_t size_hints;
00888
00889 CLIENT_LOG(client);
00890
00891
00892 if (reply != NULL)
00893 xcb_get_wm_size_hints_from_reply(&size_hints, reply);
00894 else
00895 xcb_get_wm_normal_hints_reply(conn, xcb_get_wm_normal_hints_unchecked(conn, client->child), &size_hints, NULL);
00896
00897 if ((size_hints.flags & XCB_SIZE_HINT_P_MIN_SIZE)) {
00898
00899 DLOG("Minimum size: %d (width) x %d (height)\n", size_hints.min_width, size_hints.min_height);
00900 }
00901
00902 bool changed = false;
00903 if ((size_hints.flags & XCB_SIZE_HINT_P_RESIZE_INC)) {
00904 if (size_hints.width_inc > 0 && size_hints.width_inc < 0xFFFF)
00905 if (client->width_increment != size_hints.width_inc) {
00906 client->width_increment = size_hints.width_inc;
00907 changed = true;
00908 }
00909 if (size_hints.height_inc > 0 && size_hints.height_inc < 0xFFFF)
00910 if (client->height_increment != size_hints.height_inc) {
00911 client->height_increment = size_hints.height_inc;
00912 changed = true;
00913 }
00914
00915 if (changed)
00916 DLOG("resize increments changed\n");
00917 }
00918
00919 int base_width = 0, base_height = 0;
00920
00921
00922
00923
00924 if (size_hints.flags & XCB_SIZE_HINT_BASE_SIZE) {
00925 base_width = size_hints.base_width;
00926 base_height = size_hints.base_height;
00927 } else if (size_hints.flags & XCB_SIZE_HINT_P_MIN_SIZE) {
00928
00929 base_width = size_hints.min_width;
00930 base_height = size_hints.min_height;
00931 }
00932
00933 if (base_width != client->base_width ||
00934 base_height != client->base_height) {
00935 client->base_width = base_width;
00936 client->base_height = base_height;
00937 DLOG("client's base_height changed to %d\n", base_height);
00938 DLOG("client's base_width changed to %d\n", base_width);
00939 changed = true;
00940 }
00941
00942 if (changed) {
00943 if (client->fullscreen)
00944 DLOG("Not resizing client, it is in fullscreen mode\n");
00945 else {
00946 resize_client(conn, client);
00947 xcb_flush(conn);
00948 }
00949 }
00950
00951
00952 if (!(size_hints.flags & XCB_SIZE_HINT_P_ASPECT) ||
00953 (size_hints.min_aspect_num <= 0) ||
00954 (size_hints.min_aspect_den <= 0)) {
00955 return 1;
00956 }
00957
00958 double width = client->rect.width - base_width;
00959 double height = client->rect.height - base_height;
00960
00961 double min_aspect = (double)size_hints.min_aspect_num / size_hints.min_aspect_den;
00962 double max_aspect = (double)size_hints.max_aspect_num / size_hints.min_aspect_den;
00963
00964 DLOG("Aspect ratio set: minimum %f, maximum %f\n", min_aspect, max_aspect);
00965 DLOG("width = %f, height = %f\n", width, height);
00966
00967
00968 if (max_aspect <= 0 || min_aspect <= 0 || height == 0 || (width / height) <= 0)
00969 return 1;
00970
00971
00972 if ((width / height) < min_aspect) {
00973 client->proportional_width = width;
00974 client->proportional_height = width / min_aspect;
00975 } else if ((width / height) > max_aspect) {
00976 client->proportional_width = width;
00977 client->proportional_height = width / max_aspect;
00978 } else return 1;
00979
00980 client->force_reconfigure = true;
00981
00982 if (client->container != NULL && workspace_is_visible(client->workspace)) {
00983 render_container(conn, client->container);
00984 xcb_flush(conn);
00985 }
00986
00987 return 1;
00988 }
00989
00990
00991
00992
00993
00994 int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
00995 xcb_atom_t name, xcb_get_property_reply_t *reply) {
00996 Client *client = table_get(&by_child, window);
00997 if (client == NULL) {
00998 DLOG("Received WM_HINTS for unknown client\n");
00999 return 1;
01000 }
01001 xcb_wm_hints_t hints;
01002
01003 if (reply != NULL) {
01004 if (!xcb_get_wm_hints_from_reply(&hints, reply))
01005 return 1;
01006 } else {
01007 if (!xcb_get_wm_hints_reply(conn, xcb_get_wm_hints_unchecked(conn, client->child), &hints, NULL))
01008 return 1;
01009 }
01010
01011 Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
01012 if (!client->urgent && client == last_focused) {
01013 DLOG("Ignoring urgency flag for current client\n");
01014 return 1;
01015 }
01016
01017
01018 client->urgent = (xcb_wm_hints_get_urgency(&hints) != 0);
01019 CLIENT_LOG(client);
01020 LOG("Urgency flag changed to %d\n", client->urgent);
01021
01022 workspace_update_urgent_flag(client->workspace);
01023
01024
01025
01026 if (!workspace_is_visible(client->workspace)) {
01027 Output *output = client->workspace->output;
01028 render_workspace(conn, output, output->current_workspace);
01029 xcb_flush(conn);
01030 } else {
01031 redecorate_window(conn, client);
01032 }
01033
01034 return 1;
01035 }
01036
01037
01038
01039
01040
01041
01042
01043
01044 int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
01045 xcb_atom_t name, xcb_get_property_reply_t *reply) {
01046 Client *client = table_get(&by_child, window);
01047 if (client == NULL) {
01048 DLOG("No such client\n");
01049 return 1;
01050 }
01051
01052 xcb_window_t transient_for;
01053
01054 if (reply != NULL) {
01055 if (!xcb_get_wm_transient_for_from_reply(&transient_for, reply))
01056 return 1;
01057 } else {
01058 if (!xcb_get_wm_transient_for_reply(conn, xcb_get_wm_transient_for_unchecked(conn, window),
01059 &transient_for, NULL))
01060 return 1;
01061 }
01062
01063 if (client->floating == FLOATING_AUTO_OFF) {
01064 DLOG("This is a popup window, putting into floating\n");
01065 toggle_floating_mode(conn, client, true);
01066 }
01067
01068 return 1;
01069 }
01070
01071
01072
01073
01074
01075
01076 int handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
01077 xcb_atom_t name, xcb_get_property_reply_t *prop) {
01078 if (prop == NULL) {
01079 prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
01080 false, window, WM_CLIENT_LEADER, WINDOW, 0, 32), NULL);
01081 if (prop == NULL)
01082 return 1;
01083 }
01084
01085 Client *client = table_get(&by_child, window);
01086 if (client == NULL)
01087 return 1;
01088
01089 xcb_window_t *leader = xcb_get_property_value(prop);
01090 if (leader == NULL)
01091 return 1;
01092
01093 DLOG("Client leader changed to %08x\n", *leader);
01094
01095 client->leader = *leader;
01096
01097 return 1;
01098 }