winex11: Pass the display as parameter to a few more functions instead of using threa...
[wine/multimedia.git] / dlls / winex11.drv / event.c
blob5b26b71e9217e6fdf0c0985330e14654f90223bf
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 )) continue; /* filtered, ignore it */
338 if (prev_event.type) action = merge_events( &prev_event, &event );
339 switch( action )
341 case MERGE_DISCARD: /* discard prev, keep new */
342 prev_event = event;
343 break;
344 case MERGE_HANDLE: /* handle prev, keep new */
345 call_event_handler( display, &prev_event );
346 prev_event = event;
347 break;
348 case MERGE_KEEP: /* handle new, keep prev for future merging */
349 call_event_handler( display, &event );
350 break;
353 XFlush( gdi_display );
354 if (prev_event.type) call_event_handler( display, &prev_event );
355 wine_tsx11_unlock();
356 if (count) TRACE( "processed %d events\n", count );
357 return count;
361 /***********************************************************************
362 * MsgWaitForMultipleObjectsEx (X11DRV.@)
364 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
365 DWORD timeout, DWORD mask, DWORD flags )
367 DWORD ret;
368 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
370 if (!data)
372 if (!count && !timeout) return WAIT_TIMEOUT;
373 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
374 timeout, flags & MWMO_ALERTABLE );
377 if (data->current_event) mask = 0; /* don't process nested events */
379 if (process_events( data->display, filter_event, mask )) ret = count - 1;
380 else if (count || timeout)
382 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
383 timeout, flags & MWMO_ALERTABLE );
384 if (ret == count - 1) process_events( data->display, filter_event, mask );
386 else ret = WAIT_TIMEOUT;
388 return ret;
391 /***********************************************************************
392 * EVENT_x11_time_to_win32_time
394 * Make our timer and the X timer line up as best we can
395 * Pass 0 to retrieve the current adjustment value (times -1)
397 DWORD EVENT_x11_time_to_win32_time(Time time)
399 static DWORD adjust = 0;
400 DWORD now = GetTickCount();
401 DWORD ret;
403 if (! adjust && time != 0)
405 ret = now;
406 adjust = time - now;
408 else
410 /* If we got an event in the 'future', then our clock is clearly wrong.
411 If we got it more than 10000 ms in the future, then it's most likely
412 that the clock has wrapped. */
414 ret = time - adjust;
415 if (ret > now && ((ret - now) < 10000) && time != 0)
417 adjust += ret - now;
418 ret -= ret - now;
422 return ret;
426 /*******************************************************************
427 * can_activate_window
429 * Check if we can activate the specified window.
431 static inline BOOL can_activate_window( HWND hwnd )
433 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
434 if (!(style & WS_VISIBLE)) return FALSE;
435 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
436 if (hwnd == GetDesktopWindow()) return FALSE;
437 return !(style & WS_DISABLED);
441 /**********************************************************************
442 * set_focus
444 static void set_focus( Display *display, HWND hwnd, Time time )
446 HWND focus;
447 Window win;
449 TRACE( "setting foreground window to %p\n", hwnd );
450 SetForegroundWindow( hwnd );
452 focus = GetFocus();
453 if (focus) focus = GetAncestor( focus, GA_ROOT );
454 win = X11DRV_get_whole_window(focus);
456 if (win)
458 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
459 wine_tsx11_lock();
460 XSetInputFocus( display, win, RevertToParent, time );
461 wine_tsx11_unlock();
466 /**********************************************************************
467 * handle_wm_protocols
469 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
471 Atom protocol = (Atom)event->data.l[0];
473 if (!protocol) return;
475 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
477 /* Ignore the delete window request if the window has been disabled
478 * and we are in managed mode. This is to disallow applications from
479 * being closed by the window manager while in a modal state.
481 if (IsWindowEnabled(hwnd))
483 HMENU hSysMenu;
485 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
486 hSysMenu = GetSystemMenu(hwnd, FALSE);
487 if (hSysMenu)
489 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
490 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
491 return;
493 if (GetActiveWindow() != hwnd)
495 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
496 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
497 MAKELONG(HTCLOSE,WM_LBUTTONDOWN) );
498 switch(ma)
500 case MA_NOACTIVATEANDEAT:
501 case MA_ACTIVATEANDEAT:
502 return;
503 case MA_NOACTIVATE:
504 break;
505 case MA_ACTIVATE:
506 case 0:
507 SetActiveWindow(hwnd);
508 break;
509 default:
510 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
511 break;
514 PostMessageW( hwnd, WM_X11DRV_DELETE_WINDOW, 0, 0 );
517 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
519 Time event_time = (Time)event->data.l[1];
520 HWND last_focus = x11drv_thread_data()->last_focus;
522 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
523 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
524 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
526 if (can_activate_window(hwnd))
528 /* simulate a mouse click on the caption to find out
529 * whether the window wants to be activated */
530 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
531 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
532 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
533 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
535 set_focus( event->display, hwnd, event_time );
536 return;
539 /* try to find some other window to give the focus to */
540 hwnd = GetFocus();
541 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
542 if (!hwnd) hwnd = GetActiveWindow();
543 if (!hwnd) hwnd = last_focus;
544 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
546 else if (protocol == x11drv_atom(_NET_WM_PING))
548 XClientMessageEvent xev;
549 xev = *event;
551 TRACE("NET_WM Ping\n");
552 wine_tsx11_lock();
553 xev.window = DefaultRootWindow(xev.display);
554 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
555 wine_tsx11_unlock();
556 /* this line is semi-stolen from gtk2 */
557 TRACE("NET_WM Pong\n");
562 static const char * const focus_details[] =
564 "NotifyAncestor",
565 "NotifyVirtual",
566 "NotifyInferior",
567 "NotifyNonlinear",
568 "NotifyNonlinearVirtual",
569 "NotifyPointer",
570 "NotifyPointerRoot",
571 "NotifyDetailNone"
574 /**********************************************************************
575 * X11DRV_FocusIn
577 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
579 XFocusChangeEvent *event = &xev->xfocus;
580 XIC xic;
582 if (!hwnd) return;
584 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
586 if (event->detail == NotifyPointer) return;
588 if ((xic = X11DRV_get_ic( hwnd )))
590 wine_tsx11_lock();
591 XSetICFocus( xic );
592 wine_tsx11_unlock();
594 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
596 if (!can_activate_window(hwnd))
598 HWND hwnd = GetFocus();
599 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
600 if (!hwnd) hwnd = GetActiveWindow();
601 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
602 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
604 else SetForegroundWindow( hwnd );
608 /**********************************************************************
609 * X11DRV_FocusOut
611 * Note: only top-level windows get FocusOut events.
613 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
615 XFocusChangeEvent *event = &xev->xfocus;
616 HWND hwnd_tmp;
617 Window focus_win;
618 int revert;
619 XIC xic;
621 if (!hwnd) return;
623 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
625 if (event->detail == NotifyPointer) return;
626 if (ximInComposeMode) return;
628 x11drv_thread_data()->last_focus = hwnd;
629 if ((xic = X11DRV_get_ic( hwnd )))
631 wine_tsx11_lock();
632 XUnsetICFocus( xic );
633 wine_tsx11_unlock();
635 if (hwnd != GetForegroundWindow()) return;
636 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
638 /* don't reset the foreground window, if the window which is
639 getting the focus is a Wine window */
641 wine_tsx11_lock();
642 XGetInputFocus( event->display, &focus_win, &revert );
643 if (focus_win)
645 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
646 focus_win = 0;
648 wine_tsx11_unlock();
650 if (!focus_win)
652 /* Abey : 6-Oct-99. Check again if the focus out window is the
653 Foreground window, because in most cases the messages sent
654 above must have already changed the foreground window, in which
655 case we don't have to change the foreground window to 0 */
656 if (hwnd == GetForegroundWindow())
658 TRACE( "lost focus, setting fg to desktop\n" );
659 SetForegroundWindow( GetDesktopWindow() );
665 /***********************************************************************
666 * X11DRV_Expose
668 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
670 XExposeEvent *event = &xev->xexpose;
671 RECT rect;
672 struct x11drv_win_data *data;
673 int flags = RDW_INVALIDATE | RDW_ERASE;
675 TRACE( "win %p (%lx) %d,%d %dx%d\n",
676 hwnd, event->window, event->x, event->y, event->width, event->height );
678 if (!(data = X11DRV_get_win_data( hwnd ))) return;
680 if (event->window == data->whole_window)
682 rect.left = data->whole_rect.left + event->x;
683 rect.top = data->whole_rect.top + event->y;
684 flags |= RDW_FRAME;
686 else
688 rect.left = data->client_rect.left + event->x;
689 rect.top = data->client_rect.top + event->y;
691 rect.right = rect.left + event->width;
692 rect.bottom = rect.top + event->height;
694 if (event->window != root_window)
696 SERVER_START_REQ( update_window_zorder )
698 req->window = hwnd;
699 req->rect.left = rect.left;
700 req->rect.top = rect.top;
701 req->rect.right = rect.right;
702 req->rect.bottom = rect.bottom;
703 wine_server_call( req );
705 SERVER_END_REQ;
707 /* make position relative to client area instead of parent */
708 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
709 flags |= RDW_ALLCHILDREN;
712 RedrawWindow( hwnd, &rect, 0, flags );
716 /**********************************************************************
717 * X11DRV_MapNotify
719 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
721 struct x11drv_win_data *data;
723 if (!(data = X11DRV_get_win_data( hwnd ))) return;
724 if (!data->mapped) return;
726 if (!data->managed)
728 HWND hwndFocus = GetFocus();
729 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
734 /***********************************************************************
735 * X11DRV_ConfigureNotify
737 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
739 XConfigureEvent *event = &xev->xconfigure;
740 struct x11drv_win_data *data;
741 RECT rect;
742 UINT flags;
743 int cx, cy, x = event->x, y = event->y;
745 if (!hwnd) return;
746 if (!(data = X11DRV_get_win_data( hwnd ))) return;
747 if (!data->mapped || data->iconic) return;
749 /* Get geometry */
751 if (!event->send_event) /* normal event, need to map coordinates to the root */
753 Window child;
754 wine_tsx11_lock();
755 XTranslateCoordinates( event->display, data->whole_window, root_window,
756 0, 0, &x, &y, &child );
757 wine_tsx11_unlock();
759 rect.left = x;
760 rect.top = y;
761 rect.right = x + event->width;
762 rect.bottom = y + event->height;
763 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
764 TRACE( "win %p new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
765 hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
766 event->x, event->y, event->width, event->height );
767 X11DRV_X_to_window_rect( data, &rect );
769 x = rect.left;
770 y = rect.top;
771 cx = rect.right - rect.left;
772 cy = rect.bottom - rect.top;
773 flags = SWP_NOACTIVATE | SWP_NOZORDER;
775 /* Compare what has changed */
777 GetWindowRect( hwnd, &rect );
778 if (rect.left == x && rect.top == y) flags |= SWP_NOMOVE;
779 else
780 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
781 hwnd, rect.left, rect.top, x, y );
783 if ((rect.right - rect.left == cx && rect.bottom - rect.top == cy) ||
784 (IsRectEmpty( &rect ) && event->width == 1 && event->height == 1))
786 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
787 flags |= SWP_NOSIZE;
789 else
790 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
791 hwnd, rect.right - rect.left, rect.bottom - rect.top, cx, cy );
793 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
797 /***********************************************************************
798 * get_window_wm_state
800 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
802 struct
804 CARD32 state;
805 XID icon;
806 } *state;
807 Atom type;
808 int format, ret = -1;
809 unsigned long count, remaining;
811 wine_tsx11_lock();
812 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
813 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
814 &type, &format, &count, &remaining, (unsigned char **)&state ))
816 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
817 ret = state->state;
818 XFree( state );
820 wine_tsx11_unlock();
821 return ret;
825 /***********************************************************************
826 * handle_wm_state_notify
828 * Handle a PropertyNotify for WM_STATE.
830 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
831 BOOL update_window )
833 switch(event->state)
835 case PropertyDelete:
836 data->wm_state = WithdrawnState;
837 TRACE( "%p/%lx: WM_STATE deleted\n", data->hwnd, data->whole_window );
838 break;
839 case PropertyNewValue:
841 int new_state = get_window_wm_state( event->display, data );
842 if (new_state != -1 && new_state != data->wm_state)
844 TRACE( "%p/%lx: new WM_STATE %d\n", data->hwnd, data->whole_window, new_state );
845 data->wm_state = new_state;
848 break;
851 if (!update_window || !data->managed || !data->mapped) return;
853 if (data->iconic && data->wm_state == NormalState) /* restore window */
855 int x, y;
856 unsigned int width, height, border, depth;
857 Window root, top;
858 WINDOWPLACEMENT wp;
859 RECT rect;
861 /* FIXME: hack */
862 wine_tsx11_lock();
863 XGetGeometry( event->display, data->whole_window, &root, &x, &y, &width, &height,
864 &border, &depth );
865 XTranslateCoordinates( event->display, data->whole_window, root, 0, 0, &x, &y, &top );
866 wine_tsx11_unlock();
867 rect.left = x;
868 rect.top = y;
869 rect.right = x + width;
870 rect.bottom = y + height;
871 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
872 X11DRV_X_to_window_rect( data, &rect );
874 wp.length = sizeof(wp);
875 GetWindowPlacement( data->hwnd, &wp );
876 wp.flags = 0;
877 wp.showCmd = SW_RESTORE;
878 wp.rcNormalPosition = rect;
880 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
881 data->iconic = FALSE;
882 SetWindowPlacement( data->hwnd, &wp );
884 else if (!data->iconic && data->wm_state == IconicState)
886 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
887 data->iconic = TRUE;
888 ShowWindow( data->hwnd, SW_MINIMIZE );
893 /***********************************************************************
894 * X11DRV_PropertyNotify
896 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
898 XPropertyEvent *event = &xev->xproperty;
899 struct x11drv_win_data *data;
901 if (!hwnd) return;
902 if (!(data = X11DRV_get_win_data( hwnd ))) return;
904 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
908 /* event filter to wait for a WM_STATE change notification on a window */
909 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
911 if (event->xany.window != (Window)arg) return 0;
912 return (event->type == DestroyNotify ||
913 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
916 /***********************************************************************
917 * wait_for_withdrawn_state
919 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
921 DWORD end = GetTickCount() + 2000;
923 if (!data->managed) return;
925 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
926 data->hwnd, data->whole_window, set ? "" : "not " );
928 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
930 XEvent event;
931 int count = 0;
933 wine_tsx11_lock();
934 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
936 count++;
937 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
938 if (event.type == DestroyNotify) call_event_handler( display, &event );
939 else
941 wine_tsx11_unlock();
942 handle_wm_state_notify( data, &event.xproperty, FALSE );
943 wine_tsx11_lock();
946 wine_tsx11_unlock();
948 if (!count)
950 struct pollfd pfd;
951 int timeout = end - GetTickCount();
953 pfd.fd = ConnectionNumber(display);
954 pfd.events = POLLIN;
955 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
957 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
958 break;
962 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
966 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
968 RECT tempRect;
970 if (!IsWindowEnabled(hQueryWnd)) return 0;
972 GetWindowRect(hQueryWnd, &tempRect);
974 if(!PtInRect(&tempRect, *lpPt)) return 0;
976 if (!IsIconic( hQueryWnd ))
978 POINT pt = *lpPt;
979 ScreenToClient( hQueryWnd, &pt );
980 GetClientRect( hQueryWnd, &tempRect );
982 if (PtInRect( &tempRect, pt))
984 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
985 if (ret && ret != hQueryWnd)
987 ret = find_drop_window( ret, lpPt );
988 if (ret) return ret;
993 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
995 ScreenToClient(hQueryWnd, lpPt);
997 return hQueryWnd;
1000 /**********************************************************************
1001 * EVENT_DropFromOffix
1003 * don't know if it still works (last Changelog is from 96/11/04)
1005 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1007 struct x11drv_win_data *data;
1008 unsigned long data_length;
1009 unsigned long aux_long;
1010 unsigned char* p_data = NULL;
1011 Atom atom_aux;
1012 int x, y, dummy;
1013 BOOL bAccept;
1014 Window win, w_aux_root, w_aux_child;
1016 win = X11DRV_get_whole_window(hWnd);
1017 wine_tsx11_lock();
1018 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1019 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1020 x += virtual_screen_rect.left;
1021 y += virtual_screen_rect.top;
1022 wine_tsx11_unlock();
1024 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1026 /* find out drop point and drop window */
1027 if( x < 0 || y < 0 ||
1028 x > (data->whole_rect.right - data->whole_rect.left) ||
1029 y > (data->whole_rect.bottom - data->whole_rect.top) )
1031 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1032 x = 0;
1033 y = 0;
1035 else
1037 POINT pt = { x, y };
1038 HWND hwndDrop = find_drop_window( hWnd, &pt );
1039 if (hwndDrop)
1041 x = pt.x;
1042 y = pt.y;
1043 bAccept = TRUE;
1045 else
1047 bAccept = FALSE;
1051 if (!bAccept) return;
1053 wine_tsx11_lock();
1054 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1055 x11drv_atom(DndSelection), 0, 65535, FALSE,
1056 AnyPropertyType, &atom_aux, &dummy,
1057 &data_length, &aux_long, &p_data);
1058 wine_tsx11_unlock();
1060 if( !aux_long && p_data) /* don't bother if > 64K */
1062 char *p = (char *)p_data;
1063 char *p_drop;
1065 aux_long = 0;
1066 while( *p ) /* calculate buffer size */
1068 INT len = GetShortPathNameA( p, NULL, 0 );
1069 if (len) aux_long += len + 1;
1070 p += strlen(p) + 1;
1072 if( aux_long && aux_long < 65535 )
1074 HDROP hDrop;
1075 DROPFILES *lpDrop;
1077 aux_long += sizeof(DROPFILES) + 1;
1078 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1079 lpDrop = (DROPFILES*)GlobalLock( hDrop );
1081 if( lpDrop )
1083 lpDrop->pFiles = sizeof(DROPFILES);
1084 lpDrop->pt.x = x;
1085 lpDrop->pt.y = y;
1086 lpDrop->fNC = FALSE;
1087 lpDrop->fWide = FALSE;
1088 p_drop = (char *)(lpDrop + 1);
1089 p = (char *)p_data;
1090 while(*p)
1092 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1093 p_drop += strlen( p_drop ) + 1;
1094 p += strlen(p) + 1;
1096 *p_drop = '\0';
1097 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1101 wine_tsx11_lock();
1102 if( p_data ) XFree(p_data);
1103 wine_tsx11_unlock();
1106 /**********************************************************************
1107 * EVENT_DropURLs
1109 * drop items are separated by \n
1110 * each item is prefixed by its mime type
1112 * event->data.l[3], event->data.l[4] contains drop x,y position
1114 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1116 struct x11drv_win_data *win_data;
1117 unsigned long data_length;
1118 unsigned long aux_long, drop_len = 0;
1119 unsigned char *p_data = NULL; /* property data */
1120 char *p_drop = NULL;
1121 char *p, *next;
1122 int x, y;
1123 DROPFILES *lpDrop;
1124 HDROP hDrop;
1125 union {
1126 Atom atom_aux;
1127 int i;
1128 Window w_aux;
1129 unsigned int u;
1130 } u; /* unused */
1132 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1134 wine_tsx11_lock();
1135 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1136 x11drv_atom(DndSelection), 0, 65535, FALSE,
1137 AnyPropertyType, &u.atom_aux, &u.i,
1138 &data_length, &aux_long, &p_data);
1139 wine_tsx11_unlock();
1140 if (aux_long)
1141 WARN("property too large, truncated!\n");
1142 TRACE("urls=%s\n", p_data);
1144 if( !aux_long && p_data) { /* don't bother if > 64K */
1145 /* calculate length */
1146 p = (char*) p_data;
1147 next = strchr(p, '\n');
1148 while (p) {
1149 if (next) *next=0;
1150 if (strncmp(p,"file:",5) == 0 ) {
1151 INT len = GetShortPathNameA( p+5, NULL, 0 );
1152 if (len) drop_len += len + 1;
1154 if (next) {
1155 *next = '\n';
1156 p = next + 1;
1157 next = strchr(p, '\n');
1158 } else {
1159 p = NULL;
1163 if( drop_len && drop_len < 65535 ) {
1164 wine_tsx11_lock();
1165 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1166 &x, &y, &u.i, &u.i, &u.u);
1167 x += virtual_screen_rect.left;
1168 y += virtual_screen_rect.top;
1169 wine_tsx11_unlock();
1171 drop_len += sizeof(DROPFILES) + 1;
1172 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1173 lpDrop = (DROPFILES *) GlobalLock( hDrop );
1175 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1177 lpDrop->pFiles = sizeof(DROPFILES);
1178 lpDrop->pt.x = x;
1179 lpDrop->pt.y = y;
1180 lpDrop->fNC =
1181 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1182 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1183 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1184 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1185 lpDrop->fWide = FALSE;
1186 p_drop = (char*)(lpDrop + 1);
1189 /* create message content */
1190 if (p_drop) {
1191 p = (char*) p_data;
1192 next = strchr(p, '\n');
1193 while (p) {
1194 if (next) *next=0;
1195 if (strncmp(p,"file:",5) == 0 ) {
1196 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1197 if (len) {
1198 TRACE("drop file %s as %s\n", p+5, p_drop);
1199 p_drop += len+1;
1200 } else {
1201 WARN("can't convert file %s to dos name\n", p+5);
1203 } else {
1204 WARN("unknown mime type %s\n", p);
1206 if (next) {
1207 *next = '\n';
1208 p = next + 1;
1209 next = strchr(p, '\n');
1210 } else {
1211 p = NULL;
1213 *p_drop = '\0';
1216 GlobalUnlock(hDrop);
1217 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1220 wine_tsx11_lock();
1221 if( p_data ) XFree(p_data);
1222 wine_tsx11_unlock();
1226 /**********************************************************************
1227 * handle_dnd_protocol
1229 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1231 Window root, child;
1232 int root_x, root_y, child_x, child_y;
1233 unsigned int u;
1235 /* query window (drag&drop event contains only drag window) */
1236 wine_tsx11_lock();
1237 XQueryPointer( event->display, root_window, &root, &child,
1238 &root_x, &root_y, &child_x, &child_y, &u);
1239 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1240 wine_tsx11_unlock();
1241 if (!hwnd) return;
1242 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1243 EVENT_DropFromOffiX(hwnd, event);
1244 else if (event->data.l[0] == DndURL)
1245 EVENT_DropURLs(hwnd, event);
1249 struct client_message_handler
1251 int atom; /* protocol atom */
1252 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1255 static const struct client_message_handler client_messages[] =
1257 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1258 { XATOM_DndProtocol, handle_dnd_protocol },
1259 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1260 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1261 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1262 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1266 /**********************************************************************
1267 * X11DRV_ClientMessage
1269 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1271 XClientMessageEvent *event = &xev->xclient;
1272 unsigned int i;
1274 if (!hwnd) return;
1276 if (event->format != 32)
1278 WARN( "Don't know how to handle format %d\n", event->format );
1279 return;
1282 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1284 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1286 client_messages[i].handler( hwnd, event );
1287 return;
1290 TRACE( "no handler found for %ld\n", event->message_type );
1294 /**********************************************************************
1295 * X11DRV_WindowMessage (X11DRV.@)
1297 LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
1299 switch(msg)
1301 case WM_X11DRV_ACQUIRE_SELECTION:
1302 return X11DRV_AcquireClipboard( hwnd );
1303 case WM_X11DRV_DELETE_WINDOW:
1304 return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
1305 case WM_X11DRV_SET_WIN_FORMAT:
1306 return X11DRV_set_win_format( hwnd, (XID)wp );
1307 case WM_X11DRV_RESIZE_DESKTOP:
1308 X11DRV_resize_desktop( LOWORD(lp), HIWORD(lp) );
1309 return 0;
1310 default:
1311 FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp );
1312 return 0;
1317 /***********************************************************************
1318 * X11DRV_SendInput (X11DRV.@)
1320 UINT X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1322 UINT i;
1324 for (i = 0; i < count; i++, inputs++)
1326 switch(inputs->type)
1328 case INPUT_MOUSE:
1329 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1330 inputs->u.mi.mouseData, inputs->u.mi.time,
1331 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1332 break;
1333 case INPUT_KEYBOARD:
1334 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1335 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1336 break;
1337 case INPUT_HARDWARE:
1338 FIXME( "INPUT_HARDWARE not supported\n" );
1339 break;
1342 return count;