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