mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2025-11-02 09:01:38 -05:00
Compare commits
25 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c9e6b7c9f | ||
|
|
e432b7bd1c | ||
|
|
ea1ade5e5d | ||
|
|
f56a69aa76 | ||
|
|
8fc64ae8d5 | ||
|
|
761927bbbd | ||
|
|
0d20e46498 | ||
|
|
eba71d59d4 | ||
|
|
9b42c1901d | ||
|
|
7a52788929 | ||
|
|
d4009183a1 | ||
|
|
d092e40dec | ||
|
|
bb5180ce9e | ||
|
|
73aa61686f | ||
|
|
9079380498 | ||
|
|
3118ca5c3e | ||
|
|
c1938f79c3 | ||
|
|
aa904ccf06 | ||
|
|
52e1ad01e3 | ||
|
|
30c0602457 | ||
|
|
b7205866c0 | ||
|
|
f935404e68 | ||
|
|
5aa8c192a5 | ||
|
|
68dea55970 | ||
|
|
f3fe6b9a43 |
23 changed files with 158 additions and 142 deletions
|
|
@ -41,9 +41,10 @@ tasks:
|
||||||
cd wlroots/build-gcc/tinywl
|
cd wlroots/build-gcc/tinywl
|
||||||
sudo modprobe vkms
|
sudo modprobe vkms
|
||||||
udevadm settle
|
udevadm settle
|
||||||
|
card="/dev/dri/$(ls /sys/devices/faux/vkms/drm/ | grep ^card)"
|
||||||
export WLR_BACKENDS=drm
|
export WLR_BACKENDS=drm
|
||||||
export WLR_RENDERER=pixman
|
export WLR_RENDERER=pixman
|
||||||
export WLR_DRM_DEVICES=/dev/dri/by-path/platform-vkms-card
|
export WLR_DRM_DEVICES="$card"
|
||||||
export UBSAN_OPTIONS=halt_on_error=1
|
export UBSAN_OPTIONS=halt_on_error=1
|
||||||
sudo chmod ugo+rw /dev/dri/by-path/platform-vkms-card
|
sudo chmod ugo+rw "$card"
|
||||||
sudo -E seatd-launch -- ./tinywl -s 'kill $PPID' || [ $? = 143 ]
|
sudo -E seatd-launch -- ./tinywl -s 'kill $PPID' || [ $? = 143 ]
|
||||||
|
|
|
||||||
|
|
@ -485,5 +485,10 @@ bool wlr_backend_commit(struct wlr_backend *backend,
|
||||||
output_apply_commit(state->output, &state->base);
|
output_apply_commit(state->output, &state->base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < states_len; i++) {
|
||||||
|
const struct wlr_backend_output_state *state = &states[i];
|
||||||
|
output_send_commit_event(state->output, &state->base);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ void init_device_tablet_pad(struct wlr_libinput_input_device *dev) {
|
||||||
struct udev_device *udev = libinput_device_get_udev_device(handle);
|
struct udev_device *udev = libinput_device_get_udev_device(handle);
|
||||||
char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *));
|
char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *));
|
||||||
*dst = strdup(udev_device_get_syspath(udev));
|
*dst = strdup(udev_device_get_syspath(udev));
|
||||||
|
udev_device_unref(udev);
|
||||||
|
|
||||||
int groups = libinput_device_tablet_pad_get_num_mode_groups(handle);
|
int groups = libinput_device_tablet_pad_get_num_mode_groups(handle);
|
||||||
for (int i = 0; i < groups; ++i) {
|
for (int i = 0; i < groups; ++i) {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ void init_device_tablet(struct wlr_libinput_input_device *dev) {
|
||||||
struct udev_device *udev = libinput_device_get_udev_device(dev->handle);
|
struct udev_device *udev = libinput_device_get_udev_device(dev->handle);
|
||||||
char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *));
|
char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *));
|
||||||
*dst = strdup(udev_device_get_syspath(udev));
|
*dst = strdup(udev_device_get_syspath(udev));
|
||||||
|
udev_device_unref(udev);
|
||||||
|
|
||||||
wl_list_init(&dev->tablet_tools);
|
wl_list_init(&dev->tablet_tools);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,7 +367,10 @@ void wlr_session_close_file(struct wlr_session *session,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(wl_list_empty(&dev->events.change.listener_list));
|
assert(wl_list_empty(&dev->events.change.listener_list));
|
||||||
assert(wl_list_empty(&dev->events.remove.listener_list));
|
// TODO: assert that the "remove" listener list is empty as well. Listeners
|
||||||
|
// will typically call wlr_session_close_file() in response, and
|
||||||
|
// wl_signal_emit_mutable() installs two phantom listeners, so we'd count
|
||||||
|
// these two.
|
||||||
|
|
||||||
close(dev->fd);
|
close(dev->fd);
|
||||||
wl_list_remove(&dev->link);
|
wl_list_remove(&dev->link);
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ void output_defer_present(struct wlr_output *output, struct wlr_output_event_pre
|
||||||
|
|
||||||
bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state);
|
bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state);
|
||||||
void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state);
|
void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state);
|
||||||
|
void output_send_commit_event(struct wlr_output *output, const struct wlr_output_state *state);
|
||||||
|
|
||||||
void output_state_get_buffer_src_box(const struct wlr_output_state *state,
|
void output_state_get_buffer_src_box(const struct wlr_output_state *state,
|
||||||
struct wlr_fbox *out);
|
struct wlr_fbox *out);
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,6 @@ struct wlr_drm_lease_connector_v1 {
|
||||||
|
|
||||||
struct wlr_output *output;
|
struct wlr_output *output;
|
||||||
struct wlr_drm_lease_device_v1 *device;
|
struct wlr_drm_lease_device_v1 *device;
|
||||||
/** NULL if no client is currently leasing this connector */
|
|
||||||
struct wlr_drm_lease_v1 *active_lease;
|
|
||||||
|
|
||||||
struct wl_list link; // wlr_drm_lease_device_v1.connectors
|
struct wl_list link; // wlr_drm_lease_device_v1.connectors
|
||||||
|
|
||||||
|
|
@ -93,9 +91,6 @@ struct wlr_drm_lease_v1 {
|
||||||
|
|
||||||
struct wlr_drm_lease_device_v1 *device;
|
struct wlr_drm_lease_device_v1 *device;
|
||||||
|
|
||||||
struct wlr_drm_lease_connector_v1 **connectors;
|
|
||||||
size_t n_connectors;
|
|
||||||
|
|
||||||
struct wl_list link; // wlr_drm_lease_device_v1.leases
|
struct wl_list link; // wlr_drm_lease_device_v1.leases
|
||||||
|
|
||||||
void *data;
|
void *data;
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,7 @@ struct wlr_xwm {
|
||||||
struct wl_listener drop_focus_destroy;
|
struct wl_listener drop_focus_destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// xwm_create takes ownership of wm_fd and will close it under all circumstances.
|
||||||
struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd);
|
struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd);
|
||||||
|
|
||||||
void xwm_destroy(struct wlr_xwm *xwm);
|
void xwm_destroy(struct wlr_xwm *xwm);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
project(
|
project(
|
||||||
'wlroots',
|
'wlroots',
|
||||||
'c',
|
'c',
|
||||||
version: '0.19.0',
|
version: '0.19.2',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>=1.3',
|
meson_version: '>=1.3',
|
||||||
default_options: [
|
default_options: [
|
||||||
|
|
|
||||||
20
render/egl.c
20
render/egl.c
|
|
@ -260,7 +260,8 @@ static struct wlr_egl *egl_create(void) {
|
||||||
return egl;
|
return egl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
|
static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display,
|
||||||
|
bool allow_software) {
|
||||||
egl->display = display;
|
egl->display = display;
|
||||||
|
|
||||||
EGLint major, minor;
|
EGLint major, minor;
|
||||||
|
|
@ -326,9 +327,8 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
|
||||||
|
|
||||||
// The only way a non-DRM device is selected is when the user
|
// The only way a non-DRM device is selected is when the user
|
||||||
// explicitly picks software rendering
|
// explicitly picks software rendering
|
||||||
if (check_egl_ext(device_exts_str, "EGL_MESA_device_software") &&
|
if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) {
|
||||||
egl->exts.EXT_device_drm) {
|
if (allow_software || env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) {
|
||||||
if (env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) {
|
|
||||||
wlr_log(WLR_INFO, "Using software rendering");
|
wlr_log(WLR_INFO, "Using software rendering");
|
||||||
} else {
|
} else {
|
||||||
wlr_log(WLR_ERROR, "Software rendering detected, please use "
|
wlr_log(WLR_ERROR, "Software rendering detected, please use "
|
||||||
|
|
@ -382,7 +382,7 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool egl_init(struct wlr_egl *egl, EGLenum platform,
|
static bool egl_init(struct wlr_egl *egl, EGLenum platform,
|
||||||
void *remote_display) {
|
void *remote_display, bool allow_software) {
|
||||||
EGLint display_attribs[3] = {0};
|
EGLint display_attribs[3] = {0};
|
||||||
size_t display_attribs_len = 0;
|
size_t display_attribs_len = 0;
|
||||||
|
|
||||||
|
|
@ -401,7 +401,7 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!egl_init_display(egl, display)) {
|
if (!egl_init_display(egl, display, allow_software)) {
|
||||||
if (egl->exts.KHR_display_reference) {
|
if (egl->exts.KHR_display_reference) {
|
||||||
eglTerminate(display);
|
eglTerminate(display);
|
||||||
}
|
}
|
||||||
|
|
@ -556,6 +556,8 @@ static int open_render_node(int drm_fd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
|
struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
|
||||||
|
bool allow_software = drm_fd < 0;
|
||||||
|
|
||||||
struct wlr_egl *egl = egl_create();
|
struct wlr_egl *egl = egl_create();
|
||||||
if (egl == NULL) {
|
if (egl == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create EGL context");
|
wlr_log(WLR_ERROR, "Failed to create EGL context");
|
||||||
|
|
@ -569,7 +571,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
|
||||||
*/
|
*/
|
||||||
EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd);
|
EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd);
|
||||||
if (egl_device != EGL_NO_DEVICE_EXT) {
|
if (egl_device != EGL_NO_DEVICE_EXT) {
|
||||||
if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device)) {
|
if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device, allow_software)) {
|
||||||
wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT");
|
wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT");
|
||||||
return egl;
|
return egl;
|
||||||
}
|
}
|
||||||
|
|
@ -594,7 +596,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device)) {
|
if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device, allow_software)) {
|
||||||
wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR");
|
wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR");
|
||||||
return egl;
|
return egl;
|
||||||
}
|
}
|
||||||
|
|
@ -633,7 +635,7 @@ struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!egl_init_display(egl, display)) {
|
if (!egl_init_display(egl, display, true)) {
|
||||||
free(egl);
|
free(egl);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ void wlr_render_pass_add_texture(struct wlr_render_pass *render_pass,
|
||||||
if (!wlr_fbox_empty(&options->src_box)) {
|
if (!wlr_fbox_empty(&options->src_box)) {
|
||||||
const struct wlr_fbox *box = &options->src_box;
|
const struct wlr_fbox *box = &options->src_box;
|
||||||
assert(box->x >= 0 && box->y >= 0 &&
|
assert(box->x >= 0 && box->y >= 0 &&
|
||||||
box->x + box->width <= options->texture->width &&
|
(uint32_t)(box->x + box->width) <= options->texture->width &&
|
||||||
box->y + box->height <= options->texture->height);
|
(uint32_t)(box->y + box->height) <= options->texture->height);
|
||||||
}
|
}
|
||||||
|
|
||||||
render_pass->impl->add_texture(render_pass, options);
|
render_pass->impl->add_texture(render_pass, options);
|
||||||
|
|
|
||||||
|
|
@ -66,59 +66,72 @@ static struct wlr_vk_descriptor_pool *alloc_ds(
|
||||||
struct wl_list *pool_list, size_t *last_pool_size) {
|
struct wl_list *pool_list, size_t *last_pool_size) {
|
||||||
VkResult res;
|
VkResult res;
|
||||||
|
|
||||||
bool found = false;
|
|
||||||
struct wlr_vk_descriptor_pool *pool;
|
|
||||||
wl_list_for_each(pool, pool_list, link) {
|
|
||||||
if (pool->free > 0) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) { // create new pool
|
|
||||||
pool = calloc(1, sizeof(*pool));
|
|
||||||
if (!pool) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "allocation failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t count = 2 * (*last_pool_size);
|
|
||||||
if (!count) {
|
|
||||||
count = start_descriptor_pool_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
pool->free = count;
|
|
||||||
VkDescriptorPoolSize pool_size = {
|
|
||||||
.descriptorCount = count,
|
|
||||||
.type = type,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkDescriptorPoolCreateInfo dpool_info = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
|
||||||
.maxSets = count,
|
|
||||||
.poolSizeCount = 1,
|
|
||||||
.pPoolSizes = &pool_size,
|
|
||||||
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
|
||||||
};
|
|
||||||
|
|
||||||
res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL,
|
|
||||||
&pool->pool);
|
|
||||||
if (res != VK_SUCCESS) {
|
|
||||||
wlr_vk_error("vkCreateDescriptorPool", res);
|
|
||||||
free(pool);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*last_pool_size = count;
|
|
||||||
wl_list_insert(pool_list, &pool->link);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkDescriptorSetAllocateInfo ds_info = {
|
VkDescriptorSetAllocateInfo ds_info = {
|
||||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||||
.descriptorSetCount = 1,
|
.descriptorSetCount = 1,
|
||||||
.pSetLayouts = layout,
|
.pSetLayouts = layout,
|
||||||
.descriptorPool = pool->pool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct wlr_vk_descriptor_pool *pool;
|
||||||
|
wl_list_for_each(pool, pool_list, link) {
|
||||||
|
if (pool->free > 0) {
|
||||||
|
ds_info.descriptorPool = pool->pool;
|
||||||
|
res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds);
|
||||||
|
switch (res) {
|
||||||
|
case VK_ERROR_FRAGMENTED_POOL:
|
||||||
|
case VK_ERROR_OUT_OF_POOL_MEMORY:
|
||||||
|
// Descriptor sets with more than one descriptor can cause us
|
||||||
|
// to run out of pool memory early or lead to fragmentation
|
||||||
|
// that makes the pool unable to service our allocation
|
||||||
|
// request. Try the next pool or allocate a new one.
|
||||||
|
continue;
|
||||||
|
case VK_SUCCESS:
|
||||||
|
--pool->free;
|
||||||
|
return pool;
|
||||||
|
default:
|
||||||
|
wlr_vk_error("vkAllocateDescriptorSets", res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pool = calloc(1, sizeof(*pool));
|
||||||
|
if (!pool) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "allocation failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count = 2 * (*last_pool_size);
|
||||||
|
if (!count) {
|
||||||
|
count = start_descriptor_pool_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pool->free = count;
|
||||||
|
VkDescriptorPoolSize pool_size = {
|
||||||
|
.descriptorCount = count,
|
||||||
|
.type = type,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkDescriptorPoolCreateInfo dpool_info = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||||
|
.maxSets = count,
|
||||||
|
.poolSizeCount = 1,
|
||||||
|
.pPoolSizes = &pool_size,
|
||||||
|
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL,
|
||||||
|
&pool->pool);
|
||||||
|
if (res != VK_SUCCESS) {
|
||||||
|
wlr_vk_error("vkCreateDescriptorPool", res);
|
||||||
|
free(pool);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*last_pool_size = count;
|
||||||
|
wl_list_insert(pool_list, &pool->link);
|
||||||
|
|
||||||
|
ds_info.descriptorPool = pool->pool;
|
||||||
res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds);
|
res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds);
|
||||||
if (res != VK_SUCCESS) {
|
if (res != VK_SUCCESS) {
|
||||||
wlr_vk_error("vkAllocateDescriptorSets", res);
|
wlr_vk_error("vkAllocateDescriptorSets", res);
|
||||||
|
|
|
||||||
|
|
@ -283,7 +283,8 @@ static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
wlr_ext_image_copy_capture_frame_v1_ready(frame, WL_OUTPUT_TRANSFORM_NORMAL, &now);
|
wlr_ext_image_copy_capture_frame_v1_ready(frame,
|
||||||
|
cursor_source->output->transform, &now);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wlr_ext_image_capture_source_v1_interface output_cursor_source_impl = {
|
static const struct wlr_ext_image_capture_source_v1_interface output_cursor_source_impl = {
|
||||||
|
|
|
||||||
|
|
@ -288,18 +288,10 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor)
|
||||||
static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) {
|
static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) {
|
||||||
struct wlr_output *output = cursor->output;
|
struct wlr_output *output = cursor->output;
|
||||||
|
|
||||||
if (!output->impl->set_cursor ||
|
if (!output->impl->set_cursor || output->software_cursor_locks > 0) {
|
||||||
output->software_cursor_locks > 0) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_output_cursor *hwcur = output->hardware_cursor;
|
|
||||||
if (hwcur != NULL && hwcur != cursor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->hardware_cursor = NULL;
|
|
||||||
|
|
||||||
struct wlr_texture *texture = cursor->texture;
|
struct wlr_texture *texture = cursor->texture;
|
||||||
|
|
||||||
// If the cursor was hidden or was a software cursor, the hardware
|
// If the cursor was hidden or was a software cursor, the hardware
|
||||||
|
|
@ -424,12 +416,15 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor,
|
||||||
wl_list_init(&cursor->renderer_destroy.link);
|
wl_list_init(&cursor->renderer_destroy.link);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output_cursor_attempt_hardware(cursor)) {
|
if (output->hardware_cursor == NULL || output->hardware_cursor == cursor) {
|
||||||
return true;
|
if (output_cursor_attempt_hardware(cursor)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name);
|
||||||
|
output_disable_hardware_cursor(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name);
|
|
||||||
output_disable_hardware_cursor(output);
|
|
||||||
output_cursor_damage_whole(cursor);
|
output_cursor_damage_whole(cursor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -745,7 +745,9 @@ void output_apply_commit(struct wlr_output *output, const struct wlr_output_stat
|
||||||
}
|
}
|
||||||
|
|
||||||
output_apply_state(output, state);
|
output_apply_state(output, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_send_commit_event(struct wlr_output *output, const struct wlr_output_state *state) {
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
struct wlr_output_event_commit event = {
|
struct wlr_output_event_commit event = {
|
||||||
|
|
@ -787,6 +789,7 @@ bool wlr_output_commit_state(struct wlr_output *output,
|
||||||
}
|
}
|
||||||
|
|
||||||
output_apply_commit(output, &pending);
|
output_apply_commit(output, &pending);
|
||||||
|
output_send_commit_event(output, &pending);
|
||||||
|
|
||||||
if (new_back_buffer) {
|
if (new_back_buffer) {
|
||||||
wlr_buffer_unlock(pending.buffer);
|
wlr_buffer_unlock(pending.buffer);
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,11 @@ static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buf
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(buffer->n_ignore_locks > 0);
|
// If the buffer was a single-pixel buffer where we cached its color
|
||||||
buffer->n_ignore_locks--;
|
// then it won't have been marked as damage-allowed.
|
||||||
|
if (buffer->n_ignore_locks > 0) {
|
||||||
|
buffer->n_ignore_locks--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int min(int a, int b) {
|
static int min(int a, int b) {
|
||||||
|
|
@ -165,7 +168,21 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) {
|
||||||
scene_buffer_unmark_client_buffer(scene_buffer);
|
scene_buffer_unmark_client_buffer(scene_buffer);
|
||||||
|
|
||||||
if (surface->buffer) {
|
if (surface->buffer) {
|
||||||
client_buffer_mark_next_can_damage(surface->buffer);
|
// If we've cached the buffer's single-pixel buffer color
|
||||||
|
// then any in-place updates to the texture wouldn't be
|
||||||
|
// reflected in rendering. So only allow in-place texture
|
||||||
|
// updates if it's not a single pixel buffer. Note that we
|
||||||
|
// can't use the cached scene_buffer->is_single_pixel_buffer
|
||||||
|
// because that's only set later on.
|
||||||
|
bool is_single_pixel_buffer = false;
|
||||||
|
if (surface->buffer->source != NULL) {
|
||||||
|
struct wlr_single_pixel_buffer_v1 *spb =
|
||||||
|
wlr_single_pixel_buffer_v1_try_from_buffer(surface->buffer->source);
|
||||||
|
is_single_pixel_buffer = spb != NULL;
|
||||||
|
}
|
||||||
|
if (!is_single_pixel_buffer) {
|
||||||
|
client_buffer_mark_next_can_damage(surface->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state =
|
struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state =
|
||||||
wlr_linux_drm_syncobj_v1_get_surface_state(surface);
|
wlr_linux_drm_syncobj_v1_get_surface_state(surface);
|
||||||
|
|
@ -186,7 +203,8 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) {
|
||||||
&surface->buffer->base, &options);
|
&surface->buffer->base, &options);
|
||||||
|
|
||||||
if (syncobj_surface_state != NULL &&
|
if (syncobj_surface_state != NULL &&
|
||||||
(surface->current.committed & WLR_SURFACE_STATE_BUFFER)) {
|
(surface->current.committed & WLR_SURFACE_STATE_BUFFER) &&
|
||||||
|
surface->buffer->source != NULL) {
|
||||||
wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state,
|
wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state,
|
||||||
surface->buffer->source);
|
surface->buffer->source);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -530,10 +530,6 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_
|
||||||
struct wlr_cursor *cur = output_cursor->cursor;
|
struct wlr_cursor *cur = output_cursor->cursor;
|
||||||
struct wlr_output *output = output_cursor->output_cursor->output;
|
struct wlr_output *output = output_cursor->output_cursor->output;
|
||||||
|
|
||||||
if (!output->enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor_output_cursor_reset_image(output_cursor);
|
cursor_output_cursor_reset_image(output_cursor);
|
||||||
|
|
||||||
if (cur->state->buffer != NULL) {
|
if (cur->state->buffer != NULL) {
|
||||||
|
|
@ -589,10 +585,11 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_
|
||||||
&src_box, dst_width, dst_height, surface->current.transform,
|
&src_box, dst_width, dst_height, surface->current.transform,
|
||||||
hotspot_x, hotspot_y, wait_timeline, wait_point);
|
hotspot_x, hotspot_y, wait_timeline, wait_point);
|
||||||
|
|
||||||
if (syncobj_surface_state != NULL && surface->buffer != NULL &&
|
if (syncobj_surface_state != NULL &&
|
||||||
|
surface->buffer != NULL && surface->buffer->source != NULL &&
|
||||||
(surface->current.committed & WLR_SURFACE_STATE_BUFFER)) {
|
(surface->current.committed & WLR_SURFACE_STATE_BUFFER)) {
|
||||||
wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state,
|
wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state,
|
||||||
&surface->buffer->base);
|
surface->buffer->source);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output_cursor->output_cursor->visible) {
|
if (output_cursor->output_cursor->visible) {
|
||||||
|
|
|
||||||
|
|
@ -68,10 +68,6 @@ static void drm_lease_connector_v1_destroy(
|
||||||
|
|
||||||
wlr_log(WLR_DEBUG, "Destroying connector %s", connector->output->name);
|
wlr_log(WLR_DEBUG, "Destroying connector %s", connector->output->name);
|
||||||
|
|
||||||
if (connector->active_lease) {
|
|
||||||
wlr_drm_lease_terminate(connector->active_lease->drm_lease);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_resource *resource, *tmp;
|
struct wl_resource *resource, *tmp;
|
||||||
wl_resource_for_each_safe(resource, tmp, &connector->resources) {
|
wl_resource_for_each_safe(resource, tmp, &connector->resources) {
|
||||||
wp_drm_lease_connector_v1_send_withdrawn(resource);
|
wp_drm_lease_connector_v1_send_withdrawn(resource);
|
||||||
|
|
@ -140,14 +136,9 @@ static void lease_handle_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
wl_list_remove(&lease->destroy.link);
|
wl_list_remove(&lease->destroy.link);
|
||||||
|
|
||||||
for (size_t i = 0; i < lease->n_connectors; ++i) {
|
|
||||||
lease->connectors[i]->active_lease = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_remove(&lease->link);
|
wl_list_remove(&lease->link);
|
||||||
wl_resource_set_user_data(lease->resource, NULL);
|
wl_resource_set_user_data(lease->resource, NULL);
|
||||||
|
|
||||||
free(lease->connectors);
|
|
||||||
free(lease);
|
free(lease);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,20 +171,6 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant(
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
lease->connectors = calloc(request->n_connectors, sizeof(*lease->connectors));
|
|
||||||
if (!lease->connectors) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate lease connectors list");
|
|
||||||
close(fd);
|
|
||||||
wp_drm_lease_v1_send_finished(lease->resource);
|
|
||||||
free(lease);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
lease->n_connectors = request->n_connectors;
|
|
||||||
for (size_t i = 0; i < request->n_connectors; ++i) {
|
|
||||||
lease->connectors[i] = request->connectors[i];
|
|
||||||
lease->connectors[i]->active_lease = lease;
|
|
||||||
}
|
|
||||||
|
|
||||||
lease->destroy.notify = lease_handle_destroy;
|
lease->destroy.notify = lease_handle_destroy;
|
||||||
wl_signal_add(&lease->drm_lease->events.destroy, &lease->destroy);
|
wl_signal_add(&lease->drm_lease->events.destroy, &lease->destroy);
|
||||||
|
|
||||||
|
|
@ -338,16 +315,6 @@ static void drm_lease_request_v1_handle_submit(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < request->n_connectors; ++i) {
|
|
||||||
struct wlr_drm_lease_connector_v1 *conn = request->connectors[i];
|
|
||||||
if (conn->active_lease) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create lease, connector %s has "
|
|
||||||
"already been leased", conn->output->name);
|
|
||||||
wp_drm_lease_v1_send_finished(lease_resource);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request->lease_resource = lease_resource;
|
request->lease_resource = lease_resource;
|
||||||
|
|
||||||
wl_signal_emit_mutable(&request->device->manager->events.request,
|
wl_signal_emit_mutable(&request->device->manager->events.request,
|
||||||
|
|
@ -440,10 +407,6 @@ static struct wp_drm_lease_connector_v1_interface lease_connector_impl = {
|
||||||
static void drm_lease_connector_v1_send_to_client(
|
static void drm_lease_connector_v1_send_to_client(
|
||||||
struct wlr_drm_lease_connector_v1 *connector,
|
struct wlr_drm_lease_connector_v1 *connector,
|
||||||
struct wl_resource *resource) {
|
struct wl_resource *resource) {
|
||||||
if (connector->active_lease) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_client *client = wl_resource_get_client(resource);
|
struct wl_client *client = wl_resource_get_client(resource);
|
||||||
|
|
||||||
uint32_t version = wl_resource_get_version(resource);
|
uint32_t version = wl_resource_get_version(resource);
|
||||||
|
|
@ -490,10 +453,12 @@ static void lease_device_bind(struct wl_client *wl_client, void *data,
|
||||||
if (!device) {
|
if (!device) {
|
||||||
wlr_log(WLR_DEBUG, "Failed to bind lease device, "
|
wlr_log(WLR_DEBUG, "Failed to bind lease device, "
|
||||||
"the wlr_drm_lease_device_v1 has been destroyed");
|
"the wlr_drm_lease_device_v1 has been destroyed");
|
||||||
|
wl_list_init(wl_resource_get_link(device_resource));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_resource_set_user_data(device_resource, device);
|
wl_resource_set_user_data(device_resource, device);
|
||||||
|
wl_list_insert(&device->resources, wl_resource_get_link(device_resource));
|
||||||
|
|
||||||
int fd = wlr_drm_backend_get_non_master_fd(device->backend);
|
int fd = wlr_drm_backend_get_non_master_fd(device->backend);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
|
|
@ -505,8 +470,6 @@ static void lease_device_bind(struct wl_client *wl_client, void *data,
|
||||||
wp_drm_lease_device_v1_send_drm_fd(device_resource, fd);
|
wp_drm_lease_device_v1_send_drm_fd(device_resource, fd);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
wl_list_insert(&device->resources, wl_resource_get_link(device_resource));
|
|
||||||
|
|
||||||
struct wlr_drm_lease_connector_v1 *connector;
|
struct wlr_drm_lease_connector_v1 *connector;
|
||||||
wl_list_for_each(connector, &device->connectors, link) {
|
wl_list_for_each(connector, &device->connectors, link) {
|
||||||
drm_lease_connector_v1_send_to_client(connector, device_resource);
|
drm_lease_connector_v1_send_to_client(connector, device_resource);
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ struct wlr_linux_drm_syncobj_surface_v1 {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_linux_drm_syncobj_surface_v1_commit {
|
struct wlr_linux_drm_syncobj_surface_v1_commit {
|
||||||
struct wlr_linux_drm_syncobj_surface_v1 *surface;
|
struct wlr_surface *surface;
|
||||||
struct wlr_drm_syncobj_timeline_waiter waiter;
|
struct wlr_drm_syncobj_timeline_waiter waiter;
|
||||||
uint32_t cached_seq;
|
uint32_t cached_seq;
|
||||||
|
|
||||||
|
|
@ -192,7 +192,7 @@ static struct wlr_linux_drm_syncobj_surface_v1 *surface_from_wlr_surface(
|
||||||
}
|
}
|
||||||
|
|
||||||
static void surface_commit_destroy(struct wlr_linux_drm_syncobj_surface_v1_commit *commit) {
|
static void surface_commit_destroy(struct wlr_linux_drm_syncobj_surface_v1_commit *commit) {
|
||||||
wlr_surface_unlock_cached(commit->surface->surface, commit->cached_seq);
|
wlr_surface_unlock_cached(commit->surface, commit->cached_seq);
|
||||||
wl_list_remove(&commit->surface_destroy.link);
|
wl_list_remove(&commit->surface_destroy.link);
|
||||||
wlr_drm_syncobj_timeline_waiter_finish(&commit->waiter);
|
wlr_drm_syncobj_timeline_waiter_finish(&commit->waiter);
|
||||||
free(commit);
|
free(commit);
|
||||||
|
|
@ -237,7 +237,7 @@ static bool lock_surface_commit(struct wlr_linux_drm_syncobj_surface_v1 *surface
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
commit->surface = surface;
|
commit->surface = surface->surface;
|
||||||
commit->cached_seq = wlr_surface_lock_pending(surface->surface);
|
commit->cached_seq = wlr_surface_lock_pending(surface->surface);
|
||||||
|
|
||||||
commit->surface_destroy.notify = surface_commit_handle_surface_destroy;
|
commit->surface_destroy.notify = surface_commit_handle_surface_destroy;
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ static void manager_create_transient_seat(struct wl_client *client,
|
||||||
wl_resource_set_implementation(seat->resource, &transient_seat_impl,
|
wl_resource_set_implementation(seat->resource, &transient_seat_impl,
|
||||||
seat, transient_seat_handle_resource_destroy);
|
seat, transient_seat_handle_resource_destroy);
|
||||||
|
|
||||||
|
wl_list_init(&seat->seat_destroy.link);
|
||||||
wl_signal_emit_mutable(&manager->events.create_seat, seat);
|
wl_signal_emit_mutable(&manager->events.create_seat, seat);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
20
util/box.c
20
util/box.c
|
|
@ -19,16 +19,15 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y,
|
||||||
//
|
//
|
||||||
// In order to be consistent with e.g. wlr_box_contains_point(),
|
// In order to be consistent with e.g. wlr_box_contains_point(),
|
||||||
// this function returns a point inside the bottom and right edges
|
// this function returns a point inside the bottom and right edges
|
||||||
// of the box by at least 1/65536 of a unit (pixel). 1/65536 is
|
// of the box by at least 1/256 of a unit (pixel). 1/256 is
|
||||||
// small enough to avoid a "dead zone" with high-resolution mice
|
// small enough to avoid a "dead zone" with high-resolution mice
|
||||||
// but large enough to avoid rounding to zero (due to loss of
|
// but large enough to avoid rounding to zero in wl_fixed_from_double().
|
||||||
// significant digits) in simple floating-point calculations.
|
|
||||||
|
|
||||||
// find the closest x point
|
// find the closest x point
|
||||||
if (x < box->x) {
|
if (x < box->x) {
|
||||||
*dest_x = box->x;
|
*dest_x = box->x;
|
||||||
} else if (x > box->x + box->width - 1/65536.0) {
|
} else if (x > box->x + box->width - 1/256.0) {
|
||||||
*dest_x = box->x + box->width - 1/65536.0;
|
*dest_x = box->x + box->width - 1/256.0;
|
||||||
} else {
|
} else {
|
||||||
*dest_x = x;
|
*dest_x = x;
|
||||||
}
|
}
|
||||||
|
|
@ -36,8 +35,8 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y,
|
||||||
// find closest y point
|
// find closest y point
|
||||||
if (y < box->y) {
|
if (y < box->y) {
|
||||||
*dest_y = box->y;
|
*dest_y = box->y;
|
||||||
} else if (y > box->y + box->height - 1/65536.0) {
|
} else if (y > box->y + box->height - 1/256.0) {
|
||||||
*dest_y = box->y + box->height - 1/65536.0;
|
*dest_y = box->y + box->height - 1/256.0;
|
||||||
} else {
|
} else {
|
||||||
*dest_y = y;
|
*dest_y = y;
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +66,12 @@ bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a,
|
||||||
dest->width = x2 - x1;
|
dest->width = x2 - x1;
|
||||||
dest->height = y2 - y1;
|
dest->height = y2 - y1;
|
||||||
|
|
||||||
return !wlr_box_empty(dest);
|
if (wlr_box_empty(dest)) {
|
||||||
|
*dest = (struct wlr_box){0};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wlr_box_contains_point(const struct wlr_box *box, double x, double y) {
|
bool wlr_box_contains_point(const struct wlr_box *box, double x, double y) {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ static void handle_server_start(struct wl_listener *listener, void *data) {
|
||||||
static void xwayland_mark_ready(struct wlr_xwayland *xwayland) {
|
static void xwayland_mark_ready(struct wlr_xwayland *xwayland) {
|
||||||
assert(xwayland->server->wm_fd[0] >= 0);
|
assert(xwayland->server->wm_fd[0] >= 0);
|
||||||
xwayland->xwm = xwm_create(xwayland, xwayland->server->wm_fd[0]);
|
xwayland->xwm = xwm_create(xwayland, xwayland->server->wm_fd[0]);
|
||||||
|
// xwm_create takes ownership of wm_fd[0] under all circumstances
|
||||||
|
xwayland->server->wm_fd[0] = -1;
|
||||||
|
|
||||||
if (!xwayland->xwm) {
|
if (!xwayland->xwm) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -69,6 +72,11 @@ static void handle_shell_destroy(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_xwayland *xwayland =
|
struct wlr_xwayland *xwayland =
|
||||||
wl_container_of(listener, xwayland, shell_destroy);
|
wl_container_of(listener, xwayland, shell_destroy);
|
||||||
xwayland->shell_v1 = NULL;
|
xwayland->shell_v1 = NULL;
|
||||||
|
wl_list_remove(&xwayland->shell_destroy.link);
|
||||||
|
// Will remove this list in handle_shell_destroy().
|
||||||
|
// This ensures the link is always initialized and
|
||||||
|
// avoids the need to keep check conditions in sync.
|
||||||
|
wl_list_init(&xwayland->shell_destroy.link);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) {
|
void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) {
|
||||||
|
|
|
||||||
|
|
@ -2470,6 +2470,7 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride,
|
||||||
struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
|
struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
|
||||||
struct wlr_xwm *xwm = calloc(1, sizeof(*xwm));
|
struct wlr_xwm *xwm = calloc(1, sizeof(*xwm));
|
||||||
if (xwm == NULL) {
|
if (xwm == NULL) {
|
||||||
|
close(wm_fd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2484,11 +2485,13 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
|
||||||
|
|
||||||
xwm->ping_timeout = 10000;
|
xwm->ping_timeout = 10000;
|
||||||
|
|
||||||
|
// xcb_connect_to_fd takes ownership of the FD regardless of success/failure
|
||||||
xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL);
|
xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL);
|
||||||
|
|
||||||
int rc = xcb_connection_has_error(xwm->xcb_conn);
|
int rc = xcb_connection_has_error(xwm->xcb_conn);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
wlr_log(WLR_ERROR, "xcb connect failed: %d", rc);
|
wlr_log(WLR_ERROR, "xcb connect failed: %d", rc);
|
||||||
|
xcb_disconnect(xwm->xcb_conn);
|
||||||
free(xwm);
|
free(xwm);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue