4 * Copyright 1993 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #ifdef HAVE_SYS_POLL_H
30 #include <X11/Xatom.h>
31 #include <X11/keysym.h>
33 #include <X11/Xresource.h>
34 #include <X11/Xutil.h>
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
49 /* avoid conflict with field names in included win32 headers */
51 #include "shlobj.h" /* DROPFILES */
54 #include "wine/server.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(event);
59 extern BOOL ximInComposeMode;
61 #define DndNotDnd -1 /* OffiX drag&drop */
73 #define DndURL 128 /* KDE drag&drop */
75 #define XEMBED_EMBEDDED_NOTIFY 0
76 #define XEMBED_WINDOW_ACTIVATE 1
77 #define XEMBED_WINDOW_DEACTIVATE 2
78 #define XEMBED_REQUEST_FOCUS 3
79 #define XEMBED_FOCUS_IN 4
80 #define XEMBED_FOCUS_OUT 5
81 #define XEMBED_FOCUS_NEXT 6
82 #define XEMBED_FOCUS_PREV 7
83 #define XEMBED_MODALITY_ON 10
84 #define XEMBED_MODALITY_OFF 11
85 #define XEMBED_REGISTER_ACCELERATOR 12
86 #define XEMBED_UNREGISTER_ACCELERATOR 13
87 #define XEMBED_ACTIVATE_ACCELERATOR 14
90 static void X11DRV_FocusIn( HWND hwnd, XEvent *event );
91 static void X11DRV_FocusOut( HWND hwnd, XEvent *event );
92 static void X11DRV_Expose( HWND hwnd, XEvent *event );
93 static void X11DRV_MapNotify( HWND hwnd, XEvent *event );
94 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *event );
95 static void X11DRV_ConfigureNotify( HWND hwnd, XEvent *event );
96 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
97 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
98 static void X11DRV_GravityNotify( HWND hwnd, XEvent *event );
102 int type; /* event type */
103 x11drv_event_handler handler; /* corresponding handler function */
106 #define MAX_EVENT_HANDLERS 64
108 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
110 /* list must be sorted by event type */
111 { KeyPress, X11DRV_KeyEvent },
112 { KeyRelease, X11DRV_KeyEvent },
113 { ButtonPress, X11DRV_ButtonPress },
114 { ButtonRelease, X11DRV_ButtonRelease },
115 { MotionNotify, X11DRV_MotionNotify },
116 { EnterNotify, X11DRV_EnterNotify },
118 { FocusIn, X11DRV_FocusIn },
119 { FocusOut, X11DRV_FocusOut },
120 { KeymapNotify, X11DRV_KeymapNotify },
121 { Expose, X11DRV_Expose },
124 /* VisibilityNotify */
126 { DestroyNotify, X11DRV_DestroyNotify },
128 { MapNotify, X11DRV_MapNotify },
130 { ReparentNotify, X11DRV_ReparentNotify },
131 { ConfigureNotify, X11DRV_ConfigureNotify },
132 /* ConfigureRequest */
133 { GravityNotify, X11DRV_GravityNotify },
135 /* CirculateNotify */
136 /* CirculateRequest */
137 { PropertyNotify, X11DRV_PropertyNotify },
138 { SelectionClear, X11DRV_SelectionClear },
139 { SelectionRequest, X11DRV_SelectionRequest },
140 /* SelectionNotify */
142 { ClientMessage, X11DRV_ClientMessage },
143 { MappingNotify, X11DRV_MappingNotify },
146 static int nb_event_handlers = 20; /* change this if you add handlers above */
149 /* return the name of an X event */
150 static const char *dbgstr_event( int type )
152 static const char * const event_names[] =
154 "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
155 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
156 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
157 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
158 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
159 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
160 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
161 "ClientMessage", "MappingNotify"
164 if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
165 return wine_dbg_sprintf( "Extension event %d", type );
169 /***********************************************************************
172 * Find the handler for a given event type. Caller must hold the x11 lock.
174 static inline x11drv_event_handler find_handler( int type )
176 int min = 0, max = nb_event_handlers - 1;
180 int pos = (min + max) / 2;
181 if (handlers[pos].type == type) return handlers[pos].handler;
182 if (handlers[pos].type > type) max = pos - 1;
189 /***********************************************************************
190 * X11DRV_register_event_handler
192 * Register a handler for a given event type.
193 * If already registered, overwrite the previous handler.
195 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
201 max = nb_event_handlers - 1;
204 int pos = (min + max) / 2;
205 if (handlers[pos].type == type)
207 handlers[pos].handler = handler;
210 if (handlers[pos].type > type) max = pos - 1;
213 /* insert it between max and min */
214 memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
215 handlers[min].type = type;
216 handlers[min].handler = handler;
218 assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
221 TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
225 /***********************************************************************
228 static Bool filter_event( Display *display, XEvent *event, char *arg )
230 ULONG_PTR mask = (ULONG_PTR)arg;
232 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
240 return (mask & QS_KEY) != 0;
243 return (mask & QS_MOUSEBUTTON) != 0;
247 return (mask & QS_MOUSEMOVE) != 0;
249 return (mask & QS_PAINT) != 0;
254 case ConfigureNotify:
257 return (mask & QS_POSTMESSAGE) != 0;
259 return (mask & QS_SENDMESSAGE) != 0;
264 enum event_merge_action
266 MERGE_DISCARD, /* discard the old event */
267 MERGE_HANDLE, /* handle the old event */
268 MERGE_KEEP /* keep the old event for future merging */
271 /***********************************************************************
274 * Try to merge 2 consecutive events.
276 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
280 case ConfigureNotify:
283 case ConfigureNotify:
284 if (prev->xany.window == next->xany.window)
286 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
287 return MERGE_DISCARD;
296 if (prev->xany.window == next->xany.window && next->type == MotionNotify)
298 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
299 return MERGE_DISCARD;
307 /***********************************************************************
310 static inline void call_event_handler( Display *display, XEvent *event )
313 x11drv_event_handler handler;
315 struct x11drv_thread_data *thread_data;
317 if (!(handler = find_handler( event->type )))
319 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
320 return; /* no handler, ignore it */
323 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
324 hwnd = 0; /* not for a registered window */
325 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
327 TRACE( "%lu %s for hwnd/window %p/%lx\n",
328 event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window );
330 thread_data = x11drv_thread_data();
331 prev = thread_data->current_event;
332 thread_data->current_event = event;
333 handler( hwnd, event );
334 thread_data->current_event = prev;
339 /***********************************************************************
342 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
344 XEvent event, prev_event;
346 enum event_merge_action action = MERGE_DISCARD;
350 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
353 if (XFilterEvent( &event, None ))
356 * SCIM on linux filters key events strangely. It does not filter the
357 * KeyPress events for these keys however it does filter the
358 * KeyRelease events. This causes wine to become very confused as
359 * to the keyboard state.
361 * We need to let those KeyRelease events be processed so that the
362 * keyboard state is correct.
364 if (event.type == KeyRelease)
367 XKeyEvent *keyevent = &event.xkey;
369 XLookupString(keyevent, NULL, 0, &keysym, NULL);
370 if (!(keysym == XK_Shift_L ||
371 keysym == XK_Shift_R ||
372 keysym == XK_Control_L ||
373 keysym == XK_Control_R ||
374 keysym == XK_Alt_R ||
375 keysym == XK_Alt_L ||
376 keysym == XK_Meta_R ||
377 keysym == XK_Meta_L))
378 continue; /* not a key we care about, ignore it */
381 continue; /* filtered, ignore it */
383 if (prev_event.type) action = merge_events( &prev_event, &event );
386 case MERGE_DISCARD: /* discard prev, keep new */
389 case MERGE_HANDLE: /* handle prev, keep new */
390 call_event_handler( display, &prev_event );
393 case MERGE_KEEP: /* handle new, keep prev for future merging */
394 call_event_handler( display, &event );
398 if (prev_event.type) call_event_handler( display, &prev_event );
399 XFlush( gdi_display );
401 if (count) TRACE( "processed %d events\n", count );
406 /***********************************************************************
407 * MsgWaitForMultipleObjectsEx (X11DRV.@)
409 DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
410 DWORD timeout, DWORD mask, DWORD flags )
413 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
417 if (!count && !timeout) return WAIT_TIMEOUT;
418 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
419 timeout, flags & MWMO_ALERTABLE );
422 if (data->current_event) mask = 0; /* don't process nested events */
424 if (process_events( data->display, filter_event, mask )) ret = count - 1;
425 else if (count || timeout)
427 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
428 timeout, flags & MWMO_ALERTABLE );
429 if (ret == count - 1) process_events( data->display, filter_event, mask );
431 else ret = WAIT_TIMEOUT;
436 /***********************************************************************
437 * EVENT_x11_time_to_win32_time
439 * Make our timer and the X timer line up as best we can
440 * Pass 0 to retrieve the current adjustment value (times -1)
442 DWORD EVENT_x11_time_to_win32_time(Time time)
444 static DWORD adjust = 0;
445 DWORD now = GetTickCount();
448 if (! adjust && time != 0)
455 /* If we got an event in the 'future', then our clock is clearly wrong.
456 If we got it more than 10000 ms in the future, then it's most likely
457 that the clock has wrapped. */
460 if (ret > now && ((ret - now) < 10000) && time != 0)
471 /*******************************************************************
472 * can_activate_window
474 * Check if we can activate the specified window.
476 static inline BOOL can_activate_window( HWND hwnd )
478 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
479 if (!(style & WS_VISIBLE)) return FALSE;
480 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
481 if (style & WS_MINIMIZE) return FALSE;
482 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
483 if (hwnd == GetDesktopWindow()) return FALSE;
484 return !(style & WS_DISABLED);
488 /**********************************************************************
491 static void set_focus( Display *display, HWND hwnd, Time time )
495 GUITHREADINFO threadinfo;
497 TRACE( "setting foreground window to %p\n", hwnd );
498 SetForegroundWindow( hwnd );
500 threadinfo.cbSize = sizeof(threadinfo);
501 GetGUIThreadInfo(0, &threadinfo);
502 focus = threadinfo.hwndFocus;
503 if (!focus) focus = threadinfo.hwndActive;
504 if (focus) focus = GetAncestor( focus, GA_ROOT );
505 win = X11DRV_get_whole_window(focus);
509 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
511 XSetInputFocus( display, win, RevertToParent, time );
517 /**********************************************************************
518 * handle_manager_message
520 static void handle_manager_message( HWND hwnd, XClientMessageEvent *event )
522 if (hwnd != GetDesktopWindow()) return;
523 if (systray_atom && event->data.l[1] == systray_atom)
524 change_systray_owner( event->display, event->data.l[2] );
528 /**********************************************************************
529 * handle_wm_protocols
531 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
533 Atom protocol = (Atom)event->data.l[0];
534 Time event_time = (Time)event->data.l[1];
536 if (!protocol) return;
538 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
540 update_user_time( event_time );
542 if (hwnd == GetDesktopWindow())
544 /* The desktop window does not have a close button that we can
545 * pretend to click. Therefore, we simply send it a close command. */
546 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
550 /* Ignore the delete window request if the window has been disabled
551 * and we are in managed mode. This is to disallow applications from
552 * being closed by the window manager while in a modal state.
554 if (IsWindowEnabled(hwnd))
558 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
559 hSysMenu = GetSystemMenu(hwnd, FALSE);
562 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
563 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
566 if (GetActiveWindow() != hwnd)
568 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
569 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
570 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
573 case MA_NOACTIVATEANDEAT:
574 case MA_ACTIVATEANDEAT:
580 SetActiveWindow(hwnd);
583 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
588 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
591 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
593 HWND last_focus = x11drv_thread_data()->last_focus;
595 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
596 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
597 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
599 if (can_activate_window(hwnd))
601 /* simulate a mouse click on the caption to find out
602 * whether the window wants to be activated */
603 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
604 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
605 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
606 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
608 set_focus( event->display, hwnd, event_time );
612 else if (hwnd == GetDesktopWindow())
614 hwnd = GetForegroundWindow();
615 if (!hwnd) hwnd = last_focus;
616 if (!hwnd) hwnd = GetDesktopWindow();
617 set_focus( event->display, hwnd, event_time );
620 /* try to find some other window to give the focus to */
622 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
623 if (!hwnd) hwnd = GetActiveWindow();
624 if (!hwnd) hwnd = last_focus;
625 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
627 else if (protocol == x11drv_atom(_NET_WM_PING))
629 XClientMessageEvent xev;
632 TRACE("NET_WM Ping\n");
634 xev.window = DefaultRootWindow(xev.display);
635 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
637 /* this line is semi-stolen from gtk2 */
638 TRACE("NET_WM Pong\n");
643 static const char * const focus_details[] =
649 "NotifyNonlinearVirtual",
655 /**********************************************************************
658 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
660 XFocusChangeEvent *event = &xev->xfocus;
665 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
667 if (event->detail == NotifyPointer) return;
669 if ((xic = X11DRV_get_ic( hwnd )))
675 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
677 if (!can_activate_window(hwnd))
679 HWND hwnd = GetFocus();
680 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
681 if (!hwnd) hwnd = GetActiveWindow();
682 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
683 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
685 else SetForegroundWindow( hwnd );
689 /**********************************************************************
692 * Note: only top-level windows get FocusOut events.
694 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
696 XFocusChangeEvent *event = &xev->xfocus;
704 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
706 if (event->detail == NotifyPointer) return;
707 if (ximInComposeMode) return;
709 x11drv_thread_data()->last_focus = hwnd;
710 if ((xic = X11DRV_get_ic( hwnd )))
713 XUnsetICFocus( xic );
716 if (hwnd != GetForegroundWindow()) return;
717 if (root_window != DefaultRootWindow(event->display)) return;
718 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
720 /* don't reset the foreground window, if the window which is
721 getting the focus is a Wine window */
724 XGetInputFocus( event->display, &focus_win, &revert );
727 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
734 /* Abey : 6-Oct-99. Check again if the focus out window is the
735 Foreground window, because in most cases the messages sent
736 above must have already changed the foreground window, in which
737 case we don't have to change the foreground window to 0 */
738 if (hwnd == GetForegroundWindow())
740 TRACE( "lost focus, setting fg to desktop\n" );
741 SetForegroundWindow( GetDesktopWindow() );
747 /***********************************************************************
750 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
752 XExposeEvent *event = &xev->xexpose;
754 struct x11drv_win_data *data;
755 int flags = RDW_INVALIDATE | RDW_ERASE;
757 TRACE( "win %p (%lx) %d,%d %dx%d\n",
758 hwnd, event->window, event->x, event->y, event->width, event->height );
760 if (!(data = X11DRV_get_win_data( hwnd ))) return;
762 rect.left = event->x;
764 rect.right = event->x + event->width;
765 rect.bottom = event->y + event->height;
766 if (event->window == data->whole_window)
768 OffsetRect( &rect, data->whole_rect.left - data->client_rect.left,
769 data->whole_rect.top - data->client_rect.top );
773 if (event->window != root_window)
775 if (GetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
776 mirror_rect( &data->client_rect, &rect );
778 SERVER_START_REQ( update_window_zorder )
780 req->window = wine_server_user_handle( hwnd );
781 req->rect.left = rect.left;
782 req->rect.top = rect.top;
783 req->rect.right = rect.right;
784 req->rect.bottom = rect.bottom;
785 wine_server_call( req );
789 flags |= RDW_ALLCHILDREN;
792 RedrawWindow( hwnd, &rect, 0, flags );
796 /**********************************************************************
799 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
801 struct x11drv_win_data *data;
803 if (!(data = X11DRV_get_win_data( hwnd ))) return;
804 if (!data->mapped || data->embedded) return;
808 HWND hwndFocus = GetFocus();
809 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
814 /***********************************************************************
815 * is_net_wm_state_maximized
817 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
821 unsigned long i, count, remaining;
823 if (!data->whole_window) return FALSE;
826 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
827 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
828 &remaining, (unsigned char **)&state ))
830 if (type == XA_ATOM && format == 32)
832 for (i = 0; i < count; i++)
834 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
835 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
846 /***********************************************************************
847 * X11DRV_ReparentNotify
849 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
851 XReparentEvent *event = &xev->xreparent;
852 struct x11drv_win_data *data;
853 HWND parent, old_parent;
856 if (!(data = X11DRV_get_win_data( hwnd ))) return;
857 if (!data->embedded) return;
859 if (data->whole_window)
861 if (event->parent == root_window)
863 TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
865 SendMessageW( hwnd, WM_CLOSE, 0, 0 );
868 data->embedder = event->parent;
871 TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
873 style = GetWindowLongW( hwnd, GWL_STYLE );
874 if (event->parent == root_window)
876 parent = GetDesktopWindow();
877 style = (style & ~WS_CHILD) | WS_POPUP;
881 if (!(parent = create_foreign_window( event->display, event->parent ))) return;
882 style = (style & ~WS_POPUP) | WS_CHILD;
885 ShowWindow( hwnd, SW_HIDE );
886 old_parent = SetParent( hwnd, parent );
887 SetWindowLongW( hwnd, GWL_STYLE, style );
888 SetWindowPos( hwnd, HWND_TOP, event->x, event->y, 0, 0,
889 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS |
890 ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) );
892 /* make old parent destroy itself if it no longer has children */
893 if (old_parent != GetDesktopWindow()) PostMessageW( old_parent, WM_CLOSE, 0, 0 );
897 /***********************************************************************
898 * X11DRV_ConfigureNotify
900 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
902 XConfigureEvent *event = &xev->xconfigure;
903 struct x11drv_win_data *data;
908 int cx, cy, x = event->x, y = event->y;
911 if (!(data = X11DRV_get_win_data( hwnd ))) return;
912 if (!data->mapped || data->iconic) return;
913 if (data->whole_window && !data->managed) return;
914 /* ignore synthetic events on foreign windows */
915 if (event->send_event && !data->whole_window) return;
916 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
918 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
919 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
920 event->serial, data->configure_serial );
926 parent = GetAncestor( hwnd, GA_PARENT );
927 root_coords = event->send_event; /* synthetic events are always in root coords */
929 if (!root_coords && parent == GetDesktopWindow()) /* normal event, map coordinates to the root */
933 XTranslateCoordinates( event->display, event->window, root_window,
934 0, 0, &x, &y, &child );
940 rect.right = x + event->width;
941 rect.bottom = y + event->height;
942 if (root_coords) OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
943 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
944 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
945 event->x, event->y, event->width, event->height );
947 if (is_net_wm_state_maximized( event->display, data ))
949 if (!IsZoomed( data->hwnd ))
951 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
952 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
958 if (IsZoomed( data->hwnd ))
960 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
961 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
966 X11DRV_X_to_window_rect( data, &rect );
967 if (root_coords) MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
969 /* Compare what has changed */
973 cx = rect.right - rect.left;
974 cy = rect.bottom - rect.top;
975 flags = SWP_NOACTIVATE | SWP_NOZORDER;
977 if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */
979 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
981 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
982 hwnd, data->window_rect.left, data->window_rect.top, x, y );
984 if ((data->window_rect.right - data->window_rect.left == cx &&
985 data->window_rect.bottom - data->window_rect.top == cy) ||
986 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
988 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
992 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
993 hwnd, data->window_rect.right - data->window_rect.left,
994 data->window_rect.bottom - data->window_rect.top, cx, cy );
996 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
1000 /**********************************************************************
1001 * X11DRV_GravityNotify
1003 static void X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
1005 XGravityEvent *event = &xev->xgravity;
1006 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1009 if (!data || data->whole_window) return; /* only handle this for foreign windows */
1011 rect.left = event->x;
1012 rect.top = event->y;
1013 rect.right = rect.left + data->whole_rect.right - data->whole_rect.left;
1014 rect.bottom = rect.top + data->whole_rect.bottom - data->whole_rect.top;
1016 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d)\n",
1017 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1018 event->x, event->y );
1020 X11DRV_X_to_window_rect( data, &rect );
1022 if (data->window_rect.left != rect.left || data ->window_rect.top != rect.top)
1023 SetWindowPos( hwnd, 0, rect.left, rect.top, 0, 0,
1024 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1028 /***********************************************************************
1029 * get_window_wm_state
1031 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
1039 int format, ret = -1;
1040 unsigned long count, remaining;
1043 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
1044 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1045 &type, &format, &count, &remaining, (unsigned char **)&state ))
1047 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1051 wine_tsx11_unlock();
1056 /***********************************************************************
1057 * handle_wm_state_notify
1059 * Handle a PropertyNotify for WM_STATE.
1061 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
1062 BOOL update_window )
1066 switch(event->state)
1068 case PropertyDelete:
1069 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1070 data->wm_state = WithdrawnState;
1072 case PropertyNewValue:
1074 int old_state = data->wm_state;
1075 int new_state = get_window_wm_state( event->display, data );
1076 if (new_state != -1 && new_state != data->wm_state)
1078 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1079 data->hwnd, data->whole_window, new_state, old_state );
1080 data->wm_state = new_state;
1081 /* ignore the initial state transition out of withdrawn state */
1082 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1083 if (!old_state) return;
1089 if (!update_window || !data->managed || !data->mapped) return;
1091 style = GetWindowLongW( data->hwnd, GWL_STYLE );
1093 if (data->iconic && data->wm_state == NormalState) /* restore window */
1095 data->iconic = FALSE;
1096 if (is_net_wm_state_maximized( event->display, data ))
1098 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1100 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1101 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1103 else TRACE( "not restoring to max win %p/%lx style %08x\n",
1104 data->hwnd, data->whole_window, style );
1106 else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1108 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1109 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1111 else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1113 else if (!data->iconic && data->wm_state == IconicState)
1115 data->iconic = TRUE;
1116 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1118 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1119 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1121 else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1126 /***********************************************************************
1127 * X11DRV_PropertyNotify
1129 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1131 XPropertyEvent *event = &xev->xproperty;
1132 struct x11drv_win_data *data;
1135 if (!(data = X11DRV_get_win_data( hwnd ))) return;
1137 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1141 /* event filter to wait for a WM_STATE change notification on a window */
1142 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1144 if (event->xany.window != (Window)arg) return 0;
1145 return (event->type == DestroyNotify ||
1146 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1149 /***********************************************************************
1150 * wait_for_withdrawn_state
1152 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1154 DWORD end = GetTickCount() + 2000;
1156 if (!data->managed) return;
1158 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1159 data->hwnd, data->whole_window, set ? "" : "not " );
1161 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1167 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1170 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1171 if (event.type == DestroyNotify) call_event_handler( display, &event );
1174 wine_tsx11_unlock();
1175 handle_wm_state_notify( data, &event.xproperty, FALSE );
1179 wine_tsx11_unlock();
1184 int timeout = end - GetTickCount();
1186 pfd.fd = ConnectionNumber(display);
1187 pfd.events = POLLIN;
1188 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1190 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1195 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1199 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1203 if (!IsWindowEnabled(hQueryWnd)) return 0;
1205 GetWindowRect(hQueryWnd, &tempRect);
1207 if(!PtInRect(&tempRect, *lpPt)) return 0;
1209 if (!IsIconic( hQueryWnd ))
1212 ScreenToClient( hQueryWnd, &pt );
1213 GetClientRect( hQueryWnd, &tempRect );
1215 if (PtInRect( &tempRect, pt))
1217 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1218 if (ret && ret != hQueryWnd)
1220 ret = find_drop_window( ret, lpPt );
1221 if (ret) return ret;
1226 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1228 ScreenToClient(hQueryWnd, lpPt);
1233 /**********************************************************************
1234 * EVENT_DropFromOffix
1236 * don't know if it still works (last Changelog is from 96/11/04)
1238 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1240 struct x11drv_win_data *data;
1241 unsigned long data_length;
1242 unsigned long aux_long;
1243 unsigned char* p_data = NULL;
1247 Window win, w_aux_root, w_aux_child;
1249 win = X11DRV_get_whole_window(hWnd);
1251 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1252 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1253 x += virtual_screen_rect.left;
1254 y += virtual_screen_rect.top;
1255 wine_tsx11_unlock();
1257 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1259 /* find out drop point and drop window */
1260 if( x < 0 || y < 0 ||
1261 x > (data->whole_rect.right - data->whole_rect.left) ||
1262 y > (data->whole_rect.bottom - data->whole_rect.top) )
1264 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1270 POINT pt = { x, y };
1271 HWND hwndDrop = find_drop_window( hWnd, &pt );
1284 if (!bAccept) return;
1287 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1288 x11drv_atom(DndSelection), 0, 65535, FALSE,
1289 AnyPropertyType, &atom_aux, &dummy,
1290 &data_length, &aux_long, &p_data);
1291 wine_tsx11_unlock();
1293 if( !aux_long && p_data) /* don't bother if > 64K */
1295 char *p = (char *)p_data;
1299 while( *p ) /* calculate buffer size */
1301 INT len = GetShortPathNameA( p, NULL, 0 );
1302 if (len) aux_long += len + 1;
1305 if( aux_long && aux_long < 65535 )
1310 aux_long += sizeof(DROPFILES) + 1;
1311 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1312 lpDrop = GlobalLock( hDrop );
1316 lpDrop->pFiles = sizeof(DROPFILES);
1319 lpDrop->fNC = FALSE;
1320 lpDrop->fWide = FALSE;
1321 p_drop = (char *)(lpDrop + 1);
1325 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1326 p_drop += strlen( p_drop ) + 1;
1330 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1335 if( p_data ) XFree(p_data);
1336 wine_tsx11_unlock();
1339 /**********************************************************************
1342 * drop items are separated by \n
1343 * each item is prefixed by its mime type
1345 * event->data.l[3], event->data.l[4] contains drop x,y position
1347 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1349 struct x11drv_win_data *win_data;
1350 unsigned long data_length;
1351 unsigned long aux_long, drop_len = 0;
1352 unsigned char *p_data = NULL; /* property data */
1353 char *p_drop = NULL;
1365 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1368 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1369 x11drv_atom(DndSelection), 0, 65535, FALSE,
1370 AnyPropertyType, &u.atom_aux, &u.i,
1371 &data_length, &aux_long, &p_data);
1372 wine_tsx11_unlock();
1374 WARN("property too large, truncated!\n");
1375 TRACE("urls=%s\n", p_data);
1377 if( !aux_long && p_data) { /* don't bother if > 64K */
1378 /* calculate length */
1380 next = strchr(p, '\n');
1383 if (strncmp(p,"file:",5) == 0 ) {
1384 INT len = GetShortPathNameA( p+5, NULL, 0 );
1385 if (len) drop_len += len + 1;
1390 next = strchr(p, '\n');
1396 if( drop_len && drop_len < 65535 ) {
1398 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1399 &x, &y, &u.i, &u.i, &u.u);
1400 x += virtual_screen_rect.left;
1401 y += virtual_screen_rect.top;
1402 wine_tsx11_unlock();
1404 drop_len += sizeof(DROPFILES) + 1;
1405 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1406 lpDrop = GlobalLock( hDrop );
1408 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1410 lpDrop->pFiles = sizeof(DROPFILES);
1414 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1415 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1416 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1417 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1418 lpDrop->fWide = FALSE;
1419 p_drop = (char*)(lpDrop + 1);
1422 /* create message content */
1425 next = strchr(p, '\n');
1428 if (strncmp(p,"file:",5) == 0 ) {
1429 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1431 TRACE("drop file %s as %s\n", p+5, p_drop);
1434 WARN("can't convert file %s to dos name\n", p+5);
1437 WARN("unknown mime type %s\n", p);
1442 next = strchr(p, '\n');
1449 GlobalUnlock(hDrop);
1450 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1454 if( p_data ) XFree(p_data);
1455 wine_tsx11_unlock();
1460 /**********************************************************************
1461 * handle_xembed_protocol
1463 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1465 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1469 switch (event->data.l[1])
1471 case XEMBED_EMBEDDED_NOTIFY:
1472 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1473 data->embedder = event->data.l[3];
1476 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1477 hwnd, event->window, event->data.l[1], event->data.l[2] );
1483 /**********************************************************************
1484 * handle_dnd_protocol
1486 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1489 int root_x, root_y, child_x, child_y;
1492 /* query window (drag&drop event contains only drag window) */
1494 XQueryPointer( event->display, root_window, &root, &child,
1495 &root_x, &root_y, &child_x, &child_y, &u);
1496 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1497 wine_tsx11_unlock();
1499 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1500 EVENT_DropFromOffiX(hwnd, event);
1501 else if (event->data.l[0] == DndURL)
1502 EVENT_DropURLs(hwnd, event);
1506 struct client_message_handler
1508 int atom; /* protocol atom */
1509 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1512 static const struct client_message_handler client_messages[] =
1514 { XATOM_MANAGER, handle_manager_message },
1515 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1516 { XATOM__XEMBED, handle_xembed_protocol },
1517 { XATOM_DndProtocol, handle_dnd_protocol },
1518 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1519 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1520 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1521 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1525 /**********************************************************************
1526 * X11DRV_ClientMessage
1528 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1530 XClientMessageEvent *event = &xev->xclient;
1535 if (event->format != 32)
1537 WARN( "Don't know how to handle format %d\n", event->format );
1541 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1543 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1545 client_messages[i].handler( hwnd, event );
1549 TRACE( "no handler found for %ld\n", event->message_type );
1553 /***********************************************************************
1554 * X11DRV_SendInput (X11DRV.@)
1556 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1560 for (i = 0; i < count; i++, inputs++)
1562 switch(inputs->type)
1565 X11DRV_send_mouse_input( 0, 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1566 inputs->u.mi.mouseData, inputs->u.mi.time,
1567 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1569 case INPUT_KEYBOARD:
1570 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1571 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1573 case INPUT_HARDWARE:
1574 FIXME( "INPUT_HARDWARE not supported\n" );