Add CF_UNICODETEXT as primary text clipboard format.
[wine] / windows / x11drv / event.c
1 /*
2  * X11 event driver
3  *
4  * Copyright 1993 Alexandre Julliard
5  *           1999 Noel Borthwick
6  */
7
8 #include "config.h"
9
10 #include <X11/Xatom.h>
11 #include <X11/keysym.h>
12
13 #include "ts_xlib.h"
14 #include "ts_xresource.h"
15 #include "ts_xutil.h"
16 #ifdef HAVE_LIBXXSHM
17 #include "ts_xshm.h"
18 #endif
19 #ifdef HAVE_LIBXXF86DGA2
20 #include "ts_xf86dga2.h"
21 #endif
22
23 #include <assert.h>
24 #include <string.h>
25 #include "wine/winuser16.h"
26 #include "shlobj.h"  /* DROPFILES */
27
28 #include "clipboard.h"
29 #include "dce.h"
30 #include "debugtools.h"
31 #include "heap.h"
32 #include "input.h"
33 #include "keyboard.h"
34 #include "message.h"
35 #include "mouse.h"
36 #include "options.h"
37 #include "queue.h"
38 #include "win.h"
39 #include "winpos.h"
40 #include "services.h"
41 #include "file.h"
42 #include "windef.h"
43 #include "x11drv.h"
44
45 DEFAULT_DEBUG_CHANNEL(event);
46 DECLARE_DEBUG_CHANNEL(win);
47   
48 /* X context to associate a hwnd to an X window */
49 extern XContext winContext;
50
51 extern Atom wmProtocols;
52 extern Atom wmDeleteWindow;
53 extern Atom dndProtocol;
54 extern Atom dndSelection;
55
56 extern void X11DRV_KEYBOARD_UpdateState(void);
57 extern void X11DRV_KEYBOARD_HandleEvent(WND *pWnd, XKeyEvent *event);
58
59 #define NB_BUTTONS      5     /* Windows can handle 3 buttons and the wheel too */
60
61
62 #define DndNotDnd       -1    /* OffiX drag&drop */
63 #define DndUnknown      0
64 #define DndRawData      1
65 #define DndFile         2
66 #define DndFiles        3
67 #define DndText         4
68 #define DndDir          5
69 #define DndLink         6
70 #define DndExe          7
71
72 #define DndEND          8
73
74 #define DndURL          128   /* KDE drag&drop */
75
76 /* The last X window which had the focus */
77 static Window glastXFocusWin = 0;
78
79 static const char * const event_names[] =
80 {
81   "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
82   "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
83   "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
84   "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
85   "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
86   "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
87   "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
88   "ClientMessage", "MappingNotify"
89 };
90
91
92 static void CALLBACK EVENT_Flush( ULONG_PTR arg );
93 static void CALLBACK EVENT_ProcessAllEvents( ULONG_PTR arg );
94 static void EVENT_ProcessEvent( XEvent *event );
95 BOOL X11DRV_CheckFocus(void);
96
97   /* Event handlers */
98 static void EVENT_Key( HWND hWnd, XKeyEvent *event );
99 static void EVENT_ButtonPress( HWND hWnd, XButtonEvent *event );
100 static void EVENT_ButtonRelease( HWND hWnd, XButtonEvent *event );
101 static void EVENT_MotionNotify( HWND hWnd, XMotionEvent *event );
102 static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event );
103 static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event );
104 static void EVENT_Expose( HWND hWnd, XExposeEvent *event );
105 static void EVENT_GraphicsExpose( HWND hWnd, XGraphicsExposeEvent *event );
106 static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *event );
107 static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple );
108 static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event);
109 static void EVENT_PropertyNotify( XPropertyEvent *event );
110 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event );
111 static void EVENT_MapNotify( HWND pWnd, XMapEvent *event );
112 static void EVENT_UnmapNotify( HWND pWnd, XUnmapEvent *event );
113 static void EVENT_MappingNotify( XMappingEvent *event );
114
115 #ifdef HAVE_LIBXXSHM
116 static void EVENT_ShmCompletion( XShmCompletionEvent *event );
117 static int ShmAvailable, ShmCompletionType;
118 extern int XShmGetEventBase( Display * );/* Missing prototype for function in libXext. */
119 #endif
120
121 #ifdef HAVE_LIBXXF86DGA2
122 static int DGAMotionEventType;
123 static int DGAButtonPressEventType;
124 static int DGAButtonReleaseEventType;
125 static int DGAKeyPressEventType;
126 static int DGAKeyReleaseEventType;
127
128 static BOOL DGAUsed = FALSE;
129 static HWND DGAhwnd = 0;
130
131 static void EVENT_DGAMotionEvent( XDGAMotionEvent *event );
132 static void EVENT_DGAButtonPressEvent( XDGAButtonEvent *event );
133 static void EVENT_DGAButtonReleaseEvent( XDGAButtonEvent *event );
134 #endif
135
136 /* Usable only with OLVWM - compile option perhaps?
137 static void EVENT_EnterNotify( HWND hWnd, XCrossingEvent *event );
138 */
139
140 static void EVENT_GetGeometry( Window win, int *px, int *py,
141                                unsigned int *pwidth, unsigned int *pheight );
142
143
144 static BOOL bUserRepaintDisabled = TRUE;
145
146 /* Static used for the current input method */
147 static INPUT_TYPE current_input_type = X11DRV_INPUT_ABSOLUTE;
148 static BOOL in_transition = FALSE; /* This is not used as for today */
149
150 /***********************************************************************
151  *           EVENT_Init
152  */
153 BOOL X11DRV_EVENT_Init(void)
154 {
155 #ifdef HAVE_LIBXXSHM
156     ShmAvailable = XShmQueryExtension( display );
157     if (ShmAvailable) {
158         ShmCompletionType = XShmGetEventBase( display ) + ShmCompletion;
159     }
160 #endif
161
162     /* Install the X event processing callback */
163     SERVICE_AddObject( FILE_DupUnixHandle( ConnectionNumber(display), 
164                                            GENERIC_READ | SYNCHRONIZE ),
165                        EVENT_ProcessAllEvents, 0 );
166
167     /* Install the XFlush timer callback */
168     if ( Options.synchronous ) 
169         TSXSynchronize( display, True );
170     else
171         SERVICE_AddTimer( 200, EVENT_Flush, 0 );
172
173     return TRUE;
174 }
175
176 /***********************************************************************
177  *           EVENT_Flush
178  */
179 static void CALLBACK EVENT_Flush( ULONG_PTR arg )
180 {
181     TSXFlush( display );
182 }
183
184 /***********************************************************************
185  *           EVENT_ProcessAllEvents
186  */
187 static void CALLBACK EVENT_ProcessAllEvents( ULONG_PTR arg )
188 {
189     XEvent event;
190   
191     TRACE( "called (thread %lx).\n", GetCurrentThreadId() );
192
193     EnterCriticalSection( &X11DRV_CritSection );
194     while ( XPending( display ) )
195     {
196         XNextEvent( display, &event );
197       
198         LeaveCriticalSection( &X11DRV_CritSection );
199         EVENT_ProcessEvent( &event );
200         EnterCriticalSection( &X11DRV_CritSection );
201     }
202     LeaveCriticalSection( &X11DRV_CritSection );
203 }
204
205 /***********************************************************************
206  *           X11DRV_Synchronize
207  *
208  * Synchronize with the X server. Should not be used too often.
209  */
210 void X11DRV_Synchronize( void )
211 {
212     TSXSync( display, False );
213     EVENT_ProcessAllEvents( 0 );
214 }
215
216 /***********************************************************************
217  *           X11DRV_UserRepaintDisable
218  */
219 void X11DRV_UserRepaintDisable( BOOL bDisabled )
220 {
221     bUserRepaintDisabled = bDisabled;
222 }
223
224 /***********************************************************************
225  *           EVENT_ProcessEvent
226  *
227  * Process an X event.
228  */
229 static void EVENT_ProcessEvent( XEvent *event )
230 {
231   HWND hWnd;
232
233   TRACE( "called.\n" );
234
235   switch (event->type)
236   {
237     case SelectionNotify: /* all of these should be caught by XCheckTypedWindowEvent() */
238          FIXME("Got SelectionNotify - must not happen!\n");
239          /* fall through */
240
241       /* We get all these because of StructureNotifyMask.
242          This check is placed here to avoid getting error messages below,
243          as X might send some of these even for windows that have already
244          been deleted ... */
245     case CirculateNotify:
246     case CreateNotify:
247     case DestroyNotify:
248     case GravityNotify:
249     case ReparentNotify:
250       return;
251   }
252
253 #ifdef HAVE_LIBXXSHM
254   if (ShmAvailable && (event->type == ShmCompletionType)) {
255     EVENT_ShmCompletion( (XShmCompletionEvent*)event );
256     return;
257   }
258 #endif
259       
260 #ifdef HAVE_LIBXXF86DGA2
261   if (DGAUsed) {
262     if (event->type == DGAMotionEventType) {
263       TRACE("DGAMotionEvent received.\n");
264       EVENT_DGAMotionEvent((XDGAMotionEvent *) event);
265       return;
266     }
267     if (event->type == DGAButtonPressEventType) {
268       TRACE("DGAButtonPressEvent received.\n");
269       EVENT_DGAButtonPressEvent((XDGAButtonEvent *) event);
270       return;
271     }
272     if (event->type == DGAButtonReleaseEventType) {
273       TRACE("DGAButtonReleaseEvent received.\n");
274       EVENT_DGAButtonReleaseEvent((XDGAButtonEvent *) event);
275       return;
276     }
277     if ((event->type == DGAKeyPressEventType) ||
278         (event->type == DGAKeyReleaseEventType)) {
279       /* Fill a XKeyEvent to send to EVENT_Key */
280       XKeyEvent ke;
281       XDGAKeyEvent *evt = (XDGAKeyEvent *) event;
282
283       TRACE("DGAKeyPress/ReleaseEvent received.\n");
284       
285       if (evt->type == DGAKeyReleaseEventType)
286         ke.type = KeyRelease;
287       else
288         ke.type = KeyPress;
289       ke.serial = evt->serial;
290       ke.send_event = FALSE;
291       ke.display = evt->display;
292       ke.window = 0;
293       ke.root = 0;
294       ke.subwindow = 0;
295       ke.time = evt->time;
296       ke.x = PosX;
297       ke.y = PosY;
298       ke.x_root = -1;
299       ke.y_root = -1;
300       ke.state = evt->state;
301       ke.keycode = evt->keycode;
302       ke.same_screen = TRUE;
303       
304       X11DRV_KEYBOARD_HandleEvent(NULL, &ke);
305       return;
306     }
307   }
308 #endif
309   
310   if ( TSXFindContext( display, event->xany.window, winContext,
311                        (char **)&hWnd ) != 0) {
312     if ( event->type == ClientMessage) {
313       /* query window (drag&drop event contains only drag window) */
314       Window    root, child;
315       int       root_x, root_y, child_x, child_y;
316       unsigned  u;
317       TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
318                        &root_x, &root_y, &child_x, &child_y, &u);
319       if (TSXFindContext( display, child, winContext, (char **)&hWnd ) != 0)
320         return;
321     } else {
322       hWnd = 0;  /* Not for a registered window */
323     }
324   }
325
326   if ( !hWnd && event->xany.window != X11DRV_GetXRootWindow()
327              && event->type != PropertyNotify 
328              && event->type != MappingNotify)
329       ERR("Got event %s for unknown Window %08lx\n",
330           event_names[event->type], event->xany.window );
331   else
332       TRACE("Got event %s for hwnd %04x\n",
333             event_names[event->type], hWnd );
334
335   switch(event->type)
336     {
337     case KeyPress:
338     case KeyRelease:
339       EVENT_Key( hWnd, (XKeyEvent*)event );
340       break;
341       
342     case ButtonPress:
343       EVENT_ButtonPress( hWnd, (XButtonEvent*)event );
344       break;
345       
346     case ButtonRelease:
347       EVENT_ButtonRelease( hWnd, (XButtonEvent*)event );
348       break;
349       
350     case MotionNotify:
351       /* Wine between two fast machines across the overloaded campus
352          ethernet gets very boged down in MotionEvents. The following
353          simply finds the last motion event in the queue and drops
354          the rest. On a good link events are servered before they build
355          up so this doesn't take place. On a slow link this may cause
356          problems if the event order is important. I'm not yet seen
357          of any problems. Jon 7/6/96.
358       */
359       if ((current_input_type == X11DRV_INPUT_ABSOLUTE) &&
360           (in_transition == FALSE))
361         /* Only cumulate events if in absolute mode */
362         while (TSXCheckTypedWindowEvent(display,((XAnyEvent *)event)->window,
363                                         MotionNotify, event));    
364       EVENT_MotionNotify( hWnd, (XMotionEvent*)event );
365       break;
366       
367     case FocusIn:
368     {
369       WND *pWndLastFocus = 0;
370       XWindowAttributes win_attr;
371       BOOL bIsDisabled;
372       XFocusChangeEvent *xfocChange = (XFocusChangeEvent*)event;
373
374       if (!hWnd || bUserRepaintDisabled) return;
375
376       bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED;
377
378       /* If the window has been disabled and we are in managed mode,
379        * revert the X focus back to the last focus window. This is to disallow
380        * the window manager from switching focus away while the app is
381        * in a modal state.
382        */
383       if ( Options.managed && bIsDisabled && glastXFocusWin)
384       {
385         /* Change focus only if saved focus window is registered and viewable */
386         if ( TSXFindContext( xfocChange->display, glastXFocusWin, winContext,
387                              (char **)&pWndLastFocus ) == 0 )
388         {
389           if ( TSXGetWindowAttributes( display, glastXFocusWin, &win_attr ) &&
390                  (win_attr.map_state == IsViewable) )
391           {
392             TSXSetInputFocus( xfocChange->display, glastXFocusWin, RevertToParent, CurrentTime );
393             EVENT_Synchronize();
394       break;
395           }
396         }
397       }
398        
399       EVENT_FocusIn( hWnd, xfocChange );
400       break;
401     }
402       
403     case FocusOut:
404     {
405       /* Save the last window which had the focus */
406       XFocusChangeEvent *xfocChange = (XFocusChangeEvent*)event;
407       glastXFocusWin = xfocChange->window;
408       if (!hWnd || bUserRepaintDisabled) return;
409       if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED) glastXFocusWin = 0;
410       EVENT_FocusOut( hWnd, (XFocusChangeEvent*)event );
411       break;
412     }
413       
414     case Expose:
415       if (bUserRepaintDisabled) return;
416       EVENT_Expose( hWnd, (XExposeEvent *)event );
417       break;
418       
419     case GraphicsExpose:
420       if (bUserRepaintDisabled) return;
421       EVENT_GraphicsExpose( hWnd, (XGraphicsExposeEvent *)event );
422       break;
423       
424     case ConfigureNotify:
425       if (!hWnd || bUserRepaintDisabled) return;
426       EVENT_ConfigureNotify( hWnd, (XConfigureEvent*)event );
427       break;
428
429     case SelectionRequest:
430       if (!hWnd || bUserRepaintDisabled) return;
431       EVENT_SelectionRequest( hWnd, (XSelectionRequestEvent *)event, FALSE );
432       break;
433
434     case SelectionClear:
435       if (!hWnd || bUserRepaintDisabled) return;
436       EVENT_SelectionClear( hWnd, (XSelectionClearEvent*) event );
437       break;
438       
439     case PropertyNotify:
440       EVENT_PropertyNotify( (XPropertyEvent *)event );
441       break;
442
443     case ClientMessage:
444       if (!hWnd || bUserRepaintDisabled) return;
445       EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event );
446       break;
447
448 #if 0
449     case EnterNotify:
450       EVENT_EnterNotify( hWnd, (XCrossingEvent *) event );
451       break;
452 #endif
453
454     case NoExpose:
455       break;
456       
457     case MapNotify:
458       if (!hWnd || bUserRepaintDisabled) return;
459       EVENT_MapNotify( hWnd, (XMapEvent *)event );
460       break;
461
462     case UnmapNotify:
463       if (!hWnd || bUserRepaintDisabled) return;
464       EVENT_UnmapNotify( hWnd, (XUnmapEvent *)event );
465       break;
466
467     case MappingNotify:
468       EVENT_MappingNotify( (XMappingEvent *) event );
469       break;
470
471     default:    
472       WARN("Unprocessed event %s for hwnd %04x\n",
473            event_names[event->type], hWnd );
474       break;
475     }
476     TRACE( "returns.\n" );
477 }
478
479 /***********************************************************************
480  *           EVENT_QueryZOrder
481  *
482  * Synchronize internal z-order with the window manager's.
483  */
484 static BOOL __check_query_condition( WND** pWndA, WND** pWndB )
485 {
486   /* return TRUE if we have at least two managed windows */
487   
488   for( *pWndB = NULL; *pWndA; *pWndA = (*pWndA)->next )
489     if( ((*pWndA)->dwExStyle & WS_EX_MANAGED) &&
490         ((*pWndA)->dwStyle & WS_VISIBLE )) break;
491   if( *pWndA )
492     for( *pWndB = (*pWndA)->next; *pWndB; *pWndB = (*pWndB)->next )
493       if( ((*pWndB)->dwExStyle & WS_EX_MANAGED) &&
494           ((*pWndB)->dwStyle & WS_VISIBLE )) break;
495   return ((*pWndB) != NULL);
496 }
497
498 static Window __get_common_ancestor( Window A, Window B,
499                                      Window** children, unsigned* total )
500 {
501     /* find the real root window */
502   
503     Window      root, *childrenB;
504     unsigned    totalB;
505   
506     do
507     {
508       TSXQueryTree( display, A, &root, &A, children, total );
509       TSXQueryTree( display, B, &root, &B, &childrenB, &totalB );
510       if( childrenB ) TSXFree( childrenB );
511       if( *children ) TSXFree( *children ), *children = NULL;
512     } while( A != B && A && B );
513
514     if( A && B )
515     {
516         TSXQueryTree( display, A, &root, &B, children, total );
517         return A;
518     }
519     return 0 ;
520 }
521
522 static Window __get_top_decoration( Window w, Window ancestor )
523 {
524   Window*     children, root, prev = w, parent = w;
525   unsigned    total;
526   
527   do
528     {
529       w = parent;
530       TSXQueryTree( display, w, &root, &parent, &children, &total );
531       if( children ) TSXFree( children );
532     } while( parent && parent != ancestor );
533   TRACE("\t%08x -> %08x\n", (unsigned)prev, (unsigned)w );
534   return ( parent ) ? w : 0 ;
535 }
536
537 static unsigned __td_lookup( Window w, Window* list, unsigned max )
538 {
539   unsigned    i;
540   for( i = max - 1; i >= 0; i-- ) if( list[i] == w ) break;
541   return i;
542 }
543
544 static HWND EVENT_QueryZOrder( HWND hWndCheck)
545 {
546   HWND      hwndInsertAfter = HWND_TOP;
547   WND      *pWndCheck = WIN_FindWndPtr(hWndCheck);
548   WND      *pDesktop = WIN_GetDesktop();
549   WND      *pWnd, *pWndZ = WIN_LockWndPtr(pDesktop->child);
550   Window      w, parent, *children = NULL;
551   unsigned    total, check, pos, best;
552   
553   if( !__check_query_condition(&pWndZ, &pWnd) )
554   {
555       WIN_ReleaseWndPtr(pWndCheck);
556       WIN_ReleaseWndPtr(pDesktop->child);
557       WIN_ReleaseDesktop();
558       return hwndInsertAfter;
559   }
560   WIN_LockWndPtr(pWndZ);
561   WIN_LockWndPtr(pWnd);
562   WIN_ReleaseWndPtr(pDesktop->child);
563   WIN_ReleaseDesktop();
564   
565   parent = __get_common_ancestor( X11DRV_WND_GetXWindow(pWndZ), 
566                                   X11DRV_WND_GetXWindow(pWnd),
567                                   &children, &total );
568   if( parent && children )
569   {
570       /* w is the ancestor if pWndCheck that is a direct descendant of 'parent' */
571
572       w = __get_top_decoration( X11DRV_WND_GetXWindow(pWndCheck), parent );
573
574       if( w != children[total-1] ) /* check if at the top */
575       {
576           /* X child at index 0 is at the bottom, at index total-1 is at the top */
577           check = __td_lookup( w, children, total );
578           best = total;
579
580           for( WIN_UpdateWndPtr(&pWnd,pWndZ); pWnd;WIN_UpdateWndPtr(&pWnd,pWnd->next))
581           {
582               /* go through all windows in Wine z-order... */
583
584               if( pWnd != pWndCheck )
585               {
586                   if( !(pWnd->dwExStyle & WS_EX_MANAGED) ||
587                       !(w = __get_top_decoration( X11DRV_WND_GetXWindow(pWnd), parent )) )
588                     continue;
589                   pos = __td_lookup( w, children, total );
590                   if( pos < best && pos > check )
591                   {
592                       /* find a nearest Wine window precedes 
593                        * pWndCheck in the real z-order... */
594                       best = pos;
595                       hwndInsertAfter = pWnd->hwndSelf;
596                   }
597                   if( best - check == 1 ) break;
598               }
599           }
600       }
601   }
602   if( children ) TSXFree( children );
603   WIN_ReleaseWndPtr(pWnd);
604   WIN_ReleaseWndPtr(pWndZ);
605   WIN_ReleaseWndPtr(pWndCheck);
606   return hwndInsertAfter;
607 }
608
609 /***********************************************************************
610  *           X11DRV_EVENT_XStateToKeyState
611  *
612  * Translate a X event state (Button1Mask, ShiftMask, etc...) to
613  * a Windows key state (MK_SHIFT, MK_CONTROL, etc...)
614  */
615 WORD X11DRV_EVENT_XStateToKeyState( int state )
616 {
617   int kstate = 0;
618   
619   if (state & Button1Mask) kstate |= MK_LBUTTON;
620   if (state & Button2Mask) kstate |= MK_MBUTTON;
621   if (state & Button3Mask) kstate |= MK_RBUTTON;
622   if (state & ShiftMask)   kstate |= MK_SHIFT;
623   if (state & ControlMask) kstate |= MK_CONTROL;
624   return kstate;
625 }
626
627 /***********************************************************************
628  *           EVENT_Expose
629  */
630 static void EVENT_Expose( HWND hWnd, XExposeEvent *event )
631 {
632   RECT rect;
633   int  offx = 0,offy = 0;
634
635   WND *pWnd = WIN_FindWndPtr(hWnd);
636   /* Make position relative to client area instead of window */
637   offx =  (pWnd? (pWnd->rectClient.left - pWnd->rectWindow.left) : 0);
638   offy =  (pWnd? (pWnd->rectClient.top - pWnd->rectWindow.top) : 0);
639
640   rect.left   = event->x - offx;
641   rect.top    = event->y - offy;
642
643   rect.right  = rect.left + event->width;
644   rect.bottom = rect.top + event->height;
645
646   WIN_ReleaseWndPtr(pWnd);
647
648   RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASE );
649
650   if (event->count == 0)
651     SendNotifyMessageA(hWnd,WM_SYNCPAINT, 0, 0);
652 }
653
654
655 /***********************************************************************
656  *           EVENT_GraphicsExpose
657  *
658  * This is needed when scrolling area is partially obscured
659  * by non-Wine X window.
660  */
661 static void EVENT_GraphicsExpose( HWND hWnd, XGraphicsExposeEvent *event )
662 {
663   RECT rect;
664   int  offx = 0,offy = 0;
665
666   WND *pWnd = WIN_FindWndPtr(hWnd);
667   /* Make position relative to client area instead of window */
668   offx =  (pWnd? (pWnd->rectClient.left - pWnd->rectWindow.left) : 0);
669   offy =  (pWnd? (pWnd->rectClient.top - pWnd->rectWindow.top) : 0);
670
671   rect.left   = event->x - offx;
672   rect.top    = event->y - offy;
673
674   rect.right  = rect.left + event->width;
675   rect.bottom = rect.top + event->height;
676
677   WIN_ReleaseWndPtr(pWnd);
678
679   RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE );
680
681   if (event->count == 0)
682     SendNotifyMessageA(hWnd,WM_SYNCPAINT, 0, 0);
683 }
684
685
686 /***********************************************************************
687  *           EVENT_Key
688  *
689  * Handle a X key event
690  */
691 static void EVENT_Key( HWND hWnd, XKeyEvent *event )
692 {
693     WND *pWnd = WIN_FindWndPtr(hWnd);
694   X11DRV_KEYBOARD_HandleEvent( pWnd, event );
695     WIN_ReleaseWndPtr(pWnd);
696
697 }
698
699
700 /***********************************************************************
701  *           EVENT_MotionNotify
702  */
703 static void EVENT_MotionNotify( HWND hWnd, XMotionEvent *event )
704 {
705   if (current_input_type == X11DRV_INPUT_ABSOLUTE) {
706     WND *pWnd = WIN_FindWndPtr(hWnd);
707     int xOffset = pWnd? pWnd->rectWindow.left : 0;
708     int yOffset = pWnd? pWnd->rectWindow.top  : 0;
709     WIN_ReleaseWndPtr(pWnd);
710     
711     X11DRV_SendEvent( MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, 
712                             xOffset + event->x, yOffset + event->y,
713                             X11DRV_EVENT_XStateToKeyState( event->state ), 
714                             event->time - X11DRV_server_startticks, hWnd);
715   } else {
716     X11DRV_SendEvent( MOUSEEVENTF_MOVE,
717                             event->x_root, event->y_root,
718                             X11DRV_EVENT_XStateToKeyState( event->state ), 
719                             event->time - X11DRV_server_startticks, hWnd);
720   }
721 }
722
723
724 /***********************************************************************
725  *           EVENT_ButtonPress
726  */
727 static void EVENT_ButtonPress( HWND hWnd, XButtonEvent *event )
728 {
729   static WORD statusCodes[NB_BUTTONS] = 
730   { MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL};
731   int buttonNum = event->button - 1;
732   
733   WND *pWnd = WIN_FindWndPtr(hWnd);
734   int xOffset = pWnd? pWnd->rectWindow.left : 0;
735   int yOffset = pWnd? pWnd->rectWindow.top  : 0;
736   WORD keystate,wData = 0;
737   
738   WIN_ReleaseWndPtr(pWnd);
739
740   if (buttonNum >= NB_BUTTONS) return;
741
742   /*
743    * Get the compatible keystate
744    */
745   keystate = X11DRV_EVENT_XStateToKeyState( event->state );
746   
747   /*
748    * Make sure that the state of the button that was just 
749    * pressed is "down".
750    */
751   switch (buttonNum)
752   {
753     case 0:
754       keystate |= MK_LBUTTON;
755       break;
756     case 1:
757       keystate |= MK_MBUTTON;
758       break;
759     case 2:
760       keystate |= MK_RBUTTON;
761       break;
762     case 3:
763         wData = WHEEL_DELTA;
764         break;
765     case 4:
766         wData = -WHEEL_DELTA;
767         break;
768   }
769   
770   X11DRV_SendEvent( statusCodes[buttonNum], 
771                           xOffset + event->x, yOffset + event->y,
772                           MAKEWPARAM(keystate,wData),
773                           event->time - X11DRV_server_startticks, hWnd);
774 }
775
776
777 /***********************************************************************
778  *           EVENT_ButtonRelease
779  */
780 static void EVENT_ButtonRelease( HWND hWnd, XButtonEvent *event )
781 {
782   static WORD statusCodes[NB_BUTTONS] = 
783   { MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP };
784   int buttonNum = event->button - 1;
785   WND *pWnd = WIN_FindWndPtr(hWnd);
786   int xOffset = pWnd? pWnd->rectWindow.left : 0;
787   int yOffset = pWnd? pWnd->rectWindow.top  : 0;
788   WORD keystate;
789   
790   WIN_ReleaseWndPtr(pWnd);
791   
792   if (buttonNum >= NB_BUTTONS) return;    
793   
794   /*
795    * Get the compatible keystate
796    */
797   keystate = X11DRV_EVENT_XStateToKeyState( event->state );
798
799   /*
800    * Make sure that the state of the button that was just 
801    * released is "up".
802    */
803   switch (buttonNum)
804   {
805     case 0:
806       keystate &= ~MK_LBUTTON;
807       break;
808     case 1:
809       keystate &= ~MK_MBUTTON;
810       break;
811     case 2:
812       keystate &= ~MK_RBUTTON;
813       break;
814   default:
815       return;
816   }
817
818   X11DRV_SendEvent( statusCodes[buttonNum], 
819                           xOffset + event->x, yOffset + event->y,
820                           keystate, event->time - X11DRV_server_startticks, hWnd);
821 }
822
823
824 /**********************************************************************
825  *              EVENT_FocusIn
826  */
827 static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event )
828 {
829     if (event->detail != NotifyPointer)
830         if (hWnd != GetForegroundWindow())
831         {
832             SetForegroundWindow( hWnd );
833             X11DRV_KEYBOARD_UpdateState();
834         }
835 }
836
837
838 /**********************************************************************
839  *              EVENT_FocusOut
840  *
841  * Note: only top-level override-redirect windows get FocusOut events.
842  */
843 static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event )
844 {
845     if (event->detail != NotifyPointer)
846         if (hWnd == GetForegroundWindow())
847         {
848             SendMessageA( hWnd, WM_CANCELMODE, 0, 0 );
849
850
851             /* don't reset the foreground window, if the window who's 
852                getting the focus is a Wine window */
853             if (!X11DRV_CheckFocus())
854             {
855                 /* Abey : 6-Oct-99. Check again if the focus out window is the
856                    Foreground window, because in most cases the messages sent
857                    above must have already changed the foreground window, in which
858                    case we don't have to change the foreground window to 0 */
859
860                 if (hWnd == GetForegroundWindow())
861                     SetForegroundWindow( 0 );
862             }
863         }
864 }
865
866 /**********************************************************************
867  *              X11DRV_CheckFocus
868  */
869 BOOL X11DRV_CheckFocus(void)
870 {
871   HWND   hWnd;
872   Window xW;
873   int      state;
874   
875   TSXGetInputFocus(display, &xW, &state);
876     if( xW == None ||
877         TSXFindContext(display, xW, winContext, (char **)&hWnd) ) 
878       return FALSE;
879     return TRUE;
880 }
881
882 /**********************************************************************
883  *              EVENT_GetGeometry
884  *
885  * Helper function for ConfigureNotify handling.
886  * Get the new geometry of a window relative to the root window.
887  */
888 static void EVENT_GetGeometry( Window win, int *px, int *py,
889                                unsigned int *pwidth, unsigned int *pheight )
890 {
891     Window root, top;
892     int x, y, width, height, border, depth;
893
894     EnterCriticalSection( &X11DRV_CritSection );
895
896     /* Get the geometry of the window */
897     XGetGeometry( display, win, &root, &x, &y, &width, &height,
898                   &border, &depth );
899
900     /* Translate the window origin to root coordinates */
901     XTranslateCoordinates( display, win, root, 0, 0, &x, &y, &top );
902
903     LeaveCriticalSection( &X11DRV_CritSection );
904
905     *px = x;
906     *py = y;
907     *pwidth = width;
908     *pheight = height;
909 }
910
911 /**********************************************************************
912  *              EVENT_ConfigureNotify
913  *
914  * The ConfigureNotify event is only selected on top-level windows
915  * when the -managed flag is used.
916  */
917 static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *event )
918 {
919     RECT rectWindow;
920     int x, y, flags = 0;
921     unsigned int width, height;
922     HWND newInsertAfter, oldInsertAfter;
923   
924     /* Get geometry and Z-order according to X */
925
926     EVENT_GetGeometry( event->window, &x, &y, &width, &height );
927     newInsertAfter = EVENT_QueryZOrder( hWnd );
928
929     /* Get geometry and Z-order according to Wine */
930
931     /*
932      *  Needs to find the first Visible Window above the current one
933      */
934     oldInsertAfter = hWnd;
935     for (;;)
936     {
937         oldInsertAfter = GetWindow( oldInsertAfter, GW_HWNDPREV );
938         if (!oldInsertAfter)
939         {
940             oldInsertAfter = HWND_TOP;
941             break;
942         }
943         if (GetWindowLongA( oldInsertAfter, GWL_STYLE ) & WS_VISIBLE) break;
944     }
945
946     /* Compare what has changed */
947
948     GetWindowRect( hWnd, &rectWindow );
949     if ( rectWindow.left == x && rectWindow.top == y )
950         flags |= SWP_NOMOVE;
951     else
952         TRACE_(win)( "%04x moving from (%d,%d) to (%d,%d)\n", hWnd, 
953                      rectWindow.left, rectWindow.top, x, y );
954
955     if (    rectWindow.right - rectWindow.left == width
956          && rectWindow.bottom - rectWindow.top == height )
957         flags |= SWP_NOSIZE;
958     else
959         TRACE_(win)( "%04x resizing from (%d,%d) to (%d,%d)\n", hWnd, 
960                      rectWindow.right - rectWindow.left, 
961                      rectWindow.bottom - rectWindow.top, width, height );
962
963     if ( newInsertAfter == oldInsertAfter )
964         flags |= SWP_NOZORDER;
965     else
966         TRACE_(win)( "%04x restacking from after %04x to after %04x\n", hWnd, 
967                      oldInsertAfter, newInsertAfter );
968
969     /* If anything changed, call SetWindowPos */
970
971     if ( flags != (SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER) )
972         SetWindowPos( hWnd, newInsertAfter, x, y, width, height, 
973                             flags | SWP_NOACTIVATE | SWP_WINE_NOHOSTMOVE );
974 }
975
976
977 /***********************************************************************
978  *           EVENT_SelectionRequest_TARGETS
979  *  Service a TARGETS selection request event
980  */
981 static Atom EVENT_SelectionRequest_TARGETS( Window requestor, Atom target, Atom rprop )
982 {
983     Atom xaTargets = TSXInternAtom(display, "TARGETS", False);
984     Atom* targets;
985     Atom prop;
986     UINT wFormat;
987     unsigned long cTargets;
988     BOOL bHavePixmap;
989     int xRc;
990
991     TRACE("Request for %s\n", TSXGetAtomName(display, target));
992     
993     /*
994      * Count the number of items we wish to expose as selection targets.
995      * We include the TARGETS item, and a PIXMAP if we have CF_DIB or CF_BITMAP
996      */
997     cTargets = CountClipboardFormats() + 1;
998     if ( CLIPBOARD_IsPresent(CF_DIB) ||  CLIPBOARD_IsPresent(CF_BITMAP) )
999        cTargets++;
1000     
1001     /* Allocate temp buffer */
1002     targets = (Atom*)HeapAlloc( GetProcessHeap(), 0, cTargets * sizeof(Atom));
1003     if(targets == NULL) return None;
1004
1005     /* Create TARGETS property list (First item in list is TARGETS itself) */
1006
1007     for ( targets[0] = xaTargets, cTargets = 1, wFormat = 0, bHavePixmap = FALSE;
1008           (wFormat = EnumClipboardFormats( wFormat )); )
1009     {
1010         if ( (prop = X11DRV_CLIPBOARD_MapFormatToProperty(wFormat)) != None )
1011         {
1012             /* Scan through what we have so far to avoid duplicates */
1013             int i;
1014             BOOL bExists;
1015             for (i = 0, bExists = FALSE; i < cTargets; i++)
1016             {
1017                 if (targets[i] == prop)
1018                 {
1019                     bExists = TRUE;
1020                     break;
1021                 }
1022             }
1023             if (!bExists)
1024             {
1025                 targets[cTargets++] = prop;
1026             
1027                 /* Add PIXMAP prop for bitmaps additionally */
1028                 if ( (wFormat == CF_DIB || wFormat == CF_BITMAP )
1029                      && !bHavePixmap )
1030                 {
1031                     targets[cTargets++] = XA_PIXMAP;
1032                     bHavePixmap = TRUE;
1033                 }
1034             }
1035         }
1036     }
1037
1038     if (TRACE_ON(event))
1039     {
1040         int i;
1041         for ( i = 0; i < cTargets; i++)
1042         {
1043             if (targets[i])
1044             {
1045                 char *itemFmtName = TSXGetAtomName(display, targets[i]);
1046                 TRACE("\tAtom# %d:  Type %s\n", i, itemFmtName);
1047                 TSXFree(itemFmtName);
1048             }
1049         }
1050     }
1051     
1052     /* Update the X property */
1053     TRACE("\tUpdating property %s...", TSXGetAtomName(display, rprop));
1054
1055     /* We may want to consider setting the type to xaTargets instead,
1056      * in case some apps expect this instead of XA_ATOM */
1057     xRc = TSXChangeProperty(display, requestor, rprop,
1058                             XA_ATOM, 32, PropModeReplace,
1059                             (unsigned char *)targets, cTargets);
1060     TRACE("(Rc=%d)\n", xRc);
1061     
1062     HeapFree( GetProcessHeap(), 0, targets );
1063
1064     return rprop;
1065 }
1066
1067
1068 /***********************************************************************
1069  *           EVENT_SelectionRequest_STRING
1070  *  Service a STRING selection request event
1071  */
1072 static Atom EVENT_SelectionRequest_STRING( Window requestor, Atom target, Atom rprop )
1073 {
1074     static UINT text_cp = (UINT)-1;
1075     HANDLE hUnicodeText;
1076     LPWSTR uni_text;
1077     LPSTR  text;
1078     int    size,i,j;
1079     char* lpstr = 0;
1080     char *itemFmtName;
1081     int xRc;
1082
1083     if(text_cp == (UINT)-1)
1084         text_cp = PROFILE_GetWineIniInt("x11drv", "TextCP", CP_ACP);
1085
1086     /*
1087      * Map the requested X selection property type atom name to a
1088      * windows clipboard format ID.
1089      */
1090     itemFmtName = TSXGetAtomName(display, target);
1091     TRACE("Request for %s (wFormat=%x %s)\n",
1092         itemFmtName, CF_UNICODETEXT, CLIPBOARD_GetFormatName(CF_UNICODETEXT));
1093     TSXFree(itemFmtName);
1094
1095     hUnicodeText = GetClipboardData(CF_UNICODETEXT);
1096     if(!hUnicodeText)
1097        return None;
1098     uni_text = GlobalLock(hUnicodeText);
1099     if(!uni_text)
1100        return None;
1101
1102     size = WideCharToMultiByte(text_cp, 0, uni_text, -1, NULL, 0, NULL, NULL);
1103     text = HeapAlloc(GetProcessHeap(), 0, size);
1104     if (!text)
1105        return None;
1106     WideCharToMultiByte(text_cp, 0, uni_text, -1, text, size, NULL, NULL);
1107
1108     /* remove carriage returns */
1109     
1110     lpstr = (char*)HeapAlloc( GetProcessHeap(), 0, size-- );
1111     if(lpstr == NULL) return None;
1112     for(i=0,j=0; i < size && text[i]; i++ )
1113     {
1114         if( text[i] == '\r' && 
1115             (text[i+1] == '\n' || text[i+1] == '\0') ) continue;
1116         lpstr[j++] = text[i];
1117     }
1118     lpstr[j]='\0';
1119     
1120     /* Update the X property */
1121     TRACE("\tUpdating property %s...\n", TSXGetAtomName(display, rprop));
1122     xRc = TSXChangeProperty(display, requestor, rprop,
1123                             XA_STRING, 8, PropModeReplace,
1124                             lpstr, j);
1125     TRACE("(Rc=%d)\n", xRc);
1126
1127     GlobalUnlock(hUnicodeText);
1128     HeapFree(GetProcessHeap(), 0, text);
1129     HeapFree( GetProcessHeap(), 0, lpstr );
1130
1131     return rprop;
1132 }
1133
1134 /***********************************************************************
1135  *           EVENT_SelectionRequest_PIXMAP
1136  *  Service a PIXMAP selection request event
1137  */
1138 static Atom EVENT_SelectionRequest_PIXMAP( Window requestor, Atom target, Atom rprop )
1139 {
1140     HANDLE hClipData = 0;
1141     Pixmap pixmap = 0;
1142     UINT   wFormat;
1143     char * itemFmtName;
1144     int xRc;
1145 #if(0)
1146     XSetWindowAttributes win_attr;
1147     XWindowAttributes win_attr_src;
1148 #endif
1149     
1150     /*
1151      * Map the requested X selection property type atom name to a
1152      * windows clipboard format ID.
1153      */
1154     itemFmtName = TSXGetAtomName(display, target);
1155     wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
1156     TRACE("Request for %s (wFormat=%x %s)\n",
1157                   itemFmtName, wFormat, CLIPBOARD_GetFormatName( wFormat));
1158     TSXFree(itemFmtName);
1159     
1160     hClipData = GetClipboardData(wFormat);
1161     if ( !hClipData )
1162     {
1163         TRACE("Could not retrieve a Pixmap compatible format from clipboard!\n");
1164         rprop = None; /* Fail the request */
1165         goto END;
1166     }
1167
1168     if (wFormat == CF_DIB)
1169     {
1170         HWND hwnd = GetOpenClipboardWindow();
1171         HDC hdc = GetDC(hwnd);
1172         
1173         /* For convert from packed DIB to Pixmap */
1174         pixmap = X11DRV_DIB_CreatePixmapFromDIB(hClipData, hdc);
1175         
1176         ReleaseDC(hdc, hwnd);
1177     }
1178     else if (wFormat == CF_BITMAP)
1179     {
1180         HWND hwnd = GetOpenClipboardWindow();
1181         HDC hdc = GetDC(hwnd);
1182         
1183         pixmap = X11DRV_BITMAP_CreatePixmapFromBitmap(hClipData, hdc);
1184
1185         ReleaseDC(hdc, hwnd);
1186     }
1187     else
1188     {
1189         FIXME("%s to PIXMAP conversion not yet implemented!\n",
1190                       CLIPBOARD_GetFormatName(wFormat));
1191         rprop = None;
1192         goto END;
1193     }
1194
1195     TRACE("\tUpdating property %s on Window %ld with %s %ld...\n",
1196           TSXGetAtomName(display, rprop), (long)requestor,
1197           TSXGetAtomName(display, target), pixmap);
1198
1199     /* Store the Pixmap handle in the property */
1200     xRc = TSXChangeProperty(display, requestor, rprop, target, 
1201                             32, PropModeReplace,
1202                             (unsigned char *)&pixmap, 1);
1203     TRACE("(Rc=%d)\n", xRc);
1204
1205     /* Enable the code below if you want to handle destroying Pixmap resources
1206      * in response to property notify events. Clients like XPaint don't
1207      * appear to be duplicating Pixmaps so they don't like us deleting,
1208      * the resource in response to the property being deleted.
1209      */
1210 #if(0)
1211     /* Express interest in property notify events so that we can delete the
1212      * pixmap when the client deletes the property atom.
1213      */
1214     xRc = TSXGetWindowAttributes(display, requestor, &win_attr_src);
1215     TRACE("Turning on PropertyChangeEvent notifications from window %ld\n",
1216           (long)requestor);
1217     win_attr.event_mask = win_attr_src.your_event_mask | PropertyChangeMask;
1218     TSXChangeWindowAttributes(display, requestor, CWEventMask, &win_attr);
1219
1220     /* Register the Pixmap we created with the request property Atom.
1221      * When this property is destroyed we also destroy the Pixmap in
1222      * response to the PropertyNotify event.
1223      */
1224     X11DRV_CLIPBOARD_RegisterPixmapResource( rprop, pixmap );
1225 #endif
1226     
1227 END:
1228     return rprop;
1229 }
1230
1231
1232 /***********************************************************************
1233  *           EVENT_SelectionRequest_WCF
1234  *  Service a Wine Clipboard Format selection request event.
1235  *  For <WCF>* data types we simply copy the data to X without conversion.
1236  */
1237 static Atom EVENT_SelectionRequest_WCF( Window requestor, Atom target, Atom rprop )
1238 {
1239     HANDLE hClipData = 0;
1240     void*  lpClipData;
1241     UINT   wFormat;
1242     char * itemFmtName;
1243     int cBytes;
1244     int xRc;
1245     
1246     /*
1247      * Map the requested X selection property type atom name to a
1248      * windows clipboard format ID.
1249      */
1250     itemFmtName = TSXGetAtomName(display, target);
1251     wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
1252     TRACE("Request for %s (wFormat=%x %s)\n",
1253           itemFmtName, wFormat, CLIPBOARD_GetFormatName( wFormat));
1254     TSXFree(itemFmtName);
1255     
1256     hClipData = GetClipboardData16(wFormat);
1257     
1258     if( hClipData && (lpClipData = GlobalLock16(hClipData)) )
1259     {
1260         cBytes = GlobalSize16(hClipData);
1261         
1262         TRACE("\tUpdating property %s, %d bytes...\n",
1263               TSXGetAtomName(display, rprop), cBytes);
1264         
1265         xRc = TSXChangeProperty(display, requestor, rprop,
1266                                 target, 8, PropModeReplace,
1267                                 (unsigned char *)lpClipData, cBytes);
1268         TRACE("(Rc=%d)\n", xRc);
1269         
1270         GlobalUnlock16(hClipData);
1271     }
1272     else
1273     {
1274         TRACE("\tCould not retrieve native format!\n");
1275         rprop = None; /* Fail the request */
1276     }
1277     
1278     return rprop;
1279 }
1280
1281
1282 /***********************************************************************
1283  *           EVENT_SelectionRequest_MULTIPLE
1284  *  Service a MULTIPLE selection request event
1285  *  rprop contains a list of (target,property) atom pairs.
1286  *  The first atom names a target and the second names a property.
1287  *  The effect is as if we have received a sequence of SelectionRequest events
1288  *  (one for each atom pair) except that:
1289  *  1. We reply with a SelectionNotify only when all the requested conversions
1290  *  have been performed.
1291  *  2. If we fail to convert the target named by an atom in the MULTIPLE property,
1292  *  we replace the atom in the property by None.
1293  */
1294 static Atom EVENT_SelectionRequest_MULTIPLE( HWND hWnd, XSelectionRequestEvent *pevent )
1295 {
1296     Atom           rprop;
1297     Atom           atype=AnyPropertyType;
1298     int            aformat;
1299     unsigned long  remain;
1300     Atom*          targetPropList=NULL;
1301     unsigned long  cTargetPropList = 0;
1302 /*  Atom           xAtomPair = TSXInternAtom(display, "ATOM_PAIR", False); */
1303     
1304    /* If the specified property is None the requestor is an obsolete client.
1305     * We support these by using the specified target atom as the reply property.
1306     */
1307     rprop = pevent->property;
1308     if( rprop == None ) 
1309         rprop = pevent->target;
1310     if (!rprop)
1311         goto END;
1312
1313     /* Read the MULTIPLE property contents. This should contain a list of
1314      * (target,property) atom pairs.
1315      */
1316     if(TSXGetWindowProperty(display, pevent->requestor, rprop,
1317                             0, 0x3FFF, False, AnyPropertyType, &atype,&aformat,
1318                             &cTargetPropList, &remain,
1319                             (unsigned char**)&targetPropList) != Success)
1320         TRACE("\tCouldn't read MULTIPLE property\n");
1321     else
1322     {
1323        TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
1324              TSXGetAtomName(display, atype), aformat, cTargetPropList, remain);
1325
1326        /*
1327         * Make sure we got what we expect.
1328         * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
1329         * in a MULTIPLE selection request should be of type ATOM_PAIR.
1330         * However some X apps(such as XPaint) are not compliant with this and return
1331         * a user defined atom in atype when XGetWindowProperty is called.
1332         * The data *is* an atom pair but is not denoted as such.
1333         */
1334        if(aformat == 32 /* atype == xAtomPair */ )
1335        {
1336           int i;
1337           
1338           /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
1339            * for each (target,property) pair */
1340
1341           for (i = 0; i < cTargetPropList; i+=2)
1342           {
1343               char *targetName = TSXGetAtomName(display, targetPropList[i]);
1344               char *propName = TSXGetAtomName(display, targetPropList[i+1]);
1345               XSelectionRequestEvent event;
1346
1347               TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n",
1348                     i/2, targetName, propName);
1349               TSXFree(targetName);
1350               TSXFree(propName);
1351               
1352               /* We must have a non "None" property to service a MULTIPLE target atom */
1353               if ( !targetPropList[i+1] )
1354               {
1355                   TRACE("\tMULTIPLE(%d): Skipping target with empty property!", i);
1356                   continue;
1357               }
1358               
1359               /* Set up an XSelectionRequestEvent for this (target,property) pair */
1360               memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
1361               event.target = targetPropList[i];
1362               event.property = targetPropList[i+1];
1363                   
1364               /* Fire a SelectionRequest, informing the handler that we are processing
1365                * a MULTIPLE selection request event.
1366                */
1367               EVENT_SelectionRequest( hWnd, &event, TRUE );
1368           }
1369        }
1370
1371        /* Free the list of targets/properties */
1372        TSXFree(targetPropList);
1373     }
1374
1375 END:
1376     return rprop;
1377 }
1378
1379
1380 /***********************************************************************
1381  *           EVENT_SelectionRequest
1382  *  Process an event selection request event.
1383  *  The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
1384  *  recursively while servicing a "MULTIPLE" selection target.
1385  *
1386  *  Note: We only receive this event when WINE owns the X selection
1387  */
1388 static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple )
1389 {
1390   XSelectionEvent result;
1391   Atom            rprop = None;
1392   Window          request = event->requestor;
1393   BOOL            couldOpen = FALSE;
1394   Atom            xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
1395   Atom            xaTargets = TSXInternAtom(display, "TARGETS", False);
1396   Atom            xaMultiple = TSXInternAtom(display, "MULTIPLE", False);
1397
1398   /*
1399    * We can only handle the selection request if :
1400    * The selection is PRIMARY or CLIPBOARD, AND we can successfully open the clipboard.
1401    * Don't do these checks or open the clipboard while recursively processing MULTIPLE,
1402    * since this has been already done.
1403    */
1404   if ( !bIsMultiple )
1405   {
1406     if ( ( (event->selection != XA_PRIMARY) && (event->selection != xaClipboard) )
1407         || !(couldOpen = OpenClipboard(hWnd)) )
1408        goto END;
1409   }
1410
1411   /* If the specified property is None the requestor is an obsolete client.
1412    * We support these by using the specified target atom as the reply property.
1413    */
1414   rprop = event->property;
1415   if( rprop == None ) 
1416       rprop = event->target;
1417   
1418   if(event->target == xaTargets)  /*  Return a list of all supported targets */
1419   {
1420       /* TARGETS selection request */
1421       rprop = EVENT_SelectionRequest_TARGETS( request, event->target, rprop );
1422   }
1423   else if(event->target == xaMultiple)  /*  rprop contains a list of (target, property) atom pairs */
1424   {
1425       /* MULTIPLE selection request */
1426       rprop = EVENT_SelectionRequest_MULTIPLE(  hWnd, event );
1427   }
1428   else if(event->target == XA_STRING)  /* treat CF_TEXT as Unix text */
1429   {
1430       /* XA_STRING selection request */
1431       rprop = EVENT_SelectionRequest_STRING( request, event->target, rprop );
1432   }
1433   else if(event->target == XA_PIXMAP)  /*  Convert DIB's to Pixmaps */
1434   {
1435       /* XA_PIXMAP selection request */
1436       rprop = EVENT_SelectionRequest_PIXMAP( request, event->target, rprop );
1437   }
1438   else if(event->target == XA_BITMAP)  /*  Convert DIB's to 1-bit Pixmaps */
1439   {
1440       /* XA_BITMAP selection request - TODO: create a monochrome Pixmap */
1441       rprop = EVENT_SelectionRequest_PIXMAP( request, XA_PIXMAP, rprop );
1442   }
1443   else if(X11DRV_CLIPBOARD_IsNativeProperty(event->target)) /* <WCF>* */
1444   {
1445       /* All <WCF> selection requests */
1446       rprop = EVENT_SelectionRequest_WCF( request, event->target, rprop );
1447   }
1448   else
1449       rprop = None;  /* Don't support this format */
1450
1451 END:
1452   /* close clipboard only if we opened before */
1453   if(couldOpen) CloseClipboard();
1454   
1455   if( rprop == None) 
1456       TRACE("\tRequest ignored\n");
1457
1458   /* reply to sender 
1459    * SelectionNotify should be sent only at the end of a MULTIPLE request
1460    */
1461   if ( !bIsMultiple )
1462   {
1463     result.type = SelectionNotify;
1464     result.display = display;
1465     result.requestor = request;
1466     result.selection = event->selection;
1467     result.property = rprop;
1468     result.target = event->target;
1469     result.time = event->time;
1470     TRACE("Sending SelectionNotify event...\n");
1471     TSXSendEvent(display,event->requestor,False,NoEventMask,(XEvent*)&result);
1472   }
1473 }
1474
1475 /***********************************************************************
1476  *           EVENT_SelectionClear
1477  */
1478 static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event )
1479 {
1480   Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
1481     
1482   if (event->selection == XA_PRIMARY || event->selection == xaClipboard)
1483       X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd );
1484 }
1485
1486 /***********************************************************************
1487  *           EVENT_PropertyNotify
1488  *   We use this to release resources like Pixmaps when a selection
1489  *   client no longer needs them.
1490  */
1491 static void EVENT_PropertyNotify( XPropertyEvent *event )
1492 {
1493   /* Check if we have any resources to free */
1494   TRACE("Received PropertyNotify event: ");
1495
1496   switch(event->state)
1497   {
1498     case PropertyDelete:
1499     {
1500       TRACE("\tPropertyDelete for atom %s on window %ld\n",
1501             TSXGetAtomName(event->display, event->atom), (long)event->window);
1502       
1503       if (X11DRV_IsSelectionOwner())
1504           X11DRV_CLIPBOARD_FreeResources( event->atom );
1505       break;
1506     }
1507
1508     case PropertyNewValue:
1509     {
1510       TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1511             TSXGetAtomName(event->display, event->atom), (long)event->window);
1512       break;
1513     }
1514     
1515     default:
1516       break;
1517   }
1518 }
1519
1520 /**********************************************************************
1521  *           EVENT_DropFromOffix
1522  *
1523  * don't know if it still works (last Changlog is from 96/11/04)
1524  */
1525 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1526 {
1527   unsigned long         data_length;
1528   unsigned long         aux_long;
1529   unsigned char*        p_data = NULL;
1530   union {
1531     Atom                atom_aux;
1532     struct {
1533       int x;
1534       int y;
1535     } pt_aux;
1536     int         i;
1537   }             u;
1538   int                   x, y;
1539   BOOL16                bAccept;
1540   HGLOBAL16             hDragInfo = GlobalAlloc16( GMEM_SHARE | GMEM_ZEROINIT, sizeof(DRAGINFO16));
1541   LPDRAGINFO16          lpDragInfo = (LPDRAGINFO16) GlobalLock16(hDragInfo);
1542   SEGPTR                spDragInfo = (SEGPTR) WIN16_GlobalLock16(hDragInfo);
1543   Window                w_aux_root, w_aux_child;
1544   WND*                  pDropWnd;
1545   WND*                  pWnd;
1546   
1547   if( !lpDragInfo || !spDragInfo ) return;
1548   
1549   pWnd = WIN_FindWndPtr(hWnd);
1550   
1551   TSXQueryPointer( display, X11DRV_WND_GetXWindow(pWnd), &w_aux_root, &w_aux_child, 
1552                    &x, &y, (int *) &u.pt_aux.x, (int *) &u.pt_aux.y,
1553                    (unsigned int*)&aux_long);
1554   
1555   lpDragInfo->hScope = hWnd;
1556   lpDragInfo->pt.x = (INT16)x; lpDragInfo->pt.y = (INT16)y;
1557   
1558   /* find out drop point and drop window */
1559   if( x < 0 || y < 0 ||
1560       x > (pWnd->rectWindow.right - pWnd->rectWindow.left) ||
1561       y > (pWnd->rectWindow.bottom - pWnd->rectWindow.top) )
1562     {   bAccept = pWnd->dwExStyle & WS_EX_ACCEPTFILES; x = y = 0; }
1563   else
1564     {
1565       bAccept = DRAG_QueryUpdate( hWnd, spDragInfo, TRUE );
1566       x = lpDragInfo->pt.x; y = lpDragInfo->pt.y;
1567     }
1568   pDropWnd = WIN_FindWndPtr( lpDragInfo->hScope );
1569   WIN_ReleaseWndPtr(pWnd);
1570   
1571   GlobalFree16( hDragInfo );
1572   
1573   if( bAccept )
1574     {
1575       TSXGetWindowProperty( display, DefaultRootWindow(display),
1576                             dndSelection, 0, 65535, FALSE,
1577                             AnyPropertyType, &u.atom_aux, (int *) &u.pt_aux.y,
1578                             &data_length, &aux_long, &p_data);
1579       
1580       if( !aux_long && p_data)  /* don't bother if > 64K */
1581         {
1582           char *p = (char*) p_data;
1583           char *p_drop;
1584           
1585           aux_long = 0; 
1586           while( *p )   /* calculate buffer size */
1587             {
1588               p_drop = p;
1589               if((u.i = *p) != -1 ) 
1590                 {
1591                   INT len = GetShortPathNameA( p, NULL, 0 );
1592                   if (len) aux_long += len + 1;
1593                   else *p = -1;
1594                 }
1595               p += strlen(p) + 1;
1596             }
1597           if( aux_long && aux_long < 65535 )
1598             {
1599               HDROP                 hDrop;
1600               DROPFILES *lpDrop;
1601               
1602               aux_long += sizeof(DROPFILES) + 1;
1603               hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1604               lpDrop = (DROPFILES*)GlobalLock( hDrop );
1605               
1606               if( lpDrop )
1607                 {
1608                   lpDrop->pFiles = sizeof(DROPFILES);
1609                   lpDrop->pt.x = x;
1610                   lpDrop->pt.y = y;
1611                   lpDrop->fNC =
1612                     ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
1613                       y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
1614                       x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
1615                       y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
1616                   lpDrop->fWide = FALSE;
1617                   p_drop = (char *)(lpDrop + 1);
1618                   p = p_data;
1619                   while(*p)
1620                     {
1621                       if( *p != -1 )    /* use only "good" entries */
1622                         {
1623                           GetShortPathNameA( p, p_drop, 65535 );
1624                           p_drop += strlen( p_drop ) + 1;
1625                         }
1626                       p += strlen(p) + 1;
1627                     }
1628                   *p_drop = '\0';
1629                   PostMessageA( hWnd, WM_DROPFILES, hDrop, 0L );
1630                 }
1631             }
1632         }
1633       if( p_data ) TSXFree(p_data);  
1634       
1635     } /* WS_EX_ACCEPTFILES */
1636
1637   WIN_ReleaseWndPtr(pDropWnd);
1638 }
1639
1640 /**********************************************************************
1641  *           EVENT_DropURLs
1642  *
1643  * drop items are separated by \n 
1644  * each item is prefixed by its mime type
1645  *
1646  * event->data.l[3], event->data.l[4] contains drop x,y position
1647  */
1648 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1649 {
1650   WND           *pDropWnd;
1651   WND           *pWnd;
1652   unsigned long data_length;
1653   unsigned long aux_long, drop_len = 0;
1654   unsigned char *p_data = NULL; /* property data */
1655   char          *p_drop = NULL;
1656   char          *p, *next;
1657   int           x, y;
1658   DROPFILES *lpDrop;
1659   HDROP hDrop;
1660   union {
1661     Atom        atom_aux;
1662     int         i;
1663     Window      w_aux;
1664   }             u; /* unused */
1665
1666   pWnd = WIN_FindWndPtr(hWnd);
1667
1668   if (!(pWnd->dwExStyle & WS_EX_ACCEPTFILES))
1669   {
1670     WIN_ReleaseWndPtr(pWnd);
1671     return;
1672   }
1673   WIN_ReleaseWndPtr(pWnd);
1674
1675   TSXGetWindowProperty( display, DefaultRootWindow(display),
1676                         dndSelection, 0, 65535, FALSE,
1677                         AnyPropertyType, &u.atom_aux, &u.i,
1678                         &data_length, &aux_long, &p_data);
1679   if (aux_long)
1680     WARN("property too large, truncated!\n");
1681   TRACE("urls=%s\n", p_data);
1682
1683   if( !aux_long && p_data) {    /* don't bother if > 64K */
1684     /* calculate length */
1685     p = p_data;
1686     next = strchr(p, '\n');
1687     while (p) {
1688       if (next) *next=0;
1689       if (strncmp(p,"file:",5) == 0 ) {
1690         INT len = GetShortPathNameA( p+5, NULL, 0 );
1691         if (len) drop_len += len + 1;
1692       }
1693       if (next) { 
1694         *next = '\n'; 
1695         p = next + 1;
1696         next = strchr(p, '\n');
1697       } else {
1698         p = NULL;
1699       }
1700     }
1701     
1702     if( drop_len && drop_len < 65535 ) {
1703       TSXQueryPointer( display, X11DRV_GetXRootWindow(), &u.w_aux, &u.w_aux, 
1704                        &x, &y, &u.i, &u.i, &u.i);
1705
1706       pDropWnd = WIN_FindWndPtr( hWnd );
1707       
1708       drop_len += sizeof(DROPFILES) + 1;
1709       hDrop = (HDROP)GlobalAlloc( GMEM_SHARE, drop_len );
1710       lpDrop = (DROPFILES *) GlobalLock( hDrop );
1711
1712       if( lpDrop ) {
1713           lpDrop->pFiles = sizeof(DROPFILES);
1714           lpDrop->pt.x = (INT)x;
1715           lpDrop->pt.y = (INT)y;
1716           lpDrop->fNC =
1717             ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
1718               y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
1719               x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
1720               y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
1721           lpDrop->fWide = FALSE;
1722           p_drop = (char*)(lpDrop + 1);
1723       }
1724
1725       /* create message content */
1726       if (p_drop) {
1727         p = p_data;
1728         next = strchr(p, '\n');
1729         while (p) {
1730           if (next) *next=0;
1731           if (strncmp(p,"file:",5) == 0 ) {
1732             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1733             if (len) {
1734               TRACE("drop file %s as %s\n", p+5, p_drop);
1735               p_drop += len+1;
1736             } else {
1737               WARN("can't convert file %s to dos name \n", p+5);
1738             }
1739           } else {
1740             WARN("unknown mime type %s\n", p);
1741           }
1742           if (next) { 
1743             *next = '\n'; 
1744             p = next + 1;
1745             next = strchr(p, '\n');
1746           } else {
1747             p = NULL;
1748           }
1749           *p_drop = '\0';
1750         }
1751
1752         GlobalUnlock(hDrop);
1753         PostMessageA( hWnd, WM_DROPFILES, hDrop, 0L );
1754       }
1755       WIN_ReleaseWndPtr(pDropWnd);
1756     }
1757     if( p_data ) TSXFree(p_data);  
1758   }
1759 }
1760
1761 /**********************************************************************
1762  *           EVENT_ClientMessage
1763  */
1764 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event )
1765 {
1766   if (event->message_type != None && event->format == 32) {
1767     if ((event->message_type == wmProtocols) && 
1768         (((Atom) event->data.l[0]) == wmDeleteWindow))
1769     {
1770       /* Ignore the delete window request if the window has been disabled
1771        * and we are in managed mode. This is to disallow applications from
1772        * being closed by the window manager while in a modal state.
1773        */
1774       BOOL bIsDisabled;
1775       bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED;
1776
1777       if ( !Options.managed || !bIsDisabled )
1778       PostMessage16( hWnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
1779     }
1780     else if ( event->message_type == dndProtocol &&
1781               (event->data.l[0] == DndFile || event->data.l[0] == DndFiles) )
1782       EVENT_DropFromOffiX(hWnd, event);
1783     else if ( event->message_type == dndProtocol &&
1784               event->data.l[0] == DndURL )
1785       EVENT_DropURLs(hWnd, event);
1786     else {
1787 #if 0
1788       /* enable this if you want to see the message */
1789       unsigned char* p_data = NULL;
1790       union {
1791         unsigned long   l;
1792         int             i;
1793         Atom            atom;
1794       } u; /* unused */
1795       TSXGetWindowProperty( display, DefaultRootWindow(display),
1796                             dndSelection, 0, 65535, FALSE,
1797                             AnyPropertyType, &u.atom, &u.i,
1798                             &u.l, &u.l, &p_data);
1799       TRACE("message_type=%ld, data=%ld,%ld,%ld,%ld,%ld, msg=%s\n",
1800             event->message_type, event->data.l[0], event->data.l[1], 
1801             event->data.l[2], event->data.l[3], event->data.l[4],
1802             p_data);
1803 #endif
1804       TRACE("unrecognized ClientMessage\n" );
1805     }
1806   }
1807 }
1808
1809 /**********************************************************************
1810  *           EVENT_EnterNotify
1811  *
1812  * Install colormap when Wine window is focused in
1813  * self-managed mode with private colormap
1814  */
1815 #if 0
1816 void EVENT_EnterNotify( HWND hWnd, XCrossingEvent *event )
1817 {
1818   if( !Options.managed && X11DRV_GetXRootWindow() == DefaultRootWindow(display) &&
1819       (COLOR_GetSystemPaletteFlags() & COLOR_PRIVATE) && GetFocus() )
1820     TSXInstallColormap( display, X11DRV_PALETTE_GetColormap() );
1821 }
1822 #endif
1823
1824 /**********************************************************************
1825  *              EVENT_MapNotify
1826  */
1827 void EVENT_MapNotify( HWND hWnd, XMapEvent *event )
1828 {
1829   HWND hwndFocus = GetFocus();
1830   WND *wndFocus = WIN_FindWndPtr(hwndFocus);
1831   WND *pWnd = WIN_FindWndPtr(hWnd);
1832   if (pWnd && (pWnd->dwExStyle & WS_EX_MANAGED))
1833   {
1834       DCE_InvalidateDCE( pWnd, &pWnd->rectWindow );
1835       pWnd->dwStyle &= ~WS_MINIMIZE;
1836       pWnd->dwStyle |=  WS_VISIBLE;
1837       WIN_InternalShowOwnedPopups(hWnd,TRUE,TRUE);
1838   }
1839   WIN_ReleaseWndPtr(pWnd);
1840
1841   if (hwndFocus && IsChild( hWnd, hwndFocus ))
1842       X11DRV_WND_SetFocus(wndFocus);
1843
1844   WIN_ReleaseWndPtr(wndFocus);
1845   
1846   return;
1847 }
1848
1849
1850 /**********************************************************************
1851  *              EVENT_UnmapNotify
1852  */
1853 void EVENT_UnmapNotify( HWND hWnd, XUnmapEvent *event )
1854 {
1855   WND *pWnd = WIN_FindWndPtr(hWnd);
1856   if (pWnd && (pWnd->dwExStyle & WS_EX_MANAGED))
1857   {
1858       EndMenu();
1859       if( pWnd->dwStyle & WS_VISIBLE )
1860       {
1861           pWnd->dwStyle |=  WS_MINIMIZE;
1862           pWnd->dwStyle &= ~WS_VISIBLE;
1863             WIN_InternalShowOwnedPopups(hWnd,FALSE,TRUE);
1864       }
1865   }
1866   WIN_ReleaseWndPtr(pWnd);
1867 }
1868
1869 /***********************************************************************
1870  *           EVENT_MappingNotify
1871  */
1872 static void EVENT_MappingNotify( XMappingEvent *event )
1873 {
1874     TSXRefreshKeyboardMapping(event);
1875
1876     /* reinitialize Wine-X11 driver keyboard table */
1877     X11DRV_InitKeyboard();
1878 }
1879
1880
1881 /**********************************************************************
1882  *              X11DRV_EVENT_SetInputMethod
1883  */
1884 INPUT_TYPE X11DRV_EVENT_SetInputMethod(INPUT_TYPE type)
1885 {
1886   INPUT_TYPE prev = current_input_type;
1887
1888   /* Flag not used yet */
1889   in_transition = FALSE;
1890   current_input_type = type;
1891
1892   return prev;
1893 }
1894
1895 #ifdef HAVE_LIBXXF86DGA2
1896 /**********************************************************************
1897  *              X11DRV_EVENT_SetDGAStatus
1898  */
1899 void X11DRV_EVENT_SetDGAStatus(HWND hwnd, int event_base)
1900 {
1901   if (event_base < 0) {
1902     DGAUsed = FALSE;
1903     DGAhwnd = 0;
1904   } else {
1905     DGAUsed = TRUE;
1906     DGAhwnd = hwnd;
1907     DGAMotionEventType = event_base + MotionNotify;
1908     DGAButtonPressEventType = event_base + ButtonPress;
1909     DGAButtonReleaseEventType = event_base + ButtonRelease;
1910     DGAKeyPressEventType = event_base + KeyPress;
1911     DGAKeyReleaseEventType = event_base + KeyRelease;
1912   }
1913 }
1914
1915 /* DGA2 event handlers */
1916 static void EVENT_DGAMotionEvent( XDGAMotionEvent *event )
1917 {
1918   X11DRV_SendEvent( MOUSEEVENTF_MOVE, 
1919                    event->dx, event->dy,
1920                    X11DRV_EVENT_XStateToKeyState( event->state ), 
1921                    event->time - X11DRV_server_startticks, DGAhwnd );
1922 }
1923
1924 static void EVENT_DGAButtonPressEvent( XDGAButtonEvent *event )
1925 {
1926   static WORD statusCodes[NB_BUTTONS] = 
1927     { MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN };
1928   int buttonNum = event->button - 1;
1929   
1930   WORD keystate;
1931
1932   if (buttonNum >= NB_BUTTONS) return;
1933   
1934   keystate = X11DRV_EVENT_XStateToKeyState( event->state );
1935   
1936   switch (buttonNum)
1937   {
1938     case 0:
1939       keystate |= MK_LBUTTON;
1940       break;
1941     case 1:
1942       keystate |= MK_MBUTTON;
1943       break;
1944     case 2:
1945       keystate |= MK_RBUTTON;
1946       break;
1947   }
1948   
1949   X11DRV_SendEvent( statusCodes[buttonNum], 0, 0, keystate, event->time - X11DRV_server_startticks, DGAhwnd );
1950 }
1951
1952 static void EVENT_DGAButtonReleaseEvent( XDGAButtonEvent *event )
1953 {
1954   static WORD statusCodes[NB_BUTTONS] = 
1955     { MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP };
1956   int buttonNum = event->button - 1;
1957   
1958   WORD keystate;
1959
1960   if (buttonNum >= NB_BUTTONS) return;
1961   
1962   keystate = X11DRV_EVENT_XStateToKeyState( event->state );
1963   
1964   switch (buttonNum)
1965   {
1966     case 0:
1967       keystate &= ~MK_LBUTTON;
1968       break;
1969     case 1:
1970       keystate &= ~MK_MBUTTON;
1971       break;
1972     case 2:
1973       keystate &= ~MK_RBUTTON;
1974       break;
1975   }
1976   
1977   X11DRV_SendEvent( statusCodes[buttonNum], 0, 0, keystate, event->time - X11DRV_server_startticks, DGAhwnd );
1978 }
1979
1980 #endif
1981
1982 #ifdef HAVE_LIBXXSHM
1983
1984 /*
1985 Normal XShm operation:
1986
1987 X11           service thread    app thread
1988 ------------- ----------------- ------------------------
1989               (idle)            ddraw calls XShmPutImage
1990 (copies data)                   (waiting for shm_event)
1991 ShmCompletion ->                (waiting for shm_event)
1992 (idle)        signal shm_event ->
1993               (idle)            returns to app
1994
1995 However, this situation can occur for some reason:
1996
1997 X11           service thread    app thread
1998 ------------- ----------------- ------------------------
1999 Expose ->
2000               WM_ERASEBKGND? ->
2001               (waiting for app) ddraw calls XShmPutImage
2002 (copies data) (waiting for app) (waiting for shm_event)
2003 ShmCompletion (waiting for app) (waiting for shm_event)
2004 (idle)        DEADLOCK          DEADLOCK
2005
2006 which is why I also wait for shm_read and do XCheckTypedEvent()
2007 calls in the wait loop. This results in:
2008
2009 X11           service thread    app thread
2010 ------------- ----------------- ------------------------
2011 ShmCompletion (waiting for app) waking up on shm_read
2012 (idle)        (waiting for app) XCheckTypedEvent() -> signal shm_event
2013               (waiting for app) returns
2014               (idle)
2015 */
2016
2017 typedef struct {
2018   Drawable draw;
2019   LONG state, waiter;
2020   HANDLE sema;
2021 } shm_qs;
2022    
2023 /* FIXME: this is not pretty */
2024 static HANDLE shm_read = 0;
2025
2026 #define SHM_MAX_Q 4
2027 static volatile shm_qs shm_q[SHM_MAX_Q];
2028
2029 static void EVENT_ShmCompletion( XShmCompletionEvent *event )
2030 {
2031   int n;
2032
2033   TRACE("Got ShmCompletion for drawable %ld (time %ld)\n", event->drawable, GetTickCount() );
2034
2035   for (n=0; n<SHM_MAX_Q; n++)
2036     if ((shm_q[n].draw == event->drawable) && (shm_q[n].state == 0)) {
2037       HANDLE sema = shm_q[n].sema;
2038       if (!InterlockedCompareExchange((PVOID*)&shm_q[n].state, (PVOID)1, (PVOID)0)) {
2039         ReleaseSemaphore(sema, 1, NULL);
2040         TRACE("Signaling ShmCompletion (#%d) (semaphore %x)\n", n, sema);
2041       }
2042       return;
2043     }
2044
2045   ERR("Got ShmCompletion for unknown drawable %ld\n", event->drawable );
2046 }
2047
2048 int X11DRV_EVENT_PrepareShmCompletion( Drawable dw )
2049 {
2050   int n;
2051
2052   if (!shm_read)
2053       shm_read = FILE_DupUnixHandle( ConnectionNumber(display), GENERIC_READ | SYNCHRONIZE );
2054
2055   for (n=0; n<SHM_MAX_Q; n++)
2056     if (!shm_q[n].draw)
2057       if (!InterlockedCompareExchange((PVOID*)&shm_q[n].draw, (PVOID)dw, (PVOID)0))
2058         break;
2059
2060   if (n>=SHM_MAX_Q) {
2061     ERR("Maximum number of outstanding ShmCompletions exceeded!\n");
2062     return 0;
2063   }
2064
2065   shm_q[n].state = 0;
2066   if (!shm_q[n].sema) {
2067       shm_q[n].sema = CreateSemaphoreA( NULL, 0, 256, NULL );
2068     TRACE("Allocated ShmCompletion slots have been increased to %d, new semaphore is %x\n", n+1, shm_q[n].sema);
2069   }
2070
2071   TRACE("Prepared ShmCompletion (#%d) wait for drawable %ld (thread %lx) (time %ld)\n", n, dw, GetCurrentThreadId(), GetTickCount() );
2072   return n+1;
2073 }
2074
2075 static void X11DRV_EVENT_WaitReplaceShmCompletionInternal( int *compl, Drawable dw, int creat )
2076 {
2077   int n = *compl;
2078   LONG nn, st;
2079   HANDLE sema;
2080
2081   if ((!n) || (creat && (!shm_q[n-1].draw))) {
2082     nn = X11DRV_EVENT_PrepareShmCompletion(dw);
2083     if (!(n=(LONG)InterlockedCompareExchange((PVOID*)compl, (PVOID)nn, (PVOID)n)))
2084       return;
2085     /* race for compl lost, clear slot */
2086     shm_q[nn-1].draw = 0;
2087     return;
2088   }
2089
2090   if (dw && (shm_q[n-1].draw != dw)) {
2091     /* this shouldn't happen with the current ddraw implementation */
2092     FIXME("ShmCompletion replace with different drawable!\n");
2093     return;
2094   }
2095
2096   sema = shm_q[n-1].sema;
2097   if (!sema) {
2098     /* nothing to wait on (PrepareShmCompletion not done yet?), so probably nothing to wait for */
2099     return;
2100   }
2101
2102   nn = InterlockedExchangeAdd((PLONG)&shm_q[n-1].waiter, 1);
2103   if ((!shm_q[n-1].draw) || (shm_q[n-1].state == 2)) {
2104     /* too late, the wait was just cleared (wait complete) */
2105     TRACE("Wait skip for ShmCompletion (#%d) (thread %lx) (time %ld) (semaphore %x)\n", n-1, GetCurrentThreadId(), GetTickCount(), sema);
2106   } else {
2107     TRACE("Waiting for ShmCompletion (#%d) (thread %lx) (time %ld) (semaphore %x)\n", n-1, GetCurrentThreadId(), GetTickCount(), sema);
2108     if (nn) {
2109       /* another thread is already waiting, let the primary waiter do the dirty work
2110        * (to avoid TSX critical section contention - that could get really slow) */
2111       WaitForSingleObject( sema, INFINITE );
2112     } else
2113     /* we're primary waiter - first check if it's already triggered */
2114     if ( WaitForSingleObject( sema, 0 ) != WAIT_OBJECT_0 ) {
2115       /* nope, may need to poll X event queue, in case the service thread is blocked */
2116       XEvent event;
2117       HANDLE hnd[2];
2118
2119       hnd[0] = sema;
2120       hnd[1] = shm_read;
2121       do {
2122         /* check X event queue */
2123         if (TSXCheckTypedEvent( display, ShmCompletionType, &event)) {
2124           EVENT_ProcessEvent( &event );
2125         }
2126       } while ( WaitForMultipleObjects(2, hnd, FALSE, INFINITE) > WAIT_OBJECT_0 );
2127     }
2128     TRACE("Wait complete (thread %lx) (time %ld)\n", GetCurrentThreadId(), GetTickCount() );
2129
2130     /* clear wait */
2131     st = InterlockedExchange((LPLONG)&shm_q[n-1].state, 2);
2132     if (st != 2) {
2133       /* first waiter to return, release all other waiters */
2134       nn = shm_q[n-1].waiter;
2135       TRACE("Signaling %ld additional ShmCompletion (#%d) waiter(s), semaphore %x\n", nn-1, n-1, sema);
2136       ReleaseSemaphore(sema, nn-1, NULL);
2137     }
2138   }
2139   nn = InterlockedDecrement((LPLONG)&shm_q[n-1].waiter);
2140   if (!nn) {
2141     /* last waiter to return, replace drawable and prepare new wait */
2142     shm_q[n-1].draw = dw;
2143     shm_q[n-1].state = 0;
2144   }
2145 }
2146
2147 void X11DRV_EVENT_WaitReplaceShmCompletion( int *compl, Drawable dw )
2148 {
2149   X11DRV_EVENT_WaitReplaceShmCompletionInternal( compl, dw, 1 );
2150 }
2151
2152 void X11DRV_EVENT_WaitShmCompletion( int compl )
2153 {
2154   if (!compl) return;
2155   X11DRV_EVENT_WaitReplaceShmCompletionInternal( &compl, 0, 0 );
2156 }
2157
2158 void X11DRV_EVENT_WaitShmCompletions( Drawable dw )
2159 {
2160   int n;
2161
2162   for (n=0; n<SHM_MAX_Q; n++)
2163     if (shm_q[n].draw == dw)
2164       X11DRV_EVENT_WaitShmCompletion( n+1 );
2165 }
2166
2167 #endif /* defined(HAVE_LIBXXSHM) */