1
0
mirror of https://codeberg.org/vyivel/dulcepan/ synced 2025-12-17 23:55:12 +02:00

Rework mouse controls

This commit is contained in:
Kirill Primak
2024-06-21 14:55:30 +03:00
parent 164594603e
commit 8e8cdf1f32
3 changed files with 266 additions and 48 deletions

View File

@@ -86,13 +86,32 @@ struct dp_seat {
struct wl_list link; struct wl_list link;
}; };
enum dp_selection_action {
DP_SELECTION_ACTION_NONE,
DP_SELECTION_ACTION_RESIZING,
DP_SELECTION_ACTION_MOVING,
};
enum dp_resize_edges {
DP_EDGE_NONE = 0,
DP_EDGE_TOP = 1 << 0,
DP_EDGE_BOTTOM = 1 << 1,
DP_EDGE_LEFT = 1 << 2,
DP_EDGE_RIGHT = 1 << 3,
};
struct dp_selection { struct dp_selection {
struct dp_output *output; // May be NULL struct dp_output *output; // May be NULL
int x, y, width, height; int x, y, width, height;
struct dp_output *interactive_output; // NULL if not interactively selecting enum dp_selection_action action;
int interactive_x, interactive_y; bool action_active;
bool interactive_moved; // Pointer offset to apply during movement
int ptr_off_x, ptr_off_y;
int resize_edges;
// Resize anchor
int resize_x, resize_y;
}; };
enum dp_file_format { enum dp_file_format {
@@ -161,10 +180,13 @@ void dp_output_hide_surface(struct dp_output *output);
void dp_seat_create(struct dp_state *state, uint32_t name, struct wl_seat *wl_seat); void dp_seat_create(struct dp_state *state, uint32_t name, struct wl_seat *wl_seat);
void dp_seat_destroy(struct dp_seat *seat); void dp_seat_destroy(struct dp_seat *seat);
void dp_select_interactive_start( void dp_select_start_interactive(struct dp_selection *selection, struct dp_output *output, int x,
int y, bool modify_existing);
void dp_select_stop_interactive(struct dp_selection *selection);
void dp_select_notify_pointer_position(
struct dp_selection *selection, struct dp_output *output, int x, int y); struct dp_selection *selection, struct dp_output *output, int x, int y);
void dp_select_interactive_move(struct dp_selection *selection, int x, int y);
void dp_select_interactive_stop(struct dp_selection *selection);
void dp_select_whole(struct dp_selection *selection, struct dp_output *output); void dp_select_whole(struct dp_selection *selection, struct dp_output *output);
void dp_save(struct dp_state *state); void dp_save(struct dp_state *state);

View File

@@ -85,12 +85,58 @@ static const struct wl_keyboard_listener keyboard_listener = {
.modifiers = keyboard_handle_modifiers, .modifiers = keyboard_handle_modifiers,
}; };
static void save_position(struct dp_seat *seat, wl_fixed_t x, wl_fixed_t y) { static enum wp_cursor_shape_device_v1_shape get_cursor_shape(struct dp_selection *selection) {
switch (selection->action) {
case DP_SELECTION_ACTION_NONE:
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR;
case DP_SELECTION_ACTION_RESIZING:
if (selection->width == 0 || selection->height == 0) {
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR;
}
switch (selection->resize_edges) {
case DP_EDGE_TOP:
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE;
case DP_EDGE_TOP | DP_EDGE_LEFT:
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE;
case DP_EDGE_TOP | DP_EDGE_RIGHT:
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE;
case DP_EDGE_BOTTOM:
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE;
case DP_EDGE_BOTTOM | DP_EDGE_LEFT:
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE;
case DP_EDGE_BOTTOM | DP_EDGE_RIGHT:
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE;
case DP_EDGE_LEFT:
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE;
case DP_EDGE_RIGHT:
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE;
}
break;
case DP_SELECTION_ACTION_MOVING:
if (selection->x == 0 && selection->y == 0 &&
selection->width == selection->output->width &&
selection->height == selection->output->height) {
// Moving is impossible
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT;
}
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE;
}
abort(); // Unreachable
}
static void update_cursor(struct dp_seat *seat, uint32_t serial) {
wp_cursor_shape_device_v1_set_shape(
seat->cursor_shape_device, serial, get_cursor_shape(&seat->state->selection));
}
static void process_position(struct dp_seat *seat, wl_fixed_t x, wl_fixed_t y, uint32_t serial) {
struct dp_output *output = seat->ptr_output; struct dp_output *output = seat->ptr_output;
seat->ptr_x = seat->ptr_x =
(int)(wl_fixed_to_double(x) * output->transformed_width / output->effective_width); (int)(wl_fixed_to_double(x) * output->transformed_width / output->effective_width);
seat->ptr_y = seat->ptr_y =
(int)(wl_fixed_to_double(y) * output->transformed_height / output->effective_height); (int)(wl_fixed_to_double(y) * output->transformed_height / output->effective_height);
dp_select_notify_pointer_position(&seat->state->selection, output, seat->ptr_x, seat->ptr_y);
update_cursor(seat, serial);
} }
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
@@ -98,9 +144,7 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint
struct dp_seat *seat = data; struct dp_seat *seat = data;
seat->ptr_output = wl_surface_get_user_data(surface); seat->ptr_output = wl_surface_get_user_data(surface);
assert(seat->ptr_output != NULL); assert(seat->ptr_output != NULL);
save_position(seat, sx, sy); process_position(seat, sx, sy, serial);
wp_cursor_shape_device_v1_set_shape(
seat->cursor_shape_device, serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
} }
static void pointer_handle_leave( static void pointer_handle_leave(
@@ -115,8 +159,7 @@ static void pointer_handle_motion(
if (seat->ptr_output == NULL) { if (seat->ptr_output == NULL) {
return; // Shouldn't happen return; // Shouldn't happen
} }
save_position(seat, sx, sy); process_position(seat, sx, sy, serial);
dp_select_interactive_move(&seat->state->selection, seat->ptr_x, seat->ptr_y);
} }
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
@@ -126,10 +169,10 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
struct dp_selection *selection = &state->selection; struct dp_selection *selection = &state->selection;
if (button_state != WL_POINTER_BUTTON_STATE_PRESSED) { if (button_state != WL_POINTER_BUTTON_STATE_PRESSED) {
if (selection->interactive_moved && state->config.quick_select) { if (selection->width > 0 && selection->height > 0 && state->config.quick_select) {
state->status = DP_STATUS_SAVED; state->status = DP_STATUS_SAVED;
} }
dp_select_interactive_stop(selection); dp_select_stop_interactive(selection);
return; return;
} }
@@ -139,17 +182,22 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
switch (button) { switch (button) {
case BTN_LEFT: case BTN_LEFT:
dp_select_interactive_start(selection, seat->ptr_output, seat->ptr_x, seat->ptr_y);
break;
case BTN_RIGHT: case BTN_RIGHT:
dp_select_start_interactive(
selection, seat->ptr_output, seat->ptr_x, seat->ptr_y, button == BTN_LEFT);
update_cursor(seat, serial);
break;
case BTN_MIDDLE:
dp_select_whole(selection, seat->ptr_output); dp_select_whole(selection, seat->ptr_output);
// dp_select_whole() doesn't invalidate the interactive state, so do it manually
dp_select_notify_pointer_position(selection, seat->ptr_output, seat->ptr_x, seat->ptr_y);
update_cursor(seat, serial);
if (state->config.quick_select) { if (state->config.quick_select) {
state->status = DP_STATUS_SAVED; state->status = DP_STATUS_SAVED;
} }
break; break;
case BTN_MIDDLE:
state->status = DP_STATUS_SAVED;
return;
} }
} }

View File

@@ -1,5 +1,14 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "dulcepan.h" #include "dulcepan.h"
#define RESIZE_INNER_SIZE 4
#define RESIZE_OUTER_SIZE 12
#define RESIZE_CORNER_SIZE 24
#define RESIZE_THRESHOLD (RESIZE_OUTER_SIZE + RESIZE_CORNER_SIZE)
static void set_selected_output(struct dp_selection *selection, struct dp_output *output) { static void set_selected_output(struct dp_selection *selection, struct dp_output *output) {
if (selection->output == output) { if (selection->output == output) {
return; return;
@@ -11,44 +20,183 @@ static void set_selected_output(struct dp_selection *selection, struct dp_output
} }
} }
void dp_select_interactive_start( static void update_action(struct dp_selection *selection, struct dp_output *output, int x, int y) {
struct dp_selection *selection, struct dp_output *output, int x, int y) { if (output == selection->output) {
selection->interactive_output = output; int sx = x - selection->x;
selection->interactive_x = x; int sy = y - selection->y;
selection->interactive_y = y;
selection->interactive_moved = false;
dp_output_redraw(output); if (sx >= RESIZE_INNER_SIZE && sy >= RESIZE_INNER_SIZE &&
sx < selection->width - RESIZE_INNER_SIZE &&
sy <= selection->height - RESIZE_INNER_SIZE) {
selection->action = DP_SELECTION_ACTION_MOVING;
return;
} else if (sx >= -RESIZE_OUTER_SIZE && sy >= -RESIZE_OUTER_SIZE &&
sx < selection->width + RESIZE_OUTER_SIZE &&
sy < selection->height + RESIZE_OUTER_SIZE) {
int edges = DP_EDGE_NONE;
if (sx >= selection->width - RESIZE_THRESHOLD && sx >= selection->width / 2) {
edges |= DP_EDGE_RIGHT;
} else if (sx < RESIZE_THRESHOLD) {
edges |= DP_EDGE_LEFT;
}
if (sy >= selection->height - RESIZE_THRESHOLD && sy >= selection->height / 2) {
edges |= DP_EDGE_BOTTOM;
} else if (sy < RESIZE_THRESHOLD) {
edges |= DP_EDGE_TOP;
}
if (edges != DP_EDGE_NONE) {
selection->action = DP_SELECTION_ACTION_RESIZING;
selection->resize_edges = edges;
return;
}
}
}
selection->action = DP_SELECTION_ACTION_NONE;
} }
void dp_select_interactive_move(struct dp_selection *selection, int x, int y) { static void do_resize(struct dp_selection *selection, int x, int y) {
if (selection->interactive_output == NULL) { int ptr_x = x - selection->ptr_off_x, ptr_y = y - selection->ptr_off_y;
int width = selection->width, height = selection->height;
retry_horiz:
if ((selection->resize_edges & DP_EDGE_LEFT) != 0) {
width = selection->resize_x - ptr_x;
if (width < 0) {
selection->resize_edges = (selection->resize_edges & ~DP_EDGE_LEFT) | DP_EDGE_RIGHT;
goto retry_horiz;
}
selection->x = selection->resize_x - width;
} else if ((selection->resize_edges & DP_EDGE_RIGHT) != 0) {
width = ptr_x - selection->resize_x;
if (width < 0) {
selection->resize_edges = (selection->resize_edges & ~DP_EDGE_RIGHT) | DP_EDGE_LEFT;
goto retry_horiz;
}
selection->x = selection->resize_x;
}
retry_verti:
if ((selection->resize_edges & DP_EDGE_TOP) != 0) {
height = selection->resize_y - ptr_y;
if (height < 0) {
selection->resize_edges = (selection->resize_edges & ~DP_EDGE_TOP) | DP_EDGE_BOTTOM;
goto retry_verti;
}
selection->y = selection->resize_y - height;
} else if ((selection->resize_edges & DP_EDGE_BOTTOM) != 0) {
height = ptr_y - selection->resize_y;
if (height < 0) {
selection->resize_edges = (selection->resize_edges & ~DP_EDGE_BOTTOM) | DP_EDGE_TOP;
goto retry_verti;
}
selection->y = selection->resize_y;
}
if (width == selection->width && height == selection->height) {
return; return;
} }
set_selected_output(selection, selection->interactive_output);
selection->interactive_moved = true;
if (x < selection->interactive_x) {
selection->x = x;
selection->width = selection->interactive_x - x;
} else {
selection->x = selection->interactive_x;
selection->width = x - selection->interactive_x;
}
if (y < selection->interactive_y) {
selection->y = y;
selection->height = selection->interactive_y - y;
} else {
selection->y = selection->interactive_y;
selection->height = y - selection->interactive_y;
}
selection->width = width;
selection->height = height;
dp_output_redraw(selection->output); dp_output_redraw(selection->output);
} }
void dp_select_interactive_stop(struct dp_selection *selection) { static void do_move(struct dp_selection *selection, int x, int y) {
selection->interactive_output = NULL; int new_x = x - selection->ptr_off_x, new_y = y - selection->ptr_off_y;
selection->interactive_moved = false;
if (new_x < 0) {
new_x = 0;
} else if (new_x > selection->output->width - selection->width) {
new_x = selection->output->width - selection->width;
}
if (new_y < 0) {
new_y = 0;
} else if (new_y > selection->output->height - selection->height) {
new_y = selection->output->height - selection->height;
}
if (new_x == selection->x && new_y == selection->y) {
return;
}
selection->x = new_x;
selection->y = new_y;
dp_output_redraw(selection->output);
}
static void init_resize(struct dp_selection *selection, int x, int y) {
if ((selection->resize_edges & DP_EDGE_RIGHT) != 0) {
selection->ptr_off_x = x - selection->x - selection->width;
selection->resize_x = selection->x;
} else {
selection->ptr_off_x = x - selection->x;
selection->resize_x = selection->x + selection->width;
}
if ((selection->resize_edges & DP_EDGE_BOTTOM) != 0) {
selection->ptr_off_y = y - selection->y - selection->height;
selection->resize_y = selection->y;
} else {
selection->ptr_off_y = y - selection->y;
selection->resize_y = selection->y + selection->height;
}
}
void dp_select_start_interactive(struct dp_selection *selection, struct dp_output *output, int x,
int y, bool modify_existing) {
update_action(selection, output, x, y);
selection->action_active = true;
if (modify_existing) {
switch (selection->action) {
case DP_SELECTION_ACTION_NONE:
break;
case DP_SELECTION_ACTION_RESIZING:
init_resize(selection, x, y);
return;
case DP_SELECTION_ACTION_MOVING:
selection->ptr_off_x = x - selection->x;
selection->ptr_off_y = y - selection->y;
return;
}
}
// Start a new selection
set_selected_output(selection, output);
selection->x = x;
selection->y = y;
selection->width = 0;
selection->height = 0;
selection->action = DP_SELECTION_ACTION_RESIZING;
selection->resize_edges = DP_EDGE_BOTTOM | DP_EDGE_RIGHT;
init_resize(selection, x, y);
}
void dp_select_stop_interactive(struct dp_selection *selection) {
selection->action_active = false;
}
void dp_select_notify_pointer_position(
struct dp_selection *selection, struct dp_output *output, int x, int y) {
if (selection->action_active) {
switch (selection->action) {
case DP_SELECTION_ACTION_NONE:
abort(); // Unreachable
case DP_SELECTION_ACTION_RESIZING:
do_resize(selection, x, y);
break;
case DP_SELECTION_ACTION_MOVING:
do_move(selection, x, y);
break;
}
} else {
update_action(selection, output, x, y);
}
} }
void dp_select_whole(struct dp_selection *selection, struct dp_output *output) { void dp_select_whole(struct dp_selection *selection, struct dp_output *output) {