00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include <stdio.h>
00015 #include <assert.h>
00016 #include <string.h>
00017 #include <stdlib.h>
00018 #include <time.h>
00019 #include <stdbool.h>
00020 #include <math.h>
00021
00022 #include <xcb/xcb.h>
00023 #include <xcb/xcb_atom.h>
00024 #include <xcb/xcb_icccm.h>
00025
00026 #include <X11/XKBlib.h>
00027
00028 #include "i3.h"
00029 #include "queue.h"
00030 #include "table.h"
00031 #include "config.h"
00032 #include "util.h"
00033 #include "xcb.h"
00034 #include "client.h"
00035 #include "workspace.h"
00036 #include "commands.h"
00037 #include "floating.h"
00038 #include "resize.h"
00039 #include "log.h"
00040 #include "randr.h"
00041
00042 static struct Stack_Window *get_stack_window(xcb_window_t window_id) {
00043 struct Stack_Window *current;
00044
00045 SLIST_FOREACH(current, &stack_wins, stack_windows) {
00046 if (current->window != window_id)
00047 continue;
00048
00049 return current;
00050 }
00051
00052 return NULL;
00053 }
00054
00055
00056
00057
00058
00059
00060 static bool button_press_stackwin(xcb_connection_t *conn, xcb_button_press_event_t *event) {
00061 struct Stack_Window *stack_win;
00062
00063
00064 if ((stack_win = get_stack_window(event->event)) == NULL)
00065 return false;
00066
00067
00068
00069 if (event->detail == XCB_BUTTON_INDEX_4 || event->detail == XCB_BUTTON_INDEX_5) {
00070 direction_t direction = (event->detail == XCB_BUTTON_INDEX_4 ? D_UP : D_DOWN);
00071 focus_window_in_container(conn, CUR_CELL, direction);
00072 return true;
00073 }
00074
00075
00076
00077
00078 i3Font *font = load_font(conn, config.font);
00079 int decoration_height = (font->height + 2 + 2);
00080 int destination = (event->event_y / decoration_height),
00081 c = 0,
00082 num_clients = 0;
00083 Client *client;
00084 Container *container = stack_win->container;
00085
00086 CIRCLEQ_FOREACH(client, &(container->clients), clients)
00087 num_clients++;
00088
00089
00090
00091 if (num_clients == 0)
00092 return true;
00093
00094 if (container->mode == MODE_TABBED)
00095 destination = (event->event_x / (container->width / num_clients));
00096 else if (container->mode == MODE_STACK &&
00097 container->stack_limit != STACK_LIMIT_NONE) {
00098 if (container->stack_limit == STACK_LIMIT_COLS) {
00099 int wrap = ceil((float)num_clients / container->stack_limit_value);
00100 int clicked_column = (event->event_x / (stack_win->rect.width / container->stack_limit_value));
00101 int clicked_row = (event->event_y / decoration_height);
00102 DLOG("clicked on column %d, row %d\n", clicked_column, clicked_row);
00103 destination = (wrap * clicked_column) + clicked_row;
00104 } else {
00105 int width = (stack_win->rect.width / ceil((float)num_clients / container->stack_limit_value));
00106 int clicked_column = (event->event_x / width);
00107 int clicked_row = (event->event_y / decoration_height);
00108 DLOG("clicked on column %d, row %d\n", clicked_column, clicked_row);
00109 destination = (container->stack_limit_value * clicked_column) + clicked_row;
00110 }
00111 }
00112
00113 DLOG("Click on stack_win for client %d\n", destination);
00114 CIRCLEQ_FOREACH(client, &(stack_win->container->clients), clients)
00115 if (c++ == destination) {
00116 set_focus(conn, client, true);
00117 return true;
00118 }
00119
00120 return true;
00121 }
00122
00123
00124
00125
00126
00127
00128 static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *event) {
00129 Output *output;
00130 TAILQ_FOREACH(output, &outputs, outputs) {
00131 if (output->bar != event->event)
00132 continue;
00133
00134 DLOG("Click on a bar\n");
00135
00136
00137 if (event->detail == XCB_BUTTON_INDEX_4 || event->detail == XCB_BUTTON_INDEX_5) {
00138 Workspace *ws = c_ws;
00139 if (event->detail == XCB_BUTTON_INDEX_5) {
00140 while ((ws = TAILQ_NEXT(ws, workspaces)) != TAILQ_END(workspaces_head)) {
00141 if (ws->output == output) {
00142 workspace_show(conn, ws->num + 1);
00143 return true;
00144 }
00145 }
00146 } else {
00147 while ((ws = TAILQ_PREV(ws, workspaces_head, workspaces)) != TAILQ_END(workspaces)) {
00148 if (ws->output == output) {
00149 workspace_show(conn, ws->num + 1);
00150 return true;
00151 }
00152 }
00153 }
00154 return true;
00155 }
00156 int drawn = 0;
00157
00158
00159 Workspace *ws;
00160 TAILQ_FOREACH(ws, workspaces, workspaces) {
00161 if (ws->output != output)
00162 continue;
00163 DLOG("Checking if click was on workspace %d with drawn = %d, tw = %d\n",
00164 ws->num, drawn, ws->text_width);
00165 if (event->event_x > (drawn + 1) &&
00166 event->event_x <= (drawn + 1 + ws->text_width + 5 + 5)) {
00167 workspace_show(conn, ws->num + 1);
00168 return true;
00169 }
00170
00171 drawn += ws->text_width + 5 + 5 + 2;
00172 }
00173 return true;
00174 }
00175
00176 return false;
00177 }
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187 static bool floating_mod_on_tiled_client(xcb_connection_t *conn, Client *client,
00188 xcb_button_press_event_t *event) {
00189
00190 if (event->detail != 3)
00191 return false;
00192
00193
00194
00195
00196
00197 int to_right = client->rect.width - event->event_x,
00198 to_left = event->event_x,
00199 to_top = event->event_y,
00200 to_bottom = client->rect.height - event->event_y;
00201 resize_orientation_t orientation = O_VERTICAL;
00202 Container *con = client->container;
00203 Workspace *ws = con->workspace;
00204 int first = 0, second = 0;
00205
00206 DLOG("click was %d px to the right, %d px to the left, %d px to top, %d px to bottom\n",
00207 to_right, to_left, to_top, to_bottom);
00208
00209 if (to_right < to_left &&
00210 to_right < to_top &&
00211 to_right < to_bottom) {
00212
00213 first = con->col + (con->colspan - 1);
00214 DLOG("column %d\n", first);
00215
00216 if (!cell_exists(ws, first, con->row) ||
00217 (first == (ws->cols-1)))
00218 return false;
00219
00220 second = first + 1;
00221 } else if (to_left < to_right &&
00222 to_left < to_top &&
00223 to_left < to_bottom) {
00224
00225 if (con->col == 0)
00226 return false;
00227
00228 first = con->col - 1;
00229 second = con->col;
00230 } else if (to_top < to_right &&
00231 to_top < to_left &&
00232 to_top < to_bottom) {
00233
00234 if (con->row == 0)
00235 return false;
00236 first = con->row - 1;
00237 second = con->row;
00238 orientation = O_HORIZONTAL;
00239 } else if (to_bottom < to_right &&
00240 to_bottom < to_left &&
00241 to_bottom < to_top) {
00242
00243 first = con->row + (con->rowspan - 1);
00244 if (!cell_exists(ws, con->col, first) ||
00245 (first == (ws->rows-1)))
00246 return false;
00247
00248 second = first + 1;
00249 orientation = O_HORIZONTAL;
00250 }
00251
00252 return resize_graphical_handler(conn, ws, first, second, orientation, event);
00253 }
00254
00255 int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_event_t *event) {
00256 DLOG("Button %d pressed\n", event->state);
00257
00258 Client *client = table_get(&by_child, event->event);
00259 bool border_click = false;
00260 if (client == NULL) {
00261 client = table_get(&by_parent, event->event);
00262 border_click = true;
00263 }
00264
00265
00266
00267 if (config.floating_modifier != 0 &&
00268 (event->state & config.floating_modifier) == config.floating_modifier) {
00269 if (client == NULL) {
00270 DLOG("Not handling, floating_modifier was pressed and no client found\n");
00271 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
00272 xcb_flush(conn);
00273 return 1;
00274 }
00275 if (client->fullscreen) {
00276 DLOG("Not handling, client is in fullscreen mode\n");
00277 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
00278 xcb_flush(conn);
00279 return 1;
00280 }
00281 if (client_is_floating(client)) {
00282 DLOG("button %d pressed\n", event->detail);
00283 if (event->detail == 1) {
00284 DLOG("left mouse button, dragging\n");
00285 floating_drag_window(conn, client, event);
00286 } else if (event->detail == 3) {
00287 bool proportional = (event->state & BIND_SHIFT);
00288 DLOG("right mouse button\n");
00289 floating_resize_window(conn, client, proportional, event);
00290 }
00291 return 1;
00292 }
00293
00294 if (!floating_mod_on_tiled_client(conn, client, event)) {
00295 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
00296 xcb_flush(conn);
00297 }
00298
00299 return 1;
00300 }
00301
00302 if (client == NULL) {
00303
00304 if (button_press_stackwin(conn, event))
00305 return 1;
00306
00307
00308 if (button_press_bar(conn, event))
00309 return 1;
00310
00311 DLOG("Could not handle this button press\n");
00312 return 1;
00313 }
00314
00315
00316 set_focus(conn, client, true);
00317
00318
00319 DLOG("press button on x=%d, y=%d\n", event->event_x, event->event_y);
00320 resize_orientation_t orientation = O_VERTICAL;
00321 Container *con = client->container;
00322 int first, second;
00323
00324 if (client->dock) {
00325 DLOG("dock. done.\n");
00326 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
00327 xcb_flush(conn);
00328 return 1;
00329 }
00330
00331 DLOG("event->event_x = %d, client->rect.width = %d\n", event->event_x, client->rect.width);
00332
00333
00334
00335
00336 if (border_click &&
00337 event->event_x >= client->child_rect.x &&
00338 event->event_x <= (client->child_rect.x + client->child_rect.width) &&
00339 event->event_y >= client->child_rect.y &&
00340 event->event_y <= (client->child_rect.y + client->child_rect.height)) {
00341 DLOG("Fixing border_click = false because of click in child\n");
00342 border_click = false;
00343 }
00344
00345 if (!border_click) {
00346 DLOG("client. done.\n");
00347 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
00348
00349 if (client_is_floating(client))
00350 xcb_raise_window(conn, client->frame);
00351 xcb_flush(conn);
00352 return 1;
00353 }
00354
00355
00356 i3Font *font = load_font(conn, config.font);
00357 if (event->event_y >= 2 && event->event_y <= (font->height + 2 + 2)) {
00358 DLOG("click on titlebar\n");
00359
00360
00361 if (client_is_floating(client)) {
00362
00363 xcb_raise_window(conn, client->frame);
00364 xcb_flush(conn);
00365
00366 floating_drag_window(conn, client, event);
00367 }
00368 return 1;
00369 }
00370
00371 if (client_is_floating(client))
00372 return floating_border_click(conn, client, event);
00373
00374 Workspace *ws = con->workspace;
00375
00376 if (event->event_y < 2) {
00377
00378 if (con->row == 0)
00379 return 1;
00380 first = con->row - 1;
00381 second = con->row;
00382 orientation = O_HORIZONTAL;
00383 } else if (event->event_y >= (client->rect.height - 2)) {
00384
00385 first = con->row + (con->rowspan - 1);
00386 if (!cell_exists(ws, con->col, first) ||
00387 (first == (ws->rows-1)))
00388 return 1;
00389
00390 second = first + 1;
00391 orientation = O_HORIZONTAL;
00392 } else if (event->event_x <= 2) {
00393
00394 if (con->col == 0)
00395 return 1;
00396
00397 first = con->col - 1;
00398 second = con->col;
00399 } else if (event->event_x > 2) {
00400
00401 first = con->col + (con->colspan - 1);
00402 DLOG("column %d\n", first);
00403
00404 if (!cell_exists(ws, first, con->row) ||
00405 (first == (ws->cols-1)))
00406 return 1;
00407
00408 second = first + 1;
00409 }
00410
00411 return resize_graphical_handler(conn, ws, first, second, orientation, event);
00412 }