Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright 2001-2004 Red Hat Inc., Durham, North Carolina. | |
3 | * | |
4 | * All Rights Reserved. | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining | |
7 | * a copy of this software and associated documentation files (the | |
8 | * "Software"), to deal in the Software without restriction, including | |
9 | * without limitation on the rights to use, copy, modify, merge, | |
10 | * publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, | |
12 | * subject to the following conditions: | |
13 | * | |
14 | * The above copyright notice and this permission notice (including the | |
15 | * next paragraph) shall be included in all copies or substantial | |
16 | * portions of the Software. | |
17 | * | |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
21 | * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS | |
22 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
23 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
24 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
25 | * SOFTWARE. | |
26 | */ | |
27 | ||
28 | /* | |
29 | * Authors: | |
30 | * David H. Dawes <dawes@xfree86.org> | |
31 | * Kevin E. Martin <kem@redhat.com> | |
32 | * Rickard E. (Rik) Faith <faith@redhat.com> | |
33 | * | |
34 | */ | |
35 | ||
36 | /** \file | |
37 | * This file contains code than supports cursor movement, including the | |
38 | * code that initializes and reinitializes the screen positions and | |
39 | * computes screen overlap. | |
40 | * | |
41 | * "This code is based very closely on the XFree86 equivalent | |
42 | * (xfree86/common/xf86Cursor.c)." --David Dawes. | |
43 | * | |
44 | * "This code was then extensively re-written, as explained here." | |
45 | * --Rik Faith | |
46 | * | |
47 | * The code in xf86Cursor.c used edge lists to implement the | |
48 | * CursorOffScreen function. The edge list computation was complex | |
49 | * (especially in the face of arbitrarily overlapping screens) compared | |
50 | * with the speed savings in the CursorOffScreen function. The new | |
51 | * implementation has erred on the side of correctness, readability, and | |
52 | * maintainability over efficiency. For the common (non-edge) case, the | |
53 | * dmxCursorOffScreen function does avoid a loop over all the screens. | |
54 | * When the cursor has left the screen, all the screens are searched, | |
55 | * and the first screen (in dmxScreens order) containing the cursor will | |
56 | * be returned. If run-time profiling shows that this routing is a | |
57 | * performance bottle-neck, then an edge list may have to be | |
58 | * reimplemented. An edge list algorithm is O(edges) whereas the new | |
59 | * algorithm is O(dmxNumScreens). Since edges is usually 1-3 and | |
60 | * dmxNumScreens may be 30-60 for large backend walls, this trade off | |
61 | * may be compelling. | |
62 | * | |
63 | * The xf86InitOrigins routine uses bit masks during the computation and | |
64 | * is therefore limited to the length of a word (e.g., 32 or 64 bits) | |
65 | * screens. Because Xdmx is expected to be used with a large number of | |
66 | * backend displays, this limitation was removed. The new | |
67 | * implementation has erred on the side of readability over efficiency, | |
68 | * using the dmxSL* routines to manage a screen list instead of a | |
69 | * bitmap, and a function call to decrease the length of the main | |
70 | * routine. Both algorithms are of the same order, and both are called | |
71 | * only at server generation time, so trading clarity and long-term | |
72 | * maintainability for efficiency does not seem justified in this case. | |
73 | */ | |
74 | ||
75 | #ifdef HAVE_DMX_CONFIG_H | |
76 | #include <dmx-config.h> | |
77 | #endif | |
78 | ||
79 | #define DMX_CURSOR_DEBUG 0 | |
80 | ||
81 | #include "dmx.h" | |
82 | #include "dmxsync.h" | |
83 | #include "dmxcursor.h" | |
84 | #include "dmxlog.h" | |
85 | #include "dmxprop.h" | |
86 | #include "dmxinput.h" | |
87 | ||
88 | #include "mipointer.h" | |
89 | #include "windowstr.h" | |
90 | #include "globals.h" | |
91 | #include "cursorstr.h" | |
92 | #include "dixevents.h" /* For GetSpriteCursor() */ | |
93 | #include "inputstr.h" /* for inputInfo.pointer */ | |
94 | ||
95 | #if DMX_CURSOR_DEBUG | |
96 | #define DMXDBG0(f) dmxLog(dmxDebug,f) | |
97 | #define DMXDBG1(f,a) dmxLog(dmxDebug,f,a) | |
98 | #define DMXDBG2(f,a,b) dmxLog(dmxDebug,f,a,b) | |
99 | #define DMXDBG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c) | |
100 | #define DMXDBG4(f,a,b,c,d) dmxLog(dmxDebug,f,a,b,c,d) | |
101 | #define DMXDBG5(f,a,b,c,d,e) dmxLog(dmxDebug,f,a,b,c,d,e) | |
102 | #define DMXDBG6(f,a,b,c,d,e,g) dmxLog(dmxDebug,f,a,b,c,d,e,g) | |
103 | #define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h) | |
104 | #else | |
105 | #define DMXDBG0(f) | |
106 | #define DMXDBG1(f,a) | |
107 | #define DMXDBG2(f,a,b) | |
108 | #define DMXDBG3(f,a,b,c) | |
109 | #define DMXDBG4(f,a,b,c,d) | |
110 | #define DMXDBG5(f,a,b,c,d,e) | |
111 | #define DMXDBG6(f,a,b,c,d,e,g) | |
112 | #define DMXDBG7(f,a,b,c,d,e,g,h) | |
113 | #endif | |
114 | ||
115 | static int dmxCursorDoMultiCursors = 1; | |
116 | ||
117 | /** Turn off support for displaying multiple cursors on overlapped | |
118 | back-end displays. See #dmxCursorDoMultiCursors. */ | |
119 | void | |
120 | dmxCursorNoMulti(void) | |
121 | { | |
122 | dmxCursorDoMultiCursors = 0; | |
123 | } | |
124 | ||
125 | static Bool | |
126 | dmxCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y) | |
127 | { | |
128 | DMXScreenInfo *dmxScreen; | |
129 | int i; | |
130 | int localX = *x; | |
131 | int localY = *y; | |
132 | int globalX; | |
133 | int globalY; | |
134 | ||
135 | if (screenInfo.numScreens == 1) | |
136 | return FALSE; | |
137 | ||
138 | /* On current screen? */ | |
139 | dmxScreen = &dmxScreens[(*ppScreen)->myNum]; | |
140 | if (localX >= 0 | |
141 | && localX < dmxScreen->rootWidth | |
142 | && localY >= 0 && localY < dmxScreen->rootHeight) | |
143 | return FALSE; | |
144 | ||
145 | /* Convert to global coordinate space */ | |
146 | globalX = dmxScreen->rootXOrigin + localX; | |
147 | globalY = dmxScreen->rootYOrigin + localY; | |
148 | ||
149 | /* Is cursor on the current screen? | |
150 | * This efficiently exits this routine | |
151 | * for the most common case. */ | |
152 | if (ppScreen && *ppScreen) { | |
153 | dmxScreen = &dmxScreens[(*ppScreen)->myNum]; | |
154 | if (globalX >= dmxScreen->rootXOrigin | |
155 | && globalX < dmxScreen->rootXOrigin + dmxScreen->rootWidth | |
156 | && globalY >= dmxScreen->rootYOrigin | |
157 | && globalY < dmxScreen->rootYOrigin + dmxScreen->rootHeight) | |
158 | return FALSE; | |
159 | } | |
160 | ||
161 | /* Find first screen cursor is on */ | |
162 | for (i = 0; i < dmxNumScreens; i++) { | |
163 | dmxScreen = &dmxScreens[i]; | |
164 | if (globalX >= dmxScreen->rootXOrigin | |
165 | && globalX < dmxScreen->rootXOrigin + dmxScreen->rootWidth | |
166 | && globalY >= dmxScreen->rootYOrigin | |
167 | && globalY < dmxScreen->rootYOrigin + dmxScreen->rootHeight) { | |
168 | if (dmxScreen->index == (*ppScreen)->myNum) | |
169 | return FALSE; | |
170 | *ppScreen = screenInfo.screens[dmxScreen->index]; | |
171 | *x = globalX - dmxScreen->rootXOrigin; | |
172 | *y = globalY - dmxScreen->rootYOrigin; | |
173 | return TRUE; | |
174 | } | |
175 | } | |
176 | return FALSE; | |
177 | } | |
178 | ||
179 | static void | |
180 | dmxCrossScreen(ScreenPtr pScreen, Bool entering) | |
181 | { | |
182 | } | |
183 | ||
184 | static void | |
185 | dmxWarpCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y) | |
186 | { | |
187 | DMXDBG3("dmxWarpCursor(%d,%d,%d)\n", pScreen->myNum, x, y); | |
188 | #if 11 /*BP*/ | |
189 | /* This call is depracated. Replace with???? */ | |
190 | miPointerWarpCursor(pDev, pScreen, x, y); | |
191 | #else | |
192 | pScreen->SetCursorPosition(pDev, pScreen, x, y, FALSE); | |
193 | #endif | |
194 | } | |
195 | ||
196 | miPointerScreenFuncRec dmxPointerCursorFuncs = { | |
197 | dmxCursorOffScreen, | |
198 | dmxCrossScreen, | |
199 | dmxWarpCursor, | |
200 | }; | |
201 | ||
202 | /** Create a list of screens that we'll manipulate. */ | |
203 | static int * | |
204 | dmxSLCreate(void) | |
205 | { | |
206 | int *list = malloc(dmxNumScreens * sizeof(*list)); | |
207 | int i; | |
208 | ||
209 | for (i = 0; i < dmxNumScreens; i++) | |
210 | list[i] = 1; | |
211 | return list; | |
212 | } | |
213 | ||
214 | /** Free list. */ | |
215 | static void | |
216 | dmxSLFree(int *list) | |
217 | { | |
218 | free(list); | |
219 | } | |
220 | ||
221 | /** Find next uninitialized entry in list. */ | |
222 | static int | |
223 | dmxSLFindNext(int *list) | |
224 | { | |
225 | int i; | |
226 | ||
227 | for (i = 0; i < dmxNumScreens; i++) | |
228 | if (list[i]) | |
229 | return i; | |
230 | return -1; | |
231 | } | |
232 | ||
233 | /** Make one pass over all the screens and return the number updated. */ | |
234 | static int | |
235 | dmxTryComputeScreenOrigins(int *screensLeft) | |
236 | { | |
237 | ScreenPtr pScreen, refScreen; | |
238 | DMXScreenInfo *screen; | |
239 | int i, ref; | |
240 | int changed = 0; | |
241 | ||
242 | for (i = 0; i < dmxNumScreens; i++) { | |
243 | if (!screensLeft[i]) | |
244 | continue; | |
245 | screen = &dmxScreens[i]; | |
246 | pScreen = screenInfo.screens[i]; | |
247 | switch (screen->where) { | |
248 | case PosAbsolute: | |
249 | pScreen->x = screen->whereX; | |
250 | pScreen->y = screen->whereY; | |
251 | ++changed, screensLeft[i] = 0; | |
252 | break; | |
253 | case PosRelative: | |
254 | ref = screen->whereRefScreen; | |
255 | if (screensLeft[ref]) | |
256 | break; | |
257 | refScreen = screenInfo.screens[ref]; | |
258 | pScreen->x = refScreen->x + screen->whereX; | |
259 | pScreen->y = refScreen->y + screen->whereY; | |
260 | ++changed, screensLeft[i] = 0; | |
261 | break; | |
262 | case PosRightOf: | |
263 | ref = screen->whereRefScreen; | |
264 | if (screensLeft[ref]) | |
265 | break; | |
266 | refScreen = screenInfo.screens[ref]; | |
267 | pScreen->x = refScreen->x + refScreen->width; | |
268 | pScreen->y = refScreen->y; | |
269 | ++changed, screensLeft[i] = 0; | |
270 | break; | |
271 | case PosLeftOf: | |
272 | ref = screen->whereRefScreen; | |
273 | if (screensLeft[ref]) | |
274 | break; | |
275 | refScreen = screenInfo.screens[ref]; | |
276 | pScreen->x = refScreen->x - pScreen->width; | |
277 | pScreen->y = refScreen->y; | |
278 | ++changed, screensLeft[i] = 0; | |
279 | break; | |
280 | case PosBelow: | |
281 | ref = screen->whereRefScreen; | |
282 | if (screensLeft[ref]) | |
283 | break; | |
284 | refScreen = screenInfo.screens[ref]; | |
285 | pScreen->x = refScreen->x; | |
286 | pScreen->y = refScreen->y + refScreen->height; | |
287 | ++changed, screensLeft[i] = 0; | |
288 | break; | |
289 | case PosAbove: | |
290 | ref = screen->whereRefScreen; | |
291 | if (screensLeft[ref]) | |
292 | break; | |
293 | refScreen = screenInfo.screens[ref]; | |
294 | pScreen->x = refScreen->x; | |
295 | pScreen->y = refScreen->y - pScreen->height; | |
296 | ++changed, screensLeft[i] = 0; | |
297 | break; | |
298 | case PosNone: | |
299 | dmxLog(dmxFatal, "No position information for screen %d\n", i); | |
300 | } | |
301 | } | |
302 | return changed; | |
303 | } | |
304 | ||
305 | static void | |
306 | dmxComputeScreenOrigins(void) | |
307 | { | |
308 | ScreenPtr pScreen; | |
309 | int *screensLeft; | |
310 | int i, ref; | |
311 | int minX, minY; | |
312 | ||
313 | /* Compute origins based on | |
314 | * configuration information. */ | |
315 | screensLeft = dmxSLCreate(); | |
316 | while ((i = dmxSLFindNext(screensLeft)) >= 0) { | |
317 | while (dmxTryComputeScreenOrigins(screensLeft)); | |
318 | if ((i = dmxSLFindNext(screensLeft)) >= 0) { | |
319 | /* All of the remaining screens are referencing each other. | |
320 | * Assign a value to one of them and go through again. This | |
321 | * guarantees that we will eventually terminate. | |
322 | */ | |
323 | ref = dmxScreens[i].whereRefScreen; | |
324 | pScreen = screenInfo.screens[ref]; | |
325 | pScreen->x = pScreen->y = 0; | |
326 | screensLeft[ref] = 0; | |
327 | } | |
328 | } | |
329 | dmxSLFree(screensLeft); | |
330 | ||
331 | /* Justify the topmost and leftmost to | |
332 | * (0,0). */ | |
333 | minX = screenInfo.screens[0]->x; | |
334 | minY = screenInfo.screens[0]->y; | |
335 | for (i = 1; i < dmxNumScreens; i++) { /* Compute minX, minY */ | |
336 | if (screenInfo.screens[i]->x < minX) | |
337 | minX = screenInfo.screens[i]->x; | |
338 | if (screenInfo.screens[i]->y < minY) | |
339 | minY = screenInfo.screens[i]->y; | |
340 | } | |
341 | if (minX || minY) { | |
342 | for (i = 0; i < dmxNumScreens; i++) { | |
343 | screenInfo.screens[i]->x -= minX; | |
344 | screenInfo.screens[i]->y -= minY; | |
345 | } | |
346 | } | |
347 | ||
348 | update_desktop_dimensions(); | |
349 | } | |
350 | ||
351 | /** Recompute origin information in the #dmxScreens list. This is | |
352 | * called from #dmxInitOrigins. */ | |
353 | void | |
354 | dmxReInitOrigins(void) | |
355 | { | |
356 | int i; | |
357 | ||
358 | if (dmxNumScreens > MAXSCREENS) | |
359 | dmxLog(dmxFatal, "dmxNumScreens = %d > MAXSCREENS = %d\n", | |
360 | dmxNumScreens, MAXSCREENS); | |
361 | ||
362 | for (i = 0; i < dmxNumScreens; i++) { | |
363 | DMXScreenInfo *dmxScreen = &dmxScreens[i]; | |
364 | ||
365 | dmxLogOutput(dmxScreen, | |
366 | "s=%dx%d%+d%+d r=%dx%d%+d%+d @%d,%d" | |
367 | " (be=%dx%d depth=%d bpp=%d)\n", | |
368 | dmxScreen->scrnWidth, dmxScreen->scrnHeight, | |
369 | dmxScreen->scrnX, dmxScreen->scrnY, | |
370 | dmxScreen->rootWidth, dmxScreen->rootHeight, | |
371 | dmxScreen->rootX, dmxScreen->rootY, | |
372 | dmxScreen->rootXOrigin, dmxScreen->rootYOrigin, | |
373 | dmxScreen->beWidth, dmxScreen->beHeight, | |
374 | dmxScreen->beDepth, dmxScreen->beBPP); | |
375 | } | |
376 | } | |
377 | ||
378 | /** Initialize screen origins (and relative position). This is called | |
379 | * for each server generation. For dynamic reconfiguration, use | |
380 | * #dmxReInitOrigins() instead. */ | |
381 | void | |
382 | dmxInitOrigins(void) | |
383 | { | |
384 | int i; | |
385 | ||
386 | if (dmxNumScreens > MAXSCREENS) | |
387 | dmxLog(dmxFatal, "dmxNumScreens = %d > MAXSCREENS = %d\n", | |
388 | dmxNumScreens, MAXSCREENS); | |
389 | ||
390 | for (i = 0; i < dmxNumScreens; i++) { | |
391 | DMXScreenInfo *dmxScreen = &dmxScreens[i]; | |
392 | ||
393 | dmxLogOutput(dmxScreen, | |
394 | "(request) s=%dx%d%+d%+d r=%dx%d%+d%+d @%d,%d (%d)" | |
395 | " (be=%dx%d depth=%d bpp=%d)\n", | |
396 | dmxScreen->scrnWidth, dmxScreen->scrnHeight, | |
397 | dmxScreen->scrnX, dmxScreen->scrnY, | |
398 | dmxScreen->rootWidth, dmxScreen->rootHeight, | |
399 | dmxScreen->rootX, dmxScreen->rootY, | |
400 | dmxScreen->whereX, dmxScreen->whereY, | |
401 | dmxScreen->where, | |
402 | dmxScreen->beWidth, dmxScreen->beHeight, | |
403 | dmxScreen->beDepth, dmxScreen->beBPP); | |
404 | } | |
405 | ||
406 | dmxComputeScreenOrigins(); | |
407 | ||
408 | for (i = 0; i < dmxNumScreens; i++) { | |
409 | DMXScreenInfo *dmxScreen = &dmxScreens[i]; | |
410 | ||
411 | dmxScreen->rootXOrigin = screenInfo.screens[i]->x; | |
412 | dmxScreen->rootYOrigin = screenInfo.screens[i]->y; | |
413 | } | |
414 | ||
415 | dmxReInitOrigins(); | |
416 | } | |
417 | ||
418 | /** Returns non-zero if the global \a x, \a y coordinate is on the | |
419 | * screen window of the \a dmxScreen. */ | |
420 | int | |
421 | dmxOnScreen(int x, int y, DMXScreenInfo * dmxScreen) | |
422 | { | |
423 | #if DMX_CURSOR_DEBUG > 1 | |
424 | dmxLog(dmxDebug, | |
425 | "dmxOnScreen %d %d,%d (r=%dx%d%+d%+d@%d,%d s=%dx%d%+d%+d)\n", | |
426 | dmxScreen->index, x, y, | |
427 | dmxScreen->rootWidth, dmxScreen->rootHeight, | |
428 | dmxScreen->rootX, dmxScreen->rootY, | |
429 | dmxScreen->rootXOrigin, dmxScreen->rootYOrigin, | |
430 | dmxScreen->scrnWidth, dmxScreen->scrnHeight, | |
431 | dmxScreen->scrnX, dmxScreen->scrnY); | |
432 | #endif | |
433 | if (x >= dmxScreen->rootXOrigin | |
434 | && x < dmxScreen->rootXOrigin + dmxScreen->rootWidth | |
435 | && y >= dmxScreen->rootYOrigin | |
436 | && y < dmxScreen->rootYOrigin + dmxScreen->rootHeight) | |
437 | return 1; | |
438 | return 0; | |
439 | } | |
440 | ||
441 | /** Returns non-zero if \a a overlaps \a b. */ | |
442 | static int | |
443 | dmxDoesOverlap(DMXScreenInfo * a, DMXScreenInfo * b) | |
444 | { | |
445 | if (dmxOnScreen(a->rootXOrigin, a->rootYOrigin, b)) | |
446 | return 1; | |
447 | ||
448 | if (dmxOnScreen(a->rootXOrigin, a->rootYOrigin + a->scrnWidth, b)) | |
449 | return 1; | |
450 | ||
451 | if (dmxOnScreen(a->rootXOrigin + a->scrnHeight, a->rootYOrigin, b)) | |
452 | return 1; | |
453 | ||
454 | if (dmxOnScreen(a->rootXOrigin + a->scrnHeight, | |
455 | a->rootYOrigin + a->scrnWidth, b)) | |
456 | return 1; | |
457 | ||
458 | if (dmxOnScreen(b->rootXOrigin, b->rootYOrigin, a)) | |
459 | return 1; | |
460 | ||
461 | if (dmxOnScreen(b->rootXOrigin, b->rootYOrigin + b->scrnWidth, a)) | |
462 | return 1; | |
463 | ||
464 | if (dmxOnScreen(b->rootXOrigin + b->scrnHeight, b->rootYOrigin, a)) | |
465 | return 1; | |
466 | ||
467 | if (dmxOnScreen(b->rootXOrigin + b->scrnHeight, | |
468 | b->rootYOrigin + b->scrnWidth, a)) | |
469 | return 1; | |
470 | ||
471 | return 0; | |
472 | } | |
473 | ||
474 | /** Used with \a dmxInterateOverlap to print out a list of screens which | |
475 | * overlap each other. */ | |
476 | static void * | |
477 | dmxPrintOverlap(DMXScreenInfo * dmxScreen, void *closure) | |
478 | { | |
479 | DMXScreenInfo *a = closure; | |
480 | ||
481 | if (dmxScreen != a) { | |
482 | if (dmxScreen->cursorNotShared) | |
483 | dmxLogOutputCont(a, " [%d/%s]", dmxScreen->index, dmxScreen->name); | |
484 | else | |
485 | dmxLogOutputCont(a, " %d/%s", dmxScreen->index, dmxScreen->name); | |
486 | } | |
487 | return NULL; | |
488 | } | |
489 | ||
490 | /** Iterate over the screens which overlap with the \a start screen, | |
491 | * calling \a f with the \a closure for each argument. Often used with | |
492 | * #dmxPrintOverlap. */ | |
493 | static void * | |
494 | dmxIterateOverlap(DMXScreenInfo * start, | |
495 | void *(*f) (DMXScreenInfo * dmxScreen, void *), void *closure) | |
496 | { | |
497 | DMXScreenInfo *pt; | |
498 | ||
499 | if (!start->over) | |
500 | return f(start, closure); | |
501 | ||
502 | for (pt = start->over; /* condition at end of loop */ ; pt = pt->over) { | |
503 | void *retval; | |
504 | ||
505 | if ((retval = f(pt, closure))) | |
506 | return retval; | |
507 | if (pt == start) | |
508 | break; | |
509 | } | |
510 | return NULL; | |
511 | } | |
512 | ||
513 | /** Used with #dmxPropertyIterate to determine if screen \a a is the | |
514 | * same as the screen \a closure. */ | |
515 | static void * | |
516 | dmxTestSameDisplay(DMXScreenInfo * a, void *closure) | |
517 | { | |
518 | DMXScreenInfo *b = closure; | |
519 | ||
520 | if (a == b) | |
521 | return a; | |
522 | return NULL; | |
523 | } | |
524 | ||
525 | /** Detects overlapping dmxScreens and creates circular lists. This | |
526 | * uses an O(dmxNumScreens^2) algorithm, but dmxNumScreens is < 100 and | |
527 | * the computation only needs to be performed for every server | |
528 | * generation or dynamic reconfiguration . */ | |
529 | void | |
530 | dmxInitOverlap(void) | |
531 | { | |
532 | int i, j; | |
533 | DMXScreenInfo *a, *b, *pt; | |
534 | ||
535 | for (i = 0; i < dmxNumScreens; i++) | |
536 | dmxScreens[i].over = NULL; | |
537 | ||
538 | for (i = 0; i < dmxNumScreens; i++) { | |
539 | a = &dmxScreens[i]; | |
540 | ||
541 | for (j = i + 1; j < dmxNumScreens; j++) { | |
542 | b = &dmxScreens[j]; | |
543 | if (b->over) | |
544 | continue; | |
545 | ||
546 | if (dmxDoesOverlap(a, b)) { | |
547 | DMXDBG6("%d overlaps %d: a=%p %p b=%p %p\n", | |
548 | a->index, b->index, a, a->over, b, b->over); | |
549 | b->over = (a->over ? a->over : a); | |
550 | a->over = b; | |
551 | } | |
552 | } | |
553 | } | |
554 | ||
555 | for (i = 0; i < dmxNumScreens; i++) { | |
556 | a = &dmxScreens[i]; | |
557 | ||
558 | if (!a->over) | |
559 | continue; | |
560 | ||
561 | /* Flag all pairs that are on same display */ | |
562 | for (pt = a->over; pt != a; pt = pt->over) { | |
563 | if (dmxPropertyIterate(a, dmxTestSameDisplay, pt)) { | |
564 | /* The ->over sets contain the transitive set of screens | |
565 | * that overlap. For screens that are on the same | |
566 | * backend display, we only want to exclude pairs of | |
567 | * screens that mutually overlap on the backend display, | |
568 | * so we call dmxDoesOverlap, which is stricter than the | |
569 | * ->over set. */ | |
570 | if (!dmxDoesOverlap(a, pt)) | |
571 | continue; | |
572 | a->cursorNotShared = 1; | |
573 | pt->cursorNotShared = 1; | |
574 | dmxLog(dmxInfo, | |
575 | "Screen %d and %d overlap on %s\n", | |
576 | a->index, pt->index, a->name); | |
577 | } | |
578 | } | |
579 | } | |
580 | ||
581 | for (i = 0; i < dmxNumScreens; i++) { | |
582 | a = &dmxScreens[i]; | |
583 | ||
584 | if (a->over) { | |
585 | dmxLogOutput(a, "Overlaps"); | |
586 | dmxIterateOverlap(a, dmxPrintOverlap, a); | |
587 | dmxLogOutputCont(a, "\n"); | |
588 | } | |
589 | } | |
590 | } | |
591 | ||
592 | /** Create \a pCursor on the back-end associated with \a pScreen. */ | |
593 | void | |
594 | dmxBECreateCursor(ScreenPtr pScreen, CursorPtr pCursor) | |
595 | { | |
596 | DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; | |
597 | dmxCursorPrivPtr pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen); | |
598 | CursorBitsPtr pBits = pCursor->bits; | |
599 | Pixmap src, msk; | |
600 | XColor fg, bg; | |
601 | XImage *img; | |
602 | XlibGC gc = NULL; | |
603 | XGCValues v; | |
604 | unsigned long m; | |
605 | int i; | |
606 | ||
607 | if (!pCursorPriv) | |
608 | return; | |
609 | ||
610 | m = GCFunction | GCPlaneMask | GCForeground | GCBackground | GCClipMask; | |
611 | v.function = GXcopy; | |
612 | v.plane_mask = AllPlanes; | |
613 | v.foreground = 1L; | |
614 | v.background = 0L; | |
615 | v.clip_mask = None; | |
616 | ||
617 | for (i = 0; i < dmxScreen->beNumPixmapFormats; i++) { | |
618 | if (dmxScreen->bePixmapFormats[i].depth == 1) { | |
619 | /* Create GC in the back-end servers */ | |
620 | gc = XCreateGC(dmxScreen->beDisplay, dmxScreen->scrnDefDrawables[i], | |
621 | m, &v); | |
622 | break; | |
623 | } | |
624 | } | |
625 | if (!gc) | |
626 | dmxLog(dmxFatal, "dmxRealizeCursor: gc not initialized\n"); | |
627 | ||
628 | src = XCreatePixmap(dmxScreen->beDisplay, dmxScreen->scrnWin, | |
629 | pBits->width, pBits->height, 1); | |
630 | msk = XCreatePixmap(dmxScreen->beDisplay, dmxScreen->scrnWin, | |
631 | pBits->width, pBits->height, 1); | |
632 | ||
633 | img = XCreateImage(dmxScreen->beDisplay, | |
634 | dmxScreen->beVisuals[dmxScreen->beDefVisualIndex].visual, | |
635 | 1, XYBitmap, 0, (char *) pBits->source, | |
636 | pBits->width, pBits->height, | |
637 | BitmapPad(dmxScreen->beDisplay), 0); | |
638 | ||
639 | XPutImage(dmxScreen->beDisplay, src, gc, img, 0, 0, 0, 0, | |
640 | pBits->width, pBits->height); | |
641 | ||
642 | XFree(img); | |
643 | ||
644 | img = XCreateImage(dmxScreen->beDisplay, | |
645 | dmxScreen->beVisuals[dmxScreen->beDefVisualIndex].visual, | |
646 | 1, XYBitmap, 0, (char *) pBits->mask, | |
647 | pBits->width, pBits->height, | |
648 | BitmapPad(dmxScreen->beDisplay), 0); | |
649 | ||
650 | XPutImage(dmxScreen->beDisplay, msk, gc, img, 0, 0, 0, 0, | |
651 | pBits->width, pBits->height); | |
652 | ||
653 | XFree(img); | |
654 | ||
655 | fg.red = pCursor->foreRed; | |
656 | fg.green = pCursor->foreGreen; | |
657 | fg.blue = pCursor->foreBlue; | |
658 | ||
659 | bg.red = pCursor->backRed; | |
660 | bg.green = pCursor->backGreen; | |
661 | bg.blue = pCursor->backBlue; | |
662 | ||
663 | pCursorPriv->cursor = XCreatePixmapCursor(dmxScreen->beDisplay, | |
664 | src, msk, | |
665 | &fg, &bg, | |
666 | pBits->xhot, pBits->yhot); | |
667 | ||
668 | XFreePixmap(dmxScreen->beDisplay, src); | |
669 | XFreePixmap(dmxScreen->beDisplay, msk); | |
670 | XFreeGC(dmxScreen->beDisplay, gc); | |
671 | ||
672 | dmxSync(dmxScreen, FALSE); | |
673 | } | |
674 | ||
675 | static Bool | |
676 | _dmxRealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) | |
677 | { | |
678 | DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; | |
679 | dmxCursorPrivPtr pCursorPriv; | |
680 | ||
681 | DMXDBG2("_dmxRealizeCursor(%d,%p)\n", pScreen->myNum, pCursor); | |
682 | ||
683 | DMX_SET_CURSOR_PRIV(pCursor, pScreen, malloc(sizeof(*pCursorPriv))); | |
684 | if (!DMX_GET_CURSOR_PRIV(pCursor, pScreen)) | |
685 | return FALSE; | |
686 | ||
687 | pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen); | |
688 | pCursorPriv->cursor = (Cursor) 0; | |
689 | ||
690 | if (!dmxScreen->beDisplay) | |
691 | return TRUE; | |
692 | ||
693 | dmxBECreateCursor(pScreen, pCursor); | |
694 | return TRUE; | |
695 | } | |
696 | ||
697 | /** Free \a pCursor on the back-end associated with \a pScreen. */ | |
698 | Bool | |
699 | dmxBEFreeCursor(ScreenPtr pScreen, CursorPtr pCursor) | |
700 | { | |
701 | DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; | |
702 | dmxCursorPrivPtr pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen); | |
703 | ||
704 | if (pCursorPriv) { | |
705 | XFreeCursor(dmxScreen->beDisplay, pCursorPriv->cursor); | |
706 | pCursorPriv->cursor = (Cursor) 0; | |
707 | return TRUE; | |
708 | } | |
709 | ||
710 | return FALSE; | |
711 | } | |
712 | ||
713 | static Bool | |
714 | _dmxUnrealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) | |
715 | { | |
716 | DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; | |
717 | ||
718 | DMXDBG2("_dmxUnrealizeCursor(%d,%p)\n", pScreen->myNum, pCursor); | |
719 | ||
720 | if (dmxScreen->beDisplay) { | |
721 | if (dmxBEFreeCursor(pScreen, pCursor)) | |
722 | free(DMX_GET_CURSOR_PRIV(pCursor, pScreen)); | |
723 | } | |
724 | DMX_SET_CURSOR_PRIV(pCursor, pScreen, NULL); | |
725 | ||
726 | return TRUE; | |
727 | } | |
728 | ||
729 | static void | |
730 | _dmxMoveCursor(ScreenPtr pScreen, int x, int y) | |
731 | { | |
732 | DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; | |
733 | int newX = x + dmxScreen->rootX; | |
734 | int newY = y + dmxScreen->rootY; | |
735 | ||
736 | if (newX < 0) | |
737 | newX = 0; | |
738 | if (newY < 0) | |
739 | newY = 0; | |
740 | ||
741 | DMXDBG5("_dmxMoveCursor(%d,%d,%d) -> %d,%d\n", | |
742 | pScreen->myNum, x, y, newX, newY); | |
743 | if (dmxScreen->beDisplay) { | |
744 | XWarpPointer(dmxScreen->beDisplay, None, dmxScreen->scrnWin, | |
745 | 0, 0, 0, 0, newX, newY); | |
746 | dmxSync(dmxScreen, TRUE); | |
747 | } | |
748 | } | |
749 | ||
750 | static void | |
751 | _dmxSetCursor(ScreenPtr pScreen, CursorPtr pCursor, int x, int y) | |
752 | { | |
753 | DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; | |
754 | ||
755 | DMXDBG4("_dmxSetCursor(%d,%p,%d,%d)\n", pScreen->myNum, pCursor, x, y); | |
756 | ||
757 | if (pCursor) { | |
758 | dmxCursorPrivPtr pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen); | |
759 | ||
760 | if (pCursorPriv && dmxScreen->curCursor != pCursorPriv->cursor) { | |
761 | if (dmxScreen->beDisplay) | |
762 | XDefineCursor(dmxScreen->beDisplay, dmxScreen->scrnWin, | |
763 | pCursorPriv->cursor); | |
764 | dmxScreen->cursor = pCursor; | |
765 | dmxScreen->curCursor = pCursorPriv->cursor; | |
766 | dmxScreen->cursorVisible = 1; | |
767 | } | |
768 | _dmxMoveCursor(pScreen, x, y); | |
769 | } | |
770 | else { | |
771 | if (dmxScreen->beDisplay) | |
772 | XDefineCursor(dmxScreen->beDisplay, dmxScreen->scrnWin, | |
773 | dmxScreen->noCursor); | |
774 | dmxScreen->cursor = NULL; | |
775 | dmxScreen->curCursor = (Cursor) 0; | |
776 | dmxScreen->cursorVisible = 0; | |
777 | } | |
778 | if (dmxScreen->beDisplay) | |
779 | dmxSync(dmxScreen, TRUE); | |
780 | } | |
781 | ||
782 | static Bool | |
783 | dmxRealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor) | |
784 | { | |
785 | DMXScreenInfo *start = &dmxScreens[pScreen->myNum]; | |
786 | DMXScreenInfo *pt; | |
787 | ||
788 | if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared) | |
789 | return _dmxRealizeCursor(pScreen, pCursor); | |
790 | ||
791 | for (pt = start->over; /* condition at end of loop */ ; pt = pt->over) { | |
792 | if (pt->cursorNotShared) | |
793 | continue; | |
794 | _dmxRealizeCursor(screenInfo.screens[pt->index], pCursor); | |
795 | if (pt == start) | |
796 | break; | |
797 | } | |
798 | return TRUE; | |
799 | } | |
800 | ||
801 | static Bool | |
802 | dmxUnrealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor) | |
803 | { | |
804 | DMXScreenInfo *start = &dmxScreens[pScreen->myNum]; | |
805 | DMXScreenInfo *pt; | |
806 | ||
807 | if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared) | |
808 | return _dmxUnrealizeCursor(pScreen, pCursor); | |
809 | ||
810 | for (pt = start->over; /* condition at end of loop */ ; pt = pt->over) { | |
811 | if (pt->cursorNotShared) | |
812 | continue; | |
813 | _dmxUnrealizeCursor(screenInfo.screens[pt->index], pCursor); | |
814 | if (pt == start) | |
815 | break; | |
816 | } | |
817 | return TRUE; | |
818 | } | |
819 | ||
820 | static CursorPtr | |
821 | dmxFindCursor(DMXScreenInfo * start) | |
822 | { | |
823 | DMXScreenInfo *pt; | |
824 | ||
825 | if (!start || !start->over) | |
826 | return GetSpriteCursor(inputInfo.pointer); | |
827 | for (pt = start->over; /* condition at end of loop */ ; pt = pt->over) { | |
828 | if (pt->cursor) | |
829 | return pt->cursor; | |
830 | if (pt == start) | |
831 | break; | |
832 | } | |
833 | return GetSpriteCursor(inputInfo.pointer); | |
834 | } | |
835 | ||
836 | /** Move the cursor to coordinates (\a x, \a y)on \a pScreen. This | |
837 | * function is usually called via #dmxPointerSpriteFuncs, except during | |
838 | * reconfiguration when the cursor is repositioned to force an update on | |
839 | * newley overlapping screens and on screens that no longer overlap. | |
840 | * | |
841 | * The coords (x,y) are in global coord space. We'll loop over the | |
842 | * back-end screens and see if they contain the global coord. If so, call | |
843 | * _dmxMoveCursor() (XWarpPointer) to position the pointer on that screen. | |
844 | */ | |
845 | void | |
846 | dmxMoveCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y) | |
847 | { | |
848 | DMXScreenInfo *start = &dmxScreens[pScreen->myNum]; | |
849 | DMXScreenInfo *pt; | |
850 | ||
851 | DMXDBG3("dmxMoveCursor(%d,%d,%d)\n", pScreen->myNum, x, y); | |
852 | ||
853 | if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared) { | |
854 | _dmxMoveCursor(pScreen, x, y); | |
855 | return; | |
856 | } | |
857 | ||
858 | for (pt = start->over; /* condition at end of loop */ ; pt = pt->over) { | |
859 | if (pt->cursorNotShared) | |
860 | continue; | |
861 | if (dmxOnScreen(x + start->rootXOrigin, y + start->rootYOrigin, pt)) { | |
862 | if ( /* pt != start && */ !pt->cursorVisible) { | |
863 | if (!pt->cursor) { | |
864 | /* This only happens during | |
865 | * reconfiguration when a new overlap | |
866 | * occurs. */ | |
867 | CursorPtr pCursor; | |
868 | ||
869 | if ((pCursor = dmxFindCursor(start))) | |
870 | _dmxRealizeCursor(screenInfo.screens[pt->index], | |
871 | pt->cursor = pCursor); | |
872 | ||
873 | } | |
874 | _dmxSetCursor(screenInfo.screens[pt->index], | |
875 | pt->cursor, | |
876 | x + start->rootXOrigin - pt->rootXOrigin, | |
877 | y + start->rootYOrigin - pt->rootYOrigin); | |
878 | } | |
879 | _dmxMoveCursor(screenInfo.screens[pt->index], | |
880 | x + start->rootXOrigin - pt->rootXOrigin, | |
881 | y + start->rootYOrigin - pt->rootYOrigin); | |
882 | } | |
883 | else if ( /* pt != start && */ pt->cursorVisible) { | |
884 | _dmxSetCursor(screenInfo.screens[pt->index], | |
885 | NULL, | |
886 | x + start->rootXOrigin - pt->rootXOrigin, | |
887 | y + start->rootYOrigin - pt->rootYOrigin); | |
888 | } | |
889 | if (pt == start) | |
890 | break; | |
891 | } | |
892 | } | |
893 | ||
894 | static void | |
895 | dmxSetCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor, int x, | |
896 | int y) | |
897 | { | |
898 | DMXScreenInfo *start = &dmxScreens[pScreen->myNum]; | |
899 | DMXScreenInfo *pt; | |
900 | int GX, GY, gx, gy; | |
901 | ||
902 | DMXDBG5("dmxSetCursor(%d %p, %p,%d,%d)\n", | |
903 | pScreen->myNum, start, pCursor, x, y); | |
904 | ||
905 | /* We do this check here because of two cases: | |
906 | * | |
907 | * 1) if a client calls XWarpPointer() | |
908 | * and Xinerama is not running, we can | |
909 | * have mi's notion of the pointer | |
910 | * position out of phase with DMX's | |
911 | * notion. | |
912 | * | |
913 | * 2) if a down button is held while the | |
914 | * cursor moves outside the root window, | |
915 | * mi's notion of the pointer position | |
916 | * is out of phase with DMX's notion and | |
917 | * the cursor can remain visible when it | |
918 | * shouldn't be. */ | |
919 | ||
920 | dmxGetGlobalPosition(&GX, &GY); | |
921 | gx = start->rootXOrigin + x; | |
922 | gy = start->rootYOrigin + y; | |
923 | if (x && y && (GX != gx || GY != gy)) | |
924 | dmxCoreMotion(NULL, gx, gy, 0, DMX_NO_BLOCK); | |
925 | ||
926 | if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared) { | |
927 | _dmxSetCursor(pScreen, pCursor, x, y); | |
928 | return; | |
929 | } | |
930 | ||
931 | for (pt = start->over; /* condition at end of loop */ ; pt = pt->over) { | |
932 | if (pt->cursorNotShared) | |
933 | continue; | |
934 | if (dmxOnScreen(x + start->rootXOrigin, y + start->rootYOrigin, pt)) { | |
935 | _dmxSetCursor(screenInfo.screens[pt->index], pCursor, | |
936 | x + start->rootXOrigin - pt->rootXOrigin, | |
937 | y + start->rootYOrigin - pt->rootYOrigin); | |
938 | } | |
939 | else { | |
940 | _dmxSetCursor(screenInfo.screens[pt->index], NULL, | |
941 | x + start->rootXOrigin - pt->rootXOrigin, | |
942 | y + start->rootYOrigin - pt->rootYOrigin); | |
943 | } | |
944 | if (pt == start) | |
945 | break; | |
946 | } | |
947 | } | |
948 | ||
949 | /** This routine is used by the backend input routines to hide the | |
950 | * cursor on a screen that is being used for relative input. \see | |
951 | * dmxbackend.c */ | |
952 | void | |
953 | dmxHideCursor(DMXScreenInfo * dmxScreen) | |
954 | { | |
955 | int x, y; | |
956 | ScreenPtr pScreen = screenInfo.screens[dmxScreen->index]; | |
957 | ||
958 | dmxGetGlobalPosition(&x, &y); | |
959 | _dmxSetCursor(pScreen, NULL, x, y); | |
960 | } | |
961 | ||
962 | /** This routine is called during reconfiguration to make sure the | |
963 | * cursor is visible. */ | |
964 | void | |
965 | dmxCheckCursor(void) | |
966 | { | |
967 | int i; | |
968 | int x, y; | |
969 | ScreenPtr pScreen; | |
970 | DMXScreenInfo *firstScreen; | |
971 | ||
972 | dmxGetGlobalPosition(&x, &y); | |
973 | firstScreen = dmxFindFirstScreen(x, y); | |
974 | ||
975 | DMXDBG2("dmxCheckCursor %d %d\n", x, y); | |
976 | for (i = 0; i < dmxNumScreens; i++) { | |
977 | DMXScreenInfo *dmxScreen = &dmxScreens[i]; | |
978 | ||
979 | pScreen = screenInfo.screens[dmxScreen->index]; | |
980 | ||
981 | if (!dmxOnScreen(x, y, dmxScreen)) { | |
982 | if (firstScreen && | |
983 | i == miPointerGetScreen(inputInfo.pointer)->myNum) | |
984 | miPointerSetScreen(inputInfo.pointer, firstScreen->index, x, | |
985 | y); | |
986 | _dmxSetCursor(pScreen, NULL, x - dmxScreen->rootXOrigin, | |
987 | y - dmxScreen->rootYOrigin); | |
988 | } | |
989 | else { | |
990 | if (!dmxScreen->cursor) { | |
991 | CursorPtr pCursor; | |
992 | ||
993 | if ((pCursor = dmxFindCursor(dmxScreen))) { | |
994 | _dmxRealizeCursor(pScreen, dmxScreen->cursor = pCursor); | |
995 | } | |
996 | } | |
997 | _dmxSetCursor(pScreen, dmxScreen->cursor, | |
998 | x - dmxScreen->rootXOrigin, | |
999 | y - dmxScreen->rootYOrigin); | |
1000 | } | |
1001 | } | |
1002 | DMXDBG2(" leave dmxCheckCursor %d %d\n", x, y); | |
1003 | } | |
1004 | ||
1005 | static Bool | |
1006 | dmxDeviceCursorInitialize(DeviceIntPtr pDev, ScreenPtr pScr) | |
1007 | { | |
1008 | return TRUE; | |
1009 | } | |
1010 | ||
1011 | static void | |
1012 | dmxDeviceCursorCleanup(DeviceIntPtr pDev, ScreenPtr pScr) | |
1013 | { | |
1014 | } | |
1015 | ||
1016 | miPointerSpriteFuncRec dmxPointerSpriteFuncs = { | |
1017 | dmxRealizeCursor, | |
1018 | dmxUnrealizeCursor, | |
1019 | dmxSetCursor, | |
1020 | dmxMoveCursor, | |
1021 | dmxDeviceCursorInitialize, | |
1022 | dmxDeviceCursorCleanup | |
1023 | }; |