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;
316 if (prev->xany.window == next->xany.window)
318 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
319 return MERGE_DISCARD;
322 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
324 if (next->xcookie.extension != xinput2_opcode) break;
325 if (next->xcookie.evtype != XI_RawMotion) break;
326 if (x11drv_thread_data()->warp_serial) break;
331 if (prev->xcookie.extension != xinput2_opcode) break;
332 if (prev->xcookie.evtype != XI_RawMotion) break;
336 if (next->xcookie.extension != xinput2_opcode) break;
337 if (next->xcookie.evtype != XI_RawMotion) break;
338 if (x11drv_thread_data()->warp_serial) break;
339 return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data );
348 /***********************************************************************
351 static inline void call_event_handler( Display *display, XEvent *event )
355 struct x11drv_thread_data *thread_data;
357 if (!handlers[event->type])
359 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
360 return; /* no handler, ignore it */
363 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
364 hwnd = 0; /* not for a registered window */
365 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
367 TRACE( "%lu %s for hwnd/window %p/%lx\n",
368 event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window );
369 thread_data = x11drv_thread_data();
370 prev = thread_data->current_event;
371 thread_data->current_event = event;
372 handlers[event->type]( hwnd, event );
373 thread_data->current_event = prev;
377 /***********************************************************************
380 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
382 XEvent event, prev_event;
384 enum event_merge_action action = MERGE_DISCARD;
387 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
390 if (XFilterEvent( &event, None ))
393 * SCIM on linux filters key events strangely. It does not filter the
394 * KeyPress events for these keys however it does filter the
395 * KeyRelease events. This causes wine to become very confused as
396 * to the keyboard state.
398 * We need to let those KeyRelease events be processed so that the
399 * keyboard state is correct.
401 if (event.type == KeyRelease)
404 XKeyEvent *keyevent = &event.xkey;
406 XLookupString(keyevent, NULL, 0, &keysym, NULL);
407 if (!(keysym == XK_Shift_L ||
408 keysym == XK_Shift_R ||
409 keysym == XK_Control_L ||
410 keysym == XK_Control_R ||
411 keysym == XK_Alt_R ||
412 keysym == XK_Alt_L ||
413 keysym == XK_Meta_R ||
414 keysym == XK_Meta_L))
415 continue; /* not a key we care about, ignore it */
418 continue; /* filtered, ignore it */
420 get_event_data( &event );
421 if (prev_event.type) action = merge_events( &prev_event, &event );
424 case MERGE_HANDLE: /* handle prev, keep new */
425 call_event_handler( display, &prev_event );
427 case MERGE_DISCARD: /* discard prev, keep new */
428 free_event_data( &prev_event );
431 case MERGE_KEEP: /* handle new, keep prev for future merging */
432 call_event_handler( display, &event );
434 case MERGE_IGNORE: /* ignore new, keep prev for future merging */
435 free_event_data( &event );
439 if (prev_event.type) call_event_handler( display, &prev_event );
440 free_event_data( &prev_event );
441 XFlush( gdi_display );
442 if (count) TRACE( "processed %d events\n", count );
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 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
520 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
522 if (!(style & WS_VISIBLE)) return FALSE;
523 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
524 if (style & WS_MINIMIZE) return FALSE;
525 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
526 if (hwnd == GetDesktopWindow()) return FALSE;
527 if (data && IsRectEmpty( &data->window_rect )) return FALSE;
528 return !(style & WS_DISABLED);
532 /**********************************************************************
535 static void set_focus( Display *display, HWND hwnd, Time time )
539 GUITHREADINFO threadinfo;
541 TRACE( "setting foreground window to %p\n", hwnd );
542 SetForegroundWindow( hwnd );
544 threadinfo.cbSize = sizeof(threadinfo);
545 GetGUIThreadInfo(0, &threadinfo);
546 focus = threadinfo.hwndFocus;
547 if (!focus) focus = threadinfo.hwndActive;
548 if (focus) focus = GetAncestor( focus, GA_ROOT );
549 win = X11DRV_get_whole_window(focus);
553 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
554 XSetInputFocus( display, win, RevertToParent, time );
559 /**********************************************************************
560 * handle_manager_message
562 static void handle_manager_message( HWND hwnd, XClientMessageEvent *event )
564 if (hwnd != GetDesktopWindow()) return;
565 if (systray_atom && event->data.l[1] == systray_atom)
566 change_systray_owner( event->display, event->data.l[2] );
570 /**********************************************************************
571 * handle_wm_protocols
573 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
575 Atom protocol = (Atom)event->data.l[0];
576 Time event_time = (Time)event->data.l[1];
578 if (!protocol) return;
580 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
582 update_user_time( event_time );
584 if (hwnd == GetDesktopWindow())
586 /* The desktop window does not have a close button that we can
587 * pretend to click. Therefore, we simply send it a close command. */
588 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
592 /* Ignore the delete window request if the window has been disabled
593 * and we are in managed mode. This is to disallow applications from
594 * being closed by the window manager while in a modal state.
596 if (IsWindowEnabled(hwnd))
600 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
601 hSysMenu = GetSystemMenu(hwnd, FALSE);
604 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
605 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
608 if (GetActiveWindow() != hwnd)
610 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
611 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
612 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
615 case MA_NOACTIVATEANDEAT:
616 case MA_ACTIVATEANDEAT:
622 SetActiveWindow(hwnd);
625 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
630 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
633 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
635 HWND last_focus = x11drv_thread_data()->last_focus;
637 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
638 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
639 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
641 if (can_activate_window(hwnd))
643 /* simulate a mouse click on the caption to find out
644 * whether the window wants to be activated */
645 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
646 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
647 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
648 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
650 set_focus( event->display, hwnd, event_time );
654 else if (hwnd == GetDesktopWindow())
656 hwnd = GetForegroundWindow();
657 if (!hwnd) hwnd = last_focus;
658 if (!hwnd) hwnd = GetDesktopWindow();
659 set_focus( event->display, hwnd, event_time );
662 /* try to find some other window to give the focus to */
664 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
665 if (!hwnd) hwnd = GetActiveWindow();
666 if (!hwnd) hwnd = last_focus;
667 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
669 else if (protocol == x11drv_atom(_NET_WM_PING))
671 XClientMessageEvent xev;
674 TRACE("NET_WM Ping\n");
675 xev.window = DefaultRootWindow(xev.display);
676 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
681 static const char * const focus_details[] =
687 "NotifyNonlinearVirtual",
693 /**********************************************************************
696 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
698 XFocusChangeEvent *event = &xev->xfocus;
703 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
705 if (event->detail == NotifyPointer) return;
706 if (hwnd == GetDesktopWindow()) return;
708 if ((xic = X11DRV_get_ic( hwnd ))) XSetICFocus( xic );
711 if (hwnd == GetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE );
715 if (!can_activate_window(hwnd))
717 HWND hwnd = GetFocus();
718 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
719 if (!hwnd) hwnd = GetActiveWindow();
720 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
721 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
723 else SetForegroundWindow( hwnd );
727 /**********************************************************************
730 * Note: only top-level windows get FocusOut events.
732 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
734 XFocusChangeEvent *event = &xev->xfocus;
740 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
742 if (event->detail == NotifyPointer)
744 if (!hwnd && event->window == x11drv_thread_data()->clip_window) reset_clipping_window();
748 if (ximInComposeMode) return;
750 x11drv_thread_data()->last_focus = hwnd;
751 if ((xic = X11DRV_get_ic( hwnd ))) XUnsetICFocus( xic );
753 if (root_window != DefaultRootWindow(event->display))
755 if (hwnd == GetDesktopWindow()) reset_clipping_window();
758 if (hwnd != GetForegroundWindow()) return;
759 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
761 /* don't reset the foreground window, if the window which is
762 getting the focus is a Wine window */
764 XGetInputFocus( event->display, &focus_win, &revert );
767 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
773 /* Abey : 6-Oct-99. Check again if the focus out window is the
774 Foreground window, because in most cases the messages sent
775 above must have already changed the foreground window, in which
776 case we don't have to change the foreground window to 0 */
777 if (hwnd == GetForegroundWindow())
779 TRACE( "lost focus, setting fg to desktop\n" );
780 SetForegroundWindow( GetDesktopWindow() );
786 /***********************************************************************
789 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
791 XExposeEvent *event = &xev->xexpose;
793 struct x11drv_win_data *data;
794 int flags = RDW_INVALIDATE | RDW_ERASE | RDW_FRAME;
796 TRACE( "win %p (%lx) %d,%d %dx%d\n",
797 hwnd, event->window, event->x, event->y, event->width, event->height );
799 if (!(data = X11DRV_get_win_data( hwnd ))) return;
801 rect.left = event->x;
803 rect.right = event->x + event->width;
804 rect.bottom = event->y + event->height;
808 data->surface->funcs->lock( data->surface );
809 add_bounds_rect( data->surface->funcs->get_bounds( data->surface ), &rect );
810 data->surface->funcs->unlock( data->surface );
813 if (event->window != root_window)
815 OffsetRect( &rect, data->whole_rect.left - data->client_rect.left,
816 data->whole_rect.top - data->client_rect.top );
818 if (GetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
819 mirror_rect( &data->client_rect, &rect );
821 SERVER_START_REQ( update_window_zorder )
823 req->window = wine_server_user_handle( hwnd );
824 req->rect.left = rect.left;
825 req->rect.top = rect.top;
826 req->rect.right = rect.right;
827 req->rect.bottom = rect.bottom;
828 wine_server_call( req );
832 flags |= RDW_ALLCHILDREN;
834 else OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
836 if (!data->surface) RedrawWindow( hwnd, &rect, 0, flags );
840 /**********************************************************************
843 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
845 struct x11drv_win_data *data;
847 if (event->xany.window == x11drv_thread_data()->clip_window)
852 if (!(data = X11DRV_get_win_data( hwnd ))) return;
853 if (!data->mapped || data->embedded) return;
857 HWND hwndFocus = GetFocus();
858 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
863 /**********************************************************************
866 static void X11DRV_UnmapNotify( HWND hwnd, XEvent *event )
868 if (event->xany.window == x11drv_thread_data()->clip_window) clipping_cursor = 0;
872 /***********************************************************************
873 * is_net_wm_state_maximized
875 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
879 unsigned long i, count, remaining;
881 if (!data->whole_window) return FALSE;
883 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
884 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
885 &remaining, (unsigned char **)&state ))
887 if (type == XA_ATOM && format == 32)
889 for (i = 0; i < count; i++)
891 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
892 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
902 /***********************************************************************
903 * X11DRV_ReparentNotify
905 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
907 XReparentEvent *event = &xev->xreparent;
908 struct x11drv_win_data *data;
909 HWND parent, old_parent;
912 if (!(data = X11DRV_get_win_data( hwnd ))) return;
913 if (!data->embedded) return;
915 if (data->whole_window)
917 if (event->parent == root_window)
919 TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
921 SendMessageW( hwnd, WM_CLOSE, 0, 0 );
924 data->embedder = event->parent;
927 TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
929 style = GetWindowLongW( hwnd, GWL_STYLE );
930 if (event->parent == root_window)
932 parent = GetDesktopWindow();
933 style = (style & ~WS_CHILD) | WS_POPUP;
937 if (!(parent = create_foreign_window( event->display, event->parent ))) return;
938 style = (style & ~WS_POPUP) | WS_CHILD;
941 ShowWindow( hwnd, SW_HIDE );
942 old_parent = SetParent( hwnd, parent );
943 SetWindowLongW( hwnd, GWL_STYLE, style );
944 SetWindowPos( hwnd, HWND_TOP, event->x, event->y, 0, 0,
945 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS |
946 ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) );
948 /* make old parent destroy itself if it no longer has children */
949 if (old_parent != GetDesktopWindow()) PostMessageW( old_parent, WM_CLOSE, 0, 0 );
953 /***********************************************************************
954 * X11DRV_ConfigureNotify
956 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
958 XConfigureEvent *event = &xev->xconfigure;
959 struct x11drv_win_data *data;
964 int cx, cy, x = event->x, y = event->y;
967 if (!(data = X11DRV_get_win_data( hwnd ))) return;
968 if (!data->mapped || data->iconic) return;
969 if (data->whole_window && !data->managed) return;
970 /* ignore synthetic events on foreign windows */
971 if (event->send_event && !data->whole_window) return;
972 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
974 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
975 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
976 event->serial, data->configure_serial );
982 parent = GetAncestor( hwnd, GA_PARENT );
983 root_coords = event->send_event; /* synthetic events are always in root coords */
985 if (!root_coords && parent == GetDesktopWindow()) /* normal event, map coordinates to the root */
988 XTranslateCoordinates( event->display, event->window, root_window,
989 0, 0, &x, &y, &child );
994 rect.right = x + event->width;
995 rect.bottom = y + event->height;
996 if (root_coords) OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
997 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
998 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
999 event->x, event->y, event->width, event->height );
1001 X11DRV_X_to_window_rect( data, &rect );
1002 if (root_coords) MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
1004 /* Compare what has changed */
1008 cx = rect.right - rect.left;
1009 cy = rect.bottom - rect.top;
1010 flags = SWP_NOACTIVATE | SWP_NOZORDER;
1012 if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */
1014 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
1016 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
1017 hwnd, data->window_rect.left, data->window_rect.top, x, y );
1019 if ((data->window_rect.right - data->window_rect.left == cx &&
1020 data->window_rect.bottom - data->window_rect.top == cy) ||
1021 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
1022 flags |= SWP_NOSIZE;
1024 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
1025 hwnd, data->window_rect.right - data->window_rect.left,
1026 data->window_rect.bottom - data->window_rect.top, cx, cy );
1028 if (is_net_wm_state_maximized( event->display, data ))
1030 if (!IsZoomed( data->hwnd ))
1032 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
1033 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1039 if (IsZoomed( data->hwnd ))
1041 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
1042 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1047 if ((flags & (SWP_NOSIZE | SWP_NOMOVE)) != (SWP_NOSIZE | SWP_NOMOVE))
1048 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
1052 /**********************************************************************
1053 * X11DRV_GravityNotify
1055 static void X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
1057 XGravityEvent *event = &xev->xgravity;
1058 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1061 if (!data || data->whole_window) return; /* only handle this for foreign windows */
1063 rect.left = event->x;
1064 rect.top = event->y;
1065 rect.right = rect.left + data->whole_rect.right - data->whole_rect.left;
1066 rect.bottom = rect.top + data->whole_rect.bottom - data->whole_rect.top;
1068 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d)\n",
1069 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1070 event->x, event->y );
1072 X11DRV_X_to_window_rect( data, &rect );
1074 if (data->window_rect.left != rect.left || data ->window_rect.top != rect.top)
1075 SetWindowPos( hwnd, 0, rect.left, rect.top, 0, 0,
1076 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1080 /***********************************************************************
1081 * get_window_wm_state
1083 static int get_window_wm_state( Display *display, Window window )
1091 int format, ret = -1;
1092 unsigned long count, remaining;
1094 if (!XGetWindowProperty( display, window, x11drv_atom(WM_STATE), 0,
1095 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1096 &type, &format, &count, &remaining, (unsigned char **)&state ))
1098 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1106 /***********************************************************************
1107 * handle_wm_state_notify
1109 * Handle a PropertyNotify for WM_STATE.
1111 static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL update_window )
1113 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1118 switch(event->state)
1120 case PropertyDelete:
1121 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1122 data->wm_state = WithdrawnState;
1124 case PropertyNewValue:
1126 int old_state = data->wm_state;
1127 int new_state = get_window_wm_state( event->display, data->whole_window );
1128 if (new_state != -1 && new_state != data->wm_state)
1130 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1131 data->hwnd, data->whole_window, new_state, old_state );
1132 data->wm_state = new_state;
1133 /* ignore the initial state transition out of withdrawn state */
1134 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1135 if (!old_state) return;
1141 if (!update_window || !data->managed || !data->mapped) return;
1143 style = GetWindowLongW( data->hwnd, GWL_STYLE );
1145 if (data->iconic && data->wm_state == NormalState) /* restore window */
1147 data->iconic = FALSE;
1148 if (is_net_wm_state_maximized( event->display, data ))
1150 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1152 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1153 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1155 else TRACE( "not restoring to max win %p/%lx style %08x\n",
1156 data->hwnd, data->whole_window, style );
1158 else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1160 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1161 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1163 else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1165 else if (!data->iconic && data->wm_state == IconicState)
1167 data->iconic = TRUE;
1168 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1170 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1171 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1173 else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1178 /***********************************************************************
1179 * X11DRV_PropertyNotify
1181 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1183 XPropertyEvent *event = &xev->xproperty;
1186 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE );
1190 /* event filter to wait for a WM_STATE change notification on a window */
1191 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1193 if (event->xany.window != (Window)arg) return 0;
1194 return (event->type == DestroyNotify ||
1195 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1198 /***********************************************************************
1199 * wait_for_withdrawn_state
1201 void wait_for_withdrawn_state( HWND hwnd, BOOL set )
1203 Display *display = thread_display();
1204 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1205 DWORD end = GetTickCount() + 2000;
1207 if (!data || !data->managed) return;
1209 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1210 data->hwnd, data->whole_window, set ? "" : "not " );
1212 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1217 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1220 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1221 if (event.type == DestroyNotify) call_event_handler( display, &event );
1222 else handle_wm_state_notify( hwnd, &event.xproperty, FALSE );
1228 int timeout = end - GetTickCount();
1230 pfd.fd = ConnectionNumber(display);
1231 pfd.events = POLLIN;
1232 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1234 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1239 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1243 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1247 if (!IsWindowEnabled(hQueryWnd)) return 0;
1249 GetWindowRect(hQueryWnd, &tempRect);
1251 if(!PtInRect(&tempRect, *lpPt)) return 0;
1253 if (!IsIconic( hQueryWnd ))
1256 ScreenToClient( hQueryWnd, &pt );
1257 GetClientRect( hQueryWnd, &tempRect );
1259 if (PtInRect( &tempRect, pt))
1261 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1262 if (ret && ret != hQueryWnd)
1264 ret = find_drop_window( ret, lpPt );
1265 if (ret) return ret;
1270 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1272 ScreenToClient(hQueryWnd, lpPt);
1277 /**********************************************************************
1278 * EVENT_DropFromOffix
1280 * don't know if it still works (last Changelog is from 96/11/04)
1282 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1284 struct x11drv_win_data *data;
1285 unsigned long data_length;
1286 unsigned long aux_long;
1287 unsigned char* p_data = NULL;
1291 Window win, w_aux_root, w_aux_child;
1293 win = X11DRV_get_whole_window(hWnd);
1294 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1295 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1296 x += virtual_screen_rect.left;
1297 y += virtual_screen_rect.top;
1299 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1301 /* find out drop point and drop window */
1302 if( x < 0 || y < 0 ||
1303 x > (data->whole_rect.right - data->whole_rect.left) ||
1304 y > (data->whole_rect.bottom - data->whole_rect.top) )
1306 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1312 POINT pt = { x, y };
1313 HWND hwndDrop = find_drop_window( hWnd, &pt );
1326 if (!bAccept) return;
1328 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1329 x11drv_atom(DndSelection), 0, 65535, FALSE,
1330 AnyPropertyType, &atom_aux, &dummy,
1331 &data_length, &aux_long, &p_data);
1333 if( !aux_long && p_data) /* don't bother if > 64K */
1335 char *p = (char *)p_data;
1339 while( *p ) /* calculate buffer size */
1341 INT len = GetShortPathNameA( p, NULL, 0 );
1342 if (len) aux_long += len + 1;
1345 if( aux_long && aux_long < 65535 )
1350 aux_long += sizeof(DROPFILES) + 1;
1351 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1352 lpDrop = GlobalLock( hDrop );
1356 lpDrop->pFiles = sizeof(DROPFILES);
1359 lpDrop->fNC = FALSE;
1360 lpDrop->fWide = FALSE;
1361 p_drop = (char *)(lpDrop + 1);
1365 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1366 p_drop += strlen( p_drop ) + 1;
1370 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1374 if( p_data ) XFree(p_data);
1377 /**********************************************************************
1380 * drop items are separated by \n
1381 * each item is prefixed by its mime type
1383 * event->data.l[3], event->data.l[4] contains drop x,y position
1385 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1387 struct x11drv_win_data *win_data;
1388 unsigned long data_length;
1389 unsigned long aux_long, drop_len = 0;
1390 unsigned char *p_data = NULL; /* property data */
1391 char *p_drop = NULL;
1403 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1405 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1406 x11drv_atom(DndSelection), 0, 65535, FALSE,
1407 AnyPropertyType, &u.atom_aux, &u.i,
1408 &data_length, &aux_long, &p_data);
1410 WARN("property too large, truncated!\n");
1411 TRACE("urls=%s\n", p_data);
1413 if( !aux_long && p_data) { /* don't bother if > 64K */
1414 /* calculate length */
1416 next = strchr(p, '\n');
1419 if (strncmp(p,"file:",5) == 0 ) {
1420 INT len = GetShortPathNameA( p+5, NULL, 0 );
1421 if (len) drop_len += len + 1;
1426 next = strchr(p, '\n');
1432 if( drop_len && drop_len < 65535 ) {
1433 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1434 &x, &y, &u.i, &u.i, &u.u);
1435 x += virtual_screen_rect.left;
1436 y += virtual_screen_rect.top;
1438 drop_len += sizeof(DROPFILES) + 1;
1439 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1440 lpDrop = GlobalLock( hDrop );
1442 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1444 lpDrop->pFiles = sizeof(DROPFILES);
1448 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1449 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1450 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1451 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1452 lpDrop->fWide = FALSE;
1453 p_drop = (char*)(lpDrop + 1);
1456 /* create message content */
1459 next = strchr(p, '\n');
1462 if (strncmp(p,"file:",5) == 0 ) {
1463 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1465 TRACE("drop file %s as %s\n", p+5, p_drop);
1468 WARN("can't convert file %s to dos name\n", p+5);
1471 WARN("unknown mime type %s\n", p);
1476 next = strchr(p, '\n');
1483 GlobalUnlock(hDrop);
1484 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1487 if( p_data ) XFree(p_data);
1492 /**********************************************************************
1493 * handle_xembed_protocol
1495 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1497 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1501 switch (event->data.l[1])
1503 case XEMBED_EMBEDDED_NOTIFY:
1504 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1505 data->embedder = event->data.l[3];
1508 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1509 hwnd, event->window, event->data.l[1], event->data.l[2] );
1515 /**********************************************************************
1516 * handle_dnd_protocol
1518 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1521 int root_x, root_y, child_x, child_y;
1524 /* query window (drag&drop event contains only drag window) */
1525 XQueryPointer( event->display, root_window, &root, &child,
1526 &root_x, &root_y, &child_x, &child_y, &u);
1527 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1529 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1530 EVENT_DropFromOffiX(hwnd, event);
1531 else if (event->data.l[0] == DndURL)
1532 EVENT_DropURLs(hwnd, event);
1536 struct client_message_handler
1538 int atom; /* protocol atom */
1539 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1542 static const struct client_message_handler client_messages[] =
1544 { XATOM_MANAGER, handle_manager_message },
1545 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1546 { XATOM__XEMBED, handle_xembed_protocol },
1547 { XATOM_DndProtocol, handle_dnd_protocol },
1548 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1549 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1550 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1551 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1555 /**********************************************************************
1556 * X11DRV_ClientMessage
1558 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1560 XClientMessageEvent *event = &xev->xclient;
1565 if (event->format != 32)
1567 WARN( "Don't know how to handle format %d\n", event->format );
1571 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1573 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1575 client_messages[i].handler( hwnd, event );
1579 TRACE( "no handler found for %ld\n", event->message_type );