Take into account the event mask passed to MsgWaitForMultipleObjectsEx
[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 "winreg.h"
46 #include "x11drv.h"
47 #include "shellapi.h"
48 #include "wine/debug.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(event);
51 WINE_DECLARE_DEBUG_CHANNEL(clipboard);
52
53 extern BOOL ximInComposeMode;
54
55 #define DndNotDnd       -1    /* OffiX drag&drop */
56 #define DndUnknown      0
57 #define DndRawData      1
58 #define DndFile         2
59 #define DndFiles        3
60 #define DndText         4
61 #define DndDir          5
62 #define DndLink         6
63 #define DndExe          7
64
65 #define DndEND          8
66
67 #define DndURL          128   /* KDE drag&drop */
68
69   /* Event handlers */
70 static void EVENT_FocusIn( HWND hwnd, XEvent *event );
71 static void EVENT_FocusOut( HWND hwnd, XEvent *event );
72 static void EVENT_PropertyNotify( HWND hwnd, XEvent *event );
73 static void EVENT_ClientMessage( HWND hwnd, XEvent *event );
74
75 struct event_handler
76 {
77     int                  type;    /* event type */
78     x11drv_event_handler handler; /* corresponding handler function */
79 };
80
81 #define MAX_EVENT_HANDLERS 64
82
83 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
84 {
85     /* list must be sorted by event type */
86     { KeyPress,         X11DRV_KeyEvent },
87     { KeyRelease,       X11DRV_KeyEvent },
88     { ButtonPress,      X11DRV_ButtonPress },
89     { ButtonRelease,    X11DRV_ButtonRelease },
90     { MotionNotify,     X11DRV_MotionNotify },
91     { EnterNotify,      X11DRV_EnterNotify },
92     /* LeaveNotify */
93     { FocusIn,          EVENT_FocusIn },
94     { FocusOut,         EVENT_FocusOut },
95     { KeymapNotify,     X11DRV_KeymapNotify },
96     { Expose,           X11DRV_Expose },
97     /* GraphicsExpose */
98     /* NoExpose */
99     /* VisibilityNotify */
100     /* CreateNotify */
101     /* DestroyNotify */
102     { UnmapNotify,      X11DRV_UnmapNotify },
103     { MapNotify,        X11DRV_MapNotify },
104     /* MapRequest */
105     /* ReparentNotify */
106     { ConfigureNotify,  X11DRV_ConfigureNotify },
107     /* ConfigureRequest */
108     /* GravityNotify */
109     /* ResizeRequest */
110     /* CirculateNotify */
111     /* CirculateRequest */
112     { PropertyNotify,   EVENT_PropertyNotify },
113     { SelectionClear,   X11DRV_SelectionClear },
114     { SelectionRequest, X11DRV_SelectionRequest },
115     /* SelectionNotify */
116     /* ColormapNotify */
117     { ClientMessage,    EVENT_ClientMessage },
118     { MappingNotify,    X11DRV_MappingNotify },
119 };
120
121 static int nb_event_handlers = 18;  /* change this if you add handlers above */
122
123
124 /* return the name of an X event */
125 static const char *dbgstr_event( int type )
126 {
127     static const char * const event_names[] =
128     {
129         "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
130         "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
131         "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
132         "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
133         "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
134         "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
135         "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
136         "ClientMessage", "MappingNotify"
137     };
138
139     if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
140     return wine_dbg_sprintf( "Extension event %d", type );
141 }
142
143
144 /***********************************************************************
145  *           find_handler
146  *
147  * Find the handler for a given event type. Caller must hold the x11 lock.
148  */
149 static inline x11drv_event_handler find_handler( int type )
150 {
151     int min = 0, max = nb_event_handlers - 1;
152
153     while (min <= max)
154     {
155         int pos = (min + max) / 2;
156         if (handlers[pos].type == type) return handlers[pos].handler;
157         if (handlers[pos].type > type) max = pos - 1;
158         else min = pos + 1;
159     }
160     return NULL;
161 }
162
163
164 /***********************************************************************
165  *           X11DRV_register_event_handler
166  *
167  * Register a handler for a given event type.
168  * If already registered, overwrite the previous handler.
169  */
170 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
171 {
172     int min, max;
173
174     wine_tsx11_lock();
175     min = 0;
176     max = nb_event_handlers - 1;
177     while (min <= max)
178     {
179         int pos = (min + max) / 2;
180         if (handlers[pos].type == type)
181         {
182             handlers[pos].handler = handler;
183             goto done;
184         }
185         if (handlers[pos].type > type) max = pos - 1;
186         else min = pos + 1;
187     }
188     /* insert it between max and min */
189     memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
190     handlers[min].type = type;
191     handlers[min].handler = handler;
192     nb_event_handlers++;
193     assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
194 done:
195     wine_tsx11_unlock();
196     TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
197 }
198
199
200 /***********************************************************************
201  *           filter_event
202  */
203 static Bool filter_event( Display *display, XEvent *event, char *arg )
204 {
205     DWORD mask = (ULONG_PTR)arg;
206
207     if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
208
209     switch(event->type)
210     {
211     case KeyPress:
212     case KeyRelease:
213     case KeymapNotify:
214         return (mask & QS_KEY) != 0;
215     case ButtonPress:
216     case ButtonRelease:
217         return (mask & QS_MOUSEBUTTON) != 0;
218     case MotionNotify:
219     case EnterNotify:
220     case LeaveNotify:
221         return (mask & QS_MOUSEMOVE) != 0;
222     case Expose:
223         return (mask & QS_PAINT) != 0;
224     case ClientMessage:
225         return (mask & QS_POSTMESSAGE) != 0;
226     default:
227         return (mask & QS_SENDMESSAGE) != 0;
228     }
229 }
230
231
232 /***********************************************************************
233  *           process_events
234  */
235 static int process_events( Display *display, DWORD mask )
236 {
237     XEvent event;
238     HWND hwnd;
239     int count = 0;
240     x11drv_event_handler handler;
241
242     wine_tsx11_lock();
243     while (XCheckIfEvent( display, &event, filter_event, (char *)mask ))
244     {
245         count++;
246         if (XFilterEvent( &event, None )) continue;  /* filtered, ignore it */
247
248         if (!(handler = find_handler( event.type )))
249         {
250             TRACE( "%s, ignoring\n", dbgstr_event( event.type ));
251             continue;  /* no handler, ignore it */
252         }
253
254         if (XFindContext( display, event.xany.window, winContext, (char **)&hwnd ) != 0)
255             hwnd = 0;  /* not for a registered window */
256         if (!hwnd && event.xany.window == root_window) hwnd = GetDesktopWindow();
257
258         wine_tsx11_unlock();
259         TRACE( "%s for hwnd/window %p/%lx\n",
260                dbgstr_event( event.type ), hwnd, event.xany.window );
261         handler( hwnd, &event );
262         wine_tsx11_lock();
263     }
264     wine_tsx11_unlock();
265     if (count) TRACE( "processed %d events\n", count );
266     return count;
267 }
268
269
270 /***********************************************************************
271  *           MsgWaitForMultipleObjectsEx   (X11DRV.@)
272  */
273 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
274                                           DWORD timeout, DWORD mask, DWORD flags )
275 {
276     HANDLE new_handles[MAXIMUM_WAIT_OBJECTS+1];  /* FIXME! */
277     DWORD i, ret;
278     struct x11drv_thread_data *data = NtCurrentTeb()->driver_data;
279
280     if (!data || data->process_event_count)
281         return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
282                                          timeout, flags & MWMO_ALERTABLE );
283
284     /* check whether only server queue handle was passed in */
285     if (count < 2) flags &= ~MWMO_WAITALL;
286
287     for (i = 0; i < count; i++) new_handles[i] = handles[i];
288     new_handles[count] = data->display_fd;
289
290     wine_tsx11_lock();
291     XFlush( gdi_display );
292     XFlush( data->display );
293     wine_tsx11_unlock();
294
295     data->process_event_count++;
296     if (process_events( data->display, mask )) ret = count;
297     else
298     {
299         ret = WaitForMultipleObjectsEx( count+1, new_handles, flags & MWMO_WAITALL,
300                                         timeout, flags & MWMO_ALERTABLE );
301         if (ret == count) process_events( data->display, mask );
302     }
303     data->process_event_count--;
304     return ret;
305 }
306
307 /***********************************************************************
308  *           EVENT_x11_time_to_win32_time
309  *
310  * Make our timer and the X timer line up as best we can
311  *  Pass 0 to retrieve the current adjustment value (times -1)
312  */
313 DWORD EVENT_x11_time_to_win32_time(Time time)
314 {
315   static DWORD adjust = 0;
316   DWORD now = GetTickCount();
317   DWORD ret;
318
319   if (! adjust && time != 0)
320   {
321     ret = now;
322     adjust = time - now;
323   }
324   else
325   {
326       /* If we got an event in the 'future', then our clock is clearly wrong. 
327          If we got it more than 10000 ms in the future, then it's most likely
328          that the clock has wrapped.  */
329
330       ret = time - adjust;
331       if (ret > now && ((ret - now) < 10000) && time != 0)
332       {
333         adjust += ret - now;
334         ret    -= ret - now;
335       }
336   }
337
338   return ret;
339
340 }
341
342 /*******************************************************************
343  *         can_activate_window
344  *
345  * Check if we can activate the specified window.
346  */
347 inline static BOOL can_activate_window( HWND hwnd )
348 {
349     LONG style = GetWindowLongW( hwnd, GWL_STYLE );
350     if (!(style & WS_VISIBLE)) return FALSE;
351     if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
352     return !(style & WS_DISABLED);
353 }
354
355
356 /**********************************************************************
357  *              set_focus
358  */
359 static void set_focus( HWND hwnd, Time time )
360 {
361     HWND focus;
362     Window win;
363
364     TRACE( "setting foreground window to %p\n", hwnd );
365     SetForegroundWindow( hwnd );
366
367     focus = GetFocus();
368     if (focus) focus = GetAncestor( focus, GA_ROOT );
369     win = X11DRV_get_whole_window(focus);
370
371     if (win)
372     {
373         TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
374         wine_tsx11_lock();
375         XSetInputFocus( thread_display(), win, RevertToParent, time );
376         wine_tsx11_unlock();
377     }
378 }
379
380
381 /**********************************************************************
382  *              handle_wm_protocols
383  */
384 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
385 {
386     Atom protocol = (Atom)event->data.l[0];
387
388     if (!protocol) return;
389
390     if (protocol == x11drv_atom(WM_DELETE_WINDOW))
391     {
392         /* Ignore the delete window request if the window has been disabled
393          * and we are in managed mode. This is to disallow applications from
394          * being closed by the window manager while in a modal state.
395          */
396         if (IsWindowEnabled(hwnd)) PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
397     }
398     else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
399     {
400         Time event_time = (Time)event->data.l[1];
401         HWND last_focus = x11drv_thread_data()->last_focus;
402
403         TRACE( "got take focus msg for %p, enabled=%d, focus=%p, active=%p, fg=%p, last=%p\n",
404                hwnd, IsWindowEnabled(hwnd), GetFocus(), GetActiveWindow(),
405                GetForegroundWindow(), last_focus );
406
407         if (can_activate_window(hwnd))
408         {
409             /* simulate a mouse click on the caption to find out
410              * whether the window wants to be activated */
411             LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
412                                        (WPARAM)GetAncestor( hwnd, GA_ROOT ),
413                                        MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
414             if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE) set_focus( hwnd, event_time );
415             else TRACE( "not setting focus to %p (%lx), ma=%ld\n", hwnd, event->window, ma );
416         }
417         else
418         {
419             hwnd = GetFocus();
420             if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
421             if (!hwnd) hwnd = GetActiveWindow();
422             if (!hwnd) hwnd = last_focus;
423             if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, event_time );
424         }
425     }
426     else if (protocol == x11drv_atom(_NET_WM_PING))
427     {
428       XClientMessageEvent xev;
429       xev = *event;
430       
431       TRACE("NET_WM Ping\n");
432       xev.window = DefaultRootWindow(xev.display);
433       XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
434       /* this line is semi-stolen from gtk2 */
435       TRACE("NET_WM Pong\n");
436     }
437 }
438
439
440 static const char * const focus_details[] =
441 {
442     "NotifyAncestor",
443     "NotifyVirtual",
444     "NotifyInferior",
445     "NotifyNonlinear",
446     "NotifyNonlinearVirtual",
447     "NotifyPointer",
448     "NotifyPointerRoot",
449     "NotifyDetailNone"
450 };
451
452 /**********************************************************************
453  *              EVENT_FocusIn
454  */
455 static void EVENT_FocusIn( HWND hwnd, XEvent *xev )
456 {
457     XFocusChangeEvent *event = &xev->xfocus;
458     XIC xic;
459
460     if (!hwnd) return;
461
462     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
463
464     if (event->detail == NotifyPointer) return;
465
466     if ((xic = X11DRV_get_ic( hwnd )))
467     {
468         wine_tsx11_lock();
469         XSetICFocus( xic );
470         wine_tsx11_unlock();
471     }
472     if (use_take_focus) return;  /* ignore FocusIn if we are using take focus */
473
474     if (!can_activate_window(hwnd))
475     {
476         HWND hwnd = GetFocus();
477         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
478         if (!hwnd) hwnd = GetActiveWindow();
479         if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
480         if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, CurrentTime );
481     }
482     else SetForegroundWindow( hwnd );
483 }
484
485
486 /**********************************************************************
487  *              EVENT_FocusOut
488  *
489  * Note: only top-level windows get FocusOut events.
490  */
491 static void EVENT_FocusOut( HWND hwnd, XEvent *xev )
492 {
493     XFocusChangeEvent *event = &xev->xfocus;
494     HWND hwnd_tmp;
495     Window focus_win;
496     int revert;
497     XIC xic;
498
499     if (!hwnd) return;
500
501     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
502
503     if (event->detail == NotifyPointer) return;
504     if (ximInComposeMode) return;
505
506     x11drv_thread_data()->last_focus = hwnd;
507     if ((xic = X11DRV_get_ic( hwnd )))
508     {
509         wine_tsx11_lock();
510         XUnsetICFocus( xic );
511         wine_tsx11_unlock();
512     }
513     if (hwnd != GetForegroundWindow()) return;
514     SendMessageA( hwnd, WM_CANCELMODE, 0, 0 );
515
516     /* don't reset the foreground window, if the window which is
517        getting the focus is a Wine window */
518
519     wine_tsx11_lock();
520     XGetInputFocus( thread_display(), &focus_win, &revert );
521     if (focus_win)
522     {
523         if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0)
524             focus_win = 0;
525     }
526     wine_tsx11_unlock();
527
528     if (!focus_win)
529     {
530         /* Abey : 6-Oct-99. Check again if the focus out window is the
531            Foreground window, because in most cases the messages sent
532            above must have already changed the foreground window, in which
533            case we don't have to change the foreground window to 0 */
534         if (hwnd == GetForegroundWindow())
535         {
536             TRACE( "lost focus, setting fg to 0\n" );
537             SetForegroundWindow( 0 );
538         }
539     }
540 }
541
542
543 /***********************************************************************
544  *           EVENT_PropertyNotify
545  *   We use this to release resources like Pixmaps when a selection
546  *   client no longer needs them.
547  */
548 static void EVENT_PropertyNotify( HWND hwnd, XEvent *xev )
549 {
550   XPropertyEvent *event = &xev->xproperty;
551   /* Check if we have any resources to free */
552   TRACE("Received PropertyNotify event: \n");
553
554   switch(event->state)
555   {
556     case PropertyDelete:
557     {
558       TRACE("\tPropertyDelete for atom %ld on window %ld\n",
559             event->atom, (long)event->window);
560       break;
561     }
562
563     case PropertyNewValue:
564     {
565       TRACE("\tPropertyNewValue for atom %ld on window %ld\n\n",
566             event->atom, (long)event->window);
567       break;
568     }
569
570     default:
571       break;
572   }
573 }
574
575 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
576 {
577     RECT tempRect;
578
579     if (!IsWindowEnabled(hQueryWnd)) return 0;
580     
581     GetWindowRect(hQueryWnd, &tempRect);
582
583     if(!PtInRect(&tempRect, *lpPt)) return 0;
584
585     if (!IsIconic( hQueryWnd ))
586     {
587         GetClientRect( hQueryWnd, &tempRect );
588         MapWindowPoints( hQueryWnd, 0, (LPPOINT)&tempRect, 2 );
589
590         if (PtInRect( &tempRect, *lpPt))
591         {
592             HWND *list = WIN_ListChildren( hQueryWnd );
593             HWND bResult = 0;
594
595             if (list)
596             {
597                 int i;
598                 
599                 for (i = 0; list[i]; i++)
600                 {
601                     if (GetWindowLongW( list[i], GWL_STYLE ) & WS_VISIBLE)
602                     {
603                         GetWindowRect( list[i], &tempRect );
604                         if (PtInRect( &tempRect, *lpPt )) break;
605                     }
606                 }
607                 if (list[i])
608                 {
609                     if (IsWindowEnabled( list[i] ))
610                         bResult = find_drop_window( list[i], lpPt );
611                 }
612                 HeapFree( GetProcessHeap(), 0, list );
613             }
614             if(bResult) return bResult;
615         }
616     }
617
618     if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
619     
620     ScreenToClient(hQueryWnd, lpPt);
621
622     return hQueryWnd;
623 }
624
625 /**********************************************************************
626  *           EVENT_DropFromOffix
627  *
628  * don't know if it still works (last Changlog is from 96/11/04)
629  */
630 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
631 {
632     unsigned long       data_length;
633     unsigned long       aux_long;
634     unsigned char*      p_data = NULL;
635     union {
636         Atom    atom_aux;
637         struct {
638             int x;
639             int y;
640         }       pt_aux;
641         int     i;
642     }                   u;
643     int                 x, y;
644     BOOL                bAccept;
645     Window              win, w_aux_root, w_aux_child;
646     WND*                pWnd;
647     HWND                hScope = hWnd;
648
649     win = X11DRV_get_whole_window(hWnd);
650     wine_tsx11_lock();
651     XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
652                    &x, &y, (int *) &u.pt_aux.x, (int *) &u.pt_aux.y,
653                    (unsigned int*)&aux_long);
654     wine_tsx11_unlock();
655
656     pWnd = WIN_GetPtr(hWnd);
657
658     /* find out drop point and drop window */
659     if( x < 0 || y < 0 ||
660         x > (pWnd->rectWindow.right - pWnd->rectWindow.left) ||
661         y > (pWnd->rectWindow.bottom - pWnd->rectWindow.top) )
662     {   
663         bAccept = pWnd->dwExStyle & WS_EX_ACCEPTFILES; 
664         x = 0;
665         y = 0; 
666     }
667     else
668     {
669         POINT   pt = { x, y };
670         HWND    hwndDrop = find_drop_window( hWnd, &pt );
671         if (hwndDrop)
672         {
673             x = pt.x;
674             y = pt.y;
675             hScope = hwndDrop;
676             bAccept = TRUE;
677         }
678         else
679         {
680             bAccept = FALSE;
681         }
682     }
683     WIN_ReleasePtr(pWnd);
684
685     if (!bAccept) return;
686
687     wine_tsx11_lock();
688     XGetWindowProperty( event->display, DefaultRootWindow(event->display),
689                         x11drv_atom(DndSelection), 0, 65535, FALSE,
690                         AnyPropertyType, &u.atom_aux, (int *) &u.pt_aux.y,
691                         &data_length, &aux_long, &p_data);
692     wine_tsx11_unlock();
693
694     if( !aux_long && p_data)  /* don't bother if > 64K */
695     {
696         signed char *p = (signed char*) p_data;
697         char *p_drop;
698
699         aux_long = 0;
700         while( *p )  /* calculate buffer size */
701         {
702             p_drop = p;
703             if((u.i = *p) != -1 )
704             {
705                 INT len = GetShortPathNameA( p, NULL, 0 );
706                 if (len) aux_long += len + 1;
707                 else *p = -1;
708             }
709             p += strlen(p) + 1;
710         }
711         if( aux_long && aux_long < 65535 )
712         {
713             HDROP                 hDrop;
714             DROPFILES *lpDrop;
715
716             aux_long += sizeof(DROPFILES) + 1;
717             hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
718             lpDrop = (DROPFILES*)GlobalLock( hDrop );
719
720             if( lpDrop )
721             {
722                 WND *pDropWnd = WIN_GetPtr( hScope );
723                 lpDrop->pFiles = sizeof(DROPFILES);
724                 lpDrop->pt.x = x;
725                 lpDrop->pt.y = y;
726                 lpDrop->fNC =
727                     ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
728                       y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
729                       x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
730                       y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
731                 lpDrop->fWide = FALSE;
732                 WIN_ReleasePtr(pDropWnd);
733                 p_drop = (char *)(lpDrop + 1);
734                 p = p_data;
735                 while(*p)
736                 {
737                     if( *p != -1 ) /* use only "good" entries */
738                     {
739                         GetShortPathNameA( p, p_drop, 65535 );
740                         p_drop += strlen( p_drop ) + 1;
741                     }
742                     p += strlen(p) + 1;
743                 }
744                 *p_drop = '\0';
745                 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
746             }
747         }
748     }
749     wine_tsx11_lock();
750     if( p_data ) XFree(p_data);
751     wine_tsx11_unlock();
752 }
753
754 /**********************************************************************
755  *           EVENT_DropURLs
756  *
757  * drop items are separated by \n
758  * each item is prefixed by its mime type
759  *
760  * event->data.l[3], event->data.l[4] contains drop x,y position
761  */
762 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
763 {
764   unsigned long data_length;
765   unsigned long aux_long, drop_len = 0;
766   unsigned char *p_data = NULL; /* property data */
767   char          *p_drop = NULL;
768   char          *p, *next;
769   int           x, y;
770   DROPFILES *lpDrop;
771   HDROP hDrop;
772   union {
773     Atom        atom_aux;
774     int         i;
775     Window      w_aux;
776   }             u; /* unused */
777
778   if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
779
780   wine_tsx11_lock();
781   XGetWindowProperty( event->display, DefaultRootWindow(event->display),
782                       x11drv_atom(DndSelection), 0, 65535, FALSE,
783                       AnyPropertyType, &u.atom_aux, &u.i,
784                       &data_length, &aux_long, &p_data);
785   wine_tsx11_unlock();
786   if (aux_long)
787     WARN("property too large, truncated!\n");
788   TRACE("urls=%s\n", p_data);
789
790   if( !aux_long && p_data) {    /* don't bother if > 64K */
791     /* calculate length */
792     p = p_data;
793     next = strchr(p, '\n');
794     while (p) {
795       if (next) *next=0;
796       if (strncmp(p,"file:",5) == 0 ) {
797         INT len = GetShortPathNameA( p+5, NULL, 0 );
798         if (len) drop_len += len + 1;
799       }
800       if (next) {
801         *next = '\n';
802         p = next + 1;
803         next = strchr(p, '\n');
804       } else {
805         p = NULL;
806       }
807     }
808
809     if( drop_len && drop_len < 65535 ) {
810       wine_tsx11_lock();
811       XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
812                      &x, &y, &u.i, &u.i, &u.i);
813       wine_tsx11_unlock();
814
815       drop_len += sizeof(DROPFILES) + 1;
816       hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
817       lpDrop = (DROPFILES *) GlobalLock( hDrop );
818
819       if( lpDrop ) {
820           WND *pDropWnd = WIN_GetPtr( hWnd );
821           lpDrop->pFiles = sizeof(DROPFILES);
822           lpDrop->pt.x = (INT)x;
823           lpDrop->pt.y = (INT)y;
824           lpDrop->fNC =
825             ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
826               y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
827               x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
828               y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
829           lpDrop->fWide = FALSE;
830           p_drop = (char*)(lpDrop + 1);
831           WIN_ReleasePtr(pDropWnd);
832       }
833
834       /* create message content */
835       if (p_drop) {
836         p = p_data;
837         next = strchr(p, '\n');
838         while (p) {
839           if (next) *next=0;
840           if (strncmp(p,"file:",5) == 0 ) {
841             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
842             if (len) {
843               TRACE("drop file %s as %s\n", p+5, p_drop);
844               p_drop += len+1;
845             } else {
846               WARN("can't convert file %s to dos name \n", p+5);
847             }
848           } else {
849             WARN("unknown mime type %s\n", p);
850           }
851           if (next) {
852             *next = '\n';
853             p = next + 1;
854             next = strchr(p, '\n');
855           } else {
856             p = NULL;
857           }
858           *p_drop = '\0';
859         }
860
861         GlobalUnlock(hDrop);
862         PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
863       }
864     }
865     wine_tsx11_lock();
866     if( p_data ) XFree(p_data);
867     wine_tsx11_unlock();
868   }
869 }
870
871 /**********************************************************************
872  *              handle_dnd_protocol
873  */
874 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
875 {
876     Window root, child;
877     int root_x, root_y, child_x, child_y;
878     unsigned int u;
879
880     /* query window (drag&drop event contains only drag window) */
881     wine_tsx11_lock();
882     XQueryPointer( event->display, root_window, &root, &child,
883                    &root_x, &root_y, &child_x, &child_y, &u);
884     if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
885     wine_tsx11_unlock();
886     if (!hwnd) return;
887     if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
888         EVENT_DropFromOffiX(hwnd, event);
889     else if (event->data.l[0] == DndURL)
890         EVENT_DropURLs(hwnd, event);
891 }
892
893
894 struct client_message_handler
895 {
896     int    atom;                                  /* protocol atom */
897     void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
898 };
899
900 static const struct client_message_handler client_messages[] =
901 {
902     { XATOM_WM_PROTOCOLS, handle_wm_protocols },
903     { XATOM_DndProtocol,  handle_dnd_protocol },
904     { XATOM_XdndEnter,    X11DRV_XDND_EnterEvent },
905     { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
906     { XATOM_XdndDrop,     X11DRV_XDND_DropEvent },
907     { XATOM_XdndLeave,    X11DRV_XDND_LeaveEvent }
908 };
909
910
911 /**********************************************************************
912  *           EVENT_ClientMessage
913  */
914 static void EVENT_ClientMessage( HWND hwnd, XEvent *xev )
915 {
916     XClientMessageEvent *event = &xev->xclient;
917     unsigned int i;
918
919     if (!hwnd) return;
920
921     if (event->format != 32)
922     {
923         WARN( "Don't know how to handle format %d\n", event->format );
924         return;
925     }
926
927     for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
928     {
929         if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
930         {
931             client_messages[i].handler( hwnd, event );
932             return;
933         }
934     }
935     TRACE( "no handler found for %ld\n", event->message_type );
936 }
937
938
939 /**********************************************************************
940  *           X11DRV_WindowMessage   (X11DRV.@)
941  */
942 LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
943 {
944     switch(msg)
945     {
946     case WM_X11DRV_ACQUIRE_SELECTION:
947         X11DRV_AcquireClipboard( hwnd );
948         return 0;
949     default:
950         FIXME( "got window msg %x hwnd %p wp %x lp %lx\n", msg, hwnd, wp, lp );
951         return 0;
952     }
953 }