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