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