namespace Pasteboard { const struct wl_data_offer_listener g_dataOfferListener = { // offer [] (void* data, struct wl_data_offer* offer, const char* type) { auto& dataDeviceData = *static_cast<PasteboardWayland::DataDeviceData*>(data); assert(offer == dataDeviceData.data_offer); dataDeviceData.dataTypes.push_back(type); }, }; const struct wl_data_device_listener g_dataDeviceListener = { // data_offer [] (void* data, struct wl_data_device*, struct wl_data_offer* offer) { auto& dataDeviceData = *static_cast<PasteboardWayland::DataDeviceData*>(data); dataDeviceData.dataTypes.clear(); if (dataDeviceData.data_offer) wl_data_offer_destroy(dataDeviceData.data_offer); dataDeviceData.data_offer = offer; wl_data_offer_add_listener(offer, &g_dataOfferListener, data); }, // enter [] (void*, struct wl_data_device*, uint32_t, struct wl_surface*, wl_fixed_t, wl_fixed_t, struct wl_data_offer*) {}, // leave [] (void*, struct wl_data_device*) {}, // motion [] (void*, struct wl_data_device*, uint32_t, wl_fixed_t, wl_fixed_t) {}, // drop [] (void*, struct wl_data_device*) {}, // selection [] (void*, struct wl_data_device*, wl_data_offer*) {}, }; PasteboardWayland::PasteboardWayland() { ViewBackend::WaylandDisplay& display = ViewBackend::WaylandDisplay::singleton(); if (!display.interfaces().data_device_manager) return; m_dataDevice = wl_data_device_manager_get_data_device(display.interfaces().data_device_manager, display.interfaces().seat); wl_data_device_add_listener(m_dataDevice, &g_dataDeviceListener, &m_dataDeviceData); } PasteboardWayland::~PasteboardWayland() { if (m_dataDeviceData.data_offer) wl_data_offer_destroy(m_dataDeviceData.data_offer); m_dataDeviceData.dataTypes.clear(); wl_data_device_destroy(m_dataDevice); } std::vector<std::string> PasteboardWayland::getTypes() { std::vector<std::string> types; for (auto dataType: m_dataDeviceData.dataTypes) types.push_back(dataType); return types; } std::string PasteboardWayland::getString(const std::string pasteboardType) { if (!std::any_of(m_dataDeviceData.dataTypes.cbegin(), m_dataDeviceData.dataTypes.cend(), [pasteboardType](std::string str){ return str == pasteboardType;})) return std::string(); int pipefd[2]; // FIXME: Should probably handle this error somehow. if (pipe2(pipefd, O_CLOEXEC) == -1) return std::string(); wl_data_offer_receive(m_dataDeviceData.data_offer, pasteboardType.c_str(), pipefd[1]); close(pipefd[1]); wl_display_roundtrip(ViewBackend::WaylandDisplay::singleton().display()); char buf[1024]; std::string readString; ssize_t length; do { length = read(pipefd[0], buf, 1024); readString.append(buf, length); } while (length > 0); close(pipefd[0]); return readString; } const struct wl_data_source_listener g_dataSourceListener = { // target [] (void*, struct wl_data_source*, const char*) {}, // send [] (void* data, struct wl_data_source* source, const char* mime_type, int32_t fd) { auto& dataSourceData = *static_cast<PasteboardWayland::DataSourceData*>(data); assert(dataSourceData.data_source == source); assert(!dataSourceData.dataMap.count(mime_type)); if (strncmp(mime_type, "text/", 5) == 0) { std::string stringToSend = dataSourceData.dataMap[mime_type]; const char* p = stringToSend.data(); ssize_t length = stringToSend.size(); ssize_t written = 0; while (length > 0 && written != -1) { written = write(fd, p, length); p += written; length -= written; } close(fd); wl_display_roundtrip(ViewBackend::WaylandDisplay::singleton().display()); } else return; // Eventually assert if we don't have a handler for a mimetype we are offering. }, // cancelled [] (void* data, struct wl_data_source* source) { auto& dataSourceData = *static_cast<PasteboardWayland::DataSourceData*>(data); assert(dataSourceData.data_source == source); wl_data_source_destroy(source); dataSourceData.data_source = nullptr; dataSourceData.dataMap.clear(); } }; void PasteboardWayland::write(std::map<std::string, std::string>&& dataMap) { if (m_dataSourceData.data_source) wl_data_source_destroy(m_dataSourceData.data_source); m_dataSourceData.dataMap = dataMap; ViewBackend::WaylandDisplay& display = ViewBackend::WaylandDisplay::singleton(); m_dataSourceData.data_source = wl_data_device_manager_create_data_source(display.interfaces().data_device_manager); for (auto dataPair : m_dataSourceData.dataMap) wl_data_source_offer(m_dataSourceData.data_source, dataPair.first.c_str()); wl_data_source_add_listener(m_dataSourceData.data_source, &g_dataSourceListener, &m_dataSourceData); wl_data_device_set_selection(m_dataDevice, m_dataSourceData.data_source, display.singleton().serial()); } void PasteboardWayland::write(std::string&& pasteboardType, std::string&& stringToWrite) { std::map<std::string, std::string> dataMap; dataMap[pasteboardType] = stringToWrite; write(std::move(dataMap)); } } // namespace Pasteboard
static struct display * create_display(void) { struct display *display; display = malloc(sizeof *display); if (display == NULL) { fprintf(stderr, "out of memory\n"); exit(1); } display->display = wl_display_connect(NULL); assert(display->display); display->formats = 0; display->registry = wl_display_get_registry(display->display); wl_registry_add_listener(display->registry, ®istry_listener, display); wl_display_roundtrip(display->display); if (display->shm == NULL) { fprintf(stderr, "No wl_shm global\n"); exit(1); } wl_display_roundtrip(display->display); /* * Why do we need two roundtrips here? * * wl_display_get_registry() sends a request to the server, to which * the server replies by emitting the wl_registry.global events. * The first wl_display_roundtrip() sends wl_display.sync. The server * first processes the wl_display.get_registry which includes sending * the global events, and then processes the sync. Therefore when the * sync (roundtrip) returns, we are guaranteed to have received and * processed all the global events. * * While we are inside the first wl_display_roundtrip(), incoming * events are dispatched, which causes registry_handle_global() to * be called for each global. One of these globals is wl_shm. * registry_handle_global() sends wl_registry.bind request for the * wl_shm global. However, wl_registry.bind request is sent after * the first wl_display.sync, so the reply to the sync comes before * the initial events of the wl_shm object. * * The initial events that get sent as a reply to binding to wl_shm * include wl_shm.format. These tell us which pixel formats are * supported, and we need them before we can create buffers. They * don't change at runtime, so we receive them as part of init. * * When the reply to the first sync comes, the server may or may not * have sent the initial wl_shm events. Therefore we need the second * wl_display_roundtrip() call here. * * The server processes the wl_registry.bind for wl_shm first, and * the second wl_display.sync next. During our second call to * wl_display_roundtrip() the initial wl_shm events are received and * processed. Finally, when the reply to the second wl_display.sync * arrives, it guarantees we have processed all wl_shm initial events. * * This sequence contains two examples on how wl_display_roundtrip() * can be used to guarantee, that all reply events to a request * have been received and processed. This is a general Wayland * technique. */ if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) { fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n"); exit(1); } return display; }
int Roundtrip(){ return wl_display_roundtrip(cobj); }
static int Open(vlc_object_t *obj) { demux_t *demux = (demux_t *)obj; if (demux->out == NULL) return VLC_EGENERIC; demux_sys_t *sys = malloc(sizeof (*sys)); if (unlikely(sys == NULL)) return VLC_ENOMEM; /* Connect to the display server */ char *dpy_name = var_InheritString(demux, "wl-display"); sys->display = wl_display_connect(dpy_name); free(dpy_name); if (sys->display == NULL) { free(sys); return VLC_EGENERIC; } sys->output = NULL; sys->shm = NULL; sys->screenshooter = NULL; sys->es = NULL; sys->pagemask = sysconf(_SC_PAGE_SIZE) - 1; sys->rate = var_InheritFloat(demux, "screen-fps"); sys->x = var_InheritInteger(demux, "screen-left"); sys->y = var_InheritInteger(demux, "screen-top"); sys->w = var_InheritInteger(demux, "screen-width"); sys->h = var_InheritInteger(demux, "screen-height"); if (1000.f * sys->rate <= 0x1.p-30) goto error; demux->p_sys = sys; /* Find the interesting singleton(s) */ struct wl_registry *registry = wl_display_get_registry(sys->display); if (registry == NULL) goto error; wl_registry_add_listener(registry, ®istry_cbs, demux); wl_display_roundtrip(sys->display); wl_registry_destroy(registry); if (sys->output == NULL || sys->shm == NULL || sys->screenshooter == NULL) { msg_Err(demux, "screenshooter extension not supported"); goto error; } wl_output_add_listener(sys->output, &output_cbs, demux); screenshooter_add_listener(sys->screenshooter, &screenshooter_cbs, &sys->done); wl_display_roundtrip(sys->display); if (DisplayError(demux, sys->display)) goto error; /* Initializes demux */ sys->start = vlc_tick_now(); if (vlc_clone(&sys->thread, Thread, demux, VLC_THREAD_PRIORITY_INPUT)) goto error; demux->pf_demux = NULL; demux->pf_control = Control; return VLC_SUCCESS; error: if (sys->screenshooter != NULL) screenshooter_destroy(sys->screenshooter); if (sys->shm != NULL) wl_shm_destroy(sys->shm); if (sys->output != NULL) wl_output_destroy(sys->output); wl_display_disconnect(sys->display); free(sys); return VLC_EGENERIC; }
static void *gfx_ctx_wl_init(void *video_driver) { #ifdef HAVE_OPENGL static const EGLint egl_attribs_gl[] = { WL_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE, }; #endif #ifdef HAVE_OPENGLES #ifdef HAVE_OPENGLES2 static const EGLint egl_attribs_gles[] = { WL_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE, }; #endif #ifdef HAVE_OPENGLES3 #ifdef EGL_KHR_create_context static const EGLint egl_attribs_gles3[] = { WL_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, EGL_NONE, }; #endif #endif #endif #ifdef HAVE_EGL static const EGLint egl_attribs_vg[] = { WL_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, EGL_NONE, }; EGLint major = 0, minor = 0; EGLint n; const EGLint *attrib_ptr = NULL; #endif gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*) calloc(1, sizeof(gfx_ctx_wayland_data_t)); if (!wl) return NULL; (void)video_driver; #ifdef HAVE_EGL switch (wl_api) { case GFX_CTX_OPENGL_API: #ifdef HAVE_OPENGL attrib_ptr = egl_attribs_gl; #endif break; case GFX_CTX_OPENGL_ES_API: #ifdef HAVE_OPENGLES #ifdef HAVE_OPENGLES3 #ifdef EGL_KHR_create_context if (g_egl_major >= 3) attrib_ptr = egl_attribs_gles3; else #endif #endif #ifdef HAVE_OPENGLES2 attrib_ptr = egl_attribs_gles; #endif #endif break; case GFX_CTX_OPENVG_API: #ifdef HAVE_VG attrib_ptr = egl_attribs_vg; #endif break; case GFX_CTX_NONE: default: break; } #endif frontend_driver_destroy_signal_handler_state(); wl->dpy = wl_display_connect(NULL); wl->buffer_scale = 1; if (!wl->dpy) { RARCH_ERR("Failed to connect to Wayland server.\n"); goto error; } frontend_driver_install_signal_handler(); wl->registry = wl_display_get_registry(wl->dpy); wl_registry_add_listener(wl->registry, ®istry_listener, wl); wl_display_roundtrip(wl->dpy); if (!wl->compositor) { RARCH_ERR("Failed to create compositor.\n"); goto error; } if (!wl->shell) { RARCH_ERR("Failed to create shell.\n"); goto error; } wl->fd = wl_display_get_fd(wl->dpy); switch (wl_api) { case GFX_CTX_OPENGL_API: case GFX_CTX_OPENGL_ES_API: case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL if (!egl_init_context(&wl->egl, (EGLNativeDisplayType)wl->dpy, &major, &minor, &n, attrib_ptr)) { egl_report_error(); goto error; } if (n == 0 || !egl_has_config(&wl->egl)) goto error; #endif break; case GFX_CTX_VULKAN_API: #ifdef HAVE_VULKAN if (!vulkan_context_init(&wl->vk, VULKAN_WSI_WAYLAND)) goto error; #endif break; case GFX_CTX_NONE: default: break; } return wl; error: gfx_ctx_wl_destroy_resources(wl); if (wl) free(wl); return NULL; }
static bool gfx_ctx_wl_set_video_mode(void *data, unsigned width, unsigned height, bool fullscreen) { #ifdef HAVE_EGL EGLint egl_attribs[16]; EGLint *attr = egl_fill_attribs( (gfx_ctx_wayland_data_t*)data, egl_attribs); #endif gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; wl->width = width ? width : DEFAULT_WINDOWED_WIDTH; wl->height = height ? height : DEFAULT_WINDOWED_HEIGHT; wl->surface = wl_compositor_create_surface(wl->compositor); wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale); switch (wl_api) { case GFX_CTX_OPENGL_API: case GFX_CTX_OPENGL_ES_API: case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL wl->win = wl_egl_window_create(wl->surface, wl->width, wl->height); #endif break; case GFX_CTX_NONE: default: break; } wl->shell_surf = wl_shell_get_shell_surface(wl->shell, wl->surface); wl_shell_surface_add_listener(wl->shell_surf, &shell_surface_listener, wl); wl_shell_surface_set_toplevel(wl->shell_surf); wl_shell_surface_set_class(wl->shell_surf, "RetroArch"); wl_shell_surface_set_title(wl->shell_surf, "RetroArch"); switch (wl_api) { case GFX_CTX_OPENGL_API: case GFX_CTX_OPENGL_ES_API: case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL if (!egl_create_context(&wl->egl, (attr != egl_attribs) ? egl_attribs : NULL)) { egl_report_error(); goto error; } if (!egl_create_surface(&wl->egl, (EGLNativeWindowType)wl->win)) goto error; egl_set_swap_interval(&wl->egl, wl->egl.interval); #endif break; case GFX_CTX_NONE: default: break; } if (fullscreen) wl_shell_surface_set_fullscreen(wl->shell_surf, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL); flush_wayland_fd(wl); switch (wl_api) { case GFX_CTX_VULKAN_API: wl_display_roundtrip(wl->dpy); #ifdef HAVE_VULKAN if (!vulkan_surface_create(&wl->vk, VULKAN_WSI_WAYLAND, wl->dpy, wl->surface, wl->width, wl->height, wl->swap_interval)) goto error; #endif break; case GFX_CTX_NONE: default: break; } return true; error: gfx_ctx_wl_destroy(data); return false; }
static int create_dmabuf_buffer(struct display *display, struct buffer *buffer, int width, int height) { struct zwp_linux_buffer_params_v1 *params; uint64_t modifier; uint32_t flags; if (!drm_connect(buffer)) { fprintf(stderr, "drm_connect failed\n"); goto error; } buffer->width = width; buffer->height = height; buffer->bpp = 32; /* hardcoded XRGB8888 format */ if (!alloc_bo(buffer)) { fprintf(stderr, "alloc_bo failed\n"); goto error1; } if (!map_bo(buffer)) { fprintf(stderr, "map_bo failed\n"); goto error2; } fill_content(buffer); unmap_bo(buffer); if (drm_intel_bo_gem_export_to_prime(buffer->bo, &buffer->dmabuf_fd) != 0) { fprintf(stderr, "drm_intel_bo_gem_export_to_prime failed\n"); goto error2; } if (buffer->dmabuf_fd < 0) { fprintf(stderr, "error: dmabuf_fd < 0\n"); goto error2; } /* We now have a dmabuf! It should contain 2x2 tiles (i.e. each tile * is 256x256) of misc colours, and be mappable, either as ARGB8888, or * XRGB8888. */ modifier = 0; flags = 0; params = zwp_linux_dmabuf_v1_create_params(display->dmabuf); zwp_linux_buffer_params_v1_add(params, buffer->dmabuf_fd, 0, /* plane_idx */ 0, /* offset */ buffer->stride, modifier >> 32, modifier & 0xffffffff); zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, buffer); zwp_linux_buffer_params_v1_create(params, buffer->width, buffer->height, DRM_FORMAT_XRGB8888, flags); /* params is destroyed by the event handlers */ wl_display_roundtrip(display->display); if (buffer->buffer == NULL) { goto error2; } return 0; error2: free_bo(buffer); error1: drm_shutdown(buffer); error: return -1; }
xdl_int XdevLWindowWayland::init() { XdevLWindowImpl::init(); wl_display_roundtrip(display); if(m_compositor == nullptr) { XDEVL_MODULE_ERROR("Wayland compositor object not received.\n"); return ERR_ERROR; } else { XDEVL_MODULE_SUCCESS("Received Wayland compositor object proxy\n"); } if(m_shell == nullptr) { XDEVL_MODULE_ERROR("Wayland shell object not received.\n"); return ERR_ERROR; } else { XDEVL_MODULE_SUCCESS("Received Wayland shell object proxy\n"); } if(m_sharedMemory == nullptr) { XDEVL_MODULE_ERROR("Wayland shared memory object not received.\n"); return ERR_ERROR; } else { XDEVL_MODULE_SUCCESS("Received Wayland shared memory object proxy\n"); } // // Now we create the surface we can see on the screen. // m_surface = wl_compositor_create_surface(m_compositor); if(m_surface == nullptr) { XDEVL_MODULE_ERROR("Can't create Wayland surface\n"); return ERR_ERROR; } else { XDEVL_MODULE_SUCCESS("Created surface\n"); } wl_surface_set_user_data(m_surface, this); m_shellSurface = wl_shell_get_shell_surface(m_shell, m_surface); if(m_shellSurface == nullptr) { XDEVL_MODULE_ERROR("Can't create Wayland shell surface\n"); return ERR_ERROR; } else { XDEVL_MODULE_SUCCESS("Created shell surface\n"); } wl_shell_surface_add_listener(m_shellSurface, &shell_surface_listener, this); // Bring the surface to the front. wl_shell_surface_set_toplevel(m_shellSurface); // m_frameCallback = wl_surface_frame(m_surface); // wl_callback_add_listener(m_frameCallback, &frame_listener, this); createOpaqueRegion(m_attribute.position.x, m_attribute.position.y, m_attribute.size.width, m_attribute.size.height); xdl_int ret = initializeEGL(); if(ret == ERR_ERROR) { XDEVL_MODULE_ERROR("Initializing EGL failed.\n"); return ERR_ERROR; } m_egl.m_eglWindow = wl_egl_window_create(m_surface, getWidth(), getHeight()); if(m_egl.m_eglWindow == EGL_NO_SURFACE) { XDEVL_MODULE_ERROR("Can't create egl window\n"); return ERR_ERROR; } else { XDEVL_MODULE_SUCCESS("Created egl window\n"); } m_egl.m_eglSurface = eglCreateWindowSurface(m_egl.m_eglDisplay, m_egl.m_eglConfig, m_egl.m_eglWindow, nullptr); if(!eglMakeCurrent(m_egl.m_eglDisplay, m_egl.m_eglSurface, m_egl.m_eglSurface, m_egl.m_eglContext)) { XDEVL_MODULE_ERROR("eglMakeCurrent failed\n"); } glClearColor((1.0f/255.0)*m_backgroundColor[0], (1.0f/255.0)*m_backgroundColor[1], (1.0f/255.0)*m_backgroundColor[2], (1.0f/255.0)*m_backgroundColor[3]); glClear(GL_COLOR_BUFFER_BIT); glFlush(); if(!eglSwapBuffers(m_egl.m_eglDisplay, m_egl.m_eglSurface)) { XDEVL_MODULE_ERROR("eglSwapBuffers failed\n"); } wl_display_flush(display); return ERR_OK; }
EGLBoolean dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp) { struct dri2_egl_display *dri2_dpy; const __DRIconfig *config; uint32_t id, types; int i; static const unsigned int argb_masks[4] = { 0xff0000, 0xff00, 0xff, 0xff000000 }; static const unsigned int rgb_masks[4] = { 0xff0000, 0xff00, 0xff, 0 }; drv->API.CreateWindowSurface = dri2_create_window_surface; drv->API.CreatePixmapSurface = dri2_create_pixmap_surface; drv->API.DestroySurface = dri2_destroy_surface; drv->API.SwapBuffers = dri2_swap_buffers; drv->API.CreateImageKHR = dri2_wayland_create_image_khr; drv->API.Terminate = dri2_terminate; dri2_dpy = malloc(sizeof *dri2_dpy); if (!dri2_dpy) return _eglError(EGL_BAD_ALLOC, "eglInitialize"); memset(dri2_dpy, 0, sizeof *dri2_dpy); disp->DriverData = (void *) dri2_dpy; if (disp->PlatformDisplay == NULL) { dri2_dpy->wl_dpy = wl_display_connect(NULL); if (dri2_dpy->wl_dpy == NULL) goto cleanup_dpy; dri2_dpy->own_device = 1; } else { dri2_dpy->wl_dpy = disp->PlatformDisplay; } id = wl_display_get_global(dri2_dpy->wl_dpy, "wl_drm", 1); if (id == 0) wl_display_roundtrip(dri2_dpy->wl_dpy); id = wl_display_get_global(dri2_dpy->wl_dpy, "wl_drm", 1); if (id == 0) goto cleanup_dpy; dri2_dpy->wl_drm = wl_display_bind(dri2_dpy->wl_dpy, id, &wl_drm_interface); if (!dri2_dpy->wl_drm) goto cleanup_dpy; wl_drm_add_listener(dri2_dpy->wl_drm, &drm_listener, dri2_dpy); wl_display_roundtrip(dri2_dpy->wl_dpy); if (dri2_dpy->fd == -1) goto cleanup_drm; wl_display_roundtrip(dri2_dpy->wl_dpy); if (!dri2_dpy->authenticated) goto cleanup_fd; dri2_dpy->driver_name = dri2_get_driver_for_fd(dri2_dpy->fd); if (dri2_dpy->driver_name == NULL) { _eglError(EGL_BAD_ALLOC, "DRI2: failed to get driver name"); goto cleanup_fd; } if (!dri2_load_driver(disp)) goto cleanup_driver_name; dri2_dpy->dri2_loader_extension.base.name = __DRI_DRI2_LOADER; dri2_dpy->dri2_loader_extension.base.version = 3; dri2_dpy->dri2_loader_extension.getBuffers = dri2_get_buffers; dri2_dpy->dri2_loader_extension.flushFrontBuffer = dri2_flush_front_buffer; dri2_dpy->dri2_loader_extension.getBuffersWithFormat = dri2_get_buffers_with_format; dri2_dpy->extensions[0] = &dri2_dpy->dri2_loader_extension.base; dri2_dpy->extensions[1] = &image_lookup_extension.base; dri2_dpy->extensions[2] = &use_invalidate.base; dri2_dpy->extensions[3] = NULL; if (!dri2_create_screen(disp)) goto cleanup_driver; types = EGL_WINDOW_BIT | EGL_PIXMAP_BIT; if (dri2_dpy->formats & HAS_PREMUL_ARGB32) types |= EGL_VG_ALPHA_FORMAT_PRE_BIT; for (i = 0; dri2_dpy->driver_configs[i]; i++) { config = dri2_dpy->driver_configs[i]; if (dri2_dpy->formats & HAS_XRGB32) dri2_add_config(disp, config, i + 1, 0, types, NULL, rgb_masks); if (dri2_dpy->formats & (HAS_ARGB32 | HAS_PREMUL_ARGB32)) dri2_add_config(disp, config, i + 1, 0, types, NULL, argb_masks); } disp->Extensions.KHR_image_pixmap = EGL_TRUE; disp->Extensions.WL_bind_wayland_display = EGL_TRUE; dri2_dpy->authenticate = dri2_wayland_authenticate; /* we're supporting EGL 1.4 */ disp->VersionMajor = 1; disp->VersionMinor = 4; return EGL_TRUE; cleanup_driver: dlclose(dri2_dpy->driver); cleanup_driver_name: free(dri2_dpy->driver_name); cleanup_fd: close(dri2_dpy->fd); cleanup_drm: free(dri2_dpy->device_name); wl_drm_destroy(dri2_dpy->wl_drm); cleanup_dpy: free(dri2_dpy); return EGL_FALSE; }
/* Initialize a native display for use with WSEGL */ static WSEGLError wseglInitializeDisplay (NativeDisplayType nativeDisplay, WSEGLDisplayHandle *display, const WSEGLCaps **caps, WSEGLConfig **configs) { struct wl_egl_display *egldisplay = wl_egl_display_create((struct wl_display *) nativeDisplay); if (wseglFetchContext(egldisplay) != 1) { wl_egl_display_destroy(egldisplay); return WSEGL_OUT_OF_MEMORY; } /* If it is a framebuffer */ if (egldisplay->display == NULL) { int fd; WSEGLPixelFormat format; /* Open the framebuffer and fetch its properties */ fd = open("/dev/fb0", O_RDWR, 0); if (fd < 0) { perror("/dev/fb0"); wseglReleaseContext(egldisplay); wl_egl_display_destroy(egldisplay); return WSEGL_CANNOT_INITIALISE; } if (ioctl(fd, FBIOGET_VSCREENINFO, &egldisplay->var) < 0) { perror("FBIOGET_VSCREENINFO"); wseglReleaseContext(egldisplay); wl_egl_display_destroy(egldisplay); close(fd); return WSEGL_CANNOT_INITIALISE; } if (ioctl(fd, FBIOGET_FSCREENINFO, &egldisplay->fix) < 0) { perror("FBIOGET_FSCREENINFO"); wseglReleaseContext(egldisplay); wl_egl_display_destroy(egldisplay); close(fd); return WSEGL_CANNOT_INITIALISE; } format = getwseglPixelFormat(egldisplay); egldisplay->wseglDisplayConfigs[0].ePixelFormat = format; egldisplay->wseglDisplayConfigs[1].ePixelFormat = format; } else { uint32_t id; id = wl_display_get_global(egldisplay->display, "wl_drm", 1); if (id == 0) wl_display_roundtrip(egldisplay->display); id = wl_display_get_global(egldisplay->display, "wl_drm", 1); if (id == 0) return WSEGL_CANNOT_INITIALISE; egldisplay->drm = wl_display_bind(egldisplay->display, id, &wl_drm_interface); if (!egldisplay->drm) return WSEGL_CANNOT_INITIALISE; wl_drm_add_listener(egldisplay->drm, &drm_listener, egldisplay); wl_display_roundtrip(egldisplay->display); } *display = (WSEGLDisplayHandle)egldisplay; *caps = wseglDisplayCaps; *configs = egldisplay->wseglDisplayConfigs; return WSEGL_SUCCESS; }
int _glfwPlatformInit(void) { _glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0"); if (!_glfw.wl.cursor.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to open libwayland-cursor"); return GLFW_FALSE; } _glfw.wl.cursor.theme_load = (PFN_wl_cursor_theme_load) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_load"); _glfw.wl.cursor.theme_destroy = (PFN_wl_cursor_theme_destroy) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_destroy"); _glfw.wl.cursor.theme_get_cursor = (PFN_wl_cursor_theme_get_cursor) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor"); _glfw.wl.cursor.image_get_buffer = (PFN_wl_cursor_image_get_buffer) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_image_get_buffer"); _glfw.wl.egl.handle = _glfw_dlopen("libwayland-egl.so.1"); if (!_glfw.wl.egl.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to open libwayland-egl"); return GLFW_FALSE; } _glfw.wl.egl.window_create = (PFN_wl_egl_window_create) _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_create"); _glfw.wl.egl.window_destroy = (PFN_wl_egl_window_destroy) _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_destroy"); _glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize) _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize"); _glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0"); if (!_glfw.wl.xkb.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to open libxkbcommon"); return GLFW_FALSE; } _glfw.wl.xkb.context_new = (PFN_xkb_context_new) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new"); _glfw.wl.xkb.context_unref = (PFN_xkb_context_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref"); _glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string"); _glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref"); _glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index"); _glfw.wl.xkb.keymap_key_repeats = (PFN_xkb_keymap_key_repeats) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats"); _glfw.wl.xkb.state_new = (PFN_xkb_state_new) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new"); _glfw.wl.xkb.state_unref = (PFN_xkb_state_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref"); _glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms"); _glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask"); _glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods"); #ifdef HAVE_XKBCOMMON_COMPOSE_H _glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale"); _glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref"); _glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new"); _glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref"); _glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed"); _glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status"); _glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); #endif _glfw.wl.display = wl_display_connect(NULL); if (!_glfw.wl.display) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to connect to display"); return GLFW_FALSE; } _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); createKeyTables(); _glfw.wl.xkb.context = xkb_context_new(0); if (!_glfw.wl.xkb.context) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to initialize xkb context"); return GLFW_FALSE; } // Sync so we got all registry objects wl_display_roundtrip(_glfw.wl.display); // Sync so we got all initial output events wl_display_roundtrip(_glfw.wl.display); if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; _glfwInitTimerPOSIX(); _glfw.wl.timerfd = -1; if (_glfw.wl.seatVersion >= 4) _glfw.wl.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if (_glfw.wl.pointer && _glfw.wl.shm) { _glfw.wl.cursorTheme = wl_cursor_theme_load(NULL, 32, _glfw.wl.shm); if (!_glfw.wl.cursorTheme) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unable to load default cursor theme"); return GLFW_FALSE; } _glfw.wl.cursorSurface = wl_compositor_create_surface(_glfw.wl.compositor); } return GLFW_TRUE; }
void Display::roundtrip() const { ASSERT(wl_display_roundtrip(*this) >= 0); }
static void *gfx_ctx_wl_init(void *video_driver) { static const EGLint egl_attribs_gl[] = { WL_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE, }; static const EGLint egl_attribs_gles[] = { WL_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE, }; #ifdef EGL_KHR_create_context static const EGLint egl_attribs_gles3[] = { WL_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, EGL_NONE, }; #endif static const EGLint egl_attribs_vg[] = { WL_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, EGL_NONE, }; EGLint major = 0, minor = 0; EGLint n; const EGLint *attrib_ptr; gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*) calloc(1, sizeof(gfx_ctx_wayland_data_t)); (void)video_driver; if (!wl) return NULL; switch (wl->egl.api) { case GFX_CTX_OPENGL_API: attrib_ptr = egl_attribs_gl; break; case GFX_CTX_OPENGL_ES_API: #ifdef EGL_KHR_create_context if (g_egl_major >= 3) attrib_ptr = egl_attribs_gles3; else #endif attrib_ptr = egl_attribs_gles; break; case GFX_CTX_OPENVG_API: attrib_ptr = egl_attribs_vg; break; default: attrib_ptr = NULL; } g_egl_quit = 0; wl->dpy = wl_display_connect(NULL); if (!wl->dpy) { RARCH_ERR("Failed to connect to Wayland server.\n"); goto error; } wl->registry = wl_display_get_registry(wl->dpy); wl_registry_add_listener(wl->registry, ®istry_listener, wl); wl_display_dispatch(wl->dpy); wl_display_roundtrip(wl->dpy); if (!wl->compositor) { RARCH_ERR("Failed to create compositor.\n"); goto error; } if (!wl->shell) { RARCH_ERR("Failed to create shell.\n"); goto error; } wl->fd = wl_display_get_fd(wl->dpy); if (!egl_init_context(wl, (EGLNativeDisplayType)wl->dpy, &major, &minor, &n, attrib_ptr)) { egl_report_error(); goto error; } if (n == 0 || !egl_has_config(wl)) goto error; return wl; error: gfx_ctx_wl_destroy_resources(wl); if (wl) free(wl); return NULL; }
static void vertical_blank(void *pointer) { struct wayland *wayland = pointer; wl_display_roundtrip(wayland->display); struct timespec tv; int32_t nsec_diff; clock_gettime(CLOCK_MONOTONIC, &tv); if (tv_prev.tv_sec == 0 && tv_prev.tv_nsec == 0) { nsec_diff = 0; tv_prev = tv; } else { nsec_diff = nano_elapsed(&tv_prev, &tv); tv_prev = tv; // assert(sec_diff == 0); cairo_surface_t *cairo_surface = cairo_image_surface_create_for_data( (unsigned char *)wayland->back_data, CAIRO_FORMAT_ARGB32, 256 * SCALE, 240 * SCALE, 256 * SCALE * 4); cairo_t *cairo = cairo_create(cairo_surface); char buffer[200]; double fps = 1000000000.0 / ((double) nsec_diff); sprintf(buffer, "FPS %.1f", fps); cairo_set_source_rgba(cairo, 255, 255, 255, 0.8); cairo_select_font_face(cairo, "Cousine", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cairo, 12 * SCALE); cairo_move_to(cairo, 2 * SCALE, 236 * SCALE); cairo_show_text(cairo, buffer); cairo_destroy(cairo); cairo_surface_destroy(cairo_surface); // ++frame_count; } swap_buffers(wayland); wayland->frame_callback = wl_surface_frame(wayland->surface); wl_callback_add_listener(wayland->frame_callback, &frame_callback_listener, wayland); wl_surface_damage(wayland->surface, 0, 0, wayland->width, wayland->height); wl_surface_attach(wayland->surface, wayland->front_buffer, 0, 0); wl_surface_commit(wayland->surface); wl_display_flush(wayland->display); const int32_t NSEC_PER_60FPS_TICK = 16666666; if (nsec_diff != 0) { struct timespec req = { .tv_sec = 0, .tv_nsec = NSEC_PER_60FPS_TICK - nsec_diff, }; struct timespec rem; nanosleep(&req, &rem); } } static uint8_t joypad1_read(void *pointer) { struct wayland *wayland = pointer; wl_display_roundtrip(wayland->display); uint8_t state = wayland->joypad1_state; wayland->joypad1_state = wayland->joypad1_press; return state; } uint8_t nes_emulator_ppu_backend_wayland_init( struct nes_emulator_ppu_backend **ppu_backend) { if (ppu_backend == NULL) { return EXIT_CODE_ARG_ERROR_BIT; } *ppu_backend = NULL; struct nes_emulator_ppu_backend *b = malloc(sizeof(struct nes_emulator_ppu_backend)); if (b == NULL) { return EXIT_CODE_OS_ERROR_BIT; } struct wayland *w = malloc(sizeof(struct wayland)); if (w == NULL) { free(b); return EXIT_CODE_OS_ERROR_BIT; } w->joypad1_state = 0; w->joypad1_press = 0; uint8_t exit_code = init_wayland(w); if (exit_code != 0) { free(w); free(b); return exit_code; } b->pointer = w; b->render_pixel = render_pixel; b->vertical_blank = vertical_blank; b->joypad1_read = joypad1_read; *ppu_backend = b; return 0; } uint8_t nes_emulator_ppu_backend_wayland_fini( struct nes_emulator_ppu_backend **ppu_backend) { uint8_t exit_code = 0; struct wayland *wayland = (struct wayland *) (*ppu_backend)->pointer; exit_code |= fini_wayland(wayland); free(wayland); free(*ppu_backend); *ppu_backend = NULL; return exit_code; }
int _glfwPlatformInit(void) { const char *cursorTheme; const char *cursorSizeStr; char *cursorSizeEnd; long cursorSizeLong; int cursorSize; _glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0"); if (!_glfw.wl.cursor.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to open libwayland-cursor"); return GLFW_FALSE; } _glfw.wl.cursor.theme_load = (PFN_wl_cursor_theme_load) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_load"); _glfw.wl.cursor.theme_destroy = (PFN_wl_cursor_theme_destroy) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_destroy"); _glfw.wl.cursor.theme_get_cursor = (PFN_wl_cursor_theme_get_cursor) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor"); _glfw.wl.cursor.image_get_buffer = (PFN_wl_cursor_image_get_buffer) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_image_get_buffer"); _glfw.wl.egl.handle = _glfw_dlopen("libwayland-egl.so.1"); if (!_glfw.wl.egl.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to open libwayland-egl"); return GLFW_FALSE; } _glfw.wl.egl.window_create = (PFN_wl_egl_window_create) _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_create"); _glfw.wl.egl.window_destroy = (PFN_wl_egl_window_destroy) _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_destroy"); _glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize) _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize"); _glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0"); if (!_glfw.wl.xkb.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to open libxkbcommon"); return GLFW_FALSE; } _glfw.wl.xkb.context_new = (PFN_xkb_context_new) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new"); _glfw.wl.xkb.context_unref = (PFN_xkb_context_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref"); _glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string"); _glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref"); _glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index"); _glfw.wl.xkb.keymap_key_repeats = (PFN_xkb_keymap_key_repeats) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats"); _glfw.wl.xkb.state_new = (PFN_xkb_state_new) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new"); _glfw.wl.xkb.state_unref = (PFN_xkb_state_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref"); _glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms"); _glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask"); _glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods"); #ifdef HAVE_XKBCOMMON_COMPOSE_H _glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale"); _glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref"); _glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new"); _glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref"); _glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed"); _glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status"); _glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); #endif _glfw.wl.display = wl_display_connect(NULL); if (!_glfw.wl.display) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to connect to display"); return GLFW_FALSE; } _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); createKeyTables(); _glfw.wl.xkb.context = xkb_context_new(0); if (!_glfw.wl.xkb.context) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to initialize xkb context"); return GLFW_FALSE; } // Sync so we got all registry objects wl_display_roundtrip(_glfw.wl.display); // Sync so we got all initial output events wl_display_roundtrip(_glfw.wl.display); #ifdef __linux__ if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; #endif _glfwInitTimerPOSIX(); _glfw.wl.timerfd = -1; if (_glfw.wl.seatVersion >= 4) _glfw.wl.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if (_glfw.wl.pointer && _glfw.wl.shm) { cursorTheme = getenv("XCURSOR_THEME"); cursorSizeStr = getenv("XCURSOR_SIZE"); cursorSize = 32; if (cursorSizeStr) { errno = 0; cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10); if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX) cursorSize = (int)cursorSizeLong; } _glfw.wl.cursorTheme = wl_cursor_theme_load(cursorTheme, cursorSize, _glfw.wl.shm); if (!_glfw.wl.cursorTheme) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unable to load default cursor theme"); return GLFW_FALSE; } // If this happens to be NULL, we just fallback to the scale=1 version. _glfw.wl.cursorThemeHiDPI = wl_cursor_theme_load(cursorTheme, 2 * cursorSize, _glfw.wl.shm); _glfw.wl.cursorSurface = wl_compositor_create_surface(_glfw.wl.compositor); _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); } if (_glfw.wl.seat && _glfw.wl.dataDeviceManager) { _glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat); wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, NULL); _glfw.wl.clipboardString = malloc(4096); if (!_glfw.wl.clipboardString) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unable to allocate clipboard memory"); return GLFW_FALSE; } _glfw.wl.clipboardSize = 4096; } return GLFW_TRUE; }