diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/backend.c | 8 | ||||
| -rw-r--r-- | src/common/input.c | 9 | ||||
| -rw-r--r-- | src/common/loop.c | 54 | ||||
| -rw-r--r-- | src/wayland/buffer.c | 34 | ||||
| -rw-r--r-- | src/wayland/clipboard.c | 25 | ||||
| -rw-r--r-- | src/wayland/internal.h | 9 | ||||
| -rw-r--r-- | src/wayland/pointer.c | 66 | ||||
| -rw-r--r-- | src/wayland/popup.c | 5 | ||||
| -rw-r--r-- | src/wayland/surface.c | 12 | ||||
| -rw-r--r-- | src/x11/window.c | 28 |
10 files changed, 162 insertions, 88 deletions
diff --git a/src/common/backend.c b/src/common/backend.c index 4c755bd..c8d2caf 100644 --- a/src/common/backend.c +++ b/src/common/backend.c @@ -217,9 +217,13 @@ bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer 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) { + struct yasl_damage_list clipped; + clipped.n = damage->n; + if (damage->n < 0) { + clipped.bounds = damage->bounds; + } else { clipped.n = 0; + clipped.bounds = (struct yasl_rect2i){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; diff --git a/src/common/input.c b/src/common/input.c index eac4d1a..ff578e3 100644 --- a/src/common/input.c +++ b/src/common/input.c @@ -53,8 +53,13 @@ bool input_queue_push(struct input_queue *q, const struct yasl_input_event *ev) 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]; + if (q->events && q->count > 0) { + size_t first = q->capacity - q->head; + if (first > q->count) first = q->count; + memcpy(events, q->events + q->head, first * sizeof(*events)); + if (q->count > first) + memcpy(events + first, q->events, (q->count - first) * sizeof(*events)); + } free(q->events); q->events = events; q->capacity = capacity; diff --git a/src/common/loop.c b/src/common/loop.c index a63df16..18d62b1 100644 --- a/src/common/loop.c +++ b/src/common/loop.c @@ -25,9 +25,9 @@ struct yasl_fd_source { struct yasl_timer_source { yasl_timer_cb fn; void *ud; - yasl_timer_id id; uint64_t interval_ns; uint64_t deadline_ns; + yasl_timer_id id; bool deleted; }; @@ -330,13 +330,18 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms) for (int i = 0; i < count; i++) { if (pcopy[i].revents) { short revents = pcopy[i].revents; - // Verify that the source was not deleted by an earlier callback in this step + // Fast-path: check if source is still at index i before linear scan bool valid = false; - for (int j = 0; j < l->count; j++) { - if (l->fds[j].id == scopy[i].id) { - l->pollfds[j].revents = 0; - valid = true; - break; + if (i < l->count && l->fds[i].id == scopy[i].id) { + l->pollfds[i].revents = 0; + valid = true; + } else { + for (int j = 0; j < l->count; j++) { + if (l->fds[j].id == scopy[i].id) { + l->pollfds[j].revents = 0; + valid = true; + break; + } } } if (valid && scopy[i].fn) { @@ -367,18 +372,31 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms) uint64_t next_interval = timer_src.fn(ud, tid, interval); handled++; - for (int j = 0; j < l->timer_count; j++) { - if (l->timers[j].id == tid) { - if (next_interval == 0 || l->timers[j].deleted) { - l->timers[j].deleted = true; - } else { - l->timers[j].interval_ns = next_interval; - if (l->timers[j].deadline_ns + next_interval > now) - l->timers[j].deadline_ns += next_interval; - else - l->timers[j].deadline_ns = now + next_interval; + // Fast-path: timer is usually still at index i unless an earlier timer was deleted + if (i < l->timer_count && l->timers[i].id == tid) { + if (next_interval == 0 || l->timers[i].deleted) { + l->timers[i].deleted = true; + } else { + l->timers[i].interval_ns = next_interval; + if (l->timers[i].deadline_ns + next_interval > now) + l->timers[i].deadline_ns += next_interval; + else + l->timers[i].deadline_ns = now + next_interval; + } + } else { + for (int j = 0; j < l->timer_count; j++) { + if (l->timers[j].id == tid) { + if (next_interval == 0 || l->timers[j].deleted) { + l->timers[j].deleted = true; + } else { + l->timers[j].interval_ns = next_interval; + if (l->timers[j].deadline_ns + next_interval > now) + l->timers[j].deadline_ns += next_interval; + else + l->timers[j].deadline_ns = now + next_interval; + } + break; } - break; } } } diff --git a/src/wayland/buffer.c b/src/wayland/buffer.c index a1486fa..0561da3 100644 --- a/src/wayland/buffer.c +++ b/src/wayland/buffer.c @@ -322,19 +322,16 @@ int buffer_pick_free_index(const struct buffer *bufs, int cur) return free_buf_lut[(unsigned)cur & 3][busy]; } -struct latency_feedback { - struct wayland_surface *window; - struct wp_presentation_feedback *proxy; - struct latency_feedback *next; -}; - static void feedback_destroy(struct latency_feedback *feedback) { struct latency_feedback **link = &feedback->window->latency_feedbacks; while (*link && *link != feedback) link = &(*link)->next; if (*link) *link = feedback->next; wp_presentation_feedback_destroy(feedback->proxy); - free(feedback); + if (feedback->in_pool) + *feedback = (struct latency_feedback){0}; + else + free(feedback); } void surface_feedback_destroy(struct wayland_surface *w) @@ -386,10 +383,22 @@ void surface_request_presentation_feedback(struct wayland_surface *w) return; struct wp_presentation_feedback *fb = wp_presentation_feedback(w->conn->presentation, w->surface); - struct latency_feedback *feedback = fb ? calloc(1, sizeof(*feedback)) : NULL; - if (!feedback) { - if (fb) wp_presentation_feedback_destroy(fb); + if (!fb) return; + struct latency_feedback *feedback = NULL; + for (int i = 0; i < 8; i++) { + if (!w->latency_pool[i].window) { + feedback = &w->latency_pool[i]; + feedback->in_pool = true; + break; + } + } + if (!feedback) { + feedback = calloc(1, sizeof(*feedback)); + if (!feedback) { + wp_presentation_feedback_destroy(fb); + return; + } } feedback->window = w; feedback->proxy = fb; @@ -449,7 +458,10 @@ bool wayland_surface_present(struct wayland_surface *w, const struct yasl_frameb fb->size.x != w->width || fb->size.y != w->height) return false; - w->damage = *damage; + w->damage.n = damage->n; + w->damage.bounds = damage->bounds; + if (damage->n > 0) + memcpy(w->damage.rects, damage->rects, (size_t)damage->n * sizeof(damage->rects[0])); arm_frame(w); if (w->closed) return false; diff --git a/src/wayland/clipboard.c b/src/wayland/clipboard.c index 7df1a70..e3eec0f 100644 --- a/src/wayland/clipboard.c +++ b/src/wayland/clipboard.c @@ -118,21 +118,18 @@ static int transfer_io(int fd, short events, void *ud) if (t->offset == t->len || n == 0 || (n < 0 && errno != EAGAIN && errno != EINTR)) transfer_destroy(t); } else { - char buf[8192]; - ssize_t n = read(fd, buf, sizeof(buf)); + if (t->capacity - t->len < 4096) { + size_t capacity = t->capacity ? t->capacity * 2 : 8192; + if (capacity > CLIPBOARD_TEXT_MAX) capacity = CLIPBOARD_TEXT_MAX; + if (capacity <= t->len) { transfer_destroy(t); return 0; } + char *text = realloc(t->text, capacity); + if (!text) { transfer_destroy(t); return 0; } + t->text = text; + t->capacity = capacity; + } + ssize_t n = read(fd, t->text + t->len, t->capacity - t->len); if (n > 0) { - size_t need = t->len + (size_t)n; - if (need > CLIPBOARD_TEXT_MAX) { transfer_destroy(t); return 0; } - if (need > t->capacity) { - size_t capacity = t->capacity ? t->capacity * 2 : sizeof(buf); - if (capacity > CLIPBOARD_TEXT_MAX) capacity = CLIPBOARD_TEXT_MAX; - char *text = realloc(t->text, capacity); - if (!text) { transfer_destroy(t); return 0; } - t->text = text; - t->capacity = capacity; - } - memcpy(t->text + t->len, buf, (size_t)n); - t->len += n; + t->len += (size_t)n; } else if (n == 0) { t->owner->receive(t->text ? t->text : "", t->len, t->uri_list, t->drop_surface, t->owner->ud); diff --git a/src/wayland/internal.h b/src/wayland/internal.h index 04142f7..d519abd 100644 --- a/src/wayland/internal.h +++ b/src/wayland/internal.h @@ -76,6 +76,7 @@ struct pending_press { struct press_queue { struct pending_press ev[8]; + int head; int n; }; @@ -244,6 +245,13 @@ struct wayland_display { bool lost; }; +struct latency_feedback { + struct wayland_surface *window; + struct wp_presentation_feedback *proxy; + struct latency_feedback *next; + bool in_pool; +}; + struct wayland_surface { struct yasl_surface base; /* 8-byte pointers & 64-bit values */ @@ -264,6 +272,7 @@ struct wayland_surface { struct seat *ime_seat; struct wp_tearing_control_v1 *tearing_control; struct latency_feedback *latency_feedbacks; + struct latency_feedback latency_pool[8]; struct wp_fifo_v1 *fifo; struct wp_commit_timer_v1 *commit_timer; struct wl_callback *frame_cb; diff --git a/src/wayland/pointer.c b/src/wayland/pointer.c index 5924eb1..065669f 100644 --- a/src/wayland/pointer.c +++ b/src/wayland/pointer.c @@ -248,8 +248,9 @@ static void press_queue_flush(struct seat *s, struct press_queue *q, struct yasl_mouse_event like) { int kept = 0; - for (int i = 0; i < q->n; i++) { - struct pending_press p = q->ev[i]; + int count = q->n; + for (int i = 0; i < count; i++) { + struct pending_press p = q->ev[(q->head + i) & 7]; if (only && p.surface != only) { q->ev[kept++] = p; continue; @@ -263,26 +264,30 @@ static void press_queue_flush(struct seat *s, struct press_queue *q, ev.kind = (enum yasl_pointer_kind)p.release; push_mouse(s, p.surface, ev); } + q->head = 0; q->n = kept; } static void press_queue_push(struct press_queue *q, struct wayland_surface *w, struct yasl_vector2i pos, int button, bool release) { - if (q->n == (int)(sizeof(q->ev) / sizeof(q->ev[0]))) { - memmove(q->ev, q->ev + 1, sizeof(q->ev[0]) * (size_t)(q->n - 1)); + if (q->n == 8) { + q->head = (q->head + 1) & 7; q->n--; } - q->ev[q->n++] = (struct pending_press){ + q->ev[(q->head + q->n) & 7] = (struct pending_press){ .surface = w, .pos = pos, .button = button, .release = release, }; + q->n++; } static void press_queue_forget(struct press_queue *q, struct wayland_surface *w) { - for (int i = 0; i < q->n; i++) - if (q->ev[i].surface == w) - q->ev[i].surface = NULL; + for (int i = 0; i < q->n; i++) { + int idx = (q->head + i) & 7; + if (q->ev[idx].surface == w) + q->ev[idx].surface = NULL; + } } void seat_pointer_reset(struct seat *s) @@ -566,28 +571,7 @@ static struct yasl_vector2i touch_pos(const struct touch_point *p) }; } -static int touch_count(const struct seat *s) -{ - int n = 0; - for (int i = 0; i < SEAT_TOUCH_POINTS; i++) - if (s->touch_points[i].active && !s->touch_points[i].up) - n++; - return n; -} -static double touch_centroid_y(const struct seat *s, const struct wayland_surface *on) -{ - double sum = 0; - int n = 0; - for (int i = 0; i < SEAT_TOUCH_POINTS; i++) { - const struct touch_point *p = &s->touch_points[i]; - if (p->active && !p->up && p->surface == on) { - sum += p->y; - n++; - } - } - return n ? sum / n : 0; -} static void touch_report(struct seat *s, struct touch_point *p, int button, enum yasl_pointer_kind kind) @@ -641,24 +625,35 @@ static void touch_frame(void *data, struct wl_touch *touch) { (void)touch; struct seat *s = data; + int active_n = 0; + double centroid_sum = 0; + int centroid_n = 0; for (int i = 0; i < SEAT_TOUCH_POINTS; i++) { struct touch_point *p = &s->touch_points[i]; - if (p->active && p->down && !s->touch_primary && !s->touch_scrolling) { + if (!p->active) continue; + if (p->down && !s->touch_primary && !s->touch_scrolling) { s->touch_primary = p; s->touch_pressed = true; touch_report(s, p, 0, YASL_PTR_PRESS); } + if (!p->up) { + active_n++; + if (s->touch_primary && p->surface == s->touch_primary->surface) { + centroid_sum += p->y; + centroid_n++; + } + } } struct touch_point *primary = s->touch_primary; - if (primary && !s->touch_scrolling && touch_count(s) >= 2) { + if (primary && !s->touch_scrolling && active_n >= 2) { if (s->touch_pressed) touch_report(s, primary, 0, YASL_PTR_RELEASE); s->touch_pressed = false; s->touch_scrolling = true; - s->touch_scroll_y = touch_centroid_y(s, primary->surface); + s->touch_scroll_y = centroid_n ? centroid_sum / centroid_n : 0; s->touch_scroll_rest = 0; - } else if (s->touch_scrolling && primary && touch_count(s) >= 2) { - double y = touch_centroid_y(s, primary->surface); + } else if (s->touch_scrolling && primary && active_n >= 2) { + double y = centroid_n ? centroid_sum / centroid_n : 0; double dy = y - s->touch_scroll_y; s->touch_scroll_y = y; if (dy != 0.0) { @@ -679,6 +674,7 @@ static void touch_frame(void *data, struct wl_touch *touch) struct touch_point *p = &s->touch_points[i]; if (!p->active) continue; if (p->up) { + active_n--; if (p == s->touch_primary) { if (s->touch_pressed) touch_report(s, p, 0, YASL_PTR_RELEASE); @@ -690,7 +686,7 @@ static void touch_frame(void *data, struct wl_touch *touch) p->down = p->moved = false; } } - if (!touch_count(s)) + if (active_n <= 0) s->touch_scrolling = false; } diff --git a/src/wayland/popup.c b/src/wayland/popup.c index 63bcba6..4364934 100644 --- a/src/wayland/popup.c +++ b/src/wayland/popup.c @@ -145,7 +145,10 @@ struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *par w->clock.refresh_ns = parent->clock.refresh_ns; w->surface = wl_compositor_create_surface(c->compositor); - if (!w->surface || surface_init_scaling(w) < 0) + if (!w->surface) + goto err; + wl_surface_set_user_data(w->surface, w); + if (surface_init_scaling(w) < 0) goto err; w->xdg_surface = xdg_wm_base_get_xdg_surface(c->wm_base, w->surface); if (!w->xdg_surface) diff --git a/src/wayland/surface.c b/src/wayland/surface.c index ce1764b..242531c 100644 --- a/src/wayland/surface.c +++ b/src/wayland/surface.c @@ -321,7 +321,11 @@ static void conn_unlink(struct wayland_surface *w) struct wayland_surface *conn_surface_for(struct wayland_display *c, struct wl_surface *s) { - for (struct wayland_surface *w = c->surfaces; w && s; w = w->conn_next) + if (unlikely(!c || !s)) return NULL; + struct wayland_surface *fast = (struct wayland_surface *)wl_surface_get_user_data(s); + if (likely(fast && fast->conn == c && fast->surface == s)) + return fast; + for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) if (w->surface == s) return w; return NULL; @@ -361,6 +365,7 @@ struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display * w->surface = wl_compositor_create_surface(c->compositor); if (!w->surface) goto err; + wl_surface_set_user_data(w->surface, w); if (surface_init_scaling(w) < 0) goto err; if (surface_init_shell(w, spec->title) < 0) goto err; w->opaque = spec->fullscreen; @@ -420,7 +425,10 @@ void wayland_surface_destroy(struct wayland_surface *w) if (w->toplevel) xdg_toplevel_destroy(w->toplevel); if (w->popup) xdg_popup_destroy(w->popup); if (w->xdg_surface) xdg_surface_destroy(w->xdg_surface); - if (w->surface) wl_surface_destroy(w->surface); + if (w->surface) { + wl_surface_set_user_data(w->surface, NULL); + wl_surface_destroy(w->surface); + } struct wayland_display *c = w->conn; conn_unlink(w); static const struct yasl_surface_cbs no_callbacks = {0}; diff --git a/src/x11/window.c b/src/x11/window.c index 5e1b612..271c128 100644 --- a/src/x11/window.c +++ b/src/x11/window.c @@ -1280,10 +1280,32 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb XImage *im = s->image; bool direct = im->bits_per_pixel == 32 && im->red_mask == 0xff0000 && im->green_mask == 0xff00 && im->blue_mask == 0xff; - for (int y = 0; y < s->size.y; y++) { + int y_start = 0, y_end = s->size.y; + int x_start = 0, x_end = s->size.x; + if (damage->n >= 0) { + y_start = s->size.y; y_end = 0; + x_start = s->size.x; x_end = 0; + for (int i = 0; i < damage->n; i++) { + struct yasl_rect2i r = damage->rects[i]; + int y0 = r.y > 0 ? r.y : 0; + int y1 = (r.y + r.height < s->size.y) ? r.y + r.height : s->size.y; + int x0 = r.x > 0 ? r.x : 0; + int x1 = (r.x + r.width < s->size.x) ? r.x + r.width : s->size.x; + if (y0 < y_start) y_start = y0; + if (y1 > y_end) y_end = y1; + if (x0 < x_start) x_start = x0; + if (x1 > x_end) x_end = x1; + } + if (y_start >= y_end || x_start >= x_end) { + y_start = y_end = x_start = x_end = 0; + } + } + size_t copy_bytes = (size_t)(x_end - x_start) * 4; + for (int y = y_start; y < y_end; y++) { const uint32_t *row = s->pixels + (size_t)y * s->size.x; - if (direct) memcpy(im->data + (size_t)y * im->bytes_per_line, row, (size_t)s->size.x * 4); - else for (int x = 0; x < s->size.x; x++) { + if (direct) memcpy(im->data + (size_t)y * im->bytes_per_line + (size_t)x_start * 4, + row + x_start, copy_bytes); + else for (int x = x_start; x < x_end; x++) { uint32_t p = row[x]; XPutPixel(im, x, y, component((p >> 16) & 255, im->red_mask) | component((p >> 8) & 255, im->green_mask) | component(p & 255, im->blue_mask)); |
