quartz: Silence requests for ipin on filters.
[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,      X11DRV_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 = 19;  /* 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) && format && count >= sizeof(*state)/(format/8))
681             ret = state->state;
682         XFree( state );
683     }
684     wine_tsx11_unlock();
685     return ret;
686 }
687
688
689 /***********************************************************************
690  *           EVENT_PropertyNotify
691  */
692 static void EVENT_PropertyNotify( HWND hwnd, XEvent *xev )
693 {
694     XPropertyEvent *event = &xev->xproperty;
695     struct x11drv_win_data *data;
696
697     if (!hwnd) return;
698     if (!(data = X11DRV_get_win_data( hwnd ))) return;
699
700     switch(event->state)
701     {
702     case PropertyDelete:
703         if (event->atom == x11drv_atom(WM_STATE))
704         {
705             data->wm_state = WithdrawnState;
706             TRACE( "%p/%lx: WM_STATE deleted\n", data->hwnd, data->whole_window );
707         }
708         break;
709
710     case PropertyNewValue:
711         if (event->atom == x11drv_atom(WM_STATE))
712         {
713             int new_state = get_window_wm_state( event->display, data );
714             if (new_state != -1 && new_state != data->wm_state)
715             {
716                 TRACE( "%p/%lx: new WM_STATE %d\n", data->hwnd, data->whole_window, new_state );
717                 data->wm_state = new_state;
718             }
719         }
720         break;
721     }
722 }
723
724
725 /* event filter to wait for a WM_STATE change notification on a window */
726 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
727 {
728     if (event->xany.window != (Window)arg) return 0;
729     return (event->type == DestroyNotify ||
730             (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
731 }
732
733 /***********************************************************************
734  *           wait_for_withdrawn_state
735  */
736 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
737 {
738     DWORD end = GetTickCount() + 2000;
739
740     if (!data->managed) return;
741
742     TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
743            data->hwnd, data->whole_window, set ? "" : "not " );
744
745     while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
746     {
747         if (!process_events( display, is_wm_state_notify, data->whole_window ))
748         {
749             struct pollfd pfd;
750             int timeout = end - GetTickCount();
751
752             pfd.fd = ConnectionNumber(display);
753             pfd.events = POLLIN;
754             if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
755             {
756                 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
757                 break;
758             }
759         }
760     }
761     TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
762 }
763
764
765 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
766 {
767     RECT tempRect;
768
769     if (!IsWindowEnabled(hQueryWnd)) return 0;
770     
771     GetWindowRect(hQueryWnd, &tempRect);
772
773     if(!PtInRect(&tempRect, *lpPt)) return 0;
774
775     if (!IsIconic( hQueryWnd ))
776     {
777         POINT pt = *lpPt;
778         ScreenToClient( hQueryWnd, &pt );
779         GetClientRect( hQueryWnd, &tempRect );
780
781         if (PtInRect( &tempRect, pt))
782         {
783             HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
784             if (ret && ret != hQueryWnd)
785             {
786                 ret = find_drop_window( ret, lpPt );
787                 if (ret) return ret;
788             }
789         }
790     }
791
792     if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
793     
794     ScreenToClient(hQueryWnd, lpPt);
795
796     return hQueryWnd;
797 }
798
799 /**********************************************************************
800  *           EVENT_DropFromOffix
801  *
802  * don't know if it still works (last Changelog is from 96/11/04)
803  */
804 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
805 {
806     struct x11drv_win_data *data;
807     unsigned long       data_length;
808     unsigned long       aux_long;
809     unsigned char*      p_data = NULL;
810     Atom atom_aux;
811     int                 x, y, dummy;
812     BOOL                bAccept;
813     Window              win, w_aux_root, w_aux_child;
814     HWND                hScope = hWnd;
815
816     win = X11DRV_get_whole_window(hWnd);
817     wine_tsx11_lock();
818     XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
819                    &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
820     x += virtual_screen_rect.left;
821     y += virtual_screen_rect.top;
822     wine_tsx11_unlock();
823
824     if (!(data = X11DRV_get_win_data( hWnd ))) return;
825
826     /* find out drop point and drop window */
827     if( x < 0 || y < 0 ||
828         x > (data->whole_rect.right - data->whole_rect.left) ||
829         y > (data->whole_rect.bottom - data->whole_rect.top) )
830     {   
831         bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
832         x = 0;
833         y = 0; 
834     }
835     else
836     {
837         POINT   pt = { x, y };
838         HWND    hwndDrop = find_drop_window( hWnd, &pt );
839         if (hwndDrop)
840         {
841             x = pt.x;
842             y = pt.y;
843             hScope = hwndDrop;
844             bAccept = TRUE;
845         }
846         else
847         {
848             bAccept = FALSE;
849         }
850     }
851
852     if (!bAccept) return;
853
854     wine_tsx11_lock();
855     XGetWindowProperty( event->display, DefaultRootWindow(event->display),
856                         x11drv_atom(DndSelection), 0, 65535, FALSE,
857                         AnyPropertyType, &atom_aux, &dummy,
858                         &data_length, &aux_long, &p_data);
859     wine_tsx11_unlock();
860
861     if( !aux_long && p_data)  /* don't bother if > 64K */
862     {
863         char *p = (char *)p_data;
864         char *p_drop;
865
866         aux_long = 0;
867         while( *p )  /* calculate buffer size */
868         {
869             INT len = GetShortPathNameA( p, NULL, 0 );
870             if (len) aux_long += len + 1;
871             p += strlen(p) + 1;
872         }
873         if( aux_long && aux_long < 65535 )
874         {
875             HDROP                 hDrop;
876             DROPFILES *lpDrop;
877
878             aux_long += sizeof(DROPFILES) + 1;
879             hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
880             lpDrop = (DROPFILES*)GlobalLock( hDrop );
881
882             if( lpDrop )
883             {
884                 lpDrop->pFiles = sizeof(DROPFILES);
885                 lpDrop->pt.x = x;
886                 lpDrop->pt.y = y;
887                 lpDrop->fNC = FALSE;
888                 lpDrop->fWide = FALSE;
889                 p_drop = (char *)(lpDrop + 1);
890                 p = (char *)p_data;
891                 while(*p)
892                 {
893                     if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
894                         p_drop += strlen( p_drop ) + 1;
895                     p += strlen(p) + 1;
896                 }
897                 *p_drop = '\0';
898                 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
899             }
900         }
901     }
902     wine_tsx11_lock();
903     if( p_data ) XFree(p_data);
904     wine_tsx11_unlock();
905 }
906
907 /**********************************************************************
908  *           EVENT_DropURLs
909  *
910  * drop items are separated by \n
911  * each item is prefixed by its mime type
912  *
913  * event->data.l[3], event->data.l[4] contains drop x,y position
914  */
915 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
916 {
917   struct x11drv_win_data *win_data;
918   unsigned long data_length;
919   unsigned long aux_long, drop_len = 0;
920   unsigned char *p_data = NULL; /* property data */
921   char          *p_drop = NULL;
922   char          *p, *next;
923   int           x, y;
924   DROPFILES *lpDrop;
925   HDROP hDrop;
926   union {
927     Atom        atom_aux;
928     int         i;
929     Window      w_aux;
930     unsigned int u;
931   }             u; /* unused */
932
933   if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
934
935   wine_tsx11_lock();
936   XGetWindowProperty( event->display, DefaultRootWindow(event->display),
937                       x11drv_atom(DndSelection), 0, 65535, FALSE,
938                       AnyPropertyType, &u.atom_aux, &u.i,
939                       &data_length, &aux_long, &p_data);
940   wine_tsx11_unlock();
941   if (aux_long)
942     WARN("property too large, truncated!\n");
943   TRACE("urls=%s\n", p_data);
944
945   if( !aux_long && p_data) {    /* don't bother if > 64K */
946     /* calculate length */
947     p = (char*) p_data;
948     next = strchr(p, '\n');
949     while (p) {
950       if (next) *next=0;
951       if (strncmp(p,"file:",5) == 0 ) {
952         INT len = GetShortPathNameA( p+5, NULL, 0 );
953         if (len) drop_len += len + 1;
954       }
955       if (next) {
956         *next = '\n';
957         p = next + 1;
958         next = strchr(p, '\n');
959       } else {
960         p = NULL;
961       }
962     }
963
964     if( drop_len && drop_len < 65535 ) {
965       wine_tsx11_lock();
966       XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
967                      &x, &y, &u.i, &u.i, &u.u);
968       x += virtual_screen_rect.left;
969       y += virtual_screen_rect.top;
970       wine_tsx11_unlock();
971
972       drop_len += sizeof(DROPFILES) + 1;
973       hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
974       lpDrop = (DROPFILES *) GlobalLock( hDrop );
975
976       if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
977       {
978           lpDrop->pFiles = sizeof(DROPFILES);
979           lpDrop->pt.x = x;
980           lpDrop->pt.y = y;
981           lpDrop->fNC =
982             ( x < (win_data->client_rect.left - win_data->whole_rect.left)  ||
983               y < (win_data->client_rect.top - win_data->whole_rect.top)    ||
984               x > (win_data->client_rect.right - win_data->whole_rect.left) ||
985               y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
986           lpDrop->fWide = FALSE;
987           p_drop = (char*)(lpDrop + 1);
988       }
989
990       /* create message content */
991       if (p_drop) {
992         p = (char*) p_data;
993         next = strchr(p, '\n');
994         while (p) {
995           if (next) *next=0;
996           if (strncmp(p,"file:",5) == 0 ) {
997             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
998             if (len) {
999               TRACE("drop file %s as %s\n", p+5, p_drop);
1000               p_drop += len+1;
1001             } else {
1002               WARN("can't convert file %s to dos name\n", p+5);
1003             }
1004           } else {
1005             WARN("unknown mime type %s\n", p);
1006           }
1007           if (next) {
1008             *next = '\n';
1009             p = next + 1;
1010             next = strchr(p, '\n');
1011           } else {
1012             p = NULL;
1013           }
1014           *p_drop = '\0';
1015         }
1016
1017         GlobalUnlock(hDrop);
1018         PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1019       }
1020     }
1021     wine_tsx11_lock();
1022     if( p_data ) XFree(p_data);
1023     wine_tsx11_unlock();
1024   }
1025 }
1026
1027 /**********************************************************************
1028  *              handle_dnd_protocol
1029  */
1030 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1031 {
1032     Window root, child;
1033     int root_x, root_y, child_x, child_y;
1034     unsigned int u;
1035
1036     /* query window (drag&drop event contains only drag window) */
1037     wine_tsx11_lock();
1038     XQueryPointer( event->display, root_window, &root, &child,
1039                    &root_x, &root_y, &child_x, &child_y, &u);
1040     if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1041     wine_tsx11_unlock();
1042     if (!hwnd) return;
1043     if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1044         EVENT_DropFromOffiX(hwnd, event);
1045     else if (event->data.l[0] == DndURL)
1046         EVENT_DropURLs(hwnd, event);
1047 }
1048
1049
1050 struct client_message_handler
1051 {
1052     int    atom;                                  /* protocol atom */
1053     void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1054 };
1055
1056 static const struct client_message_handler client_messages[] =
1057 {
1058     { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1059     { XATOM_DndProtocol,  handle_dnd_protocol },
1060     { XATOM_XdndEnter,    X11DRV_XDND_EnterEvent },
1061     { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1062     { XATOM_XdndDrop,     X11DRV_XDND_DropEvent },
1063     { XATOM_XdndLeave,    X11DRV_XDND_LeaveEvent }
1064 };
1065
1066
1067 /**********************************************************************
1068  *           EVENT_ClientMessage
1069  */
1070 static void EVENT_ClientMessage( HWND hwnd, XEvent *xev )
1071 {
1072     XClientMessageEvent *event = &xev->xclient;
1073     unsigned int i;
1074
1075     if (!hwnd) return;
1076
1077     if (event->format != 32)
1078     {
1079         WARN( "Don't know how to handle format %d\n", event->format );
1080         return;
1081     }
1082
1083     for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1084     {
1085         if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1086         {
1087             client_messages[i].handler( hwnd, event );
1088             return;
1089         }
1090     }
1091     TRACE( "no handler found for %ld\n", event->message_type );
1092 }
1093
1094
1095 /**********************************************************************
1096  *           X11DRV_WindowMessage   (X11DRV.@)
1097  */
1098 LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
1099 {
1100     switch(msg)
1101     {
1102     case WM_X11DRV_ACQUIRE_SELECTION:
1103         return X11DRV_AcquireClipboard( hwnd );
1104     case WM_X11DRV_DELETE_WINDOW:
1105         return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
1106     case WM_X11DRV_SET_WIN_FORMAT:
1107         return X11DRV_set_win_format( hwnd, (XID)wp );
1108     case WM_X11DRV_RESIZE_DESKTOP:
1109         X11DRV_resize_desktop( LOWORD(lp), HIWORD(lp) );
1110         return 0;
1111     default:
1112         FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp );
1113         return 0;
1114     }
1115 }
1116
1117
1118 /***********************************************************************
1119  *              X11DRV_SendInput  (X11DRV.@)
1120  */
1121 UINT X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1122 {
1123     UINT i;
1124
1125     for (i = 0; i < count; i++, inputs++)
1126     {
1127         switch(inputs->type)
1128         {
1129         case INPUT_MOUSE:
1130             X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1131                                      inputs->u.mi.mouseData, inputs->u.mi.time,
1132                                      inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1133             break;
1134         case INPUT_KEYBOARD:
1135             X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1136                                         inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1137             break;
1138         case INPUT_HARDWARE:
1139             FIXME( "INPUT_HARDWARE not supported\n" );
1140             break;
1141         }
1142     }
1143     return count;
1144 }