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