ntoskrnl.exe: Add a stub for KeQueryGroupAffinity.
[wine.git] / dlls / winex11.drv / event.c
blobc377bbc99843262f45b354e63d63a3cdc1ea5b76
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 | QS_RAWINPUT)) != 0;
233 case ButtonPress:
234 case ButtonRelease:
235 return (mask & (QS_MOUSEBUTTON | QS_RAWINPUT)) != 0;
236 case MotionNotify:
237 case EnterNotify:
238 case LeaveNotify:
239 return (mask & (QS_MOUSEMOVE | QS_RAWINPUT)) != 0;
240 case Expose:
241 return (mask & QS_PAINT) != 0;
242 case FocusIn:
243 case FocusOut:
244 case MapNotify:
245 case UnmapNotify:
246 case ConfigureNotify:
247 case PropertyNotify:
248 case ClientMessage:
249 return (mask & QS_POSTMESSAGE) != 0;
250 #ifdef GenericEvent
251 case GenericEvent:
252 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
253 if (event->xcookie.extension == xinput2_opcode) return (mask & QS_INPUT) != 0;
254 #endif
255 /* fallthrough */
256 #endif
257 default:
258 return (mask & QS_SENDMESSAGE) != 0;
263 enum event_merge_action
265 MERGE_DISCARD, /* discard the old event */
266 MERGE_HANDLE, /* handle the old event */
267 MERGE_KEEP, /* keep the old event for future merging */
268 MERGE_IGNORE /* ignore the new event, keep the old one */
271 /***********************************************************************
272 * merge_raw_motion_events
274 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
275 static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawEvent *next )
277 int i, j, k;
278 unsigned char mask;
280 if (!prev->valuators.mask_len) return MERGE_HANDLE;
281 if (!next->valuators.mask_len) return MERGE_HANDLE;
283 mask = prev->valuators.mask[0] | next->valuators.mask[0];
284 if (mask == next->valuators.mask[0]) /* keep next */
286 for (i = j = k = 0; i < 8; i++)
288 if (XIMaskIsSet( prev->valuators.mask, i ))
289 next->valuators.values[j] += prev->valuators.values[k++];
290 if (XIMaskIsSet( next->valuators.mask, i )) j++;
292 TRACE( "merging duplicate GenericEvent\n" );
293 return MERGE_DISCARD;
295 if (mask == prev->valuators.mask[0]) /* keep prev */
297 for (i = j = k = 0; i < 8; i++)
299 if (XIMaskIsSet( next->valuators.mask, i ))
300 prev->valuators.values[j] += next->valuators.values[k++];
301 if (XIMaskIsSet( prev->valuators.mask, i )) j++;
303 TRACE( "merging duplicate GenericEvent\n" );
304 return MERGE_IGNORE;
306 /* can't merge events with disjoint masks */
307 return MERGE_HANDLE;
309 #endif
311 /***********************************************************************
312 * merge_events
314 * Try to merge 2 consecutive events.
316 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
318 switch (prev->type)
320 case ConfigureNotify:
321 switch (next->type)
323 case ConfigureNotify:
324 if (prev->xany.window == next->xany.window)
326 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
327 return MERGE_DISCARD;
329 break;
330 case Expose:
331 case PropertyNotify:
332 return MERGE_KEEP;
334 break;
335 case MotionNotify:
336 switch (next->type)
338 case MotionNotify:
339 if (prev->xany.window == next->xany.window)
341 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
342 return MERGE_DISCARD;
344 break;
345 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
346 case GenericEvent:
347 if (next->xcookie.extension != xinput2_opcode) break;
348 if (next->xcookie.evtype != XI_RawMotion) break;
349 if (x11drv_thread_data()->warp_serial) break;
350 return MERGE_KEEP;
352 break;
353 case GenericEvent:
354 if (prev->xcookie.extension != xinput2_opcode) break;
355 if (prev->xcookie.evtype != XI_RawMotion) break;
356 switch (next->type)
358 case GenericEvent:
359 if (next->xcookie.extension != xinput2_opcode) break;
360 if (next->xcookie.evtype != XI_RawMotion) break;
361 if (x11drv_thread_data()->warp_serial) break;
362 return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data );
363 #endif
365 break;
367 return MERGE_HANDLE;
371 /***********************************************************************
372 * call_event_handler
374 static inline BOOL call_event_handler( Display *display, XEvent *event )
376 HWND hwnd;
377 XEvent *prev;
378 struct x11drv_thread_data *thread_data;
379 BOOL ret;
381 if (!handlers[event->type])
383 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
384 return FALSE; /* no handler, ignore it */
387 #ifdef GenericEvent
388 if (event->type == GenericEvent) hwnd = 0; else
389 #endif
390 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
391 hwnd = 0; /* not for a registered window */
392 if (!hwnd && event->xany.window == root_window) hwnd = NtUserGetDesktopWindow();
394 TRACE( "%lu %s for hwnd/window %p/%lx\n",
395 event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window );
396 thread_data = x11drv_thread_data();
397 prev = thread_data->current_event;
398 thread_data->current_event = event;
399 ret = handlers[event->type]( hwnd, event );
400 thread_data->current_event = prev;
401 return ret;
405 /***********************************************************************
406 * process_events
408 BOOL process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
410 XEvent event, prev_event;
411 int count = 0;
412 BOOL queued = FALSE;
413 enum event_merge_action action = MERGE_DISCARD;
415 prev_event.type = 0;
416 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
418 count++;
419 if (XFilterEvent( &event, None ))
422 * SCIM on linux filters key events strangely. It does not filter the
423 * KeyPress events for these keys however it does filter the
424 * KeyRelease events. This causes wine to become very confused as
425 * to the keyboard state.
427 * We need to let those KeyRelease events be processed so that the
428 * keyboard state is correct.
430 if (event.type == KeyRelease)
432 KeySym keysym = 0;
433 XKeyEvent *keyevent = &event.xkey;
435 XLookupString(keyevent, NULL, 0, &keysym, NULL);
436 if (!(keysym == XK_Shift_L ||
437 keysym == XK_Shift_R ||
438 keysym == XK_Control_L ||
439 keysym == XK_Control_R ||
440 keysym == XK_Alt_R ||
441 keysym == XK_Alt_L ||
442 keysym == XK_Meta_R ||
443 keysym == XK_Meta_L))
444 continue; /* not a key we care about, ignore it */
446 else
447 continue; /* filtered, ignore it */
449 get_event_data( &event );
450 if (prev_event.type) action = merge_events( &prev_event, &event );
451 switch( action )
453 case MERGE_HANDLE: /* handle prev, keep new */
454 queued |= call_event_handler( display, &prev_event );
455 /* fall through */
456 case MERGE_DISCARD: /* discard prev, keep new */
457 free_event_data( &prev_event );
458 prev_event = event;
459 break;
460 case MERGE_KEEP: /* handle new, keep prev for future merging */
461 queued |= call_event_handler( display, &event );
462 /* fall through */
463 case MERGE_IGNORE: /* ignore new, keep prev for future merging */
464 free_event_data( &event );
465 break;
468 if (prev_event.type) queued |= call_event_handler( display, &prev_event );
469 free_event_data( &prev_event );
470 XFlush( gdi_display );
471 if (count) TRACE( "processed %d events, returning %d\n", count, queued );
472 return queued;
476 /***********************************************************************
477 * ProcessEvents (X11DRV.@)
479 BOOL X11DRV_ProcessEvents( DWORD mask )
481 struct x11drv_thread_data *data = x11drv_thread_data();
483 if (!data) return FALSE;
484 if (data->current_event) mask = 0; /* don't process nested events */
486 return process_events( data->display, filter_event, mask );
489 /***********************************************************************
490 * EVENT_x11_time_to_win32_time
492 * Make our timer and the X timer line up as best we can
493 * Pass 0 to retrieve the current adjustment value (times -1)
495 DWORD EVENT_x11_time_to_win32_time(Time time)
497 static DWORD adjust = 0;
498 DWORD now = NtGetTickCount();
499 DWORD ret;
501 if (! adjust && time != 0)
503 ret = now;
504 adjust = time - now;
506 else
508 /* If we got an event in the 'future', then our clock is clearly wrong.
509 If we got it more than 10000 ms in the future, then it's most likely
510 that the clock has wrapped. */
512 ret = time - adjust;
513 if (ret > now && ((ret - now) < 10000) && time != 0)
515 adjust += ret - now;
516 ret -= ret - now;
520 return ret;
524 /*******************************************************************
525 * can_activate_window
527 * Check if we can activate the specified window.
529 static inline BOOL can_activate_window( HWND hwnd )
531 LONG style = NtUserGetWindowLongW( hwnd, GWL_STYLE );
532 RECT rect;
534 if (!(style & WS_VISIBLE)) return FALSE;
535 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
536 if (style & WS_MINIMIZE) return FALSE;
537 if (NtUserGetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
538 if (hwnd == NtUserGetDesktopWindow()) return FALSE;
539 if (NtUserGetWindowRect( hwnd, &rect, get_win_monitor_dpi( hwnd ) ) && IsRectEmpty( &rect )) return FALSE;
540 return !(style & WS_DISABLED);
544 /**********************************************************************
545 * set_input_focus
547 * Try to force focus for embedded or non-managed windows.
549 static void set_input_focus( struct x11drv_win_data *data )
551 XWindowChanges changes;
552 DWORD timestamp;
554 if (!data->whole_window) return;
556 if (EVENT_x11_time_to_win32_time(0))
557 /* ICCCM says don't use CurrentTime, so try to use last message time if possible */
558 /* FIXME: this is not entirely correct */
559 timestamp = NtUserGetThreadInfo()->message_time - EVENT_x11_time_to_win32_time(0);
560 else
561 timestamp = CurrentTime;
563 /* Set X focus and install colormap */
564 changes.stack_mode = Above;
565 XConfigureWindow( data->display, data->whole_window, CWStackMode, &changes );
567 if (data->embedder)
568 xembed_request_focus( data->display, data->embedder, timestamp );
569 else
570 XSetInputFocus( data->display, data->whole_window, RevertToParent, timestamp );
574 /**********************************************************************
575 * set_focus
577 static void set_focus( Display *display, HWND hwnd, Time time )
579 HWND focus;
580 Window win;
581 GUITHREADINFO threadinfo;
583 TRACE( "setting foreground window to %p\n", hwnd );
584 NtUserSetForegroundWindow( hwnd );
586 threadinfo.cbSize = sizeof(threadinfo);
587 NtUserGetGUIThreadInfo( 0, &threadinfo );
588 focus = threadinfo.hwndFocus;
589 if (!focus) focus = threadinfo.hwndActive;
590 if (focus) focus = NtUserGetAncestor( focus, GA_ROOT );
591 win = X11DRV_get_whole_window(focus);
593 if (win)
595 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
596 XSetInputFocus( display, win, RevertToParent, time );
601 /**********************************************************************
602 * handle_manager_message
604 static void handle_manager_message( HWND hwnd, XClientMessageEvent *event )
606 if (hwnd != NtUserGetDesktopWindow()) return;
608 if (systray_atom && event->data.l[1] == systray_atom)
610 TRACE( "new owner %lx\n", event->data.l[2] );
611 NtUserPostMessage( systray_hwnd, WM_USER + 1, 0, 0 );
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 BOOL is_current_process_focused(void)
751 Display *display = x11drv_thread_data()->display;
752 Window focus;
753 int revert;
754 HWND hwnd;
756 XGetInputFocus( display, &focus, &revert );
757 if (focus && !XFindContext( display, focus, winContext, (char **)&hwnd )) return TRUE;
758 return FALSE;
761 /**********************************************************************
762 * X11DRV_FocusIn
764 static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev )
766 XFocusChangeEvent *event = &xev->xfocus;
767 BOOL was_grabbed;
769 if (!hwnd) return FALSE;
771 TRACE( "win %p xwin %lx detail=%s mode=%s\n", hwnd, event->window, focus_details[event->detail], focus_modes[event->mode] );
773 if (event->detail == NotifyPointer) return FALSE;
774 /* when focusing in the virtual desktop window, re-apply the cursor clipping rect */
775 if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) reapply_cursor_clipping();
776 if (hwnd == NtUserGetDesktopWindow()) return FALSE;
778 x11drv_thread_data()->keymapnotify_hwnd = hwnd;
780 /* when keyboard grab is released, re-apply the cursor clipping rect */
781 was_grabbed = keyboard_grabbed;
782 keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed;
783 if (was_grabbed > keyboard_grabbed) reapply_cursor_clipping();
784 /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */
785 if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE;
787 xim_set_focus( hwnd, TRUE );
789 if (use_take_focus) return TRUE;
791 if (!can_activate_window(hwnd))
793 HWND hwnd = get_focus();
794 if (hwnd) hwnd = NtUserGetAncestor( hwnd, GA_ROOT );
795 if (!hwnd) hwnd = get_active_window();
796 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
797 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
799 else NtUserSetForegroundWindow( hwnd );
800 return TRUE;
803 /**********************************************************************
804 * focus_out
806 static void focus_out( Display *display , HWND hwnd )
808 if (xim_in_compose_mode()) return;
810 x11drv_thread_data()->last_focus = hwnd;
811 xim_set_focus( hwnd, FALSE );
813 if (is_virtual_desktop()) return;
814 if (hwnd != NtUserGetForegroundWindow()) return;
815 if (!(NtUserGetWindowLongW( hwnd, GWL_STYLE ) & WS_MINIMIZE))
816 send_message( hwnd, WM_CANCELMODE, 0, 0 );
818 /* don't reset the foreground window, if the window which is
819 getting the focus is a Wine window */
821 if (!is_current_process_focused())
823 /* Abey : 6-Oct-99. Check again if the focus out window is the
824 Foreground window, because in most cases the messages sent
825 above must have already changed the foreground window, in which
826 case we don't have to change the foreground window to 0 */
827 if (hwnd == NtUserGetForegroundWindow())
829 TRACE( "lost focus, setting fg to desktop\n" );
830 NtUserSetForegroundWindow( NtUserGetDesktopWindow() );
835 /**********************************************************************
836 * X11DRV_FocusOut
838 * Note: only top-level windows get FocusOut events.
840 static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev )
842 XFocusChangeEvent *event = &xev->xfocus;
844 TRACE( "win %p xwin %lx detail=%s mode=%s\n", hwnd, event->window, focus_details[event->detail], focus_modes[event->mode] );
846 if (event->detail == NotifyPointer)
848 if (!hwnd && event->window == x11drv_thread_data()->clip_window)
850 NtUserClipCursor( NULL );
851 /* NtUserClipCursor will ask the foreground window to ungrab the cursor, but
852 * it might not be responsive, so unmap the clipping window ourselves too */
853 XUnmapWindow( event->display, event->window );
855 return TRUE;
857 if (!hwnd) return FALSE;
859 /* in virtual desktop mode or when keyboard is grabbed, release any cursor grab but keep the clipping rect */
860 keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed;
861 if (is_virtual_desktop() || keyboard_grabbed) ungrab_clipping_window();
862 /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */
863 if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE;
865 focus_out( event->display, hwnd );
866 return TRUE;
870 /***********************************************************************
871 * X11DRV_Expose
873 static BOOL X11DRV_Expose( HWND hwnd, XEvent *xev )
875 XExposeEvent *event = &xev->xexpose;
876 RECT rect, abs_rect;
877 POINT pos;
878 struct x11drv_win_data *data;
879 HRGN surface_region = 0;
880 UINT flags = RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN;
882 TRACE( "win %p (%lx) %d,%d %dx%d\n",
883 hwnd, event->window, event->x, event->y, event->width, event->height );
885 if (event->window != root_window)
887 pos.x = event->x;
888 pos.y = event->y;
890 else pos = root_to_virtual_screen( event->x, event->y );
892 if (!(data = get_win_data( hwnd ))) return FALSE;
894 rect.left = pos.x;
895 rect.top = pos.y;
896 rect.right = pos.x + event->width;
897 rect.bottom = pos.y + event->height;
899 if (event->window != data->client_window)
901 if (data->surface)
903 surface_region = expose_surface( data->surface, &rect );
904 if (!surface_region) flags = 0;
905 else NtGdiOffsetRgn( surface_region, data->whole_rect.left - data->client_rect.left,
906 data->whole_rect.top - data->client_rect.top );
908 if (data->vis.visualid != default_visual.visualid)
909 window_surface_flush( data->surface );
911 OffsetRect( &rect, data->whole_rect.left - data->client_rect.left,
912 data->whole_rect.top - data->client_rect.top );
915 if (event->window != root_window)
917 if (NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
918 mirror_rect( &data->client_rect, &rect );
919 abs_rect = rect;
920 NtUserMapWindowPoints( hwnd, 0, (POINT *)&abs_rect, 2, 0 /* per-monitor DPI */ );
922 SERVER_START_REQ( update_window_zorder )
924 req->window = wine_server_user_handle( hwnd );
925 req->rect = wine_server_rectangle( abs_rect );
926 wine_server_call( req );
928 SERVER_END_REQ;
930 else flags &= ~RDW_ALLCHILDREN;
932 release_win_data( data );
934 if (flags) redraw_window( hwnd, &rect, surface_region, flags );
935 if (surface_region) NtGdiDeleteObjectApp( surface_region );
936 return TRUE;
940 /**********************************************************************
941 * X11DRV_MapNotify
943 static BOOL X11DRV_MapNotify( HWND hwnd, XEvent *event )
945 struct x11drv_win_data *data;
947 if (event->xany.window == x11drv_thread_data()->clip_window) return TRUE;
949 if (!(data = get_win_data( hwnd ))) return FALSE;
951 if (!data->managed && !data->embedded && data->mapped)
953 HWND hwndFocus = get_focus();
954 if (hwndFocus && NtUserIsChild( hwnd, hwndFocus ))
955 set_input_focus( data );
957 release_win_data( data );
958 return TRUE;
962 /**********************************************************************
963 * X11DRV_UnmapNotify
965 static BOOL X11DRV_UnmapNotify( HWND hwnd, XEvent *event )
967 return TRUE;
971 /***********************************************************************
972 * reparent_notify
974 static void reparent_notify( Display *display, HWND hwnd, Window xparent, int x, int y )
976 HWND parent, old_parent;
977 DWORD style, flags = 0;
979 style = NtUserGetWindowLongW( hwnd, GWL_STYLE );
980 if (xparent == root_window)
982 parent = NtUserGetDesktopWindow();
983 style = (style & ~WS_CHILD) | WS_POPUP;
985 else
987 if (!(parent = create_foreign_window( display, xparent ))) return;
988 style = (style & ~WS_POPUP) | WS_CHILD;
991 NtUserShowWindow( hwnd, SW_HIDE );
992 old_parent = NtUserSetParent( hwnd, parent );
993 NtUserSetWindowLong( hwnd, GWL_STYLE, style, FALSE );
995 if (style & WS_VISIBLE) flags = SWP_SHOWWINDOW;
996 set_window_pos( hwnd, HWND_TOP, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS | flags );
998 /* make old parent destroy itself if it no longer has children */
999 if (old_parent != NtUserGetDesktopWindow()) NtUserPostMessage( old_parent, WM_CLOSE, 0, 0 );
1003 /***********************************************************************
1004 * X11DRV_ReparentNotify
1006 static BOOL X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
1008 XReparentEvent *event = &xev->xreparent;
1009 struct x11drv_win_data *data;
1011 if (!(data = get_win_data( hwnd ))) return FALSE;
1013 if (!data->embedded)
1015 release_win_data( data );
1016 return FALSE;
1019 if (data->whole_window)
1021 if (event->parent == root_window)
1023 TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
1024 data->embedder = 0;
1025 release_win_data( data );
1026 send_message( hwnd, WM_CLOSE, 0, 0 );
1027 return TRUE;
1029 data->embedder = event->parent;
1032 TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
1033 release_win_data( data );
1035 reparent_notify( event->display, hwnd, event->parent, event->x, event->y );
1036 return TRUE;
1040 /***********************************************************************
1041 * X11DRV_ConfigureNotify
1043 static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
1045 XConfigureEvent *event = &xev->xconfigure;
1046 struct x11drv_win_data *data;
1047 RECT rect;
1048 POINT pos;
1049 UINT flags, dpi;
1050 HWND parent;
1051 BOOL root_coords;
1052 int cx, cy, x = event->x, y = event->y;
1053 DWORD style;
1055 if (!hwnd) return FALSE;
1056 if (!(data = get_win_data( hwnd ))) return FALSE;
1057 if (!data->mapped || data->iconic) goto done;
1058 if (data->whole_window && !data->managed) goto done;
1059 /* ignore synthetic events on foreign windows */
1060 if (event->send_event && !data->whole_window) goto done;
1061 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
1063 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
1064 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
1065 event->serial, data->configure_serial );
1066 goto done;
1069 /* Get geometry */
1071 dpi = get_win_monitor_dpi( data->hwnd );
1072 parent = NtUserGetAncestor( hwnd, GA_PARENT );
1073 root_coords = event->send_event; /* synthetic events are always in root coords */
1075 if (!root_coords && parent == NtUserGetDesktopWindow()) /* normal event, map coordinates to the root */
1077 Window child;
1078 XTranslateCoordinates( event->display, event->window, root_window,
1079 0, 0, &x, &y, &child );
1080 root_coords = TRUE;
1083 if (!root_coords)
1085 pos.x = x;
1086 pos.y = y;
1088 else pos = root_to_virtual_screen( x, y );
1090 X11DRV_X_to_window_rect( data, &rect, pos.x, pos.y, event->width, event->height );
1091 if (root_coords) NtUserMapWindowPoints( 0, parent, (POINT *)&rect, 2, 0 /* per-monitor DPI */ );
1093 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
1094 hwnd, data->whole_window, (int)rect.left, (int)rect.top,
1095 (int)(rect.right-rect.left), (int)(rect.bottom-rect.top),
1096 event->x, event->y, event->width, event->height );
1098 /* Compare what has changed */
1100 x = rect.left;
1101 y = rect.top;
1102 cx = rect.right - rect.left;
1103 cy = rect.bottom - rect.top;
1104 flags = SWP_NOACTIVATE | SWP_NOZORDER;
1106 if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */
1108 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
1109 else
1110 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
1111 hwnd, (int)data->window_rect.left, (int)data->window_rect.top, x, y );
1113 if ((data->window_rect.right - data->window_rect.left == cx &&
1114 data->window_rect.bottom - data->window_rect.top == cy) ||
1115 IsRectEmpty( &data->window_rect ))
1116 flags |= SWP_NOSIZE;
1117 else
1118 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
1119 hwnd, (int)(data->window_rect.right - data->window_rect.left),
1120 (int)(data->window_rect.bottom - data->window_rect.top), cx, cy );
1122 style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE );
1123 if ((style & WS_CAPTION) == WS_CAPTION || !NtUserIsWindowRectFullScreen( &data->whole_rect, dpi ))
1125 read_net_wm_states( event->display, data );
1126 if ((data->net_wm_state & (1 << NET_WM_STATE_MAXIMIZED)))
1128 if (!(style & WS_MAXIMIZE))
1130 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
1131 release_win_data( data );
1132 send_message( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1133 return TRUE;
1136 else if (style & WS_MAXIMIZE)
1138 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
1139 release_win_data( data );
1140 send_message( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1141 return TRUE;
1145 if ((flags & (SWP_NOSIZE | SWP_NOMOVE)) != (SWP_NOSIZE | SWP_NOMOVE))
1147 release_win_data( data );
1148 set_window_pos( hwnd, 0, x, y, cx, cy, flags );
1149 return TRUE;
1152 done:
1153 release_win_data( data );
1154 return FALSE;
1158 /**********************************************************************
1159 * X11DRV_GravityNotify
1161 static BOOL X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
1163 XGravityEvent *event = &xev->xgravity;
1164 struct x11drv_win_data *data = get_win_data( hwnd );
1165 RECT window_rect;
1166 int x, y;
1168 if (!data) return FALSE;
1170 if (data->whole_window) /* only handle this for foreign windows */
1172 release_win_data( data );
1173 return FALSE;
1176 x = event->x + data->window_rect.left - data->whole_rect.left;
1177 y = event->y + data->window_rect.top - data->whole_rect.top;
1179 TRACE( "win %p/%lx new X pos %d,%d (event %d,%d)\n",
1180 hwnd, data->whole_window, x, y, event->x, event->y );
1182 window_rect = data->window_rect;
1183 release_win_data( data );
1185 if (window_rect.left != x || window_rect.top != y)
1186 set_window_pos( hwnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1188 return TRUE;
1192 /***********************************************************************
1193 * get_window_wm_state
1195 static int get_window_wm_state( Display *display, Window window )
1197 struct
1199 CARD32 state;
1200 XID icon;
1201 } *state;
1202 Atom type;
1203 int format, ret = -1;
1204 unsigned long count, remaining;
1206 if (!XGetWindowProperty( display, window, x11drv_atom(WM_STATE), 0,
1207 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1208 &type, &format, &count, &remaining, (unsigned char **)&state ))
1210 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1211 ret = state->state;
1212 XFree( state );
1214 return ret;
1218 /***********************************************************************
1219 * handle_wm_state_notify
1221 * Handle a PropertyNotify for WM_STATE.
1223 static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL update_window )
1225 struct x11drv_win_data *data = get_win_data( hwnd );
1226 UINT style;
1228 if (!data) return;
1230 switch(event->state)
1232 case PropertyDelete:
1233 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1234 data->wm_state = WithdrawnState;
1235 break;
1236 case PropertyNewValue:
1238 int old_state = data->wm_state;
1239 int new_state = get_window_wm_state( event->display, data->whole_window );
1240 if (new_state != -1 && new_state != data->wm_state)
1242 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1243 data->hwnd, data->whole_window, new_state, old_state );
1244 data->wm_state = new_state;
1245 /* ignore the initial state transition out of withdrawn state */
1246 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1247 if (!old_state) goto done;
1250 break;
1253 if (!update_window || !data->managed || !data->mapped) goto done;
1255 style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE );
1257 if (data->iconic && data->wm_state == NormalState) /* restore window */
1259 data->iconic = FALSE;
1260 read_net_wm_states( event->display, data );
1261 if ((style & WS_CAPTION) == WS_CAPTION && (data->net_wm_state & (1 << NET_WM_STATE_MAXIMIZED)))
1263 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1265 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1266 release_win_data( data );
1267 send_message( hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1268 return;
1270 TRACE( "not restoring to max win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1272 else
1274 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1276 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1277 release_win_data( data );
1278 if ((style & (WS_MINIMIZE | WS_VISIBLE)) == (WS_MINIMIZE | WS_VISIBLE))
1279 NtUserSetActiveWindow( hwnd );
1280 send_message( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1281 return;
1283 TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1286 else if (!data->iconic && data->wm_state == IconicState)
1288 data->iconic = TRUE;
1289 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1291 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1292 release_win_data( data );
1293 send_message( hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1294 return;
1296 TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1298 done:
1299 release_win_data( data );
1303 /***********************************************************************
1304 * X11DRV_PropertyNotify
1306 static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1308 XPropertyEvent *event = &xev->xproperty;
1310 if (!hwnd) return FALSE;
1311 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE );
1312 return TRUE;
1316 /* event filter to wait for a WM_STATE change notification on a window */
1317 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1319 if (event->xany.window != (Window)arg) return 0;
1320 return (event->type == DestroyNotify ||
1321 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1324 /***********************************************************************
1325 * wait_for_withdrawn_state
1327 void wait_for_withdrawn_state( HWND hwnd, BOOL set )
1329 Display *display = thread_display();
1330 struct x11drv_win_data *data;
1331 DWORD end = NtGetTickCount() + 2000;
1333 TRACE( "waiting for window %p to become %swithdrawn\n", hwnd, set ? "" : "not " );
1335 for (;;)
1337 XEvent event;
1338 Window window;
1339 int count = 0;
1341 if (!(data = get_win_data( hwnd ))) break;
1342 if (!data->managed || data->embedded || data->display != display) break;
1343 if (!(window = data->whole_window)) break;
1344 if (!data->mapped == !set)
1346 TRACE( "window %p/%lx now %smapped\n", hwnd, window, data->mapped ? "" : "un" );
1347 break;
1349 if ((data->wm_state == WithdrawnState) != !set)
1351 TRACE( "window %p/%lx state now %d\n", hwnd, window, data->wm_state );
1352 break;
1354 release_win_data( data );
1356 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)window ))
1358 count++;
1359 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1360 if (event.type == DestroyNotify) call_event_handler( display, &event );
1361 else handle_wm_state_notify( hwnd, &event.xproperty, FALSE );
1364 if (!count)
1366 struct pollfd pfd;
1367 int timeout = end - NtGetTickCount();
1369 pfd.fd = ConnectionNumber(display);
1370 pfd.events = POLLIN;
1371 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1373 FIXME( "window %p/%lx wait timed out\n", hwnd, window );
1374 return;
1378 release_win_data( data );
1382 /*****************************************************************
1383 * SetFocus (X11DRV.@)
1385 * Set the X focus.
1387 void X11DRV_SetFocus( HWND hwnd )
1389 struct x11drv_win_data *data;
1391 HWND parent;
1393 for (;;)
1395 if (!(data = get_win_data( hwnd ))) return;
1396 if (data->embedded) break;
1397 parent = NtUserGetAncestor( hwnd, GA_PARENT );
1398 if (!parent || parent == NtUserGetDesktopWindow()) break;
1399 release_win_data( data );
1400 hwnd = parent;
1402 if (!data->managed || data->embedder) set_input_focus( data );
1403 release_win_data( data );
1407 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1409 UINT dpi = get_win_monitor_dpi( hQueryWnd );
1410 RECT tempRect;
1412 if (!NtUserIsWindowEnabled(hQueryWnd)) return 0;
1414 NtUserGetWindowRect( hQueryWnd, &tempRect, dpi );
1416 if(!PtInRect(&tempRect, *lpPt)) return 0;
1418 if (!(NtUserGetWindowLongW( hQueryWnd, GWL_STYLE ) & WS_MINIMIZE))
1420 POINT pt = *lpPt;
1421 NtUserMapWindowPoints( 0, hQueryWnd, &pt, 1, 0 /* per-monitor DPI */ );
1422 NtUserGetClientRect( hQueryWnd, &tempRect, dpi );
1424 if (PtInRect( &tempRect, pt))
1426 HWND ret = child_window_from_point( hQueryWnd, pt.x, pt.y, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED );
1427 if (ret && ret != hQueryWnd)
1429 ret = find_drop_window( ret, lpPt );
1430 if (ret) return ret;
1435 if(!(NtUserGetWindowLongW( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1437 NtUserMapWindowPoints( 0, hQueryWnd, lpPt, 1, 0 /* per-monitor DPI */ );
1439 return hQueryWnd;
1442 static void post_drop( HWND hwnd, DROPFILES *drop, ULONG size )
1444 drop->fWide = HandleToUlong( hwnd ); /* abuse fWide to pass window handle */
1445 x11drv_client_func( client_func_dnd_post_drop, drop, size );
1448 /**********************************************************************
1449 * EVENT_DropFromOffix
1451 * don't know if it still works (last Changelog is from 96/11/04)
1453 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1455 struct x11drv_win_data *data;
1456 POINT pt;
1457 unsigned long data_length;
1458 unsigned long aux_long;
1459 unsigned char* p_data = NULL;
1460 Atom atom_aux;
1461 int x, y, cx, cy, dummy, format;
1462 Window win, w_aux_root, w_aux_child;
1464 if (!(data = get_win_data( hWnd ))) return;
1465 cx = data->whole_rect.right - data->whole_rect.left;
1466 cy = data->whole_rect.bottom - data->whole_rect.top;
1467 win = data->whole_window;
1468 release_win_data( data );
1470 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1471 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1472 pt = root_to_virtual_screen( x, y );
1474 /* find out drop point and drop window */
1475 if (pt.x < 0 || pt.y < 0 || pt.x > cx || pt.y > cy)
1477 if (!(NtUserGetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1478 pt.x = pt.y = 0;
1480 else
1482 if (!find_drop_window( hWnd, &pt )) return;
1485 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1486 x11drv_atom(DndSelection), 0, 65535, FALSE,
1487 AnyPropertyType, &atom_aux, &format,
1488 &data_length, &aux_long, &p_data);
1490 if (!aux_long && p_data) /* don't bother if > 64K */
1492 DROPFILES *drop;
1493 size_t drop_size;
1495 drop = file_list_to_drop_files( p_data, get_property_size( format, data_length ), &drop_size );
1496 if (drop)
1498 post_drop( hWnd, drop, drop_size );
1499 free( drop );
1503 if (p_data) XFree(p_data);
1506 /**********************************************************************
1507 * EVENT_DropURLs
1509 * drop items are separated by \n
1510 * each item is prefixed by its mime type
1512 * event->data.l[3], event->data.l[4] contains drop x,y position
1514 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1516 struct x11drv_win_data *win_data;
1517 unsigned long data_length;
1518 unsigned long aux_long;
1519 unsigned char *p_data = NULL; /* property data */
1520 int x, y;
1521 DROPFILES *drop;
1522 int format;
1523 union {
1524 Atom atom_aux;
1525 int i;
1526 Window w_aux;
1527 unsigned int u;
1528 } u; /* unused */
1530 if (!(NtUserGetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1532 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1533 x11drv_atom(DndSelection), 0, 65535, FALSE,
1534 AnyPropertyType, &u.atom_aux, &format,
1535 &data_length, &aux_long, &p_data);
1536 if (aux_long)
1537 WARN("property too large, truncated!\n");
1538 TRACE("urls=%s\n", p_data);
1540 if (!aux_long && p_data) /* don't bother if > 64K */
1542 size_t drop_size;
1543 drop = uri_list_to_drop_files( p_data, get_property_size( format, data_length ), &drop_size );
1545 if (drop)
1547 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1548 &x, &y, &u.i, &u.i, &u.u);
1549 drop->pt = root_to_virtual_screen( x, y );
1551 if ((win_data = get_win_data( hWnd )))
1553 drop->fNC =
1554 (drop->pt.x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1555 drop->pt.y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1556 drop->pt.x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1557 drop->pt.y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1558 release_win_data( win_data );
1561 post_drop( hWnd, drop, drop_size );
1562 free( drop );
1565 if (p_data) XFree( p_data );
1569 /**********************************************************************
1570 * handle_xembed_protocol
1572 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1574 switch (event->data.l[1])
1576 case XEMBED_EMBEDDED_NOTIFY:
1578 struct x11drv_win_data *data = get_win_data( hwnd );
1579 if (!data) break;
1581 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1582 data->embedder = event->data.l[3];
1584 /* window has been marked as embedded before (e.g. systray) */
1585 if (data->embedded || !data->embedder /* broken QX11EmbedContainer implementation */)
1587 release_win_data( data );
1588 break;
1591 make_window_embedded( data );
1592 release_win_data( data );
1593 reparent_notify( event->display, hwnd, event->data.l[3], 0, 0 );
1595 break;
1597 case XEMBED_WINDOW_DEACTIVATE:
1598 TRACE( "win %p/%lx XEMBED_WINDOW_DEACTIVATE message\n", hwnd, event->window );
1599 focus_out( event->display, NtUserGetAncestor( hwnd, GA_ROOT ) );
1600 break;
1602 case XEMBED_FOCUS_OUT:
1603 TRACE( "win %p/%lx XEMBED_FOCUS_OUT message\n", hwnd, event->window );
1604 focus_out( event->display, NtUserGetAncestor( hwnd, GA_ROOT ) );
1605 break;
1607 case XEMBED_MODALITY_ON:
1608 TRACE( "win %p/%lx XEMBED_MODALITY_ON message\n", hwnd, event->window );
1609 NtUserEnableWindow( hwnd, FALSE );
1610 break;
1612 case XEMBED_MODALITY_OFF:
1613 TRACE( "win %p/%lx XEMBED_MODALITY_OFF message\n", hwnd, event->window );
1614 NtUserEnableWindow( hwnd, TRUE );
1615 break;
1617 default:
1618 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1619 hwnd, event->window, event->data.l[1], event->data.l[2] );
1620 break;
1625 /**********************************************************************
1626 * handle_dnd_protocol
1628 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1630 Window root, child;
1631 int root_x, root_y, child_x, child_y;
1632 unsigned int u;
1634 /* query window (drag&drop event contains only drag window) */
1635 XQueryPointer( event->display, root_window, &root, &child,
1636 &root_x, &root_y, &child_x, &child_y, &u);
1637 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1638 if (!hwnd) return;
1639 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1640 EVENT_DropFromOffiX(hwnd, event);
1641 else if (event->data.l[0] == DndURL)
1642 EVENT_DropURLs(hwnd, event);
1646 /**************************************************************************
1647 * handle_xdnd_enter_event
1649 * Handle an XdndEnter event.
1651 static void handle_xdnd_enter_event( HWND hWnd, XClientMessageEvent *event )
1653 struct format_entry *data;
1654 unsigned long count = 0;
1655 Atom *xdndtypes;
1656 size_t size;
1657 int version;
1659 version = (event->data.l[1] & 0xFF000000) >> 24;
1661 TRACE( "ver(%d) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
1662 version, (event->data.l[1] & 1),
1663 event->data.l[0], event->data.l[1], event->data.l[2],
1664 event->data.l[3], event->data.l[4] );
1666 if (version > WINE_XDND_VERSION)
1668 ERR("ignoring unsupported XDND version %d\n", version);
1669 return;
1672 /* If the source supports more than 3 data types we retrieve
1673 * the entire list. */
1674 if (event->data.l[1] & 1)
1676 Atom acttype;
1677 int actfmt;
1678 unsigned long bytesret;
1680 /* Request supported formats from source window */
1681 XGetWindowProperty( event->display, event->data.l[0], x11drv_atom(XdndTypeList),
1682 0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
1683 &bytesret, (unsigned char **)&xdndtypes );
1685 else
1687 count = 3;
1688 xdndtypes = (Atom *)&event->data.l[2];
1691 if (TRACE_ON(xdnd))
1693 unsigned int i;
1695 for (i = 0; i < count; i++)
1697 if (xdndtypes[i] != 0)
1699 char * pn = XGetAtomName( event->display, xdndtypes[i] );
1700 TRACE( "XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn );
1701 XFree( pn );
1706 data = import_xdnd_selection( event->display, event->window, x11drv_atom(XdndSelection),
1707 xdndtypes, count, &size );
1708 if (data)
1710 x11drv_client_func( client_func_dnd_enter_event, data, size );
1711 free( data );
1714 if (event->data.l[1] & 1)
1715 XFree(xdndtypes);
1719 static DWORD xdnd_action_to_drop_effect( long action )
1721 /* In Windows, nothing but the given effects is allowed.
1722 * In X the given action is just a hint, and you can always
1723 * XdndActionCopy and XdndActionPrivate, so be more permissive. */
1724 if (action == x11drv_atom(XdndActionCopy))
1725 return DROPEFFECT_COPY;
1726 else if (action == x11drv_atom(XdndActionMove))
1727 return DROPEFFECT_MOVE | DROPEFFECT_COPY;
1728 else if (action == x11drv_atom(XdndActionLink))
1729 return DROPEFFECT_LINK | DROPEFFECT_COPY;
1730 else if (action == x11drv_atom(XdndActionAsk))
1731 /* FIXME: should we somehow ask the user what to do here? */
1732 return DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
1734 FIXME( "unknown action %ld, assuming DROPEFFECT_COPY\n", action );
1735 return DROPEFFECT_COPY;
1739 static long drop_effect_to_xdnd_action( UINT effect )
1741 if (effect == DROPEFFECT_COPY)
1742 return x11drv_atom(XdndActionCopy);
1743 else if (effect == DROPEFFECT_MOVE)
1744 return x11drv_atom(XdndActionMove);
1745 else if (effect == DROPEFFECT_LINK)
1746 return x11drv_atom(XdndActionLink);
1747 else if (effect == DROPEFFECT_NONE)
1748 return None;
1750 FIXME( "unknown drop effect %u, assuming XdndActionCopy\n", effect );
1751 return x11drv_atom(XdndActionCopy);
1755 static void handle_xdnd_position_event( HWND hwnd, XClientMessageEvent *event )
1757 struct dnd_position_event_params params;
1758 XClientMessageEvent e;
1759 void *ret_ptr;
1760 ULONG ret_len;
1761 UINT effect;
1763 params.hwnd = HandleToUlong( hwnd );
1764 params.point = root_to_virtual_screen( event->data.l[2] >> 16, event->data.l[2] & 0xFFFF );
1765 params.effect = effect = xdnd_action_to_drop_effect( event->data.l[4] );
1767 if (KeUserModeCallback( client_func_dnd_position_event, &params, sizeof(params),
1768 &ret_ptr, &ret_len ) || ret_len != sizeof(effect))
1769 return;
1770 effect = *(UINT *)ret_ptr;
1772 TRACE( "actionRequested(%ld) chosen(0x%x) at x(%d),y(%d)\n",
1773 event->data.l[4], effect, (int)params.point.x, (int)params.point.y );
1776 * Let source know if we're accepting the drop by
1777 * sending a status message.
1779 e.type = ClientMessage;
1780 e.display = event->display;
1781 e.window = event->data.l[0];
1782 e.message_type = x11drv_atom(XdndStatus);
1783 e.format = 32;
1784 e.data.l[0] = event->window;
1785 e.data.l[1] = !!effect;
1786 e.data.l[2] = 0; /* Empty Rect */
1787 e.data.l[3] = 0; /* Empty Rect */
1788 e.data.l[4] = drop_effect_to_xdnd_action( effect );
1789 XSendEvent( event->display, event->data.l[0], False, NoEventMask, (XEvent *)&e );
1793 static void handle_xdnd_drop_event( HWND hwnd, XClientMessageEvent *event )
1795 XClientMessageEvent e;
1796 void *ret_ptr;
1797 ULONG ret_len;
1798 ULONG arg = HandleToUlong( hwnd );
1799 UINT effect;
1801 if (KeUserModeCallback( client_func_dnd_drop_event, &arg, sizeof(arg),
1802 &ret_ptr, &ret_len ) || ret_len != sizeof(effect))
1803 return;
1804 effect = *(UINT *)ret_ptr;
1806 /* Tell the target we are finished. */
1807 memset( &e, 0, sizeof(e) );
1808 e.type = ClientMessage;
1809 e.display = event->display;
1810 e.window = event->data.l[0];
1811 e.message_type = x11drv_atom(XdndFinished);
1812 e.format = 32;
1813 e.data.l[0] = event->window;
1814 e.data.l[1] = !!effect;
1815 e.data.l[2] = drop_effect_to_xdnd_action( effect );
1816 XSendEvent( event->display, event->data.l[0], False, NoEventMask, (XEvent *)&e );
1820 static void handle_xdnd_leave_event( HWND hwnd, XClientMessageEvent *event )
1822 x11drv_client_func( client_func_dnd_leave_event, NULL, 0 );
1826 struct client_message_handler
1828 int atom; /* protocol atom */
1829 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1832 static const struct client_message_handler client_messages[] =
1834 { XATOM_MANAGER, handle_manager_message },
1835 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1836 { XATOM__XEMBED, handle_xembed_protocol },
1837 { XATOM_DndProtocol, handle_dnd_protocol },
1838 { XATOM_XdndEnter, handle_xdnd_enter_event },
1839 { XATOM_XdndPosition, handle_xdnd_position_event },
1840 { XATOM_XdndDrop, handle_xdnd_drop_event },
1841 { XATOM_XdndLeave, handle_xdnd_leave_event }
1845 /**********************************************************************
1846 * X11DRV_ClientMessage
1848 static BOOL X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1850 XClientMessageEvent *event = &xev->xclient;
1851 unsigned int i;
1853 if (!hwnd) return FALSE;
1855 if (event->format != 32)
1857 WARN( "Don't know how to handle format %d\n", event->format );
1858 return FALSE;
1861 for (i = 0; i < ARRAY_SIZE( client_messages ); i++)
1863 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1865 client_messages[i].handler( hwnd, event );
1866 return TRUE;
1869 TRACE( "no handler found for %ld\n", event->message_type );
1870 return FALSE;