From 5023e416c300e5206329145040822a851f35921d Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 28 Oct 2023 21:49:59 +0200 Subject: [PATCH 1/2] Add wlr-output-management protocol --- include/output-management.h | 28 + include/output.h | 1 + meson.build | 1 + protocols/meson.build | 1 + .../wlr-output-management-unstable-v1.xml | 601 ++++++++++++++++++ src/main.c | 13 + src/output-management.c | 265 ++++++++ src/output.c | 6 +- 8 files changed, 915 insertions(+), 1 deletion(-) create mode 100644 include/output-management.h create mode 100644 protocols/wlr-output-management-unstable-v1.xml create mode 100644 src/output-management.c diff --git a/include/output-management.h b/include/output-management.h new file mode 100644 index 00000000..aae69ea3 --- /dev/null +++ b/include/output-management.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 The wayvnc authors + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#include +#include + +struct output; +struct zwlr_output_manager_v1; + +void wlr_output_manager_setup(struct zwlr_output_manager_v1* output_manager); +bool wlr_output_manager_resize_output(struct output* output, + uint16_t width, uint16_t height); +void wlr_output_manager_destroy(void); diff --git a/include/output.h b/include/output.h index 5e9a6a6f..0d11e2ac 100644 --- a/include/output.h +++ b/include/output.h @@ -57,6 +57,7 @@ struct output { bool is_dimension_changed; bool is_transform_changed; + bool is_headless; void (*on_dimension_change)(struct output*); void (*on_transform_change)(struct output*); diff --git a/meson.build b/meson.build index 61dac4b1..2f42fe35 100644 --- a/meson.build +++ b/meson.build @@ -89,6 +89,7 @@ sources = [ 'src/screencopy.c', 'src/data-control.c', 'src/output.c', + 'src/output-management.c', 'src/pointer.c', 'src/keyboard.c', 'src/seat.c', diff --git a/protocols/meson.build b/protocols/meson.build index 499fc844..d230dca0 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -21,6 +21,7 @@ client_protocols = [ 'xdg-output-unstable-v1.xml', 'linux-dmabuf-unstable-v1.xml', 'wlr-data-control-unstable-v1.xml', + 'wlr-output-management-unstable-v1.xml', 'wlr-output-power-management-unstable-v1.xml', 'ext-transient-seat-v1.xml', ] diff --git a/protocols/wlr-output-management-unstable-v1.xml b/protocols/wlr-output-management-unstable-v1.xml new file mode 100644 index 00000000..411e2f04 --- /dev/null +++ b/protocols/wlr-output-management-unstable-v1.xml @@ -0,0 +1,601 @@ + + + + Copyright © 2019 Purism SPC + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + This protocol exposes interfaces to obtain and modify output device + configuration. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This interface is a manager that allows reading and writing the current + output device configuration. + + Output devices that display pixels (e.g. a physical monitor or a virtual + output in a window) are represented as heads. Heads cannot be created nor + destroyed by the client, but they can be enabled or disabled and their + properties can be changed. Each head may have one or more available modes. + + Whenever a head appears (e.g. a monitor is plugged in), it will be + advertised via the head event. Immediately after the output manager is + bound, all current heads are advertised. + + Whenever a head's properties change, the relevant wlr_output_head events + will be sent. Not all head properties will be sent: only properties that + have changed need to. + + Whenever a head disappears (e.g. a monitor is unplugged), a + wlr_output_head.finished event will be sent. + + After one or more heads appear, change or disappear, the done event will + be sent. It carries a serial which can be used in a create_configuration + request to update heads properties. + + The information obtained from this protocol should only be used for output + configuration purposes. This protocol is not designed to be a generic + output property advertisement protocol for regular clients. Instead, + protocols such as xdg-output should be used. + + + + + This event introduces a new head. This happens whenever a new head + appears (e.g. a monitor is plugged in) or after the output manager is + bound. + + + + + + + This event is sent after all information has been sent after binding to + the output manager object and after any subsequent changes. This applies + to child head and mode objects as well. In other words, this event is + sent whenever a head or mode is created or destroyed and whenever one of + their properties has been changed. Not all state is re-sent each time + the current configuration changes: only the actual changes are sent. + + This allows changes to the output configuration to be seen as atomic, + even if they happen via multiple events. + + A serial is sent to be used in a future create_configuration request. + + + + + + + Create a new output configuration object. This allows to update head + properties. + + + + + + + + Indicates the client no longer wishes to receive events for output + configuration changes. However the compositor may emit further events, + until the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + This event indicates that the compositor is done sending manager events. + The compositor will destroy the object immediately after sending this + event, so it will become invalid and the client should release any + resources associated with it. + + + + + + + A head is an output device. The difference between a wl_output object and + a head is that heads are advertised even if they are turned off. A head + object only advertises properties and cannot be used directly to change + them. + + A head has some read-only properties: modes, name, description and + physical_size. These cannot be changed by clients. + + Other properties can be updated via a wlr_output_configuration object. + + Properties sent via this interface are applied atomically via the + wlr_output_manager.done event. No guarantees are made regarding the order + in which properties are sent. + + + + + This event describes the head name. + + The naming convention is compositor defined, but limited to alphanumeric + characters and dashes (-). Each name is unique among all wlr_output_head + objects, but if a wlr_output_head object is destroyed the same name may + be reused later. The names will also remain consistent across sessions + with the same hardware and software configuration. + + Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do + not assume that the name is a reflection of an underlying DRM + connector, X11 connection, etc. + + If the compositor implements the xdg-output protocol and this head is + enabled, the xdg_output.name event must report the same name. + + The name event is sent after a wlr_output_head object is created. This + event is only sent once per object, and the name does not change over + the lifetime of the wlr_output_head object. + + + + + + + This event describes a human-readable description of the head. + + The description is a UTF-8 string with no convention defined for its + contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11 + output via :1'. However, do not assume that the name is a reflection of + the make, model, serial of the underlying DRM connector or the display + name of the underlying X11 connection, etc. + + If the compositor implements xdg-output and this head is enabled, + the xdg_output.description must report the same description. + + The description event is sent after a wlr_output_head object is created. + This event is only sent once per object, and the description does not + change over the lifetime of the wlr_output_head object. + + + + + + + This event describes the physical size of the head. This event is only + sent if the head has a physical size (e.g. is not a projector or a + virtual device). + + + + + + + + This event introduces a mode for this head. It is sent once per + supported mode. + + + + + + + This event describes whether the head is enabled. A disabled head is not + mapped to a region of the global compositor space. + + When a head is disabled, some properties (current_mode, position, + transform and scale) are irrelevant. + + + + + + + This event describes the mode currently in use for this head. It is only + sent if the output is enabled. + + + + + + + This events describes the position of the head in the global compositor + space. It is only sent if the output is enabled. + + + + + + + + This event describes the transformation currently applied to the head. + It is only sent if the output is enabled. + + + + + + + This events describes the scale of the head in the global compositor + space. It is only sent if the output is enabled. + + + + + + + This event indicates that the head is no longer available. The head + object becomes inert. Clients should send a destroy request and release + any resources associated with it. + + + + + + + + This event describes the manufacturer of the head. + + This must report the same make as the wl_output interface does in its + geometry event. + + Together with the model and serial_number events the purpose is to + allow clients to recognize heads from previous sessions and for example + load head-specific configurations back. + + It is not guaranteed this event will be ever sent. A reason for that + can be that the compositor does not have information about the make of + the head or the definition of a make is not sensible in the current + setup, for example in a virtual session. Clients can still try to + identify the head by available information from other events but should + be aware that there is an increased risk of false positives. + + It is not recommended to display the make string in UI to users. For + that the string provided by the description event should be preferred. + + + + + + + This event describes the model of the head. + + This must report the same model as the wl_output interface does in its + geometry event. + + Together with the make and serial_number events the purpose is to + allow clients to recognize heads from previous sessions and for example + load head-specific configurations back. + + It is not guaranteed this event will be ever sent. A reason for that + can be that the compositor does not have information about the model of + the head or the definition of a model is not sensible in the current + setup, for example in a virtual session. Clients can still try to + identify the head by available information from other events but should + be aware that there is an increased risk of false positives. + + It is not recommended to display the model string in UI to users. For + that the string provided by the description event should be preferred. + + + + + + + This event describes the serial number of the head. + + Together with the make and model events the purpose is to allow clients + to recognize heads from previous sessions and for example load head- + specific configurations back. + + It is not guaranteed this event will be ever sent. A reason for that + can be that the compositor does not have information about the serial + number of the head or the definition of a serial number is not sensible + in the current setup. Clients can still try to identify the head by + available information from other events but should be aware that there + is an increased risk of false positives. + + It is not recommended to display the serial_number string in UI to + users. For that the string provided by the description event should be + preferred. + + + + + + + + + This request indicates that the client will no longer use this head + object. + + + + + + + + + + + + + This event describes whether adaptive sync is currently enabled for + the head or not. Adaptive sync is also known as Variable Refresh + Rate or VRR. + + + + + + + + This object describes an output mode. + + Some heads don't support output modes, in which case modes won't be + advertised. + + Properties sent via this interface are applied atomically via the + wlr_output_manager.done event. No guarantees are made regarding the order + in which properties are sent. + + + + + This event describes the mode size. The size is given in physical + hardware units of the output device. This is not necessarily the same as + the output size in the global compositor space. For instance, the output + may be scaled or transformed. + + + + + + + + This event describes the mode's fixed vertical refresh rate. It is only + sent if the mode has a fixed refresh rate. + + + + + + + This event advertises this mode as preferred. + + + + + + This event indicates that the mode is no longer available. The mode + object becomes inert. Clients should send a destroy request and release + any resources associated with it. + + + + + + + + This request indicates that the client will no longer use this mode + object. + + + + + + + This object is used by the client to describe a full output configuration. + + First, the client needs to setup the output configuration. Each head can + be either enabled (and configured) or disabled. It is a protocol error to + send two enable_head or disable_head requests with the same head. It is a + protocol error to omit a head in a configuration. + + Then, the client can apply or test the configuration. The compositor will + then reply with a succeeded, failed or cancelled event. Finally the client + should destroy the configuration object. + + + + + + + + + + + Enable a head. This request creates a head configuration object that can + be used to change the head's properties. + + + + + + + + Disable a head. + + + + + + + Apply the new output configuration. + + In case the configuration is successfully applied, there is no guarantee + that the new output state matches completely the requested + configuration. For instance, a compositor might round the scale if it + doesn't support fractional scaling. + + After this request has been sent, the compositor must respond with an + succeeded, failed or cancelled event. Sending a request that isn't the + destructor is a protocol error. + + + + + + Test the new output configuration. The configuration won't be applied, + but will only be validated. + + Even if the compositor succeeds to test a configuration, applying it may + fail. + + After this request has been sent, the compositor must respond with an + succeeded, failed or cancelled event. Sending a request that isn't the + destructor is a protocol error. + + + + + + Sent after the compositor has successfully applied the changes or + tested them. + + Upon receiving this event, the client should destroy this object. + + If the current configuration has changed, events to describe the changes + will be sent followed by a wlr_output_manager.done event. + + + + + + Sent if the compositor rejects the changes or failed to apply them. The + compositor should revert any changes made by the apply request that + triggered this event. + + Upon receiving this event, the client should destroy this object. + + + + + + Sent if the compositor cancels the configuration because the state of an + output changed and the client has outdated information (e.g. after an + output has been hotplugged). + + The client can create a new configuration with a newer serial and try + again. + + Upon receiving this event, the client should destroy this object. + + + + + + Using this request a client can tell the compositor that it is not going + to use the configuration object anymore. Any changes to the outputs + that have not been applied will be discarded. + + This request also destroys wlr_output_configuration_head objects created + via this object. + + + + + + + This object is used by the client to update a single head's configuration. + + It is a protocol error to set the same property twice. + + + + + + + + + + + + + + This request sets the head's mode. + + + + + + + This request assigns a custom mode to the head. The size is given in + physical hardware units of the output device. If set to zero, the + refresh rate is unspecified. + + It is a protocol error to set both a mode and a custom mode. + + + + + + + + + This request sets the head's position in the global compositor space. + + + + + + + + This request sets the head's transform. + + + + + + + This request sets the head's scale. + + + + + + + + + This request enables/disables adaptive sync. Adaptive sync is also + known as Variable Refresh Rate or VRR. + + + + + diff --git a/src/main.c b/src/main.c index 7649a85e..7bd24d6b 100644 --- a/src/main.c +++ b/src/main.c @@ -39,12 +39,14 @@ #include "virtual-keyboard-unstable-v1.h" #include "xdg-output-unstable-v1.h" #include "wlr-output-power-management-unstable-v1.h" +#include "wlr-output-management-unstable-v1.h" #include "linux-dmabuf-unstable-v1.h" #include "ext-transient-seat-v1.h" #include "screencopy.h" #include "data-control.h" #include "strlcpy.h" #include "output.h" +#include "output-management.h" #include "pointer.h" #include "keyboard.h" #include "seat.h" @@ -248,6 +250,15 @@ static void registry_add(void* data, struct wl_registry* registry, return; } + if (strcmp(interface, zwlr_output_manager_v1_interface.name) == 0) { + nvnc_trace("Registering new wlr_output_manager"); + struct zwlr_output_manager_v1* wlr_output_manager = + wl_registry_bind(registry, id, + &zwlr_output_manager_v1_interface, 1); + wlr_output_manager_setup(wlr_output_manager); + return; + } + if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) { self->screencopy.manager = wl_registry_bind(registry, id, @@ -373,6 +384,8 @@ void wayvnc_destroy(struct wayvnc* self) if (wlr_output_power_manager) zwlr_output_power_manager_v1_destroy(wlr_output_power_manager); + wlr_output_manager_destroy(); + wl_shm_destroy(wl_shm); if (self->keyboard_manager) diff --git a/src/output-management.c b/src/output-management.c new file mode 100644 index 00000000..09fa4a67 --- /dev/null +++ b/src/output-management.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2023 The wayvnc authors + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "output.h" +#include "output-management.h" + +#include "wlr-output-management-unstable-v1.h" + +struct output_manager_head { + struct zwlr_output_head_v1* head; + struct wl_list link; + char* name; + bool enabled; +}; + +static struct wl_list heads; +static uint32_t last_config_serial; +static struct zwlr_output_manager_v1* wlr_output_manager; + +/* single head properties */ +static void output_head_name(void* data, + struct zwlr_output_head_v1* output_head, const char* name) +{ + struct output_manager_head* head = data; + nvnc_trace("Got head name: %s", name); + free(head->name); + head->name = strdup(name); +} + +static void output_head_description(void* data, + struct zwlr_output_head_v1* output_head, + const char* description) +{ + nvnc_trace("Got head description: %s", description); +} + +static void output_head_physical_size(void* data, + struct zwlr_output_head_v1* output_head, + int32_t width, int32_t height) +{ + nvnc_trace("Got head size: %dx%d", width, height); +} + +static void output_head_mode(void* data, + struct zwlr_output_head_v1* output_head, + struct zwlr_output_mode_v1* mode) +{ + nvnc_trace("Got head mode"); +} + +static void output_head_enabled(void* data, + struct zwlr_output_head_v1* output_head, int32_t enabled) +{ + nvnc_trace("Got head enabled: %s", enabled ? "yes" : "no"); + struct output_manager_head* head = data; + head->enabled = !!enabled; +} + +static void output_head_current_mode(void* data, + struct zwlr_output_head_v1* output_head, + struct zwlr_output_mode_v1* mode) +{ + nvnc_trace("Got head current mode"); +} + +static void output_head_position(void* data, + struct zwlr_output_head_v1* output_head, int32_t x, int32_t y) +{ + nvnc_trace("Got head position: %d,%d", x, y); +} + +static void output_head_transform(void* data, + struct zwlr_output_head_v1* output_head, int32_t transform) +{ + nvnc_trace("Got head transform: %d", transform); +} + +static void output_head_scale(void* data, + struct zwlr_output_head_v1* output_head, wl_fixed_t scale_f) +{ + double scale = wl_fixed_to_double(scale_f); + nvnc_trace("Got head scale: %.2f", scale); +} + +static void output_head_finished(void* data, + struct zwlr_output_head_v1* output_head) +{ + nvnc_trace("head gone, removing"); + struct output_manager_head* head = data; + zwlr_output_head_v1_destroy(output_head); + wl_list_remove(&head->link); + free(head->name); + head->name = NULL; + head->head = NULL; + free(head); +} + +struct zwlr_output_head_v1_listener wlr_output_head_listener = { + .name = output_head_name, + .description = output_head_description, + .physical_size = output_head_physical_size, + .mode = output_head_mode, + .enabled = output_head_enabled, + .current_mode = output_head_current_mode, + .position = output_head_position, + .transform = output_head_transform, + .scale = output_head_scale, + .finished = output_head_finished, +}; + +/* config object */ +static void output_manager_config_succeeded(void* data, + struct zwlr_output_configuration_v1* config) +{ + nvnc_trace("config request succeeded"); + zwlr_output_configuration_v1_destroy(config); +} + +static void output_manager_config_failed(void* data, + struct zwlr_output_configuration_v1* config) +{ + nvnc_trace("config request failed"); + zwlr_output_configuration_v1_destroy(config); +} + +static void output_manager_config_cancelled(void* data, + struct zwlr_output_configuration_v1* config) +{ + nvnc_trace("config request cancelled"); + zwlr_output_configuration_v1_destroy(config); +} + +struct zwlr_output_configuration_v1_listener wlr_output_config_listener = { + .succeeded = output_manager_config_succeeded, + .failed = output_manager_config_failed, + .cancelled = output_manager_config_cancelled, +}; + +/* manager itself */ +static void output_manager_done(void* data, + struct zwlr_output_manager_v1* zwlr_output_manager_v1, + uint32_t serial) +{ + last_config_serial = serial; + nvnc_trace("Got new serial: %u", serial); +} + +static void output_manager_finished(void* data, + struct zwlr_output_manager_v1* zwlr_output_manager_v1) +{ + nvnc_trace("output-manager destroyed"); + wlr_output_manager = NULL; +} + +static void output_manager_head(void* data, + struct zwlr_output_manager_v1* zwlr_output_manager_v1, + struct zwlr_output_head_v1* output_head) +{ + struct output_manager_head* head = calloc(1, sizeof(*head)); + if (!head) { + nvnc_log(NVNC_LOG_ERROR, "OOM"); + return; + } + + head->head = output_head; + wl_list_insert(heads.prev, &head->link); + nvnc_trace("New head, now at %lu", wl_list_length(&heads)); + + zwlr_output_head_v1_add_listener(head->head, + &wlr_output_head_listener, head); +} + +static const struct zwlr_output_manager_v1_listener + wlr_output_manager_listener = { + .head = output_manager_head, + .done = output_manager_done, + .finished = output_manager_finished, +}; + +/* Public API */ +void wlr_output_manager_setup(struct zwlr_output_manager_v1* output_manager) +{ + if (wlr_output_manager) + return; + + wl_list_init(&heads); + wlr_output_manager = output_manager; + zwlr_output_manager_v1_add_listener(wlr_output_manager, + &wlr_output_manager_listener, NULL); +} + +void wlr_output_manager_destroy(void) +{ + if (!wlr_output_manager) + return; + + zwlr_output_manager_v1_destroy(wlr_output_manager); +} + +bool wlr_output_manager_resize_output(struct output* output, + uint16_t width, uint16_t height) +{ + if (!wlr_output_manager) { + nvnc_log(NVNC_LOG_INFO, + "output-management protocol not available, not resizing output"); + return false; + } + + if (!output->is_headless) { + nvnc_log(NVNC_LOG_INFO, + "not resizing output %s: not a headless one", + output->name); + return false; + } + + // TODO: This could be synced to --max-fps + int refresh_rate = 0; + + struct zwlr_output_configuration_v1* config; + struct zwlr_output_configuration_head_v1* config_head; + + config = zwlr_output_manager_v1_create_configuration( + wlr_output_manager, last_config_serial); + zwlr_output_configuration_v1_add_listener(config, + &wlr_output_config_listener, NULL); + + struct output_manager_head* head; + wl_list_for_each(head, &heads, link) { + if (!head->enabled) { + nvnc_trace("disabling output %s", head->name); + zwlr_output_configuration_v1_disable_head( + config, head->head); + continue; + } + + config_head = zwlr_output_configuration_v1_enable_head( + config, head->head); + if (head->name && strcmp(head->name, output->name) == 0) { + nvnc_trace("reconfiguring output %s", head->name); + zwlr_output_configuration_head_v1_set_custom_mode( + config_head, width, height, refresh_rate); + } + } + + nvnc_trace("applying new output config"); + zwlr_output_configuration_v1_apply(config); + return true; +} diff --git a/src/output.c b/src/output.c index 7773c510..1cd2a4f5 100644 --- a/src/output.c +++ b/src/output.c @@ -219,7 +219,11 @@ void output_name(void* data, struct zxdg_output_v1* xdg_output, struct output* self = data; strlcpy(self->name, name, sizeof(self->name)); - nvnc_trace("Output %u name: %s", self->id, self->name); + self->is_headless = strncmp( + name, "HEADLESS-", strlen("HEADLESS-")) == 0; + + nvnc_trace("Output %u name: %s, headless: %s", self->id, self->name, + self->is_headless ? "yes" : "no"); } void output_description(void* data, struct zxdg_output_v1* xdg_output, From 1ffb6ea89d2cf27a946625b021cbc082436211de Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 28 Oct 2023 21:55:01 +0200 Subject: [PATCH 2/2] Automatically resize headless outputs on client request --- src/main.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main.c b/src/main.c index 7bd24d6b..3823e68a 100644 --- a/src/main.c +++ b/src/main.c @@ -681,6 +681,34 @@ static void on_client_cut_text(struct nvnc_client* nvnc_client, } } +static bool on_client_resize(struct nvnc_client* nvnc_client, + const struct nvnc_desktop_layout* layout) +{ + struct wayvnc_client* client = nvnc_get_userdata(nvnc_client); + + uint16_t width = nvnc_desktop_layout_get_width(layout); + uint16_t height = nvnc_desktop_layout_get_height(layout); + struct output* output = client->server->selected_output; + + if (output == NULL) + return false; + + nvnc_log(NVNC_LOG_DEBUG, + "Client resolution changed: %ux%u, capturing output %s which is headless: %s", + width, height, output->name, + output->is_headless ? "yes" : "no"); + + /* + * TODO: neatvnc currently does not support anything other than + * true or false here. In the 'true' case, neatvnc will send + * RFB_RESIZE_STATUS_SUCCESS which technically is wrong and + * should be RFB_RESIZE_STATUS_REQUEST_FORWARDED instead. + * In the 'false' case neatvnc will send the correct response: + * RFB_RESIZE_STATUS_PROHIBITED. + */ + return wlr_output_manager_resize_output(output, width, height); +} + bool on_auth(const char* username, const char* password, void* ud) { struct wayvnc* self = ud; @@ -779,6 +807,8 @@ static int init_nvnc(struct wayvnc* self, const char* addr, uint16_t port, nvnc_set_name(self->nvnc, "WayVNC"); + nvnc_set_desktop_layout_fn(self->nvnc, on_client_resize); + enum nvnc_auth_flags auth_flags = 0; if (self->cfg.enable_auth) { auth_flags |= NVNC_AUTH_REQUIRE_AUTH;