multi monitor
This commit is contained in:
parent
ad0871054c
commit
2c1dca387f
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
75
src/dock.c
75
src/dock.c
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue