multi monitor

This commit is contained in:
lkn 2026-06-06 20:05:57 +02:00
parent ad0871054c
commit 2c1dca387f
14 changed files with 412 additions and 148 deletions

View File

@ -201,6 +201,7 @@ wmaker_SOURCES += \
backend/wayland/wl_xdg_activation.c \
backend/wayland/wl_output_mgmt.c \
backend/wayland/wl_output.c \
backend/wayland/wl_layout_sync.c \
backend/wayland/wl_xdg.c \
backend/wayland/wl_dock.c \
backend/wayland/wl_layer_shell.c \

View File

@ -411,6 +411,12 @@ wl_display_open(const char *name)
* Must be created before backend start so handle_backend_new_output
* can add the output to the layout when it fires. */
wl_state.output_layout = wlr_output_layout_create(wl_state.display);
if (wl_state.output_layout) {
extern void wl_handle_output_layout_change(struct wl_listener *listener, void *data);
wl_state.output_layout_change_listener.notify = wl_handle_output_layout_change;
wl_signal_add(&wl_state.output_layout->events.change,
&wl_state.output_layout_change_listener);
}
/* create hardware cursor tracker and load xcursor theme.
* wlr_cursor tracks the logical cursor position across the output
@ -441,6 +447,9 @@ wl_display_open(const char *name)
* when the output fires (for nested Wayland, new_output fires during
* wlr_backend_start via the host compositor roundtrip). */
wl_state.scene = wlr_scene_create();
if (wl_state.scene && wl_state.output_layout)
wl_state.scene_output_layout =
wlr_scene_attach_output_layout(wl_state.scene, wl_state.output_layout);
/* Register commit callback for WINGs views (defined later in file) */
{
extern void (*W_ViewCommitBackingFn)(unsigned long id);
@ -495,11 +504,49 @@ wl_display_open(const char *name)
wl_signal_add(&wl_state.backend->events.new_output,
&wl_state.backend_new_output_listener);
/* For nested mode, queue an output window before start; wlr_wl_output_create
* returns NULL before the backend is initialised — the output is created
* during wlr_backend_start and delivered via new_output. */
if (wl_state.is_nested && wl_state.wl_backend_inner)
wlr_wl_output_create(wl_state.wl_backend_inner);
wl_list_init(&wl_state.toplevel_list);
wl_list_init(&wl_state.or_list);
wl_list_init(&wl_state.frame_list);
wl_list_init(&wl_state.layer_list);
wl_list_init(&wl_state.outputs);
wl_list_init(&wl_state.real_pointer_listeners);
wl_state.output_count = 0;
{
int ki;
for (ki = 0; ki < 3; ki++)
wl_list_init(&wl_state.ctx_lists[ki]);
}
wl_state.next_synthetic_id = 0x10000000UL;
wl_state.managed_surface_count = 0;
/* For nested mode, queue output windows before start; nested wlroots
* backends don't automatically honor WLR_WL_OUTPUTS for us here, so
* create one wl_output per requested slot. If the environment variable
* is a plain integer use that count; otherwise treat any non-empty value
* as a comma-separated list of output specs and create one output per
* comma-delimited entry. */
if (wl_state.is_nested && wl_state.wl_backend_inner) {
const char *outs_env = getenv("WLR_WL_OUTPUTS");
int requested = 1;
if (outs_env && *outs_env) {
char *end = NULL;
long n = strtol(outs_env, &end, 10);
if (end && *end == '\0' && n > 0) {
requested = (int)n;
} else {
int count = 1;
for (const char *p = outs_env; *p; ++p) {
if (*p == ',')
count++;
}
requested = count;
}
}
for (int i = 0; i < requested; i++)
wlr_wl_output_create(wl_state.wl_backend_inner);
}
if (!wlr_backend_start(wl_state.backend)) {
wwarning("wl_backend: failed to start backend");
@ -572,20 +619,6 @@ wl_display_open(const char *name)
wlr_seat_set_capabilities(wl_state.seat,
WL_SEAT_CAPABILITY_KEYBOARD | WL_SEAT_CAPABILITY_POINTER);
wl_list_init(&wl_state.toplevel_list);
wl_list_init(&wl_state.or_list);
wl_list_init(&wl_state.frame_list);
wl_list_init(&wl_state.layer_list);
wl_list_init(&wl_state.outputs);
wl_state.output_count = 0;
{
int ki;
for (ki = 0; ki < 3; ki++)
wl_list_init(&wl_state.ctx_lists[ki]);
}
wl_state.next_synthetic_id = 0x10000000UL;
wl_state.managed_surface_count = 0;
wl_state.xdg_new_surface_listener.notify = handle_xdg_new_toplevel;
wl_signal_add(&wl_state.xdg_shell->events.new_toplevel,
&wl_state.xdg_new_surface_listener);
@ -857,15 +890,11 @@ wl_display_close(void)
WL_LISTENER_REMOVE(wl_state.backend_new_input_listener);
WL_LISTENER_REMOVE(wl_state.real_keyboard_key_listener);
WL_LISTENER_REMOVE(wl_state.real_keyboard_modifiers_listener);
WL_LISTENER_REMOVE(wl_state.real_pointer_motion_abs_listener);
WL_LISTENER_REMOVE(wl_state.real_pointer_button_listener);
WL_LISTENER_REMOVE(wl_state.real_pointer_frame_listener);
WL_LISTENER_REMOVE(wl_state.seat_request_cursor_listener);
WL_LISTENER_REMOVE(wl_state.seat_request_set_selection_listener);
WL_LISTENER_REMOVE(wl_state.seat_request_primary_selection_listener);
WL_LISTENER_REMOVE(wl_state.idle_new_inhibitor_listener);
WL_LISTENER_REMOVE(wl_state.pointer_axis_listener);
WL_LISTENER_REMOVE(wl_state.real_pointer_axis_listener);
WL_LISTENER_REMOVE(wl_state.new_constraint_listener);
WL_LISTENER_REMOVE(wl_state.new_shortcuts_inhibitor_listener);
WL_LISTENER_REMOVE(wl_state.output_mgr_apply_listener);
@ -1133,13 +1162,6 @@ wl_event_loop_run(void)
wwarning("wl_backend: event loop exited");
/* Remove pointer listeners before wlroots destroys the pointer device */
if (wl_state.pointer_dev) {
wl_list_remove(&wl_state.real_pointer_motion_abs_listener.link);
wl_list_remove(&wl_state.real_pointer_button_listener.link);
wl_list_remove(&wl_state.real_pointer_frame_listener.link);
wl_list_remove(&wl_state.real_pointer_axis_listener.link);
}
}
void

View File

@ -11,6 +11,7 @@
#include <limits.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/interfaces/wlr_buffer.h>
@ -82,8 +83,8 @@ static int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
}
int width = wl_state.output->width;
int height = wl_state.output->height;
int width = scr->scr_width > 0 ? scr->scr_width : wl_state.output->width;
int height = scr->scr_height > 0 ? scr->scr_height : wl_state.output->height;
/* Parse the texture proplist */
WMPropList *texpl = WMCreatePropListFromDescription(texture_desc);
@ -104,16 +105,34 @@ static int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
RColor color = {0x50, 0x50, 0x75, 0xFF};
if (colorstr)
parse_color_to_rcolor(scr, colorstr, &color);
/* For solid colors, just update the scene rect directly */
if (wl_state.bg_rect) {
/* For solid colors, update every output-local background rect. */
{
float c[4] = { color.red/255.0f, color.green/255.0f, color.blue/255.0f, 1.0f };
wlr_scene_rect_set_color(wl_state.bg_rect, c);
wlr_scene_node_set_enabled(&wl_state.bg_rect->node, true);
struct wl_wlr_output_state *ost;
wl_list_for_each(ost, &wl_state.outputs, link) {
if (!ost->bg_rect || !wl_state.output_layout)
continue;
struct wlr_box box = {0};
wlr_output_layout_get_box(wl_state.output_layout, ost->output, &box);
if (box.width <= 0 || box.height <= 0) {
box.width = (int)ost->output->width;
box.height = (int)ost->output->height;
}
wlr_scene_rect_set_size(ost->bg_rect, box.width, box.height);
wlr_scene_node_set_position(&ost->bg_rect->node, box.x, box.y);
wlr_scene_rect_set_color(ost->bg_rect, c);
wlr_scene_node_set_enabled(&ost->bg_rect->node, true);
}
if (wl_state.bg_image)
wlr_scene_node_set_enabled(&wl_state.bg_image->node, false);
}
if (wl_state.output)
wlr_output_schedule_frame(wl_state.output);
{
struct wl_wlr_output_state *ost;
wl_list_for_each(ost, &wl_state.outputs, link) {
if (ost->output)
wlr_output_schedule_frame(ost->output);
}
}
WMReleasePropList(texpl);
return;
} else if ((strcasecmp(type, "vgradient") == 0 ||
@ -302,9 +321,14 @@ static int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
wlr_buffer_init(&buf->base, &bg_buffer_impl, width, height);
/* Hide the solid-color rect */
if (wl_state.bg_rect)
wlr_scene_node_set_enabled(&wl_state.bg_rect->node, false);
/* Hide per-output solid-color rects while the image background is active. */
{
struct wl_wlr_output_state *ost;
wl_list_for_each(ost, &wl_state.outputs, link) {
if (ost->bg_rect)
wlr_scene_node_set_enabled(&ost->bg_rect->node, false);
}
}
/* Replace or create the background scene buffer */
if (wl_state.bg_image) {
@ -320,7 +344,12 @@ static int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
wlr_buffer_drop(wl_state.bg_wlr_buf);
wl_state.bg_wlr_buf = &buf->base;
/* Schedule a frame to display the new background immediately */
if (wl_state.output)
wlr_output_schedule_frame(wl_state.output);
/* Schedule all outputs so the new background is shown everywhere. */
{
struct wl_wlr_output_state *ost;
wl_list_for_each(ost, &wl_state.outputs, link) {
if (ost->output)
wlr_output_schedule_frame(ost->output);
}
}
}

View File

@ -105,8 +105,8 @@ wm_dock_create(WScreen *scr)
if (!wl_state.scene || !wl_state.output)
return;
int sw = (int)wl_state.output->width;
int sh = (int)wl_state.output->height;
int sw = scr->scr_width > 0 ? scr->scr_width : (int)wl_state.output->width;
int sh = scr->scr_height > 0 ? scr->scr_height : (int)wl_state.output->height;
int icon_sz = (wPreferences.icon_size > 0)
? wPreferences.icon_size : 64;
int dw = icon_sz + 4; /* dock width = icon + 2px pad each side */

View File

@ -362,6 +362,10 @@ wl_protocols_init(void)
wl_state.output_mgr_test_listener.notify = handle_output_mgr_test;
wl_signal_add(&wl_state.output_mgr->events.test,
&wl_state.output_mgr_test_listener);
{
extern void wl_output_manager_broadcast_initial_config(void);
wl_output_manager_broadcast_initial_config();
}
wwarning("wl_backend: output-management created (zwlr_output_management_v1)");
}
wl_state.output_power_mgr = wlr_output_power_manager_v1_create(wl_state.display);

View File

@ -222,6 +222,21 @@ frame_buf_at(int px, int py)
* Returns the button fb whose absolute rect encloses (px,py) with the
* smallest area (most specific), or NULL if no button is under the pointer.
*/
void
wl_framebuf_refresh_all_positions(void)
{
struct wl_frame_buf *fb;
wl_list_for_each(fb, &wl_state.frame_list, link) {
if (fb->parent_id)
continue;
if (fb->tree)
wlr_scene_node_set_position(&fb->tree->node, fb->x, fb->y);
else if (fb->scene_buf)
wlr_scene_node_set_position(&fb->scene_buf->node, fb->x, fb->y);
frame_buf_update_children_abs(fb->id);
}
}
struct wl_frame_buf *
frame_buf_button_at(int px, int py)
{
@ -261,11 +276,32 @@ frame_buf_button_at(int px, int py)
* back to the scene-graph-based frame_buf_at for titlebars, resizebars, and
* top-level frames.
*/
static void
wl_dump_hit_debug(int px, int py, struct wl_frame_buf *fb)
{
if (!fb)
return;
wwarning("hit: ptr=(%d,%d) fb=0x%lx abs=(%d,%d) rel=(%d,%d) size=(%d,%d) parent=0x%lx",
px, py, (unsigned long)fb->id, fb->x, fb->y, fb->rel_x, fb->rel_y,
fb->width, fb->height, (unsigned long)fb->parent_id);
if (fb->parent_id) {
struct wl_frame_buf *parent = frame_buf_find(fb->parent_id);
if (parent) {
wwarning("hit: parent fb=0x%lx abs=(%d,%d) size=(%d,%d)",
(unsigned long)parent->id, parent->x, parent->y,
parent->width, parent->height);
}
}
}
struct wl_frame_buf *
frame_buf_hit(int px, int py)
{
struct wl_frame_buf *fb = frame_buf_button_at(px, py);
return fb ? fb : frame_buf_at(px, py);
struct wl_frame_buf *fb = frame_buf_at(px, py);
if (!fb)
fb = frame_buf_button_at(px, py);
wl_dump_hit_debug(px, py, fb);
return fb;
}
/* ------------------------------------------------------------------ */

View File

@ -135,6 +135,11 @@ handle_pointer_button(struct wl_listener *listener, void *data)
{
(void)listener;
struct wlr_pointer_button_event *ev = data;
wwarning("ptr-button: dev=%p output=%s button=%u state=%u ptr=(%d,%d) grab=%d",
(void*)&ev->pointer->base,
ev->pointer->output_name ? ev->pointer->output_name : "(none)",
ev->button, (unsigned)ev->state,
wl_state.pointer_x, wl_state.pointer_y, wl_state.grab_type);
if (!wl_state.seat)
return;
/* any button release ends an active compositor grab (move/resize).
@ -205,30 +210,15 @@ handle_pointer_button(struct wl_listener *listener, void *data)
wev.type = (ev->state == WLR_BUTTON_PRESSED)
? WME_BUTTON_PRESS : WME_BUTTON_RELEASE;
/* Hit-test: determine which WM window was clicked.
* Use frame_buf_hit so button sub-windows (invisible to the scene
* graph because their pixels are composited into the titlebar buffer)
* are resolved to their own window IDs, letting handleButtonPress
* route the event to buttonMouseDown instead of titlebarMouseDown. */
struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x,
wl_state.pointer_y);
if (fb) {
wev.window = fb->id;
wev.u.button.window = fb->id;
wev.u.button.x = wl_state.pointer_x - fb->x;
wev.u.button.y = wl_state.pointer_y - fb->y;
} else {
/* No frame buffer — check if the click landed on an XDG toplevel
* and look up its WINGs view ID from the frame list. */
double _sx, _sy;
/* Hit-test: prefer scene-local coordinates when a client/view surface
* is under the pointer. Fall back to frame_buf-only hit testing for
* titlebars, buttons, resizebars, menus, and other WM-internal nodes. */
{
double _sx = 0.0, _sy = 0.0;
struct wl_toplevel_view *_tv = NULL;
view_surface_at(wl_state.pointer_x, wl_state.pointer_y,
&_sx, &_sy, &_tv);
if (_tv) {
/* Try to find a WINGs view frame buffer at this position.
* The compositor's view ID (_tv->id) is synthetic and not
* known to WINGs, so we match by bounds against frame_buf
* entries with WINGs view IDs (>= 0x20000000). */
struct wlr_surface *_surf = view_surface_at(wl_state.pointer_x, wl_state.pointer_y,
&_sx, &_sy, &_tv);
if (_surf && _tv) {
struct wl_frame_buf *_wtb = NULL;
wl_list_for_each(_wtb, &wl_state.frame_list, link) {
if (_wtb->id >= 0x20000000 &&
@ -236,26 +226,39 @@ handle_pointer_button(struct wl_listener *listener, void *data)
wl_state.pointer_x < _wtb->x + _wtb->width &&
wl_state.pointer_y >= _wtb->y &&
wl_state.pointer_y < _wtb->y + _wtb->height) {
wwarning("btn: wings fb id=0x%lx xy=(%d,%d) wh=(%d,%d) click=(%.0f,%.0f)",
(unsigned long)_wtb->id, _wtb->x, _wtb->y,
_wtb->width, _wtb->height,
wl_state.pointer_x, wl_state.pointer_y);
break;
}
}
if (!_wtb)
wwarning("btn: no wings fb found, using tv id=0x%lx", (unsigned long)_tv->id);
wev.window = _wtb ? _wtb->id : _tv->id;
wev.u.button.window = wev.window;
wev.u.button.x = _wtb ? (wl_state.pointer_x - _wtb->x) : (int)_sx;
wev.u.button.y = _wtb ? (wl_state.pointer_y - _wtb->y) : (int)_sy;
wev.u.button.x = (int)_sx;
wev.u.button.y = (int)_sy;
wwarning("btn-route: ptr=(%d,%d) tv=0x%lx surf=%p sxsy=(%.1f,%.1f) wings_fb=%s final=0x%lx local=(%d,%d)",
wl_state.pointer_x, wl_state.pointer_y,
(unsigned long)_tv->id, (void*)_surf, _sx, _sy,
_wtb ? "yes" : "no", (unsigned long)wev.window,
wev.u.button.x, wev.u.button.y);
} else {
WScreen *scr0 = wScreenWithNumber(0);
WNativeWindow root = scr0 ? scr0->root_win : (WNativeWindow)0x0FFFFFFF;
wev.window = root;
wev.u.button.window = root;
wev.u.button.x = wl_state.pointer_x;
wev.u.button.y = wl_state.pointer_y;
struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x,
wl_state.pointer_y);
if (fb) {
wev.window = fb->id;
wev.u.button.window = fb->id;
wev.u.button.x = wl_state.pointer_x - fb->x;
wev.u.button.y = wl_state.pointer_y - fb->y;
wwarning("btn-route: ptr=(%d,%d) fb=0x%lx abs=(%d,%d) final=0x%lx local=(%d,%d)",
wl_state.pointer_x, wl_state.pointer_y,
(unsigned long)fb->id, fb->x, fb->y,
(unsigned long)wev.window,
wev.u.button.x, wev.u.button.y);
} else {
WScreen *scr0 = wScreenWithNumber(0);
WNativeWindow root = scr0 ? scr0->root_win : (WNativeWindow)0x0FFFFFFF;
wev.window = root;
wev.u.button.window = root;
wev.u.button.x = wl_state.pointer_x;
wev.u.button.y = wl_state.pointer_y;
}
}
}
wev.u.button.time = ev->time_msec;
@ -276,6 +279,8 @@ handle_pointer_button(struct wl_listener *listener, void *data)
* the fb is a WM fake_leader; look up the WINGs panel via leader. */
struct W_View *target = NULL;
WNativeWindow wings_id = 0;
struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x,
wl_state.pointer_y);
if (fb && fb->id >= 0x20000000) {
wings_id = fb->id;
} else if (fb) {

View File

@ -0,0 +1,30 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <wayland-server-core.h>
#include "../../screen.h"
#include "wl_types.h"
extern void wl_screen_refresh_heads(WScreen *scr);
extern void wl_framebuf_refresh_all_positions(void);
void
wl_handle_output_layout_change(struct wl_listener *listener, void *data)
{
(void)listener;
(void)data;
if (wl_state.scr)
wl_screen_refresh_heads(wl_state.scr);
wwarning("layout-change: pointer=(%d,%d)", wl_state.pointer_x, wl_state.pointer_y);
if (wl_state.scr && wl_state.scr->heads) {
for (int i = 0; i < wl_state.scr->head_count; i++) {
wwarning("layout-change: head[%d]=(%d,%d %dx%d)", i,
wl_state.scr->heads[i].x, wl_state.scr->heads[i].y,
wl_state.scr->heads[i].width, wl_state.scr->heads[i].height);
}
}
wl_framebuf_refresh_all_positions();
}

View File

@ -7,6 +7,7 @@
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/log.h>
@ -17,6 +18,33 @@
/* Forward declaration for screen head refresh */
extern void wl_screen_refresh_heads(WScreen *scr);
void
wl_output_manager_broadcast_initial_config(void)
{
if (!wl_state.output_mgr)
return;
struct wlr_output_configuration_v1 *config =
wlr_output_configuration_v1_create();
if (!config)
return;
struct wl_wlr_output_state *ost;
wl_list_for_each(ost, &wl_state.outputs, link) {
if (!ost->output)
continue;
struct wlr_output_configuration_head_v1 *head =
wlr_output_configuration_head_v1_create(config, ost->output);
if (head && wl_state.output_layout) {
struct wlr_box box = {0};
wlr_output_layout_get_box(wl_state.output_layout, ost->output, &box);
head->state.x = box.x;
head->state.y = box.y;
}
}
wlr_output_manager_v1_set_configuration(wl_state.output_mgr, config);
}
void
handle_output_frame(struct wl_listener *listener, void *data)
@ -47,10 +75,29 @@ handle_output_frame(struct wl_listener *listener, void *data)
}
}
/* wlr_output_finish asserts that destroy.listener_list is empty,
* so we CANNOT listen to output->events.destroy. Instead the
* frame listener is cleaned up by wlr_scene_output_destroy which
* runs before wlr_output_finish in the teardown chain. */
static void
handle_output_destroy(struct wl_listener *listener, void *data)
{
(void)data;
struct wl_wlr_output_state *ost =
wl_container_of(listener, ost, destroy_listener);
if (ost->frame_listener.link.next)
wl_list_remove(&ost->frame_listener.link);
if (ost->destroy_listener.link.next)
wl_list_remove(&ost->destroy_listener.link);
if (ost->link.next)
wl_list_remove(&ost->link);
if (wl_state.output_count > 0)
wl_state.output_count--;
if (wl_state.output == ost->output) {
wl_state.output = NULL;
wl_state.scene_output = NULL;
wl_state.bg_rect = NULL;
}
wfree(ost);
}
void
handle_backend_new_output(struct wl_listener *listener, void *data)
{
@ -98,8 +145,17 @@ handle_backend_new_output(struct wl_listener *listener, void *data)
if (wl_state.scene) {
ost->scene_output = wlr_scene_output_create(wl_state.scene, output);
if (ost->scene_output && wl_state.scene_output_layout && wl_state.output_layout) {
struct wlr_output_layout_output *lo =
wlr_output_layout_get(wl_state.output_layout, output);
if (lo)
wlr_scene_output_layout_add_output(wl_state.scene_output_layout,
lo, ost->scene_output);
}
ost->frame_listener.notify = handle_output_frame;
wl_signal_add(&output->events.frame, &ost->frame_listener);
ost->destroy_listener.notify = handle_output_destroy;
wl_signal_add(&output->events.destroy, &ost->destroy_listener);
wwarning("wl_backend: scene output created for %s (%dx%d)",
output->name, output->width, output->height);
@ -123,6 +179,7 @@ handle_backend_new_output(struct wl_listener *listener, void *data)
wl_list_insert(&wl_state.outputs, &ost->link);
wl_state.output_count++;
wl_output_manager_broadcast_initial_config();
/* For backward compat, keep wl_state.output pointing to the first output */
if (!wl_state.output) {

View File

@ -2,40 +2,94 @@
#include "config.h"
#endif
#include <wayland-server-core.h>
#include <stdlib.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/backend.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/log.h>
#include "wl_types.h"
extern void wl_screen_refresh_heads(WScreen *scr);
extern void wl_output_manager_broadcast_initial_config(void);
static bool
wl_output_config_apply(struct wlr_output_configuration_v1 *config, bool test_only)
{
size_t states_len = 0;
struct wlr_backend_output_state *states =
wlr_output_configuration_v1_build_state(config, &states_len);
if (!states)
return false;
bool ok;
if (test_only)
ok = wlr_backend_test(wl_state.backend, states, states_len);
else
ok = wlr_backend_commit(wl_state.backend, states, states_len);
free(states);
if (!ok)
return false;
struct wlr_output_configuration_head_v1 *head;
wl_list_for_each(head, &config->heads, link) {
struct wlr_output *output = head->state.output;
if (!output || !wl_state.output_layout)
continue;
wlr_output_layout_remove(wl_state.output_layout, output);
if (head->state.enabled)
wlr_output_layout_add(wl_state.output_layout, output,
head->state.x, head->state.y);
}
if (!test_only) {
struct wl_wlr_output_state *ost;
wl_list_for_each(ost, &wl_state.outputs, link) {
if (!ost->output || !ost->scene_output || !wl_state.scene_output_layout || !wl_state.output_layout)
continue;
struct wlr_output_layout_output *lo =
wlr_output_layout_get(wl_state.output_layout, ost->output);
if (lo)
wlr_scene_output_layout_add_output(wl_state.scene_output_layout,
lo, ost->scene_output);
}
if (wl_state.scr)
wl_screen_refresh_heads(wl_state.scr);
wl_output_manager_broadcast_initial_config();
}
return true;
}
/* ------------------------------------------------------------------ */
/*
* handle_output_mgr_apply — client requests a randr-like output reconfiguration.
* For a single-output headless compositor there is nothing to reconfigure; just
* acknowledge success so the client does not hang waiting for feedback.
* The configuration object is compositor-owned and must be destroyed here.
*/
void
handle_output_mgr_apply(struct wl_listener *listener, void *data)
{
(void)listener;
struct wlr_output_configuration_v1 *config = data;
wlr_output_configuration_v1_send_succeeded(config);
if (wl_output_config_apply(config, false)) {
wlr_output_configuration_v1_send_succeeded(config);
wwarning("wl_backend: output manager apply request — applied");
} else {
wlr_output_configuration_v1_send_failed(config);
wwarning("wl_backend: output manager apply request — failed");
}
wlr_output_configuration_v1_destroy(config);
wwarning("wl_backend: output manager apply request — acknowledged (no-op)");
}
/*
* handle_output_mgr_test — client tests a configuration without applying it.
* Always report success (any single-output config is trivially valid).
*/
void
handle_output_mgr_test(struct wl_listener *listener, void *data)
{
(void)listener;
struct wlr_output_configuration_v1 *config = data;
wlr_output_configuration_v1_send_succeeded(config);
if (wl_output_config_apply(config, true))
wlr_output_configuration_v1_send_succeeded(config);
else
wlr_output_configuration_v1_send_failed(config);
wlr_output_configuration_v1_destroy(config);
}

View File

@ -143,6 +143,22 @@ const uint8_t ovl_font[96][OVL_GH] = {
};
/* Lazily allocate (or resize) the full-screen overlay buffer. */
static void
wl_virtual_bounds(int *w, int *h)
{
WScreen *scr = wl_state.scr;
if (scr && scr->scr_width > 0 && scr->scr_height > 0) {
*w = scr->scr_width;
*h = scr->scr_height;
} else if (wl_state.output) {
*w = (int)wl_state.output->width;
*h = (int)wl_state.output->height;
} else {
*w = 1920;
*h = 1080;
}
}
void
overlay_ensure(WScreen *scr)
{
@ -150,11 +166,8 @@ overlay_ensure(WScreen *scr)
if (scr && scr->scr_width > 0 && scr->scr_height > 0) {
w = scr->scr_width;
h = scr->scr_height;
} else if (wl_state.output) {
w = (int)wl_state.output->width;
h = (int)wl_state.output->height;
} else {
w = 1920; h = 1080;
wl_virtual_bounds(&w, &h);
}
if (wl_state.overlay_surf &&
wl_state.overlay_surf->width == w &&
@ -263,10 +276,11 @@ overlay_flush(void)
/* Notify wlroots that the pixel data has changed. */
wlr_scene_buffer_set_buffer_with_damage(s->scene_buf, &s->wlr_buf, NULL);
}
/* Commit the scene immediately so the animation step is visible
* before the caller's wusleep() delay. */
if (wl_state.scene_output && wl_state.allocator)
wlr_scene_output_commit(wl_state.scene_output, NULL);
/* Do not force an immediate commit here. In multi-output mode the
* single cached wl_state.scene_output only refers to the first output,
* and synchronous commits from overlay/grab paths can exhaust the
* nested backend swapchain. Let the normal output frame loop present
* the updated scene. */
}
/* ------------------------------------------------------------------ */

View File

@ -143,6 +143,7 @@ struct wl_wlr_output_state {
struct wlr_scene_output *scene_output;
struct wlr_scene_rect *bg_rect;
struct wl_listener frame_listener;
struct wl_listener destroy_listener;
struct wl_list link; /* wl_compositor_state.outputs */
};
@ -221,8 +222,10 @@ struct wl_compositor_state {
struct wlr_session *session; /* DRM session (standalone only) */
struct wlr_allocator *allocator;
struct wlr_scene *scene;
struct wlr_scene_output_layout *scene_output_layout;
struct wlr_scene_output *scene_output; /* first output's scene (backward compat) */
struct wl_listener backend_new_output_listener;
struct wl_listener output_layout_change_listener;
char socket_name[64];
char host_wayland_display[64]; /* saved from env before overwrite */
int started; /* non-zero after successful display_open */
@ -313,6 +316,7 @@ struct wl_compositor_state {
struct wl_listener pointer_axis_listener;
struct wl_listener real_pointer_axis_listener;
struct wl_listener real_pointer_destroy_listener;
struct wl_list real_pointer_listeners;
/* Phase 30: WM dock panel (native Wayland rendering via wm_shm_surface). */
struct wm_shm_surface *dock_surface; /* NULL until screen_create_internals */
int dock_width; /* pixels reserved on right edge */

View File

@ -358,8 +358,13 @@ handle_xwayland_new_surface(struct wl_listener *listener, void *data)
wl_list_insert(&wl_state.toplevel_list, &view->link);
wl_state.managed_surface_count++;
view->x = (wl_state.output ? (int)wl_state.output->width : 1920) / 4;
view->y = (wl_state.output ? (int)wl_state.output->height : 1080) / 4;
{
WScreen *scr = wl_state.scr;
int vw = (scr && scr->scr_width > 0) ? scr->scr_width : (wl_state.output ? (int)wl_state.output->width : 1920);
int vh = (scr && scr->scr_height > 0) ? scr->scr_height : (wl_state.output ? (int)wl_state.output->height : 1080);
view->x = vw / 4;
view->y = vh / 4;
}
if (xw->surface) {
view->xw_surface_map_listener.notify = handle_xwayland_surface_map;

View File

@ -747,27 +747,26 @@ static void unhideHereCallback(WMenu *menu, WMenuEntry *entry)
wUnhideApplication(wapp, False, True);
}
static int getDockXPositionForHead(WScreen *scr, int head, Bool on_right_side)
{
WMRect rect;
rect = wGetRectForHead(scr, head);
if (on_right_side)
return rect.pos.x + rect.size.width - ICON_SIZE - DOCK_EXTRA_SPACE;
return rect.pos.x + DOCK_EXTRA_SPACE;
}
static int getDockXPosition(WScreen *scr, Bool on_right_side)
{
int x;
int head;
if (wPreferences.keep_dock_on_primary_head) {
WMRect rect;
if (wPreferences.keep_dock_on_primary_head)
head = scr->primary_head;
else
head = wGetHeadForPointerLocation(scr);
rect = wGetRectForHead(scr, scr->primary_head);
x = rect.pos.x;
if (on_right_side)
x += rect.size.width - ICON_SIZE - DOCK_EXTRA_SPACE;
else
x += DOCK_EXTRA_SPACE;
} else {
if (on_right_side)
x = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
else
x = DOCK_EXTRA_SPACE;
}
return x;
return getDockXPositionForHead(scr, head, on_right_side);
}
/* Name is only used when type == WM_DRAWER and when restoring a specific
@ -1727,26 +1726,30 @@ WDock *wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type)
wScreenKeepInside(scr, &x, &dock->y_pos, ICON_SIZE, ICON_SIZE);
}
/* Is this needed any more? */
if (type == WM_CLIP) {
if (dock->x_pos < 0) {
dock->x_pos = 0;
} else if (dock->x_pos > scr->scr_width - ICON_SIZE) {
dock->x_pos = scr->scr_width - ICON_SIZE;
}
} else {
int left, right, midpoint;
left = getDockXPosition(scr, False);
right = getDockXPosition(scr, True);
midpoint = (left + right) / 2;
if (dock->x_pos < midpoint) {
dock->x_pos = left;
dock->on_right_side = 0;
/* Clamp restore positions relative to the head they land on. */
{
int head = wGetHeadForPoint(scr, wmkpoint(dock->x_pos, dock->y_pos));
WMRect rect = wGetRectForHead(scr, head);
if (type == WM_CLIP) {
if (dock->x_pos < rect.pos.x) {
dock->x_pos = rect.pos.x;
} else if (dock->x_pos > rect.pos.x + rect.size.width - ICON_SIZE) {
dock->x_pos = rect.pos.x + rect.size.width - ICON_SIZE;
}
} else {
dock->x_pos = right;
dock->on_right_side = 1;
int left, right, midpoint;
left = getDockXPositionForHead(scr, head, False);
right = getDockXPositionForHead(scr, head, True);
midpoint = (left + right) / 2;
if (dock->x_pos < midpoint) {
dock->x_pos = left;
dock->on_right_side = 0;
} else {
dock->x_pos = right;
dock->on_right_side = 1;
}
}
}
}