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