Implements OleLoadPicturePath.
[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  *           process_events
202  */
203 static int process_events( Display *display )
204 {
205     XEvent event;
206     HWND hwnd;
207     int count;
208     x11drv_event_handler handler;
209
210     wine_tsx11_lock();
211     for (count = 0; XPending(display); count++)
212     {
213         XNextEvent( display, &event );
214         if (XFilterEvent( &event, None )) continue;  /* filtered, ignore it */
215
216         if (!(handler = find_handler( event.type )))
217         {
218             TRACE( "%s, ignoring\n", dbgstr_event( event.type ));
219             continue;  /* no handler, ignore it */
220         }
221
222         if (XFindContext( display, event.xany.window, winContext, (char **)&hwnd ) != 0)
223             hwnd = 0;  /* not for a registered window */
224         if (!hwnd && event.xany.window == root_window) hwnd = GetDesktopWindow();
225
226         wine_tsx11_unlock();
227         TRACE( "%s for hwnd/window %p/%lx\n",
228                dbgstr_event( event.type ), hwnd, event.xany.window );
229         handler( hwnd, &event );
230         wine_tsx11_lock();
231     }
232     wine_tsx11_unlock();
233     if (count) TRACE( "processed %d events\n", count );
234     return count;
235 }
236
237
238 /***********************************************************************
239  *           MsgWaitForMultipleObjectsEx   (X11DRV.@)
240  */
241 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
242                                           DWORD timeout, DWORD mask, DWORD flags )
243 {
244     HANDLE new_handles[MAXIMUM_WAIT_OBJECTS+1];  /* FIXME! */
245     DWORD i, ret;
246     struct x11drv_thread_data *data = NtCurrentTeb()->driver_data;
247
248     if (!data || data->process_event_count)
249         return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
250                                          timeout, flags & MWMO_ALERTABLE );
251
252     /* check whether only server queue handle was passed in */
253     if (count < 2) flags &= ~MWMO_WAITALL;
254
255     for (i = 0; i < count; i++) new_handles[i] = handles[i];
256     new_handles[count] = data->display_fd;
257
258     wine_tsx11_lock();
259     XFlush( gdi_display );
260     XFlush( data->display );
261     wine_tsx11_unlock();
262
263     data->process_event_count++;
264     if (process_events( data->display )) ret = count;
265     else
266     {
267         ret = WaitForMultipleObjectsEx( count+1, new_handles, flags & MWMO_WAITALL,
268                                         timeout, flags & MWMO_ALERTABLE );
269         if (ret == count) process_events( data->display );
270     }
271     data->process_event_count--;
272     return ret;
273 }
274
275 /***********************************************************************
276  *           EVENT_x11_time_to_win32_time
277  *
278  * Make our timer and the X timer line up as best we can
279  *  Pass 0 to retrieve the current adjustment value (times -1)
280  */
281 DWORD EVENT_x11_time_to_win32_time(Time time)
282 {
283   static DWORD adjust = 0;
284   DWORD now = GetTickCount();
285   DWORD ret;
286
287   if (! adjust && time != 0)
288   {
289     ret = now;
290     adjust = time - now;
291   }
292   else
293   {
294       /* If we got an event in the 'future', then our clock is clearly wrong. 
295          If we got it more than 10000 ms in the future, then it's most likely
296          that the clock has wrapped.  */
297
298       ret = time - adjust;
299       if (ret > now && ((ret - now) < 10000) && time != 0)
300       {
301         adjust += ret - now;
302         ret    -= ret - now;
303       }
304   }
305
306   return ret;
307
308 }
309
310 /*******************************************************************
311  *         can_activate_window
312  *
313  * Check if we can activate the specified window.
314  */
315 inline static BOOL can_activate_window( HWND hwnd )
316 {
317     LONG style = GetWindowLongW( hwnd, GWL_STYLE );
318     if (!(style & WS_VISIBLE)) return FALSE;
319     if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
320     return !(style & WS_DISABLED);
321 }
322
323
324 /**********************************************************************
325  *              set_focus
326  */
327 static void set_focus( HWND hwnd, Time time )
328 {
329     HWND focus;
330     Window win;
331
332     TRACE( "setting foreground window to %p\n", hwnd );
333     SetForegroundWindow( hwnd );
334
335     focus = GetFocus();
336     if (focus) focus = GetAncestor( focus, GA_ROOT );
337     win = X11DRV_get_whole_window(focus);
338
339     if (win)
340     {
341         TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
342         wine_tsx11_lock();
343         XSetInputFocus( thread_display(), win, RevertToParent, time );
344         wine_tsx11_unlock();
345     }
346 }
347
348
349 /**********************************************************************
350  *              handle_wm_protocols
351  */
352 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
353 {
354     Atom protocol = (Atom)event->data.l[0];
355
356     if (!protocol) return;
357
358     if (protocol == x11drv_atom(WM_DELETE_WINDOW))
359     {
360         /* Ignore the delete window request if the window has been disabled
361          * and we are in managed mode. This is to disallow applications from
362          * being closed by the window manager while in a modal state.
363          */
364         if (IsWindowEnabled(hwnd)) PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
365     }
366     else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
367     {
368         Time event_time = (Time)event->data.l[1];
369         HWND last_focus = x11drv_thread_data()->last_focus;
370
371         TRACE( "got take focus msg for %p, enabled=%d, focus=%p, active=%p, fg=%p, last=%p\n",
372                hwnd, IsWindowEnabled(hwnd), GetFocus(), GetActiveWindow(),
373                GetForegroundWindow(), last_focus );
374
375         if (can_activate_window(hwnd))
376         {
377             /* simulate a mouse click on the caption to find out
378              * whether the window wants to be activated */
379             LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
380                                        (WPARAM)GetAncestor( hwnd, GA_ROOT ),
381                                        MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
382             if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE) set_focus( hwnd, event_time );
383             else TRACE( "not setting focus to %p (%lx), ma=%ld\n", hwnd, event->window, ma );
384         }
385         else
386         {
387             hwnd = GetFocus();
388             if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
389             if (!hwnd) hwnd = GetActiveWindow();
390             if (!hwnd) hwnd = last_focus;
391             if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, event_time );
392         }
393     }
394     else if (protocol == x11drv_atom(_NET_WM_PING))
395     {
396       XClientMessageEvent xev;
397       xev = *event;
398       
399       TRACE("NET_WM Ping\n");
400       xev.window = DefaultRootWindow(xev.display);
401       XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
402       /* this line is semi-stolen from gtk2 */
403       TRACE("NET_WM Pong\n");
404     }
405 }
406
407
408 static const char * const focus_details[] =
409 {
410     "NotifyAncestor",
411     "NotifyVirtual",
412     "NotifyInferior",
413     "NotifyNonlinear",
414     "NotifyNonlinearVirtual",
415     "NotifyPointer",
416     "NotifyPointerRoot",
417     "NotifyDetailNone"
418 };
419
420 /**********************************************************************
421  *              EVENT_FocusIn
422  */
423 static void EVENT_FocusIn( HWND hwnd, XEvent *xev )
424 {
425     XFocusChangeEvent *event = &xev->xfocus;
426     XIC xic;
427
428     if (!hwnd) return;
429
430     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
431
432     if (event->detail == NotifyPointer) return;
433
434     if ((xic = X11DRV_get_ic( hwnd )))
435     {
436         wine_tsx11_lock();
437         XSetICFocus( xic );
438         wine_tsx11_unlock();
439     }
440     if (use_take_focus) return;  /* ignore FocusIn if we are using take focus */
441
442     if (!can_activate_window(hwnd))
443     {
444         HWND hwnd = GetFocus();
445         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
446         if (!hwnd) hwnd = GetActiveWindow();
447         if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
448         if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, CurrentTime );
449     }
450     else SetForegroundWindow( hwnd );
451 }
452
453
454 /**********************************************************************
455  *              EVENT_FocusOut
456  *
457  * Note: only top-level windows get FocusOut events.
458  */
459 static void EVENT_FocusOut( HWND hwnd, XEvent *xev )
460 {
461     XFocusChangeEvent *event = &xev->xfocus;
462     HWND hwnd_tmp;
463     Window focus_win;
464     int revert;
465     XIC xic;
466
467     if (!hwnd) return;
468
469     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
470
471     if (event->detail == NotifyPointer) return;
472     if (ximInComposeMode) return;
473
474     x11drv_thread_data()->last_focus = hwnd;
475     if ((xic = X11DRV_get_ic( hwnd )))
476     {
477         wine_tsx11_lock();
478         XUnsetICFocus( xic );
479         wine_tsx11_unlock();
480     }
481     if (hwnd != GetForegroundWindow()) return;
482     SendMessageA( hwnd, WM_CANCELMODE, 0, 0 );
483
484     /* don't reset the foreground window, if the window which is
485        getting the focus is a Wine window */
486
487     wine_tsx11_lock();
488     XGetInputFocus( thread_display(), &focus_win, &revert );
489     if (focus_win)
490     {
491         if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0)
492             focus_win = 0;
493     }
494     wine_tsx11_unlock();
495
496     if (!focus_win)
497     {
498         /* Abey : 6-Oct-99. Check again if the focus out window is the
499            Foreground window, because in most cases the messages sent
500            above must have already changed the foreground window, in which
501            case we don't have to change the foreground window to 0 */
502         if (hwnd == GetForegroundWindow())
503         {
504             TRACE( "lost focus, setting fg to 0\n" );
505             SetForegroundWindow( 0 );
506         }
507     }
508 }
509
510
511 /***********************************************************************
512  *           EVENT_PropertyNotify
513  *   We use this to release resources like Pixmaps when a selection
514  *   client no longer needs them.
515  */
516 static void EVENT_PropertyNotify( HWND hwnd, XEvent *xev )
517 {
518   XPropertyEvent *event = &xev->xproperty;
519   /* Check if we have any resources to free */
520   TRACE("Received PropertyNotify event: \n");
521
522   switch(event->state)
523   {
524     case PropertyDelete:
525     {
526       TRACE("\tPropertyDelete for atom %ld on window %ld\n",
527             event->atom, (long)event->window);
528       break;
529     }
530
531     case PropertyNewValue:
532     {
533       TRACE("\tPropertyNewValue for atom %ld on window %ld\n\n",
534             event->atom, (long)event->window);
535       break;
536     }
537
538     default:
539       break;
540   }
541 }
542
543 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
544 {
545     RECT tempRect;
546
547     if (!IsWindowEnabled(hQueryWnd)) return 0;
548     
549     GetWindowRect(hQueryWnd, &tempRect);
550
551     if(!PtInRect(&tempRect, *lpPt)) return 0;
552
553     if (!IsIconic( hQueryWnd ))
554     {
555         GetClientRect( hQueryWnd, &tempRect );
556         MapWindowPoints( hQueryWnd, 0, (LPPOINT)&tempRect, 2 );
557
558         if (PtInRect( &tempRect, *lpPt))
559         {
560             HWND *list = WIN_ListChildren( hQueryWnd );
561             HWND bResult = 0;
562
563             if (list)
564             {
565                 int i;
566                 
567                 for (i = 0; list[i]; i++)
568                 {
569                     if (GetWindowLongW( list[i], GWL_STYLE ) & WS_VISIBLE)
570                     {
571                         GetWindowRect( list[i], &tempRect );
572                         if (PtInRect( &tempRect, *lpPt )) break;
573                     }
574                 }
575                 if (list[i])
576                 {
577                     if (IsWindowEnabled( list[i] ))
578                         bResult = find_drop_window( list[i], lpPt );
579                 }
580                 HeapFree( GetProcessHeap(), 0, list );
581             }
582             if(bResult) return bResult;
583         }
584     }
585
586     if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
587     
588     ScreenToClient(hQueryWnd, lpPt);
589
590     return hQueryWnd;
591 }
592
593 /**********************************************************************
594  *           EVENT_DropFromOffix
595  *
596  * don't know if it still works (last Changlog is from 96/11/04)
597  */
598 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
599 {
600     unsigned long       data_length;
601     unsigned long       aux_long;
602     unsigned char*      p_data = NULL;
603     union {
604         Atom    atom_aux;
605         struct {
606             int x;
607             int y;
608         }       pt_aux;
609         int     i;
610     }                   u;
611     int                 x, y;
612     BOOL                bAccept;
613     Window              win, w_aux_root, w_aux_child;
614     WND*                pWnd;
615     HWND                hScope = hWnd;
616
617     win = X11DRV_get_whole_window(hWnd);
618     wine_tsx11_lock();
619     XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
620                    &x, &y, (int *) &u.pt_aux.x, (int *) &u.pt_aux.y,
621                    (unsigned int*)&aux_long);
622     wine_tsx11_unlock();
623
624     pWnd = WIN_GetPtr(hWnd);
625
626     /* find out drop point and drop window */
627     if( x < 0 || y < 0 ||
628         x > (pWnd->rectWindow.right - pWnd->rectWindow.left) ||
629         y > (pWnd->rectWindow.bottom - pWnd->rectWindow.top) )
630     {   
631         bAccept = pWnd->dwExStyle & WS_EX_ACCEPTFILES; 
632         x = 0;
633         y = 0; 
634     }
635     else
636     {
637         POINT   pt = { x, y };
638         HWND    hwndDrop = find_drop_window( hWnd, &pt );
639         if (hwndDrop)
640         {
641             x = pt.x;
642             y = pt.y;
643             hScope = hwndDrop;
644             bAccept = TRUE;
645         }
646         else
647         {
648             bAccept = FALSE;
649         }
650     }
651     WIN_ReleasePtr(pWnd);
652
653     if (!bAccept) return;
654
655     wine_tsx11_lock();
656     XGetWindowProperty( event->display, DefaultRootWindow(event->display),
657                         x11drv_atom(DndSelection), 0, 65535, FALSE,
658                         AnyPropertyType, &u.atom_aux, (int *) &u.pt_aux.y,
659                         &data_length, &aux_long, &p_data);
660     wine_tsx11_unlock();
661
662     if( !aux_long && p_data)  /* don't bother if > 64K */
663     {
664         signed char *p = (signed char*) p_data;
665         char *p_drop;
666
667         aux_long = 0;
668         while( *p )  /* calculate buffer size */
669         {
670             p_drop = p;
671             if((u.i = *p) != -1 )
672             {
673                 INT len = GetShortPathNameA( p, NULL, 0 );
674                 if (len) aux_long += len + 1;
675                 else *p = -1;
676             }
677             p += strlen(p) + 1;
678         }
679         if( aux_long && aux_long < 65535 )
680         {
681             HDROP                 hDrop;
682             DROPFILES *lpDrop;
683
684             aux_long += sizeof(DROPFILES) + 1;
685             hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
686             lpDrop = (DROPFILES*)GlobalLock( hDrop );
687
688             if( lpDrop )
689             {
690                 WND *pDropWnd = WIN_GetPtr( hScope );
691                 lpDrop->pFiles = sizeof(DROPFILES);
692                 lpDrop->pt.x = x;
693                 lpDrop->pt.y = y;
694                 lpDrop->fNC =
695                     ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
696                       y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
697                       x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
698                       y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
699                 lpDrop->fWide = FALSE;
700                 WIN_ReleasePtr(pDropWnd);
701                 p_drop = (char *)(lpDrop + 1);
702                 p = p_data;
703                 while(*p)
704                 {
705                     if( *p != -1 ) /* use only "good" entries */
706                     {
707                         GetShortPathNameA( p, p_drop, 65535 );
708                         p_drop += strlen( p_drop ) + 1;
709                     }
710                     p += strlen(p) + 1;
711                 }
712                 *p_drop = '\0';
713                 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
714             }
715         }
716     }
717     wine_tsx11_lock();
718     if( p_data ) XFree(p_data);
719     wine_tsx11_unlock();
720 }
721
722 /**********************************************************************
723  *           EVENT_DropURLs
724  *
725  * drop items are separated by \n
726  * each item is prefixed by its mime type
727  *
728  * event->data.l[3], event->data.l[4] contains drop x,y position
729  */
730 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
731 {
732   unsigned long data_length;
733   unsigned long aux_long, drop_len = 0;
734   unsigned char *p_data = NULL; /* property data */
735   char          *p_drop = NULL;
736   char          *p, *next;
737   int           x, y;
738   DROPFILES *lpDrop;
739   HDROP hDrop;
740   union {
741     Atom        atom_aux;
742     int         i;
743     Window      w_aux;
744   }             u; /* unused */
745
746   if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
747
748   wine_tsx11_lock();
749   XGetWindowProperty( event->display, DefaultRootWindow(event->display),
750                       x11drv_atom(DndSelection), 0, 65535, FALSE,
751                       AnyPropertyType, &u.atom_aux, &u.i,
752                       &data_length, &aux_long, &p_data);
753   wine_tsx11_unlock();
754   if (aux_long)
755     WARN("property too large, truncated!\n");
756   TRACE("urls=%s\n", p_data);
757
758   if( !aux_long && p_data) {    /* don't bother if > 64K */
759     /* calculate length */
760     p = p_data;
761     next = strchr(p, '\n');
762     while (p) {
763       if (next) *next=0;
764       if (strncmp(p,"file:",5) == 0 ) {
765         INT len = GetShortPathNameA( p+5, NULL, 0 );
766         if (len) drop_len += len + 1;
767       }
768       if (next) {
769         *next = '\n';
770         p = next + 1;
771         next = strchr(p, '\n');
772       } else {
773         p = NULL;
774       }
775     }
776
777     if( drop_len && drop_len < 65535 ) {
778       wine_tsx11_lock();
779       XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
780                      &x, &y, &u.i, &u.i, &u.i);
781       wine_tsx11_unlock();
782
783       drop_len += sizeof(DROPFILES) + 1;
784       hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
785       lpDrop = (DROPFILES *) GlobalLock( hDrop );
786
787       if( lpDrop ) {
788           WND *pDropWnd = WIN_GetPtr( hWnd );
789           lpDrop->pFiles = sizeof(DROPFILES);
790           lpDrop->pt.x = (INT)x;
791           lpDrop->pt.y = (INT)y;
792           lpDrop->fNC =
793             ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
794               y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
795               x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
796               y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
797           lpDrop->fWide = FALSE;
798           p_drop = (char*)(lpDrop + 1);
799           WIN_ReleasePtr(pDropWnd);
800       }
801
802       /* create message content */
803       if (p_drop) {
804         p = p_data;
805         next = strchr(p, '\n');
806         while (p) {
807           if (next) *next=0;
808           if (strncmp(p,"file:",5) == 0 ) {
809             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
810             if (len) {
811               TRACE("drop file %s as %s\n", p+5, p_drop);
812               p_drop += len+1;
813             } else {
814               WARN("can't convert file %s to dos name \n", p+5);
815             }
816           } else {
817             WARN("unknown mime type %s\n", p);
818           }
819           if (next) {
820             *next = '\n';
821             p = next + 1;
822             next = strchr(p, '\n');
823           } else {
824             p = NULL;
825           }
826           *p_drop = '\0';
827         }
828
829         GlobalUnlock(hDrop);
830         PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
831       }
832     }
833     wine_tsx11_lock();
834     if( p_data ) XFree(p_data);
835     wine_tsx11_unlock();
836   }
837 }
838
839 /**********************************************************************
840  *              handle_dnd_protocol
841  */
842 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
843 {
844     Window root, child;
845     int root_x, root_y, child_x, child_y;
846     unsigned int u;
847
848     /* query window (drag&drop event contains only drag window) */
849     wine_tsx11_lock();
850     XQueryPointer( event->display, root_window, &root, &child,
851                    &root_x, &root_y, &child_x, &child_y, &u);
852     if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
853     wine_tsx11_unlock();
854     if (!hwnd) return;
855     if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
856         EVENT_DropFromOffiX(hwnd, event);
857     else if (event->data.l[0] == DndURL)
858         EVENT_DropURLs(hwnd, event);
859 }
860
861
862 struct client_message_handler
863 {
864     int    atom;                                  /* protocol atom */
865     void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
866 };
867
868 static const struct client_message_handler client_messages[] =
869 {
870     { XATOM_WM_PROTOCOLS, handle_wm_protocols },
871     { XATOM_DndProtocol,  handle_dnd_protocol },
872     { XATOM_XdndEnter,    X11DRV_XDND_EnterEvent },
873     { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
874     { XATOM_XdndDrop,     X11DRV_XDND_DropEvent },
875     { XATOM_XdndLeave,    X11DRV_XDND_LeaveEvent }
876 };
877
878
879 /**********************************************************************
880  *           EVENT_ClientMessage
881  */
882 static void EVENT_ClientMessage( HWND hwnd, XEvent *xev )
883 {
884     XClientMessageEvent *event = &xev->xclient;
885     unsigned int i;
886
887     if (!hwnd) return;
888
889     if (event->format != 32)
890     {
891         WARN( "Don't know how to handle format %d\n", event->format );
892         return;
893     }
894
895     for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
896     {
897         if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
898         {
899             client_messages[i].handler( hwnd, event );
900             return;
901         }
902     }
903     TRACE( "no handler found for %ld\n", event->message_type );
904 }