diff --git a/sway/config/output.c b/sway/config/output.c index 8e2528c8..5fb282d4 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -285,7 +285,6 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending mhz = mhz <= 0 ? INT_MAX : mhz; if (wl_list_empty(&output->modes) || custom) { - sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); wlr_output_state_set_custom_mode(pending, width, height, refresh_rate > 0 ? mhz : 0); return; @@ -305,10 +304,7 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending } } } - if (best) { - sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s", - best->width, best->height, best->refresh / 1000.f, output->name); - } else { + if (!best) { best = wlr_output_preferred_mode(output); sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " "applying preferred mode (%dx%d@%.3fHz)", @@ -325,7 +321,6 @@ static void set_modeline(struct wlr_output *output, sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); return; } - sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); if (mode) { wlr_output_state_set_mode(pending, mode); @@ -391,7 +386,6 @@ static int compute_default_scale(struct wlr_output *output, double dpi_x = (double) width / (output->phys_width / MM_PER_INCH); double dpi_y = (double) height / (output->phys_height / MM_PER_INCH); - sway_log(SWAY_DEBUG, "Output DPI: %fx%f", dpi_x, dpi_y); if (dpi_x <= HIDPI_DPI_LIMIT || dpi_y <= HIDPI_DPI_LIMIT) { return 1; } @@ -427,78 +421,57 @@ static void queue_output_config(struct output_config *oc, struct wlr_output *wlr_output = output->wlr_output; if (output_config_is_disabling(oc)) { - sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); wlr_output_state_set_enabled(pending, false); return; } - - sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); wlr_output_state_set_enabled(pending, true); if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { - sway_log(SWAY_DEBUG, "Set %s modeline", - wlr_output->name); set_modeline(wlr_output, pending, &oc->drm_mode); } else if (oc && oc->width > 0 && oc->height > 0) { - sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", - wlr_output->name, oc->width, oc->height, oc->refresh_rate); set_mode(wlr_output, pending, oc->width, oc->height, oc->refresh_rate, oc->custom_mode == 1); } else if (!wl_list_empty(&wlr_output->modes)) { - sway_log(SWAY_DEBUG, "Set preferred mode"); struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); wlr_output_state_set_mode(pending, preferred_mode); } - if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { - sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, - sway_wl_output_subpixel_to_string(oc->subpixel)); + if (oc && oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { wlr_output_state_set_subpixel(pending, oc->subpixel); + } else { + wlr_output_state_set_subpixel(pending, output->detected_subpixel); } - enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; if (oc && oc->transform >= 0) { - tr = oc->transform; + wlr_output_state_set_transform(pending, oc->transform); #if WLR_HAS_DRM_BACKEND } else if (wlr_output_is_drm(wlr_output)) { - tr = wlr_drm_connector_get_panel_orientation(wlr_output); - sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); + wlr_output_state_set_transform(pending, + wlr_drm_connector_get_panel_orientation(wlr_output)); #endif - } - if (wlr_output->transform != tr) { - sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr); - wlr_output_state_set_transform(pending, tr); + } else { + wlr_output_state_set_transform(pending, WL_OUTPUT_TRANSFORM_NORMAL); } - // Apply the scale last before the commit, because the scale auto-detection - // reads the pending output size - float scale; + // Apply the scale after sorting out the mode, because the scale + // auto-detection reads the pending output size if (oc && oc->scale > 0) { - scale = oc->scale; - // The factional-scale-v1 protocol uses increments of 120ths to send // the scale factor to the client. Adjust the scale so that we use the // same value as the clients'. - float adjusted_scale = round(scale * 120) / 120; - if (scale != adjusted_scale) { - sway_log(SWAY_INFO, "Adjusting output scale from %f to %f", - scale, adjusted_scale); - scale = adjusted_scale; - } + wlr_output_state_set_scale(pending, round(oc->scale * 120) / 120); } else { - scale = compute_default_scale(wlr_output, pending); - sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); - } - if (scale != wlr_output->scale) { - sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); - wlr_output_state_set_scale(pending, scale); + wlr_output_state_set_scale(pending, + compute_default_scale(wlr_output, pending)); } - if (oc && oc->adaptive_sync != -1 && wlr_output->adaptive_sync_supported) { - sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, - oc->adaptive_sync); - wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); + if (wlr_output->adaptive_sync_supported) { + if (oc && oc->adaptive_sync != -1) { + wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); + } else { + wlr_output_state_set_adaptive_sync_enabled(pending, false); + } } if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { @@ -513,6 +486,8 @@ static void queue_output_config(struct output_config *oc, } else { wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } + } else { + wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } } @@ -531,24 +506,23 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } - if (oc) { - enum scale_filter_mode scale_filter_old = output->scale_filter; - switch (oc->scale_filter) { - case SCALE_FILTER_DEFAULT: - case SCALE_FILTER_SMART: - output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ? - SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR; - break; - case SCALE_FILTER_LINEAR: - case SCALE_FILTER_NEAREST: - output->scale_filter = oc->scale_filter; - break; - } - if (scale_filter_old != output->scale_filter) { - sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, - sway_output_scale_filter_to_string(output->scale_filter)); - wlr_damage_ring_add_whole(&output->scene_output->damage_ring); - } + enum scale_filter_mode scale_filter_old = output->scale_filter; + enum scale_filter_mode scale_filter_new = oc ? oc->scale_filter : SCALE_FILTER_DEFAULT; + switch (scale_filter_new) { + case SCALE_FILTER_DEFAULT: + case SCALE_FILTER_SMART: + output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ? + SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR; + break; + case SCALE_FILTER_LINEAR: + case SCALE_FILTER_NEAREST: + output->scale_filter = scale_filter_new; + break; + } + if (scale_filter_old != output->scale_filter) { + sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, + sway_output_scale_filter_to_string(output->scale_filter)); + wlr_damage_ring_add_whole(&output->scene_output->damage_ring); } // Find position for it @@ -571,25 +545,19 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output output_enable(output); } - if (oc && oc->max_render_time >= 0) { - sway_log(SWAY_DEBUG, "Set %s max render time to %d", - oc->name, oc->max_render_time); - output->max_render_time = oc->max_render_time; - } - if (oc && oc->set_color_transform) { if (oc->color_transform) { wlr_color_transform_ref(oc->color_transform); } wlr_color_transform_unref(output->color_transform); output->color_transform = oc->color_transform; + } else { + wlr_color_transform_unref(output->color_transform); + output->color_transform = NULL; } - if (oc && oc->allow_tearing >= 0) { - sway_log(SWAY_DEBUG, "Set %s allow tearing to %d", - oc->name, oc->allow_tearing); - output->allow_tearing = oc->allow_tearing; - } + output->max_render_time = oc && oc->max_render_time > 0 ? oc->max_render_time : 0; + output->allow_tearing = oc && oc->allow_tearing > 0; return true; } @@ -605,15 +573,6 @@ static struct output_config *find_output_config_from_list( return NULL; } - // Set output defaults for the "base" configuration - result->enabled = 1; - result->power = 1; - result->scale = 0; // auto - result->subpixel = sway_output->detected_subpixel; - result->transform = WL_OUTPUT_TRANSFORM_NORMAL; - result->max_render_time = 0; - result->allow_tearing = 0; - char id[128]; output_get_identifier(id, sizeof(id), sway_output); @@ -692,6 +651,13 @@ static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_s sway_log(SWAY_DEBUG, " adaptive_sync: %s", state->adaptive_sync_enabled ? "enabled": "disabled"); } + if (state->committed & WLR_OUTPUT_STATE_SCALE) { + sway_log(SWAY_DEBUG, " scale: %f", state->scale); + } + if (state->committed & WLR_OUTPUT_STATE_SUBPIXEL) { + sway_log(SWAY_DEBUG, " subpixel: %s", + sway_wl_output_subpixel_to_string(state->subpixel)); + } } static bool search_valid_config(struct search_context *ctx, size_t output_idx); @@ -906,9 +872,8 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, backend_state->output = cfg->output->wlr_output; wlr_output_state_init(&backend_state->base); - sway_log(SWAY_DEBUG, "Preparing config for %s", - cfg->output->wlr_output->name); queue_output_config(cfg->config, cfg->output, &backend_state->base); + dump_output_state(cfg->output->wlr_output, &backend_state->base); } struct wlr_output_swapchain_manager swapchain_mgr; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index b136a24e..333c09b4 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -54,7 +54,7 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( } static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, - struct wlr_box *usable_area, struct wlr_scene_tree *tree) { + struct wlr_box *usable_area, struct wlr_scene_tree *tree, bool exclusive) { struct wlr_scene_node *node; wl_list_for_each(node, &tree->children, link) { struct sway_layer_surface *surface = scene_descriptor_try_get(node, @@ -68,6 +68,10 @@ static void arrange_surface(struct sway_output *output, const struct wlr_box *fu continue; } + if ((surface->scene->layer_surface->current.exclusive_zone > 0) != exclusive) { + continue; + } + wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); } } @@ -78,10 +82,15 @@ void arrange_layers(struct sway_output *output) { &usable_area.width, &usable_area.height); const struct wlr_box full_area = usable_area; - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true); + + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false); if (!wlr_box_equal(&usable_area, &output->usable_area)) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a4eaa4b3..18a04c09 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -188,8 +188,8 @@ static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, struct wlr_scene_buffer *buffer) { // if we are scaling down, we should always choose linear if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( - buffer->dst_width < buffer->buffer_width || - buffer->dst_height < buffer->buffer_height)) { + buffer->dst_width < buffer->WLR_PRIVATE.buffer_width || + buffer->dst_height < buffer->WLR_PRIVATE.buffer_height)) { return WLR_SCALE_FILTER_BILINEAR; } diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 1a73df01..651c44c5 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -267,6 +268,7 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, const xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { for (size_t i = 0; i < keysyms_len; ++i) { xkb_keysym_t keysym = pressed_keysyms[i]; + if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) { #if WLR_HAS_SESSION @@ -282,6 +284,36 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, return false; } +static bool keyboard_execute_pointer_keysyms(struct sway_keyboard *keyboard, + uint32_t time, const xkb_keysym_t *pressed_keysyms, size_t keysyms_len, + enum wl_keyboard_key_state state) { + struct sway_cursor *cursor = keyboard->seat_device->sway_seat->cursor; + + for (size_t i = 0; i < keysyms_len; ++i) { + xkb_keysym_t keysym = pressed_keysyms[i]; + + uint32_t button = wlr_keyboard_keysym_to_pointer_button(keysym); + if (button != 0) { + dispatch_cursor_button(cursor, &keyboard->wlr->base, time, button, + (enum wl_pointer_button_state)state); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + return true; + } + + int dx, dy; + wlr_keyboard_keysym_to_pointer_motion(keysym, &dx, &dy); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED && (dx != 0 || dy != 0)) { + dx *= 10; + dy *= 10; + pointer_motion(cursor, time, &keyboard->wlr->base, dx, dy, dx, dy); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + return true; + } + } + + return false; +} + /** * Get keysyms and modifiers from the keyboard as xkb sees them. * @@ -507,6 +539,11 @@ static void handle_key_event(struct sway_keyboard *keyboard, keyboard, keyinfo.raw_keysyms, keyinfo.raw_modifiers, keyinfo.raw_keysyms_len); } + if (!handled) { + handled = keyboard_execute_pointer_keysyms(keyboard, event->time_msec, + keyinfo.translated_keysyms, keyinfo.translated_keysyms_len, + event->state); + } if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { // If the pressed event was sent to a client and we have a focused diff --git a/sway/input/seat.c b/sway/input/seat.c index 9a00a3e2..0dd26290 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1094,6 +1094,7 @@ static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { static int handle_urgent_timeout(void *data) { struct sway_view *view = data; view_set_urgent(view, false); + container_update_itself_and_parents(view->container); return 0; } diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 42ce333b..6a7dddd9 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -355,6 +355,13 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + bool mod_pressed = modifiers & config->floating_mod; + uint32_t mod_move_btn = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; + uint32_t mod_resize_btn = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; + bool mod_move_btn_pressed = mod_pressed && button == mod_move_btn; + bool mod_resize_btn_pressed = mod_pressed && button == mod_resize_btn; + bool titlebar_left_btn_pressed = on_titlebar && button == BTN_LEFT; + // Handle mouse bindings if (trigger_pointer_button_binding(seat, device, button, state, modifiers, on_titlebar, on_border, on_contents, on_workspace)) { @@ -403,33 +410,28 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Handle tiling resize via mod - bool mod_pressed = modifiers & config->floating_mod; - if (cont && !is_floating_or_child && mod_pressed && + if (cont && !is_floating_or_child && mod_pressed && mod_resize_btn_pressed && state == WL_POINTER_BUTTON_STATE_PRESSED) { - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (button == btn_resize) { - edge = 0; - edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - - const char *image = NULL; - if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { - image = "nw-resize"; - } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { - image = "ne-resize"; - } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { - image = "se-resize"; - } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { - image = "sw-resize"; - } - cursor_set_image(seat->cursor, image, NULL); - seat_set_focus_container(seat, cont); - seatop_begin_resize_tiling(seat, cont, edge); - return; + edge = 0; + edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + + const char *image = NULL; + if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { + image = "nw-resize"; + } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { + image = "ne-resize"; + } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { + image = "se-resize"; + } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { + image = "sw-resize"; } + cursor_set_image(seat->cursor, image, NULL); + seat_set_focus_container(seat, cont); + seatop_begin_resize_tiling(seat, cont, edge); + return; } // Handle changing focus when clicking on a container @@ -454,12 +456,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle beginning floating move if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WL_POINTER_BUTTON_STATE_PRESSED) { - uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; - if (button == btn_move && (mod_pressed || on_titlebar)) { - seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); - return; - } + state == WL_POINTER_BUTTON_STATE_PRESSED && + (mod_move_btn_pressed || titlebar_left_btn_pressed)) { + seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); + return; } // Handle beginning floating resize @@ -473,9 +473,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Via mod+click - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (mod_pressed && button == btn_resize) { + if (mod_resize_btn_pressed) { struct sway_container *floater = container_toplevel_ancestor(cont); edge = 0; edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ? @@ -489,7 +487,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Handle moving a tiling container - if (config->tiling_drag && (mod_pressed || on_titlebar) && + if (config->tiling_drag && (mod_move_btn_pressed || titlebar_left_btn_pressed) && state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { // If moving a container by its title bar, use a threshold for the drag @@ -498,7 +496,6 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } else { seatop_begin_move_tiling(seat, cont); } - return; } diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 6bcd0234..e1672467 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -454,6 +454,7 @@ static void handle_im_popup_surface_unmap(struct wl_listener *listener, void *da struct sway_input_popup *popup = wl_container_of(listener, popup, popup_surface_unmap); + scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP); // relative should already be freed as it should be a child of the just unmapped scene popup->desc.relative = NULL; diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c index a30d4664..92bdda00 100644 --- a/sway/scene_descriptor.c +++ b/sway/scene_descriptor.c @@ -39,6 +39,9 @@ void *scene_descriptor_try_get(struct wlr_scene_node *node, void scene_descriptor_destroy(struct wlr_scene_node *node, enum sway_scene_descriptor_type type) { struct scene_descriptor *desc = scene_node_get_descriptor(node, type); + if (!desc) { + return; + } descriptor_destroy(desc); } diff --git a/sway/server.c b/sway/server.c index d832c66d..74174fc9 100644 --- a/sway/server.c +++ b/sway/server.c @@ -208,8 +208,8 @@ static void handle_renderer_lost(struct wl_listener *listener, void *data) { wlr_compositor_set_renderer(server->compositor, renderer); - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { wlr_output_init_render(output->wlr_output, server->allocator, server->renderer); } @@ -254,7 +254,8 @@ bool server_init(struct sway_server *server) { } } if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && - server->renderer->features.timeline) { + server->renderer->features.timeline && + server->backend->features.timeline) { wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, wlr_renderer_get_drm_fd(server->renderer)); }