Introduce wlr_drm_lease_v1
This commit is contained in:
parent
1b96ba8232
commit
d84eb9e9f5
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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_DRM_LEASE_V1_H
|
||||
#define WLR_TYPES_WLR_DRM_LEASE_V1_H
|
||||
|
||||
#include <wayland-server.h>
|
||||
#include <wlr/backend.h>
|
||||
|
||||
struct wlr_drm_backend;
|
||||
|
||||
struct wlr_drm_lease_manager_v1 {
|
||||
struct wl_list resources; // wl_resource_get_link
|
||||
struct wl_global *global;
|
||||
struct wlr_drm_backend *backend;
|
||||
|
||||
struct wl_list connectors; // wlr_drm_lease_connector_v1::link
|
||||
struct wl_list leases; // wl_resource_get_link
|
||||
struct wl_list lease_requests; // wl_resource_get_link
|
||||
|
||||
struct {
|
||||
/**
|
||||
* Upon receiving this signal, call
|
||||
* wlr_drm_lease_manager_v1_grant_lease_request to grant a lease of the
|
||||
* requested DRM resources, or
|
||||
* wlr_drm_lease_manager_v1_reject_lease_request to reject the request.
|
||||
*/
|
||||
struct wl_signal lease_requested; // wlr_drm_lease_request_v1
|
||||
} events;
|
||||
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct wlr_drm_connector;
|
||||
struct wlr_drm_lease_v1;
|
||||
|
||||
/** Represents a connector which is available for leasing, and may be leased */
|
||||
struct wlr_drm_lease_connector_v1 {
|
||||
struct wlr_output *output;
|
||||
struct wlr_drm_connector *drm_connector;
|
||||
struct wl_list resources; // wl_resource_get_link
|
||||
|
||||
/** NULL if no client is currently leasing this connector */
|
||||
struct wlr_drm_lease_v1 *active_lease;
|
||||
|
||||
struct wl_list link; // wlr_drm_lease_manager_v1::connectors
|
||||
};
|
||||
|
||||
/** Represents a connector which has been added to a lease or lease request */
|
||||
struct wlr_drm_connector_lease_v1 {
|
||||
struct wlr_drm_lease_manager_v1 *manager;
|
||||
struct wlr_drm_lease_connector_v1 *connector;
|
||||
struct wl_list link; // wlr_drm_lease_request_v1::connectors
|
||||
};
|
||||
|
||||
/** Represents a pending or submitted lease request */
|
||||
struct wlr_drm_lease_request_v1 {
|
||||
struct wlr_drm_lease_manager_v1 *manager;
|
||||
struct wl_resource *resource; // wlr_drm_manager_v1::lease_requests
|
||||
struct wl_list connectors; // wlr_drm_connector_lease_v1::link
|
||||
bool invalid;
|
||||
|
||||
/** NULL until the lease is submitted */
|
||||
struct wlr_drm_lease_v1 *lease;
|
||||
};
|
||||
|
||||
/** Represents an active or previously active lease */
|
||||
struct wlr_drm_lease_v1 {
|
||||
struct wlr_drm_lease_manager_v1 *manager;
|
||||
struct wl_resource *resource; // wlr_drm_manager_v1::leases
|
||||
struct wl_list connectors; // wlr_drm_connector_lease_v1::link
|
||||
uint32_t lessee_id;
|
||||
|
||||
struct {
|
||||
/**
|
||||
* Upon receiving this signal, it is safe to re-use the leased
|
||||
* resources. After the signal is processed, the backend will re-signal
|
||||
* new_output events for each leased output.
|
||||
*
|
||||
* The lifetime of the lease reference after the signal handler returns
|
||||
* is not defined.
|
||||
*/
|
||||
struct wl_signal revoked; // wlr_drm_lease_v1
|
||||
} events;
|
||||
|
||||
void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a DRM lease manager. The backend supplied must be a DRM backend, or a
|
||||
* multi-backend with a single DRM backend within. If the supplied backend is
|
||||
* not suitable, NULL is returned.
|
||||
*/
|
||||
struct wlr_drm_lease_manager_v1 *wlr_drm_lease_manager_v1_create(
|
||||
struct wl_display *display, struct wlr_backend *backend);
|
||||
|
||||
/**
|
||||
* Offers a wlr_output for lease. The offered output must be owned by the DRM
|
||||
* backend associated with this lease manager.
|
||||
*/
|
||||
void wlr_drm_lease_manager_v1_offer_output(
|
||||
struct wlr_drm_lease_manager_v1 *manager, struct wlr_output *output);
|
||||
|
||||
/**
|
||||
* Withdraws a previously offered output for lease. It is a programming error to
|
||||
* call this function when there are any active leases for this output.
|
||||
*/
|
||||
void wlr_drm_lease_manager_v1_widthraw_output(
|
||||
struct wlr_drm_lease_manager_v1 *manager, struct wlr_output *output);
|
||||
|
||||
/**
|
||||
* Grants a client's lease request. The lease manager will then provision the
|
||||
* DRM lease and transfer the file descriptor to the client. After calling this,
|
||||
* each wlr_output leased is destroyed, and will be re-issued through
|
||||
* wlr_backend.events.new_outputs when the lease is revoked.
|
||||
*
|
||||
* This will return NULL without leasing any resources if the lease is invalid;
|
||||
* this can happen for example if two clients request the same resources and an
|
||||
* attempt to grant both leases is made.
|
||||
*/
|
||||
struct wlr_drm_lease_v1 *wlr_drm_lease_manager_v1_grant_lease_request(
|
||||
struct wlr_drm_lease_manager_v1 *manager,
|
||||
struct wlr_drm_lease_request_v1 *request);
|
||||
|
||||
/**
|
||||
* Rejects a client's lease request.
|
||||
*/
|
||||
void wlr_drm_lease_manager_v1_reject_lease_request(
|
||||
struct wlr_drm_lease_manager_v1 *manager,
|
||||
struct wlr_drm_lease_request_v1 *request);
|
||||
|
||||
/**
|
||||
* Revokes a client's lease request. You may resume use of any of the outputs
|
||||
* leased after making this call.
|
||||
*/
|
||||
void wlr_drm_lease_manager_v1_revoke_lease(
|
||||
struct wlr_drm_lease_manager_v1 *manager,
|
||||
struct wlr_drm_lease_v1 *lease);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,246 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="drm_lease_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2018 NXP
|
||||
Copyright © 2019 Status Research & Development GmbH.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<interface name="zwp_drm_lease_manager_v1" version="1">
|
||||
<description summary="lease manager">
|
||||
This protocol is used by Wayland compositors which act as Direct
|
||||
Renderering Manager (DRM) masters to lease DRM resources to Wayland
|
||||
clients. Once leased, the compositor will not use the leased resources
|
||||
until the lease is revoked or the client closes the file descriptor.
|
||||
|
||||
The lease manager is used to advertise connectors which are available for
|
||||
leasing, and by the client to negotiate a lease request.
|
||||
|
||||
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>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="stopped_manager" value="0"
|
||||
summary="request sent to a manager which has been stopped"/>
|
||||
</enum>
|
||||
|
||||
<request name="create_lease_request">
|
||||
<description summary="create a lease request object">
|
||||
Creates a lease request object.
|
||||
|
||||
See the documentation for zwp_drm_lease_request_v1 for details.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwp_drm_lease_request_v1" />
|
||||
</request>
|
||||
|
||||
<request name="stop">
|
||||
<description summary="stop sending events">
|
||||
Indicates the client no longer wishes to receive connector events. The
|
||||
compositor may still send connector events until it sends the finish
|
||||
event, however.
|
||||
|
||||
The client must not send any requests after this one.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="connector">
|
||||
<description summary="advertise connectors available for leases">
|
||||
The compositor may choose to advertise 0 or more connectors which may be
|
||||
leased to clients, and will use this event to do so. This object may be
|
||||
passed into a lease request to lease that connector. See
|
||||
zwp_drm_lease_request_v1.add_connector for details.
|
||||
|
||||
When this global is bound, the compositor will send all connectors
|
||||
available for lease, but may send additional connectors at any time.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwp_drm_lease_connector_v1" />
|
||||
</event>
|
||||
|
||||
<event name="finished">
|
||||
<description summary="the compositor has finished using the manager">
|
||||
This event indicates that the compositor is done sending connector
|
||||
events. The compositor will destroy this object immediately after
|
||||
sending this event, and it will become invalid. The client should
|
||||
release any resources associated with this manager after receiving this
|
||||
event.
|
||||
</description>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zwp_drm_lease_connector_v1" version="1">
|
||||
<description summary="a leasable DRM connector">
|
||||
Represents a DRM connector which is available for lease. These objects are
|
||||
created via zwp_drm_lease_manager_v1.connector, and should be passed into
|
||||
lease requests via zwp_drm_lease_request_v1.add_connector.
|
||||
</description>
|
||||
|
||||
<event name="name">
|
||||
<description summary="name">
|
||||
The compositor sends this event once the connector is created to
|
||||
indicate the name of this connector. This will not change for the
|
||||
duration of the Wayland session, but is not guaranteed to be consistent
|
||||
between sessions.
|
||||
|
||||
If the compositor also supports zxdg_output_manager_v1 and this
|
||||
connector corresponds to a zxdg_output_v1, this name will match the
|
||||
name of this zxdg_output_v1 object.
|
||||
</description>
|
||||
<arg name="name" type="string" summary="connector name" />
|
||||
</event>
|
||||
|
||||
<event name="description">
|
||||
<description summary="description">
|
||||
The compositor sends this event once the connector is created to provide
|
||||
a human-readable description for this connector, which may be presented
|
||||
to the user.
|
||||
</description>
|
||||
<arg name="description" type="string" summary="connector description" />
|
||||
</event>
|
||||
|
||||
<event name="connector_id">
|
||||
<description summary="connector_id">
|
||||
The compositor will send this event to indicate the DRM ID which
|
||||
represents the underlying connector which is being offered. Note that
|
||||
the final lease may include additional object IDs, such as CRTCs and
|
||||
planes.
|
||||
</description>
|
||||
<arg name="connector_id" type="int" summary="DRM Connector ID" />
|
||||
</event>
|
||||
|
||||
<event name="edid">
|
||||
<description summary="edid">
|
||||
The compositor may send this event once the connector is created to
|
||||
provide a file descriptor which may be memory-mapped to read the
|
||||
connector's EDID, to assist in selecting the correct connectors
|
||||
for lease. The fd must be mapped with MAP_PRIVATE by the recipient.
|
||||
|
||||
Note that not all displays have an EDID, and this event will not be
|
||||
sent in such cases.
|
||||
</description>
|
||||
<arg name="edid" type="fd" summary="EDID file descriptor" />
|
||||
<arg name="size" type="uint" summary="EDID size, in bytes"/>
|
||||
</event>
|
||||
|
||||
<event name="withdrawn">
|
||||
<description summary="lease offer withdrawn">
|
||||
Sent to indicate that the compositor will no longer honor requests for
|
||||
DRM leases which include this connector. The client may still issue a
|
||||
lease request including this connector, but the compositor will send
|
||||
zwp_drm_lease_v1.finished without issuing a lease fd.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy connector">
|
||||
The client may send this request to indicate that it will not issue a
|
||||
lease request for this connector. Clients are encouraged to send this
|
||||
after receiving the "withdrawn" request so that the server can release
|
||||
the resources associated with this connector offer.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwp_drm_lease_request_v1" version="1">
|
||||
<description summary="DRM lease request">
|
||||
A client that wishes to lease DRM resources will attach the list of
|
||||
connectors advertised with zwp_drm_lease_manager_v1.connector that they
|
||||
wish to lease, then use zwp_drm_lease_request_v1.submit to submit the
|
||||
request.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="submitted_lease" value="0"
|
||||
summary="attempted to reuse a submitted lease"/>
|
||||
</enum>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroys the lease request object">
|
||||
Indicates that the client will no longer use this lease request.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="request_connector">
|
||||
<description summary="request a connector for this lease">
|
||||
Indicates that the client would like to lease the given connector.
|
||||
This is only used as a suggestion, the compositor may choose to
|
||||
include any resources in the lease it issues, or change the set of
|
||||
leased resources at any time.
|
||||
</description>
|
||||
<arg name="connector" type="object"
|
||||
interface="zwp_drm_lease_connector_v1" />
|
||||
</request>
|
||||
|
||||
<request name="submit">
|
||||
<description summary="submit the lease request">
|
||||
Submits the lease request and creates a new zwp_drm_lease_v1 object.
|
||||
After calling submit, issuing any other request than destroy is a
|
||||
protocol error.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwp_drm_lease_v1" />
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwp_drm_lease_v1" version="1">
|
||||
<description summary="a DRM lease">
|
||||
A DRM lease object is used to transfer the DRM file descriptor to the
|
||||
client and manage the lifetime of the lease.
|
||||
</description>
|
||||
|
||||
<event name="lease_fd">
|
||||
<description summary="shares the DRM file descriptor">
|
||||
This event returns a file descriptor suitable for use with DRM-related
|
||||
ioctls. The client should use drmModeGetLease to enumerate the DRM
|
||||
objects which have been leased to them. If the compositor cannot or
|
||||
will not grant a lease for the requested connectors, it will not send
|
||||
this event, instead sending the finished event immediately.
|
||||
|
||||
It is a protocol error for the compositor to send this event more than
|
||||
once for a given lease.
|
||||
</description>
|
||||
<arg name="leased_fd" type="fd" summary="leased DRM file descriptor" />
|
||||
</event>
|
||||
|
||||
<event name="finished">
|
||||
<description summary="sent when the lease has been revoked">
|
||||
When the compositor revokes the lease, it will issue this event to
|
||||
notify clients of the change. If the client requires a new lease, they
|
||||
should destroy this object and submit a new lease request. The
|
||||
compositor will send no further events for this object after sending
|
||||
the finish event.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroys the lease object">
|
||||
The client should send this to indicate that it no longer wishes to use
|
||||
this lease. The compositor should use drmModeRevokeLease on the
|
||||
appropriate file descriptor, if necessary, then release this object.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
|
@ -25,6 +25,7 @@ protocols = [
|
|||
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
|
||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
|
||||
'drm-lease-unstable-v1.xml',
|
||||
'gtk-primary-selection.xml',
|
||||
'idle.xml',
|
||||
'input-method-unstable-v2.xml',
|
||||
|
|
|
@ -28,6 +28,7 @@ lib_wlr_types = static_library(
|
|||
'wlr_compositor.c',
|
||||
'wlr_cursor.c',
|
||||
'wlr_data_control_v1.c',
|
||||
'wlr_drm_lease_v1.c',
|
||||
'wlr_export_dmabuf_v1.c',
|
||||
'wlr_foreign_toplevel_management_v1.c',
|
||||
'wlr_fullscreen_shell_v1.c',
|
||||
|
|
|
@ -0,0 +1,582 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <wlr/backend/drm.h>
|
||||
#include <wlr/backend/multi.h>
|
||||
#include <wlr/types/wlr_surface.h>
|
||||
#include <wlr/types/wlr_drm_lease_v1.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <wayland-util.h>
|
||||
#include <wayland-server.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include "backend/drm/drm.h"
|
||||
#include "drm-lease-unstable-v1-protocol.h"
|
||||
#include "util/shm.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
static struct zwp_drm_lease_manager_v1_interface lease_manager_impl;
|
||||
static struct zwp_drm_lease_request_v1_interface lease_request_impl;
|
||||
static struct zwp_drm_lease_connector_v1_interface lease_connector_impl;
|
||||
static struct zwp_drm_lease_v1_interface lease_impl;
|
||||
|
||||
static void drm_lease_connector_v1_send_to_client(
|
||||
struct wlr_drm_lease_connector_v1 *connector,
|
||||
struct wl_client *wl_client, struct wl_resource *manager);
|
||||
|
||||
static struct wlr_drm_lease_manager_v1 *wlr_drm_lease_manager_v1_from_resource(
|
||||
struct wl_resource *resource) {
|
||||
assert(wl_resource_instance_of(resource,
|
||||
&zwp_drm_lease_manager_v1_interface, &lease_manager_impl));
|
||||
return wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
static struct wlr_drm_lease_request_v1 *wlr_drm_lease_request_v1_from_resource(
|
||||
struct wl_resource *resource) {
|
||||
assert(wl_resource_instance_of(resource,
|
||||
&zwp_drm_lease_request_v1_interface, &lease_request_impl));
|
||||
return wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
static struct wlr_drm_lease_connector_v1 *
|
||||
wlr_drm_lease_connector_v1_from_resource(struct wl_resource *resource) {
|
||||
assert(wl_resource_instance_of(resource,
|
||||
&zwp_drm_lease_connector_v1_interface, &lease_connector_impl));
|
||||
return wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
static struct wlr_drm_lease_v1 *wlr_drm_lease_v1_from_resource(
|
||||
struct wl_resource *resource) {
|
||||
assert(wl_resource_instance_of(resource,
|
||||
&zwp_drm_lease_v1_interface, &lease_impl));
|
||||
return wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
static bool drm_lease_request_v1_validate(
|
||||
struct wlr_drm_lease_request_v1 *req) {
|
||||
if (req->invalid) {
|
||||
return false;
|
||||
}
|
||||
/* Don't lease connectors which are already leased */
|
||||
struct wlr_drm_connector_lease_v1 *connector;
|
||||
wl_list_for_each(connector, &req->connectors, link) {
|
||||
if (connector->connector->active_lease) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lease_terminated_by_drm(
|
||||
struct wlr_drm_connector *conn, void *data) {
|
||||
wlr_log(WLR_DEBUG, "Lease terminated by DRM");
|
||||
struct wlr_drm_lease_v1 *lease = data;
|
||||
lease->lessee_id = 0;
|
||||
wlr_drm_lease_manager_v1_revoke_lease(lease->manager, lease);
|
||||
}
|
||||
|
||||
struct wlr_drm_lease_v1 *wlr_drm_lease_manager_v1_grant_lease_request(
|
||||
struct wlr_drm_lease_manager_v1 *manager,
|
||||
struct wlr_drm_lease_request_v1 *request) {
|
||||
assert(manager && request);
|
||||
assert(request->lease);
|
||||
|
||||
struct wlr_drm_lease_v1 *lease = request->lease;
|
||||
if (!drm_lease_request_v1_validate(request)) {
|
||||
zwp_drm_lease_v1_send_finished(lease->resource);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nconns = 0;
|
||||
|
||||
/** Adopt the connector leases from the lease request */
|
||||
struct wlr_drm_connector_lease_v1 *conn, *temp;
|
||||
wl_list_for_each_safe(conn, temp, &request->connectors, link) {
|
||||
wl_list_remove(&conn->link);
|
||||
wl_list_init(&conn->link);
|
||||
wl_list_insert(&lease->connectors, &conn->link);
|
||||
++nconns;
|
||||
}
|
||||
|
||||
struct wlr_drm_connector *conns[nconns + 1];
|
||||
int i = 0;
|
||||
wl_list_for_each(conn, &lease->connectors, link) {
|
||||
conns[i] = conn->connector->drm_connector;
|
||||
++i;
|
||||
}
|
||||
|
||||
int fd = drm_create_lease(manager->backend,
|
||||
conns, nconns, &lease->lessee_id,
|
||||
lease_terminated_by_drm, lease);
|
||||
|
||||
if (fd < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "drm_create_lease failed");
|
||||
zwp_drm_lease_v1_send_finished(lease->resource);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wl_list_for_each(conn, &lease->connectors, link) {
|
||||
struct wlr_drm_lease_connector_v1 *conn_lease =
|
||||
conn->connector;
|
||||
conn_lease->active_lease = lease;
|
||||
|
||||
struct wl_resource *wl_resource, *temp;
|
||||
wl_resource_for_each_safe(wl_resource, temp, &conn_lease->resources) {
|
||||
zwp_drm_lease_connector_v1_send_withdrawn(wl_resource);
|
||||
wl_resource_set_user_data(wl_resource, NULL);
|
||||
wl_list_remove(wl_resource_get_link(wl_resource));
|
||||
wl_list_init(wl_resource_get_link(wl_resource));
|
||||
}
|
||||
}
|
||||
|
||||
zwp_drm_lease_v1_send_lease_fd(lease->resource, fd);
|
||||
close(fd);
|
||||
return lease;
|
||||
}
|
||||
|
||||
void wlr_drm_lease_manager_v1_reject_lease_request(
|
||||
struct wlr_drm_lease_manager_v1 *manager,
|
||||
struct wlr_drm_lease_request_v1 *request) {
|
||||
assert(manager && request);
|
||||
assert(request->lease);
|
||||
zwp_drm_lease_v1_send_finished(request->lease->resource);
|
||||
request->invalid = true;
|
||||
}
|
||||
|
||||
void wlr_drm_lease_manager_v1_revoke_lease(
|
||||
struct wlr_drm_lease_manager_v1 *manager,
|
||||
struct wlr_drm_lease_v1 *lease) {
|
||||
assert(manager && lease);
|
||||
if (lease->resource != NULL) {
|
||||
zwp_drm_lease_v1_send_finished(lease->resource);
|
||||
}
|
||||
if (lease->lessee_id != 0) {
|
||||
if (drm_terminate_lease(manager->backend, lease->lessee_id) < 0) {
|
||||
wlr_log_errno(WLR_DEBUG, "drm_terminate_lease");
|
||||
}
|
||||
}
|
||||
struct wlr_drm_connector_lease_v1 *conn;
|
||||
wl_list_for_each(conn, &lease->connectors, link) {
|
||||
struct wlr_drm_lease_connector_v1 *conn_lease =
|
||||
conn->connector;
|
||||
conn_lease->active_lease = NULL;
|
||||
|
||||
struct wl_resource *wl_resource;
|
||||
wl_resource_for_each(wl_resource, &manager->resources) {
|
||||
struct wl_client *wl_client = wl_resource_get_client(wl_resource);
|
||||
drm_lease_connector_v1_send_to_client(
|
||||
conn_lease, wl_client, wl_resource);
|
||||
}
|
||||
}
|
||||
wlr_signal_emit_safe(&lease->events.revoked, lease);
|
||||
}
|
||||
|
||||
static void drm_lease_v1_destroy(struct wlr_drm_lease_v1 *lease) {
|
||||
wlr_drm_lease_manager_v1_revoke_lease(lease->manager, lease);
|
||||
free(lease);
|
||||
}
|
||||
|
||||
static void drm_lease_v1_handle_resource_destroy(struct wl_resource *resource) {
|
||||
struct wlr_drm_lease_v1 *lease = wlr_drm_lease_v1_from_resource(resource);
|
||||
wl_list_remove(wl_resource_get_link(resource));
|
||||
wl_list_init(wl_resource_get_link(resource));
|
||||
lease->resource = NULL;
|
||||
drm_lease_v1_destroy(lease);
|
||||
}
|
||||
|
||||
static void drm_lease_v1_handle_destroy(
|
||||
struct wl_client *client, struct wl_resource *resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static struct zwp_drm_lease_v1_interface lease_impl = {
|
||||
.destroy = drm_lease_v1_handle_destroy,
|
||||
};
|
||||
|
||||
static void drm_lease_request_v1_destroy(struct wlr_drm_lease_request_v1 *req) {
|
||||
if (!req) {
|
||||
return;
|
||||
}
|
||||
struct wlr_drm_connector_lease_v1 *conn, *temp;
|
||||
wl_list_for_each_safe(conn, temp, &req->connectors, link) {
|
||||
wl_list_remove(&conn->link);
|
||||
wl_list_init(&conn->link);
|
||||
free(conn);
|
||||
}
|
||||
free(req);
|
||||
}
|
||||
|
||||
static void drm_lease_request_v1_handle_resource_destroy(
|
||||
struct wl_resource *resource) {
|
||||
struct wlr_drm_lease_request_v1 *req =
|
||||
wlr_drm_lease_request_v1_from_resource(resource);
|
||||
drm_lease_request_v1_destroy(req);
|
||||
wl_list_remove(wl_resource_get_link(resource));
|
||||
wl_list_init(wl_resource_get_link(resource));
|
||||
}
|
||||
|
||||
static void drm_lease_request_v1_handle_destroy(
|
||||
struct wl_client *client, struct wl_resource *resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static void drm_lease_request_v1_handle_request_connector(
|
||||
struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *connector) {
|
||||
struct wlr_drm_lease_request_v1 *request =
|
||||
wlr_drm_lease_request_v1_from_resource(resource);
|
||||
struct wlr_drm_lease_connector_v1 *conn =
|
||||
wlr_drm_lease_connector_v1_from_resource(connector);
|
||||
|
||||
if (conn == NULL) {
|
||||
/* This connector offer has been withdrawn */
|
||||
request->invalid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_drm_connector_lease_v1 *lease =
|
||||
calloc(1, sizeof(struct wlr_drm_connector_lease_v1));
|
||||
if (!lease) {
|
||||
wl_client_post_no_memory(client);
|
||||
return;
|
||||
}
|
||||
|
||||
lease->connector = conn;
|
||||
wl_list_insert(&request->connectors, &lease->link);
|
||||
}
|
||||
|
||||
static void drm_lease_request_v1_handle_submit(
|
||||
struct wl_client *client, struct wl_resource *resource, uint32_t id) {
|
||||
struct wlr_drm_lease_request_v1 *lease_request =
|
||||
wlr_drm_lease_request_v1_from_resource(resource);
|
||||
|
||||
struct wlr_drm_lease_v1 *lease = calloc(1, sizeof(struct wlr_drm_lease_v1));
|
||||
if (!lease) {
|
||||
wl_resource_post_no_memory(resource);
|
||||
return;
|
||||
}
|
||||
|
||||
struct wl_resource *wl_resource = wl_resource_create(
|
||||
client, &zwp_drm_lease_v1_interface, 1, id);
|
||||
if (!wl_resource) {
|
||||
free(lease);
|
||||
wl_resource_post_no_memory(resource);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_signal_init(&lease->events.revoked);
|
||||
wl_list_init(&lease->connectors);
|
||||
lease->manager = lease_request->manager;
|
||||
lease->resource = wl_resource;
|
||||
lease_request->lease = lease;
|
||||
wl_list_insert(&lease->manager->leases, wl_resource_get_link(wl_resource));
|
||||
|
||||
wl_resource_set_implementation(wl_resource, &lease_impl,
|
||||
lease, drm_lease_v1_handle_resource_destroy);
|
||||
|
||||
if (!drm_lease_request_v1_validate(lease_request)) {
|
||||
/* Pre-emptively reject invalid lease requests */
|
||||
zwp_drm_lease_v1_send_finished(lease->resource);
|
||||
} else {
|
||||
wlr_signal_emit_safe(
|
||||
&lease_request->manager->events.lease_requested,
|
||||
lease_request);
|
||||
}
|
||||
}
|
||||
|
||||
static struct zwp_drm_lease_request_v1_interface lease_request_impl = {
|
||||
.destroy = drm_lease_request_v1_handle_destroy,
|
||||
.request_connector = drm_lease_request_v1_handle_request_connector,
|
||||
.submit = drm_lease_request_v1_handle_submit,
|
||||
};
|
||||
|
||||
static void drm_lease_manager_v1_validate_destroy(
|
||||
struct wlr_drm_lease_manager_v1 *manager, struct wl_client *client) {
|
||||
// TODO: send protocol error if there are any bound resources
|
||||
}
|
||||
|
||||
static void drm_lease_manager_v1_handle_resource_destroy(
|
||||
struct wl_resource *resource) {
|
||||
drm_lease_manager_v1_validate_destroy(
|
||||
wlr_drm_lease_manager_v1_from_resource(resource),
|
||||
wl_resource_get_client(resource));
|
||||
wl_list_remove(wl_resource_get_link(resource));
|
||||
wl_list_init(wl_resource_get_link(resource));
|
||||
}
|
||||
|
||||
static void drm_lease_manager_v1_handle_stop(
|
||||
struct wl_client *client, struct wl_resource *resource) {
|
||||
zwp_drm_lease_manager_v1_send_finished(resource);
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
void drm_lease_manager_v1_handle_create_lease_request(
|
||||
struct wl_client *client, struct wl_resource *resource, uint32_t id) {
|
||||
struct wlr_drm_lease_manager_v1 *manager =
|
||||
wlr_drm_lease_manager_v1_from_resource(resource);
|
||||
|
||||
struct wlr_drm_lease_request_v1 *req =
|
||||
calloc(1, sizeof(struct wlr_drm_lease_request_v1));
|
||||
if (!req) {
|
||||
wl_resource_post_no_memory(resource);
|
||||
return;
|
||||
}
|
||||
|
||||
struct wl_resource *wl_resource = wl_resource_create(client,
|
||||
&zwp_drm_lease_request_v1_interface, 1, id);
|
||||
if (!wl_resource) {
|
||||
wl_resource_post_no_memory(resource);
|
||||
free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
req->manager = manager;
|
||||
req->resource = wl_resource;
|
||||
wl_list_init(&req->connectors);
|
||||
|
||||
wl_resource_set_implementation(wl_resource, &lease_request_impl,
|
||||
req, drm_lease_request_v1_handle_resource_destroy);
|
||||
|
||||
wl_list_insert(&manager->lease_requests, wl_resource_get_link(wl_resource));
|
||||
}
|
||||
|
||||
static struct zwp_drm_lease_manager_v1_interface lease_manager_impl = {
|
||||
.stop = drm_lease_manager_v1_handle_stop,
|
||||
.create_lease_request = drm_lease_manager_v1_handle_create_lease_request,
|
||||
};
|
||||
|
||||
static void drm_connector_v1_handle_resource_destroy(
|
||||
struct wl_resource *resource) {
|
||||
wl_list_remove(wl_resource_get_link(resource));
|
||||
wl_list_init(wl_resource_get_link(resource));
|
||||
}
|
||||
|
||||
static void drm_connector_v1_handle_destroy(
|
||||
struct wl_client *client, struct wl_resource *resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static struct zwp_drm_lease_connector_v1_interface lease_connector_impl = {
|
||||
.destroy = drm_connector_v1_handle_destroy,
|
||||
};
|
||||
|
||||
static void drm_lease_connector_v1_send_to_client(
|
||||
struct wlr_drm_lease_connector_v1 *connector,
|
||||
struct wl_client *wl_client, struct wl_resource *manager) {
|
||||
if (connector->active_lease) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wl_resource *wl_resource = wl_resource_create(wl_client,
|
||||
&zwp_drm_lease_connector_v1_interface, 1, 0);
|
||||
wl_resource_set_implementation(wl_resource, &lease_connector_impl,
|
||||
connector, drm_connector_v1_handle_resource_destroy);
|
||||
zwp_drm_lease_manager_v1_send_connector(manager, wl_resource);
|
||||
|
||||
struct wlr_output *output = connector->output;
|
||||
zwp_drm_lease_connector_v1_send_name(wl_resource, output->name);
|
||||
|
||||
char description[128];
|
||||
snprintf(description, sizeof(description), "%s %s %s (%s)",
|
||||
output->make, output->model, output->serial, output->name);
|
||||
zwp_drm_lease_connector_v1_send_description(wl_resource, description);
|
||||
|
||||
zwp_drm_lease_connector_v1_send_connector_id(
|
||||
wl_resource, connector->drm_connector->id);
|
||||
|
||||
struct wlr_drm_lease_manager_v1 *lease_manager =
|
||||
wlr_drm_lease_manager_v1_from_resource(manager);
|
||||
struct wlr_drm_connector *conn = connector->drm_connector;
|
||||
size_t edid_len = 0;
|
||||
uint8_t *edid = get_drm_prop_blob(lease_manager->backend->fd,
|
||||
conn->id, conn->props.edid, &edid_len);
|
||||
int edid_fd = allocate_shm_file(edid_len);
|
||||
void *ptr = mmap(NULL, edid_len, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, edid_fd, 0);
|
||||
memcpy(ptr, edid, edid_len);
|
||||
munmap(ptr, edid_len);
|
||||
|
||||
zwp_drm_lease_connector_v1_send_edid(wl_resource, edid_fd, edid_len);
|
||||
free(edid);
|
||||
close(edid_fd);
|
||||
|
||||
wl_list_insert(&connector->resources, wl_resource_get_link(wl_resource));
|
||||
}
|
||||
|
||||
static void lease_manager_bind(struct wl_client *wl_client, void *data,
|
||||
uint32_t version, uint32_t id) {
|
||||
struct wlr_drm_lease_manager_v1 *lease_manager = data;
|
||||
|
||||
struct wl_resource *wl_resource = wl_resource_create(wl_client,
|
||||
&zwp_drm_lease_manager_v1_interface, version, id);
|
||||
|
||||
if (!wl_resource) {
|
||||
wl_client_post_no_memory(wl_client);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_list_insert(&lease_manager->resources,
|
||||
wl_resource_get_link(wl_resource));
|
||||
|
||||
wl_resource_set_implementation(wl_resource, &lease_manager_impl,
|
||||
lease_manager, drm_lease_manager_v1_handle_resource_destroy);
|
||||
|
||||
struct wlr_drm_lease_connector_v1 *connector;
|
||||
wl_list_for_each(connector, &lease_manager->connectors, link) {
|
||||
drm_lease_connector_v1_send_to_client(
|
||||
connector, wl_client, wl_resource);
|
||||
}
|
||||
}
|
||||
|
||||
void wlr_drm_lease_manager_v1_offer_output(
|
||||
struct wlr_drm_lease_manager_v1 *manager, struct wlr_output *output) {
|
||||
assert(manager && output);
|
||||
assert(wlr_output_is_drm(output));
|
||||
struct wlr_drm_connector *drm_connector =
|
||||
(struct wlr_drm_connector *)output;
|
||||
/*
|
||||
* When the compositor grants a lease, we "destroy" all of the outputs on
|
||||
* that lease. When the lease ends, the outputs re-appear. However, the
|
||||
* underlying DRM connector remains the same. If the compositor offers
|
||||
* outputs based on some criteria, then sees the output re-appear with the
|
||||
* same critera, this code allows it to safely re-offer outputs which are
|
||||
* backed by DRM connectors it has leased in the past.
|
||||
*/
|
||||
struct wlr_drm_lease_connector_v1 *connector;
|
||||
wl_list_for_each(connector, &manager->connectors, link) {
|
||||
if (connector->drm_connector == drm_connector) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
connector = calloc(1, sizeof(struct wlr_drm_lease_connector_v1));
|
||||
connector->drm_connector = drm_connector;
|
||||
connector->output = &drm_connector->output;
|
||||
wl_list_init(&connector->resources);
|
||||
wl_list_insert(&manager->connectors, &connector->link);
|
||||
|
||||
struct wl_resource *resource;
|
||||
wl_resource_for_each(resource, &manager->resources) {
|
||||
drm_lease_connector_v1_send_to_client(
|
||||
connector, wl_resource_get_client(resource), resource);
|
||||
}
|
||||
}
|
||||
|
||||
void wlr_drm_lease_manager_v1_widthraw_output(
|
||||
struct wlr_drm_lease_manager_v1 *manager, struct wlr_output *output) {
|
||||
struct wlr_drm_lease_connector_v1 *connector = NULL, *_connector;
|
||||
wl_list_for_each(_connector, &manager->connectors, link) {
|
||||
if (_connector->output == output) {
|
||||
connector = _connector;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!connector) {
|
||||
return;
|
||||
}
|
||||
assert(connector->active_lease == NULL && "Cannot withdraw a leased output");
|
||||
|
||||
struct wl_resource *wl_resource, *temp;
|
||||
wl_resource_for_each_safe(wl_resource, temp, &connector->resources) {
|
||||
zwp_drm_lease_connector_v1_send_withdrawn(wl_resource);
|
||||
wl_resource_set_user_data(wl_resource, NULL);
|
||||
wl_list_remove(wl_resource_get_link(wl_resource));
|
||||
wl_list_init(wl_resource_get_link(wl_resource));
|
||||
}
|
||||
|
||||
wl_resource_for_each(wl_resource, &manager->lease_requests) {
|
||||
struct wlr_drm_lease_request_v1 *request =
|
||||
wlr_drm_lease_request_v1_from_resource(wl_resource);
|
||||
request->invalid = true;
|
||||
}
|
||||
|
||||
wl_list_remove(&connector->link);
|
||||
wl_list_init(&connector->link);
|
||||
free(connector);
|
||||
}
|
||||
|
||||
static void multi_backend_cb(struct wlr_backend *backend, void *data) {
|
||||
struct wlr_backend **ptr = data;
|
||||
if (wlr_backend_is_drm(backend)) {
|
||||
*ptr = backend;
|
||||
}
|
||||
}
|
||||
|
||||
struct wlr_drm_lease_manager_v1 *wlr_drm_lease_manager_v1_create(
|
||||
struct wl_display *display, struct wlr_backend *backend) {
|
||||
assert(display);
|
||||
|
||||
if (!wlr_backend_is_drm(backend) && wlr_backend_is_multi(backend)) {
|
||||
wlr_multi_for_each_backend(backend, multi_backend_cb, &backend);
|
||||
if (!wlr_backend_is_drm(backend)) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_drm_lease_manager_v1 *lease_manager =
|
||||
calloc(1, sizeof(struct wlr_drm_lease_manager_v1));
|
||||
|
||||
if (!lease_manager) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lease_manager->backend = get_drm_backend_from_backend(backend);
|
||||
wl_list_init(&lease_manager->resources);
|
||||
wl_list_init(&lease_manager->connectors);
|
||||
wl_list_init(&lease_manager->lease_requests);
|
||||
wl_list_init(&lease_manager->leases);
|
||||
|
||||
wl_signal_init(&lease_manager->events.lease_requested);
|
||||
|
||||
lease_manager->global = wl_global_create(display,
|
||||
&zwp_drm_lease_manager_v1_interface, 1,
|
||||
lease_manager, lease_manager_bind);
|
||||
|
||||
if (!lease_manager->global) {
|
||||
free(lease_manager);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return lease_manager;
|
||||
}
|
||||
|
||||
void wlr_drm_lease_manager_v1_destroy(
|
||||
struct wlr_drm_lease_manager_v1 *manager) {
|
||||
if (!manager) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wl_resource *resource;
|
||||
struct wl_resource *tmp_resource;
|
||||
wl_resource_for_each_safe(resource, tmp_resource, &manager->resources) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
wl_resource_for_each_safe(resource, tmp_resource,
|
||||
&manager->lease_requests) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
wl_resource_for_each_safe(resource, tmp_resource, &manager->leases) {
|
||||
struct wlr_drm_lease_v1 *lease =
|
||||
wlr_drm_lease_v1_from_resource(resource);
|
||||
wlr_drm_lease_manager_v1_revoke_lease(manager, lease);
|
||||
}
|
||||
|
||||
struct wlr_drm_lease_connector_v1 *connector, *tmp_connector;
|
||||
wl_list_for_each_safe(connector, tmp_connector,
|
||||
&manager->connectors, link) {
|
||||
wl_list_remove(&connector->link);
|
||||
wl_list_init(&connector->link);
|
||||
free(connector);
|
||||
}
|
||||
|
||||
free(manager);
|
||||
}
|
Loading…
Reference in New Issue