msi/tests: Skip some source tests if a required product key cannot be created.
[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 #ifdef HAVE_POLL_H
25 #include <poll.h>
26 #endif
27 #ifdef HAVE_SYS_POLL_H
28 #include <sys/poll.h>
29 #endif
30 #include <X11/Xatom.h>
31 #include <X11/keysym.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xresource.h>
34 #include <X11/Xutil.h>
35
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <string.h>
39
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h"
45 #include "wingdi.h"
46
47 #include "x11drv.h"
48
49 /* avoid conflict with field names in included win32 headers */
50 #undef Status
51 #include "shlobj.h"  /* DROPFILES */
52 #include "shellapi.h"
53
54 #include "wine/server.h"
55 #include "wine/debug.h"
56
57 WINE_DEFAULT_DEBUG_CHANNEL(event);
58
59 extern BOOL ximInComposeMode;
60
61 #define DndNotDnd       -1    /* OffiX drag&drop */
62 #define DndUnknown      0
63 #define DndRawData      1
64 #define DndFile         2
65 #define DndFiles        3
66 #define DndText         4
67 #define DndDir          5
68 #define DndLink         6
69 #define DndExe          7
70
71 #define DndEND          8
72
73 #define DndURL          128   /* KDE drag&drop */
74
75   /* Event handlers */
76 static void X11DRV_FocusIn( HWND hwnd, XEvent *event );
77 static void X11DRV_FocusOut( HWND hwnd, XEvent *event );
78 static void X11DRV_Expose( HWND hwnd, XEvent *event );
79 static void X11DRV_MapNotify( HWND hwnd, XEvent *event );
80 static void X11DRV_ConfigureNotify( HWND hwnd, XEvent *event );
81 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
82 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
83
84 struct event_handler
85 {
86     int                  type;    /* event type */
87     x11drv_event_handler handler; /* corresponding handler function */
88 };
89
90 #define MAX_EVENT_HANDLERS 64
91
92 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
93 {
94     /* list must be sorted by event type */
95     { KeyPress,         X11DRV_KeyEvent },
96     { KeyRelease,       X11DRV_KeyEvent },
97     { ButtonPress,      X11DRV_ButtonPress },
98     { ButtonRelease,    X11DRV_ButtonRelease },
99     { MotionNotify,     X11DRV_MotionNotify },
100     { EnterNotify,      X11DRV_EnterNotify },
101     /* LeaveNotify */
102     { FocusIn,          X11DRV_FocusIn },
103     { FocusOut,         X11DRV_FocusOut },
104     { KeymapNotify,     X11DRV_KeymapNotify },
105     { Expose,           X11DRV_Expose },
106     /* GraphicsExpose */
107     /* NoExpose */
108     /* VisibilityNotify */
109     /* CreateNotify */
110     { DestroyNotify,    X11DRV_DestroyNotify },
111     /* UnmapNotify */
112     { MapNotify,        X11DRV_MapNotify },
113     /* MapRequest */
114     /* ReparentNotify */
115     { ConfigureNotify,  X11DRV_ConfigureNotify },
116     /* ConfigureRequest */
117     /* GravityNotify */
118     /* ResizeRequest */
119     /* CirculateNotify */
120     /* CirculateRequest */
121     { PropertyNotify,   X11DRV_PropertyNotify },
122     { SelectionClear,   X11DRV_SelectionClear },
123     { SelectionRequest, X11DRV_SelectionRequest },
124     /* SelectionNotify */
125     /* ColormapNotify */
126     { ClientMessage,    X11DRV_ClientMessage },
127     { MappingNotify,    X11DRV_MappingNotify },
128 };
129
130 static int nb_event_handlers = 18;  /* change this if you add handlers above */
131
132
133 /* return the name of an X event */
134 static const char *dbgstr_event( int type )
135 {
136     static const char * const event_names[] =
137     {
138         "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
139         "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
140         "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
141         "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
142         "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
143         "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
144         "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
145         "ClientMessage", "MappingNotify"
146     };
147
148     if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
149     return wine_dbg_sprintf( "Extension event %d", type );
150 }
151
152
153 /***********************************************************************
154  *           find_handler
155  *
156  * Find the handler for a given event type. Caller must hold the x11 lock.
157  */
158 static inline x11drv_event_handler find_handler( int type )
159 {
160     int min = 0, max = nb_event_handlers - 1;
161
162     while (min <= max)
163     {
164         int pos = (min + max) / 2;
165         if (handlers[pos].type == type) return handlers[pos].handler;
166         if (handlers[pos].type > type) max = pos - 1;
167         else min = pos + 1;
168     }
169     return NULL;
170 }
171
172
173 /***********************************************************************
174  *           X11DRV_register_event_handler
175  *
176  * Register a handler for a given event type.
177  * If already registered, overwrite the previous handler.
178  */
179 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
180 {
181     int min, max;
182
183     wine_tsx11_lock();
184     min = 0;
185     max = nb_event_handlers - 1;
186     while (min <= max)
187     {
188         int pos = (min + max) / 2;
189         if (handlers[pos].type == type)
190         {
191             handlers[pos].handler = handler;
192             goto done;
193         }
194         if (handlers[pos].type > type) max = pos - 1;
195         else min = pos + 1;
196     }
197     /* insert it between max and min */
198     memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
199     handlers[min].type = type;
200     handlers[min].handler = handler;
201     nb_event_handlers++;
202     assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
203 done:
204     wine_tsx11_unlock();
205     TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
206 }
207
208
209 /***********************************************************************
210  *           filter_event
211  */
212 static Bool filter_event( Display *display, XEvent *event, char *arg )
213 {
214     ULONG_PTR mask = (ULONG_PTR)arg;
215
216     if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
217
218     switch(event->type)
219     {
220     case KeyPress:
221     case KeyRelease:
222     case KeymapNotify:
223     case MappingNotify:
224         return (mask & QS_KEY) != 0;
225     case ButtonPress:
226     case ButtonRelease:
227         return (mask & QS_MOUSEBUTTON) != 0;
228     case MotionNotify:
229     case EnterNotify:
230     case LeaveNotify:
231         return (mask & QS_MOUSEMOVE) != 0;
232     case Expose:
233         return (mask & QS_PAINT) != 0;
234     case FocusIn:
235     case FocusOut:
236     case MapNotify:
237     case UnmapNotify:
238     case ConfigureNotify:
239     case PropertyNotify:
240     case ClientMessage:
241         return (mask & QS_POSTMESSAGE) != 0;
242     default:
243         return (mask & QS_SENDMESSAGE) != 0;
244     }
245 }
246
247
248 enum event_merge_action
249 {
250     MERGE_DISCARD,  /* discard the old event */
251     MERGE_HANDLE,   /* handle the old event */
252     MERGE_KEEP      /* keep the old event for future merging */
253 };
254
255 /***********************************************************************
256  *           merge_events
257  *
258  * Try to merge 2 consecutive events.
259  */
260 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
261 {
262     switch (prev->type)
263     {
264     case ConfigureNotify:
265         switch (next->type)
266         {
267         case ConfigureNotify:
268             if (prev->xany.window == next->xany.window)
269             {
270                 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
271                 return MERGE_DISCARD;
272             }
273             break;
274         case Expose:
275         case PropertyNotify:
276             return MERGE_KEEP;
277         }
278         break;
279     case MotionNotify:
280         if (prev->xany.window == next->xany.window && next->type == MotionNotify)
281         {
282             TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
283             return MERGE_DISCARD;
284         }
285         break;
286     }
287     return MERGE_HANDLE;
288 }
289
290
291 /***********************************************************************
292  *           call_event_handler
293  */
294 static inline void call_event_handler( Display *display, XEvent *event )
295 {
296     HWND hwnd;
297     x11drv_event_handler handler;
298     XEvent *prev;
299     struct x11drv_thread_data *thread_data;
300
301     if (!(handler = find_handler( event->type )))
302     {
303         TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
304         return;  /* no handler, ignore it */
305     }
306
307     if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
308         hwnd = 0;  /* not for a registered window */
309     if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
310
311     TRACE( "%lu %s for hwnd/window %p/%lx\n",
312            event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window );
313     wine_tsx11_unlock();
314     thread_data = x11drv_thread_data();
315     prev = thread_data->current_event;
316     thread_data->current_event = event;
317     handler( hwnd, event );
318     thread_data->current_event = prev;
319     wine_tsx11_lock();
320 }
321
322
323 /***********************************************************************
324  *           process_events
325  */
326 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
327 {
328     XEvent event, prev_event;
329     int count = 0;
330     enum event_merge_action action = MERGE_DISCARD;
331
332     prev_event.type = 0;
333     wine_tsx11_lock();
334     while (XCheckIfEvent( display, &event, filter, (char *)arg ))
335     {
336         count++;
337         if (XFilterEvent( &event, None ))
338         {
339             /*
340              * SCIM on linux filters key events strangely. It does not filter the
341              * KeyPress events for these keys however it does filter the
342              * KeyRelease events. This causes wine to become very confused as
343              * to the keyboard state.
344              *
345              * We need to let those KeyRelease events be processed so that the
346              * keyboard state is correct.
347              */
348             if (event.type == KeyRelease)
349             {
350                 KeySym keysym = 0;
351                 XKeyEvent *keyevent = &event.xkey;
352
353                 XLookupString(keyevent, NULL, 0, &keysym, NULL);
354                 if (!(keysym == XK_Shift_L ||
355                     keysym == XK_Shift_R ||
356                     keysym == XK_Control_L ||
357                     keysym == XK_Control_R ||
358                     keysym == XK_Alt_R ||
359                     keysym == XK_Alt_L ||
360                     keysym == XK_Meta_R ||
361                     keysym == XK_Meta_L))
362                         continue; /* not a key we care about, ignore it */
363             }
364             else
365                 continue;  /* filtered, ignore it */
366         }
367         if (prev_event.type) action = merge_events( &prev_event, &event );
368         switch( action )
369         {
370         case MERGE_DISCARD:  /* discard prev, keep new */
371             prev_event = event;
372             break;
373         case MERGE_HANDLE:  /* handle prev, keep new */
374             call_event_handler( display, &prev_event );
375             prev_event = event;
376             break;
377         case MERGE_KEEP:  /* handle new, keep prev for future merging */
378             call_event_handler( display, &event );
379             break;
380         }
381     }
382     if (prev_event.type) call_event_handler( display, &prev_event );
383     XFlush( gdi_display );
384     wine_tsx11_unlock();
385     if (count) TRACE( "processed %d events\n", count );
386     return count;
387 }
388
389
390 /***********************************************************************
391  *           MsgWaitForMultipleObjectsEx   (X11DRV.@)
392  */
393 DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
394                                                 DWORD timeout, DWORD mask, DWORD flags )
395 {
396     DWORD ret;
397     struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
398
399     if (!data)
400     {
401         if (!count && !timeout) return WAIT_TIMEOUT;
402         return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
403                                          timeout, flags & MWMO_ALERTABLE );
404     }
405
406     if (data->current_event) mask = 0;  /* don't process nested events */
407
408     if (process_events( data->display, filter_event, mask )) ret = count - 1;
409     else if (count || timeout)
410     {
411         ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
412                                         timeout, flags & MWMO_ALERTABLE );
413         if (ret == count - 1) process_events( data->display, filter_event, mask );
414     }
415     else ret = WAIT_TIMEOUT;
416
417     return ret;
418 }
419
420 /***********************************************************************
421  *           EVENT_x11_time_to_win32_time
422  *
423  * Make our timer and the X timer line up as best we can
424  *  Pass 0 to retrieve the current adjustment value (times -1)
425  */
426 DWORD EVENT_x11_time_to_win32_time(Time time)
427 {
428   static DWORD adjust = 0;
429   DWORD now = GetTickCount();
430   DWORD ret;
431
432   if (! adjust && time != 0)
433   {
434     ret = now;
435     adjust = time - now;
436   }
437   else
438   {
439       /* If we got an event in the 'future', then our clock is clearly wrong. 
440          If we got it more than 10000 ms in the future, then it's most likely
441          that the clock has wrapped.  */
442
443       ret = time - adjust;
444       if (ret > now && ((ret - now) < 10000) && time != 0)
445       {
446         adjust += ret - now;
447         ret    -= ret - now;
448       }
449   }
450
451   return ret;
452
453 }
454
455 /*******************************************************************
456  *         can_activate_window
457  *
458  * Check if we can activate the specified window.
459  */
460 static inline BOOL can_activate_window( HWND hwnd )
461 {
462     LONG style = GetWindowLongW( hwnd, GWL_STYLE );
463     if (!(style & WS_VISIBLE)) return FALSE;
464     if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
465     if (style & WS_MINIMIZE) return FALSE;
466     if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
467     if (hwnd == GetDesktopWindow()) return FALSE;
468     return !(style & WS_DISABLED);
469 }
470
471
472 /**********************************************************************
473  *              set_focus
474  */
475 static void set_focus( Display *display, HWND hwnd, Time time )
476 {
477     HWND focus;
478     Window win;
479     GUITHREADINFO threadinfo;
480
481     TRACE( "setting foreground window to %p\n", hwnd );
482     SetForegroundWindow( hwnd );
483
484     GetGUIThreadInfo(0, &threadinfo);
485     focus = threadinfo.hwndFocus;
486     if (!focus) focus = threadinfo.hwndActive;
487     if (focus) focus = GetAncestor( focus, GA_ROOT );
488     win = X11DRV_get_whole_window(focus);
489
490     if (win)
491     {
492         TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
493         wine_tsx11_lock();
494         XSetInputFocus( display, win, RevertToParent, time );
495         wine_tsx11_unlock();
496     }
497 }
498
499
500 /**********************************************************************
501  *              handle_wm_protocols
502  */
503 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
504 {
505     Atom protocol = (Atom)event->data.l[0];
506     Time event_time = (Time)event->data.l[1];
507
508     if (!protocol) return;
509
510     if (protocol == x11drv_atom(WM_DELETE_WINDOW))
511     {
512         update_user_time( event_time );
513
514         if (hwnd == GetDesktopWindow())
515         {
516             /* The desktop window does not have a close button that we can
517              * pretend to click. Therefore, we simply send it a close command. */
518             SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
519             return;
520         }
521
522         /* Ignore the delete window request if the window has been disabled
523          * and we are in managed mode. This is to disallow applications from
524          * being closed by the window manager while in a modal state.
525          */
526         if (IsWindowEnabled(hwnd))
527         {
528             HMENU hSysMenu;
529
530             if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
531             hSysMenu = GetSystemMenu(hwnd, FALSE);
532             if (hSysMenu)
533             {
534                 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
535                 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
536                     return;
537             }
538             if (GetActiveWindow() != hwnd)
539             {
540                 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
541                                            (WPARAM)GetAncestor( hwnd, GA_ROOT ),
542                                            MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
543                 switch(ma)
544                 {
545                     case MA_NOACTIVATEANDEAT:
546                     case MA_ACTIVATEANDEAT:
547                         return;
548                     case MA_NOACTIVATE:
549                         break;
550                     case MA_ACTIVATE:
551                     case 0:
552                         SetActiveWindow(hwnd);
553                         break;
554                     default:
555                         WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
556                         break;
557                 }
558             }
559
560             PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
561         }
562     }
563     else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
564     {
565         HWND last_focus = x11drv_thread_data()->last_focus;
566
567         TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
568                hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
569                GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
570
571         if (can_activate_window(hwnd))
572         {
573             /* simulate a mouse click on the caption to find out
574              * whether the window wants to be activated */
575             LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
576                                        (WPARAM)GetAncestor( hwnd, GA_ROOT ),
577                                        MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
578             if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
579             {
580                 set_focus( event->display, hwnd, event_time );
581                 return;
582             }
583         }
584         else if (hwnd == GetDesktopWindow())
585         {
586             hwnd = GetForegroundWindow();
587             if (!hwnd) hwnd = last_focus;
588             if (!hwnd) hwnd = GetDesktopWindow();
589             set_focus( event->display, hwnd, event_time );
590             return;
591         }
592         /* try to find some other window to give the focus to */
593         hwnd = GetFocus();
594         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
595         if (!hwnd) hwnd = GetActiveWindow();
596         if (!hwnd) hwnd = last_focus;
597         if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
598     }
599     else if (protocol == x11drv_atom(_NET_WM_PING))
600     {
601       XClientMessageEvent xev;
602       xev = *event;
603       
604       TRACE("NET_WM Ping\n");
605       wine_tsx11_lock();
606       xev.window = DefaultRootWindow(xev.display);
607       XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
608       wine_tsx11_unlock();
609       /* this line is semi-stolen from gtk2 */
610       TRACE("NET_WM Pong\n");
611     }
612 }
613
614
615 static const char * const focus_details[] =
616 {
617     "NotifyAncestor",
618     "NotifyVirtual",
619     "NotifyInferior",
620     "NotifyNonlinear",
621     "NotifyNonlinearVirtual",
622     "NotifyPointer",
623     "NotifyPointerRoot",
624     "NotifyDetailNone"
625 };
626
627 /**********************************************************************
628  *              X11DRV_FocusIn
629  */
630 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
631 {
632     XFocusChangeEvent *event = &xev->xfocus;
633     XIC xic;
634
635     if (!hwnd) return;
636
637     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
638
639     if (event->detail == NotifyPointer) return;
640
641     if ((xic = X11DRV_get_ic( hwnd )))
642     {
643         wine_tsx11_lock();
644         XSetICFocus( xic );
645         wine_tsx11_unlock();
646     }
647     if (use_take_focus) return;  /* ignore FocusIn if we are using take focus */
648
649     if (!can_activate_window(hwnd))
650     {
651         HWND hwnd = GetFocus();
652         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
653         if (!hwnd) hwnd = GetActiveWindow();
654         if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
655         if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
656     }
657     else SetForegroundWindow( hwnd );
658 }
659
660
661 /**********************************************************************
662  *              X11DRV_FocusOut
663  *
664  * Note: only top-level windows get FocusOut events.
665  */
666 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
667 {
668     XFocusChangeEvent *event = &xev->xfocus;
669     HWND hwnd_tmp;
670     Window focus_win;
671     int revert;
672     XIC xic;
673
674     if (!hwnd) return;
675
676     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
677
678     if (event->detail == NotifyPointer) return;
679     if (ximInComposeMode) return;
680
681     x11drv_thread_data()->last_focus = hwnd;
682     if ((xic = X11DRV_get_ic( hwnd )))
683     {
684         wine_tsx11_lock();
685         XUnsetICFocus( xic );
686         wine_tsx11_unlock();
687     }
688     if (hwnd != GetForegroundWindow()) return;
689     if (root_window != DefaultRootWindow(event->display)) return;
690     SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
691
692     /* don't reset the foreground window, if the window which is
693        getting the focus is a Wine window */
694
695     wine_tsx11_lock();
696     XGetInputFocus( event->display, &focus_win, &revert );
697     if (focus_win)
698     {
699         if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
700             focus_win = 0;
701     }
702     wine_tsx11_unlock();
703
704     if (!focus_win)
705     {
706         /* Abey : 6-Oct-99. Check again if the focus out window is the
707            Foreground window, because in most cases the messages sent
708            above must have already changed the foreground window, in which
709            case we don't have to change the foreground window to 0 */
710         if (hwnd == GetForegroundWindow())
711         {
712             TRACE( "lost focus, setting fg to desktop\n" );
713             SetForegroundWindow( GetDesktopWindow() );
714         }
715     }
716 }
717
718
719 /***********************************************************************
720  *           X11DRV_Expose
721  */
722 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
723 {
724     XExposeEvent *event = &xev->xexpose;
725     RECT rect;
726     struct x11drv_win_data *data;
727     int flags = RDW_INVALIDATE | RDW_ERASE;
728
729     TRACE( "win %p (%lx) %d,%d %dx%d\n",
730            hwnd, event->window, event->x, event->y, event->width, event->height );
731
732     if (!(data = X11DRV_get_win_data( hwnd ))) return;
733
734     if (event->window == data->whole_window)
735     {
736         rect.left = data->whole_rect.left + event->x;
737         rect.top  = data->whole_rect.top + event->y;
738         flags |= RDW_FRAME;
739     }
740     else
741     {
742         rect.left = data->client_rect.left + event->x;
743         rect.top  = data->client_rect.top + event->y;
744     }
745     rect.right  = rect.left + event->width;
746     rect.bottom = rect.top + event->height;
747
748     if (event->window != root_window)
749     {
750         SERVER_START_REQ( update_window_zorder )
751         {
752             req->window      = wine_server_user_handle( hwnd );
753             req->rect.left   = rect.left;
754             req->rect.top    = rect.top;
755             req->rect.right  = rect.right;
756             req->rect.bottom = rect.bottom;
757             wine_server_call( req );
758         }
759         SERVER_END_REQ;
760
761         /* make position relative to client area instead of parent */
762         OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
763         flags |= RDW_ALLCHILDREN;
764     }
765
766     RedrawWindow( hwnd, &rect, 0, flags );
767 }
768
769
770 /**********************************************************************
771  *              X11DRV_MapNotify
772  */
773 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
774 {
775     struct x11drv_win_data *data;
776
777     if (!(data = X11DRV_get_win_data( hwnd ))) return;
778     if (!data->mapped) return;
779
780     if (!data->managed)
781     {
782         HWND hwndFocus = GetFocus();
783         if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus);  /* FIXME */
784     }
785 }
786
787
788 /***********************************************************************
789  *     is_net_wm_state_maximized
790  */
791 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
792 {
793     Atom type, *state;
794     int format, ret = 0;
795     unsigned long i, count, remaining;
796
797     wine_tsx11_lock();
798     if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
799                              65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
800                              &remaining, (unsigned char **)&state ))
801     {
802         if (type == XA_ATOM && format == 32)
803         {
804             for (i = 0; i < count; i++)
805             {
806                 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
807                     state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
808                     ret++;
809             }
810         }
811         XFree( state );
812     }
813     wine_tsx11_unlock();
814     return (ret == 2);
815 }
816
817
818 /***********************************************************************
819  *              X11DRV_ConfigureNotify
820  */
821 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
822 {
823     XConfigureEvent *event = &xev->xconfigure;
824     struct x11drv_win_data *data;
825     RECT rect;
826     UINT flags;
827     int cx, cy, x = event->x, y = event->y;
828
829     if (!hwnd) return;
830     if (!(data = X11DRV_get_win_data( hwnd ))) return;
831     if (!data->mapped || data->iconic || !data->managed) return;
832     if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
833     {
834         TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
835                hwnd, data->whole_window, event->x, event->y, event->width, event->height,
836                event->serial, data->configure_serial );
837         return;
838     }
839
840     /* Get geometry */
841
842     if (!event->send_event)  /* normal event, need to map coordinates to the root */
843     {
844         Window child;
845         wine_tsx11_lock();
846         XTranslateCoordinates( event->display, data->whole_window, root_window,
847                                0, 0, &x, &y, &child );
848         wine_tsx11_unlock();
849     }
850     rect.left   = x;
851     rect.top    = y;
852     rect.right  = x + event->width;
853     rect.bottom = y + event->height;
854     OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
855     TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
856            hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
857            event->x, event->y, event->width, event->height );
858     X11DRV_X_to_window_rect( data, &rect );
859
860     if (is_net_wm_state_maximized( event->display, data ))
861     {
862         if (!IsZoomed( data->hwnd ))
863         {
864             TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
865             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
866             return;
867         }
868     }
869     else
870     {
871         if (IsZoomed( data->hwnd ))
872         {
873             TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
874             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
875             return;
876         }
877     }
878
879     /* Compare what has changed */
880
881     x     = rect.left;
882     y     = rect.top;
883     cx    = rect.right - rect.left;
884     cy    = rect.bottom - rect.top;
885     flags = SWP_NOACTIVATE | SWP_NOZORDER;
886
887     if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
888     else
889         TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
890                hwnd, data->window_rect.left, data->window_rect.top, x, y );
891
892     if ((data->window_rect.right - data->window_rect.left == cx &&
893          data->window_rect.bottom - data->window_rect.top == cy) ||
894         (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
895     {
896         if (flags & SWP_NOMOVE) return;  /* if nothing changed, don't do anything */
897         flags |= SWP_NOSIZE;
898     }
899     else
900         TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
901                hwnd, data->window_rect.right - data->window_rect.left,
902                data->window_rect.bottom - data->window_rect.top, cx, cy );
903
904     SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
905 }
906
907
908 /***********************************************************************
909  *           get_window_wm_state
910  */
911 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
912 {
913     struct
914     {
915         CARD32 state;
916         XID     icon;
917     } *state;
918     Atom type;
919     int format, ret = -1;
920     unsigned long count, remaining;
921
922     wine_tsx11_lock();
923     if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
924                              sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
925                              &type, &format, &count, &remaining, (unsigned char **)&state ))
926     {
927         if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
928             ret = state->state;
929         XFree( state );
930     }
931     wine_tsx11_unlock();
932     return ret;
933 }
934
935
936 /***********************************************************************
937  *           handle_wm_state_notify
938  *
939  * Handle a PropertyNotify for WM_STATE.
940  */
941 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
942                                     BOOL update_window )
943 {
944     DWORD style;
945
946     switch(event->state)
947     {
948     case PropertyDelete:
949         TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
950         data->wm_state = WithdrawnState;
951         break;
952     case PropertyNewValue:
953         {
954             int old_state = data->wm_state;
955             int new_state = get_window_wm_state( event->display, data );
956             if (new_state != -1 && new_state != data->wm_state)
957             {
958                 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
959                        data->hwnd, data->whole_window, new_state, old_state );
960                 data->wm_state = new_state;
961                 /* ignore the initial state transition out of withdrawn state */
962                 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
963                 if (!old_state) return;
964             }
965         }
966         break;
967     }
968
969     if (!update_window || !data->managed || !data->mapped) return;
970
971     style = GetWindowLongW( data->hwnd, GWL_STYLE );
972
973     if (data->iconic && data->wm_state == NormalState)  /* restore window */
974     {
975         data->iconic = FALSE;
976         if (is_net_wm_state_maximized( event->display, data ))
977         {
978             if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
979             {
980                 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
981                 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
982             }
983             else TRACE( "not restoring to max win %p/%lx style %08x\n",
984                         data->hwnd, data->whole_window, style );
985         }
986         else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
987         {
988             TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
989             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
990         }
991         else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
992     }
993     else if (!data->iconic && data->wm_state == IconicState)
994     {
995         data->iconic = TRUE;
996         if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
997         {
998             TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
999             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1000         }
1001         else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1002     }
1003 }
1004
1005
1006 /***********************************************************************
1007  *           X11DRV_PropertyNotify
1008  */
1009 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1010 {
1011     XPropertyEvent *event = &xev->xproperty;
1012     struct x11drv_win_data *data;
1013
1014     if (!hwnd) return;
1015     if (!(data = X11DRV_get_win_data( hwnd ))) return;
1016
1017     if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1018 }
1019
1020
1021 /* event filter to wait for a WM_STATE change notification on a window */
1022 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1023 {
1024     if (event->xany.window != (Window)arg) return 0;
1025     return (event->type == DestroyNotify ||
1026             (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1027 }
1028
1029 /***********************************************************************
1030  *           wait_for_withdrawn_state
1031  */
1032 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1033 {
1034     DWORD end = GetTickCount() + 2000;
1035
1036     if (!data->managed) return;
1037
1038     TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1039            data->hwnd, data->whole_window, set ? "" : "not " );
1040
1041     while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1042     {
1043         XEvent event;
1044         int count = 0;
1045
1046         wine_tsx11_lock();
1047         while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1048         {
1049             count++;
1050             if (XFilterEvent( &event, None )) continue;  /* filtered, ignore it */
1051             if (event.type == DestroyNotify) call_event_handler( display, &event );
1052             else
1053             {
1054                 wine_tsx11_unlock();
1055                 handle_wm_state_notify( data, &event.xproperty, FALSE );
1056                 wine_tsx11_lock();
1057             }
1058         }
1059         wine_tsx11_unlock();
1060
1061         if (!count)
1062         {
1063             struct pollfd pfd;
1064             int timeout = end - GetTickCount();
1065
1066             pfd.fd = ConnectionNumber(display);
1067             pfd.events = POLLIN;
1068             if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1069             {
1070                 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1071                 break;
1072             }
1073         }
1074     }
1075     TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1076 }
1077
1078
1079 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1080 {
1081     RECT tempRect;
1082
1083     if (!IsWindowEnabled(hQueryWnd)) return 0;
1084     
1085     GetWindowRect(hQueryWnd, &tempRect);
1086
1087     if(!PtInRect(&tempRect, *lpPt)) return 0;
1088
1089     if (!IsIconic( hQueryWnd ))
1090     {
1091         POINT pt = *lpPt;
1092         ScreenToClient( hQueryWnd, &pt );
1093         GetClientRect( hQueryWnd, &tempRect );
1094
1095         if (PtInRect( &tempRect, pt))
1096         {
1097             HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1098             if (ret && ret != hQueryWnd)
1099             {
1100                 ret = find_drop_window( ret, lpPt );
1101                 if (ret) return ret;
1102             }
1103         }
1104     }
1105
1106     if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1107     
1108     ScreenToClient(hQueryWnd, lpPt);
1109
1110     return hQueryWnd;
1111 }
1112
1113 /**********************************************************************
1114  *           EVENT_DropFromOffix
1115  *
1116  * don't know if it still works (last Changelog is from 96/11/04)
1117  */
1118 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1119 {
1120     struct x11drv_win_data *data;
1121     unsigned long       data_length;
1122     unsigned long       aux_long;
1123     unsigned char*      p_data = NULL;
1124     Atom atom_aux;
1125     int                 x, y, dummy;
1126     BOOL                bAccept;
1127     Window              win, w_aux_root, w_aux_child;
1128
1129     win = X11DRV_get_whole_window(hWnd);
1130     wine_tsx11_lock();
1131     XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1132                    &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1133     x += virtual_screen_rect.left;
1134     y += virtual_screen_rect.top;
1135     wine_tsx11_unlock();
1136
1137     if (!(data = X11DRV_get_win_data( hWnd ))) return;
1138
1139     /* find out drop point and drop window */
1140     if( x < 0 || y < 0 ||
1141         x > (data->whole_rect.right - data->whole_rect.left) ||
1142         y > (data->whole_rect.bottom - data->whole_rect.top) )
1143     {   
1144         bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1145         x = 0;
1146         y = 0; 
1147     }
1148     else
1149     {
1150         POINT   pt = { x, y };
1151         HWND    hwndDrop = find_drop_window( hWnd, &pt );
1152         if (hwndDrop)
1153         {
1154             x = pt.x;
1155             y = pt.y;
1156             bAccept = TRUE;
1157         }
1158         else
1159         {
1160             bAccept = FALSE;
1161         }
1162     }
1163
1164     if (!bAccept) return;
1165
1166     wine_tsx11_lock();
1167     XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1168                         x11drv_atom(DndSelection), 0, 65535, FALSE,
1169                         AnyPropertyType, &atom_aux, &dummy,
1170                         &data_length, &aux_long, &p_data);
1171     wine_tsx11_unlock();
1172
1173     if( !aux_long && p_data)  /* don't bother if > 64K */
1174     {
1175         char *p = (char *)p_data;
1176         char *p_drop;
1177
1178         aux_long = 0;
1179         while( *p )  /* calculate buffer size */
1180         {
1181             INT len = GetShortPathNameA( p, NULL, 0 );
1182             if (len) aux_long += len + 1;
1183             p += strlen(p) + 1;
1184         }
1185         if( aux_long && aux_long < 65535 )
1186         {
1187             HDROP                 hDrop;
1188             DROPFILES *lpDrop;
1189
1190             aux_long += sizeof(DROPFILES) + 1;
1191             hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1192             lpDrop = GlobalLock( hDrop );
1193
1194             if( lpDrop )
1195             {
1196                 lpDrop->pFiles = sizeof(DROPFILES);
1197                 lpDrop->pt.x = x;
1198                 lpDrop->pt.y = y;
1199                 lpDrop->fNC = FALSE;
1200                 lpDrop->fWide = FALSE;
1201                 p_drop = (char *)(lpDrop + 1);
1202                 p = (char *)p_data;
1203                 while(*p)
1204                 {
1205                     if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1206                         p_drop += strlen( p_drop ) + 1;
1207                     p += strlen(p) + 1;
1208                 }
1209                 *p_drop = '\0';
1210                 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1211             }
1212         }
1213     }
1214     wine_tsx11_lock();
1215     if( p_data ) XFree(p_data);
1216     wine_tsx11_unlock();
1217 }
1218
1219 /**********************************************************************
1220  *           EVENT_DropURLs
1221  *
1222  * drop items are separated by \n
1223  * each item is prefixed by its mime type
1224  *
1225  * event->data.l[3], event->data.l[4] contains drop x,y position
1226  */
1227 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1228 {
1229   struct x11drv_win_data *win_data;
1230   unsigned long data_length;
1231   unsigned long aux_long, drop_len = 0;
1232   unsigned char *p_data = NULL; /* property data */
1233   char          *p_drop = NULL;
1234   char          *p, *next;
1235   int           x, y;
1236   DROPFILES *lpDrop;
1237   HDROP hDrop;
1238   union {
1239     Atom        atom_aux;
1240     int         i;
1241     Window      w_aux;
1242     unsigned int u;
1243   }             u; /* unused */
1244
1245   if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1246
1247   wine_tsx11_lock();
1248   XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1249                       x11drv_atom(DndSelection), 0, 65535, FALSE,
1250                       AnyPropertyType, &u.atom_aux, &u.i,
1251                       &data_length, &aux_long, &p_data);
1252   wine_tsx11_unlock();
1253   if (aux_long)
1254     WARN("property too large, truncated!\n");
1255   TRACE("urls=%s\n", p_data);
1256
1257   if( !aux_long && p_data) {    /* don't bother if > 64K */
1258     /* calculate length */
1259     p = (char*) p_data;
1260     next = strchr(p, '\n');
1261     while (p) {
1262       if (next) *next=0;
1263       if (strncmp(p,"file:",5) == 0 ) {
1264         INT len = GetShortPathNameA( p+5, NULL, 0 );
1265         if (len) drop_len += len + 1;
1266       }
1267       if (next) {
1268         *next = '\n';
1269         p = next + 1;
1270         next = strchr(p, '\n');
1271       } else {
1272         p = NULL;
1273       }
1274     }
1275
1276     if( drop_len && drop_len < 65535 ) {
1277       wine_tsx11_lock();
1278       XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1279                      &x, &y, &u.i, &u.i, &u.u);
1280       x += virtual_screen_rect.left;
1281       y += virtual_screen_rect.top;
1282       wine_tsx11_unlock();
1283
1284       drop_len += sizeof(DROPFILES) + 1;
1285       hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1286       lpDrop = GlobalLock( hDrop );
1287
1288       if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1289       {
1290           lpDrop->pFiles = sizeof(DROPFILES);
1291           lpDrop->pt.x = x;
1292           lpDrop->pt.y = y;
1293           lpDrop->fNC =
1294             ( x < (win_data->client_rect.left - win_data->whole_rect.left)  ||
1295               y < (win_data->client_rect.top - win_data->whole_rect.top)    ||
1296               x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1297               y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1298           lpDrop->fWide = FALSE;
1299           p_drop = (char*)(lpDrop + 1);
1300       }
1301
1302       /* create message content */
1303       if (p_drop) {
1304         p = (char*) p_data;
1305         next = strchr(p, '\n');
1306         while (p) {
1307           if (next) *next=0;
1308           if (strncmp(p,"file:",5) == 0 ) {
1309             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1310             if (len) {
1311               TRACE("drop file %s as %s\n", p+5, p_drop);
1312               p_drop += len+1;
1313             } else {
1314               WARN("can't convert file %s to dos name\n", p+5);
1315             }
1316           } else {
1317             WARN("unknown mime type %s\n", p);
1318           }
1319           if (next) {
1320             *next = '\n';
1321             p = next + 1;
1322             next = strchr(p, '\n');
1323           } else {
1324             p = NULL;
1325           }
1326           *p_drop = '\0';
1327         }
1328
1329         GlobalUnlock(hDrop);
1330         PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1331       }
1332     }
1333     wine_tsx11_lock();
1334     if( p_data ) XFree(p_data);
1335     wine_tsx11_unlock();
1336   }
1337 }
1338
1339 /**********************************************************************
1340  *              handle_dnd_protocol
1341  */
1342 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1343 {
1344     Window root, child;
1345     int root_x, root_y, child_x, child_y;
1346     unsigned int u;
1347
1348     /* query window (drag&drop event contains only drag window) */
1349     wine_tsx11_lock();
1350     XQueryPointer( event->display, root_window, &root, &child,
1351                    &root_x, &root_y, &child_x, &child_y, &u);
1352     if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1353     wine_tsx11_unlock();
1354     if (!hwnd) return;
1355     if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1356         EVENT_DropFromOffiX(hwnd, event);
1357     else if (event->data.l[0] == DndURL)
1358         EVENT_DropURLs(hwnd, event);
1359 }
1360
1361
1362 struct client_message_handler
1363 {
1364     int    atom;                                  /* protocol atom */
1365     void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1366 };
1367
1368 static const struct client_message_handler client_messages[] =
1369 {
1370     { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1371     { XATOM_DndProtocol,  handle_dnd_protocol },
1372     { XATOM_XdndEnter,    X11DRV_XDND_EnterEvent },
1373     { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1374     { XATOM_XdndDrop,     X11DRV_XDND_DropEvent },
1375     { XATOM_XdndLeave,    X11DRV_XDND_LeaveEvent }
1376 };
1377
1378
1379 /**********************************************************************
1380  *           X11DRV_ClientMessage
1381  */
1382 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1383 {
1384     XClientMessageEvent *event = &xev->xclient;
1385     unsigned int i;
1386
1387     if (!hwnd) return;
1388
1389     if (event->format != 32)
1390     {
1391         WARN( "Don't know how to handle format %d\n", event->format );
1392         return;
1393     }
1394
1395     for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1396     {
1397         if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1398         {
1399             client_messages[i].handler( hwnd, event );
1400             return;
1401         }
1402     }
1403     TRACE( "no handler found for %ld\n", event->message_type );
1404 }
1405
1406
1407 /***********************************************************************
1408  *              X11DRV_SendInput  (X11DRV.@)
1409  */
1410 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1411 {
1412     UINT i;
1413
1414     for (i = 0; i < count; i++, inputs++)
1415     {
1416         switch(inputs->type)
1417         {
1418         case INPUT_MOUSE:
1419             X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1420                                      inputs->u.mi.mouseData, inputs->u.mi.time,
1421                                      inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1422             break;
1423         case INPUT_KEYBOARD:
1424             X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1425                                         inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1426             break;
1427         case INPUT_HARDWARE:
1428             FIXME( "INPUT_HARDWARE not supported\n" );
1429             break;
1430         }
1431     }
1432     return count;
1433 }