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