X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fhandlers.c;h=d058b650eec5f39c2224742ad4982d7eae221d3e;hb=1335e4a4c9c7af2f4ade5169d8385b1bd4c1b0e2;hp=5aef05e975ab1f5e3d2ac326c10b7db1cfe075a9;hpb=09cd7bd2d0093c7312fafc0956640fb89338fd38;p=i3%2Fi3 diff --git a/src/handlers.c b/src/handlers.c index 5aef05e9..d058b650 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -3,7 +3,7 @@ * * i3 - an improved dynamic tiling window manager * - * (c) 2009 Michael Stapelberg and contributors + * © 2009 Michael Stapelberg and contributors * * See file LICENSE for license information. * @@ -41,10 +41,8 @@ int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_release_ev } /* - * There was a key press. We lookup the key symbol and see if there are any bindings - * on that. This allows to do things like binding special characters (think of ä) to - * functions to get one more modifier while not losing AltGr :-) - * TODO: this description needs to be more understandable + * There was a key press. We compare this key code with our bindings table and pass + * the bound action to parse_command(). * */ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) { @@ -59,36 +57,29 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_ printf("state %d\n", event->state); + /* Remove the numlock bit, all other bits are modifiers we can bind to */ + uint16_t state_filtered = event->state & ~XCB_MOD_MASK_LOCK; + /* Find the binding */ - /* TODO: event->state durch eine bitmask filtern und dann direkt vergleichen */ - Binding *bind, *best_match = TAILQ_END(&bindings); - TAILQ_FOREACH(bind, &bindings, bindings) { - if (bind->keycode == event->detail && - (bind->mods & event->state) == bind->mods) { - if (best_match == TAILQ_END(&bindings) || - bind->mods > best_match->mods) - best_match = bind; - } - } + Binding *bind; + TAILQ_FOREACH(bind, &bindings, bindings) + if (bind->keycode == event->detail && bind->mods == state_filtered) + break; /* No match? Then it was an actively grabbed key, that is with Mode_switch, and the user did not press Mode_switch, so just pass it… */ - if (best_match == TAILQ_END(&bindings)) { + if (bind == TAILQ_END(&bindings)) { xcb_allow_events(conn, ReplayKeyboard, event->time); xcb_flush(conn); return 1; } + parse_command(conn, bind->command); if (event->state & 0x2) { - printf("that's mode_switch\n"); - parse_command(conn, best_match->command); - printf("ok, hiding this event.\n"); + printf("Mode_switch -> allow_events(SyncKeyboard)\n"); xcb_allow_events(conn, SyncKeyboard, event->time); xcb_flush(conn); - return 1; } - - parse_command(conn, best_match->command); return 1; } @@ -98,7 +89,7 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_ * */ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_event_t *event) { - printf("enter_notify\n"); + printf("enter_notify for %08x\n", event->event); /* This was either a focus for a client’s parent (= titlebar)… */ Client *client = table_get(byParent, event->event); @@ -119,6 +110,10 @@ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_ return 1; } + /* When in stacking, enter notifications are ignored. Focus will be changed via keyboard only. */ + if (client->container->mode == MODE_STACK) + return 1; + set_focus(conn, client); return 1; @@ -128,8 +123,11 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ printf("button press!\n"); /* This was either a focus for a client’s parent (= titlebar)… */ Client *client = table_get(byChild, event->event); - if (client == NULL) + bool border_click = false; + if (client == NULL) { client = table_get(byParent, event->event); + border_click = true; + } if (client == NULL) return 1; @@ -140,56 +138,80 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ set_focus(conn, client); /* Let’s see if this was on the borders (= resize). If not, we’re done */ - i3Font *font = load_font(conn, pattern); printf("press button on x=%d, y=%d\n", event->event_x, event->event_y); - if (event->event_y <= (font->height + 2)) + + Container *con = client->container, + *first = NULL, + *second = NULL; + enum { O_HORIZONTAL, O_VERTICAL } orientation = O_VERTICAL; + + if (con == NULL) { + printf("dock. done.\n"); + xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time); + xcb_flush(conn); + return 1; + } + + printf("event->event_x = %d, client->rect.width = %d\n", event->event_x, client->rect.width); + + if (!border_click) { + printf("client. done.\n"); + xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time); + xcb_flush(conn); return 1; + } - printf("that was resize\n"); + if (event->event_y < 2) { + /* This was a press on the top border */ + if (con->row == 0) + return 1; + first = con->workspace->table[con->col][con->row-1]; + second = con; + orientation = O_HORIZONTAL; + } else if (event->event_y >= (client->rect.height - 2)) { + /* …bottom border */ + if (con->row == (con->workspace->rows-1)) + return 1; + first = con; + second = con->workspace->table[con->col][con->row+1]; + orientation = O_HORIZONTAL; + } else if (event->event_x < 2) { + /* …left border */ + if (con->col == 0) + return 1; + first = con->workspace->table[con->col-1][con->row]; + second = con; + } else if (event->event_x > 2) { + /* …right border */ + if (con->col == (con->workspace->cols-1)) + return 1; + first = con; + second = con->workspace->table[con->col+1][con->row]; + } /* Open a new window, the resizebar. Grab the pointer and move the window around as the user moves the pointer. */ + Rect grabrect = {0, 0, root_screen->width_in_pixels, root_screen->height_in_pixels}; + xcb_window_t grabwin = create_window(conn, grabrect, XCB_WINDOW_CLASS_INPUT_ONLY, 0, NULL); + + Rect helprect; + if (orientation == O_VERTICAL) { + helprect.x = event->root_x; + helprect.y = 0; + helprect.width = 2; + helprect.height = root_screen->height_in_pixels; /* this has to be the cell’s height */ + } else { + helprect.x = 0; + helprect.y = event->root_y; + helprect.width = root_screen->width_in_pixels; /* this has to be the cell’s width*/ + helprect.height = 2; + } + xcb_window_t helpwin = create_window(conn, helprect, XCB_WINDOW_CLASS_INPUT_OUTPUT, 0, NULL); + uint32_t values[1] = {get_colorpixel(conn, NULL, helpwin, "#4c7899")}; + xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(conn, helpwin, XCB_CW_BACK_PIXEL, values); + check_error(conn, cookie, "Could not change window attributes (background color)"); - /* TODO: the whole logic is missing. this is just a proof of concept */ - xcb_window_t grabwin = xcb_generate_id(conn); - - uint32_t mask = 0; - uint32_t values[3]; - - xcb_create_window(conn, - 0, - grabwin, - root, - 0, /* x */ - 0, /* y */ - root_screen->width_in_pixels, /* width */ - root_screen->height_in_pixels, /* height */ - /* border_width */ 0, - XCB_WINDOW_CLASS_INPUT_ONLY, - root_screen->root_visual, - 0, - values); - - /* Map the window on the screen (= make it visible) */ - xcb_map_window(conn, grabwin); - - xcb_window_t helpwin = xcb_generate_id(conn); - - mask = XCB_CW_BACK_PIXEL; - values[0] = root_screen->white_pixel; - xcb_create_window(conn, root_screen->root_depth, helpwin, root, - event->root_x, - 0, - 5, - root_screen->height_in_pixels, - /* bordor */ 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - root_screen->root_visual, - mask, - values); - - xcb_map_window(conn, helpwin); xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, helpwin); xcb_grab_pointer(conn, false, root, XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, @@ -203,21 +225,29 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ /* Same as get_event_handler in xcb */ int nr = inside_event->response_type; if (nr == 0) { + /* An error occured */ handle_event(NULL, conn, inside_event); + free(inside_event); continue; } assert(nr < 256); nr &= XCB_EVENT_RESPONSE_TYPE_MASK; assert(nr >= 2); - /* Check if we need to escape this loop… */ + /* Check if we need to escape this loop */ if (nr == XCB_BUTTON_RELEASE) break; switch (nr) { case XCB_MOTION_NOTIFY: - values[0] = ((xcb_motion_notify_event_t*)inside_event)->root_x; - xcb_configure_window(conn, helpwin, XCB_CONFIG_WINDOW_X, values); + if (orientation == O_VERTICAL) { + values[0] = ((xcb_motion_notify_event_t*)inside_event)->root_x; + xcb_configure_window(conn, helpwin, XCB_CONFIG_WINDOW_X, values); + } else { + values[0] = ((xcb_motion_notify_event_t*)inside_event)->root_y; + xcb_configure_window(conn, helpwin, XCB_CONFIG_WINDOW_Y, values); + } + xcb_flush(conn); break; case XCB_EXPOSE: @@ -228,7 +258,6 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ printf("Ignoring event of type %d\n", nr); break; } - printf("---\n"); free(inside_event); } @@ -237,9 +266,40 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ xcb_destroy_window(conn, grabwin); xcb_flush(conn); + Workspace *ws = con->workspace; + if (orientation == O_VERTICAL) { + printf("Resize was from X = %d to X = %d\n", event->root_x, values[0]); + + /* Convert 0 (for default width_factor) to actual numbers */ + if (first->width_factor == 0) + first->width_factor = ((float)ws->rect.width / ws->cols) / ws->rect.width; + if (second->width_factor == 0) + second->width_factor = ((float)ws->rect.width / ws->cols) / ws->rect.width; + + first->width_factor *= (float)(first->width + (values[0] - event->root_x)) / first->width; + second->width_factor *= (float)(second->width - (values[0] - event->root_x)) / second->width; + } else { + printf("Resize was from Y = %d to Y = %d\n", event->root_y, values[0]); + + /* Convert 0 (for default height_factor) to actual numbers */ + if (first->height_factor == 0) + first->height_factor = ((float)ws->rect.height / ws->rows) / ws->rect.height; + if (second->height_factor == 0) + second->height_factor = ((float)ws->rect.height / ws->rows) / ws->rect.height; + + first->height_factor *= (float)(first->height + (values[0] - event->root_y)) / first->height; + second->height_factor *= (float)(second->height - (values[0] - event->root_y)) / second->height; + } + + render_layout(conn); + return 1; } +/* + * A new window appeared on the screen (=was mapped), so let’s manage it. + * + */ int handle_map_notify_event(void *prophs, xcb_connection_t *conn, xcb_map_notify_event_t *event) { window_attributes_t wa = { TAG_VALUE }; wa.u.override_redirect = event->override_redirect; @@ -267,21 +327,30 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *c, xcb_unmap_notify_ client = table_remove(byChild, e->window); printf("UnmapNotify for 0x%08x (received from 0x%08x): ", e->window, e->event); - if(client == NULL) { + if (client == NULL) { printf("not a managed window. Ignoring.\n"); return 0; } - - if (client->container->currently_focused == client) - client->container->currently_focused = NULL; - CIRCLEQ_REMOVE(&(client->container->clients), client, clients); + if (client->container != NULL) { + Client *to_focus = CIRCLEQ_NEXT_OR_NULL(&(client->container->clients), client, clients); + if (to_focus == NULL) + to_focus = CIRCLEQ_PREV_OR_NULL(&(client->container->clients), client, clients); + if (client->container->currently_focused == client) + client->container->currently_focused = to_focus; + CIRCLEQ_REMOVE(&(client->container->clients), client, clients); + if (to_focus != NULL) + set_focus(c, to_focus); + } printf("child of 0x%08x.\n", client->frame); xcb_reparent_window(c, client->child, root, 0, 0); xcb_destroy_window(c, client->frame); xcb_flush(c); table_remove(byParent, client->frame); + + cleanup_table(client->container->workspace); + free(client); render_layout(c); @@ -305,7 +374,7 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, strncpy(client->name, xcb_get_property_value(prop), client->name_len); printf("rename to \"%.*s\".\n", client->name_len, client->name); - decorate_window(conn, client); + decorate_window(conn, client, client->frame, client->titlegc, 0); xcb_flush(conn); return 1; @@ -316,11 +385,28 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, * */ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *e) { - printf("handle_expose_event()\n"); + printf("got expose_event\n"); + /* e->count is the number of minimum remaining expose events for this window, so we + skip all events but the last one */ + if (e->count != 0) + return 1; + Client *client = table_get(byParent, e->window); - if(!client || e->count != 0) + if (client == NULL) { + /* There was no client in the table, so this is probably an expose event for + one of our stack_windows. */ + struct Stack_Window *stack_win; + SLIST_FOREACH(stack_win, &stack_wins, stack_windows) + if (stack_win->window == e->window) { + render_container(conn, stack_win->container); + return 1; + } return 1; - decorate_window(conn, client); + } + + printf("handle_expose_event()\n"); + if (client->container->mode != MODE_STACK) + decorate_window(conn, client, client->frame, client->titlegc, 0); return 1; } @@ -341,7 +427,7 @@ int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message if (client == NULL) return 0; - /* Check if the fullscreen state should be toggled… */ + /* Check if the fullscreen state should be toggled */ if ((client->fullscreen && (event->data.data32[0] == _NET_WM_STATE_REMOVE || event->data.data32[0] == _NET_WM_STATE_TOGGLE)) || @@ -356,3 +442,11 @@ int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message return 1; } + +int window_type_handler(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, + xcb_atom_t atom, xcb_get_property_reply_t *property) { + /* TODO: Implement this one. To do this, implement a little test program which sleep(1)s + before changing this property. */ + printf("_NET_WM_WINDOW_TYPE changed, this is not yet implemented.\n"); + return 0; +}