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