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>
35 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
36 #include <X11/extensions/XInput2.h>
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
52 /* avoid conflict with field names in included win32 headers */
54 #include "shlobj.h" /* DROPFILES */
57 #include "wine/server.h"
58 #include "wine/debug.h"
60 WINE_DEFAULT_DEBUG_CHANNEL(event);
62 extern BOOL ximInComposeMode;
64 #define DndNotDnd -1 /* OffiX drag&drop */
76 #define DndURL 128 /* KDE drag&drop */
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
92 Bool (*pXGetEventData)( Display *display, XEvent /*XGenericEventCookie*/ *event ) = NULL;
93 void (*pXFreeEventData)( Display *display, XEvent /*XGenericEventCookie*/ *event ) = NULL;
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 );
107 #define MAX_EVENT_HANDLERS 128
109 static x11drv_event_handler handlers[MAX_EVENT_HANDLERS] =
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 */
149 static const char * event_names[MAX_EVENT_HANDLERS] =
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"
160 int xinput2_opcode = 0;
162 /* return the name of an X event */
163 static const char *dbgstr_event( int type )
165 if (type < MAX_EVENT_HANDLERS && event_names[type]) return event_names[type];
166 return wine_dbg_sprintf( "Unknown event %d", type );
169 static inline void get_event_data( XEvent *event )
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;
177 static inline void free_event_data( XEvent *event )
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 );
185 /***********************************************************************
186 * X11DRV_register_event_handler
188 * Register a handler for a given event type.
189 * If already registered, overwrite the previous handler.
191 void X11DRV_register_event_handler( int type, x11drv_event_handler handler, const char *name )
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) );
201 /***********************************************************************
204 static Bool filter_event( Display *display, XEvent *event, char *arg )
206 ULONG_PTR mask = (ULONG_PTR)arg;
208 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
216 return (mask & QS_KEY) != 0;
219 return (mask & QS_MOUSEBUTTON) != 0;
223 return (mask & QS_MOUSEMOVE) != 0;
225 return (mask & QS_PAINT) != 0;
230 case ConfigureNotify:
233 return (mask & QS_POSTMESSAGE) != 0;
235 return (mask & QS_SENDMESSAGE) != 0;
240 enum event_merge_action
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 */
248 /***********************************************************************
249 * merge_raw_motion_events
251 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
252 static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawEvent *next )
257 if (!prev->valuators.mask_len) return MERGE_HANDLE;
258 if (!next->valuators.mask_len) return MERGE_HANDLE;
260 mask = prev->valuators.mask[0] | next->valuators.mask[0];
261 if (mask == next->valuators.mask[0]) /* keep next */
263 for (i = j = k = 0; i < 8; i++)
265 if (XIMaskIsSet( prev->valuators.mask, i ))
266 next->valuators.values[j] += prev->valuators.values[k++];
267 if (XIMaskIsSet( next->valuators.mask, i )) j++;
269 TRACE( "merging duplicate GenericEvent\n" );
270 return MERGE_DISCARD;
272 if (mask == prev->valuators.mask[0]) /* keep prev */
274 for (i = j = k = 0; i < 8; i++)
276 if (XIMaskIsSet( next->valuators.mask, i ))
277 prev->valuators.values[j] += next->valuators.values[k++];
278 if (XIMaskIsSet( prev->valuators.mask, i )) j++;
280 TRACE( "merging duplicate GenericEvent\n" );
283 /* can't merge events with disjoint masks */
288 /***********************************************************************
291 * Try to merge 2 consecutive events.
293 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
297 case ConfigureNotify:
300 case ConfigureNotify:
301 if (prev->xany.window == next->xany.window)
303 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
304 return MERGE_DISCARD;
313 if (prev->xany.window == next->xany.window && next->type == MotionNotify)
315 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
316 return MERGE_DISCARD;
319 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
321 if (prev->xcookie.extension != xinput2_opcode) break;
322 if (prev->xcookie.evtype != XI_RawMotion) break;
326 if (next->xany.window == x11drv_thread_data()->clip_window)
328 TRACE( "ignoring MotionNotify for clip window\n" );
333 if (next->xcookie.extension != xinput2_opcode) break;
334 if (next->xcookie.evtype != XI_RawMotion) break;
335 return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data );
344 /***********************************************************************
347 static inline void call_event_handler( Display *display, XEvent *event )
351 struct x11drv_thread_data *thread_data;
353 if (!handlers[event->type])
355 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
356 return; /* no handler, ignore it */
359 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
360 hwnd = 0; /* not for a registered window */
361 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
363 TRACE( "%lu %s for hwnd/window %p/%lx\n",
364 event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window );
366 thread_data = x11drv_thread_data();
367 prev = thread_data->current_event;
368 thread_data->current_event = event;
369 handlers[event->type]( hwnd, event );
370 thread_data->current_event = prev;
375 /***********************************************************************
378 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
380 XEvent event, prev_event;
382 enum event_merge_action action = MERGE_DISCARD;
386 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
389 if (XFilterEvent( &event, None ))
392 * SCIM on linux filters key events strangely. It does not filter the
393 * KeyPress events for these keys however it does filter the
394 * KeyRelease events. This causes wine to become very confused as
395 * to the keyboard state.
397 * We need to let those KeyRelease events be processed so that the
398 * keyboard state is correct.
400 if (event.type == KeyRelease)
403 XKeyEvent *keyevent = &event.xkey;
405 XLookupString(keyevent, NULL, 0, &keysym, NULL);
406 if (!(keysym == XK_Shift_L ||
407 keysym == XK_Shift_R ||
408 keysym == XK_Control_L ||
409 keysym == XK_Control_R ||
410 keysym == XK_Alt_R ||
411 keysym == XK_Alt_L ||
412 keysym == XK_Meta_R ||
413 keysym == XK_Meta_L))
414 continue; /* not a key we care about, ignore it */
417 continue; /* filtered, ignore it */
419 get_event_data( &event );
420 if (prev_event.type) action = merge_events( &prev_event, &event );
423 case MERGE_HANDLE: /* handle prev, keep new */
424 call_event_handler( display, &prev_event );
426 case MERGE_DISCARD: /* discard prev, keep new */
427 free_event_data( &prev_event );
430 case MERGE_KEEP: /* handle new, keep prev for future merging */
431 call_event_handler( display, &event );
433 case MERGE_IGNORE: /* ignore new, keep prev for future merging */
434 free_event_data( &event );
438 if (prev_event.type) call_event_handler( display, &prev_event );
439 free_event_data( &prev_event );
440 XFlush( gdi_display );
442 if (count) TRACE( "processed %d events\n", count );
447 /***********************************************************************
448 * MsgWaitForMultipleObjectsEx (X11DRV.@)
450 DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
451 DWORD timeout, DWORD mask, DWORD flags )
454 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
458 if (!count && !timeout) return WAIT_TIMEOUT;
459 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
460 timeout, flags & MWMO_ALERTABLE );
463 if (data->current_event) mask = 0; /* don't process nested events */
465 if (process_events( data->display, filter_event, mask )) ret = count - 1;
466 else if (count || timeout)
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 );
472 else ret = WAIT_TIMEOUT;
477 /***********************************************************************
478 * EVENT_x11_time_to_win32_time
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)
483 DWORD EVENT_x11_time_to_win32_time(Time time)
485 static DWORD adjust = 0;
486 DWORD now = GetTickCount();
489 if (! adjust && time != 0)
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. */
501 if (ret > now && ((ret - now) < 10000) && time != 0)
512 /*******************************************************************
513 * can_activate_window
515 * Check if we can activate the specified window.
517 static inline BOOL can_activate_window( HWND hwnd )
519 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
520 if (!(style & WS_VISIBLE)) return FALSE;
521 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
522 if (style & WS_MINIMIZE) return FALSE;
523 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
524 if (hwnd == GetDesktopWindow()) return FALSE;
525 return !(style & WS_DISABLED);
529 /**********************************************************************
532 static void set_focus( Display *display, HWND hwnd, Time time )
536 GUITHREADINFO threadinfo;
538 TRACE( "setting foreground window to %p\n", hwnd );
539 SetForegroundWindow( hwnd );
541 threadinfo.cbSize = sizeof(threadinfo);
542 GetGUIThreadInfo(0, &threadinfo);
543 focus = threadinfo.hwndFocus;
544 if (!focus) focus = threadinfo.hwndActive;
545 if (focus) focus = GetAncestor( focus, GA_ROOT );
546 win = X11DRV_get_whole_window(focus);
550 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
552 XSetInputFocus( display, win, RevertToParent, time );
558 /**********************************************************************
559 * handle_manager_message
561 static void handle_manager_message( HWND hwnd, XClientMessageEvent *event )
563 if (hwnd != GetDesktopWindow()) return;
564 if (systray_atom && event->data.l[1] == systray_atom)
565 change_systray_owner( event->display, event->data.l[2] );
569 /**********************************************************************
570 * handle_wm_protocols
572 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
574 Atom protocol = (Atom)event->data.l[0];
575 Time event_time = (Time)event->data.l[1];
577 if (!protocol) return;
579 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
581 update_user_time( event_time );
583 if (hwnd == GetDesktopWindow())
585 /* The desktop window does not have a close button that we can
586 * pretend to click. Therefore, we simply send it a close command. */
587 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
591 /* Ignore the delete window request if the window has been disabled
592 * and we are in managed mode. This is to disallow applications from
593 * being closed by the window manager while in a modal state.
595 if (IsWindowEnabled(hwnd))
599 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
600 hSysMenu = GetSystemMenu(hwnd, FALSE);
603 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
604 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
607 if (GetActiveWindow() != hwnd)
609 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
610 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
611 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
614 case MA_NOACTIVATEANDEAT:
615 case MA_ACTIVATEANDEAT:
621 SetActiveWindow(hwnd);
624 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
629 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
632 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
634 HWND last_focus = x11drv_thread_data()->last_focus;
636 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
637 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
638 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
640 if (can_activate_window(hwnd))
642 /* simulate a mouse click on the caption to find out
643 * whether the window wants to be activated */
644 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
645 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
646 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
647 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
649 set_focus( event->display, hwnd, event_time );
653 else if (hwnd == GetDesktopWindow())
655 hwnd = GetForegroundWindow();
656 if (!hwnd) hwnd = last_focus;
657 if (!hwnd) hwnd = GetDesktopWindow();
658 set_focus( event->display, hwnd, event_time );
661 /* try to find some other window to give the focus to */
663 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
664 if (!hwnd) hwnd = GetActiveWindow();
665 if (!hwnd) hwnd = last_focus;
666 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
668 else if (protocol == x11drv_atom(_NET_WM_PING))
670 XClientMessageEvent xev;
673 TRACE("NET_WM Ping\n");
675 xev.window = DefaultRootWindow(xev.display);
676 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
678 /* this line is semi-stolen from gtk2 */
679 TRACE("NET_WM Pong\n");
684 static const char * const focus_details[] =
690 "NotifyNonlinearVirtual",
696 /**********************************************************************
699 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
701 XFocusChangeEvent *event = &xev->xfocus;
706 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
708 if (event->detail == NotifyPointer) return;
710 if ((xic = X11DRV_get_ic( hwnd )))
716 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
718 if (!can_activate_window(hwnd))
720 HWND hwnd = GetFocus();
721 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
722 if (!hwnd) hwnd = GetActiveWindow();
723 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
724 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
726 else SetForegroundWindow( hwnd );
730 /**********************************************************************
733 * Note: only top-level windows get FocusOut events.
735 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
737 XFocusChangeEvent *event = &xev->xfocus;
745 if (event->detail == NotifyPointer && event->window == x11drv_thread_data()->clip_window)
747 TRACE( "clip window lost focus\n" );
748 ungrab_clipping_window();
749 ClipCursor( NULL ); /* make sure the clip rectangle is reset too */
754 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
756 if (event->detail == NotifyPointer) return;
757 if (ximInComposeMode) return;
759 x11drv_thread_data()->last_focus = hwnd;
760 if ((xic = X11DRV_get_ic( hwnd )))
763 XUnsetICFocus( xic );
766 if (hwnd != GetForegroundWindow()) return;
767 if (root_window != DefaultRootWindow(event->display)) return;
768 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
770 /* don't reset the foreground window, if the window which is
771 getting the focus is a Wine window */
774 XGetInputFocus( event->display, &focus_win, &revert );
777 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
784 /* Abey : 6-Oct-99. Check again if the focus out window is the
785 Foreground window, because in most cases the messages sent
786 above must have already changed the foreground window, in which
787 case we don't have to change the foreground window to 0 */
788 if (hwnd == GetForegroundWindow())
790 TRACE( "lost focus, setting fg to desktop\n" );
791 SetForegroundWindow( GetDesktopWindow() );
797 /***********************************************************************
800 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
802 XExposeEvent *event = &xev->xexpose;
804 struct x11drv_win_data *data;
805 int flags = RDW_INVALIDATE | RDW_ERASE;
807 TRACE( "win %p (%lx) %d,%d %dx%d\n",
808 hwnd, event->window, event->x, event->y, event->width, event->height );
810 if (!(data = X11DRV_get_win_data( hwnd ))) return;
812 rect.left = event->x;
814 rect.right = event->x + event->width;
815 rect.bottom = event->y + event->height;
816 if (event->window == data->whole_window)
818 OffsetRect( &rect, data->whole_rect.left - data->client_rect.left,
819 data->whole_rect.top - data->client_rect.top );
823 if (event->window != root_window)
825 if (GetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
826 mirror_rect( &data->client_rect, &rect );
828 SERVER_START_REQ( update_window_zorder )
830 req->window = wine_server_user_handle( hwnd );
831 req->rect.left = rect.left;
832 req->rect.top = rect.top;
833 req->rect.right = rect.right;
834 req->rect.bottom = rect.bottom;
835 wine_server_call( req );
839 flags |= RDW_ALLCHILDREN;
841 else OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
843 RedrawWindow( hwnd, &rect, 0, flags );
847 /**********************************************************************
850 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
852 struct x11drv_win_data *data;
854 if (event->xany.window == x11drv_thread_data()->clip_window)
859 if (!(data = X11DRV_get_win_data( hwnd ))) return;
860 if (!data->mapped || data->embedded) return;
864 HWND hwndFocus = GetFocus();
865 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
870 /**********************************************************************
873 static void X11DRV_UnmapNotify( HWND hwnd, XEvent *event )
875 if (event->xany.window == x11drv_thread_data()->clip_window) clipping_cursor = 0;
879 /***********************************************************************
880 * is_net_wm_state_maximized
882 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
886 unsigned long i, count, remaining;
888 if (!data->whole_window) return FALSE;
891 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
892 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
893 &remaining, (unsigned char **)&state ))
895 if (type == XA_ATOM && format == 32)
897 for (i = 0; i < count; i++)
899 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
900 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
911 /***********************************************************************
912 * X11DRV_ReparentNotify
914 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
916 XReparentEvent *event = &xev->xreparent;
917 struct x11drv_win_data *data;
918 HWND parent, old_parent;
921 if (!(data = X11DRV_get_win_data( hwnd ))) return;
922 if (!data->embedded) return;
924 if (data->whole_window)
926 if (event->parent == root_window)
928 TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
930 SendMessageW( hwnd, WM_CLOSE, 0, 0 );
933 data->embedder = event->parent;
936 TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
938 style = GetWindowLongW( hwnd, GWL_STYLE );
939 if (event->parent == root_window)
941 parent = GetDesktopWindow();
942 style = (style & ~WS_CHILD) | WS_POPUP;
946 if (!(parent = create_foreign_window( event->display, event->parent ))) return;
947 style = (style & ~WS_POPUP) | WS_CHILD;
950 ShowWindow( hwnd, SW_HIDE );
951 old_parent = SetParent( hwnd, parent );
952 SetWindowLongW( hwnd, GWL_STYLE, style );
953 SetWindowPos( hwnd, HWND_TOP, event->x, event->y, 0, 0,
954 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS |
955 ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) );
957 /* make old parent destroy itself if it no longer has children */
958 if (old_parent != GetDesktopWindow()) PostMessageW( old_parent, WM_CLOSE, 0, 0 );
962 /***********************************************************************
963 * X11DRV_ConfigureNotify
965 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
967 XConfigureEvent *event = &xev->xconfigure;
968 struct x11drv_win_data *data;
973 int cx, cy, x = event->x, y = event->y;
976 if (!(data = X11DRV_get_win_data( hwnd ))) return;
977 if (!data->mapped || data->iconic) return;
978 if (data->whole_window && !data->managed) return;
979 /* ignore synthetic events on foreign windows */
980 if (event->send_event && !data->whole_window) return;
981 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
983 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
984 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
985 event->serial, data->configure_serial );
991 parent = GetAncestor( hwnd, GA_PARENT );
992 root_coords = event->send_event; /* synthetic events are always in root coords */
994 if (!root_coords && parent == GetDesktopWindow()) /* normal event, map coordinates to the root */
998 XTranslateCoordinates( event->display, event->window, root_window,
999 0, 0, &x, &y, &child );
1000 wine_tsx11_unlock();
1005 rect.right = x + event->width;
1006 rect.bottom = y + event->height;
1007 if (root_coords) OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
1008 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
1009 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1010 event->x, event->y, event->width, event->height );
1012 X11DRV_X_to_window_rect( data, &rect );
1013 if (root_coords) MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
1015 /* Compare what has changed */
1019 cx = rect.right - rect.left;
1020 cy = rect.bottom - rect.top;
1021 flags = SWP_NOACTIVATE | SWP_NOZORDER;
1023 if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */
1025 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
1027 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
1028 hwnd, data->window_rect.left, data->window_rect.top, x, y );
1030 if ((data->window_rect.right - data->window_rect.left == cx &&
1031 data->window_rect.bottom - data->window_rect.top == cy) ||
1032 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
1034 if (flags & SWP_NOMOVE) /* if nothing changed, don't do anything */
1036 TRACE( "Nothing has changed, ignoring event\n" );
1039 flags |= SWP_NOSIZE;
1042 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
1043 hwnd, data->window_rect.right - data->window_rect.left,
1044 data->window_rect.bottom - data->window_rect.top, cx, cy );
1046 if (is_net_wm_state_maximized( event->display, data ))
1048 if (!IsZoomed( data->hwnd ))
1050 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
1051 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1057 if (IsZoomed( data->hwnd ))
1059 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
1060 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1065 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
1069 /**********************************************************************
1070 * X11DRV_GravityNotify
1072 static void X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
1074 XGravityEvent *event = &xev->xgravity;
1075 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1078 if (!data || data->whole_window) return; /* only handle this for foreign windows */
1080 rect.left = event->x;
1081 rect.top = event->y;
1082 rect.right = rect.left + data->whole_rect.right - data->whole_rect.left;
1083 rect.bottom = rect.top + data->whole_rect.bottom - data->whole_rect.top;
1085 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d)\n",
1086 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1087 event->x, event->y );
1089 X11DRV_X_to_window_rect( data, &rect );
1091 if (data->window_rect.left != rect.left || data ->window_rect.top != rect.top)
1092 SetWindowPos( hwnd, 0, rect.left, rect.top, 0, 0,
1093 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1097 /***********************************************************************
1098 * get_window_wm_state
1100 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
1108 int format, ret = -1;
1109 unsigned long count, remaining;
1112 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
1113 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1114 &type, &format, &count, &remaining, (unsigned char **)&state ))
1116 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1120 wine_tsx11_unlock();
1125 /***********************************************************************
1126 * handle_wm_state_notify
1128 * Handle a PropertyNotify for WM_STATE.
1130 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
1131 BOOL update_window )
1135 switch(event->state)
1137 case PropertyDelete:
1138 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1139 data->wm_state = WithdrawnState;
1141 case PropertyNewValue:
1143 int old_state = data->wm_state;
1144 int new_state = get_window_wm_state( event->display, data );
1145 if (new_state != -1 && new_state != data->wm_state)
1147 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1148 data->hwnd, data->whole_window, new_state, old_state );
1149 data->wm_state = new_state;
1150 /* ignore the initial state transition out of withdrawn state */
1151 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1152 if (!old_state) return;
1158 if (!update_window || !data->managed || !data->mapped) return;
1160 style = GetWindowLongW( data->hwnd, GWL_STYLE );
1162 if (data->iconic && data->wm_state == NormalState) /* restore window */
1164 data->iconic = FALSE;
1165 if (is_net_wm_state_maximized( event->display, data ))
1167 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1169 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1170 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1172 else TRACE( "not restoring to max win %p/%lx style %08x\n",
1173 data->hwnd, data->whole_window, style );
1175 else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1177 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1178 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1180 else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1182 else if (!data->iconic && data->wm_state == IconicState)
1184 data->iconic = TRUE;
1185 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1187 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1188 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1190 else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1195 /***********************************************************************
1196 * X11DRV_PropertyNotify
1198 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1200 XPropertyEvent *event = &xev->xproperty;
1201 struct x11drv_win_data *data;
1204 if (!(data = X11DRV_get_win_data( hwnd ))) return;
1206 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1210 /* event filter to wait for a WM_STATE change notification on a window */
1211 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1213 if (event->xany.window != (Window)arg) return 0;
1214 return (event->type == DestroyNotify ||
1215 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1218 /***********************************************************************
1219 * wait_for_withdrawn_state
1221 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1223 DWORD end = GetTickCount() + 2000;
1225 if (!data->managed) return;
1227 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1228 data->hwnd, data->whole_window, set ? "" : "not " );
1230 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1236 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1239 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1240 if (event.type == DestroyNotify) call_event_handler( display, &event );
1243 wine_tsx11_unlock();
1244 handle_wm_state_notify( data, &event.xproperty, FALSE );
1248 wine_tsx11_unlock();
1253 int timeout = end - GetTickCount();
1255 pfd.fd = ConnectionNumber(display);
1256 pfd.events = POLLIN;
1257 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1259 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1264 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1268 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1272 if (!IsWindowEnabled(hQueryWnd)) return 0;
1274 GetWindowRect(hQueryWnd, &tempRect);
1276 if(!PtInRect(&tempRect, *lpPt)) return 0;
1278 if (!IsIconic( hQueryWnd ))
1281 ScreenToClient( hQueryWnd, &pt );
1282 GetClientRect( hQueryWnd, &tempRect );
1284 if (PtInRect( &tempRect, pt))
1286 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1287 if (ret && ret != hQueryWnd)
1289 ret = find_drop_window( ret, lpPt );
1290 if (ret) return ret;
1295 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1297 ScreenToClient(hQueryWnd, lpPt);
1302 /**********************************************************************
1303 * EVENT_DropFromOffix
1305 * don't know if it still works (last Changelog is from 96/11/04)
1307 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1309 struct x11drv_win_data *data;
1310 unsigned long data_length;
1311 unsigned long aux_long;
1312 unsigned char* p_data = NULL;
1316 Window win, w_aux_root, w_aux_child;
1318 win = X11DRV_get_whole_window(hWnd);
1320 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1321 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1322 x += virtual_screen_rect.left;
1323 y += virtual_screen_rect.top;
1324 wine_tsx11_unlock();
1326 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1328 /* find out drop point and drop window */
1329 if( x < 0 || y < 0 ||
1330 x > (data->whole_rect.right - data->whole_rect.left) ||
1331 y > (data->whole_rect.bottom - data->whole_rect.top) )
1333 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1339 POINT pt = { x, y };
1340 HWND hwndDrop = find_drop_window( hWnd, &pt );
1353 if (!bAccept) return;
1356 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1357 x11drv_atom(DndSelection), 0, 65535, FALSE,
1358 AnyPropertyType, &atom_aux, &dummy,
1359 &data_length, &aux_long, &p_data);
1360 wine_tsx11_unlock();
1362 if( !aux_long && p_data) /* don't bother if > 64K */
1364 char *p = (char *)p_data;
1368 while( *p ) /* calculate buffer size */
1370 INT len = GetShortPathNameA( p, NULL, 0 );
1371 if (len) aux_long += len + 1;
1374 if( aux_long && aux_long < 65535 )
1379 aux_long += sizeof(DROPFILES) + 1;
1380 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1381 lpDrop = GlobalLock( hDrop );
1385 lpDrop->pFiles = sizeof(DROPFILES);
1388 lpDrop->fNC = FALSE;
1389 lpDrop->fWide = FALSE;
1390 p_drop = (char *)(lpDrop + 1);
1394 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1395 p_drop += strlen( p_drop ) + 1;
1399 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1404 if( p_data ) XFree(p_data);
1405 wine_tsx11_unlock();
1408 /**********************************************************************
1411 * drop items are separated by \n
1412 * each item is prefixed by its mime type
1414 * event->data.l[3], event->data.l[4] contains drop x,y position
1416 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1418 struct x11drv_win_data *win_data;
1419 unsigned long data_length;
1420 unsigned long aux_long, drop_len = 0;
1421 unsigned char *p_data = NULL; /* property data */
1422 char *p_drop = NULL;
1434 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1437 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1438 x11drv_atom(DndSelection), 0, 65535, FALSE,
1439 AnyPropertyType, &u.atom_aux, &u.i,
1440 &data_length, &aux_long, &p_data);
1441 wine_tsx11_unlock();
1443 WARN("property too large, truncated!\n");
1444 TRACE("urls=%s\n", p_data);
1446 if( !aux_long && p_data) { /* don't bother if > 64K */
1447 /* calculate length */
1449 next = strchr(p, '\n');
1452 if (strncmp(p,"file:",5) == 0 ) {
1453 INT len = GetShortPathNameA( p+5, NULL, 0 );
1454 if (len) drop_len += len + 1;
1459 next = strchr(p, '\n');
1465 if( drop_len && drop_len < 65535 ) {
1467 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1468 &x, &y, &u.i, &u.i, &u.u);
1469 x += virtual_screen_rect.left;
1470 y += virtual_screen_rect.top;
1471 wine_tsx11_unlock();
1473 drop_len += sizeof(DROPFILES) + 1;
1474 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1475 lpDrop = GlobalLock( hDrop );
1477 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1479 lpDrop->pFiles = sizeof(DROPFILES);
1483 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1484 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1485 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1486 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1487 lpDrop->fWide = FALSE;
1488 p_drop = (char*)(lpDrop + 1);
1491 /* create message content */
1494 next = strchr(p, '\n');
1497 if (strncmp(p,"file:",5) == 0 ) {
1498 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1500 TRACE("drop file %s as %s\n", p+5, p_drop);
1503 WARN("can't convert file %s to dos name\n", p+5);
1506 WARN("unknown mime type %s\n", p);
1511 next = strchr(p, '\n');
1518 GlobalUnlock(hDrop);
1519 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1523 if( p_data ) XFree(p_data);
1524 wine_tsx11_unlock();
1529 /**********************************************************************
1530 * handle_xembed_protocol
1532 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1534 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1538 switch (event->data.l[1])
1540 case XEMBED_EMBEDDED_NOTIFY:
1541 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1542 data->embedder = event->data.l[3];
1545 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1546 hwnd, event->window, event->data.l[1], event->data.l[2] );
1552 /**********************************************************************
1553 * handle_dnd_protocol
1555 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1558 int root_x, root_y, child_x, child_y;
1561 /* query window (drag&drop event contains only drag window) */
1563 XQueryPointer( event->display, root_window, &root, &child,
1564 &root_x, &root_y, &child_x, &child_y, &u);
1565 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1566 wine_tsx11_unlock();
1568 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1569 EVENT_DropFromOffiX(hwnd, event);
1570 else if (event->data.l[0] == DndURL)
1571 EVENT_DropURLs(hwnd, event);
1575 struct client_message_handler
1577 int atom; /* protocol atom */
1578 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1581 static const struct client_message_handler client_messages[] =
1583 { XATOM_MANAGER, handle_manager_message },
1584 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1585 { XATOM__XEMBED, handle_xembed_protocol },
1586 { XATOM_DndProtocol, handle_dnd_protocol },
1587 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1588 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1589 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1590 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1594 /**********************************************************************
1595 * X11DRV_ClientMessage
1597 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1599 XClientMessageEvent *event = &xev->xclient;
1604 if (event->format != 32)
1606 WARN( "Don't know how to handle format %d\n", event->format );
1610 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1612 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1614 client_messages[i].handler( hwnd, event );
1618 TRACE( "no handler found for %ld\n", event->message_type );