summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/backend.c9
-rw-r--r--src/common/backend.h16
-rw-r--r--src/platform/cocoa/window.m15
-rw-r--r--src/platform/wayland/api.h2
-rw-r--r--src/platform/wayland/backend.c7
-rw-r--r--src/platform/wayland/buffer.c14
-rw-r--r--src/platform/wayland/internal.h3
-rw-r--r--src/platform/x11/window.c12
8 files changed, 78 insertions, 0 deletions
diff --git a/src/common/backend.c b/src/common/backend.c
index b15233f..bb5ae33 100644
--- a/src/common/backend.c
+++ b/src/common/backend.c
@@ -322,6 +322,15 @@ bool yasl_surface_poll_event(struct yasl_surface *s, struct yasl_event *ev)
return input_queue_pop(&s->input, ev);
}
+bool yasl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
+ struct yasl_vector2i *size)
+{
+ if (!size) return false;
+ *size = (struct yasl_vector2i){0};
+ if (!s || (!out && cap) || !s->display->ops->surface_read_pixels) return false;
+ return s->display->ops->surface_read_pixels(s, out, cap, size);
+}
+
bool yasl_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_pointer_mode mode)
{
if (!s || (mode != YASL_POINTER_NORMAL && mode != YASL_POINTER_RELATIVE))
diff --git a/src/common/backend.h b/src/common/backend.h
index 96ce9e7..13ce9b5 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -7,6 +7,8 @@
#include "common/input.h"
#include "common/timing.h"
+#include <string.h>
+
struct yasl_backend_ops;
struct yasl_display {
@@ -43,6 +45,17 @@ void yasl_surface_clear_timers(struct yasl_surface *s);
struct yasl_surface *yasl_create_toplevel(struct yasl_display *d,
const struct yasl_window_spec *spec, void *graphics_device);
+/* Report a presented CPU frame's extent, and copy it when out can hold it. */
+static inline bool yasl_copy_frame(const uint32_t *pixels, struct yasl_vector2i extent,
+ uint32_t *out, size_t cap, struct yasl_vector2i *size)
+{
+ *size = extent;
+ size_t count = (size_t)extent.x * (size_t)extent.y;
+ if (!count || count > cap) return false;
+ memcpy(out, pixels, count * sizeof(*pixels));
+ return true;
+}
+
static inline uint64_t yasl_frame_begin(struct yasl_surface *s)
{
if (++s->frame_generation == 0) ++s->frame_generation;
@@ -95,6 +108,9 @@ struct yasl_backend_ops {
bool (*surface_place_under_pointer)(struct yasl_surface *s, struct yasl_vector2i at);
/* Optional; NULL means the bell is unsupported. */
bool (*surface_bell)(struct yasl_surface *s);
+ /* Optional; NULL means no readback. Fills size only for an available frame. */
+ bool (*surface_read_pixels)(struct yasl_surface *s, uint32_t *out, size_t cap,
+ struct yasl_vector2i *size);
void *(*display_get_native_handle)(struct yasl_display *d);
void *(*surface_get_native_handle)(struct yasl_surface *s);
bool (*surface_get_native_frame)(struct yasl_surface *s,
diff --git a/src/platform/cocoa/window.m b/src/platform/cocoa/window.m
index 1efff59..2f9f8e5 100644
--- a/src/platform/cocoa/window.m
+++ b/src/platform/cocoa/window.m
@@ -89,6 +89,8 @@ struct cocoa_surface {
struct yasl_frame frame;
struct yasl_damage_list damage;
bool did_present;
+ /* Extent of the frame in pixels, valid while did_present is set. */
+ struct yasl_vector2i shown_size;
unsigned idle_frames;
bool want_draw;
bool frame_due;
@@ -1073,6 +1075,7 @@ bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_framebuffe
}
yasl_clock_presented(&w->clock, yasl_loop_now_ns(), false);
w->did_present = true;
+ w->shown_size = w->size;
return true;
}
@@ -2198,6 +2201,17 @@ static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl
return cocoa_surface_place_under_pointer((struct cocoa_surface *)s, at);
}
+static bool impl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
+ struct yasl_vector2i *size)
+{
+ struct cocoa_surface *w = (struct cocoa_surface *)s;
+ /* did_present stays set from the last draw until the next one starts, and
+ * pixels are reallocated only when a draw starts. */
+ if (w->closed || w->rendering || !w->did_present || !w->software_queue || !w->pixels)
+ return false;
+ return yasl_copy_frame(w->pixels, w->shown_size, out, cap, size);
+}
+
static bool impl_surface_bell(struct yasl_surface *s)
{
assert([NSThread isMainThread]);
@@ -2266,6 +2280,7 @@ const struct yasl_backend_ops yasl_cocoa_backend = {
.surface_drag_carry = impl_surface_drag_carry,
.surface_place_under_pointer = impl_surface_place_under_pointer,
.surface_bell = impl_surface_bell,
+ .surface_read_pixels = impl_surface_read_pixels,
.display_get_native_handle = impl_display_get_native_handle,
.surface_get_native_handle = impl_surface_get_native_handle,
.surface_get_native_frame = impl_surface_get_native_frame,
diff --git a/src/platform/wayland/api.h b/src/platform/wayland/api.h
index 6c3154c..9f290f5 100644
--- a/src/platform/wayland/api.h
+++ b/src/platform/wayland/api.h
@@ -39,5 +39,7 @@ bool wayland_surface_show_window_menu(struct wayland_surface *s, struct yasl_vec
bool wayland_surface_drag_carry(struct wayland_surface *s, struct yasl_vector2i offset);
bool wayland_surface_place_under_pointer(struct wayland_surface *s, struct yasl_vector2i at);
bool wayland_surface_bell(struct wayland_surface *s);
+bool wayland_surface_read_pixels(struct wayland_surface *s, uint32_t *out, size_t cap,
+ struct yasl_vector2i *size);
#endif
diff --git a/src/platform/wayland/backend.c b/src/platform/wayland/backend.c
index ad9ed4c..4585cd6 100644
--- a/src/platform/wayland/backend.c
+++ b/src/platform/wayland/backend.c
@@ -185,6 +185,12 @@ static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl
return wayland_surface_place_under_pointer((struct wayland_surface *)s, at);
}
+static bool impl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
+ struct yasl_vector2i *size)
+{
+ return wayland_surface_read_pixels((struct wayland_surface *)s, out, cap, size);
+}
+
static bool impl_surface_bell(struct yasl_surface *s)
{
return wayland_surface_bell((struct wayland_surface *)s);
@@ -240,6 +246,7 @@ const struct yasl_backend_ops yasl_wayland_backend = {
.surface_drag_carry = impl_surface_drag_carry,
.surface_place_under_pointer = impl_surface_place_under_pointer,
.surface_bell = impl_surface_bell,
+ .surface_read_pixels = impl_surface_read_pixels,
.display_get_native_handle = impl_display_get_native_handle,
.surface_get_native_handle = impl_surface_get_native_handle,
};
diff --git a/src/platform/wayland/buffer.c b/src/platform/wayland/buffer.c
index f5a2c0f..b906f4f 100644
--- a/src/platform/wayland/buffer.c
+++ b/src/platform/wayland/buffer.c
@@ -472,9 +472,20 @@ bool wayland_surface_present(struct wayland_surface *w, const struct yasl_frameb
return false;
commit_buffer(w, w->cur);
w->frame_presented = true;
+ w->shown = w->cur;
+ w->has_shown = true;
return true;
}
+bool wayland_surface_read_pixels(struct wayland_surface *w, uint32_t *out, size_t cap,
+ struct yasl_vector2i *size)
+{
+ if (!w || w->closed || w->frame_active || !w->has_shown) return false;
+ const struct buffer *b = &w->buffers[w->shown];
+ if (!b->pixels) return false;
+ return yasl_copy_frame(b->pixels, (struct yasl_vector2i){b->width, b->height}, out, cap, size);
+}
+
void surface_draw_frame(struct wayland_surface *w)
{
if (w->closed || !w->configured)
@@ -491,6 +502,9 @@ void surface_draw_frame(struct wayland_surface *w)
w->frames_skipped++;
return;
}
+ /* Drawing reuses the presented buffer only after the compositor released it. */
+ if (w->has_shown && idx == w->shown)
+ w->has_shown = false;
if (buffer_prepare(w, &w->buffers[idx], w->width, w->height, &buffer_listener) < 0) {
w->frames_skipped++;
return;
diff --git a/src/platform/wayland/internal.h b/src/platform/wayland/internal.h
index 84a603d..34d4133 100644
--- a/src/platform/wayland/internal.h
+++ b/src/platform/wayland/internal.h
@@ -307,6 +307,8 @@ struct wayland_surface {
int ime_shown_begin, ime_shown_end;
int floating_width, floating_height;
int cur;
+ /* Buffer holding the last presented frame, valid while has_shown is set. */
+ int shown;
int width, height;
int logical_width, logical_height;
int req_width, req_height;
@@ -318,6 +320,7 @@ struct wayland_surface {
/* 1-byte booleans packed tightly */
bool frame_active;
bool frame_presented;
+ bool has_shown;
bool size_pending;
bool sample_refresh;
bool has_pending_serial;
diff --git a/src/platform/x11/window.c b/src/platform/x11/window.c
index 724987f..765d83c 100644
--- a/src/platform/x11/window.c
+++ b/src/platform/x11/window.c
@@ -473,6 +473,8 @@ static bool resize(struct x11_surface *s, struct yasl_vector2i size)
return true;
}
uint64_t start = yasl_loop_now_ns();
+ /* The presented frame goes with the old pixels. */
+ s->presented = false;
if (s->shm_active) {
XShmDetach(d->native, &s->shm_info);
@@ -1883,6 +1885,15 @@ static int double_click_ms(const struct yasl_display *base)
return portal_double_click_ms();
}
+static bool read_pixels(struct yasl_surface *base, uint32_t *out, size_t cap,
+ struct yasl_vector2i *size)
+{
+ struct x11_surface *s = (struct x11_surface *)base;
+ /* presented stays set from the last draw until the next one starts. */
+ if (s->destroyed || s->drawing || !s->presented || !s->pixels) return false;
+ return yasl_copy_frame(s->pixels, s->size, out, cap, size);
+}
+
static bool bell(struct yasl_surface *base)
{
struct x11_surface *s = (struct x11_surface *)base;
@@ -2138,6 +2149,7 @@ const struct yasl_backend_ops yasl_x11_backend = {
.surface_drag_carry = drag_carry,
.surface_place_under_pointer = place,
.surface_bell = bell,
+ .surface_read_pixels = read_pixels,
.display_double_click_ms = double_click_ms,
.surface_start_interactive_move = interactive_move,
.surface_start_interactive_resize = interactive_resize,