Commit | Line | Data |
---|---|---|
a09e091a JB |
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 | |
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 | |
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 = realloc(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, (pointer) 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 | |
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 | |
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 | |
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 * | |
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 = malloc((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 | |
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 | |
253 | RRModeDestroyResource(pointer value, XID pid) | |
254 | { | |
255 | RRModeDestroy((RRModePtr) value); | |
256 | return 1; | |
257 | } | |
258 | ||
259 | /* | |
260 | * Initialize mode type | |
261 | */ | |
262 | Bool | |
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 | |
278 | RRModeInitErrorValue(void) | |
279 | { | |
280 | SetResourceTypeErrorValue(RRModeType, RRErrorBase + BadRRMode); | |
281 | } | |
282 | ||
283 | int | |
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 | |
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 | |
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 | return RROutputAddUserMode(output, mode); | |
361 | } | |
362 | ||
363 | int | |
364 | ProcRRDeleteOutputMode(ClientPtr client) | |
365 | { | |
366 | REQUEST(xRRDeleteOutputModeReq); | |
367 | RRModePtr mode; | |
368 | RROutputPtr output; | |
369 | ||
370 | REQUEST_SIZE_MATCH(xRRDeleteOutputModeReq); | |
371 | VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); | |
372 | VERIFY_RR_MODE(stuff->mode, mode, DixUseAccess); | |
373 | ||
374 | return RROutputDeleteUserMode(output, mode); | |
375 | } |