user32: Check structure size in GetGUIThreadInfo.
[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
792     RedrawWindow( hwnd, &rect, 0, flags );
793 }
794
795
796 /**********************************************************************
797  *              X11DRV_MapNotify
798  */
799 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
800 {
801     struct x11drv_win_data *data;
802
803     if (!(data = X11DRV_get_win_data( hwnd ))) return;
804     if (!data->mapped || data->embedded) return;
805
806     if (!data->managed)
807     {
808         HWND hwndFocus = GetFocus();
809         if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus);  /* FIXME */
810     }
811 }
812
813
814 /***********************************************************************
815  *     is_net_wm_state_maximized
816  */
817 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
818 {
819     Atom type, *state;
820     int format, ret = 0;
821     unsigned long i, count, remaining;
822
823     if (!data->whole_window) return FALSE;
824
825     wine_tsx11_lock();
826     if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
827                              65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
828                              &remaining, (unsigned char **)&state ))
829     {
830         if (type == XA_ATOM && format == 32)
831         {
832             for (i = 0; i < count; i++)
833             {
834                 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
835                     state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
836                     ret++;
837             }
838         }
839         XFree( state );
840     }
841     wine_tsx11_unlock();
842     return (ret == 2);
843 }
844
845
846 /***********************************************************************
847  *           X11DRV_ReparentNotify
848  */
849 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
850 {
851     XReparentEvent *event = &xev->xreparent;
852     struct x11drv_win_data *data;
853     HWND parent, old_parent;
854     DWORD style;
855
856     if (!(data = X11DRV_get_win_data( hwnd ))) return;
857     if (!data->embedded) return;
858
859     if (data->whole_window)
860     {
861         if (event->parent == root_window)
862         {
863             TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
864             data->embedder = 0;
865             SendMessageW( hwnd, WM_CLOSE, 0, 0 );
866             return;
867         }
868         data->embedder = event->parent;
869     }
870
871     TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
872
873     style = GetWindowLongW( hwnd, GWL_STYLE );
874     if (event->parent == root_window)
875     {
876         parent = GetDesktopWindow();
877         style = (style & ~WS_CHILD) | WS_POPUP;
878     }
879     else
880     {
881         if (!(parent = create_foreign_window( event->display, event->parent ))) return;
882         style = (style & ~WS_POPUP) | WS_CHILD;
883     }
884
885     ShowWindow( hwnd, SW_HIDE );
886     old_parent = SetParent( hwnd, parent );
887     SetWindowLongW( hwnd, GWL_STYLE, style );
888     SetWindowPos( hwnd, HWND_TOP, event->x, event->y, 0, 0,
889                   SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS |
890                   ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) );
891
892     /* make old parent destroy itself if it no longer has children */
893     if (old_parent != GetDesktopWindow()) PostMessageW( old_parent, WM_CLOSE, 0, 0 );
894 }
895
896
897 /***********************************************************************
898  *              X11DRV_ConfigureNotify
899  */
900 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
901 {
902     XConfigureEvent *event = &xev->xconfigure;
903     struct x11drv_win_data *data;
904     RECT rect;
905     UINT flags;
906     HWND parent;
907     BOOL root_coords;
908     int cx, cy, x = event->x, y = event->y;
909
910     if (!hwnd) return;
911     if (!(data = X11DRV_get_win_data( hwnd ))) return;
912     if (!data->mapped || data->iconic) return;
913     if (data->whole_window && !data->managed) return;
914     /* ignore synthetic events on foreign windows */
915     if (event->send_event && !data->whole_window) return;
916     if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
917     {
918         TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
919                hwnd, data->whole_window, event->x, event->y, event->width, event->height,
920                event->serial, data->configure_serial );
921         return;
922     }
923
924     /* Get geometry */
925
926     parent = GetAncestor( hwnd, GA_PARENT );
927     root_coords = event->send_event;  /* synthetic events are always in root coords */
928
929     if (!root_coords && parent == GetDesktopWindow()) /* normal event, map coordinates to the root */
930     {
931         Window child;
932         wine_tsx11_lock();
933         XTranslateCoordinates( event->display, event->window, root_window,
934                                0, 0, &x, &y, &child );
935         wine_tsx11_unlock();
936         root_coords = TRUE;
937     }
938     rect.left   = x;
939     rect.top    = y;
940     rect.right  = x + event->width;
941     rect.bottom = y + event->height;
942     if (root_coords) OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
943     TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
944            hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
945            event->x, event->y, event->width, event->height );
946
947     if (is_net_wm_state_maximized( event->display, data ))
948     {
949         if (!IsZoomed( data->hwnd ))
950         {
951             TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
952             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
953             return;
954         }
955     }
956     else
957     {
958         if (IsZoomed( data->hwnd ))
959         {
960             TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
961             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
962             return;
963         }
964     }
965
966     X11DRV_X_to_window_rect( data, &rect );
967     if (root_coords) MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
968
969     /* Compare what has changed */
970
971     x     = rect.left;
972     y     = rect.top;
973     cx    = rect.right - rect.left;
974     cy    = rect.bottom - rect.top;
975     flags = SWP_NOACTIVATE | SWP_NOZORDER;
976
977     if (!data->whole_window) flags |= SWP_NOCOPYBITS;  /* we can't copy bits of foreign windows */
978
979     if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
980     else
981         TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
982                hwnd, data->window_rect.left, data->window_rect.top, x, y );
983
984     if ((data->window_rect.right - data->window_rect.left == cx &&
985          data->window_rect.bottom - data->window_rect.top == cy) ||
986         (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
987     {
988         if (flags & SWP_NOMOVE) return;  /* if nothing changed, don't do anything */
989         flags |= SWP_NOSIZE;
990     }
991     else
992         TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
993                hwnd, data->window_rect.right - data->window_rect.left,
994                data->window_rect.bottom - data->window_rect.top, cx, cy );
995
996     SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
997 }
998
999
1000 /**********************************************************************
1001  *           X11DRV_GravityNotify
1002  */
1003 static void X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
1004 {
1005     XGravityEvent *event = &xev->xgravity;
1006     struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1007     RECT rect;
1008
1009     if (!data || data->whole_window) return;  /* only handle this for foreign windows */
1010
1011     rect.left   = event->x;
1012     rect.top    = event->y;
1013     rect.right  = rect.left + data->whole_rect.right - data->whole_rect.left;
1014     rect.bottom = rect.top + data->whole_rect.bottom - data->whole_rect.top;
1015
1016     TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d)\n",
1017            hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1018            event->x, event->y );
1019
1020     X11DRV_X_to_window_rect( data, &rect );
1021
1022     if (data->window_rect.left != rect.left || data ->window_rect.top != rect.top)
1023         SetWindowPos( hwnd, 0, rect.left, rect.top, 0, 0,
1024                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1025 }
1026
1027
1028 /***********************************************************************
1029  *           get_window_wm_state
1030  */
1031 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
1032 {
1033     struct
1034     {
1035         CARD32 state;
1036         XID     icon;
1037     } *state;
1038     Atom type;
1039     int format, ret = -1;
1040     unsigned long count, remaining;
1041
1042     wine_tsx11_lock();
1043     if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
1044                              sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1045                              &type, &format, &count, &remaining, (unsigned char **)&state ))
1046     {
1047         if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1048             ret = state->state;
1049         XFree( state );
1050     }
1051     wine_tsx11_unlock();
1052     return ret;
1053 }
1054
1055
1056 /***********************************************************************
1057  *           handle_wm_state_notify
1058  *
1059  * Handle a PropertyNotify for WM_STATE.
1060  */
1061 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
1062                                     BOOL update_window )
1063 {
1064     DWORD style;
1065
1066     switch(event->state)
1067     {
1068     case PropertyDelete:
1069         TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1070         data->wm_state = WithdrawnState;
1071         break;
1072     case PropertyNewValue:
1073         {
1074             int old_state = data->wm_state;
1075             int new_state = get_window_wm_state( event->display, data );
1076             if (new_state != -1 && new_state != data->wm_state)
1077             {
1078                 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1079                        data->hwnd, data->whole_window, new_state, old_state );
1080                 data->wm_state = new_state;
1081                 /* ignore the initial state transition out of withdrawn state */
1082                 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1083                 if (!old_state) return;
1084             }
1085         }
1086         break;
1087     }
1088
1089     if (!update_window || !data->managed || !data->mapped) return;
1090
1091     style = GetWindowLongW( data->hwnd, GWL_STYLE );
1092
1093     if (data->iconic && data->wm_state == NormalState)  /* restore window */
1094     {
1095         data->iconic = FALSE;
1096         if (is_net_wm_state_maximized( event->display, data ))
1097         {
1098             if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1099             {
1100                 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1101                 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1102             }
1103             else TRACE( "not restoring to max win %p/%lx style %08x\n",
1104                         data->hwnd, data->whole_window, style );
1105         }
1106         else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1107         {
1108             TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1109             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1110         }
1111         else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1112     }
1113     else if (!data->iconic && data->wm_state == IconicState)
1114     {
1115         data->iconic = TRUE;
1116         if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1117         {
1118             TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1119             SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1120         }
1121         else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1122     }
1123 }
1124
1125
1126 /***********************************************************************
1127  *           X11DRV_PropertyNotify
1128  */
1129 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1130 {
1131     XPropertyEvent *event = &xev->xproperty;
1132     struct x11drv_win_data *data;
1133
1134     if (!hwnd) return;
1135     if (!(data = X11DRV_get_win_data( hwnd ))) return;
1136
1137     if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1138 }
1139
1140
1141 /* event filter to wait for a WM_STATE change notification on a window */
1142 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1143 {
1144     if (event->xany.window != (Window)arg) return 0;
1145     return (event->type == DestroyNotify ||
1146             (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1147 }
1148
1149 /***********************************************************************
1150  *           wait_for_withdrawn_state
1151  */
1152 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1153 {
1154     DWORD end = GetTickCount() + 2000;
1155
1156     if (!data->managed) return;
1157
1158     TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1159            data->hwnd, data->whole_window, set ? "" : "not " );
1160
1161     while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1162     {
1163         XEvent event;
1164         int count = 0;
1165
1166         wine_tsx11_lock();
1167         while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1168         {
1169             count++;
1170             if (XFilterEvent( &event, None )) continue;  /* filtered, ignore it */
1171             if (event.type == DestroyNotify) call_event_handler( display, &event );
1172             else
1173             {
1174                 wine_tsx11_unlock();
1175                 handle_wm_state_notify( data, &event.xproperty, FALSE );
1176                 wine_tsx11_lock();
1177             }
1178         }
1179         wine_tsx11_unlock();
1180
1181         if (!count)
1182         {
1183             struct pollfd pfd;
1184             int timeout = end - GetTickCount();
1185
1186             pfd.fd = ConnectionNumber(display);
1187             pfd.events = POLLIN;
1188             if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1189             {
1190                 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1191                 break;
1192             }
1193         }
1194     }
1195     TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1196 }
1197
1198
1199 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1200 {
1201     RECT tempRect;
1202
1203     if (!IsWindowEnabled(hQueryWnd)) return 0;
1204     
1205     GetWindowRect(hQueryWnd, &tempRect);
1206
1207     if(!PtInRect(&tempRect, *lpPt)) return 0;
1208
1209     if (!IsIconic( hQueryWnd ))
1210     {
1211         POINT pt = *lpPt;
1212         ScreenToClient( hQueryWnd, &pt );
1213         GetClientRect( hQueryWnd, &tempRect );
1214
1215         if (PtInRect( &tempRect, pt))
1216         {
1217             HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1218             if (ret && ret != hQueryWnd)
1219             {
1220                 ret = find_drop_window( ret, lpPt );
1221                 if (ret) return ret;
1222             }
1223         }
1224     }
1225
1226     if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1227     
1228     ScreenToClient(hQueryWnd, lpPt);
1229
1230     return hQueryWnd;
1231 }
1232
1233 /**********************************************************************
1234  *           EVENT_DropFromOffix
1235  *
1236  * don't know if it still works (last Changelog is from 96/11/04)
1237  */
1238 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1239 {
1240     struct x11drv_win_data *data;
1241     unsigned long       data_length;
1242     unsigned long       aux_long;
1243     unsigned char*      p_data = NULL;
1244     Atom atom_aux;
1245     int                 x, y, dummy;
1246     BOOL                bAccept;
1247     Window              win, w_aux_root, w_aux_child;
1248
1249     win = X11DRV_get_whole_window(hWnd);
1250     wine_tsx11_lock();
1251     XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1252                    &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1253     x += virtual_screen_rect.left;
1254     y += virtual_screen_rect.top;
1255     wine_tsx11_unlock();
1256
1257     if (!(data = X11DRV_get_win_data( hWnd ))) return;
1258
1259     /* find out drop point and drop window */
1260     if( x < 0 || y < 0 ||
1261         x > (data->whole_rect.right - data->whole_rect.left) ||
1262         y > (data->whole_rect.bottom - data->whole_rect.top) )
1263     {   
1264         bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1265         x = 0;
1266         y = 0; 
1267     }
1268     else
1269     {
1270         POINT   pt = { x, y };
1271         HWND    hwndDrop = find_drop_window( hWnd, &pt );
1272         if (hwndDrop)
1273         {
1274             x = pt.x;
1275             y = pt.y;
1276             bAccept = TRUE;
1277         }
1278         else
1279         {
1280             bAccept = FALSE;
1281         }
1282     }
1283
1284     if (!bAccept) return;
1285
1286     wine_tsx11_lock();
1287     XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1288                         x11drv_atom(DndSelection), 0, 65535, FALSE,
1289                         AnyPropertyType, &atom_aux, &dummy,
1290                         &data_length, &aux_long, &p_data);
1291     wine_tsx11_unlock();
1292
1293     if( !aux_long && p_data)  /* don't bother if > 64K */
1294     {
1295         char *p = (char *)p_data;
1296         char *p_drop;
1297
1298         aux_long = 0;
1299         while( *p )  /* calculate buffer size */
1300         {
1301             INT len = GetShortPathNameA( p, NULL, 0 );
1302             if (len) aux_long += len + 1;
1303             p += strlen(p) + 1;
1304         }
1305         if( aux_long && aux_long < 65535 )
1306         {
1307             HDROP                 hDrop;
1308             DROPFILES *lpDrop;
1309
1310             aux_long += sizeof(DROPFILES) + 1;
1311             hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1312             lpDrop = GlobalLock( hDrop );
1313
1314             if( lpDrop )
1315             {
1316                 lpDrop->pFiles = sizeof(DROPFILES);
1317                 lpDrop->pt.x = x;
1318                 lpDrop->pt.y = y;
1319                 lpDrop->fNC = FALSE;
1320                 lpDrop->fWide = FALSE;
1321                 p_drop = (char *)(lpDrop + 1);
1322                 p = (char *)p_data;
1323                 while(*p)
1324                 {
1325                     if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1326                         p_drop += strlen( p_drop ) + 1;
1327                     p += strlen(p) + 1;
1328                 }
1329                 *p_drop = '\0';
1330                 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1331             }
1332         }
1333     }
1334     wine_tsx11_lock();
1335     if( p_data ) XFree(p_data);
1336     wine_tsx11_unlock();
1337 }
1338
1339 /**********************************************************************
1340  *           EVENT_DropURLs
1341  *
1342  * drop items are separated by \n
1343  * each item is prefixed by its mime type
1344  *
1345  * event->data.l[3], event->data.l[4] contains drop x,y position
1346  */
1347 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1348 {
1349   struct x11drv_win_data *win_data;
1350   unsigned long data_length;
1351   unsigned long aux_long, drop_len = 0;
1352   unsigned char *p_data = NULL; /* property data */
1353   char          *p_drop = NULL;
1354   char          *p, *next;
1355   int           x, y;
1356   DROPFILES *lpDrop;
1357   HDROP hDrop;
1358   union {
1359     Atom        atom_aux;
1360     int         i;
1361     Window      w_aux;
1362     unsigned int u;
1363   }             u; /* unused */
1364
1365   if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1366
1367   wine_tsx11_lock();
1368   XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1369                       x11drv_atom(DndSelection), 0, 65535, FALSE,
1370                       AnyPropertyType, &u.atom_aux, &u.i,
1371                       &data_length, &aux_long, &p_data);
1372   wine_tsx11_unlock();
1373   if (aux_long)
1374     WARN("property too large, truncated!\n");
1375   TRACE("urls=%s\n", p_data);
1376
1377   if( !aux_long && p_data) {    /* don't bother if > 64K */
1378     /* calculate length */
1379     p = (char*) p_data;
1380     next = strchr(p, '\n');
1381     while (p) {
1382       if (next) *next=0;
1383       if (strncmp(p,"file:",5) == 0 ) {
1384         INT len = GetShortPathNameA( p+5, NULL, 0 );
1385         if (len) drop_len += len + 1;
1386       }
1387       if (next) {
1388         *next = '\n';
1389         p = next + 1;
1390         next = strchr(p, '\n');
1391       } else {
1392         p = NULL;
1393       }
1394     }
1395
1396     if( drop_len && drop_len < 65535 ) {
1397       wine_tsx11_lock();
1398       XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1399                      &x, &y, &u.i, &u.i, &u.u);
1400       x += virtual_screen_rect.left;
1401       y += virtual_screen_rect.top;
1402       wine_tsx11_unlock();
1403
1404       drop_len += sizeof(DROPFILES) + 1;
1405       hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1406       lpDrop = GlobalLock( hDrop );
1407
1408       if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1409       {
1410           lpDrop->pFiles = sizeof(DROPFILES);
1411           lpDrop->pt.x = x;
1412           lpDrop->pt.y = y;
1413           lpDrop->fNC =
1414             ( x < (win_data->client_rect.left - win_data->whole_rect.left)  ||
1415               y < (win_data->client_rect.top - win_data->whole_rect.top)    ||
1416               x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1417               y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1418           lpDrop->fWide = FALSE;
1419           p_drop = (char*)(lpDrop + 1);
1420       }
1421
1422       /* create message content */
1423       if (p_drop) {
1424         p = (char*) p_data;
1425         next = strchr(p, '\n');
1426         while (p) {
1427           if (next) *next=0;
1428           if (strncmp(p,"file:",5) == 0 ) {
1429             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1430             if (len) {
1431               TRACE("drop file %s as %s\n", p+5, p_drop);
1432               p_drop += len+1;
1433             } else {
1434               WARN("can't convert file %s to dos name\n", p+5);
1435             }
1436           } else {
1437             WARN("unknown mime type %s\n", p);
1438           }
1439           if (next) {
1440             *next = '\n';
1441             p = next + 1;
1442             next = strchr(p, '\n');
1443           } else {
1444             p = NULL;
1445           }
1446           *p_drop = '\0';
1447         }
1448
1449         GlobalUnlock(hDrop);
1450         PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1451       }
1452     }
1453     wine_tsx11_lock();
1454     if( p_data ) XFree(p_data);
1455     wine_tsx11_unlock();
1456   }
1457 }
1458
1459
1460 /**********************************************************************
1461  *              handle_xembed_protocol
1462  */
1463 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1464 {
1465     struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1466
1467     if (!data) return;
1468
1469     switch (event->data.l[1])
1470     {
1471     case XEMBED_EMBEDDED_NOTIFY:
1472         TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1473         data->embedder = event->data.l[3];
1474         break;
1475     default:
1476         TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1477                hwnd, event->window, event->data.l[1], event->data.l[2] );
1478         break;
1479     }
1480 }
1481
1482
1483 /**********************************************************************
1484  *              handle_dnd_protocol
1485  */
1486 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1487 {
1488     Window root, child;
1489     int root_x, root_y, child_x, child_y;
1490     unsigned int u;
1491
1492     /* query window (drag&drop event contains only drag window) */
1493     wine_tsx11_lock();
1494     XQueryPointer( event->display, root_window, &root, &child,
1495                    &root_x, &root_y, &child_x, &child_y, &u);
1496     if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1497     wine_tsx11_unlock();
1498     if (!hwnd) return;
1499     if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1500         EVENT_DropFromOffiX(hwnd, event);
1501     else if (event->data.l[0] == DndURL)
1502         EVENT_DropURLs(hwnd, event);
1503 }
1504
1505
1506 struct client_message_handler
1507 {
1508     int    atom;                                  /* protocol atom */
1509     void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1510 };
1511
1512 static const struct client_message_handler client_messages[] =
1513 {
1514     { XATOM_MANAGER,      handle_manager_message },
1515     { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1516     { XATOM__XEMBED,      handle_xembed_protocol },
1517     { XATOM_DndProtocol,  handle_dnd_protocol },
1518     { XATOM_XdndEnter,    X11DRV_XDND_EnterEvent },
1519     { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1520     { XATOM_XdndDrop,     X11DRV_XDND_DropEvent },
1521     { XATOM_XdndLeave,    X11DRV_XDND_LeaveEvent }
1522 };
1523
1524
1525 /**********************************************************************
1526  *           X11DRV_ClientMessage
1527  */
1528 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1529 {
1530     XClientMessageEvent *event = &xev->xclient;
1531     unsigned int i;
1532
1533     if (!hwnd) return;
1534
1535     if (event->format != 32)
1536     {
1537         WARN( "Don't know how to handle format %d\n", event->format );
1538         return;
1539     }
1540
1541     for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1542     {
1543         if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1544         {
1545             client_messages[i].handler( hwnd, event );
1546             return;
1547         }
1548     }
1549     TRACE( "no handler found for %ld\n", event->message_type );
1550 }
1551
1552
1553 /***********************************************************************
1554  *              X11DRV_SendInput  (X11DRV.@)
1555  */
1556 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1557 {
1558     UINT i;
1559
1560     for (i = 0; i < count; i++, inputs++)
1561     {
1562         switch(inputs->type)
1563         {
1564         case INPUT_MOUSE:
1565             X11DRV_send_mouse_input( 0, 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1566                                      inputs->u.mi.mouseData, inputs->u.mi.time,
1567                                      inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1568             break;
1569         case INPUT_KEYBOARD:
1570             X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1571                                         inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1572             break;
1573         case INPUT_HARDWARE:
1574             FIXME( "INPUT_HARDWARE not supported\n" );
1575             break;
1576         }
1577     }
1578     return count;
1579 }