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