i3
|
00001 /* 00002 * vim:ts=4:sw=4:expandtab 00003 * 00004 * i3 - an improved dynamic tiling window manager 00005 * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) 00006 * 00007 * floating.c: Floating windows. 00008 * 00009 */ 00010 #include "all.h" 00011 00012 extern xcb_connection_t *conn; 00013 00014 /* 00015 * Calculates sum of heights and sum of widths of all currently active outputs 00016 * 00017 */ 00018 static Rect total_outputs_dimensions(void) { 00019 Output *output; 00020 /* Use Rect to encapsulate dimensions, ignoring x/y */ 00021 Rect outputs_dimensions = {0, 0, 0, 0}; 00022 TAILQ_FOREACH(output, &outputs, outputs) { 00023 outputs_dimensions.height += output->rect.height; 00024 outputs_dimensions.width += output->rect.width; 00025 } 00026 return outputs_dimensions; 00027 } 00028 00029 void floating_enable(Con *con, bool automatic) { 00030 bool set_focus = (con == focused); 00031 00032 if (con->parent && con->parent->type == CT_DOCKAREA) { 00033 LOG("Container is a dock window, not enabling floating mode.\n"); 00034 return; 00035 } 00036 00037 if (con_is_floating(con)) { 00038 LOG("Container is already in floating mode, not doing anything.\n"); 00039 return; 00040 } 00041 00042 /* 1: If the container is a workspace container, we need to create a new 00043 * split-container with the same orientation and make that one floating. We 00044 * cannot touch the workspace container itself because floating containers 00045 * are children of the workspace. */ 00046 if (con->type == CT_WORKSPACE) { 00047 LOG("This is a workspace, creating new container around content\n"); 00048 if (con_num_children(con) == 0) { 00049 LOG("Workspace is empty, aborting\n"); 00050 return; 00051 } 00052 /* TODO: refactor this with src/con.c:con_set_layout */ 00053 Con *new = con_new(NULL, NULL); 00054 new->parent = con; 00055 new->orientation = con->orientation; 00056 00057 /* since the new container will be set into floating mode directly 00058 * afterwards, we need to copy the workspace rect. */ 00059 memcpy(&(new->rect), &(con->rect), sizeof(Rect)); 00060 00061 Con *old_focused = TAILQ_FIRST(&(con->focus_head)); 00062 if (old_focused == TAILQ_END(&(con->focus_head))) 00063 old_focused = NULL; 00064 00065 /* 4: move the existing cons of this workspace below the new con */ 00066 DLOG("Moving cons\n"); 00067 Con *child; 00068 while (!TAILQ_EMPTY(&(con->nodes_head))) { 00069 child = TAILQ_FIRST(&(con->nodes_head)); 00070 con_detach(child); 00071 con_attach(child, new, true); 00072 } 00073 00074 /* 4: attach the new split container to the workspace */ 00075 DLOG("Attaching new split to ws\n"); 00076 con_attach(new, con, false); 00077 00078 if (old_focused) 00079 con_focus(old_focused); 00080 00081 con = new; 00082 set_focus = false; 00083 } 00084 00085 /* 1: detach the container from its parent */ 00086 /* TODO: refactor this with tree_close() */ 00087 TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes); 00088 TAILQ_REMOVE(&(con->parent->focus_head), con, focused); 00089 00090 con_fix_percent(con->parent); 00091 00092 /* 2: create a new container to render the decoration on, add 00093 * it as a floating window to the workspace */ 00094 Con *nc = con_new(NULL, NULL); 00095 /* we need to set the parent afterwards instead of passing it as an 00096 * argument to con_new() because nc would be inserted into the tiling layer 00097 * otherwise. */ 00098 Con *ws = con_get_workspace(con); 00099 nc->parent = ws; 00100 nc->orientation = NO_ORIENTATION; 00101 nc->type = CT_FLOATING_CON; 00102 /* We insert nc already, even though its rect is not yet calculated. This 00103 * is necessary because otherwise the workspace might be empty (and get 00104 * closed in tree_close()) even though it’s not. */ 00105 TAILQ_INSERT_TAIL(&(ws->floating_head), nc, floating_windows); 00106 TAILQ_INSERT_TAIL(&(ws->focus_head), nc, focused); 00107 00108 /* check if the parent container is empty and close it if so */ 00109 if ((con->parent->type == CT_CON || con->parent->type == CT_FLOATING_CON) && 00110 con_num_children(con->parent) == 0) { 00111 DLOG("Old container empty after setting this child to floating, closing\n"); 00112 tree_close(con->parent, DONT_KILL_WINDOW, false, false); 00113 } 00114 00115 char *name; 00116 sasprintf(&name, "[i3 con] floatingcon around %p", con); 00117 x_set_name(nc, name); 00118 free(name); 00119 00120 /* find the height for the decorations */ 00121 int deco_height = config.font.height + 5; 00122 00123 DLOG("Original rect: (%d, %d) with %d x %d\n", con->rect.x, con->rect.y, con->rect.width, con->rect.height); 00124 DLOG("Geometry = (%d, %d) with %d x %d\n", con->geometry.x, con->geometry.y, con->geometry.width, con->geometry.height); 00125 Rect zero = { 0, 0, 0, 0 }; 00126 nc->rect = con->geometry; 00127 /* If the geometry was not set (split containers), we need to determine a 00128 * sensible one by combining the geometry of all children */ 00129 if (memcmp(&(nc->rect), &zero, sizeof(Rect)) == 0) { 00130 DLOG("Geometry not set, combining children\n"); 00131 Con *child; 00132 TAILQ_FOREACH(child, &(con->nodes_head), nodes) { 00133 DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height); 00134 nc->rect.width += child->geometry.width; 00135 nc->rect.height = max(nc->rect.height, child->geometry.height); 00136 } 00137 } 00138 00139 /* Define reasonable minimal and maximal sizes for floating windows */ 00140 const int floating_sane_min_height = 50; 00141 const int floating_sane_min_width = 75; 00142 00143 Rect floating_sane_max_dimensions; 00144 floating_sane_max_dimensions = total_outputs_dimensions(); 00145 00146 /* Unless user requests otherwise (-1), ensure width/height do not exceed 00147 * configured maxima or, if unconfigured, limit to combined width of all 00148 * outputs */ 00149 if (config.floating_maximum_height != -1) { 00150 if (config.floating_maximum_height == 0) 00151 nc->rect.height = min(nc->rect.height, floating_sane_max_dimensions.height); 00152 else 00153 nc->rect.height = min(nc->rect.height, config.floating_maximum_height); 00154 } 00155 if (config.floating_maximum_width != -1) { 00156 if (config.floating_maximum_width == 0) 00157 nc->rect.width = min(nc->rect.width, floating_sane_max_dimensions.width); 00158 else 00159 nc->rect.width = min(nc->rect.width, config.floating_maximum_width); 00160 } 00161 00162 /* Unless user requests otherwise (-1), raise the width/height to 00163 * reasonable minimum dimensions */ 00164 if (config.floating_minimum_height != -1) { 00165 if (config.floating_minimum_height == 0) 00166 nc->rect.height = max(nc->rect.height, floating_sane_min_height); 00167 else 00168 nc->rect.height = max(nc->rect.height, config.floating_minimum_height); 00169 } 00170 if (config.floating_minimum_width != -1) { 00171 if (config.floating_minimum_width == 0) 00172 nc->rect.width = max(nc->rect.width, floating_sane_min_width); 00173 else 00174 nc->rect.width = max(nc->rect.width, config.floating_minimum_width); 00175 } 00176 00177 /* add pixels for the decoration */ 00178 /* TODO: don’t add them when the user automatically puts new windows into 00179 * 1pixel/borderless mode */ 00180 nc->rect.height += deco_height + 2; 00181 nc->rect.width += 4; 00182 00183 /* Honor the X11 border */ 00184 nc->rect.height += con->border_width * 2; 00185 nc->rect.width += con->border_width * 2; 00186 00187 /* Some clients (like GIMP’s color picker window) get mapped 00188 * to (0, 0), so we push them to a reasonable position 00189 * (centered over their leader) */ 00190 if (nc->rect.x == 0 && nc->rect.y == 0) { 00191 Con *leader; 00192 if (con->window && con->window->leader != XCB_NONE && 00193 (leader = con_by_window_id(con->window->leader)) != NULL) { 00194 DLOG("Centering above leader\n"); 00195 nc->rect.x = leader->rect.x + (leader->rect.width / 2) - (nc->rect.width / 2); 00196 nc->rect.y = leader->rect.y + (leader->rect.height / 2) - (nc->rect.height / 2); 00197 } else { 00198 /* center the window on workspace as fallback */ 00199 nc->rect.x = ws->rect.x + (ws->rect.width / 2) - (nc->rect.width / 2); 00200 nc->rect.y = ws->rect.y + (ws->rect.height / 2) - (nc->rect.height / 2); 00201 } 00202 } 00203 00204 /* Sanity check: Are the coordinates on the appropriate output? If not, we 00205 * need to change them */ 00206 Output *current_output = get_output_containing(nc->rect.x, nc->rect.y); 00207 Con *correct_output = con_get_output(ws); 00208 if (!current_output || current_output->con != correct_output) { 00209 DLOG("This floating window is on the wrong output, fixing coordinates (currently (%d, %d))\n", 00210 nc->rect.x, nc->rect.y); 00211 /* Take the relative coordinates of the current output, then add them 00212 * to the coordinate space of the correct output */ 00213 uint32_t rel_x = (nc->rect.x - (current_output ? current_output->con->rect.x : 0)); 00214 uint32_t rel_y = (nc->rect.y - (current_output ? current_output->con->rect.y : 0)); 00215 nc->rect.x = correct_output->rect.x + rel_x; 00216 nc->rect.y = correct_output->rect.y + rel_y; 00217 } 00218 00219 DLOG("Floating rect: (%d, %d) with %d x %d\n", nc->rect.x, nc->rect.y, nc->rect.width, nc->rect.height); 00220 00221 /* 3: attach the child to the new parent container */ 00222 con->parent = nc; 00223 con->percent = 1.0; 00224 con->floating = FLOATING_USER_ON; 00225 00226 /* 4: set the border style as specified with new_float */ 00227 if (automatic) 00228 con->border_style = config.default_floating_border; 00229 00230 /* 5: Subtract the deco_height in order to make the floating window appear 00231 * at precisely the position it specified in its original geometry (which 00232 * is what applications might remember). */ 00233 deco_height = (con->border_style == BS_NORMAL ? config.font.height + 5 : 0); 00234 nc->rect.y -= deco_height; 00235 00236 DLOG("Corrected y = %d (deco_height = %d)\n", nc->rect.y, deco_height); 00237 00238 TAILQ_INSERT_TAIL(&(nc->nodes_head), con, nodes); 00239 TAILQ_INSERT_TAIL(&(nc->focus_head), con, focused); 00240 00241 /* render the cons to get initial window_rect correct */ 00242 render_con(nc, false); 00243 render_con(con, false); 00244 00245 if (set_focus) 00246 con_focus(con); 00247 00248 /* Check if we need to re-assign it to a different workspace because of its 00249 * coordinates and exit if that was done successfully. */ 00250 if (floating_maybe_reassign_ws(nc)) 00251 return; 00252 00253 /* Sanitize coordinates: Check if they are on any output */ 00254 if (get_output_containing(nc->rect.x, nc->rect.y) != NULL) 00255 return; 00256 00257 ELOG("No output found at destination coordinates, centering floating window on current ws\n"); 00258 nc->rect.x = ws->rect.x + (ws->rect.width / 2) - (nc->rect.width / 2); 00259 nc->rect.y = ws->rect.y + (ws->rect.height / 2) - (nc->rect.height / 2); 00260 } 00261 00262 void floating_disable(Con *con, bool automatic) { 00263 if (!con_is_floating(con)) { 00264 LOG("Container isn't floating, not doing anything.\n"); 00265 return; 00266 } 00267 00268 Con *ws = con_get_workspace(con); 00269 00270 /* 1: detach from parent container */ 00271 TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes); 00272 TAILQ_REMOVE(&(con->parent->focus_head), con, focused); 00273 00274 /* 2: kill parent container */ 00275 TAILQ_REMOVE(&(con->parent->parent->floating_head), con->parent, floating_windows); 00276 TAILQ_REMOVE(&(con->parent->parent->focus_head), con->parent, focused); 00277 tree_close(con->parent, DONT_KILL_WINDOW, true, false); 00278 00279 /* 3: re-attach to the parent of the currently focused con on the workspace 00280 * this floating con was on */ 00281 Con *focused = con_descend_tiling_focused(ws); 00282 00283 /* if there is no other container on this workspace, focused will be the 00284 * workspace itself */ 00285 if (focused->type == CT_WORKSPACE) 00286 con->parent = focused; 00287 else con->parent = focused->parent; 00288 00289 /* con_fix_percent will adjust the percent value */ 00290 con->percent = 0.0; 00291 00292 con->floating = FLOATING_USER_OFF; 00293 00294 con_attach(con, con->parent, false); 00295 00296 con_fix_percent(con->parent); 00297 // TODO: don’t influence focus handling when Con was not focused before. 00298 con_focus(con); 00299 } 00300 00301 /* 00302 * Toggles floating mode for the given container. 00303 * 00304 * If the automatic flag is set to true, this was an automatic update by a change of the 00305 * window class from the application which can be overwritten by the user. 00306 * 00307 */ 00308 void toggle_floating_mode(Con *con, bool automatic) { 00309 /* see if the client is already floating */ 00310 if (con_is_floating(con)) { 00311 LOG("already floating, re-setting to tiling\n"); 00312 00313 floating_disable(con, automatic); 00314 return; 00315 } 00316 00317 floating_enable(con, automatic); 00318 } 00319 00320 /* 00321 * Raises the given container in the list of floating containers 00322 * 00323 */ 00324 void floating_raise_con(Con *con) { 00325 DLOG("Raising floating con %p / %s\n", con, con->name); 00326 TAILQ_REMOVE(&(con->parent->floating_head), con, floating_windows); 00327 TAILQ_INSERT_TAIL(&(con->parent->floating_head), con, floating_windows); 00328 } 00329 00330 /* 00331 * Checks if con’s coordinates are within its workspace and re-assigns it to 00332 * the actual workspace if not. 00333 * 00334 */ 00335 bool floating_maybe_reassign_ws(Con *con) { 00336 Output *output = get_output_containing( 00337 con->rect.x + (con->rect.width / 2), 00338 con->rect.y + (con->rect.height / 2)); 00339 00340 if (!output) { 00341 ELOG("No output found at destination coordinates?\n"); 00342 return false; 00343 } 00344 00345 if (con_get_output(con) == output->con) { 00346 DLOG("still the same ws\n"); 00347 return false; 00348 } 00349 00350 DLOG("Need to re-assign!\n"); 00351 00352 Con *content = output_get_content(output->con); 00353 Con *ws = TAILQ_FIRST(&(content->focus_head)); 00354 DLOG("Moving con %p / %s to workspace %p / %s\n", con, con->name, ws, ws->name); 00355 con_move_to_workspace(con, ws, false, true); 00356 con_focus(con_descend_focused(con)); 00357 return true; 00358 } 00359 00360 DRAGGING_CB(drag_window_callback) { 00361 const struct xcb_button_press_event_t *event = extra; 00362 00363 /* Reposition the client correctly while moving */ 00364 con->rect.x = old_rect->x + (new_x - event->root_x); 00365 con->rect.y = old_rect->y + (new_y - event->root_y); 00366 00367 render_con(con, false); 00368 x_push_node(con); 00369 xcb_flush(conn); 00370 00371 /* Check if we cross workspace boundaries while moving */ 00372 if (!floating_maybe_reassign_ws(con)) 00373 return; 00374 tree_render(); 00375 } 00376 00377 /* 00378 * Called when the user clicked on the titlebar of a floating window. 00379 * Calls the drag_pointer function with the drag_window callback 00380 * 00381 */ 00382 void floating_drag_window(Con *con, const xcb_button_press_event_t *event) { 00383 DLOG("floating_drag_window\n"); 00384 00385 /* Push changes before dragging, so that the window gets raised now and not 00386 * after the user releases the mouse button */ 00387 tree_render(); 00388 00389 /* Drag the window */ 00390 drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, drag_window_callback, event); 00391 tree_render(); 00392 } 00393 00394 /* 00395 * This is an ugly data structure which we need because there is no standard 00396 * way of having nested functions (only available as a gcc extension at the 00397 * moment, clang doesn’t support it) or blocks (only available as a clang 00398 * extension and only on Mac OS X systems at the moment). 00399 * 00400 */ 00401 struct resize_window_callback_params { 00402 const border_t corner; 00403 const bool proportional; 00404 const xcb_button_press_event_t *event; 00405 }; 00406 00407 DRAGGING_CB(resize_window_callback) { 00408 const struct resize_window_callback_params *params = extra; 00409 const xcb_button_press_event_t *event = params->event; 00410 border_t corner = params->corner; 00411 00412 int32_t dest_x = con->rect.x; 00413 int32_t dest_y = con->rect.y; 00414 uint32_t dest_width; 00415 uint32_t dest_height; 00416 00417 double ratio = (double) old_rect->width / old_rect->height; 00418 00419 /* First guess: We resize by exactly the amount the mouse moved, 00420 * taking into account in which corner the client was grabbed */ 00421 if (corner & BORDER_LEFT) 00422 dest_width = old_rect->width - (new_x - event->root_x); 00423 else dest_width = old_rect->width + (new_x - event->root_x); 00424 00425 if (corner & BORDER_TOP) 00426 dest_height = old_rect->height - (new_y - event->root_y); 00427 else dest_height = old_rect->height + (new_y - event->root_y); 00428 00429 /* Obey minimum window size */ 00430 Rect minimum = con_minimum_size(con); 00431 dest_width = max(dest_width, minimum.width); 00432 dest_height = max(dest_height, minimum.height); 00433 00434 /* User wants to keep proportions, so we may have to adjust our values */ 00435 if (params->proportional) { 00436 dest_width = max(dest_width, (int) (dest_height * ratio)); 00437 dest_height = max(dest_height, (int) (dest_width / ratio)); 00438 } 00439 00440 /* If not the lower right corner is grabbed, we must also reposition 00441 * the client by exactly the amount we resized it */ 00442 if (corner & BORDER_LEFT) 00443 dest_x = old_rect->x + (old_rect->width - dest_width); 00444 00445 if (corner & BORDER_TOP) 00446 dest_y = old_rect->y + (old_rect->height - dest_height); 00447 00448 con->rect = (Rect) { dest_x, dest_y, dest_width, dest_height }; 00449 00450 /* TODO: don’t re-render the whole tree just because we change 00451 * coordinates of a floating window */ 00452 tree_render(); 00453 x_push_changes(croot); 00454 } 00455 00456 /* 00457 * Called when the user clicked on a floating window while holding the 00458 * floating_modifier and the right mouse button. 00459 * Calls the drag_pointer function with the resize_window callback 00460 * 00461 */ 00462 void floating_resize_window(Con *con, const bool proportional, 00463 const xcb_button_press_event_t *event) { 00464 DLOG("floating_resize_window\n"); 00465 00466 /* corner saves the nearest corner to the original click. It contains 00467 * a bitmask of the nearest borders (BORDER_LEFT, BORDER_RIGHT, …) */ 00468 border_t corner = 0; 00469 00470 if (event->event_x <= (con->rect.width / 2)) 00471 corner |= BORDER_LEFT; 00472 else corner |= BORDER_RIGHT; 00473 00474 if (event->event_y <= (con->rect.height / 2)) 00475 corner |= BORDER_TOP; 00476 else corner |= BORDER_BOTTOM; 00477 00478 struct resize_window_callback_params params = { corner, proportional, event }; 00479 00480 drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, resize_window_callback, ¶ms); 00481 } 00482 00483 /* 00484 * This function grabs your pointer and lets you drag stuff around (borders). 00485 * Every time you move your mouse, an XCB_MOTION_NOTIFY event will be received 00486 * and the given callback will be called with the parameters specified (client, 00487 * border on which the click originally was), the original rect of the client, 00488 * the event and the new coordinates (x, y). 00489 * 00490 */ 00491 void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t 00492 confine_to, border_t border, callback_t callback, const void *extra) 00493 { 00494 uint32_t new_x, new_y; 00495 Rect old_rect; 00496 if (con != NULL) 00497 memcpy(&old_rect, &(con->rect), sizeof(Rect)); 00498 00499 /* Grab the pointer */ 00500 xcb_grab_pointer_cookie_t cookie; 00501 xcb_grab_pointer_reply_t *reply; 00502 cookie = xcb_grab_pointer(conn, 00503 false, /* get all pointer events specified by the following mask */ 00504 root, /* grab the root window */ 00505 XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, /* which events to let through */ 00506 XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */ 00507 XCB_GRAB_MODE_ASYNC, /* keyboard mode */ 00508 confine_to, /* confine_to = in which window should the cursor stay */ 00509 XCB_NONE, /* don’t display a special cursor */ 00510 XCB_CURRENT_TIME); 00511 00512 if ((reply = xcb_grab_pointer_reply(conn, cookie, NULL)) == NULL) { 00513 ELOG("Could not grab pointer\n"); 00514 return; 00515 } 00516 00517 free(reply); 00518 00519 /* Go into our own event loop */ 00520 xcb_flush(conn); 00521 00522 xcb_generic_event_t *inside_event, *last_motion_notify = NULL; 00523 bool loop_done = false; 00524 /* I’ve always wanted to have my own eventhandler… */ 00525 while (!loop_done && (inside_event = xcb_wait_for_event(conn))) { 00526 /* We now handle all events we can get using xcb_poll_for_event */ 00527 do { 00528 /* skip x11 errors */ 00529 if (inside_event->response_type == 0) { 00530 free(inside_event); 00531 continue; 00532 } 00533 /* Strip off the highest bit (set if the event is generated) */ 00534 int type = (inside_event->response_type & 0x7F); 00535 00536 switch (type) { 00537 case XCB_BUTTON_RELEASE: 00538 loop_done = true; 00539 break; 00540 00541 case XCB_MOTION_NOTIFY: 00542 /* motion_notify events are saved for later */ 00543 FREE(last_motion_notify); 00544 last_motion_notify = inside_event; 00545 break; 00546 00547 case XCB_UNMAP_NOTIFY: 00548 case XCB_KEY_PRESS: 00549 case XCB_KEY_RELEASE: 00550 DLOG("Unmap-notify, aborting\n"); 00551 handle_event(type, inside_event); 00552 loop_done = true; 00553 break; 00554 00555 default: 00556 DLOG("Passing to original handler\n"); 00557 /* Use original handler */ 00558 handle_event(type, inside_event); 00559 break; 00560 } 00561 if (last_motion_notify != inside_event) 00562 free(inside_event); 00563 } while ((inside_event = xcb_poll_for_event(conn)) != NULL); 00564 00565 if (last_motion_notify == NULL) 00566 continue; 00567 00568 new_x = ((xcb_motion_notify_event_t*)last_motion_notify)->root_x; 00569 new_y = ((xcb_motion_notify_event_t*)last_motion_notify)->root_y; 00570 00571 callback(con, &old_rect, new_x, new_y, extra); 00572 FREE(last_motion_notify); 00573 } 00574 00575 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); 00576 xcb_flush(conn); 00577 } 00578 00579 /* 00580 * Repositions the CT_FLOATING_CON to have the coordinates specified by 00581 * newrect, but only if the coordinates are not out-of-bounds. Also reassigns 00582 * the floating con to a different workspace if this move was across different 00583 * outputs. 00584 * 00585 */ 00586 void floating_reposition(Con *con, Rect newrect) { 00587 /* Sanity check: Are the new coordinates on any output? If not, we 00588 * ignore that request. */ 00589 Output *output = get_output_containing( 00590 newrect.x + (newrect.width / 2), 00591 newrect.y + (newrect.height / 2)); 00592 00593 if (!output) { 00594 ELOG("No output found at destination coordinates. Not repositioning.\n"); 00595 return; 00596 } 00597 00598 con->rect = newrect; 00599 00600 floating_maybe_reassign_ws(con); 00601 tree_render(); 00602 } 00603 00604 /* 00605 * Fixes the coordinates of the floating window whenever the window gets 00606 * reassigned to a different output (or when the output’s rect changes). 00607 * 00608 */ 00609 void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect) { 00610 DLOG("Fixing coordinates of floating window %p\n", con); 00611 /* First we get the x/y coordinates relative to the x/y coordinates 00612 * of the output on which the window is on */ 00613 uint32_t rel_x = (con->rect.x - old_rect->x); 00614 uint32_t rel_y = (con->rect.y - old_rect->y); 00615 /* Then we calculate a fraction, for example 0.63 for a window 00616 * which is at y = 1212 of a 1920 px high output */ 00617 double fraction_x = ((double)rel_x / old_rect->width); 00618 double fraction_y = ((double)rel_y / old_rect->height); 00619 DLOG("rel_x = %d, rel_y = %d, fraction_x = %f, fraction_y = %f, output->w = %d, output->h = %d\n", 00620 rel_x, rel_y, fraction_x, fraction_y, old_rect->width, old_rect->height); 00621 con->rect.x = new_rect->x + (fraction_x * new_rect->width); 00622 con->rect.y = new_rect->y + (fraction_y * new_rect->height); 00623 DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y); 00624 } 00625 00626 #if 0 00627 /* 00628 * Moves the client 10px to the specified direction. 00629 * 00630 */ 00631 void floating_move(xcb_connection_t *conn, Client *currently_focused, direction_t direction) { 00632 DLOG("floating move\n"); 00633 00634 Rect destination = currently_focused->rect; 00635 Rect *screen = &(currently_focused->workspace->output->rect); 00636 00637 switch (direction) { 00638 case D_LEFT: 00639 destination.x -= 10; 00640 break; 00641 case D_RIGHT: 00642 destination.x += 10; 00643 break; 00644 case D_UP: 00645 destination.y -= 10; 00646 break; 00647 case D_DOWN: 00648 destination.y += 10; 00649 break; 00650 /* to make static analyzers happy */ 00651 default: 00652 break; 00653 } 00654 00655 /* Prevent windows from vanishing completely */ 00656 if ((int32_t)(destination.x + destination.width - 5) <= (int32_t)screen->x || 00657 (int32_t)(destination.x + 5) >= (int32_t)(screen->x + screen->width) || 00658 (int32_t)(destination.y + destination.height - 5) <= (int32_t)screen->y || 00659 (int32_t)(destination.y + 5) >= (int32_t)(screen->y + screen->height)) { 00660 DLOG("boundary check failed, not moving\n"); 00661 return; 00662 } 00663 00664 currently_focused->rect = destination; 00665 reposition_client(conn, currently_focused); 00666 00667 /* Because reposition_client does not send a faked configure event (only resize does), 00668 * we need to initiate that on our own */ 00669 fake_absolute_configure_notify(conn, currently_focused); 00670 /* fake_absolute_configure_notify flushes */ 00671 } 00672 00673 /* 00674 * Hides all floating clients (or show them if they are currently hidden) on 00675 * the specified workspace. 00676 * 00677 */ 00678 void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace) { 00679 Client *client; 00680 00681 workspace->floating_hidden = !workspace->floating_hidden; 00682 DLOG("floating_hidden is now: %d\n", workspace->floating_hidden); 00683 TAILQ_FOREACH(client, &(workspace->floating_clients), floating_clients) { 00684 if (workspace->floating_hidden) 00685 client_unmap(conn, client); 00686 else client_map(conn, client); 00687 } 00688 00689 /* If we just unmapped all floating windows we should ensure that the focus 00690 * is set correctly, that ist, to the first non-floating client in stack */ 00691 if (workspace->floating_hidden) 00692 SLIST_FOREACH(client, &(workspace->focus_stack), focus_clients) { 00693 if (client_is_floating(client)) 00694 continue; 00695 set_focus(conn, client, true); 00696 return; 00697 } 00698 00699 xcb_flush(conn); 00700 } 00701 #endif