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
00017 #include <xcb/xcb.h>
00018 #include <xcb/xcb_event.h>
00019
00020 #include "i3.h"
00021 #include "config.h"
00022 #include "data.h"
00023 #include "util.h"
00024 #include "xcb.h"
00025 #include "debug.h"
00026 #include "layout.h"
00027 #include "client.h"
00028 #include "floating.h"
00029 #include "workspace.h"
00030 #include "log.h"
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 void toggle_floating_mode(xcb_connection_t *conn, Client *client, bool automatic) {
00042 Container *con = client->container;
00043 i3Font *font = load_font(conn, config.font);
00044
00045 if (client->dock) {
00046 DLOG("Not putting dock client into floating mode\n");
00047 return;
00048 }
00049
00050 if (con == NULL) {
00051 DLOG("This client is already in floating (container == NULL), re-inserting\n");
00052 Client *next_tiling;
00053 Workspace *ws = client->workspace;
00054 SLIST_FOREACH(next_tiling, &(ws->focus_stack), focus_clients)
00055 if (!client_is_floating(next_tiling))
00056 break;
00057
00058
00059 if (next_tiling == TAILQ_END(&(ws->focus_stack)))
00060 con = ws->table[0][0];
00061 else con = next_tiling->container;
00062
00063
00064 TAILQ_REMOVE(&(ws->floating_clients), client, floating_clients);
00065
00066 DLOG("destination container = %p\n", con);
00067 Client *old_focused = con->currently_focused;
00068
00069 memcpy(&(client->floating_rect), &(client->rect), sizeof(Rect));
00070
00071 client->floating = FLOATING_USER_OFF;
00072 client->container = con;
00073
00074 if (old_focused != NULL && !old_focused->dock)
00075 CIRCLEQ_INSERT_AFTER(&(con->clients), old_focused, client, clients);
00076 else CIRCLEQ_INSERT_TAIL(&(con->clients), client, clients);
00077
00078 DLOG("Re-inserted the window.\n");
00079 con->currently_focused = client;
00080
00081 client_set_below_floating(conn, client);
00082
00083 render_container(conn, con);
00084 xcb_flush(conn);
00085
00086 return;
00087 }
00088
00089 DLOG("Entering floating for client %08x\n", client->child);
00090
00091
00092 client_remove_from_container(conn, client, con, false);
00093 client->container = NULL;
00094
00095
00096 TAILQ_INSERT_TAIL(&(client->workspace->floating_clients), client, floating_clients);
00097
00098 if (con->currently_focused == client) {
00099 DLOG("Need to re-adjust currently_focused\n");
00100
00101 con->currently_focused = get_last_focused_client(conn, con, NULL);
00102 }
00103
00104 if (automatic)
00105 client->floating = FLOATING_AUTO_ON;
00106 else client->floating = FLOATING_USER_ON;
00107
00108
00109
00110 if (client->floating_rect.x == -1) {
00111
00112 client->floating_rect.x = client->rect.x;
00113 client->floating_rect.y = client->rect.y;
00114
00115
00116 client->child_rect.width = client->floating_rect.width;
00117 client->child_rect.height = client->floating_rect.height;
00118
00119 client->rect.width = client->child_rect.width + 2 + 2;
00120 client->rect.height = client->child_rect.height + (font->height + 2 + 2) + 2;
00121
00122 DLOG("copying size from tiling (%d, %d) size (%d, %d)\n", client->floating_rect.x, client->floating_rect.y,
00123 client->floating_rect.width, client->floating_rect.height);
00124 } else {
00125
00126 DLOG("using: (%d, %d) size (%d, %d)\n", client->floating_rect.x, client->floating_rect.y,
00127 client->floating_rect.width, client->floating_rect.height);
00128 memcpy(&(client->rect), &(client->floating_rect), sizeof(Rect));
00129 }
00130
00131
00132 xcb_raise_window(conn, client->frame);
00133 reposition_client(conn, client);
00134 resize_client(conn, client);
00135
00136 redecorate_window(conn, client);
00137
00138
00139 render_container(conn, con);
00140 xcb_flush(conn);
00141 }
00142
00143
00144
00145
00146
00147
00148
00149 void floating_assign_to_workspace(Client *client, Workspace *new_workspace) {
00150
00151 SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients);
00152 TAILQ_REMOVE(&(client->workspace->floating_clients), client, floating_clients);
00153
00154 if (client->workspace->fullscreen_client == client)
00155 client->workspace->fullscreen_client = NULL;
00156
00157
00158 client->workspace = new_workspace;
00159 SLIST_INSERT_HEAD(&(client->workspace->focus_stack), client, focus_clients);
00160 TAILQ_INSERT_TAIL(&(client->workspace->floating_clients), client, floating_clients);
00161 if (client->fullscreen)
00162 client->workspace->fullscreen_client = client;
00163 }
00164
00165
00166
00167
00168
00169
00170
00171
00172 struct resize_callback_params {
00173 border_t border;
00174 xcb_button_press_event_t *event;
00175 };
00176
00177 DRAGGING_CB(resize_callback) {
00178 struct resize_callback_params *params = extra;
00179 xcb_button_press_event_t *event = params->event;
00180 switch (params->border) {
00181 case BORDER_RIGHT: {
00182 int new_width = old_rect->width + (new_x - event->root_x);
00183 if ((new_width < 0) ||
00184 (new_width < client_min_width(client) && client->rect.width >= new_width))
00185 return;
00186 client->rect.width = new_width;
00187 break;
00188 }
00189
00190 case BORDER_BOTTOM: {
00191 int new_height = old_rect->height + (new_y - event->root_y);
00192 if ((new_height < 0) ||
00193 (new_height < client_min_height(client) && client->rect.height >= new_height))
00194 return;
00195 client->rect.height = old_rect->height + (new_y - event->root_y);
00196 break;
00197 }
00198
00199 case BORDER_TOP: {
00200 int new_height = old_rect->height + (event->root_y - new_y);
00201 if ((new_height < 0) ||
00202 (new_height < client_min_height(client) && client->rect.height >= new_height))
00203 return;
00204
00205 client->rect.y = old_rect->y + (new_y - event->root_y);
00206 client->rect.height = new_height;
00207 break;
00208 }
00209
00210 case BORDER_LEFT: {
00211 int new_width = old_rect->width + (event->root_x - new_x);
00212 if ((new_width < 0) ||
00213 (new_width < client_min_width(client) && client->rect.width >= new_width))
00214 return;
00215 client->rect.x = old_rect->x + (new_x - event->root_x);
00216 client->rect.width = new_width;
00217 break;
00218 }
00219 }
00220
00221
00222 reposition_client(conn, client);
00223 resize_client(conn, client);
00224 xcb_flush(conn);
00225 }
00226
00227
00228
00229
00230
00231
00232
00233
00234 int floating_border_click(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event) {
00235 DLOG("floating border click\n");
00236
00237 border_t border;
00238
00239 if (event->event_y < 2)
00240 border = BORDER_TOP;
00241 else if (event->event_y >= (client->rect.height - 2))
00242 border = BORDER_BOTTOM;
00243 else if (event->event_x <= 2)
00244 border = BORDER_LEFT;
00245 else if (event->event_x >= (client->rect.width - 2))
00246 border = BORDER_RIGHT;
00247 else {
00248 DLOG("Not on any border, not doing anything.\n");
00249 return 1;
00250 }
00251
00252 DLOG("border = %d\n", border);
00253
00254 struct resize_callback_params params = { border, event };
00255
00256 drag_pointer(conn, client, event, XCB_NONE, border, resize_callback, ¶ms);
00257
00258 return 1;
00259 }
00260
00261 DRAGGING_CB(drag_window_callback) {
00262 struct xcb_button_press_event_t *event = extra;
00263
00264
00265 client->rect.x = old_rect->x + (new_x - event->root_x);
00266 client->rect.y = old_rect->y + (new_y - event->root_y);
00267 reposition_client(conn, client);
00268
00269
00270 fake_absolute_configure_notify(conn, client);
00271
00272 }
00273
00274
00275
00276
00277
00278
00279 void floating_drag_window(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event) {
00280 DLOG("floating_drag_window\n");
00281
00282 drag_pointer(conn, client, event, XCB_NONE, BORDER_TOP , drag_window_callback, event);
00283 }
00284
00285
00286
00287
00288
00289
00290
00291
00292 struct resize_window_callback_params {
00293 border_t corner;
00294 bool proportional;
00295 xcb_button_press_event_t *event;
00296 };
00297
00298 DRAGGING_CB(resize_window_callback) {
00299 struct resize_window_callback_params *params = extra;
00300 xcb_button_press_event_t *event = params->event;
00301 border_t corner = params->corner;
00302
00303 int32_t dest_x = client->rect.x;
00304 int32_t dest_y = client->rect.y;
00305 uint32_t dest_width;
00306 uint32_t dest_height;
00307
00308 double ratio = (double) old_rect->width / old_rect->height;
00309
00310
00311
00312 if (corner & BORDER_LEFT)
00313 dest_width = old_rect->width - (new_x - event->root_x);
00314 else dest_width = old_rect->width + (new_x - event->root_x);
00315
00316 if (corner & BORDER_TOP)
00317 dest_height = old_rect->height - (new_y - event->root_y);
00318 else dest_height = old_rect->height + (new_y - event->root_y);
00319
00320
00321 dest_width = max(dest_width, client_min_width(client));
00322 dest_height = max(dest_height, client_min_height(client));
00323
00324
00325 if (params->proportional) {
00326 dest_width = max(dest_width, (int) (dest_height * ratio));
00327 dest_height = max(dest_height, (int) (dest_width / ratio));
00328 }
00329
00330
00331
00332 if (corner & BORDER_LEFT)
00333 dest_x = old_rect->x + (old_rect->width - dest_width);
00334
00335 if (corner & BORDER_TOP)
00336 dest_y = old_rect->y + (old_rect->height - dest_height);
00337
00338 client->rect = (Rect) { dest_x, dest_y, dest_width, dest_height };
00339
00340
00341 resize_client(conn, client);
00342 }
00343
00344
00345
00346
00347
00348
00349
00350 void floating_resize_window(xcb_connection_t *conn, Client *client,
00351 bool proportional, xcb_button_press_event_t *event) {
00352 DLOG("floating_resize_window\n");
00353
00354
00355
00356 border_t corner = 0;
00357
00358 if (event->event_x <= (client->rect.width / 2))
00359 corner |= BORDER_LEFT;
00360 else corner |= BORDER_RIGHT;
00361
00362 if (event->event_y <= (client->rect.height / 2))
00363 corner |= BORDER_TOP;
00364 else corner |= BORDER_RIGHT;
00365
00366 struct resize_window_callback_params params = { corner, proportional, event };
00367
00368 drag_pointer(conn, client, event, XCB_NONE, BORDER_TOP , resize_window_callback, ¶ms);
00369 }
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380 void drag_pointer(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event,
00381 xcb_window_t confine_to, border_t border, callback_t callback, void *extra) {
00382 xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
00383 uint32_t new_x, new_y;
00384 Rect old_rect;
00385 if (client != NULL)
00386 memcpy(&old_rect, &(client->rect), sizeof(Rect));
00387
00388
00389
00390 xcb_grab_pointer(conn,
00391 false,
00392 root,
00393 XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION,
00394 XCB_GRAB_MODE_ASYNC,
00395 XCB_GRAB_MODE_ASYNC,
00396 confine_to,
00397 XCB_NONE,
00398 XCB_CURRENT_TIME);
00399
00400
00401 xcb_flush(conn);
00402
00403 xcb_generic_event_t *inside_event, *last_motion_notify = NULL;
00404
00405 while ((inside_event = xcb_wait_for_event(conn))) {
00406
00407 do {
00408
00409 int nr = inside_event->response_type;
00410 if (nr == 0) {
00411
00412 handle_event(NULL, conn, inside_event);
00413 free(inside_event);
00414 continue;
00415 }
00416 assert(nr < 256);
00417 nr &= XCB_EVENT_RESPONSE_TYPE_MASK;
00418 assert(nr >= 2);
00419
00420 switch (nr) {
00421 case XCB_BUTTON_RELEASE:
00422 goto done;
00423
00424 case XCB_MOTION_NOTIFY:
00425
00426 FREE(last_motion_notify);
00427 last_motion_notify = inside_event;
00428 break;
00429
00430 case XCB_UNMAP_NOTIFY:
00431 DLOG("Unmap-notify, aborting\n");
00432 xcb_event_handle(&evenths, inside_event);
00433 goto done;
00434
00435 default:
00436 DLOG("Passing to original handler\n");
00437
00438 xcb_event_handle(&evenths, inside_event);
00439 break;
00440 }
00441 if (last_motion_notify != inside_event)
00442 free(inside_event);
00443 } while ((inside_event = xcb_poll_for_event(conn)) != NULL);
00444
00445 if (last_motion_notify == NULL)
00446 continue;
00447
00448 new_x = ((xcb_motion_notify_event_t*)last_motion_notify)->root_x;
00449 new_y = ((xcb_motion_notify_event_t*)last_motion_notify)->root_y;
00450
00451 callback(conn, client, &old_rect, new_x, new_y, extra);
00452 FREE(last_motion_notify);
00453 }
00454 done:
00455 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
00456 xcb_flush(conn);
00457 }
00458
00459
00460
00461
00462
00463
00464
00465
00466 void floating_focus_direction(xcb_connection_t *conn, Client *currently_focused, direction_t direction) {
00467 DLOG("floating focus\n");
00468
00469 if (direction == D_LEFT || direction == D_RIGHT) {
00470
00471 Client *client;
00472
00473 while ((client = (direction == D_LEFT ? TAILQ_PREV(currently_focused, floating_clients_head, floating_clients) :
00474 TAILQ_NEXT(currently_focused, floating_clients))) !=
00475 TAILQ_END(&(currently_focused->workspace->floating_clients))) {
00476 if (!client->floating)
00477 continue;
00478 set_focus(conn, client, true);
00479 return;
00480 }
00481 }
00482 }
00483
00484
00485
00486
00487
00488 void floating_move(xcb_connection_t *conn, Client *currently_focused, direction_t direction) {
00489 DLOG("floating move\n");
00490
00491 if (currently_focused->fullscreen) {
00492 DLOG("Cannot move fullscreen windows\n");
00493 return;
00494 }
00495
00496 Rect destination = currently_focused->rect;
00497 Rect *screen = &(currently_focused->workspace->output->rect);
00498
00499 switch (direction) {
00500 case D_LEFT:
00501 destination.x -= 10;
00502 break;
00503 case D_RIGHT:
00504 destination.x += 10;
00505 break;
00506 case D_UP:
00507 destination.y -= 10;
00508 break;
00509 case D_DOWN:
00510 destination.y += 10;
00511 break;
00512
00513 default:
00514 break;
00515 }
00516
00517
00518 if ((int32_t)(destination.x + destination.width - 5) <= (int32_t)screen->x ||
00519 (int32_t)(destination.x + 5) >= (int32_t)(screen->x + screen->width) ||
00520 (int32_t)(destination.y + destination.height - 5) <= (int32_t)screen->y ||
00521 (int32_t)(destination.y + 5) >= (int32_t)(screen->y + screen->height)) {
00522 DLOG("boundary check failed, not moving\n");
00523 return;
00524 }
00525
00526 currently_focused->rect = destination;
00527 reposition_client(conn, currently_focused);
00528
00529
00530
00531 fake_absolute_configure_notify(conn, currently_focused);
00532
00533 }
00534
00535
00536
00537
00538
00539
00540 void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace) {
00541 Client *client;
00542
00543 workspace->floating_hidden = !workspace->floating_hidden;
00544 DLOG("floating_hidden is now: %d\n", workspace->floating_hidden);
00545 TAILQ_FOREACH(client, &(workspace->floating_clients), floating_clients) {
00546 if (workspace->floating_hidden)
00547 client_unmap(conn, client);
00548 else client_map(conn, client);
00549 }
00550
00551
00552
00553 if (workspace->floating_hidden)
00554 SLIST_FOREACH(client, &(workspace->focus_stack), focus_clients) {
00555 if (client_is_floating(client))
00556 continue;
00557 set_focus(conn, client, true);
00558 return;
00559 }
00560
00561 xcb_flush(conn);
00562 }