Add support for wlr-output-management-unstable-v1

This commit is contained in:
emersion 2019-03-07 17:51:43 +01:00 committed by Drew DeVault
parent b6d0de177a
commit 3a233b3fcc
6 changed files with 928 additions and 0 deletions

View file

@ -23,6 +23,7 @@ install_headers(
'wlr_matrix.h',
'wlr_output_damage.h',
'wlr_output_layout.h',
'wlr_output_management_v1.h',
'wlr_output.h',
'wlr_pointer_constraints_v1.h',
'wlr_pointer_gestures_v1.h',

View file

@ -0,0 +1,66 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_TYPES_WLR_OUTPUT_MANAGEMENT_V1_H
#define WLR_TYPES_WLR_OUTPUT_MANAGEMENT_V1_H
#include <wayland-server.h>
#include <wlr/types/wlr_output.h>
struct wlr_output_configuration_v1;
struct wlr_output_manager_v1 {
struct wl_display *display;
struct wl_global *global;
struct wl_list resources;
struct wlr_output_configuration_v1 *current;
struct {
struct wl_signal destroy;
} events;
struct wl_listener display_destroy;
void *data;
};
// TODO: split this into multiple structs (state + output_head + output_configuration_head)
struct wlr_output_configuration_head_v1 {
struct wlr_output_configuration_v1 *config;
struct wlr_output *output;
struct wl_list link;
// for current config only
struct wl_list resources;
bool enabled;
struct wl_listener output_destroy;
};
struct wlr_output_configuration_v1 {
struct wl_list heads;
uint32_t serial;
};
struct wlr_output_manager_v1 *wlr_output_manager_v1_create(
struct wl_display *display);
void wlr_output_manager_v1_set_configuration(
struct wlr_output_manager_v1 *manager,
struct wlr_output_configuration_v1 *config);
struct wlr_output_configuration_v1 *wlr_output_configuration_v1_create(void);
void wlr_output_configuration_v1_destroy(
struct wlr_output_configuration_v1 *config);
struct wlr_output_configuration_head_v1 *
wlr_output_configuration_head_v1_create(
struct wlr_output_configuration_v1 *config, struct wlr_output *output);
#endif

View file

@ -38,6 +38,7 @@ protocols = [
'wlr-gamma-control-unstable-v1.xml',
'wlr-input-inhibitor-unstable-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'wlr-output-management-unstable-v1.xml',
'wlr-screencopy-unstable-v1.xml',
]

View file

@ -0,0 +1,444 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_output_management_unstable_v1">
<copyright>
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.
</copyright>
<description summary="protocol to configure output devices">
This protocol exposes interfaces to get and change 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.
</description>
<interface name="zwlr_output_manager_v1" version="1">
<description summary="output device configuration manager">
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, but they can be enabled or disabled and their properties can be
changed. Each head may have one or more available modes.
Heads are advertised when the output manager is bound, and whenever they
appear.
Whenever the number of heads or modes changes, the done event will be
sent. It carries a serial which can be used in a create_configuration
request to change 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.
</description>
<event name="head">
<description summary="introduce a new head">
This event introduces a new head.
</description>
<arg name="head" type="new_id" interface="zwlr_output_head_v1"/>
</event>
<event name="done">
<description summary="sent all information about current configuration">
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.
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.
</description>
<arg name="serial" type="uint" summary="current configuration serial"/>
</event>
<request name="create_configuration">
<description summary="create a new output configuration object">
Create a new output configuration object. This allows to update head
properties.
</description>
<arg name="id" type="new_id" interface="zwlr_output_configuration_v1"/>
<arg name="serial" type="uint"/>
</request>
<request name="stop">
<description summary="stop sending events">
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.
</description>
</request>
<event name="finished">
<description summary="the compositor has finished with the manager">
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.
</description>
</event>
</interface>
<interface name="zwlr_output_head_v1" version="1">
<description summary="output device">
A head is an output device. The difference with wl_output is that heads
are advertized even if they are turned off. A head object only advertises
properties and cannot be used directly to change them. In order to update
some properties, one needs to create a wlr_output_configuration object.
A head has some read-only properties: mode, name, description and
physical_size. These cannot be changed by clients.
enabled and current_mode are physical properties. Updating them might take
some time, depending on hardware limitations.
position, transform and scale are logical properties. They describe how
the output is mapped in the global compositor space.
</description>
<event name="mode">
<description summary="advertise a supported mode">
If the head supports modes, this event is sent once per supported mode.
</description>
<arg name="mode" type="new_id" interface="zwlr_output_mode_v1"/>
</event>
<event name="name">
<description summary="head name">
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.
</description>
<arg name="name" type="string"/>
</event>
<event name="description">
<description summary="head description">
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'.
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.
</description>
<arg name="description" type="string"/>
</event>
<event name="physical_size">
<description summary="head physical size">
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).
</description>
<arg name="width" type="int" summary="width in millimeters of the output"/>
<arg name="height" type="int" summary="height in millimeters of the output"/>
</event>
<event name="enabled">
<description summary="head is enabled or disabled">
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.
</description>
<arg name="enabled" type="int" summary="zero if disabled, non-zero if enabled"/>
</event>
<event name="current_mode">
<description summary="current mode">
This event describes the mode currently in use for this head. It is only
sent if the output is enabled and supports modes.
</description>
<arg name="mode" type="object" interface="zwlr_output_mode_v1"/>
</event>
<event name="position">
<description summary="current position">
This events describes the position of the head in the global compositor
space. It is only sent if the output is enabled.
</description>
<arg name="x" type="int"
summary="x position within the global compositor space"/>
<arg name="y" type="int"
summary="y position within the global compositor space"/>
</event>
<event name="transform">
<description summary="current transformation">
This event describes the transformation currently applied to the head.
It is only sent if the output is enabled.
</description>
<arg name="transform" type="int" enum="wl_output.transform"/>
</event>
<event name="scale">
<description summary="current scale">
This events describes the scale of the head in the global compositor
space. It is only sent if the output is enabled.
</description>
<arg name="scale" type="fixed"/>
</event>
<event name="finished">
<description summary="the head has been destroyed">
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.
</description>
</event>
</interface>
<interface name="zwlr_output_mode_v1" version="1">
<description summary="output mode">
This object describes an output mode.
Some heads don't support output modes, in which case modes won't be
advertised.
</description>
<event name="size">
<description summary="mode size">
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.
</description>
<arg name="width" type="int" summary="width of the mode in hardware units"/>
<arg name="height" type="int" summary="height of the mode in hardware units"/>
</event>
<event name="refresh">
<description summary="mode refresh rate">
This event describes the mode's fixed vertical refresh rate, if any.
</description>
<arg name="refresh" type="int" summary="vertical refresh rate in mHz"/>
</event>
<event name="preferred">
<description summary="mode is preferred">
This event advertises this mode as preferred.
</description>
</event>
<event name="finished">
<description summary="the mode has been destroyed">
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.
</description>
</event>
</interface>
<interface name="zwlr_output_configuration_v1" version="1">
<description summary="output configuration">
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.
</description>
<enum name="error">
<entry name="already_configured_head" value="1"
summary="head has been configured twice"/>
<entry name="unconfigured_head" value="2"
summary="head has not been configured"/>
<entry name="already_used" value="3"
summary="request sent after configuration has been applied or tested"/>
</enum>
<request name="enable_head">
<description summary="enable and configure a head">
Enable a head. This request creates a head configuration object that can
be used to change the head's properties.
</description>
<arg name="id" type="new_id" interface="zwlr_output_configuration_head_v1"
summary="a new object to configure the head"/>
<arg name="head" type="object" interface="zwlr_output_head_v1"
summary="the head to be enabled"/>
</request>
<request name="disable_head">
<description summary="disable a head">
Disable a head. The head's properties are irrelevant in this case.
</description>
<arg name="head" type="object" interface="zwlr_output_head_v1"
summary="the head to be disabled"/>
</request>
<request name="apply">
<description summary="apply the configuration">
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.
</description>
</request>
<request name="test">
<description summary="test the configuration">
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.
</description>
</request>
<event name="succeeded">
<description summary="configuration changes succeeded">
Sent after the compositor has successfully applied the changes or
tested them.
Upon receiving this event, the client should destroy this object.
</description>
</event>
<event name="failed">
<description summary="configuration changes failed">
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.
</description>
</event>
<event name="cancelled">
<description summary="configuration has been cancelled">
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.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the output configuration">
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.
</description>
</request>
</interface>
<interface name="zwlr_output_configuration_head_v1" version="1">
<description summary="head configuration">
This object is used by the client to update a single head's configuration.
</description>
<enum name="error">
<entry name="invalid_mode" value="1" summary="mode doesn't belong to head"/>
<entry name="invalid_transform" value="2" summary="transform value outside enum"/>
<entry name="invalid_scale" value="3" summary="scale negative or zero"/>
</enum>
<request name="set_mode">
<description summary="set the mode">
This request sets the head's mode.
</description>
<arg name="mode" type="object" interface="zwlr_output_mode_v1"/>
</request>
<request name="set_position">
<description summary="set the position">
This request sets the head's position in the global compositor space.
</description>
<arg name="x" type="int" summary="x position in the global compositor space"/>
<arg name="y" type="int" summary="y position in the global compositor space"/>
</request>
<request name="set_transform">
<description summary="set the transform">
This request sets the head's transform.
</description>
<arg name="transform" type="int" enum="wl_output.transform"/>
</request>
<request name="set_scale">
<description summary="set the scale">
This request sets the head's scale.
</description>
<arg name="scale" type="fixed"/>
</request>
</interface>
</protocol>

View file

@ -46,6 +46,7 @@ lib_wlr_types = static_library(
'wlr_matrix.c',
'wlr_output_damage.c',
'wlr_output_layout.c',
'wlr_output_management_v1.c',
'wlr_output.c',
'wlr_pointer_constraints_v1.c',
'wlr_pointer_gestures_v1.c',

View file

@ -0,0 +1,415 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <wlr/types/wlr_output_management_v1.h>
#include "util/signal.h"
#include "wlr-output-management-unstable-v1-protocol.h"
#define OUTPUT_MANAGER_VERSION 1
enum {
HEAD_STATE_ENABLED = 1 << 0,
// TODO: other properties
};
static const uint32_t HEAD_STATE_ALL = HEAD_STATE_ENABLED;
// Can return NULL if the head is inert
static struct wlr_output_configuration_head_v1 *
config_head_from_head_resource(struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&zwlr_output_head_v1_interface, NULL));
return wl_resource_get_user_data(resource);
}
static void config_head_destroy(struct wlr_output_configuration_head_v1 *head) {
if (head == NULL) {
return;
}
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &head->resources) {
zwlr_output_head_v1_send_finished(resource);
wl_resource_set_user_data(resource, NULL); // make the resource inert
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
}
wl_list_remove(&head->link);
wl_list_remove(&head->output_destroy.link);
free(head);
}
static void config_head_handle_output_destroy(struct wl_listener *listener,
void *data) {
struct wlr_output_configuration_head_v1 *head =
wl_container_of(listener, head, output_destroy);
config_head_destroy(head);
}
struct wlr_output_configuration_head_v1 *
wlr_output_configuration_head_v1_create(
struct wlr_output_configuration_v1 *config, struct wlr_output *output) {
struct wlr_output_configuration_head_v1 *head = calloc(1, sizeof(*head));
if (head == NULL) {
return NULL;
}
head->config = config;
head->output = output;
wl_list_init(&head->resources);
wl_list_insert(&config->heads, &head->link);
head->output_destroy.notify = config_head_handle_output_destroy;
wl_signal_add(&output->events.destroy, &head->output_destroy);
return head;
}
static const struct zwlr_output_configuration_head_v1_interface config_head_impl;
static struct wlr_output_configuration_head_v1 *config_head_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&zwlr_output_head_v1_interface, &config_head_impl));
return wl_resource_get_user_data(resource);
}
static const struct zwlr_output_configuration_head_v1_interface config_head_impl = {
0 // TODO
};
static void config_head_handle_resource_destroy(struct wl_resource *resource) {
struct wlr_output_configuration_head_v1 *head =
config_head_from_resource(resource);
config_head_destroy(head);
}
static const struct zwlr_output_configuration_v1_interface config_impl;
static struct wlr_output_configuration_v1 *config_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&zwlr_output_configuration_v1_interface, &config_impl));
return wl_resource_get_user_data(resource);
}
static struct wlr_output_configuration_head_v1 *config_create_head(
struct wlr_output_configuration_v1 *config, struct wlr_output *output) {
struct wlr_output_configuration_head_v1 *head;
wl_list_for_each(head, &config->heads, link) {
if (head->output == output) {
return NULL; // TODO: post already_configured_head error instead
}
}
return wlr_output_configuration_head_v1_create(config, output);
}
static void config_handle_enable_head(struct wl_client *client,
struct wl_resource *config_resource, uint32_t id,
struct wl_resource *head_resource) {
struct wlr_output_configuration_v1 *config =
config_from_resource(config_resource);
// Can be NULL if the head no longer exists
struct wlr_output_configuration_head_v1 *existing_head =
config_head_from_head_resource(head_resource);
struct wlr_output_configuration_head_v1 *config_head = NULL;
if (existing_head != NULL) {
config_head = config_create_head(config, existing_head->output);
if (config_head == NULL) {
wl_resource_post_no_memory(config_resource);
return;
}
}
uint32_t version = wl_resource_get_version(config_resource);
struct wl_resource *resource = wl_resource_create(client,
&zwlr_output_configuration_head_v1_interface, version, id);
if (resource == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &config_head_impl,
config_head, config_head_handle_resource_destroy);
config_head->enabled = true;
// TODO: when the output is destroyed, make this resource inert
}
static void config_handle_disable_head(struct wl_client *client,
struct wl_resource *config_resource,
struct wl_resource *head_resource) {
struct wlr_output_configuration_v1 *config =
config_from_resource(config_resource);
struct wlr_output_configuration_head_v1 *existing_head =
config_head_from_head_resource(head_resource);
if (existing_head == NULL) {
return;
}
struct wlr_output_configuration_head_v1 *config_head =
config_create_head(config, existing_head->output);
if (config_head == NULL) {
wl_resource_post_no_memory(config_resource);
return;
}
config_head->enabled = false;
}
static void config_handle_apply(struct wl_client *client,
struct wl_resource *config_resource) {
//struct wlr_output_configuration_v1 *config =
// config_from_resource(config_resource);
// TODO: post already_used if needed
// TODO
}
static void config_handle_destroy(struct wl_client *client,
struct wl_resource *config_resource) {
wl_resource_destroy(config_resource);
// TODO: destroy head configurations
}
static const struct zwlr_output_configuration_v1_interface config_impl = {
.enable_head = config_handle_enable_head,
.disable_head = config_handle_disable_head,
.apply = config_handle_apply,
// TODO: test
.destroy = config_handle_destroy,
};
struct wlr_output_configuration_v1 *wlr_output_configuration_v1_create(void) {
struct wlr_output_configuration_v1 *config = calloc(1, sizeof(*config));
if (config == NULL) {
return NULL;
}
wl_list_init(&config->heads);
return config;
}
void wlr_output_configuration_v1_destroy(
struct wlr_output_configuration_v1 *config) {
if (config == NULL) {
return;
}
struct wlr_output_configuration_head_v1 *head, *tmp;
wl_list_for_each_safe(head, tmp, &config->heads, link) {
config_head_destroy(head);
}
free(config);
}
static void config_handle_resource_destroy(struct wl_resource *resource) {
struct wlr_output_configuration_v1 *config = config_from_resource(resource);
wlr_output_configuration_v1_destroy(config);
}
static void manager_handle_create_configuration(struct wl_client *client,
struct wl_resource *manager_resource, uint32_t id, uint32_t serial) {
struct wlr_output_configuration_v1 *config =
wlr_output_configuration_v1_create();
config->serial = serial;
uint32_t version = wl_resource_get_version(manager_resource);
struct wl_resource *resource = wl_resource_create(client,
&zwlr_output_configuration_v1_interface, version, id);
if (resource == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &config_impl,
config, config_handle_resource_destroy);
}
static void manager_handle_stop(struct wl_client *client,
struct wl_resource *manager_resource) {
zwlr_output_manager_v1_send_finished(manager_resource);
wl_resource_destroy(manager_resource);
}
static const struct zwlr_output_manager_v1_interface manager_impl = {
.create_configuration = manager_handle_create_configuration,
.stop = manager_handle_stop,
};
static void manager_handle_resource_destroy(struct wl_resource *resource) {
wl_list_remove(wl_resource_get_link(resource));
}
static void manager_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id) {
struct wlr_output_manager_v1 *manager = data;
struct wl_resource *resource = wl_resource_create(client,
&zwlr_output_manager_v1_interface, version, id);
if (resource == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &manager_impl, manager,
manager_handle_resource_destroy);
wl_list_insert(&manager->resources, wl_resource_get_link(resource));
}
static void manager_handle_display_destroy(struct wl_listener *listener,
void *data) {
struct wlr_output_manager_v1 *manager =
wl_container_of(listener, manager, display_destroy);
wlr_signal_emit_safe(&manager->events.destroy, manager);
wl_list_remove(&manager->display_destroy.link);
wlr_output_configuration_v1_destroy(manager->current);
wl_global_destroy(manager->global);
free(manager);
}
struct wlr_output_manager_v1 *wlr_output_manager_v1_create(
struct wl_display *display) {
struct wlr_output_manager_v1 *manager = calloc(1, sizeof(*manager));
if (manager == NULL) {
return NULL;
}
manager->display = display;
wl_signal_init(&manager->events.destroy);
manager->global = wl_global_create(display,
&zwlr_output_manager_v1_interface, OUTPUT_MANAGER_VERSION,
manager, manager_bind);
if (manager->global == NULL) {
free(manager);
return NULL;
}
manager->display_destroy.notify = manager_handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->display_destroy);
return manager;
}
static struct wlr_output_configuration_head_v1 *configuration_get_head(
struct wlr_output_configuration_v1 *config, struct wlr_output *output) {
struct wlr_output_configuration_head_v1 *head;
wl_list_for_each(head, &config->heads, link) {
if (head->output == output) {
return head;
}
}
return NULL;
}
static void head_send_state(struct wlr_output_configuration_head_v1 *head,
struct wl_resource *resource, uint32_t state) {
if (state & HEAD_STATE_ENABLED) {
zwlr_output_head_v1_send_enabled(resource, head->enabled);
}
if (!head->enabled) {
return;
}
// TODO: send other properties
}
static void head_handle_resource_destroy(struct wl_resource *resource) {
wl_list_remove(wl_resource_get_link(resource));
}
static void manager_send_head(struct wlr_output_manager_v1 *manager,
struct wlr_output_configuration_head_v1 *head,
struct wl_resource *manager_resource) {
struct wlr_output *output = head->output;
struct wl_client *client = wl_resource_get_client(manager_resource);
uint32_t version = wl_resource_get_version(manager_resource);
struct wl_resource *resource = wl_resource_create(client,
&zwlr_output_head_v1_interface, version, 0);
if (resource == NULL) {
wl_resource_post_no_memory(manager_resource);
return;
}
wl_resource_set_implementation(resource, NULL, head,
head_handle_resource_destroy);
wl_list_insert(&head->resources, wl_resource_get_link(resource));
zwlr_output_manager_v1_send_head(manager_resource, resource);
// TODO: send modes
zwlr_output_head_v1_send_name(resource, output->name);
char description[128];
snprintf(description, sizeof(description), "%s %s %s (%s)",
output->make, output->model, output->serial, output->name);
zwlr_output_head_v1_send_description(resource, description);
if (output->phys_width > 0 && output->phys_height > 0) {
zwlr_output_head_v1_send_physical_size(resource,
output->phys_width, output->phys_height);
}
head_send_state(head, resource, HEAD_STATE_ALL);
}
static void manager_update_head(struct wlr_output_manager_v1 *manager,
struct wlr_output_configuration_head_v1 *head,
struct wlr_output_configuration_head_v1 *next) {
uint32_t state = 0;
if (head->enabled != next->enabled) {
state |= HEAD_STATE_ENABLED;
head->enabled = next->enabled;
}
// TODO: update other properties
struct wl_resource *resource;
wl_resource_for_each(resource, &head->resources) {
head_send_state(head, resource, state);
}
}
void wlr_output_manager_v1_set_configuration(
struct wlr_output_manager_v1 *manager,
struct wlr_output_configuration_v1 *config) {
if (manager->current != NULL) {
// Either update or destroy existing heads
struct wlr_output_configuration_head_v1 *existing_head, *tmp;
wl_list_for_each_safe(existing_head, tmp,
&manager->current->heads, link) {
struct wlr_output_configuration_head_v1 *updated_head =
configuration_get_head(config, existing_head->output);
if (updated_head != NULL) {
manager_update_head(manager, existing_head, updated_head);
config_head_destroy(updated_head);
} else {
config_head_destroy(existing_head);
}
}
// Heads remaining in `config` are new heads
} else {
manager->current = wlr_output_configuration_v1_create();
}
// Move new heads to current config
struct wlr_output_configuration_head_v1 *head, *tmp;
wl_list_for_each_safe(head, tmp, &config->heads, link) {
head->config = manager->current;
wl_list_remove(&head->link);
wl_list_insert(&manager->current->heads, &head->link);
struct wl_resource *manager_resource;
wl_resource_for_each(manager_resource, &manager->resources) {
manager_send_head(manager, head, manager_resource);
}
}
manager->current->serial = wl_display_next_serial(manager->display);
struct wl_resource *manager_resource;
wl_resource_for_each(manager_resource, &manager->resources) {
zwlr_output_manager_v1_send_done(manager_resource,
manager->current->serial);
}
}