mirror of
https://codeberg.org/vyivel/dulcepan/
synced 2025-06-24 22:44:18 +03:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
5bbfbd4d84 | |||
e797e5a324 | |||
bdf8308c4e | |||
6158dd112b | |||
6f7dfab735 | |||
553147259b | |||
e81e274380 |
@ -33,7 +33,8 @@ animation-duration = 0
|
||||
# or when a whole output is selected with a mouse button.
|
||||
quick-select = false
|
||||
|
||||
# If true, dulcepan will remember selection between runs
|
||||
# If true, dulcepan will remember selection between runs.
|
||||
# The state is stored at $XDG_CACHE_HOME/dulcepan.
|
||||
persistence = true
|
||||
|
||||
# PNG (zlib) compression level, 0-9
|
||||
|
@ -1,7 +1,7 @@
|
||||
project(
|
||||
'dulcepan',
|
||||
'c',
|
||||
version: '1.0.0',
|
||||
version: '1.0.1',
|
||||
license: 'GPL-3.0-only',
|
||||
default_options: [
|
||||
'c_std=c11',
|
||||
|
@ -71,6 +71,7 @@ struct dp_output {
|
||||
void *frame_data;
|
||||
size_t frame_size;
|
||||
|
||||
// wl_buffer is NULL if uninitialized
|
||||
struct dp_buffer swapchain[DP_SWAPCHAIN_LEN];
|
||||
|
||||
struct wl_list link;
|
||||
@ -88,6 +89,7 @@ struct dp_seat {
|
||||
struct xkb_keymap *xkb_keymap;
|
||||
struct xkb_state *xkb_state;
|
||||
|
||||
uint32_t pointer_serial;
|
||||
struct wp_cursor_shape_device_v1 *cursor_shape_device;
|
||||
|
||||
// The output the pointer is on
|
||||
@ -172,6 +174,8 @@ struct dp_state {
|
||||
struct wl_shm *shm;
|
||||
struct zwlr_layer_shell_v1 *layer_shell;
|
||||
struct zwlr_screencopy_manager_v1 *screencopy_manager;
|
||||
|
||||
// Optional
|
||||
struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
|
||||
|
||||
bool initialized;
|
||||
|
18
src/main.c
18
src/main.c
@ -89,8 +89,6 @@ static void run(struct dp_state *state) {
|
||||
dp_log_fatal("The compositor has no zwlr_layer_shell_v1");
|
||||
} else if (state->screencopy_manager == NULL) {
|
||||
dp_log_fatal("The compositor has no zwlr_screencopy_manager_v1");
|
||||
} else if (state->cursor_shape_manager == NULL) {
|
||||
dp_log_fatal("The compositor has no wp_cursor_shape_manager_v1");
|
||||
}
|
||||
|
||||
if (wl_list_empty(&state->outputs)) {
|
||||
@ -150,7 +148,10 @@ static void run(struct dp_state *state) {
|
||||
wl_shm_destroy(state->shm);
|
||||
zwlr_layer_shell_v1_destroy(state->layer_shell);
|
||||
zwlr_screencopy_manager_v1_destroy(state->screencopy_manager);
|
||||
|
||||
if (state->cursor_shape_manager) {
|
||||
wp_cursor_shape_manager_v1_destroy(state->cursor_shape_manager);
|
||||
}
|
||||
|
||||
wl_registry_destroy(registry);
|
||||
}
|
||||
@ -165,12 +166,15 @@ static void help(const char *prog) {
|
||||
" -f <format> Specify the output file format.\n"
|
||||
" -o <path> Specify the output file path.\n"
|
||||
"\n"
|
||||
"If the output file path is not specified, the resuling image will be printed\n"
|
||||
"to the standard output. If the output file format is not specified, it is\n"
|
||||
"guessed from the output file path if it's specified, and assumed to be PNG\n"
|
||||
"otherwise.\n"
|
||||
"If the output file path is not specified, the resuling image will be printed to\n"
|
||||
"the standard output. If the output file format is not specified, it is guessed\n"
|
||||
"from the output file path if it's specified, and assumed to be PNG otherwise.\n"
|
||||
"\n"
|
||||
"Supported formats: png, ppm.\n",
|
||||
"Supported formats: png, ppm.\n"
|
||||
"\n"
|
||||
"Use the left or right mouse button to select a part of the output. A selection\n"
|
||||
"can also be moved and resized with the left mouse button. Clicking the middle\n"
|
||||
"mouse button selects the entire output.\n",
|
||||
prog);
|
||||
}
|
||||
|
||||
|
45
src/output.c
45
src/output.c
@ -126,6 +126,19 @@ static const struct wl_buffer_listener buffer_listener = {
|
||||
.release = buffer_handle_release,
|
||||
};
|
||||
|
||||
static void init_buffer(struct dp_output *output, struct dp_buffer *buffer) {
|
||||
int stride = output->transformed_width * 4;
|
||||
|
||||
buffer->wl_buffer =
|
||||
dp_buffer_create(output->state, output->transformed_width, output->transformed_height,
|
||||
stride, WL_SHM_FORMAT_ARGB8888, &buffer->data, &buffer->size);
|
||||
buffer->cairo_surface = cairo_image_surface_create_for_data(buffer->data, CAIRO_FORMAT_ARGB32,
|
||||
output->transformed_width, output->transformed_height, stride);
|
||||
buffer->cairo = cairo_create(buffer->cairo_surface);
|
||||
|
||||
wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer);
|
||||
}
|
||||
|
||||
static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y,
|
||||
int32_t phys_width, int32_t phys_height, int32_t subpixel, const char *make,
|
||||
const char *model, int32_t transform) {
|
||||
@ -208,19 +221,6 @@ static void output_handle_done(void *data, struct wl_output *wl_output) {
|
||||
wl_subsurface_set_desync(output->select_subsurface);
|
||||
|
||||
wl_surface_set_user_data(output->select_surface, output);
|
||||
|
||||
for (size_t i = 0; i < DP_SWAPCHAIN_LEN; i++) {
|
||||
struct dp_buffer *buffer = &output->swapchain[i];
|
||||
int stride = output->transformed_width * 4;
|
||||
buffer->wl_buffer =
|
||||
dp_buffer_create(state, output->transformed_width, output->transformed_height,
|
||||
stride, WL_SHM_FORMAT_ARGB8888, &buffer->data, &buffer->size);
|
||||
buffer->cairo_surface = cairo_image_surface_create_for_data(buffer->data,
|
||||
CAIRO_FORMAT_ARGB32, output->transformed_width, output->transformed_height, stride);
|
||||
buffer->cairo = cairo_create(buffer->cairo_surface);
|
||||
|
||||
wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void output_handle_scale(void *data, struct wl_output *wl_output, int32_t scale) {
|
||||
@ -330,6 +330,8 @@ static void redraw(struct dp_output *output) {
|
||||
if (buffer == NULL) {
|
||||
dp_log_error("Output %s: no free buffers in a swapchain\n", output->name);
|
||||
return;
|
||||
} else if (buffer->wl_buffer == NULL) {
|
||||
init_buffer(output, buffer);
|
||||
}
|
||||
buffer->used = true;
|
||||
|
||||
@ -371,15 +373,22 @@ static void redraw(struct dp_output *output) {
|
||||
}
|
||||
|
||||
cairo_stroke(buffer->cairo);
|
||||
|
||||
if (config->border_gradient != DP_BORDER_GRADIENT_NONE &&
|
||||
config->animation_duration != 0) {
|
||||
bool whole = selection->x == 0 && selection->y == 0 &&
|
||||
selection->width == output->effective_width &&
|
||||
selection->height == output->effective_height;
|
||||
if (!whole) {
|
||||
// The border is animated and visible
|
||||
output->needs_redraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cairo_rectangle(buffer->cairo, x, y, width, height);
|
||||
set_cairo_color(buffer->cairo, config->selected_color);
|
||||
cairo_fill(buffer->cairo);
|
||||
|
||||
if (config->border_gradient != DP_BORDER_GRADIENT_NONE && config->animation_duration != 0) {
|
||||
output->needs_redraw = true;
|
||||
}
|
||||
}
|
||||
|
||||
wl_surface_attach(output->select_surface, buffer->wl_buffer, 0, 0);
|
||||
@ -431,11 +440,13 @@ void dp_output_destroy(struct dp_output *output) {
|
||||
|
||||
for (size_t i = 0; i < DP_SWAPCHAIN_LEN; i++) {
|
||||
struct dp_buffer *buffer = &output->swapchain[i];
|
||||
if (buffer->wl_buffer != NULL) {
|
||||
wl_buffer_destroy(buffer->wl_buffer);
|
||||
cairo_destroy(buffer->cairo);
|
||||
cairo_surface_destroy(buffer->cairo_surface);
|
||||
munmap(buffer->data, buffer->size);
|
||||
}
|
||||
}
|
||||
|
||||
wl_subsurface_destroy(output->select_subsurface);
|
||||
wp_viewport_destroy(output->select_viewport);
|
||||
|
33
src/seat.c
33
src/seat.c
@ -123,17 +123,19 @@ static enum wp_cursor_shape_device_v1_shape get_cursor_shape(struct dp_selection
|
||||
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 update_cursor(struct dp_seat *seat) {
|
||||
if (seat->cursor_shape_device != NULL) {
|
||||
wp_cursor_shape_device_v1_set_shape(seat->cursor_shape_device, seat->pointer_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) {
|
||||
static void process_position(struct dp_seat *seat, wl_fixed_t x, wl_fixed_t y) {
|
||||
struct dp_output *output = seat->ptr_output;
|
||||
seat->ptr_x = wl_fixed_to_double(x);
|
||||
seat->ptr_y = wl_fixed_to_double(y);
|
||||
dp_select_notify_pointer_position(&seat->state->selection, output, seat->ptr_x, seat->ptr_y);
|
||||
update_cursor(seat, serial);
|
||||
update_cursor(seat);
|
||||
}
|
||||
|
||||
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
|
||||
@ -141,22 +143,24 @@ 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);
|
||||
process_position(seat, sx, sy, serial);
|
||||
seat->pointer_serial = serial;
|
||||
process_position(seat, sx, sy);
|
||||
}
|
||||
|
||||
static void pointer_handle_leave(
|
||||
void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) {
|
||||
struct dp_seat *seat = data;
|
||||
seat->ptr_output = NULL;
|
||||
seat->pointer_serial = serial;
|
||||
}
|
||||
|
||||
static void pointer_handle_motion(
|
||||
void *data, struct wl_pointer *wl_pointer, uint32_t serial, wl_fixed_t sx, wl_fixed_t sy) {
|
||||
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time_msec,
|
||||
wl_fixed_t sx, wl_fixed_t sy) {
|
||||
struct dp_seat *seat = data;
|
||||
if (seat->ptr_output == NULL) {
|
||||
return; // Shouldn't happen
|
||||
}
|
||||
process_position(seat, sx, sy, serial);
|
||||
process_position(seat, sx, sy);
|
||||
}
|
||||
|
||||
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
|
||||
@ -164,6 +168,8 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
|
||||
struct dp_seat *seat = data;
|
||||
struct dp_state *state = seat->state;
|
||||
|
||||
seat->pointer_serial = serial;
|
||||
|
||||
struct dp_selection *selection = &state->selection;
|
||||
if (button_state != WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
if (selection->width > 0 && selection->height > 0 && state->config.quick_select) {
|
||||
@ -182,14 +188,14 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
|
||||
case BTN_RIGHT:
|
||||
dp_select_start_interactive(
|
||||
selection, seat->ptr_output, seat->ptr_x, seat->ptr_y, button == BTN_LEFT);
|
||||
update_cursor(seat, serial);
|
||||
update_cursor(seat);
|
||||
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);
|
||||
update_cursor(seat);
|
||||
|
||||
if (state->config.quick_select) {
|
||||
state->status = DP_STATUS_SAVED;
|
||||
@ -220,10 +226,13 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32
|
||||
if ((caps & WL_SEAT_CAPABILITY_POINTER) != 0 && seat->pointer == NULL) {
|
||||
seat->pointer = wl_seat_get_pointer(wl_seat);
|
||||
wl_pointer_add_listener(seat->pointer, &pointer_listener, seat);
|
||||
|
||||
if (seat->state->cursor_shape_manager != NULL) {
|
||||
seat->cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(
|
||||
seat->state->cursor_shape_manager, seat->pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) {
|
||||
// Ignored
|
||||
@ -256,6 +265,8 @@ void dp_seat_destroy(struct dp_seat *seat) {
|
||||
}
|
||||
if (seat->pointer != NULL) {
|
||||
wl_pointer_release(seat->pointer);
|
||||
}
|
||||
if (seat->cursor_shape_device != NULL) {
|
||||
wp_cursor_shape_device_v1_destroy(seat->cursor_shape_device);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user