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 uint32_t new_x, new_y;
00383 Rect old_rect;
00384 if (client != NULL)
00385 memcpy(&old_rect, &(client->rect), sizeof(Rect));
00386
00387
00388
00389 xcb_grab_pointer(conn,
00390 false,
00391 root,
00392 XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION,
00393 XCB_GRAB_MODE_ASYNC,
00394 XCB_GRAB_MODE_ASYNC,
00395 confine_to,
00396 XCB_NONE,
00397 XCB_CURRENT_TIME);
00398
00399
00400 xcb_flush(conn);
00401
00402 xcb_generic_event_t *inside_event, *last_motion_notify = NULL;
00403
00404 while ((inside_event = xcb_wait_for_event(conn))) {
00405
00406 do {
00407
00408 int nr = inside_event->response_type;
00409 if (nr == 0) {
00410
00411 handle_event(NULL, conn, inside_event);
00412 free(inside_event);
00413 continue;
00414 }
00415 assert(nr < 256);
00416 nr &= XCB_EVENT_RESPONSE_TYPE_MASK;
00417 assert(nr >= 2);
00418
00419 switch (nr) {
00420 case XCB_BUTTON_RELEASE:
00421 goto done;
00422
00423 case XCB_MOTION_NOTIFY:
00424
00425 FREE(last_motion_notify);
00426 last_motion_notify = inside_event;
00427 break;
00428
00429 case XCB_UNMAP_NOTIFY:
00430 DLOG("Unmap-notify, aborting\n");
00431 xcb_event_handle(&evenths, inside_event);
00432 goto done;
00433
00434 default:
00435 DLOG("Passing to original handler\n");
00436
00437 xcb_event_handle(&evenths, inside_event);
00438 break;
00439 }
00440 if (last_motion_notify != inside_event)
00441 free(inside_event);
00442 } while ((inside_event = xcb_poll_for_event(conn)) != NULL);
00443
00444 if (last_motion_notify == NULL)
00445 continue;
00446
00447 new_x = ((xcb_motion_notify_event_t*)last_motion_notify)->root_x;
00448 new_y = ((xcb_motion_notify_event_t*)last_motion_notify)->root_y;
00449
00450 callback(conn, client, &old_rect, new_x, new_y, extra);
00451 FREE(last_motion_notify);
00452 }
00453 done:
00454 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
00455 xcb_flush(conn);
00456 }
00457
00458
00459
00460
00461
00462
00463
00464
00465 void floating_focus_direction(xcb_connection_t *conn, Client *currently_focused, direction_t direction) {
00466 DLOG("floating focus\n");
00467
00468 if (direction == D_LEFT || direction == D_RIGHT) {
00469
00470 Client *client;
00471
00472 while ((client = (direction == D_LEFT ? TAILQ_PREV(currently_focused, floating_clients_head, floating_clients) :
00473 TAILQ_NEXT(currently_focused, floating_clients))) !=
00474 TAILQ_END(&(currently_focused->workspace->floating_clients))) {
00475 if (!client->floating)
00476 continue;
00477 set_focus(conn, client, true);
00478 return;
00479 }
00480 }
00481 }
00482
00483
00484
00485
00486
00487 void floating_move(xcb_connection_t *conn, Client *currently_focused, direction_t direction) {
00488 DLOG("floating move\n");
00489
00490 if (currently_focused->fullscreen) {
00491 DLOG("Cannot move fullscreen windows\n");
00492 return;
00493 }
00494
00495 Rect destination = currently_focused->rect;
00496 Rect *screen = &(currently_focused->workspace->output->rect);
00497
00498 switch (direction) {
00499 case D_LEFT:
00500 destination.x -= 10;
00501 break;
00502 case D_RIGHT:
00503 destination.x += 10;
00504 break;
00505 case D_UP:
00506 destination.y -= 10;
00507 break;
00508 case D_DOWN:
00509 destination.y += 10;
00510 break;
00511
00512 default:
00513 break;
00514 }
00515
00516
00517 if ((int32_t)(destination.x + destination.width - 5) <= (int32_t)screen->x ||
00518 (int32_t)(destination.x + 5) >= (int32_t)(screen->x + screen->width) ||
00519 (int32_t)(destination.y + destination.height - 5) <= (int32_t)screen->y ||
00520 (int32_t)(destination.y + 5) >= (int32_t)(screen->y + screen->height)) {
00521 DLOG("boundary check failed, not moving\n");
00522 return;
00523 }
00524
00525 currently_focused->rect = destination;
00526 reposition_client(conn, currently_focused);
00527
00528
00529
00530 fake_absolute_configure_notify(conn, currently_focused);
00531
00532 }
00533
00534
00535
00536
00537
00538
00539 void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace) {
00540 Client *client;
00541
00542 workspace->floating_hidden = !workspace->floating_hidden;
00543 DLOG("floating_hidden is now: %d\n", workspace->floating_hidden);
00544 TAILQ_FOREACH(client, &(workspace->floating_clients), floating_clients) {
00545 if (workspace->floating_hidden)
00546 client_unmap(conn, client);
00547 else client_map(conn, client);
00548 }
00549
00550
00551
00552 if (workspace->floating_hidden)
00553 SLIST_FOREACH(client, &(workspace->focus_stack), focus_clients) {
00554 if (client_is_floating(client))
00555 continue;
00556 set_focus(conn, client, true);
00557 return;
00558 }
00559
00560 xcb_flush(conn);
00561 }