#include #include #include #include "log.h" #include "config.h" #include "input_state.h" #define KEY_STATE_MAX_LENGTH 64 struct key_state { /* * Aims to store state regardless of modifiers. * If you press a key, then hold shift, then release the key, we'll * get two different key syms, but the same key code. This handles * that scenario and makes sure we can use the right bindings. */ uint32_t key_sym; uint32_t alt_sym; uint32_t key_code; }; static struct key_state key_state_array[KEY_STATE_MAX_LENGTH]; static struct key_state last_released; static uint32_t modifiers_state; void input_init(void) { int i; for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) { struct key_state none = { 0, 0, 0 }; key_state_array[i] = none; } struct key_state none = { 0, 0, 0 }; last_released = none; modifiers_state = 0; } uint32_t modifier_state_changed(uint32_t new_state, uint32_t mod) { if ((new_state & mod) != 0) { // pressed if ((modifiers_state & mod) != 0) { // already pressed return MOD_STATE_UNCHANGED; } else { // pressed return MOD_STATE_PRESSED; } } else { // not pressed if ((modifiers_state & mod) != 0) { // released return MOD_STATE_RELEASED; } else { // already released return MOD_STATE_UNCHANGED; } } } void modifiers_state_update(uint32_t new_state) { modifiers_state = new_state; } static uint8_t find_key(uint32_t key_sym, uint32_t key_code, bool update) { int i; for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) { if (0 == key_sym && 0 == key_code && key_state_array[i].key_sym == 0) { break; } if (key_sym != 0 && (key_state_array[i].key_sym == key_sym || key_state_array[i].alt_sym == key_sym)) { break; } if (update && key_state_array[i].key_code == key_code) { key_state_array[i].alt_sym = key_sym; break; } if (key_sym == 0 && key_code != 0 && key_state_array[i].key_code == key_code) { break; } } return i; } bool check_key(uint32_t key_sym, uint32_t key_code) { return find_key(key_sym, key_code, false) < KEY_STATE_MAX_LENGTH; } bool check_released_key(uint32_t key_sym) { return (key_sym != 0 && (last_released.key_sym == key_sym || last_released.alt_sym == key_sym)); } void press_key(uint32_t key_sym, uint32_t key_code) { if (key_code == 0) { return; } // Check if key exists if (!check_key(key_sym, key_code)) { // Check that we don't exceed buffer length int insert = find_key(0, 0, true); if (insert < KEY_STATE_MAX_LENGTH) { key_state_array[insert].key_sym = key_sym; key_state_array[insert].key_code = key_code; } } } void release_key(uint32_t key_sym, uint32_t key_code) { uint8_t index = find_key(key_sym, key_code, true); if (index < KEY_STATE_MAX_LENGTH) { last_released.key_sym = key_state_array[index].key_sym; last_released.alt_sym = key_state_array[index].alt_sym; last_released.key_code = key_state_array[index].key_code; struct key_state none = { 0, 0, 0 }; key_state_array[index] = none; } } // Pointer state and mode struct pointer_state pointer_state; static struct mode_state { // initial view state double x, y, w, h; swayc_t *ptr; // Containers used for resizing horizontally struct { double w; swayc_t *ptr; struct { double w; swayc_t *ptr; } parent; } horiz; // Containers used for resizing vertically struct { double h; swayc_t *ptr; struct { double h; swayc_t *ptr; } parent; } vert; } initial; static struct { bool left; bool top; } lock; // initial set/unset static void set_initial_view(swayc_t *view) { initial.ptr = view; initial.x = view->x; initial.y = view->y; initial.w = view->width; initial.h = view->height; } static void set_initial_sibling(void) { bool reset = true; swayc_t *ws = swayc_active_workspace_for(initial.ptr); if ((initial.horiz.ptr = get_swayc_in_direction_under(initial.ptr, lock.left ? MOVE_RIGHT: MOVE_LEFT, ws))) { initial.horiz.w = initial.horiz.ptr->width; initial.horiz.parent.ptr = get_swayc_in_direction_under(initial.horiz.ptr, lock.left ? MOVE_LEFT : MOVE_RIGHT, ws); initial.horiz.parent.w = initial.horiz.parent.ptr->width; reset = false; } if ((initial.vert.ptr = get_swayc_in_direction_under(initial.ptr, lock.top ? MOVE_DOWN: MOVE_UP, ws))) { initial.vert.h = initial.vert.ptr->height; initial.vert.parent.ptr = get_swayc_in_direction_under(initial.vert.ptr, lock.top ? MOVE_UP : MOVE_DOWN, ws); initial.vert.parent.h = initial.vert.parent.ptr->height; reset = false; } // If nothing will change just undo the mode if (reset) { pointer_state.mode = 0; } } static void reset_initial_view(void) { initial.ptr->x = initial.x; initial.ptr->y = initial.y; initial.ptr->width = initial.w; initial.ptr->height = initial.h; arrange_windows(initial.ptr, -1, -1); pointer_state.mode = 0; } static void reset_initial_sibling(void) { initial.horiz.ptr->width = initial.horiz.w; initial.horiz.parent.ptr->width = initial.horiz.parent.w; initial.vert.ptr->height = initial.vert.h; initial.vert.parent.ptr->height = initial.vert.parent.h; arrange_windows(initial.horiz.ptr->parent, -1, -1); arrange_windows(initial.vert.ptr->parent, -1, -1); pointer_state.mode = 0; } void pointer_position_set(struct wlc_point *new_origin, bool force_focus) { struct wlc_point origin; wlc_pointer_get_position(&origin); pointer_state.delta.x = new_origin->x - origin.x; pointer_state.delta.y = new_origin->y - origin.y; wlc_pointer_set_position(new_origin); // Update view under pointer swayc_t *prev_view = pointer_state.view; pointer_state.view = container_under_pointer(); // If pointer is in a mode, update it if (pointer_state.mode) { pointer_mode_update(); // Otherwise change focus if config is set } else if (force_focus || (prev_view != pointer_state.view && config->focus_follows_mouse)) { if (pointer_state.view && pointer_state.view->type == C_VIEW) { set_focused_container(pointer_state.view); } } } void center_pointer_on(swayc_t *view) { struct wlc_point new_origin; new_origin.x = view->x + view->width/2; new_origin.y = view->y + view->height/2; pointer_position_set(&new_origin, true); } // Mode set left/right click static void pointer_mode_set_dragging(void) { switch (config->dragging_key) { case M_LEFT_CLICK: set_initial_view(pointer_state.left.view); break; case M_RIGHT_CLICK: set_initial_view(pointer_state.right.view); break; } if (initial.ptr->is_floating) { pointer_state.mode = M_DRAGGING | M_FLOATING; } else { pointer_state.mode = M_DRAGGING | M_TILING; // unset mode if we can't drag tile if (initial.ptr->parent->type == C_WORKSPACE && initial.ptr->parent->children->length == 1) { pointer_state.mode = 0; } } } static void pointer_mode_set_resizing(void) { switch (config->resizing_key) { case M_LEFT_CLICK: set_initial_view(pointer_state.left.view); break; case M_RIGHT_CLICK: set_initial_view(pointer_state.right.view); break; } // Setup locking information int midway_x = initial.ptr->x + initial.ptr->width/2; int midway_y = initial.ptr->y + initial.ptr->height/2; struct wlc_point origin; wlc_pointer_get_position(&origin); lock.left = origin.x > midway_x; lock.top = origin.y > midway_y; if (initial.ptr->is_floating) { pointer_state.mode = M_RESIZING | M_FLOATING; } else { pointer_state.mode = M_RESIZING | M_TILING; set_initial_sibling(); } } // Mode set/update/reset void pointer_mode_set(uint32_t button, bool condition) { // switch on drag/resize mode switch (pointer_state.mode & (M_DRAGGING | M_RESIZING)) { case M_DRAGGING: // end drag mode when 'dragging' click is unpressed if (config->dragging_key == M_LEFT_CLICK && !pointer_state.left.held) { pointer_state.mode = 0; } else if (config->dragging_key == M_RIGHT_CLICK && !pointer_state.right.held) { pointer_state.mode = 0; } break; case M_RESIZING: // end resize mode when 'resizing' click is unpressed if (config->resizing_key == M_LEFT_CLICK && !pointer_state.left.held) { pointer_state.mode = 0; } else if (config->resizing_key == M_RIGHT_CLICK && !pointer_state.right.held) { pointer_state.mode = 0; } break; // No mode case default: // return if failed condition, or no view if (!condition || !pointer_state.view) { break; } // Set mode depending on current button press switch (button) { // Start left-click mode case M_LEFT_CLICK: // if button release don't do anything if (pointer_state.left.held) { if (config->dragging_key == M_LEFT_CLICK) { pointer_mode_set_dragging(); } else if (config->resizing_key == M_LEFT_CLICK) { pointer_mode_set_resizing(); } } break; // Start right-click mode case M_RIGHT_CLICK: // if button release don't do anyhting if (pointer_state.right.held) { if (config->dragging_key == M_RIGHT_CLICK) { pointer_mode_set_dragging(); } else if (config->resizing_key == M_RIGHT_CLICK) { pointer_mode_set_resizing(); } } break; } } } void pointer_mode_update(void) { if (initial.ptr->type != C_VIEW) { pointer_state.mode = 0; return; } struct wlc_point origin; wlc_pointer_get_position(&origin); int dx = origin.x; int dy = origin.y; switch (pointer_state.mode) { case M_FLOATING | M_DRAGGING: // Update position switch (config->dragging_key) { case M_LEFT_CLICK: dx -= pointer_state.left.x; dy -= pointer_state.left.y; break; case M_RIGHT_CLICK: dx -= pointer_state.right.x; dy -= pointer_state.right.y; break; } if (initial.x + dx != initial.ptr->x) { initial.ptr->x = initial.x + dx; } if (initial.y + dy != initial.ptr->y) { initial.ptr->y = initial.y + dy; } update_geometry(initial.ptr); break; case M_FLOATING | M_RESIZING: switch (config->resizing_key) { case M_LEFT_CLICK: dx -= pointer_state.left.x; dy -= pointer_state.left.y; initial.ptr = pointer_state.left.view; break; case M_RIGHT_CLICK: dx -= pointer_state.right.x; dy -= pointer_state.right.y; initial.ptr = pointer_state.right.view; break; } if (lock.left) { if (initial.w + dx > min_sane_w) { initial.ptr->width = initial.w + dx; } } else { // lock.right if (initial.w - dx > min_sane_w) { initial.ptr->width = initial.w - dx; initial.ptr->x = initial.x + dx; } } if (lock.top) { if (initial.h + dy > min_sane_h) { initial.ptr->height = initial.h + dy; } } else { // lock.bottom if (initial.h - dy > min_sane_h) { initial.ptr->height = initial.h - dy; initial.ptr->y = initial.y + dy; } } update_geometry(initial.ptr); break; case M_TILING | M_DRAGGING: // swap current view under pointer with dragged view if (pointer_state.view && pointer_state.view->type == C_VIEW && pointer_state.view != initial.ptr && !pointer_state.view->is_floating) { // Swap them around swap_container(pointer_state.view, initial.ptr); swap_geometry(pointer_state.view, initial.ptr); update_geometry(pointer_state.view); update_geometry(initial.ptr); // Set focus back to initial view set_focused_container(initial.ptr); } break; case M_TILING | M_RESIZING: switch (config->resizing_key) { case M_LEFT_CLICK: dx -= pointer_state.left.x; dy -= pointer_state.left.y; break; case M_RIGHT_CLICK: dx -= pointer_state.right.x; dy -= pointer_state.right.y; break; } // resize if we can if (initial.horiz.ptr) { if (lock.left) { // Check whether its fine to resize if (initial.w + dx > min_sane_w && initial.horiz.w - dx > min_sane_w) { initial.horiz.ptr->width = initial.horiz.w - dx; initial.horiz.parent.ptr->width = initial.horiz.parent.w + dx; } } else { // lock.right if (initial.w - dx > min_sane_w && initial.horiz.w + dx > min_sane_w) { initial.horiz.ptr->width = initial.horiz.w + dx; initial.horiz.parent.ptr->width = initial.horiz.parent.w - dx; } } arrange_windows(initial.horiz.ptr->parent, -1, -1); } if (initial.vert.ptr) { if (lock.top) { if (initial.h + dy > min_sane_h && initial.vert.h - dy > min_sane_h) { initial.vert.ptr->height = initial.vert.h - dy; initial.vert.parent.ptr->height = initial.vert.parent.h + dy; } } else { // lock.bottom if (initial.h - dy > min_sane_h && initial.vert.h + dy > min_sane_h) { initial.vert.ptr->height = initial.vert.h + dy; initial.vert.parent.ptr->height = initial.vert.parent.h - dy; } } arrange_windows(initial.vert.ptr->parent, -1, -1); } default: return; } } void pointer_mode_reset(void) { switch (pointer_state.mode) { case M_FLOATING | M_RESIZING: case M_FLOATING | M_DRAGGING: reset_initial_view(); break; case M_TILING | M_RESIZING: (void) reset_initial_sibling; break; case M_TILING | M_DRAGGING: default: break; } }