diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/backend.h | 1 | ||||
| -rw-r--r-- | src/common/surface.c | 4 | ||||
| -rw-r--r-- | src/common/types.h | 3 | ||||
| -rw-r--r-- | src/common/vulkan.c | 1 | ||||
| -rw-r--r-- | src/platform/wayland/backend.c | 82 | ||||
| -rw-r--r-- | src/platform/wayland/buffer.c | 8 | ||||
| -rw-r--r-- | src/platform/wayland/internal.h | 15 | ||||
| -rw-r--r-- | src/platform/wayland/pointer.c | 2 | ||||
| -rw-r--r-- | src/platform/wayland/surface.c | 347 | ||||
| -rw-r--r-- | src/platform/x11/window.c | 1 |
10 files changed, 309 insertions, 155 deletions
diff --git a/src/common/backend.h b/src/common/backend.h index e3dc6de..bf6815a 100644 --- a/src/common/backend.h +++ b/src/common/backend.h @@ -16,6 +16,7 @@ struct yasl_private_event_state; struct yasl_private_display { const struct yasl_private_backend_ops *ops; void *native_handle; + bool offline; /* Connection lost; native handles are unusable until it is restored. */ struct yasl_private_display_cbs callbacks; struct yasl_private_loop *loop; /* Backend's copy of yasl_private_display_spec.app_id, or NULL; for desktop services. */ diff --git a/src/common/surface.c b/src/common/surface.c index 897b95a..9b4a8ad 100644 --- a/src/common/surface.c +++ b/src/common/surface.c @@ -32,13 +32,14 @@ bool yasl_public_fail(int error) else if (error==ENOMEM) code=YASL_ERROR_NO_MEMORY; else if (error==EBUSY) code=YASL_ERROR_BUSY; else if (error==EPERM) code=YASL_ERROR_WRONG_THREAD; + else if (error==ENOTCONN) code=YASL_ERROR_DISCONNECTED; yasl_public_set_error(code,error); errno=error; return false; } enum yasl_error yasl_get_error(const char **description) { static const char *const messages[]={"No error","Invalid argument","Unsupported operation", "Out of memory","Library not initialized","Operation is busy","Wrong thread", - "Input context expired","Native operation failed"}; + "Input context expired","Native operation failed","Display disconnected"}; if(description) *description=messages[last_error]; return last_error; } @@ -254,6 +255,7 @@ static bool convert_event(const struct yasl_private_event *p,yasl_event *e) case YASL_PRIVATE_EVENT_DISPLAY_QUIT:e->type=YASL_EVENT_QUIT;break; case YASL_PRIVATE_EVENT_DISPLAY_REOPEN:e->type=YASL_EVENT_REOPEN;break; case YASL_PRIVATE_EVENT_DISPLAY_LOST:e->type=YASL_EVENT_DISPLAY_LOST;break; + case YASL_PRIVATE_EVENT_DISPLAY_RESTORED:e->type=YASL_EVENT_DISPLAY_RESTORED;break; case YASL_PRIVATE_EVENT_REFRESH: if(!p->surface) return false; if(!refresh_ready(p->surface)) { p->surface->refresh_owed=true; return false; } diff --git a/src/common/types.h b/src/common/types.h index 91f8a47..40c502f 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -355,7 +355,8 @@ enum yasl_private_event_type { YASL_PRIVATE_EVENT_DISPLAY_LOST, YASL_PRIVATE_EVENT_PEN, YASL_PRIVATE_EVENT_TEXT_EDIT, - YASL_PRIVATE_EVENT_REFRESH, /**< No payload; display connection failed. */ + YASL_PRIVATE_EVENT_REFRESH, /**< No payload; surface contents are needed. */ + YASL_PRIVATE_EVENT_DISPLAY_RESTORED, /**< No payload; display reconnected after DISPLAY_LOST. */ }; /** Physical keyboard transition with optional layout labels. diff --git a/src/common/vulkan.c b/src/common/vulkan.c index 1bca415..62bc3a0 100644 --- a/src/common/vulkan.c +++ b/src/common/vulkan.c @@ -55,6 +55,7 @@ VkResult yasl_vulkan_create_surface(yasl_surface *s,VkInstance instance,const Vk { if(out)*out=VK_NULL_HANDLE; if(!yasl_public_surface(s,false)||!instance||!out||!get_proc){yasl_public_fail(EINVAL);return VK_ERROR_INITIALIZATION_FAILED;} + if(s->display->offline){yasl_public_fail(ENOTCONN);return VK_ERROR_SURFACE_LOST_KHR;} if(s->presenter!=YASL_PRIVATE_PRESENTER_NONE){yasl_public_fail(EBUSY);return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;} VkResult result=VK_ERROR_EXTENSION_NOT_PRESENT; switch(s->display->ops->type) { diff --git a/src/platform/wayland/backend.c b/src/platform/wayland/backend.c index 02aae59..b3337cc 100644 --- a/src/platform/wayland/backend.c +++ b/src/platform/wayland/backend.c @@ -125,42 +125,36 @@ static bool egl_configure(struct wayland_display *d, const struct yasl_private_g return eglBindAPI(api) == EGL_TRUE; } -static struct yasl_surface *impl_surface_create_gl(struct yasl_private_display *display, - const struct yasl_private_window_spec *spec, const int *attributes, struct yasl_surface *share) -{ - struct wayland_display *d = (struct wayland_display *)display; - struct yasl_private_gl_options o; - if (!spec || !yasl_private_gl_parse_options(attributes, &o) || !d || !d->egl_initialized) - return NULL; - struct yasl_private_window_spec gl_spec = *spec; - gl_spec.external_gpu = true; - gl_spec.flags &= ~YASL_PRIVATE_SURFACE_CPU; - struct wayland_surface *w = wayland_surface_create_toplevel(d, &gl_spec, NULL); - if (!w) return NULL; - if (!egl_configure(d, &o, &w->egl_config)) goto fail; - EGLContext share_context = share && share->display == display - ? ((struct wayland_surface *)share)->egl_context : EGL_NO_CONTEXT; - if (share && share_context == EGL_NO_CONTEXT) goto fail; +bool surface_build_gl(struct wayland_surface *w) +{ + struct wayland_display *d = w->conn; + const struct yasl_private_gl_options *o = &w->gl_options; + if (!egl_configure(d, o, &w->egl_config)) return false; + EGLContext share_context = EGL_NO_CONTEXT; + if (w->gl_share) { + if (!w->gl_share->gl_built && !surface_build_gl(w->gl_share)) return false; + share_context = w->gl_share->egl_context; + } w->egl_window = wl_egl_window_create(w->surface, w->width, w->height); - if (!w->egl_window) goto fail; + if (!w->egl_window) return false; EGLint context_attrs[12]; int n = 0; - if (o.api == YASL_PRIVATE_GL_OPENGL_ES) { + if (o->api == YASL_PRIVATE_GL_OPENGL_ES) { context_attrs[n++] = EGL_CONTEXT_CLIENT_VERSION; - context_attrs[n++] = o.major; + context_attrs[n++] = o->major; } else { context_attrs[n++] = EGL_CONTEXT_MAJOR_VERSION_KHR; - context_attrs[n++] = o.major; + context_attrs[n++] = o->major; context_attrs[n++] = EGL_CONTEXT_MINOR_VERSION_KHR; - context_attrs[n++] = o.minor; - if (o.profile != YASL_PRIVATE_GL_ANY) { + context_attrs[n++] = o->minor; + if (o->profile != YASL_PRIVATE_GL_ANY) { context_attrs[n++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; - context_attrs[n++] = o.profile == YASL_PRIVATE_GL_CORE + context_attrs[n++] = o->profile == YASL_PRIVATE_GL_CORE ? EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR : EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; } - EGLint flags = (o.debug ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0) | - (o.forward_compat ? EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR : 0); + EGLint flags = (o->debug ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0) | + (o->forward_compat ? EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR : 0); if (flags) { context_attrs[n++] = EGL_CONTEXT_FLAGS_KHR; context_attrs[n++] = flags; @@ -168,21 +162,42 @@ static struct yasl_surface *impl_surface_create_gl(struct yasl_private_display * } context_attrs[n] = EGL_NONE; w->egl_context = eglCreateContext(d->egl_display, w->egl_config, share_context, context_attrs); - if (w->egl_context == EGL_NO_CONTEXT) goto fail; + if (w->egl_context == EGL_NO_CONTEXT) { w->egl_context = 0; return false; } EGLint surface_attrs[] = { #ifdef EGL_GL_COLORSPACE_KHR - EGL_GL_COLORSPACE_KHR, o.srgb ? EGL_GL_COLORSPACE_SRGB_KHR : EGL_GL_COLORSPACE_LINEAR_KHR, + EGL_GL_COLORSPACE_KHR, o->srgb ? EGL_GL_COLORSPACE_SRGB_KHR : EGL_GL_COLORSPACE_LINEAR_KHR, #endif EGL_NONE, }; w->egl_surface = eglCreateWindowSurface(d->egl_display, w->egl_config, (EGLNativeWindowType)w->egl_window, surface_attrs); - if (w->egl_surface == EGL_NO_SURFACE) { w->egl_surface = 0; goto fail; } + if (w->egl_surface == EGL_NO_SURFACE) { w->egl_surface = 0; return false; } + w->gl_built = true; + return true; +} + +static struct yasl_surface *impl_surface_create_gl(struct yasl_private_display *display, + const struct yasl_private_window_spec *spec, const int *attributes, struct yasl_surface *share) +{ + struct wayland_display *d = (struct wayland_display *)display; + struct yasl_private_gl_options o; + if (!spec || !yasl_private_gl_parse_options(attributes, &o) || !d || !d->egl_initialized) + return NULL; + if (share && (share->display != display || !((struct wayland_surface *)share)->egl_context)) + return NULL; + struct yasl_private_window_spec gl_spec = *spec; + gl_spec.external_gpu = true; + gl_spec.flags &= ~YASL_PRIVATE_SURFACE_CPU; + struct wayland_surface *w = wayland_surface_create_toplevel(d, &gl_spec, NULL); + if (!w) return NULL; + w->gl_options = o; + w->gl_share = (struct wayland_surface *)share; + if (!surface_build_gl(w)) { + wayland_surface_destroy(w); + return NULL; + } w->base.presenter = YASL_PRIVATE_PRESENTER_GL; return &w->base; -fail: - wayland_surface_destroy(w); - return NULL; } static bool impl_gl_make_current(struct yasl_private_display *display, struct yasl_surface *surface) @@ -250,7 +265,7 @@ static struct input_queue *impl_surface_input_queue(struct yasl_surface *s) static bool impl_surface_set_size(struct yasl_surface *s, struct yasl_private_vector2i size) { struct wayland_surface *w = (struct wayland_surface *)s; - if (!w || w->closed || w->destroyed || !w->toplevel || + if (!w || w->closed || w->destroyed || (!w->toplevel && !w->conn->lost) || size.x <= 0 || size.y <= 0) { errno = EINVAL; return false; @@ -258,7 +273,8 @@ static bool impl_surface_set_size(struct yasl_surface *s, struct yasl_private_ve /* Floating windows choose their own size; constrained states keep it for later. */ w->floating_width = size.x; w->floating_height = size.y; - if (w->base.state & (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_TILED)) + if (w->conn->lost || + (w->base.state & (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_TILED))) return true; w->req_width = w->req_height = 0; w->size_pending = true; diff --git a/src/platform/wayland/buffer.c b/src/platform/wayland/buffer.c index bbc055d..029b684 100644 --- a/src/platform/wayland/buffer.c +++ b/src/platform/wayland/buffer.c @@ -211,6 +211,7 @@ static const struct wp_fractional_scale_v1_listener frac_scale_listener = { void surface_follow_outputs(struct wayland_surface *w) { + if (!w->surface) return; /* Not rebuilt yet after a reconnect. */ uint32_t refresh = 0; int32_t max_scale = 0; bool need_scale = !w->frac_scale && @@ -563,11 +564,12 @@ int surface_init_scaling(struct wayland_surface *w) void wayland_surface_set_title(struct wayland_surface *w, const char *title) { - if (!w || !w->toplevel || !title) return; + if (!w || !title) return; char *copy = yasl_private_utf8_sanitize_dup(title); if (!copy) return; - xdg_toplevel_set_title(w->toplevel, copy); - free(copy); + free(w->title); + w->title = copy; + if (w->toplevel) xdg_toplevel_set_title(w->toplevel, copy); } struct yasl_private_timing wayland_surface_timing(const struct wayland_surface *w) diff --git a/src/platform/wayland/internal.h b/src/platform/wayland/internal.h index a479e2b..92108c1 100644 --- a/src/platform/wayland/internal.h +++ b/src/platform/wayland/internal.h @@ -251,6 +251,10 @@ struct wayland_display { struct conn_drag drag; struct wayland_surface *surfaces; struct wayland_surface *dead; + /* While lost: retries a connection; stale marks objects of the dead one. */ + yasl_private_timer_id reconnect_timer; + uint64_t reconnect_delay_ns; + bool stale; /* 4-byte integers */ int cursor_size; @@ -287,7 +291,12 @@ struct wayland_surface { EGLSurface egl_surface; EGLContext egl_context; EGLConfig egl_config; + struct yasl_private_gl_options gl_options; + struct wayland_surface *gl_share; + bool gl_built; #endif + char *title; + bool tearing; struct xdg_surface *xdg_surface; struct xdg_toplevel *toplevel; struct xdg_popup *popup; @@ -393,6 +402,12 @@ void surface_draw_frame(struct wayland_surface *w); void wayland_surface_arm_frame(struct wayland_surface *w); void surface_fail(struct wayland_surface *w); void display_leave(struct wayland_display *c); +/* Native objects of a toplevel: built at creation and again after a reconnect. */ +int surface_build_toplevel(struct wayland_surface *w, bool fullscreen); +void surface_release_native(struct wayland_surface *w); +#if YASL_HAS_EGL +bool surface_build_gl(struct wayland_surface *w); +#endif static inline struct wayland_surface *surface_root(struct wayland_surface *w) { diff --git a/src/platform/wayland/pointer.c b/src/platform/wayland/pointer.c index 5f486ae..0cd8c5b 100644 --- a/src/platform/wayland/pointer.c +++ b/src/platform/wayland/pointer.c @@ -542,7 +542,7 @@ static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = { static void surface_lock_pointer(struct wayland_surface *w, struct seat *s) { struct wayland_display *c = w->conn; - if (!w->relative || w->pointer_lock || w->destroyed || !s->pointer) + if (!w->relative || w->pointer_lock || w->destroyed || !w->surface || !s->pointer) return; w->pointer_lock = zwp_pointer_constraints_v1_lock_pointer(c->pointer_constraints, w->surface, s->pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); diff --git a/src/platform/wayland/surface.c b/src/platform/wayland/surface.c index 31096f4..e04a594 100644 --- a/src/platform/wayland/surface.c +++ b/src/platform/wayland/surface.c @@ -3,6 +3,7 @@ #include <errno.h> static void display_free(struct wayland_display *c); +static uint64_t reconnect_tick(void *ud, yasl_private_timer_id id, uint64_t interval_ns); static void display_cancel_read(struct wayland_display *c) { @@ -12,19 +13,16 @@ static void display_cancel_read(struct wayland_display *c) c->reading = false; } +/* A compositor going away is expected; a protocol error is a YASL bug worth reporting. */ static void report_display_error(struct wayland_display *c) { - int err = wl_display_get_error(c->wl); - if (!err) + if (wl_display_get_error(c->wl) != EPROTO) return; uint32_t object_id = 0; const struct wl_interface *iface = NULL; uint32_t protocol_err = wl_display_get_protocol_error(c->wl, &iface, &object_id); - if (protocol_err) - fprintf(stderr, "[yasl] protocol error %u on %s (object %u)\n", - protocol_err, iface ? iface->name : "?", object_id); - else - fprintf(stderr, "[yasl] display error %d\n", err); + fprintf(stderr, "[yasl] protocol error %u on %s (object %u)\n", + protocol_err, iface ? iface->name : "?", object_id); } void display_leave(struct wayland_display *c) @@ -39,20 +37,32 @@ void display_leave(struct wayland_display *c) } } +/* Windows outlive the connection. Its objects stay until the next dispatch, after + * the application has seen DISPLAY_LOST and released GPU objects built on them. */ static void display_lose(struct wayland_display *c) { if (c->lost) return; c->lost = true; + c->stale = true; + c->base.offline = true; report_display_error(c); display_cancel_read(c); + if (c->loop && c->watch > 0) + yasl_private_loop_del_fd(c->loop, c->watch); + c->watch = 0; c->depth++; + conn_drag_end(c); for (struct wayland_surface *w = c->surfaces, *next; w; w = next) { next = w->conn_next; - surface_fail(w); + if (w->parent) + surface_fail(w); } display_leave(c); yasl_private_display_notify_event(&c->base, YASL_PRIVATE_EVENT_DISPLAY_LOST); + c->reconnect_delay_ns = YASL_PRIVATE_MS_TO_NS(100); + if (c->loop && !c->reconnect_timer) + c->reconnect_timer = yasl_private_loop_add_timer(c->loop, c->reconnect_delay_ns, reconnect_tick, c); } void surface_fail(struct wayland_surface *w) @@ -131,21 +141,28 @@ static inline bool display_has_immediate_work(const struct wayland_display *c) int wayland_display_dispatch_timeout(struct wayland_display *c, int timeout_ms) { - if (!c || c->lost) + if (!c) return -1; c->depth++; + if (c->lost) { + /* Reconnect attempts run as a loop timer. */ + int n = c->loop ? yasl_private_loop_step(c->loop, timeout_ms) : 0; + display_leave(c); + return n; + } + /* Losing the connection is reported as DISPLAY_LOST, not as a failed wait. */ if (wl_display_dispatch_pending(c->wl) < 0) { display_lose(c); display_leave(c); - return -1; + return 0; } display_arm(c); if (c->lost) { display_leave(c); - return -1; + return 0; } if (timeout_ms != 0 && display_has_immediate_work(c)) { @@ -161,10 +178,10 @@ int wayland_display_dispatch_timeout(struct wayland_display *c, int timeout_ms) display_cancel_read(c); } - if (wl_display_dispatch_pending(c->wl) < 0) { + if (c->wl && wl_display_dispatch_pending(c->wl) < 0) { display_lose(c); display_leave(c); - return -1; + return 0; } display_leave(c); @@ -187,40 +204,126 @@ void wayland_display_flush(struct wayland_display *c) display_leave(c); } -static void display_free(struct wayland_display *c) +#define RELEASE(field, destroy) do { if (c->field) { destroy(c->field); c->field = NULL; } } while (0) + +static void connection_release(struct wayland_display *c) { + for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) + surface_release_native(w); conn_drag_end(c); if (c->loop && c->watch > 0) yasl_private_loop_del_fd(c->loop, c->watch); + c->watch = 0; if (c->wl) display_cancel_read(c); while (c->seats) seat_destroy(c->seats); - if (c->toplevel_drag_mgr) xdg_toplevel_drag_manager_v1_destroy(c->toplevel_drag_mgr); - if (c->primary_selection_mgr) zwp_primary_selection_device_manager_v1_destroy(c->primary_selection_mgr); - if (c->data_manager) wl_data_device_manager_destroy(c->data_manager); - if (c->cursor_shape_mgr) wp_cursor_shape_manager_v1_destroy(c->cursor_shape_mgr); - if (c->relative_pointer_mgr) zwp_relative_pointer_manager_v1_destroy(c->relative_pointer_mgr); - if (c->pointer_constraints) zwp_pointer_constraints_v1_destroy(c->pointer_constraints); - if (c->system_bell) xdg_system_bell_v1_destroy(c->system_bell); - if (c->cursor_theme) wl_cursor_theme_destroy(c->cursor_theme); - if (c->tablet_mgr) zwp_tablet_manager_v2_destroy(c->tablet_mgr); - if (c->text_input_mgr) zwp_text_input_manager_v3_destroy(c->text_input_mgr); - if (c->tearing_mgr) wp_tearing_control_manager_v1_destroy(c->tearing_mgr); - if (c->fifo_mgr) wp_fifo_manager_v1_destroy(c->fifo_mgr); - if (c->commit_timing_mgr) wp_commit_timing_manager_v1_destroy(c->commit_timing_mgr); - if (c->presentation) wp_presentation_destroy(c->presentation); - if (c->xkb_ctx) xkb_context_unref(c->xkb_ctx); - if (c->scale_mgr) wp_fractional_scale_manager_v1_destroy(c->scale_mgr); - if (c->viewporter) wp_viewporter_destroy(c->viewporter); - if (c->decor_mgr) zxdg_decoration_manager_v1_destroy(c->decor_mgr); + RELEASE(toplevel_drag_mgr, xdg_toplevel_drag_manager_v1_destroy); + RELEASE(primary_selection_mgr, zwp_primary_selection_device_manager_v1_destroy); + RELEASE(data_manager, wl_data_device_manager_destroy); + RELEASE(cursor_shape_mgr, wp_cursor_shape_manager_v1_destroy); + RELEASE(relative_pointer_mgr, zwp_relative_pointer_manager_v1_destroy); + RELEASE(pointer_constraints, zwp_pointer_constraints_v1_destroy); + RELEASE(system_bell, xdg_system_bell_v1_destroy); + RELEASE(cursor_theme, wl_cursor_theme_destroy); + RELEASE(tablet_mgr, zwp_tablet_manager_v2_destroy); + RELEASE(text_input_mgr, zwp_text_input_manager_v3_destroy); + RELEASE(tearing_mgr, wp_tearing_control_manager_v1_destroy); + RELEASE(fifo_mgr, wp_fifo_manager_v1_destroy); + RELEASE(commit_timing_mgr, wp_commit_timing_manager_v1_destroy); + RELEASE(presentation, wp_presentation_destroy); + RELEASE(scale_mgr, wp_fractional_scale_manager_v1_destroy); + RELEASE(viewporter, wp_viewporter_destroy); + RELEASE(decor_mgr, zxdg_decoration_manager_v1_destroy); conn_destroy_outputs(c); - if (c->wm_base) xdg_wm_base_destroy(c->wm_base); - if (c->shm) wl_shm_destroy(c->shm); - if (c->compositor) wl_compositor_destroy(c->compositor); - if (c->registry) wl_registry_destroy(c->registry); + RELEASE(wm_base, xdg_wm_base_destroy); + RELEASE(shm, wl_shm_destroy); + RELEASE(compositor, wl_compositor_destroy); + RELEASE(registry, wl_registry_destroy); #if YASL_HAS_EGL if (c->egl_initialized) eglTerminate(c->egl_display); + c->egl_initialized = false; + c->egl_display = EGL_NO_DISPLAY; + c->egl_api = 0; #endif - if (c->wl) wl_display_disconnect(c->wl); + RELEASE(wl, wl_display_disconnect); + c->base.native_handle = NULL; + c->cursor_theme_scale = 0; + c->pres_clock_known = false; + c->active_seat = NULL; +} + +static bool connection_open(struct wayland_display *c) +{ + c->wl = wl_display_connect(NULL); + if (!c->wl) return false; + c->base.native_handle = c->wl; +#if YASL_HAS_EGL + c->egl_display = eglGetDisplay((EGLNativeDisplayType)c->wl); + if (c->egl_display != EGL_NO_DISPLAY) { + EGLint major = 0, minor = 0; + c->egl_initialized = eglInitialize(c->egl_display, &major, &minor) == EGL_TRUE; + } +#endif + c->registry = wl_display_get_registry(c->wl); + if (!c->registry) return false; + wl_registry_add_listener(c->registry, ®istry_listener, c); + if (wl_display_roundtrip(c->wl) < 0 || !c->compositor || !c->shm || !c->wm_base) + return false; + surface_init_cursor(c); + if (c->loop) { + c->watch = yasl_private_loop_add_fd(c->loop, wl_display_get_fd(c->wl), POLLIN, display_ready, c); + if (c->watch <= 0) { c->watch = 0; return false; } + } + return display_input_init(c) == 0; +} + +/* Recreates every window on a new connection; one that cannot be rebuilt is closed. */ +static void display_rebuild(struct wayland_display *c) +{ + for (struct wayland_surface *w = c->surfaces; w && !c->lost; w = w->conn_next) { + if (w->parent || w->closed || w->destroyed) continue; + w->req_width = w->req_height = 0; + if (surface_build_toplevel(w, w->base.state & YASL_PRIVATE_STATE_FULLSCREEN) < 0 && !c->lost) + surface_fail(w); + } +#if YASL_HAS_EGL + for (struct wayland_surface *w = c->surfaces; w && !c->lost; w = w->conn_next) + if (w->base.presenter == YASL_PRIVATE_PRESENTER_GL && !w->closed && !w->gl_built && !surface_build_gl(w)) + surface_fail(w); +#endif + for (struct wayland_surface *w = c->surfaces; w && !c->lost; w = w->conn_next) + if (!w->closed) yasl_private_surface_notify_refresh(&w->base); +} + +static uint64_t reconnect_tick(void *ud, yasl_private_timer_id id, uint64_t interval_ns) +{ + (void)id; (void)interval_ns; + struct wayland_display *c = ud; + c->depth++; + if (c->stale) { + connection_release(c); + c->stale = false; + } + if (!connection_open(c)) { + connection_release(c); + display_leave(c); + uint64_t limit = YASL_PRIVATE_MS_TO_NS(2000); + c->reconnect_delay_ns = c->reconnect_delay_ns * 2 < limit ? c->reconnect_delay_ns * 2 : limit; + return c->reconnect_delay_ns; + } + c->reconnect_timer = 0; + c->lost = false; + c->base.offline = false; + yasl_private_display_notify_event(&c->base, YASL_PRIVATE_EVENT_DISPLAY_RESTORED); + display_rebuild(c); + display_leave(c); + return 0; +} + +static void display_free(struct wayland_display *c) +{ + connection_release(c); + if (c->loop && c->reconnect_timer) yasl_private_loop_del_timer(c->loop, c->reconnect_timer); + if (c->xkb_ctx) xkb_context_unref(c->xkb_ctx); if (c->owns_loop && c->loop) { yasl_private_loop_destroy(c->loop); c->loop = NULL; @@ -251,29 +354,7 @@ struct wayland_display *wayland_display_open(struct yasl_private_loop *loop, con c->base.app_id = c->app_id; } c->xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - c->wl = c->xkb_ctx ? wl_display_connect(NULL) : NULL; - if (!c->wl) goto err; - c->base.native_handle = c->wl; -#if YASL_HAS_EGL - c->egl_display = eglGetDisplay((EGLNativeDisplayType)c->wl); - if (c->egl_display != EGL_NO_DISPLAY) { - EGLint major = 0, minor = 0; - c->egl_initialized = eglInitialize(c->egl_display, &major, &minor) == EGL_TRUE; - } -#endif - - c->registry = wl_display_get_registry(c->wl); - if (!c->registry) goto err; - wl_registry_add_listener(c->registry, ®istry_listener, c); - if (wl_display_roundtrip(c->wl) < 0 || !c->compositor || !c->shm || !c->wm_base) - goto err; - - surface_init_cursor(c); - if (c->loop) { - c->watch = yasl_private_loop_add_fd(c->loop, wl_display_get_fd(c->wl), POLLIN, display_ready, c); - if (c->watch <= 0) { c->watch = 0; goto err; } - } - if (display_input_init(c) < 0) + if (!c->xkb_ctx || !connection_open(c)) goto err; return c; @@ -343,11 +424,91 @@ static bool spec_valid(const struct yasl_private_window_spec *spec) : buffer_size(spec->size.x, spec->size.y, &bytes); } +int surface_build_toplevel(struct wayland_surface *w, bool fullscreen) +{ + struct wayland_display *c = w->conn; + w->surface = wl_compositor_create_surface(c->compositor); + if (!w->surface) return -1; + w->base.native_handle = w->surface; + wl_surface_set_user_data(w->surface, w); + if (surface_init_scaling(w) < 0) return -1; + if (surface_init_shell(w, w->title) < 0) return -1; + if (w->base.creation_flags & YASL_PRIVATE_SURFACE_FIXED_SIZE) { + xdg_toplevel_set_min_size(w->toplevel, w->logical_width, w->logical_height); + xdg_toplevel_set_max_size(w->toplevel, w->logical_width, w->logical_height); + } + if (fullscreen) + xdg_toplevel_set_fullscreen(w->toplevel, NULL); + else if (w->base.state & YASL_PRIVATE_STATE_MAXIMIZED) + xdg_toplevel_set_maximized(w->toplevel); + if (w->tearing && c->tearing_mgr) { + w->tearing_control = wp_tearing_control_manager_v1_get_tearing_control( + c->tearing_mgr, w->surface); + if (w->tearing_control) + wp_tearing_control_v1_set_presentation_hint(w->tearing_control, + WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC); + } + w->size_pending = true; + wl_surface_commit(w->surface); + if (wl_display_roundtrip(c->wl) < 0) { + display_lose(c); + return -1; + } + if (apply_pending_geometry(w) < 0 || w->closed) return -1; + return 0; +} + +void surface_release_native(struct wayland_surface *w) +{ + surface_unlock_pointer(w); + w->ime_seat = NULL; +#define DROP(field, destroy) do { if (w->field) { destroy(w->field); w->field = NULL; } } while (0) + DROP(tearing_control, wp_tearing_control_v1_destroy); +#if YASL_HAS_EGL + struct wayland_display *c = w->conn; + if (w->egl_context) { + if (eglGetCurrentContext() == w->egl_context) + eglMakeCurrent(c->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(c->egl_display, w->egl_context); + w->egl_context = 0; + } + if (w->egl_surface) eglDestroySurface(c->egl_display, w->egl_surface); + w->egl_surface = 0; + DROP(egl_window, wl_egl_window_destroy); + w->gl_built = false; +#endif + DROP(fifo, wp_fifo_v1_destroy); + DROP(commit_timer, wp_commit_timer_v1_destroy); + surface_feedback_destroy(w); + DROP(frame_cb, wl_callback_destroy); + for (int i = 0; i < NBUF; i++) buffer_destroy(&w->buffers[i]); + DROP(frac_scale, wp_fractional_scale_v1_destroy); + DROP(viewport, wp_viewport_destroy); + DROP(toplevel_decor, zxdg_toplevel_decoration_v1_destroy); + DROP(toplevel, xdg_toplevel_destroy); + DROP(popup, xdg_popup_destroy); + DROP(xdg_surface, xdg_surface_destroy); + if (w->surface) { + wl_surface_set_user_data(w->surface, NULL); + wl_surface_destroy(w->surface); + w->surface = NULL; + } +#undef DROP + w->base.native_handle = NULL; + /* A Vulkan surface built on the old wl_surface is gone; allow a new one. */ + if (w->base.presenter == YASL_PRIVATE_PRESENTER_VULKAN) + w->base.presenter = YASL_PRIVATE_PRESENTER_NONE; + w->configured = w->has_pending_serial = w->has_shown = false; + w->frame_active = w->want_draw = false; + w->outputs = 0; +} + struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display *c, const struct yasl_private_window_spec *spec, void *graphics_device) { (void)graphics_device; - if (!c || c->lost || !spec_valid(spec)) + if (c && c->lost) { errno = ENOTCONN; return NULL; } + if (!c || !spec_valid(spec)) return NULL; int width = spec->size.x, height = spec->size.y; display_cancel_read(c); @@ -360,6 +521,7 @@ struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display * w->base.presenter = (spec->external_gpu && !(spec->flags & YASL_PRIVATE_SURFACE_CPU)) ? YASL_PRIVATE_PRESENTER_NONE : YASL_PRIVATE_PRESENTER_CPU; wayland_surface_set_callbacks(w, NULL, NULL); + wayland_surface_set_title(w, spec->title ? spec->title : "yasl_private_app"); w->cursor = YASL_PRIVATE_CURSOR_TEXT; w->req_width = width; w->req_height = height; @@ -368,40 +530,13 @@ struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display * w->scale_120 = YASL_PRIVATE_SCALE_UNIT; w->pending_scale_120 = YASL_PRIVATE_SCALE_UNIT; w->scale_factor = 1.0; - w->size_pending = true; + w->tearing = spec->tearing; + w->opaque = spec->fullscreen; w->clock.refresh_ns = c->outputs && c->outputs->refresh_ns ? c->outputs->refresh_ns : YASL_PRIVATE_REFRESH_DEFAULT_NS; - - w->surface = wl_compositor_create_surface(c->compositor); - if (!w->surface) goto err; - w->base.native_handle = w->surface; w->base.size = (struct yasl_private_vector2i){width, height}; w->base.scale_120 = YASL_PRIVATE_SCALE_UNIT; - 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; - if (spec->flags & YASL_PRIVATE_SURFACE_FIXED_SIZE) { - xdg_toplevel_set_min_size(w->toplevel, width, height); - xdg_toplevel_set_max_size(w->toplevel, width, height); - } - w->opaque = spec->fullscreen; - if (spec->fullscreen) - xdg_toplevel_set_fullscreen(w->toplevel, NULL); - if (spec->tearing && c->tearing_mgr) { - w->tearing_control = wp_tearing_control_manager_v1_get_tearing_control( - c->tearing_mgr, w->surface); - if (w->tearing_control) - wp_tearing_control_v1_set_presentation_hint(w->tearing_control, - WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC); - } - - wl_surface_commit(w->surface); - if (wl_display_roundtrip(c->wl) < 0) { - display_lose(c); - goto err; - } - if (apply_pending_geometry(w) < 0) goto err; - if (w->closed) goto err; + if (surface_build_toplevel(w, spec->fullscreen) < 0) goto err; display_leave(c); return w; @@ -426,37 +561,17 @@ void wayland_surface_destroy(struct wayland_surface *w) if (!w || w->destroyed) return; for (struct wayland_surface *o = w->conn ? w->conn->surfaces : NULL; o; o = o->conn_next) assert(o->parent != w && "destroy a popup before its parent"); - surface_unlock_pointer(w); + surface_release_native(w); input_queue_clear(&w->input); free(w->ime_preedit); free(w->ime_commit); free(w->ime_shown); - if (w->tearing_control) wp_tearing_control_v1_destroy(w->tearing_control); + free(w->title); + struct wayland_display *c = w->conn; #if YASL_HAS_EGL - if (w->egl_context) { - if (eglGetCurrentContext() == w->egl_context) - eglMakeCurrent(w->conn->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroyContext(w->conn->egl_display, w->egl_context); - } - if (w->egl_surface) eglDestroySurface(w->conn->egl_display, w->egl_surface); - if (w->egl_window) wl_egl_window_destroy(w->egl_window); + for (struct wayland_surface *o = c ? c->surfaces : NULL; o; o = o->conn_next) + if (o->gl_share == w) o->gl_share = NULL; #endif - if (w->fifo) wp_fifo_v1_destroy(w->fifo); - if (w->commit_timer) wp_commit_timer_v1_destroy(w->commit_timer); - surface_feedback_destroy(w); - if (w->frame_cb) wl_callback_destroy(w->frame_cb); - for (int i = 0; i < NBUF; i++) buffer_destroy(&w->buffers[i]); - if (w->frac_scale) wp_fractional_scale_v1_destroy(w->frac_scale); - if (w->viewport) wp_viewport_destroy(w->viewport); - if (w->toplevel_decor) zxdg_toplevel_decoration_v1_destroy(w->toplevel_decor); - 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_set_user_data(w->surface, NULL); - wl_surface_destroy(w->surface); - } - struct wayland_display *c = w->conn; conn_unlink(w); static const struct yasl_private_window_cbs no_callbacks = {0}; *w = (struct wayland_surface){ diff --git a/src/platform/x11/window.c b/src/platform/x11/window.c index 8129dd5..ef13afe 100644 --- a/src/platform/x11/window.c +++ b/src/platform/x11/window.c @@ -2391,6 +2391,7 @@ static int ready(int fd, short events, void *ud) struct x11_display *d = (struct x11_display *)base; if ((events & (POLLERR | POLLHUP)) && !d->lost) { d->lost = true; + base->offline = true; yasl_private_display_notify_event(base, YASL_PRIVATE_EVENT_DISPLAY_LOST); return 0; } |
