1706f2543Smrg/*
2706f2543Smrg * Copyright © 2006 Keith Packard
3706f2543Smrg *
4706f2543Smrg * Permission to use, copy, modify, distribute, and sell this software and its
5706f2543Smrg * documentation for any purpose is hereby granted without fee, provided that
6706f2543Smrg * the above copyright notice appear in all copies and that both that copyright
7706f2543Smrg * notice and this permission notice appear in supporting documentation, and
8706f2543Smrg * that the name of the copyright holders not be used in advertising or
9706f2543Smrg * publicity pertaining to distribution of the software without specific,
10706f2543Smrg * written prior permission.  The copyright holders make no representations
11706f2543Smrg * about the suitability of this software for any purpose.  It is provided "as
12706f2543Smrg * is" without express or implied warranty.
13706f2543Smrg *
14706f2543Smrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15706f2543Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16706f2543Smrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17706f2543Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18706f2543Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19706f2543Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20706f2543Smrg * OF THIS SOFTWARE.
21706f2543Smrg */
22706f2543Smrg
23706f2543Smrg#include "randrstr.h"
24706f2543Smrg
25706f2543SmrgRESTYPE	RRModeType;
26706f2543Smrg
27706f2543Smrgstatic Bool
28706f2543SmrgRRModeEqual (xRRModeInfo *a, xRRModeInfo *b)
29706f2543Smrg{
30706f2543Smrg    if (a->width != b->width) return FALSE;
31706f2543Smrg    if (a->height != b->height) return FALSE;
32706f2543Smrg    if (a->dotClock != b->dotClock) return FALSE;
33706f2543Smrg    if (a->hSyncStart != b->hSyncStart) return FALSE;
34706f2543Smrg    if (a->hSyncEnd != b->hSyncEnd) return FALSE;
35706f2543Smrg    if (a->hTotal != b->hTotal) return FALSE;
36706f2543Smrg    if (a->hSkew != b->hSkew) return FALSE;
37706f2543Smrg    if (a->vSyncStart != b->vSyncStart) return FALSE;
38706f2543Smrg    if (a->vSyncEnd != b->vSyncEnd) return FALSE;
39706f2543Smrg    if (a->vTotal != b->vTotal) return FALSE;
40706f2543Smrg    if (a->nameLength != b->nameLength) return FALSE;
41706f2543Smrg    if (a->modeFlags != b->modeFlags) return FALSE;
42706f2543Smrg    return TRUE;
43706f2543Smrg}
44706f2543Smrg
45706f2543Smrg/*
46706f2543Smrg * Keep a list so it's easy to find modes in the resource database.
47706f2543Smrg */
48706f2543Smrgstatic int	    num_modes;
49706f2543Smrgstatic RRModePtr    *modes;
50706f2543Smrg
51706f2543Smrgstatic RRModePtr
52706f2543SmrgRRModeCreate (xRRModeInfo   *modeInfo,
53706f2543Smrg	      const char    *name,
54706f2543Smrg	      ScreenPtr	    userScreen)
55706f2543Smrg{
56706f2543Smrg    RRModePtr	mode, *newModes;
57706f2543Smrg
58706f2543Smrg    if (!RRInit ())
59706f2543Smrg	return NULL;
60706f2543Smrg
61706f2543Smrg    mode = malloc(sizeof (RRModeRec) + modeInfo->nameLength + 1);
62706f2543Smrg    if (!mode)
63706f2543Smrg	return NULL;
64706f2543Smrg    mode->refcnt = 1;
65706f2543Smrg    mode->mode = *modeInfo;
66706f2543Smrg    mode->name = (char *) (mode + 1);
67706f2543Smrg    memcpy (mode->name, name, modeInfo->nameLength);
68706f2543Smrg    mode->name[modeInfo->nameLength] = '\0';
69706f2543Smrg    mode->userScreen = userScreen;
70706f2543Smrg
71706f2543Smrg    if (num_modes)
72706f2543Smrg	newModes = realloc(modes, (num_modes + 1) * sizeof (RRModePtr));
73706f2543Smrg    else
74706f2543Smrg	newModes = malloc(sizeof (RRModePtr));
75706f2543Smrg
76706f2543Smrg    if (!newModes)
77706f2543Smrg    {
78706f2543Smrg	free(mode);
79706f2543Smrg	return NULL;
80706f2543Smrg    }
81706f2543Smrg
82706f2543Smrg    mode->mode.id = FakeClientID(0);
83706f2543Smrg    if (!AddResource (mode->mode.id, RRModeType, (pointer) mode))
84706f2543Smrg	return NULL;
85706f2543Smrg    modes = newModes;
86706f2543Smrg    modes[num_modes++] = mode;
87706f2543Smrg
88706f2543Smrg    /*
89706f2543Smrg     * give the caller a reference to this mode
90706f2543Smrg     */
91706f2543Smrg    ++mode->refcnt;
92706f2543Smrg    return mode;
93706f2543Smrg}
94706f2543Smrg
95706f2543Smrgstatic RRModePtr
96706f2543SmrgRRModeFindByName (const char	*name,
97706f2543Smrg		  CARD16    	nameLength)
98706f2543Smrg{
99706f2543Smrg    int		i;
100706f2543Smrg    RRModePtr	mode;
101706f2543Smrg
102706f2543Smrg    for (i = 0; i < num_modes; i++)
103706f2543Smrg    {
104706f2543Smrg	mode = modes[i];
105706f2543Smrg	if (mode->mode.nameLength == nameLength &&
106706f2543Smrg	    !memcmp (name, mode->name, nameLength))
107706f2543Smrg	{
108706f2543Smrg	    return mode;
109706f2543Smrg	}
110706f2543Smrg    }
111706f2543Smrg    return NULL;
112706f2543Smrg}
113706f2543Smrg
114706f2543SmrgRRModePtr
115706f2543SmrgRRModeGet (xRRModeInfo	*modeInfo,
116706f2543Smrg	   const char	*name)
117706f2543Smrg{
118706f2543Smrg    int	i;
119706f2543Smrg
120706f2543Smrg    for (i = 0; i < num_modes; i++)
121706f2543Smrg    {
122706f2543Smrg	RRModePtr   mode = modes[i];
123706f2543Smrg	if (RRModeEqual (&mode->mode, modeInfo) &&
124706f2543Smrg	    !memcmp (name, mode->name, modeInfo->nameLength))
125706f2543Smrg	{
126706f2543Smrg	    ++mode->refcnt;
127706f2543Smrg	    return mode;
128706f2543Smrg	}
129706f2543Smrg    }
130706f2543Smrg
131706f2543Smrg    return RRModeCreate (modeInfo, name, NULL);
132706f2543Smrg}
133706f2543Smrg
134706f2543Smrgstatic RRModePtr
135706f2543SmrgRRModeCreateUser (ScreenPtr	pScreen,
136706f2543Smrg		  xRRModeInfo	*modeInfo,
137706f2543Smrg		  const char	*name,
138706f2543Smrg		  int		*error)
139706f2543Smrg{
140706f2543Smrg    RRModePtr	mode;
141706f2543Smrg
142706f2543Smrg    mode = RRModeFindByName (name, modeInfo->nameLength);
143706f2543Smrg    if (mode)
144706f2543Smrg    {
145706f2543Smrg	*error = BadName;
146706f2543Smrg	return NULL;
147706f2543Smrg    }
148706f2543Smrg
149706f2543Smrg    mode = RRModeCreate (modeInfo, name, pScreen);
150706f2543Smrg    if (!mode)
151706f2543Smrg    {
152706f2543Smrg	*error = BadAlloc;
153706f2543Smrg	return NULL;
154706f2543Smrg    }
155706f2543Smrg    *error = Success;
156706f2543Smrg    return mode;
157706f2543Smrg}
158706f2543Smrg
159706f2543SmrgRRModePtr *
160706f2543SmrgRRModesForScreen (ScreenPtr pScreen, int *num_ret)
161706f2543Smrg{
162706f2543Smrg    rrScrPriv(pScreen);
163706f2543Smrg    int		o, c, m;
164706f2543Smrg    RRModePtr	*screen_modes;
165706f2543Smrg    int		num_screen_modes = 0;
166706f2543Smrg
167706f2543Smrg    screen_modes = malloc((num_modes ? num_modes : 1) * sizeof (RRModePtr));
168706f2543Smrg    if (!screen_modes)
169706f2543Smrg	return NULL;
170706f2543Smrg
171706f2543Smrg    /*
172706f2543Smrg     * Add modes from all outputs
173706f2543Smrg     */
174706f2543Smrg    for (o = 0; o < pScrPriv->numOutputs; o++)
175706f2543Smrg    {
176706f2543Smrg	RROutputPtr	output = pScrPriv->outputs[o];
177706f2543Smrg	int		m, n;
178706f2543Smrg
179706f2543Smrg	for (m = 0; m < output->numModes + output->numUserModes; m++)
180706f2543Smrg	{
181706f2543Smrg	    RRModePtr   mode = (m < output->numModes ?
182706f2543Smrg				output->modes[m] :
183706f2543Smrg				output->userModes[m-output->numModes]);
184706f2543Smrg	    for (n = 0; n < num_screen_modes; n++)
185706f2543Smrg		if (screen_modes[n] == mode)
186706f2543Smrg		    break;
187706f2543Smrg	    if (n == num_screen_modes)
188706f2543Smrg		screen_modes[num_screen_modes++] = mode;
189706f2543Smrg	}
190706f2543Smrg    }
191706f2543Smrg    /*
192706f2543Smrg     * Add modes from all crtcs. The goal is to
193706f2543Smrg     * make sure all available and active modes
194706f2543Smrg     * are visible to the client
195706f2543Smrg     */
196706f2543Smrg    for (c = 0; c < pScrPriv->numCrtcs; c++)
197706f2543Smrg    {
198706f2543Smrg	RRCrtcPtr	crtc = pScrPriv->crtcs[c];
199706f2543Smrg	RRModePtr	mode = crtc->mode;
200706f2543Smrg	int		n;
201706f2543Smrg
202706f2543Smrg	if (!mode) continue;
203706f2543Smrg	for (n = 0; n < num_screen_modes; n++)
204706f2543Smrg	    if (screen_modes[n] == mode)
205706f2543Smrg		break;
206706f2543Smrg	if (n == num_screen_modes)
207706f2543Smrg	    screen_modes[num_screen_modes++] = mode;
208706f2543Smrg    }
209706f2543Smrg    /*
210706f2543Smrg     * Add all user modes for this screen
211706f2543Smrg     */
212706f2543Smrg    for (m = 0; m < num_modes; m++)
213706f2543Smrg    {
214706f2543Smrg	RRModePtr	mode = modes[m];
215706f2543Smrg	int		n;
216706f2543Smrg
217706f2543Smrg	if (mode->userScreen != pScreen)
218706f2543Smrg	    continue;
219706f2543Smrg	for (n = 0; n < num_screen_modes; n++)
220706f2543Smrg	    if (screen_modes[n] == mode)
221706f2543Smrg		break;
222706f2543Smrg	if (n == num_screen_modes)
223706f2543Smrg	    screen_modes[num_screen_modes++] = mode;
224706f2543Smrg    }
225706f2543Smrg
226706f2543Smrg    *num_ret = num_screen_modes;
227706f2543Smrg    return screen_modes;
228706f2543Smrg}
229706f2543Smrg
230706f2543Smrgvoid
231706f2543SmrgRRModeDestroy (RRModePtr mode)
232706f2543Smrg{
233706f2543Smrg    int	m;
234706f2543Smrg
235706f2543Smrg    if (--mode->refcnt > 0)
236706f2543Smrg	return;
237706f2543Smrg    for (m = 0; m < num_modes; m++)
238706f2543Smrg    {
239706f2543Smrg	if (modes[m] == mode)
240706f2543Smrg	{
241706f2543Smrg	    memmove (modes + m, modes + m + 1,
242706f2543Smrg		     (num_modes - m - 1) * sizeof (RRModePtr));
243706f2543Smrg	    num_modes--;
244706f2543Smrg	    if (!num_modes)
245706f2543Smrg	    {
246706f2543Smrg		free(modes);
247706f2543Smrg		modes = NULL;
248706f2543Smrg	    }
249706f2543Smrg	    break;
250706f2543Smrg	}
251706f2543Smrg    }
252706f2543Smrg
253706f2543Smrg    free(mode);
254706f2543Smrg}
255706f2543Smrg
256706f2543Smrgstatic int
257706f2543SmrgRRModeDestroyResource (pointer value, XID pid)
258706f2543Smrg{
259706f2543Smrg    RRModeDestroy ((RRModePtr) value);
260706f2543Smrg    return 1;
261706f2543Smrg}
262706f2543Smrg
263706f2543Smrg/*
264706f2543Smrg * Initialize mode type
265706f2543Smrg */
266706f2543SmrgBool
267706f2543SmrgRRModeInit (void)
268706f2543Smrg{
269706f2543Smrg    assert (num_modes == 0);
270706f2543Smrg    assert (modes == NULL);
271706f2543Smrg    RRModeType = CreateNewResourceType (RRModeDestroyResource, "MODE");
272706f2543Smrg    if (!RRModeType)
273706f2543Smrg	return FALSE;
274706f2543Smrg
275706f2543Smrg    return TRUE;
276706f2543Smrg}
277706f2543Smrg
278706f2543Smrg/*
279706f2543Smrg * Initialize mode type error value
280706f2543Smrg */
281706f2543Smrgvoid
282706f2543SmrgRRModeInitErrorValue(void)
283706f2543Smrg{
284706f2543Smrg    SetResourceTypeErrorValue(RRModeType, RRErrorBase + BadRRMode);
285706f2543Smrg}
286706f2543Smrg
287706f2543Smrgint
288706f2543SmrgProcRRCreateMode (ClientPtr client)
289706f2543Smrg{
290706f2543Smrg    REQUEST(xRRCreateModeReq);
291706f2543Smrg    xRRCreateModeReply	rep = {0};
292706f2543Smrg    WindowPtr		pWin;
293706f2543Smrg    ScreenPtr		pScreen;
294706f2543Smrg    rrScrPrivPtr	pScrPriv;
295706f2543Smrg    xRRModeInfo		*modeInfo;
296706f2543Smrg    long		units_after;
297706f2543Smrg    char		*name;
298706f2543Smrg    int			error, rc;
299706f2543Smrg    RRModePtr		mode;
300706f2543Smrg
301706f2543Smrg    REQUEST_AT_LEAST_SIZE (xRRCreateModeReq);
302706f2543Smrg    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
303706f2543Smrg    if (rc != Success)
304706f2543Smrg	return rc;
305706f2543Smrg
306706f2543Smrg    pScreen = pWin->drawable.pScreen;
307706f2543Smrg    pScrPriv = rrGetScrPriv(pScreen);
308706f2543Smrg
309706f2543Smrg    modeInfo = &stuff->modeInfo;
310706f2543Smrg    name = (char *) (stuff + 1);
311706f2543Smrg    units_after = (stuff->length - bytes_to_int32(sizeof (xRRCreateModeReq)));
312706f2543Smrg
313706f2543Smrg    /* check to make sure requested name fits within the data provided */
314706f2543Smrg    if (bytes_to_int32(modeInfo->nameLength) > units_after)
315706f2543Smrg	return BadLength;
316706f2543Smrg
317706f2543Smrg    mode = RRModeCreateUser (pScreen, modeInfo, name, &error);
318706f2543Smrg    if (!mode)
319706f2543Smrg	return error;
320706f2543Smrg
321706f2543Smrg    rep.type = X_Reply;
322706f2543Smrg    rep.pad0 = 0;
323706f2543Smrg    rep.sequenceNumber = client->sequence;
324706f2543Smrg    rep.length = 0;
325706f2543Smrg    rep.mode = mode->mode.id;
326706f2543Smrg    if (client->swapped)
327706f2543Smrg    {
328706f2543Smrg	int n;
329706f2543Smrg    	swaps(&rep.sequenceNumber, n);
330706f2543Smrg    	swapl(&rep.length, n);
331706f2543Smrg	swapl(&rep.mode, n);
332706f2543Smrg    }
333706f2543Smrg    WriteToClient(client, sizeof(xRRCreateModeReply), (char *)&rep);
334706f2543Smrg    /* Drop out reference to this mode */
335706f2543Smrg    RRModeDestroy (mode);
336706f2543Smrg    return Success;
337706f2543Smrg}
338706f2543Smrg
339706f2543Smrgint
340706f2543SmrgProcRRDestroyMode (ClientPtr client)
341706f2543Smrg{
342706f2543Smrg    REQUEST(xRRDestroyModeReq);
343706f2543Smrg    RRModePtr	mode;
344706f2543Smrg
345706f2543Smrg    REQUEST_SIZE_MATCH(xRRDestroyModeReq);
346706f2543Smrg    VERIFY_RR_MODE(stuff->mode, mode, DixDestroyAccess);
347706f2543Smrg
348706f2543Smrg    if (!mode->userScreen)
349706f2543Smrg	return BadMatch;
350706f2543Smrg    if (mode->refcnt > 1)
351706f2543Smrg	return BadAccess;
352706f2543Smrg    FreeResource (stuff->mode, 0);
353706f2543Smrg    return Success;
354706f2543Smrg}
355706f2543Smrg
356706f2543Smrgint
357706f2543SmrgProcRRAddOutputMode (ClientPtr client)
358706f2543Smrg{
359706f2543Smrg    REQUEST(xRRAddOutputModeReq);
360706f2543Smrg    RRModePtr	mode;
361706f2543Smrg    RROutputPtr	output;
362706f2543Smrg
363706f2543Smrg    REQUEST_SIZE_MATCH(xRRAddOutputModeReq);
364706f2543Smrg    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
365706f2543Smrg    VERIFY_RR_MODE(stuff->mode, mode, DixUseAccess);
366706f2543Smrg
367706f2543Smrg    return RROutputAddUserMode (output, mode);
368706f2543Smrg}
369706f2543Smrg
370706f2543Smrgint
371706f2543SmrgProcRRDeleteOutputMode (ClientPtr client)
372706f2543Smrg{
373706f2543Smrg    REQUEST(xRRDeleteOutputModeReq);
374706f2543Smrg    RRModePtr	mode;
375706f2543Smrg    RROutputPtr	output;
376706f2543Smrg
377706f2543Smrg    REQUEST_SIZE_MATCH(xRRDeleteOutputModeReq);
378706f2543Smrg    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
379706f2543Smrg    VERIFY_RR_MODE(stuff->mode, mode, DixUseAccess);
380706f2543Smrg
381706f2543Smrg    return RROutputDeleteUserMode (output, mode);
382706f2543Smrg}
383