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