merge in upstream

This commit is contained in:
DerVerruckteFuchs 2020-12-22 05:57:05 -05:00
commit 3576eec3cf
154 changed files with 4860 additions and 4410 deletions

View file

@ -16,7 +16,7 @@ sources:
tasks: tasks:
- setup: | - setup: |
cd wlroots cd wlroots
meson build -Dauto_features=enabled -Dlogind=disabled -Dxcb-errors=disabled meson build -Dauto_features=enabled -Dlogind=disabled -Dlibseat=disabled -Dxcb-errors=disabled
- build: | - build: |
cd wlroots cd wlroots
ninja -C build ninja -C build

View file

@ -12,6 +12,7 @@ packages:
- xcb-util-errors - xcb-util-errors
- xcb-util-image - xcb-util-image
- xcb-util-wm - xcb-util-wm
- seatd
sources: sources:
- https://github.com/swaywm/wlroots - https://github.com/swaywm/wlroots
tasks: tasks:

View file

@ -18,6 +18,7 @@ packages:
- x11/pixman - x11/pixman
- x11/xcb-util-errors - x11/xcb-util-errors
- x11/xcb-util-wm - x11/xcb-util-wm
- sysutils/seatd
sources: sources:
- https://github.com/swaywm/wlroots - https://github.com/swaywm/wlroots
tasks: tasks:

View file

@ -151,7 +151,7 @@ static struct wlr_backend *attempt_noop_backend(struct wl_display *display) {
static struct wlr_backend *attempt_drm_backend(struct wl_display *display, static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
struct wlr_backend *backend, struct wlr_session *session, struct wlr_backend *backend, struct wlr_session *session,
wlr_renderer_create_func_t create_renderer_func) { wlr_renderer_create_func_t create_renderer_func) {
int gpus[8]; struct wlr_device *gpus[8];
size_t num_gpus = wlr_session_find_gpus(session, 8, gpus); size_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
struct wlr_backend *primary_drm = NULL; struct wlr_backend *primary_drm = NULL;
wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus); wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus);
@ -160,7 +160,7 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
struct wlr_backend *drm = wlr_drm_backend_create(display, session, struct wlr_backend *drm = wlr_drm_backend_create(display, session,
gpus[i], primary_drm, create_renderer_func); gpus[i], primary_drm, create_renderer_func);
if (!drm) { if (!drm) {
wlr_log(WLR_ERROR, "Failed to open DRM device %d", gpus[i]); wlr_log(WLR_ERROR, "Failed to create DRM backend");
continue; continue;
} }
@ -254,8 +254,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
return backend; return backend;
} }
if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY") || if (getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET")) {
getenv("WAYLAND_SOCKET")) {
struct wlr_backend *wl_backend = attempt_wl_backend(display, struct wlr_backend *wl_backend = attempt_wl_backend(display,
create_renderer_func); create_renderer_func);
if (!wl_backend) { if (!wl_backend) {

View file

@ -25,8 +25,7 @@ static void atomic_begin(struct atomic *atom) {
static bool atomic_commit(struct atomic *atom, static bool atomic_commit(struct atomic *atom,
struct wlr_drm_connector *conn, uint32_t flags) { struct wlr_drm_connector *conn, uint32_t flags) {
struct wlr_drm_backend *drm = struct wlr_drm_backend *drm = conn->backend;
get_drm_backend_from_backend(conn->output.backend);
if (atom->failed) { if (atom->failed) {
return false; return false;
} }
@ -34,7 +33,7 @@ static bool atomic_commit(struct atomic *atom,
int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, drm); int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, drm);
if (ret) { if (ret) {
wlr_log_errno(WLR_ERROR, "%s: Atomic %s failed (%s)", wlr_log_errno(WLR_ERROR, "%s: Atomic %s failed (%s)",
conn->output.name, conn->name,
(flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "test" : "commit", (flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "test" : "commit",
(flags & DRM_MODE_ATOMIC_ALLOW_MODESET) ? "modeset" : "pageflip"); (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) ? "modeset" : "pageflip");
return false; return false;

View file

@ -38,7 +38,7 @@ static void backend_destroy(struct wlr_backend *backend) {
struct wlr_drm_connector *conn, *next; struct wlr_drm_connector *conn, *next;
wl_list_for_each_safe(conn, next, &drm->outputs, link) { wl_list_for_each_safe(conn, next, &drm->outputs, link) {
wlr_output_destroy(&conn->output); destroy_drm_connector(conn);
} }
wlr_signal_emit_safe(&backend->events.destroy, backend); wlr_signal_emit_safe(&backend->events.destroy, backend);
@ -50,7 +50,9 @@ static void backend_destroy(struct wlr_backend *backend) {
finish_drm_resources(drm); finish_drm_resources(drm);
finish_drm_renderer(&drm->renderer); finish_drm_renderer(&drm->renderer);
wlr_session_close_file(drm->session, drm->fd);
free(drm->name);
wlr_session_close_file(drm->session, drm->dev);
wl_event_source_remove(drm->drm_event); wl_event_source_remove(drm->drm_event);
free(drm); free(drm);
} }
@ -85,7 +87,7 @@ bool wlr_backend_is_drm(struct wlr_backend *b) {
static void session_signal(struct wl_listener *listener, void *data) { static void session_signal(struct wl_listener *listener, void *data) {
struct wlr_drm_backend *drm = struct wlr_drm_backend *drm =
wl_container_of(listener, drm, session_signal); wl_container_of(listener, drm, session_signal);
struct wlr_session *session = data; struct wlr_session *session = drm->session;
if (session->active) { if (session->active) {
wlr_log(WLR_INFO, "DRM fd resumed"); wlr_log(WLR_INFO, "DRM fd resumed");
@ -108,9 +110,7 @@ static void drm_invalidated(struct wl_listener *listener, void *data) {
struct wlr_drm_backend *drm = struct wlr_drm_backend *drm =
wl_container_of(listener, drm, drm_invalidated); wl_container_of(listener, drm, drm_invalidated);
char *name = drmGetDeviceNameFromFd2(drm->fd); wlr_log(WLR_DEBUG, "%s invalidated", drm->name);
wlr_log(WLR_DEBUG, "%s invalidated", name);
free(name);
scan_drm_connectors(drm); scan_drm_connectors(drm);
} }
@ -128,15 +128,15 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
} }
struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
struct wlr_session *session, int gpu_fd, struct wlr_backend *parent, struct wlr_session *session, struct wlr_device *dev,
struct wlr_backend *parent,
wlr_renderer_create_func_t create_renderer_func) { wlr_renderer_create_func_t create_renderer_func) {
assert(display && session && gpu_fd >= 0); assert(display && session && dev);
assert(!parent || wlr_backend_is_drm(parent)); assert(!parent || wlr_backend_is_drm(parent));
char *name = drmGetDeviceNameFromFd2(gpu_fd); char *name = drmGetDeviceNameFromFd2(dev->fd);
drmVersion *version = drmGetVersion(gpu_fd); drmVersion *version = drmGetVersion(dev->fd);
wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name); wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name);
free(name);
drmFreeVersion(version); drmFreeVersion(version);
struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend)); struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend));
@ -149,13 +149,15 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
drm->session = session; drm->session = session;
wl_list_init(&drm->outputs); wl_list_init(&drm->outputs);
drm->fd = gpu_fd; drm->dev = dev;
drm->fd = dev->fd;
drm->name = name;
if (parent != NULL) { if (parent != NULL) {
drm->parent = get_drm_backend_from_backend(parent); drm->parent = get_drm_backend_from_backend(parent);
} }
drm->drm_invalidated.notify = drm_invalidated; drm->drm_invalidated.notify = drm_invalidated;
wlr_session_signal_add(session, gpu_fd, &drm->drm_invalidated); wl_signal_add(&dev->events.change, &drm->drm_invalidated);
drm->display = display; drm->display = display;
struct wl_event_loop *event_loop = wl_display_get_event_loop(display); struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
@ -168,7 +170,7 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
} }
drm->session_signal.notify = session_signal; drm->session_signal.notify = session_signal;
wl_signal_add(&session->session_signal, &drm->session_signal); wl_signal_add(&session->events.active, &drm->session_signal);
if (!check_drm_features(drm)) { if (!check_drm_features(drm)) {
goto error_event; goto error_event;
@ -195,7 +197,7 @@ error_event:
wl_list_remove(&drm->session_signal.link); wl_list_remove(&drm->session_signal.link);
wl_event_source_remove(drm->drm_event); wl_event_source_remove(drm->drm_event);
error_fd: error_fd:
wlr_session_close_file(drm->session, drm->fd); wlr_session_close_file(drm->session, dev);
free(drm); free(drm);
return NULL; return NULL;
} }

View file

@ -27,6 +27,7 @@
#include "backend/drm/drm.h" #include "backend/drm/drm.h"
#include "backend/drm/iface.h" #include "backend/drm/iface.h"
#include "backend/drm/util.h" #include "backend/drm/util.h"
#include "render/swapchain.h"
#include "util/signal.h" #include "util/signal.h"
bool check_drm_features(struct wlr_drm_backend *drm) { bool check_drm_features(struct wlr_drm_backend *drm) {
@ -104,22 +105,6 @@ static bool add_plane(struct wlr_drm_backend *drm,
DRM_FORMAT_MOD_INVALID); DRM_FORMAT_MOD_INVALID);
} }
// Choose an RGB format for the plane
uint32_t rgb_format = DRM_FORMAT_INVALID;
for (size_t j = 0; j < drm_plane->count_formats; ++j) {
uint32_t fmt = drm_plane->formats[j];
if (fmt == DRM_FORMAT_ARGB2101010 || fmt == DRM_FORMAT_XRGB2101010 ||
fmt == DRM_FORMAT_ARGB8888) {
// Prefer formats with 10 bit color and/or alpha channel.
rgb_format = fmt;
break;
} else if (fmt == DRM_FORMAT_XRGB8888) {
rgb_format = fmt;
}
}
p->drm_format = rgb_format;
if (p->props.in_formats) { if (p->props.in_formats) {
uint64_t blob_id; uint64_t blob_id;
if (!get_drm_prop(drm->fd, p->id, p->props.in_formats, &blob_id)) { if (!get_drm_prop(drm->fd, p->id, p->props.in_formats, &blob_id)) {
@ -197,35 +182,29 @@ static bool init_planes(struct wlr_drm_backend *drm) {
goto error; goto error;
} }
/* // We don't really care about overlay planes, as we don't support them
* This is a very naive implementation of the plane matching // yet.
* logic. Primary and cursor planes should only work on a
* single CRTC, and this should be perfectly adequate, but
* overlay planes can potentially work with multiple CRTCs,
* meaning this could return inefficient/skewed results.
*
* However, we don't really care about overlay planes, as we
* don't support them yet. We only bother to keep basic
* tracking of them for DRM lease clients.
*
* possible_crtcs is a bitmask of crtcs, where each bit is an
* index into drmModeRes.crtcs. So if bit 0 is set (ffs starts
* counting from 1), crtc 0 is possible.
*/
int crtc_bit = ffs(plane->possible_crtcs) - 1;
// This would be a kernel bug
assert(crtc_bit >= 0 && (size_t)crtc_bit < drm->num_crtcs);
struct wlr_drm_crtc *crtc = &drm->crtcs[crtc_bit];
if (type == DRM_PLANE_TYPE_OVERLAY) { if (type == DRM_PLANE_TYPE_OVERLAY) {
uint32_t *tmp = realloc(crtc->overlays, drmModeFreePlane(plane);
sizeof(*crtc->overlays) * (crtc->num_overlays + 1)); continue;
if (tmp) { }
crtc->overlays = tmp;
crtc->overlays[crtc->num_overlays++] = id; assert(drm->num_crtcs <= 32);
struct wlr_drm_crtc *crtc = NULL;
for (size_t j = 0; j < drm->num_crtcs ; j++) {
uint32_t crtc_bit = 1 << j;
if ((plane->possible_crtcs & crtc_bit) == 0) {
continue;
} }
struct wlr_drm_crtc *candidate = &drm->crtcs[j];
if ((type == DRM_PLANE_TYPE_PRIMARY && !candidate->primary) ||
(type == DRM_PLANE_TYPE_CURSOR && !candidate->cursor)) {
crtc = candidate;
break;
}
}
if (!crtc) {
drmModeFreePlane(plane); drmModeFreePlane(plane);
continue; continue;
} }
@ -314,7 +293,6 @@ void finish_drm_resources(struct wlr_drm_backend *drm) {
wlr_drm_format_set_finish(&crtc->cursor->formats); wlr_drm_format_set_finish(&crtc->cursor->formats);
free(crtc->cursor); free(crtc->cursor);
} }
free(crtc->overlays);
} }
free(drm->crtcs); free(drm->crtcs);
@ -332,16 +310,24 @@ static bool drm_connector_attach_render(struct wlr_output *output,
return drm_surface_make_current(&conn->crtc->primary->surf, buffer_age); return drm_surface_make_current(&conn->crtc->primary->surf, buffer_age);
} }
static void drm_plane_set_committed(struct wlr_drm_plane *plane) {
drm_fb_move(&plane->queued_fb, &plane->pending_fb);
struct wlr_buffer *queued = plane->queued_fb.wlr_buf;
if (queued != NULL) {
wlr_swapchain_set_buffer_submitted(plane->surf.swapchain, queued);
}
}
static bool drm_crtc_commit(struct wlr_drm_connector *conn, uint32_t flags) { static bool drm_crtc_commit(struct wlr_drm_connector *conn, uint32_t flags) {
struct wlr_drm_backend *drm = struct wlr_drm_backend *drm = conn->backend;
get_drm_backend_from_backend(conn->output.backend);
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
bool ok = drm->iface->crtc_commit(drm, conn, flags); bool ok = drm->iface->crtc_commit(drm, conn, flags);
if (ok && !(flags & DRM_MODE_ATOMIC_TEST_ONLY)) { if (ok && !(flags & DRM_MODE_ATOMIC_TEST_ONLY)) {
memcpy(&crtc->current, &crtc->pending, sizeof(struct wlr_drm_crtc_state)); memcpy(&crtc->current, &crtc->pending, sizeof(struct wlr_drm_crtc_state));
drm_fb_move(&crtc->primary->queued_fb, &crtc->primary->pending_fb); drm_plane_set_committed(crtc->primary);
if (crtc->cursor != NULL) { if (crtc->cursor != NULL) {
drm_fb_move(&crtc->cursor->queued_fb, &crtc->cursor->pending_fb); drm_plane_set_committed(crtc->cursor);
} }
} else { } else {
memcpy(&crtc->pending, &crtc->current, sizeof(struct wlr_drm_crtc_state)); memcpy(&crtc->pending, &crtc->current, sizeof(struct wlr_drm_crtc_state));
@ -362,18 +348,24 @@ static bool drm_crtc_page_flip(struct wlr_drm_connector *conn) {
// we'll wait for all queued page-flips to complete, so we don't need this // we'll wait for all queued page-flips to complete, so we don't need this
// safeguard. // safeguard.
if (conn->pageflip_pending && !crtc->pending_modeset) { if (conn->pageflip_pending && !crtc->pending_modeset) {
wlr_log(WLR_ERROR, "Failed to page-flip output '%s': " wlr_drm_conn_log(conn, WLR_ERROR, "Failed to page-flip output: "
"a page-flip is already pending", conn->output.name); "a page-flip is already pending");
return false; return false;
} }
assert(crtc->pending.active); assert(crtc->pending.active);
assert(plane_get_next_fb(crtc->primary)->type != WLR_DRM_FB_TYPE_NONE); assert(plane_get_next_fb(crtc->primary)->bo);
if (!drm_crtc_commit(conn, DRM_MODE_PAGE_FLIP_EVENT)) { if (!drm_crtc_commit(conn, DRM_MODE_PAGE_FLIP_EVENT)) {
return false; return false;
} }
conn->pageflip_pending = true; conn->pageflip_pending = true;
// wlr_output's API guarantees that submitting a buffer will schedule a
// frame event. However the DRM backend will also schedule a frame event
// when performing a modeset. Set frame_pending to true so that
// wlr_output_schedule_frame doesn't trigger a synthetic frame event.
conn->output.frame_pending = true;
return true; return true;
} }
@ -381,8 +373,6 @@ static uint32_t strip_alpha_channel(uint32_t format) {
switch (format) { switch (format) {
case DRM_FORMAT_ARGB8888: case DRM_FORMAT_ARGB8888:
return DRM_FORMAT_XRGB8888; return DRM_FORMAT_XRGB8888;
case DRM_FORMAT_ARGB2101010:
return DRM_FORMAT_XRGB2101010;
default: default:
return DRM_FORMAT_INVALID; return DRM_FORMAT_INVALID;
} }
@ -390,8 +380,7 @@ static uint32_t strip_alpha_channel(uint32_t format) {
static bool test_buffer(struct wlr_drm_connector *conn, static bool test_buffer(struct wlr_drm_connector *conn,
struct wlr_buffer *wlr_buffer) { struct wlr_buffer *wlr_buffer) {
struct wlr_output *output = &conn->output; struct wlr_drm_backend *drm = conn->backend;
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
if (!drm->session->active) { if (!drm->session->active) {
return false; return false;
@ -441,7 +430,8 @@ static bool drm_connector_test(struct wlr_output *output) {
output->pending.enabled) { output->pending.enabled) {
if (output->current_mode == NULL && if (output->current_mode == NULL &&
!(output->pending.committed & WLR_OUTPUT_STATE_MODE)) { !(output->pending.committed & WLR_OUTPUT_STATE_MODE)) {
wlr_log(WLR_DEBUG, "Can't enable an output without a mode"); wlr_drm_conn_log(conn, WLR_DEBUG,
"Can't enable an output without a mode");
return false; return false;
} }
} }
@ -476,7 +466,7 @@ static struct wlr_output_mode *drm_connector_get_pending_mode(
static bool drm_connector_commit_buffer(struct wlr_output *output) { static bool drm_connector_commit_buffer(struct wlr_output *output) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); struct wlr_drm_backend *drm = conn->backend;
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) { if (!crtc) {
@ -488,7 +478,7 @@ static bool drm_connector_commit_buffer(struct wlr_output *output) {
switch (output->pending.buffer_type) { switch (output->pending.buffer_type) {
case WLR_OUTPUT_STATE_BUFFER_RENDER: case WLR_OUTPUT_STATE_BUFFER_RENDER:
if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) { if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) {
wlr_log(WLR_ERROR, "drm_fb_lock_surface failed"); wlr_drm_conn_log(conn, WLR_ERROR, "drm_fb_lock_surface failed");
return false; return false;
} }
break; break;
@ -512,8 +502,7 @@ static bool drm_connector_commit_buffer(struct wlr_output *output) {
} }
bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) { bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) {
struct wlr_drm_backend *drm = struct wlr_drm_backend *drm = conn->backend;
get_drm_backend_from_backend(conn->output.backend);
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) { if (!crtc) {
@ -524,13 +513,13 @@ bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) {
if (conn->props.vrr_capable == 0 || if (conn->props.vrr_capable == 0 ||
!get_drm_prop(drm->fd, conn->id, conn->props.vrr_capable, !get_drm_prop(drm->fd, conn->id, conn->props.vrr_capable,
&vrr_capable) || !vrr_capable) { &vrr_capable) || !vrr_capable) {
wlr_log(WLR_DEBUG, "Failed to enable adaptive sync: " wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to enable adaptive sync: "
"connector '%s' doesn't support VRR", conn->output.name); "connector doesn't support VRR");
return false; return false;
} }
if (crtc->props.vrr_enabled == 0) { if (crtc->props.vrr_enabled == 0) {
wlr_log(WLR_DEBUG, "Failed to enable adaptive sync: " wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to enable adaptive sync: "
"CRTC %"PRIu32" doesn't support VRR", crtc->id); "CRTC %"PRIu32" doesn't support VRR", crtc->id);
return false; return false;
} }
@ -540,7 +529,7 @@ bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) {
static bool drm_connector_commit(struct wlr_output *output) { static bool drm_connector_commit(struct wlr_output *output) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); struct wlr_drm_backend *drm = conn->backend;
if (!drm_connector_test(output)) { if (!drm_connector_test(output)) {
return false; return false;
@ -590,13 +579,13 @@ static bool drm_connector_commit(struct wlr_output *output) {
} }
static void drm_connector_rollback_render(struct wlr_output *output) { static void drm_connector_rollback_render(struct wlr_output *output) {
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
wlr_egl_unset_current(&drm->renderer.egl); return drm_surface_unset_current(&conn->crtc->primary->surf);
} }
size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc) { struct wlr_drm_crtc *crtc) {
if (crtc->props.gamma_lut_size == 0) { if (crtc->props.gamma_lut_size == 0 || drm->iface == &legacy_iface) {
return (size_t)crtc->legacy_crtc->gamma_size; return (size_t)crtc->legacy_crtc->gamma_size;
} }
@ -612,7 +601,7 @@ size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm,
static size_t drm_connector_get_gamma_size(struct wlr_output *output) { static size_t drm_connector_get_gamma_size(struct wlr_output *output) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); struct wlr_drm_backend *drm = conn->backend;
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
if (crtc == NULL) { if (crtc == NULL) {
@ -625,7 +614,7 @@ static size_t drm_connector_get_gamma_size(struct wlr_output *output) {
static bool drm_connector_export_dmabuf(struct wlr_output *output, static bool drm_connector_export_dmabuf(struct wlr_output *output,
struct wlr_dmabuf_attributes *attribs) { struct wlr_dmabuf_attributes *attribs) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); struct wlr_drm_backend *drm = conn->backend;
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
if (!drm->session->active) { if (!drm->session->active) {
@ -636,20 +625,22 @@ static bool drm_connector_export_dmabuf(struct wlr_output *output,
return false; return false;
} }
struct wlr_drm_plane *plane = crtc->primary; struct wlr_drm_fb *fb = &crtc->primary->queued_fb;
if (fb->bo == NULL) {
if (plane->current_fb.type == WLR_DRM_FB_TYPE_NONE) { fb = &crtc->primary->current_fb;
}
if (fb->bo == NULL) {
return false; return false;
} }
return export_drm_bo(plane->current_fb.bo, attribs); return export_drm_bo(fb->bo, attribs);
} }
struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane) { struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane) {
if (plane->pending_fb.type != WLR_DRM_FB_TYPE_NONE) { if (plane->pending_fb.bo) {
return &plane->pending_fb; return &plane->pending_fb;
} }
if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) { if (plane->queued_fb.bo) {
return &plane->queued_fb; return &plane->queued_fb;
} }
return &plane->current_fb; return &plane->current_fb;
@ -658,15 +649,16 @@ struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane) {
static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn) { static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn) {
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) { if (!crtc) {
wlr_log(WLR_ERROR, "Page-flip failed on connector '%s': no CRTC", wlr_drm_conn_log(conn, WLR_ERROR, "Page-flip failed: no CRTC");
conn->output.name);
return false; return false;
} }
// drm_crtc_page_flip expects a FB to be available // drm_crtc_page_flip expects a FB to be available
struct wlr_drm_plane *plane = crtc->primary; struct wlr_drm_plane *plane = crtc->primary;
if (plane_get_next_fb(plane)->type == WLR_DRM_FB_TYPE_NONE) { if (!plane_get_next_fb(plane)->bo) {
drm_surface_render_black_frame(&plane->surf); if (!drm_surface_render_black_frame(&plane->surf)) {
return false;
}
if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) { if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) {
return false; return false;
} }
@ -677,21 +669,19 @@ static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn) {
static bool drm_connector_init_renderer(struct wlr_drm_connector *conn, static bool drm_connector_init_renderer(struct wlr_drm_connector *conn,
struct wlr_drm_mode *mode) { struct wlr_drm_mode *mode) {
struct wlr_drm_backend *drm = struct wlr_drm_backend *drm = conn->backend;
get_drm_backend_from_backend(conn->output.backend);
if (conn->state != WLR_DRM_CONN_CONNECTED && if (conn->state != WLR_DRM_CONN_CONNECTED &&
conn->state != WLR_DRM_CONN_NEEDS_MODESET) { conn->state != WLR_DRM_CONN_NEEDS_MODESET) {
return false; return false;
} }
wlr_log(WLR_DEBUG, "Initializing renderer on connector '%s'", wlr_drm_conn_log(conn, WLR_DEBUG, "Initializing renderer");
conn->output.name);
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) { if (!crtc) {
wlr_log(WLR_ERROR, "Failed to initialize renderer on connector '%s': " wlr_drm_conn_log(conn, WLR_ERROR,
"no CRTC", conn->output.name); "Failed to initialize renderer: no CRTC");
return false; return false;
} }
struct wlr_drm_plane *plane = crtc->primary; struct wlr_drm_plane *plane = crtc->primary;
@ -702,28 +692,28 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn,
int width = mode->wlr_mode.width; int width = mode->wlr_mode.width;
int height = mode->wlr_mode.height; int height = mode->wlr_mode.height;
uint32_t format = drm->renderer.gbm_format; uint32_t format = DRM_FORMAT_ARGB8888;
bool modifiers = true; bool modifiers = true;
const char *no_modifiers = getenv("WLR_DRM_NO_MODIFIERS"); const char *no_modifiers = getenv("WLR_DRM_NO_MODIFIERS");
if (no_modifiers != NULL && strcmp(no_modifiers, "1") == 0) { if (no_modifiers != NULL && strcmp(no_modifiers, "1") == 0) {
wlr_log(WLR_DEBUG, wlr_drm_conn_log(conn, WLR_DEBUG,
"WLR_DRM_NO_MODIFIERS set, initializing planes without modifiers"); "WLR_DRM_NO_MODIFIERS set, initializing planes without modifiers");
modifiers = false; modifiers = false;
} }
if (!drm_plane_init_surface(plane, drm, width, height, format, 0, modifiers) || if (!drm_plane_init_surface(plane, drm, width, height, format, false, modifiers) ||
!drm_connector_pageflip_renderer(conn)) { !drm_connector_pageflip_renderer(conn)) {
if (!modifiers) { if (!modifiers) {
wlr_log(WLR_ERROR, "Failed to initialize renderer " wlr_drm_conn_log(conn, WLR_ERROR, "Failed to initialize renderer:"
"on connector '%s': initial page-flip failed", "initial page-flip failed");
conn->output.name);
return false; return false;
} }
// If page-flipping with modifiers enabled doesn't work, retry without // If page-flipping with modifiers enabled doesn't work, retry without
// modifiers // modifiers
wlr_log(WLR_INFO, "Page-flip failed with primary FB modifiers enabled, " wlr_drm_conn_log(conn, WLR_INFO,
"Page-flip failed with primary FB modifiers enabled, "
"retrying without modifiers"); "retrying without modifiers");
modifiers = false; modifiers = false;
@ -732,13 +722,12 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn,
crtc->pending.mode = mode; crtc->pending.mode = mode;
if (!drm_plane_init_surface(plane, drm, width, height, format, if (!drm_plane_init_surface(plane, drm, width, height, format,
0, modifiers)) { false, modifiers)) {
return false; return false;
} }
if (!drm_connector_pageflip_renderer(conn)) { if (!drm_connector_pageflip_renderer(conn)) {
wlr_log(WLR_ERROR, "Failed to initialize renderer " wlr_drm_conn_log(conn, WLR_ERROR, "Failed to initialize renderer:"
"on connector '%s': initial page-flip failed", "initial page-flip failed");
conn->output.name);
return false; return false;
} }
} }
@ -756,19 +745,16 @@ static void attempt_enable_needs_modeset(struct wlr_drm_backend *drm) {
if (conn->state == WLR_DRM_CONN_NEEDS_MODESET && if (conn->state == WLR_DRM_CONN_NEEDS_MODESET &&
conn->crtc != NULL && conn->desired_mode != NULL && conn->crtc != NULL && conn->desired_mode != NULL &&
conn->desired_enabled) { conn->desired_enabled) {
wlr_log(WLR_DEBUG, "Output %s has a desired mode and a CRTC, " wlr_drm_conn_log(conn, WLR_DEBUG,
"attempting a modeset", conn->output.name); "Output has a desired mode and a CRTC, attempting a modeset");
drm_connector_set_mode(conn, conn->desired_mode); drm_connector_set_mode(conn, conn->desired_mode);
} }
} }
} }
static void drm_connector_cleanup(struct wlr_drm_connector *conn);
bool drm_connector_set_mode(struct wlr_drm_connector *conn, bool drm_connector_set_mode(struct wlr_drm_connector *conn,
struct wlr_output_mode *wlr_mode) { struct wlr_output_mode *wlr_mode) {
struct wlr_drm_backend *drm = struct wlr_drm_backend *drm = conn->backend;
get_drm_backend_from_backend(conn->output.backend);
conn->desired_enabled = wlr_mode != NULL; conn->desired_enabled = wlr_mode != NULL;
conn->desired_mode = wlr_mode; conn->desired_mode = wlr_mode;
@ -789,7 +775,8 @@ bool drm_connector_set_mode(struct wlr_drm_connector *conn,
if (conn->state != WLR_DRM_CONN_CONNECTED if (conn->state != WLR_DRM_CONN_CONNECTED
&& conn->state != WLR_DRM_CONN_NEEDS_MODESET) { && conn->state != WLR_DRM_CONN_NEEDS_MODESET) {
wlr_log(WLR_ERROR, "Cannot modeset a disconnected output"); wlr_drm_conn_log(conn, WLR_ERROR,
"Cannot modeset a disconnected output");
return false; return false;
} }
@ -798,18 +785,19 @@ bool drm_connector_set_mode(struct wlr_drm_connector *conn,
realloc_crtcs(drm); realloc_crtcs(drm);
} }
if (conn->crtc == NULL) { if (conn->crtc == NULL) {
wlr_log(WLR_ERROR, "Cannot modeset '%s': no CRTC for this connector", wlr_drm_conn_log(conn, WLR_ERROR,
conn->output.name); "Cannot perform modeset: no CRTC for this connector");
return false; return false;
} }
wlr_log(WLR_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", wlr_drm_conn_log(conn, WLR_INFO,
conn->output.name, wlr_mode->width, wlr_mode->height, "Modesetting with '%" PRId32 "x%" PRId32 "@%" PRId32 "mHz'",
wlr_mode->refresh); wlr_mode->width, wlr_mode->height, wlr_mode->refresh);
struct wlr_drm_mode *mode = (struct wlr_drm_mode *)wlr_mode; struct wlr_drm_mode *mode = (struct wlr_drm_mode *)wlr_mode;
if (!drm_connector_init_renderer(conn, mode)) { if (!drm_connector_init_renderer(conn, mode)) {
wlr_log(WLR_ERROR, "Failed to initialize renderer for plane"); wlr_drm_conn_log(conn, WLR_ERROR,
"Failed to initialize renderer for plane");
return false; return false;
} }
@ -852,7 +840,7 @@ struct wlr_output_mode *wlr_drm_connector_add_mode(struct wlr_output *output,
mode->wlr_mode.height = mode->drm_mode.vdisplay; mode->wlr_mode.height = mode->drm_mode.vdisplay;
mode->wlr_mode.refresh = calculate_refresh_rate(modeinfo); mode->wlr_mode.refresh = calculate_refresh_rate(modeinfo);
wlr_log(WLR_INFO, "Registered custom mode " wlr_drm_conn_log(conn, WLR_INFO, "Registered custom mode "
"%"PRId32"x%"PRId32"@%"PRId32, "%"PRId32"x%"PRId32"@%"PRId32,
mode->wlr_mode.width, mode->wlr_mode.height, mode->wlr_mode.width, mode->wlr_mode.height,
mode->wlr_mode.refresh); mode->wlr_mode.refresh);
@ -866,7 +854,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
enum wl_output_transform transform, enum wl_output_transform transform,
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); struct wlr_drm_backend *drm = conn->backend;
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) { if (!crtc) {
@ -878,7 +866,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
return false; return false;
} }
if (!plane->surf.gbm) { if (!plane->surf.swapchain) {
int ret; int ret;
uint64_t w, h; uint64_t w, h;
ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &w); ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &w);
@ -887,8 +875,8 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
h = ret ? 64 : h; h = ret ? 64 : h;
if (!drm_plane_init_surface(plane, drm, w, h, if (!drm_plane_init_surface(plane, drm, w, h,
DRM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR, false)) { DRM_FORMAT_ARGB8888, true, false)) {
wlr_log(WLR_ERROR, "Cannot allocate cursor resources"); wlr_drm_conn_log(conn, WLR_ERROR, "Cannot allocate cursor resources");
return false; return false;
} }
} }
@ -926,7 +914,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
height = height * output->scale / scale; height = height * output->scale / scale;
if (width > (int)plane->surf.width || height > (int)plane->surf.height) { if (width > (int)plane->surf.width || height > (int)plane->surf.height) {
wlr_log(WLR_ERROR, "Cursor too large (max %dx%d)", wlr_drm_conn_log(conn, WLR_ERROR, "Cursor too large (max %dx%d)",
(int)plane->surf.width, (int)plane->surf.height); (int)plane->surf.width, (int)plane->surf.height);
return false; return false;
} }
@ -954,19 +942,6 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
plane->cursor_enabled = true; plane->cursor_enabled = true;
} }
if (plane->cursor_enabled) {
drm_fb_acquire(&plane->pending_fb, drm, &plane->mgpu_surf);
/* Workaround for nouveau buffers created with GBM_BO_USER_LINEAR are
* placed in NOUVEAU_GEM_DOMAIN_GART. When the bo is attached to the
* cursor plane it is moved to NOUVEAU_GEM_DOMAIN_VRAM. However, this
* does not wait for the render operations to complete, leaving an
* empty surface. See
* https://gitlab.freedesktop.org/xorg/driver/xf86-video-nouveau/issues/480
* The render operations can be waited for using:
*/
glFinish();
}
wlr_output_update_needs_frame(output); wlr_output_update_needs_frame(output);
return true; return true;
} }
@ -991,10 +966,8 @@ static bool drm_connector_move_cursor(struct wlr_output *output,
wlr_output_transform_invert(output->transform); wlr_output_transform_invert(output->transform);
wlr_box_transform(&box, &box, transform, width, height); wlr_box_transform(&box, &box, transform, width, height);
if (plane != NULL) { box.x -= plane->cursor_hotspot_x;
box.x -= plane->cursor_hotspot_x; box.y -= plane->cursor_hotspot_y;
box.y -= plane->cursor_hotspot_y;
}
conn->cursor_x = box.x; conn->cursor_x = box.x;
conn->cursor_y = box.y; conn->cursor_y = box.y;
@ -1014,18 +987,38 @@ bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn) {
conn->cursor_y + (int)plane->surf.height >= 0; conn->cursor_y + (int)plane->surf.height >= 0;
} }
static void drm_connector_destroy(struct wlr_output *output) { static void dealloc_crtc(struct wlr_drm_connector *conn);
/**
* Destroy the compositor-facing part of a connector.
*
* The connector isn't destroyed when disconnected. Only the compositor-facing
* wlr_output interface is cleaned up.
*/
static void drm_connector_destroy_output(struct wlr_output *output) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
drm_connector_cleanup(conn);
drmModeFreeCrtc(conn->old_crtc); dealloc_crtc(conn);
wl_list_remove(&conn->link);
free(conn); conn->state = WLR_DRM_CONN_DISCONNECTED;
conn->desired_enabled = false;
conn->desired_mode = NULL;
conn->possible_crtc = 0;
conn->pageflip_pending = false;
struct wlr_drm_mode *mode, *mode_tmp;
wl_list_for_each_safe(mode, mode_tmp, &conn->output.modes, wlr_mode.link) {
wl_list_remove(&mode->wlr_mode.link);
free(mode);
}
memset(&conn->output, 0, sizeof(struct wlr_output));
} }
static const struct wlr_output_impl output_impl = { static const struct wlr_output_impl output_impl = {
.set_cursor = drm_connector_set_cursor, .set_cursor = drm_connector_set_cursor,
.move_cursor = drm_connector_move_cursor, .move_cursor = drm_connector_move_cursor,
.destroy = drm_connector_destroy, .destroy = drm_connector_destroy_output,
.attach_render = drm_connector_attach_render, .attach_render = drm_connector_attach_render,
.test = drm_connector_test, .test = drm_connector_test,
.commit = drm_connector_commit, .commit = drm_connector_commit,
@ -1048,14 +1041,13 @@ static const int32_t subpixel_map[] = {
}; };
static void dealloc_crtc(struct wlr_drm_connector *conn) { static void dealloc_crtc(struct wlr_drm_connector *conn) {
struct wlr_drm_backend *drm = struct wlr_drm_backend *drm = conn->backend;
get_drm_backend_from_backend(conn->output.backend);
if (conn->crtc == NULL) { if (conn->crtc == NULL) {
return; return;
} }
wlr_log(WLR_DEBUG, "De-allocating CRTC %zu for output '%s'", wlr_drm_conn_log(conn, WLR_DEBUG, "De-allocating CRTC %zu",
conn->crtc - drm->crtcs, conn->output.name); conn->crtc - drm->crtcs);
conn->crtc->pending_modeset = true; conn->crtc->pending_modeset = true;
conn->crtc->pending.active = false; conn->crtc->pending.active = false;
@ -1098,8 +1090,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) {
connectors[i] = conn; connectors[i] = conn;
wlr_log(WLR_DEBUG, " '%s' crtc=%d state=%d desired_enabled=%d", wlr_log(WLR_DEBUG, " '%s' crtc=%d state=%d desired_enabled=%d",
conn->output.name, conn->name, conn->crtc ? (int)(conn->crtc - drm->crtcs) : -1,
conn->crtc ? (int)(conn->crtc - drm->crtcs) : -1,
conn->state, conn->desired_enabled); conn->state, conn->desired_enabled);
if (conn->crtc) { if (conn->crtc) {
@ -1157,9 +1148,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) {
bool prev_enabled = conn->crtc; bool prev_enabled = conn->crtc;
wlr_log(WLR_DEBUG, " '%s' crtc=%zd state=%d desired_enabled=%d", wlr_log(WLR_DEBUG, " '%s' crtc=%zd state=%d desired_enabled=%d",
conn->output.name, conn->name, connector_match[i], conn->state, conn->desired_enabled);
connector_match[i],
conn->state, conn->desired_enabled);
// We don't need to change anything. // We don't need to change anything.
if (prev_enabled && connector_match[i] == conn->crtc - drm->crtcs) { if (prev_enabled && connector_match[i] == conn->crtc - drm->crtcs) {
@ -1170,8 +1159,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) {
if (connector_match[i] == -1) { if (connector_match[i] == -1) {
if (prev_enabled) { if (prev_enabled) {
wlr_log(WLR_DEBUG, "Output has %s lost its CRTC", wlr_drm_conn_log(conn, WLR_DEBUG, "Output has lost its CRTC");
conn->output.name);
conn->state = WLR_DRM_CONN_NEEDS_MODESET; conn->state = WLR_DRM_CONN_NEEDS_MODESET;
wlr_output_update_enabled(&conn->output, false); wlr_output_update_enabled(&conn->output, false);
conn->desired_mode = conn->output.current_mode; conn->desired_mode = conn->output.current_mode;
@ -1190,8 +1178,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) {
struct wlr_drm_mode *mode = struct wlr_drm_mode *mode =
(struct wlr_drm_mode *)conn->output.current_mode; (struct wlr_drm_mode *)conn->output.current_mode;
if (!drm_connector_init_renderer(conn, mode)) { if (!drm_connector_init_renderer(conn, mode)) {
wlr_log(WLR_ERROR, "Failed to initialize renderer on output %s", wlr_drm_conn_log(conn, WLR_ERROR, "Failed to initialize renderer");
conn->output.name);
wlr_output_update_enabled(&conn->output, false); wlr_output_update_enabled(&conn->output, false);
continue; continue;
} }
@ -1201,8 +1188,8 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) {
} }
static uint32_t get_possible_crtcs(int fd, drmModeRes *res, static uint32_t get_possible_crtcs(int fd, drmModeRes *res,
drmModeConnector *conn, bool is_mst) { drmModeConnector *conn) {
uint32_t ret = 0; uint32_t possible_crtcs = 0;
for (int i = 0; i < conn->count_encoders; ++i) { for (int i = 0; i < conn->count_encoders; ++i) {
drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]); drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]);
@ -1210,35 +1197,16 @@ static uint32_t get_possible_crtcs(int fd, drmModeRes *res,
continue; continue;
} }
ret |= enc->possible_crtcs; possible_crtcs |= enc->possible_crtcs;
drmModeFreeEncoder(enc); drmModeFreeEncoder(enc);
} }
// Sometimes DP MST connectors report no encoders, so we'll loop though return possible_crtcs;
// all of the encoders of the MST type instead.
// TODO: See if there is a better solution.
if (!is_mst || ret) {
return ret;
}
for (int i = 0; i < res->count_encoders; ++i) {
drmModeEncoder *enc = drmModeGetEncoder(fd, res->encoders[i]);
if (!enc) {
continue;
}
if (enc->encoder_type == DRM_MODE_ENCODER_DPMST) {
ret |= enc->possible_crtcs;
}
drmModeFreeEncoder(enc);
}
return ret;
} }
static void disconnect_drm_connector(struct wlr_drm_connector *conn);
void scan_drm_connectors(struct wlr_drm_backend *drm) { void scan_drm_connectors(struct wlr_drm_backend *drm) {
/* /*
* This GPU is not really a modesetting device. * This GPU is not really a modesetting device.
@ -1248,7 +1216,7 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
return; return;
} }
wlr_log(WLR_INFO, "Scanning DRM connectors"); wlr_log(WLR_INFO, "Scanning DRM connectors on %s", drm->name);
drmModeRes *res = drmModeGetResources(drm->fd); drmModeRes *res = drmModeGetResources(drm->fd);
if (!res) { if (!res) {
@ -1292,13 +1260,12 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
drmModeFreeConnector(drm_conn); drmModeFreeConnector(drm_conn);
continue; continue;
} }
wlr_output_init(&wlr_conn->output, &drm->backend, &output_impl,
drm->display);
wlr_conn->backend = drm;
wlr_conn->state = WLR_DRM_CONN_DISCONNECTED; wlr_conn->state = WLR_DRM_CONN_DISCONNECTED;
wlr_conn->id = drm_conn->connector_id; wlr_conn->id = drm_conn->connector_id;
snprintf(wlr_conn->output.name, sizeof(wlr_conn->output.name), snprintf(wlr_conn->name, sizeof(wlr_conn->name),
"%s-%"PRIu32, conn_get_name(drm_conn->connector_type), "%s-%"PRIu32, conn_get_name(drm_conn->connector_type),
drm_conn->connector_type_id); drm_conn->connector_type_id);
@ -1307,7 +1274,7 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
} }
wl_list_insert(drm->outputs.prev, &wlr_conn->link); wl_list_insert(drm->outputs.prev, &wlr_conn->link);
wlr_log(WLR_INFO, "Found connector '%s'", wlr_conn->output.name); wlr_log(WLR_INFO, "Found connector '%s'", wlr_conn->name);
} else { } else {
seen[index] = true; seen[index] = true;
} }
@ -1329,24 +1296,30 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
uint64_t link_status; uint64_t link_status;
if (!get_drm_prop(drm->fd, wlr_conn->id, if (!get_drm_prop(drm->fd, wlr_conn->id,
wlr_conn->props.link_status, &link_status)) { wlr_conn->props.link_status, &link_status)) {
wlr_log(WLR_ERROR, "Failed to get link status for '%s'", wlr_drm_conn_log(wlr_conn, WLR_ERROR,
wlr_conn->output.name); "Failed to get link status prop");
continue; continue;
} }
if (link_status == DRM_MODE_LINK_STATUS_BAD) { if (link_status == DRM_MODE_LINK_STATUS_BAD) {
// We need to reload our list of modes and force a modeset // We need to reload our list of modes and force a modeset
wlr_log(WLR_INFO, "Bad link for '%s'", wlr_conn->output.name); wlr_drm_conn_log(wlr_conn, WLR_INFO, "Bad link detected");
drm_connector_cleanup(wlr_conn); disconnect_drm_connector(wlr_conn);
} }
} }
if (wlr_conn->state == WLR_DRM_CONN_DISCONNECTED && if (wlr_conn->state == WLR_DRM_CONN_DISCONNECTED &&
drm_conn->connection == DRM_MODE_CONNECTED) { drm_conn->connection == DRM_MODE_CONNECTED) {
wlr_log(WLR_INFO, "'%s' connected", wlr_conn->output.name); wlr_log(WLR_INFO, "'%s' connected", wlr_conn->name);
wlr_log(WLR_DEBUG, "Current CRTC: %d", wlr_log(WLR_DEBUG, "Current CRTC: %d",
wlr_conn->crtc ? (int)wlr_conn->crtc->id : -1); wlr_conn->crtc ? (int)wlr_conn->crtc->id : -1);
wlr_output_init(&wlr_conn->output, &drm->backend, &output_impl,
drm->display);
memcpy(wlr_conn->output.name, wlr_conn->name,
sizeof(wlr_conn->output.name));
wlr_conn->output.phys_width = drm_conn->mmWidth; wlr_conn->output.phys_width = drm_conn->mmWidth;
wlr_conn->output.phys_height = drm_conn->mmHeight; wlr_conn->output.phys_height = drm_conn->mmHeight;
wlr_log(WLR_INFO, "Physical size: %"PRId32"x%"PRId32, wlr_log(WLR_INFO, "Physical size: %"PRId32"x%"PRId32,
@ -1397,20 +1370,9 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
wl_list_insert(&wlr_conn->output.modes, &mode->wlr_mode.link); wl_list_insert(&wlr_conn->output.modes, &mode->wlr_mode.link);
} }
size_t path_len; wlr_conn->possible_crtc = get_possible_crtcs(drm->fd, res, drm_conn);
bool is_mst = false;
char *path = get_drm_prop_blob(drm->fd, wlr_conn->id,
wlr_conn->props.path, &path_len);
if (path_len > 4 && path && strncmp(path, "mst:", 4) == 0) {
is_mst = true;
}
free(path);
wlr_conn->possible_crtc = get_possible_crtcs(drm->fd, res, drm_conn,
is_mst);
if (wlr_conn->possible_crtc == 0) { if (wlr_conn->possible_crtc == 0) {
wlr_log(WLR_ERROR, "No CRTC possible for connector '%s'", wlr_drm_conn_log(wlr_conn, WLR_ERROR, "No CRTC possible");
wlr_conn->output.name);
} }
// TODO: this results in connectors being enabled without a mode // TODO: this results in connectors being enabled without a mode
@ -1423,9 +1385,8 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
} else if ((wlr_conn->state == WLR_DRM_CONN_CONNECTED || } else if ((wlr_conn->state == WLR_DRM_CONN_CONNECTED ||
wlr_conn->state == WLR_DRM_CONN_NEEDS_MODESET) && wlr_conn->state == WLR_DRM_CONN_NEEDS_MODESET) &&
drm_conn->connection != DRM_MODE_CONNECTED) { drm_conn->connection != DRM_MODE_CONNECTED) {
wlr_log(WLR_INFO, "'%s' disconnected", wlr_conn->output.name); wlr_log(WLR_INFO, "'%s' disconnected", wlr_conn->name);
disconnect_drm_connector(wlr_conn);
drm_connector_cleanup(wlr_conn);
} }
drmModeFreeEncoder(curr_enc); drmModeFreeEncoder(curr_enc);
@ -1444,10 +1405,8 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
continue; continue;
} }
wlr_log(WLR_INFO, "'%s' disappeared", conn->output.name); wlr_log(WLR_INFO, "'%s' disappeared", conn->name);
drm_connector_cleanup(conn); disconnect_drm_connector(conn);
wlr_output_destroy(&conn->output);
} }
realloc_crtcs(drm); realloc_crtcs(drm);
@ -1455,8 +1414,7 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
for (size_t i = 0; i < new_outputs_len; ++i) { for (size_t i = 0; i < new_outputs_len; ++i) {
struct wlr_drm_connector *conn = new_outputs[i]; struct wlr_drm_connector *conn = new_outputs[i];
wlr_log(WLR_INFO, "Requesting modeset for '%s'", wlr_drm_conn_log(conn, WLR_INFO, "Requesting modeset");
conn->output.name);
wlr_signal_emit_safe(&drm->backend.events.new_output, wlr_signal_emit_safe(&drm->backend.events.new_output,
&conn->output); &conn->output);
} }
@ -1481,7 +1439,7 @@ static void page_flip_handler(int fd, unsigned seq,
} }
} }
if (!conn) { if (!conn) {
wlr_log(WLR_DEBUG, "No connector for crtc_id %u", crtc_id); wlr_log(WLR_DEBUG, "No connector for CRTC %u", crtc_id);
return; return;
} }
@ -1492,11 +1450,10 @@ static void page_flip_handler(int fd, unsigned seq,
} }
struct wlr_drm_plane *plane = conn->crtc->primary; struct wlr_drm_plane *plane = conn->crtc->primary;
if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) { if (plane->queued_fb.bo) {
drm_fb_move(&plane->current_fb, &plane->queued_fb); drm_fb_move(&plane->current_fb, &plane->queued_fb);
} }
if (conn->crtc->cursor && if (conn->crtc->cursor && conn->crtc->cursor->queued_fb.bo) {
conn->crtc->cursor->queued_fb.type != WLR_DRM_FB_TYPE_NONE) {
drm_fb_move(&conn->crtc->cursor->current_fb, drm_fb_move(&conn->crtc->cursor->current_fb,
&conn->crtc->cursor->queued_fb); &conn->crtc->cursor->queued_fb);
} }
@ -1507,7 +1464,8 @@ static void page_flip_handler(int fd, unsigned seq,
* data between the GPUs, even if we were using the direct scanout * data between the GPUs, even if we were using the direct scanout
* interface. * interface.
*/ */
if (!drm->parent && plane->current_fb.type == WLR_DRM_FB_TYPE_WLR_BUFFER) { if (!drm->parent && plane->current_fb.wlr_buf &&
wlr_client_buffer_get(plane->current_fb.wlr_buf)) {
present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY;
} }
@ -1526,7 +1484,7 @@ static void page_flip_handler(int fd, unsigned seq,
}; };
wlr_output_send_present(&conn->output, &present_event); wlr_output_send_present(&conn->output, &present_event);
if (drm->session->active) { if (drm->session->active && conn->output.enabled) {
wlr_output_send_frame(&conn->output); wlr_output_send_frame(&conn->output);
} }
} }
@ -1581,49 +1539,20 @@ void restore_drm_outputs(struct wlr_drm_backend *drm) {
} }
} }
static void drm_connector_cleanup(struct wlr_drm_connector *conn) { static void disconnect_drm_connector(struct wlr_drm_connector *conn) {
if (!conn) { if (conn->state == WLR_DRM_CONN_DISCONNECTED) {
return; return;
} }
switch (conn->state) { // This will cleanup the compositor-facing wlr_output, but won't destroy
case WLR_DRM_CONN_CONNECTED: // our wlr_drm_connector.
case WLR_DRM_CONN_CLEANUP: wlr_output_destroy(&conn->output);
conn->output.current_mode = NULL; }
conn->desired_mode = NULL;
struct wlr_drm_mode *mode, *tmp; void destroy_drm_connector(struct wlr_drm_connector *conn) {
wl_list_for_each_safe(mode, tmp, &conn->output.modes, wlr_mode.link) { disconnect_drm_connector(conn);
wl_list_remove(&mode->wlr_mode.link);
free(mode); drmModeFreeCrtc(conn->old_crtc);
} wl_list_remove(&conn->link);
free(conn);
conn->output.enabled = false;
conn->output.width = conn->output.height = conn->output.refresh = 0;
memset(&conn->output.make, 0, sizeof(conn->output.make));
memset(&conn->output.model, 0, sizeof(conn->output.model));
memset(&conn->output.serial, 0, sizeof(conn->output.serial));
if (conn->output.idle_frame != NULL) {
wl_event_source_remove(conn->output.idle_frame);
conn->output.idle_frame = NULL;
}
conn->output.needs_frame = false;
conn->output.frame_pending = false;
/* Fallthrough */
case WLR_DRM_CONN_NEEDS_MODESET:
wlr_log(WLR_INFO, "Emitting destruction signal for '%s'",
conn->output.name);
dealloc_crtc(conn);
conn->possible_crtc = 0;
conn->desired_mode = NULL;
conn->pageflip_pending = false;
wlr_signal_emit_safe(&conn->output.events.destroy, &conn->output);
break;
case WLR_DRM_CONN_DISCONNECTED:
break;
}
conn->state = WLR_DRM_CONN_DISCONNECTED;
} }

View file

@ -42,15 +42,14 @@ static bool legacy_crtc_commit(struct wlr_drm_backend *drm,
DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF; DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
if (drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms, if (drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms,
dpms) != 0) { dpms) != 0) {
wlr_log_errno(WLR_ERROR, "%s: failed to set DPMS property", wlr_drm_conn_log_errno(conn, WLR_ERROR,
conn->output.name); "Failed to set DPMS property");
return false; return false;
} }
if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0, if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0,
conns, conns_len, mode)) { conns, conns_len, mode)) {
wlr_log_errno(WLR_ERROR, "%s: failed to set CRTC", wlr_drm_conn_log_errno(conn, WLR_ERROR, "Failed to set CRTC");
conn->output.name);
return false; return false;
} }
} }
@ -67,16 +66,15 @@ static bool legacy_crtc_commit(struct wlr_drm_backend *drm,
if (drmModeObjectSetProperty(drm->fd, crtc->id, DRM_MODE_OBJECT_CRTC, if (drmModeObjectSetProperty(drm->fd, crtc->id, DRM_MODE_OBJECT_CRTC,
crtc->props.vrr_enabled, crtc->props.vrr_enabled,
output->pending.adaptive_sync_enabled) != 0) { output->pending.adaptive_sync_enabled) != 0) {
wlr_log_errno(WLR_ERROR, wlr_drm_conn_log_errno(conn, WLR_ERROR,
"drmModeObjectSetProperty(VRR_ENABLED) failed"); "drmModeObjectSetProperty(VRR_ENABLED) failed");
return false; return false;
} }
output->adaptive_sync_status = output->pending.adaptive_sync_enabled ? output->adaptive_sync_status = output->pending.adaptive_sync_enabled ?
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED :
WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
wlr_log(WLR_DEBUG, "VRR %s on connector '%s'", wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s",
output->pending.adaptive_sync_enabled ? "enabled" : "disabled", output->pending.adaptive_sync_enabled ? "enabled" : "disabled");
output->name);
} }
if (cursor != NULL && drm_connector_is_cursor_visible(conn)) { if (cursor != NULL && drm_connector_is_cursor_visible(conn)) {
@ -84,29 +82,26 @@ static bool legacy_crtc_commit(struct wlr_drm_backend *drm,
struct gbm_bo *cursor_bo = struct gbm_bo *cursor_bo =
drm_fb_acquire(cursor_fb, drm, &cursor->mgpu_surf); drm_fb_acquire(cursor_fb, drm, &cursor->mgpu_surf);
if (!cursor_bo) { if (!cursor_bo) {
wlr_log_errno(WLR_DEBUG, "%s: failed to acquire cursor FB", wlr_drm_conn_log_errno(conn, WLR_DEBUG,
conn->output.name); "Failed to acquire cursor FB");
return false; return false;
} }
if (drmModeSetCursor(drm->fd, crtc->id, if (drmModeSetCursor(drm->fd, crtc->id,
gbm_bo_get_handle(cursor_bo).u32, gbm_bo_get_handle(cursor_bo).u32,
cursor->surf.width, cursor->surf.height)) { cursor->surf.width, cursor->surf.height)) {
wlr_log_errno(WLR_DEBUG, "%s: failed to set hardware cursor", wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed");
conn->output.name);
return false; return false;
} }
if (drmModeMoveCursor(drm->fd, if (drmModeMoveCursor(drm->fd,
crtc->id, conn->cursor_x, conn->cursor_y) != 0) { crtc->id, conn->cursor_x, conn->cursor_y) != 0) {
wlr_log_errno(WLR_ERROR, "%s: failed to move cursor", wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModeMoveCursor failed");
conn->output.name);
return false; return false;
} }
} else { } else {
if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) { if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) {
wlr_log_errno(WLR_DEBUG, "%s: failed to unset hardware cursor", wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed");
conn->output.name);
return false; return false;
} }
} }
@ -114,7 +109,7 @@ static bool legacy_crtc_commit(struct wlr_drm_backend *drm,
if (flags & DRM_MODE_PAGE_FLIP_EVENT) { if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
if (drmModePageFlip(drm->fd, crtc->id, fb_id, if (drmModePageFlip(drm->fd, crtc->id, fb_id,
DRM_MODE_PAGE_FLIP_EVENT, drm)) { DRM_MODE_PAGE_FLIP_EVENT, drm)) {
wlr_log_errno(WLR_ERROR, "%s: Failed to page flip", conn->output.name); wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModePageFlip failed");
return false; return false;
} }
} }

View file

@ -35,8 +35,6 @@ static const struct prop_info crtc_info[] = {
{ "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) }, { "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) },
{ "MODE_ID", INDEX(mode_id) }, { "MODE_ID", INDEX(mode_id) },
{ "VRR_ENABLED", INDEX(vrr_enabled) }, { "VRR_ENABLED", INDEX(vrr_enabled) },
{ "rotation", INDEX(rotation) },
{ "scaling mode", INDEX(scaling_mode) },
#undef INDEX #undef INDEX
}; };
@ -53,6 +51,7 @@ static const struct prop_info plane_info[] = {
{ "SRC_W", INDEX(src_w) }, { "SRC_W", INDEX(src_w) },
{ "SRC_X", INDEX(src_x) }, { "SRC_X", INDEX(src_x) },
{ "SRC_Y", INDEX(src_y) }, { "SRC_Y", INDEX(src_y) },
{ "rotation", INDEX(rotation) },
{ "type", INDEX(type) }, { "type", INDEX(type) },
#undef INDEX #undef INDEX
}; };

View file

@ -1,5 +1,7 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h> #include <assert.h>
#include <drm_fourcc.h> #include <drm_fourcc.h>
#include <fcntl.h>
#include <gbm.h> #include <gbm.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
@ -12,9 +14,14 @@
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "backend/drm/drm.h" #include "backend/drm/drm.h"
#include "render/drm_format_set.h"
#include "render/gbm_allocator.h"
#include "render/swapchain.h"
#include "render/wlr_renderer.h"
bool init_drm_renderer(struct wlr_drm_backend *drm, bool init_drm_renderer(struct wlr_drm_backend *drm,
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_renderer_func) { struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_renderer_func) {
// TODO: get rid of renderer->gbm
renderer->gbm = gbm_create_device(drm->fd); renderer->gbm = gbm_create_device(drm->fd);
if (!renderer->gbm) { if (!renderer->gbm) {
wlr_log(WLR_ERROR, "Failed to create GBM device"); wlr_log(WLR_ERROR, "Failed to create GBM device");
@ -25,29 +32,31 @@ bool init_drm_renderer(struct wlr_drm_backend *drm,
create_renderer_func = wlr_renderer_autocreate; create_renderer_func = wlr_renderer_autocreate;
} }
static EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 1,
EGL_NONE,
};
// TODO: allow setting from sway config/args. Have sane default set.
renderer->gbm_format = GBM_FORMAT_ARGB2101010;
renderer->wlr_rend = create_renderer_func(&renderer->egl, renderer->wlr_rend = create_renderer_func(&renderer->egl,
EGL_PLATFORM_GBM_MESA, renderer->gbm, EGL_PLATFORM_GBM_KHR, renderer->gbm, NULL, 0);
config_attribs, renderer->gbm_format);
if (!renderer->wlr_rend) { if (!renderer->wlr_rend) {
wlr_log(WLR_ERROR, "Failed to create EGL/WLR renderer"); wlr_log(WLR_ERROR, "Failed to create EGL/WLR renderer");
goto error_gbm; goto error_gbm;
} }
int alloc_fd = fcntl(drm->fd, F_DUPFD_CLOEXEC, 0);
if (alloc_fd < 0) {
wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed");
goto error_wlr_rend;
}
renderer->allocator = wlr_gbm_allocator_create(alloc_fd);
if (renderer->allocator == NULL) {
wlr_log(WLR_ERROR, "Failed to create allocator");
close(alloc_fd);
goto error_wlr_rend;
}
renderer->fd = drm->fd; renderer->fd = drm->fd;
return true; return true;
error_wlr_rend:
wlr_renderer_destroy(renderer->wlr_rend);
error_gbm: error_gbm:
gbm_device_destroy(renderer->gbm); gbm_device_destroy(renderer->gbm);
return false; return false;
@ -58,6 +67,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
return; return;
} }
wlr_allocator_destroy(&renderer->allocator->base);
wlr_renderer_destroy(renderer->wlr_rend); wlr_renderer_destroy(renderer->wlr_rend);
wlr_egl_finish(&renderer->egl); wlr_egl_finish(&renderer->egl);
gbm_device_destroy(renderer->gbm); gbm_device_destroy(renderer->gbm);
@ -65,7 +75,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
static bool init_drm_surface(struct wlr_drm_surface *surf, static bool init_drm_surface(struct wlr_drm_surface *surf,
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height, struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
uint32_t format, struct wlr_drm_format_set *set, uint32_t flags) { const struct wlr_drm_format *drm_format) {
if (surf->width == width && surf->height == height) { if (surf->width == width && surf->height == height) {
return true; return true;
} }
@ -74,43 +84,20 @@ static bool init_drm_surface(struct wlr_drm_surface *surf,
surf->width = width; surf->width = width;
surf->height = height; surf->height = height;
if (surf->gbm) { wlr_buffer_unlock(surf->back_buffer);
gbm_surface_destroy(surf->gbm); surf->back_buffer = NULL;
surf->gbm = NULL; wlr_swapchain_destroy(surf->swapchain);
} surf->swapchain = NULL;
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
if (!(flags & GBM_BO_USE_LINEAR) && set != NULL) { surf->swapchain = wlr_swapchain_create(&renderer->allocator->base,
const struct wlr_drm_format *drm_format = width, height, drm_format);
wlr_drm_format_set_get(set, format); if (surf->swapchain == NULL) {
if (drm_format != NULL) { wlr_log(WLR_ERROR, "Failed to create swapchain");
surf->gbm = gbm_surface_create_with_modifiers(renderer->gbm, memset(surf, 0, sizeof(*surf));
width, height, format, drm_format->modifiers, drm_format->len); return false;
}
}
if (surf->gbm == NULL) {
surf->gbm = gbm_surface_create(renderer->gbm, width, height,
format, GBM_BO_USE_RENDERING | flags);
}
if (!surf->gbm) {
wlr_log_errno(WLR_ERROR, "Failed to create GBM surface");
goto error_zero;
}
surf->egl = wlr_egl_create_surface(&renderer->egl, surf->gbm);
if (surf->egl == EGL_NO_SURFACE) {
wlr_log(WLR_ERROR, "Failed to create EGL surface");
goto error_gbm;
} }
return true; return true;
error_gbm:
gbm_surface_destroy(surf->gbm);
error_zero:
memset(surf, 0, sizeof(*surf));
return false;
} }
static void finish_drm_surface(struct wlr_drm_surface *surf) { static void finish_drm_surface(struct wlr_drm_surface *surf) {
@ -118,17 +105,40 @@ static void finish_drm_surface(struct wlr_drm_surface *surf) {
return; return;
} }
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl); wlr_buffer_unlock(surf->back_buffer);
if (surf->gbm) { wlr_swapchain_destroy(surf->swapchain);
gbm_surface_destroy(surf->gbm);
}
memset(surf, 0, sizeof(*surf)); memset(surf, 0, sizeof(*surf));
} }
bool drm_surface_make_current(struct wlr_drm_surface *surf, bool drm_surface_make_current(struct wlr_drm_surface *surf,
int *buffer_age) { int *buffer_age) {
return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_age); wlr_buffer_unlock(surf->back_buffer);
surf->back_buffer = wlr_swapchain_acquire(surf->swapchain, buffer_age);
if (surf->back_buffer == NULL) {
wlr_log(WLR_ERROR, "Failed to acquire swapchain buffer");
return false;
}
if (!wlr_egl_make_current(&surf->renderer->egl, EGL_NO_SURFACE, NULL)) {
return false;
}
if (!wlr_renderer_bind_buffer(surf->renderer->wlr_rend, surf->back_buffer)) {
wlr_log(WLR_ERROR, "Failed to attach buffer to renderer");
return false;
}
return true;
}
void drm_surface_unset_current(struct wlr_drm_surface *surf) {
assert(surf->back_buffer != NULL);
wlr_renderer_bind_buffer(surf->renderer->wlr_rend, NULL);
wlr_egl_unset_current(&surf->renderer->egl);
wlr_buffer_unlock(surf->back_buffer);
surf->back_buffer = NULL;
} }
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) { bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) {
@ -203,8 +213,6 @@ static uint32_t strip_alpha_channel(uint32_t format) {
switch (format) { switch (format) {
case DRM_FORMAT_ARGB8888: case DRM_FORMAT_ARGB8888:
return DRM_FORMAT_XRGB8888; return DRM_FORMAT_XRGB8888;
case DRM_FORMAT_ARGB2101010:
return DRM_FORMAT_XRGB2101010;
default: default:
return DRM_FORMAT_INVALID; return DRM_FORMAT_INVALID;
} }
@ -212,85 +220,117 @@ static uint32_t strip_alpha_channel(uint32_t format) {
bool drm_plane_init_surface(struct wlr_drm_plane *plane, bool drm_plane_init_surface(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height, struct wlr_drm_backend *drm, int32_t width, uint32_t height,
uint32_t format, uint32_t flags, bool with_modifiers) { uint32_t format, bool force_linear, bool with_modifiers) {
if (!wlr_drm_format_set_has(&plane->formats, format, DRM_FORMAT_MOD_INVALID)) { if (!wlr_drm_format_set_has(&plane->formats, format, DRM_FORMAT_MOD_INVALID)) {
format = strip_alpha_channel(format); format = strip_alpha_channel(format);
} }
if (!wlr_drm_format_set_has(&plane->formats, format, DRM_FORMAT_MOD_INVALID)) { const struct wlr_drm_format *plane_format =
wlr_drm_format_set_get(&plane->formats, format);
if (plane_format == NULL) {
wlr_log(WLR_ERROR, "Plane %"PRIu32" doesn't support format 0x%"PRIX32, wlr_log(WLR_ERROR, "Plane %"PRIu32" doesn't support format 0x%"PRIX32,
plane->id, format); plane->id, format);
return false; return false;
} }
struct wlr_drm_format_set *format_set = const struct wlr_drm_format_set *render_formats =
with_modifiers ? &plane->formats : NULL; wlr_renderer_get_dmabuf_render_formats(drm->renderer.wlr_rend);
if (render_formats == NULL) {
wlr_log(WLR_ERROR, "Failed to get render formats");
return false;
}
const struct wlr_drm_format *render_format =
wlr_drm_format_set_get(render_formats, format);
if (render_format == NULL) {
wlr_log(WLR_ERROR, "Renderer doesn't support format 0x%"PRIX32,
format);
return false;
}
struct wlr_drm_format *drm_format = NULL;
if (with_modifiers) {
drm_format = wlr_drm_format_intersect(plane_format, render_format);
if (drm_format == NULL) {
wlr_log(WLR_ERROR,
"Failed to intersect plane and render formats 0x%"PRIX32,
format);
return false;
}
} else {
drm_format = wlr_drm_format_create(format);
}
struct wlr_drm_format *drm_format_linear = wlr_drm_format_create(format);
if (drm_format_linear == NULL) {
free(drm_format);
return false;
}
if (!wlr_drm_format_add(&drm_format_linear, DRM_FORMAT_MOD_LINEAR)) {
free(drm_format_linear);
free(drm_format);
return false;
}
if (force_linear) {
free(drm_format);
drm_format = wlr_drm_format_dup(drm_format_linear);
}
drm_plane_finish_surface(plane); drm_plane_finish_surface(plane);
bool ok = true;
if (!drm->parent) { if (!drm->parent) {
return init_drm_surface(&plane->surf, &drm->renderer, width, height, ok = init_drm_surface(&plane->surf, &drm->renderer,
format, format_set, flags | GBM_BO_USE_SCANOUT); width, height, drm_format);
} else {
ok = init_drm_surface(&plane->surf, &drm->parent->renderer,
width, height, drm_format_linear);
if (ok && !init_drm_surface(&plane->mgpu_surf, &drm->renderer,
width, height, drm_format)) {
finish_drm_surface(&plane->surf);
ok = false;
}
} }
if (!init_drm_surface(&plane->surf, &drm->parent->renderer, free(drm_format_linear);
width, height, format, NULL, free(drm_format);
flags | GBM_BO_USE_LINEAR)) {
return false;
}
if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer, return ok;
width, height, format, format_set,
flags | GBM_BO_USE_SCANOUT)) {
finish_drm_surface(&plane->surf);
return false;
}
return true;
} }
void drm_fb_clear(struct wlr_drm_fb *fb) { void drm_fb_clear(struct wlr_drm_fb *fb) {
switch (fb->type) { if (!fb->bo) {
case WLR_DRM_FB_TYPE_NONE: assert(!fb->wlr_buf);
assert(!fb->bo); return;
break;
case WLR_DRM_FB_TYPE_SURFACE:
gbm_surface_release_buffer(fb->surf->gbm, fb->bo);
break;
case WLR_DRM_FB_TYPE_WLR_BUFFER:
gbm_bo_destroy(fb->bo);
wlr_buffer_unlock(fb->wlr_buf);
fb->wlr_buf = NULL;
break;
} }
fb->type = WLR_DRM_FB_TYPE_NONE; gbm_bo_destroy(fb->bo);
wlr_buffer_unlock(fb->wlr_buf);
fb->wlr_buf = NULL;
fb->bo = NULL; fb->bo = NULL;
if (fb->mgpu_bo) { if (fb->mgpu_bo) {
assert(fb->mgpu_surf); assert(fb->mgpu_surf);
gbm_surface_release_buffer(fb->mgpu_surf->gbm, fb->mgpu_bo); gbm_bo_destroy(fb->mgpu_bo);
wlr_buffer_unlock(fb->mgpu_wlr_buf);
fb->mgpu_bo = NULL; fb->mgpu_bo = NULL;
fb->mgpu_wlr_buf = NULL;
fb->mgpu_surf = NULL; fb->mgpu_surf = NULL;
} }
} }
bool drm_fb_lock_surface(struct wlr_drm_fb *fb, struct wlr_drm_surface *surf) { bool drm_fb_lock_surface(struct wlr_drm_fb *fb, struct wlr_drm_surface *surf) {
drm_fb_clear(fb); assert(surf->back_buffer != NULL);
if (!wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, NULL)) { struct wlr_buffer *buffer = wlr_buffer_lock(surf->back_buffer);
wlr_log(WLR_ERROR, "Failed to swap buffers");
return false;
}
fb->bo = gbm_surface_lock_front_buffer(surf->gbm); // Unset the current EGL context ASAP, because other operations may require
if (!fb->bo) { // making another context current.
wlr_log(WLR_ERROR, "Failed to lock front buffer"); drm_surface_unset_current(surf);
return false;
}
fb->type = WLR_DRM_FB_TYPE_SURFACE; bool ok = drm_fb_import_wlr(fb, surf->renderer, buffer, NULL);
fb->surf = surf; wlr_buffer_unlock(buffer);
return true; return ok;
} }
bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer, bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer,
@ -302,7 +342,7 @@ bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer,
return false; return false;
} }
if (!wlr_drm_format_set_has(set, attribs.format, attribs.modifier)) { if (set && !wlr_drm_format_set_has(set, attribs.format, attribs.modifier)) {
// The format isn't supported by the plane. Try stripping the alpha // The format isn't supported by the plane. Try stripping the alpha
// channel, if any. // channel, if any.
uint32_t format = strip_alpha_channel(attribs.format); uint32_t format = strip_alpha_channel(attribs.format);
@ -352,7 +392,6 @@ bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer,
return false; return false;
} }
fb->type = WLR_DRM_FB_TYPE_WLR_BUFFER;
fb->wlr_buf = wlr_buffer_lock(buf); fb->wlr_buf = wlr_buffer_lock(buf);
return true; return true;
@ -413,17 +452,15 @@ struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm
wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f); wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
wlr_renderer_end(renderer); wlr_renderer_end(renderer);
if (!wlr_egl_swap_buffers(&mgpu->renderer->egl, mgpu->egl, NULL)) { struct wlr_drm_fb mgpu_fb = {
wlr_log(WLR_ERROR, "Failed to swap buffers"); .bo = fb->mgpu_bo,
return NULL; .wlr_buf = fb->mgpu_wlr_buf,
};
if (!drm_fb_lock_surface(&mgpu_fb, mgpu)) {
return false;
} }
fb->mgpu_bo = mgpu_fb.bo;
fb->mgpu_bo = gbm_surface_lock_front_buffer(mgpu->gbm); fb->mgpu_wlr_buf = mgpu_fb.wlr_buf;
if (!fb->mgpu_bo) {
wlr_log(WLR_ERROR, "Failed to lock front buffer");
return NULL;
}
fb->mgpu_surf = mgpu; fb->mgpu_surf = mgpu;
return fb->mgpu_bo; return fb->mgpu_bo;
} }

View file

@ -221,7 +221,7 @@ uint32_t get_fb_for_bo(struct gbm_bo *bo, bool with_modifiers) {
return id; return id;
} }
static inline bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) { static bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) {
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
if (arr[i] == key) { if (arr[i] == key) {
return true; return true;

View file

@ -1,12 +1,20 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h> #include <assert.h>
#include <GLES2/gl2.h> #include <drm_fourcc.h>
#include <GLES2/gl2ext.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <wlr/interfaces/wlr_input_device.h> #include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_output.h> #include <wlr/interfaces/wlr_output.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/render/gles2.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <xf86drm.h>
#include "backend/headless.h" #include "backend/headless.h"
#include "render/drm_format_set.h"
#include "render/gbm_allocator.h"
#include "render/wlr_renderer.h"
#include "util/signal.h" #include "util/signal.h"
struct wlr_headless_backend *headless_backend_from_backend( struct wlr_headless_backend *headless_backend_from_backend(
@ -62,10 +70,12 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend); wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
free(backend->format);
if (backend->egl == &backend->priv_egl) { if (backend->egl == &backend->priv_egl) {
wlr_renderer_destroy(backend->renderer); wlr_renderer_destroy(backend->renderer);
wlr_egl_finish(&backend->priv_egl); wlr_egl_finish(&backend->priv_egl);
} }
wlr_allocator_destroy(backend->allocator);
free(backend); free(backend);
} }
@ -95,26 +105,30 @@ static void handle_renderer_destroy(struct wl_listener *listener, void *data) {
} }
static bool backend_init(struct wlr_headless_backend *backend, static bool backend_init(struct wlr_headless_backend *backend,
struct wl_display *display, struct wlr_renderer *renderer) { struct wl_display *display, struct wlr_allocator *allocator,
struct wlr_renderer *renderer) {
wlr_backend_init(&backend->backend, &backend_impl); wlr_backend_init(&backend->backend, &backend_impl);
backend->display = display; backend->display = display;
wl_list_init(&backend->outputs); wl_list_init(&backend->outputs);
wl_list_init(&backend->input_devices); wl_list_init(&backend->input_devices);
backend->allocator = allocator;
backend->renderer = renderer; backend->renderer = renderer;
backend->egl = wlr_gles2_renderer_get_egl(renderer); backend->egl = wlr_gles2_renderer_get_egl(renderer);
if (wlr_gles2_renderer_check_ext(backend->renderer, "GL_OES_rgb8_rgba8") || const struct wlr_drm_format_set *formats =
wlr_gles2_renderer_check_ext(backend->renderer, wlr_renderer_get_dmabuf_render_formats(backend->renderer);
"GL_OES_required_internalformat") || if (formats == NULL) {
wlr_gles2_renderer_check_ext(backend->renderer, "GL_ARM_rgba8")) { wlr_log(WLR_ERROR, "Failed to get available DMA-BUF formats from renderer");
backend->internal_format = GL_RGBA8_OES; return false;
} else {
wlr_log(WLR_INFO, "GL_RGBA8_OES not supported, "
"falling back to GL_RGBA4 internal format "
"(performance may be affected)");
backend->internal_format = GL_RGBA4;
} }
const struct wlr_drm_format *format =
wlr_drm_format_set_get(formats, DRM_FORMAT_XRGB8888);
if (format == NULL) {
wlr_log(WLR_ERROR, "Renderer doesn't support XRGB8888");
return false;
}
backend->format = wlr_drm_format_dup(format);
backend->display_destroy.notify = handle_display_destroy; backend->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &backend->display_destroy); wl_display_add_destroy_listener(display, &backend->display_destroy);
@ -124,68 +138,144 @@ static bool backend_init(struct wlr_headless_backend *backend,
return true; return true;
} }
static int open_drm_render_node(void) {
uint32_t flags = 0;
int devices_len = drmGetDevices2(flags, NULL, 0);
if (devices_len < 0) {
wlr_log(WLR_ERROR, "drmGetDevices2 failed");
return -1;
}
drmDevice **devices = calloc(devices_len, sizeof(drmDevice *));
if (devices == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return -1;
}
devices_len = drmGetDevices2(flags, devices, devices_len);
if (devices_len < 0) {
free(devices);
wlr_log(WLR_ERROR, "drmGetDevices2 failed");
return -1;
}
int fd = -1;
for (int i = 0; i < devices_len; i++) {
drmDevice *dev = devices[i];
if (dev->available_nodes & (1 << DRM_NODE_RENDER)) {
const char *name = dev->nodes[DRM_NODE_RENDER];
wlr_log(WLR_DEBUG, "Opening DRM render node '%s'", name);
fd = open(name, O_RDWR | O_CLOEXEC);
if (fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open '%s'", name);
goto out;
}
break;
}
}
if (fd < 0) {
wlr_log(WLR_ERROR, "Failed to find any DRM render node");
}
out:
for (int i = 0; i < devices_len; i++) {
drmFreeDevice(&devices[i]);
}
free(devices);
return fd;
}
struct wlr_backend *wlr_headless_backend_create(struct wl_display *display, struct wlr_backend *wlr_headless_backend_create(struct wl_display *display,
wlr_renderer_create_func_t create_renderer_func) { wlr_renderer_create_func_t create_renderer_func) {
wlr_log(WLR_INFO, "Creating headless backend"); wlr_log(WLR_INFO, "Creating headless backend");
int drm_fd = open_drm_render_node();
if (drm_fd < 0) {
wlr_log(WLR_ERROR, "Failed to open DRM render node");
return NULL;
}
struct wlr_gbm_allocator *gbm_alloc = wlr_gbm_allocator_create(drm_fd);
if (gbm_alloc == NULL) {
wlr_log(WLR_ERROR, "Failed to create GBM allocator");
return false;
}
struct wlr_headless_backend *backend = struct wlr_headless_backend *backend =
calloc(1, sizeof(struct wlr_headless_backend)); calloc(1, sizeof(struct wlr_headless_backend));
if (!backend) { if (!backend) {
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend"); wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend");
return NULL; goto error_backend;
} }
static const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, 0,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_BLUE_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_RED_SIZE, 1,
EGL_NONE,
};
if (!create_renderer_func) { if (!create_renderer_func) {
create_renderer_func = wlr_renderer_autocreate; create_renderer_func = wlr_renderer_autocreate;
} }
struct wlr_renderer *renderer = create_renderer_func(&backend->priv_egl, struct wlr_renderer *renderer = create_renderer_func(&backend->priv_egl,
EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, EGL_PLATFORM_GBM_KHR, gbm_alloc->gbm_device, NULL, 0);
(EGLint*)config_attribs, 0);
if (!renderer) { if (!renderer) {
wlr_log(WLR_ERROR, "Failed to create renderer"); wlr_log(WLR_ERROR, "Failed to create renderer");
free(backend); goto error_renderer;
return NULL;
} }
if (!backend_init(backend, display, renderer)) { if (!backend_init(backend, display, &gbm_alloc->base, renderer)) {
wlr_renderer_destroy(backend->renderer); goto error_init;
free(backend);
return NULL;
} }
return &backend->backend; return &backend->backend;
error_init:
wlr_renderer_destroy(renderer);
error_renderer:
free(backend);
error_backend:
wlr_allocator_destroy(&gbm_alloc->base);
return NULL;
} }
struct wlr_backend *wlr_headless_backend_create_with_renderer( struct wlr_backend *wlr_headless_backend_create_with_renderer(
struct wl_display *display, struct wlr_renderer *renderer) { struct wl_display *display, struct wlr_renderer *renderer) {
wlr_log(WLR_INFO, "Creating headless backend"); wlr_log(WLR_INFO, "Creating headless backend with parent renderer");
int drm_fd = wlr_renderer_get_drm_fd(renderer);
if (drm_fd < 0) {
wlr_log(WLR_ERROR, "Failed to get DRM device FD from renderer");
return false;
}
drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0);
if (drm_fd < 0) {
wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed");
return false;
}
struct wlr_gbm_allocator *gbm_alloc = wlr_gbm_allocator_create(drm_fd);
if (gbm_alloc == NULL) {
wlr_log(WLR_ERROR, "Failed to create GBM allocator");
return false;
}
struct wlr_headless_backend *backend = struct wlr_headless_backend *backend =
calloc(1, sizeof(struct wlr_headless_backend)); calloc(1, sizeof(struct wlr_headless_backend));
if (!backend) { if (!backend) {
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend"); wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend");
return NULL; goto error_backend;
} }
if (!backend_init(backend, display, renderer)) { if (!backend_init(backend, display, &gbm_alloc->base, renderer)) {
free(backend); goto error_init;
return NULL;
} }
backend->renderer_destroy.notify = handle_renderer_destroy; backend->renderer_destroy.notify = handle_renderer_destroy;
wl_signal_add(&renderer->events.destroy, &backend->renderer_destroy); wl_signal_add(&renderer->events.destroy, &backend->renderer_destroy);
return &backend->backend; return &backend->backend;
error_init:
free(backend);
error_backend:
wlr_allocator_destroy(&gbm_alloc->base);
return NULL;
} }
bool wlr_backend_is_headless(struct wlr_backend *backend) { bool wlr_backend_is_headless(struct wlr_backend *backend) {

View file

@ -1,11 +1,12 @@
#include <assert.h> #include <assert.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <wlr/interfaces/wlr_output.h> #include <wlr/interfaces/wlr_output.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "backend/headless.h" #include "backend/headless.h"
#include "render/swapchain.h"
#include "render/wlr_renderer.h"
#include "util/signal.h" #include "util/signal.h"
static struct wlr_headless_output *headless_output_from_output( static struct wlr_headless_output *headless_output_from_output(
@ -14,53 +15,6 @@ static struct wlr_headless_output *headless_output_from_output(
return (struct wlr_headless_output *)wlr_output; return (struct wlr_headless_output *)wlr_output;
} }
static bool create_fbo(struct wlr_headless_output *output,
unsigned int width, unsigned int height) {
if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) {
return false;
}
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, output->backend->internal_format,
width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, rbo);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
wlr_egl_unset_current(output->backend->egl);
if (status != GL_FRAMEBUFFER_COMPLETE) {
wlr_log(WLR_ERROR, "Failed to create FBO");
return false;
}
output->fbo = fbo;
output->rbo = rbo;
return true;
}
static void destroy_fbo(struct wlr_headless_output *output) {
if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) {
return;
}
glDeleteFramebuffers(1, &output->fbo);
glDeleteRenderbuffers(1, &output->rbo);
wlr_egl_unset_current(output->backend->egl);
output->fbo = 0;
output->rbo = 0;
}
static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width, static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
int32_t height, int32_t refresh) { int32_t height, int32_t refresh) {
struct wlr_headless_output *output = struct wlr_headless_output *output =
@ -70,8 +24,10 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
refresh = HEADLESS_DEFAULT_REFRESH; refresh = HEADLESS_DEFAULT_REFRESH;
} }
destroy_fbo(output); wlr_swapchain_destroy(output->swapchain);
if (!create_fbo(output, width, height)) { output->swapchain = wlr_swapchain_create(output->backend->allocator,
width, height, output->backend->format);
if (!output->swapchain) {
wlr_output_destroy(wlr_output); wlr_output_destroy(wlr_output);
return false; return false;
} }
@ -87,15 +43,20 @@ static bool output_attach_render(struct wlr_output *wlr_output,
struct wlr_headless_output *output = struct wlr_headless_output *output =
headless_output_from_output(wlr_output); headless_output_from_output(wlr_output);
if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) { wlr_buffer_unlock(output->back_buffer);
output->back_buffer = wlr_swapchain_acquire(output->swapchain, buffer_age);
if (!output->back_buffer) {
return false; return false;
} }
glBindFramebuffer(GL_FRAMEBUFFER, output->fbo); if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) {
return false;
if (buffer_age != NULL) {
*buffer_age = 0; // We only have one buffer
} }
if (!wlr_renderer_bind_buffer(output->backend->renderer,
output->back_buffer)) {
return false;
}
return true; return true;
} }
@ -130,10 +91,28 @@ static bool output_commit(struct wlr_output *wlr_output) {
} }
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
glBindFramebuffer(GL_FRAMEBUFFER, 0); struct wlr_buffer *buffer = NULL;
wlr_egl_unset_current(output->backend->egl); switch (wlr_output->pending.buffer_type) {
case WLR_OUTPUT_STATE_BUFFER_RENDER:
assert(output->back_buffer != NULL);
wlr_renderer_bind_buffer(output->backend->renderer, NULL);
wlr_egl_unset_current(output->backend->egl);
buffer = output->back_buffer;
output->back_buffer = NULL;
break;
case WLR_OUTPUT_STATE_BUFFER_SCANOUT:
buffer = wlr_buffer_lock(wlr_output->pending.buffer);
break;
}
assert(buffer != NULL);
wlr_buffer_unlock(output->front_buffer);
output->front_buffer = buffer;
wlr_swapchain_set_buffer_submitted(output->swapchain, buffer);
// Nothing needs to be done for FBOs
wlr_output_send_present(wlr_output, NULL); wlr_output_send_present(wlr_output, NULL);
} }
@ -144,8 +123,29 @@ static void output_rollback_render(struct wlr_output *wlr_output) {
struct wlr_headless_output *output = struct wlr_headless_output *output =
headless_output_from_output(wlr_output); headless_output_from_output(wlr_output);
assert(wlr_egl_is_current(output->backend->egl)); assert(wlr_egl_is_current(output->backend->egl));
glBindFramebuffer(GL_FRAMEBUFFER, 0);
wlr_renderer_bind_buffer(output->backend->renderer, NULL);
wlr_egl_unset_current(output->backend->egl); wlr_egl_unset_current(output->backend->egl);
wlr_buffer_unlock(output->back_buffer);
output->back_buffer = NULL;
}
static bool output_export_dmabuf(struct wlr_output *wlr_output,
struct wlr_dmabuf_attributes *attribs) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);
if (!output->front_buffer) {
return false;
}
struct wlr_dmabuf_attributes tmp;
if (!wlr_buffer_get_dmabuf(output->front_buffer, &tmp)) {
return false;
}
return wlr_dmabuf_attributes_copy(attribs, &tmp);
} }
static void output_destroy(struct wlr_output *wlr_output) { static void output_destroy(struct wlr_output *wlr_output) {
@ -153,7 +153,9 @@ static void output_destroy(struct wlr_output *wlr_output) {
headless_output_from_output(wlr_output); headless_output_from_output(wlr_output);
wl_list_remove(&output->link); wl_list_remove(&output->link);
wl_event_source_remove(output->frame_timer); wl_event_source_remove(output->frame_timer);
destroy_fbo(output); wlr_swapchain_destroy(output->swapchain);
wlr_buffer_unlock(output->back_buffer);
wlr_buffer_unlock(output->front_buffer);
free(output); free(output);
} }
@ -162,6 +164,7 @@ static const struct wlr_output_impl output_impl = {
.attach_render = output_attach_render, .attach_render = output_attach_render,
.commit = output_commit, .commit = output_commit,
.rollback_render = output_rollback_render, .rollback_render = output_rollback_render,
.export_dmabuf = output_export_dmabuf,
}; };
bool wlr_output_is_headless(struct wlr_output *wlr_output) { bool wlr_output_is_headless(struct wlr_output *wlr_output) {
@ -191,7 +194,9 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
backend->display); backend->display);
struct wlr_output *wlr_output = &output->wlr_output; struct wlr_output *wlr_output = &output->wlr_output;
if (!create_fbo(output, width, height)) { output->swapchain = wlr_swapchain_create(backend->allocator,
width, height, backend->format);
if (!output->swapchain) {
goto error; goto error;
} }
@ -206,14 +211,6 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
"Headless output %zd", backend->last_output_num); "Headless output %zd", backend->last_output_num);
wlr_output_set_description(wlr_output, description); wlr_output_set_description(wlr_output, description);
if (!output_attach_render(wlr_output, NULL)) {
goto error;
}
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
wlr_renderer_end(backend->renderer);
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display); struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output); output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);

View file

@ -1,6 +1,7 @@
#include <assert.h> #include <assert.h>
#include <libinput.h> #include <libinput.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <wlr/backend/interface.h> #include <wlr/backend/interface.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
@ -16,12 +17,27 @@ static struct wlr_libinput_backend *get_libinput_backend_from_backend(
static int libinput_open_restricted(const char *path, static int libinput_open_restricted(const char *path,
int flags, void *_backend) { int flags, void *_backend) {
struct wlr_libinput_backend *backend = _backend; struct wlr_libinput_backend *backend = _backend;
return wlr_session_open_file(backend->session, path); struct wlr_device *dev = wlr_session_open_file(backend->session, path);
if (dev == NULL) {
return -1;
}
return dev->fd;
} }
static void libinput_close_restricted(int fd, void *_backend) { static void libinput_close_restricted(int fd, void *_backend) {
struct wlr_libinput_backend *backend = _backend; struct wlr_libinput_backend *backend = _backend;
wlr_session_close_file(backend->session, fd);
struct wlr_device *dev;
bool found = false;
wl_list_for_each(dev, &backend->session->devices, link) {
if (dev->fd == fd) {
found = true;
break;
}
}
if (found) {
wlr_session_close_file(backend->session, dev);
}
} }
static const struct libinput_interface libinput_impl = { static const struct libinput_interface libinput_impl = {
@ -44,9 +60,24 @@ static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) {
return 0; return 0;
} }
static enum wlr_log_importance libinput_log_priority_to_wlr(
enum libinput_log_priority priority) {
switch (priority) {
case LIBINPUT_LOG_PRIORITY_ERROR:
return WLR_ERROR;
case LIBINPUT_LOG_PRIORITY_INFO:
return WLR_INFO;
default:
return WLR_DEBUG;
}
}
static void log_libinput(struct libinput *libinput_context, static void log_libinput(struct libinput *libinput_context,
enum libinput_log_priority priority, const char *fmt, va_list args) { enum libinput_log_priority priority, const char *fmt, va_list args) {
_wlr_vlog(WLR_ERROR, fmt, args); enum wlr_log_importance importance = libinput_log_priority_to_wlr(priority);
static char wlr_fmt[1024];
snprintf(wlr_fmt, sizeof(wlr_fmt), "[libinput] %s", fmt);
_wlr_vlog(importance, wlr_fmt, args);
} }
static bool backend_start(struct wlr_backend *wlr_backend) { static bool backend_start(struct wlr_backend *wlr_backend) {
@ -144,7 +175,7 @@ bool wlr_backend_is_libinput(struct wlr_backend *b) {
static void session_signal(struct wl_listener *listener, void *data) { static void session_signal(struct wl_listener *listener, void *data) {
struct wlr_libinput_backend *backend = struct wlr_libinput_backend *backend =
wl_container_of(listener, backend, session_signal); wl_container_of(listener, backend, session_signal);
struct wlr_session *session = data; struct wlr_session *session = backend->session;
if (!backend->libinput_context) { if (!backend->libinput_context) {
return; return;
@ -188,7 +219,7 @@ struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display,
backend->display = display; backend->display = display;
backend->session_signal.notify = session_signal; backend->session_signal.notify = session_signal;
wl_signal_add(&session->session_signal, &backend->session_signal); wl_signal_add(&session->events.active, &backend->session_signal);
backend->session_destroy.notify = handle_session_destroy; backend->session_destroy.notify = handle_session_destroy;
wl_signal_add(&session->events.destroy, &backend->session_destroy); wl_signal_add(&session->events.destroy, &backend->session_destroy);

View file

@ -72,10 +72,10 @@ void handle_keyboard_key(struct libinput_event *event,
libinput_event_keyboard_get_key_state(kbevent); libinput_event_keyboard_get_key_state(kbevent);
switch (state) { switch (state) {
case LIBINPUT_KEY_STATE_RELEASED: case LIBINPUT_KEY_STATE_RELEASED:
wlr_event.state = WLR_KEY_RELEASED; wlr_event.state = WL_KEYBOARD_KEY_STATE_RELEASED;
break; break;
case LIBINPUT_KEY_STATE_PRESSED: case LIBINPUT_KEY_STATE_PRESSED:
wlr_event.state = WLR_KEY_PRESSED; wlr_event.state = WL_KEYBOARD_KEY_STATE_PRESSED;
break; break;
} }
wlr_event.update_state = true; wlr_event.update_state = true;

View file

@ -113,10 +113,8 @@ static enum wlr_tablet_tool_type wlr_type_from_libinput_type(
return WLR_TABLET_TOOL_TYPE_MOUSE; return WLR_TABLET_TOOL_TYPE_MOUSE;
case LIBINPUT_TABLET_TOOL_TYPE_LENS: case LIBINPUT_TABLET_TOOL_TYPE_LENS:
return WLR_TABLET_TOOL_TYPE_LENS; return WLR_TABLET_TOOL_TYPE_LENS;
#if LIBINPUT_MINOR >= 14
case LIBINPUT_TABLET_TOOL_TYPE_TOTEM: case LIBINPUT_TABLET_TOOL_TYPE_TOTEM:
return WLR_TABLET_TOOL_TYPE_TOTEM; return WLR_TABLET_TOOL_TYPE_TOTEM;
#endif
} }
abort(); // unreachable abort(); // unreachable
} }

View file

@ -18,6 +18,7 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <xf86drm.h> #include <xf86drm.h>
#include "backend/session/direct-ipc.h" #include "backend/session/direct-ipc.h"
#include "backend/session/session.h"
#include "util/signal.h" #include "util/signal.h"
const struct session_impl session_direct; const struct session_impl session_direct;
@ -114,7 +115,7 @@ static int vt_handler(int signo, void *data) {
if (session->base.active) { if (session->base.active) {
session->base.active = false; session->base.active = false;
wlr_signal_emit_safe(&session->base.session_signal, session); wlr_signal_emit_safe(&session->base.events.active, NULL);
wl_list_for_each(dev, &session->base.devices, link) { wl_list_for_each(dev, &session->base.devices, link) {
if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) { if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) {
@ -133,7 +134,7 @@ static int vt_handler(int signo, void *data) {
} }
session->base.active = true; session->base.active = true;
wlr_signal_emit_safe(&session->base.session_signal, session); wlr_signal_emit_safe(&session->base.events.active, NULL);
} }
return 1; return 1;
@ -274,6 +275,7 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) {
return NULL; return NULL;
} }
session_init(&session->base);
session->sock = direct_ipc_init(&session->child); session->sock = direct_ipc_init(&session->child);
if (session->sock == -1) { if (session->sock == -1) {
goto error_session; goto error_session;
@ -297,6 +299,7 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) {
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
session->base.impl = &session_direct; session->base.impl = &session_direct;
session->base.active = true;
return &session->base; return &session->base;
error_ipc: error_ipc:

View file

@ -24,16 +24,6 @@
enum { DRM_MAJOR = 226 }; enum { DRM_MAJOR = 226 };
static bool have_permissions(void) {
#ifdef __linux__
if (geteuid() != 0) {
wlr_log(WLR_ERROR, "Do not have root privileges; cannot become DRM master");
return false;
}
#endif
return true;
}
static void send_msg(int sock, int fd, void *buf, size_t buf_len) { static void send_msg(int sock, int fd, void *buf, size_t buf_len) {
char control[CMSG_SPACE(sizeof(fd))] = {0}; char control[CMSG_SPACE(sizeof(fd))] = {0};
struct iovec iovec = { .iov_base = buf, .iov_len = buf_len }; struct iovec iovec = { .iov_base = buf, .iov_len = buf_len };
@ -223,10 +213,6 @@ void direct_ipc_finish(int sock, pid_t pid) {
} }
int direct_ipc_init(pid_t *pid_out) { int direct_ipc_init(pid_t *pid_out) {
if (!have_permissions()) {
return -1;
}
int sock[2]; int sock[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) { if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) {
wlr_log_errno(WLR_ERROR, "Failed to create socket pair"); wlr_log_errno(WLR_ERROR, "Failed to create socket pair");

View file

@ -18,6 +18,7 @@
#include <wlr/backend/session/interface.h> #include <wlr/backend/session/interface.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "backend/session/direct-ipc.h" #include "backend/session/direct-ipc.h"
#include "backend/session/session.h"
#include "util/signal.h" #include "util/signal.h"
enum { DRM_MAJOR = 226 }; enum { DRM_MAJOR = 226 };
@ -125,7 +126,7 @@ static int vt_handler(int signo, void *data) {
if (session->base.active) { if (session->base.active) {
session->base.active = false; session->base.active = false;
wlr_signal_emit_safe(&session->base.session_signal, session); wlr_signal_emit_safe(&session->base.events.active, NULL);
struct wlr_device *dev; struct wlr_device *dev;
wl_list_for_each(dev, &session->base.devices, link) { wl_list_for_each(dev, &session->base.devices, link) {
@ -148,7 +149,7 @@ static int vt_handler(int signo, void *data) {
} }
session->base.active = true; session->base.active = true;
wlr_signal_emit_safe(&session->base.session_signal, session); wlr_signal_emit_safe(&session->base.events.active, NULL);
} }
return 1; return 1;
@ -246,6 +247,7 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) {
return NULL; return NULL;
} }
session_init(&session->base);
session->sock = direct_ipc_init(&session->child); session->sock = direct_ipc_init(&session->child);
if (session->sock == -1) { if (session->sock == -1) {
goto error_session; goto error_session;
@ -267,6 +269,7 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) {
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
session->base.impl = &session_direct; session->base.impl = &session_direct;
session->base.active = true;
wlr_log(WLR_INFO, "Successfully loaded direct session"); wlr_log(WLR_INFO, "Successfully loaded direct session");
return &session->base; return &session->base;

218
backend/session/libseat.c Normal file
View file

@ -0,0 +1,218 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/backend/session/interface.h>
#include <wlr/config.h>
#include <wlr/util/log.h>
#include "backend/session/session.h"
#include "util/signal.h"
#include <libseat.h>
const struct session_impl session_libseat;
struct libseat_device {
struct wl_list link;
int fd;
int device_id;
};
struct libseat_session {
struct wlr_session base;
struct libseat *seat;
struct wl_event_source *event;
struct wl_list devices;
};
static void handle_enable_seat(struct libseat *seat, void *data) {
struct libseat_session *session = data;
session->base.active = true;
wlr_signal_emit_safe(&session->base.events.active, NULL);
}
static void handle_disable_seat(struct libseat *seat, void *data) {
struct libseat_session *session = data;
session->base.active = false;
wlr_signal_emit_safe(&session->base.events.active, NULL);
libseat_disable_seat(session->seat);
}
static int libseat_event(int fd, uint32_t mask, void *data) {
struct libseat *seat = data;
libseat_dispatch(seat, 0);
return 1;
}
static struct libseat_seat_listener seat_listener = {
.enable_seat = handle_enable_seat,
.disable_seat = handle_disable_seat,
};
static struct libseat_session *libseat_session_from_session(
struct wlr_session *base) {
assert(base->impl == &session_libseat);
return (struct libseat_session *)base;
}
static enum wlr_log_importance libseat_log_level_to_wlr(
enum libseat_log_level level) {
switch (level) {
case LIBSEAT_LOG_LEVEL_ERROR:
return WLR_ERROR;
case LIBSEAT_LOG_LEVEL_INFO:
return WLR_INFO;
default:
return WLR_DEBUG;
}
}
static void log_libseat(enum libseat_log_level level,
const char *fmt, va_list args) {
enum wlr_log_importance importance = libseat_log_level_to_wlr(level);
static char wlr_fmt[1024];
snprintf(wlr_fmt, sizeof(wlr_fmt), "[libseat] %s", fmt);
_wlr_vlog(importance, wlr_fmt, args);
}
static struct wlr_session *libseat_session_create(struct wl_display *disp) {
struct libseat_session *session = calloc(1, sizeof(*session));
if (!session) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL;
}
session_init(&session->base);
wl_list_init(&session->devices);
libseat_set_log_handler(log_libseat);
libseat_set_log_level(LIBSEAT_LOG_LEVEL_ERROR);
// libseat will take care of updating the logind state if necessary
setenv("XDG_SESSION_TYPE", "wayland", 1);
session->seat = libseat_open_seat(&seat_listener, session);
if (session->seat == NULL) {
wlr_log_errno(WLR_ERROR, "Unable to create seat");
goto error;
}
const char *seat_name = libseat_seat_name(session->seat);
if (seat_name == NULL) {
wlr_log_errno(WLR_ERROR, "Unable to get seat info");
goto error;
}
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat_name);
struct wl_event_loop *event_loop = wl_display_get_event_loop(disp);
session->event = wl_event_loop_add_fd(event_loop, libseat_get_fd(session->seat),
WL_EVENT_READABLE, libseat_event, session->seat);
if (session->event == NULL) {
wlr_log(WLR_ERROR, "Failed to create libseat event source");
goto error;
}
// We may have received enable_seat immediately after the open_seat result,
// so, dispatch once without timeout to speed up activation.
if (libseat_dispatch(session->seat, 0) == -1) {
wlr_log_errno(WLR_ERROR, "libseat dispatch failed");
goto error;
}
wlr_log(WLR_INFO, "Successfully loaded libseat session");
session->base.impl = &session_libseat;
return &session->base;
error:
if (session->seat != NULL) {
libseat_close_seat(session->seat);
}
if (session->event != NULL) {
wl_event_source_remove(session->event);
}
free(session);
return NULL;
}
static void libseat_session_destroy(struct wlr_session *base) {
struct libseat_session *session = libseat_session_from_session(base);
libseat_close_seat(session->seat);
wl_event_source_remove(session->event);
free(session);
}
static struct libseat_device *find_device_by_fd(struct libseat_session *session, int fd) {
struct libseat_device *dev;
wl_list_for_each(dev, &session->devices, link) {
if (dev->fd == fd) {
return dev;
}
}
return NULL;
}
static int libseat_session_open_device(struct wlr_session *base, const char *path) {
struct libseat_session *session = libseat_session_from_session(base);
int fd;
int device_id = libseat_open_device(session->seat, path, &fd);
if (device_id == -1) {
wlr_log_errno(WLR_ERROR, "Failed to open device '%s'", path);
return -1;
}
struct libseat_device *dev = calloc(1, sizeof(struct libseat_device));
if (dev == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
libseat_close_device(session->seat, device_id);
return -1;
}
dev->fd = fd;
dev->device_id = device_id;
wl_list_insert(&session->devices, &dev->link);
return fd;
}
static void libseat_session_close_device(struct wlr_session *base, int fd) {
struct libseat_session *session = libseat_session_from_session(base);
struct libseat_device *dev = find_device_by_fd(session, fd);
if (dev == NULL) {
wlr_log(WLR_ERROR, "No device with fd %d found", fd);
close(fd);
return;
}
if (libseat_close_device(session->seat, dev->device_id) == -1) {
wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id);
}
wl_list_remove(&dev->link);
free(dev);
close(fd);
}
static bool libseat_change_vt(struct wlr_session *base, unsigned vt) {
struct libseat_session *session = libseat_session_from_session(base);
return libseat_switch_session(session->seat, vt) == 0;
}
const struct session_impl session_libseat = {
.create = libseat_session_create,
.destroy = libseat_session_destroy,
.open = libseat_session_open_device,
.close = libseat_session_close_device,
.change_vt = libseat_change_vt,
};

View file

@ -13,6 +13,7 @@
#include <wlr/backend/session/interface.h> #include <wlr/backend/session/interface.h>
#include <wlr/config.h> #include <wlr/config.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "backend/session/session.h"
#include "util/signal.h" #include "util/signal.h"
#if WLR_HAS_SYSTEMD #if WLR_HAS_SYSTEMD
@ -35,9 +36,7 @@ struct logind_session {
char *id; char *id;
char *path; char *path;
char *seat_path;
bool can_graphical;
// specifies whether a drm device was taken // specifies whether a drm device was taken
// if so, the session will be (de)activated with the drm fd, // if so, the session will be (de)activated with the drm fd,
// otherwise with the dbus PropertiesChanged on "active" signal // otherwise with the dbus PropertiesChanged on "active" signal
@ -129,8 +128,8 @@ static void logind_release_device(struct wlr_session *base, int fd) {
static bool logind_change_vt(struct wlr_session *base, unsigned vt) { static bool logind_change_vt(struct wlr_session *base, unsigned vt) {
struct logind_session *session = logind_session_from_session(base); struct logind_session *session = logind_session_from_session(base);
// Only seat0 has VTs associated with it // Only if seat has VTs associated with it
if (strcmp(session->base.seat, "seat0") != 0) { if (!sd_seat_can_tty(session->base.seat)) {
return true; return true;
} }
@ -142,7 +141,7 @@ static bool logind_change_vt(struct wlr_session *base, unsigned vt) {
"/org/freedesktop/login1/seat/seat0", "org.freedesktop.login1.Seat", "SwitchTo", "/org/freedesktop/login1/seat/seat0", "org.freedesktop.login1.Seat", "SwitchTo",
&error, &msg, "u", (uint32_t)vt); &error, &msg, "u", (uint32_t)vt);
if (ret < 0) { if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to change to vt '%d'", vt); wlr_log(WLR_ERROR, "Failed to change to vt '%u'", vt);
} }
sd_bus_error_free(&error); sd_bus_error_free(&error);
@ -178,34 +177,6 @@ out:
return ret >= 0; return ret >= 0;
} }
static bool find_seat_path(struct logind_session *session) {
int ret;
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
"/org/freedesktop/login1", "org.freedesktop.login1.Manager",
"GetSeat", &error, &msg, "s", session->base.seat);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to get seat path: %s", error.message);
goto out;
}
const char *path;
ret = sd_bus_message_read(msg, "o", &path);
if (ret < 0) {
wlr_log(WLR_ERROR, "Could not parse seat path: %s", error.message);
goto out;
}
session->seat_path = strdup(path);
out:
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
return ret >= 0;
}
static bool session_activate(struct logind_session *session) { static bool session_activate(struct logind_session *session) {
int ret; int ret;
sd_bus_message *msg = NULL; sd_bus_message *msg = NULL;
@ -241,6 +212,32 @@ static bool take_control(struct logind_session *session) {
return ret >= 0; return ret >= 0;
} }
static void set_type(struct logind_session *session) {
int ret;
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
session->path, "org.freedesktop.login1.Session", "SetType",
&error, &msg, "s", "wayland");
if (ret < 0) {
wlr_log(WLR_DEBUG, "Failed to set logind session type for session: %s",
error.message);
}
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
if (ret < 0) {
return;
}
ret = setenv("XDG_SESSION_TYPE", "wayland", 1);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to set XDG_SESSION_TYPE for session");
}
}
static void release_control(struct logind_session *session) { static void release_control(struct logind_session *session) {
int ret; int ret;
sd_bus_message *msg = NULL; sd_bus_message *msg = NULL;
@ -267,7 +264,6 @@ static void logind_session_destroy(struct wlr_session *base) {
sd_bus_unref(session->bus); sd_bus_unref(session->bus);
free(session->id); free(session->id);
free(session->path); free(session->path);
free(session->seat_path);
free(session); free(session);
} }
@ -310,7 +306,7 @@ static int pause_device(sd_bus_message *msg, void *userdata,
if (major == DRM_MAJOR && strcmp(type, "gone") != 0) { if (major == DRM_MAJOR && strcmp(type, "gone") != 0) {
assert(session->has_drm); assert(session->has_drm);
session->base.active = false; session->base.active = false;
wlr_signal_emit_safe(&session->base.session_signal, session); wlr_signal_emit_safe(&session->base.events.active, NULL);
} }
if (strcmp(type, "pause") == 0) { if (strcmp(type, "pause") == 0) {
@ -352,7 +348,7 @@ static int resume_device(sd_bus_message *msg, void *userdata,
if (!session->base.active) { if (!session->base.active) {
session->base.active = true; session->base.active = true;
wlr_signal_emit_safe(&session->base.session_signal, session); wlr_signal_emit_safe(&session->base.events.active, NULL);
} }
} }
@ -411,7 +407,7 @@ static int session_properties_changed(sd_bus_message *msg, void *userdata,
if (session->base.active != active) { if (session->base.active != active) {
session->base.active = active; session->base.active = active;
wlr_signal_emit_safe(&session->base.session_signal, session); wlr_signal_emit_safe(&session->base.events.active, NULL);
} }
return 0; return 0;
} else { } else {
@ -451,7 +447,7 @@ static int session_properties_changed(sd_bus_message *msg, void *userdata,
if (session->base.active != active) { if (session->base.active != active) {
session->base.active = active; session->base.active = active;
wlr_signal_emit_safe(&session->base.session_signal, session); wlr_signal_emit_safe(&session->base.events.active, NULL);
} }
return 0; return 0;
} }
@ -469,95 +465,6 @@ error:
return 0; return 0;
} }
static int seat_properties_changed(sd_bus_message *msg, void *userdata,
sd_bus_error *ret_error) {
struct logind_session *session = userdata;
int ret = 0;
// if we have a drm fd we don't depend on this
if (session->has_drm) {
return 0;
}
// PropertiesChanged arg 1: interface
const char *interface;
ret = sd_bus_message_read_basic(msg, 's', &interface); // skip path
if (ret < 0) {
goto error;
}
if (strcmp(interface, "org.freedesktop.login1.Seat") != 0) {
// not interesting for us; ignore
wlr_log(WLR_DEBUG, "ignoring PropertiesChanged from %s", interface);
return 0;
}
// PropertiesChanged arg 2: changed properties with values
ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
if (ret < 0) {
goto error;
}
const char *s;
while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
ret = sd_bus_message_read_basic(msg, 's', &s);
if (ret < 0) {
goto error;
}
if (strcmp(s, "CanGraphical") == 0) {
int ret;
ret = sd_bus_message_enter_container(msg, 'v', "b");
if (ret < 0) {
goto error;
}
ret = sd_bus_message_read_basic(msg, 'b', &session->can_graphical);
if (ret < 0) {
goto error;
}
return 0;
} else {
sd_bus_message_skip(msg, "{sv}");
}
ret = sd_bus_message_exit_container(msg);
if (ret < 0) {
goto error;
}
}
if (ret < 0) {
goto error;
}
ret = sd_bus_message_exit_container(msg);
if (ret < 0) {
goto error;
}
// PropertiesChanged arg 3: changed properties without values
sd_bus_message_enter_container(msg, 'a', "s");
while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) {
if (strcmp(s, "CanGraphical") == 0) {
session->can_graphical = sd_seat_can_graphical(session->base.seat);
return 0;
}
}
if (ret < 0) {
goto error;
}
return 0;
error:
wlr_log(WLR_ERROR, "Failed to parse D-Bus PropertiesChanged: %s",
strerror(-ret));
return 0;
}
static bool add_signal_matches(struct logind_session *session) { static bool add_signal_matches(struct logind_session *session) {
static const char *logind = "org.freedesktop.login1"; static const char *logind = "org.freedesktop.login1";
static const char *logind_path = "/org/freedesktop/login1"; static const char *logind_path = "/org/freedesktop/login1";
@ -595,14 +502,6 @@ static bool add_signal_matches(struct logind_session *session) {
return false; return false;
} }
ret = sd_bus_match_signal(session->bus, NULL, logind, session->seat_path,
property_interface, "PropertiesChanged",
seat_properties_changed, session);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
return false;
}
return true; return true;
} }
@ -670,6 +569,8 @@ static bool get_display_session(char **session_id) {
char *xdg_session_id = getenv("XDG_SESSION_ID"); char *xdg_session_id = getenv("XDG_SESSION_ID");
if (xdg_session_id) { if (xdg_session_id) {
wlr_log(WLR_INFO, "Selecting session from XDG_SESSION_ID: %s", xdg_session_id);
// This just checks whether the supplied session ID is valid // This just checks whether the supplied session ID is valid
if (sd_session_is_active(xdg_session_id) < 0) { if (sd_session_is_active(xdg_session_id) < 0) {
wlr_log(WLR_ERROR, "Invalid XDG_SESSION_ID: '%s'", xdg_session_id); wlr_log(WLR_ERROR, "Invalid XDG_SESSION_ID: '%s'", xdg_session_id);
@ -750,6 +651,8 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) {
return NULL; return NULL;
} }
session_init(&session->base);
if (!get_display_session(&session->id)) { if (!get_display_session(&session->id)) {
goto error; goto error;
} }
@ -762,7 +665,7 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) {
} }
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
if (strcmp(seat, "seat0") == 0) { if (sd_seat_can_tty(seat)) {
ret = sd_session_get_vt(session->id, &session->base.vtnr); ret = sd_session_get_vt(session->id, &session->base.vtnr);
if (ret < 0) { if (ret < 0) {
wlr_log(WLR_ERROR, "Session not running in virtual terminal"); wlr_log(WLR_ERROR, "Session not running in virtual terminal");
@ -782,12 +685,6 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) {
goto error; goto error;
} }
if (!find_seat_path(session)) {
sd_bus_unref(session->bus);
free(session->path);
goto error;
}
if (!add_signal_matches(session)) { if (!add_signal_matches(session)) {
goto error_bus; goto error_bus;
} }
@ -804,30 +701,18 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) {
goto error_bus; goto error_bus;
} }
// Check for CanGraphical first set_type(session);
session->can_graphical = sd_seat_can_graphical(session->base.seat);
if (!session->can_graphical) {
wlr_log(WLR_INFO, "Waiting for 'CanGraphical' on seat %s", session->base.seat);
}
while (!session->can_graphical) {
ret = wl_event_loop_dispatch(event_loop, -1);
if (ret < 0) {
wlr_log(WLR_ERROR, "Polling error waiting for 'CanGraphical' on seat %s",
session->base.seat);
goto error_bus;
}
}
wlr_log(WLR_INFO, "Successfully loaded logind session"); wlr_log(WLR_INFO, "Successfully loaded logind session");
session->base.impl = &session_logind; session->base.impl = &session_logind;
session->base.active = true;
return &session->base; return &session->base;
error_bus: error_bus:
sd_bus_unref(session->bus); sd_bus_unref(session->bus);
free(session->path); free(session->path);
free(session->seat_path);
error: error:
free(session->id); free(session->id);

View file

@ -62,3 +62,12 @@ if logind_found
wlr_files += files('logind.c') wlr_files += files('logind.c')
wlr_deps += logind wlr_deps += logind
endif endif
# libseat
libseat = dependency('libseat', required: get_option('libseat'), version: '>=0.2.0')
if libseat.found()
wlr_files += files('libseat.c')
wlr_deps += libseat
conf_data.set10('WLR_HAS_LIBSEAT', true)
endif

View file

@ -6,6 +6,7 @@
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/backend/session/interface.h> #include <wlr/backend/session/interface.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "backend/session/session.h"
#include "util/signal.h" #include "util/signal.h"
const struct session_impl session_noop; const struct session_impl session_noop;
@ -33,7 +34,9 @@ static struct wlr_session *noop_session_create(struct wl_display *disp) {
return NULL; return NULL;
} }
session_init(session);
session->impl = &session_noop; session->impl = &session_noop;
session->active = true;
wlr_log(WLR_INFO, "Successfully initialized noop session"); wlr_log(WLR_INFO, "Successfully initialized noop session");
return session; return session;

View file

@ -6,6 +6,8 @@
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/backend/session/interface.h> #include <wlr/backend/session/interface.h>
@ -13,13 +15,20 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <xf86drm.h> #include <xf86drm.h>
#include <xf86drmMode.h> #include <xf86drmMode.h>
#include "backend/session/session.h"
#include "util/signal.h" #include "util/signal.h"
#define WAIT_GPU_TIMEOUT 10000 // ms
extern const struct session_impl session_libseat;
extern const struct session_impl session_logind; extern const struct session_impl session_logind;
extern const struct session_impl session_direct; extern const struct session_impl session_direct;
extern const struct session_impl session_noop; extern const struct session_impl session_noop;
static const struct session_impl *impls[] = { static const struct session_impl *impls[] = {
#if WLR_HAS_LIBSEAT
&session_libseat,
#endif
#if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND #if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND
&session_logind, &session_logind,
#endif #endif
@ -27,6 +36,19 @@ static const struct session_impl *impls[] = {
NULL, NULL,
}; };
static bool is_drm_card(const char *sysname) {
const char prefix[] = "card";
if (strncmp(sysname, prefix, strlen(prefix)) != 0) {
return false;
}
for (size_t i = strlen(prefix); sysname[i] != '\0'; i++) {
if (sysname[i] < '0' || sysname[i] > '9') {
return false;
}
}
return true;
}
static int udev_event(int fd, uint32_t mask, void *data) { static int udev_event(int fd, uint32_t mask, void *data) {
struct wlr_session *session = data; struct wlr_session *session = data;
@ -35,22 +57,38 @@ static int udev_event(int fd, uint32_t mask, void *data) {
return 1; return 1;
} }
const char *sysname = udev_device_get_sysname(udev_dev);
const char *devnode = udev_device_get_devnode(udev_dev);
const char *action = udev_device_get_action(udev_dev); const char *action = udev_device_get_action(udev_dev);
wlr_log(WLR_DEBUG, "udev event for %s (%s)", sysname, action);
wlr_log(WLR_DEBUG, "udev event for %s (%s)", if (!is_drm_card(sysname) || !action || !devnode) {
udev_device_get_sysname(udev_dev), action);
if (!action || strcmp(action, "change") != 0) {
goto out; goto out;
} }
dev_t devnum = udev_device_get_devnum(udev_dev); const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT");
struct wlr_device *dev; if (!seat) {
seat = "seat0";
}
if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) {
goto out;
}
wl_list_for_each(dev, &session->devices, link) { if (strcmp(action, "add") == 0) {
if (dev->dev == devnum) { wlr_log(WLR_DEBUG, "DRM device %s added", sysname);
wlr_signal_emit_safe(&dev->signal, session); struct wlr_session_add_event event = {
break; .path = devnode,
};
wlr_signal_emit_safe(&session->events.add_drm_card, &event);
} else if (strcmp(action, "change") == 0) {
dev_t devnum = udev_device_get_devnum(udev_dev);
struct wlr_device *dev;
wl_list_for_each(dev, &session->devices, link) {
if (dev->dev == devnum) {
wlr_log(WLR_DEBUG, "DRM device %s changed", sysname);
wlr_signal_emit_safe(&dev->events.change, NULL);
break;
}
} }
} }
@ -65,12 +103,25 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
wlr_session_destroy(session); wlr_session_destroy(session);
} }
void session_init(struct wlr_session *session) {
wl_signal_init(&session->events.active);
wl_signal_init(&session->events.add_drm_card);
wl_signal_init(&session->events.destroy);
wl_list_init(&session->devices);
}
struct wlr_session *wlr_session_create(struct wl_display *disp) { struct wlr_session *wlr_session_create(struct wl_display *disp) {
struct wlr_session *session = NULL; struct wlr_session *session = NULL;
const char *env_wlr_session = getenv("WLR_SESSION"); const char *env_wlr_session = getenv("WLR_SESSION");
if (env_wlr_session) { if (env_wlr_session) {
if (strcmp(env_wlr_session, "logind") == 0 || if (strcmp(env_wlr_session, "libseat") == 0) {
#if WLR_HAS_LIBSEAT
session = session_libseat.create(disp);
#else
wlr_log(WLR_ERROR, "wlroots is not compiled with libseat support");
#endif
} else if (strcmp(env_wlr_session, "logind") == 0 ||
strcmp(env_wlr_session, "systemd") == 0) { strcmp(env_wlr_session, "systemd") == 0) {
#if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND #if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND
session = session_logind.create(disp); session = session_logind.create(disp);
@ -97,11 +148,6 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) {
return NULL; return NULL;
} }
session->active = true;
wl_signal_init(&session->session_signal);
wl_signal_init(&session->events.destroy);
wl_list_init(&session->devices);
session->udev = udev_new(); session->udev = udev_new();
if (!session->udev) { if (!session->udev) {
wlr_log_errno(WLR_ERROR, "Failed to create udev context"); wlr_log_errno(WLR_ERROR, "Failed to create udev context");
@ -127,6 +173,8 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) {
goto error_mon; goto error_mon;
} }
session->display = disp;
session->display_destroy.notify = handle_display_destroy; session->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(disp, &session->display_destroy); wl_display_add_destroy_listener(disp, &session->display_destroy);
@ -156,10 +204,11 @@ void wlr_session_destroy(struct wlr_session *session) {
session->impl->destroy(session); session->impl->destroy(session);
} }
int wlr_session_open_file(struct wlr_session *session, const char *path) { struct wlr_device *wlr_session_open_file(struct wlr_session *session,
const char *path) {
int fd = session->impl->open(session, path); int fd = session->impl->open(session, path);
if (fd < 0) { if (fd < 0) {
return fd; return NULL;
} }
struct wlr_device *dev = malloc(sizeof(*dev)); struct wlr_device *dev = malloc(sizeof(*dev));
@ -176,45 +225,24 @@ int wlr_session_open_file(struct wlr_session *session, const char *path) {
dev->fd = fd; dev->fd = fd;
dev->dev = st.st_rdev; dev->dev = st.st_rdev;
wl_signal_init(&dev->signal); wl_signal_init(&dev->events.change);
wl_list_insert(&session->devices, &dev->link); wl_list_insert(&session->devices, &dev->link);
return fd; return dev;
error: error:
free(dev); free(dev);
return fd; close(fd);
}
static struct wlr_device *find_device(struct wlr_session *session, int fd) {
struct wlr_device *dev;
wl_list_for_each(dev, &session->devices, link) {
if (dev->fd == fd) {
return dev;
}
}
wlr_log(WLR_ERROR, "Tried to use fd %d not opened by session", fd);
assert(0);
return NULL; return NULL;
} }
void wlr_session_close_file(struct wlr_session *session, int fd) { void wlr_session_close_file(struct wlr_session *session,
struct wlr_device *dev = find_device(session, fd); struct wlr_device *dev) {
session->impl->close(session, dev->fd);
session->impl->close(session, fd);
wl_list_remove(&dev->link); wl_list_remove(&dev->link);
free(dev); free(dev);
} }
void wlr_session_signal_add(struct wlr_session *session, int fd,
struct wl_listener *listener) {
struct wlr_device *dev = find_device(session, fd);
wl_signal_add(&dev->signal, listener);
}
bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) { bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) {
if (!session) { if (!session) {
return false; return false;
@ -226,32 +254,32 @@ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) {
/* Tests if 'path' is KMS compatible by trying to open it. /* Tests if 'path' is KMS compatible by trying to open it.
* It leaves the open device in *fd_out it it succeeds. * It leaves the open device in *fd_out it it succeeds.
*/ */
static int open_if_kms(struct wlr_session *restrict session, static struct wlr_device *open_if_kms(struct wlr_session *restrict session,
const char *restrict path) { const char *restrict path) {
if (!path) { if (!path) {
return -1; return NULL;
} }
int fd = wlr_session_open_file(session, path); struct wlr_device *dev = wlr_session_open_file(session, path);
if (fd < 0) { if (!dev) {
return -1; return NULL;
} }
drmVersion *ver = drmGetVersion(fd); drmVersion *ver = drmGetVersion(dev->fd);
if (!ver) { if (!ver) {
goto out_fd; goto out_dev;
} }
drmFreeVersion(ver); drmFreeVersion(ver);
return fd; return dev;
out_fd: out_dev:
wlr_session_close_file(session, fd); wlr_session_close_file(session, dev);
return -1; return NULL;
} }
static size_t explicit_find_gpus(struct wlr_session *session, static size_t explicit_find_gpus(struct wlr_session *session,
size_t ret_len, int ret[static ret_len], const char *str) { size_t ret_len, struct wlr_device *ret[static ret_len], const char *str) {
char *gpus = strdup(str); char *gpus = strdup(str);
if (!gpus) { if (!gpus) {
wlr_log_errno(WLR_ERROR, "Allocation failed"); wlr_log_errno(WLR_ERROR, "Allocation failed");
@ -267,7 +295,7 @@ static size_t explicit_find_gpus(struct wlr_session *session,
} }
ret[i] = open_if_kms(session, ptr); ret[i] = open_if_kms(session, ptr);
if (ret[i] < 0) { if (!ret[i]) {
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr); wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr);
} else { } else {
++i; ++i;
@ -278,25 +306,92 @@ static size_t explicit_find_gpus(struct wlr_session *session,
return i; return i;
} }
static struct udev_enumerate *enumerate_drm_cards(struct udev *udev) {
struct udev_enumerate *en = udev_enumerate_new(udev);
if (!en) {
wlr_log(WLR_ERROR, "udev_enumerate_new failed");
return NULL;
}
udev_enumerate_add_match_subsystem(en, "drm");
udev_enumerate_add_match_sysname(en, "card[0-9]*");
if (udev_enumerate_scan_devices(en) != 0) {
wlr_log(WLR_ERROR, "udev_enumerate_scan_devices failed");
udev_enumerate_unref(en);
return NULL;
}
return en;
}
static uint64_t get_current_time_ms(void) {
struct timespec ts = {0};
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
}
struct find_gpus_add_handler {
bool added;
struct wl_listener listener;
};
static void find_gpus_handle_add(struct wl_listener *listener, void *data) {
struct find_gpus_add_handler *handler =
wl_container_of(listener, handler, listener);
handler->added = true;
}
/* Tries to find the primary GPU by checking for the "boot_vga" attribute. /* Tries to find the primary GPU by checking for the "boot_vga" attribute.
* If it's not found, it returns the first valid GPU it finds. * If it's not found, it returns the first valid GPU it finds.
*/ */
size_t wlr_session_find_gpus(struct wlr_session *session, size_t wlr_session_find_gpus(struct wlr_session *session,
size_t ret_len, int *ret) { size_t ret_len, struct wlr_device **ret) {
const char *explicit = getenv("WLR_DRM_DEVICES"); const char *explicit = getenv("WLR_DRM_DEVICES");
if (explicit) { if (explicit) {
return explicit_find_gpus(session, ret_len, ret, explicit); return explicit_find_gpus(session, ret_len, ret, explicit);
} }
struct udev_enumerate *en = udev_enumerate_new(session->udev); struct udev_enumerate *en = enumerate_drm_cards(session->udev);
if (!en) { if (!en) {
wlr_log(WLR_ERROR, "Failed to create udev enumeration");
return -1; return -1;
} }
udev_enumerate_add_match_subsystem(en, "drm"); if (udev_enumerate_get_list_entry(en) == NULL) {
udev_enumerate_add_match_sysname(en, "card[0-9]*"); udev_enumerate_unref(en);
udev_enumerate_scan_devices(en); wlr_log(WLR_INFO, "Waiting for a DRM card device");
struct find_gpus_add_handler handler = {0};
handler.listener.notify = find_gpus_handle_add;
wl_signal_add(&session->events.add_drm_card, &handler.listener);
uint64_t started_at = get_current_time_ms();
uint64_t timeout = WAIT_GPU_TIMEOUT;
struct wl_event_loop *event_loop =
wl_display_get_event_loop(session->display);
while (!handler.added) {
int ret = wl_event_loop_dispatch(event_loop, (int)timeout);
if (ret < 0) {
wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: "
"wl_event_loop_dispatch failed");
udev_enumerate_unref(en);
return -1;
}
uint64_t now = get_current_time_ms();
if (now >= started_at + WAIT_GPU_TIMEOUT) {
break;
}
timeout = started_at + WAIT_GPU_TIMEOUT - now;
}
wl_list_remove(&handler.listener.link);
en = enumerate_drm_cards(session->udev);
if (!en) {
return -1;
}
}
struct udev_list_entry *entry; struct udev_list_entry *entry;
size_t i = 0; size_t i = 0;
@ -334,17 +429,18 @@ size_t wlr_session_find_gpus(struct wlr_session *session,
} }
} }
int fd = open_if_kms(session, udev_device_get_devnode(dev)); struct wlr_device *wlr_dev =
if (fd < 0) { open_if_kms(session, udev_device_get_devnode(dev));
if (!wlr_dev) {
udev_device_unref(dev); udev_device_unref(dev);
continue; continue;
} }
udev_device_unref(dev); udev_device_unref(dev);
ret[i] = fd; ret[i] = wlr_dev;
if (is_boot_vga) { if (is_boot_vga) {
int tmp = ret[0]; struct wlr_device *tmp = ret[0];
ret[0] = ret[i]; ret[0] = ret[i];
ret[i] = tmp; ret[i] = tmp;
} }

View file

@ -1,7 +1,10 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h> #include <assert.h>
#include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <wlr/config.h> #include <wlr/config.h>
@ -16,7 +19,11 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "backend/wayland.h" #include "backend/wayland.h"
#include "render/drm_format_set.h"
#include "render/gbm_allocator.h"
#include "render/wlr_renderer.h"
#include "util/signal.h" #include "util/signal.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "pointer-gestures-unstable-v1-client-protocol.h" #include "pointer-gestures-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h" #include "presentation-time-client-protocol.h"
@ -97,15 +104,17 @@ static void registry_global(void *data, struct wl_registry *registry,
uint32_t name, const char *iface, uint32_t version) { uint32_t name, const char *iface, uint32_t version) {
struct wlr_wl_backend *wl = data; struct wlr_wl_backend *wl = data;
wlr_log(WLR_DEBUG, "Remote wayland global: %s v%d", iface, version); wlr_log(WLR_DEBUG, "Remote wayland global: %s v%" PRIu32, iface, version);
if (strcmp(iface, wl_compositor_interface.name) == 0) { if (strcmp(iface, wl_compositor_interface.name) == 0) {
wl->compositor = wl_registry_bind(registry, name, wl->compositor = wl_registry_bind(registry, name,
&wl_compositor_interface, 4); &wl_compositor_interface, 4);
} else if (strcmp(iface, wl_seat_interface.name) == 0) { } else if (strcmp(iface, wl_seat_interface.name) == 0) {
wl->seat = wl_registry_bind(registry, name, struct wl_seat *wl_seat = wl_registry_bind(registry, name,
&wl_seat_interface, 5); &wl_seat_interface, 5);
wl_seat_add_listener(wl->seat, &seat_listener, wl); if (!create_wl_seat(wl_seat, wl)) {
wl_seat_destroy(wl_seat);
}
} else if (strcmp(iface, xdg_wm_base_interface.name) == 0) { } else if (strcmp(iface, xdg_wm_base_interface.name) == 0) {
wl->xdg_wm_base = wl_registry_bind(registry, name, wl->xdg_wm_base = wl_registry_bind(registry, name,
&xdg_wm_base_interface, 1); &xdg_wm_base_interface, 1);
@ -155,13 +164,15 @@ static bool backend_start(struct wlr_backend *backend) {
wl->started = true; wl->started = true;
if (wl->keyboard) { struct wlr_wl_seat *seat;
create_wl_keyboard(wl->keyboard, wl); wl_list_for_each(seat, &wl->seats, link) {
} if (seat->keyboard) {
create_wl_keyboard(seat);
}
if (wl->tablet_manager && wl->seat) { if (wl->tablet_manager) {
wl_add_tablet_seat(wl->tablet_manager, wl_add_tablet_seat(wl->tablet_manager, seat);
wl->seat, wl); }
} }
for (size_t i = 0; i < wl->requested_outputs; ++i) { for (size_t i = 0; i < wl->requested_outputs; ++i) {
@ -192,8 +203,6 @@ static void backend_destroy(struct wlr_backend *backend) {
wl_list_remove(&wl->local_display_destroy.link); wl_list_remove(&wl->local_display_destroy.link);
free(wl->seat_name);
wl_event_source_remove(wl->remote_display_src); wl_event_source_remove(wl->remote_display_src);
wlr_renderer_destroy(wl->renderer); wlr_renderer_destroy(wl->renderer);
@ -201,12 +210,12 @@ static void backend_destroy(struct wlr_backend *backend) {
wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats); wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats);
if (wl->pointer) { struct wlr_wl_buffer *buffer, *tmp_buffer;
wl_pointer_destroy(wl->pointer); wl_list_for_each_safe(buffer, tmp_buffer, &wl->buffers, link) {
} destroy_wl_buffer(buffer);
if (wl->seat) {
wl_seat_destroy(wl->seat);
} }
destroy_wl_seats(wl);
if (wl->zxdg_decoration_manager_v1) { if (wl->zxdg_decoration_manager_v1) {
zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1); zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1);
} }
@ -265,6 +274,8 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
wl->local_display = display; wl->local_display = display;
wl_list_init(&wl->devices); wl_list_init(&wl->devices);
wl_list_init(&wl->outputs); wl_list_init(&wl->outputs);
wl_list_init(&wl->seats);
wl_list_init(&wl->buffers);
wl->remote_display = wl_display_connect(remote); wl->remote_display = wl_display_connect(remote);
if (!wl->remote_display) { if (!wl->remote_display) {
@ -279,8 +290,8 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
} }
wl_registry_add_listener(wl->registry, &registry_listener, wl); wl_registry_add_listener(wl->registry, &registry_listener, wl);
wl_display_dispatch(wl->remote_display); wl_display_roundtrip(wl->remote_display); // get globals
wl_display_roundtrip(wl->remote_display); wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats
if (!wl->compositor) { if (!wl->compositor) {
wlr_log(WLR_ERROR, wlr_log(WLR_ERROR,
@ -304,28 +315,66 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
} }
wl_event_source_check(wl->remote_display_src); wl_event_source_check(wl->remote_display_src);
static EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 1,
EGL_NONE,
};
if (!create_renderer_func) { if (!create_renderer_func) {
create_renderer_func = wlr_renderer_autocreate; create_renderer_func = wlr_renderer_autocreate;
} }
wl->renderer = create_renderer_func(&wl->egl, EGL_PLATFORM_WAYLAND_EXT, wl->renderer = create_renderer_func(&wl->egl, EGL_PLATFORM_WAYLAND_EXT,
wl->remote_display, config_attribs, WL_SHM_FORMAT_ARGB8888); wl->remote_display, NULL, 0);
if (!wl->renderer) { if (!wl->renderer) {
wlr_log(WLR_ERROR, "Could not create renderer"); wlr_log(WLR_ERROR, "Could not create renderer");
goto error_event; goto error_event;
} }
// TODO: get FD from linux-dmabuf hints instead
int drm_fd = wlr_renderer_get_drm_fd(wl->renderer);
if (drm_fd < 0) {
wlr_log(WLR_ERROR, "Failed to get DRM device FD from renderer");
goto error_event;
}
drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0);
if (drm_fd < 0) {
wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed");
goto error_event;
}
struct wlr_gbm_allocator *alloc = wlr_gbm_allocator_create(drm_fd);
if (alloc == NULL) {
wlr_log(WLR_ERROR, "Failed to create GBM allocator");
goto error_event;
}
wl->allocator = &alloc->base;
uint32_t fmt = DRM_FORMAT_ARGB8888;
const struct wlr_drm_format *remote_format =
wlr_drm_format_set_get(&wl->linux_dmabuf_v1_formats, fmt);
if (remote_format == NULL) {
wlr_log(WLR_ERROR, "Remote compositor doesn't support format "
"0x%"PRIX32" via linux-dmabuf-unstable-v1", fmt);
goto error_event;
}
const struct wlr_drm_format_set *render_formats =
wlr_renderer_get_dmabuf_render_formats(wl->renderer);
if (render_formats == NULL) {
wlr_log(WLR_ERROR, "Failed to get available DMA-BUF formats from renderer");
return false;
}
const struct wlr_drm_format *render_format =
wlr_drm_format_set_get(render_formats, fmt);
if (render_format == NULL) {
wlr_log(WLR_ERROR, "Renderer doesn't support DRM format 0x%"PRIX32, fmt);
return false;
}
wl->format = wlr_drm_format_intersect(remote_format, render_format);
if (wl->format == NULL) {
wlr_log(WLR_ERROR, "Failed to intersect remote and render modifiers "
"for format 0x%"PRIX32, fmt);
return false;
}
wl->local_display_destroy.notify = handle_display_destroy; wl->local_display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &wl->local_display_destroy); wl_display_add_destroy_listener(display, &wl->local_display_destroy);

View file

@ -15,7 +15,10 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "backend/wayland.h" #include "backend/wayland.h"
#include "render/swapchain.h"
#include "render/wlr_renderer.h"
#include "util/signal.h" #include "util/signal.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h" #include "presentation-time-client-protocol.h"
#include "xdg-decoration-unstable-v1-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h"
@ -94,37 +97,69 @@ static const struct wp_presentation_feedback_listener
static bool output_set_custom_mode(struct wlr_output *wlr_output, static bool output_set_custom_mode(struct wlr_output *wlr_output,
int32_t width, int32_t height, int32_t refresh) { int32_t width, int32_t height, int32_t refresh) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
wl_egl_window_resize(output->egl_window, width, height, 0, 0);
if (wlr_output->width != width || wlr_output->height != height) {
struct wlr_swapchain *swapchain = wlr_swapchain_create(
output->backend->allocator, width, height, output->backend->format);
if (swapchain == NULL) {
return false;
}
wlr_swapchain_destroy(output->swapchain);
output->swapchain = swapchain;
}
wlr_output_update_custom_mode(&output->wlr_output, width, height, 0); wlr_output_update_custom_mode(&output->wlr_output, width, height, 0);
return true; return true;
} }
static bool output_attach_render(struct wlr_output *wlr_output, static bool output_attach_render(struct wlr_output *wlr_output,
int *buffer_age) { int *buffer_age) {
struct wlr_wl_output *output = struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
get_wl_output_from_output(wlr_output);
return wlr_egl_make_current(&output->backend->egl, output->egl_surface, wlr_buffer_unlock(output->back_buffer);
buffer_age); output->back_buffer = wlr_swapchain_acquire(output->swapchain, buffer_age);
if (!output->back_buffer) {
return false;
}
if (!wlr_egl_make_current(&output->backend->egl, EGL_NO_SURFACE, NULL)) {
return false;
}
if (!wlr_renderer_bind_buffer(output->backend->renderer,
output->back_buffer)) {
return false;
}
return true;
} }
static void destroy_wl_buffer(struct wlr_wl_buffer *buffer) { void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
if (buffer == NULL) { if (buffer == NULL) {
return; return;
} }
wl_list_remove(&buffer->buffer_destroy.link);
wl_list_remove(&buffer->link);
wl_buffer_destroy(buffer->wl_buffer); wl_buffer_destroy(buffer->wl_buffer);
wlr_buffer_unlock(buffer->buffer);
free(buffer); free(buffer);
} }
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
struct wlr_wl_buffer *buffer = data; struct wlr_wl_buffer *buffer = data;
destroy_wl_buffer(buffer); buffer->released = true;
wlr_buffer_unlock(buffer->buffer); // might free buffer
} }
static const struct wl_buffer_listener buffer_listener = { static const struct wl_buffer_listener buffer_listener = {
.release = buffer_handle_release, .release = buffer_handle_release,
}; };
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
void *data) {
struct wlr_wl_buffer *buffer =
wl_container_of(listener, buffer, buffer_destroy);
destroy_wl_buffer(buffer);
}
static bool test_buffer(struct wlr_wl_backend *wl, static bool test_buffer(struct wlr_wl_backend *wl,
struct wlr_buffer *wlr_buffer) { struct wlr_buffer *wlr_buffer) {
struct wlr_dmabuf_attributes attribs; struct wlr_dmabuf_attributes attribs;
@ -181,12 +216,33 @@ static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl,
} }
buffer->wl_buffer = wl_buffer; buffer->wl_buffer = wl_buffer;
buffer->buffer = wlr_buffer_lock(wlr_buffer); buffer->buffer = wlr_buffer_lock(wlr_buffer);
wl_list_insert(&wl->buffers, &buffer->link);
wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer); wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer);
buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy);
return buffer; return buffer;
} }
static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl,
struct wlr_buffer *wlr_buffer) {
struct wlr_wl_buffer *buffer;
wl_list_for_each(buffer, &wl->buffers, link) {
// We can only re-use a wlr_wl_buffer if the parent compositor has
// released it, because wl_buffer.release is per-wl_buffer, not per
// wl_surface.commit.
if (buffer->buffer == wlr_buffer && buffer->released) {
buffer->released = false;
wlr_buffer_lock(buffer->buffer);
return buffer;
}
}
return create_wl_buffer(wl, wlr_buffer);
}
static bool output_test(struct wlr_output *wlr_output) { static bool output_test(struct wlr_output *wlr_output) {
struct wlr_wl_output *output = struct wlr_wl_output *output =
get_wl_output_from_output(wlr_output); get_wl_output_from_output(wlr_output);
@ -246,39 +302,49 @@ static bool output_commit(struct wlr_output *wlr_output) {
output->frame_callback = wl_surface_frame(output->surface); output->frame_callback = wl_surface_frame(output->surface);
wl_callback_add_listener(output->frame_callback, &frame_listener, output); wl_callback_add_listener(output->frame_callback, &frame_listener, output);
struct wlr_buffer *wlr_buffer = NULL;
switch (wlr_output->pending.buffer_type) { switch (wlr_output->pending.buffer_type) {
case WLR_OUTPUT_STATE_BUFFER_RENDER: case WLR_OUTPUT_STATE_BUFFER_RENDER:
if (!wlr_egl_swap_buffers(&output->backend->egl, assert(output->back_buffer != NULL);
output->egl_surface, damage)) { wlr_buffer = output->back_buffer;
return false;
} wlr_renderer_bind_buffer(output->backend->renderer, NULL);
wlr_egl_unset_current(&output->backend->egl);
break; break;
case WLR_OUTPUT_STATE_BUFFER_SCANOUT:; case WLR_OUTPUT_STATE_BUFFER_SCANOUT:;
struct wlr_wl_buffer *buffer = wlr_buffer = wlr_output->pending.buffer;
create_wl_buffer(output->backend, wlr_output->pending.buffer);
if (buffer == NULL) {
return false;
}
wl_surface_attach(output->surface, buffer->wl_buffer, 0, 0);
if (damage == NULL) {
wl_surface_damage_buffer(output->surface,
0, 0, INT32_MAX, INT32_MAX);
} else {
int rects_len;
pixman_box32_t *rects =
pixman_region32_rectangles(damage, &rects_len);
for (int i = 0; i < rects_len; i++) {
pixman_box32_t *r = &rects[i];
wl_surface_damage_buffer(output->surface, r->x1, r->y1,
r->x2 - r->x1, r->y2 - r->y1);
}
}
wl_surface_commit(output->surface);
break; break;
} }
struct wlr_wl_buffer *buffer =
get_or_create_wl_buffer(output->backend, wlr_buffer);
if (buffer == NULL) {
return false;
}
wl_surface_attach(output->surface, buffer->wl_buffer, 0, 0);
if (damage == NULL) {
wl_surface_damage_buffer(output->surface,
0, 0, INT32_MAX, INT32_MAX);
} else {
int rects_len;
pixman_box32_t *rects =
pixman_region32_rectangles(damage, &rects_len);
for (int i = 0; i < rects_len; i++) {
pixman_box32_t *r = &rects[i];
wl_surface_damage_buffer(output->surface, r->x1, r->y1,
r->x2 - r->x1, r->y2 - r->y1);
}
}
wl_surface_commit(output->surface);
wlr_buffer_unlock(output->back_buffer);
output->back_buffer = NULL;
wlr_swapchain_set_buffer_submitted(output->swapchain, wlr_buffer);
if (wp_feedback != NULL) { if (wp_feedback != NULL) {
struct wlr_wl_presentation_feedback *feedback = struct wlr_wl_presentation_feedback *feedback =
calloc(1, sizeof(*feedback)); calloc(1, sizeof(*feedback));
@ -302,8 +368,8 @@ static bool output_commit(struct wlr_output *wlr_output) {
} }
static void output_rollback_render(struct wlr_output *wlr_output) { static void output_rollback_render(struct wlr_output *wlr_output) {
struct wlr_wl_output *output = struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
get_wl_output_from_output(wlr_output); wlr_renderer_bind_buffer(output->backend->renderer, NULL);
wlr_egl_unset_current(&output->backend->egl); wlr_egl_unset_current(&output->backend->egl);
} }
@ -341,19 +407,30 @@ static bool output_set_cursor(struct wlr_output *wlr_output,
width = width * wlr_output->scale / scale; width = width * wlr_output->scale / scale;
height = height * wlr_output->scale / scale; height = height * wlr_output->scale / scale;
output->cursor.width = width; if (output->cursor.swapchain == NULL ||
output->cursor.height = height; output->cursor.swapchain->width != width ||
output->cursor.swapchain->height != height) {
if (output->cursor.egl_window == NULL) { wlr_swapchain_destroy(output->cursor.swapchain);
output->cursor.egl_window = output->cursor.swapchain = wlr_swapchain_create(
wl_egl_window_create(surface, width, height); output->backend->allocator, width, height,
output->backend->format);
if (output->cursor.swapchain == NULL) {
return false;
}
} }
wl_egl_window_resize(output->cursor.egl_window, width, height, 0, 0);
EGLSurface egl_surface = struct wlr_buffer *wlr_buffer =
wlr_egl_create_surface(&backend->egl, output->cursor.egl_window); wlr_swapchain_acquire(output->cursor.swapchain, NULL);
if (wlr_buffer == NULL) {
return false;
}
wlr_egl_make_current(&backend->egl, egl_surface, NULL); if (!wlr_egl_make_current(&output->backend->egl, EGL_NO_SURFACE, NULL)) {
return false;
}
if (!wlr_renderer_bind_buffer(output->backend->renderer, wlr_buffer)) {
return false;
}
struct wlr_box cursor_box = { struct wlr_box cursor_box = {
.width = width, .width = width,
@ -371,8 +448,23 @@ static bool output_set_cursor(struct wlr_output *wlr_output,
wlr_render_texture_with_matrix(backend->renderer, texture, matrix, 1.0); wlr_render_texture_with_matrix(backend->renderer, texture, matrix, 1.0);
wlr_renderer_end(backend->renderer); wlr_renderer_end(backend->renderer);
wlr_egl_swap_buffers(&backend->egl, egl_surface, NULL); wlr_renderer_bind_buffer(output->backend->renderer, NULL);
wlr_egl_destroy_surface(&backend->egl, egl_surface); wlr_egl_unset_current(&output->backend->egl);
struct wlr_wl_buffer *buffer =
create_wl_buffer(output->backend, wlr_buffer);
if (buffer == NULL) {
return false;
}
wl_surface_attach(surface, buffer->wl_buffer, 0, 0);
wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(surface);
wlr_buffer_unlock(wlr_buffer);
output->cursor.width = width;
output->cursor.height = height;
} else { } else {
wl_surface_attach(surface, NULL, 0, 0); wl_surface_attach(surface, NULL, 0, 0);
wl_surface_commit(surface); wl_surface_commit(surface);
@ -390,9 +482,7 @@ static void output_destroy(struct wlr_output *wlr_output) {
wl_list_remove(&output->link); wl_list_remove(&output->link);
if (output->cursor.egl_window != NULL) { wlr_swapchain_destroy(output->cursor.swapchain);
wl_egl_window_destroy(output->cursor.egl_window);
}
if (output->cursor.surface) { if (output->cursor.surface) {
wl_surface_destroy(output->cursor.surface); wl_surface_destroy(output->cursor.surface);
} }
@ -407,8 +497,8 @@ static void output_destroy(struct wlr_output *wlr_output) {
presentation_feedback_destroy(feedback); presentation_feedback_destroy(feedback);
} }
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface); wlr_buffer_unlock(output->back_buffer);
wl_egl_window_destroy(output->egl_window); wlr_swapchain_destroy(output->swapchain);
if (output->zxdg_toplevel_decoration_v1) { if (output->zxdg_toplevel_decoration_v1) {
zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1); zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1);
} }
@ -419,8 +509,11 @@ static void output_destroy(struct wlr_output *wlr_output) {
} }
void update_wl_output_cursor(struct wlr_wl_output *output) { void update_wl_output_cursor(struct wlr_wl_output *output) {
if (output->backend->pointer && output->enter_serial) { struct wlr_wl_pointer *pointer = output->cursor.pointer;
wl_pointer_set_cursor(output->backend->pointer, output->enter_serial, if (pointer) {
assert(pointer->output == output);
assert(output->enter_serial);
wl_pointer_set_cursor(pointer->wl_pointer, output->enter_serial,
output->cursor.surface, output->cursor.hotspot_x, output->cursor.surface, output->cursor.hotspot_x,
output->cursor.hotspot_y); output->cursor.hotspot_y);
} }
@ -555,40 +648,29 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
&xdg_toplevel_listener, output); &xdg_toplevel_listener, output);
wl_surface_commit(output->surface); wl_surface_commit(output->surface);
output->egl_window = wl_egl_window_create(output->surface, output->swapchain = wlr_swapchain_create(output->backend->allocator,
wlr_output->width, wlr_output->height); wlr_output->width, wlr_output->height, output->backend->format);
output->egl_surface = wlr_egl_create_surface(&backend->egl, if (output->swapchain == NULL) {
output->egl_window); goto error;
}
wl_display_roundtrip(output->backend->remote_display); wl_display_roundtrip(output->backend->remote_display);
// start rendering loop per callbacks by rendering first frame
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
NULL)) {
goto error;
}
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
wlr_renderer_end(backend->renderer);
output->frame_callback = wl_surface_frame(output->surface);
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
if (!wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface,
NULL)) {
goto error;
}
wl_list_insert(&backend->outputs, &output->link); wl_list_insert(&backend->outputs, &output->link);
wlr_output_update_enabled(wlr_output, true); wlr_output_update_enabled(wlr_output, true);
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output); wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
if (backend->pointer != NULL) { struct wlr_wl_seat *seat;
create_wl_pointer(backend->pointer, output); wl_list_for_each(seat, &backend->seats, link) {
if (seat->pointer) {
create_wl_pointer(seat, output);
}
} }
// Start the rendering loop by requesting the compositor to render a frame
wlr_output_schedule_frame(wlr_output);
return wlr_output; return wlr_output;
error: error:

View file

@ -22,14 +22,16 @@
#include "util/signal.h" #include "util/signal.h"
#include "util/time.h" #include "util/time.h"
static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) { static struct wlr_wl_pointer *output_get_pointer(
struct wlr_wl_output *output,
const struct wl_pointer *wl_pointer) {
struct wlr_input_device *wlr_dev; struct wlr_input_device *wlr_dev;
wl_list_for_each(wlr_dev, &output->backend->devices, link) { wl_list_for_each(wlr_dev, &output->backend->devices, link) {
if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) { if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) {
continue; continue;
} }
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer); struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer);
if (pointer->output == output) { if (pointer->output == output && pointer->wl_pointer == wl_pointer) {
return pointer; return pointer;
} }
} }
@ -40,43 +42,54 @@ static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) {
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx,
wl_fixed_t sy) { wl_fixed_t sy) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
if (surface == NULL) { if (surface == NULL) {
return; return;
} }
struct wlr_wl_output *output = wl_surface_get_user_data(surface); struct wlr_wl_output *output = wl_surface_get_user_data(surface);
assert(output); assert(output);
struct wlr_wl_pointer *pointer = output_get_pointer(output); struct wlr_wl_pointer *pointer = output_get_pointer(output, wl_pointer);
seat->active_pointer = pointer;
// Manage cursor icon/rendering on output
struct wlr_wl_pointer *current_pointer = output->cursor.pointer;
if (current_pointer && current_pointer != pointer) {
wlr_log(WLR_INFO, "Ignoring seat %s pointer cursor in favor of seat %s",
seat->name, current_pointer->input_device->seat->name);
return;
}
output->enter_serial = serial; output->enter_serial = serial;
backend->current_pointer = pointer; output->cursor.pointer = pointer;
update_wl_output_cursor(output); update_wl_output_cursor(output);
} }
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface) { uint32_t serial, struct wl_surface *surface) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
if (surface == NULL) { if (surface == NULL) {
return; return;
} }
struct wlr_wl_output *output = wl_surface_get_user_data(surface); struct wlr_wl_output *output = wl_surface_get_user_data(surface);
assert(output); assert(output);
output->enter_serial = 0;
if (backend->current_pointer == NULL || if (seat->active_pointer != NULL &&
backend->current_pointer->output != output) { seat->active_pointer->output == output) {
return; seat->active_pointer = NULL;
} }
backend->current_pointer = NULL; if (output->cursor.pointer == seat->active_pointer) {
output->enter_serial = 0;
output->cursor.pointer = NULL;
}
} }
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t sx, wl_fixed_t sy) { uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = backend->current_pointer; struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) { if (pointer == NULL) {
return; return;
} }
@ -93,8 +106,8 @@ static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = backend->current_pointer; struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) { if (pointer == NULL) {
return; return;
} }
@ -110,8 +123,8 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value) { uint32_t time, uint32_t axis, wl_fixed_t value) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = backend->current_pointer; struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) { if (pointer == NULL) {
return; return;
} }
@ -130,8 +143,8 @@ static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
} }
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) { static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = backend->current_pointer; struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) { if (pointer == NULL) {
return; return;
} }
@ -142,8 +155,8 @@ static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
static void pointer_handle_axis_source(void *data, static void pointer_handle_axis_source(void *data,
struct wl_pointer *wl_pointer, uint32_t axis_source) { struct wl_pointer *wl_pointer, uint32_t axis_source) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = backend->current_pointer; struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) { if (pointer == NULL) {
return; return;
} }
@ -153,8 +166,8 @@ static void pointer_handle_axis_source(void *data,
static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis) { uint32_t time, uint32_t axis) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = backend->current_pointer; struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) { if (pointer == NULL) {
return; return;
} }
@ -172,8 +185,8 @@ static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
static void pointer_handle_axis_discrete(void *data, static void pointer_handle_axis_discrete(void *data,
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = backend->current_pointer; struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) { if (pointer == NULL) {
return; return;
} }
@ -208,7 +221,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
wl_array_for_each(keycode_ptr, keys) { wl_array_for_each(keycode_ptr, keys) {
struct wlr_event_keyboard_key event = { struct wlr_event_keyboard_key event = {
.keycode = *keycode_ptr, .keycode = *keycode_ptr,
.state = WLR_KEY_PRESSED, .state = WL_KEYBOARD_KEY_STATE_PRESSED,
.time_msec = time, .time_msec = time,
.update_state = false, .update_state = false,
}; };
@ -231,7 +244,7 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
struct wlr_event_keyboard_key event = { struct wlr_event_keyboard_key event = {
.keycode = keycode, .keycode = keycode,
.state = WLR_KEY_RELEASED, .state = WL_KEYBOARD_KEY_STATE_RELEASED,
.time_msec = time, .time_msec = time,
.update_state = false, .update_state = false,
}; };
@ -276,19 +289,153 @@ static struct wl_keyboard_listener keyboard_listener = {
.repeat_info = keyboard_handle_repeat_info .repeat_info = keyboard_handle_repeat_info
}; };
static void touch_coordinates_to_absolute(struct wlr_wl_input_device *device,
wl_fixed_t x, wl_fixed_t y, double *sx, double *sy) {
// TODO: each output needs its own touch
struct wlr_wl_output *output, *tmp;
wl_list_for_each_safe(output, tmp, &device->backend->outputs, link) {
*sx = wl_fixed_to_double(x) / output->wlr_output.width;
*sy = wl_fixed_to_double(y) / output->wlr_output.height;
return; // Choose the first output in the list
}
*sx = *sy = 0;
}
static void touch_handle_down(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, struct wl_surface *surface,
int32_t id, wl_fixed_t x, wl_fixed_t y) {
struct wlr_wl_input_device *device = data;
assert(device && device->wlr_input_device.touch);
double sx, sy;
touch_coordinates_to_absolute(device, x, y, &sx, &sy);
struct wlr_event_touch_down event = {
.device = &device->wlr_input_device,
.time_msec = time,
.touch_id = id,
.x = sx,
.y = sy
};
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.down, &event);
}
static void touch_handle_up(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, int32_t id) {
struct wlr_wl_input_device *device = data;
assert(device && device->wlr_input_device.touch);
struct wlr_event_touch_up event = {
.device = &device->wlr_input_device,
.time_msec = time,
.touch_id = id,
};
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.up, &event);
}
static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) {
struct wlr_wl_input_device *device = data;
assert(device && device->wlr_input_device.touch);
double sx, sy;
touch_coordinates_to_absolute(device, x, y, &sx, &sy);
struct wlr_event_touch_motion event = {
.device = &device->wlr_input_device,
.time_msec = time,
.touch_id = id,
.x = sx,
.y = sy
};
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.motion, &event);
}
static void touch_handle_frame(void *data, struct wl_touch *wl_touch) {
// no-op
}
static void touch_handle_cancel(void *data, struct wl_touch *wl_touch) {
// no-op
}
static void touch_handle_shape(void *data, struct wl_touch *wl_touch,
int32_t id, wl_fixed_t major, wl_fixed_t minor) {
// no-op
}
static void touch_handle_orientation(void *data, struct wl_touch *wl_touch,
int32_t id, wl_fixed_t orientation) {
// no-op
}
static struct wl_touch_listener touch_listener = {
.down = touch_handle_down,
.up = touch_handle_up,
.motion = touch_handle_motion,
.frame = touch_handle_frame,
.cancel = touch_handle_cancel,
.shape = touch_handle_shape,
.orientation = touch_handle_orientation,
};
static struct wlr_wl_input_device *get_wl_input_device_from_input_device( static struct wlr_wl_input_device *get_wl_input_device_from_input_device(
struct wlr_input_device *wlr_dev) { struct wlr_input_device *wlr_dev) {
assert(wlr_input_device_is_wl(wlr_dev)); assert(wlr_input_device_is_wl(wlr_dev));
return (struct wlr_wl_input_device *)wlr_dev; return (struct wlr_wl_input_device *)wlr_dev;
} }
bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl) {
struct wlr_wl_seat *seat = calloc(1, sizeof(struct wlr_wl_seat));
if (!seat) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return false;
}
seat->wl_seat = wl_seat;
seat->backend = wl;
wl_list_insert(&wl->seats, &seat->link);
wl_seat_add_listener(wl_seat, &seat_listener, seat);
return true;
}
void destroy_wl_seats(struct wlr_wl_backend *wl) {
struct wlr_wl_seat *seat, *tmp_seat;
wl_list_for_each_safe(seat, tmp_seat, &wl->seats, link) {
if (seat->touch) {
wl_touch_destroy(seat->touch);
}
if (seat->pointer) {
wl_pointer_destroy(seat->pointer);
}
if (seat->keyboard && !wl->started) {
// early termination will not be handled by input_device_destroy
wl_keyboard_destroy(seat->keyboard);
}
free(seat->name);
assert(seat->wl_seat);
wl_seat_destroy(seat->wl_seat);
wl_list_remove(&seat->link);
free(seat);
}
}
static struct wlr_wl_seat *input_device_get_seat(struct wlr_input_device *wlr_dev) {
struct wlr_wl_input_device *dev =
get_wl_input_device_from_input_device(wlr_dev);
assert(dev->seat);
return dev->seat;
}
static void input_device_destroy(struct wlr_input_device *wlr_dev) { static void input_device_destroy(struct wlr_input_device *wlr_dev) {
struct wlr_wl_input_device *dev = struct wlr_wl_input_device *dev =
get_wl_input_device_from_input_device(wlr_dev); get_wl_input_device_from_input_device(wlr_dev);
if (dev->wlr_input_device.type == WLR_INPUT_DEVICE_KEYBOARD) { if (dev->wlr_input_device.type == WLR_INPUT_DEVICE_KEYBOARD) {
wl_keyboard_release(dev->backend->keyboard); struct wlr_wl_seat *seat = input_device_get_seat(wlr_dev);
dev->backend->keyboard = NULL; wl_keyboard_release(seat->keyboard);
seat->keyboard = NULL;
} }
// We can't destroy pointer here because we might have multiple devices
// exposing it to compositor.
wl_list_remove(&dev->wlr_input_device.link); wl_list_remove(&dev->wlr_input_device.link);
free(dev); free(dev);
} }
@ -302,22 +449,27 @@ bool wlr_input_device_is_wl(struct wlr_input_device *dev) {
} }
struct wlr_wl_input_device *create_wl_input_device( struct wlr_wl_input_device *create_wl_input_device(
struct wlr_wl_backend *backend, enum wlr_input_device_type type) { struct wlr_wl_seat *seat, enum wlr_input_device_type type) {
struct wlr_wl_input_device *dev = struct wlr_wl_input_device *dev =
calloc(1, sizeof(struct wlr_wl_input_device)); calloc(1, sizeof(struct wlr_wl_input_device));
if (dev == NULL) { if (dev == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed"); wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL; return NULL;
} }
dev->backend = backend; dev->backend = seat->backend;
dev->seat = seat;
struct wlr_input_device *wlr_dev = &dev->wlr_input_device; struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
unsigned int vendor = 0, product = 0; unsigned int vendor = 0, product = 0;
const char *name = "wayland";
size_t name_size = 8 + strlen(seat->name) + 1;
char name[name_size];
(void) snprintf(name, name_size, "wayland-%s", seat->name);
wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor, wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor,
product); product);
wl_list_insert(&backend->devices, &wlr_dev->link); wl_list_insert(&seat->backend->devices, &wlr_dev->link);
return dev; return dev;
} }
@ -331,8 +483,13 @@ struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) {
static void pointer_destroy(struct wlr_pointer *wlr_pointer) { static void pointer_destroy(struct wlr_pointer *wlr_pointer) {
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_pointer); struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_pointer);
if (pointer->output->backend->current_pointer == pointer) { if (pointer->output->cursor.pointer == pointer) {
pointer->output->backend->current_pointer = NULL; pointer->output->cursor.pointer = NULL;
}
struct wlr_wl_seat *seat = pointer->input_device->seat;
if (seat->active_pointer == pointer) {
seat->active_pointer = NULL;
} }
wl_list_remove(&pointer->output_destroy.link); wl_list_remove(&pointer->output_destroy.link);
@ -450,6 +607,9 @@ static void relative_pointer_handle_relative_motion(void *data,
wl_fixed_t dy_unaccel) { wl_fixed_t dy_unaccel) {
struct wlr_wl_input_device *input_device = data; struct wlr_wl_input_device *input_device = data;
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device; struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
if (pointer_get_wl(wlr_dev->pointer) != input_device->seat->active_pointer) {
return;
}
uint64_t time_usec = (uint64_t)utime_hi << 32 | utime_lo; uint64_t time_usec = (uint64_t)utime_hi << 32 | utime_lo;
@ -473,21 +633,22 @@ static void pointer_handle_output_destroy(struct wl_listener *listener,
void *data) { void *data) {
struct wlr_wl_pointer *pointer = struct wlr_wl_pointer *pointer =
wl_container_of(listener, pointer, output_destroy); wl_container_of(listener, pointer, output_destroy);
if (pointer->relative_pointer) {
zwp_relative_pointer_v1_destroy(pointer->relative_pointer);
}
wlr_input_device_destroy(&pointer->input_device->wlr_input_device); wlr_input_device_destroy(&pointer->input_device->wlr_input_device);
} }
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output) { void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output) {
assert(seat->pointer);
struct wl_pointer *wl_pointer = seat->pointer;
struct wlr_wl_backend *backend = output->backend; struct wlr_wl_backend *backend = output->backend;
struct wlr_input_device *wlr_dev; if (output_get_pointer(output, wl_pointer)) {
wl_list_for_each(wlr_dev, &output->backend->devices, link) { wlr_log(WLR_DEBUG,
if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) { "Pointer for seat %s and output %s already exists (ignoring)",
continue; seat->name, output->wlr_output.name);
} return;
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer);
if (pointer->output == output) {
return;
}
} }
struct wlr_wl_pointer *pointer = calloc(1, sizeof(struct wlr_wl_pointer)); struct wlr_wl_pointer *pointer = calloc(1, sizeof(struct wlr_wl_pointer));
@ -496,13 +657,10 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp
return; return;
} }
pointer->wl_pointer = wl_pointer; pointer->wl_pointer = wl_pointer;
pointer->output = output; pointer->output = output; // we need output to map absolute coordinates onto
wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy);
pointer->output_destroy.notify = pointer_handle_output_destroy;
struct wlr_wl_input_device *dev = struct wlr_wl_input_device *dev =
create_wl_input_device(backend, WLR_INPUT_DEVICE_POINTER); create_wl_input_device(seat, WLR_INPUT_DEVICE_POINTER);
if (dev == NULL) { if (dev == NULL) {
free(pointer); free(pointer);
wlr_log(WLR_ERROR, "Allocation failed"); wlr_log(WLR_ERROR, "Allocation failed");
@ -510,7 +668,10 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp
} }
pointer->input_device = dev; pointer->input_device = dev;
wlr_dev = &dev->wlr_input_device; wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy);
pointer->output_destroy.notify = pointer_handle_output_destroy;
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
wlr_dev->pointer = &pointer->wlr_pointer; wlr_dev->pointer = &pointer->wlr_pointer;
wlr_dev->output_name = strdup(output->wlr_output.name); wlr_dev->output_name = strdup(output->wlr_output.name);
wlr_pointer_init(wlr_dev->pointer, &pointer_impl); wlr_pointer_init(wlr_dev->pointer, &pointer_impl);
@ -532,13 +693,15 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp
&relative_pointer_listener, dev); &relative_pointer_listener, dev);
} }
wl_pointer_add_listener(wl_pointer, &pointer_listener, backend); wl_pointer_add_listener(wl_pointer, &pointer_listener, seat);
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev); wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev);
} }
void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl) { void create_wl_keyboard(struct wlr_wl_seat *seat) {
assert(seat->keyboard);
struct wl_keyboard *wl_keyboard = seat->keyboard;
struct wlr_wl_input_device *dev = struct wlr_wl_input_device *dev =
create_wl_input_device(wl, WLR_INPUT_DEVICE_KEYBOARD); create_wl_input_device(seat, WLR_INPUT_DEVICE_KEYBOARD);
if (!dev) { if (!dev) {
return; return;
} }
@ -548,75 +711,138 @@ void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *
wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard)); wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard));
if (!wlr_dev->keyboard) { if (!wlr_dev->keyboard) {
wlr_log_errno(WLR_ERROR, "Allocation failed"); wlr_log_errno(WLR_ERROR, "Allocation failed");
free(dev); wlr_input_device_destroy(wlr_dev);
return; return;
} }
wlr_keyboard_init(wlr_dev->keyboard, NULL); wlr_keyboard_init(wlr_dev->keyboard, NULL);
wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev); wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev);
wlr_signal_emit_safe(&wl->backend.events.new_input, wlr_dev); wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev);
} }
void create_wl_touch(struct wlr_wl_seat *seat) {
assert(seat->touch);
struct wl_touch *wl_touch = seat->touch;
struct wlr_wl_input_device *dev =
create_wl_input_device(seat, WLR_INPUT_DEVICE_TOUCH);
if (!dev) {
return;
}
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
wlr_dev->touch = calloc(1, sizeof(*wlr_dev->touch));
if (!wlr_dev->touch) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
wlr_input_device_destroy(wlr_dev);
return;
}
wlr_touch_init(wlr_dev->touch, NULL);
wl_touch_add_listener(wl_touch, &touch_listener, dev);
wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev);
}
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps) { enum wl_seat_capability caps) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
assert(backend->seat == wl_seat); struct wlr_wl_backend *backend = seat->backend;
if ((caps & WL_SEAT_CAPABILITY_POINTER) && backend->pointer == NULL) { if ((caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer == NULL) {
wlr_log(WLR_DEBUG, "seat %p offered pointer", (void *)wl_seat); wlr_log(WLR_DEBUG, "seat %p offered pointer", (void *)wl_seat);
struct wl_pointer *wl_pointer = wl_seat_get_pointer(wl_seat); struct wl_pointer *wl_pointer = wl_seat_get_pointer(wl_seat);
backend->pointer = wl_pointer; seat->pointer = wl_pointer;
struct wlr_wl_output *output; struct wlr_wl_output *output;
wl_list_for_each(output, &backend->outputs, link) { wl_list_for_each(output, &backend->outputs, link) {
create_wl_pointer(wl_pointer, output); create_wl_pointer(seat, output);
} }
} }
if (!(caps & WL_SEAT_CAPABILITY_POINTER) && backend->pointer != NULL) { if (!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer != NULL) {
wlr_log(WLR_DEBUG, "seat %p dropped pointer", (void *)wl_seat); wlr_log(WLR_DEBUG, "seat %p dropped pointer", (void *)wl_seat);
struct wl_pointer *wl_pointer = seat->pointer;
struct wlr_input_device *device, *tmp; struct wlr_input_device *device, *tmp;
wl_list_for_each_safe(device, tmp, &backend->devices, link) { wl_list_for_each_safe(device, tmp, &backend->devices, link) {
if (device->type == WLR_INPUT_DEVICE_POINTER) { if (device->type != WLR_INPUT_DEVICE_POINTER) {
wlr_input_device_destroy(device); continue;
} }
struct wlr_wl_pointer *pointer = pointer_get_wl(device->pointer);
if (pointer->wl_pointer != wl_pointer) {
continue;
}
wlr_log(WLR_DEBUG, "dropping pointer %s",
pointer->input_device->wlr_input_device.name);
struct wlr_wl_output *output = pointer->output;
wlr_input_device_destroy(device);
assert(seat->active_pointer != pointer);
assert(output->cursor.pointer != pointer);
} }
wl_pointer_release(backend->pointer); wl_pointer_release(seat->pointer);
backend->pointer = NULL; seat->pointer = NULL;
} }
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && backend->keyboard == NULL) { if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard == NULL) {
wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void *)wl_seat); wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void *)wl_seat);
struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat); struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat);
backend->keyboard = wl_keyboard; seat->keyboard = wl_keyboard;
if (backend->started) { if (backend->started) {
create_wl_keyboard(wl_keyboard, backend); create_wl_keyboard(seat);
} }
} }
if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && backend->keyboard != NULL) { if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard != NULL) {
wlr_log(WLR_DEBUG, "seat %p dropped keyboard", (void *)wl_seat); wlr_log(WLR_DEBUG, "seat %p dropped keyboard", (void *)wl_seat);
struct wlr_input_device *device, *tmp; struct wlr_input_device *device, *tmp;
wl_list_for_each_safe(device, tmp, &backend->devices, link) { wl_list_for_each_safe(device, tmp, &backend->devices, link) {
if (device->type == WLR_INPUT_DEVICE_KEYBOARD) { if (device->type != WLR_INPUT_DEVICE_KEYBOARD) {
continue;
}
struct wlr_wl_input_device *input_device =
get_wl_input_device_from_input_device(device);
if (input_device->seat != seat) {
continue;
}
wlr_input_device_destroy(device);
}
assert(seat->keyboard == NULL); // free'ed by input_device_destroy
}
if ((caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch == NULL) {
wlr_log(WLR_DEBUG, "seat %p offered touch", (void *)wl_seat);
seat->touch = wl_seat_get_touch(wl_seat);
if (backend->started) {
create_wl_touch(seat);
}
}
if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch != NULL) {
wlr_log(WLR_DEBUG, "seat %p dropped touch", (void *)wl_seat);
struct wlr_input_device *device, *tmp;
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
if (device->type == WLR_INPUT_DEVICE_TOUCH) {
wlr_input_device_destroy(device); wlr_input_device_destroy(device);
} }
} }
assert(backend->keyboard == NULL); // free'ed by input_device_destroy
wl_touch_release(seat->touch);
seat->touch = NULL;
} }
} }
static void seat_handle_name(void *data, struct wl_seat *wl_seat, static void seat_handle_name(void *data, struct wl_seat *wl_seat,
const char *name) { const char *name) {
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
assert(backend->seat == wl_seat); free(seat->name);
// Do we need to check if seatName was previously set for name change? seat->name = strdup(name);
free(backend->seat_name);
backend->seat_name = strdup(name);
} }
const struct wl_seat_listener seat_listener = { const struct wl_seat_listener seat_listener = {
@ -625,7 +851,5 @@ const struct wl_seat_listener seat_listener = {
}; };
struct wl_seat *wlr_wl_input_device_get_seat(struct wlr_input_device *wlr_dev) { struct wl_seat *wlr_wl_input_device_get_seat(struct wlr_input_device *wlr_dev) {
struct wlr_wl_input_device *dev = return input_device_get_seat(wlr_dev)->wl_seat;
get_wl_input_device_from_input_device(wlr_dev);
return dev->backend->seat;
} }

View file

@ -429,9 +429,9 @@ static void handle_pad_added(void *data,
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
struct zwp_tablet_pad_v2 *id) { struct zwp_tablet_pad_v2 *id) {
wlr_log(WLR_DEBUG, "New tablet pad"); wlr_log(WLR_DEBUG, "New tablet pad");
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
struct wlr_wl_input_device *dev = create_wl_input_device( struct wlr_wl_input_device *dev = create_wl_input_device(
backend, WLR_INPUT_DEVICE_TABLET_PAD); seat, WLR_INPUT_DEVICE_TABLET_PAD);
if (!dev) { if (!dev) {
/* This leaks a couple of server-sent resource ids. iirc this /* This leaks a couple of server-sent resource ids. iirc this
* shouldn't ever be a problem, but it isn't exactly nice * shouldn't ever be a problem, but it isn't exactly nice
@ -889,9 +889,9 @@ static void handle_tab_added(void *data,
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
struct zwp_tablet_v2 *id) { struct zwp_tablet_v2 *id) {
wlr_log(WLR_DEBUG, "New tablet"); wlr_log(WLR_DEBUG, "New tablet");
struct wlr_wl_backend *backend = data; struct wlr_wl_seat *seat = data;
struct wlr_wl_input_device *dev = create_wl_input_device( struct wlr_wl_input_device *dev = create_wl_input_device(
backend, WLR_INPUT_DEVICE_TABLET_TOOL); seat, WLR_INPUT_DEVICE_TABLET_TOOL);
if (!dev) { if (!dev) {
zwp_tablet_v2_destroy(id); zwp_tablet_v2_destroy(id);
@ -919,18 +919,18 @@ static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
struct wlr_wl_tablet_seat *wl_add_tablet_seat( struct wlr_wl_tablet_seat *wl_add_tablet_seat(
struct zwp_tablet_manager_v2 *manager, struct zwp_tablet_manager_v2 *manager,
struct wl_seat *seat, struct wlr_wl_backend *backend) { struct wlr_wl_seat *seat) {
struct wlr_wl_tablet_seat *ret = struct wlr_wl_tablet_seat *ret =
calloc(1, sizeof(struct wlr_wl_tablet_seat)); calloc(1, sizeof(struct wlr_wl_tablet_seat));
if (!(ret->tablet_seat = if (!(ret->tablet_seat =
zwp_tablet_manager_v2_get_tablet_seat(manager, seat))) { zwp_tablet_manager_v2_get_tablet_seat(manager, seat->wl_seat))) {
free(ret); free(ret);
return NULL; return NULL;
} }
zwp_tablet_seat_v2_add_listener(ret->tablet_seat, zwp_tablet_seat_v2_add_listener(ret->tablet_seat,
&tablet_seat_listener, backend); &tablet_seat_listener, seat);
return ret; return ret;
} }

View file

@ -1,17 +1,21 @@
#define _POSIX_C_SOURCE 200112L #define _POSIX_C_SOURCE 200809L
#include <assert.h> #include <assert.h>
#include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
#include <unistd.h>
#include <wlr/config.h> #include <wlr/config.h>
#include <drm_fourcc.h>
#include <X11/Xlib-xcb.h> #include <X11/Xlib-xcb.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/dri3.h>
#include <xcb/present.h>
#include <xcb/xfixes.h> #include <xcb/xfixes.h>
#include <xcb/xinput.h> #include <xcb/xinput.h>
@ -25,8 +29,26 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "backend/x11.h" #include "backend/x11.h"
#include "render/drm_format_set.h"
#include "render/gbm_allocator.h"
#include "render/wlr_renderer.h"
#include "util/signal.h" #include "util/signal.h"
// See dri2_format_for_depth in mesa
const struct wlr_x11_format formats[] = {
{ .drm = DRM_FORMAT_XRGB8888, .depth = 24, .bpp = 32 },
{ .drm = DRM_FORMAT_ARGB8888, .depth = 32, .bpp = 32 },
};
static const struct wlr_x11_format *x11_format_from_depth(uint8_t depth) {
for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
if (formats[i].depth == depth) {
return &formats[i];
}
}
return NULL;
}
struct wlr_x11_output *get_x11_output_from_window_id( struct wlr_x11_output *get_x11_output_from_window_id(
struct wlr_x11_backend *x11, xcb_window_t window) { struct wlr_x11_backend *x11, xcb_window_t window) {
struct wlr_x11_output *output; struct wlr_x11_output *output;
@ -38,6 +60,10 @@ struct wlr_x11_output *get_x11_output_from_window_id(
return NULL; return NULL;
} }
static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev);
static void handle_x11_unknown_event(struct wlr_x11_backend *x11,
xcb_generic_event_t *ev);
static void handle_x11_event(struct wlr_x11_backend *x11, static void handle_x11_event(struct wlr_x11_backend *x11,
xcb_generic_event_t *event) { xcb_generic_event_t *event) {
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
@ -68,6 +94,9 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
if (output != NULL) { if (output != NULL) {
wlr_output_destroy(&output->wlr_output); wlr_output_destroy(&output->wlr_output);
} }
} else {
wlr_log(WLR_DEBUG, "Unhandled client message %"PRIu32,
ev->data.data32[0]);
} }
break; break;
} }
@ -75,8 +104,21 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event; xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event;
if (ev->extension == x11->xinput_opcode) { if (ev->extension == x11->xinput_opcode) {
handle_x11_xinput_event(x11, ev); handle_x11_xinput_event(x11, ev);
} else if (ev->extension == x11->present_opcode) {
handle_x11_present_event(x11, ev);
} else {
handle_x11_unknown_event(x11, event);
} }
break;
} }
case 0: {
xcb_value_error_t *ev = (xcb_value_error_t *)event;
handle_x11_error(x11, ev);
break;
}
default:
handle_x11_unknown_event(x11, event);
break;
} }
} }
@ -139,6 +181,13 @@ static void backend_destroy(struct wlr_backend *backend) {
wlr_renderer_destroy(x11->renderer); wlr_renderer_destroy(x11->renderer);
wlr_egl_finish(&x11->egl); wlr_egl_finish(&x11->egl);
wlr_allocator_destroy(x11->allocator);
wlr_drm_format_set_finish(&x11->dri3_formats);
free(x11->drm_format);
#if WLR_HAS_XCB_ERRORS
xcb_errors_context_free(x11->errors_context);
#endif
if (x11->xlib_conn) { if (x11->xlib_conn) {
XCloseDisplay(x11->xlib_conn); XCloseDisplay(x11->xlib_conn);
@ -168,6 +217,110 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
backend_destroy(&x11->backend); backend_destroy(&x11->backend);
} }
static xcb_depth_t *get_depth(xcb_screen_t *screen, uint8_t depth) {
xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen);
while (iter.rem > 0) {
if (iter.data->depth == depth) {
return iter.data;
}
xcb_depth_next(&iter);
}
return NULL;
}
static xcb_visualid_t pick_visualid(xcb_depth_t *depth) {
xcb_visualtype_t *visuals = xcb_depth_visuals(depth);
for (int i = 0; i < xcb_depth_visuals_length(depth); i++) {
if (visuals[i]._class == XCB_VISUAL_CLASS_TRUE_COLOR) {
return visuals[i].visual_id;
}
}
return 0;
}
static int query_dri3_drm_fd(struct wlr_x11_backend *x11) {
xcb_dri3_open_cookie_t open_cookie =
xcb_dri3_open(x11->xcb, x11->screen->root, 0);
xcb_dri3_open_reply_t *open_reply =
xcb_dri3_open_reply(x11->xcb, open_cookie, NULL);
if (open_reply == NULL) {
return -1;
}
int *open_fds = xcb_dri3_open_reply_fds(x11->xcb, open_reply);
if (open_fds == NULL) {
free(open_reply);
return -1;
}
assert(open_reply->nfd == 1);
int drm_fd = open_fds[0];
free(open_reply);
int flags = fcntl(drm_fd, F_GETFD);
if (flags < 0) {
close(drm_fd);
return -1;
}
if (fcntl(drm_fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
close(drm_fd);
return -1;
}
return drm_fd;
}
static bool query_dri3_modifiers(struct wlr_x11_backend *x11,
const struct wlr_x11_format *format) {
// Query the root window's supported modifiers, because we only care about
// screen_modifiers for now
xcb_dri3_get_supported_modifiers_cookie_t modifiers_cookie =
xcb_dri3_get_supported_modifiers(x11->xcb, x11->screen->root,
format->depth, format->bpp);
xcb_dri3_get_supported_modifiers_reply_t *modifiers_reply =
xcb_dri3_get_supported_modifiers_reply(x11->xcb, modifiers_cookie,
NULL);
if (!modifiers_reply) {
wlr_log(WLR_ERROR, "Failed to get DMA-BUF modifiers supported by "
"the X11 server for the format 0x%"PRIX32, format->drm);
return false;
}
// If modifiers aren't supported, DRI3 will return an empty list
const uint64_t *modifiers =
xcb_dri3_get_supported_modifiers_screen_modifiers(modifiers_reply);
int modifiers_len =
xcb_dri3_get_supported_modifiers_screen_modifiers_length(modifiers_reply);
for (int i = 0; i < modifiers_len; i++) {
wlr_drm_format_set_add(&x11->dri3_formats, format->drm, modifiers[i]);
}
free(modifiers_reply);
return true;
}
static bool query_dri3_formats(struct wlr_x11_backend *x11) {
xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(x11->screen);
while (iter.rem > 0) {
uint8_t depth = iter.data->depth;
const struct wlr_x11_format *format = x11_format_from_depth(depth);
if (format != NULL) {
wlr_drm_format_set_add(&x11->dri3_formats, format->drm,
DRM_FORMAT_MOD_INVALID);
if (!query_dri3_modifiers(x11, format)) {
return false;
}
}
xcb_depth_next(&iter);
}
return true;
}
struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
const char *x11_display, const char *x11_display,
wlr_renderer_create_func_t create_renderer_func) { wlr_renderer_create_func_t create_renderer_func) {
@ -225,6 +378,47 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
const xcb_query_extension_reply_t *ext; const xcb_query_extension_reply_t *ext;
// DRI3 extension
ext = xcb_get_extension_data(x11->xcb, &xcb_dri3_id);
if (!ext || !ext->present) {
wlr_log(WLR_ERROR, "X11 does not support DRI3 extension");
goto error_display;
}
xcb_dri3_query_version_cookie_t dri3_cookie =
xcb_dri3_query_version(x11->xcb, 1, 2);
xcb_dri3_query_version_reply_t *dri3_reply =
xcb_dri3_query_version_reply(x11->xcb, dri3_cookie, NULL);
if (!dri3_reply || dri3_reply->major_version < 1 ||
dri3_reply->minor_version < 2) {
wlr_log(WLR_ERROR, "X11 does not support required DRI3 version");
goto error_display;
}
free(dri3_reply);
// Present extension
ext = xcb_get_extension_data(x11->xcb, &xcb_present_id);
if (!ext || !ext->present) {
wlr_log(WLR_ERROR, "X11 does not support Present extension");
goto error_display;
}
x11->present_opcode = ext->major_opcode;
xcb_present_query_version_cookie_t present_cookie =
xcb_present_query_version(x11->xcb, 1, 2);
xcb_present_query_version_reply_t *present_reply =
xcb_present_query_version_reply(x11->xcb, present_cookie, NULL);
if (!present_reply || present_reply->major_version < 1) {
wlr_log(WLR_ERROR, "X11 does not support required Present version");
free(present_reply);
goto error_display;
}
free(present_reply);
// Xfixes extension
ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id); ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id);
if (!ext || !ext->present) { if (!ext || !ext->present) {
wlr_log(WLR_ERROR, "X11 does not support Xfixes extension"); wlr_log(WLR_ERROR, "X11 does not support Xfixes extension");
@ -243,6 +437,8 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
} }
free(fixes_reply); free(fixes_reply);
// Xinput extension
ext = xcb_get_extension_data(x11->xcb, &xcb_input_id); ext = xcb_get_extension_data(x11->xcb, &xcb_input_id);
if (!ext || !ext->present) { if (!ext || !ext->present) {
wlr_log(WLR_ERROR, "X11 does not support Xinput extension"); wlr_log(WLR_ERROR, "X11 does not support Xinput extension");
@ -273,29 +469,103 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
wl_event_source_check(x11->event_source); wl_event_source_check(x11->event_source);
x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data; x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data;
if (!x11->screen) {
wlr_log(WLR_ERROR, "Failed to get X11 screen");
goto error_event;
}
x11->depth = get_depth(x11->screen, 32);
if (!x11->depth) {
wlr_log(WLR_ERROR, "Failed to get 32-bit depth for X11 screen");
goto error_event;
}
x11->visualid = pick_visualid(x11->depth);
if (!x11->visualid) {
wlr_log(WLR_ERROR, "Failed to pick X11 visual");
goto error_event;
}
x11->x11_format = x11_format_from_depth(x11->depth->depth);
if (!x11->x11_format) {
wlr_log(WLR_ERROR, "Unsupported depth %"PRIu8, x11->depth->depth);
goto error_event;
}
x11->colormap = xcb_generate_id(x11->xcb);
xcb_create_colormap(x11->xcb, XCB_COLORMAP_ALLOC_NONE, x11->colormap,
x11->screen->root, x11->visualid);
// DRI3 may return a render node (Xwayland) or an authenticated primary
// node (plain Glamor).
int drm_fd = query_dri3_drm_fd(x11);
if (drm_fd < 0) {
wlr_log(WLR_ERROR, "Failed to query DRI3 DRM FD");
goto error_event;
}
struct wlr_gbm_allocator *gbm_alloc = wlr_gbm_allocator_create(drm_fd);
if (gbm_alloc == NULL) {
wlr_log(WLR_ERROR, "Failed to create GBM allocator");
close(drm_fd);
goto error_event;
}
x11->allocator = &gbm_alloc->base;
if (!create_renderer_func) { if (!create_renderer_func) {
create_renderer_func = wlr_renderer_autocreate; create_renderer_func = wlr_renderer_autocreate;
} }
static EGLint config_attribs[] = { x11->renderer = create_renderer_func(&x11->egl, EGL_PLATFORM_GBM_KHR,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT, gbm_alloc->gbm_device, NULL, 0);
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 0,
EGL_NONE,
};
x11->renderer = create_renderer_func(&x11->egl, EGL_PLATFORM_X11_KHR,
x11->xlib_conn, config_attribs, x11->screen->root_visual);
if (x11->renderer == NULL) { if (x11->renderer == NULL) {
wlr_log(WLR_ERROR, "Failed to create renderer"); wlr_log(WLR_ERROR, "Failed to create renderer");
goto error_event; goto error_event;
} }
const struct wlr_drm_format_set *render_formats =
wlr_renderer_get_dmabuf_render_formats(x11->renderer);
if (render_formats == NULL) {
wlr_log(WLR_ERROR, "Failed to get available DMA-BUF formats from renderer");
return false;
}
const struct wlr_drm_format *render_format =
wlr_drm_format_set_get(render_formats, x11->x11_format->drm);
if (render_format == NULL) {
wlr_log(WLR_ERROR, "Renderer doesn't support DRM format 0x%"PRIX32,
x11->x11_format->drm);
return false;
}
if (!query_dri3_formats(x11)) {
wlr_log(WLR_ERROR, "Failed to query supported DRI3 formats");
return false;
}
const struct wlr_drm_format *dri3_format =
wlr_drm_format_set_get(&x11->dri3_formats, x11->x11_format->drm);
if (dri3_format == NULL) {
wlr_log(WLR_ERROR, "X11 server doesn't support DRM format 0x%"PRIX32,
x11->x11_format->drm);
return false;
}
x11->drm_format = wlr_drm_format_intersect(dri3_format, render_format);
if (x11->drm_format == NULL) {
wlr_log(WLR_ERROR, "Failed to intersect DRI3 and render modifiers for "
"format 0x%"PRIX32, x11->x11_format->drm);
return false;
}
#if WLR_HAS_XCB_ERRORS
if (xcb_errors_context_new(x11->xcb, &x11->errors_context) != 0) {
wlr_log(WLR_ERROR, "Failed to create error context");
return false;
}
#endif
x11->present_event_id = xcb_generate_id(x11->xcb);
wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD, wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD,
&input_device_impl, "X11 keyboard", 0, 0); &input_device_impl, "X11 keyboard", 0, 0);
wlr_keyboard_init(&x11->keyboard, &keyboard_impl); wlr_keyboard_init(&x11->keyboard, &keyboard_impl);
@ -314,3 +584,58 @@ error_x11:
free(x11); free(x11);
return NULL; return NULL;
} }
static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev) {
#if WLR_HAS_XCB_ERRORS
const char *major_name = xcb_errors_get_name_for_major_code(
x11->errors_context, ev->major_opcode);
if (!major_name) {
wlr_log(WLR_DEBUG, "X11 error happened, but could not get major name");
goto log_raw;
}
const char *minor_name = xcb_errors_get_name_for_minor_code(
x11->errors_context, ev->major_opcode, ev->minor_opcode);
const char *extension;
const char *error_name = xcb_errors_get_name_for_error(x11->errors_context,
ev->error_code, &extension);
if (!error_name) {
wlr_log(WLR_DEBUG, "X11 error happened, but could not get error name");
goto log_raw;
}
wlr_log(WLR_ERROR, "X11 error: op %s (%s), code %s (%s), "
"sequence %"PRIu16", value %"PRIu32,
major_name, minor_name ? minor_name : "no minor",
error_name, extension ? extension : "no extension",
ev->sequence, ev->bad_value);
return;
log_raw:
#endif
wlr_log(WLR_ERROR, "X11 error: op %"PRIu8":%"PRIu16", code %"PRIu8", "
"sequence %"PRIu16", value %"PRIu32,
ev->major_opcode, ev->minor_opcode, ev->error_code,
ev->sequence, ev->bad_value);
}
static void handle_x11_unknown_event(struct wlr_x11_backend *x11,
xcb_generic_event_t *ev) {
#if WLR_HAS_XCB_ERRORS
const char *extension;
const char *event_name = xcb_errors_get_name_for_xcb_event(
x11->errors_context, ev, &extension);
if (!event_name) {
wlr_log(WLR_DEBUG, "No name for unhandled event: %u",
ev->response_type);
return;
}
wlr_log(WLR_DEBUG, "Unhandled X11 event: %s (%u)", event_name, ev->response_type);
#else
wlr_log(WLR_DEBUG, "Unhandled X11 event: %u", ev->response_type);
#endif
}

View file

@ -4,6 +4,8 @@
#include <linux/input-event-codes.h> #include <linux/input-event-codes.h>
#include <wayland-server-protocol.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xfixes.h> #include <xcb/xfixes.h>
#include <xcb/xinput.h> #include <xcb/xinput.h>
@ -18,7 +20,7 @@
#include "util/signal.h" #include "util/signal.h"
static void send_key_event(struct wlr_x11_backend *x11, uint32_t key, static void send_key_event(struct wlr_x11_backend *x11, uint32_t key,
enum wlr_key_state st, xcb_timestamp_t time) { enum wl_keyboard_key_state st, xcb_timestamp_t time) {
struct wlr_event_keyboard_key ev = { struct wlr_event_keyboard_key ev = {
.time_msec = time, .time_msec = time,
.keycode = key, .keycode = key,
@ -123,7 +125,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base, wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
ev->mods.latched, ev->mods.locked, ev->mods.effective); ev->mods.latched, ev->mods.locked, ev->mods.effective);
send_key_event(x11, ev->detail - 8, WLR_KEY_PRESSED, ev->time); send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_PRESSED, ev->time);
x11->time = ev->time; x11->time = ev->time;
break; break;
} }
@ -133,7 +135,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base, wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
ev->mods.latched, ev->mods.locked, ev->mods.effective); ev->mods.latched, ev->mods.locked, ev->mods.effective);
send_key_event(x11, ev->detail - 8, WLR_KEY_RELEASED, ev->time); send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_RELEASED, ev->time);
x11->time = ev->time; x11->time = ev->time;
break; break;
} }

View file

@ -2,8 +2,10 @@ x11_libs = []
x11_required = [ x11_required = [
'x11-xcb', 'x11-xcb',
'xcb', 'xcb',
'xcb-xinput', 'xcb-dri3',
'xcb-present',
'xcb-xfixes', 'xcb-xfixes',
'xcb-xinput',
] ]
msg = [] msg = []

View file

@ -4,6 +4,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <drm_fourcc.h>
#include <xcb/dri3.h>
#include <xcb/present.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xinput.h> #include <xcb/xinput.h>
@ -13,7 +16,10 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "backend/x11.h" #include "backend/x11.h"
#include "render/swapchain.h"
#include "render/wlr_renderer.h"
#include "util/signal.h" #include "util/signal.h"
#include "util/time.h"
static int signal_frame(void *data) { static int signal_frame(void *data) {
struct wlr_x11_output *output = data; struct wlr_x11_output *output = data;
@ -76,6 +82,8 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output,
return true; return true;
} }
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer);
static void output_destroy(struct wlr_output *wlr_output) { static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11; struct wlr_x11_backend *x11 = output->x11;
@ -83,9 +91,15 @@ static void output_destroy(struct wlr_output *wlr_output) {
wlr_input_device_destroy(&output->pointer_dev); wlr_input_device_destroy(&output->pointer_dev);
wlr_input_device_destroy(&output->touch_dev); wlr_input_device_destroy(&output->touch_dev);
struct wlr_x11_buffer *buffer, *buffer_tmp;
wl_list_for_each_safe(buffer, buffer_tmp, &output->buffers, link) {
destroy_x11_buffer(buffer);
}
wl_list_remove(&output->link); wl_list_remove(&output->link);
wl_event_source_remove(output->frame_timer); wl_event_source_remove(output->frame_timer);
wlr_egl_destroy_surface(&x11->egl, output->surf); wlr_buffer_unlock(output->back_buffer);
wlr_swapchain_destroy(output->swapchain);
xcb_destroy_window(x11->xcb, output->win); xcb_destroy_window(x11->xcb, output->win);
xcb_flush(x11->xcb); xcb_flush(x11->xcb);
free(output); free(output);
@ -96,7 +110,20 @@ static bool output_attach_render(struct wlr_output *wlr_output,
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11; struct wlr_x11_backend *x11 = output->x11;
return wlr_egl_make_current(&x11->egl, output->surf, buffer_age); wlr_buffer_unlock(output->back_buffer);
output->back_buffer = wlr_swapchain_acquire(output->swapchain, buffer_age);
if (!output->back_buffer) {
return false;
}
if (!wlr_egl_make_current(&x11->egl, EGL_NO_SURFACE, NULL)) {
return false;
}
if (!wlr_renderer_bind_buffer(x11->renderer, output->back_buffer)) {
return false;
}
return true;
} }
static bool output_test(struct wlr_output *wlr_output) { static bool output_test(struct wlr_output *wlr_output) {
@ -112,6 +139,147 @@ static bool output_test(struct wlr_output *wlr_output) {
return true; return true;
} }
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer) {
if (!buffer) {
return;
}
wl_list_remove(&buffer->buffer_destroy.link);
wl_list_remove(&buffer->link);
xcb_free_pixmap(buffer->x11->xcb, buffer->pixmap);
free(buffer);
}
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
void *data) {
struct wlr_x11_buffer *buffer =
wl_container_of(listener, buffer, buffer_destroy);
destroy_x11_buffer(buffer);
}
static struct wlr_x11_buffer *create_x11_buffer(struct wlr_x11_output *output,
struct wlr_buffer *wlr_buffer) {
struct wlr_x11_backend *x11 = output->x11;
struct wlr_dmabuf_attributes attrs = {0};
if (!wlr_buffer_get_dmabuf(wlr_buffer, &attrs)) {
return NULL;
}
if (attrs.format != x11->x11_format->drm) {
// The pixmap's depth must match the window's depth, otherwise Present
// will throw a Match error
return NULL;
}
// xcb closes the FDs after sending them, so we need to dup them here
struct wlr_dmabuf_attributes dup_attrs = {0};
if (!wlr_dmabuf_attributes_copy(&dup_attrs, &attrs)) {
return NULL;
}
const struct wlr_x11_format *x11_fmt = x11->x11_format;
xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb);
xcb_dri3_pixmap_from_buffers(x11->xcb, pixmap, output->win,
attrs.n_planes, attrs.width, attrs.height, attrs.stride[0],
attrs.offset[0], attrs.stride[1], attrs.offset[1], attrs.stride[2],
attrs.offset[2], attrs.stride[3], attrs.offset[3], x11_fmt->depth,
x11_fmt->bpp, attrs.modifier, dup_attrs.fd);
struct wlr_x11_buffer *buffer = calloc(1, sizeof(struct wlr_x11_buffer));
if (!buffer) {
xcb_free_pixmap(x11->xcb, pixmap);
return NULL;
}
buffer->buffer = wlr_buffer_lock(wlr_buffer);
buffer->pixmap = pixmap;
buffer->x11 = x11;
wl_list_insert(&output->buffers, &buffer->link);
buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy);
return buffer;
}
static struct wlr_x11_buffer *get_or_create_x11_buffer(
struct wlr_x11_output *output, struct wlr_buffer *wlr_buffer) {
struct wlr_x11_buffer *buffer;
wl_list_for_each(buffer, &output->buffers, link) {
if (buffer->buffer == wlr_buffer) {
wlr_buffer_lock(buffer->buffer);
return buffer;
}
}
return create_x11_buffer(output, wlr_buffer);
}
static bool output_commit_buffer(struct wlr_x11_output *output) {
struct wlr_x11_backend *x11 = output->x11;
assert(output->back_buffer != NULL);
wlr_renderer_bind_buffer(x11->renderer, NULL);
wlr_egl_unset_current(&x11->egl);
struct wlr_x11_buffer *x11_buffer =
get_or_create_x11_buffer(output, output->back_buffer);
if (!x11_buffer) {
goto error;
}
xcb_xfixes_region_t region = XCB_NONE;
if (output->wlr_output.pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
pixman_region32_t *damage = &output->wlr_output.pending.damage;
int rects_len = 0;
pixman_box32_t *rects = pixman_region32_rectangles(damage, &rects_len);
xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t));
if (!xcb_rects) {
goto error;
}
for (int i = 0; i < rects_len; i++) {
pixman_box32_t *box = &rects[i];
xcb_rects[i] = (struct xcb_rectangle_t){
.x = box->x1,
.y = box->y1,
.width = box->x2 - box->x1,
.height = box->y2 - box->y1,
};
}
xcb_xfixes_region_t region = xcb_generate_id(x11->xcb);
xcb_xfixes_create_region(x11->xcb, region, rects_len, xcb_rects);
free(xcb_rects);
}
uint32_t serial = output->wlr_output.commit_seq;
uint32_t options = 0;
xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial,
0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, 0, 0, 0,
0, NULL);
if (region != XCB_NONE) {
xcb_xfixes_destroy_region(x11->xcb, region);
}
wlr_buffer_unlock(output->back_buffer);
output->back_buffer = NULL;
wlr_swapchain_set_buffer_submitted(output->swapchain, x11_buffer->buffer);
return true;
error:
destroy_x11_buffer(x11_buffer);
wlr_buffer_unlock(output->back_buffer);
output->back_buffer = NULL;
return false;
}
static bool output_commit(struct wlr_output *wlr_output) { static bool output_commit(struct wlr_output *wlr_output) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11; struct wlr_x11_backend *x11 = output->x11;
@ -145,24 +313,21 @@ static bool output_commit(struct wlr_output *wlr_output) {
} }
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
pixman_region32_t *damage = NULL; if (!output_commit_buffer(output)) {
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
damage = &wlr_output->pending.damage;
}
if (!wlr_egl_swap_buffers(&x11->egl, output->surf, damage)) {
return false; return false;
} }
wlr_output_send_present(wlr_output, NULL);
} }
xcb_flush(x11->xcb);
return true; return true;
} }
static void output_rollback_render(struct wlr_output *wlr_output) { static void output_rollback_render(struct wlr_output *wlr_output) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
wlr_egl_unset_current(&output->x11->egl); struct wlr_x11_backend *x11 = output->x11;
wlr_renderer_bind_buffer(x11->renderer, NULL);
wlr_egl_unset_current(&x11->egl);
} }
static const struct wlr_output_impl output_impl = { static const struct wlr_output_impl output_impl = {
@ -186,6 +351,7 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
return NULL; return NULL;
} }
output->x11 = x11; output->x11 = x11;
wl_list_init(&output->buffers);
struct wlr_output *wlr_output = &output->wlr_output; struct wlr_output *wlr_output = &output->wlr_output;
wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display); wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display);
@ -193,6 +359,14 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
wlr_output->width = 1024; wlr_output->width = 1024;
wlr_output->height = 768; wlr_output->height = 768;
output->swapchain = wlr_swapchain_create(x11->allocator,
wlr_output->width, wlr_output->height, x11->drm_format);
if (!output->swapchain) {
wlr_log(WLR_ERROR, "Failed to create swapchain");
free(output);
return NULL;
}
output_set_refresh(&output->wlr_output, 0); output_set_refresh(&output->wlr_output, 0);
snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%zd", snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%zd",
@ -204,14 +378,18 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
"X11 output %zd", x11->last_output_num); "X11 output %zd", x11->last_output_num);
wlr_output_set_description(wlr_output, description); wlr_output_set_description(wlr_output, description);
uint32_t mask = XCB_CW_EVENT_MASK; // The X11 protocol requires us to set a colormap and border pixel if the
// depth doesn't match the root window's
uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
uint32_t values[] = { uint32_t values[] = {
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY 0,
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY,
x11->colormap,
}; };
output->win = xcb_generate_id(x11->xcb); output->win = xcb_generate_id(x11->xcb);
xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win, xcb_create_window(x11->xcb, x11->depth->depth, output->win,
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1, x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->screen->root_visual, mask, values); XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->visualid, mask, values);
struct { struct {
xcb_input_event_mask_t head; xcb_input_event_mask_t head;
@ -231,12 +409,10 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
}; };
xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head); xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head);
output->surf = wlr_egl_create_surface(&x11->egl, &output->win); uint32_t present_mask = XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY |
if (!output->surf) { XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY;
wlr_log(WLR_ERROR, "Failed to create EGL surface"); xcb_present_select_input(x11->xcb, x11->present_event_id, output->win,
free(output); present_mask);
return NULL;
}
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win, xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1, x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1,
@ -278,17 +454,30 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
void handle_x11_configure_notify(struct wlr_x11_output *output, void handle_x11_configure_notify(struct wlr_x11_output *output,
xcb_configure_notify_event_t *ev) { xcb_configure_notify_event_t *ev) {
// ignore events that set an invalid size: // ignore events that set an invalid size:
if (ev->width > 0 && ev->height > 0) { if (ev->width == 0 || ev->height == 0) {
wlr_output_update_custom_mode(&output->wlr_output, ev->width,
ev->height, output->wlr_output.refresh);
// Move the pointer to its new location
update_x11_pointer_position(output, output->x11->time);
} else {
wlr_log(WLR_DEBUG, wlr_log(WLR_DEBUG,
"Ignoring X11 configure event for height=%d, width=%d", "Ignoring X11 configure event for height=%d, width=%d",
ev->width, ev->height); ev->width, ev->height);
return;
} }
if (output->swapchain->width != ev->width ||
output->swapchain->height != ev->height) {
struct wlr_swapchain *swapchain = wlr_swapchain_create(
output->x11->allocator, ev->width, ev->height,
output->x11->drm_format);
if (!swapchain) {
return;
}
wlr_swapchain_destroy(output->swapchain);
output->swapchain = swapchain;
}
wlr_output_update_custom_mode(&output->wlr_output, ev->width,
ev->height, output->wlr_output.refresh);
// Move the pointer to its new location
update_x11_pointer_position(output, output->x11->time);
} }
bool wlr_output_is_x11(struct wlr_output *wlr_output) { bool wlr_output_is_x11(struct wlr_output *wlr_output) {
@ -310,3 +499,70 @@ void wlr_x11_output_set_title(struct wlr_output *output, const char *title) {
x11_output->x11->atoms.net_wm_name, x11_output->x11->atoms.utf8_string, 8, x11_output->x11->atoms.net_wm_name, x11_output->x11->atoms.utf8_string, 8,
strlen(title), title); strlen(title), title);
} }
static struct wlr_x11_buffer *get_x11_buffer(struct wlr_x11_output *output,
xcb_pixmap_t pixmap) {
struct wlr_x11_buffer *buffer;
wl_list_for_each(buffer, &output->buffers, link) {
if (buffer->pixmap == pixmap) {
return buffer;
}
}
return NULL;
}
void handle_x11_present_event(struct wlr_x11_backend *x11,
xcb_ge_generic_event_t *event) {
struct wlr_x11_output *output;
switch (event->event_type) {
case XCB_PRESENT_EVENT_IDLE_NOTIFY:;
xcb_present_idle_notify_event_t *idle_notify =
(xcb_present_idle_notify_event_t *)event;
output = get_x11_output_from_window_id(x11, idle_notify->window);
if (!output) {
wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown window");
return;
}
struct wlr_x11_buffer *buffer =
get_x11_buffer(output, idle_notify->pixmap);
if (!buffer) {
wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown buffer");
return;
}
wlr_buffer_unlock(buffer->buffer); // may destroy buffer
break;
case XCB_PRESENT_COMPLETE_NOTIFY:;
xcb_present_complete_notify_event_t *complete_notify =
(xcb_present_complete_notify_event_t *)event;
output = get_x11_output_from_window_id(x11, complete_notify->window);
if (!output) {
wlr_log(WLR_DEBUG, "Got PresentCompleteNotify event for unknown window");
return;
}
struct timespec t;
timespec_from_nsec(&t, complete_notify->ust * 1000);
uint32_t flags = 0;
if (complete_notify->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) {
flags |= WLR_OUTPUT_PRESENT_ZERO_COPY;
}
struct wlr_output_event_present present_event = {
.output = &output->wlr_output,
.commit_seq = complete_notify->serial,
.when = &t,
.seq = complete_notify->msc,
.flags = flags,
};
wlr_output_send_present(&output->wlr_output, &present_event);
break;
default:
wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type);
}
}

View file

@ -9,6 +9,8 @@ wlroots reads these environment variables
* *WLR_SESSION*: specifies the wlr\_session to be used (available sessions: * *WLR_SESSION*: specifies the wlr\_session to be used (available sessions:
logind/systemd, direct) logind/systemd, direct)
* *WLR_DIRECT_TTY*: specifies the tty to be used (instead of using /dev/tty) * *WLR_DIRECT_TTY*: specifies the tty to be used (instead of using /dev/tty)
* *WLR_XWAYLAND*: specifies the path to an Xwayland binary to be used (instead
of following shell search semantics for "Xwayland")
## DRM backend ## DRM backend

View file

@ -753,8 +753,11 @@ static int init(struct capture_context *ctx) {
ctx->registry = wl_display_get_registry(ctx->display); ctx->registry = wl_display_get_registry(ctx->display);
wl_registry_add_listener(ctx->registry, &registry_listener, ctx); wl_registry_add_listener(ctx->registry, &registry_listener, ctx);
// First roundtrip to fetch globals
wl_display_roundtrip(ctx->display);
// Second roundtrip to fetch wl_output information
wl_display_roundtrip(ctx->display); wl_display_roundtrip(ctx->display);
wl_display_dispatch(ctx->display);
if (!ctx->export_manager) { if (!ctx->export_manager) {
av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n", av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n",

View file

@ -7,7 +7,7 @@
#include <wayland-client.h> #include <wayland-client.h>
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" #include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 2 #define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 3
/** /**
* Usage: * Usage:
@ -38,11 +38,14 @@ enum toplevel_state_field {
TOPLEVEL_STATE_INVALID = (1 << 4), TOPLEVEL_STATE_INVALID = (1 << 4),
}; };
static const uint32_t no_parent = (uint32_t)-1;
struct toplevel_state { struct toplevel_state {
char *title; char *title;
char *app_id; char *app_id;
uint32_t state; uint32_t state;
uint32_t parent_id;
}; };
static void copy_state(struct toplevel_state *current, static void copy_state(struct toplevel_state *current,
@ -67,6 +70,8 @@ static void copy_state(struct toplevel_state *current,
current->state = pending->state; current->state = pending->state;
} }
current->parent_id = pending->parent_id;
pending->state = TOPLEVEL_STATE_INVALID; pending->state = TOPLEVEL_STATE_INVALID;
} }
@ -84,6 +89,12 @@ static void print_toplevel(struct toplevel_v1 *toplevel, bool print_endl) {
toplevel->current.title ?: "(nil)", toplevel->current.title ?: "(nil)",
toplevel->current.app_id ?: "(nil)"); toplevel->current.app_id ?: "(nil)");
if (toplevel->current.parent_id != no_parent) {
printf(" parent=%u", toplevel->current.parent_id);
} else {
printf(" no parent");
}
if (print_endl) { if (print_endl) {
printf("\n"); printf("\n");
} }
@ -172,6 +183,28 @@ static void toplevel_handle_state(void *data,
toplevel->pending.state = array_to_state(state); toplevel->pending.state = array_to_state(state);
} }
static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL;
static struct wl_list toplevel_list;
static void toplevel_handle_parent(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_parent) {
struct toplevel_v1 *toplevel = data;
toplevel->pending.parent_id = no_parent;
if (zwlr_parent) {
struct toplevel_v1 *toplevel_tmp;
wl_list_for_each(toplevel_tmp, &toplevel_list, link) {
if (toplevel_tmp->zwlr_toplevel == zwlr_parent) {
toplevel->pending.parent_id = toplevel_tmp->id;
break;
}
}
if (toplevel->pending.parent_id == no_parent) {
fprintf(stderr, "Cannot find parent toplevel!\n");
}
}
}
static void toplevel_handle_done(void *data, static void toplevel_handle_done(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) { struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
struct toplevel_v1 *toplevel = data; struct toplevel_v1 *toplevel = data;
@ -202,11 +235,9 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
.state = toplevel_handle_state, .state = toplevel_handle_state,
.done = toplevel_handle_done, .done = toplevel_handle_done,
.closed = toplevel_handle_closed, .closed = toplevel_handle_closed,
.parent = toplevel_handle_parent
}; };
static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL;
static struct wl_list toplevel_list;
static void toplevel_manager_handle_toplevel(void *data, static void toplevel_manager_handle_toplevel(void *data,
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager, struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) { struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
@ -218,6 +249,8 @@ static void toplevel_manager_handle_toplevel(void *data,
toplevel->id = global_id++; toplevel->id = global_id++;
toplevel->zwlr_toplevel = zwlr_toplevel; toplevel->zwlr_toplevel = zwlr_toplevel;
toplevel->current.parent_id = no_parent;
toplevel->pending.parent_id = no_parent;
wl_list_insert(&toplevel_list, &toplevel->link); wl_list_insert(&toplevel_list, &toplevel->link);
zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl, zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl,
@ -332,7 +365,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (toplevel_manager == NULL) { if (toplevel_manager == NULL) {

View file

@ -162,7 +162,6 @@ int main(int argc, char *argv[]) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (gamma_control_manager == NULL) { if (gamma_control_manager == NULL) {

View file

@ -177,7 +177,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (compositor == NULL) { if (compositor == NULL) {

View file

@ -125,7 +125,6 @@ int main(int argc, char *argv[]) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
wl_registry_destroy(registry); wl_registry_destroy(registry);

View file

@ -150,7 +150,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
assert(registry); assert(registry);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
assert(compositor && seat && wm_base && input_inhibit_manager); assert(compositor && seat && wm_base && input_inhibit_manager);

View file

@ -191,7 +191,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (input_method_manager == NULL) { if (input_method_manager == NULL) {

View file

@ -324,7 +324,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (compositor == NULL) { if (compositor == NULL) {

View file

@ -209,7 +209,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (compositor == NULL) { if (compositor == NULL) {

View file

@ -1,4 +1,5 @@
threads = dependency('threads') threads = dependency('threads')
wayland_egl = dependency('wayland-egl')
wayland_cursor = dependency('wayland-cursor') wayland_cursor = dependency('wayland-cursor')
libpng = dependency('libpng', required: false, disabler: true) libpng = dependency('libpng', required: false, disabler: true)
# These versions correspond to ffmpeg 4.0 # These versions correspond to ffmpeg 4.0
@ -54,7 +55,7 @@ clients = {
}, },
'idle-inhibit': { 'idle-inhibit': {
'src': 'idle-inhibit.c', 'src': 'idle-inhibit.c',
'dep': wlroots, 'dep': [wayland_egl, wlroots],
'proto': [ 'proto': [
'idle-inhibit-unstable-v1', 'idle-inhibit-unstable-v1',
'xdg-shell', 'xdg-shell',
@ -62,7 +63,7 @@ clients = {
}, },
'keyboard-shortcuts-inhibit': { 'keyboard-shortcuts-inhibit': {
'src': 'keyboard-shortcuts-inhibit.c', 'src': 'keyboard-shortcuts-inhibit.c',
'dep': [wayland_cursor, wlroots], 'dep': [wayland_egl, wayland_cursor, wlroots],
'proto': [ 'proto': [
'keyboard-shortcuts-inhibit-unstable-v1', 'keyboard-shortcuts-inhibit-unstable-v1',
'xdg-shell', 'xdg-shell',
@ -70,7 +71,7 @@ clients = {
}, },
'layer-shell': { 'layer-shell': {
'src': 'layer-shell.c', 'src': 'layer-shell.c',
'dep': [wayland_cursor, wlroots], 'dep': [wayland_egl, wayland_cursor, wlroots],
'proto': [ 'proto': [
'wlr-layer-shell-unstable-v1', 'wlr-layer-shell-unstable-v1',
'xdg-shell', 'xdg-shell',
@ -78,7 +79,7 @@ clients = {
}, },
'input-inhibitor': { 'input-inhibitor': {
'src': 'input-inhibitor.c', 'src': 'input-inhibitor.c',
'dep': [wayland_cursor, wlroots], 'dep': [wayland_egl, wayland_cursor, wlroots],
'proto': [ 'proto': [
'wlr-input-inhibitor-unstable-v1', 'wlr-input-inhibitor-unstable-v1',
'xdg-shell', 'xdg-shell',
@ -96,7 +97,7 @@ clients = {
}, },
'pointer-constraints': { 'pointer-constraints': {
'src': 'pointer-constraints.c', 'src': 'pointer-constraints.c',
'dep': wlroots, 'dep': [wayland_egl, wlroots],
'proto': [ 'proto': [
'pointer-constraints-unstable-v1', 'pointer-constraints-unstable-v1',
'xdg-shell', 'xdg-shell',
@ -104,7 +105,7 @@ clients = {
}, },
'relative-pointer': { 'relative-pointer': {
'src': 'relative-pointer-unstable-v1.c', 'src': 'relative-pointer-unstable-v1.c',
'dep': wlroots, 'dep': [wayland_egl, wlroots],
'proto': [ 'proto': [
'pointer-constraints-unstable-v1', 'pointer-constraints-unstable-v1',
'relative-pointer-unstable-v1', 'relative-pointer-unstable-v1',
@ -137,7 +138,7 @@ clients = {
}, },
'toplevel-decoration': { 'toplevel-decoration': {
'src': 'toplevel-decoration.c', 'src': 'toplevel-decoration.c',
'dep': wlroots, 'dep': [wayland_egl, wlroots],
'proto': [ 'proto': [
'xdg-decoration-unstable-v1', 'xdg-decoration-unstable-v1',
'xdg-shell', 'xdg-shell',
@ -145,7 +146,7 @@ clients = {
}, },
'input-method': { 'input-method': {
'src': 'input-method.c', 'src': 'input-method.c',
'dep': libepoll, 'dep': [wayland_egl, libepoll],
'proto': [ 'proto': [
'input-method-unstable-v2', 'input-method-unstable-v2',
'text-input-unstable-v3', 'text-input-unstable-v3',
@ -154,7 +155,7 @@ clients = {
}, },
'text-input': { 'text-input': {
'src': 'text-input.c', 'src': 'text-input.c',
'dep': [wayland_cursor, wlroots], 'dep': [wayland_egl, wayland_cursor, wlroots],
'proto': [ 'proto': [
'text-input-unstable-v3', 'text-input-unstable-v3',
'xdg-shell', 'xdg-shell',

View file

@ -10,13 +10,13 @@
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_keyboard.h> #include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_list.h> #include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <wlr/xcursor.h> #include <wlr/xcursor.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>

View file

@ -190,7 +190,7 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
// and make this change in pixels/sec^2 // and make this change in pixels/sec^2
// Also, key repeat // Also, key repeat
int delta = 75; int delta = 75;
if (event->state == WLR_KEY_PRESSED) { if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
switch (sym) { switch (sym) {
case XKB_KEY_Left: case XKB_KEY_Left:
update_velocities(sample, -delta, 0); update_velocities(sample, -delta, 0);

View file

@ -109,12 +109,11 @@ int main(int argc, char *argv[]) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (output_power_manager == NULL) { if (output_power_manager == NULL) {
fprintf(stderr, fprintf(stderr,
"compositor doesn't support wlr-output-power-managment-unstable-v1\n"); "compositor doesn't support wlr-output-power-management-unstable-v1\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View file

@ -198,7 +198,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
struct wl_region *disjoint_region = wl_compositor_create_region(compositor); struct wl_region *disjoint_region = wl_compositor_create_region(compositor);

View file

@ -9,13 +9,15 @@
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_keyboard.h> #include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_list.h> #include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_tablet_tool.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_xcursor_manager.h> #include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>

View file

@ -412,7 +412,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
/* Check that all the global interfaces were captured */ /* Check that all the global interfaces were captured */

View file

@ -140,7 +140,7 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
if (sym == XKB_KEY_Escape) { if (sym == XKB_KEY_Escape) {
wl_display_terminate(sample->display); wl_display_terminate(sample->display);
} }
if (event->state == WLR_KEY_PRESSED) { if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
switch (sym) { switch (sym) {
case XKB_KEY_Left: case XKB_KEY_Left:
update_velocities(sample, -16, 0); update_velocities(sample, -16, 0);

View file

@ -297,7 +297,7 @@ int main(int argc, char *argv[]) {
drm_fd = open(render_node, O_RDWR); drm_fd = open(render_node, O_RDWR);
if (drm_fd < 0) { if (drm_fd < 0) {
fprintf(stderr, "Failed to open drm render node: %m\n"); perror("Failed to open drm render node");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -309,13 +309,12 @@ int main(int argc, char *argv[]) {
struct wl_display *display = wl_display_connect(NULL); struct wl_display *display = wl_display_connect(NULL);
if (display == NULL) { if (display == NULL) {
fprintf(stderr, "failed to create display: %m\n"); perror("failed to create display");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (dmabuf == NULL) { if (dmabuf == NULL) {
@ -344,7 +343,7 @@ int main(int argc, char *argv[]) {
void *data = gbm_bo_map(buffer.bo, 0, 0, buffer.width, buffer.height, void *data = gbm_bo_map(buffer.bo, 0, 0, buffer.width, buffer.height,
GBM_BO_TRANSFER_READ, &stride, &map_data); GBM_BO_TRANSFER_READ, &stride, &map_data);
if (!data) { if (!data) {
fprintf(stderr, "Failed to map gbm bo: %m\n"); perror("Failed to map gbm bo");
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View file

@ -90,7 +90,7 @@ static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt,
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) { if (data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %m\n"); perror("mmap failed");
close(fd); close(fd);
return NULL; return NULL;
} }
@ -228,13 +228,12 @@ static void write_image(char *filename, enum wl_shm_format wl_fmt, int width,
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
struct wl_display * display = wl_display_connect(NULL); struct wl_display * display = wl_display_connect(NULL);
if (display == NULL) { if (display == NULL) {
fprintf(stderr, "failed to create display: %m\n"); perror("failed to create display");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (shm == NULL) { if (shm == NULL) {

View file

@ -1,5 +1,4 @@
#define _POSIX_C_SOURCE 200112L #define _POSIX_C_SOURCE 200112L
#include <GLES2/gl2.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -8,8 +7,10 @@
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h> #include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
@ -18,7 +19,7 @@ struct sample_state {
struct wl_listener new_output; struct wl_listener new_output;
struct wl_listener new_input; struct wl_listener new_input;
struct timespec last_frame; struct timespec last_frame;
float color[3]; float color[4];
int dec; int dec;
}; };
@ -40,6 +41,8 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = struct sample_output *sample_output =
wl_container_of(listener, sample_output, frame); wl_container_of(listener, sample_output, frame);
struct sample_state *sample = sample_output->sample; struct sample_state *sample = sample_output->sample;
struct wlr_output *wlr_output = sample_output->output;
struct timespec now; struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
@ -56,12 +59,15 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
sample->dec = inc; sample->dec = inc;
} }
wlr_output_attach_render(sample_output->output, NULL); wlr_output_attach_render(wlr_output, NULL);
glClearColor(sample->color[0], sample->color[1], sample->color[2], 1.0); struct wlr_renderer *renderer =
glClear(GL_COLOR_BUFFER_BIT); wlr_backend_get_renderer(wlr_output->backend);
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(renderer, sample->color);
wlr_renderer_end(renderer);
wlr_output_commit(sample_output->output); wlr_output_commit(wlr_output);
sample->last_frame = now; sample->last_frame = now;
} }
@ -80,9 +86,9 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
wl_container_of(listener, sample, new_output); wl_container_of(listener, sample, new_output);
struct sample_output *sample_output = struct sample_output *sample_output =
calloc(1, sizeof(struct sample_output)); calloc(1, sizeof(struct sample_output));
if (!wl_list_empty(&output->modes)) {
struct wlr_output_mode *mode = struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
wl_container_of(output->modes.prev, mode, link); if (mode != NULL) {
wlr_output_set_mode(output, mode); wlr_output_set_mode(output, mode);
} }
sample_output->output = output; sample_output->output = output;
@ -162,7 +168,7 @@ int main(void) {
wlr_log_init(WLR_DEBUG, NULL); wlr_log_init(WLR_DEBUG, NULL);
struct wl_display *display = wl_display_create(); struct wl_display *display = wl_display_create();
struct sample_state state = { struct sample_state state = {
.color = { 1.0, 0.0, 0.0 }, .color = { 1.0, 0.0, 0.0, 1.0 },
.dec = 0, .dec = 0,
.last_frame = { 0 }, .last_frame = { 0 },
.display = display .display = display

View file

@ -14,6 +14,7 @@
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h> #include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_tablet_pad.h> #include <wlr/types/wlr_tablet_pad.h>
#include <wlr/types/wlr_tablet_tool.h> #include <wlr/types/wlr_tablet_tool.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>

View file

@ -344,7 +344,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (compositor == NULL) { if (compositor == NULL) {

View file

@ -203,7 +203,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (compositor == NULL) { if (compositor == NULL) {

View file

@ -14,7 +14,9 @@
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_list.h> #include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_input_device.h> #include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include "cat.h" #include "cat.h"

View file

@ -61,13 +61,12 @@ int main(int argc, char *argv[]) {
} }
struct wl_display * display = wl_display_connect(NULL); struct wl_display * display = wl_display_connect(NULL);
if (display == NULL) { if (display == NULL) {
fprintf(stderr, "failed to create display: %m\n"); perror("failed to create display");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
struct wl_registry *registry = wl_display_get_registry(display); struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL); wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (pointer_manager == NULL) { if (pointer_manager == NULL) {
@ -132,7 +131,6 @@ int main(int argc, char *argv[]) {
zwlr_virtual_pointer_v1_frame(pointer); zwlr_virtual_pointer_v1_frame(pointer);
zwlr_virtual_pointer_v1_destroy(pointer); zwlr_virtual_pointer_v1_destroy(pointer);
wl_display_dispatch(display);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View file

@ -32,7 +32,6 @@ struct wlr_drm_plane {
/* Buffer currently displayed on screen */ /* Buffer currently displayed on screen */
struct wlr_drm_fb current_fb; struct wlr_drm_fb current_fb;
uint32_t drm_format; // ARGB2101010, XRGB2101010, ARGB8888, or XRGB8888
struct wlr_drm_format_set formats; struct wlr_drm_format_set formats;
// Only used by cursor // Only used by cursor
@ -63,13 +62,6 @@ struct wlr_drm_crtc {
struct wlr_drm_plane *primary; struct wlr_drm_plane *primary;
struct wlr_drm_plane *cursor; struct wlr_drm_plane *cursor;
/*
* We don't support overlay planes yet, but we keep track of them to
* give to DRM lease clients.
*/
size_t num_overlays;
uint32_t *overlays;
union wlr_drm_crtc_props props; union wlr_drm_crtc_props props;
}; };
@ -82,6 +74,8 @@ struct wlr_drm_backend {
bool addfb2_modifiers; bool addfb2_modifiers;
int fd; int fd;
char *name;
struct wlr_device *dev;
size_t num_crtcs; size_t num_crtcs;
struct wlr_drm_crtc *crtcs; struct wlr_drm_crtc *crtcs;
@ -115,8 +109,10 @@ struct wlr_drm_mode {
}; };
struct wlr_drm_connector { struct wlr_drm_connector {
struct wlr_output output; struct wlr_output output; // only valid if state != DISCONNECTED
struct wlr_drm_backend *backend;
char name[24];
enum wlr_drm_connector_state state; enum wlr_drm_connector_state state;
struct wlr_output_mode *desired_mode; struct wlr_output_mode *desired_mode;
bool desired_enabled; bool desired_enabled;
@ -134,7 +130,7 @@ struct wlr_drm_connector {
struct wl_list link; struct wl_list link;
/* /*
* We've asked for a state change in the kernel, and yet to recieve a * We've asked for a state change in the kernel, and yet to receive a
* notification for its completion. Currently, the kernel only has a * notification for its completion. Currently, the kernel only has a
* queue length of 1, and no way to modify your submissions after * queue length of 1, and no way to modify your submissions after
* they're sent. * they're sent.
@ -150,6 +146,7 @@ void finish_drm_resources(struct wlr_drm_backend *drm);
void restore_drm_outputs(struct wlr_drm_backend *drm); void restore_drm_outputs(struct wlr_drm_backend *drm);
void scan_drm_connectors(struct wlr_drm_backend *state); void scan_drm_connectors(struct wlr_drm_backend *state);
int handle_drm_event(int fd, uint32_t mask, void *data); int handle_drm_event(int fd, uint32_t mask, void *data);
void destroy_drm_connector(struct wlr_drm_connector *conn);
bool drm_connector_set_mode(struct wlr_drm_connector *conn, bool drm_connector_set_mode(struct wlr_drm_connector *conn,
struct wlr_output_mode *mode); struct wlr_output_mode *mode);
bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn); bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn);
@ -159,4 +156,9 @@ size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm,
struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane); struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane);
#define wlr_drm_conn_log(conn, verb, fmt, ...) \
wlr_log(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__)
#define wlr_drm_conn_log_errno(conn, verb, fmt, ...) \
wlr_log_errno(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__)
#endif #endif

View file

@ -28,8 +28,6 @@ union wlr_drm_connector_props {
union wlr_drm_crtc_props { union wlr_drm_crtc_props {
struct { struct {
// Neither of these are guaranteed to exist // Neither of these are guaranteed to exist
uint32_t rotation;
uint32_t scaling_mode;
uint32_t vrr_enabled; uint32_t vrr_enabled;
uint32_t gamma_lut; uint32_t gamma_lut;
uint32_t gamma_lut_size; uint32_t gamma_lut_size;

View file

@ -17,9 +17,8 @@ struct wlr_drm_renderer {
struct gbm_device *gbm; struct gbm_device *gbm;
struct wlr_egl egl; struct wlr_egl egl;
uint32_t gbm_format;
struct wlr_renderer *wlr_rend; struct wlr_renderer *wlr_rend;
struct wlr_gbm_allocator *allocator;
}; };
struct wlr_drm_surface { struct wlr_drm_surface {
@ -28,27 +27,17 @@ struct wlr_drm_surface {
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
struct gbm_surface *gbm; struct wlr_swapchain *swapchain;
EGLSurface egl; struct wlr_buffer *back_buffer;
};
enum wlr_drm_fb_type {
WLR_DRM_FB_TYPE_NONE,
WLR_DRM_FB_TYPE_SURFACE,
WLR_DRM_FB_TYPE_WLR_BUFFER
}; };
struct wlr_drm_fb { struct wlr_drm_fb {
enum wlr_drm_fb_type type;
struct gbm_bo *bo; struct gbm_bo *bo;
struct wlr_buffer *wlr_buf;
struct wlr_drm_surface *mgpu_surf; struct wlr_drm_surface *mgpu_surf;
struct gbm_bo *mgpu_bo; struct gbm_bo *mgpu_bo;
struct wlr_buffer *mgpu_wlr_buf;
union {
struct wlr_drm_surface *surf;
struct wlr_buffer *wlr_buf;
};
}; };
bool init_drm_renderer(struct wlr_drm_backend *drm, bool init_drm_renderer(struct wlr_drm_backend *drm,
@ -56,6 +45,7 @@ bool init_drm_renderer(struct wlr_drm_backend *drm,
void finish_drm_renderer(struct wlr_drm_renderer *renderer); void finish_drm_renderer(struct wlr_drm_renderer *renderer);
bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age); bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age);
void drm_surface_unset_current(struct wlr_drm_surface *surf);
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs); bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs);
void drm_fb_clear(struct wlr_drm_fb *fb); void drm_fb_clear(struct wlr_drm_fb *fb);
@ -71,7 +61,7 @@ struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm
bool drm_plane_init_surface(struct wlr_drm_plane *plane, bool drm_plane_init_surface(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height, struct wlr_drm_backend *drm, int32_t width, uint32_t height,
uint32_t format, uint32_t flags, bool with_modifiers); uint32_t format, bool force_linear, bool with_modifiers);
void drm_plane_finish_surface(struct wlr_drm_plane *plane); void drm_plane_finish_surface(struct wlr_drm_plane *plane);
#endif #endif

View file

@ -3,7 +3,6 @@
#include <wlr/backend/headless.h> #include <wlr/backend/headless.h>
#include <wlr/backend/interface.h> #include <wlr/backend/interface.h>
#include <wlr/render/gles2.h>
#define HEADLESS_DEFAULT_REFRESH (60 * 1000) // 60 Hz #define HEADLESS_DEFAULT_REFRESH (60 * 1000) // 60 Hz
@ -12,6 +11,8 @@ struct wlr_headless_backend {
struct wlr_egl priv_egl; // may be uninitialized struct wlr_egl priv_egl; // may be uninitialized
struct wlr_egl *egl; struct wlr_egl *egl;
struct wlr_renderer *renderer; struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_drm_format *format;
struct wl_display *display; struct wl_display *display;
struct wl_list outputs; struct wl_list outputs;
size_t last_output_num; size_t last_output_num;
@ -19,7 +20,6 @@ struct wlr_headless_backend {
struct wl_listener display_destroy; struct wl_listener display_destroy;
struct wl_listener renderer_destroy; struct wl_listener renderer_destroy;
bool started; bool started;
GLenum internal_format;
}; };
struct wlr_headless_output { struct wlr_headless_output {
@ -28,7 +28,8 @@ struct wlr_headless_output {
struct wlr_headless_backend *backend; struct wlr_headless_backend *backend;
struct wl_list link; struct wl_list link;
GLuint fbo, rbo; struct wlr_swapchain *swapchain;
struct wlr_buffer *back_buffer, *front_buffer;
struct wl_event_source *frame_timer; struct wl_event_source *frame_timer;
int frame_delay; // ms int frame_delay; // ms

View file

@ -0,0 +1,8 @@
#ifndef BACKEND_SESSION_SESSION_H
#define BACKEND_SESSION_SESSION_H
struct wlr_session;
void session_init(struct wlr_session *session);
#endif

View file

@ -4,14 +4,13 @@
#include <stdbool.h> #include <stdbool.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <wayland-egl.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wayland-util.h>
#include <wlr/backend/wayland.h> #include <wlr/backend/wayland.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/render/drm_format_set.h> #include <wlr/render/drm_format_set.h>
struct wlr_wl_backend { struct wlr_wl_backend {
@ -24,9 +23,13 @@ struct wlr_wl_backend {
struct wl_list outputs; struct wl_list outputs;
struct wlr_egl egl; struct wlr_egl egl;
struct wlr_renderer *renderer; struct wlr_renderer *renderer;
struct wlr_drm_format *format;
struct wlr_allocator *allocator;
struct wl_list buffers; // wlr_wl_buffer.link
size_t requested_outputs; size_t requested_outputs;
size_t last_output_num; size_t last_output_num;
struct wl_listener local_display_destroy; struct wl_listener local_display_destroy;
/* remote state */ /* remote state */
struct wl_display *remote_display; struct wl_display *remote_display;
struct wl_event_source *remote_display_src; struct wl_event_source *remote_display_src;
@ -38,18 +41,17 @@ struct wlr_wl_backend {
struct wp_presentation *presentation; struct wp_presentation *presentation;
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1; struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1;
struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1; struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1;
struct wl_seat *seat; struct wl_list seats; // wlr_wl_seat.link
struct wl_pointer *pointer;
struct wl_keyboard *keyboard;
struct wlr_wl_pointer *current_pointer;
struct zwp_tablet_manager_v2 *tablet_manager; struct zwp_tablet_manager_v2 *tablet_manager;
char *seat_name;
struct wlr_drm_format_set linux_dmabuf_v1_formats; struct wlr_drm_format_set linux_dmabuf_v1_formats;
}; };
struct wlr_wl_buffer { struct wlr_wl_buffer {
struct wlr_buffer *buffer; struct wlr_buffer *buffer;
struct wl_buffer *wl_buffer; struct wl_buffer *wl_buffer;
bool released;
struct wl_list link; // wlr_wl_backend.buffers
struct wl_listener buffer_destroy;
}; };
struct wlr_wl_presentation_feedback { struct wlr_wl_presentation_feedback {
@ -70,15 +72,17 @@ struct wlr_wl_output {
struct xdg_surface *xdg_surface; struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel; struct xdg_toplevel *xdg_toplevel;
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1; struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1;
struct wl_egl_window *egl_window;
EGLSurface egl_surface;
struct wl_list presentation_feedbacks; struct wl_list presentation_feedbacks;
struct wlr_swapchain *swapchain;
struct wlr_buffer *back_buffer;
uint32_t enter_serial; uint32_t enter_serial;
struct { struct {
struct wlr_wl_pointer *pointer;
struct wl_surface *surface; struct wl_surface *surface;
struct wl_egl_window *egl_window; struct wlr_swapchain *swapchain;
int32_t hotspot_x, hotspot_y; int32_t hotspot_x, hotspot_y;
int32_t width, height; int32_t width, height;
} cursor; } cursor;
@ -89,6 +93,7 @@ struct wlr_wl_input_device {
uint32_t fingers; uint32_t fingers;
struct wlr_wl_backend *backend; struct wlr_wl_backend *backend;
struct wlr_wl_seat *seat;
void *resource; void *resource;
}; };
@ -107,18 +112,35 @@ struct wlr_wl_pointer {
struct wl_listener output_destroy; struct wl_listener output_destroy;
}; };
struct wlr_wl_seat {
struct wl_seat *wl_seat;
struct wl_list link; // wlr_wl_backend.seats
char *name;
struct wl_touch *touch;
struct wl_pointer *pointer;
struct wl_keyboard *keyboard;
struct wlr_wl_backend *backend;
struct wlr_wl_pointer *active_pointer;
};
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend); struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend);
void update_wl_output_cursor(struct wlr_wl_output *output); void update_wl_output_cursor(struct wlr_wl_output *output);
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer); struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer);
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output); void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output);
void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl); void create_wl_keyboard(struct wlr_wl_seat *seat);
void create_wl_touch(struct wlr_wl_seat *seat);
struct wlr_wl_input_device *create_wl_input_device( struct wlr_wl_input_device *create_wl_input_device(
struct wlr_wl_backend *backend, enum wlr_input_device_type type); struct wlr_wl_seat *seat, enum wlr_input_device_type type);
bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl);
void destroy_wl_seats(struct wlr_wl_backend *wl);
void destroy_wl_buffer(struct wlr_wl_buffer *buffer);
extern const struct wl_seat_listener seat_listener; extern const struct wl_seat_listener seat_listener;
struct wlr_wl_tablet_seat *wl_add_tablet_seat( struct wlr_wl_tablet_seat *wl_add_tablet_seat(
struct zwp_tablet_manager_v2 *manager, struct zwp_tablet_manager_v2 *manager,
struct wl_seat *seat, struct wlr_wl_backend *backend); struct wlr_wl_seat *seat);
#endif #endif

View file

@ -1,16 +1,26 @@
#ifndef BACKEND_X11_H #ifndef BACKEND_X11_H
#define BACKEND_X11_H #define BACKEND_X11_H
#include <wlr/config.h>
#include <stdbool.h> #include <stdbool.h>
#include <X11/Xlib-xcb.h> #include <X11/Xlib-xcb.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/present.h>
#if WLR_HAS_XCB_ERRORS
#include <xcb/xcb_errors.h>
#endif
#include <wlr/backend/x11.h> #include <wlr/backend/x11.h>
#include <wlr/config.h>
#include <wlr/interfaces/wlr_input_device.h> #include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_output.h> #include <wlr/interfaces/wlr_output.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/interfaces/wlr_touch.h>
#include <wlr/render/drm_format_set.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
@ -26,7 +36,9 @@ struct wlr_x11_output {
struct wl_list link; // wlr_x11_backend::outputs struct wl_list link; // wlr_x11_backend::outputs
xcb_window_t win; xcb_window_t win;
EGLSurface surf;
struct wlr_swapchain *swapchain;
struct wlr_buffer *back_buffer;
struct wlr_pointer pointer; struct wlr_pointer pointer;
struct wlr_input_device pointer_dev; struct wlr_input_device pointer_dev;
@ -38,6 +50,8 @@ struct wlr_x11_output {
struct wl_event_source *frame_timer; struct wl_event_source *frame_timer;
int frame_delay; int frame_delay;
struct wl_list buffers; // wlr_x11_buffer::link
bool cursor_hidden; bool cursor_hidden;
}; };
@ -55,6 +69,10 @@ struct wlr_x11_backend {
Display *xlib_conn; Display *xlib_conn;
xcb_connection_t *xcb; xcb_connection_t *xcb;
xcb_screen_t *screen; xcb_screen_t *screen;
xcb_depth_t *depth;
xcb_visualid_t visualid;
xcb_colormap_t colormap;
xcb_present_event_t present_event_id;
size_t requested_outputs; size_t requested_outputs;
size_t last_output_num; size_t last_output_num;
@ -65,6 +83,10 @@ struct wlr_x11_backend {
struct wlr_egl egl; struct wlr_egl egl;
struct wlr_renderer *renderer; struct wlr_renderer *renderer;
struct wlr_drm_format_set dri3_formats;
const struct wlr_x11_format *x11_format;
struct wlr_drm_format *drm_format;
struct wlr_allocator *allocator;
struct wl_event_source *event_source; struct wl_event_source *event_source;
struct { struct {
@ -78,11 +100,29 @@ struct wlr_x11_backend {
// The time we last received an event // The time we last received an event
xcb_timestamp_t time; xcb_timestamp_t time;
#if WLR_HAS_XCB_ERRORS
xcb_errors_context_t *errors_context;
#endif
uint8_t present_opcode;
uint8_t xinput_opcode; uint8_t xinput_opcode;
struct wl_listener display_destroy; struct wl_listener display_destroy;
}; };
struct wlr_x11_buffer {
struct wlr_x11_backend *x11;
struct wlr_buffer *buffer;
xcb_pixmap_t pixmap;
struct wl_list link; // wlr_x11_output::buffers
struct wl_listener buffer_destroy;
};
struct wlr_x11_format {
uint32_t drm;
uint8_t depth, bpp;
};
struct wlr_x11_backend *get_x11_backend_from_backend( struct wlr_x11_backend *get_x11_backend_from_backend(
struct wlr_backend *wlr_backend); struct wlr_backend *wlr_backend);
struct wlr_x11_output *get_x11_output_from_window_id( struct wlr_x11_output *get_x11_output_from_window_id(
@ -100,5 +140,7 @@ void update_x11_pointer_position(struct wlr_x11_output *output,
void handle_x11_configure_notify(struct wlr_x11_output *output, void handle_x11_configure_notify(struct wlr_x11_output *output,
xcb_configure_notify_event_t *event); xcb_configure_notify_event_t *event);
void handle_x11_present_event(struct wlr_x11_backend *x11,
xcb_ge_generic_event_t *event);
#endif #endif

View file

@ -0,0 +1,42 @@
#ifndef RENDER_ALLOCATOR
#define RENDER_ALLOCATOR
#include <stdbool.h>
#include <wayland-server-core.h>
#include <wlr/render/dmabuf.h>
#include <wlr/render/drm_format_set.h>
struct wlr_allocator;
struct wlr_allocator_interface {
struct wlr_buffer *(*create_buffer)(struct wlr_allocator *alloc,
int width, int height, const struct wlr_drm_format *format);
void (*destroy)(struct wlr_allocator *alloc);
};
struct wlr_allocator {
const struct wlr_allocator_interface *impl;
struct {
struct wl_signal destroy;
} events;
};
/**
* Destroy the allocator.
*/
void wlr_allocator_destroy(struct wlr_allocator *alloc);
/**
* Allocate a new buffer.
*
* When the caller is done with it, they must unreference it by calling
* wlr_buffer_drop.
*/
struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc,
int width, int height, const struct wlr_drm_format *format);
// For wlr_allocator implementors
void wlr_allocator_init(struct wlr_allocator *alloc,
const struct wlr_allocator_interface *impl);
#endif

View file

@ -0,0 +1,19 @@
#ifndef RENDER_DRM_FORMAT_SET_H
#define RENDER_DRM_FORMAT_SET_H
#include <wlr/render/drm_format_set.h>
struct wlr_drm_format *wlr_drm_format_create(uint32_t format);
bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier);
struct wlr_drm_format *wlr_drm_format_dup(const struct wlr_drm_format *format);
/**
* Intersect modifiers for two DRM formats.
*
* Both arguments must have the same format field. If the formats aren't
* compatible, NULL is returned. If either format doesn't support any modifier,
* a format that doesn't support any modifier is returned.
*/
struct wlr_drm_format *wlr_drm_format_intersect(
const struct wlr_drm_format *a, const struct wlr_drm_format *b);
#endif

View file

@ -0,0 +1,29 @@
#ifndef RENDER_GBM_ALLOCATOR_H
#define RENDER_GBM_ALLOCATOR_H
#include <gbm.h>
#include <wlr/types/wlr_buffer.h>
#include "render/allocator.h"
struct wlr_gbm_buffer {
struct wlr_buffer base;
struct gbm_bo *gbm_bo;
struct wlr_dmabuf_attributes dmabuf;
};
struct wlr_gbm_allocator {
struct wlr_allocator base;
int fd;
struct gbm_device *gbm_device;
};
/**
* Creates a new GBM allocator from a render FD.
*
* Takes ownership over the FD.
*/
struct wlr_gbm_allocator *wlr_gbm_allocator_create(int render_fd);
#endif

View file

@ -14,17 +14,6 @@
#include <wlr/render/wlr_texture.h> #include <wlr/render/wlr_texture.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
struct wlr_gles2_procs {
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR;
PFNGLDEBUGMESSAGECONTROLKHRPROC glDebugMessageControlKHR;
PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR;
PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR;
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES;
};
extern struct wlr_gles2_procs gles2_procs;
struct wlr_gles2_pixel_format { struct wlr_gles2_pixel_format {
enum wl_shm_format wl_format; enum wl_shm_format wl_format;
GLint gl_format, gl_type; GLint gl_format, gl_type;
@ -46,6 +35,7 @@ struct wlr_gles2_renderer {
struct wlr_renderer wlr_renderer; struct wlr_renderer wlr_renderer;
struct wlr_egl *egl; struct wlr_egl *egl;
int drm_fd;
const char *exts_str; const char *exts_str;
struct { struct {
@ -55,6 +45,15 @@ struct wlr_gles2_renderer {
bool egl_image_oes; bool egl_image_oes;
} exts; } exts;
struct {
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR;
PFNGLDEBUGMESSAGECONTROLKHRPROC glDebugMessageControlKHR;
PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR;
PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR;
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES;
} procs;
struct { struct {
struct { struct {
GLuint program; GLuint program;
@ -74,12 +73,27 @@ struct wlr_gles2_renderer {
struct wlr_gles2_tex_shader tex_ext; struct wlr_gles2_tex_shader tex_ext;
} shaders; } shaders;
struct wl_list buffers; // wlr_gles2_buffer.link
struct wlr_gles2_buffer *current_buffer;
uint32_t viewport_width, viewport_height; uint32_t viewport_width, viewport_height;
}; };
struct wlr_gles2_buffer {
struct wlr_buffer *buffer;
struct wlr_gles2_renderer *renderer;
struct wl_list link; // wlr_gles2_renderer.buffers
EGLImageKHR image;
GLuint rbo;
GLuint fbo;
struct wl_listener buffer_destroy;
};
struct wlr_gles2_texture { struct wlr_gles2_texture {
struct wlr_texture wlr_texture; struct wlr_texture wlr_texture;
struct wlr_egl *egl; struct wlr_gles2_renderer *renderer;
// Basically: // Basically:
// GL_TEXTURE_2D == mutable // GL_TEXTURE_2D == mutable
@ -102,12 +116,22 @@ const struct wlr_gles2_pixel_format *get_gles2_format_from_gl(
GLint gl_format, GLint gl_type, bool alpha); GLint gl_format, GLint gl_type, bool alpha);
const enum wl_shm_format *get_gles2_wl_formats(size_t *len); const enum wl_shm_format *get_gles2_wl_formats(size_t *len);
struct wlr_gles2_renderer *gles2_get_renderer(
struct wlr_renderer *wlr_renderer);
struct wlr_gles2_texture *gles2_get_texture( struct wlr_gles2_texture *gles2_get_texture(
struct wlr_texture *wlr_texture); struct wlr_texture *wlr_texture);
void push_gles2_marker(const char *file, const char *func); struct wlr_texture *gles2_texture_from_pixels(struct wlr_renderer *wlr_renderer,
void pop_gles2_marker(void); enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height,
#define PUSH_GLES2_DEBUG push_gles2_marker(_WLR_FILENAME, __func__) const void *data);
#define POP_GLES2_DEBUG pop_gles2_marker() struct wlr_texture *gles2_texture_from_wl_drm(struct wlr_renderer *wlr_renderer,
struct wl_resource *data);
struct wlr_texture *gles2_texture_from_dmabuf(struct wlr_renderer *wlr_renderer,
struct wlr_dmabuf_attributes *attribs);
void push_gles2_debug_(struct wlr_gles2_renderer *renderer,
const char *file, const char *func);
#define push_gles2_debug(renderer) push_gles2_debug_(renderer, _WLR_FILENAME, __func__)
void pop_gles2_debug(struct wlr_gles2_renderer *renderer);
#endif #endif

View file

@ -0,0 +1,50 @@
#ifndef RENDER_SWAPCHAIN_H
#define RENDER_SWAPCHAIN_H
#include <stdbool.h>
#include <wayland-server-core.h>
#include <wlr/render/drm_format_set.h>
#define WLR_SWAPCHAIN_CAP 4
struct wlr_swapchain_slot {
struct wlr_buffer *buffer;
bool acquired; // waiting for release
int age;
struct wl_listener release;
};
struct wlr_swapchain {
struct wlr_allocator *allocator; // NULL if destroyed
int width, height;
struct wlr_drm_format *format;
struct wlr_swapchain_slot slots[WLR_SWAPCHAIN_CAP];
struct wl_listener allocator_destroy;
};
struct wlr_swapchain *wlr_swapchain_create(
struct wlr_allocator *alloc, int width, int height,
const struct wlr_drm_format *format);
void wlr_swapchain_destroy(struct wlr_swapchain *swapchain);
/**
* Acquire a buffer from the swap chain.
*
* The returned buffer is locked. When the caller is done with it, they must
* unlock it by calling wlr_buffer_unlock.
*/
struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain,
int *age);
/**
* Mark the buffer as submitted for presentation. This needs to be called by
* swap chain users on frame boundaries.
*
* If the buffer hasn't been created via the swap chain, the call is ignored.
*/
void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain,
struct wlr_buffer *buffer);
#endif

View file

@ -0,0 +1,14 @@
#ifndef RENDER_WLR_RENDERER_H
#define RENDER_WLR_RENDERER_H
#include <wlr/render/wlr_renderer.h>
bool wlr_renderer_bind_buffer(struct wlr_renderer *r, struct wlr_buffer *buffer);
/**
* Get the DMA-BUF formats supporting rendering usage. Buffers allocated with
* a format from this list may be attached via wlr_renderer_bind_buffer.
*/
const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_render_formats(
struct wlr_renderer *renderer);
#endif

View file

@ -1,47 +0,0 @@
#ifndef TYPES_WLR_XDG_SHELL_V6_H
#define TYPES_WLR_XDG_SHELL_V6_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include "xdg-shell-unstable-v6-protocol.h"
struct wlr_xdg_positioner_v6_resource {
struct wl_resource *resource;
struct wlr_xdg_positioner_v6 attrs;
};
extern const struct wlr_surface_role xdg_toplevel_v6_surface_role;
extern const struct wlr_surface_role xdg_popup_v6_surface_role;
uint32_t schedule_xdg_surface_v6_configure(struct wlr_xdg_surface_v6 *surface);
struct wlr_xdg_surface_v6 *create_xdg_surface_v6(
struct wlr_xdg_client_v6 *client, struct wlr_surface *surface,
uint32_t id);
void unmap_xdg_surface_v6(struct wlr_xdg_surface_v6 *surface);
void destroy_xdg_surface_v6(struct wlr_xdg_surface_v6 *surface);
void handle_xdg_surface_v6_commit(struct wlr_surface *wlr_surface);
void handle_xdg_surface_v6_precommit(struct wlr_surface *wlr_surface);
void create_xdg_positioner_v6(struct wlr_xdg_client_v6 *client, uint32_t id);
struct wlr_xdg_positioner_v6_resource *get_xdg_positioner_v6_from_resource(
struct wl_resource *resource);
void create_xdg_popup_v6(struct wlr_xdg_surface_v6 *xdg_surface,
struct wlr_xdg_surface_v6 *parent,
struct wlr_xdg_positioner_v6_resource *positioner, int32_t id);
void handle_xdg_surface_v6_popup_committed(struct wlr_xdg_surface_v6 *surface);
struct wlr_xdg_popup_grab_v6 *get_xdg_shell_v6_popup_grab_from_seat(
struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat);
void destroy_xdg_popup_v6(struct wlr_xdg_surface_v6 *surface);
void create_xdg_toplevel_v6(struct wlr_xdg_surface_v6 *xdg_surface,
uint32_t id);
void handle_xdg_surface_v6_toplevel_committed(struct wlr_xdg_surface_v6 *surface);
void send_xdg_toplevel_v6_configure(struct wlr_xdg_surface_v6 *surface,
struct wlr_xdg_surface_v6_configure *configure);
void handle_xdg_toplevel_v6_ack_configure(struct wlr_xdg_surface_v6 *surface,
struct wlr_xdg_surface_v6_configure *configure);
bool compare_xdg_surface_v6_toplevel_state(struct wlr_xdg_toplevel_v6 *state);
void destroy_xdg_toplevel_v6(struct wlr_xdg_surface_v6 *surface);
#endif

View file

@ -13,6 +13,11 @@ uint32_t get_current_time_msec(void);
*/ */
int64_t timespec_to_msec(const struct timespec *a); int64_t timespec_to_msec(const struct timespec *a);
/**
* Convert nanoseconds to a timespec.
*/
void timespec_from_nsec(struct timespec *r, int64_t nsec);
/** /**
* Subtracts timespec `b` from timespec `a`, and stores the difference in `r`. * Subtracts timespec `b` from timespec `a`, and stores the difference in `r`.
*/ */

View file

@ -22,7 +22,8 @@
* a DRM backend, other kinds of backends raise SIGABRT). * a DRM backend, other kinds of backends raise SIGABRT).
*/ */
struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
struct wlr_session *session, int gpu_fd, struct wlr_backend *parent, struct wlr_session *session, struct wlr_device *dev,
struct wlr_backend *parent,
wlr_renderer_create_func_t create_renderer_func); wlr_renderer_create_func_t create_renderer_func);
bool wlr_backend_is_drm(struct wlr_backend *backend); bool wlr_backend_is_drm(struct wlr_backend *backend);

View file

@ -11,9 +11,11 @@ struct session_impl;
struct wlr_device { struct wlr_device {
int fd; int fd;
dev_t dev; dev_t dev;
struct wl_signal signal;
struct wl_list link; struct wl_list link;
struct {
struct wl_signal change;
} events;
}; };
struct wlr_session { struct wlr_session {
@ -22,7 +24,6 @@ struct wlr_session {
* Signal for when the session becomes active/inactive. * Signal for when the session becomes active/inactive.
* It's called when we swap virtual terminal. * It's called when we swap virtual terminal.
*/ */
struct wl_signal session_signal;
bool active; bool active;
/* /*
@ -38,13 +39,20 @@ struct wlr_session {
struct wl_list devices; struct wl_list devices;
struct wl_display *display;
struct wl_listener display_destroy; struct wl_listener display_destroy;
struct { struct {
struct wl_signal active;
struct wl_signal add_drm_card; // struct wlr_session_add_event
struct wl_signal destroy; struct wl_signal destroy;
} events; } events;
}; };
struct wlr_session_add_event {
const char *path;
};
/* /*
* Opens a session, taking control of the current virtual terminal. * Opens a session, taking control of the current virtual terminal.
* This should not be called if another program is already in control * This should not be called if another program is already in control
@ -74,21 +82,21 @@ void wlr_session_destroy(struct wlr_session *session);
* *
* Returns -errno on error. * Returns -errno on error.
*/ */
int wlr_session_open_file(struct wlr_session *session, const char *path); struct wlr_device *wlr_session_open_file(struct wlr_session *session,
const char *path);
/* /*
* Closes a file previously opened with wlr_session_open_file. * Closes a file previously opened with wlr_session_open_file.
*/ */
void wlr_session_close_file(struct wlr_session *session, int fd); void wlr_session_close_file(struct wlr_session *session,
struct wlr_device *device);
void wlr_session_signal_add(struct wlr_session *session, int fd,
struct wl_listener *listener);
/* /*
* Changes the virtual terminal. * Changes the virtual terminal.
*/ */
bool wlr_session_change_vt(struct wlr_session *session, unsigned vt); bool wlr_session_change_vt(struct wlr_session *session, unsigned vt);
size_t wlr_session_find_gpus(struct wlr_session *session, size_t wlr_session_find_gpus(struct wlr_session *session,
size_t ret_len, int *ret); size_t ret_len, struct wlr_device **ret);
#endif #endif

View file

@ -1,13 +1,11 @@
#ifndef WLR_CONFIG_H #ifndef WLR_CONFIG_H
#define WLR_CONFIG_H #define WLR_CONFIG_H
#mesondefine WLR_HAS_EGLMESAEXT_H
#mesondefine WLR_HAS_LIBCAP
#mesondefine WLR_HAS_SYSTEMD #mesondefine WLR_HAS_SYSTEMD
#mesondefine WLR_HAS_ELOGIND #mesondefine WLR_HAS_ELOGIND
#mesondefine WLR_HAS_LIBSEAT
#mesondefine WLR_HAS_X11_BACKEND #mesondefine WLR_HAS_X11_BACKEND
#mesondefine WLR_HAS_XWAYLAND #mesondefine WLR_HAS_XWAYLAND

View file

@ -15,9 +15,9 @@
#define WLR_DMABUF_MAX_PLANES 4 #define WLR_DMABUF_MAX_PLANES 4
enum wlr_dmabuf_attributes_flags { enum wlr_dmabuf_attributes_flags {
WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT = 1, WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT = 1 << 0,
WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED = 2, WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED = 1 << 1,
WLR_DMABUF_ATTRIBUTES_FLAGS_BOTTOM_FIRST = 4, WLR_DMABUF_ATTRIBUTES_FLAGS_BOTTOM_FIRST = 1 << 2,
}; };
struct wlr_dmabuf_attributes { struct wlr_dmabuf_attributes {

View file

@ -15,15 +15,14 @@
#ifndef EGL_NO_X11 #ifndef EGL_NO_X11
#define EGL_NO_X11 #define EGL_NO_X11
#endif #endif
#ifndef EGL_NO_PLATFORM_SPECIFIC_TYPES
#define EGL_NO_PLATFORM_SPECIFIC_TYPES
#endif
#include <wlr/config.h> #include <wlr/config.h>
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
#if WLR_HAS_EGLMESAEXT_H
// TODO: remove eglmesaext.h
#include <EGL/eglmesaext.h>
#endif
#include <pixman.h> #include <pixman.h>
#include <stdbool.h> #include <stdbool.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
@ -42,8 +41,10 @@ struct wlr_egl {
EGLDisplay display; EGLDisplay display;
EGLConfig config; EGLConfig config;
EGLContext context; EGLContext context;
EGLDeviceEXT device; // may be EGL_NO_DEVICE_EXT
struct { struct {
// Display extensions
bool bind_wayland_display_wl; bool bind_wayland_display_wl;
bool buffer_age_ext; bool buffer_age_ext;
bool image_base_khr; bool image_base_khr;
@ -51,6 +52,9 @@ struct wlr_egl {
bool image_dmabuf_import_ext; bool image_dmabuf_import_ext;
bool image_dmabuf_import_modifiers_ext; bool image_dmabuf_import_modifiers_ext;
bool swap_buffers_with_damage; bool swap_buffers_with_damage;
// Device extensions
bool device_drm_ext;
} exts; } exts;
struct { struct {
@ -67,12 +71,14 @@ struct wlr_egl {
PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC eglExportDMABUFImageQueryMESA; PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC eglExportDMABUFImageQueryMESA;
PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA; PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA;
PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR; PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR;
PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT;
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT;
} procs; } procs;
struct wl_display *wl_display; struct wl_display *wl_display;
struct wlr_drm_format_set dmabuf_formats; struct wlr_drm_format_set dmabuf_texture_formats;
EGLBoolean **external_only_dmabuf_formats; struct wlr_drm_format_set dmabuf_render_formats;
}; };
// TODO: Allocate and return a wlr_egl // TODO: Allocate and return a wlr_egl
@ -117,9 +123,15 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl,
struct wlr_dmabuf_attributes *attributes, bool *external_only); struct wlr_dmabuf_attributes *attributes, bool *external_only);
/** /**
* Get the available dmabuf formats * Get DMA-BUF formats suitable for sampling usage.
*/ */
const struct wlr_drm_format_set *wlr_egl_get_dmabuf_formats(struct wlr_egl *egl); const struct wlr_drm_format_set *wlr_egl_get_dmabuf_texture_formats(
struct wlr_egl *egl);
/**
* Get DMA-BUF formats suitable for rendering usage.
*/
const struct wlr_drm_format_set *wlr_egl_get_dmabuf_render_formats(
struct wlr_egl *egl);
bool wlr_egl_export_image_to_dmabuf(struct wlr_egl *egl, EGLImageKHR image, bool wlr_egl_export_image_to_dmabuf(struct wlr_egl *egl, EGLImageKHR image,
int32_t width, int32_t height, uint32_t flags, int32_t width, int32_t height, uint32_t flags,
@ -161,4 +173,6 @@ bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface,
bool wlr_egl_destroy_surface(struct wlr_egl *egl, EGLSurface surface); bool wlr_egl_destroy_surface(struct wlr_egl *egl, EGLSurface surface);
int wlr_egl_dup_drm_fd(struct wlr_egl *egl);
#endif #endif

View file

@ -21,14 +21,6 @@ struct wlr_egl *wlr_gles2_renderer_get_egl(struct wlr_renderer *renderer);
bool wlr_gles2_renderer_check_ext(struct wlr_renderer *renderer, bool wlr_gles2_renderer_check_ext(struct wlr_renderer *renderer,
const char *ext); const char *ext);
struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl,
enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height,
const void *data);
struct wlr_texture *wlr_gles2_texture_from_wl_drm(struct wlr_egl *egl,
struct wl_resource *data);
struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl,
struct wlr_dmabuf_attributes *attribs);
struct wlr_gles2_texture_attribs { struct wlr_gles2_texture_attribs {
GLenum target; /* either GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES */ GLenum target; /* either GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES */
GLuint tex; GLuint tex;

View file

@ -15,6 +15,9 @@
#ifndef EGL_NO_X11 #ifndef EGL_NO_X11
#define EGL_NO_X11 #define EGL_NO_X11
#endif #endif
#ifndef EGL_NO_PLATFORM_SPECIFIC_TYPES
#define EGL_NO_PLATFORM_SPECIFIC_TYPES
#endif
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
@ -27,6 +30,8 @@
#include <wlr/render/dmabuf.h> #include <wlr/render/dmabuf.h>
struct wlr_renderer_impl { struct wlr_renderer_impl {
bool (*bind_buffer)(struct wlr_renderer *renderer,
struct wlr_buffer *buffer);
void (*begin)(struct wlr_renderer *renderer, uint32_t width, void (*begin)(struct wlr_renderer *renderer, uint32_t width,
uint32_t height); uint32_t height);
void (*end)(struct wlr_renderer *renderer); void (*end)(struct wlr_renderer *renderer);
@ -39,15 +44,15 @@ struct wlr_renderer_impl {
const float color[static 4], const float matrix[static 9]); const float color[static 4], const float matrix[static 9]);
void (*render_ellipse_with_matrix)(struct wlr_renderer *renderer, void (*render_ellipse_with_matrix)(struct wlr_renderer *renderer,
const float color[static 4], const float matrix[static 9]); const float color[static 4], const float matrix[static 9]);
const enum wl_shm_format *(*formats)( const enum wl_shm_format *(*get_shm_texture_formats)(
struct wlr_renderer *renderer, size_t *len); struct wlr_renderer *renderer, size_t *len);
bool (*format_supported)(struct wlr_renderer *renderer,
enum wl_shm_format fmt);
bool (*resource_is_wl_drm_buffer)(struct wlr_renderer *renderer, bool (*resource_is_wl_drm_buffer)(struct wlr_renderer *renderer,
struct wl_resource *resource); struct wl_resource *resource);
void (*wl_drm_buffer_get_size)(struct wlr_renderer *renderer, void (*wl_drm_buffer_get_size)(struct wlr_renderer *renderer,
struct wl_resource *buffer, int *width, int *height); struct wl_resource *buffer, int *width, int *height);
const struct wlr_drm_format_set *(*get_dmabuf_formats)( const struct wlr_drm_format_set *(*get_dmabuf_texture_formats)(
struct wlr_renderer *renderer);
const struct wlr_drm_format_set *(*get_dmabuf_render_formats)(
struct wlr_renderer *renderer); struct wlr_renderer *renderer);
enum wl_shm_format (*preferred_read_format)(struct wlr_renderer *renderer); enum wl_shm_format (*preferred_read_format)(struct wlr_renderer *renderer);
bool (*read_pixels)(struct wlr_renderer *renderer, enum wl_shm_format fmt, bool (*read_pixels)(struct wlr_renderer *renderer, enum wl_shm_format fmt,
@ -67,6 +72,7 @@ struct wlr_renderer_impl {
bool (*blit_dmabuf)(struct wlr_renderer *renderer, bool (*blit_dmabuf)(struct wlr_renderer *renderer,
struct wlr_dmabuf_attributes *dst, struct wlr_dmabuf_attributes *dst,
struct wlr_dmabuf_attributes *src); struct wlr_dmabuf_attributes *src);
int (*get_drm_fd)(struct wlr_renderer *renderer);
}; };
void wlr_renderer_init(struct wlr_renderer *renderer, void wlr_renderer_init(struct wlr_renderer *renderer,

View file

@ -21,6 +21,7 @@ enum wlr_renderer_read_pixels_flags {
struct wlr_renderer_impl; struct wlr_renderer_impl;
struct wlr_drm_format_set; struct wlr_drm_format_set;
struct wlr_buffer;
struct wlr_renderer { struct wlr_renderer {
const struct wlr_renderer_impl *impl; const struct wlr_renderer_impl *impl;
@ -35,7 +36,7 @@ struct wlr_renderer {
struct wlr_renderer *wlr_renderer_autocreate(struct wlr_egl *egl, EGLenum platform, struct wlr_renderer *wlr_renderer_autocreate(struct wlr_egl *egl, EGLenum platform,
void *remote_display, EGLint *config_attribs, EGLint visual_id); void *remote_display, EGLint *config_attribs, EGLint visual_id);
void wlr_renderer_begin(struct wlr_renderer *r, int width, int height); void wlr_renderer_begin(struct wlr_renderer *r, uint32_t width, uint32_t height);
void wlr_renderer_end(struct wlr_renderer *r); void wlr_renderer_end(struct wlr_renderer *r);
void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]); void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]);
/** /**
@ -82,10 +83,11 @@ void wlr_render_ellipse(struct wlr_renderer *r, const struct wlr_box *box,
void wlr_render_ellipse_with_matrix(struct wlr_renderer *r, void wlr_render_ellipse_with_matrix(struct wlr_renderer *r,
const float color[static 4], const float matrix[static 9]); const float color[static 4], const float matrix[static 9]);
/** /**
* Returns a list of pixel formats supported by this renderer. * Get the shared-memory formats supporting import usage. Buffers allocated
* with a format from this list may be imported via wlr_texture_from_pixels.
*/ */
const enum wl_shm_format *wlr_renderer_get_formats(struct wlr_renderer *r, const enum wl_shm_format *wlr_renderer_get_shm_texture_formats(
size_t *len); struct wlr_renderer *r, size_t *len);
/** /**
* Returns true if this wl_buffer is a wl_drm buffer. * Returns true if this wl_buffer is a wl_drm buffer.
*/ */
@ -97,9 +99,10 @@ bool wlr_renderer_resource_is_wl_drm_buffer(struct wlr_renderer *renderer,
void wlr_renderer_wl_drm_buffer_get_size(struct wlr_renderer *renderer, void wlr_renderer_wl_drm_buffer_get_size(struct wlr_renderer *renderer,
struct wl_resource *buffer, int *width, int *height); struct wl_resource *buffer, int *width, int *height);
/** /**
* Get the available DMA-BUF formats. * Get the DMA-BUF formats supporting sampling usage. Buffers allocated with
* a format from this list may be imported via wlr_texture_from_dmabuf.
*/ */
const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_formats( const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_texture_formats(
struct wlr_renderer *renderer); struct wlr_renderer *renderer);
/** /**
* Reads out of pixels of the currently bound surface into data. `stride` is in * Reads out of pixels of the currently bound surface into data. `stride` is in
@ -117,11 +120,6 @@ bool wlr_renderer_read_pixels(struct wlr_renderer *r, enum wl_shm_format fmt,
*/ */
bool wlr_renderer_blit_dmabuf(struct wlr_renderer *r, bool wlr_renderer_blit_dmabuf(struct wlr_renderer *r,
struct wlr_dmabuf_attributes *dst, struct wlr_dmabuf_attributes *src); struct wlr_dmabuf_attributes *dst, struct wlr_dmabuf_attributes *src);
/**
* Checks if a format is supported.
*/
bool wlr_renderer_format_supported(struct wlr_renderer *r,
enum wl_shm_format fmt);
/** /**
* Creates necessary shm and invokes the initialization of the implementation. * Creates necessary shm and invokes the initialization of the implementation.
* *
@ -130,6 +128,13 @@ bool wlr_renderer_format_supported(struct wlr_renderer *r,
bool wlr_renderer_init_wl_display(struct wlr_renderer *r, bool wlr_renderer_init_wl_display(struct wlr_renderer *r,
struct wl_display *wl_display); struct wl_display *wl_display);
/**
* Obtains the FD of the DRM device used for rendering, or -1 if unavailable.
*
* The caller doesn't have ownership of the FD, it must not close it.
*/
int wlr_renderer_get_drm_fd(struct wlr_renderer *r);
/** /**
* Destroys this wlr_renderer. Textures must be destroyed separately. * Destroys this wlr_renderer. Textures must be destroyed separately.
*/ */

View file

@ -24,6 +24,9 @@ struct wlr_texture {
/** /**
* Create a new texture from raw pixel data. `stride` is in bytes. The returned * Create a new texture from raw pixel data. `stride` is in bytes. The returned
* texture is mutable. * texture is mutable.
*
* Should not be called in a rendering block like renderer_begin()/end() or
* between attaching a renderer to an output and committing it.
*/ */
struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer, struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer,
enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height, enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height,
@ -32,12 +35,18 @@ struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer,
/** /**
* Create a new texture from a wl_drm resource. The returned texture is * Create a new texture from a wl_drm resource. The returned texture is
* immutable. * immutable.
*
* Should not be called in a rendering block like renderer_begin()/end() or
* between attaching a renderer to an output and committing it.
*/ */
struct wlr_texture *wlr_texture_from_wl_drm(struct wlr_renderer *renderer, struct wlr_texture *wlr_texture_from_wl_drm(struct wlr_renderer *renderer,
struct wl_resource *data); struct wl_resource *data);
/** /**
* Create a new texture from a DMA-BUF. The returned texture is immutable. * Create a new texture from a DMA-BUF. The returned texture is immutable.
*
* Should not be called in a rendering block like renderer_begin()/end() or
* between attaching a renderer to an output and committing it.
*/ */
struct wlr_texture *wlr_texture_from_dmabuf(struct wlr_renderer *renderer, struct wlr_texture *wlr_texture_from_dmabuf(struct wlr_renderer *renderer,
struct wlr_dmabuf_attributes *attribs); struct wlr_dmabuf_attributes *attribs);
@ -58,6 +67,9 @@ bool wlr_texture_is_opaque(struct wlr_texture *texture);
/** /**
* Update a texture with raw pixels. The texture must be mutable, and the input * Update a texture with raw pixels. The texture must be mutable, and the input
* data must have the same pixel format that the texture was created with. * data must have the same pixel format that the texture was created with.
*
* Should not be called in a rendering block like renderer_begin()/end() or
* between attaching a renderer to an output and committing it.
*/ */
bool wlr_texture_write_pixels(struct wlr_texture *texture, bool wlr_texture_write_pixels(struct wlr_texture *texture,
uint32_t stride, uint32_t width, uint32_t height, uint32_t stride, uint32_t width, uint32_t height,

View file

@ -103,6 +103,11 @@ struct wlr_client_buffer {
struct wlr_renderer; struct wlr_renderer;
/**
* Get a client buffer from a generic buffer. If the buffer isn't a client
* buffer, returns NULL.
*/
struct wlr_client_buffer *wlr_client_buffer_get(struct wlr_buffer *buffer);
/** /**
* Check if a resource is a wl_buffer resource. * Check if a resource is a wl_buffer resource.
*/ */

View file

@ -136,7 +136,6 @@ struct wlr_drag {
struct wl_signal destroy; struct wl_signal destroy;
} events; } events;
struct wl_listener point_destroy;
struct wl_listener source_destroy; struct wl_listener source_destroy;
struct wl_listener seat_client_destroy; struct wl_listener seat_client_destroy;
struct wl_listener icon_destroy; struct wl_listener icon_destroy;

View file

@ -29,12 +29,11 @@ struct wlr_export_dmabuf_frame_v1 {
struct wlr_export_dmabuf_manager_v1 *manager; struct wlr_export_dmabuf_manager_v1 *manager;
struct wl_list link; // wlr_export_dmabuf_manager_v1::frames struct wl_list link; // wlr_export_dmabuf_manager_v1::frames
struct wlr_dmabuf_attributes attribs;
struct wlr_output *output; struct wlr_output *output;
bool cursor_locked; bool cursor_locked;
struct wl_listener output_precommit; struct wl_listener output_commit;
}; };
struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create( struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create(

View file

@ -50,6 +50,7 @@ struct wlr_foreign_toplevel_handle_v1 {
char *title; char *title;
char *app_id; char *app_id;
struct wlr_foreign_toplevel_handle_v1 *parent;
struct wl_list outputs; // wlr_foreign_toplevel_v1_output struct wl_list outputs; // wlr_foreign_toplevel_v1_output
uint32_t state; // wlr_foreign_toplevel_v1_state uint32_t state; // wlr_foreign_toplevel_v1_state
@ -104,6 +105,12 @@ struct wlr_foreign_toplevel_manager_v1 *wlr_foreign_toplevel_manager_v1_create(
struct wlr_foreign_toplevel_handle_v1 *wlr_foreign_toplevel_handle_v1_create( struct wlr_foreign_toplevel_handle_v1 *wlr_foreign_toplevel_handle_v1_create(
struct wlr_foreign_toplevel_manager_v1 *manager); struct wlr_foreign_toplevel_manager_v1 *manager);
/* Destroy the given toplevel handle, sending the closed event to any
* client. Also, if the destroyed toplevel is set as a parent of any
* other valid toplevel, clients still holding a handle to both are
* sent a parent signal with NULL parent. If this is not desired, the
* caller should ensure that any child toplevels are destroyed before
* the parent. */
void wlr_foreign_toplevel_handle_v1_destroy( void wlr_foreign_toplevel_handle_v1_destroy(
struct wlr_foreign_toplevel_handle_v1 *toplevel); struct wlr_foreign_toplevel_handle_v1 *toplevel);
@ -126,4 +133,14 @@ void wlr_foreign_toplevel_handle_v1_set_activated(
void wlr_foreign_toplevel_handle_v1_set_fullscreen( void wlr_foreign_toplevel_handle_v1_set_fullscreen(
struct wlr_foreign_toplevel_handle_v1* toplevel, bool fullscreen); struct wlr_foreign_toplevel_handle_v1* toplevel, bool fullscreen);
/* Set the parent of a toplevel. If the parent changed from its previous
* value, also sends a parent event to all clients that hold handles to
* both toplevel and parent (no message is sent to clients that have
* previously destroyed their parent handle). NULL is allowed as the
* parent, meaning no parent exists. */
void wlr_foreign_toplevel_handle_v1_set_parent(
struct wlr_foreign_toplevel_handle_v1 *toplevel,
struct wlr_foreign_toplevel_handle_v1 *parent);
#endif #endif

View file

@ -21,6 +21,10 @@ struct wlr_gamma_control_v1 {
struct wlr_output *output; struct wlr_output *output;
struct wl_list link; struct wl_list link;
uint16_t *table;
size_t ramp_size;
struct wl_listener output_commit_listener;
struct wl_listener output_destroy_listener; struct wl_listener output_destroy_listener;
void *data; void *data;

View file

@ -9,6 +9,8 @@
#ifndef WLR_TYPES_WLR_INPUT_DEVICE_H #ifndef WLR_TYPES_WLR_INPUT_DEVICE_H
#define WLR_TYPES_WLR_INPUT_DEVICE_H #define WLR_TYPES_WLR_INPUT_DEVICE_H
#include <wayland-server-core.h>
enum wlr_button_state { enum wlr_button_state {
WLR_BUTTON_RELEASED, WLR_BUTTON_RELEASED,
WLR_BUTTON_PRESSED, WLR_BUTTON_PRESSED,
@ -23,14 +25,6 @@ enum wlr_input_device_type {
WLR_INPUT_DEVICE_SWITCH, WLR_INPUT_DEVICE_SWITCH,
}; };
/* Note: these are circular dependencies */
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_tablet_tool.h>
#include <wlr/types/wlr_tablet_pad.h>
#include <wlr/types/wlr_switch.h>
struct wlr_input_device_impl; struct wlr_input_device_impl;
struct wlr_input_device { struct wlr_input_device {

View file

@ -12,28 +12,28 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wayland-server-core.h> #include <wayland-server-protocol.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#define WLR_LED_COUNT 3 #define WLR_LED_COUNT 3
enum wlr_keyboard_led { enum wlr_keyboard_led {
WLR_LED_NUM_LOCK = 1, WLR_LED_NUM_LOCK = 1 << 0,
WLR_LED_CAPS_LOCK = 2, WLR_LED_CAPS_LOCK = 1 << 1,
WLR_LED_SCROLL_LOCK = 4, WLR_LED_SCROLL_LOCK = 1 << 2,
}; };
#define WLR_MODIFIER_COUNT 8 #define WLR_MODIFIER_COUNT 8
enum wlr_keyboard_modifier { enum wlr_keyboard_modifier {
WLR_MODIFIER_SHIFT = 1, WLR_MODIFIER_SHIFT = 1 << 0,
WLR_MODIFIER_CAPS = 2, WLR_MODIFIER_CAPS = 1 << 1,
WLR_MODIFIER_CTRL = 4, WLR_MODIFIER_CTRL = 1 << 2,
WLR_MODIFIER_ALT = 8, WLR_MODIFIER_ALT = 1 << 3,
WLR_MODIFIER_MOD2 = 16, WLR_MODIFIER_MOD2 = 1 << 4,
WLR_MODIFIER_MOD3 = 32, WLR_MODIFIER_MOD3 = 1 << 5,
WLR_MODIFIER_LOGO = 64, WLR_MODIFIER_LOGO = 1 << 6,
WLR_MODIFIER_MOD5 = 128, WLR_MODIFIER_MOD5 = 1 << 7,
}; };
#define WLR_KEYBOARD_KEYS_CAP 32 #define WLR_KEYBOARD_KEYS_CAP 32
@ -91,16 +91,11 @@ struct wlr_keyboard {
void *data; void *data;
}; };
enum wlr_key_state {
WLR_KEY_RELEASED,
WLR_KEY_PRESSED,
};
struct wlr_event_keyboard_key { struct wlr_event_keyboard_key {
uint32_t time_msec; uint32_t time_msec;
uint32_t keycode; uint32_t keycode;
bool update_state; // if backend doesn't update modifiers on its own bool update_state; // if backend doesn't update modifiers on its own
enum wlr_key_state state; enum wl_keyboard_key_state state;
}; };
bool wlr_keyboard_set_keymap(struct wlr_keyboard *kb, bool wlr_keyboard_set_keymap(struct wlr_keyboard *kb,

View file

@ -138,6 +138,10 @@ struct wlr_layer_surface_v1 *wlr_layer_surface_v1_from_wlr_surface(
void wlr_layer_surface_v1_for_each_surface(struct wlr_layer_surface_v1 *surface, void wlr_layer_surface_v1_for_each_surface(struct wlr_layer_surface_v1 *surface,
wlr_surface_iterator_func_t iterator, void *user_data); wlr_surface_iterator_func_t iterator, void *user_data);
/* Calls the iterator function for each popup of this surface */
void wlr_layer_surface_v1_for_each_popup(struct wlr_layer_surface_v1 *surface,
wlr_surface_iterator_func_t iterator, void *user_data);
/** /**
* Find a surface within this layer-surface tree at the given surface-local * Find a surface within this layer-surface tree at the given surface-local
* coordinates. Returns the surface and coordinates in the leaf surface * coordinates. Returns the surface and coordinates in the leaf surface

View file

@ -164,9 +164,11 @@ struct wlr_output {
// Emitted right before commit // Emitted right before commit
struct wl_signal precommit; // wlr_output_event_precommit struct wl_signal precommit; // wlr_output_event_precommit
// Emitted right after commit // Emitted right after commit
struct wl_signal commit; struct wl_signal commit; // wlr_output_event_commit
// Emitted right after the buffer has been presented to the user // Emitted right after the buffer has been presented to the user
struct wl_signal present; // wlr_output_event_present struct wl_signal present; // wlr_output_event_present
// Emitted after a client bound the wl_output global
struct wl_signal bind; // wlr_output_event_bind
struct wl_signal enable; struct wl_signal enable;
struct wl_signal mode; struct wl_signal mode;
struct wl_signal scale; struct wl_signal scale;
@ -199,6 +201,12 @@ struct wlr_output_event_precommit {
struct timespec *when; struct timespec *when;
}; };
struct wlr_output_event_commit {
struct wlr_output *output;
uint32_t committed; // bitmask of enum wlr_output_state_field
struct timespec *when;
};
enum wlr_output_present_flag { enum wlr_output_present_flag {
// The presentation was synchronized to the "vertical retrace" by the // The presentation was synchronized to the "vertical retrace" by the
// display hardware such that tearing does not happen. // display hardware such that tearing does not happen.
@ -228,6 +236,11 @@ struct wlr_output_event_present {
uint32_t flags; // enum wlr_output_present_flag uint32_t flags; // enum wlr_output_present_flag
}; };
struct wlr_output_event_bind {
struct wlr_output *output;
struct wl_resource *resource;
};
struct wlr_surface; struct wlr_surface;
/** /**
@ -342,8 +355,7 @@ bool wlr_output_preferred_read_format(struct wlr_output *output,
* the screen that has changed since the last frame. * the screen that has changed since the last frame.
* *
* Compositors implementing damage tracking should call this function with the * Compositors implementing damage tracking should call this function with the
* damaged region in output-buffer-local coordinates (ie. scaled and * damaged region in output-buffer-local coordinates.
* transformed).
* *
* This region is not to be confused with the renderer's buffer damage, ie. the * This region is not to be confused with the renderer's buffer damage, ie. the
* region compositors need to repaint. Compositors usually need to repaint more * region compositors need to repaint. Compositors usually need to repaint more
@ -392,8 +404,7 @@ size_t wlr_output_get_gamma_size(struct wlr_output *output);
void wlr_output_set_gamma(struct wlr_output *output, size_t size, void wlr_output_set_gamma(struct wlr_output *output, size_t size,
const uint16_t *r, const uint16_t *g, const uint16_t *b); const uint16_t *r, const uint16_t *g, const uint16_t *b);
/** /**
* Exports the output's current back-buffer as a DMA-BUF (ie. the buffer that * Exports the last committed buffer as a DMA-BUF.
* will be displayed on next commit).
* *
* The caller is responsible for cleaning up the DMA-BUF attributes. * The caller is responsible for cleaning up the DMA-BUF attributes.
*/ */

View file

@ -124,10 +124,10 @@ struct wlr_output *wlr_output_layout_get_center_output(
struct wlr_output_layout *layout); struct wlr_output_layout *layout);
enum wlr_direction { enum wlr_direction {
WLR_DIRECTION_UP = 1, WLR_DIRECTION_UP = 1 << 0,
WLR_DIRECTION_DOWN = 2, WLR_DIRECTION_DOWN = 1 << 1,
WLR_DIRECTION_LEFT = 4, WLR_DIRECTION_LEFT = 1 << 2,
WLR_DIRECTION_RIGHT = 8, WLR_DIRECTION_RIGHT = 1 << 3,
}; };
/** /**

View file

@ -25,7 +25,7 @@ struct wlr_output_power_v1 {
struct wl_list link; struct wl_list link;
struct wl_listener output_destroy_listener; struct wl_listener output_destroy_listener;
struct wl_listener output_enable_listener; struct wl_listener output_commit_listener;
void *data; void *data;
}; };

View file

@ -11,6 +11,7 @@
#include <stdint.h> #include <stdint.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wayland-server-protocol.h>
#include <wlr/types/wlr_input_device.h> #include <wlr/types/wlr_input_device.h>
struct wlr_pointer_impl; struct wlr_pointer_impl;

View file

@ -53,6 +53,7 @@ struct wlr_screencopy_frame_v1 {
struct wlr_output *output; struct wlr_output *output;
struct wl_listener output_precommit; struct wl_listener output_precommit;
struct wl_listener output_commit;
struct wl_listener output_destroy; struct wl_listener output_destroy;
struct wl_listener output_enable; struct wl_listener output_enable;

View file

@ -13,6 +13,7 @@
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/types/wlr_input_device.h> #include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h> #include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_surface.h>
#define WLR_SERIAL_RINGSET_SIZE 128 #define WLR_SERIAL_RINGSET_SIZE 128

View file

@ -67,6 +67,15 @@ struct wlr_surface_role {
void (*precommit)(struct wlr_surface *surface); void (*precommit)(struct wlr_surface *surface);
}; };
struct wlr_surface_output {
struct wlr_surface *surface;
struct wlr_output *output;
struct wl_list link; // wlr_surface::current_outputs
struct wl_listener bind;
struct wl_listener destroy;
};
struct wlr_surface { struct wlr_surface {
struct wl_resource *resource; struct wl_resource *resource;
struct wlr_renderer *renderer; struct wlr_renderer *renderer;
@ -126,6 +135,8 @@ struct wlr_surface {
// wlr_subsurface::parent_pending_link // wlr_subsurface::parent_pending_link
struct wl_list subsurface_pending_list; struct wl_list subsurface_pending_list;
struct wl_list current_outputs; // wlr_surface_output::link
struct wl_listener renderer_destroy; struct wl_listener renderer_destroy;
void *data; void *data;

View file

@ -48,8 +48,6 @@ struct wlr_xdg_client {
}; };
struct wlr_xdg_positioner { struct wlr_xdg_positioner {
struct wl_resource *resource;
struct wlr_box anchor_rect; struct wlr_box anchor_rect;
enum xdg_positioner_anchor anchor; enum xdg_positioner_anchor anchor;
enum xdg_positioner_gravity gravity; enum xdg_positioner_gravity gravity;
@ -124,6 +122,7 @@ struct wlr_xdg_toplevel {
struct wlr_xdg_toplevel_state client_pending; struct wlr_xdg_toplevel_state client_pending;
struct wlr_xdg_toplevel_state server_pending; struct wlr_xdg_toplevel_state server_pending;
struct wlr_xdg_toplevel_state last_acked;
struct wlr_xdg_toplevel_state current; struct wlr_xdg_toplevel_state current;
char *title; char *title;

View file

@ -1,374 +0,0 @@
/*
* This protocol is obsolete and will be removed in a future version. The
* recommended replacement is xdg-shell.
*/
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_TYPES_WLR_XDG_SHELL_V6_H
#define WLR_TYPES_WLR_XDG_SHELL_V6_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_seat.h>
#include "xdg-shell-unstable-v6-protocol.h"
/**
* An interface enabling clients to turn their wl_surfaces into windows in a
* desktop environment.
*/
struct wlr_xdg_shell_v6 {
struct wl_global *global;
struct wl_list clients;
struct wl_list popup_grabs;
uint32_t ping_timeout;
struct wl_listener display_destroy;
struct {
/**
* The `new_surface` event signals that a client has requested to
* create a new shell surface. At this point, the surface is ready to
* be configured but is not mapped or ready receive input events. The
* surface will be ready to be managed on the `map` event.
*/
struct wl_signal new_surface;
struct wl_signal destroy;
} events;
void *data;
};
struct wlr_xdg_client_v6 {
struct wlr_xdg_shell_v6 *shell;
struct wl_resource *resource;
struct wl_client *client;
struct wl_list surfaces;
struct wl_list link; // wlr_xdg_shell_v6::clients
uint32_t ping_serial;
struct wl_event_source *ping_timer;
};
struct wlr_xdg_positioner_v6 {
struct wlr_box anchor_rect;
enum zxdg_positioner_v6_anchor anchor;
enum zxdg_positioner_v6_gravity gravity;
enum zxdg_positioner_v6_constraint_adjustment constraint_adjustment;
struct {
int32_t width, height;
} size;
struct {
int32_t x, y;
} offset;
};
struct wlr_xdg_popup_v6 {
struct wlr_xdg_surface_v6 *base;
struct wl_list link;
struct wl_resource *resource;
bool committed;
struct wlr_xdg_surface_v6 *parent;
struct wlr_seat *seat;
// Position of the popup relative to the upper left corner of the window
// geometry of the parent surface
struct wlr_box geometry;
struct wlr_xdg_positioner_v6 positioner;
struct wl_list grab_link; // wlr_xdg_popup_grab_v6::popups
};
// each seat gets a popup grab
struct wlr_xdg_popup_grab_v6 {
struct wl_client *client;
struct wlr_seat_pointer_grab pointer_grab;
struct wlr_seat_keyboard_grab keyboard_grab;
struct wlr_seat_touch_grab touch_grab;
struct wlr_seat *seat;
struct wl_list popups;
struct wl_list link; // wlr_xdg_shell_v6::popup_grabs
struct wl_listener seat_destroy;
};
enum wlr_xdg_surface_v6_role {
WLR_XDG_SURFACE_V6_ROLE_NONE,
WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL,
WLR_XDG_SURFACE_V6_ROLE_POPUP,
};
struct wlr_xdg_toplevel_v6_state {
bool maximized, fullscreen, resizing, activated;
uint32_t width, height;
uint32_t max_width, max_height;
uint32_t min_width, min_height;
// Since the fullscreen request may be made before the toplevel's surface
// is mapped, this is used to store the requested fullscreen output (if
// any) for wlr_xdg_toplevel_v6::client_pending.
struct wlr_output *fullscreen_output;
struct wl_listener fullscreen_output_destroy;
};
/**
* An xdg-surface is a user interface element requiring management by the
* compositor. An xdg-surface alone isn't useful, a role should be assigned to
* it in order to map it.
*
* When a surface has a role and is ready to be displayed, the `map` event is
* emitted. When a surface should no longer be displayed, the `unmap` event is
* emitted. The `unmap` event is guaranteed to be emitted before the `destroy`
* event if the view is destroyed when mapped.
*/
struct wlr_xdg_toplevel_v6 {
struct wl_resource *resource;
struct wlr_xdg_surface_v6 *base;
struct wlr_xdg_surface_v6 *parent;
bool added;
struct wlr_xdg_toplevel_v6_state client_pending;
struct wlr_xdg_toplevel_v6_state server_pending;
struct wlr_xdg_toplevel_v6_state current;
char *title;
char *app_id;
struct {
struct wl_signal request_maximize;
struct wl_signal request_fullscreen;
struct wl_signal request_minimize;
struct wl_signal request_move;
struct wl_signal request_resize;
struct wl_signal request_show_window_menu;
struct wl_signal set_parent;
struct wl_signal set_title;
struct wl_signal set_app_id;
} events;
};
struct wlr_xdg_surface_v6_configure {
struct wl_list link; // wlr_xdg_surface_v6::configure_list
uint32_t serial;
struct wlr_xdg_toplevel_v6_state *toplevel_state;
};
struct wlr_xdg_surface_v6 {
struct wlr_xdg_client_v6 *client;
struct wl_resource *resource;
struct wlr_surface *surface;
struct wl_list link; // wlr_xdg_client_v6::surfaces
enum wlr_xdg_surface_v6_role role;
union {
struct wlr_xdg_toplevel_v6 *toplevel;
struct wlr_xdg_popup_v6 *popup;
};
struct wl_list popups; // wlr_xdg_popup_v6::link
bool added, configured, mapped;
uint32_t configure_serial;
struct wl_event_source *configure_idle;
uint32_t configure_next_serial;
struct wl_list configure_list;
bool has_next_geometry;
struct wlr_box next_geometry;
struct wlr_box geometry;
struct wl_listener surface_destroy;
struct wl_listener surface_commit;
struct {
struct wl_signal destroy;
struct wl_signal ping_timeout;
struct wl_signal new_popup;
/**
* The `map` event signals that the shell surface is ready to be
* managed by the compositor and rendered on the screen. At this point,
* the surface has configured its properties, has had the opportunity
* to bind to the seat to receive input events, and has a buffer that
* is ready to be rendered. You can now safely add this surface to a
* list of views.
*/
struct wl_signal map;
/**
* The `unmap` event signals that the surface is no longer in a state
* where it should be shown on the screen. This might happen if the
* surface no longer has a displayable buffer because either the
* surface has been hidden or is about to be destroyed.
*/
struct wl_signal unmap;
} events;
void *data;
};
struct wlr_xdg_toplevel_v6_move_event {
struct wlr_xdg_surface_v6 *surface;
struct wlr_seat_client *seat;
uint32_t serial;
};
struct wlr_xdg_toplevel_v6_resize_event {
struct wlr_xdg_surface_v6 *surface;
struct wlr_seat_client *seat;
uint32_t serial;
uint32_t edges;
};
struct wlr_xdg_toplevel_v6_set_fullscreen_event {
struct wlr_xdg_surface_v6 *surface;
bool fullscreen;
struct wlr_output *output;
};
struct wlr_xdg_toplevel_v6_show_window_menu_event {
struct wlr_xdg_surface_v6 *surface;
struct wlr_seat_client *seat;
uint32_t serial;
uint32_t x, y;
};
struct wlr_xdg_shell_v6 *wlr_xdg_shell_v6_create(struct wl_display *display);
/**
* Send a ping to the surface. If the surface does not respond in a reasonable
* amount of time, the ping_timeout event will be emitted.
*/
void wlr_xdg_surface_v6_ping(struct wlr_xdg_surface_v6 *surface);
/**
* Request that this toplevel surface be the given size. Returns the associated
* configure serial.
*/
uint32_t wlr_xdg_toplevel_v6_set_size(struct wlr_xdg_surface_v6 *surface,
uint32_t width, uint32_t height);
/**
* Request that this toplevel surface show itself in an activated or deactivated
* state. Returns the associated configure serial.
*/
uint32_t wlr_xdg_toplevel_v6_set_activated(struct wlr_xdg_surface_v6 *surface,
bool activated);
/**
* Request that this toplevel surface consider itself maximized or not
* maximized. Returns the associated configure serial.
*/
uint32_t wlr_xdg_toplevel_v6_set_maximized(struct wlr_xdg_surface_v6 *surface,
bool maximized);
/**
* Request that this toplevel surface consider itself fullscreen or not
* fullscreen. Returns the associated configure serial.
*/
uint32_t wlr_xdg_toplevel_v6_set_fullscreen(struct wlr_xdg_surface_v6 *surface,
bool fullscreen);
/**
* Request that this toplevel surface consider itself to be resizing or not
* resizing. Returns the associated configure serial.
*/
uint32_t wlr_xdg_toplevel_v6_set_resizing(struct wlr_xdg_surface_v6 *surface,
bool resizing);
/**
* Request that this xdg surface closes.
*/
void wlr_xdg_surface_v6_send_close(struct wlr_xdg_surface_v6 *surface);
/**
* Find a surface within this xdg-surface tree at the given surface-local
* coordinates. Returns the surface and coordinates in the leaf surface
* coordinate system or NULL if no surface is found at that location.
*/
struct wlr_surface *wlr_xdg_surface_v6_surface_at(
struct wlr_xdg_surface_v6 *surface, double sx, double sy,
double *sub_x, double *sub_y);
/**
* Get the geometry for this positioner based on the anchor rect, gravity, and
* size of this positioner.
*/
struct wlr_box wlr_xdg_positioner_v6_get_geometry(
struct wlr_xdg_positioner_v6 *positioner);
/**
* Get the anchor point for this popup in the toplevel parent's coordinate system.
*/
void wlr_xdg_popup_v6_get_anchor_point(struct wlr_xdg_popup_v6 *popup,
int *toplevel_sx, int *toplevel_sy);
/**
* Convert the given coordinates in the popup coordinate system to the toplevel
* surface coordinate system.
*/
void wlr_xdg_popup_v6_get_toplevel_coords(struct wlr_xdg_popup_v6 *popup,
int popup_sx, int popup_sy, int *toplevel_sx, int *toplevel_sy);
/**
* Set the geometry of this popup to unconstrain it according to its
* xdg-positioner rules. The box should be in the popup's root toplevel parent
* surface coordinate system.
*/
void wlr_xdg_popup_v6_unconstrain_from_box(struct wlr_xdg_popup_v6 *popup,
struct wlr_box *toplevel_sx_box);
/**
Invert the right/left anchor and gravity for this positioner. This can be
used to "flip" the positioner around the anchor rect in the x direction.
*/
void wlr_positioner_v6_invert_x(
struct wlr_xdg_positioner_v6 *positioner);
/**
Invert the top/bottom anchor and gravity for this positioner. This can be
used to "flip" the positioner around the anchor rect in the y direction.
*/
void wlr_positioner_v6_invert_y(
struct wlr_xdg_positioner_v6 *positioner);
bool wlr_surface_is_xdg_surface_v6(struct wlr_surface *surface);
struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6_from_wlr_surface(
struct wlr_surface *surface);
/**
* Get the surface geometry.
* This is either the geometry as set by the client, or defaulted to the bounds
* of the surface + the subsurfaces (as specified by the protocol).
*
* The x and y value can be <0
*/
void wlr_xdg_surface_v6_get_geometry(struct wlr_xdg_surface_v6 *surface, struct wlr_box *box);
/**
* Call `iterator` on each surface and popup in the xdg-surface tree, with the
* surface's position relative to the root xdg-surface. The function is called
* from root to leaves (in rendering order).
*/
void wlr_xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface,
wlr_surface_iterator_func_t iterator, void *user_data);
/**
* Call `iterator` on each popup in the xdg-surface tree, with the popup's
* position relative to the root xdg-surface. The function is called from root
* to leaves (in rendering order).
*/
void wlr_xdg_surface_v6_for_each_popup(struct wlr_xdg_surface_v6 *surface,
wlr_surface_iterator_func_t iterator, void *user_data);
#endif

View file

@ -19,10 +19,10 @@
enum wlr_edges { enum wlr_edges {
WLR_EDGE_NONE = 0, WLR_EDGE_NONE = 0,
WLR_EDGE_TOP = 1, WLR_EDGE_TOP = 1 << 0,
WLR_EDGE_BOTTOM = 2, WLR_EDGE_BOTTOM = 1 << 1,
WLR_EDGE_LEFT = 4, WLR_EDGE_LEFT = 1 << 2,
WLR_EDGE_RIGHT = 8, WLR_EDGE_RIGHT = 1 << 3,
}; };
#endif #endif

Some files were not shown because too many files have changed in this diff Show more