INT21_GetFreeDiskSpace(): The drive parameter is found in the DL
[wine] / dlls / x11drv / event.c
1 /*
2  * X11 event driver
3  *
4  * Copyright 1993 Alexandre Julliard
5  *           1999 Noel Borthwick
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #define COM_NO_WINDOWS_H
23 #include "config.h"
24
25 #include <X11/Xatom.h>
26 #include <X11/keysym.h>
27 #include <X11/Xlib.h>
28 #include <X11/Xresource.h>
29 #include <X11/Xutil.h>
30 #ifdef HAVE_LIBXXF86DGA2
31 #include <X11/extensions/xf86dga.h>
32 #endif
33
34 #include <assert.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include "wine/winuser16.h"
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winuser.h"
41 #include "wingdi.h"
42 #include "shlobj.h"  /* DROPFILES */
43
44 #include "clipboard.h"
45 #include "win.h"
46 #include "winpos.h"
47 #include "winreg.h"
48 #include "x11drv.h"
49 #include "shellapi.h"
50 #include "wine/debug.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(event);
53 WINE_DECLARE_DEBUG_CHANNEL(clipboard);
54
55 /* X context to associate a hwnd to an X window */
56 extern XContext winContext;
57
58 #define DndNotDnd       -1    /* OffiX drag&drop */
59 #define DndUnknown      0
60 #define DndRawData      1
61 #define DndFile         2
62 #define DndFiles        3
63 #define DndText         4
64 #define DndDir          5
65 #define DndLink         6
66 #define DndExe          7
67
68 #define DndEND          8
69
70 #define DndURL          128   /* KDE drag&drop */
71
72 static const char * const event_names[] =
73 {
74   "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
75   "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
76   "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
77   "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
78   "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
79   "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
80   "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
81   "ClientMessage", "MappingNotify"
82 };
83
84
85 static void EVENT_ProcessEvent( XEvent *event );
86
87   /* Event handlers */
88 static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event );
89 static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event );
90 static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple );
91 static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event);
92 static void EVENT_PropertyNotify( XPropertyEvent *event );
93 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event );
94
95 extern void X11DRV_ButtonPress( HWND hwnd, XButtonEvent *event );
96 extern void X11DRV_ButtonRelease( HWND hwnd, XButtonEvent *event );
97 extern void X11DRV_MotionNotify( HWND hwnd, XMotionEvent *event );
98 extern void X11DRV_EnterNotify( HWND hwnd, XCrossingEvent *event );
99 extern void X11DRV_KeyEvent( HWND hwnd, XKeyEvent *event );
100 extern void X11DRV_KeymapNotify( HWND hwnd, XKeymapEvent *event );
101 extern void X11DRV_Expose( HWND hwnd, XExposeEvent *event );
102 extern void X11DRV_MapNotify( HWND hwnd, XMapEvent *event );
103 extern void X11DRV_UnmapNotify( HWND hwnd, XUnmapEvent *event );
104 extern void X11DRV_ConfigureNotify( HWND hwnd, XConfigureEvent *event );
105 extern void X11DRV_MappingNotify( XMappingEvent *event );
106
107 #ifdef HAVE_LIBXXF86DGA2
108 static int DGAMotionEventType;
109 static int DGAButtonPressEventType;
110 static int DGAButtonReleaseEventType;
111 static int DGAKeyPressEventType;
112 static int DGAKeyReleaseEventType;
113
114 static BOOL DGAUsed = FALSE;
115 static HWND DGAhwnd = 0;
116
117 extern void X11DRV_DGAMotionEvent( HWND hwnd, XDGAMotionEvent *event );
118 extern void X11DRV_DGAButtonPressEvent( HWND hwnd, XDGAButtonEvent *event );
119 extern void X11DRV_DGAButtonReleaseEvent( HWND hwnd, XDGAButtonEvent *event );
120 #endif
121
122 /* Static used for the current input method */
123 static INPUT_TYPE current_input_type = X11DRV_INPUT_ABSOLUTE;
124 static BOOL in_transition = FALSE; /* This is not used as for today */
125
126
127 /***********************************************************************
128  *           process_events
129  */
130 static int process_events( struct x11drv_thread_data *data )
131 {
132     XEvent event;
133     int count = 0;
134
135     wine_tsx11_lock();
136     while ( XPending( data->display ) )
137     {
138         Bool ignore;
139
140         XNextEvent( data->display, &event );
141         ignore = XFilterEvent( &event, None );
142         wine_tsx11_unlock();
143         if (!ignore) EVENT_ProcessEvent( &event );
144         count++;
145         wine_tsx11_lock();
146     }
147     wine_tsx11_unlock();
148     return count;
149 }
150
151
152 /***********************************************************************
153  *           MsgWaitForMultipleObjectsEx   (X11DRV.@)
154  */
155 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
156                                           DWORD timeout, DWORD mask, DWORD flags )
157 {
158     HANDLE new_handles[MAXIMUM_WAIT_OBJECTS+1];  /* FIXME! */
159     DWORD i, ret;
160     struct x11drv_thread_data *data = NtCurrentTeb()->driver_data;
161
162     if (!data || data->process_event_count)
163         return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
164                                          timeout, flags & MWMO_ALERTABLE );
165
166     /* check whether only server queue handle was passed in */
167     if (count < 2) flags &= ~MWMO_WAITALL;
168
169     for (i = 0; i < count; i++) new_handles[i] = handles[i];
170     new_handles[count] = data->display_fd;
171
172     wine_tsx11_lock();
173     XFlush( gdi_display );
174     XFlush( data->display );
175     wine_tsx11_unlock();
176
177     data->process_event_count++;
178     if (process_events( data )) ret = count;
179     else
180     {
181         ret = WaitForMultipleObjectsEx( count+1, new_handles, flags & MWMO_WAITALL,
182                                         timeout, flags & MWMO_ALERTABLE );
183         if (ret == count) process_events( data );
184     }
185     data->process_event_count--;
186     return ret;
187 }
188
189
190 /***********************************************************************
191  *           EVENT_ProcessEvent
192  *
193  * Process an X event.
194  */
195 static void EVENT_ProcessEvent( XEvent *event )
196 {
197   HWND hWnd;
198   Display *display = event->xany.display;
199
200   TRACE( "called.\n" );
201
202   switch (event->type)
203   {
204     case SelectionNotify: /* all of these should be caught by XCheckTypedWindowEvent() */
205          FIXME("Got SelectionNotify - must not happen!\n");
206          /* fall through */
207
208       /* We get all these because of StructureNotifyMask.
209          This check is placed here to avoid getting error messages below,
210          as X might send some of these even for windows that have already
211          been deleted ... */
212     case CirculateNotify:
213     case CreateNotify:
214     case DestroyNotify:
215     case GravityNotify:
216     case ReparentNotify:
217       return;
218   }
219
220 #ifdef HAVE_LIBXXF86DGA2
221   if (DGAUsed) {
222     if (event->type == DGAMotionEventType) {
223       TRACE("DGAMotionEvent received.\n");
224       X11DRV_DGAMotionEvent( DGAhwnd, (XDGAMotionEvent *)event );
225       return;
226     }
227     if (event->type == DGAButtonPressEventType) {
228       TRACE("DGAButtonPressEvent received.\n");
229       X11DRV_DGAButtonPressEvent( DGAhwnd, (XDGAButtonEvent *)event );
230       return;
231     }
232     if (event->type == DGAButtonReleaseEventType) {
233       TRACE("DGAButtonReleaseEvent received.\n");
234       X11DRV_DGAButtonReleaseEvent( DGAhwnd, (XDGAButtonEvent *)event );
235       return;
236     }
237     if ((event->type == DGAKeyPressEventType) ||
238         (event->type == DGAKeyReleaseEventType)) {
239       /* Fill a XKeyEvent to send to EVENT_Key */
240       XKeyEvent ke;
241       XDGAKeyEvent *evt = (XDGAKeyEvent *) event;
242
243       TRACE("DGAKeyPress/ReleaseEvent received.\n");
244
245       if (evt->type == DGAKeyReleaseEventType)
246         ke.type = KeyRelease;
247       else
248         ke.type = KeyPress;
249       ke.serial = evt->serial;
250       ke.send_event = FALSE;
251       ke.display = evt->display;
252       ke.window = 0;
253       ke.root = 0;
254       ke.subwindow = 0;
255       ke.time = evt->time;
256       ke.x = -1;
257       ke.y = -1;
258       ke.x_root = -1;
259       ke.y_root = -1;
260       ke.state = evt->state;
261       ke.keycode = evt->keycode;
262       ke.same_screen = TRUE;
263       X11DRV_KeyEvent( 0, &ke );
264       return;
265     }
266   }
267 #endif
268
269   wine_tsx11_lock();
270   if (XFindContext( display, event->xany.window, winContext, (char **)&hWnd ) != 0)
271       hWnd = 0;  /* Not for a registered window */
272   wine_tsx11_unlock();
273   if (!hWnd && event->xany.window == root_window) hWnd = GetDesktopWindow();
274
275   if (!hWnd && event->type != PropertyNotify && event->type != MappingNotify)
276       WARN( "Got event %s for unknown Window %08lx\n",
277             event_names[event->type], event->xany.window );
278   else
279       TRACE("Got event %s for hwnd %p\n",
280             event_names[event->type], hWnd );
281
282   switch(event->type)
283     {
284     case KeyPress:
285     case KeyRelease:
286       /* FIXME: should generate a motion event if event point is different from current pos */
287       X11DRV_KeyEvent( hWnd, (XKeyEvent*)event );
288       break;
289
290     case ButtonPress:
291       X11DRV_ButtonPress( hWnd, (XButtonEvent*)event );
292       break;
293
294     case ButtonRelease:
295       X11DRV_ButtonRelease( hWnd, (XButtonEvent*)event );
296       break;
297
298     case MotionNotify:
299       X11DRV_MotionNotify( hWnd, (XMotionEvent*)event );
300       break;
301
302     case EnterNotify:
303       X11DRV_EnterNotify( hWnd, (XCrossingEvent*)event );
304       break;
305
306     case FocusIn:
307       EVENT_FocusIn( hWnd, (XFocusChangeEvent*)event );
308       break;
309
310     case FocusOut:
311       EVENT_FocusOut( hWnd, (XFocusChangeEvent*)event );
312       break;
313
314     case Expose:
315       X11DRV_Expose( hWnd, &event->xexpose );
316       break;
317
318     case ConfigureNotify:
319       if (!hWnd) return;
320       X11DRV_ConfigureNotify( hWnd, &event->xconfigure );
321       break;
322
323     case SelectionRequest:
324       if (!hWnd) return;
325       EVENT_SelectionRequest( hWnd, (XSelectionRequestEvent *)event, FALSE );
326       break;
327
328     case SelectionClear:
329       if (!hWnd) return;
330       EVENT_SelectionClear( hWnd, (XSelectionClearEvent*) event );
331       break;
332
333     case PropertyNotify:
334       EVENT_PropertyNotify( (XPropertyEvent *)event );
335       break;
336
337     case ClientMessage:
338       if (!hWnd) return;
339       EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event );
340       break;
341
342     case NoExpose:
343       break;
344
345     case MapNotify:
346       X11DRV_MapNotify( hWnd, (XMapEvent *)event );
347       break;
348
349     case UnmapNotify:
350       X11DRV_UnmapNotify( hWnd, (XUnmapEvent *)event );
351       break;
352
353     case KeymapNotify:
354       X11DRV_KeymapNotify( hWnd, (XKeymapEvent *)event );
355       break;
356
357     case MappingNotify:
358       X11DRV_MappingNotify( (XMappingEvent *) event );
359       break;
360
361     default:
362       WARN("Unprocessed event %s for hwnd %p\n", event_names[event->type], hWnd );
363       break;
364     }
365     TRACE( "returns.\n" );
366 }
367
368
369 /*******************************************************************
370  *         can_activate_window
371  *
372  * Check if we can activate the specified window.
373  */
374 inline static BOOL can_activate_window( HWND hwnd )
375 {
376     LONG style = GetWindowLongW( hwnd, GWL_STYLE );
377     if (!(style & WS_VISIBLE)) return FALSE;
378     if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
379     return !(style & WS_DISABLED);
380 }
381
382
383 /**********************************************************************
384  *              set_focus
385  */
386 static void set_focus( HWND hwnd, Time time )
387 {
388     HWND focus;
389     Window win;
390
391     TRACE( "setting foreground window to %p\n", hwnd );
392     SetForegroundWindow( hwnd );
393
394     focus = GetFocus();
395     if (focus) focus = GetAncestor( focus, GA_ROOT );
396     win = X11DRV_get_whole_window(focus);
397
398     if (win)
399     {
400         TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
401         wine_tsx11_lock();
402         XSetInputFocus( thread_display(), win, RevertToParent, time );
403         wine_tsx11_unlock();
404     }
405 }
406
407
408 /**********************************************************************
409  *              handle_wm_protocols_message
410  */
411 static void handle_wm_protocols_message( HWND hwnd, XClientMessageEvent *event )
412 {
413     Atom protocol = (Atom)event->data.l[0];
414
415     if (!protocol) return;
416
417     if (protocol == x11drv_atom(WM_DELETE_WINDOW))
418     {
419         /* Ignore the delete window request if the window has been disabled
420          * and we are in managed mode. This is to disallow applications from
421          * being closed by the window manager while in a modal state.
422          */
423         if (IsWindowEnabled(hwnd)) PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
424     }
425     else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
426     {
427         Time event_time = (Time)event->data.l[1];
428         HWND last_focus = x11drv_thread_data()->last_focus;
429
430         TRACE( "got take focus msg for %p, enabled=%d, focus=%p, active=%p, fg=%p, last=%p\n",
431                hwnd, IsWindowEnabled(hwnd), GetFocus(), GetActiveWindow(),
432                GetForegroundWindow(), last_focus );
433
434         if (can_activate_window(hwnd))
435         {
436             /* simulate a mouse click on the caption to find out
437              * whether the window wants to be activated */
438             LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
439                                        (WPARAM)GetAncestor( hwnd, GA_ROOT ),
440                                        MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
441             if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE) set_focus( hwnd, event_time );
442             else TRACE( "not setting focus to %p (%lx), ma=%ld\n", hwnd, event->window, ma );
443         }
444         else
445         {
446             hwnd = GetFocus();
447             if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
448             if (!hwnd) hwnd = GetActiveWindow();
449             if (!hwnd) hwnd = last_focus;
450             if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, event_time );
451         }
452     }
453     else if (protocol == x11drv_atom(_NET_WM_PING))
454     {
455       XClientMessageEvent xev;
456       xev = *event;
457       
458       TRACE("NET_WM Ping\n");
459       xev.window = DefaultRootWindow(xev.display);
460       XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
461       /* this line is semi-stolen from gtk2 */
462       TRACE("NET_WM Pong\n");
463     }
464 }
465
466
467 static const char * const focus_details[] =
468 {
469     "NotifyAncestor",
470     "NotifyVirtual",
471     "NotifyInferior",
472     "NotifyNonlinear",
473     "NotifyNonlinearVirtual",
474     "NotifyPointer",
475     "NotifyPointerRoot",
476     "NotifyDetailNone"
477 };
478
479 /**********************************************************************
480  *              EVENT_FocusIn
481  */
482 static void EVENT_FocusIn( HWND hwnd, XFocusChangeEvent *event )
483 {
484     XIC xic;
485
486     if (!hwnd) return;
487
488     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
489
490     if (event->detail == NotifyPointer) return;
491
492     if ((xic = X11DRV_get_ic( hwnd )))
493     {
494         wine_tsx11_lock();
495         XSetICFocus( xic );
496         wine_tsx11_unlock();
497     }
498     if (use_take_focus) return;  /* ignore FocusIn if we are using take focus */
499
500     if (!can_activate_window(hwnd))
501     {
502         HWND hwnd = GetFocus();
503         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
504         if (!hwnd) hwnd = GetActiveWindow();
505         if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
506         if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, CurrentTime );
507     }
508     else SetForegroundWindow( hwnd );
509 }
510
511
512 /**********************************************************************
513  *              EVENT_FocusOut
514  *
515  * Note: only top-level windows get FocusOut events.
516  */
517 static void EVENT_FocusOut( HWND hwnd, XFocusChangeEvent *event )
518 {
519     HWND hwnd_tmp;
520     Window focus_win;
521     int revert;
522     XIC xic;
523
524     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
525
526     if (event->detail == NotifyPointer) return;
527     x11drv_thread_data()->last_focus = hwnd;
528     if ((xic = X11DRV_get_ic( hwnd )))
529     {
530         wine_tsx11_lock();
531         XUnsetICFocus( xic );
532         wine_tsx11_unlock();
533     }
534     if (hwnd != GetForegroundWindow()) return;
535     SendMessageA( hwnd, WM_CANCELMODE, 0, 0 );
536
537     /* don't reset the foreground window, if the window which is
538        getting the focus is a Wine window */
539
540     wine_tsx11_lock();
541     XGetInputFocus( thread_display(), &focus_win, &revert );
542     if (focus_win)
543     {
544         if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0)
545             focus_win = 0;
546     }
547     wine_tsx11_unlock();
548
549     if (!focus_win)
550     {
551         /* Abey : 6-Oct-99. Check again if the focus out window is the
552            Foreground window, because in most cases the messages sent
553            above must have already changed the foreground window, in which
554            case we don't have to change the foreground window to 0 */
555         if (hwnd == GetForegroundWindow())
556         {
557             TRACE( "lost focus, setting fg to 0\n" );
558             SetForegroundWindow( 0 );
559         }
560     }
561 }
562
563
564 /***********************************************************************
565  *           EVENT_SelectionRequest_AddTARGETS
566  *  Utility function for EVENT_SelectionRequest_TARGETS.
567  */
568 static void EVENT_SelectionRequest_AddTARGETS(Atom* targets, unsigned long* cTargets, Atom prop)
569 {
570     int i;
571     BOOL bExists;
572
573     /* Scan through what we have so far to avoid duplicates */
574     for (i = 0, bExists = FALSE; i < *cTargets; i++)
575     {
576         if (targets[i] == prop)
577         {
578             bExists = TRUE;
579             break;
580         }
581     }
582
583     if (!bExists)
584         targets[(*cTargets)++] = prop;
585 }
586
587
588 /***********************************************************************
589  *           EVENT_SelectionRequest_TARGETS
590  *  Service a TARGETS selection request event
591  */
592 static Atom EVENT_SelectionRequest_TARGETS( Display *display, Window requestor,
593                                             Atom target, Atom rprop )
594 {
595     Atom* targets;
596     UINT wFormat;
597     UINT alias;
598     ULONG cTargets;
599
600     /*
601      * Count the number of items we wish to expose as selection targets.
602      * We include the TARGETS item, and propery aliases
603      */
604     cTargets = X11DRV_CountClipboardFormats() + 1;
605
606     for (wFormat = 0; (wFormat = X11DRV_EnumClipboardFormats(wFormat));)
607     {
608         LPWINE_CLIPFORMAT lpFormat = X11DRV_CLIPBOARD_LookupFormat(wFormat);
609         if (lpFormat && X11DRV_CLIPBOARD_LookupPropertyAlias(lpFormat->drvData))
610             cTargets++;
611     }
612
613     TRACE_(clipboard)(" found %ld formats\n", cTargets);
614
615     /* Allocate temp buffer */
616     targets = (Atom*)HeapAlloc( GetProcessHeap(), 0, cTargets * sizeof(Atom));
617     if(targets == NULL) 
618         return None;
619
620     /* Create TARGETS property list (First item in list is TARGETS itself) */
621     for (targets[0] = x11drv_atom(TARGETS), cTargets = 1, wFormat = 0;
622           (wFormat = X11DRV_EnumClipboardFormats(wFormat));)
623     {
624         LPWINE_CLIPFORMAT lpFormat = X11DRV_CLIPBOARD_LookupFormat(wFormat);
625
626         EVENT_SelectionRequest_AddTARGETS(targets, &cTargets, lpFormat->drvData);
627
628         /* Check if any alias should be listed */
629         alias = X11DRV_CLIPBOARD_LookupPropertyAlias(lpFormat->drvData);
630         if (alias)
631             EVENT_SelectionRequest_AddTARGETS(targets, &cTargets, alias);
632     }
633
634     wine_tsx11_lock();
635
636     if (TRACE_ON(clipboard))
637     {
638         int i;
639         for ( i = 0; i < cTargets; i++)
640         {
641             if (targets[i])
642             {
643                 char *itemFmtName = XGetAtomName(display, targets[i]);
644                 TRACE_(clipboard)("\tAtom# %d:  Property %ld Type %s\n", i, targets[i], itemFmtName);
645                 XFree(itemFmtName);
646             }
647         }
648     }
649
650     /* We may want to consider setting the type to xaTargets instead,
651      * in case some apps expect this instead of XA_ATOM */
652     XChangeProperty(display, requestor, rprop, XA_ATOM, 32,
653                     PropModeReplace, (unsigned char *)targets, cTargets);
654     wine_tsx11_unlock();
655
656     HeapFree(GetProcessHeap(), 0, targets);
657
658     return rprop;
659 }
660
661
662 /***********************************************************************
663  *           EVENT_SelectionRequest_MULTIPLE
664  *  Service a MULTIPLE selection request event
665  *  rprop contains a list of (target,property) atom pairs.
666  *  The first atom names a target and the second names a property.
667  *  The effect is as if we have received a sequence of SelectionRequest events
668  *  (one for each atom pair) except that:
669  *  1. We reply with a SelectionNotify only when all the requested conversions
670  *  have been performed.
671  *  2. If we fail to convert the target named by an atom in the MULTIPLE property,
672  *  we replace the atom in the property by None.
673  */
674 static Atom EVENT_SelectionRequest_MULTIPLE( HWND hWnd, XSelectionRequestEvent *pevent )
675 {
676     Display *display = pevent->display;
677     Atom           rprop;
678     Atom           atype=AnyPropertyType;
679     int            aformat;
680     unsigned long  remain;
681     Atom*          targetPropList=NULL;
682     unsigned long  cTargetPropList = 0;
683
684    /* If the specified property is None the requestor is an obsolete client.
685     * We support these by using the specified target atom as the reply property.
686     */
687     rprop = pevent->property;
688     if( rprop == None )
689         rprop = pevent->target;
690     if (!rprop)
691         goto END;
692
693     /* Read the MULTIPLE property contents. This should contain a list of
694      * (target,property) atom pairs.
695      */
696     wine_tsx11_lock();
697     if(XGetWindowProperty(display, pevent->requestor, rprop,
698                             0, 0x3FFF, False, AnyPropertyType, &atype,&aformat,
699                             &cTargetPropList, &remain,
700                             (unsigned char**)&targetPropList) != Success)
701     {
702         wine_tsx11_unlock();
703         TRACE("\tCouldn't read MULTIPLE property\n");
704     }
705     else
706     {
707        TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
708              XGetAtomName(display, atype), aformat, cTargetPropList, remain);
709        wine_tsx11_unlock();
710
711        /*
712         * Make sure we got what we expect.
713         * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
714         * in a MULTIPLE selection request should be of type ATOM_PAIR.
715         * However some X apps(such as XPaint) are not compliant with this and return
716         * a user defined atom in atype when XGetWindowProperty is called.
717         * The data *is* an atom pair but is not denoted as such.
718         */
719        if(aformat == 32 /* atype == xAtomPair */ )
720        {
721           int i;
722
723           /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
724            * for each (target,property) pair */
725
726           for (i = 0; i < cTargetPropList; i+=2)
727           {
728               XSelectionRequestEvent event;
729
730               if (TRACE_ON(event))
731               {
732                   char *targetName, *propName;
733                   wine_tsx11_lock();
734                   targetName = XGetAtomName(display, targetPropList[i]);
735                   propName = XGetAtomName(display, targetPropList[i+1]);
736                   TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n",
737                         i/2, targetName, propName);
738                   XFree(targetName);
739                   XFree(propName);
740                   wine_tsx11_unlock();
741               }
742
743               /* We must have a non "None" property to service a MULTIPLE target atom */
744               if ( !targetPropList[i+1] )
745               {
746                   TRACE("\tMULTIPLE(%d): Skipping target with empty property!\n", i);
747                   continue;
748               }
749
750               /* Set up an XSelectionRequestEvent for this (target,property) pair */
751               memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
752               event.target = targetPropList[i];
753               event.property = targetPropList[i+1];
754
755               /* Fire a SelectionRequest, informing the handler that we are processing
756                * a MULTIPLE selection request event.
757                */
758               EVENT_SelectionRequest( hWnd, &event, TRUE );
759           }
760        }
761
762        /* Free the list of targets/properties */
763        wine_tsx11_lock();
764        XFree(targetPropList);
765        wine_tsx11_unlock();
766     }
767
768 END:
769     return rprop;
770 }
771
772
773 /***********************************************************************
774  *           EVENT_SelectionRequest
775  *  Process an event selection request event.
776  *  The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
777  *  recursively while servicing a "MULTIPLE" selection target.
778  *
779  *  Note: We only receive this event when WINE owns the X selection
780  */
781 static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple )
782 {
783     Display *display = event->display;
784   XSelectionEvent result;
785   Atom            rprop = None;
786   Window          request = event->requestor;
787
788   TRACE_(clipboard)("\n");
789
790   /*
791    * We can only handle the selection request if :
792    * The selection is PRIMARY or CLIPBOARD, AND we can successfully open the clipboard.
793    * Don't do these checks or open the clipboard while recursively processing MULTIPLE,
794    * since this has been already done.
795    */
796   if ( !bIsMultiple )
797   {
798     if (((event->selection != XA_PRIMARY) && (event->selection != x11drv_atom(CLIPBOARD))))
799        goto END;
800   }
801
802   /* If the specified property is None the requestor is an obsolete client.
803    * We support these by using the specified target atom as the reply property.
804    */
805   rprop = event->property;
806   if( rprop == None )
807       rprop = event->target;
808
809   if(event->target == x11drv_atom(TARGETS))  /*  Return a list of all supported targets */
810   {
811       /* TARGETS selection request */
812       rprop = EVENT_SelectionRequest_TARGETS( display, request, event->target, rprop );
813   }
814   else if(event->target == x11drv_atom(MULTIPLE))  /*  rprop contains a list of (target, property) atom pairs */
815   {
816       /* MULTIPLE selection request */
817       rprop = EVENT_SelectionRequest_MULTIPLE( hWnd, event );
818   }
819   else
820   {
821       LPWINE_CLIPFORMAT lpFormat = X11DRV_CLIPBOARD_LookupProperty(event->target);
822
823       if (!lpFormat)
824           lpFormat = X11DRV_CLIPBOARD_LookupAliasProperty(event->target);
825
826       if (lpFormat)
827       {
828           LPWINE_CLIPDATA lpData = X11DRV_CLIPBOARD_LookupData(lpFormat->wFormatID);
829
830           if (lpData)
831           {
832               unsigned char* lpClipData;
833               DWORD cBytes;
834               HANDLE hClipData = lpFormat->lpDrvExportFunc(request, event->target,
835                   rprop, lpData, &cBytes);
836
837               if (hClipData && (lpClipData = GlobalLock(hClipData)))
838               {
839
840                   TRACE_(clipboard)("\tUpdating property %s, %ld bytes\n",
841                       lpFormat->Name, cBytes);
842
843                   wine_tsx11_lock();
844                   XChangeProperty(display, request, rprop, event->target,
845                       8, PropModeReplace, (unsigned char *)lpClipData, cBytes);
846                   wine_tsx11_unlock();
847
848                   GlobalUnlock(hClipData);
849                   GlobalFree(hClipData);
850               }
851           }
852       }
853   }
854
855 END:
856   /* reply to sender
857    * SelectionNotify should be sent only at the end of a MULTIPLE request
858    */
859   if ( !bIsMultiple )
860   {
861     result.type = SelectionNotify;
862     result.display = display;
863     result.requestor = request;
864     result.selection = event->selection;
865     result.property = rprop;
866     result.target = event->target;
867     result.time = event->time;
868     TRACE("Sending SelectionNotify event...\n");
869     wine_tsx11_lock();
870     XSendEvent(display,event->requestor,False,NoEventMask,(XEvent*)&result);
871     wine_tsx11_unlock();
872   }
873 }
874
875 /***********************************************************************
876  *           EVENT_SelectionClear
877  */
878 static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event )
879 {
880   if (event->selection == XA_PRIMARY || event->selection == x11drv_atom(CLIPBOARD))
881       X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd );
882 }
883
884 /***********************************************************************
885  *           EVENT_PropertyNotify
886  *   We use this to release resources like Pixmaps when a selection
887  *   client no longer needs them.
888  */
889 static void EVENT_PropertyNotify( XPropertyEvent *event )
890 {
891   /* Check if we have any resources to free */
892   TRACE("Received PropertyNotify event: \n");
893
894   switch(event->state)
895   {
896     case PropertyDelete:
897     {
898       TRACE("\tPropertyDelete for atom %ld on window %ld\n",
899             event->atom, (long)event->window);
900       break;
901     }
902
903     case PropertyNewValue:
904     {
905       TRACE("\tPropertyNewValue for atom %ld on window %ld\n\n",
906             event->atom, (long)event->window);
907       break;
908     }
909
910     default:
911       break;
912   }
913 }
914
915 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
916 {
917     RECT tempRect;
918
919     if (!IsWindowEnabled(hQueryWnd)) return 0;
920     
921     GetWindowRect(hQueryWnd, &tempRect);
922
923     if(!PtInRect(&tempRect, *lpPt)) return 0;
924
925     if (!IsIconic( hQueryWnd ))
926     {
927         GetClientRect( hQueryWnd, &tempRect );
928         MapWindowPoints( hQueryWnd, 0, (LPPOINT)&tempRect, 2 );
929
930         if (PtInRect( &tempRect, *lpPt))
931         {
932             HWND *list = WIN_ListChildren( hQueryWnd );
933             HWND bResult = 0;
934
935             if (list)
936             {
937                 int i;
938                 
939                 for (i = 0; list[i]; i++)
940                 {
941                     if (GetWindowLongW( list[i], GWL_STYLE ) & WS_VISIBLE)
942                     {
943                         GetWindowRect( list[i], &tempRect );
944                         if (PtInRect( &tempRect, *lpPt )) break;
945                     }
946                 }
947                 if (list[i])
948                 {
949                     if (IsWindowEnabled( list[i] ))
950                         bResult = find_drop_window( list[i], lpPt );
951                 }
952                 HeapFree( GetProcessHeap(), 0, list );
953             }
954             if(bResult) return bResult;
955         }
956     }
957
958     if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
959     
960     ScreenToClient(hQueryWnd, lpPt);
961
962     return hQueryWnd;
963 }
964
965 /**********************************************************************
966  *           EVENT_DropFromOffix
967  *
968  * don't know if it still works (last Changlog is from 96/11/04)
969  */
970 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
971 {
972     unsigned long       data_length;
973     unsigned long       aux_long;
974     unsigned char*      p_data = NULL;
975     union {
976         Atom    atom_aux;
977         struct {
978             int x;
979             int y;
980         }       pt_aux;
981         int     i;
982     }                   u;
983     int                 x, y;
984     BOOL                bAccept;
985     Window              w_aux_root, w_aux_child;
986     WND*                pWnd;
987     HWND                hScope = hWnd;
988
989     pWnd = WIN_FindWndPtr(hWnd);
990
991     wine_tsx11_lock();
992     XQueryPointer( event->display, get_whole_window(pWnd), &w_aux_root, &w_aux_child,
993                    &x, &y, (int *) &u.pt_aux.x, (int *) &u.pt_aux.y,
994                    (unsigned int*)&aux_long);
995     wine_tsx11_unlock();
996
997     /* find out drop point and drop window */
998     if( x < 0 || y < 0 ||
999         x > (pWnd->rectWindow.right - pWnd->rectWindow.left) ||
1000         y > (pWnd->rectWindow.bottom - pWnd->rectWindow.top) )
1001     {   
1002         bAccept = pWnd->dwExStyle & WS_EX_ACCEPTFILES; 
1003         x = 0;
1004         y = 0; 
1005     }
1006     else
1007     {
1008         POINT   pt = { x, y };
1009         HWND    hwndDrop = find_drop_window( hWnd, &pt );
1010         if (hwndDrop)
1011         {
1012             x = pt.x;
1013             y = pt.y;
1014             hScope = hwndDrop;
1015             bAccept = TRUE;
1016         }
1017         else
1018         {
1019             bAccept = FALSE;
1020         }
1021     }
1022     WIN_ReleaseWndPtr(pWnd);
1023
1024     if (!bAccept) return;
1025
1026     wine_tsx11_lock();
1027     XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1028                         x11drv_atom(DndSelection), 0, 65535, FALSE,
1029                         AnyPropertyType, &u.atom_aux, (int *) &u.pt_aux.y,
1030                         &data_length, &aux_long, &p_data);
1031     wine_tsx11_unlock();
1032
1033     if( !aux_long && p_data)  /* don't bother if > 64K */
1034     {
1035         signed char *p = (signed char*) p_data;
1036         char *p_drop;
1037
1038         aux_long = 0;
1039         while( *p )  /* calculate buffer size */
1040         {
1041             p_drop = p;
1042             if((u.i = *p) != -1 )
1043             {
1044                 INT len = GetShortPathNameA( p, NULL, 0 );
1045                 if (len) aux_long += len + 1;
1046                 else *p = -1;
1047             }
1048             p += strlen(p) + 1;
1049         }
1050         if( aux_long && aux_long < 65535 )
1051         {
1052             HDROP                 hDrop;
1053             DROPFILES *lpDrop;
1054
1055             aux_long += sizeof(DROPFILES) + 1;
1056             hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1057             lpDrop = (DROPFILES*)GlobalLock( hDrop );
1058
1059             if( lpDrop )
1060             {
1061                 WND *pDropWnd = WIN_FindWndPtr( hScope );
1062                 lpDrop->pFiles = sizeof(DROPFILES);
1063                 lpDrop->pt.x = x;
1064                 lpDrop->pt.y = y;
1065                 lpDrop->fNC =
1066                     ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
1067                       y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
1068                       x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
1069                       y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
1070                 lpDrop->fWide = FALSE;
1071                 WIN_ReleaseWndPtr(pDropWnd);
1072                 p_drop = (char *)(lpDrop + 1);
1073                 p = p_data;
1074                 while(*p)
1075                 {
1076                     if( *p != -1 ) /* use only "good" entries */
1077                     {
1078                         GetShortPathNameA( p, p_drop, 65535 );
1079                         p_drop += strlen( p_drop ) + 1;
1080                     }
1081                     p += strlen(p) + 1;
1082                 }
1083                 *p_drop = '\0';
1084                 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1085             }
1086         }
1087     }
1088     wine_tsx11_lock();
1089     if( p_data ) XFree(p_data);
1090     wine_tsx11_unlock();
1091 }
1092
1093 /**********************************************************************
1094  *           EVENT_DropURLs
1095  *
1096  * drop items are separated by \n
1097  * each item is prefixed by its mime type
1098  *
1099  * event->data.l[3], event->data.l[4] contains drop x,y position
1100  */
1101 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1102 {
1103   unsigned long data_length;
1104   unsigned long aux_long, drop_len = 0;
1105   unsigned char *p_data = NULL; /* property data */
1106   char          *p_drop = NULL;
1107   char          *p, *next;
1108   int           x, y;
1109   DROPFILES *lpDrop;
1110   HDROP hDrop;
1111   union {
1112     Atom        atom_aux;
1113     int         i;
1114     Window      w_aux;
1115   }             u; /* unused */
1116
1117   if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1118
1119   wine_tsx11_lock();
1120   XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1121                       x11drv_atom(DndSelection), 0, 65535, FALSE,
1122                       AnyPropertyType, &u.atom_aux, &u.i,
1123                       &data_length, &aux_long, &p_data);
1124   wine_tsx11_unlock();
1125   if (aux_long)
1126     WARN("property too large, truncated!\n");
1127   TRACE("urls=%s\n", p_data);
1128
1129   if( !aux_long && p_data) {    /* don't bother if > 64K */
1130     /* calculate length */
1131     p = p_data;
1132     next = strchr(p, '\n');
1133     while (p) {
1134       if (next) *next=0;
1135       if (strncmp(p,"file:",5) == 0 ) {
1136         INT len = GetShortPathNameA( p+5, NULL, 0 );
1137         if (len) drop_len += len + 1;
1138       }
1139       if (next) {
1140         *next = '\n';
1141         p = next + 1;
1142         next = strchr(p, '\n');
1143       } else {
1144         p = NULL;
1145       }
1146     }
1147
1148     if( drop_len && drop_len < 65535 ) {
1149       wine_tsx11_lock();
1150       XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1151                      &x, &y, &u.i, &u.i, &u.i);
1152       wine_tsx11_unlock();
1153
1154       drop_len += sizeof(DROPFILES) + 1;
1155       hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1156       lpDrop = (DROPFILES *) GlobalLock( hDrop );
1157
1158       if( lpDrop ) {
1159           WND *pDropWnd = WIN_FindWndPtr( hWnd );
1160           lpDrop->pFiles = sizeof(DROPFILES);
1161           lpDrop->pt.x = (INT)x;
1162           lpDrop->pt.y = (INT)y;
1163           lpDrop->fNC =
1164             ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
1165               y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
1166               x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
1167               y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
1168           lpDrop->fWide = FALSE;
1169           p_drop = (char*)(lpDrop + 1);
1170           WIN_ReleaseWndPtr(pDropWnd);
1171       }
1172
1173       /* create message content */
1174       if (p_drop) {
1175         p = p_data;
1176         next = strchr(p, '\n');
1177         while (p) {
1178           if (next) *next=0;
1179           if (strncmp(p,"file:",5) == 0 ) {
1180             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1181             if (len) {
1182               TRACE("drop file %s as %s\n", p+5, p_drop);
1183               p_drop += len+1;
1184             } else {
1185               WARN("can't convert file %s to dos name \n", p+5);
1186             }
1187           } else {
1188             WARN("unknown mime type %s\n", p);
1189           }
1190           if (next) {
1191             *next = '\n';
1192             p = next + 1;
1193             next = strchr(p, '\n');
1194           } else {
1195             p = NULL;
1196           }
1197           *p_drop = '\0';
1198         }
1199
1200         GlobalUnlock(hDrop);
1201         PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1202       }
1203     }
1204     wine_tsx11_lock();
1205     if( p_data ) XFree(p_data);
1206     wine_tsx11_unlock();
1207   }
1208 }
1209
1210 /**********************************************************************
1211  *           EVENT_ClientMessage
1212  */
1213 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event )
1214 {
1215   if (event->message_type != None && event->format == 32) {
1216     if (event->message_type == x11drv_atom(WM_PROTOCOLS))
1217         handle_wm_protocols_message( hWnd, event );
1218     else if (event->message_type == x11drv_atom(DndProtocol))
1219     {
1220         /* query window (drag&drop event contains only drag window) */
1221         Window root, child;
1222         int root_x, root_y, child_x, child_y;
1223         unsigned int u;
1224
1225         wine_tsx11_lock();
1226         XQueryPointer( event->display, root_window, &root, &child,
1227                        &root_x, &root_y, &child_x, &child_y, &u);
1228         if (XFindContext( event->display, child, winContext, (char **)&hWnd ) != 0) hWnd = 0;
1229         wine_tsx11_unlock();
1230         if (!hWnd) return;
1231         if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1232             EVENT_DropFromOffiX(hWnd, event);
1233         else if (event->data.l[0] == DndURL)
1234             EVENT_DropURLs(hWnd, event);
1235     }
1236     else if (!X11DRV_XDND_Event(hWnd, event))
1237     {
1238 #if 0
1239       /* enable this if you want to see the message */
1240       unsigned char* p_data = NULL;
1241       union {
1242         unsigned long   l;
1243         int             i;
1244         Atom            atom;
1245       } u; /* unused */
1246       wine_tsx11_lock();
1247       XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1248                             dndSelection, 0, 65535, FALSE,
1249                             AnyPropertyType, &u.atom, &u.i,
1250                             &u.l, &u.l, &p_data);
1251       wine_tsx11_unlock();
1252       TRACE("message_type=%ld, data=%ld,%ld,%ld,%ld,%ld, msg=%s\n",
1253             event->message_type, event->data.l[0], event->data.l[1],
1254             event->data.l[2], event->data.l[3], event->data.l[4],
1255             p_data);
1256 #endif
1257       TRACE("unrecognized ClientMessage\n" );
1258     }
1259   }
1260 }
1261
1262
1263 /**********************************************************************
1264  *              X11DRV_EVENT_SetInputMethod
1265  */
1266 INPUT_TYPE X11DRV_EVENT_SetInputMethod(INPUT_TYPE type)
1267 {
1268   INPUT_TYPE prev = current_input_type;
1269
1270   /* Flag not used yet */
1271   in_transition = FALSE;
1272   current_input_type = type;
1273
1274   return prev;
1275 }
1276
1277 #ifdef HAVE_LIBXXF86DGA2
1278 /**********************************************************************
1279  *              X11DRV_EVENT_SetDGAStatus
1280  */
1281 void X11DRV_EVENT_SetDGAStatus(HWND hwnd, int event_base)
1282 {
1283   if (event_base < 0) {
1284     DGAUsed = FALSE;
1285     DGAhwnd = 0;
1286   } else {
1287     DGAUsed = TRUE;
1288     DGAhwnd = hwnd;
1289     DGAMotionEventType = event_base + MotionNotify;
1290     DGAButtonPressEventType = event_base + ButtonPress;
1291     DGAButtonReleaseEventType = event_base + ButtonRelease;
1292     DGAKeyPressEventType = event_base + KeyPress;
1293     DGAKeyReleaseEventType = event_base + KeyRelease;
1294   }
1295 }
1296 #endif