Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | ||
3 | Copyright 1992, 1998 The Open Group | |
4 | ||
5 | Permission to use, copy, modify, distribute, and sell this software and its | |
6 | documentation for any purpose is hereby granted without fee, provided that | |
7 | the above copyright notice appear in all copies and that both that | |
8 | copyright notice and this permission notice appear in supporting | |
9 | documentation. | |
10 | ||
11 | The above copyright notice and this permission notice shall be included | |
12 | in all copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
17 | IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | OTHER DEALINGS IN THE SOFTWARE. | |
21 | ||
22 | Except as contained in this notice, the name of The Open Group shall | |
23 | not be used in advertising or otherwise to promote the sale, use or | |
24 | other dealings in this Software without prior written authorization | |
25 | from The Open Group. | |
26 | ||
27 | */ | |
28 | ||
29 | #ifdef HAVE_DIX_CONFIG_H | |
30 | #include <dix-config.h> | |
31 | #endif | |
32 | ||
33 | #include <X11/X.h> | |
34 | #include <X11/Xproto.h> | |
35 | #include <X11/Xatom.h> | |
36 | #include "misc.h" | |
37 | #include "os.h" | |
38 | #include "dixstruct.h" | |
39 | #include "extnsionst.h" | |
40 | #include "windowstr.h" | |
41 | #include "inputstr.h" | |
42 | #include "scrnintstr.h" | |
43 | #include "dixevents.h" | |
44 | #include "sleepuntil.h" | |
45 | #include "mi.h" | |
46 | #include "xkbsrv.h" | |
47 | #include "xkbstr.h" | |
48 | #include <X11/extensions/xtestproto.h> | |
49 | #include <X11/extensions/XI.h> | |
50 | #include <X11/extensions/XIproto.h> | |
51 | #include "exglobals.h" | |
52 | #include "mipointer.h" | |
53 | #include "xserver-properties.h" | |
54 | #include "exevents.h" | |
55 | #include "eventstr.h" | |
56 | #include "inpututils.h" | |
57 | ||
58 | #include "extinit.h" | |
59 | ||
60 | /* XTest events are sent during request processing and may be interruped by | |
61 | * a SIGIO. We need a separate event list to avoid events overwriting each | |
62 | * other's memory */ | |
63 | static InternalEvent *xtest_evlist; | |
64 | ||
65 | /** | |
66 | * xtestpointer | |
67 | * is the virtual pointer for XTest. It is the first slave | |
68 | * device of the VCP. | |
69 | * xtestkeyboard | |
70 | * is the virtual keyboard for XTest. It is the first slave | |
71 | * device of the VCK | |
72 | * | |
73 | * Neither of these devices can be deleted. | |
74 | */ | |
75 | DeviceIntPtr xtestpointer, xtestkeyboard; | |
76 | ||
77 | #ifdef PANORAMIX | |
78 | #include "panoramiX.h" | |
79 | #include "panoramiXsrv.h" | |
80 | #endif | |
81 | ||
82 | static int XTestSwapFakeInput(ClientPtr /* client */ , | |
83 | xReq * /* req */ | |
84 | ); | |
85 | ||
86 | static int | |
87 | ProcXTestGetVersion(ClientPtr client) | |
88 | { | |
89 | xXTestGetVersionReply rep = { | |
90 | .type = X_Reply, | |
91 | .sequenceNumber = client->sequence, | |
92 | .length = 0, | |
93 | .majorVersion = XTestMajorVersion, | |
94 | .minorVersion = XTestMinorVersion | |
95 | }; | |
96 | ||
97 | REQUEST_SIZE_MATCH(xXTestGetVersionReq); | |
98 | ||
99 | if (client->swapped) { | |
100 | swaps(&rep.sequenceNumber); | |
101 | swaps(&rep.minorVersion); | |
102 | } | |
103 | WriteToClient(client, sizeof(xXTestGetVersionReply), &rep); | |
104 | return Success; | |
105 | } | |
106 | ||
107 | static int | |
108 | ProcXTestCompareCursor(ClientPtr client) | |
109 | { | |
110 | REQUEST(xXTestCompareCursorReq); | |
111 | xXTestCompareCursorReply rep; | |
112 | WindowPtr pWin; | |
113 | CursorPtr pCursor; | |
114 | int rc; | |
115 | DeviceIntPtr ptr = PickPointer(client); | |
116 | ||
117 | REQUEST_SIZE_MATCH(xXTestCompareCursorReq); | |
118 | rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); | |
119 | if (rc != Success) | |
120 | return rc; | |
121 | ||
122 | if (!ptr) | |
123 | return BadAccess; | |
124 | ||
125 | if (stuff->cursor == None) | |
126 | pCursor = NullCursor; | |
127 | else if (stuff->cursor == XTestCurrentCursor) | |
128 | pCursor = GetSpriteCursor(ptr); | |
129 | else { | |
130 | rc = dixLookupResourceByType((pointer *) &pCursor, stuff->cursor, | |
131 | RT_CURSOR, client, DixReadAccess); | |
132 | if (rc != Success) { | |
133 | client->errorValue = stuff->cursor; | |
134 | return rc; | |
135 | } | |
136 | } | |
137 | rep = (xXTestCompareCursorReply) { | |
138 | .type = X_Reply, | |
139 | .sequenceNumber = client->sequence, | |
140 | .length = 0, | |
141 | .same = (wCursor(pWin) == pCursor) | |
142 | }; | |
143 | if (client->swapped) { | |
144 | swaps(&rep.sequenceNumber); | |
145 | } | |
146 | WriteToClient(client, sizeof(xXTestCompareCursorReply), &rep); | |
147 | return Success; | |
148 | } | |
149 | ||
150 | static int | |
151 | ProcXTestFakeInput(ClientPtr client) | |
152 | { | |
153 | REQUEST(xXTestFakeInputReq); | |
154 | int nev, n, type, rc; | |
155 | xEvent *ev; | |
156 | DeviceIntPtr dev = NULL; | |
157 | WindowPtr root; | |
158 | Bool extension = FALSE; | |
159 | ValuatorMask mask; | |
160 | int valuators[MAX_VALUATORS] = { 0 }; | |
161 | int numValuators = 0; | |
162 | int firstValuator = 0; | |
163 | int nevents = 0; | |
164 | int i; | |
165 | int base = 0; | |
166 | int flags = 0; | |
167 | int need_ptr_update = 1; | |
168 | ||
169 | nev = (stuff->length << 2) - sizeof(xReq); | |
170 | if ((nev % sizeof(xEvent)) || !nev) | |
171 | return BadLength; | |
172 | nev /= sizeof(xEvent); | |
173 | UpdateCurrentTime(); | |
174 | ev = (xEvent *) &((xReq *) stuff)[1]; | |
175 | type = ev->u.u.type & 0177; | |
176 | ||
177 | if (type >= EXTENSION_EVENT_BASE) { | |
178 | extension = TRUE; | |
179 | ||
180 | /* check device */ | |
181 | rc = dixLookupDevice(&dev, stuff->deviceid & 0177, client, | |
182 | DixWriteAccess); | |
183 | if (rc != Success) { | |
184 | client->errorValue = stuff->deviceid & 0177; | |
185 | return rc; | |
186 | } | |
187 | ||
188 | /* check type */ | |
189 | type -= DeviceValuator; | |
190 | switch (type) { | |
191 | case XI_DeviceKeyPress: | |
192 | case XI_DeviceKeyRelease: | |
193 | if (!dev->key) { | |
194 | client->errorValue = ev->u.u.type; | |
195 | return BadValue; | |
196 | } | |
197 | break; | |
198 | case XI_DeviceButtonPress: | |
199 | case XI_DeviceButtonRelease: | |
200 | if (!dev->button) { | |
201 | client->errorValue = ev->u.u.type; | |
202 | return BadValue; | |
203 | } | |
204 | break; | |
205 | case XI_DeviceMotionNotify: | |
206 | if (!dev->valuator) { | |
207 | client->errorValue = ev->u.u.type; | |
208 | return BadValue; | |
209 | } | |
210 | break; | |
211 | case XI_ProximityIn: | |
212 | case XI_ProximityOut: | |
213 | if (!dev->proximity) { | |
214 | client->errorValue = ev->u.u.type; | |
215 | return BadValue; | |
216 | } | |
217 | break; | |
218 | default: | |
219 | client->errorValue = ev->u.u.type; | |
220 | return BadValue; | |
221 | } | |
222 | ||
223 | /* check validity */ | |
224 | if (nev == 1 && type == XI_DeviceMotionNotify) | |
225 | return BadLength; /* DevMotion must be followed by DevValuator */ | |
226 | ||
227 | if (type == XI_DeviceMotionNotify) { | |
228 | firstValuator = ((deviceValuator *) (ev + 1))->first_valuator; | |
229 | if (firstValuator > dev->valuator->numAxes) { | |
230 | client->errorValue = ev->u.u.type; | |
231 | return BadValue; | |
232 | } | |
233 | ||
234 | if (ev->u.u.detail == xFalse) | |
235 | flags |= POINTER_ABSOLUTE; | |
236 | } | |
237 | else { | |
238 | firstValuator = 0; | |
239 | flags |= POINTER_ABSOLUTE; | |
240 | } | |
241 | ||
242 | if (nev > 1 && !dev->valuator) { | |
243 | client->errorValue = firstValuator; | |
244 | return BadValue; | |
245 | } | |
246 | ||
247 | /* check validity of valuator events */ | |
248 | base = firstValuator; | |
249 | for (n = 1; n < nev; n++) { | |
250 | deviceValuator *dv = (deviceValuator *) (ev + n); | |
251 | if (dv->type != DeviceValuator) { | |
252 | client->errorValue = dv->type; | |
253 | return BadValue; | |
254 | } | |
255 | if (dv->first_valuator != base) { | |
256 | client->errorValue = dv->first_valuator; | |
257 | return BadValue; | |
258 | } | |
259 | switch (dv->num_valuators) { | |
260 | case 6: | |
261 | valuators[base + 5] = dv->valuator5; | |
262 | case 5: | |
263 | valuators[base + 4] = dv->valuator4; | |
264 | case 4: | |
265 | valuators[base + 3] = dv->valuator3; | |
266 | case 3: | |
267 | valuators[base + 2] = dv->valuator2; | |
268 | case 2: | |
269 | valuators[base + 1] = dv->valuator1; | |
270 | case 1: | |
271 | valuators[base] = dv->valuator0; | |
272 | break; | |
273 | default: | |
274 | client->errorValue = dv->num_valuators; | |
275 | return BadValue; | |
276 | } | |
277 | ||
278 | base += dv->num_valuators; | |
279 | numValuators += dv->num_valuators; | |
280 | ||
281 | if (firstValuator + numValuators > dev->valuator->numAxes) { | |
282 | client->errorValue = dv->num_valuators; | |
283 | return BadValue; | |
284 | } | |
285 | } | |
286 | type = type - XI_DeviceKeyPress + KeyPress; | |
287 | ||
288 | } | |
289 | else { | |
290 | if (nev != 1) | |
291 | return BadLength; | |
292 | switch (type) { | |
293 | case KeyPress: | |
294 | case KeyRelease: | |
295 | dev = PickKeyboard(client); | |
296 | break; | |
297 | case ButtonPress: | |
298 | case ButtonRelease: | |
299 | dev = PickPointer(client); | |
300 | break; | |
301 | case MotionNotify: | |
302 | dev = PickPointer(client); | |
303 | valuators[0] = ev->u.keyButtonPointer.rootX; | |
304 | valuators[1] = ev->u.keyButtonPointer.rootY; | |
305 | numValuators = 2; | |
306 | firstValuator = 0; | |
307 | if (ev->u.u.detail == xFalse) | |
308 | flags = POINTER_ABSOLUTE | POINTER_DESKTOP; | |
309 | break; | |
310 | default: | |
311 | client->errorValue = ev->u.u.type; | |
312 | return BadValue; | |
313 | } | |
314 | ||
315 | /* Technically the protocol doesn't allow for BadAccess here but | |
316 | * this can only happen when all MDs are disabled. */ | |
317 | if (!dev) | |
318 | return BadAccess; | |
319 | ||
320 | dev = GetXTestDevice(dev); | |
321 | } | |
322 | ||
323 | ||
324 | /* If the event has a time set, wait for it to pass */ | |
325 | if (ev->u.keyButtonPointer.time) { | |
326 | TimeStamp activateTime; | |
327 | CARD32 ms; | |
328 | ||
329 | activateTime = currentTime; | |
330 | ms = activateTime.milliseconds + ev->u.keyButtonPointer.time; | |
331 | if (ms < activateTime.milliseconds) | |
332 | activateTime.months++; | |
333 | activateTime.milliseconds = ms; | |
334 | ev->u.keyButtonPointer.time = 0; | |
335 | ||
336 | /* see mbuf.c:QueueDisplayRequest (from the deprecated Multibuffer | |
337 | * extension) for code similar to this */ | |
338 | ||
339 | if (!ClientSleepUntil(client, &activateTime, NULL, NULL)) { | |
340 | return BadAlloc; | |
341 | } | |
342 | /* swap the request back so we can simply re-execute it */ | |
343 | if (client->swapped) { | |
344 | (void) XTestSwapFakeInput(client, (xReq *) stuff); | |
345 | swaps(&stuff->length); | |
346 | } | |
347 | ResetCurrentRequest(client); | |
348 | client->sequence--; | |
349 | return Success; | |
350 | } | |
351 | ||
352 | switch (type) { | |
353 | case KeyPress: | |
354 | case KeyRelease: | |
355 | if (!dev->key) | |
356 | return BadDevice; | |
357 | ||
358 | if (ev->u.u.detail < dev->key->xkbInfo->desc->min_key_code || | |
359 | ev->u.u.detail > dev->key->xkbInfo->desc->max_key_code) { | |
360 | client->errorValue = ev->u.u.detail; | |
361 | return BadValue; | |
362 | } | |
363 | ||
364 | need_ptr_update = 0; | |
365 | break; | |
366 | case MotionNotify: | |
367 | if (!dev->valuator) | |
368 | return BadDevice; | |
369 | ||
370 | if (!(extension || ev->u.keyButtonPointer.root == None)) { | |
371 | rc = dixLookupWindow(&root, ev->u.keyButtonPointer.root, | |
372 | client, DixGetAttrAccess); | |
373 | if (rc != Success) | |
374 | return rc; | |
375 | if (root->parent) { | |
376 | client->errorValue = ev->u.keyButtonPointer.root; | |
377 | return BadValue; | |
378 | } | |
379 | ||
380 | /* Add the root window's offset to the valuators */ | |
381 | if ((flags & POINTER_ABSOLUTE) && firstValuator <= 1 && numValuators > 0) { | |
382 | if (firstValuator == 0) | |
383 | valuators[0] += root->drawable.pScreen->x; | |
384 | if (firstValuator < 2 && firstValuator + numValuators > 1) | |
385 | valuators[1 - firstValuator] += root->drawable.pScreen->y; | |
386 | } | |
387 | } | |
388 | if (ev->u.u.detail != xTrue && ev->u.u.detail != xFalse) { | |
389 | client->errorValue = ev->u.u.detail; | |
390 | return BadValue; | |
391 | } | |
392 | ||
393 | /* FIXME: Xinerama! */ | |
394 | ||
395 | break; | |
396 | case ButtonPress: | |
397 | case ButtonRelease: | |
398 | if (!dev->button) | |
399 | return BadDevice; | |
400 | ||
401 | if (!ev->u.u.detail || ev->u.u.detail > dev->button->numButtons) { | |
402 | client->errorValue = ev->u.u.detail; | |
403 | return BadValue; | |
404 | } | |
405 | break; | |
406 | } | |
407 | if (screenIsSaved == SCREEN_SAVER_ON) | |
408 | dixSaveScreens(serverClient, SCREEN_SAVER_OFF, ScreenSaverReset); | |
409 | ||
410 | switch (type) { | |
411 | case MotionNotify: | |
412 | valuator_mask_set_range(&mask, firstValuator, numValuators, valuators); | |
413 | nevents = GetPointerEvents(xtest_evlist, dev, type, 0, flags, &mask); | |
414 | break; | |
415 | case ButtonPress: | |
416 | case ButtonRelease: | |
417 | valuator_mask_set_range(&mask, firstValuator, numValuators, valuators); | |
418 | nevents = GetPointerEvents(xtest_evlist, dev, type, ev->u.u.detail, | |
419 | flags, &mask); | |
420 | break; | |
421 | case KeyPress: | |
422 | case KeyRelease: | |
423 | nevents = | |
424 | GetKeyboardEvents(xtest_evlist, dev, type, ev->u.u.detail, NULL); | |
425 | break; | |
426 | } | |
427 | ||
428 | for (i = 0; i < nevents; i++) | |
429 | mieqProcessDeviceEvent(dev, &xtest_evlist[i], miPointerGetScreen(inputInfo.pointer)); | |
430 | ||
431 | if (need_ptr_update) | |
432 | miPointerUpdateSprite(dev); | |
433 | return Success; | |
434 | } | |
435 | ||
436 | static int | |
437 | ProcXTestGrabControl(ClientPtr client) | |
438 | { | |
439 | REQUEST(xXTestGrabControlReq); | |
440 | ||
441 | REQUEST_SIZE_MATCH(xXTestGrabControlReq); | |
442 | if ((stuff->impervious != xTrue) && (stuff->impervious != xFalse)) { | |
443 | client->errorValue = stuff->impervious; | |
444 | return BadValue; | |
445 | } | |
446 | if (stuff->impervious) | |
447 | MakeClientGrabImpervious(client); | |
448 | else | |
449 | MakeClientGrabPervious(client); | |
450 | return Success; | |
451 | } | |
452 | ||
453 | static int | |
454 | ProcXTestDispatch(ClientPtr client) | |
455 | { | |
456 | REQUEST(xReq); | |
457 | switch (stuff->data) { | |
458 | case X_XTestGetVersion: | |
459 | return ProcXTestGetVersion(client); | |
460 | case X_XTestCompareCursor: | |
461 | return ProcXTestCompareCursor(client); | |
462 | case X_XTestFakeInput: | |
463 | return ProcXTestFakeInput(client); | |
464 | case X_XTestGrabControl: | |
465 | return ProcXTestGrabControl(client); | |
466 | default: | |
467 | return BadRequest; | |
468 | } | |
469 | } | |
470 | ||
471 | static int | |
472 | SProcXTestGetVersion(ClientPtr client) | |
473 | { | |
474 | REQUEST(xXTestGetVersionReq); | |
475 | ||
476 | swaps(&stuff->length); | |
477 | REQUEST_SIZE_MATCH(xXTestGetVersionReq); | |
478 | swaps(&stuff->minorVersion); | |
479 | return ProcXTestGetVersion(client); | |
480 | } | |
481 | ||
482 | static int | |
483 | SProcXTestCompareCursor(ClientPtr client) | |
484 | { | |
485 | REQUEST(xXTestCompareCursorReq); | |
486 | ||
487 | swaps(&stuff->length); | |
488 | REQUEST_SIZE_MATCH(xXTestCompareCursorReq); | |
489 | swapl(&stuff->window); | |
490 | swapl(&stuff->cursor); | |
491 | return ProcXTestCompareCursor(client); | |
492 | } | |
493 | ||
494 | static int | |
495 | XTestSwapFakeInput(ClientPtr client, xReq * req) | |
496 | { | |
497 | int nev; | |
498 | xEvent *ev; | |
499 | xEvent sev; | |
500 | EventSwapPtr proc; | |
501 | ||
502 | nev = ((req->length << 2) - sizeof(xReq)) / sizeof(xEvent); | |
503 | for (ev = (xEvent *) &req[1]; --nev >= 0; ev++) { | |
504 | /* Swap event */ | |
505 | proc = EventSwapVector[ev->u.u.type & 0177]; | |
506 | /* no swapping proc; invalid event type? */ | |
507 | if (!proc || proc == NotImplemented) { | |
508 | client->errorValue = ev->u.u.type; | |
509 | return BadValue; | |
510 | } | |
511 | (*proc) (ev, &sev); | |
512 | *ev = sev; | |
513 | } | |
514 | return Success; | |
515 | } | |
516 | ||
517 | static int | |
518 | SProcXTestFakeInput(ClientPtr client) | |
519 | { | |
520 | int n; | |
521 | ||
522 | REQUEST(xReq); | |
523 | ||
524 | swaps(&stuff->length); | |
525 | n = XTestSwapFakeInput(client, stuff); | |
526 | if (n != Success) | |
527 | return n; | |
528 | return ProcXTestFakeInput(client); | |
529 | } | |
530 | ||
531 | static int | |
532 | SProcXTestGrabControl(ClientPtr client) | |
533 | { | |
534 | REQUEST(xXTestGrabControlReq); | |
535 | ||
536 | swaps(&stuff->length); | |
537 | REQUEST_SIZE_MATCH(xXTestGrabControlReq); | |
538 | return ProcXTestGrabControl(client); | |
539 | } | |
540 | ||
541 | static int | |
542 | SProcXTestDispatch(ClientPtr client) | |
543 | { | |
544 | REQUEST(xReq); | |
545 | switch (stuff->data) { | |
546 | case X_XTestGetVersion: | |
547 | return SProcXTestGetVersion(client); | |
548 | case X_XTestCompareCursor: | |
549 | return SProcXTestCompareCursor(client); | |
550 | case X_XTestFakeInput: | |
551 | return SProcXTestFakeInput(client); | |
552 | case X_XTestGrabControl: | |
553 | return SProcXTestGrabControl(client); | |
554 | default: | |
555 | return BadRequest; | |
556 | } | |
557 | } | |
558 | ||
559 | /** | |
560 | * Allocate an virtual slave device for xtest events, this | |
561 | * is a slave device to inputInfo master devices | |
562 | */ | |
563 | void | |
564 | InitXTestDevices(void) | |
565 | { | |
566 | if (AllocXTestDevice(serverClient, "Virtual core", | |
567 | &xtestpointer, &xtestkeyboard, | |
568 | inputInfo.pointer, inputInfo.keyboard) != Success) | |
569 | FatalError("Failed to allocate XTest devices"); | |
570 | ||
571 | if (ActivateDevice(xtestpointer, TRUE) != Success || | |
572 | ActivateDevice(xtestkeyboard, TRUE) != Success) | |
573 | FatalError("Failed to activate XTest core devices."); | |
574 | if (!EnableDevice(xtestpointer, TRUE) || !EnableDevice(xtestkeyboard, TRUE)) | |
575 | FatalError("Failed to enable XTest core devices."); | |
576 | ||
577 | AttachDevice(NULL, xtestpointer, inputInfo.pointer); | |
578 | ||
579 | AttachDevice(NULL, xtestkeyboard, inputInfo.keyboard); | |
580 | } | |
581 | ||
582 | /** | |
583 | * Don't allow changing the XTest property. | |
584 | */ | |
585 | static int | |
586 | DeviceSetXTestProperty(DeviceIntPtr dev, Atom property, | |
587 | XIPropertyValuePtr prop, BOOL checkonly) | |
588 | { | |
589 | if (property == XIGetKnownProperty(XI_PROP_XTEST_DEVICE)) | |
590 | return BadAccess; | |
591 | ||
592 | return Success; | |
593 | } | |
594 | ||
595 | /** | |
596 | * Allocate a device pair that is initialised as a slave | |
597 | * device with properties that identify the devices as belonging | |
598 | * to XTest subsystem. | |
599 | * This only creates the pair, Activate/Enable Device | |
600 | * still need to be called. | |
601 | */ | |
602 | int | |
603 | AllocXTestDevice(ClientPtr client, const char *name, | |
604 | DeviceIntPtr *ptr, DeviceIntPtr *keybd, | |
605 | DeviceIntPtr master_ptr, DeviceIntPtr master_keybd) | |
606 | { | |
607 | int retval; | |
608 | char *xtestname; | |
609 | char dummy = 1; | |
610 | ||
611 | if (asprintf(&xtestname, "%s XTEST", name) == -1) | |
612 | return BadAlloc; | |
613 | ||
614 | retval = | |
615 | AllocDevicePair(client, xtestname, ptr, keybd, CorePointerProc, | |
616 | CoreKeyboardProc, FALSE); | |
617 | if (retval == Success) { | |
618 | (*ptr)->xtest_master_id = master_ptr->id; | |
619 | (*keybd)->xtest_master_id = master_keybd->id; | |
620 | ||
621 | XIChangeDeviceProperty(*ptr, XIGetKnownProperty(XI_PROP_XTEST_DEVICE), | |
622 | XA_INTEGER, 8, PropModeReplace, 1, &dummy, | |
623 | FALSE); | |
624 | XISetDevicePropertyDeletable(*ptr, | |
625 | XIGetKnownProperty(XI_PROP_XTEST_DEVICE), | |
626 | FALSE); | |
627 | XIRegisterPropertyHandler(*ptr, DeviceSetXTestProperty, NULL, NULL); | |
628 | XIChangeDeviceProperty(*keybd, XIGetKnownProperty(XI_PROP_XTEST_DEVICE), | |
629 | XA_INTEGER, 8, PropModeReplace, 1, &dummy, | |
630 | FALSE); | |
631 | XISetDevicePropertyDeletable(*keybd, | |
632 | XIGetKnownProperty(XI_PROP_XTEST_DEVICE), | |
633 | FALSE); | |
634 | XIRegisterPropertyHandler(*keybd, DeviceSetXTestProperty, NULL, NULL); | |
635 | } | |
636 | ||
637 | free(xtestname); | |
638 | ||
639 | return retval; | |
640 | } | |
641 | ||
642 | /** | |
643 | * If master is NULL, return TRUE if the given device is an xtest device or | |
644 | * FALSE otherwise. | |
645 | * If master is not NULL, return TRUE if the given device is this master's | |
646 | * xtest device. | |
647 | */ | |
648 | BOOL | |
649 | IsXTestDevice(DeviceIntPtr dev, DeviceIntPtr master) | |
650 | { | |
651 | if (IsMaster(dev)) | |
652 | return FALSE; | |
653 | ||
654 | /* deviceid 0 is reserved for XIAllDevices, non-zero mid means XTest | |
655 | * device */ | |
656 | if (master) | |
657 | return dev->xtest_master_id == master->id; | |
658 | ||
659 | return dev->xtest_master_id != 0; | |
660 | } | |
661 | ||
662 | /** | |
663 | * @return The X Test virtual device for the given master. | |
664 | */ | |
665 | DeviceIntPtr | |
666 | GetXTestDevice(DeviceIntPtr master) | |
667 | { | |
668 | DeviceIntPtr it; | |
669 | ||
670 | for (it = inputInfo.devices; it; it = it->next) { | |
671 | if (IsXTestDevice(it, master)) | |
672 | return it; | |
673 | } | |
674 | ||
675 | /* This only happens if master is a slave device. don't do that */ | |
676 | return NULL; | |
677 | } | |
678 | ||
679 | static void | |
680 | XTestExtensionTearDown(ExtensionEntry * e) | |
681 | { | |
682 | FreeEventList(xtest_evlist, GetMaximumEventsNum()); | |
683 | xtest_evlist = NULL; | |
684 | } | |
685 | ||
686 | void | |
687 | XTestExtensionInit(void) | |
688 | { | |
689 | AddExtension(XTestExtensionName, 0, 0, | |
690 | ProcXTestDispatch, SProcXTestDispatch, | |
691 | XTestExtensionTearDown, StandardMinorOpcode); | |
692 | ||
693 | xtest_evlist = InitEventList(GetMaximumEventsNum()); | |
694 | } |