ws2_32: Work around the host name resolving to 127.x.x.x when using that for binding.
[wine/hacks.git] / dlls / winex11.drv / event.c
blob36aa088cc30994bba63bda582c9fdfec1a64cc24
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( 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( thread_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( 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( 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( 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( thread_display(), &focus_win, &revert );
643 if (focus_win)
645 if (XFindContext( thread_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;
1015 HWND hScope = hWnd;
1017 win = X11DRV_get_whole_window(hWnd);
1018 wine_tsx11_lock();
1019 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1020 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1021 x += virtual_screen_rect.left;
1022 y += virtual_screen_rect.top;
1023 wine_tsx11_unlock();
1025 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1027 /* find out drop point and drop window */
1028 if( x < 0 || y < 0 ||
1029 x > (data->whole_rect.right - data->whole_rect.left) ||
1030 y > (data->whole_rect.bottom - data->whole_rect.top) )
1032 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1033 x = 0;
1034 y = 0;
1036 else
1038 POINT pt = { x, y };
1039 HWND hwndDrop = find_drop_window( hWnd, &pt );
1040 if (hwndDrop)
1042 x = pt.x;
1043 y = pt.y;
1044 hScope = hwndDrop;
1045 bAccept = TRUE;
1047 else
1049 bAccept = FALSE;
1053 if (!bAccept) return;
1055 wine_tsx11_lock();
1056 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1057 x11drv_atom(DndSelection), 0, 65535, FALSE,
1058 AnyPropertyType, &atom_aux, &dummy,
1059 &data_length, &aux_long, &p_data);
1060 wine_tsx11_unlock();
1062 if( !aux_long && p_data) /* don't bother if > 64K */
1064 char *p = (char *)p_data;
1065 char *p_drop;
1067 aux_long = 0;
1068 while( *p ) /* calculate buffer size */
1070 INT len = GetShortPathNameA( p, NULL, 0 );
1071 if (len) aux_long += len + 1;
1072 p += strlen(p) + 1;
1074 if( aux_long && aux_long < 65535 )
1076 HDROP hDrop;
1077 DROPFILES *lpDrop;
1079 aux_long += sizeof(DROPFILES) + 1;
1080 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1081 lpDrop = (DROPFILES*)GlobalLock( hDrop );
1083 if( lpDrop )
1085 lpDrop->pFiles = sizeof(DROPFILES);
1086 lpDrop->pt.x = x;
1087 lpDrop->pt.y = y;
1088 lpDrop->fNC = FALSE;
1089 lpDrop->fWide = FALSE;
1090 p_drop = (char *)(lpDrop + 1);
1091 p = (char *)p_data;
1092 while(*p)
1094 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1095 p_drop += strlen( p_drop ) + 1;
1096 p += strlen(p) + 1;
1098 *p_drop = '\0';
1099 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1103 wine_tsx11_lock();
1104 if( p_data ) XFree(p_data);
1105 wine_tsx11_unlock();
1108 /**********************************************************************
1109 * EVENT_DropURLs
1111 * drop items are separated by \n
1112 * each item is prefixed by its mime type
1114 * event->data.l[3], event->data.l[4] contains drop x,y position
1116 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1118 struct x11drv_win_data *win_data;
1119 unsigned long data_length;
1120 unsigned long aux_long, drop_len = 0;
1121 unsigned char *p_data = NULL; /* property data */
1122 char *p_drop = NULL;
1123 char *p, *next;
1124 int x, y;
1125 DROPFILES *lpDrop;
1126 HDROP hDrop;
1127 union {
1128 Atom atom_aux;
1129 int i;
1130 Window w_aux;
1131 unsigned int u;
1132 } u; /* unused */
1134 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1136 wine_tsx11_lock();
1137 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1138 x11drv_atom(DndSelection), 0, 65535, FALSE,
1139 AnyPropertyType, &u.atom_aux, &u.i,
1140 &data_length, &aux_long, &p_data);
1141 wine_tsx11_unlock();
1142 if (aux_long)
1143 WARN("property too large, truncated!\n");
1144 TRACE("urls=%s\n", p_data);
1146 if( !aux_long && p_data) { /* don't bother if > 64K */
1147 /* calculate length */
1148 p = (char*) p_data;
1149 next = strchr(p, '\n');
1150 while (p) {
1151 if (next) *next=0;
1152 if (strncmp(p,"file:",5) == 0 ) {
1153 INT len = GetShortPathNameA( p+5, NULL, 0 );
1154 if (len) drop_len += len + 1;
1156 if (next) {
1157 *next = '\n';
1158 p = next + 1;
1159 next = strchr(p, '\n');
1160 } else {
1161 p = NULL;
1165 if( drop_len && drop_len < 65535 ) {
1166 wine_tsx11_lock();
1167 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1168 &x, &y, &u.i, &u.i, &u.u);
1169 x += virtual_screen_rect.left;
1170 y += virtual_screen_rect.top;
1171 wine_tsx11_unlock();
1173 drop_len += sizeof(DROPFILES) + 1;
1174 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1175 lpDrop = (DROPFILES *) GlobalLock( hDrop );
1177 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1179 lpDrop->pFiles = sizeof(DROPFILES);
1180 lpDrop->pt.x = x;
1181 lpDrop->pt.y = y;
1182 lpDrop->fNC =
1183 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1184 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1185 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1186 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1187 lpDrop->fWide = FALSE;
1188 p_drop = (char*)(lpDrop + 1);
1191 /* create message content */
1192 if (p_drop) {
1193 p = (char*) p_data;
1194 next = strchr(p, '\n');
1195 while (p) {
1196 if (next) *next=0;
1197 if (strncmp(p,"file:",5) == 0 ) {
1198 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1199 if (len) {
1200 TRACE("drop file %s as %s\n", p+5, p_drop);
1201 p_drop += len+1;
1202 } else {
1203 WARN("can't convert file %s to dos name\n", p+5);
1205 } else {
1206 WARN("unknown mime type %s\n", p);
1208 if (next) {
1209 *next = '\n';
1210 p = next + 1;
1211 next = strchr(p, '\n');
1212 } else {
1213 p = NULL;
1215 *p_drop = '\0';
1218 GlobalUnlock(hDrop);
1219 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1222 wine_tsx11_lock();
1223 if( p_data ) XFree(p_data);
1224 wine_tsx11_unlock();
1228 /**********************************************************************
1229 * handle_dnd_protocol
1231 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1233 Window root, child;
1234 int root_x, root_y, child_x, child_y;
1235 unsigned int u;
1237 /* query window (drag&drop event contains only drag window) */
1238 wine_tsx11_lock();
1239 XQueryPointer( event->display, root_window, &root, &child,
1240 &root_x, &root_y, &child_x, &child_y, &u);
1241 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1242 wine_tsx11_unlock();
1243 if (!hwnd) return;
1244 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1245 EVENT_DropFromOffiX(hwnd, event);
1246 else if (event->data.l[0] == DndURL)
1247 EVENT_DropURLs(hwnd, event);
1251 struct client_message_handler
1253 int atom; /* protocol atom */
1254 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1257 static const struct client_message_handler client_messages[] =
1259 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1260 { XATOM_DndProtocol, handle_dnd_protocol },
1261 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1262 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1263 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1264 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1268 /**********************************************************************
1269 * X11DRV_ClientMessage
1271 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1273 XClientMessageEvent *event = &xev->xclient;
1274 unsigned int i;
1276 if (!hwnd) return;
1278 if (event->format != 32)
1280 WARN( "Don't know how to handle format %d\n", event->format );
1281 return;
1284 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1286 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1288 client_messages[i].handler( hwnd, event );
1289 return;
1292 TRACE( "no handler found for %ld\n", event->message_type );
1296 /**********************************************************************
1297 * X11DRV_WindowMessage (X11DRV.@)
1299 LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
1301 switch(msg)
1303 case WM_X11DRV_ACQUIRE_SELECTION:
1304 return X11DRV_AcquireClipboard( hwnd );
1305 case WM_X11DRV_DELETE_WINDOW:
1306 return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
1307 case WM_X11DRV_SET_WIN_FORMAT:
1308 return X11DRV_set_win_format( hwnd, (XID)wp );
1309 case WM_X11DRV_RESIZE_DESKTOP:
1310 X11DRV_resize_desktop( LOWORD(lp), HIWORD(lp) );
1311 return 0;
1312 default:
1313 FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp );
1314 return 0;
1319 /***********************************************************************
1320 * X11DRV_SendInput (X11DRV.@)
1322 UINT X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1324 UINT i;
1326 for (i = 0; i < count; i++, inputs++)
1328 switch(inputs->type)
1330 case INPUT_MOUSE:
1331 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1332 inputs->u.mi.mouseData, inputs->u.mi.time,
1333 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1334 break;
1335 case INPUT_KEYBOARD:
1336 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1337 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1338 break;
1339 case INPUT_HARDWARE:
1340 FIXME( "INPUT_HARDWARE not supported\n" );
1341 break;
1344 return count;