4 * Copyright 1998 Ulrich Weigand
12 #include "debugtools.h"
18 DEFAULT_DEBUG_CHANNEL(cursor
);
20 /**********************************************************************/
22 Cursor X11DRV_MOUSE_XCursor
= None
; /* Current X cursor */
24 static LONG X11DRV_MOUSE_WarpPointer
= 0; /* hack; see DISPLAY_MoveCursor */
25 static LPMOUSE_EVENT_PROC DefMouseEventProc
= NULL
;
27 /***********************************************************************
28 * X11DRV_MOUSE_DoSetCursor
30 static BOOL
X11DRV_MOUSE_DoSetCursor( CURSORICONINFO
*ptr
)
32 Pixmap pixmapBits
, pixmapMask
, pixmapMaskInv
, pixmapAll
;
36 if (!ptr
) /* Create an empty cursor */
38 static const char data
[] = { 0 };
40 bg
.red
= bg
.green
= bg
.blue
= 0x0000;
41 pixmapBits
= XCreateBitmapFromData( display
, X11DRV_GetXRootWindow(), data
, 1, 1 );
44 cursor
= XCreatePixmapCursor( display
, pixmapBits
, pixmapBits
,
46 XFreePixmap( display
, pixmapBits
);
49 else /* Create the X cursor from the bits */
53 if (ptr
->bPlanes
* ptr
->bBitsPerPixel
!= 1)
55 WARN("Cursor has more than 1 bpp!\n" );
59 /* Create a pixmap and transfer all the bits to it */
61 /* NOTE: Following hack works, but only because XFree depth
62 * 1 images really use 1 bit/pixel (and so the same layout
63 * as the Windows cursor data). Perhaps use a more generic
66 pixmapAll
= XCreatePixmap( display
, X11DRV_GetXRootWindow(),
67 ptr
->nWidth
, ptr
->nHeight
* 2, 1 );
68 image
= XCreateImage( display
, X11DRV_GetVisual(),
69 1, ZPixmap
, 0, (char *)(ptr
+ 1), ptr
->nWidth
,
70 ptr
->nHeight
* 2, 16, ptr
->nWidthBytes
);
73 image
->byte_order
= MSBFirst
;
74 image
->bitmap_bit_order
= MSBFirst
;
75 image
->bitmap_unit
= 16;
76 _XInitImageFuncPtrs(image
);
78 XPutImage( display
, pixmapAll
, BITMAP_monoGC
, image
,
79 0, 0, 0, 0, ptr
->nWidth
, ptr
->nHeight
* 2 );
81 XDestroyImage( image
);
84 /* Now create the 2 pixmaps for bits and mask */
86 pixmapBits
= XCreatePixmap( display
, X11DRV_GetXRootWindow(),
87 ptr
->nWidth
, ptr
->nHeight
, 1 );
88 pixmapMask
= XCreatePixmap( display
, X11DRV_GetXRootWindow(),
89 ptr
->nWidth
, ptr
->nHeight
, 1 );
90 pixmapMaskInv
= XCreatePixmap( display
, X11DRV_GetXRootWindow(),
91 ptr
->nWidth
, ptr
->nHeight
, 1 );
93 /* Make sure everything went OK so far */
95 if (pixmapBits
&& pixmapMask
&& pixmapAll
)
97 /* We have to do some magic here, as cursors are not fully
98 * compatible between Windows and X11. Under X11, there
99 * are only 3 possible color cursor: black, white and
100 * masked. So we map the 4th Windows color (invert the
101 * bits on the screen) to black and an additional white bit on
102 * an other place (+1,+1). This require some boolean arithmetic:
105 * And Xor Result | Bits Mask Result
106 * 0 0 black | 0 1 background
107 * 0 1 white | 1 1 foreground
108 * 1 0 no change | X 0 no change
109 * 1 1 inverted | 0 1 background
112 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
113 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
115 * FIXME: apparently some servers do support 'inverted' color.
116 * I don't know if it's correct per the X spec, but maybe
117 * we ought to take advantage of it. -- AJ
119 XSetFunction( display
, BITMAP_monoGC
, GXcopy
);
120 XCopyArea( display
, pixmapAll
, pixmapBits
, BITMAP_monoGC
,
121 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
122 XCopyArea( display
, pixmapAll
, pixmapMask
, BITMAP_monoGC
,
123 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
124 XCopyArea( display
, pixmapAll
, pixmapMaskInv
, BITMAP_monoGC
,
125 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
126 XSetFunction( display
, BITMAP_monoGC
, GXand
);
127 XCopyArea( display
, pixmapAll
, pixmapMaskInv
, BITMAP_monoGC
,
128 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
129 XSetFunction( display
, BITMAP_monoGC
, GXandReverse
);
130 XCopyArea( display
, pixmapAll
, pixmapBits
, BITMAP_monoGC
,
131 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
132 XSetFunction( display
, BITMAP_monoGC
, GXorReverse
);
133 XCopyArea( display
, pixmapAll
, pixmapMask
, BITMAP_monoGC
,
134 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
135 /* Additional white */
136 XSetFunction( display
, BITMAP_monoGC
, GXor
);
137 XCopyArea( display
, pixmapMaskInv
, pixmapMask
, BITMAP_monoGC
,
138 0, 0, ptr
->nWidth
, ptr
->nHeight
, 1, 1 );
139 XCopyArea( display
, pixmapMaskInv
, pixmapBits
, BITMAP_monoGC
,
140 0, 0, ptr
->nWidth
, ptr
->nHeight
, 1, 1 );
141 XSetFunction( display
, BITMAP_monoGC
, GXcopy
);
142 fg
.red
= fg
.green
= fg
.blue
= 0xffff;
143 bg
.red
= bg
.green
= bg
.blue
= 0x0000;
144 cursor
= XCreatePixmapCursor( display
, pixmapBits
, pixmapMask
,
145 &fg
, &bg
, ptr
->ptHotSpot
.x
, ptr
->ptHotSpot
.y
);
148 /* Now free everything */
150 if (pixmapAll
) XFreePixmap( display
, pixmapAll
);
151 if (pixmapBits
) XFreePixmap( display
, pixmapBits
);
152 if (pixmapMask
) XFreePixmap( display
, pixmapMask
);
153 if (pixmapMaskInv
) XFreePixmap( display
, pixmapMaskInv
);
156 if (cursor
== None
) return FALSE
;
157 if (X11DRV_MOUSE_XCursor
!= None
) XFreeCursor( display
, X11DRV_MOUSE_XCursor
);
158 X11DRV_MOUSE_XCursor
= cursor
;
163 /***********************************************************************
164 * X11DRV_SetCursor (X11DRV.@)
166 void X11DRV_SetCursor( CURSORICONINFO
*lpCursor
)
170 EnterCriticalSection( &X11DRV_CritSection
);
171 success
= CALL_LARGE_STACK( X11DRV_MOUSE_DoSetCursor
, lpCursor
);
172 LeaveCriticalSection( &X11DRV_CritSection
);
173 if ( !success
) return;
175 if (X11DRV_GetXRootWindow() != DefaultRootWindow(display
))
177 /* If in desktop mode, set the cursor on the desktop window */
179 TSXDefineCursor( display
, X11DRV_GetXRootWindow(), X11DRV_MOUSE_XCursor
);
183 /* Else, set the same cursor for all top-level windows */
185 /* FIXME: we should not reference USER internals here, but native USER
186 works only in desktop mode anyway, so this should not matter */
188 HWND hwnd
= GetWindow( GetDesktopWindow(), GW_CHILD
);
191 WND
*tmpWnd
= WIN_FindWndPtr(hwnd
);
192 Window win
= X11DRV_WND_FindXWindow(tmpWnd
);
193 if (win
&& win
!=DefaultRootWindow(display
))
194 TSXDefineCursor( display
, win
, X11DRV_MOUSE_XCursor
);
195 hwnd
= GetWindow( hwnd
, GW_HWNDNEXT
);
196 WIN_ReleaseWndPtr(tmpWnd
);
201 /***********************************************************************
202 * X11DRV_MoveCursor (X11DRV.@)
204 void X11DRV_MoveCursor(WORD wAbsX
, WORD wAbsY
)
207 * We do not want the to create MotionNotify events here,
208 * otherwise we will get an endless recursion:
209 * XMotionEvent -> MOUSEEVENTF_MOVE -> mouse_event -> DisplayMoveCursor
210 * -> XWarpPointer -> XMotionEvent -> ...
212 * Unfortunately, the XWarpPointer call does create a MotionNotify
213 * event. So, we use a hack: before MOUSE_SendEvent calls the mouse event
214 * procedure, it sets a global flag. If this flag is set, we skip the
215 * XWarpPointer call. If we are *not* called from within MOUSE_SendEvent,
216 * we will call XWarpPointer, which will create a MotionNotify event.
217 * Strictly speaking, this is also wrong, but that should normally not
218 * have any negative effects ...
220 * But first of all, we check whether we already are at the position
221 * are supposed to move to; if so, we don't need to do anything.
225 int rootX
, rootY
, winX
, winY
;
228 if (X11DRV_MOUSE_WarpPointer
< 0) return;
230 if (!TSXQueryPointer( display
, X11DRV_GetXRootWindow(), &root
, &child
,
231 &rootX
, &rootY
, &winX
, &winY
, &xstate
))
234 if ( winX
== wAbsX
&& winY
== wAbsY
)
237 TRACE("(%d,%d): moving from (%d,%d)\n", wAbsX
, wAbsY
, winX
, winY
);
239 TSXWarpPointer( display
, X11DRV_GetXRootWindow(), X11DRV_GetXRootWindow(),
240 0, 0, 0, 0, wAbsX
, wAbsY
);
243 /***********************************************************************
244 * X11DRV_InitMouse (X11DRV.@)
246 void X11DRV_InitMouse( LPMOUSE_EVENT_PROC proc
)
248 static int init_done
;
250 DefMouseEventProc
= proc
;
255 int root_x
, root_y
, child_x
, child_y
;
256 unsigned int KeyState
;
259 /* Get the current mouse position and simulate an absolute mouse
260 movement to initialize the mouse global variables */
261 TSXQueryPointer( display
, X11DRV_GetXRootWindow(), &root
, &child
,
262 &root_x
, &root_y
, &child_x
, &child_y
, &KeyState
);
263 X11DRV_SendEvent(MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
,
264 root_x
, root_y
, X11DRV_EVENT_XStateToKeyState(KeyState
),
270 /***********************************************************************
271 * X11DRV_SendEvent (internal)
273 void X11DRV_SendEvent( DWORD mouseStatus
, DWORD posX
, DWORD posY
,
274 DWORD keyState
, DWORD time
, HWND hWnd
)
276 int width
= GetSystemMetrics( SM_CXSCREEN
);
277 int height
= GetSystemMetrics( SM_CYSCREEN
);
281 if ( !DefMouseEventProc
) return;
283 TRACE("(%04lX,%ld,%ld)\n", mouseStatus
, posX
, posY
);
285 if (mouseStatus
& MOUSEEVENTF_MOVE
) {
286 if (mouseStatus
& MOUSEEVENTF_ABSOLUTE
) {
287 /* Relative mouse movements seems not to be scaled as absolute ones */
288 posX
= (((long)posX
<< 16) + width
-1) / width
;
289 posY
= (((long)posY
<< 16) + height
-1) / height
;
293 wme
.magic
= WINE_MOUSEEVENT_MAGIC
;
296 wme
.keyState
= keyState
;
298 InterlockedDecrement( &X11DRV_MOUSE_WarpPointer
);
299 /* To avoid deadlocks, we have to suspend all locks on windows structures
300 before the program control is passed to the mouse driver */
301 iWndsLocks
= WIN_SuspendWndsLock();
302 DefMouseEventProc( mouseStatus
, posX
, posY
, 0, (DWORD
)&wme
);
303 WIN_RestoreWndsLock(iWndsLocks
);
304 InterlockedIncrement( &X11DRV_MOUSE_WarpPointer
);