kernel32: Add a structure to store all the information about an executable.
[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( "%s for hwnd/window %p/%lx\n",
312            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     XFlush( gdi_display );
383     if (prev_event.type) call_event_handler( display, &prev_event );
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
480     TRACE( "setting foreground window to %p\n", hwnd );
481     SetForegroundWindow( hwnd );
482
483     focus = GetFocus();
484     if (focus) focus = GetAncestor( focus, GA_ROOT );
485     win = X11DRV_get_whole_window(focus);
486
487     if (win)
488     {
489         TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
490         wine_tsx11_lock();
491         XSetInputFocus( display, win, RevertToParent, time );
492         wine_tsx11_unlock();
493     }
494 }
495
496
497 /**********************************************************************
498  *              handle_wm_protocols
499  */
500 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
501 {
502     Atom protocol = (Atom)event->data.l[0];
503
504     if (!protocol) return;
505
506     if (protocol == x11drv_atom(WM_DELETE_WINDOW))
507     {
508         /* Ignore the delete window request if the window has been disabled
509          * and we are in managed mode. This is to disallow applications from
510          * being closed by the window manager while in a modal state.
511          */
512         if (IsWindowEnabled(hwnd))
513         {
514             HMENU hSysMenu;
515             POINT pt;
516
517             if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
518             hSysMenu = GetSystemMenu(hwnd, FALSE);
519             if (hSysMenu)
520             {
521                 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
522                 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
523                     return;
524             }
525             if (GetActiveWindow() != hwnd)
526             {
527                 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
528                                            (WPARAM)GetAncestor( hwnd, GA_ROOT ),
529                                            MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
530                 switch(ma)
531                 {
532                     case MA_NOACTIVATEANDEAT:
533                     case MA_ACTIVATEANDEAT:
534                         return;
535                     case MA_NOACTIVATE:
536                         break;
537                     case MA_ACTIVATE:
538                     case 0:
539                         SetActiveWindow(hwnd);
540                         break;
541                     default:
542                         WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
543                         break;
544                 }
545             }
546             /* Simulate clicking the caption Close button */
547             GetCursorPos( &pt );
548             PostMessageW( hwnd, WM_NCLBUTTONDOWN, HTCLOSE, MAKELPARAM( pt.x, pt.y ) );
549             PostMessageW( hwnd, WM_LBUTTONUP, HTCLOSE, MAKELPARAM( pt.x, pt.y ) );
550         }
551     }
552     else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
553     {
554         Time event_time = (Time)event->data.l[1];
555         HWND last_focus = x11drv_thread_data()->last_focus;
556
557         TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
558                hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
559                GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
560
561         if (can_activate_window(hwnd))
562         {
563             /* simulate a mouse click on the caption to find out
564              * whether the window wants to be activated */
565             LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
566                                        (WPARAM)GetAncestor( hwnd, GA_ROOT ),
567                                        MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
568             if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
569             {
570                 set_focus( event->display, hwnd, event_time );
571                 return;
572             }
573         }
574         else if (hwnd == GetDesktopWindow())
575         {
576             hwnd = GetForegroundWindow();
577             if (!hwnd) hwnd = last_focus;
578             if (!hwnd) hwnd = GetDesktopWindow();
579             set_focus( event->display, hwnd, event_time );
580             return;
581         }
582         /* try to find some other window to give the focus to */
583         hwnd = GetFocus();
584         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
585         if (!hwnd) hwnd = GetActiveWindow();
586         if (!hwnd) hwnd = last_focus;
587         if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
588     }
589     else if (protocol == x11drv_atom(_NET_WM_PING))
590     {
591       XClientMessageEvent xev;
592       xev = *event;
593       
594       TRACE("NET_WM Ping\n");
595       wine_tsx11_lock();
596       xev.window = DefaultRootWindow(xev.display);
597       XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
598       wine_tsx11_unlock();
599       /* this line is semi-stolen from gtk2 */
600       TRACE("NET_WM Pong\n");
601     }
602 }
603
604
605 static const char * const focus_details[] =
606 {
607     "NotifyAncestor",
608     "NotifyVirtual",
609     "NotifyInferior",
610     "NotifyNonlinear",
611     "NotifyNonlinearVirtual",
612     "NotifyPointer",
613     "NotifyPointerRoot",
614     "NotifyDetailNone"
615 };
616
617 /**********************************************************************
618  *              X11DRV_FocusIn
619  */
620 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
621 {
622     XFocusChangeEvent *event = &xev->xfocus;
623     XIC xic;
624
625     if (!hwnd) return;
626
627     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
628
629     if (event->detail == NotifyPointer) return;
630
631     if ((xic = X11DRV_get_ic( hwnd )))
632     {
633         wine_tsx11_lock();
634         XSetICFocus( xic );
635         wine_tsx11_unlock();
636     }
637     if (use_take_focus) return;  /* ignore FocusIn if we are using take focus */
638
639     if (!can_activate_window(hwnd))
640     {
641         HWND hwnd = GetFocus();
642         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
643         if (!hwnd) hwnd = GetActiveWindow();
644         if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
645         if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
646     }
647     else SetForegroundWindow( hwnd );
648 }
649
650
651 /**********************************************************************
652  *              X11DRV_FocusOut
653  *
654  * Note: only top-level windows get FocusOut events.
655  */
656 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
657 {
658     XFocusChangeEvent *event = &xev->xfocus;
659     HWND hwnd_tmp;
660     Window focus_win;
661     int revert;
662     XIC xic;
663
664     if (!hwnd) return;
665
666     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
667
668     if (event->detail == NotifyPointer) return;
669     if (ximInComposeMode) return;
670
671     x11drv_thread_data()->last_focus = hwnd;
672     if ((xic = X11DRV_get_ic( hwnd )))
673     {
674         wine_tsx11_lock();
675         XUnsetICFocus( xic );
676         wine_tsx11_unlock();
677     }
678     if (hwnd != GetForegroundWindow()) return;
679     SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
680
681     /* don't reset the foreground window, if the window which is
682        getting the focus is a Wine window */
683
684     wine_tsx11_lock();
685     XGetInputFocus( event->display, &focus_win, &revert );
686     if (focus_win)
687     {
688         if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
689             focus_win = 0;
690     }
691     wine_tsx11_unlock();
692
693     if (!focus_win)
694     {
695         /* Abey : 6-Oct-99. Check again if the focus out window is the
696            Foreground window, because in most cases the messages sent
697            above must have already changed the foreground window, in which
698            case we don't have to change the foreground window to 0 */
699         if (hwnd == GetForegroundWindow())
700         {
701             TRACE( "lost focus, setting fg to desktop\n" );
702             SetForegroundWindow( GetDesktopWindow() );
703         }
704     }
705 }
706
707
708 /***********************************************************************
709  *           X11DRV_Expose
710  */
711 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
712 {
713     XExposeEvent *event = &xev->xexpose;
714     RECT rect;
715     struct x11drv_win_data *data;
716     int flags = RDW_INVALIDATE | RDW_ERASE;
717
718     TRACE( "win %p (%lx) %d,%d %dx%d\n",
719            hwnd, event->window, event->x, event->y, event->width, event->height );
720
721     if (!(data = X11DRV_get_win_data( hwnd ))) return;
722
723     if (event->window == data->whole_window)
724     {
725         rect.left = data->whole_rect.left + event->x;
726         rect.top  = data->whole_rect.top + event->y;
727         flags |= RDW_FRAME;
728     }
729     else
730     {
731         rect.left = data->client_rect.left + event->x;
732         rect.top  = data->client_rect.top + event->y;
733     }
734     rect.right  = rect.left + event->width;
735     rect.bottom = rect.top + event->height;
736
737     if (event->window != root_window)
738     {
739         SERVER_START_REQ( update_window_zorder )
740         {
741             req->window      = wine_server_user_handle( hwnd );
742             req->rect.left   = rect.left;
743             req->rect.top    = rect.top;
744             req->rect.right  = rect.right;
745             req->rect.bottom = rect.bottom;
746             wine_server_call( req );
747         }
748         SERVER_END_REQ;
749
750         /* make position relative to client area instead of parent */
751         OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
752         flags |= RDW_ALLCHILDREN;
753     }
754
755     RedrawWindow( hwnd, &rect, 0, flags );
756 }
757
758
759 /**********************************************************************
760  *              X11DRV_MapNotify
761  */
762 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
763 {
764     struct x11drv_win_data *data;
765
766     if (!(data = X11DRV_get_win_data( hwnd ))) return;
767     if (!data->mapped) return;
768
769     if (!data->managed)
770     {
771         HWND hwndFocus = GetFocus();
772         if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus);  /* FIXME */
773     }
774 }
775
776
777 /***********************************************************************
778  *     is_net_wm_state_maximized
779  */
780 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
781 {
782     Atom type, *state;
783     int format, ret = 0;
784     unsigned long i, count, remaining;
785
786     wine_tsx11_lock();
787     if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
788                              65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
789                              &remaining, (unsigned char **)&state ))
790     {
791         if (type == XA_ATOM && format == 32)
792         {
793             for (i = 0; i < count; i++)
794             {
795                 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
796                     state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
797                     ret++;
798             }
799         }
800         XFree( state );
801     }
802     wine_tsx11_unlock();
803     return (ret == 2);
804 }
805
806
807 /***********************************************************************
808  *              X11DRV_ConfigureNotify
809  */
810 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
811 {
812     XConfigureEvent *event = &xev->xconfigure;
813     struct x11drv_win_data *data;
814     RECT rect;
815     UINT flags;
816     int cx, cy, x = event->x, y = event->y;
817
818     if (!hwnd) return;
819     if (!(data = X11DRV_get_win_data( hwnd ))) return;
820     if (!data->mapped || data->iconic || !data->managed) return;
821
822     /* Get geometry */
823
824     if (!event->send_event)  /* normal event, need to map coordinates to the root */
825     {
826         Window child;
827         wine_tsx11_lock();
828         XTranslateCoordinates( event->display, data->whole_window, root_window,
829                                0, 0, &x, &y, &child );
830         wine_tsx11_unlock();
831     }
832     rect.left   = x;
833     rect.top    = y;
834     rect.right  = x + event->width;
835     rect.bottom = y + event->height;
836     OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
837     TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
838            hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
839            event->x, event->y, event->width, event->height );
840     X11DRV_X_to_window_rect( data, &rect );
841
842     if (is_net_wm_state_maximized( event->display, data ))
843     {
844         if (!IsZoomed( data->hwnd ))
845         {
846             TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
847             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
848             return;
849         }
850     }
851     else
852     {
853         if (IsZoomed( data->hwnd ))
854         {
855             TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
856             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
857             return;
858         }
859     }
860
861     /* Compare what has changed */
862
863     x     = rect.left;
864     y     = rect.top;
865     cx    = rect.right - rect.left;
866     cy    = rect.bottom - rect.top;
867     flags = SWP_NOACTIVATE | SWP_NOZORDER;
868
869     if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
870     else
871         TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
872                hwnd, data->window_rect.left, data->window_rect.top, x, y );
873
874     if ((data->window_rect.right - data->window_rect.left == cx &&
875          data->window_rect.bottom - data->window_rect.top == cy) ||
876         (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
877     {
878         if (flags & SWP_NOMOVE) return;  /* if nothing changed, don't do anything */
879         flags |= SWP_NOSIZE;
880     }
881     else
882         TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
883                hwnd, data->window_rect.right - data->window_rect.left,
884                data->window_rect.bottom - data->window_rect.top, cx, cy );
885
886     SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
887 }
888
889
890 /***********************************************************************
891  *           get_window_wm_state
892  */
893 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
894 {
895     struct
896     {
897         CARD32 state;
898         XID     icon;
899     } *state;
900     Atom type;
901     int format, ret = -1;
902     unsigned long count, remaining;
903
904     wine_tsx11_lock();
905     if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
906                              sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
907                              &type, &format, &count, &remaining, (unsigned char **)&state ))
908     {
909         if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
910             ret = state->state;
911         XFree( state );
912     }
913     wine_tsx11_unlock();
914     return ret;
915 }
916
917
918 /***********************************************************************
919  *           handle_wm_state_notify
920  *
921  * Handle a PropertyNotify for WM_STATE.
922  */
923 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
924                                     BOOL update_window )
925 {
926     switch(event->state)
927     {
928     case PropertyDelete:
929         TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
930         data->wm_state = WithdrawnState;
931         break;
932     case PropertyNewValue:
933         {
934             int old_state = data->wm_state;
935             int new_state = get_window_wm_state( event->display, data );
936             if (new_state != -1 && new_state != data->wm_state)
937             {
938                 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
939                        data->hwnd, data->whole_window, new_state, old_state );
940                 data->wm_state = new_state;
941                 /* ignore the initial state transition out of withdrawn state */
942                 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
943                 if (!old_state) return;
944             }
945         }
946         break;
947     }
948
949     if (!update_window || !data->managed || !data->mapped) return;
950
951     if (data->iconic && data->wm_state == NormalState)  /* restore window */
952     {
953         data->iconic = FALSE;
954         if (is_net_wm_state_maximized( event->display, data ))
955         {
956             TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
957             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
958         }
959         else
960         {
961             TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
962             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
963         }
964     }
965     else if (!data->iconic && data->wm_state == IconicState)
966     {
967         TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
968         data->iconic = TRUE;
969         SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
970     }
971 }
972
973
974 /***********************************************************************
975  *           X11DRV_PropertyNotify
976  */
977 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
978 {
979     XPropertyEvent *event = &xev->xproperty;
980     struct x11drv_win_data *data;
981
982     if (!hwnd) return;
983     if (!(data = X11DRV_get_win_data( hwnd ))) return;
984
985     if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
986 }
987
988
989 /* event filter to wait for a WM_STATE change notification on a window */
990 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
991 {
992     if (event->xany.window != (Window)arg) return 0;
993     return (event->type == DestroyNotify ||
994             (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
995 }
996
997 /***********************************************************************
998  *           wait_for_withdrawn_state
999  */
1000 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1001 {
1002     DWORD end = GetTickCount() + 2000;
1003
1004     if (!data->managed) return;
1005
1006     TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1007            data->hwnd, data->whole_window, set ? "" : "not " );
1008
1009     while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1010     {
1011         XEvent event;
1012         int count = 0;
1013
1014         wine_tsx11_lock();
1015         while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1016         {
1017             count++;
1018             if (XFilterEvent( &event, None )) continue;  /* filtered, ignore it */
1019             if (event.type == DestroyNotify) call_event_handler( display, &event );
1020             else
1021             {
1022                 wine_tsx11_unlock();
1023                 handle_wm_state_notify( data, &event.xproperty, FALSE );
1024                 wine_tsx11_lock();
1025             }
1026         }
1027         wine_tsx11_unlock();
1028
1029         if (!count)
1030         {
1031             struct pollfd pfd;
1032             int timeout = end - GetTickCount();
1033
1034             pfd.fd = ConnectionNumber(display);
1035             pfd.events = POLLIN;
1036             if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1037             {
1038                 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1039                 break;
1040             }
1041         }
1042     }
1043     TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1044 }
1045
1046
1047 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1048 {
1049     RECT tempRect;
1050
1051     if (!IsWindowEnabled(hQueryWnd)) return 0;
1052     
1053     GetWindowRect(hQueryWnd, &tempRect);
1054
1055     if(!PtInRect(&tempRect, *lpPt)) return 0;
1056
1057     if (!IsIconic( hQueryWnd ))
1058     {
1059         POINT pt = *lpPt;
1060         ScreenToClient( hQueryWnd, &pt );
1061         GetClientRect( hQueryWnd, &tempRect );
1062
1063         if (PtInRect( &tempRect, pt))
1064         {
1065             HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1066             if (ret && ret != hQueryWnd)
1067             {
1068                 ret = find_drop_window( ret, lpPt );
1069                 if (ret) return ret;
1070             }
1071         }
1072     }
1073
1074     if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1075     
1076     ScreenToClient(hQueryWnd, lpPt);
1077
1078     return hQueryWnd;
1079 }
1080
1081 /**********************************************************************
1082  *           EVENT_DropFromOffix
1083  *
1084  * don't know if it still works (last Changelog is from 96/11/04)
1085  */
1086 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1087 {
1088     struct x11drv_win_data *data;
1089     unsigned long       data_length;
1090     unsigned long       aux_long;
1091     unsigned char*      p_data = NULL;
1092     Atom atom_aux;
1093     int                 x, y, dummy;
1094     BOOL                bAccept;
1095     Window              win, w_aux_root, w_aux_child;
1096
1097     win = X11DRV_get_whole_window(hWnd);
1098     wine_tsx11_lock();
1099     XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1100                    &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1101     x += virtual_screen_rect.left;
1102     y += virtual_screen_rect.top;
1103     wine_tsx11_unlock();
1104
1105     if (!(data = X11DRV_get_win_data( hWnd ))) return;
1106
1107     /* find out drop point and drop window */
1108     if( x < 0 || y < 0 ||
1109         x > (data->whole_rect.right - data->whole_rect.left) ||
1110         y > (data->whole_rect.bottom - data->whole_rect.top) )
1111     {   
1112         bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1113         x = 0;
1114         y = 0; 
1115     }
1116     else
1117     {
1118         POINT   pt = { x, y };
1119         HWND    hwndDrop = find_drop_window( hWnd, &pt );
1120         if (hwndDrop)
1121         {
1122             x = pt.x;
1123             y = pt.y;
1124             bAccept = TRUE;
1125         }
1126         else
1127         {
1128             bAccept = FALSE;
1129         }
1130     }
1131
1132     if (!bAccept) return;
1133
1134     wine_tsx11_lock();
1135     XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1136                         x11drv_atom(DndSelection), 0, 65535, FALSE,
1137                         AnyPropertyType, &atom_aux, &dummy,
1138                         &data_length, &aux_long, &p_data);
1139     wine_tsx11_unlock();
1140
1141     if( !aux_long && p_data)  /* don't bother if > 64K */
1142     {
1143         char *p = (char *)p_data;
1144         char *p_drop;
1145
1146         aux_long = 0;
1147         while( *p )  /* calculate buffer size */
1148         {
1149             INT len = GetShortPathNameA( p, NULL, 0 );
1150             if (len) aux_long += len + 1;
1151             p += strlen(p) + 1;
1152         }
1153         if( aux_long && aux_long < 65535 )
1154         {
1155             HDROP                 hDrop;
1156             DROPFILES *lpDrop;
1157
1158             aux_long += sizeof(DROPFILES) + 1;
1159             hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1160             lpDrop = GlobalLock( hDrop );
1161
1162             if( lpDrop )
1163             {
1164                 lpDrop->pFiles = sizeof(DROPFILES);
1165                 lpDrop->pt.x = x;
1166                 lpDrop->pt.y = y;
1167                 lpDrop->fNC = FALSE;
1168                 lpDrop->fWide = FALSE;
1169                 p_drop = (char *)(lpDrop + 1);
1170                 p = (char *)p_data;
1171                 while(*p)
1172                 {
1173                     if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1174                         p_drop += strlen( p_drop ) + 1;
1175                     p += strlen(p) + 1;
1176                 }
1177                 *p_drop = '\0';
1178                 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1179             }
1180         }
1181     }
1182     wine_tsx11_lock();
1183     if( p_data ) XFree(p_data);
1184     wine_tsx11_unlock();
1185 }
1186
1187 /**********************************************************************
1188  *           EVENT_DropURLs
1189  *
1190  * drop items are separated by \n
1191  * each item is prefixed by its mime type
1192  *
1193  * event->data.l[3], event->data.l[4] contains drop x,y position
1194  */
1195 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1196 {
1197   struct x11drv_win_data *win_data;
1198   unsigned long data_length;
1199   unsigned long aux_long, drop_len = 0;
1200   unsigned char *p_data = NULL; /* property data */
1201   char          *p_drop = NULL;
1202   char          *p, *next;
1203   int           x, y;
1204   DROPFILES *lpDrop;
1205   HDROP hDrop;
1206   union {
1207     Atom        atom_aux;
1208     int         i;
1209     Window      w_aux;
1210     unsigned int u;
1211   }             u; /* unused */
1212
1213   if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1214
1215   wine_tsx11_lock();
1216   XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1217                       x11drv_atom(DndSelection), 0, 65535, FALSE,
1218                       AnyPropertyType, &u.atom_aux, &u.i,
1219                       &data_length, &aux_long, &p_data);
1220   wine_tsx11_unlock();
1221   if (aux_long)
1222     WARN("property too large, truncated!\n");
1223   TRACE("urls=%s\n", p_data);
1224
1225   if( !aux_long && p_data) {    /* don't bother if > 64K */
1226     /* calculate length */
1227     p = (char*) p_data;
1228     next = strchr(p, '\n');
1229     while (p) {
1230       if (next) *next=0;
1231       if (strncmp(p,"file:",5) == 0 ) {
1232         INT len = GetShortPathNameA( p+5, NULL, 0 );
1233         if (len) drop_len += len + 1;
1234       }
1235       if (next) {
1236         *next = '\n';
1237         p = next + 1;
1238         next = strchr(p, '\n');
1239       } else {
1240         p = NULL;
1241       }
1242     }
1243
1244     if( drop_len && drop_len < 65535 ) {
1245       wine_tsx11_lock();
1246       XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1247                      &x, &y, &u.i, &u.i, &u.u);
1248       x += virtual_screen_rect.left;
1249       y += virtual_screen_rect.top;
1250       wine_tsx11_unlock();
1251
1252       drop_len += sizeof(DROPFILES) + 1;
1253       hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1254       lpDrop = GlobalLock( hDrop );
1255
1256       if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1257       {
1258           lpDrop->pFiles = sizeof(DROPFILES);
1259           lpDrop->pt.x = x;
1260           lpDrop->pt.y = y;
1261           lpDrop->fNC =
1262             ( x < (win_data->client_rect.left - win_data->whole_rect.left)  ||
1263               y < (win_data->client_rect.top - win_data->whole_rect.top)    ||
1264               x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1265               y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1266           lpDrop->fWide = FALSE;
1267           p_drop = (char*)(lpDrop + 1);
1268       }
1269
1270       /* create message content */
1271       if (p_drop) {
1272         p = (char*) p_data;
1273         next = strchr(p, '\n');
1274         while (p) {
1275           if (next) *next=0;
1276           if (strncmp(p,"file:",5) == 0 ) {
1277             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1278             if (len) {
1279               TRACE("drop file %s as %s\n", p+5, p_drop);
1280               p_drop += len+1;
1281             } else {
1282               WARN("can't convert file %s to dos name\n", p+5);
1283             }
1284           } else {
1285             WARN("unknown mime type %s\n", p);
1286           }
1287           if (next) {
1288             *next = '\n';
1289             p = next + 1;
1290             next = strchr(p, '\n');
1291           } else {
1292             p = NULL;
1293           }
1294           *p_drop = '\0';
1295         }
1296
1297         GlobalUnlock(hDrop);
1298         PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1299       }
1300     }
1301     wine_tsx11_lock();
1302     if( p_data ) XFree(p_data);
1303     wine_tsx11_unlock();
1304   }
1305 }
1306
1307 /**********************************************************************
1308  *              handle_dnd_protocol
1309  */
1310 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1311 {
1312     Window root, child;
1313     int root_x, root_y, child_x, child_y;
1314     unsigned int u;
1315
1316     /* query window (drag&drop event contains only drag window) */
1317     wine_tsx11_lock();
1318     XQueryPointer( event->display, root_window, &root, &child,
1319                    &root_x, &root_y, &child_x, &child_y, &u);
1320     if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1321     wine_tsx11_unlock();
1322     if (!hwnd) return;
1323     if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1324         EVENT_DropFromOffiX(hwnd, event);
1325     else if (event->data.l[0] == DndURL)
1326         EVENT_DropURLs(hwnd, event);
1327 }
1328
1329
1330 struct client_message_handler
1331 {
1332     int    atom;                                  /* protocol atom */
1333     void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1334 };
1335
1336 static const struct client_message_handler client_messages[] =
1337 {
1338     { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1339     { XATOM_DndProtocol,  handle_dnd_protocol },
1340     { XATOM_XdndEnter,    X11DRV_XDND_EnterEvent },
1341     { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1342     { XATOM_XdndDrop,     X11DRV_XDND_DropEvent },
1343     { XATOM_XdndLeave,    X11DRV_XDND_LeaveEvent }
1344 };
1345
1346
1347 /**********************************************************************
1348  *           X11DRV_ClientMessage
1349  */
1350 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1351 {
1352     XClientMessageEvent *event = &xev->xclient;
1353     unsigned int i;
1354
1355     if (!hwnd) return;
1356
1357     if (event->format != 32)
1358     {
1359         WARN( "Don't know how to handle format %d\n", event->format );
1360         return;
1361     }
1362
1363     for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1364     {
1365         if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1366         {
1367             client_messages[i].handler( hwnd, event );
1368             return;
1369         }
1370     }
1371     TRACE( "no handler found for %ld\n", event->message_type );
1372 }
1373
1374
1375 /***********************************************************************
1376  *              X11DRV_SendInput  (X11DRV.@)
1377  */
1378 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1379 {
1380     UINT i;
1381
1382     for (i = 0; i < count; i++, inputs++)
1383     {
1384         switch(inputs->type)
1385         {
1386         case INPUT_MOUSE:
1387             X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1388                                      inputs->u.mi.mouseData, inputs->u.mi.time,
1389                                      inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1390             break;
1391         case INPUT_KEYBOARD:
1392             X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1393                                         inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1394             break;
1395         case INPUT_HARDWARE:
1396             FIXME( "INPUT_HARDWARE not supported\n" );
1397             break;
1398         }
1399     }
1400     return count;
1401 }