371 lines
9.4 KiB
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;
|
|
}
|