push 6fe5edf8439c19d3885814583531c2f2b1495177
[wine/hacks.git] / dlls / winex11.drv / event.c
blobadbf585c99395236059fa380aa03d005b8df7180
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 #include "config.h"
24 #ifdef HAVE_POLL_H
25 #include <poll.h>
26 #endif
27 #ifdef HAVE_SYS_POLL_H
28 #include <sys/poll.h>
29 #endif
30 #include <X11/Xatom.h>
31 #include <X11/keysym.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xresource.h>
34 #include <X11/Xutil.h>
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <string.h>
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h"
45 #include "wingdi.h"
47 #include "x11drv.h"
49 /* avoid conflict with field names in included win32 headers */
50 #undef Status
51 #include "shlobj.h" /* DROPFILES */
52 #include "shellapi.h"
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 */
62 #define DndUnknown 0
63 #define DndRawData 1
64 #define DndFile 2
65 #define DndFiles 3
66 #define DndText 4
67 #define DndDir 5
68 #define DndLink 6
69 #define DndExe 7
71 #define DndEND 8
73 #define DndURL 128 /* KDE drag&drop */
75 /* Event handlers */
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_PropertyNotify( HWND hwnd, XEvent *event );
81 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
83 struct event_handler
85 int type; /* event type */
86 x11drv_event_handler handler; /* corresponding handler function */
89 #define MAX_EVENT_HANDLERS 64
91 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
93 /* list must be sorted by event type */
94 { KeyPress, X11DRV_KeyEvent },
95 { KeyRelease, X11DRV_KeyEvent },
96 { ButtonPress, X11DRV_ButtonPress },
97 { ButtonRelease, X11DRV_ButtonRelease },
98 { MotionNotify, X11DRV_MotionNotify },
99 { EnterNotify, X11DRV_EnterNotify },
100 /* LeaveNotify */
101 { FocusIn, X11DRV_FocusIn },
102 { FocusOut, X11DRV_FocusOut },
103 { KeymapNotify, X11DRV_KeymapNotify },
104 { Expose, X11DRV_Expose },
105 /* GraphicsExpose */
106 /* NoExpose */
107 /* VisibilityNotify */
108 /* CreateNotify */
109 { DestroyNotify, X11DRV_DestroyNotify },
110 /* UnmapNotify */
111 { MapNotify, X11DRV_MapNotify },
112 /* MapRequest */
113 /* ReparentNotify */
114 { ConfigureNotify, X11DRV_ConfigureNotify },
115 /* ConfigureRequest */
116 /* GravityNotify */
117 /* ResizeRequest */
118 /* CirculateNotify */
119 /* CirculateRequest */
120 { PropertyNotify, X11DRV_PropertyNotify },
121 { SelectionClear, X11DRV_SelectionClear },
122 { SelectionRequest, X11DRV_SelectionRequest },
123 /* SelectionNotify */
124 /* ColormapNotify */
125 { ClientMessage, X11DRV_ClientMessage },
126 { MappingNotify, X11DRV_MappingNotify },
129 static int nb_event_handlers = 18; /* change this if you add handlers above */
132 /* return the name of an X event */
133 static const char *dbgstr_event( int type )
135 static const char * const event_names[] =
137 "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
138 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
139 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
140 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
141 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
142 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
143 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
144 "ClientMessage", "MappingNotify"
147 if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
148 return wine_dbg_sprintf( "Extension event %d", type );
152 /***********************************************************************
153 * find_handler
155 * Find the handler for a given event type. Caller must hold the x11 lock.
157 static inline x11drv_event_handler find_handler( int type )
159 int min = 0, max = nb_event_handlers - 1;
161 while (min <= max)
163 int pos = (min + max) / 2;
164 if (handlers[pos].type == type) return handlers[pos].handler;
165 if (handlers[pos].type > type) max = pos - 1;
166 else min = pos + 1;
168 return NULL;
172 /***********************************************************************
173 * X11DRV_register_event_handler
175 * Register a handler for a given event type.
176 * If already registered, overwrite the previous handler.
178 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
180 int min, max;
182 wine_tsx11_lock();
183 min = 0;
184 max = nb_event_handlers - 1;
185 while (min <= max)
187 int pos = (min + max) / 2;
188 if (handlers[pos].type == type)
190 handlers[pos].handler = handler;
191 goto done;
193 if (handlers[pos].type > type) max = pos - 1;
194 else min = pos + 1;
196 /* insert it between max and min */
197 memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
198 handlers[min].type = type;
199 handlers[min].handler = handler;
200 nb_event_handlers++;
201 assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
202 done:
203 wine_tsx11_unlock();
204 TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
208 /***********************************************************************
209 * filter_event
211 static Bool filter_event( Display *display, XEvent *event, char *arg )
213 ULONG_PTR mask = (ULONG_PTR)arg;
215 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
217 switch(event->type)
219 case KeyPress:
220 case KeyRelease:
221 case KeymapNotify:
222 case MappingNotify:
223 return (mask & QS_KEY) != 0;
224 case ButtonPress:
225 case ButtonRelease:
226 return (mask & QS_MOUSEBUTTON) != 0;
227 case MotionNotify:
228 case EnterNotify:
229 case LeaveNotify:
230 return (mask & QS_MOUSEMOVE) != 0;
231 case Expose:
232 return (mask & QS_PAINT) != 0;
233 case FocusIn:
234 case FocusOut:
235 case MapNotify:
236 case UnmapNotify:
237 case ConfigureNotify:
238 case PropertyNotify:
239 case ClientMessage:
240 return (mask & QS_POSTMESSAGE) != 0;
241 default:
242 return (mask & QS_SENDMESSAGE) != 0;
247 enum event_merge_action
249 MERGE_DISCARD, /* discard the old event */
250 MERGE_HANDLE, /* handle the old event */
251 MERGE_KEEP /* keep the old event for future merging */
254 /***********************************************************************
255 * merge_events
257 * Try to merge 2 consecutive events.
259 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
261 switch (prev->type)
263 case ConfigureNotify:
264 switch (next->type)
266 case ConfigureNotify:
267 if (prev->xany.window == next->xany.window)
269 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
270 return MERGE_DISCARD;
272 break;
273 case Expose:
274 case PropertyNotify:
275 return MERGE_KEEP;
277 break;
278 case MotionNotify:
279 if (prev->xany.window == next->xany.window && next->type == MotionNotify)
281 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
282 return MERGE_DISCARD;
284 break;
286 return MERGE_HANDLE;
290 /***********************************************************************
291 * call_event_handler
293 static inline void call_event_handler( Display *display, XEvent *event )
295 HWND hwnd;
296 x11drv_event_handler handler;
297 XEvent *prev;
298 struct x11drv_thread_data *thread_data;
300 if (!(handler = find_handler( event->type )))
302 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
303 return; /* no handler, ignore it */
306 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
307 hwnd = 0; /* not for a registered window */
308 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
310 TRACE( "%s for hwnd/window %p/%lx\n",
311 dbgstr_event( event->type ), hwnd, event->xany.window );
312 wine_tsx11_unlock();
313 thread_data = x11drv_thread_data();
314 prev = thread_data->current_event;
315 thread_data->current_event = event;
316 handler( hwnd, event );
317 thread_data->current_event = prev;
318 wine_tsx11_lock();
322 /***********************************************************************
323 * process_events
325 static int process_events( Display *display, Bool (*filter)(), ULONG_PTR arg )
327 XEvent event, prev_event;
328 int count = 0;
329 enum event_merge_action action = MERGE_DISCARD;
331 prev_event.type = 0;
332 wine_tsx11_lock();
333 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
335 count++;
336 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
337 if (prev_event.type) action = merge_events( &prev_event, &event );
338 switch( action )
340 case MERGE_DISCARD: /* discard prev, keep new */
341 prev_event = event;
342 break;
343 case MERGE_HANDLE: /* handle prev, keep new */
344 call_event_handler( display, &prev_event );
345 prev_event = event;
346 break;
347 case MERGE_KEEP: /* handle new, keep prev for future merging */
348 call_event_handler( display, &event );
349 break;
352 XFlush( gdi_display );
353 if (prev_event.type) call_event_handler( display, &prev_event );
354 wine_tsx11_unlock();
355 if (count) TRACE( "processed %d events\n", count );
356 return count;
360 /***********************************************************************
361 * MsgWaitForMultipleObjectsEx (X11DRV.@)
363 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
364 DWORD timeout, DWORD mask, DWORD flags )
366 DWORD ret;
367 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
369 if (!data)
371 if (!count && !timeout) return WAIT_TIMEOUT;
372 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
373 timeout, flags & MWMO_ALERTABLE );
376 if (data->current_event) mask = 0; /* don't process nested events */
378 if (process_events( data->display, filter_event, mask )) ret = count - 1;
379 else if (count || timeout)
381 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
382 timeout, flags & MWMO_ALERTABLE );
383 if (ret == count - 1) process_events( data->display, filter_event, mask );
385 else ret = WAIT_TIMEOUT;
387 return ret;
390 /***********************************************************************
391 * EVENT_x11_time_to_win32_time
393 * Make our timer and the X timer line up as best we can
394 * Pass 0 to retrieve the current adjustment value (times -1)
396 DWORD EVENT_x11_time_to_win32_time(Time time)
398 static DWORD adjust = 0;
399 DWORD now = GetTickCount();
400 DWORD ret;
402 if (! adjust && time != 0)
404 ret = now;
405 adjust = time - now;
407 else
409 /* If we got an event in the 'future', then our clock is clearly wrong.
410 If we got it more than 10000 ms in the future, then it's most likely
411 that the clock has wrapped. */
413 ret = time - adjust;
414 if (ret > now && ((ret - now) < 10000) && time != 0)
416 adjust += ret - now;
417 ret -= ret - now;
421 return ret;
425 /*******************************************************************
426 * can_activate_window
428 * Check if we can activate the specified window.
430 static inline BOOL can_activate_window( HWND hwnd )
432 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
433 if (!(style & WS_VISIBLE)) return FALSE;
434 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
435 if (hwnd == GetDesktopWindow()) return FALSE;
436 return !(style & WS_DISABLED);
440 /**********************************************************************
441 * set_focus
443 static void set_focus( HWND hwnd, Time time )
445 HWND focus;
446 Window win;
448 TRACE( "setting foreground window to %p\n", hwnd );
449 SetForegroundWindow( hwnd );
451 focus = GetFocus();
452 if (focus) focus = GetAncestor( focus, GA_ROOT );
453 win = X11DRV_get_whole_window(focus);
455 if (win)
457 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
458 wine_tsx11_lock();
459 XSetInputFocus( thread_display(), win, RevertToParent, time );
460 wine_tsx11_unlock();
465 /**********************************************************************
466 * handle_wm_protocols
468 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
470 Atom protocol = (Atom)event->data.l[0];
472 if (!protocol) return;
474 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
476 /* Ignore the delete window request if the window has been disabled
477 * and we are in managed mode. This is to disallow applications from
478 * being closed by the window manager while in a modal state.
480 if (IsWindowEnabled(hwnd))
482 HMENU hSysMenu;
484 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
485 hSysMenu = GetSystemMenu(hwnd, FALSE);
486 if (hSysMenu)
488 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
489 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
490 return;
492 if (GetActiveWindow() != hwnd)
494 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
495 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
496 MAKELONG(HTCLOSE,WM_LBUTTONDOWN) );
497 switch(ma)
499 case MA_NOACTIVATEANDEAT:
500 case MA_ACTIVATEANDEAT:
501 return;
502 case MA_NOACTIVATE:
503 break;
504 case MA_ACTIVATE:
505 case 0:
506 SetActiveWindow(hwnd);
507 break;
508 default:
509 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
510 break;
513 PostMessageW( hwnd, WM_X11DRV_DELETE_WINDOW, 0, 0 );
516 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
518 Time event_time = (Time)event->data.l[1];
519 HWND last_focus = x11drv_thread_data()->last_focus;
521 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
522 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
523 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
525 if (can_activate_window(hwnd))
527 /* simulate a mouse click on the caption to find out
528 * whether the window wants to be activated */
529 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
530 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
531 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
532 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
534 set_focus( hwnd, event_time );
535 return;
538 /* try to find some other window to give the focus to */
539 hwnd = GetFocus();
540 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
541 if (!hwnd) hwnd = GetActiveWindow();
542 if (!hwnd) hwnd = last_focus;
543 if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, event_time );
545 else if (protocol == x11drv_atom(_NET_WM_PING))
547 XClientMessageEvent xev;
548 xev = *event;
550 TRACE("NET_WM Ping\n");
551 wine_tsx11_lock();
552 xev.window = DefaultRootWindow(xev.display);
553 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
554 wine_tsx11_unlock();
555 /* this line is semi-stolen from gtk2 */
556 TRACE("NET_WM Pong\n");
561 static const char * const focus_details[] =
563 "NotifyAncestor",
564 "NotifyVirtual",
565 "NotifyInferior",
566 "NotifyNonlinear",
567 "NotifyNonlinearVirtual",
568 "NotifyPointer",
569 "NotifyPointerRoot",
570 "NotifyDetailNone"
573 /**********************************************************************
574 * X11DRV_FocusIn
576 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
578 XFocusChangeEvent *event = &xev->xfocus;
579 XIC xic;
581 if (!hwnd) return;
583 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
585 if (event->detail == NotifyPointer) return;
587 if ((xic = X11DRV_get_ic( hwnd )))
589 wine_tsx11_lock();
590 XSetICFocus( xic );
591 wine_tsx11_unlock();
593 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
595 if (!can_activate_window(hwnd))
597 HWND hwnd = GetFocus();
598 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
599 if (!hwnd) hwnd = GetActiveWindow();
600 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
601 if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, CurrentTime );
603 else SetForegroundWindow( hwnd );
607 /**********************************************************************
608 * X11DRV_FocusOut
610 * Note: only top-level windows get FocusOut events.
612 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
614 XFocusChangeEvent *event = &xev->xfocus;
615 HWND hwnd_tmp;
616 Window focus_win;
617 int revert;
618 XIC xic;
620 if (!hwnd) return;
622 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
624 if (event->detail == NotifyPointer) return;
625 if (ximInComposeMode) return;
627 x11drv_thread_data()->last_focus = hwnd;
628 if ((xic = X11DRV_get_ic( hwnd )))
630 wine_tsx11_lock();
631 XUnsetICFocus( xic );
632 wine_tsx11_unlock();
634 if (hwnd != GetForegroundWindow()) return;
635 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
637 /* don't reset the foreground window, if the window which is
638 getting the focus is a Wine window */
640 wine_tsx11_lock();
641 XGetInputFocus( thread_display(), &focus_win, &revert );
642 if (focus_win)
644 if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0)
645 focus_win = 0;
647 wine_tsx11_unlock();
649 if (!focus_win)
651 /* Abey : 6-Oct-99. Check again if the focus out window is the
652 Foreground window, because in most cases the messages sent
653 above must have already changed the foreground window, in which
654 case we don't have to change the foreground window to 0 */
655 if (hwnd == GetForegroundWindow())
657 TRACE( "lost focus, setting fg to desktop\n" );
658 SetForegroundWindow( GetDesktopWindow() );
664 /***********************************************************************
665 * X11DRV_Expose
667 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
669 XExposeEvent *event = &xev->xexpose;
670 RECT rect;
671 struct x11drv_win_data *data;
672 int flags = RDW_INVALIDATE | RDW_ERASE;
674 TRACE( "win %p (%lx) %d,%d %dx%d\n",
675 hwnd, event->window, event->x, event->y, event->width, event->height );
677 if (!(data = X11DRV_get_win_data( hwnd ))) return;
679 if (event->window == data->whole_window)
681 rect.left = data->whole_rect.left + event->x;
682 rect.top = data->whole_rect.top + event->y;
683 flags |= RDW_FRAME;
685 else
687 rect.left = data->client_rect.left + event->x;
688 rect.top = data->client_rect.top + event->y;
690 rect.right = rect.left + event->width;
691 rect.bottom = rect.top + event->height;
693 if (event->window != root_window)
695 SERVER_START_REQ( update_window_zorder )
697 req->window = hwnd;
698 req->rect.left = rect.left;
699 req->rect.top = rect.top;
700 req->rect.right = rect.right;
701 req->rect.bottom = rect.bottom;
702 wine_server_call( req );
704 SERVER_END_REQ;
706 /* make position relative to client area instead of parent */
707 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
708 flags |= RDW_ALLCHILDREN;
711 RedrawWindow( hwnd, &rect, 0, flags );
715 /**********************************************************************
716 * X11DRV_MapNotify
718 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
720 struct x11drv_win_data *data;
722 if (!(data = X11DRV_get_win_data( hwnd ))) return;
723 if (!data->mapped) return;
725 if (!data->managed)
727 HWND hwndFocus = GetFocus();
728 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
733 /***********************************************************************
734 * get_window_wm_state
736 int get_window_wm_state( Display *display, struct x11drv_win_data *data )
738 struct
740 CARD32 state;
741 XID icon;
742 } *state;
743 Atom type;
744 int format, ret = -1;
745 unsigned long count, remaining;
747 wine_tsx11_lock();
748 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
749 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
750 &type, &format, &count, &remaining, (unsigned char **)&state ))
752 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
753 ret = state->state;
754 XFree( state );
756 wine_tsx11_unlock();
757 return ret;
761 /***********************************************************************
762 * handle_wm_state_notify
764 * Handle a PropertyNotify for WM_STATE.
766 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
767 BOOL update_window )
769 switch(event->state)
771 case PropertyDelete:
772 data->wm_state = WithdrawnState;
773 TRACE( "%p/%lx: WM_STATE deleted\n", data->hwnd, data->whole_window );
774 break;
775 case PropertyNewValue:
777 int new_state = get_window_wm_state( event->display, data );
778 if (new_state != -1 && new_state != data->wm_state)
780 TRACE( "%p/%lx: new WM_STATE %d\n", data->hwnd, data->whole_window, new_state );
781 data->wm_state = new_state;
784 break;
787 if (!update_window || !data->managed || !data->mapped) return;
789 if (data->iconic && data->wm_state == NormalState) /* restore window */
791 int x, y;
792 unsigned int width, height, border, depth;
793 Window root, top;
794 WINDOWPLACEMENT wp;
795 RECT rect;
797 /* FIXME: hack */
798 wine_tsx11_lock();
799 XGetGeometry( event->display, data->whole_window, &root, &x, &y, &width, &height,
800 &border, &depth );
801 XTranslateCoordinates( event->display, data->whole_window, root, 0, 0, &x, &y, &top );
802 wine_tsx11_unlock();
803 rect.left = x;
804 rect.top = y;
805 rect.right = x + width;
806 rect.bottom = y + height;
807 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
808 X11DRV_X_to_window_rect( data, &rect );
810 wp.length = sizeof(wp);
811 GetWindowPlacement( data->hwnd, &wp );
812 wp.flags = 0;
813 wp.showCmd = SW_RESTORE;
814 wp.rcNormalPosition = rect;
816 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
817 data->iconic = FALSE;
818 SetWindowPlacement( data->hwnd, &wp );
820 else if (!data->iconic && data->wm_state == IconicState)
822 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
823 data->iconic = TRUE;
824 ShowWindow( data->hwnd, SW_MINIMIZE );
829 /***********************************************************************
830 * X11DRV_PropertyNotify
832 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
834 XPropertyEvent *event = &xev->xproperty;
835 struct x11drv_win_data *data;
837 if (!hwnd) return;
838 if (!(data = X11DRV_get_win_data( hwnd ))) return;
840 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
844 /* event filter to wait for a WM_STATE change notification on a window */
845 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
847 if (event->xany.window != (Window)arg) return 0;
848 return (event->type == DestroyNotify ||
849 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
852 /***********************************************************************
853 * wait_for_withdrawn_state
855 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
857 DWORD end = GetTickCount() + 2000;
859 if (!data->managed) return;
861 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
862 data->hwnd, data->whole_window, set ? "" : "not " );
864 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
866 XEvent event;
867 int count = 0;
869 wine_tsx11_lock();
870 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
872 count++;
873 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
874 if (event.type == DestroyNotify) call_event_handler( display, &event );
875 else
877 wine_tsx11_unlock();
878 handle_wm_state_notify( data, &event.xproperty, FALSE );
879 wine_tsx11_lock();
882 wine_tsx11_unlock();
884 if (!count)
886 struct pollfd pfd;
887 int timeout = end - GetTickCount();
889 pfd.fd = ConnectionNumber(display);
890 pfd.events = POLLIN;
891 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
893 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
894 break;
898 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
902 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
904 RECT tempRect;
906 if (!IsWindowEnabled(hQueryWnd)) return 0;
908 GetWindowRect(hQueryWnd, &tempRect);
910 if(!PtInRect(&tempRect, *lpPt)) return 0;
912 if (!IsIconic( hQueryWnd ))
914 POINT pt = *lpPt;
915 ScreenToClient( hQueryWnd, &pt );
916 GetClientRect( hQueryWnd, &tempRect );
918 if (PtInRect( &tempRect, pt))
920 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
921 if (ret && ret != hQueryWnd)
923 ret = find_drop_window( ret, lpPt );
924 if (ret) return ret;
929 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
931 ScreenToClient(hQueryWnd, lpPt);
933 return hQueryWnd;
936 /**********************************************************************
937 * EVENT_DropFromOffix
939 * don't know if it still works (last Changelog is from 96/11/04)
941 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
943 struct x11drv_win_data *data;
944 unsigned long data_length;
945 unsigned long aux_long;
946 unsigned char* p_data = NULL;
947 Atom atom_aux;
948 int x, y, dummy;
949 BOOL bAccept;
950 Window win, w_aux_root, w_aux_child;
951 HWND hScope = hWnd;
953 win = X11DRV_get_whole_window(hWnd);
954 wine_tsx11_lock();
955 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
956 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
957 x += virtual_screen_rect.left;
958 y += virtual_screen_rect.top;
959 wine_tsx11_unlock();
961 if (!(data = X11DRV_get_win_data( hWnd ))) return;
963 /* find out drop point and drop window */
964 if( x < 0 || y < 0 ||
965 x > (data->whole_rect.right - data->whole_rect.left) ||
966 y > (data->whole_rect.bottom - data->whole_rect.top) )
968 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
969 x = 0;
970 y = 0;
972 else
974 POINT pt = { x, y };
975 HWND hwndDrop = find_drop_window( hWnd, &pt );
976 if (hwndDrop)
978 x = pt.x;
979 y = pt.y;
980 hScope = hwndDrop;
981 bAccept = TRUE;
983 else
985 bAccept = FALSE;
989 if (!bAccept) return;
991 wine_tsx11_lock();
992 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
993 x11drv_atom(DndSelection), 0, 65535, FALSE,
994 AnyPropertyType, &atom_aux, &dummy,
995 &data_length, &aux_long, &p_data);
996 wine_tsx11_unlock();
998 if( !aux_long && p_data) /* don't bother if > 64K */
1000 char *p = (char *)p_data;
1001 char *p_drop;
1003 aux_long = 0;
1004 while( *p ) /* calculate buffer size */
1006 INT len = GetShortPathNameA( p, NULL, 0 );
1007 if (len) aux_long += len + 1;
1008 p += strlen(p) + 1;
1010 if( aux_long && aux_long < 65535 )
1012 HDROP hDrop;
1013 DROPFILES *lpDrop;
1015 aux_long += sizeof(DROPFILES) + 1;
1016 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1017 lpDrop = (DROPFILES*)GlobalLock( hDrop );
1019 if( lpDrop )
1021 lpDrop->pFiles = sizeof(DROPFILES);
1022 lpDrop->pt.x = x;
1023 lpDrop->pt.y = y;
1024 lpDrop->fNC = FALSE;
1025 lpDrop->fWide = FALSE;
1026 p_drop = (char *)(lpDrop + 1);
1027 p = (char *)p_data;
1028 while(*p)
1030 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1031 p_drop += strlen( p_drop ) + 1;
1032 p += strlen(p) + 1;
1034 *p_drop = '\0';
1035 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1039 wine_tsx11_lock();
1040 if( p_data ) XFree(p_data);
1041 wine_tsx11_unlock();
1044 /**********************************************************************
1045 * EVENT_DropURLs
1047 * drop items are separated by \n
1048 * each item is prefixed by its mime type
1050 * event->data.l[3], event->data.l[4] contains drop x,y position
1052 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1054 struct x11drv_win_data *win_data;
1055 unsigned long data_length;
1056 unsigned long aux_long, drop_len = 0;
1057 unsigned char *p_data = NULL; /* property data */
1058 char *p_drop = NULL;
1059 char *p, *next;
1060 int x, y;
1061 DROPFILES *lpDrop;
1062 HDROP hDrop;
1063 union {
1064 Atom atom_aux;
1065 int i;
1066 Window w_aux;
1067 unsigned int u;
1068 } u; /* unused */
1070 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1072 wine_tsx11_lock();
1073 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1074 x11drv_atom(DndSelection), 0, 65535, FALSE,
1075 AnyPropertyType, &u.atom_aux, &u.i,
1076 &data_length, &aux_long, &p_data);
1077 wine_tsx11_unlock();
1078 if (aux_long)
1079 WARN("property too large, truncated!\n");
1080 TRACE("urls=%s\n", p_data);
1082 if( !aux_long && p_data) { /* don't bother if > 64K */
1083 /* calculate length */
1084 p = (char*) p_data;
1085 next = strchr(p, '\n');
1086 while (p) {
1087 if (next) *next=0;
1088 if (strncmp(p,"file:",5) == 0 ) {
1089 INT len = GetShortPathNameA( p+5, NULL, 0 );
1090 if (len) drop_len += len + 1;
1092 if (next) {
1093 *next = '\n';
1094 p = next + 1;
1095 next = strchr(p, '\n');
1096 } else {
1097 p = NULL;
1101 if( drop_len && drop_len < 65535 ) {
1102 wine_tsx11_lock();
1103 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1104 &x, &y, &u.i, &u.i, &u.u);
1105 x += virtual_screen_rect.left;
1106 y += virtual_screen_rect.top;
1107 wine_tsx11_unlock();
1109 drop_len += sizeof(DROPFILES) + 1;
1110 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1111 lpDrop = (DROPFILES *) GlobalLock( hDrop );
1113 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1115 lpDrop->pFiles = sizeof(DROPFILES);
1116 lpDrop->pt.x = x;
1117 lpDrop->pt.y = y;
1118 lpDrop->fNC =
1119 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1120 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1121 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1122 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1123 lpDrop->fWide = FALSE;
1124 p_drop = (char*)(lpDrop + 1);
1127 /* create message content */
1128 if (p_drop) {
1129 p = (char*) p_data;
1130 next = strchr(p, '\n');
1131 while (p) {
1132 if (next) *next=0;
1133 if (strncmp(p,"file:",5) == 0 ) {
1134 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1135 if (len) {
1136 TRACE("drop file %s as %s\n", p+5, p_drop);
1137 p_drop += len+1;
1138 } else {
1139 WARN("can't convert file %s to dos name\n", p+5);
1141 } else {
1142 WARN("unknown mime type %s\n", p);
1144 if (next) {
1145 *next = '\n';
1146 p = next + 1;
1147 next = strchr(p, '\n');
1148 } else {
1149 p = NULL;
1151 *p_drop = '\0';
1154 GlobalUnlock(hDrop);
1155 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1158 wine_tsx11_lock();
1159 if( p_data ) XFree(p_data);
1160 wine_tsx11_unlock();
1164 /**********************************************************************
1165 * handle_dnd_protocol
1167 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1169 Window root, child;
1170 int root_x, root_y, child_x, child_y;
1171 unsigned int u;
1173 /* query window (drag&drop event contains only drag window) */
1174 wine_tsx11_lock();
1175 XQueryPointer( event->display, root_window, &root, &child,
1176 &root_x, &root_y, &child_x, &child_y, &u);
1177 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1178 wine_tsx11_unlock();
1179 if (!hwnd) return;
1180 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1181 EVENT_DropFromOffiX(hwnd, event);
1182 else if (event->data.l[0] == DndURL)
1183 EVENT_DropURLs(hwnd, event);
1187 struct client_message_handler
1189 int atom; /* protocol atom */
1190 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1193 static const struct client_message_handler client_messages[] =
1195 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1196 { XATOM_DndProtocol, handle_dnd_protocol },
1197 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1198 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1199 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1200 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1204 /**********************************************************************
1205 * X11DRV_ClientMessage
1207 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1209 XClientMessageEvent *event = &xev->xclient;
1210 unsigned int i;
1212 if (!hwnd) return;
1214 if (event->format != 32)
1216 WARN( "Don't know how to handle format %d\n", event->format );
1217 return;
1220 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1222 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1224 client_messages[i].handler( hwnd, event );
1225 return;
1228 TRACE( "no handler found for %ld\n", event->message_type );
1232 /**********************************************************************
1233 * X11DRV_WindowMessage (X11DRV.@)
1235 LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
1237 switch(msg)
1239 case WM_X11DRV_ACQUIRE_SELECTION:
1240 return X11DRV_AcquireClipboard( hwnd );
1241 case WM_X11DRV_DELETE_WINDOW:
1242 return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
1243 case WM_X11DRV_SET_WIN_FORMAT:
1244 return X11DRV_set_win_format( hwnd, (XID)wp );
1245 case WM_X11DRV_RESIZE_DESKTOP:
1246 X11DRV_resize_desktop( LOWORD(lp), HIWORD(lp) );
1247 return 0;
1248 default:
1249 FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp );
1250 return 0;
1255 /***********************************************************************
1256 * X11DRV_SendInput (X11DRV.@)
1258 UINT X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1260 UINT i;
1262 for (i = 0; i < count; i++, inputs++)
1264 switch(inputs->type)
1266 case INPUT_MOUSE:
1267 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1268 inputs->u.mi.mouseData, inputs->u.mi.time,
1269 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1270 break;
1271 case INPUT_KEYBOARD:
1272 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1273 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1274 break;
1275 case INPUT_HARDWARE:
1276 FIXME( "INPUT_HARDWARE not supported\n" );
1277 break;
1280 return count;