00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include <stdlib.h>
00014 #include <string.h>
00015 #include <assert.h>
00016 #include <limits.h>
00017
00018 #include <xcb/xcb.h>
00019 #include <xcb/xcb_icccm.h>
00020
00021 #include "data.h"
00022 #include "i3.h"
00023 #include "xcb.h"
00024 #include "util.h"
00025 #include "queue.h"
00026 #include "layout.h"
00027 #include "client.h"
00028 #include "table.h"
00029 #include "workspace.h"
00030 #include "config.h"
00031 #include "log.h"
00032
00033
00034
00035
00036
00037
00038 void client_remove_from_container(xcb_connection_t *conn, Client *client, Container *container, bool remove_from_focusstack) {
00039 CIRCLEQ_REMOVE(&(container->clients), client, clients);
00040
00041 if (remove_from_focusstack)
00042 SLIST_REMOVE(&(container->workspace->focus_stack), client, Client, focus_clients);
00043
00044
00045
00046 if (CIRCLEQ_EMPTY(&(container->clients)) &&
00047 (container->mode == MODE_STACK ||
00048 container->mode == MODE_TABBED)) {
00049 DLOG("Unmapping stack window\n");
00050 struct Stack_Window *stack_win = &(container->stack_win);
00051 stack_win->rect.height = 0;
00052 xcb_unmap_window(conn, stack_win->window);
00053 xcb_flush(conn);
00054 }
00055 }
00056
00057
00058
00059
00060
00061
00062 void client_warp_pointer_into(xcb_connection_t *conn, Client *client) {
00063 int mid_x = client->rect.width / 2,
00064 mid_y = client->rect.height / 2;
00065 xcb_warp_pointer(conn, XCB_NONE, client->child, 0, 0, 0, 0, mid_x, mid_y);
00066 }
00067
00068
00069
00070
00071
00072 static bool client_supports_protocol(xcb_connection_t *conn, Client *client, xcb_atom_t atom) {
00073 xcb_get_property_cookie_t cookie;
00074 xcb_get_wm_protocols_reply_t protocols;
00075 bool result = false;
00076
00077 cookie = xcb_get_wm_protocols_unchecked(conn, client->child, atoms[WM_PROTOCOLS]);
00078 if (xcb_get_wm_protocols_reply(conn, cookie, &protocols, NULL) != 1)
00079 return false;
00080
00081
00082 for (uint32_t i = 0; i < protocols.atoms_len; i++)
00083 if (protocols.atoms[i] == atom)
00084 result = true;
00085
00086 xcb_get_wm_protocols_reply_wipe(&protocols);
00087
00088 return result;
00089 }
00090
00091
00092
00093
00094
00095 void client_kill(xcb_connection_t *conn, Client *window) {
00096
00097 if (!client_supports_protocol(conn, window, atoms[WM_DELETE_WINDOW])) {
00098 LOG("Killing window the hard way\n");
00099 xcb_kill_client(conn, window->child);
00100 return;
00101 }
00102
00103 xcb_client_message_event_t ev;
00104
00105 memset(&ev, 0, sizeof(xcb_client_message_event_t));
00106
00107 ev.response_type = XCB_CLIENT_MESSAGE;
00108 ev.window = window->child;
00109 ev.type = atoms[WM_PROTOCOLS];
00110 ev.format = 32;
00111 ev.data.data32[0] = atoms[WM_DELETE_WINDOW];
00112 ev.data.data32[1] = XCB_CURRENT_TIME;
00113
00114 LOG("Sending WM_DELETE to the client\n");
00115 xcb_send_event(conn, false, window->child, XCB_EVENT_MASK_NO_EVENT, (char*)&ev);
00116 xcb_flush(conn);
00117 }
00118
00119
00120
00121
00122
00123
00124
00125 bool client_matches_class_name(Client *client, char *to_class, char *to_title,
00126 char *to_title_ucs, int to_title_ucs_len) {
00127
00128 if ((client->window_class_instance == NULL ||
00129 strcasestr(client->window_class_instance, to_class) == NULL) &&
00130 (client->window_class_class == NULL ||
00131 strcasestr(client->window_class_class, to_class) == NULL))
00132 return false;
00133
00134
00135 if (to_title == NULL)
00136 return true;
00137
00138 if (client->name_len > -1) {
00139
00140 if (client->name == NULL || memmem(client->name, (client->name_len * 2), to_title_ucs, (to_title_ucs_len * 2)) == NULL)
00141 return false;
00142 } else {
00143
00144 if (client->name == NULL || strcasestr(client->name, to_title) == NULL)
00145 return false;
00146 }
00147
00148 return true;
00149 }
00150
00151
00152
00153
00154
00155
00156 void client_enter_fullscreen(xcb_connection_t *conn, Client *client, bool global) {
00157 Workspace *workspace;
00158 Output *output;
00159 Rect r;
00160
00161 if (global) {
00162 TAILQ_FOREACH(output, &outputs, outputs) {
00163 if (!output->active)
00164 continue;
00165
00166 if (output->current_workspace->fullscreen_client == NULL)
00167 continue;
00168
00169 LOG("Not entering global fullscreen mode, there already "
00170 "is a fullscreen client on output %s.\n", output->name);
00171 return;
00172 }
00173
00174 r = (Rect) { UINT_MAX, UINT_MAX, 0,0 };
00175 Output *output;
00176
00177
00178
00179 TAILQ_FOREACH(output, &outputs, outputs) {
00180 if (!output->active)
00181 continue;
00182
00183 output->current_workspace->fullscreen_client = client;
00184
00185
00186 if (r.x > output->rect.x)
00187 r.x = output->rect.x;
00188 if (r.y > output->rect.y)
00189 r.y = output->rect.y;
00190 if (r.x + r.width < output->rect.x + output->rect.width)
00191 r.width = output->rect.x + output->rect.width;
00192 if (r.y + r.height < output->rect.y + output->rect.height)
00193 r.height = output->rect.y + output->rect.height;
00194 }
00195
00196
00197 r.height -= r.x;
00198 r.width -= r.y;
00199
00200 LOG("Entering global fullscreen mode...\n");
00201 } else {
00202 workspace = client->workspace;
00203 if (workspace->fullscreen_client != NULL && workspace->fullscreen_client != client) {
00204 LOG("Not entering fullscreen mode, there already is a fullscreen client.\n");
00205 return;
00206 }
00207
00208 workspace->fullscreen_client = client;
00209 r = workspace->rect;
00210
00211 LOG("Entering fullscreen mode...\n");
00212 }
00213
00214 client->fullscreen = true;
00215
00216
00217 DLOG("child itself will be at %dx%d with size %dx%d\n",
00218 r.x, r.y, r.width, r.height);
00219
00220 xcb_set_window_rect(conn, client->frame, r);
00221
00222
00223 r.x = 0;
00224 r.y = 0;
00225 xcb_set_window_rect(conn, client->child, r);
00226
00227
00228 uint32_t values[] = { XCB_STACK_MODE_ABOVE };
00229 xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_STACK_MODE, values);
00230
00231
00232 values[0] = atoms[_NET_WM_STATE_FULLSCREEN];
00233 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[_NET_WM_STATE], ATOM, 32, 1, values);
00234
00235 fake_configure_notify(conn, r, client->child);
00236
00237 xcb_flush(conn);
00238 }
00239
00240
00241
00242
00243
00244 void client_leave_fullscreen(xcb_connection_t *conn, Client *client) {
00245 LOG("leaving fullscreen mode\n");
00246 client->fullscreen = false;
00247 Workspace *ws;
00248 TAILQ_FOREACH(ws, workspaces, workspaces)
00249 if (ws->fullscreen_client == client)
00250 ws->fullscreen_client = NULL;
00251
00252 if (client_is_floating(client)) {
00253
00254
00255 reposition_client(conn, client);
00256 resize_client(conn, client);
00257
00258 redecorate_window(conn, client);
00259 } else {
00260 client_set_below_floating(conn, client);
00261
00262
00263
00264 client->force_reconfigure = true;
00265
00266 render_layout(conn);
00267 }
00268
00269
00270 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[_NET_WM_STATE], ATOM, 32, 0, NULL);
00271
00272 xcb_flush(conn);
00273 }
00274
00275
00276
00277
00278
00279
00280
00281 void client_toggle_fullscreen(xcb_connection_t *conn, Client *client) {
00282
00283 assert(!client->dock);
00284
00285 if (!client->fullscreen) {
00286 client_enter_fullscreen(conn, client, false);
00287 } else {
00288 client_leave_fullscreen(conn, client);
00289 }
00290 }
00291
00292
00293
00294
00295
00296 void client_toggle_fullscreen_global(xcb_connection_t *conn, Client *client) {
00297
00298 assert(!client->dock);
00299
00300 if (!client->fullscreen) {
00301 client_enter_fullscreen(conn, client, true);
00302 } else {
00303 client_leave_fullscreen(conn, client);
00304 }
00305 }
00306
00307
00308
00309
00310
00311
00312
00313 void client_set_below_floating(xcb_connection_t *conn, Client *client) {
00314
00315 Workspace *ws = client->workspace;
00316 Client *first_floating = TAILQ_FIRST(&(ws->floating_clients));
00317 if (first_floating == TAILQ_END(&(ws->floating_clients)))
00318 return;
00319
00320 DLOG("Setting below floating\n");
00321 uint32_t values[] = { first_floating->frame, XCB_STACK_MODE_BELOW };
00322 xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
00323
00324 if (client->workspace->fullscreen_client == NULL)
00325 return;
00326
00327 DLOG("(and below fullscreen)\n");
00328
00329 values[0] = client->workspace->fullscreen_client->frame;
00330 xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
00331 }
00332
00333
00334
00335
00336
00337
00338
00339 bool client_is_floating(Client *client) {
00340 return (client->floating >= FLOATING_AUTO_ON);
00341 }
00342
00343
00344
00345
00346
00347
00348
00349 bool client_init_border(xcb_connection_t *conn, Client *client, char border_type) {
00350 switch (border_type) {
00351 case 'n':
00352 LOG("Changing to normal border\n");
00353 client->titlebar_position = TITLEBAR_TOP;
00354 client->borderless = false;
00355 return true;
00356 case 'p':
00357 LOG("Changing to 1px border\n");
00358 client->titlebar_position = TITLEBAR_OFF;
00359 client->borderless = false;
00360 return true;
00361 case 'b':
00362 LOG("Changing to borderless\n");
00363 client->titlebar_position = TITLEBAR_OFF;
00364 client->borderless = true;
00365 return true;
00366 default:
00367 LOG("Unknown border mode\n");
00368 return false;
00369 }
00370 }
00371
00372
00373
00374
00375
00376
00377 void client_change_border(xcb_connection_t *conn, Client *client, char border_type) {
00378 if (!client_init_border(conn, client, border_type))
00379 return;
00380
00381
00382 client->force_reconfigure = true;
00383
00384
00385 if (client->container != NULL)
00386 render_container(conn, client->container);
00387 else {
00388
00389 if (client_is_floating(client))
00390 resize_client(conn, client);
00391
00392 else render_layout(conn);
00393 }
00394
00395 redecorate_window(conn, client);
00396 }
00397
00398
00399
00400
00401
00402 void client_unmap(xcb_connection_t *conn, Client *client) {
00403
00404 long data[] = { XCB_WM_STATE_WITHDRAWN, XCB_NONE };
00405 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
00406
00407 xcb_unmap_window(conn, client->frame);
00408 }
00409
00410
00411
00412
00413
00414 void client_map(xcb_connection_t *conn, Client *client) {
00415
00416
00417 long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
00418 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
00419
00420 xcb_map_window(conn, client->frame);
00421 }
00422
00423
00424
00425
00426
00427
00428 void client_mark(xcb_connection_t *conn, Client *client, const char *mark) {
00429 if (client->mark != NULL)
00430 free(client->mark);
00431 client->mark = sstrdup(mark);
00432
00433
00434 Client *current;
00435 Workspace *ws;
00436 TAILQ_FOREACH(ws, workspaces, workspaces)
00437 SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) {
00438 if (current == client ||
00439 current->mark == NULL ||
00440 strcmp(current->mark, mark) != 0)
00441 continue;
00442
00443 free(current->mark);
00444 current->mark = NULL;
00445
00446
00447 break;
00448 }
00449 }
00450
00451
00452
00453
00454
00455
00456
00457 uint32_t client_min_height(Client *client) {
00458 uint32_t height = max(2, client->base_height);
00459 i3Font *font = load_font(global_conn, config.font);
00460
00461 if (client->titlebar_position == TITLEBAR_OFF && client->borderless)
00462 return height;
00463
00464 if (client->titlebar_position == TITLEBAR_OFF && !client->borderless)
00465 return height + 2;
00466
00467 return height + font->height + 2 + 2;
00468 }
00469
00470
00471
00472
00473
00474 uint32_t client_min_width(Client *client) {
00475 uint32_t width = max(2, client->base_width);
00476
00477 if (client->titlebar_position == TITLEBAR_OFF && client->borderless)
00478 return width;
00479
00480 if (client->titlebar_position == TITLEBAR_OFF && !client->borderless)
00481 return width + 2;
00482
00483 return width + 2 + 2;
00484 }