4 * Copyright 1998 Ulrich Weigand
9 #ifndef X_DISPLAY_MISSING
14 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(cursor
)
22 /**********************************************************************/
24 Cursor X11DRV_MOUSE_XCursor
= None
; /* Current X cursor */
26 static LONG X11DRV_MOUSE_WarpPointer
= 0; /* hack; see DISPLAY_MoveCursor */
28 /***********************************************************************
29 * X11DRV_MOUSE_DoSetCursor
31 static BOOL
X11DRV_MOUSE_DoSetCursor( CURSORICONINFO
*ptr
)
33 Pixmap pixmapBits
, pixmapMask
, pixmapMaskInv
, pixmapAll
;
37 if (!ptr
) /* Create an empty cursor */
39 static const char data
[] = { 0 };
41 bg
.red
= bg
.green
= bg
.blue
= 0x0000;
42 pixmapBits
= XCreateBitmapFromData( display
, X11DRV_GetXRootWindow(), data
, 1, 1 );
45 cursor
= XCreatePixmapCursor( display
, pixmapBits
, pixmapBits
,
47 XFreePixmap( display
, pixmapBits
);
50 else /* Create the X cursor from the bits */
54 if (ptr
->bPlanes
* ptr
->bBitsPerPixel
!= 1)
56 WARN("Cursor has more than 1 bpp!\n" );
60 /* Create a pixmap and transfer all the bits to it */
62 /* NOTE: Following hack works, but only because XFree depth
63 * 1 images really use 1 bit/pixel (and so the same layout
64 * as the Windows cursor data). Perhaps use a more generic
67 pixmapAll
= XCreatePixmap( display
, X11DRV_GetXRootWindow(),
68 ptr
->nWidth
, ptr
->nHeight
* 2, 1 );
69 image
= XCreateImage( display
, X11DRV_GetVisual(),
70 1, ZPixmap
, 0, (char *)(ptr
+ 1), ptr
->nWidth
,
71 ptr
->nHeight
* 2, 16, ptr
->nWidthBytes
);
74 image
->byte_order
= MSBFirst
;
75 image
->bitmap_bit_order
= MSBFirst
;
76 image
->bitmap_unit
= 16;
77 _XInitImageFuncPtrs(image
);
79 XPutImage( display
, pixmapAll
, BITMAP_monoGC
, image
,
80 0, 0, 0, 0, ptr
->nWidth
, ptr
->nHeight
* 2 );
82 XDestroyImage( image
);
85 /* Now create the 2 pixmaps for bits and mask */
87 pixmapBits
= XCreatePixmap( display
, X11DRV_GetXRootWindow(),
88 ptr
->nWidth
, ptr
->nHeight
, 1 );
89 pixmapMask
= XCreatePixmap( display
, X11DRV_GetXRootWindow(),
90 ptr
->nWidth
, ptr
->nHeight
, 1 );
91 pixmapMaskInv
= XCreatePixmap( display
, X11DRV_GetXRootWindow(),
92 ptr
->nWidth
, ptr
->nHeight
, 1 );
94 /* Make sure everything went OK so far */
96 if (pixmapBits
&& pixmapMask
&& pixmapAll
)
98 /* We have to do some magic here, as cursors are not fully
99 * compatible between Windows and X11. Under X11, there
100 * are only 3 possible color cursor: black, white and
101 * masked. So we map the 4th Windows color (invert the
102 * bits on the screen) to black and an additional white bit on
103 * an other place (+1,+1). This require some boolean arithmetic:
106 * And Xor Result | Bits Mask Result
107 * 0 0 black | 0 1 background
108 * 0 1 white | 1 1 foreground
109 * 1 0 no change | X 0 no change
110 * 1 1 inverted | 0 1 background
113 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
114 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
116 * FIXME: apparently some servers do support 'inverted' color.
117 * I don't know if it's correct per the X spec, but maybe
118 * we ought to take advantage of it. -- AJ
120 XSetFunction( display
, BITMAP_monoGC
, GXcopy
);
121 XCopyArea( display
, pixmapAll
, pixmapBits
, BITMAP_monoGC
,
122 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
123 XCopyArea( display
, pixmapAll
, pixmapMask
, BITMAP_monoGC
,
124 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
125 XCopyArea( display
, pixmapAll
, pixmapMaskInv
, BITMAP_monoGC
,
126 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
127 XSetFunction( display
, BITMAP_monoGC
, GXand
);
128 XCopyArea( display
, pixmapAll
, pixmapMaskInv
, BITMAP_monoGC
,
129 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
130 XSetFunction( display
, BITMAP_monoGC
, GXandReverse
);
131 XCopyArea( display
, pixmapAll
, pixmapBits
, BITMAP_monoGC
,
132 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
133 XSetFunction( display
, BITMAP_monoGC
, GXorReverse
);
134 XCopyArea( display
, pixmapAll
, pixmapMask
, BITMAP_monoGC
,
135 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
136 /* Additional white */
137 XSetFunction( display
, BITMAP_monoGC
, GXor
);
138 XCopyArea( display
, pixmapMaskInv
, pixmapMask
, BITMAP_monoGC
,
139 0, 0, ptr
->nWidth
, ptr
->nHeight
, 1, 1 );
140 XCopyArea( display
, pixmapMaskInv
, pixmapBits
, BITMAP_monoGC
,
141 0, 0, ptr
->nWidth
, ptr
->nHeight
, 1, 1 );
142 XSetFunction( display
, BITMAP_monoGC
, GXcopy
);
143 fg
.red
= fg
.green
= fg
.blue
= 0xffff;
144 bg
.red
= bg
.green
= bg
.blue
= 0x0000;
145 cursor
= XCreatePixmapCursor( display
, pixmapBits
, pixmapMask
,
146 &fg
, &bg
, ptr
->ptHotSpot
.x
, ptr
->ptHotSpot
.y
);
149 /* Now free everything */
151 if (pixmapAll
) XFreePixmap( display
, pixmapAll
);
152 if (pixmapBits
) XFreePixmap( display
, pixmapBits
);
153 if (pixmapMask
) XFreePixmap( display
, pixmapMask
);
154 if (pixmapMaskInv
) XFreePixmap( display
, pixmapMaskInv
);
157 if (cursor
== None
) return FALSE
;
158 if (X11DRV_MOUSE_XCursor
!= None
) XFreeCursor( display
, X11DRV_MOUSE_XCursor
);
159 X11DRV_MOUSE_XCursor
= cursor
;
164 /***********************************************************************
165 * X11DRV_MOUSE_SetCursor
167 void X11DRV_MOUSE_SetCursor( CURSORICONINFO
*lpCursor
)
171 EnterCriticalSection( &X11DRV_CritSection
);
172 success
= CALL_LARGE_STACK( X11DRV_MOUSE_DoSetCursor
, lpCursor
);
173 LeaveCriticalSection( &X11DRV_CritSection
);
174 if ( !success
) return;
176 if (X11DRV_GetXRootWindow() != DefaultRootWindow(display
))
178 /* If in desktop mode, set the cursor on the desktop window */
180 TSXDefineCursor( display
, X11DRV_GetXRootWindow(), X11DRV_MOUSE_XCursor
);
184 /* Else, set the same cursor for all top-level windows */
186 /* FIXME: we should not reference USER internals here, but native USER
187 works only in desktop mode anyway, so this should not matter */
189 HWND hwnd
= GetWindow( GetDesktopWindow(), GW_CHILD
);
192 WND
*tmpWnd
= WIN_FindWndPtr(hwnd
);
193 Window win
= X11DRV_WND_FindXWindow(tmpWnd
);
194 if (win
&& win
!=DefaultRootWindow(display
))
195 TSXDefineCursor( display
, win
, X11DRV_MOUSE_XCursor
);
196 hwnd
= GetWindow( hwnd
, GW_HWNDNEXT
);
197 WIN_ReleaseWndPtr(tmpWnd
);
202 /***********************************************************************
203 * X11DRV_MOUSE_MoveCursor
205 void X11DRV_MOUSE_MoveCursor(WORD wAbsX
, WORD wAbsY
)
208 * We do not want the to create MotionNotify events here,
209 * otherwise we will get an endless recursion:
210 * XMotionEvent -> MOUSEEVENTF_MOVE -> mouse_event -> DisplayMoveCursor
211 * -> XWarpPointer -> XMotionEvent -> ...
213 * Unfortunately, the XWarpPointer call does create a MotionNotify
214 * event. So, we use a hack: before MOUSE_SendEvent calls the mouse event
215 * procedure, it sets a global flag. If this flag is set, we skip the
216 * XWarpPointer call. If we are *not* called from within MOUSE_SendEvent,
217 * we will call XWarpPointer, which will create a MotionNotify event.
218 * Strictly speaking, this is also wrong, but that should normally not
219 * have any negative effects ...
221 * But first of all, we check whether we already are at the position
222 * are supposed to move to; if so, we don't need to do anything.
226 int rootX
, rootY
, winX
, winY
;
229 if (X11DRV_MOUSE_WarpPointer
< 0) return;
231 if (!TSXQueryPointer( display
, X11DRV_GetXRootWindow(), &root
, &child
,
232 &rootX
, &rootY
, &winX
, &winY
, &xstate
))
235 if ( winX
== wAbsX
&& winY
== wAbsY
)
238 TRACE("(%d,%d): moving from (%d,%d)\n", wAbsX
, wAbsY
, winX
, winY
);
240 TSXWarpPointer( display
, X11DRV_GetXRootWindow(), X11DRV_GetXRootWindow(),
241 0, 0, 0, 0, wAbsX
, wAbsY
);
244 /***********************************************************************
245 * X11DRV_MOUSE_EnableWarpPointer
247 LONG
X11DRV_MOUSE_EnableWarpPointer(BOOL bEnable
)
250 return InterlockedIncrement( &X11DRV_MOUSE_WarpPointer
);
252 return InterlockedDecrement( &X11DRV_MOUSE_WarpPointer
);
255 /***********************************************************************
258 void X11DRV_MOUSE_Init()
261 int root_x
, root_y
, child_x
, child_y
;
262 unsigned int KeyState
;
264 /* Get the current mouse position and simulate an absolute mouse
265 movement to initialize the mouse global variables */
266 TSXQueryPointer( display
, X11DRV_GetXRootWindow(), &root
, &child
,
267 &root_x
, &root_y
, &child_x
, &child_y
, &KeyState
);
269 MOUSE_SendEvent(MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
,
271 X11DRV_EVENT_XStateToKeyState(KeyState
),
277 #endif /* !defined(X_DISPLAY_MISSING) */