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|QS_HOTKEY)) != 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
322 struct x11drv_thread_data *thread_data = x11drv_thread_data();
323 if (prev->xcookie.extension != xinput2_opcode) break;
324 if (prev->xcookie.evtype != XI_RawMotion) break;
325 if (thread_data->warp_serial) break;
329 if (next->xany.window == thread_data->clip_window &&
330 next->xmotion.time - thread_data->last_motion_notify < 1000)
332 TRACE( "ignoring MotionNotify for clip window\n" );
337 if (next->xcookie.extension != xinput2_opcode) break;
338 if (next->xcookie.evtype != XI_RawMotion) break;
339 return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data );
349 /***********************************************************************
352 static inline void call_event_handler( Display *display, XEvent *event )
356 struct x11drv_thread_data *thread_data;
358 if (!handlers[event->type])
360 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
361 return; /* no handler, ignore it */
364 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
365 hwnd = 0; /* not for a registered window */
366 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
368 TRACE( "%lu %s for hwnd/window %p/%lx\n",
369 event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window );
371 thread_data = x11drv_thread_data();
372 prev = thread_data->current_event;
373 thread_data->current_event = event;
374 handlers[event->type]( hwnd, event );
375 thread_data->current_event = prev;
380 /***********************************************************************
383 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
385 XEvent event, prev_event;
387 enum event_merge_action action = MERGE_DISCARD;
391 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
394 if (XFilterEvent( &event, None ))
397 * SCIM on linux filters key events strangely. It does not filter the
398 * KeyPress events for these keys however it does filter the
399 * KeyRelease events. This causes wine to become very confused as
400 * to the keyboard state.
402 * We need to let those KeyRelease events be processed so that the
403 * keyboard state is correct.
405 if (event.type == KeyRelease)
408 XKeyEvent *keyevent = &event.xkey;
410 XLookupString(keyevent, NULL, 0, &keysym, NULL);
411 if (!(keysym == XK_Shift_L ||
412 keysym == XK_Shift_R ||
413 keysym == XK_Control_L ||
414 keysym == XK_Control_R ||
415 keysym == XK_Alt_R ||
416 keysym == XK_Alt_L ||
417 keysym == XK_Meta_R ||
418 keysym == XK_Meta_L))
419 continue; /* not a key we care about, ignore it */
422 continue; /* filtered, ignore it */
424 get_event_data( &event );
425 if (prev_event.type) action = merge_events( &prev_event, &event );
428 case MERGE_HANDLE: /* handle prev, keep new */
429 call_event_handler( display, &prev_event );
431 case MERGE_DISCARD: /* discard prev, keep new */
432 free_event_data( &prev_event );
435 case MERGE_KEEP: /* handle new, keep prev for future merging */
436 call_event_handler( display, &event );
438 case MERGE_IGNORE: /* ignore new, keep prev for future merging */
439 free_event_data( &event );
443 if (prev_event.type) call_event_handler( display, &prev_event );
444 free_event_data( &prev_event );
445 XFlush( gdi_display );
447 if (count) TRACE( "processed %d events\n", count );
452 /***********************************************************************
453 * MsgWaitForMultipleObjectsEx (X11DRV.@)
455 DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
456 DWORD timeout, DWORD mask, DWORD flags )
459 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
463 if (!count && !timeout) return WAIT_TIMEOUT;
464 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
465 timeout, flags & MWMO_ALERTABLE );
468 if (data->current_event) mask = 0; /* don't process nested events */
470 if (process_events( data->display, filter_event, mask )) ret = count - 1;
471 else if (count || timeout)
473 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
474 timeout, flags & MWMO_ALERTABLE );
475 if (ret == count - 1) process_events( data->display, filter_event, mask );
477 else ret = WAIT_TIMEOUT;
482 /***********************************************************************
483 * EVENT_x11_time_to_win32_time
485 * Make our timer and the X timer line up as best we can
486 * Pass 0 to retrieve the current adjustment value (times -1)
488 DWORD EVENT_x11_time_to_win32_time(Time time)
490 static DWORD adjust = 0;
491 DWORD now = GetTickCount();
494 if (! adjust && time != 0)
501 /* If we got an event in the 'future', then our clock is clearly wrong.
502 If we got it more than 10000 ms in the future, then it's most likely
503 that the clock has wrapped. */
506 if (ret > now && ((ret - now) < 10000) && time != 0)
517 /*******************************************************************
518 * can_activate_window
520 * Check if we can activate the specified window.
522 static inline BOOL can_activate_window( HWND hwnd )
524 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
525 if (!(style & WS_VISIBLE)) return FALSE;
526 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
527 if (style & WS_MINIMIZE) return FALSE;
528 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
529 if (hwnd == GetDesktopWindow()) return FALSE;
530 return !(style & WS_DISABLED);
534 /**********************************************************************
537 static void set_focus( Display *display, HWND hwnd, Time time )
541 GUITHREADINFO threadinfo;
543 TRACE( "setting foreground window to %p\n", hwnd );
544 SetForegroundWindow( hwnd );
546 threadinfo.cbSize = sizeof(threadinfo);
547 GetGUIThreadInfo(0, &threadinfo);
548 focus = threadinfo.hwndFocus;
549 if (!focus) focus = threadinfo.hwndActive;
550 if (focus) focus = GetAncestor( focus, GA_ROOT );
551 win = X11DRV_get_whole_window(focus);
555 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
557 XSetInputFocus( display, win, RevertToParent, time );
563 /**********************************************************************
564 * handle_manager_message
566 static void handle_manager_message( HWND hwnd, XClientMessageEvent *event )
568 if (hwnd != GetDesktopWindow()) return;
569 if (systray_atom && event->data.l[1] == systray_atom)
570 change_systray_owner( event->display, event->data.l[2] );
574 /**********************************************************************
575 * handle_wm_protocols
577 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
579 Atom protocol = (Atom)event->data.l[0];
580 Time event_time = (Time)event->data.l[1];
582 if (!protocol) return;
584 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
586 update_user_time( event_time );
588 if (hwnd == GetDesktopWindow())
590 /* The desktop window does not have a close button that we can
591 * pretend to click. Therefore, we simply send it a close command. */
592 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
596 /* Ignore the delete window request if the window has been disabled
597 * and we are in managed mode. This is to disallow applications from
598 * being closed by the window manager while in a modal state.
600 if (IsWindowEnabled(hwnd))
604 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
605 hSysMenu = GetSystemMenu(hwnd, FALSE);
608 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
609 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
612 if (GetActiveWindow() != hwnd)
614 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
615 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
616 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
619 case MA_NOACTIVATEANDEAT:
620 case MA_ACTIVATEANDEAT:
626 SetActiveWindow(hwnd);
629 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
634 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
637 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
639 HWND last_focus = x11drv_thread_data()->last_focus;
641 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
642 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
643 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
645 if (can_activate_window(hwnd))
647 /* simulate a mouse click on the caption to find out
648 * whether the window wants to be activated */
649 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
650 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
651 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
652 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
654 set_focus( event->display, hwnd, event_time );
658 else if (hwnd == GetDesktopWindow())
660 hwnd = GetForegroundWindow();
661 if (!hwnd) hwnd = last_focus;
662 if (!hwnd) hwnd = GetDesktopWindow();
663 set_focus( event->display, hwnd, event_time );
666 /* try to find some other window to give the focus to */
668 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
669 if (!hwnd) hwnd = GetActiveWindow();
670 if (!hwnd) hwnd = last_focus;
671 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
673 else if (protocol == x11drv_atom(_NET_WM_PING))
675 XClientMessageEvent xev;
678 TRACE("NET_WM Ping\n");
680 xev.window = DefaultRootWindow(xev.display);
681 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
683 /* this line is semi-stolen from gtk2 */
684 TRACE("NET_WM Pong\n");
689 static const char * const focus_details[] =
695 "NotifyNonlinearVirtual",
701 /**********************************************************************
704 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
706 XFocusChangeEvent *event = &xev->xfocus;
711 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
713 if (event->detail == NotifyPointer) return;
714 if (hwnd == GetDesktopWindow()) return;
716 if ((xic = X11DRV_get_ic( hwnd )))
724 if (hwnd == GetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE );
728 if (!can_activate_window(hwnd))
730 HWND hwnd = GetFocus();
731 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
732 if (!hwnd) hwnd = GetActiveWindow();
733 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
734 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
736 else SetForegroundWindow( hwnd );
740 /**********************************************************************
743 * Note: only top-level windows get FocusOut events.
745 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
747 XFocusChangeEvent *event = &xev->xfocus;
753 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
755 if (event->detail == NotifyPointer)
757 if (!hwnd && event->window == x11drv_thread_data()->clip_window) reset_clipping_window();
760 if (ximInComposeMode) return;
762 x11drv_thread_data()->last_focus = hwnd;
763 if ((xic = X11DRV_get_ic( hwnd )))
766 XUnsetICFocus( xic );
769 if (root_window != DefaultRootWindow(event->display))
771 if (hwnd == GetDesktopWindow()) reset_clipping_window();
774 if (hwnd != GetForegroundWindow()) return;
775 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
777 /* don't reset the foreground window, if the window which is
778 getting the focus is a Wine window */
781 XGetInputFocus( event->display, &focus_win, &revert );
784 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
791 /* Abey : 6-Oct-99. Check again if the focus out window is the
792 Foreground window, because in most cases the messages sent
793 above must have already changed the foreground window, in which
794 case we don't have to change the foreground window to 0 */
795 if (hwnd == GetForegroundWindow())
797 TRACE( "lost focus, setting fg to desktop\n" );
798 SetForegroundWindow( GetDesktopWindow() );
804 /***********************************************************************
807 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
809 XExposeEvent *event = &xev->xexpose;
811 struct x11drv_win_data *data;
812 int flags = RDW_INVALIDATE | RDW_ERASE;
814 TRACE( "win %p (%lx) %d,%d %dx%d\n",
815 hwnd, event->window, event->x, event->y, event->width, event->height );
817 if (!(data = X11DRV_get_win_data( hwnd ))) return;
819 rect.left = event->x;
821 rect.right = event->x + event->width;
822 rect.bottom = event->y + event->height;
823 if (event->window == data->whole_window)
825 OffsetRect( &rect, data->whole_rect.left - data->client_rect.left,
826 data->whole_rect.top - data->client_rect.top );
830 if (event->window != root_window)
832 if (GetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
833 mirror_rect( &data->client_rect, &rect );
835 SERVER_START_REQ( update_window_zorder )
837 req->window = wine_server_user_handle( hwnd );
838 req->rect.left = rect.left;
839 req->rect.top = rect.top;
840 req->rect.right = rect.right;
841 req->rect.bottom = rect.bottom;
842 wine_server_call( req );
846 flags |= RDW_ALLCHILDREN;
848 else OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
850 RedrawWindow( hwnd, &rect, 0, flags );
854 /**********************************************************************
857 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
859 struct x11drv_win_data *data;
861 if (event->xany.window == x11drv_thread_data()->clip_window)
866 if (!(data = X11DRV_get_win_data( hwnd ))) return;
867 if (!data->mapped || data->embedded) return;
871 HWND hwndFocus = GetFocus();
872 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
877 /**********************************************************************
880 static void X11DRV_UnmapNotify( HWND hwnd, XEvent *event )
882 if (event->xany.window == x11drv_thread_data()->clip_window) clipping_cursor = 0;
886 /***********************************************************************
887 * is_net_wm_state_maximized
889 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
893 unsigned long i, count, remaining;
895 if (!data->whole_window) return FALSE;
898 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
899 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
900 &remaining, (unsigned char **)&state ))
902 if (type == XA_ATOM && format == 32)
904 for (i = 0; i < count; i++)
906 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
907 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
918 /***********************************************************************
919 * X11DRV_ReparentNotify
921 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
923 XReparentEvent *event = &xev->xreparent;
924 struct x11drv_win_data *data;
925 HWND parent, old_parent;
928 if (!(data = X11DRV_get_win_data( hwnd ))) return;
929 if (!data->embedded) return;
931 if (data->whole_window)
933 if (event->parent == root_window)
935 TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
937 SendMessageW( hwnd, WM_CLOSE, 0, 0 );
940 data->embedder = event->parent;
943 TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
945 style = GetWindowLongW( hwnd, GWL_STYLE );
946 if (event->parent == root_window)
948 parent = GetDesktopWindow();
949 style = (style & ~WS_CHILD) | WS_POPUP;
953 if (!(parent = create_foreign_window( event->display, event->parent ))) return;
954 style = (style & ~WS_POPUP) | WS_CHILD;
957 ShowWindow( hwnd, SW_HIDE );
958 old_parent = SetParent( hwnd, parent );
959 SetWindowLongW( hwnd, GWL_STYLE, style );
960 SetWindowPos( hwnd, HWND_TOP, event->x, event->y, 0, 0,
961 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS |
962 ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) );
964 /* make old parent destroy itself if it no longer has children */
965 if (old_parent != GetDesktopWindow()) PostMessageW( old_parent, WM_CLOSE, 0, 0 );
969 /***********************************************************************
970 * X11DRV_ConfigureNotify
972 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
974 XConfigureEvent *event = &xev->xconfigure;
975 struct x11drv_win_data *data;
980 int cx, cy, x = event->x, y = event->y;
983 if (!(data = X11DRV_get_win_data( hwnd ))) return;
984 if (!data->mapped || data->iconic) return;
985 if (data->whole_window && !data->managed) return;
986 /* ignore synthetic events on foreign windows */
987 if (event->send_event && !data->whole_window) return;
988 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
990 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
991 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
992 event->serial, data->configure_serial );
998 parent = GetAncestor( hwnd, GA_PARENT );
999 root_coords = event->send_event; /* synthetic events are always in root coords */
1001 if (!root_coords && parent == GetDesktopWindow()) /* normal event, map coordinates to the root */
1005 XTranslateCoordinates( event->display, event->window, root_window,
1006 0, 0, &x, &y, &child );
1007 wine_tsx11_unlock();
1012 rect.right = x + event->width;
1013 rect.bottom = y + event->height;
1014 if (root_coords) OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
1015 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
1016 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1017 event->x, event->y, event->width, event->height );
1019 X11DRV_X_to_window_rect( data, &rect );
1020 if (root_coords) MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
1022 /* Compare what has changed */
1026 cx = rect.right - rect.left;
1027 cy = rect.bottom - rect.top;
1028 flags = SWP_NOACTIVATE | SWP_NOZORDER;
1030 if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */
1032 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
1034 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
1035 hwnd, data->window_rect.left, data->window_rect.top, x, y );
1037 if ((data->window_rect.right - data->window_rect.left == cx &&
1038 data->window_rect.bottom - data->window_rect.top == cy) ||
1039 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
1041 if (flags & SWP_NOMOVE) /* if nothing changed, don't do anything */
1043 TRACE( "Nothing has changed, ignoring event\n" );
1046 flags |= SWP_NOSIZE;
1049 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
1050 hwnd, data->window_rect.right - data->window_rect.left,
1051 data->window_rect.bottom - data->window_rect.top, cx, cy );
1053 if (is_net_wm_state_maximized( event->display, data ))
1055 if (!IsZoomed( data->hwnd ))
1057 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
1058 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1064 if (IsZoomed( data->hwnd ))
1066 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
1067 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1072 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
1076 /**********************************************************************
1077 * X11DRV_GravityNotify
1079 static void X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
1081 XGravityEvent *event = &xev->xgravity;
1082 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1085 if (!data || data->whole_window) return; /* only handle this for foreign windows */
1087 rect.left = event->x;
1088 rect.top = event->y;
1089 rect.right = rect.left + data->whole_rect.right - data->whole_rect.left;
1090 rect.bottom = rect.top + data->whole_rect.bottom - data->whole_rect.top;
1092 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d)\n",
1093 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1094 event->x, event->y );
1096 X11DRV_X_to_window_rect( data, &rect );
1098 if (data->window_rect.left != rect.left || data ->window_rect.top != rect.top)
1099 SetWindowPos( hwnd, 0, rect.left, rect.top, 0, 0,
1100 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1104 /***********************************************************************
1105 * get_window_wm_state
1107 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
1115 int format, ret = -1;
1116 unsigned long count, remaining;
1119 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
1120 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1121 &type, &format, &count, &remaining, (unsigned char **)&state ))
1123 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1127 wine_tsx11_unlock();
1132 /***********************************************************************
1133 * handle_wm_state_notify
1135 * Handle a PropertyNotify for WM_STATE.
1137 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
1138 BOOL update_window )
1142 switch(event->state)
1144 case PropertyDelete:
1145 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1146 data->wm_state = WithdrawnState;
1148 case PropertyNewValue:
1150 int old_state = data->wm_state;
1151 int new_state = get_window_wm_state( event->display, data );
1152 if (new_state != -1 && new_state != data->wm_state)
1154 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1155 data->hwnd, data->whole_window, new_state, old_state );
1156 data->wm_state = new_state;
1157 /* ignore the initial state transition out of withdrawn state */
1158 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1159 if (!old_state) return;
1165 if (!update_window || !data->managed || !data->mapped) return;
1167 style = GetWindowLongW( data->hwnd, GWL_STYLE );
1169 if (data->iconic && data->wm_state == NormalState) /* restore window */
1171 data->iconic = FALSE;
1172 if (is_net_wm_state_maximized( event->display, data ))
1174 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1176 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1177 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1179 else TRACE( "not restoring to max win %p/%lx style %08x\n",
1180 data->hwnd, data->whole_window, style );
1182 else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1184 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1185 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1187 else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1189 else if (!data->iconic && data->wm_state == IconicState)
1191 data->iconic = TRUE;
1192 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1194 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1195 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1197 else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1202 /***********************************************************************
1203 * X11DRV_PropertyNotify
1205 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1207 XPropertyEvent *event = &xev->xproperty;
1208 struct x11drv_win_data *data;
1211 if (!(data = X11DRV_get_win_data( hwnd ))) return;
1213 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1217 /* event filter to wait for a WM_STATE change notification on a window */
1218 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1220 if (event->xany.window != (Window)arg) return 0;
1221 return (event->type == DestroyNotify ||
1222 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1225 /***********************************************************************
1226 * wait_for_withdrawn_state
1228 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1230 DWORD end = GetTickCount() + 2000;
1232 if (!data->managed) return;
1234 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1235 data->hwnd, data->whole_window, set ? "" : "not " );
1237 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1243 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1246 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1247 if (event.type == DestroyNotify) call_event_handler( display, &event );
1250 wine_tsx11_unlock();
1251 handle_wm_state_notify( data, &event.xproperty, FALSE );
1255 wine_tsx11_unlock();
1260 int timeout = end - GetTickCount();
1262 pfd.fd = ConnectionNumber(display);
1263 pfd.events = POLLIN;
1264 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1266 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1271 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1275 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1279 if (!IsWindowEnabled(hQueryWnd)) return 0;
1281 GetWindowRect(hQueryWnd, &tempRect);
1283 if(!PtInRect(&tempRect, *lpPt)) return 0;
1285 if (!IsIconic( hQueryWnd ))
1288 ScreenToClient( hQueryWnd, &pt );
1289 GetClientRect( hQueryWnd, &tempRect );
1291 if (PtInRect( &tempRect, pt))
1293 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1294 if (ret && ret != hQueryWnd)
1296 ret = find_drop_window( ret, lpPt );
1297 if (ret) return ret;
1302 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1304 ScreenToClient(hQueryWnd, lpPt);
1309 /**********************************************************************
1310 * EVENT_DropFromOffix
1312 * don't know if it still works (last Changelog is from 96/11/04)
1314 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1316 struct x11drv_win_data *data;
1317 unsigned long data_length;
1318 unsigned long aux_long;
1319 unsigned char* p_data = NULL;
1323 Window win, w_aux_root, w_aux_child;
1325 win = X11DRV_get_whole_window(hWnd);
1327 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1328 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1329 x += virtual_screen_rect.left;
1330 y += virtual_screen_rect.top;
1331 wine_tsx11_unlock();
1333 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1335 /* find out drop point and drop window */
1336 if( x < 0 || y < 0 ||
1337 x > (data->whole_rect.right - data->whole_rect.left) ||
1338 y > (data->whole_rect.bottom - data->whole_rect.top) )
1340 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1346 POINT pt = { x, y };
1347 HWND hwndDrop = find_drop_window( hWnd, &pt );
1360 if (!bAccept) return;
1363 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1364 x11drv_atom(DndSelection), 0, 65535, FALSE,
1365 AnyPropertyType, &atom_aux, &dummy,
1366 &data_length, &aux_long, &p_data);
1367 wine_tsx11_unlock();
1369 if( !aux_long && p_data) /* don't bother if > 64K */
1371 char *p = (char *)p_data;
1375 while( *p ) /* calculate buffer size */
1377 INT len = GetShortPathNameA( p, NULL, 0 );
1378 if (len) aux_long += len + 1;
1381 if( aux_long && aux_long < 65535 )
1386 aux_long += sizeof(DROPFILES) + 1;
1387 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1388 lpDrop = GlobalLock( hDrop );
1392 lpDrop->pFiles = sizeof(DROPFILES);
1395 lpDrop->fNC = FALSE;
1396 lpDrop->fWide = FALSE;
1397 p_drop = (char *)(lpDrop + 1);
1401 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1402 p_drop += strlen( p_drop ) + 1;
1406 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1411 if( p_data ) XFree(p_data);
1412 wine_tsx11_unlock();
1415 /**********************************************************************
1418 * drop items are separated by \n
1419 * each item is prefixed by its mime type
1421 * event->data.l[3], event->data.l[4] contains drop x,y position
1423 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1425 struct x11drv_win_data *win_data;
1426 unsigned long data_length;
1427 unsigned long aux_long, drop_len = 0;
1428 unsigned char *p_data = NULL; /* property data */
1429 char *p_drop = NULL;
1441 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1444 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1445 x11drv_atom(DndSelection), 0, 65535, FALSE,
1446 AnyPropertyType, &u.atom_aux, &u.i,
1447 &data_length, &aux_long, &p_data);
1448 wine_tsx11_unlock();
1450 WARN("property too large, truncated!\n");
1451 TRACE("urls=%s\n", p_data);
1453 if( !aux_long && p_data) { /* don't bother if > 64K */
1454 /* calculate length */
1456 next = strchr(p, '\n');
1459 if (strncmp(p,"file:",5) == 0 ) {
1460 INT len = GetShortPathNameA( p+5, NULL, 0 );
1461 if (len) drop_len += len + 1;
1466 next = strchr(p, '\n');
1472 if( drop_len && drop_len < 65535 ) {
1474 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1475 &x, &y, &u.i, &u.i, &u.u);
1476 x += virtual_screen_rect.left;
1477 y += virtual_screen_rect.top;
1478 wine_tsx11_unlock();
1480 drop_len += sizeof(DROPFILES) + 1;
1481 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1482 lpDrop = GlobalLock( hDrop );
1484 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1486 lpDrop->pFiles = sizeof(DROPFILES);
1490 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1491 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1492 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1493 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1494 lpDrop->fWide = FALSE;
1495 p_drop = (char*)(lpDrop + 1);
1498 /* create message content */
1501 next = strchr(p, '\n');
1504 if (strncmp(p,"file:",5) == 0 ) {
1505 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1507 TRACE("drop file %s as %s\n", p+5, p_drop);
1510 WARN("can't convert file %s to dos name\n", p+5);
1513 WARN("unknown mime type %s\n", p);
1518 next = strchr(p, '\n');
1525 GlobalUnlock(hDrop);
1526 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1530 if( p_data ) XFree(p_data);
1531 wine_tsx11_unlock();
1536 /**********************************************************************
1537 * handle_xembed_protocol
1539 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1541 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1545 switch (event->data.l[1])
1547 case XEMBED_EMBEDDED_NOTIFY:
1548 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1549 data->embedder = event->data.l[3];
1552 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1553 hwnd, event->window, event->data.l[1], event->data.l[2] );
1559 /**********************************************************************
1560 * handle_dnd_protocol
1562 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1565 int root_x, root_y, child_x, child_y;
1568 /* query window (drag&drop event contains only drag window) */
1570 XQueryPointer( event->display, root_window, &root, &child,
1571 &root_x, &root_y, &child_x, &child_y, &u);
1572 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1573 wine_tsx11_unlock();
1575 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1576 EVENT_DropFromOffiX(hwnd, event);
1577 else if (event->data.l[0] == DndURL)
1578 EVENT_DropURLs(hwnd, event);
1582 struct client_message_handler
1584 int atom; /* protocol atom */
1585 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1588 static const struct client_message_handler client_messages[] =
1590 { XATOM_MANAGER, handle_manager_message },
1591 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1592 { XATOM__XEMBED, handle_xembed_protocol },
1593 { XATOM_DndProtocol, handle_dnd_protocol },
1594 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1595 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1596 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1597 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1601 /**********************************************************************
1602 * X11DRV_ClientMessage
1604 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1606 XClientMessageEvent *event = &xev->xclient;
1611 if (event->format != 32)
1613 WARN( "Don't know how to handle format %d\n", event->format );
1617 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1619 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1621 client_messages[i].handler( hwnd, event );
1625 TRACE( "no handler found for %ld\n", event->message_type );