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