mirror of
https://github.com/jtheoof/swappy.git
synced 2024-08-17 02:30:47 +03:00
refactor(wayland): make all wayland code optional
This commit is contained in:
parent
c4ea305ae6
commit
cf33f3a4fb
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -17,6 +17,7 @@
|
||||
"ostream": "c",
|
||||
"sstream": "c",
|
||||
"streambuf": "c",
|
||||
"clipboard.h": "c"
|
||||
"clipboard.h": "c",
|
||||
"buffer.h": "c"
|
||||
}
|
||||
}
|
@ -94,13 +94,14 @@ Install dependencies:
|
||||
|
||||
- meson
|
||||
- wayland
|
||||
- wayland-protocols
|
||||
- cairo
|
||||
- gtk
|
||||
|
||||
Optional dependencies:
|
||||
|
||||
- libnotify
|
||||
- wayland-protocols (for the `-g` option to work with `screencopy` protocol).
|
||||
- libnotify (not get notified when swappshot is copied or saved).
|
||||
- scdoc to generate the man page
|
||||
|
||||
Then run:
|
||||
|
||||
|
@ -4,24 +4,12 @@
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "swappy.h"
|
||||
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
||||
|
||||
void screencopy_frame_handle_buffer(void *data,
|
||||
struct zwlr_screencopy_frame_v1 *frame,
|
||||
uint32_t format, uint32_t width,
|
||||
uint32_t height, uint32_t stride);
|
||||
void screencopy_frame_handle_flags(void *data,
|
||||
struct zwlr_screencopy_frame_v1 *frame,
|
||||
uint32_t flags);
|
||||
void screencopy_frame_handle_ready(void *data,
|
||||
struct zwlr_screencopy_frame_v1 *frame,
|
||||
uint32_t tv_sec_hi, uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec);
|
||||
void screencopy_frame_handle_failed(void *data,
|
||||
struct zwlr_screencopy_frame_v1 *frame);
|
||||
void screencopy_destroy_buffer(struct swappy_buffer *buffer);
|
||||
void buffer_wayland_destroy(struct swappy_buffer *buffer);
|
||||
|
||||
bool buffer_init_from_screencopy(struct swappy_state *state);
|
||||
bool buffer_init_from_file(struct swappy_state *state);
|
||||
|
||||
bool buffer_parse_geometry(struct swappy_state *state);
|
||||
|
||||
void buffer_free_all(struct swappy_state *state);
|
||||
|
@ -6,9 +6,11 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <wayland-client.h>
|
||||
#ifdef HAVE_WAYLAND_PROTOCOLS
|
||||
#include <xdg-output-unstable-v1-client-protocol.h>
|
||||
#endif
|
||||
|
||||
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
||||
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||
|
||||
#define MAX_PATH 4096
|
||||
|
||||
@ -100,23 +102,56 @@ struct swappy_state_ui {
|
||||
GtkButton *stroke_size;
|
||||
};
|
||||
|
||||
struct swappy_state {
|
||||
GtkApplication *app;
|
||||
struct swappy_buffer {
|
||||
struct wl_buffer *wl_buffer;
|
||||
void *data;
|
||||
int32_t width, height, stride;
|
||||
size_t size;
|
||||
enum wl_shm_format format;
|
||||
};
|
||||
|
||||
struct swappy_state_ui *ui;
|
||||
struct swappy_output {
|
||||
struct swappy_state *state;
|
||||
struct swappy_box geometry;
|
||||
struct swappy_box logical_geometry;
|
||||
struct wl_output *wl_output;
|
||||
struct wl_list link;
|
||||
int32_t scale;
|
||||
struct swappy_buffer *buffer;
|
||||
|
||||
cairo_surface_t *cairo_surface;
|
||||
cairo_surface_t *image_surface;
|
||||
double logical_scale; // guessed from the logical size
|
||||
char *name;
|
||||
|
||||
enum wl_output_transform transform;
|
||||
struct zwlr_screencopy_frame_v1 *screencopy_frame;
|
||||
uint32_t screencopy_frame_flags; // enum zwlr_screencopy_frame_v1_flags
|
||||
|
||||
#ifdef HAVE_WAYLAND_PROTOCOLS
|
||||
struct zxdg_output_v1 *xdg_output;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct swappy_wayland {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_shm *shm;
|
||||
struct zxdg_output_manager_v1 *xdg_output_manager;
|
||||
struct wl_list outputs;
|
||||
struct zwlr_screencopy_manager_v1 *zwlr_screencopy_manager;
|
||||
struct wl_list outputs; // mako_output::link
|
||||
|
||||
size_t n_done;
|
||||
#ifdef HAVE_WAYLAND_PROTOCOLS
|
||||
struct zxdg_output_manager_v1 *xdg_output_manager;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct swappy_state {
|
||||
GtkApplication *app;
|
||||
|
||||
struct swappy_state_ui *ui;
|
||||
struct swappy_wayland *wl;
|
||||
|
||||
cairo_surface_t *cairo_surface;
|
||||
GList *patterns; // List of cairo_pattern_t
|
||||
|
||||
char *storage_path;
|
||||
|
||||
@ -137,30 +172,3 @@ struct swappy_state {
|
||||
int argc;
|
||||
char **argv;
|
||||
};
|
||||
|
||||
struct swappy_buffer {
|
||||
struct wl_buffer *wl_buffer;
|
||||
void *data;
|
||||
int32_t width, height, stride;
|
||||
size_t size;
|
||||
enum wl_shm_format format;
|
||||
};
|
||||
|
||||
struct swappy_output {
|
||||
struct swappy_state *state;
|
||||
struct wl_output *wl_output;
|
||||
struct zxdg_output_v1 *xdg_output;
|
||||
struct wl_list link;
|
||||
|
||||
struct swappy_box geometry;
|
||||
enum wl_output_transform transform;
|
||||
int32_t scale;
|
||||
|
||||
struct swappy_box logical_geometry;
|
||||
double logical_scale; // guessed from the logical size
|
||||
char *name;
|
||||
|
||||
struct swappy_buffer *buffer;
|
||||
struct zwlr_screencopy_frame_v1 *screencopy_frame;
|
||||
uint32_t screencopy_frame_flags; // enum zwlr_screencopy_frame_v1_flags
|
||||
};
|
||||
|
@ -28,7 +28,7 @@ realtime = cc.find_library('rt')
|
||||
gtk = dependency('gtk+-3.0', version: '>=3.20.0')
|
||||
wayland_client = dependency('wayland-client')
|
||||
wayland_cursor = dependency('wayland-cursor')
|
||||
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
|
||||
wayland_protos = dependency('wayland-protocols', version: '>=1.14', required: false)
|
||||
|
||||
libnotify = dependency('libnotify', required: false)
|
||||
|
||||
@ -36,6 +36,11 @@ if libnotify.found()
|
||||
add_project_arguments('-DHAVE_LIBNOTIFY', language: 'c')
|
||||
endif
|
||||
|
||||
if wayland_protos.found()
|
||||
add_project_arguments('-DHAVE_WAYLAND_PROTOCOLS', language: 'c')
|
||||
endif
|
||||
|
||||
|
||||
subdir('res')
|
||||
subdir('protocol')
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
|
||||
|
||||
wayland_scanner = find_program('wayland-scanner')
|
||||
|
||||
# should check wayland_scanner's version, but it is hard to get
|
||||
@ -21,11 +19,19 @@ wayland_scanner_client = generator(
|
||||
arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
client_protocols = [
|
||||
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
|
||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||
['wlr-screencopy-unstable-v1.xml'],
|
||||
]
|
||||
if wayland_protos.found()
|
||||
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
|
||||
client_protocols = [
|
||||
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
|
||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||
['wlr-screencopy-unstable-v1.xml'],
|
||||
]
|
||||
else
|
||||
client_protocols = [
|
||||
['wlr-screencopy-unstable-v1.xml'],
|
||||
]
|
||||
endif
|
||||
|
||||
|
||||
client_protos_src = []
|
||||
client_protos_headers = []
|
||||
|
@ -154,14 +154,16 @@ void arrow_clicked_handler(GtkWidget *widget, struct swappy_state *state) {
|
||||
|
||||
void application_finish(struct swappy_state *state) {
|
||||
paint_free_all(state);
|
||||
buffer_free_all(state);
|
||||
cairo_surface_destroy(state->cairo_surface);
|
||||
cairo_surface_destroy(state->image_surface);
|
||||
g_free(state->storage_path);
|
||||
g_free(state->file_str);
|
||||
g_free(state->geometry_str);
|
||||
g_free(state->geometry);
|
||||
g_free(state->ui);
|
||||
g_object_unref(state->app);
|
||||
|
||||
wayland_finish(state);
|
||||
}
|
||||
|
||||
static void action_save_area_to_file(struct swappy_state *state) {
|
||||
@ -544,6 +546,12 @@ static gboolean is_file_from_stdin(const char *file) {
|
||||
static gint command_line_handler(GtkApplication *app,
|
||||
GApplicationCommandLine *cmdline,
|
||||
struct swappy_state *state) {
|
||||
if (!wayland_init(state)) {
|
||||
g_warning(
|
||||
"error while initializing wayland objects, can only be used in file "
|
||||
"mode");
|
||||
}
|
||||
|
||||
if (has_option_geometry(state)) {
|
||||
if (!buffer_parse_geometry(state)) {
|
||||
return EXIT_FAILURE;
|
||||
|
153
src/buffer.c
153
src/buffer.c
@ -5,6 +5,7 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "box.h"
|
||||
#include "wayland.h"
|
||||
|
||||
static void randname(char *buf) {
|
||||
struct timespec ts;
|
||||
@ -49,6 +50,30 @@ static int create_shm_file(off_t size) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
static cairo_format_t get_cairo_format(enum wl_shm_format wl_fmt) {
|
||||
switch (wl_fmt) {
|
||||
case WL_SHM_FORMAT_ARGB8888:
|
||||
return CAIRO_FORMAT_ARGB32;
|
||||
case WL_SHM_FORMAT_XRGB8888:
|
||||
return CAIRO_FORMAT_RGB24;
|
||||
default:
|
||||
return CAIRO_FORMAT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_output_flipped(enum wl_output_transform transform) {
|
||||
return transform & WL_OUTPUT_TRANSFORM_FLIPPED ? -1 : 1;
|
||||
}
|
||||
|
||||
static void apply_output_transform(enum wl_output_transform transform,
|
||||
int32_t *width, int32_t *height) {
|
||||
if (transform & WL_OUTPUT_TRANSFORM_90) {
|
||||
int32_t tmp = *width;
|
||||
*width = *height;
|
||||
*height = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static struct swappy_buffer *create_buffer(struct wl_shm *shm,
|
||||
enum wl_shm_format format,
|
||||
int32_t width, int32_t height,
|
||||
@ -85,23 +110,13 @@ static struct swappy_buffer *create_buffer(struct wl_shm *shm,
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void screencopy_destroy_buffer(struct swappy_buffer *buffer) {
|
||||
if (buffer == NULL) {
|
||||
return;
|
||||
}
|
||||
munmap(buffer->data, buffer->size);
|
||||
wl_buffer_destroy(buffer->wl_buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void screencopy_frame_handle_buffer(void *data,
|
||||
struct zwlr_screencopy_frame_v1 *frame,
|
||||
uint32_t format, uint32_t width,
|
||||
uint32_t height, uint32_t stride) {
|
||||
static void screencopy_frame_handle_buffer(
|
||||
void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format,
|
||||
uint32_t width, uint32_t height, uint32_t stride) {
|
||||
struct swappy_output *output = data;
|
||||
|
||||
output->buffer =
|
||||
create_buffer(output->state->shm, format, width, height, stride);
|
||||
create_buffer(output->state->wl->shm, format, width, height, stride);
|
||||
if (output->buffer == NULL) {
|
||||
g_warning("failed to create buffer");
|
||||
exit(EXIT_FAILURE);
|
||||
@ -110,29 +125,28 @@ void screencopy_frame_handle_buffer(void *data,
|
||||
zwlr_screencopy_frame_v1_copy(frame, output->buffer->wl_buffer);
|
||||
}
|
||||
|
||||
void screencopy_frame_handle_flags(void *data,
|
||||
struct zwlr_screencopy_frame_v1 *frame,
|
||||
uint32_t flags) {
|
||||
static void screencopy_frame_handle_flags(
|
||||
void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
|
||||
struct swappy_output *output = data;
|
||||
output->screencopy_frame_flags = flags;
|
||||
}
|
||||
|
||||
void screencopy_frame_handle_ready(void *data,
|
||||
struct zwlr_screencopy_frame_v1 *frame,
|
||||
uint32_t tv_sec_hi, uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec) {
|
||||
static void screencopy_frame_handle_ready(
|
||||
void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi,
|
||||
uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
||||
struct swappy_output *output = data;
|
||||
++output->state->n_done;
|
||||
++output->state->wl->n_done;
|
||||
}
|
||||
|
||||
void screencopy_frame_handle_failed(void *data,
|
||||
struct zwlr_screencopy_frame_v1 *frame) {
|
||||
static void screencopy_frame_handle_failed(
|
||||
void *data, struct zwlr_screencopy_frame_v1 *frame) {
|
||||
struct swappy_output *output = data;
|
||||
g_warning("screencopy: failed to copy output %s", output->name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
bool buffer_init_from_screencopy(struct swappy_state *state) {
|
||||
struct swappy_box *geometry = state->geometry;
|
||||
int32_t with_cursor = 0;
|
||||
size_t n_pending = 0;
|
||||
struct swappy_output *output;
|
||||
@ -144,14 +158,14 @@ bool buffer_init_from_screencopy(struct swappy_state *state) {
|
||||
.failed = screencopy_frame_handle_failed,
|
||||
};
|
||||
|
||||
wl_list_for_each(output, &state->outputs, link) {
|
||||
wl_list_for_each(output, &state->wl->outputs, link) {
|
||||
if (state->geometry != NULL &&
|
||||
!intersect_box(state->geometry, &output->logical_geometry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
output->screencopy_frame = zwlr_screencopy_manager_v1_capture_output(
|
||||
state->zwlr_screencopy_manager, with_cursor, output->wl_output);
|
||||
state->wl->zwlr_screencopy_manager, with_cursor, output->wl_output);
|
||||
zwlr_screencopy_frame_v1_add_listener(output->screencopy_frame,
|
||||
&screencopy_frame_listener, output);
|
||||
|
||||
@ -163,14 +177,70 @@ bool buffer_init_from_screencopy(struct swappy_state *state) {
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
while (!done && wl_display_dispatch(state->display) != -1) {
|
||||
done = (state->n_done == n_pending);
|
||||
while (!done && wl_display_dispatch(state->wl->display) != -1) {
|
||||
done = (state->wl->n_done == n_pending);
|
||||
}
|
||||
if (!done) {
|
||||
g_warning("failed to screenshot all outputs");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
wl_list_for_each(output, &state->wl->outputs, link) {
|
||||
struct swappy_buffer *buffer = output->buffer;
|
||||
|
||||
if (output->buffer == NULL) {
|
||||
// screencopy buffer is empty, cannot draw it onto the paint area"
|
||||
continue;
|
||||
}
|
||||
|
||||
cairo_format_t format = get_cairo_format(buffer->format);
|
||||
|
||||
g_assert(format != CAIRO_FORMAT_INVALID);
|
||||
|
||||
int32_t output_x = output->logical_geometry.x - geometry->x;
|
||||
int32_t output_y = output->logical_geometry.y - geometry->y;
|
||||
int32_t output_width = output->logical_geometry.width;
|
||||
int32_t output_height = output->logical_geometry.height;
|
||||
int32_t scale = output->scale;
|
||||
|
||||
int32_t raw_output_width = output->geometry.width;
|
||||
int32_t raw_output_height = output->geometry.height;
|
||||
apply_output_transform(output->transform, &raw_output_width,
|
||||
&raw_output_height);
|
||||
|
||||
int output_flipped_x = get_output_flipped(output->transform);
|
||||
int output_flipped_y =
|
||||
output->screencopy_frame_flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT
|
||||
? -1
|
||||
: 1;
|
||||
|
||||
cairo_surface_t *output_surface = cairo_image_surface_create_for_data(
|
||||
buffer->data, format, buffer->width, buffer->height, buffer->stride);
|
||||
cairo_pattern_t *output_pattern =
|
||||
cairo_pattern_create_for_surface(output_surface);
|
||||
|
||||
// All transformations are in pattern-local coordinates
|
||||
cairo_matrix_t matrix;
|
||||
cairo_matrix_init_identity(&matrix);
|
||||
cairo_matrix_translate(&matrix, (double)output->geometry.width / 2,
|
||||
(double)output->geometry.height / 2);
|
||||
// cairo_matrix_rotate(&matrix, -get_output_rotation(output->transform));
|
||||
cairo_matrix_scale(
|
||||
&matrix, (double)raw_output_width / output_width * output_flipped_x,
|
||||
(double)raw_output_height / output_height * output_flipped_y);
|
||||
cairo_matrix_translate(&matrix, -(double)output_width / 2,
|
||||
-(double)output_height / 2);
|
||||
cairo_matrix_translate(&matrix, -output_x, -output_y);
|
||||
cairo_matrix_scale(&matrix, 1 / scale, 1 / scale);
|
||||
cairo_pattern_set_matrix(output_pattern, &matrix);
|
||||
|
||||
cairo_pattern_set_filter(output_pattern, CAIRO_FILTER_BEST);
|
||||
|
||||
state->patterns = g_list_append(state->patterns, output_pattern);
|
||||
|
||||
cairo_surface_destroy(output_surface);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -197,7 +267,11 @@ bool buffer_init_from_file(struct swappy_state *state) {
|
||||
geometry->height = (int32_t)height;
|
||||
|
||||
state->geometry = geometry;
|
||||
state->image_surface = surface;
|
||||
|
||||
cairo_pattern_t *output_pattern = cairo_pattern_create_for_surface(surface);
|
||||
state->patterns = g_list_append(state->patterns, output_pattern);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -214,3 +288,24 @@ bool buffer_parse_geometry(struct swappy_state *state) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void buffer_wayland_destroy(struct swappy_buffer *buffer) {
|
||||
if (buffer == NULL) {
|
||||
return;
|
||||
}
|
||||
munmap(buffer->data, buffer->size);
|
||||
wl_buffer_destroy(buffer->wl_buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void free_pattern(gpointer data) {
|
||||
cairo_pattern_t *pattern = data;
|
||||
cairo_pattern_destroy(pattern);
|
||||
}
|
||||
|
||||
void buffer_free_all(struct swappy_state *state) {
|
||||
if (state->patterns) {
|
||||
g_list_free_full(state->patterns, free_pattern);
|
||||
state->patterns = NULL;
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include "application.h"
|
||||
#include "config.h"
|
||||
#include "wayland.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct swappy_state state = {0};
|
||||
@ -22,11 +21,6 @@ int main(int argc, char *argv[]) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!wayland_init(&state)) {
|
||||
g_critical("failed to initialize wayland");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
status = application_run(&state);
|
||||
|
||||
if (status == 0) {
|
||||
@ -34,7 +28,6 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
application_finish(&state);
|
||||
wayland_finish(&state);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
107
src/render.c
107
src/render.c
@ -7,30 +7,6 @@
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
static cairo_format_t get_cairo_format(enum wl_shm_format wl_fmt) {
|
||||
switch (wl_fmt) {
|
||||
case WL_SHM_FORMAT_ARGB8888:
|
||||
return CAIRO_FORMAT_ARGB32;
|
||||
case WL_SHM_FORMAT_XRGB8888:
|
||||
return CAIRO_FORMAT_RGB24;
|
||||
default:
|
||||
return CAIRO_FORMAT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_output_flipped(enum wl_output_transform transform) {
|
||||
return transform & WL_OUTPUT_TRANSFORM_FLIPPED ? -1 : 1;
|
||||
}
|
||||
|
||||
static void apply_output_transform(enum wl_output_transform transform,
|
||||
int32_t *width, int32_t *height) {
|
||||
if (transform & WL_OUTPUT_TRANSFORM_90) {
|
||||
int32_t tmp = *width;
|
||||
*width = *height;
|
||||
*height = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void render_shape_arrow(cairo_t *cr, struct swappy_paint_shape shape) {
|
||||
cairo_set_source_rgba(cr, shape.r, shape.g, shape.b, shape.a);
|
||||
cairo_set_line_width(cr, shape.w);
|
||||
@ -142,82 +118,18 @@ static void render_shape(cairo_t *cr, struct swappy_paint_shape shape) {
|
||||
cairo_restore(cr);
|
||||
}
|
||||
|
||||
static void render_buffer(cairo_t *cr, struct swappy_state *state) {
|
||||
// FIXME This is wrong, the geometry here is not quite valid
|
||||
// It must be based on output, but will work fine on single screen
|
||||
struct swappy_box *geometry = state->geometry;
|
||||
struct swappy_output *output;
|
||||
|
||||
wl_list_for_each(output, &state->outputs, link) {
|
||||
struct swappy_buffer *buffer = output->buffer;
|
||||
|
||||
if (output->buffer == NULL) {
|
||||
// screencopy buffer is empty, cannot draw it onto the paint area"
|
||||
continue;
|
||||
}
|
||||
|
||||
cairo_format_t format = get_cairo_format(buffer->format);
|
||||
|
||||
g_assert(format != CAIRO_FORMAT_INVALID);
|
||||
|
||||
int32_t output_x = output->logical_geometry.x - geometry->x;
|
||||
int32_t output_y = output->logical_geometry.y - geometry->y;
|
||||
int32_t output_width = output->logical_geometry.width;
|
||||
int32_t output_height = output->logical_geometry.height;
|
||||
int32_t scale = output->scale;
|
||||
|
||||
int32_t raw_output_width = output->geometry.width;
|
||||
int32_t raw_output_height = output->geometry.height;
|
||||
apply_output_transform(output->transform, &raw_output_width,
|
||||
&raw_output_height);
|
||||
|
||||
int output_flipped_x = get_output_flipped(output->transform);
|
||||
int output_flipped_y =
|
||||
output->screencopy_frame_flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT
|
||||
? -1
|
||||
: 1;
|
||||
|
||||
cairo_surface_t *output_surface = cairo_image_surface_create_for_data(
|
||||
buffer->data, format, buffer->width, buffer->height, buffer->stride);
|
||||
cairo_pattern_t *output_pattern =
|
||||
cairo_pattern_create_for_surface(output_surface);
|
||||
|
||||
// All transformations are in pattern-local coordinates
|
||||
cairo_matrix_t matrix;
|
||||
cairo_matrix_init_identity(&matrix);
|
||||
cairo_matrix_translate(&matrix, (double)output->geometry.width / 2,
|
||||
(double)output->geometry.height / 2);
|
||||
// cairo_matrix_rotate(&matrix, -get_output_rotation(output->transform));
|
||||
cairo_matrix_scale(
|
||||
&matrix, (double)raw_output_width / output_width * output_flipped_x,
|
||||
(double)raw_output_height / output_height * output_flipped_y);
|
||||
cairo_matrix_translate(&matrix, -(double)output_width / 2,
|
||||
-(double)output_height / 2);
|
||||
cairo_matrix_translate(&matrix, -output_x, -output_y);
|
||||
cairo_matrix_scale(&matrix, 1 / scale, 1 / scale);
|
||||
cairo_pattern_set_matrix(output_pattern, &matrix);
|
||||
|
||||
cairo_pattern_set_filter(output_pattern, CAIRO_FILTER_BEST);
|
||||
|
||||
cairo_set_source(cr, output_pattern);
|
||||
cairo_pattern_destroy(output_pattern);
|
||||
|
||||
cairo_paint(cr);
|
||||
|
||||
cairo_surface_destroy(output_surface);
|
||||
}
|
||||
}
|
||||
|
||||
static void render_image(cairo_t *cr, struct swappy_state *state) {
|
||||
if (!state->image_surface) {
|
||||
static void render_buffers(cairo_t *cr, struct swappy_state *state) {
|
||||
if (!state->patterns) {
|
||||
return;
|
||||
}
|
||||
|
||||
cairo_pattern_t *output_pattern =
|
||||
cairo_pattern_create_for_surface(state->image_surface);
|
||||
cairo_set_source(cr, output_pattern);
|
||||
cairo_pattern_destroy(output_pattern);
|
||||
cairo_paint(cr);
|
||||
|
||||
for (GList *elem = state->patterns; elem; elem = elem->prev) {
|
||||
cairo_pattern_t *pattern = elem->data;
|
||||
cairo_set_source(cr, pattern);
|
||||
cairo_paint(cr);
|
||||
}
|
||||
}
|
||||
|
||||
static void render_background(cairo_t *cr) {
|
||||
@ -280,8 +192,7 @@ void render_state(struct swappy_state *state) {
|
||||
cairo_t *cr = cairo_create(state->cairo_surface);
|
||||
|
||||
render_background(cr);
|
||||
render_buffer(cr, state);
|
||||
render_image(cr, state);
|
||||
render_buffers(cr, state);
|
||||
render_paints(cr, state);
|
||||
|
||||
// Drawing is finished, notify the GtkDrawingArea it needs to be redrawn.
|
||||
|
111
src/wayland.c
111
src/wayland.c
@ -7,11 +7,14 @@
|
||||
#include "buffer.h"
|
||||
#include "swappy.h"
|
||||
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
||||
|
||||
#ifdef HAVE_WAYLAND_PROTOCOLS
|
||||
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||
#endif
|
||||
|
||||
static bool guess_output_logical_geometry(struct swappy_output *output) {
|
||||
// TODO Implement
|
||||
g_critical("guessing output is not yet implemented");
|
||||
g_warning("guessing output is not yet implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -24,6 +27,7 @@ void apply_output_transform(enum wl_output_transform transform, int32_t *width,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_WAYLAND_PROTOCOLS
|
||||
static void xdg_output_handle_logical_position(
|
||||
void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) {
|
||||
struct swappy_output *output = data;
|
||||
@ -70,6 +74,7 @@ static const struct zxdg_output_v1_listener xdg_output_listener = {
|
||||
.name = xdg_output_handle_name,
|
||||
.description = xdg_output_handle_description,
|
||||
};
|
||||
#endif
|
||||
|
||||
static void output_handle_geometry(void *data, struct wl_output *wl_output,
|
||||
int32_t x, int32_t y, int32_t physical_width,
|
||||
@ -120,11 +125,12 @@ static void global_registry_handler(void *data, struct wl_registry *registry,
|
||||
bool bound = false;
|
||||
|
||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||
state->compositor =
|
||||
state->wl->compositor =
|
||||
wl_registry_bind(registry, name, &wl_compositor_interface, version);
|
||||
bound = true;
|
||||
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||
state->shm = wl_registry_bind(registry, name, &wl_shm_interface, version);
|
||||
state->wl->shm =
|
||||
wl_registry_bind(registry, name, &wl_shm_interface, version);
|
||||
bound = true;
|
||||
} else if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
struct swappy_output *output = calloc(1, sizeof(struct swappy_output));
|
||||
@ -133,17 +139,21 @@ static void global_registry_handler(void *data, struct wl_registry *registry,
|
||||
output->wl_output =
|
||||
wl_registry_bind(registry, name, &wl_output_interface, 3);
|
||||
wl_output_add_listener(output->wl_output, &output_listener, output);
|
||||
wl_list_insert(&state->outputs, &output->link);
|
||||
bound = true;
|
||||
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
|
||||
state->xdg_output_manager = wl_registry_bind(
|
||||
registry, name, &zxdg_output_manager_v1_interface, version);
|
||||
wl_list_insert(&state->wl->outputs, &output->link);
|
||||
bound = true;
|
||||
} else if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) ==
|
||||
0) {
|
||||
state->zwlr_screencopy_manager = wl_registry_bind(
|
||||
state->wl->zwlr_screencopy_manager = wl_registry_bind(
|
||||
registry, name, &zwlr_screencopy_manager_v1_interface, version);
|
||||
bound = true;
|
||||
} else {
|
||||
#ifdef HAVE_WAYLAND_PROTOCOLS
|
||||
if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
|
||||
state->wl->xdg_output_manager = wl_registry_bind(
|
||||
registry, name, &zxdg_output_manager_v1_interface, version);
|
||||
bound = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (bound) {
|
||||
@ -161,49 +171,56 @@ static struct wl_registry_listener registry_listener = {
|
||||
};
|
||||
|
||||
bool wayland_init(struct swappy_state *state) {
|
||||
state->display = wl_display_connect(NULL);
|
||||
if (state->display == NULL) {
|
||||
state->wl = g_new(struct swappy_wayland, 1);
|
||||
|
||||
state->wl->display = wl_display_connect(NULL);
|
||||
if (state->wl->display == NULL) {
|
||||
g_warning("cannot connect to wayland display");
|
||||
return false;
|
||||
}
|
||||
|
||||
wl_list_init(&state->outputs);
|
||||
state->registry = wl_display_get_registry(state->display);
|
||||
wl_registry_add_listener(state->registry, ®istry_listener, state);
|
||||
wl_display_roundtrip(state->display);
|
||||
wl_list_init(&state->wl->outputs);
|
||||
state->wl->registry = wl_display_get_registry(state->wl->display);
|
||||
wl_registry_add_listener(state->wl->registry, ®istry_listener, state);
|
||||
wl_display_roundtrip(state->wl->display);
|
||||
|
||||
if (state->compositor == NULL) {
|
||||
if (state->wl->compositor == NULL) {
|
||||
g_warning("compositor doesn't support wl_compositor");
|
||||
return false;
|
||||
}
|
||||
if (state->shm == NULL) {
|
||||
if (state->wl->shm == NULL) {
|
||||
g_warning("compositor doesn't support wl_shm");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wl_list_empty(&state->outputs)) {
|
||||
if (wl_list_empty(&state->wl->outputs)) {
|
||||
g_warning("no wl_output found");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state->xdg_output_manager != NULL) {
|
||||
bool found_output_layout = false;
|
||||
#ifdef HAVE_WAYLAND_PROTOCOLS
|
||||
if (state->wl->xdg_output_manager != NULL) {
|
||||
struct swappy_output *output;
|
||||
wl_list_for_each(output, &state->outputs, link) {
|
||||
wl_list_for_each(output, &state->wl->outputs, link) {
|
||||
output->xdg_output = zxdg_output_manager_v1_get_xdg_output(
|
||||
state->xdg_output_manager, output->wl_output);
|
||||
state->wl->xdg_output_manager, output->wl_output);
|
||||
zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener,
|
||||
output);
|
||||
}
|
||||
|
||||
wl_display_dispatch(state->display);
|
||||
wl_display_roundtrip(state->display);
|
||||
} else {
|
||||
wl_display_dispatch(state->wl->display);
|
||||
wl_display_roundtrip(state->wl->display);
|
||||
found_output_layout = true;
|
||||
}
|
||||
#endif
|
||||
if (!found_output_layout) {
|
||||
g_warning(
|
||||
"zxdg_output_manager_v1 isn't available, guessing the output layout");
|
||||
|
||||
struct swappy_output *output;
|
||||
bool output_guessed = false;
|
||||
wl_list_for_each(output, &state->outputs, link) {
|
||||
wl_list_for_each(output, &state->wl->outputs, link) {
|
||||
output_guessed = guess_output_logical_geometry(output);
|
||||
}
|
||||
if (!output_guessed) {
|
||||
@ -212,7 +229,7 @@ bool wayland_init(struct swappy_state *state) {
|
||||
}
|
||||
}
|
||||
|
||||
if (state->zwlr_screencopy_manager == NULL) {
|
||||
if (state->wl->zwlr_screencopy_manager == NULL) {
|
||||
g_warning("compositor does not support zwlr_screencopy_v1");
|
||||
return false;
|
||||
}
|
||||
@ -223,41 +240,53 @@ bool wayland_init(struct swappy_state *state) {
|
||||
void wayland_finish(struct swappy_state *state) {
|
||||
struct swappy_output *output;
|
||||
struct swappy_output *output_tmp;
|
||||
wl_list_for_each_safe(output, output_tmp, &state->outputs, link) {
|
||||
|
||||
if (!state->wl) {
|
||||
return;
|
||||
}
|
||||
|
||||
wl_list_for_each_safe(output, output_tmp, &state->wl->outputs, link) {
|
||||
wl_list_remove(&output->link);
|
||||
free(output->name);
|
||||
if (output->screencopy_frame != NULL) {
|
||||
zwlr_screencopy_frame_v1_destroy(output->screencopy_frame);
|
||||
}
|
||||
#ifdef HAVE_WAYLAND_PROTOCOLS
|
||||
if (output->xdg_output != NULL) {
|
||||
zxdg_output_v1_destroy(output->xdg_output);
|
||||
}
|
||||
#endif
|
||||
wl_output_release(output->wl_output);
|
||||
screencopy_destroy_buffer(output->buffer);
|
||||
buffer_wayland_destroy(output->buffer);
|
||||
free(output);
|
||||
}
|
||||
|
||||
if (state->compositor != NULL) {
|
||||
wl_compositor_destroy(state->compositor);
|
||||
if (state->wl->compositor != NULL) {
|
||||
wl_compositor_destroy(state->wl->compositor);
|
||||
}
|
||||
|
||||
if (state->zwlr_screencopy_manager != NULL) {
|
||||
zwlr_screencopy_manager_v1_destroy(state->zwlr_screencopy_manager);
|
||||
if (state->wl->zwlr_screencopy_manager != NULL) {
|
||||
zwlr_screencopy_manager_v1_destroy(state->wl->zwlr_screencopy_manager);
|
||||
}
|
||||
|
||||
if (state->xdg_output_manager != NULL) {
|
||||
zxdg_output_manager_v1_destroy(state->xdg_output_manager);
|
||||
#ifdef HAVE_WAYLAND_PROTOCOLS
|
||||
if (state->wl->xdg_output_manager != NULL) {
|
||||
zxdg_output_manager_v1_destroy(state->wl->xdg_output_manager);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (state->wl->shm != NULL) {
|
||||
wl_shm_destroy(state->wl->shm);
|
||||
}
|
||||
|
||||
if (state->shm != NULL) {
|
||||
wl_shm_destroy(state->shm);
|
||||
if (state->wl->registry != NULL) {
|
||||
wl_registry_destroy(state->wl->registry);
|
||||
}
|
||||
|
||||
if (state->registry != NULL) {
|
||||
wl_registry_destroy(state->registry);
|
||||
if (state->wl->display) {
|
||||
wl_display_disconnect(state->wl->display);
|
||||
}
|
||||
|
||||
if (state->display) {
|
||||
wl_display_disconnect(state->display);
|
||||
}
|
||||
g_free(state->wl);
|
||||
state->wl = NULL;
|
||||
}
|
Loading…
Reference in New Issue
Block a user