winex11: Post internal WM_IME_NOTIFY wparam on composition updates.
[wine.git] / dlls / winex11.drv / event.c
blob701d0c1faf6496c27c9613126fd19b434dfa3149
1 /*
2 * X11 event driver
4 * Copyright 1993 Alexandre Julliard
5 * 1999 Noel Borthwick
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
22 #if 0
23 #pragma makedep unix
24 #endif
26 #include "config.h"
28 #include <poll.h>
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xresource.h>
33 #include <X11/Xutil.h>
34 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
35 #include <X11/extensions/XInput2.h>
36 #endif
38 #include <assert.h>
39 #include <stdarg.h>
40 #include <string.h>
42 #include "x11drv.h"
43 #include "shlobj.h" /* DROPFILES */
44 #include "shellapi.h"
46 #include "wine/server.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(event);
50 WINE_DECLARE_DEBUG_CHANNEL(xdnd);
52 #define DndNotDnd -1 /* OffiX drag&drop */
53 #define DndUnknown 0
54 #define DndRawData 1
55 #define DndFile 2
56 #define DndFiles 3
57 #define DndText 4
58 #define DndDir 5
59 #define DndLink 6
60 #define DndExe 7
62 #define DndEND 8
64 #define DndURL 128 /* KDE drag&drop */
66 #define XEMBED_EMBEDDED_NOTIFY 0
67 #define XEMBED_WINDOW_ACTIVATE 1
68 #define XEMBED_WINDOW_DEACTIVATE 2
69 #define XEMBED_REQUEST_FOCUS 3
70 #define XEMBED_FOCUS_IN 4
71 #define XEMBED_FOCUS_OUT 5
72 #define XEMBED_FOCUS_NEXT 6
73 #define XEMBED_FOCUS_PREV 7
74 #define XEMBED_MODALITY_ON 10
75 #define XEMBED_MODALITY_OFF 11
76 #define XEMBED_REGISTER_ACCELERATOR 12
77 #define XEMBED_UNREGISTER_ACCELERATOR 13
78 #define XEMBED_ACTIVATE_ACCELERATOR 14
80 Bool (*pXGetEventData)( Display *display, XEvent /*XGenericEventCookie*/ *event ) = NULL;
81 void (*pXFreeEventData)( Display *display, XEvent /*XGenericEventCookie*/ *event ) = NULL;
83 /* Event handlers */
84 static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *event );
85 static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *event );
86 static BOOL X11DRV_Expose( HWND hwnd, XEvent *event );
87 static BOOL X11DRV_MapNotify( HWND hwnd, XEvent *event );
88 static BOOL X11DRV_UnmapNotify( HWND hwnd, XEvent *event );
89 static BOOL X11DRV_ReparentNotify( HWND hwnd, XEvent *event );
90 static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *event );
91 static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
92 static BOOL X11DRV_ClientMessage( HWND hwnd, XEvent *event );
93 static BOOL X11DRV_GravityNotify( HWND hwnd, XEvent *event );
95 #define MAX_EVENT_HANDLERS 128
97 static x11drv_event_handler handlers[MAX_EVENT_HANDLERS] =
99 NULL, /* 0 reserved */
100 NULL, /* 1 reserved */
101 X11DRV_KeyEvent, /* 2 KeyPress */
102 X11DRV_KeyEvent, /* 3 KeyRelease */
103 X11DRV_ButtonPress, /* 4 ButtonPress */
104 X11DRV_ButtonRelease, /* 5 ButtonRelease */
105 X11DRV_MotionNotify, /* 6 MotionNotify */
106 X11DRV_EnterNotify, /* 7 EnterNotify */
107 NULL, /* 8 LeaveNotify */
108 X11DRV_FocusIn, /* 9 FocusIn */
109 X11DRV_FocusOut, /* 10 FocusOut */
110 X11DRV_KeymapNotify, /* 11 KeymapNotify */
111 X11DRV_Expose, /* 12 Expose */
112 NULL, /* 13 GraphicsExpose */
113 NULL, /* 14 NoExpose */
114 NULL, /* 15 VisibilityNotify */
115 NULL, /* 16 CreateNotify */
116 X11DRV_DestroyNotify, /* 17 DestroyNotify */
117 X11DRV_UnmapNotify, /* 18 UnmapNotify */
118 X11DRV_MapNotify, /* 19 MapNotify */
119 NULL, /* 20 MapRequest */
120 X11DRV_ReparentNotify, /* 21 ReparentNotify */
121 X11DRV_ConfigureNotify, /* 22 ConfigureNotify */
122 NULL, /* 23 ConfigureRequest */
123 X11DRV_GravityNotify, /* 24 GravityNotify */
124 NULL, /* 25 ResizeRequest */
125 NULL, /* 26 CirculateNotify */
126 NULL, /* 27 CirculateRequest */
127 X11DRV_PropertyNotify, /* 28 PropertyNotify */
128 X11DRV_SelectionClear, /* 29 SelectionClear */
129 X11DRV_SelectionRequest, /* 30 SelectionRequest */
130 NULL, /* 31 SelectionNotify */
131 NULL, /* 32 ColormapNotify */
132 X11DRV_ClientMessage, /* 33 ClientMessage */
133 X11DRV_MappingNotify, /* 34 MappingNotify */
134 X11DRV_GenericEvent /* 35 GenericEvent */
137 static const char * event_names[MAX_EVENT_HANDLERS] =
139 NULL, NULL, "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
140 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
141 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
142 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
143 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify", "ResizeRequest",
144 "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear", "SelectionRequest",
145 "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify", "GenericEvent"
148 /* is someone else grabbing the keyboard, for example the WM, when manipulating the window */
149 BOOL keyboard_grabbed = FALSE;
151 int xinput2_opcode = 0;
153 /* return the name of an X event */
154 static const char *dbgstr_event( int type )
156 if (type < MAX_EVENT_HANDLERS && event_names[type]) return event_names[type];
157 return wine_dbg_sprintf( "Unknown event %d", type );
160 static inline void get_event_data( XEvent *event )
162 #if defined(GenericEvent) && defined(HAVE_XEVENT_XCOOKIE)
163 if (event->xany.type != GenericEvent) return;
164 if (!pXGetEventData || !pXGetEventData( event->xany.display, event )) event->xcookie.data = NULL;
165 #endif
168 static inline void free_event_data( XEvent *event )
170 #if defined(GenericEvent) && defined(HAVE_XEVENT_XCOOKIE)
171 if (event->xany.type != GenericEvent) return;
172 if (event->xcookie.data) pXFreeEventData( event->xany.display, event );
173 #endif
176 /***********************************************************************
177 * xembed_request_focus
179 static void xembed_request_focus( Display *display, Window window, DWORD timestamp )
181 XEvent xev;
183 xev.xclient.type = ClientMessage;
184 xev.xclient.window = window;
185 xev.xclient.message_type = x11drv_atom(_XEMBED);
186 xev.xclient.serial = 0;
187 xev.xclient.display = display;
188 xev.xclient.send_event = True;
189 xev.xclient.format = 32;
191 xev.xclient.data.l[0] = timestamp;
192 xev.xclient.data.l[1] = XEMBED_REQUEST_FOCUS;
193 xev.xclient.data.l[2] = 0;
194 xev.xclient.data.l[3] = 0;
195 xev.xclient.data.l[4] = 0;
197 XSendEvent(display, window, False, NoEventMask, &xev);
198 XFlush( display );
201 /***********************************************************************
202 * X11DRV_register_event_handler
204 * Register a handler for a given event type.
205 * If already registered, overwrite the previous handler.
207 void X11DRV_register_event_handler( int type, x11drv_event_handler handler, const char *name )
209 assert( type < MAX_EVENT_HANDLERS );
210 assert( !handlers[type] || handlers[type] == handler );
211 handlers[type] = handler;
212 event_names[type] = name;
213 TRACE("registered handler %p for event %d %s\n", handler, type, debugstr_a(name) );
217 /***********************************************************************
218 * filter_event
220 static Bool filter_event( Display *display, XEvent *event, char *arg )
222 ULONG_PTR mask = (ULONG_PTR)arg;
224 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
226 switch(event->type)
228 case KeyPress:
229 case KeyRelease:
230 case KeymapNotify:
231 case MappingNotify:
232 return (mask & (QS_KEY|QS_HOTKEY)) != 0;
233 case ButtonPress:
234 case ButtonRelease:
235 return (mask & QS_MOUSEBUTTON) != 0;
236 #ifdef GenericEvent
237 case GenericEvent:
238 #endif
239 case MotionNotify:
240 case EnterNotify:
241 case LeaveNotify:
242 return (mask & QS_MOUSEMOVE) != 0;
243 case Expose:
244 return (mask & QS_PAINT) != 0;
245 case FocusIn:
246 case FocusOut:
247 case MapNotify:
248 case UnmapNotify:
249 case ConfigureNotify:
250 case PropertyNotify:
251 case ClientMessage:
252 return (mask & QS_POSTMESSAGE) != 0;
253 default:
254 return (mask & QS_SENDMESSAGE) != 0;
259 enum event_merge_action
261 MERGE_DISCARD, /* discard the old event */
262 MERGE_HANDLE, /* handle the old event */
263 MERGE_KEEP, /* keep the old event for future merging */
264 MERGE_IGNORE /* ignore the new event, keep the old one */
267 /***********************************************************************
268 * merge_raw_motion_events
270 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
271 static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawEvent *next )
273 int i, j, k;
274 unsigned char mask;
276 if (!prev->valuators.mask_len) return MERGE_HANDLE;
277 if (!next->valuators.mask_len) return MERGE_HANDLE;
279 mask = prev->valuators.mask[0] | next->valuators.mask[0];
280 if (mask == next->valuators.mask[0]) /* keep next */
282 for (i = j = k = 0; i < 8; i++)
284 if (XIMaskIsSet( prev->valuators.mask, i ))
285 next->valuators.values[j] += prev->valuators.values[k++];
286 if (XIMaskIsSet( next->valuators.mask, i )) j++;
288 TRACE( "merging duplicate GenericEvent\n" );
289 return MERGE_DISCARD;
291 if (mask == prev->valuators.mask[0]) /* keep prev */
293 for (i = j = k = 0; i < 8; i++)
295 if (XIMaskIsSet( next->valuators.mask, i ))
296 prev->valuators.values[j] += next->valuators.values[k++];
297 if (XIMaskIsSet( prev->valuators.mask, i )) j++;
299 TRACE( "merging duplicate GenericEvent\n" );
300 return MERGE_IGNORE;
302 /* can't merge events with disjoint masks */
303 return MERGE_HANDLE;
305 #endif
307 /***********************************************************************
308 * merge_events
310 * Try to merge 2 consecutive events.
312 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
314 switch (prev->type)
316 case ConfigureNotify:
317 switch (next->type)
319 case ConfigureNotify:
320 if (prev->xany.window == next->xany.window)
322 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
323 return MERGE_DISCARD;
325 break;
326 case Expose:
327 case PropertyNotify:
328 return MERGE_KEEP;
330 break;
331 case MotionNotify:
332 switch (next->type)
334 case MotionNotify:
335 if (prev->xany.window == next->xany.window)
337 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
338 return MERGE_DISCARD;
340 break;
341 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
342 case GenericEvent:
343 if (next->xcookie.extension != xinput2_opcode) break;
344 if (next->xcookie.evtype != XI_RawMotion) break;
345 if (x11drv_thread_data()->warp_serial) break;
346 return MERGE_KEEP;
348 break;
349 case GenericEvent:
350 if (prev->xcookie.extension != xinput2_opcode) break;
351 if (prev->xcookie.evtype != XI_RawMotion) break;
352 switch (next->type)
354 case GenericEvent:
355 if (next->xcookie.extension != xinput2_opcode) break;
356 if (next->xcookie.evtype != XI_RawMotion) break;
357 if (x11drv_thread_data()->warp_serial) break;
358 return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data );
359 #endif
361 break;
363 return MERGE_HANDLE;
367 /***********************************************************************
368 * call_event_handler
370 static inline BOOL call_event_handler( Display *display, XEvent *event )
372 HWND hwnd;
373 XEvent *prev;
374 struct x11drv_thread_data *thread_data;
375 BOOL ret;
377 if (!handlers[event->type])
379 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
380 return FALSE; /* no handler, ignore it */
383 #ifdef GenericEvent
384 if (event->type == GenericEvent) hwnd = 0; else
385 #endif
386 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
387 hwnd = 0; /* not for a registered window */
388 if (!hwnd && event->xany.window == root_window) hwnd = NtUserGetDesktopWindow();
390 TRACE( "%lu %s for hwnd/window %p/%lx\n",
391 event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window );
392 thread_data = x11drv_thread_data();
393 prev = thread_data->current_event;
394 thread_data->current_event = event;
395 ret = handlers[event->type]( hwnd, event );
396 thread_data->current_event = prev;
397 return ret;
401 /***********************************************************************
402 * process_events
404 static BOOL process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
406 XEvent event, prev_event;
407 int count = 0;
408 BOOL queued = FALSE;
409 enum event_merge_action action = MERGE_DISCARD;
411 prev_event.type = 0;
412 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
414 count++;
415 if (XFilterEvent( &event, None ))
418 * SCIM on linux filters key events strangely. It does not filter the
419 * KeyPress events for these keys however it does filter the
420 * KeyRelease events. This causes wine to become very confused as
421 * to the keyboard state.
423 * We need to let those KeyRelease events be processed so that the
424 * keyboard state is correct.
426 if (event.type == KeyRelease)
428 KeySym keysym = 0;
429 XKeyEvent *keyevent = &event.xkey;
431 XLookupString(keyevent, NULL, 0, &keysym, NULL);
432 if (!(keysym == XK_Shift_L ||
433 keysym == XK_Shift_R ||
434 keysym == XK_Control_L ||
435 keysym == XK_Control_R ||
436 keysym == XK_Alt_R ||
437 keysym == XK_Alt_L ||
438 keysym == XK_Meta_R ||
439 keysym == XK_Meta_L))
440 continue; /* not a key we care about, ignore it */
442 else
443 continue; /* filtered, ignore it */
445 get_event_data( &event );
446 if (prev_event.type) action = merge_events( &prev_event, &event );
447 switch( action )
449 case MERGE_HANDLE: /* handle prev, keep new */
450 queued |= call_event_handler( display, &prev_event );
451 /* fall through */
452 case MERGE_DISCARD: /* discard prev, keep new */
453 free_event_data( &prev_event );
454 prev_event = event;
455 break;
456 case MERGE_KEEP: /* handle new, keep prev for future merging */
457 queued |= call_event_handler( display, &event );
458 /* fall through */
459 case MERGE_IGNORE: /* ignore new, keep prev for future merging */
460 free_event_data( &event );
461 break;
464 if (prev_event.type) queued |= call_event_handler( display, &prev_event );
465 free_event_data( &prev_event );
466 XFlush( gdi_display );
467 if (count) TRACE( "processed %d events, returning %d\n", count, queued );
468 return queued;
472 /***********************************************************************
473 * ProcessEvents (X11DRV.@)
475 BOOL X11DRV_ProcessEvents( DWORD mask )
477 struct x11drv_thread_data *data = x11drv_thread_data();
479 if (!data) return FALSE;
480 if (data->current_event) mask = 0; /* don't process nested events */
482 return process_events( data->display, filter_event, mask );
485 /***********************************************************************
486 * EVENT_x11_time_to_win32_time
488 * Make our timer and the X timer line up as best we can
489 * Pass 0 to retrieve the current adjustment value (times -1)
491 DWORD EVENT_x11_time_to_win32_time(Time time)
493 static DWORD adjust = 0;
494 DWORD now = NtGetTickCount();
495 DWORD ret;
497 if (! adjust && time != 0)
499 ret = now;
500 adjust = time - now;
502 else
504 /* If we got an event in the 'future', then our clock is clearly wrong.
505 If we got it more than 10000 ms in the future, then it's most likely
506 that the clock has wrapped. */
508 ret = time - adjust;
509 if (ret > now && ((ret - now) < 10000) && time != 0)
511 adjust += ret - now;
512 ret -= ret - now;
516 return ret;
520 /*******************************************************************
521 * can_activate_window
523 * Check if we can activate the specified window.
525 static inline BOOL can_activate_window( HWND hwnd )
527 LONG style = NtUserGetWindowLongW( hwnd, GWL_STYLE );
528 RECT rect;
530 if (!(style & WS_VISIBLE)) return FALSE;
531 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
532 if (style & WS_MINIMIZE) return FALSE;
533 if (NtUserGetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
534 if (hwnd == NtUserGetDesktopWindow()) return FALSE;
535 if (NtUserGetWindowRect( hwnd, &rect ) && IsRectEmpty( &rect )) return FALSE;
536 return !(style & WS_DISABLED);
540 /**********************************************************************
541 * set_input_focus
543 * Try to force focus for embedded or non-managed windows.
545 static void set_input_focus( struct x11drv_win_data *data )
547 XWindowChanges changes;
548 DWORD timestamp;
550 if (!data->whole_window) return;
552 if (EVENT_x11_time_to_win32_time(0))
553 /* ICCCM says don't use CurrentTime, so try to use last message time if possible */
554 /* FIXME: this is not entirely correct */
555 timestamp = NtUserGetThreadInfo()->message_time - EVENT_x11_time_to_win32_time(0);
556 else
557 timestamp = CurrentTime;
559 /* Set X focus and install colormap */
560 changes.stack_mode = Above;
561 XConfigureWindow( data->display, data->whole_window, CWStackMode, &changes );
563 if (data->embedder)
564 xembed_request_focus( data->display, data->embedder, timestamp );
565 else
566 XSetInputFocus( data->display, data->whole_window, RevertToParent, timestamp );
570 /**********************************************************************
571 * set_focus
573 static void set_focus( Display *display, HWND hwnd, Time time )
575 HWND focus;
576 Window win;
577 GUITHREADINFO threadinfo;
579 TRACE( "setting foreground window to %p\n", hwnd );
580 NtUserSetForegroundWindow( hwnd );
582 threadinfo.cbSize = sizeof(threadinfo);
583 NtUserGetGUIThreadInfo( 0, &threadinfo );
584 focus = threadinfo.hwndFocus;
585 if (!focus) focus = threadinfo.hwndActive;
586 if (focus) focus = NtUserGetAncestor( focus, GA_ROOT );
587 win = X11DRV_get_whole_window(focus);
589 if (win)
591 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
592 XSetInputFocus( display, win, RevertToParent, time );
597 /**********************************************************************
598 * handle_manager_message
600 static void handle_manager_message( HWND hwnd, XClientMessageEvent *event )
602 if (hwnd != NtUserGetDesktopWindow()) return;
604 if (systray_atom && event->data.l[1] == systray_atom)
606 struct systray_change_owner_params params;
608 TRACE( "new owner %lx\n", event->data.l[2] );
610 params.event_handle = (UINT_PTR)event;
611 x11drv_client_func( client_func_systray_change_owner, &params, sizeof(params) );
616 /**********************************************************************
617 * handle_wm_protocols
619 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
621 Atom protocol = (Atom)event->data.l[0];
622 Time event_time = (Time)event->data.l[1];
624 if (!protocol) return;
626 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
628 update_user_time( event_time );
630 if (hwnd == NtUserGetDesktopWindow())
632 /* The desktop window does not have a close button that we can
633 * pretend to click. Therefore, we simply send it a close command. */
634 send_message( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
635 return;
638 /* Ignore the delete window request if the window has been disabled
639 * and we are in managed mode. This is to disallow applications from
640 * being closed by the window manager while in a modal state.
642 if (NtUserIsWindowEnabled( hwnd ))
644 HMENU hSysMenu;
646 if (NtUserGetClassLongW( hwnd, GCL_STYLE ) & CS_NOCLOSE) return;
647 hSysMenu = NtUserGetSystemMenu( hwnd, FALSE );
648 if (hSysMenu)
650 UINT state = NtUserThunkedMenuItemInfo( hSysMenu, SC_CLOSE, MF_BYCOMMAND,
651 NtUserGetMenuState, NULL, NULL );
652 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
653 return;
655 if (get_active_window() != hwnd)
657 LRESULT ma = send_message( hwnd, WM_MOUSEACTIVATE,
658 (WPARAM)NtUserGetAncestor( hwnd, GA_ROOT ),
659 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
660 switch(ma)
662 case MA_NOACTIVATEANDEAT:
663 case MA_ACTIVATEANDEAT:
664 return;
665 case MA_NOACTIVATE:
666 break;
667 case MA_ACTIVATE:
668 case 0:
669 NtUserSetActiveWindow( hwnd );
670 break;
671 default:
672 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
673 break;
677 NtUserPostMessage( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
680 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
682 HWND last_focus = x11drv_thread_data()->last_focus;
684 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
685 hwnd, NtUserIsWindowEnabled(hwnd), NtUserIsWindowVisible(hwnd),
686 (int)NtUserGetWindowLongW(hwnd, GWL_STYLE),
687 get_focus(), get_active_window(), NtUserGetForegroundWindow(), last_focus );
689 if (can_activate_window(hwnd))
691 /* simulate a mouse click on the menu to find out
692 * whether the window wants to be activated */
693 LRESULT ma = send_message( hwnd, WM_MOUSEACTIVATE,
694 (WPARAM)NtUserGetAncestor( hwnd, GA_ROOT ),
695 MAKELONG( HTMENU, WM_LBUTTONDOWN ) );
696 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
698 set_focus( event->display, hwnd, event_time );
699 return;
702 else if (hwnd == NtUserGetDesktopWindow())
704 hwnd = NtUserGetForegroundWindow();
705 if (!hwnd) hwnd = last_focus;
706 if (!hwnd) hwnd = NtUserGetDesktopWindow();
707 set_focus( event->display, hwnd, event_time );
708 return;
710 /* try to find some other window to give the focus to */
711 hwnd = get_focus();
712 if (hwnd) hwnd = NtUserGetAncestor( hwnd, GA_ROOT );
713 if (!hwnd) hwnd = get_active_window();
714 if (!hwnd) hwnd = last_focus;
715 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
717 else if (protocol == x11drv_atom(_NET_WM_PING))
719 XClientMessageEvent xev;
720 xev = *event;
722 TRACE("NET_WM Ping\n");
723 xev.window = DefaultRootWindow(xev.display);
724 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
729 static const char * const focus_details[] =
731 "NotifyAncestor",
732 "NotifyVirtual",
733 "NotifyInferior",
734 "NotifyNonlinear",
735 "NotifyNonlinearVirtual",
736 "NotifyPointer",
737 "NotifyPointerRoot",
738 "NotifyDetailNone"
741 static const char * const focus_modes[] =
743 "NotifyNormal",
744 "NotifyGrab",
745 "NotifyUngrab",
746 "NotifyWhileGrabbed"
749 /**********************************************************************
750 * X11DRV_FocusIn
752 static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev )
754 XFocusChangeEvent *event = &xev->xfocus;
756 if (!hwnd) return FALSE;
758 TRACE( "win %p xwin %lx detail=%s mode=%s\n", hwnd, event->window, focus_details[event->detail], focus_modes[event->mode] );
760 if (event->detail == NotifyPointer) return FALSE;
761 if (hwnd == NtUserGetDesktopWindow()) return FALSE;
763 switch (event->mode)
765 case NotifyGrab:
766 /* these are received when moving undecorated managed windows on mutter */
767 keyboard_grabbed = TRUE;
768 return FALSE;
769 case NotifyWhileGrabbed:
770 keyboard_grabbed = TRUE;
771 break;
772 case NotifyNormal:
773 keyboard_grabbed = FALSE;
774 break;
775 case NotifyUngrab:
776 keyboard_grabbed = FALSE;
777 retry_grab_clipping_window();
778 return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */
781 xim_set_focus( hwnd, TRUE );
783 if (use_take_focus)
785 if (hwnd == NtUserGetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE );
786 return TRUE;
789 if (!can_activate_window(hwnd))
791 HWND hwnd = get_focus();
792 if (hwnd) hwnd = NtUserGetAncestor( hwnd, GA_ROOT );
793 if (!hwnd) hwnd = get_active_window();
794 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
795 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
797 else NtUserSetForegroundWindow( hwnd );
798 return TRUE;
801 /**********************************************************************
802 * focus_out
804 static void focus_out( Display *display , HWND hwnd )
806 HWND hwnd_tmp;
807 Window focus_win;
808 int revert;
810 if (xim_in_compose_mode()) return;
812 x11drv_thread_data()->last_focus = hwnd;
813 xim_set_focus( hwnd, FALSE );
815 if (is_virtual_desktop())
817 if (hwnd == NtUserGetDesktopWindow()) reset_clipping_window();
818 return;
820 if (hwnd != NtUserGetForegroundWindow()) return;
821 if (!(NtUserGetWindowLongW( hwnd, GWL_STYLE ) & WS_MINIMIZE))
822 send_message( hwnd, WM_CANCELMODE, 0, 0 );
824 /* don't reset the foreground window, if the window which is
825 getting the focus is a Wine window */
827 XGetInputFocus( display, &focus_win, &revert );
828 if (focus_win)
830 if (XFindContext( display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
831 focus_win = 0;
834 if (!focus_win)
836 /* Abey : 6-Oct-99. Check again if the focus out window is the
837 Foreground window, because in most cases the messages sent
838 above must have already changed the foreground window, in which
839 case we don't have to change the foreground window to 0 */
840 if (hwnd == NtUserGetForegroundWindow())
842 TRACE( "lost focus, setting fg to desktop\n" );
843 NtUserSetForegroundWindow( NtUserGetDesktopWindow() );
848 /**********************************************************************
849 * X11DRV_FocusOut
851 * Note: only top-level windows get FocusOut events.
853 static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev )
855 XFocusChangeEvent *event = &xev->xfocus;
857 TRACE( "win %p xwin %lx detail=%s mode=%s\n", hwnd, event->window, focus_details[event->detail], focus_modes[event->mode] );
859 if (event->detail == NotifyPointer)
861 if (!hwnd && event->window == x11drv_thread_data()->clip_window) reset_clipping_window();
862 return TRUE;
864 if (!hwnd) return FALSE;
866 switch (event->mode)
868 case NotifyUngrab:
869 /* these are received when moving undecorated managed windows on mutter */
870 keyboard_grabbed = FALSE;
871 return FALSE;
872 case NotifyNormal:
873 keyboard_grabbed = FALSE;
874 break;
875 case NotifyWhileGrabbed:
876 keyboard_grabbed = TRUE;
877 break;
878 case NotifyGrab:
879 keyboard_grabbed = TRUE;
881 /* This will do nothing due to keyboard_grabbed == TRUE, but it
882 * will save the current clipping rect so we can restore it on
883 * FocusIn with NotifyUngrab mode.
885 retry_grab_clipping_window();
887 return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */
890 focus_out( event->display, hwnd );
891 return TRUE;
895 /***********************************************************************
896 * X11DRV_Expose
898 static BOOL X11DRV_Expose( HWND hwnd, XEvent *xev )
900 XExposeEvent *event = &xev->xexpose;
901 RECT rect, abs_rect;
902 POINT pos;
903 struct x11drv_win_data *data;
904 HRGN surface_region = 0;
905 UINT flags = RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN;
907 TRACE( "win %p (%lx) %d,%d %dx%d\n",
908 hwnd, event->window, event->x, event->y, event->width, event->height );
910 if (event->window != root_window)
912 pos.x = event->x;
913 pos.y = event->y;
915 else pos = root_to_virtual_screen( event->x, event->y );
917 if (!(data = get_win_data( hwnd ))) return FALSE;
919 rect.left = pos.x;
920 rect.top = pos.y;
921 rect.right = pos.x + event->width;
922 rect.bottom = pos.y + event->height;
924 if (event->window != data->client_window)
926 if (data->surface)
928 surface_region = expose_surface( data->surface, &rect );
929 if (!surface_region) flags = 0;
930 else NtGdiOffsetRgn( surface_region, data->whole_rect.left - data->client_rect.left,
931 data->whole_rect.top - data->client_rect.top );
933 if (data->vis.visualid != default_visual.visualid)
934 data->surface->funcs->flush( data->surface );
936 OffsetRect( &rect, data->whole_rect.left - data->client_rect.left,
937 data->whole_rect.top - data->client_rect.top );
940 if (event->window != root_window)
942 if (NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
943 mirror_rect( &data->client_rect, &rect );
944 abs_rect = rect;
945 NtUserMapWindowPoints( hwnd, 0, (POINT *)&abs_rect, 2 );
947 SERVER_START_REQ( update_window_zorder )
949 req->window = wine_server_user_handle( hwnd );
950 req->rect.left = abs_rect.left;
951 req->rect.top = abs_rect.top;
952 req->rect.right = abs_rect.right;
953 req->rect.bottom = abs_rect.bottom;
954 wine_server_call( req );
956 SERVER_END_REQ;
958 else flags &= ~RDW_ALLCHILDREN;
960 release_win_data( data );
962 if (flags) NtUserRedrawWindow( hwnd, &rect, surface_region, flags );
963 if (surface_region) NtGdiDeleteObjectApp( surface_region );
964 return TRUE;
968 /**********************************************************************
969 * X11DRV_MapNotify
971 static BOOL X11DRV_MapNotify( HWND hwnd, XEvent *event )
973 struct x11drv_win_data *data;
975 if (event->xany.window == x11drv_thread_data()->clip_window) return TRUE;
977 if (!(data = get_win_data( hwnd ))) return FALSE;
979 if (!data->managed && !data->embedded && data->mapped)
981 HWND hwndFocus = get_focus();
982 if (hwndFocus && NtUserIsChild( hwnd, hwndFocus ))
983 set_input_focus( data );
985 release_win_data( data );
986 return TRUE;
990 /**********************************************************************
991 * X11DRV_UnmapNotify
993 static BOOL X11DRV_UnmapNotify( HWND hwnd, XEvent *event )
995 return TRUE;
999 /***********************************************************************
1000 * reparent_notify
1002 static void reparent_notify( Display *display, HWND hwnd, Window xparent, int x, int y )
1004 HWND parent, old_parent;
1005 DWORD style;
1007 style = NtUserGetWindowLongW( hwnd, GWL_STYLE );
1008 if (xparent == root_window)
1010 parent = NtUserGetDesktopWindow();
1011 style = (style & ~WS_CHILD) | WS_POPUP;
1013 else
1015 if (!(parent = create_foreign_window( display, xparent ))) return;
1016 style = (style & ~WS_POPUP) | WS_CHILD;
1019 NtUserShowWindow( hwnd, SW_HIDE );
1020 old_parent = NtUserSetParent( hwnd, parent );
1021 NtUserSetWindowLong( hwnd, GWL_STYLE, style, FALSE );
1022 NtUserSetWindowPos( hwnd, HWND_TOP, x, y, 0, 0,
1023 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS |
1024 ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) );
1026 /* make old parent destroy itself if it no longer has children */
1027 if (old_parent != NtUserGetDesktopWindow()) NtUserPostMessage( old_parent, WM_CLOSE, 0, 0 );
1031 /***********************************************************************
1032 * X11DRV_ReparentNotify
1034 static BOOL X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
1036 XReparentEvent *event = &xev->xreparent;
1037 struct x11drv_win_data *data;
1039 if (!(data = get_win_data( hwnd ))) return FALSE;
1041 if (!data->embedded)
1043 release_win_data( data );
1044 return FALSE;
1047 if (data->whole_window)
1049 if (event->parent == root_window)
1051 TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
1052 data->embedder = 0;
1053 release_win_data( data );
1054 send_message( hwnd, WM_CLOSE, 0, 0 );
1055 return TRUE;
1057 data->embedder = event->parent;
1060 TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
1061 release_win_data( data );
1063 reparent_notify( event->display, hwnd, event->parent, event->x, event->y );
1064 return TRUE;
1068 /***********************************************************************
1069 * X11DRV_ConfigureNotify
1071 static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
1073 XConfigureEvent *event = &xev->xconfigure;
1074 struct x11drv_win_data *data;
1075 RECT rect;
1076 POINT pos;
1077 UINT flags;
1078 HWND parent;
1079 BOOL root_coords;
1080 int cx, cy, x = event->x, y = event->y;
1081 DWORD style;
1083 if (!hwnd) return FALSE;
1084 if (!(data = get_win_data( hwnd ))) return FALSE;
1085 if (!data->mapped || data->iconic) goto done;
1086 if (data->whole_window && !data->managed) goto done;
1087 /* ignore synthetic events on foreign windows */
1088 if (event->send_event && !data->whole_window) goto done;
1089 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
1091 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
1092 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
1093 event->serial, data->configure_serial );
1094 goto done;
1097 /* Get geometry */
1099 parent = NtUserGetAncestor( hwnd, GA_PARENT );
1100 root_coords = event->send_event; /* synthetic events are always in root coords */
1102 if (!root_coords && parent == NtUserGetDesktopWindow()) /* normal event, map coordinates to the root */
1104 Window child;
1105 XTranslateCoordinates( event->display, event->window, root_window,
1106 0, 0, &x, &y, &child );
1107 root_coords = TRUE;
1110 if (!root_coords)
1112 pos.x = x;
1113 pos.y = y;
1115 else pos = root_to_virtual_screen( x, y );
1117 X11DRV_X_to_window_rect( data, &rect, pos.x, pos.y, event->width, event->height );
1118 if (root_coords) NtUserMapWindowPoints( 0, parent, (POINT *)&rect, 2 );
1120 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
1121 hwnd, data->whole_window, (int)rect.left, (int)rect.top,
1122 (int)(rect.right-rect.left), (int)(rect.bottom-rect.top),
1123 event->x, event->y, event->width, event->height );
1125 /* Compare what has changed */
1127 x = rect.left;
1128 y = rect.top;
1129 cx = rect.right - rect.left;
1130 cy = rect.bottom - rect.top;
1131 flags = SWP_NOACTIVATE | SWP_NOZORDER;
1133 if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */
1135 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
1136 else
1137 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
1138 hwnd, (int)data->window_rect.left, (int)data->window_rect.top, x, y );
1140 if ((data->window_rect.right - data->window_rect.left == cx &&
1141 data->window_rect.bottom - data->window_rect.top == cy) ||
1142 IsRectEmpty( &data->window_rect ))
1143 flags |= SWP_NOSIZE;
1144 else
1145 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
1146 hwnd, (int)(data->window_rect.right - data->window_rect.left),
1147 (int)(data->window_rect.bottom - data->window_rect.top), cx, cy );
1149 style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE );
1150 if ((style & WS_CAPTION) == WS_CAPTION || !NtUserIsWindowRectFullScreen( &data->whole_rect ))
1152 read_net_wm_states( event->display, data );
1153 if ((data->net_wm_state & (1 << NET_WM_STATE_MAXIMIZED)))
1155 if (!(style & WS_MAXIMIZE))
1157 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
1158 release_win_data( data );
1159 send_message( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1160 return TRUE;
1163 else if (style & WS_MAXIMIZE)
1165 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
1166 release_win_data( data );
1167 send_message( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1168 return TRUE;
1172 if ((flags & (SWP_NOSIZE | SWP_NOMOVE)) != (SWP_NOSIZE | SWP_NOMOVE))
1174 release_win_data( data );
1175 NtUserSetWindowPos( hwnd, 0, x, y, cx, cy, flags );
1176 return TRUE;
1179 done:
1180 release_win_data( data );
1181 return FALSE;
1185 /**********************************************************************
1186 * X11DRV_GravityNotify
1188 static BOOL X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
1190 XGravityEvent *event = &xev->xgravity;
1191 struct x11drv_win_data *data = get_win_data( hwnd );
1192 RECT window_rect;
1193 int x, y;
1195 if (!data) return FALSE;
1197 if (data->whole_window) /* only handle this for foreign windows */
1199 release_win_data( data );
1200 return FALSE;
1203 x = event->x + data->window_rect.left - data->whole_rect.left;
1204 y = event->y + data->window_rect.top - data->whole_rect.top;
1206 TRACE( "win %p/%lx new X pos %d,%d (event %d,%d)\n",
1207 hwnd, data->whole_window, x, y, event->x, event->y );
1209 window_rect = data->window_rect;
1210 release_win_data( data );
1212 if (window_rect.left != x || window_rect.top != y)
1213 NtUserSetWindowPos( hwnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1215 return TRUE;
1219 /***********************************************************************
1220 * get_window_wm_state
1222 static int get_window_wm_state( Display *display, Window window )
1224 struct
1226 CARD32 state;
1227 XID icon;
1228 } *state;
1229 Atom type;
1230 int format, ret = -1;
1231 unsigned long count, remaining;
1233 if (!XGetWindowProperty( display, window, x11drv_atom(WM_STATE), 0,
1234 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1235 &type, &format, &count, &remaining, (unsigned char **)&state ))
1237 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1238 ret = state->state;
1239 XFree( state );
1241 return ret;
1245 /***********************************************************************
1246 * handle_wm_state_notify
1248 * Handle a PropertyNotify for WM_STATE.
1250 static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL update_window )
1252 struct x11drv_win_data *data = get_win_data( hwnd );
1253 UINT style;
1255 if (!data) return;
1257 switch(event->state)
1259 case PropertyDelete:
1260 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1261 data->wm_state = WithdrawnState;
1262 break;
1263 case PropertyNewValue:
1265 int old_state = data->wm_state;
1266 int new_state = get_window_wm_state( event->display, data->whole_window );
1267 if (new_state != -1 && new_state != data->wm_state)
1269 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1270 data->hwnd, data->whole_window, new_state, old_state );
1271 data->wm_state = new_state;
1272 /* ignore the initial state transition out of withdrawn state */
1273 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1274 if (!old_state) goto done;
1277 break;
1280 if (!update_window || !data->managed || !data->mapped) goto done;
1282 style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE );
1284 if (data->iconic && data->wm_state == NormalState) /* restore window */
1286 data->iconic = FALSE;
1287 read_net_wm_states( event->display, data );
1288 if ((style & WS_CAPTION) == WS_CAPTION && (data->net_wm_state & (1 << NET_WM_STATE_MAXIMIZED)))
1290 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1292 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1293 release_win_data( data );
1294 send_message( hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1295 return;
1297 TRACE( "not restoring to max win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1299 else
1301 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1303 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1304 release_win_data( data );
1305 if ((style & (WS_MINIMIZE | WS_VISIBLE)) == (WS_MINIMIZE | WS_VISIBLE))
1306 NtUserSetActiveWindow( hwnd );
1307 send_message( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1308 return;
1310 TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1313 else if (!data->iconic && data->wm_state == IconicState)
1315 data->iconic = TRUE;
1316 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1318 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1319 release_win_data( data );
1320 send_message( hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1321 return;
1323 TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1325 done:
1326 release_win_data( data );
1330 /***********************************************************************
1331 * X11DRV_PropertyNotify
1333 static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1335 XPropertyEvent *event = &xev->xproperty;
1337 if (!hwnd) return FALSE;
1338 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE );
1339 return TRUE;
1343 /* event filter to wait for a WM_STATE change notification on a window */
1344 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1346 if (event->xany.window != (Window)arg) return 0;
1347 return (event->type == DestroyNotify ||
1348 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1351 /***********************************************************************
1352 * wait_for_withdrawn_state
1354 void wait_for_withdrawn_state( HWND hwnd, BOOL set )
1356 Display *display = thread_display();
1357 struct x11drv_win_data *data;
1358 DWORD end = NtGetTickCount() + 2000;
1360 TRACE( "waiting for window %p to become %swithdrawn\n", hwnd, set ? "" : "not " );
1362 for (;;)
1364 XEvent event;
1365 Window window;
1366 int count = 0;
1368 if (!(data = get_win_data( hwnd ))) break;
1369 if (!data->managed || data->embedded || data->display != display) break;
1370 if (!(window = data->whole_window)) break;
1371 if (!data->mapped == !set)
1373 TRACE( "window %p/%lx now %smapped\n", hwnd, window, data->mapped ? "" : "un" );
1374 break;
1376 if ((data->wm_state == WithdrawnState) != !set)
1378 TRACE( "window %p/%lx state now %d\n", hwnd, window, data->wm_state );
1379 break;
1381 release_win_data( data );
1383 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)window ))
1385 count++;
1386 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1387 if (event.type == DestroyNotify) call_event_handler( display, &event );
1388 else handle_wm_state_notify( hwnd, &event.xproperty, FALSE );
1391 if (!count)
1393 struct pollfd pfd;
1394 int timeout = end - NtGetTickCount();
1396 pfd.fd = ConnectionNumber(display);
1397 pfd.events = POLLIN;
1398 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1400 FIXME( "window %p/%lx wait timed out\n", hwnd, window );
1401 return;
1405 release_win_data( data );
1409 /*****************************************************************
1410 * SetFocus (X11DRV.@)
1412 * Set the X focus.
1414 void X11DRV_SetFocus( HWND hwnd )
1416 struct x11drv_win_data *data;
1418 HWND parent;
1420 for (;;)
1422 if (!(data = get_win_data( hwnd ))) return;
1423 if (data->embedded) break;
1424 parent = NtUserGetAncestor( hwnd, GA_PARENT );
1425 if (!parent || parent == NtUserGetDesktopWindow()) break;
1426 release_win_data( data );
1427 hwnd = parent;
1429 if (!data->managed || data->embedder) set_input_focus( data );
1430 release_win_data( data );
1434 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1436 RECT tempRect;
1438 if (!NtUserIsWindowEnabled(hQueryWnd)) return 0;
1440 NtUserGetWindowRect(hQueryWnd, &tempRect);
1442 if(!PtInRect(&tempRect, *lpPt)) return 0;
1444 if (!(NtUserGetWindowLongW( hQueryWnd, GWL_STYLE ) & WS_MINIMIZE))
1446 POINT pt = *lpPt;
1447 NtUserScreenToClient( hQueryWnd, &pt );
1448 NtUserGetClientRect( hQueryWnd, &tempRect );
1450 if (PtInRect( &tempRect, pt))
1452 HWND ret = NtUserChildWindowFromPointEx( hQueryWnd, pt.x, pt.y,
1453 CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1454 if (ret && ret != hQueryWnd)
1456 ret = find_drop_window( ret, lpPt );
1457 if (ret) return ret;
1462 if(!(NtUserGetWindowLongW( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1464 NtUserScreenToClient( hQueryWnd, lpPt );
1466 return hQueryWnd;
1469 static void post_drop( HWND hwnd, DROPFILES *drop, ULONG size )
1471 drop->fWide = HandleToUlong( hwnd ); /* abuse fWide to pass window handle */
1472 x11drv_client_func( client_func_dnd_post_drop, drop, size );
1475 /**********************************************************************
1476 * EVENT_DropFromOffix
1478 * don't know if it still works (last Changelog is from 96/11/04)
1480 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1482 struct x11drv_win_data *data;
1483 POINT pt;
1484 unsigned long data_length;
1485 unsigned long aux_long;
1486 unsigned char* p_data = NULL;
1487 Atom atom_aux;
1488 int x, y, cx, cy, dummy, format;
1489 Window win, w_aux_root, w_aux_child;
1491 if (!(data = get_win_data( hWnd ))) return;
1492 cx = data->whole_rect.right - data->whole_rect.left;
1493 cy = data->whole_rect.bottom - data->whole_rect.top;
1494 win = data->whole_window;
1495 release_win_data( data );
1497 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1498 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1499 pt = root_to_virtual_screen( x, y );
1501 /* find out drop point and drop window */
1502 if (pt.x < 0 || pt.y < 0 || pt.x > cx || pt.y > cy)
1504 if (!(NtUserGetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1505 pt.x = pt.y = 0;
1507 else
1509 if (!find_drop_window( hWnd, &pt )) return;
1512 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1513 x11drv_atom(DndSelection), 0, 65535, FALSE,
1514 AnyPropertyType, &atom_aux, &format,
1515 &data_length, &aux_long, &p_data);
1517 if (!aux_long && p_data) /* don't bother if > 64K */
1519 DROPFILES *drop;
1520 size_t drop_size;
1522 drop = file_list_to_drop_files( p_data, get_property_size( format, data_length ), &drop_size );
1523 if (drop)
1525 post_drop( hWnd, drop, drop_size );
1526 free( drop );
1530 if (p_data) XFree(p_data);
1533 /**********************************************************************
1534 * EVENT_DropURLs
1536 * drop items are separated by \n
1537 * each item is prefixed by its mime type
1539 * event->data.l[3], event->data.l[4] contains drop x,y position
1541 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1543 struct x11drv_win_data *win_data;
1544 unsigned long data_length;
1545 unsigned long aux_long;
1546 unsigned char *p_data = NULL; /* property data */
1547 int x, y;
1548 DROPFILES *drop;
1549 int format;
1550 union {
1551 Atom atom_aux;
1552 int i;
1553 Window w_aux;
1554 unsigned int u;
1555 } u; /* unused */
1557 if (!(NtUserGetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1559 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1560 x11drv_atom(DndSelection), 0, 65535, FALSE,
1561 AnyPropertyType, &u.atom_aux, &format,
1562 &data_length, &aux_long, &p_data);
1563 if (aux_long)
1564 WARN("property too large, truncated!\n");
1565 TRACE("urls=%s\n", p_data);
1567 if (!aux_long && p_data) /* don't bother if > 64K */
1569 size_t drop_size;
1570 drop = uri_list_to_drop_files( p_data, get_property_size( format, data_length ), &drop_size );
1572 if (drop)
1574 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1575 &x, &y, &u.i, &u.i, &u.u);
1576 drop->pt = root_to_virtual_screen( x, y );
1578 if ((win_data = get_win_data( hWnd )))
1580 drop->fNC =
1581 (drop->pt.x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1582 drop->pt.y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1583 drop->pt.x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1584 drop->pt.y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1585 release_win_data( win_data );
1588 post_drop( hWnd, drop, drop_size );
1589 free( drop );
1592 if (p_data) XFree( p_data );
1596 /**********************************************************************
1597 * handle_xembed_protocol
1599 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1601 switch (event->data.l[1])
1603 case XEMBED_EMBEDDED_NOTIFY:
1605 struct x11drv_win_data *data = get_win_data( hwnd );
1606 if (!data) break;
1608 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1609 data->embedder = event->data.l[3];
1611 /* window has been marked as embedded before (e.g. systray) */
1612 if (data->embedded || !data->embedder /* broken QX11EmbedContainer implementation */)
1614 release_win_data( data );
1615 break;
1618 make_window_embedded( data );
1619 release_win_data( data );
1620 reparent_notify( event->display, hwnd, event->data.l[3], 0, 0 );
1622 break;
1624 case XEMBED_WINDOW_DEACTIVATE:
1625 TRACE( "win %p/%lx XEMBED_WINDOW_DEACTIVATE message\n", hwnd, event->window );
1626 focus_out( event->display, NtUserGetAncestor( hwnd, GA_ROOT ) );
1627 break;
1629 case XEMBED_FOCUS_OUT:
1630 TRACE( "win %p/%lx XEMBED_FOCUS_OUT message\n", hwnd, event->window );
1631 focus_out( event->display, NtUserGetAncestor( hwnd, GA_ROOT ) );
1632 break;
1634 case XEMBED_MODALITY_ON:
1635 TRACE( "win %p/%lx XEMBED_MODALITY_ON message\n", hwnd, event->window );
1636 NtUserEnableWindow( hwnd, FALSE );
1637 break;
1639 case XEMBED_MODALITY_OFF:
1640 TRACE( "win %p/%lx XEMBED_MODALITY_OFF message\n", hwnd, event->window );
1641 NtUserEnableWindow( hwnd, TRUE );
1642 break;
1644 default:
1645 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1646 hwnd, event->window, event->data.l[1], event->data.l[2] );
1647 break;
1652 /**********************************************************************
1653 * handle_dnd_protocol
1655 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1657 Window root, child;
1658 int root_x, root_y, child_x, child_y;
1659 unsigned int u;
1661 /* query window (drag&drop event contains only drag window) */
1662 XQueryPointer( event->display, root_window, &root, &child,
1663 &root_x, &root_y, &child_x, &child_y, &u);
1664 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1665 if (!hwnd) return;
1666 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1667 EVENT_DropFromOffiX(hwnd, event);
1668 else if (event->data.l[0] == DndURL)
1669 EVENT_DropURLs(hwnd, event);
1673 /**************************************************************************
1674 * handle_xdnd_enter_event
1676 * Handle an XdndEnter event.
1678 static void handle_xdnd_enter_event( HWND hWnd, XClientMessageEvent *event )
1680 struct format_entry *data;
1681 unsigned long count = 0;
1682 Atom *xdndtypes;
1683 size_t size;
1684 int version;
1686 version = (event->data.l[1] & 0xFF000000) >> 24;
1688 TRACE( "ver(%d) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
1689 version, (event->data.l[1] & 1),
1690 event->data.l[0], event->data.l[1], event->data.l[2],
1691 event->data.l[3], event->data.l[4] );
1693 if (version > WINE_XDND_VERSION)
1695 ERR("ignoring unsupported XDND version %d\n", version);
1696 return;
1699 /* If the source supports more than 3 data types we retrieve
1700 * the entire list. */
1701 if (event->data.l[1] & 1)
1703 Atom acttype;
1704 int actfmt;
1705 unsigned long bytesret;
1707 /* Request supported formats from source window */
1708 XGetWindowProperty( event->display, event->data.l[0], x11drv_atom(XdndTypeList),
1709 0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
1710 &bytesret, (unsigned char **)&xdndtypes );
1712 else
1714 count = 3;
1715 xdndtypes = (Atom *)&event->data.l[2];
1718 if (TRACE_ON(xdnd))
1720 unsigned int i;
1722 for (i = 0; i < count; i++)
1724 if (xdndtypes[i] != 0)
1726 char * pn = XGetAtomName( event->display, xdndtypes[i] );
1727 TRACE( "XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn );
1728 XFree( pn );
1733 data = import_xdnd_selection( event->display, event->window, x11drv_atom(XdndSelection),
1734 xdndtypes, count, &size );
1735 if (data)
1737 x11drv_client_func( client_func_dnd_enter_event, data, size );
1738 free( data );
1741 if (event->data.l[1] & 1)
1742 XFree(xdndtypes);
1746 static DWORD xdnd_action_to_drop_effect( long action )
1748 /* In Windows, nothing but the given effects is allowed.
1749 * In X the given action is just a hint, and you can always
1750 * XdndActionCopy and XdndActionPrivate, so be more permissive. */
1751 if (action == x11drv_atom(XdndActionCopy))
1752 return DROPEFFECT_COPY;
1753 else if (action == x11drv_atom(XdndActionMove))
1754 return DROPEFFECT_MOVE | DROPEFFECT_COPY;
1755 else if (action == x11drv_atom(XdndActionLink))
1756 return DROPEFFECT_LINK | DROPEFFECT_COPY;
1757 else if (action == x11drv_atom(XdndActionAsk))
1758 /* FIXME: should we somehow ask the user what to do here? */
1759 return DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
1761 FIXME( "unknown action %ld, assuming DROPEFFECT_COPY\n", action );
1762 return DROPEFFECT_COPY;
1766 static long drop_effect_to_xdnd_action( UINT effect )
1768 if (effect == DROPEFFECT_COPY)
1769 return x11drv_atom(XdndActionCopy);
1770 else if (effect == DROPEFFECT_MOVE)
1771 return x11drv_atom(XdndActionMove);
1772 else if (effect == DROPEFFECT_LINK)
1773 return x11drv_atom(XdndActionLink);
1774 else if (effect == DROPEFFECT_NONE)
1775 return None;
1777 FIXME( "unknown drop effect %u, assuming XdndActionCopy\n", effect );
1778 return x11drv_atom(XdndActionCopy);
1782 static void handle_xdnd_position_event( HWND hwnd, XClientMessageEvent *event )
1784 struct dnd_position_event_params params;
1785 XClientMessageEvent e;
1786 UINT effect;
1788 params.hwnd = HandleToUlong( hwnd );
1789 params.point = root_to_virtual_screen( event->data.l[2] >> 16, event->data.l[2] & 0xFFFF );
1790 params.effect = effect = xdnd_action_to_drop_effect( event->data.l[4] );
1792 effect = x11drv_client_func( client_func_dnd_position_event, &params, sizeof(params) );
1794 TRACE( "actionRequested(%ld) chosen(0x%x) at x(%d),y(%d)\n",
1795 event->data.l[4], effect, (int)params.point.x, (int)params.point.y );
1798 * Let source know if we're accepting the drop by
1799 * sending a status message.
1801 e.type = ClientMessage;
1802 e.display = event->display;
1803 e.window = event->data.l[0];
1804 e.message_type = x11drv_atom(XdndStatus);
1805 e.format = 32;
1806 e.data.l[0] = event->window;
1807 e.data.l[1] = !!effect;
1808 e.data.l[2] = 0; /* Empty Rect */
1809 e.data.l[3] = 0; /* Empty Rect */
1810 e.data.l[4] = drop_effect_to_xdnd_action( effect );
1811 XSendEvent( event->display, event->data.l[0], False, NoEventMask, (XEvent *)&e );
1815 static void handle_xdnd_drop_event( HWND hwnd, XClientMessageEvent *event )
1817 XClientMessageEvent e;
1818 DWORD effect;
1820 effect = x11drv_client_call( client_dnd_drop_event, HandleToUlong( hwnd ));
1822 /* Tell the target we are finished. */
1823 memset( &e, 0, sizeof(e) );
1824 e.type = ClientMessage;
1825 e.display = event->display;
1826 e.window = event->data.l[0];
1827 e.message_type = x11drv_atom(XdndFinished);
1828 e.format = 32;
1829 e.data.l[0] = event->window;
1830 e.data.l[1] = !!effect;
1831 e.data.l[2] = drop_effect_to_xdnd_action( effect );
1832 XSendEvent( event->display, event->data.l[0], False, NoEventMask, (XEvent *)&e );
1836 static void handle_xdnd_leave_event( HWND hwnd, XClientMessageEvent *event )
1838 x11drv_client_call( client_dnd_leave_event, 0 );
1842 struct client_message_handler
1844 int atom; /* protocol atom */
1845 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1848 static const struct client_message_handler client_messages[] =
1850 { XATOM_MANAGER, handle_manager_message },
1851 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1852 { XATOM__XEMBED, handle_xembed_protocol },
1853 { XATOM_DndProtocol, handle_dnd_protocol },
1854 { XATOM_XdndEnter, handle_xdnd_enter_event },
1855 { XATOM_XdndPosition, handle_xdnd_position_event },
1856 { XATOM_XdndDrop, handle_xdnd_drop_event },
1857 { XATOM_XdndLeave, handle_xdnd_leave_event }
1861 /**********************************************************************
1862 * X11DRV_ClientMessage
1864 static BOOL X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1866 XClientMessageEvent *event = &xev->xclient;
1867 unsigned int i;
1869 if (!hwnd) return FALSE;
1871 if (event->format != 32)
1873 WARN( "Don't know how to handle format %d\n", event->format );
1874 return FALSE;
1877 for (i = 0; i < ARRAY_SIZE( client_messages ); i++)
1879 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1881 client_messages[i].handler( hwnd, event );
1882 return TRUE;
1885 TRACE( "no handler found for %ld\n", event->message_type );
1886 return FALSE;