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 fake_configure_notify(conn, r, client->child);
00232
00233 xcb_flush(conn);
00234 }
00235
00236
00237
00238
00239
00240 void client_leave_fullscreen(xcb_connection_t *conn, Client *client) {
00241 LOG("leaving fullscreen mode\n");
00242 client->fullscreen = false;
00243 Workspace *ws;
00244 TAILQ_FOREACH(ws, workspaces, workspaces)
00245 if (ws->fullscreen_client == client)
00246 ws->fullscreen_client = NULL;
00247
00248 if (client_is_floating(client)) {
00249
00250
00251 reposition_client(conn, client);
00252 resize_client(conn, client);
00253
00254 redecorate_window(conn, client);
00255 } else {
00256 client_set_below_floating(conn, client);
00257
00258
00259
00260 client->force_reconfigure = true;
00261
00262 render_layout(conn);
00263 }
00264
00265 xcb_flush(conn);
00266 }
00267
00268
00269
00270
00271
00272
00273
00274 void client_toggle_fullscreen(xcb_connection_t *conn, Client *client) {
00275
00276 assert(!client->dock);
00277
00278 if (!client->fullscreen) {
00279 client_enter_fullscreen(conn, client, false);
00280 } else {
00281 client_leave_fullscreen(conn, client);
00282 }
00283 }
00284
00285
00286
00287
00288
00289 void client_toggle_fullscreen_global(xcb_connection_t *conn, Client *client) {
00290
00291 assert(!client->dock);
00292
00293 if (!client->fullscreen) {
00294 client_enter_fullscreen(conn, client, true);
00295 } else {
00296 client_leave_fullscreen(conn, client);
00297 }
00298 }
00299
00300
00301
00302
00303
00304
00305
00306 void client_set_below_floating(xcb_connection_t *conn, Client *client) {
00307
00308 Workspace *ws = client->workspace;
00309 Client *first_floating = TAILQ_FIRST(&(ws->floating_clients));
00310 if (first_floating == TAILQ_END(&(ws->floating_clients)))
00311 return;
00312
00313 DLOG("Setting below floating\n");
00314 uint32_t values[] = { first_floating->frame, XCB_STACK_MODE_BELOW };
00315 xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
00316
00317 if (client->workspace->fullscreen_client == NULL)
00318 return;
00319
00320 DLOG("(and below fullscreen)\n");
00321
00322 values[0] = client->workspace->fullscreen_client->frame;
00323 xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
00324 }
00325
00326
00327
00328
00329
00330
00331
00332 bool client_is_floating(Client *client) {
00333 return (client->floating >= FLOATING_AUTO_ON);
00334 }
00335
00336
00337
00338
00339
00340
00341
00342 bool client_init_border(xcb_connection_t *conn, Client *client, char border_type) {
00343 switch (border_type) {
00344 case 'n':
00345 LOG("Changing to normal border\n");
00346 client->titlebar_position = TITLEBAR_TOP;
00347 client->borderless = false;
00348 return true;
00349 case 'p':
00350 LOG("Changing to 1px border\n");
00351 client->titlebar_position = TITLEBAR_OFF;
00352 client->borderless = false;
00353 return true;
00354 case 'b':
00355 LOG("Changing to borderless\n");
00356 client->titlebar_position = TITLEBAR_OFF;
00357 client->borderless = true;
00358 return true;
00359 default:
00360 LOG("Unknown border mode\n");
00361 return false;
00362 }
00363 }
00364
00365
00366
00367
00368
00369
00370 void client_change_border(xcb_connection_t *conn, Client *client, char border_type) {
00371 if (!client_init_border(conn, client, border_type))
00372 return;
00373
00374
00375 client->force_reconfigure = true;
00376
00377
00378 if (client->container != NULL)
00379 render_container(conn, client->container);
00380 else {
00381
00382 if (client_is_floating(client))
00383 resize_client(conn, client);
00384
00385 else render_layout(conn);
00386 }
00387
00388 redecorate_window(conn, client);
00389 }
00390
00391
00392
00393
00394
00395 void client_unmap(xcb_connection_t *conn, Client *client) {
00396
00397 long data[] = { XCB_WM_STATE_WITHDRAWN, XCB_NONE };
00398 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
00399
00400 xcb_unmap_window(conn, client->frame);
00401 }
00402
00403
00404
00405
00406
00407 void client_map(xcb_connection_t *conn, Client *client) {
00408
00409
00410 long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
00411 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
00412
00413 xcb_map_window(conn, client->frame);
00414 }
00415
00416
00417
00418
00419
00420
00421 void client_mark(xcb_connection_t *conn, Client *client, const char *mark) {
00422 if (client->mark != NULL)
00423 free(client->mark);
00424 client->mark = sstrdup(mark);
00425
00426
00427 Client *current;
00428 Workspace *ws;
00429 TAILQ_FOREACH(ws, workspaces, workspaces)
00430 SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) {
00431 if (current == client ||
00432 current->mark == NULL ||
00433 strcmp(current->mark, mark) != 0)
00434 continue;
00435
00436 free(current->mark);
00437 current->mark = NULL;
00438
00439
00440 break;
00441 }
00442 }
00443
00444
00445
00446
00447
00448
00449
00450 uint32_t client_min_height(Client *client) {
00451 uint32_t height = max(2, client->base_height);
00452 i3Font *font = load_font(global_conn, config.font);
00453
00454 if (client->titlebar_position == TITLEBAR_OFF && client->borderless)
00455 return height;
00456
00457 if (client->titlebar_position == TITLEBAR_OFF && !client->borderless)
00458 return height + 2;
00459
00460 return height + font->height + 2 + 2;
00461 }
00462
00463
00464
00465
00466
00467 uint32_t client_min_width(Client *client) {
00468 uint32_t width = max(2, client->base_width);
00469
00470 if (client->titlebar_position == TITLEBAR_OFF && client->borderless)
00471 return width;
00472
00473 if (client->titlebar_position == TITLEBAR_OFF && !client->borderless)
00474 return width + 2;
00475
00476 return width + 2 + 2;
00477 }