rrmode.c revision 4642e01f
1/*
2 * Copyright © 2006 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "randrstr.h"
24#include "registry.h"
25
26RESTYPE	RRModeType;
27
28static Bool
29RRModeEqual (xRRModeInfo *a, xRRModeInfo *b)
30{
31    if (a->width != b->width) return FALSE;
32    if (a->height != b->height) return FALSE;
33    if (a->dotClock != b->dotClock) return FALSE;
34    if (a->hSyncStart != b->hSyncStart) return FALSE;
35    if (a->hSyncEnd != b->hSyncEnd) return FALSE;
36    if (a->hTotal != b->hTotal) return FALSE;
37    if (a->hSkew != b->hSkew) return FALSE;
38    if (a->vSyncStart != b->vSyncStart) return FALSE;
39    if (a->vSyncEnd != b->vSyncEnd) return FALSE;
40    if (a->vTotal != b->vTotal) return FALSE;
41    if (a->nameLength != b->nameLength) return FALSE;
42    if (a->modeFlags != b->modeFlags) return FALSE;
43    return TRUE;
44}
45
46/*
47 * Keep a list so it's easy to find modes in the resource database.
48 */
49static int	    num_modes;
50static RRModePtr    *modes;
51
52static RRModePtr
53RRModeCreate (xRRModeInfo   *modeInfo,
54	      const char    *name,
55	      ScreenPtr	    userScreen)
56{
57    RRModePtr	mode, *newModes;
58
59    if (!RRInit ())
60	return NULL;
61
62    mode = xalloc (sizeof (RRModeRec) + modeInfo->nameLength + 1);
63    if (!mode)
64	return NULL;
65    mode->refcnt = 1;
66    mode->mode = *modeInfo;
67    mode->name = (char *) (mode + 1);
68    memcpy (mode->name, name, modeInfo->nameLength);
69    mode->name[modeInfo->nameLength] = '\0';
70    mode->userScreen = userScreen;
71
72    if (num_modes)
73	newModes = xrealloc (modes, (num_modes + 1) * sizeof (RRModePtr));
74    else
75	newModes = xalloc (sizeof (RRModePtr));
76
77    if (!newModes)
78    {
79	xfree (mode);
80	return NULL;
81    }
82
83    mode->mode.id = FakeClientID(0);
84    if (!AddResource (mode->mode.id, RRModeType, (pointer) mode))
85	return NULL;
86    modes = newModes;
87    modes[num_modes++] = mode;
88
89    /*
90     * give the caller a reference to this mode
91     */
92    ++mode->refcnt;
93    return mode;
94}
95
96static RRModePtr
97RRModeFindByName (const char	*name,
98		  CARD16    	nameLength)
99{
100    int		i;
101    RRModePtr	mode;
102
103    for (i = 0; i < num_modes; i++)
104    {
105	mode = modes[i];
106	if (mode->mode.nameLength == nameLength &&
107	    !memcmp (name, mode->name, nameLength))
108	{
109	    return mode;
110	}
111    }
112    return NULL;
113}
114
115RRModePtr
116RRModeGet (xRRModeInfo	*modeInfo,
117	   const char	*name)
118{
119    int	i;
120
121    for (i = 0; i < num_modes; i++)
122    {
123	RRModePtr   mode = modes[i];
124	if (RRModeEqual (&mode->mode, modeInfo) &&
125	    !memcmp (name, mode->name, modeInfo->nameLength))
126	{
127	    ++mode->refcnt;
128	    return mode;
129	}
130    }
131
132    return RRModeCreate (modeInfo, name, NULL);
133}
134
135static RRModePtr
136RRModeCreateUser (ScreenPtr	pScreen,
137		  xRRModeInfo	*modeInfo,
138		  const char	*name,
139		  int		*error)
140{
141    RRModePtr	mode;
142
143    mode = RRModeFindByName (name, modeInfo->nameLength);
144    if (mode)
145    {
146	*error = BadName;
147	return NULL;
148    }
149
150    mode = RRModeCreate (modeInfo, name, pScreen);
151    if (!mode)
152    {
153	*error = BadAlloc;
154	return NULL;
155    }
156    *error = Success;
157    return mode;
158}
159
160RRModePtr *
161RRModesForScreen (ScreenPtr pScreen, int *num_ret)
162{
163    rrScrPriv(pScreen);
164    int		o, c, m;
165    RRModePtr	*screen_modes;
166    int		num_screen_modes = 0;
167
168    screen_modes = xalloc ((num_modes ? num_modes : 1) * sizeof (RRModePtr));
169    if (!screen_modes)
170	return NULL;
171
172    /*
173     * Add modes from all outputs
174     */
175    for (o = 0; o < pScrPriv->numOutputs; o++)
176    {
177	RROutputPtr	output = pScrPriv->outputs[o];
178	int		m, n;
179
180	for (m = 0; m < output->numModes + output->numUserModes; m++)
181	{
182	    RRModePtr   mode = (m < output->numModes ?
183				output->modes[m] :
184				output->userModes[m-output->numModes]);
185	    for (n = 0; n < num_screen_modes; n++)
186		if (screen_modes[n] == mode)
187		    break;
188	    if (n == num_screen_modes)
189		screen_modes[num_screen_modes++] = mode;
190	}
191    }
192    /*
193     * Add modes from all crtcs. The goal is to
194     * make sure all available and active modes
195     * are visible to the client
196     */
197    for (c = 0; c < pScrPriv->numCrtcs; c++)
198    {
199	RRCrtcPtr	crtc = pScrPriv->crtcs[c];
200	RRModePtr	mode = crtc->mode;
201	int		n;
202
203	if (!mode) continue;
204	for (n = 0; n < num_screen_modes; n++)
205	    if (screen_modes[n] == mode)
206		break;
207	if (n == num_screen_modes)
208	    screen_modes[num_screen_modes++] = mode;
209    }
210    /*
211     * Add all user modes for this screen
212     */
213    for (m = 0; m < num_modes; m++)
214    {
215	RRModePtr	mode = modes[m];
216	int		n;
217
218	if (mode->userScreen != pScreen)
219	    continue;
220	for (n = 0; n < num_screen_modes; n++)
221	    if (screen_modes[n] == mode)
222		break;
223	if (n == num_screen_modes)
224	    screen_modes[num_screen_modes++] = mode;
225    }
226
227    *num_ret = num_screen_modes;
228    return screen_modes;
229}
230
231void
232RRModeDestroy (RRModePtr mode)
233{
234    int	m;
235
236    if (--mode->refcnt > 0)
237	return;
238    for (m = 0; m < num_modes; m++)
239    {
240	if (modes[m] == mode)
241	{
242	    memmove (modes + m, modes + m + 1,
243		     (num_modes - m - 1) * sizeof (RRModePtr));
244	    num_modes--;
245	    if (!num_modes)
246	    {
247		xfree (modes);
248		modes = NULL;
249	    }
250	    break;
251	}
252    }
253
254    xfree (mode);
255}
256
257static int
258RRModeDestroyResource (pointer value, XID pid)
259{
260    RRModeDestroy ((RRModePtr) value);
261    return 1;
262}
263
264Bool
265RRModeInit (void)
266{
267    assert (num_modes == 0);
268    assert (modes == NULL);
269    RRModeType = CreateNewResourceType (RRModeDestroyResource);
270    if (!RRModeType)
271	return FALSE;
272    RegisterResourceName (RRModeType, "MODE");
273    return TRUE;
274}
275
276int
277ProcRRCreateMode (ClientPtr client)
278{
279    REQUEST(xRRCreateModeReq);
280    xRRCreateModeReply	rep;
281    WindowPtr		pWin;
282    ScreenPtr		pScreen;
283    rrScrPrivPtr	pScrPriv;
284    xRRModeInfo		*modeInfo;
285    long		units_after;
286    char		*name;
287    int			error, rc;
288    RRModePtr		mode;
289
290    REQUEST_AT_LEAST_SIZE (xRRCreateModeReq);
291    rc = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess);
292    if (rc != Success)
293	return rc;
294
295    pScreen = pWin->drawable.pScreen;
296    pScrPriv = rrGetScrPriv(pScreen);
297
298    modeInfo = &stuff->modeInfo;
299    name = (char *) (stuff + 1);
300    units_after = (stuff->length - (sizeof (xRRCreateModeReq) >> 2));
301
302    /* check to make sure requested name fits within the data provided */
303    if ((int) (modeInfo->nameLength + 3) >> 2 > units_after)
304	return BadLength;
305
306    mode = RRModeCreateUser (pScreen, modeInfo, name, &error);
307    if (!mode)
308	return error;
309
310    rep.type = X_Reply;
311    rep.pad0 = 0;
312    rep.sequenceNumber = client->sequence;
313    rep.length = 0;
314    rep.mode = mode->mode.id;
315    if (client->swapped)
316    {
317	int n;
318    	swaps(&rep.sequenceNumber, n);
319    	swapl(&rep.length, n);
320	swapl(&rep.mode, n);
321    }
322    WriteToClient(client, sizeof(xRRCreateModeReply), (char *)&rep);
323    /* Drop out reference to this mode */
324    RRModeDestroy (mode);
325    return client->noClientException;
326}
327
328int
329ProcRRDestroyMode (ClientPtr client)
330{
331    REQUEST(xRRDestroyModeReq);
332    RRModePtr	mode;
333
334    REQUEST_SIZE_MATCH(xRRDestroyModeReq);
335    mode = LookupIDByType (stuff->mode, RRModeType);
336    if (!mode)
337    {
338	client->errorValue = stuff->mode;
339	return RRErrorBase + BadRRMode;
340    }
341    if (!mode->userScreen)
342	return BadMatch;
343    if (mode->refcnt > 1)
344	return BadAccess;
345    FreeResource (stuff->mode, 0);
346    return Success;
347}
348
349int
350ProcRRAddOutputMode (ClientPtr client)
351{
352    REQUEST(xRRAddOutputModeReq);
353    RRModePtr	mode;
354    RROutputPtr	output;
355
356    REQUEST_SIZE_MATCH(xRRAddOutputModeReq);
357    output = LookupOutput(client, stuff->output, DixReadAccess);
358
359    if (!output)
360    {
361	client->errorValue = stuff->output;
362	return RRErrorBase + BadRROutput;
363    }
364
365    mode = LookupIDByType (stuff->mode, RRModeType);
366    if (!mode)
367    {
368	client->errorValue = stuff->mode;
369	return RRErrorBase + BadRRMode;
370    }
371
372    return RROutputAddUserMode (output, mode);
373}
374
375int
376ProcRRDeleteOutputMode (ClientPtr client)
377{
378    REQUEST(xRRDeleteOutputModeReq);
379    RRModePtr	mode;
380    RROutputPtr	output;
381
382    REQUEST_SIZE_MATCH(xRRDeleteOutputModeReq);
383    output = LookupOutput(client, stuff->output, DixReadAccess);
384
385    if (!output)
386    {
387	client->errorValue = stuff->output;
388	return RRErrorBase + BadRROutput;
389    }
390
391    mode = LookupIDByType (stuff->mode, RRModeType);
392    if (!mode)
393    {
394	client->errorValue = stuff->mode;
395	return RRErrorBase + BadRRMode;
396    }
397
398    return RROutputDeleteUserMode (output, mode);
399}
400