4 * Copyright 1993 Alexandre Julliard
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.
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.
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
27 #ifdef HAVE_SYS_POLL_H
30 #include <X11/Xatom.h>
31 #include <X11/keysym.h>
33 #include <X11/Xresource.h>
34 #include <X11/Xutil.h>
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
49 /* avoid conflict with field names in included win32 headers */
51 #include "shlobj.h" /* DROPFILES */
54 #include "wine/server.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(event);
59 extern BOOL ximInComposeMode;
61 #define DndNotDnd -1 /* OffiX drag&drop */
73 #define DndURL 128 /* KDE drag&drop */
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
89 Bool (*pXGetEventData)( Display *display, XEvent /*XGenericEventCookie*/ *event ) = NULL;
90 void (*pXFreeEventData)( Display *display, XEvent /*XGenericEventCookie*/ *event ) = NULL;
93 static void X11DRV_FocusIn( HWND hwnd, XEvent *event );
94 static void X11DRV_FocusOut( HWND hwnd, XEvent *event );
95 static void X11DRV_Expose( HWND hwnd, XEvent *event );
96 static void X11DRV_MapNotify( HWND hwnd, XEvent *event );
97 static void X11DRV_UnmapNotify( HWND hwnd, XEvent *event );
98 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *event );
99 static void X11DRV_ConfigureNotify( HWND hwnd, XEvent *event );
100 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
101 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
102 static void X11DRV_GravityNotify( HWND hwnd, XEvent *event );
104 #define MAX_EVENT_HANDLERS 128
106 static x11drv_event_handler handlers[MAX_EVENT_HANDLERS] =
108 NULL, /* 0 reserved */
109 NULL, /* 1 reserved */
110 X11DRV_KeyEvent, /* 2 KeyPress */
111 X11DRV_KeyEvent, /* 3 KeyRelease */
112 X11DRV_ButtonPress, /* 4 ButtonPress */
113 X11DRV_ButtonRelease, /* 5 ButtonRelease */
114 X11DRV_MotionNotify, /* 6 MotionNotify */
115 X11DRV_EnterNotify, /* 7 EnterNotify */
116 NULL, /* 8 LeaveNotify */
117 X11DRV_FocusIn, /* 9 FocusIn */
118 X11DRV_FocusOut, /* 10 FocusOut */
119 X11DRV_KeymapNotify, /* 11 KeymapNotify */
120 X11DRV_Expose, /* 12 Expose */
121 NULL, /* 13 GraphicsExpose */
122 NULL, /* 14 NoExpose */
123 NULL, /* 15 VisibilityNotify */
124 NULL, /* 16 CreateNotify */
125 X11DRV_DestroyNotify, /* 17 DestroyNotify */
126 X11DRV_UnmapNotify, /* 18 UnmapNotify */
127 X11DRV_MapNotify, /* 19 MapNotify */
128 NULL, /* 20 MapRequest */
129 X11DRV_ReparentNotify, /* 21 ReparentNotify */
130 X11DRV_ConfigureNotify, /* 22 ConfigureNotify */
131 NULL, /* 23 ConfigureRequest */
132 X11DRV_GravityNotify, /* 24 GravityNotify */
133 NULL, /* 25 ResizeRequest */
134 NULL, /* 26 CirculateNotify */
135 NULL, /* 27 CirculateRequest */
136 X11DRV_PropertyNotify, /* 28 PropertyNotify */
137 X11DRV_SelectionClear, /* 29 SelectionClear */
138 X11DRV_SelectionRequest, /* 30 SelectionRequest */
139 NULL, /* 31 SelectionNotify */
140 NULL, /* 32 ColormapNotify */
141 X11DRV_ClientMessage, /* 33 ClientMessage */
142 X11DRV_MappingNotify, /* 34 MappingNotify */
143 X11DRV_GenericEvent /* 35 GenericEvent */
146 static const char * event_names[MAX_EVENT_HANDLERS] =
148 NULL, NULL, "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
149 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
150 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
151 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
152 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify", "ResizeRequest",
153 "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear", "SelectionRequest",
154 "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify", "GenericEvent"
157 /* return the name of an X event */
158 static const char *dbgstr_event( int type )
160 if (type < MAX_EVENT_HANDLERS && event_names[type]) return event_names[type];
161 return wine_dbg_sprintf( "Unknown event %d", type );
164 static inline void get_event_data( XEvent *event )
166 #if defined(GenericEvent) && defined(HAVE_XEVENT_XCOOKIE)
167 if (event->xany.type != GenericEvent) return;
168 if (!pXGetEventData || !pXGetEventData( event->xany.display, event )) event->xcookie.data = NULL;
172 static inline void free_event_data( XEvent *event )
174 #if defined(GenericEvent) && defined(HAVE_XEVENT_XCOOKIE)
175 if (event->xany.type != GenericEvent) return;
176 if (event->xcookie.data) pXFreeEventData( event->xany.display, event );
180 /***********************************************************************
181 * X11DRV_register_event_handler
183 * Register a handler for a given event type.
184 * If already registered, overwrite the previous handler.
186 void X11DRV_register_event_handler( int type, x11drv_event_handler handler, const char *name )
188 assert( type <= MAX_EVENT_HANDLERS );
189 assert( !handlers[type] );
190 handlers[type] = handler;
191 event_names[type] = name;
192 TRACE("registered handler %p for event %d %s\n", handler, type, debugstr_a(name) );
196 /***********************************************************************
199 static Bool filter_event( Display *display, XEvent *event, char *arg )
201 ULONG_PTR mask = (ULONG_PTR)arg;
203 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
211 return (mask & QS_KEY) != 0;
214 return (mask & QS_MOUSEBUTTON) != 0;
218 return (mask & QS_MOUSEMOVE) != 0;
220 return (mask & QS_PAINT) != 0;
225 case ConfigureNotify:
228 return (mask & QS_POSTMESSAGE) != 0;
230 return (mask & QS_SENDMESSAGE) != 0;
235 enum event_merge_action
237 MERGE_DISCARD, /* discard the old event */
238 MERGE_HANDLE, /* handle the old event */
239 MERGE_KEEP /* keep the old event for future merging */
242 /***********************************************************************
245 * Try to merge 2 consecutive events.
247 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
251 case ConfigureNotify:
254 case ConfigureNotify:
255 if (prev->xany.window == next->xany.window)
257 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
258 return MERGE_DISCARD;
267 if (prev->xany.window == next->xany.window && next->type == MotionNotify)
269 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
270 return MERGE_DISCARD;
278 /***********************************************************************
281 static inline void call_event_handler( Display *display, XEvent *event )
285 struct x11drv_thread_data *thread_data;
287 if (!handlers[event->type])
289 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
290 return; /* no handler, ignore it */
293 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
294 hwnd = 0; /* not for a registered window */
295 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
297 TRACE( "%lu %s for hwnd/window %p/%lx\n",
298 event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window );
300 thread_data = x11drv_thread_data();
301 prev = thread_data->current_event;
302 thread_data->current_event = event;
303 handlers[event->type]( hwnd, event );
304 thread_data->current_event = prev;
309 /***********************************************************************
312 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
314 XEvent event, prev_event;
316 enum event_merge_action action = MERGE_DISCARD;
320 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
323 if (XFilterEvent( &event, None ))
326 * SCIM on linux filters key events strangely. It does not filter the
327 * KeyPress events for these keys however it does filter the
328 * KeyRelease events. This causes wine to become very confused as
329 * to the keyboard state.
331 * We need to let those KeyRelease events be processed so that the
332 * keyboard state is correct.
334 if (event.type == KeyRelease)
337 XKeyEvent *keyevent = &event.xkey;
339 XLookupString(keyevent, NULL, 0, &keysym, NULL);
340 if (!(keysym == XK_Shift_L ||
341 keysym == XK_Shift_R ||
342 keysym == XK_Control_L ||
343 keysym == XK_Control_R ||
344 keysym == XK_Alt_R ||
345 keysym == XK_Alt_L ||
346 keysym == XK_Meta_R ||
347 keysym == XK_Meta_L))
348 continue; /* not a key we care about, ignore it */
351 continue; /* filtered, ignore it */
353 get_event_data( &event );
354 if (prev_event.type) action = merge_events( &prev_event, &event );
357 case MERGE_HANDLE: /* handle prev, keep new */
358 call_event_handler( display, &prev_event );
360 case MERGE_DISCARD: /* discard prev, keep new */
361 free_event_data( &prev_event );
364 case MERGE_KEEP: /* handle new, keep prev for future merging */
365 call_event_handler( display, &event );
366 free_event_data( &event );
370 if (prev_event.type) call_event_handler( display, &prev_event );
371 free_event_data( &prev_event );
372 XFlush( gdi_display );
374 if (count) TRACE( "processed %d events\n", count );
379 /***********************************************************************
380 * MsgWaitForMultipleObjectsEx (X11DRV.@)
382 DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
383 DWORD timeout, DWORD mask, DWORD flags )
386 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
390 if (!count && !timeout) return WAIT_TIMEOUT;
391 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
392 timeout, flags & MWMO_ALERTABLE );
395 if (data->current_event) mask = 0; /* don't process nested events */
397 if (process_events( data->display, filter_event, mask )) ret = count - 1;
398 else if (count || timeout)
400 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
401 timeout, flags & MWMO_ALERTABLE );
402 if (ret == count - 1) process_events( data->display, filter_event, mask );
404 else ret = WAIT_TIMEOUT;
409 /***********************************************************************
410 * EVENT_x11_time_to_win32_time
412 * Make our timer and the X timer line up as best we can
413 * Pass 0 to retrieve the current adjustment value (times -1)
415 DWORD EVENT_x11_time_to_win32_time(Time time)
417 static DWORD adjust = 0;
418 DWORD now = GetTickCount();
421 if (! adjust && time != 0)
428 /* If we got an event in the 'future', then our clock is clearly wrong.
429 If we got it more than 10000 ms in the future, then it's most likely
430 that the clock has wrapped. */
433 if (ret > now && ((ret - now) < 10000) && time != 0)
444 /*******************************************************************
445 * can_activate_window
447 * Check if we can activate the specified window.
449 static inline BOOL can_activate_window( HWND hwnd )
451 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
452 if (!(style & WS_VISIBLE)) return FALSE;
453 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
454 if (style & WS_MINIMIZE) return FALSE;
455 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
456 if (hwnd == GetDesktopWindow()) return FALSE;
457 return !(style & WS_DISABLED);
461 /**********************************************************************
464 static void set_focus( Display *display, HWND hwnd, Time time )
468 GUITHREADINFO threadinfo;
470 TRACE( "setting foreground window to %p\n", hwnd );
471 SetForegroundWindow( hwnd );
473 threadinfo.cbSize = sizeof(threadinfo);
474 GetGUIThreadInfo(0, &threadinfo);
475 focus = threadinfo.hwndFocus;
476 if (!focus) focus = threadinfo.hwndActive;
477 if (focus) focus = GetAncestor( focus, GA_ROOT );
478 win = X11DRV_get_whole_window(focus);
482 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
484 XSetInputFocus( display, win, RevertToParent, time );
490 /**********************************************************************
491 * handle_manager_message
493 static void handle_manager_message( HWND hwnd, XClientMessageEvent *event )
495 if (hwnd != GetDesktopWindow()) return;
496 if (systray_atom && event->data.l[1] == systray_atom)
497 change_systray_owner( event->display, event->data.l[2] );
501 /**********************************************************************
502 * handle_wm_protocols
504 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
506 Atom protocol = (Atom)event->data.l[0];
507 Time event_time = (Time)event->data.l[1];
509 if (!protocol) return;
511 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
513 update_user_time( event_time );
515 if (hwnd == GetDesktopWindow())
517 /* The desktop window does not have a close button that we can
518 * pretend to click. Therefore, we simply send it a close command. */
519 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
523 /* Ignore the delete window request if the window has been disabled
524 * and we are in managed mode. This is to disallow applications from
525 * being closed by the window manager while in a modal state.
527 if (IsWindowEnabled(hwnd))
531 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
532 hSysMenu = GetSystemMenu(hwnd, FALSE);
535 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
536 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
539 if (GetActiveWindow() != hwnd)
541 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
542 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
543 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
546 case MA_NOACTIVATEANDEAT:
547 case MA_ACTIVATEANDEAT:
553 SetActiveWindow(hwnd);
556 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
561 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
564 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
566 HWND last_focus = x11drv_thread_data()->last_focus;
568 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
569 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
570 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
572 if (can_activate_window(hwnd))
574 /* simulate a mouse click on the caption to find out
575 * whether the window wants to be activated */
576 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
577 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
578 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
579 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
581 set_focus( event->display, hwnd, event_time );
585 else if (hwnd == GetDesktopWindow())
587 hwnd = GetForegroundWindow();
588 if (!hwnd) hwnd = last_focus;
589 if (!hwnd) hwnd = GetDesktopWindow();
590 set_focus( event->display, hwnd, event_time );
593 /* try to find some other window to give the focus to */
595 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
596 if (!hwnd) hwnd = GetActiveWindow();
597 if (!hwnd) hwnd = last_focus;
598 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
600 else if (protocol == x11drv_atom(_NET_WM_PING))
602 XClientMessageEvent xev;
605 TRACE("NET_WM Ping\n");
607 xev.window = DefaultRootWindow(xev.display);
608 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
610 /* this line is semi-stolen from gtk2 */
611 TRACE("NET_WM Pong\n");
616 static const char * const focus_details[] =
622 "NotifyNonlinearVirtual",
628 /**********************************************************************
631 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
633 XFocusChangeEvent *event = &xev->xfocus;
638 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
640 if (event->detail == NotifyPointer) return;
642 if ((xic = X11DRV_get_ic( hwnd )))
648 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
650 if (!can_activate_window(hwnd))
652 HWND hwnd = GetFocus();
653 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
654 if (!hwnd) hwnd = GetActiveWindow();
655 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
656 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
658 else SetForegroundWindow( hwnd );
662 /**********************************************************************
665 * Note: only top-level windows get FocusOut events.
667 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
669 XFocusChangeEvent *event = &xev->xfocus;
677 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
679 if (event->detail == NotifyPointer) return;
680 if (ximInComposeMode) return;
682 x11drv_thread_data()->last_focus = hwnd;
683 if ((xic = X11DRV_get_ic( hwnd )))
686 XUnsetICFocus( xic );
689 if (hwnd != GetForegroundWindow()) return;
690 if (root_window != DefaultRootWindow(event->display)) return;
691 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
693 /* don't reset the foreground window, if the window which is
694 getting the focus is a Wine window */
697 XGetInputFocus( event->display, &focus_win, &revert );
700 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
707 /* Abey : 6-Oct-99. Check again if the focus out window is the
708 Foreground window, because in most cases the messages sent
709 above must have already changed the foreground window, in which
710 case we don't have to change the foreground window to 0 */
711 if (hwnd == GetForegroundWindow())
713 TRACE( "lost focus, setting fg to desktop\n" );
714 SetForegroundWindow( GetDesktopWindow() );
720 /***********************************************************************
723 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
725 XExposeEvent *event = &xev->xexpose;
727 struct x11drv_win_data *data;
728 int flags = RDW_INVALIDATE | RDW_ERASE;
730 TRACE( "win %p (%lx) %d,%d %dx%d\n",
731 hwnd, event->window, event->x, event->y, event->width, event->height );
733 if (!(data = X11DRV_get_win_data( hwnd ))) return;
735 rect.left = event->x;
737 rect.right = event->x + event->width;
738 rect.bottom = event->y + event->height;
739 if (event->window == data->whole_window)
741 OffsetRect( &rect, data->whole_rect.left - data->client_rect.left,
742 data->whole_rect.top - data->client_rect.top );
746 if (event->window != root_window)
748 if (GetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
749 mirror_rect( &data->client_rect, &rect );
751 SERVER_START_REQ( update_window_zorder )
753 req->window = wine_server_user_handle( hwnd );
754 req->rect.left = rect.left;
755 req->rect.top = rect.top;
756 req->rect.right = rect.right;
757 req->rect.bottom = rect.bottom;
758 wine_server_call( req );
762 flags |= RDW_ALLCHILDREN;
764 else OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
766 RedrawWindow( hwnd, &rect, 0, flags );
770 /**********************************************************************
773 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
775 struct x11drv_win_data *data;
777 if (event->xany.window == clip_window)
782 if (!(data = X11DRV_get_win_data( hwnd ))) return;
783 if (!data->mapped || data->embedded) return;
787 HWND hwndFocus = GetFocus();
788 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
793 /**********************************************************************
796 static void X11DRV_UnmapNotify( HWND hwnd, XEvent *event )
798 if (event->xany.window == clip_window) clipping_window_unmapped();
802 /***********************************************************************
803 * is_net_wm_state_maximized
805 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
809 unsigned long i, count, remaining;
811 if (!data->whole_window) return FALSE;
814 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
815 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
816 &remaining, (unsigned char **)&state ))
818 if (type == XA_ATOM && format == 32)
820 for (i = 0; i < count; i++)
822 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
823 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
834 /***********************************************************************
835 * X11DRV_ReparentNotify
837 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
839 XReparentEvent *event = &xev->xreparent;
840 struct x11drv_win_data *data;
841 HWND parent, old_parent;
844 if (!(data = X11DRV_get_win_data( hwnd ))) return;
845 if (!data->embedded) return;
847 if (data->whole_window)
849 if (event->parent == root_window)
851 TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
853 SendMessageW( hwnd, WM_CLOSE, 0, 0 );
856 data->embedder = event->parent;
859 TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
861 style = GetWindowLongW( hwnd, GWL_STYLE );
862 if (event->parent == root_window)
864 parent = GetDesktopWindow();
865 style = (style & ~WS_CHILD) | WS_POPUP;
869 if (!(parent = create_foreign_window( event->display, event->parent ))) return;
870 style = (style & ~WS_POPUP) | WS_CHILD;
873 ShowWindow( hwnd, SW_HIDE );
874 old_parent = SetParent( hwnd, parent );
875 SetWindowLongW( hwnd, GWL_STYLE, style );
876 SetWindowPos( hwnd, HWND_TOP, event->x, event->y, 0, 0,
877 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS |
878 ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) );
880 /* make old parent destroy itself if it no longer has children */
881 if (old_parent != GetDesktopWindow()) PostMessageW( old_parent, WM_CLOSE, 0, 0 );
885 /***********************************************************************
886 * X11DRV_ConfigureNotify
888 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
890 XConfigureEvent *event = &xev->xconfigure;
891 struct x11drv_win_data *data;
896 int cx, cy, x = event->x, y = event->y;
899 if (!(data = X11DRV_get_win_data( hwnd ))) return;
900 if (!data->mapped || data->iconic) return;
901 if (data->whole_window && !data->managed) return;
902 /* ignore synthetic events on foreign windows */
903 if (event->send_event && !data->whole_window) return;
904 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
906 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
907 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
908 event->serial, data->configure_serial );
914 parent = GetAncestor( hwnd, GA_PARENT );
915 root_coords = event->send_event; /* synthetic events are always in root coords */
917 if (!root_coords && parent == GetDesktopWindow()) /* normal event, map coordinates to the root */
921 XTranslateCoordinates( event->display, event->window, root_window,
922 0, 0, &x, &y, &child );
928 rect.right = x + event->width;
929 rect.bottom = y + event->height;
930 if (root_coords) OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
931 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
932 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
933 event->x, event->y, event->width, event->height );
935 X11DRV_X_to_window_rect( data, &rect );
936 if (root_coords) MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
938 /* Compare what has changed */
942 cx = rect.right - rect.left;
943 cy = rect.bottom - rect.top;
944 flags = SWP_NOACTIVATE | SWP_NOZORDER;
946 if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */
948 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
950 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
951 hwnd, data->window_rect.left, data->window_rect.top, x, y );
953 if ((data->window_rect.right - data->window_rect.left == cx &&
954 data->window_rect.bottom - data->window_rect.top == cy) ||
955 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
957 if (flags & SWP_NOMOVE) /* if nothing changed, don't do anything */
959 TRACE( "Nothing has changed, ignoring event\n" );
965 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
966 hwnd, data->window_rect.right - data->window_rect.left,
967 data->window_rect.bottom - data->window_rect.top, cx, cy );
969 if (is_net_wm_state_maximized( event->display, data ))
971 if (!IsZoomed( data->hwnd ))
973 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
974 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
980 if (IsZoomed( data->hwnd ))
982 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
983 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
988 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
992 /**********************************************************************
993 * X11DRV_GravityNotify
995 static void X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
997 XGravityEvent *event = &xev->xgravity;
998 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1001 if (!data || data->whole_window) return; /* only handle this for foreign windows */
1003 rect.left = event->x;
1004 rect.top = event->y;
1005 rect.right = rect.left + data->whole_rect.right - data->whole_rect.left;
1006 rect.bottom = rect.top + data->whole_rect.bottom - data->whole_rect.top;
1008 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d)\n",
1009 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1010 event->x, event->y );
1012 X11DRV_X_to_window_rect( data, &rect );
1014 if (data->window_rect.left != rect.left || data ->window_rect.top != rect.top)
1015 SetWindowPos( hwnd, 0, rect.left, rect.top, 0, 0,
1016 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1020 /***********************************************************************
1021 * get_window_wm_state
1023 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
1031 int format, ret = -1;
1032 unsigned long count, remaining;
1035 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
1036 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1037 &type, &format, &count, &remaining, (unsigned char **)&state ))
1039 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1043 wine_tsx11_unlock();
1048 /***********************************************************************
1049 * handle_wm_state_notify
1051 * Handle a PropertyNotify for WM_STATE.
1053 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
1054 BOOL update_window )
1058 switch(event->state)
1060 case PropertyDelete:
1061 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1062 data->wm_state = WithdrawnState;
1064 case PropertyNewValue:
1066 int old_state = data->wm_state;
1067 int new_state = get_window_wm_state( event->display, data );
1068 if (new_state != -1 && new_state != data->wm_state)
1070 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1071 data->hwnd, data->whole_window, new_state, old_state );
1072 data->wm_state = new_state;
1073 /* ignore the initial state transition out of withdrawn state */
1074 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1075 if (!old_state) return;
1081 if (!update_window || !data->managed || !data->mapped) return;
1083 style = GetWindowLongW( data->hwnd, GWL_STYLE );
1085 if (data->iconic && data->wm_state == NormalState) /* restore window */
1087 data->iconic = FALSE;
1088 if (is_net_wm_state_maximized( event->display, data ))
1090 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1092 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1093 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1095 else TRACE( "not restoring to max win %p/%lx style %08x\n",
1096 data->hwnd, data->whole_window, style );
1098 else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1100 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1101 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1103 else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1105 else if (!data->iconic && data->wm_state == IconicState)
1107 data->iconic = TRUE;
1108 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1110 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1111 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1113 else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1118 /***********************************************************************
1119 * X11DRV_PropertyNotify
1121 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1123 XPropertyEvent *event = &xev->xproperty;
1124 struct x11drv_win_data *data;
1127 if (!(data = X11DRV_get_win_data( hwnd ))) return;
1129 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1133 /* event filter to wait for a WM_STATE change notification on a window */
1134 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1136 if (event->xany.window != (Window)arg) return 0;
1137 return (event->type == DestroyNotify ||
1138 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1141 /***********************************************************************
1142 * wait_for_withdrawn_state
1144 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1146 DWORD end = GetTickCount() + 2000;
1148 if (!data->managed) return;
1150 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1151 data->hwnd, data->whole_window, set ? "" : "not " );
1153 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1159 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1162 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1163 if (event.type == DestroyNotify) call_event_handler( display, &event );
1166 wine_tsx11_unlock();
1167 handle_wm_state_notify( data, &event.xproperty, FALSE );
1171 wine_tsx11_unlock();
1176 int timeout = end - GetTickCount();
1178 pfd.fd = ConnectionNumber(display);
1179 pfd.events = POLLIN;
1180 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1182 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1187 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1191 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1195 if (!IsWindowEnabled(hQueryWnd)) return 0;
1197 GetWindowRect(hQueryWnd, &tempRect);
1199 if(!PtInRect(&tempRect, *lpPt)) return 0;
1201 if (!IsIconic( hQueryWnd ))
1204 ScreenToClient( hQueryWnd, &pt );
1205 GetClientRect( hQueryWnd, &tempRect );
1207 if (PtInRect( &tempRect, pt))
1209 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1210 if (ret && ret != hQueryWnd)
1212 ret = find_drop_window( ret, lpPt );
1213 if (ret) return ret;
1218 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1220 ScreenToClient(hQueryWnd, lpPt);
1225 /**********************************************************************
1226 * EVENT_DropFromOffix
1228 * don't know if it still works (last Changelog is from 96/11/04)
1230 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1232 struct x11drv_win_data *data;
1233 unsigned long data_length;
1234 unsigned long aux_long;
1235 unsigned char* p_data = NULL;
1239 Window win, w_aux_root, w_aux_child;
1241 win = X11DRV_get_whole_window(hWnd);
1243 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1244 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1245 x += virtual_screen_rect.left;
1246 y += virtual_screen_rect.top;
1247 wine_tsx11_unlock();
1249 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1251 /* find out drop point and drop window */
1252 if( x < 0 || y < 0 ||
1253 x > (data->whole_rect.right - data->whole_rect.left) ||
1254 y > (data->whole_rect.bottom - data->whole_rect.top) )
1256 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1262 POINT pt = { x, y };
1263 HWND hwndDrop = find_drop_window( hWnd, &pt );
1276 if (!bAccept) return;
1279 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1280 x11drv_atom(DndSelection), 0, 65535, FALSE,
1281 AnyPropertyType, &atom_aux, &dummy,
1282 &data_length, &aux_long, &p_data);
1283 wine_tsx11_unlock();
1285 if( !aux_long && p_data) /* don't bother if > 64K */
1287 char *p = (char *)p_data;
1291 while( *p ) /* calculate buffer size */
1293 INT len = GetShortPathNameA( p, NULL, 0 );
1294 if (len) aux_long += len + 1;
1297 if( aux_long && aux_long < 65535 )
1302 aux_long += sizeof(DROPFILES) + 1;
1303 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1304 lpDrop = GlobalLock( hDrop );
1308 lpDrop->pFiles = sizeof(DROPFILES);
1311 lpDrop->fNC = FALSE;
1312 lpDrop->fWide = FALSE;
1313 p_drop = (char *)(lpDrop + 1);
1317 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1318 p_drop += strlen( p_drop ) + 1;
1322 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1327 if( p_data ) XFree(p_data);
1328 wine_tsx11_unlock();
1331 /**********************************************************************
1334 * drop items are separated by \n
1335 * each item is prefixed by its mime type
1337 * event->data.l[3], event->data.l[4] contains drop x,y position
1339 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1341 struct x11drv_win_data *win_data;
1342 unsigned long data_length;
1343 unsigned long aux_long, drop_len = 0;
1344 unsigned char *p_data = NULL; /* property data */
1345 char *p_drop = NULL;
1357 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1360 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1361 x11drv_atom(DndSelection), 0, 65535, FALSE,
1362 AnyPropertyType, &u.atom_aux, &u.i,
1363 &data_length, &aux_long, &p_data);
1364 wine_tsx11_unlock();
1366 WARN("property too large, truncated!\n");
1367 TRACE("urls=%s\n", p_data);
1369 if( !aux_long && p_data) { /* don't bother if > 64K */
1370 /* calculate length */
1372 next = strchr(p, '\n');
1375 if (strncmp(p,"file:",5) == 0 ) {
1376 INT len = GetShortPathNameA( p+5, NULL, 0 );
1377 if (len) drop_len += len + 1;
1382 next = strchr(p, '\n');
1388 if( drop_len && drop_len < 65535 ) {
1390 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1391 &x, &y, &u.i, &u.i, &u.u);
1392 x += virtual_screen_rect.left;
1393 y += virtual_screen_rect.top;
1394 wine_tsx11_unlock();
1396 drop_len += sizeof(DROPFILES) + 1;
1397 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1398 lpDrop = GlobalLock( hDrop );
1400 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1402 lpDrop->pFiles = sizeof(DROPFILES);
1406 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1407 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1408 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1409 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1410 lpDrop->fWide = FALSE;
1411 p_drop = (char*)(lpDrop + 1);
1414 /* create message content */
1417 next = strchr(p, '\n');
1420 if (strncmp(p,"file:",5) == 0 ) {
1421 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1423 TRACE("drop file %s as %s\n", p+5, p_drop);
1426 WARN("can't convert file %s to dos name\n", p+5);
1429 WARN("unknown mime type %s\n", p);
1434 next = strchr(p, '\n');
1441 GlobalUnlock(hDrop);
1442 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1446 if( p_data ) XFree(p_data);
1447 wine_tsx11_unlock();
1452 /**********************************************************************
1453 * handle_xembed_protocol
1455 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1457 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1461 switch (event->data.l[1])
1463 case XEMBED_EMBEDDED_NOTIFY:
1464 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1465 data->embedder = event->data.l[3];
1468 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1469 hwnd, event->window, event->data.l[1], event->data.l[2] );
1475 /**********************************************************************
1476 * handle_dnd_protocol
1478 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1481 int root_x, root_y, child_x, child_y;
1484 /* query window (drag&drop event contains only drag window) */
1486 XQueryPointer( event->display, root_window, &root, &child,
1487 &root_x, &root_y, &child_x, &child_y, &u);
1488 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1489 wine_tsx11_unlock();
1491 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1492 EVENT_DropFromOffiX(hwnd, event);
1493 else if (event->data.l[0] == DndURL)
1494 EVENT_DropURLs(hwnd, event);
1498 struct client_message_handler
1500 int atom; /* protocol atom */
1501 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1504 static const struct client_message_handler client_messages[] =
1506 { XATOM_MANAGER, handle_manager_message },
1507 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1508 { XATOM__XEMBED, handle_xembed_protocol },
1509 { XATOM_DndProtocol, handle_dnd_protocol },
1510 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1511 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1512 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1513 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1517 /**********************************************************************
1518 * X11DRV_ClientMessage
1520 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1522 XClientMessageEvent *event = &xev->xclient;
1527 if (event->format != 32)
1529 WARN( "Don't know how to handle format %d\n", event->format );
1533 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1535 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1537 client_messages[i].handler( hwnd, event );
1541 TRACE( "no handler found for %ld\n", event->message_type );