user32/tests: Show that vertical and horizontal window scroll bar info is not created...
[wine] / dlls / winex11.drv / event.c
1 /*
2  * X11 event driver
3  *
4  * Copyright 1993 Alexandre Julliard
5  *           1999 Noel Borthwick
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23
24 #ifdef HAVE_POLL_H
25 #include <poll.h>
26 #endif
27 #ifdef HAVE_SYS_POLL_H
28 #include <sys/poll.h>
29 #endif
30 #include <X11/Xatom.h>
31 #include <X11/keysym.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xresource.h>
34 #include <X11/Xutil.h>
35
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <string.h>
39
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h"
45 #include "wingdi.h"
46
47 #include "x11drv.h"
48
49 /* avoid conflict with field names in included win32 headers */
50 #undef Status
51 #include "shlobj.h"  /* DROPFILES */
52 #include "shellapi.h"
53
54 #include "wine/server.h"
55 #include "wine/debug.h"
56
57 WINE_DEFAULT_DEBUG_CHANNEL(event);
58
59 extern BOOL ximInComposeMode;
60
61 #define DndNotDnd       -1    /* OffiX drag&drop */
62 #define DndUnknown      0
63 #define DndRawData      1
64 #define DndFile         2
65 #define DndFiles        3
66 #define DndText         4
67 #define DndDir          5
68 #define DndLink         6
69 #define DndExe          7
70
71 #define DndEND          8
72
73 #define DndURL          128   /* KDE drag&drop */
74
75   /* Event handlers */
76 static void X11DRV_FocusIn( HWND hwnd, XEvent *event );
77 static void X11DRV_FocusOut( HWND hwnd, XEvent *event );
78 static void X11DRV_Expose( HWND hwnd, XEvent *event );
79 static void X11DRV_MapNotify( HWND hwnd, XEvent *event );
80 static void X11DRV_ConfigureNotify( HWND hwnd, XEvent *event );
81 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
82 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
83
84 struct event_handler
85 {
86     int                  type;    /* event type */
87     x11drv_event_handler handler; /* corresponding handler function */
88 };
89
90 #define MAX_EVENT_HANDLERS 64
91
92 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
93 {
94     /* list must be sorted by event type */
95     { KeyPress,         X11DRV_KeyEvent },
96     { KeyRelease,       X11DRV_KeyEvent },
97     { ButtonPress,      X11DRV_ButtonPress },
98     { ButtonRelease,    X11DRV_ButtonRelease },
99     { MotionNotify,     X11DRV_MotionNotify },
100     { EnterNotify,      X11DRV_EnterNotify },
101     /* LeaveNotify */
102     { FocusIn,          X11DRV_FocusIn },
103     { FocusOut,         X11DRV_FocusOut },
104     { KeymapNotify,     X11DRV_KeymapNotify },
105     { Expose,           X11DRV_Expose },
106     /* GraphicsExpose */
107     /* NoExpose */
108     /* VisibilityNotify */
109     /* CreateNotify */
110     { DestroyNotify,    X11DRV_DestroyNotify },
111     /* UnmapNotify */
112     { MapNotify,        X11DRV_MapNotify },
113     /* MapRequest */
114     /* ReparentNotify */
115     { ConfigureNotify,  X11DRV_ConfigureNotify },
116     /* ConfigureRequest */
117     /* GravityNotify */
118     /* ResizeRequest */
119     /* CirculateNotify */
120     /* CirculateRequest */
121     { PropertyNotify,   X11DRV_PropertyNotify },
122     { SelectionClear,   X11DRV_SelectionClear },
123     { SelectionRequest, X11DRV_SelectionRequest },
124     /* SelectionNotify */
125     /* ColormapNotify */
126     { ClientMessage,    X11DRV_ClientMessage },
127     { MappingNotify,    X11DRV_MappingNotify },
128 };
129
130 static int nb_event_handlers = 18;  /* change this if you add handlers above */
131
132
133 /* return the name of an X event */
134 static const char *dbgstr_event( int type )
135 {
136     static const char * const event_names[] =
137     {
138         "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
139         "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
140         "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
141         "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
142         "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
143         "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
144         "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
145         "ClientMessage", "MappingNotify"
146     };
147
148     if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
149     return wine_dbg_sprintf( "Extension event %d", type );
150 }
151
152
153 /***********************************************************************
154  *           find_handler
155  *
156  * Find the handler for a given event type. Caller must hold the x11 lock.
157  */
158 static inline x11drv_event_handler find_handler( int type )
159 {
160     int min = 0, max = nb_event_handlers - 1;
161
162     while (min <= max)
163     {
164         int pos = (min + max) / 2;
165         if (handlers[pos].type == type) return handlers[pos].handler;
166         if (handlers[pos].type > type) max = pos - 1;
167         else min = pos + 1;
168     }
169     return NULL;
170 }
171
172
173 /***********************************************************************
174  *           X11DRV_register_event_handler
175  *
176  * Register a handler for a given event type.
177  * If already registered, overwrite the previous handler.
178  */
179 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
180 {
181     int min, max;
182
183     wine_tsx11_lock();
184     min = 0;
185     max = nb_event_handlers - 1;
186     while (min <= max)
187     {
188         int pos = (min + max) / 2;
189         if (handlers[pos].type == type)
190         {
191             handlers[pos].handler = handler;
192             goto done;
193         }
194         if (handlers[pos].type > type) max = pos - 1;
195         else min = pos + 1;
196     }
197     /* insert it between max and min */
198     memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
199     handlers[min].type = type;
200     handlers[min].handler = handler;
201     nb_event_handlers++;
202     assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
203 done:
204     wine_tsx11_unlock();
205     TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
206 }
207
208
209 /***********************************************************************
210  *           filter_event
211  */
212 static Bool filter_event( Display *display, XEvent *event, char *arg )
213 {
214     ULONG_PTR mask = (ULONG_PTR)arg;
215
216     if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
217
218     switch(event->type)
219     {
220     case KeyPress:
221     case KeyRelease:
222     case KeymapNotify:
223     case MappingNotify:
224         return (mask & QS_KEY) != 0;
225     case ButtonPress:
226     case ButtonRelease:
227         return (mask & QS_MOUSEBUTTON) != 0;
228     case MotionNotify:
229     case EnterNotify:
230     case LeaveNotify:
231         return (mask & QS_MOUSEMOVE) != 0;
232     case Expose:
233         return (mask & QS_PAINT) != 0;
234     case FocusIn:
235     case FocusOut:
236     case MapNotify:
237     case UnmapNotify:
238     case ConfigureNotify:
239     case PropertyNotify:
240     case ClientMessage:
241         return (mask & QS_POSTMESSAGE) != 0;
242     default:
243         return (mask & QS_SENDMESSAGE) != 0;
244     }
245 }
246
247
248 enum event_merge_action
249 {
250     MERGE_DISCARD,  /* discard the old event */
251     MERGE_HANDLE,   /* handle the old event */
252     MERGE_KEEP      /* keep the old event for future merging */
253 };
254
255 /***********************************************************************
256  *           merge_events
257  *
258  * Try to merge 2 consecutive events.
259  */
260 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
261 {
262     switch (prev->type)
263     {
264     case ConfigureNotify:
265         switch (next->type)
266         {
267         case ConfigureNotify:
268             if (prev->xany.window == next->xany.window)
269             {
270                 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
271                 return MERGE_DISCARD;
272             }
273             break;
274         case Expose:
275         case PropertyNotify:
276             return MERGE_KEEP;
277         }
278         break;
279     case MotionNotify:
280         if (prev->xany.window == next->xany.window && next->type == MotionNotify)
281         {
282             TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
283             return MERGE_DISCARD;
284         }
285         break;
286     }
287     return MERGE_HANDLE;
288 }
289
290
291 /***********************************************************************
292  *           call_event_handler
293  */
294 static inline void call_event_handler( Display *display, XEvent *event )
295 {
296     HWND hwnd;
297     x11drv_event_handler handler;
298     XEvent *prev;
299     struct x11drv_thread_data *thread_data;
300
301     if (!(handler = find_handler( event->type )))
302     {
303         TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
304         return;  /* no handler, ignore it */
305     }
306
307     if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
308         hwnd = 0;  /* not for a registered window */
309     if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
310
311     TRACE( "%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 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
515             if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
516             hSysMenu = GetSystemMenu(hwnd, FALSE);
517             if (hSysMenu)
518             {
519                 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
520                 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
521                     return;
522             }
523             if (GetActiveWindow() != hwnd)
524             {
525                 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
526                                            (WPARAM)GetAncestor( hwnd, GA_ROOT ),
527                                            MAKELONG(HTCLOSE,WM_LBUTTONDOWN) );
528                 switch(ma)
529                 {
530                     case MA_NOACTIVATEANDEAT:
531                     case MA_ACTIVATEANDEAT:
532                         return;
533                     case MA_NOACTIVATE:
534                         break;
535                     case MA_ACTIVATE:
536                     case 0:
537                         SetActiveWindow(hwnd);
538                         break;
539                     default:
540                         WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
541                         break;
542                 }
543             }
544             PostMessageW( hwnd, WM_X11DRV_DELETE_WINDOW, 0, 0 );
545         }
546     }
547     else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
548     {
549         Time event_time = (Time)event->data.l[1];
550         HWND last_focus = x11drv_thread_data()->last_focus;
551
552         TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
553                hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
554                GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
555
556         if (can_activate_window(hwnd))
557         {
558             /* simulate a mouse click on the caption to find out
559              * whether the window wants to be activated */
560             LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
561                                        (WPARAM)GetAncestor( hwnd, GA_ROOT ),
562                                        MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
563             if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
564             {
565                 set_focus( event->display, hwnd, event_time );
566                 return;
567             }
568         }
569         /* try to find some other window to give the focus to */
570         hwnd = GetFocus();
571         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
572         if (!hwnd) hwnd = GetActiveWindow();
573         if (!hwnd) hwnd = last_focus;
574         if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
575     }
576     else if (protocol == x11drv_atom(_NET_WM_PING))
577     {
578       XClientMessageEvent xev;
579       xev = *event;
580       
581       TRACE("NET_WM Ping\n");
582       wine_tsx11_lock();
583       xev.window = DefaultRootWindow(xev.display);
584       XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
585       wine_tsx11_unlock();
586       /* this line is semi-stolen from gtk2 */
587       TRACE("NET_WM Pong\n");
588     }
589 }
590
591
592 static const char * const focus_details[] =
593 {
594     "NotifyAncestor",
595     "NotifyVirtual",
596     "NotifyInferior",
597     "NotifyNonlinear",
598     "NotifyNonlinearVirtual",
599     "NotifyPointer",
600     "NotifyPointerRoot",
601     "NotifyDetailNone"
602 };
603
604 /**********************************************************************
605  *              X11DRV_FocusIn
606  */
607 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
608 {
609     XFocusChangeEvent *event = &xev->xfocus;
610     XIC xic;
611
612     if (!hwnd) return;
613
614     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
615
616     if (event->detail == NotifyPointer) return;
617
618     if ((xic = X11DRV_get_ic( hwnd )))
619     {
620         wine_tsx11_lock();
621         XSetICFocus( xic );
622         wine_tsx11_unlock();
623     }
624     if (use_take_focus) return;  /* ignore FocusIn if we are using take focus */
625
626     if (!can_activate_window(hwnd))
627     {
628         HWND hwnd = GetFocus();
629         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
630         if (!hwnd) hwnd = GetActiveWindow();
631         if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
632         if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
633     }
634     else SetForegroundWindow( hwnd );
635 }
636
637
638 /**********************************************************************
639  *              X11DRV_FocusOut
640  *
641  * Note: only top-level windows get FocusOut events.
642  */
643 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
644 {
645     XFocusChangeEvent *event = &xev->xfocus;
646     HWND hwnd_tmp;
647     Window focus_win;
648     int revert;
649     XIC xic;
650
651     if (!hwnd) return;
652
653     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
654
655     if (event->detail == NotifyPointer) return;
656     if (ximInComposeMode) return;
657
658     x11drv_thread_data()->last_focus = hwnd;
659     if ((xic = X11DRV_get_ic( hwnd )))
660     {
661         wine_tsx11_lock();
662         XUnsetICFocus( xic );
663         wine_tsx11_unlock();
664     }
665     if (hwnd != GetForegroundWindow()) return;
666     SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
667
668     /* don't reset the foreground window, if the window which is
669        getting the focus is a Wine window */
670
671     wine_tsx11_lock();
672     XGetInputFocus( event->display, &focus_win, &revert );
673     if (focus_win)
674     {
675         if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
676             focus_win = 0;
677     }
678     wine_tsx11_unlock();
679
680     if (!focus_win)
681     {
682         /* Abey : 6-Oct-99. Check again if the focus out window is the
683            Foreground window, because in most cases the messages sent
684            above must have already changed the foreground window, in which
685            case we don't have to change the foreground window to 0 */
686         if (hwnd == GetForegroundWindow())
687         {
688             TRACE( "lost focus, setting fg to desktop\n" );
689             SetForegroundWindow( GetDesktopWindow() );
690         }
691     }
692 }
693
694
695 /***********************************************************************
696  *           X11DRV_Expose
697  */
698 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
699 {
700     XExposeEvent *event = &xev->xexpose;
701     RECT rect;
702     struct x11drv_win_data *data;
703     int flags = RDW_INVALIDATE | RDW_ERASE;
704
705     TRACE( "win %p (%lx) %d,%d %dx%d\n",
706            hwnd, event->window, event->x, event->y, event->width, event->height );
707
708     if (!(data = X11DRV_get_win_data( hwnd ))) return;
709
710     if (event->window == data->whole_window)
711     {
712         rect.left = data->whole_rect.left + event->x;
713         rect.top  = data->whole_rect.top + event->y;
714         flags |= RDW_FRAME;
715     }
716     else
717     {
718         rect.left = data->client_rect.left + event->x;
719         rect.top  = data->client_rect.top + event->y;
720     }
721     rect.right  = rect.left + event->width;
722     rect.bottom = rect.top + event->height;
723
724     if (event->window != root_window)
725     {
726         SERVER_START_REQ( update_window_zorder )
727         {
728             req->window      = wine_server_user_handle( hwnd );
729             req->rect.left   = rect.left;
730             req->rect.top    = rect.top;
731             req->rect.right  = rect.right;
732             req->rect.bottom = rect.bottom;
733             wine_server_call( req );
734         }
735         SERVER_END_REQ;
736
737         /* make position relative to client area instead of parent */
738         OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
739         flags |= RDW_ALLCHILDREN;
740     }
741
742     RedrawWindow( hwnd, &rect, 0, flags );
743 }
744
745
746 /**********************************************************************
747  *              X11DRV_MapNotify
748  */
749 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
750 {
751     struct x11drv_win_data *data;
752
753     if (!(data = X11DRV_get_win_data( hwnd ))) return;
754     if (!data->mapped) return;
755
756     if (!data->managed)
757     {
758         HWND hwndFocus = GetFocus();
759         if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus);  /* FIXME */
760     }
761 }
762
763
764 /***********************************************************************
765  *     is_net_wm_state_maximized
766  */
767 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
768 {
769     Atom type, *state;
770     int format, ret = 0;
771     unsigned long i, count, remaining;
772
773     wine_tsx11_lock();
774     if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
775                              65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
776                              &remaining, (unsigned char **)&state ))
777     {
778         if (type == XA_ATOM && format == 32)
779         {
780             for (i = 0; i < count; i++)
781             {
782                 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
783                     state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
784                     ret++;
785             }
786         }
787         XFree( state );
788     }
789     wine_tsx11_unlock();
790     return (ret == 2);
791 }
792
793
794 /***********************************************************************
795  *              X11DRV_ConfigureNotify
796  */
797 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
798 {
799     XConfigureEvent *event = &xev->xconfigure;
800     struct x11drv_win_data *data;
801     RECT rect;
802     UINT flags;
803     int cx, cy, x = event->x, y = event->y;
804
805     if (!hwnd) return;
806     if (!(data = X11DRV_get_win_data( hwnd ))) return;
807     if (!data->mapped || data->iconic || !data->managed) return;
808
809     /* Get geometry */
810
811     if (!event->send_event)  /* normal event, need to map coordinates to the root */
812     {
813         Window child;
814         wine_tsx11_lock();
815         XTranslateCoordinates( event->display, data->whole_window, root_window,
816                                0, 0, &x, &y, &child );
817         wine_tsx11_unlock();
818     }
819     rect.left   = x;
820     rect.top    = y;
821     rect.right  = x + event->width;
822     rect.bottom = y + event->height;
823     OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
824     TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
825            hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
826            event->x, event->y, event->width, event->height );
827     X11DRV_X_to_window_rect( data, &rect );
828
829     if (is_net_wm_state_maximized( event->display, data ))
830     {
831         if (!IsZoomed( data->hwnd ))
832         {
833             TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
834             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
835             return;
836         }
837     }
838     else
839     {
840         if (IsZoomed( data->hwnd ))
841         {
842             TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
843             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
844             return;
845         }
846     }
847
848     /* Compare what has changed */
849
850     x     = rect.left;
851     y     = rect.top;
852     cx    = rect.right - rect.left;
853     cy    = rect.bottom - rect.top;
854     flags = SWP_NOACTIVATE | SWP_NOZORDER;
855
856     if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
857     else
858         TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
859                hwnd, data->window_rect.left, data->window_rect.top, x, y );
860
861     if ((data->window_rect.right - data->window_rect.left == cx &&
862          data->window_rect.bottom - data->window_rect.top == cy) ||
863         (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
864     {
865         if (flags & SWP_NOMOVE) return;  /* if nothing changed, don't do anything */
866         flags |= SWP_NOSIZE;
867     }
868     else
869         TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
870                hwnd, data->window_rect.right - data->window_rect.left,
871                data->window_rect.bottom - data->window_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         TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
917         data->wm_state = WithdrawnState;
918         break;
919     case PropertyNewValue:
920         {
921             int old_state = data->wm_state;
922             int new_state = get_window_wm_state( event->display, data );
923             if (new_state != -1 && new_state != data->wm_state)
924             {
925                 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
926                        data->hwnd, data->whole_window, new_state, old_state );
927                 data->wm_state = new_state;
928                 /* ignore the initial state transition out of withdrawn state */
929                 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
930                 if (!old_state) return;
931             }
932         }
933         break;
934     }
935
936     if (!update_window || !data->managed || !data->mapped) return;
937
938     if (data->iconic && data->wm_state == NormalState)  /* restore window */
939     {
940         data->iconic = FALSE;
941         if (is_net_wm_state_maximized( event->display, data ))
942         {
943             TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
944             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
945         }
946         else
947         {
948             TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
949             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
950         }
951     }
952     else if (!data->iconic && data->wm_state == IconicState)
953     {
954         TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
955         data->iconic = TRUE;
956         SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
957     }
958 }
959
960
961 /***********************************************************************
962  *           X11DRV_PropertyNotify
963  */
964 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
965 {
966     XPropertyEvent *event = &xev->xproperty;
967     struct x11drv_win_data *data;
968
969     if (!hwnd) return;
970     if (!(data = X11DRV_get_win_data( hwnd ))) return;
971
972     if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
973 }
974
975
976 /* event filter to wait for a WM_STATE change notification on a window */
977 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
978 {
979     if (event->xany.window != (Window)arg) return 0;
980     return (event->type == DestroyNotify ||
981             (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
982 }
983
984 /***********************************************************************
985  *           wait_for_withdrawn_state
986  */
987 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
988 {
989     DWORD end = GetTickCount() + 2000;
990
991     if (!data->managed) return;
992
993     TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
994            data->hwnd, data->whole_window, set ? "" : "not " );
995
996     while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
997     {
998         XEvent event;
999         int count = 0;
1000
1001         wine_tsx11_lock();
1002         while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1003         {
1004             count++;
1005             if (XFilterEvent( &event, None )) continue;  /* filtered, ignore it */
1006             if (event.type == DestroyNotify) call_event_handler( display, &event );
1007             else
1008             {
1009                 wine_tsx11_unlock();
1010                 handle_wm_state_notify( data, &event.xproperty, FALSE );
1011                 wine_tsx11_lock();
1012             }
1013         }
1014         wine_tsx11_unlock();
1015
1016         if (!count)
1017         {
1018             struct pollfd pfd;
1019             int timeout = end - GetTickCount();
1020
1021             pfd.fd = ConnectionNumber(display);
1022             pfd.events = POLLIN;
1023             if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1024             {
1025                 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1026                 break;
1027             }
1028         }
1029     }
1030     TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1031 }
1032
1033
1034 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1035 {
1036     RECT tempRect;
1037
1038     if (!IsWindowEnabled(hQueryWnd)) return 0;
1039     
1040     GetWindowRect(hQueryWnd, &tempRect);
1041
1042     if(!PtInRect(&tempRect, *lpPt)) return 0;
1043
1044     if (!IsIconic( hQueryWnd ))
1045     {
1046         POINT pt = *lpPt;
1047         ScreenToClient( hQueryWnd, &pt );
1048         GetClientRect( hQueryWnd, &tempRect );
1049
1050         if (PtInRect( &tempRect, pt))
1051         {
1052             HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1053             if (ret && ret != hQueryWnd)
1054             {
1055                 ret = find_drop_window( ret, lpPt );
1056                 if (ret) return ret;
1057             }
1058         }
1059     }
1060
1061     if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1062     
1063     ScreenToClient(hQueryWnd, lpPt);
1064
1065     return hQueryWnd;
1066 }
1067
1068 /**********************************************************************
1069  *           EVENT_DropFromOffix
1070  *
1071  * don't know if it still works (last Changelog is from 96/11/04)
1072  */
1073 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1074 {
1075     struct x11drv_win_data *data;
1076     unsigned long       data_length;
1077     unsigned long       aux_long;
1078     unsigned char*      p_data = NULL;
1079     Atom atom_aux;
1080     int                 x, y, dummy;
1081     BOOL                bAccept;
1082     Window              win, w_aux_root, w_aux_child;
1083
1084     win = X11DRV_get_whole_window(hWnd);
1085     wine_tsx11_lock();
1086     XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1087                    &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1088     x += virtual_screen_rect.left;
1089     y += virtual_screen_rect.top;
1090     wine_tsx11_unlock();
1091
1092     if (!(data = X11DRV_get_win_data( hWnd ))) return;
1093
1094     /* find out drop point and drop window */
1095     if( x < 0 || y < 0 ||
1096         x > (data->whole_rect.right - data->whole_rect.left) ||
1097         y > (data->whole_rect.bottom - data->whole_rect.top) )
1098     {   
1099         bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1100         x = 0;
1101         y = 0; 
1102     }
1103     else
1104     {
1105         POINT   pt = { x, y };
1106         HWND    hwndDrop = find_drop_window( hWnd, &pt );
1107         if (hwndDrop)
1108         {
1109             x = pt.x;
1110             y = pt.y;
1111             bAccept = TRUE;
1112         }
1113         else
1114         {
1115             bAccept = FALSE;
1116         }
1117     }
1118
1119     if (!bAccept) return;
1120
1121     wine_tsx11_lock();
1122     XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1123                         x11drv_atom(DndSelection), 0, 65535, FALSE,
1124                         AnyPropertyType, &atom_aux, &dummy,
1125                         &data_length, &aux_long, &p_data);
1126     wine_tsx11_unlock();
1127
1128     if( !aux_long && p_data)  /* don't bother if > 64K */
1129     {
1130         char *p = (char *)p_data;
1131         char *p_drop;
1132
1133         aux_long = 0;
1134         while( *p )  /* calculate buffer size */
1135         {
1136             INT len = GetShortPathNameA( p, NULL, 0 );
1137             if (len) aux_long += len + 1;
1138             p += strlen(p) + 1;
1139         }
1140         if( aux_long && aux_long < 65535 )
1141         {
1142             HDROP                 hDrop;
1143             DROPFILES *lpDrop;
1144
1145             aux_long += sizeof(DROPFILES) + 1;
1146             hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1147             lpDrop = GlobalLock( hDrop );
1148
1149             if( lpDrop )
1150             {
1151                 lpDrop->pFiles = sizeof(DROPFILES);
1152                 lpDrop->pt.x = x;
1153                 lpDrop->pt.y = y;
1154                 lpDrop->fNC = FALSE;
1155                 lpDrop->fWide = FALSE;
1156                 p_drop = (char *)(lpDrop + 1);
1157                 p = (char *)p_data;
1158                 while(*p)
1159                 {
1160                     if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1161                         p_drop += strlen( p_drop ) + 1;
1162                     p += strlen(p) + 1;
1163                 }
1164                 *p_drop = '\0';
1165                 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1166             }
1167         }
1168     }
1169     wine_tsx11_lock();
1170     if( p_data ) XFree(p_data);
1171     wine_tsx11_unlock();
1172 }
1173
1174 /**********************************************************************
1175  *           EVENT_DropURLs
1176  *
1177  * drop items are separated by \n
1178  * each item is prefixed by its mime type
1179  *
1180  * event->data.l[3], event->data.l[4] contains drop x,y position
1181  */
1182 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1183 {
1184   struct x11drv_win_data *win_data;
1185   unsigned long data_length;
1186   unsigned long aux_long, drop_len = 0;
1187   unsigned char *p_data = NULL; /* property data */
1188   char          *p_drop = NULL;
1189   char          *p, *next;
1190   int           x, y;
1191   DROPFILES *lpDrop;
1192   HDROP hDrop;
1193   union {
1194     Atom        atom_aux;
1195     int         i;
1196     Window      w_aux;
1197     unsigned int u;
1198   }             u; /* unused */
1199
1200   if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1201
1202   wine_tsx11_lock();
1203   XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1204                       x11drv_atom(DndSelection), 0, 65535, FALSE,
1205                       AnyPropertyType, &u.atom_aux, &u.i,
1206                       &data_length, &aux_long, &p_data);
1207   wine_tsx11_unlock();
1208   if (aux_long)
1209     WARN("property too large, truncated!\n");
1210   TRACE("urls=%s\n", p_data);
1211
1212   if( !aux_long && p_data) {    /* don't bother if > 64K */
1213     /* calculate length */
1214     p = (char*) p_data;
1215     next = strchr(p, '\n');
1216     while (p) {
1217       if (next) *next=0;
1218       if (strncmp(p,"file:",5) == 0 ) {
1219         INT len = GetShortPathNameA( p+5, NULL, 0 );
1220         if (len) drop_len += len + 1;
1221       }
1222       if (next) {
1223         *next = '\n';
1224         p = next + 1;
1225         next = strchr(p, '\n');
1226       } else {
1227         p = NULL;
1228       }
1229     }
1230
1231     if( drop_len && drop_len < 65535 ) {
1232       wine_tsx11_lock();
1233       XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1234                      &x, &y, &u.i, &u.i, &u.u);
1235       x += virtual_screen_rect.left;
1236       y += virtual_screen_rect.top;
1237       wine_tsx11_unlock();
1238
1239       drop_len += sizeof(DROPFILES) + 1;
1240       hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1241       lpDrop = GlobalLock( hDrop );
1242
1243       if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1244       {
1245           lpDrop->pFiles = sizeof(DROPFILES);
1246           lpDrop->pt.x = x;
1247           lpDrop->pt.y = y;
1248           lpDrop->fNC =
1249             ( x < (win_data->client_rect.left - win_data->whole_rect.left)  ||
1250               y < (win_data->client_rect.top - win_data->whole_rect.top)    ||
1251               x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1252               y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1253           lpDrop->fWide = FALSE;
1254           p_drop = (char*)(lpDrop + 1);
1255       }
1256
1257       /* create message content */
1258       if (p_drop) {
1259         p = (char*) p_data;
1260         next = strchr(p, '\n');
1261         while (p) {
1262           if (next) *next=0;
1263           if (strncmp(p,"file:",5) == 0 ) {
1264             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1265             if (len) {
1266               TRACE("drop file %s as %s\n", p+5, p_drop);
1267               p_drop += len+1;
1268             } else {
1269               WARN("can't convert file %s to dos name\n", p+5);
1270             }
1271           } else {
1272             WARN("unknown mime type %s\n", p);
1273           }
1274           if (next) {
1275             *next = '\n';
1276             p = next + 1;
1277             next = strchr(p, '\n');
1278           } else {
1279             p = NULL;
1280           }
1281           *p_drop = '\0';
1282         }
1283
1284         GlobalUnlock(hDrop);
1285         PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1286       }
1287     }
1288     wine_tsx11_lock();
1289     if( p_data ) XFree(p_data);
1290     wine_tsx11_unlock();
1291   }
1292 }
1293
1294 /**********************************************************************
1295  *              handle_dnd_protocol
1296  */
1297 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1298 {
1299     Window root, child;
1300     int root_x, root_y, child_x, child_y;
1301     unsigned int u;
1302
1303     /* query window (drag&drop event contains only drag window) */
1304     wine_tsx11_lock();
1305     XQueryPointer( event->display, root_window, &root, &child,
1306                    &root_x, &root_y, &child_x, &child_y, &u);
1307     if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1308     wine_tsx11_unlock();
1309     if (!hwnd) return;
1310     if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1311         EVENT_DropFromOffiX(hwnd, event);
1312     else if (event->data.l[0] == DndURL)
1313         EVENT_DropURLs(hwnd, event);
1314 }
1315
1316
1317 struct client_message_handler
1318 {
1319     int    atom;                                  /* protocol atom */
1320     void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1321 };
1322
1323 static const struct client_message_handler client_messages[] =
1324 {
1325     { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1326     { XATOM_DndProtocol,  handle_dnd_protocol },
1327     { XATOM_XdndEnter,    X11DRV_XDND_EnterEvent },
1328     { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1329     { XATOM_XdndDrop,     X11DRV_XDND_DropEvent },
1330     { XATOM_XdndLeave,    X11DRV_XDND_LeaveEvent }
1331 };
1332
1333
1334 /**********************************************************************
1335  *           X11DRV_ClientMessage
1336  */
1337 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1338 {
1339     XClientMessageEvent *event = &xev->xclient;
1340     unsigned int i;
1341
1342     if (!hwnd) return;
1343
1344     if (event->format != 32)
1345     {
1346         WARN( "Don't know how to handle format %d\n", event->format );
1347         return;
1348     }
1349
1350     for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1351     {
1352         if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1353         {
1354             client_messages[i].handler( hwnd, event );
1355             return;
1356         }
1357     }
1358     TRACE( "no handler found for %ld\n", event->message_type );
1359 }
1360
1361
1362 /***********************************************************************
1363  *              X11DRV_SendInput  (X11DRV.@)
1364  */
1365 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1366 {
1367     UINT i;
1368
1369     for (i = 0; i < count; i++, inputs++)
1370     {
1371         switch(inputs->type)
1372         {
1373         case INPUT_MOUSE:
1374             X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1375                                      inputs->u.mi.mouseData, inputs->u.mi.time,
1376                                      inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1377             break;
1378         case INPUT_KEYBOARD:
1379             X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1380                                         inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1381             break;
1382         case INPUT_HARDWARE:
1383             FIXME( "INPUT_HARDWARE not supported\n" );
1384             break;
1385         }
1386     }
1387     return count;
1388 }