push 319afc849e77c7d120112c7718dd0b4fc9a636fe
[wine/hacks.git] / dlls / winex11.drv / event.c
blobaccd6b4cbfd27d3e6e6f1ce609e8c92b06da25fc
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_ConfigureNotify( HWND hwnd, XEvent *event );
81 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
82 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
84 struct event_handler
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 },
101 /* LeaveNotify */
102 { FocusIn, X11DRV_FocusIn },
103 { FocusOut, X11DRV_FocusOut },
104 { KeymapNotify, X11DRV_KeymapNotify },
105 { Expose, X11DRV_Expose },
106 /* GraphicsExpose */
107 /* NoExpose */
108 /* VisibilityNotify */
109 /* CreateNotify */
110 { DestroyNotify, X11DRV_DestroyNotify },
111 /* UnmapNotify */
112 { MapNotify, X11DRV_MapNotify },
113 /* MapRequest */
114 /* ReparentNotify */
115 { ConfigureNotify, X11DRV_ConfigureNotify },
116 /* ConfigureRequest */
117 /* GravityNotify */
118 /* ResizeRequest */
119 /* CirculateNotify */
120 /* CirculateRequest */
121 { PropertyNotify, X11DRV_PropertyNotify },
122 { SelectionClear, X11DRV_SelectionClear },
123 { SelectionRequest, X11DRV_SelectionRequest },
124 /* SelectionNotify */
125 /* ColormapNotify */
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 /***********************************************************************
154 * find_handler
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;
162 while (min <= max)
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;
167 else min = pos + 1;
169 return NULL;
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 )
181 int min, max;
183 wine_tsx11_lock();
184 min = 0;
185 max = nb_event_handlers - 1;
186 while (min <= max)
188 int pos = (min + max) / 2;
189 if (handlers[pos].type == type)
191 handlers[pos].handler = handler;
192 goto done;
194 if (handlers[pos].type > type) max = pos - 1;
195 else min = 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;
201 nb_event_handlers++;
202 assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
203 done:
204 wine_tsx11_unlock();
205 TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
209 /***********************************************************************
210 * filter_event
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;
218 switch(event->type)
220 case KeyPress:
221 case KeyRelease:
222 case KeymapNotify:
223 case MappingNotify:
224 return (mask & QS_KEY) != 0;
225 case ButtonPress:
226 case ButtonRelease:
227 return (mask & QS_MOUSEBUTTON) != 0;
228 case MotionNotify:
229 case EnterNotify:
230 case LeaveNotify:
231 return (mask & QS_MOUSEMOVE) != 0;
232 case Expose:
233 return (mask & QS_PAINT) != 0;
234 case FocusIn:
235 case FocusOut:
236 case MapNotify:
237 case UnmapNotify:
238 case ConfigureNotify:
239 case PropertyNotify:
240 case ClientMessage:
241 return (mask & QS_POSTMESSAGE) != 0;
242 default:
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 /***********************************************************************
256 * merge_events
258 * Try to merge 2 consecutive events.
260 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
262 switch (prev->type)
264 case ConfigureNotify:
265 switch (next->type)
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;
273 break;
274 case Expose:
275 case PropertyNotify:
276 return MERGE_KEEP;
278 break;
279 case MotionNotify:
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;
285 break;
287 return MERGE_HANDLE;
291 /***********************************************************************
292 * call_event_handler
294 static inline void call_event_handler( Display *display, XEvent *event )
296 HWND hwnd;
297 x11drv_event_handler handler;
298 XEvent *prev;
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 );
313 wine_tsx11_unlock();
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;
319 wine_tsx11_lock();
323 /***********************************************************************
324 * process_events
326 static int process_events( Display *display, Bool (*filter)(), ULONG_PTR arg )
328 XEvent event, prev_event;
329 int count = 0;
330 enum event_merge_action action = MERGE_DISCARD;
332 prev_event.type = 0;
333 wine_tsx11_lock();
334 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
336 count++;
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)
350 KeySym keysym = 0;
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 */
364 else
365 continue; /* filtered, ignore it */
367 if (prev_event.type) action = merge_events( &prev_event, &event );
368 switch( action )
370 case MERGE_DISCARD: /* discard prev, keep new */
371 prev_event = event;
372 break;
373 case MERGE_HANDLE: /* handle prev, keep new */
374 call_event_handler( display, &prev_event );
375 prev_event = event;
376 break;
377 case MERGE_KEEP: /* handle new, keep prev for future merging */
378 call_event_handler( display, &event );
379 break;
382 XFlush( gdi_display );
383 if (prev_event.type) call_event_handler( display, &prev_event );
384 wine_tsx11_unlock();
385 if (count) TRACE( "processed %d events\n", count );
386 return count;
390 /***********************************************************************
391 * MsgWaitForMultipleObjectsEx (X11DRV.@)
393 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
394 DWORD timeout, DWORD mask, DWORD flags )
396 DWORD ret;
397 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
399 if (!data)
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;
417 return ret;
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();
430 DWORD ret;
432 if (! adjust && time != 0)
434 ret = now;
435 adjust = time - now;
437 else
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. */
443 ret = time - adjust;
444 if (ret > now && ((ret - now) < 10000) && time != 0)
446 adjust += ret - now;
447 ret -= ret - now;
451 return ret;
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 (hwnd == GetDesktopWindow()) return FALSE;
466 return !(style & WS_DISABLED);
470 /**********************************************************************
471 * set_focus
473 static void set_focus( Display *display, HWND hwnd, Time time )
475 HWND focus;
476 Window win;
478 TRACE( "setting foreground window to %p\n", hwnd );
479 SetForegroundWindow( hwnd );
481 focus = GetFocus();
482 if (focus) focus = GetAncestor( focus, GA_ROOT );
483 win = X11DRV_get_whole_window(focus);
485 if (win)
487 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
488 wine_tsx11_lock();
489 XSetInputFocus( display, win, RevertToParent, time );
490 wine_tsx11_unlock();
495 /**********************************************************************
496 * handle_wm_protocols
498 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
500 Atom protocol = (Atom)event->data.l[0];
502 if (!protocol) return;
504 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
506 /* Ignore the delete window request if the window has been disabled
507 * and we are in managed mode. This is to disallow applications from
508 * being closed by the window manager while in a modal state.
510 if (IsWindowEnabled(hwnd))
512 HMENU hSysMenu;
514 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
515 hSysMenu = GetSystemMenu(hwnd, FALSE);
516 if (hSysMenu)
518 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
519 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
520 return;
522 if (GetActiveWindow() != hwnd)
524 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
525 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
526 MAKELONG(HTCLOSE,WM_LBUTTONDOWN) );
527 switch(ma)
529 case MA_NOACTIVATEANDEAT:
530 case MA_ACTIVATEANDEAT:
531 return;
532 case MA_NOACTIVATE:
533 break;
534 case MA_ACTIVATE:
535 case 0:
536 SetActiveWindow(hwnd);
537 break;
538 default:
539 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
540 break;
543 PostMessageW( hwnd, WM_X11DRV_DELETE_WINDOW, 0, 0 );
546 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
548 Time event_time = (Time)event->data.l[1];
549 HWND last_focus = x11drv_thread_data()->last_focus;
551 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
552 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
553 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
555 if (hwnd == GetForegroundWindow()) return;
557 if (can_activate_window(hwnd))
559 /* simulate a mouse click on the caption to find out
560 * whether the window wants to be activated */
561 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
562 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
563 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
564 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
566 set_focus( event->display, hwnd, event_time );
567 return;
570 /* try to find some other window to give the focus to */
571 hwnd = GetFocus();
572 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
573 if (!hwnd) hwnd = GetActiveWindow();
574 if (!hwnd) hwnd = last_focus;
575 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
577 else if (protocol == x11drv_atom(_NET_WM_PING))
579 XClientMessageEvent xev;
580 xev = *event;
582 TRACE("NET_WM Ping\n");
583 wine_tsx11_lock();
584 xev.window = DefaultRootWindow(xev.display);
585 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
586 wine_tsx11_unlock();
587 /* this line is semi-stolen from gtk2 */
588 TRACE("NET_WM Pong\n");
593 static const char * const focus_details[] =
595 "NotifyAncestor",
596 "NotifyVirtual",
597 "NotifyInferior",
598 "NotifyNonlinear",
599 "NotifyNonlinearVirtual",
600 "NotifyPointer",
601 "NotifyPointerRoot",
602 "NotifyDetailNone"
605 /**********************************************************************
606 * X11DRV_FocusIn
608 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
610 XFocusChangeEvent *event = &xev->xfocus;
611 XIC xic;
613 if (!hwnd) return;
615 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
617 if (event->detail == NotifyPointer) return;
619 if ((xic = X11DRV_get_ic( hwnd )))
621 wine_tsx11_lock();
622 XSetICFocus( xic );
623 wine_tsx11_unlock();
625 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
627 if (!can_activate_window(hwnd))
629 HWND hwnd = GetFocus();
630 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
631 if (!hwnd) hwnd = GetActiveWindow();
632 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
633 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
635 else SetForegroundWindow( hwnd );
639 /**********************************************************************
640 * X11DRV_FocusOut
642 * Note: only top-level windows get FocusOut events.
644 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
646 XFocusChangeEvent *event = &xev->xfocus;
647 HWND hwnd_tmp;
648 Window focus_win;
649 int revert;
650 XIC xic;
652 if (!hwnd) return;
654 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
656 if (event->detail == NotifyPointer) return;
657 if (ximInComposeMode) return;
659 x11drv_thread_data()->last_focus = hwnd;
660 if ((xic = X11DRV_get_ic( hwnd )))
662 wine_tsx11_lock();
663 XUnsetICFocus( xic );
664 wine_tsx11_unlock();
666 if (hwnd != GetForegroundWindow()) return;
667 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
669 /* don't reset the foreground window, if the window which is
670 getting the focus is a Wine window */
672 wine_tsx11_lock();
673 XGetInputFocus( event->display, &focus_win, &revert );
674 if (focus_win)
676 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
677 focus_win = 0;
679 wine_tsx11_unlock();
681 if (!focus_win)
683 /* Abey : 6-Oct-99. Check again if the focus out window is the
684 Foreground window, because in most cases the messages sent
685 above must have already changed the foreground window, in which
686 case we don't have to change the foreground window to 0 */
687 if (hwnd == GetForegroundWindow())
689 TRACE( "lost focus, setting fg to desktop\n" );
690 SetForegroundWindow( GetDesktopWindow() );
696 /***********************************************************************
697 * X11DRV_Expose
699 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
701 XExposeEvent *event = &xev->xexpose;
702 RECT rect;
703 struct x11drv_win_data *data;
704 int flags = RDW_INVALIDATE | RDW_ERASE;
706 TRACE( "win %p (%lx) %d,%d %dx%d\n",
707 hwnd, event->window, event->x, event->y, event->width, event->height );
709 if (!(data = X11DRV_get_win_data( hwnd ))) return;
711 if (event->window == data->whole_window)
713 rect.left = data->whole_rect.left + event->x;
714 rect.top = data->whole_rect.top + event->y;
715 flags |= RDW_FRAME;
717 else
719 rect.left = data->client_rect.left + event->x;
720 rect.top = data->client_rect.top + event->y;
722 rect.right = rect.left + event->width;
723 rect.bottom = rect.top + event->height;
725 if (event->window != root_window)
727 SERVER_START_REQ( update_window_zorder )
729 req->window = hwnd;
730 req->rect.left = rect.left;
731 req->rect.top = rect.top;
732 req->rect.right = rect.right;
733 req->rect.bottom = rect.bottom;
734 wine_server_call( req );
736 SERVER_END_REQ;
738 /* make position relative to client area instead of parent */
739 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
740 flags |= RDW_ALLCHILDREN;
743 RedrawWindow( hwnd, &rect, 0, flags );
747 /**********************************************************************
748 * X11DRV_MapNotify
750 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
752 struct x11drv_win_data *data;
754 if (!(data = X11DRV_get_win_data( hwnd ))) return;
755 if (!data->mapped) return;
757 if (!data->managed)
759 HWND hwndFocus = GetFocus();
760 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
765 /***********************************************************************
766 * X11DRV_ConfigureNotify
768 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
770 XConfigureEvent *event = &xev->xconfigure;
771 struct x11drv_win_data *data;
772 RECT rect;
773 UINT flags;
774 int cx, cy, x = event->x, y = event->y;
776 if (!hwnd) return;
777 if (!(data = X11DRV_get_win_data( hwnd ))) return;
778 if (!data->mapped || data->iconic) return;
780 /* Get geometry */
782 if (!event->send_event) /* normal event, need to map coordinates to the root */
784 Window child;
785 wine_tsx11_lock();
786 XTranslateCoordinates( event->display, data->whole_window, root_window,
787 0, 0, &x, &y, &child );
788 wine_tsx11_unlock();
790 rect.left = x;
791 rect.top = y;
792 rect.right = x + event->width;
793 rect.bottom = y + event->height;
794 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
795 TRACE( "win %p new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
796 hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
797 event->x, event->y, event->width, event->height );
798 X11DRV_X_to_window_rect( data, &rect );
800 x = rect.left;
801 y = rect.top;
802 cx = rect.right - rect.left;
803 cy = rect.bottom - rect.top;
804 flags = SWP_NOACTIVATE | SWP_NOZORDER;
806 /* Compare what has changed */
808 GetWindowRect( hwnd, &rect );
809 if (rect.left == x && rect.top == y) flags |= SWP_NOMOVE;
810 else
811 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
812 hwnd, rect.left, rect.top, x, y );
814 if ((rect.right - rect.left == cx && rect.bottom - rect.top == cy) ||
815 (IsRectEmpty( &rect ) && event->width == 1 && event->height == 1))
817 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
818 flags |= SWP_NOSIZE;
820 else
821 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
822 hwnd, rect.right - rect.left, rect.bottom - rect.top, cx, cy );
824 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
828 /***********************************************************************
829 * get_window_wm_state
831 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
833 struct
835 CARD32 state;
836 XID icon;
837 } *state;
838 Atom type;
839 int format, ret = -1;
840 unsigned long count, remaining;
842 wine_tsx11_lock();
843 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
844 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
845 &type, &format, &count, &remaining, (unsigned char **)&state ))
847 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
848 ret = state->state;
849 XFree( state );
851 wine_tsx11_unlock();
852 return ret;
856 /***********************************************************************
857 * handle_wm_state_notify
859 * Handle a PropertyNotify for WM_STATE.
861 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
862 BOOL update_window )
864 switch(event->state)
866 case PropertyDelete:
867 data->wm_state = WithdrawnState;
868 TRACE( "%p/%lx: WM_STATE deleted\n", data->hwnd, data->whole_window );
869 break;
870 case PropertyNewValue:
872 int new_state = get_window_wm_state( event->display, data );
873 if (new_state != -1 && new_state != data->wm_state)
875 TRACE( "%p/%lx: new WM_STATE %d\n", data->hwnd, data->whole_window, new_state );
876 data->wm_state = new_state;
879 break;
882 if (!update_window || !data->managed || !data->mapped) return;
884 if (data->iconic && data->wm_state == NormalState) /* restore window */
886 int x, y;
887 unsigned int width, height, border, depth;
888 Window root, top;
889 WINDOWPLACEMENT wp;
890 RECT rect;
892 /* FIXME: hack */
893 wine_tsx11_lock();
894 XGetGeometry( event->display, data->whole_window, &root, &x, &y, &width, &height,
895 &border, &depth );
896 XTranslateCoordinates( event->display, data->whole_window, root, 0, 0, &x, &y, &top );
897 wine_tsx11_unlock();
898 rect.left = x;
899 rect.top = y;
900 rect.right = x + width;
901 rect.bottom = y + height;
902 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
903 X11DRV_X_to_window_rect( data, &rect );
905 wp.length = sizeof(wp);
906 GetWindowPlacement( data->hwnd, &wp );
907 wp.flags = 0;
908 wp.showCmd = SW_RESTORE;
909 wp.rcNormalPosition = rect;
911 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
912 data->iconic = FALSE;
913 SetWindowPlacement( data->hwnd, &wp );
915 else if (!data->iconic && data->wm_state == IconicState)
917 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
918 data->iconic = TRUE;
919 ShowWindow( data->hwnd, SW_MINIMIZE );
924 /***********************************************************************
925 * X11DRV_PropertyNotify
927 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
929 XPropertyEvent *event = &xev->xproperty;
930 struct x11drv_win_data *data;
932 if (!hwnd) return;
933 if (!(data = X11DRV_get_win_data( hwnd ))) return;
935 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
939 /* event filter to wait for a WM_STATE change notification on a window */
940 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
942 if (event->xany.window != (Window)arg) return 0;
943 return (event->type == DestroyNotify ||
944 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
947 /***********************************************************************
948 * wait_for_withdrawn_state
950 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
952 DWORD end = GetTickCount() + 2000;
954 if (!data->managed) return;
956 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
957 data->hwnd, data->whole_window, set ? "" : "not " );
959 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
961 XEvent event;
962 int count = 0;
964 wine_tsx11_lock();
965 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
967 count++;
968 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
969 if (event.type == DestroyNotify) call_event_handler( display, &event );
970 else
972 wine_tsx11_unlock();
973 handle_wm_state_notify( data, &event.xproperty, FALSE );
974 wine_tsx11_lock();
977 wine_tsx11_unlock();
979 if (!count)
981 struct pollfd pfd;
982 int timeout = end - GetTickCount();
984 pfd.fd = ConnectionNumber(display);
985 pfd.events = POLLIN;
986 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
988 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
989 break;
993 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
997 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
999 RECT tempRect;
1001 if (!IsWindowEnabled(hQueryWnd)) return 0;
1003 GetWindowRect(hQueryWnd, &tempRect);
1005 if(!PtInRect(&tempRect, *lpPt)) return 0;
1007 if (!IsIconic( hQueryWnd ))
1009 POINT pt = *lpPt;
1010 ScreenToClient( hQueryWnd, &pt );
1011 GetClientRect( hQueryWnd, &tempRect );
1013 if (PtInRect( &tempRect, pt))
1015 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1016 if (ret && ret != hQueryWnd)
1018 ret = find_drop_window( ret, lpPt );
1019 if (ret) return ret;
1024 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1026 ScreenToClient(hQueryWnd, lpPt);
1028 return hQueryWnd;
1031 /**********************************************************************
1032 * EVENT_DropFromOffix
1034 * don't know if it still works (last Changelog is from 96/11/04)
1036 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1038 struct x11drv_win_data *data;
1039 unsigned long data_length;
1040 unsigned long aux_long;
1041 unsigned char* p_data = NULL;
1042 Atom atom_aux;
1043 int x, y, dummy;
1044 BOOL bAccept;
1045 Window win, w_aux_root, w_aux_child;
1047 win = X11DRV_get_whole_window(hWnd);
1048 wine_tsx11_lock();
1049 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1050 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1051 x += virtual_screen_rect.left;
1052 y += virtual_screen_rect.top;
1053 wine_tsx11_unlock();
1055 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1057 /* find out drop point and drop window */
1058 if( x < 0 || y < 0 ||
1059 x > (data->whole_rect.right - data->whole_rect.left) ||
1060 y > (data->whole_rect.bottom - data->whole_rect.top) )
1062 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1063 x = 0;
1064 y = 0;
1066 else
1068 POINT pt = { x, y };
1069 HWND hwndDrop = find_drop_window( hWnd, &pt );
1070 if (hwndDrop)
1072 x = pt.x;
1073 y = pt.y;
1074 bAccept = TRUE;
1076 else
1078 bAccept = FALSE;
1082 if (!bAccept) return;
1084 wine_tsx11_lock();
1085 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1086 x11drv_atom(DndSelection), 0, 65535, FALSE,
1087 AnyPropertyType, &atom_aux, &dummy,
1088 &data_length, &aux_long, &p_data);
1089 wine_tsx11_unlock();
1091 if( !aux_long && p_data) /* don't bother if > 64K */
1093 char *p = (char *)p_data;
1094 char *p_drop;
1096 aux_long = 0;
1097 while( *p ) /* calculate buffer size */
1099 INT len = GetShortPathNameA( p, NULL, 0 );
1100 if (len) aux_long += len + 1;
1101 p += strlen(p) + 1;
1103 if( aux_long && aux_long < 65535 )
1105 HDROP hDrop;
1106 DROPFILES *lpDrop;
1108 aux_long += sizeof(DROPFILES) + 1;
1109 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1110 lpDrop = (DROPFILES*)GlobalLock( hDrop );
1112 if( lpDrop )
1114 lpDrop->pFiles = sizeof(DROPFILES);
1115 lpDrop->pt.x = x;
1116 lpDrop->pt.y = y;
1117 lpDrop->fNC = FALSE;
1118 lpDrop->fWide = FALSE;
1119 p_drop = (char *)(lpDrop + 1);
1120 p = (char *)p_data;
1121 while(*p)
1123 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1124 p_drop += strlen( p_drop ) + 1;
1125 p += strlen(p) + 1;
1127 *p_drop = '\0';
1128 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1132 wine_tsx11_lock();
1133 if( p_data ) XFree(p_data);
1134 wine_tsx11_unlock();
1137 /**********************************************************************
1138 * EVENT_DropURLs
1140 * drop items are separated by \n
1141 * each item is prefixed by its mime type
1143 * event->data.l[3], event->data.l[4] contains drop x,y position
1145 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1147 struct x11drv_win_data *win_data;
1148 unsigned long data_length;
1149 unsigned long aux_long, drop_len = 0;
1150 unsigned char *p_data = NULL; /* property data */
1151 char *p_drop = NULL;
1152 char *p, *next;
1153 int x, y;
1154 DROPFILES *lpDrop;
1155 HDROP hDrop;
1156 union {
1157 Atom atom_aux;
1158 int i;
1159 Window w_aux;
1160 unsigned int u;
1161 } u; /* unused */
1163 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1165 wine_tsx11_lock();
1166 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1167 x11drv_atom(DndSelection), 0, 65535, FALSE,
1168 AnyPropertyType, &u.atom_aux, &u.i,
1169 &data_length, &aux_long, &p_data);
1170 wine_tsx11_unlock();
1171 if (aux_long)
1172 WARN("property too large, truncated!\n");
1173 TRACE("urls=%s\n", p_data);
1175 if( !aux_long && p_data) { /* don't bother if > 64K */
1176 /* calculate length */
1177 p = (char*) p_data;
1178 next = strchr(p, '\n');
1179 while (p) {
1180 if (next) *next=0;
1181 if (strncmp(p,"file:",5) == 0 ) {
1182 INT len = GetShortPathNameA( p+5, NULL, 0 );
1183 if (len) drop_len += len + 1;
1185 if (next) {
1186 *next = '\n';
1187 p = next + 1;
1188 next = strchr(p, '\n');
1189 } else {
1190 p = NULL;
1194 if( drop_len && drop_len < 65535 ) {
1195 wine_tsx11_lock();
1196 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1197 &x, &y, &u.i, &u.i, &u.u);
1198 x += virtual_screen_rect.left;
1199 y += virtual_screen_rect.top;
1200 wine_tsx11_unlock();
1202 drop_len += sizeof(DROPFILES) + 1;
1203 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1204 lpDrop = (DROPFILES *) GlobalLock( hDrop );
1206 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1208 lpDrop->pFiles = sizeof(DROPFILES);
1209 lpDrop->pt.x = x;
1210 lpDrop->pt.y = y;
1211 lpDrop->fNC =
1212 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1213 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1214 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1215 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1216 lpDrop->fWide = FALSE;
1217 p_drop = (char*)(lpDrop + 1);
1220 /* create message content */
1221 if (p_drop) {
1222 p = (char*) p_data;
1223 next = strchr(p, '\n');
1224 while (p) {
1225 if (next) *next=0;
1226 if (strncmp(p,"file:",5) == 0 ) {
1227 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1228 if (len) {
1229 TRACE("drop file %s as %s\n", p+5, p_drop);
1230 p_drop += len+1;
1231 } else {
1232 WARN("can't convert file %s to dos name\n", p+5);
1234 } else {
1235 WARN("unknown mime type %s\n", p);
1237 if (next) {
1238 *next = '\n';
1239 p = next + 1;
1240 next = strchr(p, '\n');
1241 } else {
1242 p = NULL;
1244 *p_drop = '\0';
1247 GlobalUnlock(hDrop);
1248 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1251 wine_tsx11_lock();
1252 if( p_data ) XFree(p_data);
1253 wine_tsx11_unlock();
1257 /**********************************************************************
1258 * handle_dnd_protocol
1260 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1262 Window root, child;
1263 int root_x, root_y, child_x, child_y;
1264 unsigned int u;
1266 /* query window (drag&drop event contains only drag window) */
1267 wine_tsx11_lock();
1268 XQueryPointer( event->display, root_window, &root, &child,
1269 &root_x, &root_y, &child_x, &child_y, &u);
1270 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1271 wine_tsx11_unlock();
1272 if (!hwnd) return;
1273 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1274 EVENT_DropFromOffiX(hwnd, event);
1275 else if (event->data.l[0] == DndURL)
1276 EVENT_DropURLs(hwnd, event);
1280 struct client_message_handler
1282 int atom; /* protocol atom */
1283 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1286 static const struct client_message_handler client_messages[] =
1288 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1289 { XATOM_DndProtocol, handle_dnd_protocol },
1290 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1291 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1292 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1293 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1297 /**********************************************************************
1298 * X11DRV_ClientMessage
1300 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1302 XClientMessageEvent *event = &xev->xclient;
1303 unsigned int i;
1305 if (!hwnd) return;
1307 if (event->format != 32)
1309 WARN( "Don't know how to handle format %d\n", event->format );
1310 return;
1313 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1315 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1317 client_messages[i].handler( hwnd, event );
1318 return;
1321 TRACE( "no handler found for %ld\n", event->message_type );
1325 /**********************************************************************
1326 * X11DRV_WindowMessage (X11DRV.@)
1328 LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
1330 switch(msg)
1332 case WM_X11DRV_ACQUIRE_SELECTION:
1333 return X11DRV_AcquireClipboard( hwnd );
1334 case WM_X11DRV_DELETE_WINDOW:
1335 return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
1336 case WM_X11DRV_SET_WIN_FORMAT:
1337 return X11DRV_set_win_format( hwnd, (XID)wp );
1338 case WM_X11DRV_RESIZE_DESKTOP:
1339 X11DRV_resize_desktop( LOWORD(lp), HIWORD(lp) );
1340 return 0;
1341 default:
1342 FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp );
1343 return 0;
1348 /***********************************************************************
1349 * X11DRV_SendInput (X11DRV.@)
1351 UINT X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1353 UINT i;
1355 for (i = 0; i < count; i++, inputs++)
1357 switch(inputs->type)
1359 case INPUT_MOUSE:
1360 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1361 inputs->u.mi.mouseData, inputs->u.mi.time,
1362 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1363 break;
1364 case INPUT_KEYBOARD:
1365 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1366 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1367 break;
1368 case INPUT_HARDWARE:
1369 FIXME( "INPUT_HARDWARE not supported\n" );
1370 break;
1373 return count;