protocols: expose output-management, honor resizes standalone
Advertise zwlr_output_manager_v1 with the single head, published on setup and re-published from the commit path whenever the size lands (resizes apply lazily, so the commit -- not the request -- is where the new size becomes observable). Client sizes apply standalone through the normal resize path (tabs reflow, swapchain follows); nested refuses anything but the live size since the host owns the geometry. Disabling, scale and transform changes are refused; superseded configs are destroyed after feedback. Standalone output_resize now owns the canvas size so client resizes reach the lazy modeset. wltest gains --output-mgmt; smoke tests cover the global, head listing, and an 800x600 apply with tab reflow.
This commit is contained in:
parent
fcd3bca53b
commit
d8164ce315
6 changed files with 1158 additions and 3 deletions
171
kick.c
171
kick.c
|
|
@ -96,6 +96,7 @@
|
|||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_output_power_management_v1.h>
|
||||
#include <wlr/types/wlr_output_management_v1.h>
|
||||
#include <wlr/types/wlr_pointer.h>
|
||||
#include <wlr/types/wlr_pointer_constraints_v1.h>
|
||||
#include <wlr/types/wlr_pointer_gestures_v1.h>
|
||||
|
|
@ -393,6 +394,13 @@ struct kick_server {
|
|||
struct wlr_output_power_manager_v1 *output_power_mgr;
|
||||
struct wl_listener output_power_set_mode;
|
||||
bool output_powered;
|
||||
/* Output management (kanshi/wdisplays reconfigure the output): the
|
||||
single server output is published as a head; client size requests
|
||||
are honored standalone and refused nested (the host owns the size).
|
||||
Scale/transform changes and disabling are unsupported. */
|
||||
struct wlr_output_manager_v1 *output_mgr;
|
||||
struct wl_listener output_mgr_apply;
|
||||
struct wl_listener output_mgr_test;
|
||||
/* Gamma control (night-light tools like wlsunset/gammastep): a client
|
||||
sets a per-output ramp; wlroots rejects outputs with no LUT (e.g.
|
||||
the headless output) itself, so the handler only commits ramps for
|
||||
|
|
@ -870,6 +878,9 @@ static const char *foreign_str(const char *s);
|
|||
static struct kick_window *tab_current_window(struct kick_tab *tab);
|
||||
/* Toplevel-icon body lives with the overlay code; called from teardown. */
|
||||
static void icon_proto_release(struct kick_window *w);
|
||||
/* Output-management publish lives with the modeset handlers; called from
|
||||
output setup and every resize. */
|
||||
static void output_mgmt_publish(void);
|
||||
static struct kick_tab *pick_successor(struct kick_tab *tab,
|
||||
bool prefer_right);
|
||||
static void remove_tab(struct kick_tab *tab);
|
||||
|
|
@ -2479,6 +2490,15 @@ static void output_resize(int width, int height) {
|
|||
if (height < 50) height = 50;
|
||||
if (!server.output || !server.allocator) return;
|
||||
|
||||
/* Standalone has no host window tracking the size: the canvas follows
|
||||
the request so the lazy modeset in apply_pending_output_resize
|
||||
picks it up at render (nested is a no-op: the args already are the
|
||||
host window's size). */
|
||||
if (server.standalone) {
|
||||
host.width = width;
|
||||
host.height = height;
|
||||
}
|
||||
|
||||
/* The new size is applied to the output lazily at render time (see
|
||||
apply_pending_output_resize); here only the scene geometry and the
|
||||
clients are told, so configure-time stays free of modesets and
|
||||
|
|
@ -2706,6 +2726,10 @@ server.output = output;
|
|||
server.overlay_damage_rect = wlr_scene_rect_create(
|
||||
&server.scene->tree, w, h, (float[]){ 0.0f, 0.0f, 0.0f, 0.0f });
|
||||
}
|
||||
|
||||
/* Publish the fresh output to management clients (kanshi/wdisplays
|
||||
learn the head and its modes/size on bind). */
|
||||
output_mgmt_publish();
|
||||
}
|
||||
|
||||
/* Nearest live tab, scanning right first (prefer_right) or left first.
|
||||
|
|
@ -6193,6 +6217,141 @@ static void handle_output_power_set_mode(struct wl_listener *listener,
|
|||
kick_request_redraw();
|
||||
}
|
||||
|
||||
/* ===================== Output management =====================
|
||||
kanshi/wdisplays-style clients list the single head and request sizes.
|
||||
Standalone honors custom sizes through the normal resize path (tabs
|
||||
reflow, swapchain follows lazily at render); nested refuses anything
|
||||
but the live size because the host window owns the geometry. Anything
|
||||
beyond size (disabling, scale, transform) is refused everywhere: one
|
||||
fixed output per session. Successful applies republish so listening
|
||||
clients converge. */
|
||||
|
||||
/* Broadcast the live output as the manager's current configuration;
|
||||
wlroots creates/updates/destroys heads from it and only notifies on
|
||||
change. No-op until both manager and output exist. */
|
||||
static void output_mgmt_publish(void) {
|
||||
if (!server.output_mgr || !server.output)
|
||||
return;
|
||||
struct wlr_output_configuration_v1 *config =
|
||||
wlr_output_configuration_v1_create();
|
||||
if (!config)
|
||||
return;
|
||||
if (!wlr_output_configuration_head_v1_create(config, server.output)) {
|
||||
wlr_output_configuration_v1_destroy(config);
|
||||
return;
|
||||
}
|
||||
wlr_output_manager_v1_set_configuration(server.output_mgr, config);
|
||||
}
|
||||
|
||||
/* Re-publish after a commit changed the output size. Resizes land
|
||||
lazily (host configures, client applies), so the commit -- not the
|
||||
request -- is where the new size becomes observable. */
|
||||
static void output_mgmt_sync(void) {
|
||||
if (!server.output_mgr || !server.output)
|
||||
return;
|
||||
static int last_w = -1, last_h = -1;
|
||||
if (server.output->width == last_w && server.output->height == last_h)
|
||||
return;
|
||||
last_w = server.output->width;
|
||||
last_h = server.output->height;
|
||||
output_mgmt_publish();
|
||||
}
|
||||
|
||||
/* The config head addressing our output, if the client included it. */
|
||||
static struct wlr_output_configuration_head_v1 *mgmt_head_for_output(
|
||||
struct wlr_output_configuration_v1 *config) {
|
||||
if (!server.output)
|
||||
return NULL;
|
||||
struct wlr_output_configuration_head_v1 *h;
|
||||
wl_list_for_each(h, &config->heads, link) {
|
||||
if (h->state.output == server.output)
|
||||
return h;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Requested size, preferring an explicit mode over a custom one. */
|
||||
static void mgmt_head_size(struct wlr_output_configuration_head_v1 *h,
|
||||
int *w, int *hh) {
|
||||
if (h->state.mode) {
|
||||
*w = h->state.mode->width;
|
||||
*hh = h->state.mode->height;
|
||||
} else {
|
||||
*w = h->state.custom_mode.width;
|
||||
*hh = h->state.custom_mode.height;
|
||||
}
|
||||
}
|
||||
|
||||
/* Whether a client config is one we can honor: our output, still
|
||||
enabled, same scale/transform, sane size. Nested additionally
|
||||
requires the live size (the host owns it). */
|
||||
static bool mgmt_config_ok(struct wlr_output_configuration_v1 *config,
|
||||
int *w, int *hh) {
|
||||
struct wlr_output_configuration_head_v1 *h =
|
||||
mgmt_head_for_output(config);
|
||||
if (!h || !h->state.enabled || !server.output)
|
||||
return false;
|
||||
mgmt_head_size(h, w, hh);
|
||||
if (*w < 50 || *hh < 50 || *w > 16384 || *hh > 16384)
|
||||
return false;
|
||||
if (h->state.scale != (float)server.output->scale ||
|
||||
h->state.transform != server.output->transform)
|
||||
return false;
|
||||
if (!server.standalone &&
|
||||
(*w != server.output->width || *hh != server.output->height))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_output_mgr_test(struct wl_listener *listener,
|
||||
void *data) {
|
||||
(void)listener;
|
||||
struct wlr_output_configuration_v1 *config = data;
|
||||
int w = 0, hh = 0;
|
||||
bool ok = mgmt_config_ok(config, &w, &hh);
|
||||
if (ok && server.standalone) {
|
||||
/* Honest modeset probe on top of the sanity check. */
|
||||
struct wlr_output_state state;
|
||||
wlr_output_state_init(&state);
|
||||
wlr_output_state_set_custom_mode(&state, w, hh, 0);
|
||||
ok = wlr_output_test_state(server.output, &state);
|
||||
wlr_output_state_finish(&state);
|
||||
}
|
||||
kick_log(KICK_LOG_DEBUG, "[output] test %dx%d %s\n", w, hh,
|
||||
ok ? "ok" : "failed");
|
||||
if (ok)
|
||||
wlr_output_configuration_v1_send_succeeded(config);
|
||||
else
|
||||
wlr_output_configuration_v1_send_failed(config);
|
||||
wlr_output_configuration_v1_destroy(config);
|
||||
}
|
||||
|
||||
static void handle_output_mgr_apply(struct wl_listener *listener,
|
||||
void *data) {
|
||||
(void)listener;
|
||||
struct wlr_output_configuration_v1 *config = data;
|
||||
int w = 0, hh = 0;
|
||||
bool ok = mgmt_config_ok(config, &w, &hh);
|
||||
if (ok && server.standalone) {
|
||||
struct wlr_output_state state;
|
||||
wlr_output_state_init(&state);
|
||||
wlr_output_state_set_custom_mode(&state, w, hh, 0);
|
||||
ok = wlr_output_test_state(server.output, &state);
|
||||
wlr_output_state_finish(&state);
|
||||
}
|
||||
if (ok) {
|
||||
if (server.standalone)
|
||||
output_resize(w, hh);
|
||||
output_mgmt_publish();
|
||||
kick_log(KICK_LOG_INFO, "[output] apply %dx%d\n", w, hh);
|
||||
wlr_output_configuration_v1_send_succeeded(config);
|
||||
} else {
|
||||
kick_log(KICK_LOG_INFO, "[output] apply %dx%d refused\n", w, hh);
|
||||
wlr_output_configuration_v1_send_failed(config);
|
||||
}
|
||||
wlr_output_configuration_v1_destroy(config);
|
||||
}
|
||||
|
||||
/* Gamma control: apply the client's ramp to the single output (or reset it
|
||||
when the control went away: wlroots emits set_gamma with NULL then). A
|
||||
failed commit fails the control so the client (wlsunset) retries later
|
||||
|
|
@ -10063,6 +10222,8 @@ static void render_standalone(void) {
|
|||
apply_tearing_hint(&state);
|
||||
if (!wlr_output_commit_state(server.output, &state))
|
||||
kick_log(KICK_LOG_DEBUG, "[render] output commit failed\n");
|
||||
else
|
||||
output_mgmt_sync();
|
||||
|
||||
frame_done:
|
||||
struct timespec now;
|
||||
|
|
@ -10147,6 +10308,8 @@ static void render_and_present(void) {
|
|||
apply_tearing_hint(&state);
|
||||
if (!wlr_output_commit_state(server.output, &state))
|
||||
kick_log(KICK_LOG_DEBUG, "[render] output commit failed\n");
|
||||
else
|
||||
output_mgmt_sync();
|
||||
|
||||
frame_done:
|
||||
struct timespec now;
|
||||
|
|
@ -11189,6 +11352,14 @@ int main(int argc, char *argv[]) {
|
|||
wl_signal_add(&server.output_power_mgr->events.set_mode,
|
||||
&server.output_power_set_mode);
|
||||
|
||||
/* Output management: publish the single head; client resizes apply
|
||||
standalone and are refused nested (see the section). */
|
||||
server.output_mgr = wlr_output_manager_v1_create(server.wl_display);
|
||||
server.output_mgr_apply.notify = handle_output_mgr_apply;
|
||||
wl_signal_add(&server.output_mgr->events.apply, &server.output_mgr_apply);
|
||||
server.output_mgr_test.notify = handle_output_mgr_test;
|
||||
wl_signal_add(&server.output_mgr->events.test, &server.output_mgr_test);
|
||||
|
||||
/* Gamma control: night-light clients set the output ramp (see
|
||||
handle_gamma_set); wlroots itself fails controls for outputs with
|
||||
no LUT, so headless/nested clients learn it is unsupported. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue