xwayland/randr/rrlease.c

371 lines
9.4 KiB
C

/*
* Copyright © 2017 Keith Packard
*
* 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.
*/
#include "randrstr.h"
#include "swaprep.h"
#include <unistd.h>
RESTYPE RRLeaseType;
/*
* Notify of some lease change
*/
void
RRDeliverLeaseEvent(ClientPtr client, WindowPtr window)
{
ScreenPtr screen = window->drawable.pScreen;
rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
RRLeasePtr lease;
UpdateCurrentTimeIf();
xorg_list_for_each_entry(lease, &scr_priv->leases, list) {
if (lease->id != None && (lease->state == RRLeaseCreating ||
lease->state == RRLeaseTerminating))
{
xRRLeaseNotifyEvent le = (xRRLeaseNotifyEvent) {
.type = RRNotify + RREventBase,
.subCode = RRNotify_Lease,
.timestamp = currentTime.milliseconds,
.window = window->drawable.id,
.lease = lease->id,
.created = lease->state == RRLeaseCreating,
};
WriteEventsToClient(client, 1, (xEvent *) &le);
}
}
}
/*
* Change the state of a lease and let anyone watching leases know
*/
static void
RRLeaseChangeState(RRLeasePtr lease, RRLeaseState old, RRLeaseState new)
{
ScreenPtr screen = lease->screen;
rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
lease->state = old;
scr_priv->leasesChanged = TRUE;
RRSetChanged(lease->screen);
RRTellChanged(lease->screen);
scr_priv->leasesChanged = FALSE;
lease->state = new;
}
/*
* Allocate and initialize a lease
*/
static RRLeasePtr
RRLeaseAlloc(ScreenPtr screen, RRLease lid, int numCrtcs, int numOutputs)
{
RRLeasePtr lease;
lease = calloc(1,
sizeof(RRLeaseRec) +
numCrtcs * sizeof (RRCrtcPtr) +
numOutputs * sizeof(RROutputPtr));
if (!lease)
return NULL;
lease->screen = screen;
xorg_list_init(&lease->list);
lease->id = lid;
lease->state = RRLeaseCreating;
lease->numCrtcs = numCrtcs;
lease->numOutputs = numOutputs;
lease->crtcs = (RRCrtcPtr *) (lease + 1);
lease->outputs = (RROutputPtr *) (lease->crtcs + numCrtcs);
return lease;
}
/*
* Check if a crtc is leased
*/
Bool
RRCrtcIsLeased(RRCrtcPtr crtc)
{
ScreenPtr screen = crtc->pScreen;
rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
RRLeasePtr lease;
int c;
xorg_list_for_each_entry(lease, &scr_priv->leases, list) {
for (c = 0; c < lease->numCrtcs; c++)
if (lease->crtcs[c] == crtc)
return TRUE;
}
return FALSE;
}
/*
* Check if an output is leased
*/
Bool
RROutputIsLeased(RROutputPtr output)
{
ScreenPtr screen = output->pScreen;
rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
RRLeasePtr lease;
int o;
xorg_list_for_each_entry(lease, &scr_priv->leases, list) {
for (o = 0; o < lease->numOutputs; o++)
if (lease->outputs[o] == output)
return TRUE;
}
return FALSE;
}
/*
* A lease has been terminated.
* The driver is responsible for noticing and
* calling this function when that happens
*/
void
RRLeaseTerminated(RRLeasePtr lease)
{
/* Notify clients with events, but only if this isn't during lease creation */
if (lease->state == RRLeaseRunning)
RRLeaseChangeState(lease, RRLeaseTerminating, RRLeaseTerminating);
if (lease->id != None)
FreeResource(lease->id, RT_NONE);
xorg_list_del(&lease->list);
}
/*
* A lease is completely shut down and is
* ready to be deallocated
*/
void
RRLeaseFree(RRLeasePtr lease)
{
free(lease);
}
/*
* Ask the driver to terminate a lease. The
* driver will call RRLeaseTerminated when that has
* finished, which may be some time after this function returns
* if the driver operation is asynchronous
*/
void
RRTerminateLease(RRLeasePtr lease)
{
ScreenPtr screen = lease->screen;
rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
scr_priv->rrTerminateLease(screen, lease);
}
/*
* Destroy a lease resource ID. All this
* does is note that the lease no longer has an ID, and
* so doesn't appear over the protocol anymore.
*/
static int
RRLeaseDestroyResource(void *value, XID pid)
{
RRLeasePtr lease = value;
lease->id = None;
return 1;
}
/*
* Create the lease resource type during server initialization
*/
Bool
RRLeaseInit(void)
{
RRLeaseType = CreateNewResourceType(RRLeaseDestroyResource, "LEASE");
if (!RRLeaseType)
return FALSE;
return TRUE;
}
int
ProcRRCreateLease(ClientPtr client)
{
REQUEST(xRRCreateLeaseReq);
xRRCreateLeaseReply rep;
WindowPtr window;
ScreenPtr screen;
rrScrPrivPtr scr_priv;
RRLeasePtr lease;
RRCrtc *crtcIds;
RROutput *outputIds;
int fd;
int rc;
unsigned long len;
int c, o;
REQUEST_AT_LEAST_SIZE(xRRCreateLeaseReq);
LEGAL_NEW_RESOURCE(stuff->lid, client);
rc = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess);
if (rc != Success)
return rc;
len = client->req_len - bytes_to_int32(sizeof(xRRCreateLeaseReq));
if (len != stuff->nCrtcs + stuff->nOutputs)
return BadLength;
screen = window->drawable.pScreen;
scr_priv = rrGetScrPriv(screen);
if (!scr_priv)
return BadMatch;
if (!scr_priv->rrCreateLease && !scr_priv->rrRequestLease)
return BadMatch;
if (scr_priv->rrGetLease) {
scr_priv->rrGetLease(client, screen, &lease, &fd);
if (lease) {
if (fd >= 0)
goto leaseReturned;
else
goto bail_lease;
}
}
/* Allocate a structure to hold all of the lease information */
lease = RRLeaseAlloc(screen, stuff->lid, stuff->nCrtcs, stuff->nOutputs);
if (!lease)
return BadAlloc;
/* Look up all of the crtcs */
crtcIds = (RRCrtc *) (stuff + 1);
for (c = 0; c < stuff->nCrtcs; c++) {
RRCrtcPtr crtc;
rc = dixLookupResourceByType((void **)&crtc, crtcIds[c],
RRCrtcType, client, DixSetAttrAccess);
if (rc != Success) {
client->errorValue = crtcIds[c];
goto bail_lease;
}
if (RRCrtcIsLeased(crtc)) {
client->errorValue = crtcIds[c];
rc = BadAccess;
goto bail_lease;
}
lease->crtcs[c] = crtc;
}
/* Look up all of the outputs */
outputIds = (RROutput *) (crtcIds + stuff->nCrtcs);
for (o = 0; o < stuff->nOutputs; o++) {
RROutputPtr output;
rc = dixLookupResourceByType((void **)&output, outputIds[o],
RROutputType, client, DixSetAttrAccess);
if (rc != Success) {
client->errorValue = outputIds[o];
goto bail_lease;
}
if (RROutputIsLeased(output)) {
client->errorValue = outputIds[o];
rc = BadAccess;
goto bail_lease;
}
lease->outputs[o] = output;
}
if (scr_priv->rrRequestLease) {
rc = scr_priv->rrRequestLease(client, screen, lease);
if (rc == Success)
return Success;
else
goto bail_lease;
} else {
rc = scr_priv->rrCreateLease(screen, lease, &fd);
if (rc != Success)
goto bail_lease;
}
leaseReturned:
xorg_list_add(&lease->list, &scr_priv->leases);
if (!AddResource(stuff->lid, RRLeaseType, lease)) {
close(fd);
return BadAlloc;
}
if (WriteFdToClient(client, fd, TRUE) < 0) {
RRTerminateLease(lease);
close(fd);
return BadAlloc;
}
RRLeaseChangeState(lease, RRLeaseCreating, RRLeaseRunning);
rep = (xRRCreateLeaseReply) {
.type = X_Reply,
.nfd = 1,
.sequenceNumber = client->sequence,
.length = 0,
};
if (client->swapped) {
swaps(&rep.sequenceNumber);
swapl(&rep.length);
}
WriteToClient(client, sizeof (rep), &rep);
return Success;
bail_lease:
free(lease);
return rc;
}
int
ProcRRFreeLease(ClientPtr client)
{
REQUEST(xRRFreeLeaseReq);
RRLeasePtr lease;
REQUEST_SIZE_MATCH(xRRFreeLeaseReq);
VERIFY_RR_LEASE(stuff->lid, lease, DixDestroyAccess);
if (stuff->terminate)
RRTerminateLease(lease);
else
/* Get rid of the resource database entry */
FreeResource(stuff->lid, RT_NONE);
return Success;
}