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 */
76 static void X11DRV_FocusIn( HWND hwnd, XEvent *event );
77 static void X11DRV_FocusOut( HWND hwnd, XEvent *event );
78 static void X11DRV_Expose( HWND hwnd, XEvent *event );
79 static void X11DRV_MapNotify( HWND hwnd, XEvent *event );
80 static void X11DRV_ConfigureNotify( HWND hwnd, XEvent *event );
81 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
82 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
86 int type; /* event type */
87 x11drv_event_handler handler; /* corresponding handler function */
90 #define MAX_EVENT_HANDLERS 64
92 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
94 /* list must be sorted by event type */
95 { KeyPress, X11DRV_KeyEvent },
96 { KeyRelease, X11DRV_KeyEvent },
97 { ButtonPress, X11DRV_ButtonPress },
98 { ButtonRelease, X11DRV_ButtonRelease },
99 { MotionNotify, X11DRV_MotionNotify },
100 { EnterNotify, X11DRV_EnterNotify },
102 { FocusIn, X11DRV_FocusIn },
103 { FocusOut, X11DRV_FocusOut },
104 { KeymapNotify, X11DRV_KeymapNotify },
105 { Expose, X11DRV_Expose },
108 /* VisibilityNotify */
110 { DestroyNotify, X11DRV_DestroyNotify },
112 { MapNotify, X11DRV_MapNotify },
115 { ConfigureNotify, X11DRV_ConfigureNotify },
116 /* ConfigureRequest */
119 /* CirculateNotify */
120 /* CirculateRequest */
121 { PropertyNotify, X11DRV_PropertyNotify },
122 { SelectionClear, X11DRV_SelectionClear },
123 { SelectionRequest, X11DRV_SelectionRequest },
124 /* SelectionNotify */
126 { ClientMessage, X11DRV_ClientMessage },
127 { MappingNotify, X11DRV_MappingNotify },
130 static int nb_event_handlers = 18; /* change this if you add handlers above */
133 /* return the name of an X event */
134 static const char *dbgstr_event( int type )
136 static const char * const event_names[] =
138 "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
139 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
140 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
141 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
142 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
143 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
144 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
145 "ClientMessage", "MappingNotify"
148 if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
149 return wine_dbg_sprintf( "Extension event %d", type );
153 /***********************************************************************
156 * Find the handler for a given event type. Caller must hold the x11 lock.
158 static inline x11drv_event_handler find_handler( int type )
160 int min = 0, max = nb_event_handlers - 1;
164 int pos = (min + max) / 2;
165 if (handlers[pos].type == type) return handlers[pos].handler;
166 if (handlers[pos].type > type) max = pos - 1;
173 /***********************************************************************
174 * X11DRV_register_event_handler
176 * Register a handler for a given event type.
177 * If already registered, overwrite the previous handler.
179 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
185 max = nb_event_handlers - 1;
188 int pos = (min + max) / 2;
189 if (handlers[pos].type == type)
191 handlers[pos].handler = handler;
194 if (handlers[pos].type > type) max = pos - 1;
197 /* insert it between max and min */
198 memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
199 handlers[min].type = type;
200 handlers[min].handler = handler;
202 assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
205 TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
209 /***********************************************************************
212 static Bool filter_event( Display *display, XEvent *event, char *arg )
214 ULONG_PTR mask = (ULONG_PTR)arg;
216 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
224 return (mask & QS_KEY) != 0;
227 return (mask & QS_MOUSEBUTTON) != 0;
231 return (mask & QS_MOUSEMOVE) != 0;
233 return (mask & QS_PAINT) != 0;
238 case ConfigureNotify:
241 return (mask & QS_POSTMESSAGE) != 0;
243 return (mask & QS_SENDMESSAGE) != 0;
248 enum event_merge_action
250 MERGE_DISCARD, /* discard the old event */
251 MERGE_HANDLE, /* handle the old event */
252 MERGE_KEEP /* keep the old event for future merging */
255 /***********************************************************************
258 * Try to merge 2 consecutive events.
260 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
264 case ConfigureNotify:
267 case ConfigureNotify:
268 if (prev->xany.window == next->xany.window)
270 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
271 return MERGE_DISCARD;
280 if (prev->xany.window == next->xany.window && next->type == MotionNotify)
282 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
283 return MERGE_DISCARD;
291 /***********************************************************************
294 static inline void call_event_handler( Display *display, XEvent *event )
297 x11drv_event_handler handler;
299 struct x11drv_thread_data *thread_data;
301 if (!(handler = find_handler( event->type )))
303 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
304 return; /* no handler, ignore it */
307 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
308 hwnd = 0; /* not for a registered window */
309 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
311 TRACE( "%s for hwnd/window %p/%lx\n",
312 dbgstr_event( event->type ), hwnd, event->xany.window );
314 thread_data = x11drv_thread_data();
315 prev = thread_data->current_event;
316 thread_data->current_event = event;
317 handler( hwnd, event );
318 thread_data->current_event = prev;
323 /***********************************************************************
326 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
328 XEvent event, prev_event;
330 enum event_merge_action action = MERGE_DISCARD;
334 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
337 if (XFilterEvent( &event, None ))
340 * SCIM on linux filters key events strangely. It does not filter the
341 * KeyPress events for these keys however it does filter the
342 * KeyRelease events. This causes wine to become very confused as
343 * to the keyboard state.
345 * We need to let those KeyRelease events be processed so that the
346 * keyboard state is correct.
348 if (event.type == KeyRelease)
351 XKeyEvent *keyevent = &event.xkey;
353 XLookupString(keyevent, NULL, 0, &keysym, NULL);
354 if (!(keysym == XK_Shift_L ||
355 keysym == XK_Shift_R ||
356 keysym == XK_Control_L ||
357 keysym == XK_Control_R ||
358 keysym == XK_Alt_R ||
359 keysym == XK_Alt_L ||
360 keysym == XK_Meta_R ||
361 keysym == XK_Meta_L))
362 continue; /* not a key we care about, ignore it */
365 continue; /* filtered, ignore it */
367 if (prev_event.type) action = merge_events( &prev_event, &event );
370 case MERGE_DISCARD: /* discard prev, keep new */
373 case MERGE_HANDLE: /* handle prev, keep new */
374 call_event_handler( display, &prev_event );
377 case MERGE_KEEP: /* handle new, keep prev for future merging */
378 call_event_handler( display, &event );
382 XFlush( gdi_display );
383 if (prev_event.type) call_event_handler( display, &prev_event );
385 if (count) TRACE( "processed %d events\n", count );
390 /***********************************************************************
391 * MsgWaitForMultipleObjectsEx (X11DRV.@)
393 DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
394 DWORD timeout, DWORD mask, DWORD flags )
397 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
401 if (!count && !timeout) return WAIT_TIMEOUT;
402 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
403 timeout, flags & MWMO_ALERTABLE );
406 if (data->current_event) mask = 0; /* don't process nested events */
408 if (process_events( data->display, filter_event, mask )) ret = count - 1;
409 else if (count || timeout)
411 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
412 timeout, flags & MWMO_ALERTABLE );
413 if (ret == count - 1) process_events( data->display, filter_event, mask );
415 else ret = WAIT_TIMEOUT;
420 /***********************************************************************
421 * EVENT_x11_time_to_win32_time
423 * Make our timer and the X timer line up as best we can
424 * Pass 0 to retrieve the current adjustment value (times -1)
426 DWORD EVENT_x11_time_to_win32_time(Time time)
428 static DWORD adjust = 0;
429 DWORD now = GetTickCount();
432 if (! adjust && time != 0)
439 /* If we got an event in the 'future', then our clock is clearly wrong.
440 If we got it more than 10000 ms in the future, then it's most likely
441 that the clock has wrapped. */
444 if (ret > now && ((ret - now) < 10000) && time != 0)
455 /*******************************************************************
456 * can_activate_window
458 * Check if we can activate the specified window.
460 static inline BOOL can_activate_window( HWND hwnd )
462 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
463 if (!(style & WS_VISIBLE)) return FALSE;
464 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
465 if (style & WS_MINIMIZE) return FALSE;
466 if (hwnd == GetDesktopWindow()) return FALSE;
467 return !(style & WS_DISABLED);
471 /**********************************************************************
474 static void set_focus( Display *display, HWND hwnd, Time time )
479 TRACE( "setting foreground window to %p\n", hwnd );
480 SetForegroundWindow( hwnd );
483 if (focus) focus = GetAncestor( focus, GA_ROOT );
484 win = X11DRV_get_whole_window(focus);
488 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
490 XSetInputFocus( display, win, RevertToParent, time );
496 /**********************************************************************
497 * handle_wm_protocols
499 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
501 Atom protocol = (Atom)event->data.l[0];
503 if (!protocol) return;
505 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
507 /* Ignore the delete window request if the window has been disabled
508 * and we are in managed mode. This is to disallow applications from
509 * being closed by the window manager while in a modal state.
511 if (IsWindowEnabled(hwnd))
516 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
517 hSysMenu = GetSystemMenu(hwnd, FALSE);
520 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
521 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
524 if (GetActiveWindow() != hwnd)
526 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
527 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
528 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
531 case MA_NOACTIVATEANDEAT:
532 case MA_ACTIVATEANDEAT:
538 SetActiveWindow(hwnd);
541 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
545 /* Simulate clicking the caption Close button */
547 PostMessageW( hwnd, WM_NCLBUTTONDOWN, HTCLOSE, MAKELPARAM( pt.x, pt.y ) );
548 PostMessageW( hwnd, WM_LBUTTONUP, HTCLOSE, MAKELPARAM( pt.x, pt.y ) );
551 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
553 Time event_time = (Time)event->data.l[1];
554 HWND last_focus = x11drv_thread_data()->last_focus;
556 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
557 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
558 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
560 if (can_activate_window(hwnd))
562 /* simulate a mouse click on the caption to find out
563 * whether the window wants to be activated */
564 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
565 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
566 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
567 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
569 set_focus( event->display, hwnd, event_time );
573 else if (hwnd == GetDesktopWindow())
575 hwnd = GetForegroundWindow();
576 if (!hwnd) hwnd = last_focus;
577 if (!hwnd) hwnd = GetDesktopWindow();
578 set_focus( event->display, hwnd, event_time );
581 /* try to find some other window to give the focus to */
583 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
584 if (!hwnd) hwnd = GetActiveWindow();
585 if (!hwnd) hwnd = last_focus;
586 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
588 else if (protocol == x11drv_atom(_NET_WM_PING))
590 XClientMessageEvent xev;
593 TRACE("NET_WM Ping\n");
595 xev.window = DefaultRootWindow(xev.display);
596 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
598 /* this line is semi-stolen from gtk2 */
599 TRACE("NET_WM Pong\n");
604 static const char * const focus_details[] =
610 "NotifyNonlinearVirtual",
616 /**********************************************************************
619 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
621 XFocusChangeEvent *event = &xev->xfocus;
626 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
628 if (event->detail == NotifyPointer) return;
630 if ((xic = X11DRV_get_ic( hwnd )))
636 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
638 if (!can_activate_window(hwnd))
640 HWND hwnd = GetFocus();
641 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
642 if (!hwnd) hwnd = GetActiveWindow();
643 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
644 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
646 else SetForegroundWindow( hwnd );
650 /**********************************************************************
653 * Note: only top-level windows get FocusOut events.
655 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
657 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;
668 if (ximInComposeMode) return;
670 x11drv_thread_data()->last_focus = hwnd;
671 if ((xic = X11DRV_get_ic( hwnd )))
674 XUnsetICFocus( xic );
677 if (hwnd != GetForegroundWindow()) return;
678 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
680 /* don't reset the foreground window, if the window which is
681 getting the focus is a Wine window */
684 XGetInputFocus( event->display, &focus_win, &revert );
687 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
694 /* Abey : 6-Oct-99. Check again if the focus out window is the
695 Foreground window, because in most cases the messages sent
696 above must have already changed the foreground window, in which
697 case we don't have to change the foreground window to 0 */
698 if (hwnd == GetForegroundWindow())
700 TRACE( "lost focus, setting fg to desktop\n" );
701 SetForegroundWindow( GetDesktopWindow() );
707 /***********************************************************************
710 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
712 XExposeEvent *event = &xev->xexpose;
714 struct x11drv_win_data *data;
715 int flags = RDW_INVALIDATE | RDW_ERASE;
717 TRACE( "win %p (%lx) %d,%d %dx%d\n",
718 hwnd, event->window, event->x, event->y, event->width, event->height );
720 if (!(data = X11DRV_get_win_data( hwnd ))) return;
722 if (event->window == data->whole_window)
724 rect.left = data->whole_rect.left + event->x;
725 rect.top = data->whole_rect.top + event->y;
730 rect.left = data->client_rect.left + event->x;
731 rect.top = data->client_rect.top + event->y;
733 rect.right = rect.left + event->width;
734 rect.bottom = rect.top + event->height;
736 if (event->window != root_window)
738 SERVER_START_REQ( update_window_zorder )
740 req->window = wine_server_user_handle( hwnd );
741 req->rect.left = rect.left;
742 req->rect.top = rect.top;
743 req->rect.right = rect.right;
744 req->rect.bottom = rect.bottom;
745 wine_server_call( req );
749 /* make position relative to client area instead of parent */
750 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
751 flags |= RDW_ALLCHILDREN;
754 RedrawWindow( hwnd, &rect, 0, flags );
758 /**********************************************************************
761 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
763 struct x11drv_win_data *data;
765 if (!(data = X11DRV_get_win_data( hwnd ))) return;
766 if (!data->mapped) return;
770 HWND hwndFocus = GetFocus();
771 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
776 /***********************************************************************
777 * is_net_wm_state_maximized
779 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
783 unsigned long i, count, remaining;
786 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
787 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
788 &remaining, (unsigned char **)&state ))
790 if (type == XA_ATOM && format == 32)
792 for (i = 0; i < count; i++)
794 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
795 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
806 /***********************************************************************
807 * X11DRV_ConfigureNotify
809 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
811 XConfigureEvent *event = &xev->xconfigure;
812 struct x11drv_win_data *data;
815 int cx, cy, x = event->x, y = event->y;
818 if (!(data = X11DRV_get_win_data( hwnd ))) return;
819 if (!data->mapped || data->iconic || !data->managed) return;
823 if (!event->send_event) /* normal event, need to map coordinates to the root */
827 XTranslateCoordinates( event->display, data->whole_window, root_window,
828 0, 0, &x, &y, &child );
833 rect.right = x + event->width;
834 rect.bottom = y + event->height;
835 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
836 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
837 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
838 event->x, event->y, event->width, event->height );
839 X11DRV_X_to_window_rect( data, &rect );
841 if (is_net_wm_state_maximized( event->display, data ))
843 if (!IsZoomed( data->hwnd ))
845 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
846 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
852 if (IsZoomed( data->hwnd ))
854 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
855 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
860 /* Compare what has changed */
864 cx = rect.right - rect.left;
865 cy = rect.bottom - rect.top;
866 flags = SWP_NOACTIVATE | SWP_NOZORDER;
868 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
870 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
871 hwnd, data->window_rect.left, data->window_rect.top, x, y );
873 if ((data->window_rect.right - data->window_rect.left == cx &&
874 data->window_rect.bottom - data->window_rect.top == cy) ||
875 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
877 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
881 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
882 hwnd, data->window_rect.right - data->window_rect.left,
883 data->window_rect.bottom - data->window_rect.top, cx, cy );
885 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
889 /***********************************************************************
890 * get_window_wm_state
892 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
900 int format, ret = -1;
901 unsigned long count, remaining;
904 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
905 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
906 &type, &format, &count, &remaining, (unsigned char **)&state ))
908 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
917 /***********************************************************************
918 * handle_wm_state_notify
920 * Handle a PropertyNotify for WM_STATE.
922 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
928 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
929 data->wm_state = WithdrawnState;
931 case PropertyNewValue:
933 int old_state = data->wm_state;
934 int new_state = get_window_wm_state( event->display, data );
935 if (new_state != -1 && new_state != data->wm_state)
937 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
938 data->hwnd, data->whole_window, new_state, old_state );
939 data->wm_state = new_state;
940 /* ignore the initial state transition out of withdrawn state */
941 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
942 if (!old_state) return;
948 if (!update_window || !data->managed || !data->mapped) return;
950 if (data->iconic && data->wm_state == NormalState) /* restore window */
952 data->iconic = FALSE;
953 if (is_net_wm_state_maximized( event->display, data ))
955 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
956 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
960 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
961 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
964 else if (!data->iconic && data->wm_state == IconicState)
966 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
968 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
973 /***********************************************************************
974 * X11DRV_PropertyNotify
976 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
978 XPropertyEvent *event = &xev->xproperty;
979 struct x11drv_win_data *data;
982 if (!(data = X11DRV_get_win_data( hwnd ))) return;
984 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
988 /* event filter to wait for a WM_STATE change notification on a window */
989 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
991 if (event->xany.window != (Window)arg) return 0;
992 return (event->type == DestroyNotify ||
993 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
996 /***********************************************************************
997 * wait_for_withdrawn_state
999 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1001 DWORD end = GetTickCount() + 2000;
1003 if (!data->managed) return;
1005 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1006 data->hwnd, data->whole_window, set ? "" : "not " );
1008 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1014 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1017 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1018 if (event.type == DestroyNotify) call_event_handler( display, &event );
1021 wine_tsx11_unlock();
1022 handle_wm_state_notify( data, &event.xproperty, FALSE );
1026 wine_tsx11_unlock();
1031 int timeout = end - GetTickCount();
1033 pfd.fd = ConnectionNumber(display);
1034 pfd.events = POLLIN;
1035 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1037 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1042 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1046 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1050 if (!IsWindowEnabled(hQueryWnd)) return 0;
1052 GetWindowRect(hQueryWnd, &tempRect);
1054 if(!PtInRect(&tempRect, *lpPt)) return 0;
1056 if (!IsIconic( hQueryWnd ))
1059 ScreenToClient( hQueryWnd, &pt );
1060 GetClientRect( hQueryWnd, &tempRect );
1062 if (PtInRect( &tempRect, pt))
1064 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1065 if (ret && ret != hQueryWnd)
1067 ret = find_drop_window( ret, lpPt );
1068 if (ret) return ret;
1073 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1075 ScreenToClient(hQueryWnd, lpPt);
1080 /**********************************************************************
1081 * EVENT_DropFromOffix
1083 * don't know if it still works (last Changelog is from 96/11/04)
1085 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1087 struct x11drv_win_data *data;
1088 unsigned long data_length;
1089 unsigned long aux_long;
1090 unsigned char* p_data = NULL;
1094 Window win, w_aux_root, w_aux_child;
1096 win = X11DRV_get_whole_window(hWnd);
1098 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1099 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1100 x += virtual_screen_rect.left;
1101 y += virtual_screen_rect.top;
1102 wine_tsx11_unlock();
1104 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1106 /* find out drop point and drop window */
1107 if( x < 0 || y < 0 ||
1108 x > (data->whole_rect.right - data->whole_rect.left) ||
1109 y > (data->whole_rect.bottom - data->whole_rect.top) )
1111 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1117 POINT pt = { x, y };
1118 HWND hwndDrop = find_drop_window( hWnd, &pt );
1131 if (!bAccept) return;
1134 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1135 x11drv_atom(DndSelection), 0, 65535, FALSE,
1136 AnyPropertyType, &atom_aux, &dummy,
1137 &data_length, &aux_long, &p_data);
1138 wine_tsx11_unlock();
1140 if( !aux_long && p_data) /* don't bother if > 64K */
1142 char *p = (char *)p_data;
1146 while( *p ) /* calculate buffer size */
1148 INT len = GetShortPathNameA( p, NULL, 0 );
1149 if (len) aux_long += len + 1;
1152 if( aux_long && aux_long < 65535 )
1157 aux_long += sizeof(DROPFILES) + 1;
1158 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1159 lpDrop = GlobalLock( hDrop );
1163 lpDrop->pFiles = sizeof(DROPFILES);
1166 lpDrop->fNC = FALSE;
1167 lpDrop->fWide = FALSE;
1168 p_drop = (char *)(lpDrop + 1);
1172 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1173 p_drop += strlen( p_drop ) + 1;
1177 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1182 if( p_data ) XFree(p_data);
1183 wine_tsx11_unlock();
1186 /**********************************************************************
1189 * drop items are separated by \n
1190 * each item is prefixed by its mime type
1192 * event->data.l[3], event->data.l[4] contains drop x,y position
1194 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1196 struct x11drv_win_data *win_data;
1197 unsigned long data_length;
1198 unsigned long aux_long, drop_len = 0;
1199 unsigned char *p_data = NULL; /* property data */
1200 char *p_drop = NULL;
1212 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1215 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1216 x11drv_atom(DndSelection), 0, 65535, FALSE,
1217 AnyPropertyType, &u.atom_aux, &u.i,
1218 &data_length, &aux_long, &p_data);
1219 wine_tsx11_unlock();
1221 WARN("property too large, truncated!\n");
1222 TRACE("urls=%s\n", p_data);
1224 if( !aux_long && p_data) { /* don't bother if > 64K */
1225 /* calculate length */
1227 next = strchr(p, '\n');
1230 if (strncmp(p,"file:",5) == 0 ) {
1231 INT len = GetShortPathNameA( p+5, NULL, 0 );
1232 if (len) drop_len += len + 1;
1237 next = strchr(p, '\n');
1243 if( drop_len && drop_len < 65535 ) {
1245 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1246 &x, &y, &u.i, &u.i, &u.u);
1247 x += virtual_screen_rect.left;
1248 y += virtual_screen_rect.top;
1249 wine_tsx11_unlock();
1251 drop_len += sizeof(DROPFILES) + 1;
1252 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1253 lpDrop = GlobalLock( hDrop );
1255 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1257 lpDrop->pFiles = sizeof(DROPFILES);
1261 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1262 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1263 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1264 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1265 lpDrop->fWide = FALSE;
1266 p_drop = (char*)(lpDrop + 1);
1269 /* create message content */
1272 next = strchr(p, '\n');
1275 if (strncmp(p,"file:",5) == 0 ) {
1276 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1278 TRACE("drop file %s as %s\n", p+5, p_drop);
1281 WARN("can't convert file %s to dos name\n", p+5);
1284 WARN("unknown mime type %s\n", p);
1289 next = strchr(p, '\n');
1296 GlobalUnlock(hDrop);
1297 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1301 if( p_data ) XFree(p_data);
1302 wine_tsx11_unlock();
1306 /**********************************************************************
1307 * handle_dnd_protocol
1309 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1312 int root_x, root_y, child_x, child_y;
1315 /* query window (drag&drop event contains only drag window) */
1317 XQueryPointer( event->display, root_window, &root, &child,
1318 &root_x, &root_y, &child_x, &child_y, &u);
1319 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1320 wine_tsx11_unlock();
1322 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1323 EVENT_DropFromOffiX(hwnd, event);
1324 else if (event->data.l[0] == DndURL)
1325 EVENT_DropURLs(hwnd, event);
1329 struct client_message_handler
1331 int atom; /* protocol atom */
1332 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1335 static const struct client_message_handler client_messages[] =
1337 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1338 { XATOM_DndProtocol, handle_dnd_protocol },
1339 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1340 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1341 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1342 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1346 /**********************************************************************
1347 * X11DRV_ClientMessage
1349 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1351 XClientMessageEvent *event = &xev->xclient;
1356 if (event->format != 32)
1358 WARN( "Don't know how to handle format %d\n", event->format );
1362 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1364 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1366 client_messages[i].handler( hwnd, event );
1370 TRACE( "no handler found for %ld\n", event->message_type );
1374 /***********************************************************************
1375 * X11DRV_SendInput (X11DRV.@)
1377 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1381 for (i = 0; i < count; i++, inputs++)
1383 switch(inputs->type)
1386 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1387 inputs->u.mi.mouseData, inputs->u.mi.time,
1388 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1390 case INPUT_KEYBOARD:
1391 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1392 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1394 case INPUT_HARDWARE:
1395 FIXME( "INPUT_HARDWARE not supported\n" );