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