summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/backend.c57
-rw-r--r--src/common/backend.h9
-rw-r--r--src/common/input.c103
-rw-r--r--src/common/input.h19
-rw-r--r--src/common/meson.build2
-rw-r--r--src/common/timing.c1
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(&copy, 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)