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