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