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