static void checkScaleChange(_GLFWwindow* window) { int scaledWidth, scaledHeight; int scale = 1; int i; int monitorScale; // Check if we will be able to set the buffer scale or not. if (_glfw.wl.compositorVersion < 3) return; // Get the scale factor from the highest scale monitor. for (i = 0; i < window->wl.monitorsCount; ++i) { monitorScale = window->wl.monitors[i]->wl.scale; if (scale < monitorScale) scale = monitorScale; } // Only change the framebuffer size if the scale changed. if (scale != window->wl.scale) { window->wl.scale = scale; scaledWidth = window->wl.width * scale; scaledHeight = window->wl.height * scale; wl_surface_set_buffer_scale(window->wl.surface, scale); wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); } }
bool WaylandEGLContext::create_context () { int scale = gdk_window_get_scale_factor (gdk_window); gdk_window_get_geometry (gdk_window, &x, &y, &width, &height); EGLint surface_attribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_RED_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_NONE }; EGLint context_attribs[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, EGL_NONE }; EGLint num_configs = 0; if (!subsurface) return false; egl_display = eglGetDisplay ((EGLNativeDisplayType) display); eglInitialize (egl_display, NULL, NULL); if (!eglChooseConfig (egl_display, surface_attribs, &egl_config, 1, &num_configs)) { printf ("Couldn't find matching config.\n"); return false; } eglBindAPI (EGL_OPENGL_API); egl_window = wl_egl_window_create (child, width * scale, height * scale); if (!egl_window) { printf ("Couldn't create window.\n"); return false; } egl_surface = eglCreateWindowSurface (egl_display, egl_config, (EGLNativeWindowType) egl_window, NULL); if (!egl_surface) { printf ("Couldn't create surface.\n"); return false; } egl_context = eglCreateContext (egl_display, egl_config, EGL_NO_CONTEXT, context_attribs); if (!egl_context) { printf ("Couldn't create context.\n"); return false; } wl_surface_set_buffer_scale (child, scale); gdk_window_invalidate_rect (gdk_window, NULL, FALSE); return true; }
int window_render(struct window *window) { window->frame_cb = wl_surface_frame(window->surface); wl_callback_add_listener(window->frame_cb, &listener, window); wl_surface_attach(window->surface, window->buffer->buffer, 0, 0); wl_surface_set_buffer_scale(window->surface, window->scale); wl_surface_damage(window->surface, 0, 0, window->width, window->height); wl_surface_commit(window->surface); return 1; }
void render_frame(struct swaynag *swaynag) { if (!swaynag->run_display) { return; } cairo_surface_t *recorder = cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA, NULL); cairo_t *cairo = cairo_create(recorder); cairo_save(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); cairo_paint(cairo); cairo_restore(cairo); uint32_t height = render_to_cairo(cairo, swaynag); if (height != swaynag->height) { zwlr_layer_surface_v1_set_size(swaynag->layer_surface, 0, height); zwlr_layer_surface_v1_set_exclusive_zone(swaynag->layer_surface, height); wl_surface_commit(swaynag->surface); wl_display_roundtrip(swaynag->display); } else { swaynag->current_buffer = get_next_buffer(swaynag->shm, swaynag->buffers, swaynag->width * swaynag->scale, swaynag->height * swaynag->scale); if (!swaynag->current_buffer) { sway_log(SWAY_DEBUG, "Failed to get buffer. Skipping frame."); goto cleanup; } cairo_t *shm = swaynag->current_buffer->cairo; cairo_save(shm); cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); cairo_paint(shm); cairo_restore(shm); cairo_set_source_surface(shm, recorder, 0.0, 0.0); cairo_paint(shm); wl_surface_set_buffer_scale(swaynag->surface, swaynag->scale); wl_surface_attach(swaynag->surface, swaynag->current_buffer->buffer, 0, 0); wl_surface_damage(swaynag->surface, 0, 0, swaynag->width, swaynag->height); wl_surface_commit(swaynag->surface); wl_display_roundtrip(swaynag->display); } cleanup: cairo_surface_destroy(recorder); cairo_destroy(cairo); }
struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height, int32_t scale, bool shell_surface) { struct window *window = malloc(sizeof(struct window)); memset(window, 0, sizeof(struct window)); window->width = width; window->height = height; window->scale = scale; window->registry = registry; window->font = "monospace 10"; window->surface = wl_compositor_create_surface(registry->compositor); if (shell_surface) { window_make_shell(window); } if (registry->pointer) { wl_pointer_add_listener(registry->pointer, &pointer_listener, window); } get_next_buffer(window); if (registry->pointer) { char *cursor_theme = getenv("SWAY_CURSOR_THEME"); if (!cursor_theme) { cursor_theme = "default"; } char *cursor_size = getenv("SWAY_CURSOR_SIZE"); if (!cursor_size) { cursor_size = "16"; } sway_log(L_DEBUG, "Cursor scale: %d", scale); window->cursor.cursor_theme = wl_cursor_theme_load(cursor_theme, atoi(cursor_size) * scale, registry->shm); window->cursor.cursor = wl_cursor_theme_get_cursor(window->cursor.cursor_theme, "left_ptr"); window->cursor.surface = wl_compositor_create_surface(registry->compositor); struct wl_cursor_image *image = window->cursor.cursor->images[0]; struct wl_buffer *cursor_buf = wl_cursor_image_get_buffer(image); wl_surface_attach(window->cursor.surface, cursor_buf, 0, 0); wl_surface_set_buffer_scale(window->cursor.surface, scale); wl_surface_damage(window->cursor.surface, 0, 0, image->width, image->height); wl_surface_commit(window->cursor.surface); } return window; }
static void egl_resize(struct vo_wayland_state *wl) { int32_t x = wl->window.sh_x; int32_t y = wl->window.sh_y; int32_t width = wl->window.sh_width; int32_t height = wl->window.sh_height; int32_t scale = 1; if (!wl->egl_context.egl_window) return; if (wl->display.current_output) scale = wl->display.current_output->scale; // get the real size of the window // this improves moving the window while resizing it wl_egl_window_get_attached_size(wl->egl_context.egl_window, &wl->window.width, &wl->window.height); MP_VERBOSE(wl, "resizing %dx%d -> %dx%d\n", wl->window.width, wl->window.height, width, height); if (x != 0) x = wl->window.width - width; if (y != 0) y = wl->window.height - height; wl_surface_set_buffer_scale(wl->window.video_surface, scale); wl_egl_window_resize(wl->egl_context.egl_window, scale*width, scale*height, x, y); wl->window.width = width; wl->window.height = height; /* set size for mplayer */ wl->vo->dwidth = scale*wl->window.width; wl->vo->dheight = scale*wl->window.height; wl->vo->want_redraw = true; wl->window.events = 0; }
static void setCursor(_GLFWwindow* window, const char* name) { struct wl_buffer* buffer; struct wl_cursor* cursor; struct wl_cursor_image* image; struct wl_surface* surface = _glfw.wl.cursorSurface; struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; int scale = 1; if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) { // We only support up to scale=2 for now, since libwayland-cursor // requires us to load a different theme for each size. scale = 2; theme = _glfw.wl.cursorThemeHiDPI; } cursor = wl_cursor_theme_get_cursor(theme, name); if (!cursor) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Standard cursor not found"); return; } // TODO: handle animated cursors too. image = cursor->images[0]; if (!image) return; buffer = wl_cursor_image_get_buffer(image); if (!buffer) return; wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, surface, image->hotspot_x / scale, image->hotspot_y / scale); wl_surface_set_buffer_scale(surface, scale); wl_surface_attach(surface, buffer, 0, 0); wl_surface_damage(surface, 0, 0, image->width, image->height); wl_surface_commit(surface); }
static void redraw(void *data, struct wl_callback *callback, uint32_t time) { struct window *window = data; struct buffer *buffer; int off_x, off_y, bwidth, bheight, bborder, bpitch, bradius; uint32_t *buffer_data; float bx, by; buffer = window_next_buffer(window); if (!buffer) { fprintf(stderr, !callback ? "Failed to create the first buffer.\n" : "Both buffers busy at redraw(). Server bug?\n"); abort(); } /* Rotate the damage, but keep the even/odd parity so the * dimensions of the buffers don't change */ if (window->flags & WINDOW_FLAG_ROTATING_TRANSFORM) window->transform = (window->transform + 2) % 8; switch (window->transform) { default: case WL_OUTPUT_TRANSFORM_NORMAL: case WL_OUTPUT_TRANSFORM_180: case WL_OUTPUT_TRANSFORM_FLIPPED: case WL_OUTPUT_TRANSFORM_FLIPPED_180: bwidth = window->width * window->scale; bheight = window->height * window->scale; break; case WL_OUTPUT_TRANSFORM_90: case WL_OUTPUT_TRANSFORM_270: case WL_OUTPUT_TRANSFORM_FLIPPED_90: case WL_OUTPUT_TRANSFORM_FLIPPED_270: bwidth = window->height * window->scale; bheight = window->width * window->scale; break; } bpitch = bwidth; bborder = window->border * window->scale; bradius = window->ball.radius * window->scale; buffer_data = buffer->shm_data; if (window->viewport) { /* Fill the whole thing with red to detect viewport errors */ paint_box(buffer->shm_data, bpitch, 0, 0, bwidth, bheight, 0xffff0000); /* The buffer is the same size. However, we crop it * and scale it up by a factor of 2 */ bborder /= 2; bradius /= 2; bwidth /= 2; bheight /= 2; /* Offset the drawing region */ off_x = (window->width / 3) * window->scale; off_y = (window->height / 5) * window->scale; switch (window->transform) { default: case WL_OUTPUT_TRANSFORM_NORMAL: buffer_data += off_y * bpitch + off_x; break; case WL_OUTPUT_TRANSFORM_90: buffer_data += off_x * bpitch + (bwidth - off_y); break; case WL_OUTPUT_TRANSFORM_180: buffer_data += (bheight - off_y) * bpitch + (bwidth - off_x); break; case WL_OUTPUT_TRANSFORM_270: buffer_data += (bheight - off_x) * bpitch + off_y; break; case WL_OUTPUT_TRANSFORM_FLIPPED: buffer_data += off_y * bpitch + (bwidth - off_x); break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: buffer_data += (bheight - off_x) * bpitch + (bwidth - off_y); break; case WL_OUTPUT_TRANSFORM_FLIPPED_180: buffer_data += (bheight - off_y) * bpitch + off_x; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: buffer_data += off_x * bpitch + off_y; break; } wl_viewport_set_source(window->viewport, wl_fixed_from_int(window->width / 3), wl_fixed_from_int(window->height / 5), wl_fixed_from_int(window->width / 2), wl_fixed_from_int(window->height / 2)); } /* Paint the border */ paint_box(buffer_data, bpitch, 0, 0, bwidth, bborder, 0xffffffff); paint_box(buffer_data, bpitch, 0, 0, bborder, bheight, 0xffffffff); paint_box(buffer_data, bpitch, bwidth - bborder, 0, bborder, bheight, 0xffffffff); paint_box(buffer_data, bpitch, 0, bheight - bborder, bwidth, bborder, 0xffffffff); /* fill with translucent */ paint_box(buffer_data, bpitch, bborder, bborder, bwidth - 2 * bborder, bheight - 2 * bborder, 0x80000000); /* Damage where the ball was */ wl_surface_damage(window->surface, window->ball.x - window->ball.radius, window->ball.y - window->ball.radius, window->ball.radius * 2 + 1, window->ball.radius * 2 + 1); window_advance_game(window, time); window_get_transformed_ball(window, &bx, &by); /* Paint the ball */ paint_circle(buffer_data, bpitch, bx, by, bradius, 0xff00ff00); if (print_debug) { printf("Ball now located at (%f, %f)\n", window->ball.x, window->ball.y); printf("Circle painted at (%f, %f), radius %d\n", bx, by, bradius); printf("Buffer damage rectangle: (%d, %d) @ %dx%d\n", (int)(bx - bradius), (int)(by - bradius), bradius * 2 + 1, bradius * 2 + 1); } /* Damage where the ball is now */ wl_surface_damage(window->surface, window->ball.x - window->ball.radius, window->ball.y - window->ball.radius, window->ball.radius * 2 + 1, window->ball.radius * 2 + 1); wl_surface_attach(window->surface, buffer->buffer, 0, 0); if (window->display->compositor_version >= 2 && (window->transform != WL_OUTPUT_TRANSFORM_NORMAL || window->flags & WINDOW_FLAG_ROTATING_TRANSFORM)) wl_surface_set_buffer_transform(window->surface, window->transform); if (window->viewport) wl_viewport_set_destination(window->viewport, window->width, window->height); if (window->scale != 1) wl_surface_set_buffer_scale(window->surface, window->scale); if (callback) wl_callback_destroy(callback); window->callback = wl_surface_frame(window->surface); wl_callback_add_listener(window->callback, &frame_listener, window); wl_surface_commit(window->surface); buffer->busy = 1; }
static bool gfx_ctx_wl_set_video_mode(void *data, video_frame_info_t *video_info, 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->input); switch (wl_api) { case GFX_CTX_VULKAN_API: wl_display_roundtrip(wl->input.dpy); #ifdef HAVE_VULKAN if (!vulkan_surface_create(&wl->vk, VULKAN_WSI_WAYLAND, wl->input.dpy, wl->surface, wl->width, wl->height, wl->swap_interval)) goto error; #endif break; case GFX_CTX_NONE: default: break; } if (fullscreen) { wl->cursor.visible = false; gfx_ctx_wl_show_mouse(wl, false); } else wl->cursor.visible = true; return true; error: gfx_ctx_wl_destroy(data); return false; }
static gboolean _eventd_nd_wl_create_buffer(EventdNdSurface *self) { struct wl_shm_pool *pool; struct wl_buffer *buffer; gint fd; gpointer data; gint width, height, stride; gsize size; width = self->width * self->context->scale; height = self->height * self->context->scale; stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); size = stride * height; gchar *filename; filename = g_build_filename(g_get_user_runtime_dir(), PACKAGE_NAME G_DIR_SEPARATOR_S "wayland-surface", NULL); fd = g_open(filename, O_CREAT | O_RDWR | O_CLOEXEC, 0); g_unlink(filename); g_free(filename); if ( fd < 0 ) { g_warning("creating a buffer file for %zu B failed: %s\n", size, g_strerror(errno)); return FALSE; } if ( ftruncate(fd, size) < 0 ) { close(fd); return FALSE; } data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if ( data == MAP_FAILED ) { g_warning("mmap failed: %s\n", g_strerror(errno)); close(fd); return FALSE; } cairo_surface_t *cairo_surface; cairo_surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, 4 * width); cairo_surface_set_device_scale(cairo_surface, self->context->scale, self->context->scale); self->context->nd->notification_draw(self->notification, cairo_surface, TRUE); cairo_surface_destroy(cairo_surface); munmap(data, size); pool = wl_shm_create_pool(self->context->shm, fd, size); buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); wl_shm_pool_destroy(pool); close(fd); if ( self->buffer != NULL ) _eventd_nd_wl_buffer_release(self->buffer, self->buffer->buffer); self->buffer = g_new0(EventdNdWlBuffer, 1); self->buffer->buffer = buffer; self->buffer->data = data; self->buffer->size = size; wl_buffer_add_listener(buffer, &_eventd_nd_wl_buffer_listener, self->buffer); wl_surface_damage(self->surface, 0, 0, self->width, self->height); wl_surface_attach(self->surface, self->buffer->buffer, 0, 0); if ( wl_surface_get_version(self->surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION ) wl_surface_set_buffer_scale(self->surface, self->context->scale); wl_surface_commit(self->surface); return TRUE; }
static bool wayland_vk_init(struct ra_ctx *ctx) { struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); struct mpvk_ctx *vk = &p->vk; int msgl = ctx->opts.probing ? MSGL_V : MSGL_ERR; if (!mpvk_instance_init(vk, ctx->log, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, ctx->opts.debug)) goto error; if (!vo_wayland_init(ctx->vo)) goto error; VkWaylandSurfaceCreateInfoKHR wlinfo = { .sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, .display = ctx->vo->wl->display, .surface = ctx->vo->wl->surface, }; VkResult res = vkCreateWaylandSurfaceKHR(vk->inst, &wlinfo, MPVK_ALLOCATOR, &vk->surf); if (res != VK_SUCCESS) { MP_MSG(ctx, msgl, "Failed creating Wayland surface: %s\n", vk_err(res)); goto error; } /* Because in Wayland clients render whenever they receive a callback from * the compositor, and the fact that the compositor usually stops sending * callbacks once the surface is no longer visible, using FIFO here would * mean the entire player would block on acquiring swapchain images. Hence, * use MAILBOX to guarantee that there'll always be a swapchain image and * the player won't block waiting on those */ if (!ra_vk_ctx_init(ctx, vk, VK_PRESENT_MODE_MAILBOX_KHR)) goto error; return true; error: wayland_vk_uninit(ctx); return false; } static void resize(struct ra_ctx *ctx) { struct vo_wayland_state *wl = ctx->vo->wl; MP_VERBOSE(wl, "Handling resize on the vk side\n"); const int32_t width = wl->scaling*mp_rect_w(wl->geometry); const int32_t height = wl->scaling*mp_rect_h(wl->geometry); wl_surface_set_buffer_scale(wl->surface, wl->scaling); wl->vo->dwidth = width; wl->vo->dheight = height; } static bool wayland_vk_reconfig(struct ra_ctx *ctx) { if (!vo_wayland_reconfig(ctx->vo)) return false; return true; } static int wayland_vk_control(struct ra_ctx *ctx, int *events, int request, void *arg) { int ret = vo_wayland_control(ctx->vo, events, request, arg); if (*events & VO_EVENT_RESIZE) { resize(ctx); if (ra_vk_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight)) return VO_ERROR; } return ret; }