1
0
mirror of https://codeberg.org/vyivel/dulcepan/ synced 2025-06-24 22:44:18 +03:00

9 Commits

6 changed files with 86 additions and 44 deletions

View File

@ -33,7 +33,8 @@ animation-duration = 0
# or when a whole output is selected with a mouse button. # or when a whole output is selected with a mouse button.
quick-select = false 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 persistence = true
# PNG (zlib) compression level, 0-9 # PNG (zlib) compression level, 0-9

View File

@ -1,7 +1,7 @@
project( project(
'dulcepan', 'dulcepan',
'c', 'c',
version: '1.0.0', version: '1.0.2',
license: 'GPL-3.0-only', license: 'GPL-3.0-only',
default_options: [ default_options: [
'c_std=c11', 'c_std=c11',

View File

@ -71,6 +71,7 @@ struct dp_output {
void *frame_data; void *frame_data;
size_t frame_size; size_t frame_size;
// wl_buffer is NULL if uninitialized
struct dp_buffer swapchain[DP_SWAPCHAIN_LEN]; struct dp_buffer swapchain[DP_SWAPCHAIN_LEN];
struct wl_list link; struct wl_list link;
@ -88,6 +89,7 @@ struct dp_seat {
struct xkb_keymap *xkb_keymap; struct xkb_keymap *xkb_keymap;
struct xkb_state *xkb_state; struct xkb_state *xkb_state;
uint32_t pointer_serial;
struct wp_cursor_shape_device_v1 *cursor_shape_device; struct wp_cursor_shape_device_v1 *cursor_shape_device;
// The output the pointer is on // The output the pointer is on
@ -172,6 +174,8 @@ struct dp_state {
struct wl_shm *shm; struct wl_shm *shm;
struct zwlr_layer_shell_v1 *layer_shell; struct zwlr_layer_shell_v1 *layer_shell;
struct zwlr_screencopy_manager_v1 *screencopy_manager; struct zwlr_screencopy_manager_v1 *screencopy_manager;
// Optional
struct wp_cursor_shape_manager_v1 *cursor_shape_manager; struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
bool initialized; bool initialized;

View File

@ -89,8 +89,6 @@ static void run(struct dp_state *state) {
dp_log_fatal("The compositor has no zwlr_layer_shell_v1"); dp_log_fatal("The compositor has no zwlr_layer_shell_v1");
} else if (state->screencopy_manager == NULL) { } else if (state->screencopy_manager == NULL) {
dp_log_fatal("The compositor has no zwlr_screencopy_manager_v1"); 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)) { if (wl_list_empty(&state->outputs)) {
@ -150,7 +148,10 @@ static void run(struct dp_state *state) {
wl_shm_destroy(state->shm); wl_shm_destroy(state->shm);
zwlr_layer_shell_v1_destroy(state->layer_shell); zwlr_layer_shell_v1_destroy(state->layer_shell);
zwlr_screencopy_manager_v1_destroy(state->screencopy_manager); zwlr_screencopy_manager_v1_destroy(state->screencopy_manager);
if (state->cursor_shape_manager) {
wp_cursor_shape_manager_v1_destroy(state->cursor_shape_manager); wp_cursor_shape_manager_v1_destroy(state->cursor_shape_manager);
}
wl_registry_destroy(registry); wl_registry_destroy(registry);
} }
@ -165,12 +166,15 @@ static void help(const char *prog) {
" -f <format> Specify the output file format.\n" " -f <format> Specify the output file format.\n"
" -o <path> Specify the output file path.\n" " -o <path> Specify the output file path.\n"
"\n" "\n"
"If the output file path is not specified, the resuling image will be printed\n" "If the output file path is not specified, the resuling image will be printed to\n"
"to the standard output. If the output file format is not specified, it is\n" "the standard output. If the output file format is not specified, it is guessed\n"
"guessed from the output file path if it's specified, and assumed to be PNG\n" "from the output file path if it's specified, and assumed to be PNG otherwise.\n"
"otherwise.\n"
"\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); prog);
} }

View File

@ -79,6 +79,12 @@ static void frame_handle_buffer(void *data, struct zwlr_screencopy_frame_v1 *fra
wl_buffer_destroy(output->frame_buffer); wl_buffer_destroy(output->frame_buffer);
} }
if ((int32_t)width != output->width || (int32_t)height != output->height) {
dp_log_fatal("Output/buffer size mismatch: mode=%" PRIi32 "x%" PRIi32 ", buffer=%" PRIu32
"x%" PRIu32,
output->width, output->height, width, height);
}
output->frame_format = format; output->frame_format = format;
output->frame_stride = (int)stride; output->frame_stride = (int)stride;
output->frame_buffer = dp_buffer_create(output->state, output->width, output->height, output->frame_buffer = dp_buffer_create(output->state, output->width, output->height,
@ -126,6 +132,19 @@ static const struct wl_buffer_listener buffer_listener = {
.release = buffer_handle_release, .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, 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, int32_t phys_width, int32_t phys_height, int32_t subpixel, const char *make,
const char *model, int32_t transform) { const char *model, int32_t transform) {
@ -143,6 +162,10 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output, int3
static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
int32_t width, int32_t height, int32_t refresh) { int32_t width, int32_t height, int32_t refresh) {
if ((flags & WL_OUTPUT_MODE_CURRENT) == 0) {
return;
}
struct dp_output *output = data; struct dp_output *output = data;
if (output->has_geom) { if (output->has_geom) {
@ -165,6 +188,7 @@ static void output_handle_done(void *data, struct wl_output *wl_output) {
assert(output->name != NULL); assert(output->name != NULL);
assert(output->width > 0 && output->height > 0);
if ((output->transform & WL_OUTPUT_TRANSFORM_90) != 0) { if ((output->transform & WL_OUTPUT_TRANSFORM_90) != 0) {
output->transformed_width = output->height; output->transformed_width = output->height;
output->transformed_height = output->width; output->transformed_height = output->width;
@ -208,19 +232,6 @@ static void output_handle_done(void *data, struct wl_output *wl_output) {
wl_subsurface_set_desync(output->select_subsurface); wl_subsurface_set_desync(output->select_subsurface);
wl_surface_set_user_data(output->select_surface, output); 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) { static void output_handle_scale(void *data, struct wl_output *wl_output, int32_t scale) {
@ -330,6 +341,8 @@ static void redraw(struct dp_output *output) {
if (buffer == NULL) { if (buffer == NULL) {
dp_log_error("Output %s: no free buffers in a swapchain\n", output->name); dp_log_error("Output %s: no free buffers in a swapchain\n", output->name);
return; return;
} else if (buffer->wl_buffer == NULL) {
init_buffer(output, buffer);
} }
buffer->used = true; buffer->used = true;
@ -371,15 +384,22 @@ static void redraw(struct dp_output *output) {
} }
cairo_stroke(buffer->cairo); 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); cairo_rectangle(buffer->cairo, x, y, width, height);
set_cairo_color(buffer->cairo, config->selected_color); set_cairo_color(buffer->cairo, config->selected_color);
cairo_fill(buffer->cairo); 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); wl_surface_attach(output->select_surface, buffer->wl_buffer, 0, 0);
@ -431,11 +451,13 @@ void dp_output_destroy(struct dp_output *output) {
for (size_t i = 0; i < DP_SWAPCHAIN_LEN; i++) { for (size_t i = 0; i < DP_SWAPCHAIN_LEN; i++) {
struct dp_buffer *buffer = &output->swapchain[i]; struct dp_buffer *buffer = &output->swapchain[i];
if (buffer->wl_buffer != NULL) {
wl_buffer_destroy(buffer->wl_buffer); wl_buffer_destroy(buffer->wl_buffer);
cairo_destroy(buffer->cairo); cairo_destroy(buffer->cairo);
cairo_surface_destroy(buffer->cairo_surface); cairo_surface_destroy(buffer->cairo_surface);
munmap(buffer->data, buffer->size); munmap(buffer->data, buffer->size);
} }
}
wl_subsurface_destroy(output->select_subsurface); wl_subsurface_destroy(output->select_subsurface);
wp_viewport_destroy(output->select_viewport); wp_viewport_destroy(output->select_viewport);

View File

@ -123,17 +123,19 @@ static enum wp_cursor_shape_device_v1_shape get_cursor_shape(struct dp_selection
abort(); // Unreachable abort(); // Unreachable
} }
static void update_cursor(struct dp_seat *seat, uint32_t serial) { static void update_cursor(struct dp_seat *seat) {
wp_cursor_shape_device_v1_set_shape( if (seat->cursor_shape_device != NULL) {
seat->cursor_shape_device, serial, get_cursor_shape(&seat->state->selection)); 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; struct dp_output *output = seat->ptr_output;
seat->ptr_x = wl_fixed_to_double(x); seat->ptr_x = wl_fixed_to_double(x);
seat->ptr_y = wl_fixed_to_double(y); seat->ptr_y = wl_fixed_to_double(y);
dp_select_notify_pointer_position(&seat->state->selection, output, seat->ptr_x, seat->ptr_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, 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; 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);
process_position(seat, sx, sy, serial); seat->pointer_serial = serial;
process_position(seat, sx, sy);
} }
static void pointer_handle_leave( static void pointer_handle_leave(
void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) {
struct dp_seat *seat = data; struct dp_seat *seat = data;
seat->ptr_output = NULL; seat->ptr_output = NULL;
seat->pointer_serial = serial;
} }
static void pointer_handle_motion( static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time_msec,
void *data, struct wl_pointer *wl_pointer, uint32_t serial, wl_fixed_t sx, wl_fixed_t sy) { wl_fixed_t sx, wl_fixed_t sy) {
struct dp_seat *seat = data; struct dp_seat *seat = data;
if (seat->ptr_output == NULL) { if (seat->ptr_output == NULL) {
return; // Shouldn't happen 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, 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_seat *seat = data;
struct dp_state *state = seat->state; struct dp_state *state = seat->state;
seat->pointer_serial = serial;
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->width > 0 && selection->height > 0 && state->config.quick_select) { 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: case BTN_RIGHT:
dp_select_start_interactive( dp_select_start_interactive(
selection, seat->ptr_output, seat->ptr_x, seat->ptr_y, button == BTN_LEFT); selection, seat->ptr_output, seat->ptr_x, seat->ptr_y, button == BTN_LEFT);
update_cursor(seat, serial); update_cursor(seat);
break; break;
case BTN_MIDDLE: 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_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); 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) { if (state->config.quick_select) {
state->status = DP_STATUS_SAVED; state->status = DP_STATUS_SAVED;
@ -220,9 +226,12 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32
if ((caps & WL_SEAT_CAPABILITY_POINTER) != 0 && seat->pointer == NULL) { if ((caps & WL_SEAT_CAPABILITY_POINTER) != 0 && seat->pointer == NULL) {
seat->pointer = wl_seat_get_pointer(wl_seat); seat->pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(seat->pointer, &pointer_listener, 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->cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(
seat->state->cursor_shape_manager, seat->pointer); seat->state->cursor_shape_manager, seat->pointer);
} }
}
} }
static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) { static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) {
@ -256,6 +265,8 @@ void dp_seat_destroy(struct dp_seat *seat) {
} }
if (seat->pointer != NULL) { if (seat->pointer != NULL) {
wl_pointer_release(seat->pointer); wl_pointer_release(seat->pointer);
}
if (seat->cursor_shape_device != NULL) {
wp_cursor_shape_device_v1_destroy(seat->cursor_shape_device); wp_cursor_shape_device_v1_destroy(seat->cursor_shape_device);
} }