diff options
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/backend.c | 57 | ||||
| -rw-r--r-- | src/common/backend.h | 9 | ||||
| -rw-r--r-- | src/common/input.c | 103 | ||||
| -rw-r--r-- | src/common/input.h | 19 | ||||
| -rw-r--r-- | src/common/meson.build | 2 | ||||
| -rw-r--r-- | src/common/timing.c | 1 |
6 files changed, 180 insertions, 11 deletions
diff --git a/src/common/backend.c b/src/common/backend.c index 2220f1f..214854b 100644 --- a/src/common/backend.c +++ b/src/common/backend.c @@ -113,14 +113,18 @@ int yasl_display_modifiers(const struct yasl_display *d) void yasl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text) { - if (!d || !d->ops->display_copy) + if (!d || !d->ops->display_copy || !text || + (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY) || + (sel == YASL_PRIMARY && !(yasl_display_caps(d) & YASL_CAP_PRIMARY_SELECTION))) return; d->ops->display_copy(d, sel, text); } void yasl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target) { - if (!d || !d->ops->display_paste) + if (!d || !d->ops->display_paste || + (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY) || + (sel == YASL_PRIMARY && !(yasl_display_caps(d) & YASL_CAP_PRIMARY_SELECTION))) return; if (!target || target->display != d) return; @@ -145,8 +149,17 @@ void yasl_display_drag_cancel(struct yasl_display *d) struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *d, const struct yasl_surface_spec *spec) { - if (!d || !d->ops->surface_create_toplevel) - return 0; + if (!d || !d->ops->surface_create_toplevel || !spec || + spec->size.x <= 0 || spec->size.y <= 0 || + spec->size.x > 32767 || spec->size.y > 32767 || + (int64_t)spec->size.x * spec->size.y * 4 > INT32_MAX) { + errno = EINVAL; + return NULL; + } + if (spec->graphics_device && !(yasl_display_caps(d) & YASL_CAP_NATIVE_GRAPHICS)) { + errno = ENOTSUP; + return NULL; + } return d->ops->surface_create_toplevel(d, spec); } @@ -201,14 +214,34 @@ void yasl_surface_schedule_redraw(struct yasl_surface *s) bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage) { - if (!s || !s->display->ops->surface_present) - return 0; - return s->display->ops->surface_present(s, fb, damage); + if (!s || !s->display->ops->surface_present || !fb || !damage || + !fb->generation || fb->generation != s->frame_generation || + fb->size.x <= 0 || fb->size.y <= 0 || + damage->n < -1 || damage->n > YASL_DAMAGE_MAX) + return false; + struct yasl_damage_list clipped = {.n = damage->n}; + if (damage->n >= 0) { + clipped.n = 0; + for (int i = 0; i < damage->n; i++) { + struct yasl_rect2i r = damage->rects[i]; + if (r.width < 0 || r.height < 0) return false; + int64_t right = (int64_t)r.x + r.width; + int64_t bottom = (int64_t)r.y + r.height; + int64_t x = r.x > 0 ? r.x : 0, y = r.y > 0 ? r.y : 0; + if (right > fb->size.x) right = fb->size.x; + if (bottom > fb->size.y) bottom = fb->size.y; + if (right > x && bottom > y) + clipped.rects[clipped.n++] = (struct yasl_rect2i){ + (int)x, (int)y, (int)(right - x), (int)(bottom - y), + }; + } + } + return s->display->ops->surface_present(s, fb, &clipped); } bool yasl_surface_input_poll(struct yasl_surface *s, struct yasl_input_event *ev) { - if (!s || !s->display->ops->surface_input_poll) + if (!s || !ev || !s->display->ops->surface_input_poll) return 0; return s->display->ops->surface_input_poll(s, ev); } @@ -271,7 +304,7 @@ const char *yasl_surface_ime_preedit(const struct yasl_surface *s) bool yasl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end) { - if (!s || !s->display->ops->surface_ime_preedit_cursor) + if (!s || !begin || !end || !s->display->ops->surface_ime_preedit_cursor) return 0; return s->display->ops->surface_ime_preedit_cursor(s, begin, end); } @@ -292,7 +325,11 @@ bool yasl_surface_start_interactive_move(struct yasl_surface *s) bool yasl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge) { - if (!s || !s->display->ops->surface_start_interactive_resize) + if (!s || !s->display->ops->surface_start_interactive_resize || + (edge != YASL_EDGE_TOP && edge != YASL_EDGE_BOTTOM && + edge != YASL_EDGE_LEFT && edge != YASL_EDGE_RIGHT && + edge != YASL_EDGE_TOP_LEFT && edge != YASL_EDGE_TOP_RIGHT && + edge != YASL_EDGE_BOTTOM_LEFT && edge != YASL_EDGE_BOTTOM_RIGHT)) return 0; return s->display->ops->surface_start_interactive_resize(s, edge); } diff --git a/src/common/backend.h b/src/common/backend.h index c1d878b..c6691eb 100644 --- a/src/common/backend.h +++ b/src/common/backend.h @@ -7,12 +7,21 @@ struct yasl_backend_ops; struct yasl_display { const struct yasl_backend_ops *ops; + struct yasl_display_cbs callbacks; }; struct yasl_surface { struct yasl_display *display; + struct yasl_surface_cbs callbacks; + uint64_t frame_generation; }; +static inline uint64_t yasl_frame_begin(struct yasl_surface *s) +{ + if (++s->frame_generation == 0) ++s->frame_generation; + return s->frame_generation; +} + struct yasl_backend_ops { enum yasl_backend type; struct yasl_display *(*display_open)(struct yasl_loop *loop, const struct yasl_display_spec *spec); diff --git a/src/common/input.c b/src/common/input.c new file mode 100644 index 0000000..eac4d1a --- /dev/null +++ b/src/common/input.c @@ -0,0 +1,103 @@ +#include "input.h" + +#include <stdlib.h> +#include <string.h> + +static const char *event_text(const struct yasl_input_event *ev, size_t *len) +{ + switch (ev->kind) { + case YASL_INPUT_TEXT: *len = ev->text.len; return ev->text.text; + case YASL_INPUT_PASTE: *len = ev->paste.len; return ev->paste.text; + case YASL_INPUT_DROP_FILES: *len = ev->drop_files.len; return ev->drop_files.files; + default: *len = 0; return NULL; + } +} + +static void set_text(struct yasl_input_event *ev, const char *text) +{ + switch (ev->kind) { + case YASL_INPUT_TEXT: ev->text.text = text; break; + case YASL_INPUT_PASTE: ev->paste.text = text; break; + case YASL_INPUT_DROP_FILES: ev->drop_files.files = text; break; + default: break; + } +} + +static void discard_pending(struct input_queue *q) +{ + for (size_t i = 0; i < q->count; i++) { + size_t len; + free((void *)event_text(&q->events[(q->head + i) % q->capacity], &len)); + } + q->head = q->count = 0; +} + +bool input_queue_push(struct input_queue *q, const struct yasl_input_event *ev) +{ + struct yasl_input_event copy = *ev; + size_t len; + const char *text = event_text(ev, &len); + char *owned = NULL; + if (ev->kind == YASL_INPUT_TEXT || ev->kind == YASL_INPUT_PASTE || + ev->kind == YASL_INPUT_DROP_FILES) { + if ((!text && len) || len == SIZE_MAX) goto failed; + owned = malloc(len + 1); + if (!owned) goto failed; + if (len) memcpy(owned, text, len); + owned[len] = '\0'; + set_text(©, owned); + } + if (q->count == q->capacity) { + size_t capacity = q->capacity ? q->capacity * 2 : 32; + if (capacity < q->capacity || capacity > SIZE_MAX / sizeof(*q->events)) + goto failed; + struct yasl_input_event *events = malloc(capacity * sizeof(*events)); + if (!events) goto failed; + for (size_t i = 0; i < q->count; i++) + events[i] = q->events[(q->head + i) % q->capacity]; + free(q->events); + q->events = events; + q->capacity = capacity; + q->head = 0; + } + q->events[(q->head + q->count) % q->capacity] = copy; + q->count++; + return true; + +failed: + free(owned); + /* A missing release must never silently leave app input state stuck. + * Discard the old stream and deliver an allocation-free reset first. */ + discard_pending(q); + q->reset_surface = ev->surface; + q->reset_pending = true; + return false; +} + +bool input_queue_pop(struct input_queue *q, struct yasl_input_event *ev) +{ + if (!ev || (!q->count && !q->reset_pending)) return false; + free(q->last_text); + q->last_text = NULL; + if (q->reset_pending) { + *ev = (struct yasl_input_event){ + .surface = q->reset_surface, .kind = YASL_INPUT_RESET, + }; + q->reset_pending = false; + return true; + } + *ev = q->events[q->head]; + size_t len; + q->last_text = (char *)event_text(ev, &len); + q->head = (q->head + 1) % q->capacity; + q->count--; + return true; +} + +void input_queue_clear(struct input_queue *q) +{ + discard_pending(q); + free(q->last_text); + free(q->events); + *q = (struct input_queue){0}; +} diff --git a/src/common/input.h b/src/common/input.h new file mode 100644 index 0000000..d7264f6 --- /dev/null +++ b/src/common/input.h @@ -0,0 +1,19 @@ +#ifndef YASL_INPUT_H +#define YASL_INPUT_H + +#include "yasl/yasl.h" + +/* Each queued string and the last successfully polled string are owned here. */ +struct input_queue { + struct yasl_input_event *events; + size_t head, count, capacity; + char *last_text; + struct yasl_surface *reset_surface; + bool reset_pending; +}; + +bool input_queue_push(struct input_queue *q, const struct yasl_input_event *ev); +bool input_queue_pop(struct input_queue *q, struct yasl_input_event *ev); +void input_queue_clear(struct input_queue *q); + +#endif diff --git a/src/common/meson.build b/src/common/meson.build index 61653df..5cfb187 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -1 +1 @@ -sources += files('backend.c', 'loop.c', 'timing.c', 'utf8.c') +sources += files('backend.c', 'input.c', 'loop.c', 'timing.c', 'utf8.c') diff --git a/src/common/timing.c b/src/common/timing.c index 3e3a705..17f71b5 100644 --- a/src/common/timing.c +++ b/src/common/timing.c @@ -17,6 +17,7 @@ void yasl_clock_presented(struct yasl_frame_clock *c, uint64_t now, bool sample) c->refresh_ns += 0.125 * (dt - c->refresh_ns); } c->last_present_ns = now; + c->source = YASL_TIMING_SUBMISSION; } void yasl_clock_note_resize(struct yasl_frame_clock *c, uint64_t dt_ns) |
