mirror of
https://codeberg.org/vyivel/dulcepan/
synced 2025-12-17 15:45:12 +02:00
Rework mouse controls
This commit is contained in:
@@ -86,13 +86,32 @@ struct dp_seat {
|
||||
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_output *output; // May be NULL
|
||||
int x, y, width, height;
|
||||
|
||||
struct dp_output *interactive_output; // NULL if not interactively selecting
|
||||
int interactive_x, interactive_y;
|
||||
bool interactive_moved;
|
||||
enum dp_selection_action action;
|
||||
bool action_active;
|
||||
// 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 {
|
||||
@@ -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_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);
|
||||
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_save(struct dp_state *state);
|
||||
|
||||
74
src/seat.c
74
src/seat.c
@@ -85,12 +85,58 @@ static const struct wl_keyboard_listener keyboard_listener = {
|
||||
.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;
|
||||
seat->ptr_x =
|
||||
(int)(wl_fixed_to_double(x) * output->transformed_width / output->effective_width);
|
||||
seat->ptr_y =
|
||||
(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,
|
||||
@@ -98,9 +144,7 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint
|
||||
struct dp_seat *seat = data;
|
||||
seat->ptr_output = wl_surface_get_user_data(surface);
|
||||
assert(seat->ptr_output != NULL);
|
||||
save_position(seat, sx, sy);
|
||||
wp_cursor_shape_device_v1_set_shape(
|
||||
seat->cursor_shape_device, serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
|
||||
process_position(seat, sx, sy, serial);
|
||||
}
|
||||
|
||||
static void pointer_handle_leave(
|
||||
@@ -115,8 +159,7 @@ static void pointer_handle_motion(
|
||||
if (seat->ptr_output == NULL) {
|
||||
return; // Shouldn't happen
|
||||
}
|
||||
save_position(seat, sx, sy);
|
||||
dp_select_interactive_move(&seat->state->selection, seat->ptr_x, seat->ptr_y);
|
||||
process_position(seat, sx, sy, 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;
|
||||
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;
|
||||
}
|
||||
dp_select_interactive_stop(selection);
|
||||
dp_select_stop_interactive(selection);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -139,17 +182,22 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
|
||||
|
||||
switch (button) {
|
||||
case BTN_LEFT:
|
||||
dp_select_interactive_start(selection, seat->ptr_output, seat->ptr_x, seat->ptr_y);
|
||||
break;
|
||||
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() 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) {
|
||||
state->status = DP_STATUS_SAVED;
|
||||
}
|
||||
break;
|
||||
case BTN_MIDDLE:
|
||||
state->status = DP_STATUS_SAVED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
206
src/select.c
206
src/select.c
@@ -1,5 +1,14 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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) {
|
||||
if (selection->output == output) {
|
||||
return;
|
||||
@@ -11,44 +20,183 @@ static void set_selected_output(struct dp_selection *selection, struct dp_output
|
||||
}
|
||||
}
|
||||
|
||||
void dp_select_interactive_start(
|
||||
struct dp_selection *selection, struct dp_output *output, int x, int y) {
|
||||
selection->interactive_output = output;
|
||||
selection->interactive_x = x;
|
||||
selection->interactive_y = y;
|
||||
selection->interactive_moved = false;
|
||||
static void update_action(struct dp_selection *selection, struct dp_output *output, int x, int y) {
|
||||
if (output == selection->output) {
|
||||
int sx = x - selection->x;
|
||||
int sy = y - selection->y;
|
||||
|
||||
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) {
|
||||
if (selection->interactive_output == NULL) {
|
||||
static void do_resize(struct dp_selection *selection, int x, int y) {
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
void dp_select_interactive_stop(struct dp_selection *selection) {
|
||||
selection->interactive_output = NULL;
|
||||
selection->interactive_moved = false;
|
||||
static void do_move(struct dp_selection *selection, int x, int y) {
|
||||
int new_x = x - selection->ptr_off_x, new_y = y - selection->ptr_off_y;
|
||||
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user