quartz: Fix discontinuities in wave parser.
[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/debug.h"
55
56 WINE_DEFAULT_DEBUG_CHANNEL(event);
57
58 extern BOOL ximInComposeMode;
59
60 #define DndNotDnd       -1    /* OffiX drag&drop */
61 #define DndUnknown      0
62 #define DndRawData      1
63 #define DndFile         2
64 #define DndFiles        3
65 #define DndText         4
66 #define DndDir          5
67 #define DndLink         6
68 #define DndExe          7
69
70 #define DndEND          8
71
72 #define DndURL          128   /* KDE drag&drop */
73
74   /* Event handlers */
75 static void EVENT_FocusIn( HWND hwnd, XEvent *event );
76 static void EVENT_FocusOut( HWND hwnd, XEvent *event );
77 static void EVENT_PropertyNotify( HWND hwnd, XEvent *event );
78 static void EVENT_ClientMessage( HWND hwnd, XEvent *event );
79
80 struct event_handler
81 {
82     int                  type;    /* event type */
83     x11drv_event_handler handler; /* corresponding handler function */
84 };
85
86 #define MAX_EVENT_HANDLERS 64
87
88 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
89 {
90     /* list must be sorted by event type */
91     { KeyPress,         X11DRV_KeyEvent },
92     { KeyRelease,       X11DRV_KeyEvent },
93     { ButtonPress,      X11DRV_ButtonPress },
94     { ButtonRelease,    X11DRV_ButtonRelease },
95     { MotionNotify,     X11DRV_MotionNotify },
96     { EnterNotify,      X11DRV_EnterNotify },
97     /* LeaveNotify */
98     { FocusIn,          EVENT_FocusIn },
99     { FocusOut,         EVENT_FocusOut },
100     { KeymapNotify,     X11DRV_KeymapNotify },
101     { Expose,           X11DRV_Expose },
102     /* GraphicsExpose */
103     /* NoExpose */
104     /* VisibilityNotify */
105     /* CreateNotify */
106     { DestroyNotify,    X11DRV_DestroyNotify },
107     /* UnmapNotify */
108     { MapNotify,        X11DRV_MapNotify },
109     /* MapRequest */
110     /* ReparentNotify */
111     { ConfigureNotify,  X11DRV_ConfigureNotify },
112     /* ConfigureRequest */
113     /* GravityNotify */
114     /* ResizeRequest */
115     /* CirculateNotify */
116     /* CirculateRequest */
117     { PropertyNotify,   EVENT_PropertyNotify },
118     { SelectionClear,   X11DRV_SelectionClear },
119     { SelectionRequest, X11DRV_SelectionRequest },
120     /* SelectionNotify */
121     /* ColormapNotify */
122     { ClientMessage,    EVENT_ClientMessage },
123     { MappingNotify,    X11DRV_MappingNotify },
124 };
125
126 static int nb_event_handlers = 18;  /* change this if you add handlers above */
127
128
129 /* return the name of an X event */
130 static const char *dbgstr_event( int type )
131 {
132     static const char * const event_names[] =
133     {
134         "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
135         "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
136         "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
137         "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
138         "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
139         "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
140         "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
141         "ClientMessage", "MappingNotify"
142     };
143
144     if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
145     return wine_dbg_sprintf( "Extension event %d", type );
146 }
147
148
149 /***********************************************************************
150  *           find_handler
151  *
152  * Find the handler for a given event type. Caller must hold the x11 lock.
153  */
154 static inline x11drv_event_handler find_handler( int type )
155 {
156     int min = 0, max = nb_event_handlers - 1;
157
158     while (min <= max)
159     {
160         int pos = (min + max) / 2;
161         if (handlers[pos].type == type) return handlers[pos].handler;
162         if (handlers[pos].type > type) max = pos - 1;
163         else min = pos + 1;
164     }
165     return NULL;
166 }
167
168
169 /***********************************************************************
170  *           X11DRV_register_event_handler
171  *
172  * Register a handler for a given event type.
173  * If already registered, overwrite the previous handler.
174  */
175 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
176 {
177     int min, max;
178
179     wine_tsx11_lock();
180     min = 0;
181     max = nb_event_handlers - 1;
182     while (min <= max)
183     {
184         int pos = (min + max) / 2;
185         if (handlers[pos].type == type)
186         {
187             handlers[pos].handler = handler;
188             goto done;
189         }
190         if (handlers[pos].type > type) max = pos - 1;
191         else min = pos + 1;
192     }
193     /* insert it between max and min */
194     memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
195     handlers[min].type = type;
196     handlers[min].handler = handler;
197     nb_event_handlers++;
198     assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
199 done:
200     wine_tsx11_unlock();
201     TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
202 }
203
204
205 /***********************************************************************
206  *           filter_event
207  */
208 static Bool filter_event( Display *display, XEvent *event, char *arg )
209 {
210     ULONG_PTR mask = (ULONG_PTR)arg;
211
212     if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
213
214     switch(event->type)
215     {
216     case KeyPress:
217     case KeyRelease:
218     case KeymapNotify:
219     case MappingNotify:
220         return (mask & QS_KEY) != 0;
221     case ButtonPress:
222     case ButtonRelease:
223         return (mask & QS_MOUSEBUTTON) != 0;
224     case MotionNotify:
225     case EnterNotify:
226     case LeaveNotify:
227         return (mask & QS_MOUSEMOVE) != 0;
228     case Expose:
229         return (mask & QS_PAINT) != 0;
230     case FocusIn:
231     case FocusOut:
232     case MapNotify:
233     case UnmapNotify:
234     case ConfigureNotify:
235     case PropertyNotify:
236     case ClientMessage:
237         return (mask & QS_POSTMESSAGE) != 0;
238     default:
239         return (mask & QS_SENDMESSAGE) != 0;
240     }
241 }
242
243
244 enum event_merge_action
245 {
246     MERGE_DISCARD,  /* discard the old event */
247     MERGE_HANDLE,   /* handle the old event */
248     MERGE_KEEP      /* keep the old event for future merging */
249 };
250
251 /***********************************************************************
252  *           merge_events
253  *
254  * Try to merge 2 consecutive events.
255  */
256 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
257 {
258     switch (prev->type)
259     {
260     case ConfigureNotify:
261         switch (next->type)
262         {
263         case ConfigureNotify:
264             if (prev->xany.window == next->xany.window)
265             {
266                 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
267                 return MERGE_DISCARD;
268             }
269             break;
270         case Expose:
271         case PropertyNotify:
272             return MERGE_KEEP;
273         }
274         break;
275     case MotionNotify:
276         if (prev->xany.window == next->xany.window && next->type == MotionNotify)
277         {
278             TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
279             return MERGE_DISCARD;
280         }
281         break;
282     }
283     return MERGE_HANDLE;
284 }
285
286
287 /***********************************************************************
288  *           call_event_handler
289  */
290 static inline void call_event_handler( Display *display, XEvent *event )
291 {
292     HWND hwnd;
293     x11drv_event_handler handler;
294     XEvent *prev;
295     struct x11drv_thread_data *thread_data;
296
297     if (!(handler = find_handler( event->type )))
298     {
299         TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
300         return;  /* no handler, ignore it */
301     }
302
303     if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
304         hwnd = 0;  /* not for a registered window */
305     if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
306
307     TRACE( "%s for hwnd/window %p/%lx\n",
308            dbgstr_event( event->type ), hwnd, event->xany.window );
309     wine_tsx11_unlock();
310     thread_data = x11drv_thread_data();
311     prev = thread_data->current_event;
312     thread_data->current_event = event;
313     handler( hwnd, event );
314     thread_data->current_event = prev;
315     wine_tsx11_lock();
316 }
317
318
319 /***********************************************************************
320  *           process_events
321  */
322 static int process_events( Display *display, Bool (*filter)(), ULONG_PTR arg )
323 {
324     XEvent event, prev_event;
325     int count = 0;
326     enum event_merge_action action = MERGE_DISCARD;
327
328     prev_event.type = 0;
329     wine_tsx11_lock();
330     while (XCheckIfEvent( display, &event, filter, (char *)arg ))
331     {
332         count++;
333         if (XFilterEvent( &event, None )) continue;  /* filtered, ignore it */
334         if (prev_event.type) action = merge_events( &prev_event, &event );
335         switch( action )
336         {
337         case MERGE_DISCARD:  /* discard prev, keep new */
338             prev_event = event;
339             break;
340         case MERGE_HANDLE:  /* handle prev, keep new */
341             call_event_handler( display, &prev_event );
342             prev_event = event;
343             break;
344         case MERGE_KEEP:  /* handle new, keep prev for future merging */
345             call_event_handler( display, &event );
346             break;
347         }
348     }
349     XFlush( gdi_display );
350     if (prev_event.type) call_event_handler( display, &prev_event );
351     wine_tsx11_unlock();
352     if (count) TRACE( "processed %d events\n", count );
353     return count;
354 }
355
356
357 /***********************************************************************
358  *           MsgWaitForMultipleObjectsEx   (X11DRV.@)
359  */
360 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
361                                           DWORD timeout, DWORD mask, DWORD flags )
362 {
363     DWORD ret;
364     struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
365
366     if (!data)
367     {
368         if (!count && !timeout) return WAIT_TIMEOUT;
369         return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
370                                          timeout, flags & MWMO_ALERTABLE );
371     }
372
373     if (data->current_event) mask = 0;  /* don't process nested events */
374
375     if (process_events( data->display, filter_event, mask )) ret = count - 1;
376     else if (count || timeout)
377     {
378         ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
379                                         timeout, flags & MWMO_ALERTABLE );
380         if (ret == count - 1) process_events( data->display, filter_event, mask );
381     }
382     else ret = WAIT_TIMEOUT;
383
384     return ret;
385 }
386
387 /***********************************************************************
388  *           EVENT_x11_time_to_win32_time
389  *
390  * Make our timer and the X timer line up as best we can
391  *  Pass 0 to retrieve the current adjustment value (times -1)
392  */
393 DWORD EVENT_x11_time_to_win32_time(Time time)
394 {
395   static DWORD adjust = 0;
396   DWORD now = GetTickCount();
397   DWORD ret;
398
399   if (! adjust && time != 0)
400   {
401     ret = now;
402     adjust = time - now;
403   }
404   else
405   {
406       /* If we got an event in the 'future', then our clock is clearly wrong. 
407          If we got it more than 10000 ms in the future, then it's most likely
408          that the clock has wrapped.  */
409
410       ret = time - adjust;
411       if (ret > now && ((ret - now) < 10000) && time != 0)
412       {
413         adjust += ret - now;
414         ret    -= ret - now;
415       }
416   }
417
418   return ret;
419
420 }
421
422 /*******************************************************************
423  *         can_activate_window
424  *
425  * Check if we can activate the specified window.
426  */
427 static inline BOOL can_activate_window( HWND hwnd )
428 {
429     LONG style = GetWindowLongW( hwnd, GWL_STYLE );
430     if (!(style & WS_VISIBLE)) return FALSE;
431     if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
432     if (hwnd == GetDesktopWindow()) return FALSE;
433     return !(style & WS_DISABLED);
434 }
435
436
437 /**********************************************************************
438  *              set_focus
439  */
440 static void set_focus( HWND hwnd, Time time )
441 {
442     HWND focus;
443     Window win;
444
445     TRACE( "setting foreground window to %p\n", hwnd );
446     SetForegroundWindow( hwnd );
447
448     focus = GetFocus();
449     if (focus) focus = GetAncestor( focus, GA_ROOT );
450     win = X11DRV_get_whole_window(focus);
451
452     if (win)
453     {
454         TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
455         wine_tsx11_lock();
456         XSetInputFocus( thread_display(), win, RevertToParent, time );
457         wine_tsx11_unlock();
458     }
459 }
460
461
462 /**********************************************************************
463  *              handle_wm_protocols
464  */
465 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
466 {
467     Atom protocol = (Atom)event->data.l[0];
468
469     if (!protocol) return;
470
471     if (protocol == x11drv_atom(WM_DELETE_WINDOW))
472     {
473         /* Ignore the delete window request if the window has been disabled
474          * and we are in managed mode. This is to disallow applications from
475          * being closed by the window manager while in a modal state.
476          */
477         if (IsWindowEnabled(hwnd))
478         {
479             HMENU hSysMenu;
480
481             if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
482             hSysMenu = GetSystemMenu(hwnd, FALSE);
483             if (hSysMenu)
484             {
485                 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
486                 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
487                     return;
488             }
489             if (GetActiveWindow() != hwnd)
490             {
491                 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
492                                            (WPARAM)GetAncestor( hwnd, GA_ROOT ),
493                                            MAKELONG(HTCLOSE,WM_LBUTTONDOWN) );
494                 switch(ma)
495                 {
496                     case MA_NOACTIVATEANDEAT:
497                     case MA_ACTIVATEANDEAT:
498                         return;
499                     case MA_NOACTIVATE:
500                         break;
501                     case MA_ACTIVATE:
502                     case 0:
503                         SetActiveWindow(hwnd);
504                         break;
505                     default:
506                         WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
507                         break;
508                 }
509             }
510             PostMessageW( hwnd, WM_X11DRV_DELETE_WINDOW, 0, 0 );
511         }
512     }
513     else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
514     {
515         Time event_time = (Time)event->data.l[1];
516         HWND last_focus = x11drv_thread_data()->last_focus;
517
518         TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
519                hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
520                GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
521
522         if (can_activate_window(hwnd))
523         {
524             /* simulate a mouse click on the caption to find out
525              * whether the window wants to be activated */
526             LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
527                                        (WPARAM)GetAncestor( hwnd, GA_ROOT ),
528                                        MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
529             if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
530             {
531                 set_focus( hwnd, event_time );
532                 return;
533             }
534         }
535         /* try to find some other window to give the focus to */
536         hwnd = GetFocus();
537         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
538         if (!hwnd) hwnd = GetActiveWindow();
539         if (!hwnd) hwnd = last_focus;
540         if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, event_time );
541     }
542     else if (protocol == x11drv_atom(_NET_WM_PING))
543     {
544       XClientMessageEvent xev;
545       xev = *event;
546       
547       TRACE("NET_WM Ping\n");
548       wine_tsx11_lock();
549       xev.window = DefaultRootWindow(xev.display);
550       XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
551       wine_tsx11_unlock();
552       /* this line is semi-stolen from gtk2 */
553       TRACE("NET_WM Pong\n");
554     }
555 }
556
557
558 static const char * const focus_details[] =
559 {
560     "NotifyAncestor",
561     "NotifyVirtual",
562     "NotifyInferior",
563     "NotifyNonlinear",
564     "NotifyNonlinearVirtual",
565     "NotifyPointer",
566     "NotifyPointerRoot",
567     "NotifyDetailNone"
568 };
569
570 /**********************************************************************
571  *              EVENT_FocusIn
572  */
573 static void EVENT_FocusIn( HWND hwnd, XEvent *xev )
574 {
575     XFocusChangeEvent *event = &xev->xfocus;
576     XIC xic;
577
578     if (!hwnd) return;
579
580     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
581
582     if (event->detail == NotifyPointer) return;
583
584     if ((xic = X11DRV_get_ic( hwnd )))
585     {
586         wine_tsx11_lock();
587         XSetICFocus( xic );
588         wine_tsx11_unlock();
589     }
590     if (use_take_focus) return;  /* ignore FocusIn if we are using take focus */
591
592     if (!can_activate_window(hwnd))
593     {
594         HWND hwnd = GetFocus();
595         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
596         if (!hwnd) hwnd = GetActiveWindow();
597         if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
598         if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, CurrentTime );
599     }
600     else SetForegroundWindow( hwnd );
601 }
602
603
604 /**********************************************************************
605  *              EVENT_FocusOut
606  *
607  * Note: only top-level windows get FocusOut events.
608  */
609 static void EVENT_FocusOut( HWND hwnd, XEvent *xev )
610 {
611     XFocusChangeEvent *event = &xev->xfocus;
612     HWND hwnd_tmp;
613     Window focus_win;
614     int revert;
615     XIC xic;
616
617     if (!hwnd) return;
618
619     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
620
621     if (event->detail == NotifyPointer) return;
622     if (ximInComposeMode) return;
623
624     x11drv_thread_data()->last_focus = hwnd;
625     if ((xic = X11DRV_get_ic( hwnd )))
626     {
627         wine_tsx11_lock();
628         XUnsetICFocus( xic );
629         wine_tsx11_unlock();
630     }
631     if (hwnd != GetForegroundWindow()) return;
632     SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
633
634     /* don't reset the foreground window, if the window which is
635        getting the focus is a Wine window */
636
637     wine_tsx11_lock();
638     XGetInputFocus( thread_display(), &focus_win, &revert );
639     if (focus_win)
640     {
641         if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0)
642             focus_win = 0;
643     }
644     wine_tsx11_unlock();
645
646     if (!focus_win)
647     {
648         /* Abey : 6-Oct-99. Check again if the focus out window is the
649            Foreground window, because in most cases the messages sent
650            above must have already changed the foreground window, in which
651            case we don't have to change the foreground window to 0 */
652         if (hwnd == GetForegroundWindow())
653         {
654             TRACE( "lost focus, setting fg to desktop\n" );
655             SetForegroundWindow( GetDesktopWindow() );
656         }
657     }
658 }
659
660
661 /***********************************************************************
662  *           get_window_wm_state
663  */
664 int get_window_wm_state( Display *display, struct x11drv_win_data *data )
665 {
666     struct
667     {
668         CARD32 state;
669         XID     icon;
670     } *state;
671     Atom type;
672     int format, ret = -1;
673     unsigned long count, remaining;
674
675     wine_tsx11_lock();
676     if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
677                              sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
678                              &type, &format, &count, &remaining, (unsigned char **)&state ))
679     {
680         if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
681             ret = state->state;
682         XFree( state );
683     }
684     wine_tsx11_unlock();
685     return ret;
686 }
687
688
689 /***********************************************************************
690  *           handle_wm_state_notify
691  *
692  * Handle a PropertyNotify for WM_STATE.
693  */
694 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
695                                     BOOL update_window )
696 {
697     switch(event->state)
698     {
699     case PropertyDelete:
700         data->wm_state = WithdrawnState;
701         TRACE( "%p/%lx: WM_STATE deleted\n", data->hwnd, data->whole_window );
702         break;
703     case PropertyNewValue:
704         {
705             int new_state = get_window_wm_state( event->display, data );
706             if (new_state != -1 && new_state != data->wm_state)
707             {
708                 TRACE( "%p/%lx: new WM_STATE %d\n", data->hwnd, data->whole_window, new_state );
709                 data->wm_state = new_state;
710             }
711         }
712         break;
713     }
714
715     if (!update_window || !data->managed || !data->mapped) return;
716
717     if (data->iconic && data->wm_state == NormalState)  /* restore window */
718     {
719         int x, y;
720         unsigned int width, height, border, depth;
721         Window root, top;
722         WINDOWPLACEMENT wp;
723         RECT rect;
724
725         /* FIXME: hack */
726         wine_tsx11_lock();
727         XGetGeometry( event->display, data->whole_window, &root, &x, &y, &width, &height,
728                         &border, &depth );
729         XTranslateCoordinates( event->display, data->whole_window, root, 0, 0, &x, &y, &top );
730         wine_tsx11_unlock();
731         rect.left   = x;
732         rect.top    = y;
733         rect.right  = x + width;
734         rect.bottom = y + height;
735         OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
736         X11DRV_X_to_window_rect( data, &rect );
737
738         wp.length = sizeof(wp);
739         GetWindowPlacement( data->hwnd, &wp );
740         wp.flags = 0;
741         wp.showCmd = SW_RESTORE;
742         wp.rcNormalPosition = rect;
743
744         TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
745         data->iconic = FALSE;
746         SetWindowPlacement( data->hwnd, &wp );
747     }
748     else if (!data->iconic && data->wm_state == IconicState)
749     {
750         TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
751         data->iconic = TRUE;
752         ShowWindow( data->hwnd, SW_MINIMIZE );
753     }
754 }
755
756
757 /***********************************************************************
758  *           EVENT_PropertyNotify
759  */
760 static void EVENT_PropertyNotify( HWND hwnd, XEvent *xev )
761 {
762     XPropertyEvent *event = &xev->xproperty;
763     struct x11drv_win_data *data;
764
765     if (!hwnd) return;
766     if (!(data = X11DRV_get_win_data( hwnd ))) return;
767
768     if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
769 }
770
771
772 /* event filter to wait for a WM_STATE change notification on a window */
773 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
774 {
775     if (event->xany.window != (Window)arg) return 0;
776     return (event->type == DestroyNotify ||
777             (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
778 }
779
780 /***********************************************************************
781  *           wait_for_withdrawn_state
782  */
783 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
784 {
785     DWORD end = GetTickCount() + 2000;
786
787     if (!data->managed) return;
788
789     TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
790            data->hwnd, data->whole_window, set ? "" : "not " );
791
792     while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
793     {
794         XEvent event;
795         int count = 0;
796
797         wine_tsx11_lock();
798         while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
799         {
800             count++;
801             if (XFilterEvent( &event, None )) continue;  /* filtered, ignore it */
802             if (event.type == DestroyNotify) call_event_handler( display, &event );
803             else
804             {
805                 wine_tsx11_unlock();
806                 handle_wm_state_notify( data, &event.xproperty, FALSE );
807                 wine_tsx11_lock();
808             }
809         }
810         wine_tsx11_unlock();
811
812         if (!count)
813         {
814             struct pollfd pfd;
815             int timeout = end - GetTickCount();
816
817             pfd.fd = ConnectionNumber(display);
818             pfd.events = POLLIN;
819             if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
820             {
821                 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
822                 break;
823             }
824         }
825     }
826     TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
827 }
828
829
830 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
831 {
832     RECT tempRect;
833
834     if (!IsWindowEnabled(hQueryWnd)) return 0;
835     
836     GetWindowRect(hQueryWnd, &tempRect);
837
838     if(!PtInRect(&tempRect, *lpPt)) return 0;
839
840     if (!IsIconic( hQueryWnd ))
841     {
842         POINT pt = *lpPt;
843         ScreenToClient( hQueryWnd, &pt );
844         GetClientRect( hQueryWnd, &tempRect );
845
846         if (PtInRect( &tempRect, pt))
847         {
848             HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
849             if (ret && ret != hQueryWnd)
850             {
851                 ret = find_drop_window( ret, lpPt );
852                 if (ret) return ret;
853             }
854         }
855     }
856
857     if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
858     
859     ScreenToClient(hQueryWnd, lpPt);
860
861     return hQueryWnd;
862 }
863
864 /**********************************************************************
865  *           EVENT_DropFromOffix
866  *
867  * don't know if it still works (last Changelog is from 96/11/04)
868  */
869 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
870 {
871     struct x11drv_win_data *data;
872     unsigned long       data_length;
873     unsigned long       aux_long;
874     unsigned char*      p_data = NULL;
875     Atom atom_aux;
876     int                 x, y, dummy;
877     BOOL                bAccept;
878     Window              win, w_aux_root, w_aux_child;
879     HWND                hScope = hWnd;
880
881     win = X11DRV_get_whole_window(hWnd);
882     wine_tsx11_lock();
883     XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
884                    &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
885     x += virtual_screen_rect.left;
886     y += virtual_screen_rect.top;
887     wine_tsx11_unlock();
888
889     if (!(data = X11DRV_get_win_data( hWnd ))) return;
890
891     /* find out drop point and drop window */
892     if( x < 0 || y < 0 ||
893         x > (data->whole_rect.right - data->whole_rect.left) ||
894         y > (data->whole_rect.bottom - data->whole_rect.top) )
895     {   
896         bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
897         x = 0;
898         y = 0; 
899     }
900     else
901     {
902         POINT   pt = { x, y };
903         HWND    hwndDrop = find_drop_window( hWnd, &pt );
904         if (hwndDrop)
905         {
906             x = pt.x;
907             y = pt.y;
908             hScope = hwndDrop;
909             bAccept = TRUE;
910         }
911         else
912         {
913             bAccept = FALSE;
914         }
915     }
916
917     if (!bAccept) return;
918
919     wine_tsx11_lock();
920     XGetWindowProperty( event->display, DefaultRootWindow(event->display),
921                         x11drv_atom(DndSelection), 0, 65535, FALSE,
922                         AnyPropertyType, &atom_aux, &dummy,
923                         &data_length, &aux_long, &p_data);
924     wine_tsx11_unlock();
925
926     if( !aux_long && p_data)  /* don't bother if > 64K */
927     {
928         char *p = (char *)p_data;
929         char *p_drop;
930
931         aux_long = 0;
932         while( *p )  /* calculate buffer size */
933         {
934             INT len = GetShortPathNameA( p, NULL, 0 );
935             if (len) aux_long += len + 1;
936             p += strlen(p) + 1;
937         }
938         if( aux_long && aux_long < 65535 )
939         {
940             HDROP                 hDrop;
941             DROPFILES *lpDrop;
942
943             aux_long += sizeof(DROPFILES) + 1;
944             hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
945             lpDrop = (DROPFILES*)GlobalLock( hDrop );
946
947             if( lpDrop )
948             {
949                 lpDrop->pFiles = sizeof(DROPFILES);
950                 lpDrop->pt.x = x;
951                 lpDrop->pt.y = y;
952                 lpDrop->fNC = FALSE;
953                 lpDrop->fWide = FALSE;
954                 p_drop = (char *)(lpDrop + 1);
955                 p = (char *)p_data;
956                 while(*p)
957                 {
958                     if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
959                         p_drop += strlen( p_drop ) + 1;
960                     p += strlen(p) + 1;
961                 }
962                 *p_drop = '\0';
963                 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
964             }
965         }
966     }
967     wine_tsx11_lock();
968     if( p_data ) XFree(p_data);
969     wine_tsx11_unlock();
970 }
971
972 /**********************************************************************
973  *           EVENT_DropURLs
974  *
975  * drop items are separated by \n
976  * each item is prefixed by its mime type
977  *
978  * event->data.l[3], event->data.l[4] contains drop x,y position
979  */
980 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
981 {
982   struct x11drv_win_data *win_data;
983   unsigned long data_length;
984   unsigned long aux_long, drop_len = 0;
985   unsigned char *p_data = NULL; /* property data */
986   char          *p_drop = NULL;
987   char          *p, *next;
988   int           x, y;
989   DROPFILES *lpDrop;
990   HDROP hDrop;
991   union {
992     Atom        atom_aux;
993     int         i;
994     Window      w_aux;
995     unsigned int u;
996   }             u; /* unused */
997
998   if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
999
1000   wine_tsx11_lock();
1001   XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1002                       x11drv_atom(DndSelection), 0, 65535, FALSE,
1003                       AnyPropertyType, &u.atom_aux, &u.i,
1004                       &data_length, &aux_long, &p_data);
1005   wine_tsx11_unlock();
1006   if (aux_long)
1007     WARN("property too large, truncated!\n");
1008   TRACE("urls=%s\n", p_data);
1009
1010   if( !aux_long && p_data) {    /* don't bother if > 64K */
1011     /* calculate length */
1012     p = (char*) p_data;
1013     next = strchr(p, '\n');
1014     while (p) {
1015       if (next) *next=0;
1016       if (strncmp(p,"file:",5) == 0 ) {
1017         INT len = GetShortPathNameA( p+5, NULL, 0 );
1018         if (len) drop_len += len + 1;
1019       }
1020       if (next) {
1021         *next = '\n';
1022         p = next + 1;
1023         next = strchr(p, '\n');
1024       } else {
1025         p = NULL;
1026       }
1027     }
1028
1029     if( drop_len && drop_len < 65535 ) {
1030       wine_tsx11_lock();
1031       XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1032                      &x, &y, &u.i, &u.i, &u.u);
1033       x += virtual_screen_rect.left;
1034       y += virtual_screen_rect.top;
1035       wine_tsx11_unlock();
1036
1037       drop_len += sizeof(DROPFILES) + 1;
1038       hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1039       lpDrop = (DROPFILES *) GlobalLock( hDrop );
1040
1041       if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1042       {
1043           lpDrop->pFiles = sizeof(DROPFILES);
1044           lpDrop->pt.x = x;
1045           lpDrop->pt.y = y;
1046           lpDrop->fNC =
1047             ( x < (win_data->client_rect.left - win_data->whole_rect.left)  ||
1048               y < (win_data->client_rect.top - win_data->whole_rect.top)    ||
1049               x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1050               y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1051           lpDrop->fWide = FALSE;
1052           p_drop = (char*)(lpDrop + 1);
1053       }
1054
1055       /* create message content */
1056       if (p_drop) {
1057         p = (char*) p_data;
1058         next = strchr(p, '\n');
1059         while (p) {
1060           if (next) *next=0;
1061           if (strncmp(p,"file:",5) == 0 ) {
1062             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1063             if (len) {
1064               TRACE("drop file %s as %s\n", p+5, p_drop);
1065               p_drop += len+1;
1066             } else {
1067               WARN("can't convert file %s to dos name\n", p+5);
1068             }
1069           } else {
1070             WARN("unknown mime type %s\n", p);
1071           }
1072           if (next) {
1073             *next = '\n';
1074             p = next + 1;
1075             next = strchr(p, '\n');
1076           } else {
1077             p = NULL;
1078           }
1079           *p_drop = '\0';
1080         }
1081
1082         GlobalUnlock(hDrop);
1083         PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1084       }
1085     }
1086     wine_tsx11_lock();
1087     if( p_data ) XFree(p_data);
1088     wine_tsx11_unlock();
1089   }
1090 }
1091
1092 /**********************************************************************
1093  *              handle_dnd_protocol
1094  */
1095 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1096 {
1097     Window root, child;
1098     int root_x, root_y, child_x, child_y;
1099     unsigned int u;
1100
1101     /* query window (drag&drop event contains only drag window) */
1102     wine_tsx11_lock();
1103     XQueryPointer( event->display, root_window, &root, &child,
1104                    &root_x, &root_y, &child_x, &child_y, &u);
1105     if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1106     wine_tsx11_unlock();
1107     if (!hwnd) return;
1108     if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1109         EVENT_DropFromOffiX(hwnd, event);
1110     else if (event->data.l[0] == DndURL)
1111         EVENT_DropURLs(hwnd, event);
1112 }
1113
1114
1115 struct client_message_handler
1116 {
1117     int    atom;                                  /* protocol atom */
1118     void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1119 };
1120
1121 static const struct client_message_handler client_messages[] =
1122 {
1123     { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1124     { XATOM_DndProtocol,  handle_dnd_protocol },
1125     { XATOM_XdndEnter,    X11DRV_XDND_EnterEvent },
1126     { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1127     { XATOM_XdndDrop,     X11DRV_XDND_DropEvent },
1128     { XATOM_XdndLeave,    X11DRV_XDND_LeaveEvent }
1129 };
1130
1131
1132 /**********************************************************************
1133  *           EVENT_ClientMessage
1134  */
1135 static void EVENT_ClientMessage( HWND hwnd, XEvent *xev )
1136 {
1137     XClientMessageEvent *event = &xev->xclient;
1138     unsigned int i;
1139
1140     if (!hwnd) return;
1141
1142     if (event->format != 32)
1143     {
1144         WARN( "Don't know how to handle format %d\n", event->format );
1145         return;
1146     }
1147
1148     for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1149     {
1150         if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1151         {
1152             client_messages[i].handler( hwnd, event );
1153             return;
1154         }
1155     }
1156     TRACE( "no handler found for %ld\n", event->message_type );
1157 }
1158
1159
1160 /**********************************************************************
1161  *           X11DRV_WindowMessage   (X11DRV.@)
1162  */
1163 LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
1164 {
1165     switch(msg)
1166     {
1167     case WM_X11DRV_ACQUIRE_SELECTION:
1168         return X11DRV_AcquireClipboard( hwnd );
1169     case WM_X11DRV_DELETE_WINDOW:
1170         return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
1171     case WM_X11DRV_SET_WIN_FORMAT:
1172         return X11DRV_set_win_format( hwnd, (XID)wp );
1173     case WM_X11DRV_RESIZE_DESKTOP:
1174         X11DRV_resize_desktop( LOWORD(lp), HIWORD(lp) );
1175         return 0;
1176     default:
1177         FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp );
1178         return 0;
1179     }
1180 }
1181
1182
1183 /***********************************************************************
1184  *              X11DRV_SendInput  (X11DRV.@)
1185  */
1186 UINT X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1187 {
1188     UINT i;
1189
1190     for (i = 0; i < count; i++, inputs++)
1191     {
1192         switch(inputs->type)
1193         {
1194         case INPUT_MOUSE:
1195             X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1196                                      inputs->u.mi.mouseData, inputs->u.mi.time,
1197                                      inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1198             break;
1199         case INPUT_KEYBOARD:
1200             X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1201                                         inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1202             break;
1203         case INPUT_HARDWARE:
1204             FIXME( "INPUT_HARDWARE not supported\n" );
1205             break;
1206         }
1207     }
1208     return count;
1209 }