pop b9dc5e3b122188b492ca3b22b2645fadb18a6ce1
[wine/hacks.git] / dlls / winex11.drv / event.c
blob0e8d60cbee11d8cb7a005409e1234c619298dd8b
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( "%lu %s for hwnd/window %p/%lx\n",
312 event->xany.serial, 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)(Display*, XEvent*,XPointer), 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 if (prev_event.type) call_event_handler( display, &prev_event );
383 XFlush( gdi_display );
384 wine_tsx11_unlock();
385 if (count) TRACE( "processed %d events\n", count );
386 return count;
390 /***********************************************************************
391 * MsgWaitForMultipleObjectsEx (X11DRV.@)
393 DWORD CDECL 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 (style & WS_MINIMIZE) return FALSE;
466 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
467 if (hwnd == GetDesktopWindow()) return FALSE;
468 return !(style & WS_DISABLED);
472 /**********************************************************************
473 * set_focus
475 static void set_focus( Display *display, HWND hwnd, Time time )
477 HWND focus;
478 Window win;
479 GUITHREADINFO threadinfo;
481 TRACE( "setting foreground window to %p\n", hwnd );
482 SetForegroundWindow( hwnd );
484 GetGUIThreadInfo(0, &threadinfo);
485 focus = threadinfo.hwndFocus;
486 if (!focus) focus = threadinfo.hwndActive;
487 if (focus) focus = GetAncestor( focus, GA_ROOT );
488 win = X11DRV_get_whole_window(focus);
490 if (win)
492 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
493 wine_tsx11_lock();
494 XSetInputFocus( display, win, RevertToParent, time );
495 wine_tsx11_unlock();
500 /**********************************************************************
501 * handle_wm_protocols
503 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
505 Atom protocol = (Atom)event->data.l[0];
507 if (!protocol) return;
509 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
511 if (hwnd == GetDesktopWindow())
513 /* The desktop window does not have a close button that we can
514 * pretend to click. Therefore, we simply send it a close command. */
515 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
516 return;
519 /* Ignore the delete window request if the window has been disabled
520 * and we are in managed mode. This is to disallow applications from
521 * being closed by the window manager while in a modal state.
523 if (IsWindowEnabled(hwnd))
525 HMENU hSysMenu;
527 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
528 hSysMenu = GetSystemMenu(hwnd, FALSE);
529 if (hSysMenu)
531 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
532 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
533 return;
535 if (GetActiveWindow() != hwnd)
537 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
538 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
539 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
540 switch(ma)
542 case MA_NOACTIVATEANDEAT:
543 case MA_ACTIVATEANDEAT:
544 return;
545 case MA_NOACTIVATE:
546 break;
547 case MA_ACTIVATE:
548 case 0:
549 SetActiveWindow(hwnd);
550 break;
551 default:
552 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
553 break;
557 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
560 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
562 Time event_time = (Time)event->data.l[1];
563 HWND last_focus = x11drv_thread_data()->last_focus;
565 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
566 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
567 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
569 if (can_activate_window(hwnd))
571 /* simulate a mouse click on the caption to find out
572 * whether the window wants to be activated */
573 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
574 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
575 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
576 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
578 set_focus( event->display, hwnd, event_time );
579 return;
582 else if (hwnd == GetDesktopWindow())
584 hwnd = GetForegroundWindow();
585 if (!hwnd) hwnd = last_focus;
586 if (!hwnd) hwnd = GetDesktopWindow();
587 set_focus( event->display, hwnd, event_time );
588 return;
590 /* try to find some other window to give the focus to */
591 hwnd = GetFocus();
592 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
593 if (!hwnd) hwnd = GetActiveWindow();
594 if (!hwnd) hwnd = last_focus;
595 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
597 else if (protocol == x11drv_atom(_NET_WM_PING))
599 XClientMessageEvent xev;
600 xev = *event;
602 TRACE("NET_WM Ping\n");
603 wine_tsx11_lock();
604 xev.window = DefaultRootWindow(xev.display);
605 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
606 wine_tsx11_unlock();
607 /* this line is semi-stolen from gtk2 */
608 TRACE("NET_WM Pong\n");
613 static const char * const focus_details[] =
615 "NotifyAncestor",
616 "NotifyVirtual",
617 "NotifyInferior",
618 "NotifyNonlinear",
619 "NotifyNonlinearVirtual",
620 "NotifyPointer",
621 "NotifyPointerRoot",
622 "NotifyDetailNone"
625 /**********************************************************************
626 * X11DRV_FocusIn
628 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
630 XFocusChangeEvent *event = &xev->xfocus;
631 XIC xic;
633 if (!hwnd) return;
635 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
637 if (event->detail == NotifyPointer) return;
639 if ((xic = X11DRV_get_ic( hwnd )))
641 wine_tsx11_lock();
642 XSetICFocus( xic );
643 wine_tsx11_unlock();
645 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
647 if (!can_activate_window(hwnd))
649 HWND hwnd = GetFocus();
650 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
651 if (!hwnd) hwnd = GetActiveWindow();
652 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
653 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
655 else SetForegroundWindow( hwnd );
659 /**********************************************************************
660 * X11DRV_FocusOut
662 * Note: only top-level windows get FocusOut events.
664 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
666 XFocusChangeEvent *event = &xev->xfocus;
667 HWND hwnd_tmp;
668 Window focus_win;
669 int revert;
670 XIC xic;
672 if (!hwnd) return;
674 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
676 if (event->detail == NotifyPointer) return;
677 if (ximInComposeMode) return;
679 x11drv_thread_data()->last_focus = hwnd;
680 if ((xic = X11DRV_get_ic( hwnd )))
682 wine_tsx11_lock();
683 XUnsetICFocus( xic );
684 wine_tsx11_unlock();
686 if (hwnd != GetForegroundWindow()) return;
687 if (root_window != DefaultRootWindow(event->display)) return;
688 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
690 /* don't reset the foreground window, if the window which is
691 getting the focus is a Wine window */
693 wine_tsx11_lock();
694 XGetInputFocus( event->display, &focus_win, &revert );
695 if (focus_win)
697 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
698 focus_win = 0;
700 wine_tsx11_unlock();
702 if (!focus_win)
704 /* Abey : 6-Oct-99. Check again if the focus out window is the
705 Foreground window, because in most cases the messages sent
706 above must have already changed the foreground window, in which
707 case we don't have to change the foreground window to 0 */
708 if (hwnd == GetForegroundWindow())
710 TRACE( "lost focus, setting fg to desktop\n" );
711 SetForegroundWindow( GetDesktopWindow() );
717 /***********************************************************************
718 * X11DRV_Expose
720 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
722 XExposeEvent *event = &xev->xexpose;
723 RECT rect;
724 struct x11drv_win_data *data;
725 int flags = RDW_INVALIDATE | RDW_ERASE;
727 TRACE( "win %p (%lx) %d,%d %dx%d\n",
728 hwnd, event->window, event->x, event->y, event->width, event->height );
730 if (!(data = X11DRV_get_win_data( hwnd ))) return;
732 if (event->window == data->whole_window)
734 rect.left = data->whole_rect.left + event->x;
735 rect.top = data->whole_rect.top + event->y;
736 flags |= RDW_FRAME;
738 else
740 rect.left = data->client_rect.left + event->x;
741 rect.top = data->client_rect.top + event->y;
743 rect.right = rect.left + event->width;
744 rect.bottom = rect.top + event->height;
746 if (event->window != root_window)
748 SERVER_START_REQ( update_window_zorder )
750 req->window = wine_server_user_handle( hwnd );
751 req->rect.left = rect.left;
752 req->rect.top = rect.top;
753 req->rect.right = rect.right;
754 req->rect.bottom = rect.bottom;
755 wine_server_call( req );
757 SERVER_END_REQ;
759 /* make position relative to client area instead of parent */
760 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
761 flags |= RDW_ALLCHILDREN;
764 RedrawWindow( hwnd, &rect, 0, flags );
768 /**********************************************************************
769 * X11DRV_MapNotify
771 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
773 struct x11drv_win_data *data;
775 if (!(data = X11DRV_get_win_data( hwnd ))) return;
776 if (!data->mapped) return;
778 if (!data->managed)
780 HWND hwndFocus = GetFocus();
781 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
786 /***********************************************************************
787 * is_net_wm_state_maximized
789 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
791 Atom type, *state;
792 int format, ret = 0;
793 unsigned long i, count, remaining;
795 wine_tsx11_lock();
796 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
797 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
798 &remaining, (unsigned char **)&state ))
800 if (type == XA_ATOM && format == 32)
802 for (i = 0; i < count; i++)
804 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
805 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
806 ret++;
809 XFree( state );
811 wine_tsx11_unlock();
812 return (ret == 2);
816 /***********************************************************************
817 * X11DRV_ConfigureNotify
819 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
821 XConfigureEvent *event = &xev->xconfigure;
822 struct x11drv_win_data *data;
823 RECT rect;
824 UINT flags;
825 int cx, cy, x = event->x, y = event->y;
827 if (!hwnd) return;
828 if (!(data = X11DRV_get_win_data( hwnd ))) return;
829 if (!data->mapped || data->iconic || !data->managed) return;
830 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
832 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
833 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
834 event->serial, data->configure_serial );
835 return;
838 /* Get geometry */
840 if (!event->send_event) /* normal event, need to map coordinates to the root */
842 Window child;
843 wine_tsx11_lock();
844 XTranslateCoordinates( event->display, data->whole_window, root_window,
845 0, 0, &x, &y, &child );
846 wine_tsx11_unlock();
848 rect.left = x;
849 rect.top = y;
850 rect.right = x + event->width;
851 rect.bottom = y + event->height;
852 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
853 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
854 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
855 event->x, event->y, event->width, event->height );
856 X11DRV_X_to_window_rect( data, &rect );
858 if (is_net_wm_state_maximized( event->display, data ))
860 if (!IsZoomed( data->hwnd ))
862 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
863 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
864 return;
867 else
869 if (IsZoomed( data->hwnd ))
871 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
872 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
873 return;
877 /* Compare what has changed */
879 x = rect.left;
880 y = rect.top;
881 cx = rect.right - rect.left;
882 cy = rect.bottom - rect.top;
883 flags = SWP_NOACTIVATE | SWP_NOZORDER;
885 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
886 else
887 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
888 hwnd, data->window_rect.left, data->window_rect.top, x, y );
890 if ((data->window_rect.right - data->window_rect.left == cx &&
891 data->window_rect.bottom - data->window_rect.top == cy) ||
892 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
894 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
895 flags |= SWP_NOSIZE;
897 else
898 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
899 hwnd, data->window_rect.right - data->window_rect.left,
900 data->window_rect.bottom - data->window_rect.top, cx, cy );
902 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
906 /***********************************************************************
907 * get_window_wm_state
909 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
911 struct
913 CARD32 state;
914 XID icon;
915 } *state;
916 Atom type;
917 int format, ret = -1;
918 unsigned long count, remaining;
920 wine_tsx11_lock();
921 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
922 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
923 &type, &format, &count, &remaining, (unsigned char **)&state ))
925 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
926 ret = state->state;
927 XFree( state );
929 wine_tsx11_unlock();
930 return ret;
934 /***********************************************************************
935 * handle_wm_state_notify
937 * Handle a PropertyNotify for WM_STATE.
939 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
940 BOOL update_window )
942 DWORD style;
944 switch(event->state)
946 case PropertyDelete:
947 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
948 data->wm_state = WithdrawnState;
949 break;
950 case PropertyNewValue:
952 int old_state = data->wm_state;
953 int new_state = get_window_wm_state( event->display, data );
954 if (new_state != -1 && new_state != data->wm_state)
956 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
957 data->hwnd, data->whole_window, new_state, old_state );
958 data->wm_state = new_state;
959 /* ignore the initial state transition out of withdrawn state */
960 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
961 if (!old_state) return;
964 break;
967 if (!update_window || !data->managed || !data->mapped) return;
969 style = GetWindowLongW( data->hwnd, GWL_STYLE );
971 if (data->iconic && data->wm_state == NormalState) /* restore window */
973 data->iconic = FALSE;
974 if (is_net_wm_state_maximized( event->display, data ))
976 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
978 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
979 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
981 else TRACE( "not restoring to max win %p/%lx style %08x\n",
982 data->hwnd, data->whole_window, style );
984 else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
986 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
987 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
989 else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
991 else if (!data->iconic && data->wm_state == IconicState)
993 data->iconic = TRUE;
994 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
996 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
997 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
999 else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1004 /***********************************************************************
1005 * X11DRV_PropertyNotify
1007 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1009 XPropertyEvent *event = &xev->xproperty;
1010 struct x11drv_win_data *data;
1012 if (!hwnd) return;
1013 if (!(data = X11DRV_get_win_data( hwnd ))) return;
1015 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1019 /* event filter to wait for a WM_STATE change notification on a window */
1020 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1022 if (event->xany.window != (Window)arg) return 0;
1023 return (event->type == DestroyNotify ||
1024 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1027 /***********************************************************************
1028 * wait_for_withdrawn_state
1030 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1032 DWORD end = GetTickCount() + 2000;
1034 if (!data->managed) return;
1036 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1037 data->hwnd, data->whole_window, set ? "" : "not " );
1039 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1041 XEvent event;
1042 int count = 0;
1044 wine_tsx11_lock();
1045 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1047 count++;
1048 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1049 if (event.type == DestroyNotify) call_event_handler( display, &event );
1050 else
1052 wine_tsx11_unlock();
1053 handle_wm_state_notify( data, &event.xproperty, FALSE );
1054 wine_tsx11_lock();
1057 wine_tsx11_unlock();
1059 if (!count)
1061 struct pollfd pfd;
1062 int timeout = end - GetTickCount();
1064 pfd.fd = ConnectionNumber(display);
1065 pfd.events = POLLIN;
1066 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1068 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1069 break;
1073 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1077 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1079 RECT tempRect;
1081 if (!IsWindowEnabled(hQueryWnd)) return 0;
1083 GetWindowRect(hQueryWnd, &tempRect);
1085 if(!PtInRect(&tempRect, *lpPt)) return 0;
1087 if (!IsIconic( hQueryWnd ))
1089 POINT pt = *lpPt;
1090 ScreenToClient( hQueryWnd, &pt );
1091 GetClientRect( hQueryWnd, &tempRect );
1093 if (PtInRect( &tempRect, pt))
1095 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1096 if (ret && ret != hQueryWnd)
1098 ret = find_drop_window( ret, lpPt );
1099 if (ret) return ret;
1104 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1106 ScreenToClient(hQueryWnd, lpPt);
1108 return hQueryWnd;
1111 /**********************************************************************
1112 * EVENT_DropFromOffix
1114 * don't know if it still works (last Changelog is from 96/11/04)
1116 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1118 struct x11drv_win_data *data;
1119 unsigned long data_length;
1120 unsigned long aux_long;
1121 unsigned char* p_data = NULL;
1122 Atom atom_aux;
1123 int x, y, dummy;
1124 BOOL bAccept;
1125 Window win, w_aux_root, w_aux_child;
1127 win = X11DRV_get_whole_window(hWnd);
1128 wine_tsx11_lock();
1129 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1130 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1131 x += virtual_screen_rect.left;
1132 y += virtual_screen_rect.top;
1133 wine_tsx11_unlock();
1135 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1137 /* find out drop point and drop window */
1138 if( x < 0 || y < 0 ||
1139 x > (data->whole_rect.right - data->whole_rect.left) ||
1140 y > (data->whole_rect.bottom - data->whole_rect.top) )
1142 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1143 x = 0;
1144 y = 0;
1146 else
1148 POINT pt = { x, y };
1149 HWND hwndDrop = find_drop_window( hWnd, &pt );
1150 if (hwndDrop)
1152 x = pt.x;
1153 y = pt.y;
1154 bAccept = TRUE;
1156 else
1158 bAccept = FALSE;
1162 if (!bAccept) return;
1164 wine_tsx11_lock();
1165 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1166 x11drv_atom(DndSelection), 0, 65535, FALSE,
1167 AnyPropertyType, &atom_aux, &dummy,
1168 &data_length, &aux_long, &p_data);
1169 wine_tsx11_unlock();
1171 if( !aux_long && p_data) /* don't bother if > 64K */
1173 char *p = (char *)p_data;
1174 char *p_drop;
1176 aux_long = 0;
1177 while( *p ) /* calculate buffer size */
1179 INT len = GetShortPathNameA( p, NULL, 0 );
1180 if (len) aux_long += len + 1;
1181 p += strlen(p) + 1;
1183 if( aux_long && aux_long < 65535 )
1185 HDROP hDrop;
1186 DROPFILES *lpDrop;
1188 aux_long += sizeof(DROPFILES) + 1;
1189 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1190 lpDrop = GlobalLock( hDrop );
1192 if( lpDrop )
1194 lpDrop->pFiles = sizeof(DROPFILES);
1195 lpDrop->pt.x = x;
1196 lpDrop->pt.y = y;
1197 lpDrop->fNC = FALSE;
1198 lpDrop->fWide = FALSE;
1199 p_drop = (char *)(lpDrop + 1);
1200 p = (char *)p_data;
1201 while(*p)
1203 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1204 p_drop += strlen( p_drop ) + 1;
1205 p += strlen(p) + 1;
1207 *p_drop = '\0';
1208 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1212 wine_tsx11_lock();
1213 if( p_data ) XFree(p_data);
1214 wine_tsx11_unlock();
1217 /**********************************************************************
1218 * EVENT_DropURLs
1220 * drop items are separated by \n
1221 * each item is prefixed by its mime type
1223 * event->data.l[3], event->data.l[4] contains drop x,y position
1225 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1227 struct x11drv_win_data *win_data;
1228 unsigned long data_length;
1229 unsigned long aux_long, drop_len = 0;
1230 unsigned char *p_data = NULL; /* property data */
1231 char *p_drop = NULL;
1232 char *p, *next;
1233 int x, y;
1234 DROPFILES *lpDrop;
1235 HDROP hDrop;
1236 union {
1237 Atom atom_aux;
1238 int i;
1239 Window w_aux;
1240 unsigned int u;
1241 } u; /* unused */
1243 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1245 wine_tsx11_lock();
1246 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1247 x11drv_atom(DndSelection), 0, 65535, FALSE,
1248 AnyPropertyType, &u.atom_aux, &u.i,
1249 &data_length, &aux_long, &p_data);
1250 wine_tsx11_unlock();
1251 if (aux_long)
1252 WARN("property too large, truncated!\n");
1253 TRACE("urls=%s\n", p_data);
1255 if( !aux_long && p_data) { /* don't bother if > 64K */
1256 /* calculate length */
1257 p = (char*) p_data;
1258 next = strchr(p, '\n');
1259 while (p) {
1260 if (next) *next=0;
1261 if (strncmp(p,"file:",5) == 0 ) {
1262 INT len = GetShortPathNameA( p+5, NULL, 0 );
1263 if (len) drop_len += len + 1;
1265 if (next) {
1266 *next = '\n';
1267 p = next + 1;
1268 next = strchr(p, '\n');
1269 } else {
1270 p = NULL;
1274 if( drop_len && drop_len < 65535 ) {
1275 wine_tsx11_lock();
1276 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1277 &x, &y, &u.i, &u.i, &u.u);
1278 x += virtual_screen_rect.left;
1279 y += virtual_screen_rect.top;
1280 wine_tsx11_unlock();
1282 drop_len += sizeof(DROPFILES) + 1;
1283 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1284 lpDrop = GlobalLock( hDrop );
1286 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1288 lpDrop->pFiles = sizeof(DROPFILES);
1289 lpDrop->pt.x = x;
1290 lpDrop->pt.y = y;
1291 lpDrop->fNC =
1292 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1293 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1294 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1295 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1296 lpDrop->fWide = FALSE;
1297 p_drop = (char*)(lpDrop + 1);
1300 /* create message content */
1301 if (p_drop) {
1302 p = (char*) p_data;
1303 next = strchr(p, '\n');
1304 while (p) {
1305 if (next) *next=0;
1306 if (strncmp(p,"file:",5) == 0 ) {
1307 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1308 if (len) {
1309 TRACE("drop file %s as %s\n", p+5, p_drop);
1310 p_drop += len+1;
1311 } else {
1312 WARN("can't convert file %s to dos name\n", p+5);
1314 } else {
1315 WARN("unknown mime type %s\n", p);
1317 if (next) {
1318 *next = '\n';
1319 p = next + 1;
1320 next = strchr(p, '\n');
1321 } else {
1322 p = NULL;
1324 *p_drop = '\0';
1327 GlobalUnlock(hDrop);
1328 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1331 wine_tsx11_lock();
1332 if( p_data ) XFree(p_data);
1333 wine_tsx11_unlock();
1337 /**********************************************************************
1338 * handle_dnd_protocol
1340 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1342 Window root, child;
1343 int root_x, root_y, child_x, child_y;
1344 unsigned int u;
1346 /* query window (drag&drop event contains only drag window) */
1347 wine_tsx11_lock();
1348 XQueryPointer( event->display, root_window, &root, &child,
1349 &root_x, &root_y, &child_x, &child_y, &u);
1350 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1351 wine_tsx11_unlock();
1352 if (!hwnd) return;
1353 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1354 EVENT_DropFromOffiX(hwnd, event);
1355 else if (event->data.l[0] == DndURL)
1356 EVENT_DropURLs(hwnd, event);
1360 struct client_message_handler
1362 int atom; /* protocol atom */
1363 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1366 static const struct client_message_handler client_messages[] =
1368 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1369 { XATOM_DndProtocol, handle_dnd_protocol },
1370 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1371 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1372 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1373 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1377 /**********************************************************************
1378 * X11DRV_ClientMessage
1380 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1382 XClientMessageEvent *event = &xev->xclient;
1383 unsigned int i;
1385 if (!hwnd) return;
1387 if (event->format != 32)
1389 WARN( "Don't know how to handle format %d\n", event->format );
1390 return;
1393 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1395 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1397 client_messages[i].handler( hwnd, event );
1398 return;
1401 TRACE( "no handler found for %ld\n", event->message_type );
1405 /***********************************************************************
1406 * X11DRV_SendInput (X11DRV.@)
1408 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1410 UINT i;
1412 for (i = 0; i < count; i++, inputs++)
1414 switch(inputs->type)
1416 case INPUT_MOUSE:
1417 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1418 inputs->u.mi.mouseData, inputs->u.mi.time,
1419 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1420 break;
1421 case INPUT_KEYBOARD:
1422 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1423 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1424 break;
1425 case INPUT_HARDWARE:
1426 FIXME( "INPUT_HARDWARE not supported\n" );
1427 break;
1430 return count;