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 
25 RESTYPE RRModeType;
26 
27 static Bool
RRModeEqual(xRRModeInfo * a,xRRModeInfo * b)28 RRModeEqual(xRRModeInfo * a, xRRModeInfo * b)
29 {
30     if (a->width != b->width)
31         return FALSE;
32     if (a->height != b->height)
33         return FALSE;
34     if (a->dotClock != b->dotClock)
35         return FALSE;
36     if (a->hSyncStart != b->hSyncStart)
37         return FALSE;
38     if (a->hSyncEnd != b->hSyncEnd)
39         return FALSE;
40     if (a->hTotal != b->hTotal)
41         return FALSE;
42     if (a->hSkew != b->hSkew)
43         return FALSE;
44     if (a->vSyncStart != b->vSyncStart)
45         return FALSE;
46     if (a->vSyncEnd != b->vSyncEnd)
47         return FALSE;
48     if (a->vTotal != b->vTotal)
49         return FALSE;
50     if (a->nameLength != b->nameLength)
51         return FALSE;
52     if (a->modeFlags != b->modeFlags)
53         return FALSE;
54     return TRUE;
55 }
56 
57 /*
58  * Keep a list so it's easy to find modes in the resource database.
59  */
60 static int num_modes;
61 static RRModePtr *modes;
62 
63 static RRModePtr
RRModeCreate(xRRModeInfo * modeInfo,const char * name,ScreenPtr userScreen)64 RRModeCreate(xRRModeInfo * modeInfo, const char *name, ScreenPtr userScreen)
65 {
66     RRModePtr mode, *newModes;
67 
68     if (!RRInit())
69         return NULL;
70 
71     mode = malloc(sizeof(RRModeRec) + modeInfo->nameLength + 1);
72     if (!mode)
73         return NULL;
74     mode->refcnt = 1;
75     mode->mode = *modeInfo;
76     mode->name = (char *) (mode + 1);
77     memcpy(mode->name, name, modeInfo->nameLength);
78     mode->name[modeInfo->nameLength] = '\0';
79     mode->userScreen = userScreen;
80 
81     if (num_modes)
82         newModes = reallocarray(modes, num_modes + 1, sizeof(RRModePtr));
83     else
84         newModes = malloc(sizeof(RRModePtr));
85 
86     if (!newModes) {
87         free(mode);
88         return NULL;
89     }
90 
91     mode->mode.id = FakeClientID(0);
92     if (!AddResource(mode->mode.id, RRModeType, (void *) mode)) {
93         free(newModes);
94         return NULL;
95     }
96     modes = newModes;
97     modes[num_modes++] = mode;
98 
99     /*
100      * give the caller a reference to this mode
101      */
102     ++mode->refcnt;
103     return mode;
104 }
105 
106 static RRModePtr
RRModeFindByName(const char * name,CARD16 nameLength)107 RRModeFindByName(const char *name, CARD16 nameLength)
108 {
109     int i;
110     RRModePtr mode;
111 
112     for (i = 0; i < num_modes; i++) {
113         mode = modes[i];
114         if (mode->mode.nameLength == nameLength &&
115             !memcmp(name, mode->name, nameLength)) {
116             return mode;
117         }
118     }
119     return NULL;
120 }
121 
122 RRModePtr
RRModeGet(xRRModeInfo * modeInfo,const char * name)123 RRModeGet(xRRModeInfo * modeInfo, const char *name)
124 {
125     int i;
126 
127     for (i = 0; i < num_modes; i++) {
128         RRModePtr mode = modes[i];
129 
130         if (RRModeEqual(&mode->mode, modeInfo) &&
131             !memcmp(name, mode->name, modeInfo->nameLength)) {
132             ++mode->refcnt;
133             return mode;
134         }
135     }
136 
137     return RRModeCreate(modeInfo, name, NULL);
138 }
139 
140 static RRModePtr
RRModeCreateUser(ScreenPtr pScreen,xRRModeInfo * modeInfo,const char * name,int * error)141 RRModeCreateUser(ScreenPtr pScreen,
142                  xRRModeInfo * modeInfo, const char *name, int *error)
143 {
144     RRModePtr mode;
145 
146     mode = RRModeFindByName(name, modeInfo->nameLength);
147     if (mode) {
148         *error = BadName;
149         return NULL;
150     }
151 
152     mode = RRModeCreate(modeInfo, name, pScreen);
153     if (!mode) {
154         *error = BadAlloc;
155         return NULL;
156     }
157     *error = Success;
158     return mode;
159 }
160 
161 RRModePtr *
RRModesForScreen(ScreenPtr pScreen,int * num_ret)162 RRModesForScreen(ScreenPtr pScreen, int *num_ret)
163 {
164     rrScrPriv(pScreen);
165     int o, c, m;
166     RRModePtr *screen_modes;
167     int num_screen_modes = 0;
168 
169     screen_modes = xallocarray((num_modes ? num_modes : 1), sizeof(RRModePtr));
170     if (!screen_modes)
171         return NULL;
172 
173     /*
174      * Add modes from all outputs
175      */
176     for (o = 0; o < pScrPriv->numOutputs; o++) {
177         RROutputPtr output = pScrPriv->outputs[o];
178         int n;
179 
180         for (m = 0; m < output->numModes + output->numUserModes; m++) {
181             RRModePtr mode = (m < output->numModes ?
182                               output->modes[m] :
183                               output->userModes[m - output->numModes]);
184             for (n = 0; n < num_screen_modes; n++)
185                 if (screen_modes[n] == mode)
186                     break;
187             if (n == num_screen_modes)
188                 screen_modes[num_screen_modes++] = mode;
189         }
190     }
191     /*
192      * Add modes from all crtcs. The goal is to
193      * make sure all available and active modes
194      * are visible to the client
195      */
196     for (c = 0; c < pScrPriv->numCrtcs; c++) {
197         RRCrtcPtr crtc = pScrPriv->crtcs[c];
198         RRModePtr mode = crtc->mode;
199         int n;
200 
201         if (!mode)
202             continue;
203         for (n = 0; n < num_screen_modes; n++)
204             if (screen_modes[n] == mode)
205                 break;
206         if (n == num_screen_modes)
207             screen_modes[num_screen_modes++] = mode;
208     }
209     /*
210      * Add all user modes for this screen
211      */
212     for (m = 0; m < num_modes; m++) {
213         RRModePtr mode = modes[m];
214         int n;
215 
216         if (mode->userScreen != pScreen)
217             continue;
218         for (n = 0; n < num_screen_modes; n++)
219             if (screen_modes[n] == mode)
220                 break;
221         if (n == num_screen_modes)
222             screen_modes[num_screen_modes++] = mode;
223     }
224 
225     *num_ret = num_screen_modes;
226     return screen_modes;
227 }
228 
229 void
RRModeDestroy(RRModePtr mode)230 RRModeDestroy(RRModePtr mode)
231 {
232     int m;
233 
234     if (--mode->refcnt > 0)
235         return;
236     for (m = 0; m < num_modes; m++) {
237         if (modes[m] == mode) {
238             memmove(modes + m, modes + m + 1,
239                     (num_modes - m - 1) * sizeof(RRModePtr));
240             num_modes--;
241             if (!num_modes) {
242                 free(modes);
243                 modes = NULL;
244             }
245             break;
246         }
247     }
248 
249     free(mode);
250 }
251 
252 static int
RRModeDestroyResource(void * value,XID pid)253 RRModeDestroyResource(void *value, XID pid)
254 {
255     RRModeDestroy((RRModePtr) value);
256     return 1;
257 }
258 
259 /*
260  * Initialize mode type
261  */
262 Bool
RRModeInit(void)263 RRModeInit(void)
264 {
265     assert(num_modes == 0);
266     assert(modes == NULL);
267     RRModeType = CreateNewResourceType(RRModeDestroyResource, "MODE");
268     if (!RRModeType)
269         return FALSE;
270 
271     return TRUE;
272 }
273 
274 /*
275  * Initialize mode type error value
276  */
277 void
RRModeInitErrorValue(void)278 RRModeInitErrorValue(void)
279 {
280     SetResourceTypeErrorValue(RRModeType, RRErrorBase + BadRRMode);
281 }
282 
283 int
ProcRRCreateMode(ClientPtr client)284 ProcRRCreateMode(ClientPtr client)
285 {
286     REQUEST(xRRCreateModeReq);
287     xRRCreateModeReply rep;
288     WindowPtr pWin;
289     ScreenPtr pScreen;
290     xRRModeInfo *modeInfo;
291     long units_after;
292     char *name;
293     int error, rc;
294     RRModePtr mode;
295 
296     REQUEST_AT_LEAST_SIZE(xRRCreateModeReq);
297     rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
298     if (rc != Success)
299         return rc;
300 
301     pScreen = pWin->drawable.pScreen;
302 
303     modeInfo = &stuff->modeInfo;
304     name = (char *) (stuff + 1);
305     units_after = (stuff->length - bytes_to_int32(sizeof(xRRCreateModeReq)));
306 
307     /* check to make sure requested name fits within the data provided */
308     if (bytes_to_int32(modeInfo->nameLength) > units_after)
309         return BadLength;
310 
311     mode = RRModeCreateUser(pScreen, modeInfo, name, &error);
312     if (!mode)
313         return error;
314 
315     rep = (xRRCreateModeReply) {
316         .type = X_Reply,
317         .sequenceNumber = client->sequence,
318         .length = 0,
319         .mode = mode->mode.id
320 	};
321     if (client->swapped) {
322         swaps(&rep.sequenceNumber);
323         swapl(&rep.length);
324         swapl(&rep.mode);
325     }
326     WriteToClient(client, sizeof(xRRCreateModeReply), &rep);
327     /* Drop out reference to this mode */
328     RRModeDestroy(mode);
329     return Success;
330 }
331 
332 int
ProcRRDestroyMode(ClientPtr client)333 ProcRRDestroyMode(ClientPtr client)
334 {
335     REQUEST(xRRDestroyModeReq);
336     RRModePtr mode;
337 
338     REQUEST_SIZE_MATCH(xRRDestroyModeReq);
339     VERIFY_RR_MODE(stuff->mode, mode, DixDestroyAccess);
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 
349 int
ProcRRAddOutputMode(ClientPtr client)350 ProcRRAddOutputMode(ClientPtr client)
351 {
352     REQUEST(xRRAddOutputModeReq);
353     RRModePtr mode;
354     RROutputPtr output;
355 
356     REQUEST_SIZE_MATCH(xRRAddOutputModeReq);
357     VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
358     VERIFY_RR_MODE(stuff->mode, mode, DixUseAccess);
359 
360     if (RROutputIsLeased(output))
361         return BadAccess;
362 
363     return RROutputAddUserMode(output, mode);
364 }
365 
366 int
ProcRRDeleteOutputMode(ClientPtr client)367 ProcRRDeleteOutputMode(ClientPtr client)
368 {
369     REQUEST(xRRDeleteOutputModeReq);
370     RRModePtr mode;
371     RROutputPtr output;
372 
373     REQUEST_SIZE_MATCH(xRRDeleteOutputModeReq);
374     VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
375     VERIFY_RR_MODE(stuff->mode, mode, DixUseAccess);
376 
377     if (RROutputIsLeased(output))
378         return BadAccess;
379 
380     return RROutputDeleteUserMode(output, mode);
381 }
382