winex11: Prepare X11DRV_PALETTE_ToPhysical/_ToLogical for a new ColorShift structure.
[wine/hacks.git] / dlls / winex11.drv / event.c
blob9a489cd29805df0eca0a3ed6b7afdf20cae10e0e
1 /*
2 * X11 event driver
4 * Copyright 1993 Alexandre Julliard
5 * 1999 Noel Borthwick
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "config.h"
24 #ifdef HAVE_POLL_H
25 #include <poll.h>
26 #endif
27 #ifdef HAVE_SYS_POLL_H
28 #include <sys/poll.h>
29 #endif
30 #include <X11/Xatom.h>
31 #include <X11/keysym.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xresource.h>
34 #include <X11/Xutil.h>
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <string.h>
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h"
45 #include "wingdi.h"
47 #include "x11drv.h"
49 /* avoid conflict with field names in included win32 headers */
50 #undef Status
51 #include "shlobj.h" /* DROPFILES */
52 #include "shellapi.h"
54 #include "wine/server.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(event);
59 extern BOOL ximInComposeMode;
61 #define DndNotDnd -1 /* OffiX drag&drop */
62 #define DndUnknown 0
63 #define DndRawData 1
64 #define DndFile 2
65 #define DndFiles 3
66 #define DndText 4
67 #define DndDir 5
68 #define DndLink 6
69 #define DndExe 7
71 #define DndEND 8
73 #define DndURL 128 /* KDE drag&drop */
75 /* Event handlers */
76 static void X11DRV_FocusIn( HWND hwnd, XEvent *event );
77 static void X11DRV_FocusOut( HWND hwnd, XEvent *event );
78 static void X11DRV_Expose( HWND hwnd, XEvent *event );
79 static void X11DRV_MapNotify( HWND hwnd, XEvent *event );
80 static void X11DRV_ConfigureNotify( HWND hwnd, XEvent *event );
81 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
82 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
84 struct event_handler
86 int type; /* event type */
87 x11drv_event_handler handler; /* corresponding handler function */
90 #define MAX_EVENT_HANDLERS 64
92 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
94 /* list must be sorted by event type */
95 { KeyPress, X11DRV_KeyEvent },
96 { KeyRelease, X11DRV_KeyEvent },
97 { ButtonPress, X11DRV_ButtonPress },
98 { ButtonRelease, X11DRV_ButtonRelease },
99 { MotionNotify, X11DRV_MotionNotify },
100 { EnterNotify, X11DRV_EnterNotify },
101 /* LeaveNotify */
102 { FocusIn, X11DRV_FocusIn },
103 { FocusOut, X11DRV_FocusOut },
104 { KeymapNotify, X11DRV_KeymapNotify },
105 { Expose, X11DRV_Expose },
106 /* GraphicsExpose */
107 /* NoExpose */
108 /* VisibilityNotify */
109 /* CreateNotify */
110 { DestroyNotify, X11DRV_DestroyNotify },
111 /* UnmapNotify */
112 { MapNotify, X11DRV_MapNotify },
113 /* MapRequest */
114 /* ReparentNotify */
115 { ConfigureNotify, X11DRV_ConfigureNotify },
116 /* ConfigureRequest */
117 /* GravityNotify */
118 /* ResizeRequest */
119 /* CirculateNotify */
120 /* CirculateRequest */
121 { PropertyNotify, X11DRV_PropertyNotify },
122 { SelectionClear, X11DRV_SelectionClear },
123 { SelectionRequest, X11DRV_SelectionRequest },
124 /* SelectionNotify */
125 /* ColormapNotify */
126 { ClientMessage, X11DRV_ClientMessage },
127 { MappingNotify, X11DRV_MappingNotify },
130 static int nb_event_handlers = 18; /* change this if you add handlers above */
133 /* return the name of an X event */
134 static const char *dbgstr_event( int type )
136 static const char * const event_names[] =
138 "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
139 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
140 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
141 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
142 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
143 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
144 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
145 "ClientMessage", "MappingNotify"
148 if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
149 return wine_dbg_sprintf( "Extension event %d", type );
153 /***********************************************************************
154 * find_handler
156 * Find the handler for a given event type. Caller must hold the x11 lock.
158 static inline x11drv_event_handler find_handler( int type )
160 int min = 0, max = nb_event_handlers - 1;
162 while (min <= max)
164 int pos = (min + max) / 2;
165 if (handlers[pos].type == type) return handlers[pos].handler;
166 if (handlers[pos].type > type) max = pos - 1;
167 else min = pos + 1;
169 return NULL;
173 /***********************************************************************
174 * X11DRV_register_event_handler
176 * Register a handler for a given event type.
177 * If already registered, overwrite the previous handler.
179 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
181 int min, max;
183 wine_tsx11_lock();
184 min = 0;
185 max = nb_event_handlers - 1;
186 while (min <= max)
188 int pos = (min + max) / 2;
189 if (handlers[pos].type == type)
191 handlers[pos].handler = handler;
192 goto done;
194 if (handlers[pos].type > type) max = pos - 1;
195 else min = pos + 1;
197 /* insert it between max and min */
198 memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
199 handlers[min].type = type;
200 handlers[min].handler = handler;
201 nb_event_handlers++;
202 assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
203 done:
204 wine_tsx11_unlock();
205 TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
209 /***********************************************************************
210 * filter_event
212 static Bool filter_event( Display *display, XEvent *event, char *arg )
214 ULONG_PTR mask = (ULONG_PTR)arg;
216 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
218 switch(event->type)
220 case KeyPress:
221 case KeyRelease:
222 case KeymapNotify:
223 case MappingNotify:
224 return (mask & QS_KEY) != 0;
225 case ButtonPress:
226 case ButtonRelease:
227 return (mask & QS_MOUSEBUTTON) != 0;
228 case MotionNotify:
229 case EnterNotify:
230 case LeaveNotify:
231 return (mask & QS_MOUSEMOVE) != 0;
232 case Expose:
233 return (mask & QS_PAINT) != 0;
234 case FocusIn:
235 case FocusOut:
236 case MapNotify:
237 case UnmapNotify:
238 case ConfigureNotify:
239 case PropertyNotify:
240 case ClientMessage:
241 return (mask & QS_POSTMESSAGE) != 0;
242 default:
243 return (mask & QS_SENDMESSAGE) != 0;
248 enum event_merge_action
250 MERGE_DISCARD, /* discard the old event */
251 MERGE_HANDLE, /* handle the old event */
252 MERGE_KEEP /* keep the old event for future merging */
255 /***********************************************************************
256 * merge_events
258 * Try to merge 2 consecutive events.
260 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
262 switch (prev->type)
264 case ConfigureNotify:
265 switch (next->type)
267 case ConfigureNotify:
268 if (prev->xany.window == next->xany.window)
270 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
271 return MERGE_DISCARD;
273 break;
274 case Expose:
275 case PropertyNotify:
276 return MERGE_KEEP;
278 break;
279 case MotionNotify:
280 if (prev->xany.window == next->xany.window && next->type == MotionNotify)
282 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
283 return MERGE_DISCARD;
285 break;
287 return MERGE_HANDLE;
291 /***********************************************************************
292 * call_event_handler
294 static inline void call_event_handler( Display *display, XEvent *event )
296 HWND hwnd;
297 x11drv_event_handler handler;
298 XEvent *prev;
299 struct x11drv_thread_data *thread_data;
301 if (!(handler = find_handler( event->type )))
303 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
304 return; /* no handler, ignore it */
307 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
308 hwnd = 0; /* not for a registered window */
309 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
311 TRACE( "%s for hwnd/window %p/%lx\n",
312 dbgstr_event( event->type ), hwnd, event->xany.window );
313 wine_tsx11_unlock();
314 thread_data = x11drv_thread_data();
315 prev = thread_data->current_event;
316 thread_data->current_event = event;
317 handler( hwnd, event );
318 thread_data->current_event = prev;
319 wine_tsx11_lock();
323 /***********************************************************************
324 * process_events
326 static int process_events( Display *display, Bool (*filter)(), ULONG_PTR arg )
328 XEvent event, prev_event;
329 int count = 0;
330 enum event_merge_action action = MERGE_DISCARD;
332 prev_event.type = 0;
333 wine_tsx11_lock();
334 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
336 count++;
337 if (XFilterEvent( &event, None ))
340 * SCIM on linux filters key events strangely. It does not filter the
341 * KeyPress events for these keys however it does filter the
342 * KeyRelease events. This causes wine to become very confused as
343 * to the keyboard state.
345 * We need to let those KeyRelease events be processed so that the
346 * keyboard state is correct.
348 if (event.type == KeyRelease)
350 KeySym keysym = 0;
351 XKeyEvent *keyevent = &event.xkey;
353 XLookupString(keyevent, NULL, 0, &keysym, NULL);
354 if (!(keysym == XK_Shift_L ||
355 keysym == XK_Shift_R ||
356 keysym == XK_Control_L ||
357 keysym == XK_Control_R ||
358 keysym == XK_Alt_R ||
359 keysym == XK_Alt_L ||
360 keysym == XK_Meta_R ||
361 keysym == XK_Meta_L))
362 continue; /* not a key we care about, ignore it */
364 else
365 continue; /* filtered, ignore it */
367 if (prev_event.type) action = merge_events( &prev_event, &event );
368 switch( action )
370 case MERGE_DISCARD: /* discard prev, keep new */
371 prev_event = event;
372 break;
373 case MERGE_HANDLE: /* handle prev, keep new */
374 call_event_handler( display, &prev_event );
375 prev_event = event;
376 break;
377 case MERGE_KEEP: /* handle new, keep prev for future merging */
378 call_event_handler( display, &event );
379 break;
382 XFlush( gdi_display );
383 if (prev_event.type) call_event_handler( display, &prev_event );
384 wine_tsx11_unlock();
385 if (count) TRACE( "processed %d events\n", count );
386 return count;
390 /***********************************************************************
391 * MsgWaitForMultipleObjectsEx (X11DRV.@)
393 DWORD 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 (hwnd == GetDesktopWindow()) return FALSE;
467 return !(style & WS_DISABLED);
471 /**********************************************************************
472 * set_focus
474 static void set_focus( Display *display, HWND hwnd, Time time )
476 HWND focus;
477 Window win;
479 TRACE( "setting foreground window to %p\n", hwnd );
480 SetForegroundWindow( hwnd );
482 focus = GetFocus();
483 if (focus) focus = GetAncestor( focus, GA_ROOT );
484 win = X11DRV_get_whole_window(focus);
486 if (win)
488 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
489 wine_tsx11_lock();
490 XSetInputFocus( display, win, RevertToParent, time );
491 wine_tsx11_unlock();
496 /**********************************************************************
497 * handle_wm_protocols
499 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
501 Atom protocol = (Atom)event->data.l[0];
503 if (!protocol) return;
505 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
507 /* Ignore the delete window request if the window has been disabled
508 * and we are in managed mode. This is to disallow applications from
509 * being closed by the window manager while in a modal state.
511 if (IsWindowEnabled(hwnd))
513 HMENU hSysMenu;
515 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
516 hSysMenu = GetSystemMenu(hwnd, FALSE);
517 if (hSysMenu)
519 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
520 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
521 return;
523 if (GetActiveWindow() != hwnd)
525 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
526 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
527 MAKELONG(HTCLOSE,WM_LBUTTONDOWN) );
528 switch(ma)
530 case MA_NOACTIVATEANDEAT:
531 case MA_ACTIVATEANDEAT:
532 return;
533 case MA_NOACTIVATE:
534 break;
535 case MA_ACTIVATE:
536 case 0:
537 SetActiveWindow(hwnd);
538 break;
539 default:
540 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
541 break;
544 PostMessageW( hwnd, WM_X11DRV_DELETE_WINDOW, 0, 0 );
547 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
549 Time event_time = (Time)event->data.l[1];
550 HWND last_focus = x11drv_thread_data()->last_focus;
552 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
553 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
554 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
556 if (can_activate_window(hwnd))
558 /* simulate a mouse click on the caption to find out
559 * whether the window wants to be activated */
560 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
561 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
562 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
563 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
565 set_focus( event->display, hwnd, event_time );
566 return;
569 else if (hwnd == GetDesktopWindow())
571 hwnd = GetForegroundWindow();
572 if (!hwnd) hwnd = last_focus;
573 if (!hwnd) hwnd = GetDesktopWindow();
574 set_focus( event->display, hwnd, event_time );
575 return;
577 /* try to find some other window to give the focus to */
578 hwnd = GetFocus();
579 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
580 if (!hwnd) hwnd = GetActiveWindow();
581 if (!hwnd) hwnd = last_focus;
582 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
584 else if (protocol == x11drv_atom(_NET_WM_PING))
586 XClientMessageEvent xev;
587 xev = *event;
589 TRACE("NET_WM Ping\n");
590 wine_tsx11_lock();
591 xev.window = DefaultRootWindow(xev.display);
592 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
593 wine_tsx11_unlock();
594 /* this line is semi-stolen from gtk2 */
595 TRACE("NET_WM Pong\n");
600 static const char * const focus_details[] =
602 "NotifyAncestor",
603 "NotifyVirtual",
604 "NotifyInferior",
605 "NotifyNonlinear",
606 "NotifyNonlinearVirtual",
607 "NotifyPointer",
608 "NotifyPointerRoot",
609 "NotifyDetailNone"
612 /**********************************************************************
613 * X11DRV_FocusIn
615 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
617 XFocusChangeEvent *event = &xev->xfocus;
618 XIC xic;
620 if (!hwnd) return;
622 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
624 if (event->detail == NotifyPointer) return;
626 if ((xic = X11DRV_get_ic( hwnd )))
628 wine_tsx11_lock();
629 XSetICFocus( xic );
630 wine_tsx11_unlock();
632 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
634 if (!can_activate_window(hwnd))
636 HWND hwnd = GetFocus();
637 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
638 if (!hwnd) hwnd = GetActiveWindow();
639 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
640 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
642 else SetForegroundWindow( hwnd );
646 /**********************************************************************
647 * X11DRV_FocusOut
649 * Note: only top-level windows get FocusOut events.
651 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
653 XFocusChangeEvent *event = &xev->xfocus;
654 HWND hwnd_tmp;
655 Window focus_win;
656 int revert;
657 XIC xic;
659 if (!hwnd) return;
661 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
663 if (event->detail == NotifyPointer) return;
664 if (ximInComposeMode) return;
666 x11drv_thread_data()->last_focus = hwnd;
667 if ((xic = X11DRV_get_ic( hwnd )))
669 wine_tsx11_lock();
670 XUnsetICFocus( xic );
671 wine_tsx11_unlock();
673 if (hwnd != GetForegroundWindow()) return;
674 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
676 /* don't reset the foreground window, if the window which is
677 getting the focus is a Wine window */
679 wine_tsx11_lock();
680 XGetInputFocus( event->display, &focus_win, &revert );
681 if (focus_win)
683 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
684 focus_win = 0;
686 wine_tsx11_unlock();
688 if (!focus_win)
690 /* Abey : 6-Oct-99. Check again if the focus out window is the
691 Foreground window, because in most cases the messages sent
692 above must have already changed the foreground window, in which
693 case we don't have to change the foreground window to 0 */
694 if (hwnd == GetForegroundWindow())
696 TRACE( "lost focus, setting fg to desktop\n" );
697 SetForegroundWindow( GetDesktopWindow() );
703 /***********************************************************************
704 * X11DRV_Expose
706 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
708 XExposeEvent *event = &xev->xexpose;
709 RECT rect;
710 struct x11drv_win_data *data;
711 int flags = RDW_INVALIDATE | RDW_ERASE;
713 TRACE( "win %p (%lx) %d,%d %dx%d\n",
714 hwnd, event->window, event->x, event->y, event->width, event->height );
716 if (!(data = X11DRV_get_win_data( hwnd ))) return;
718 if (event->window == data->whole_window)
720 rect.left = data->whole_rect.left + event->x;
721 rect.top = data->whole_rect.top + event->y;
722 flags |= RDW_FRAME;
724 else
726 rect.left = data->client_rect.left + event->x;
727 rect.top = data->client_rect.top + event->y;
729 rect.right = rect.left + event->width;
730 rect.bottom = rect.top + event->height;
732 if (event->window != root_window)
734 SERVER_START_REQ( update_window_zorder )
736 req->window = wine_server_user_handle( hwnd );
737 req->rect.left = rect.left;
738 req->rect.top = rect.top;
739 req->rect.right = rect.right;
740 req->rect.bottom = rect.bottom;
741 wine_server_call( req );
743 SERVER_END_REQ;
745 /* make position relative to client area instead of parent */
746 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
747 flags |= RDW_ALLCHILDREN;
750 RedrawWindow( hwnd, &rect, 0, flags );
754 /**********************************************************************
755 * X11DRV_MapNotify
757 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
759 struct x11drv_win_data *data;
761 if (!(data = X11DRV_get_win_data( hwnd ))) return;
762 if (!data->mapped) return;
764 if (!data->managed)
766 HWND hwndFocus = GetFocus();
767 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
772 /***********************************************************************
773 * is_net_wm_state_maximized
775 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
777 Atom type, *state;
778 int format, ret = 0;
779 unsigned long i, count, remaining;
781 wine_tsx11_lock();
782 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
783 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
784 &remaining, (unsigned char **)&state ))
786 if (type == XA_ATOM && format == 32)
788 for (i = 0; i < count; i++)
790 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
791 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
792 ret++;
795 XFree( state );
797 wine_tsx11_unlock();
798 return (ret == 2);
802 /***********************************************************************
803 * X11DRV_ConfigureNotify
805 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
807 XConfigureEvent *event = &xev->xconfigure;
808 struct x11drv_win_data *data;
809 RECT rect;
810 UINT flags;
811 int cx, cy, x = event->x, y = event->y;
813 if (!hwnd) return;
814 if (!(data = X11DRV_get_win_data( hwnd ))) return;
815 if (!data->mapped || data->iconic || !data->managed) return;
817 /* Get geometry */
819 if (!event->send_event) /* normal event, need to map coordinates to the root */
821 Window child;
822 wine_tsx11_lock();
823 XTranslateCoordinates( event->display, data->whole_window, root_window,
824 0, 0, &x, &y, &child );
825 wine_tsx11_unlock();
827 rect.left = x;
828 rect.top = y;
829 rect.right = x + event->width;
830 rect.bottom = y + event->height;
831 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
832 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
833 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
834 event->x, event->y, event->width, event->height );
835 X11DRV_X_to_window_rect( data, &rect );
837 if (is_net_wm_state_maximized( event->display, data ))
839 if (!IsZoomed( data->hwnd ))
841 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
842 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
843 return;
846 else
848 if (IsZoomed( data->hwnd ))
850 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
851 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
852 return;
856 /* Compare what has changed */
858 x = rect.left;
859 y = rect.top;
860 cx = rect.right - rect.left;
861 cy = rect.bottom - rect.top;
862 flags = SWP_NOACTIVATE | SWP_NOZORDER;
864 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
865 else
866 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
867 hwnd, data->window_rect.left, data->window_rect.top, x, y );
869 if ((data->window_rect.right - data->window_rect.left == cx &&
870 data->window_rect.bottom - data->window_rect.top == cy) ||
871 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
873 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
874 flags |= SWP_NOSIZE;
876 else
877 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
878 hwnd, data->window_rect.right - data->window_rect.left,
879 data->window_rect.bottom - data->window_rect.top, cx, cy );
881 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
885 /***********************************************************************
886 * get_window_wm_state
888 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
890 struct
892 CARD32 state;
893 XID icon;
894 } *state;
895 Atom type;
896 int format, ret = -1;
897 unsigned long count, remaining;
899 wine_tsx11_lock();
900 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
901 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
902 &type, &format, &count, &remaining, (unsigned char **)&state ))
904 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
905 ret = state->state;
906 XFree( state );
908 wine_tsx11_unlock();
909 return ret;
913 /***********************************************************************
914 * handle_wm_state_notify
916 * Handle a PropertyNotify for WM_STATE.
918 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
919 BOOL update_window )
921 switch(event->state)
923 case PropertyDelete:
924 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
925 data->wm_state = WithdrawnState;
926 break;
927 case PropertyNewValue:
929 int old_state = data->wm_state;
930 int new_state = get_window_wm_state( event->display, data );
931 if (new_state != -1 && new_state != data->wm_state)
933 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
934 data->hwnd, data->whole_window, new_state, old_state );
935 data->wm_state = new_state;
936 /* ignore the initial state transition out of withdrawn state */
937 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
938 if (!old_state) return;
941 break;
944 if (!update_window || !data->managed || !data->mapped) return;
946 if (data->iconic && data->wm_state == NormalState) /* restore window */
948 data->iconic = FALSE;
949 if (is_net_wm_state_maximized( event->display, data ))
951 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
952 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
954 else
956 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
957 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
960 else if (!data->iconic && data->wm_state == IconicState)
962 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
963 data->iconic = TRUE;
964 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
969 /***********************************************************************
970 * X11DRV_PropertyNotify
972 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
974 XPropertyEvent *event = &xev->xproperty;
975 struct x11drv_win_data *data;
977 if (!hwnd) return;
978 if (!(data = X11DRV_get_win_data( hwnd ))) return;
980 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
984 /* event filter to wait for a WM_STATE change notification on a window */
985 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
987 if (event->xany.window != (Window)arg) return 0;
988 return (event->type == DestroyNotify ||
989 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
992 /***********************************************************************
993 * wait_for_withdrawn_state
995 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
997 DWORD end = GetTickCount() + 2000;
999 if (!data->managed) return;
1001 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1002 data->hwnd, data->whole_window, set ? "" : "not " );
1004 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1006 XEvent event;
1007 int count = 0;
1009 wine_tsx11_lock();
1010 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1012 count++;
1013 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1014 if (event.type == DestroyNotify) call_event_handler( display, &event );
1015 else
1017 wine_tsx11_unlock();
1018 handle_wm_state_notify( data, &event.xproperty, FALSE );
1019 wine_tsx11_lock();
1022 wine_tsx11_unlock();
1024 if (!count)
1026 struct pollfd pfd;
1027 int timeout = end - GetTickCount();
1029 pfd.fd = ConnectionNumber(display);
1030 pfd.events = POLLIN;
1031 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1033 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1034 break;
1038 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1042 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1044 RECT tempRect;
1046 if (!IsWindowEnabled(hQueryWnd)) return 0;
1048 GetWindowRect(hQueryWnd, &tempRect);
1050 if(!PtInRect(&tempRect, *lpPt)) return 0;
1052 if (!IsIconic( hQueryWnd ))
1054 POINT pt = *lpPt;
1055 ScreenToClient( hQueryWnd, &pt );
1056 GetClientRect( hQueryWnd, &tempRect );
1058 if (PtInRect( &tempRect, pt))
1060 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1061 if (ret && ret != hQueryWnd)
1063 ret = find_drop_window( ret, lpPt );
1064 if (ret) return ret;
1069 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1071 ScreenToClient(hQueryWnd, lpPt);
1073 return hQueryWnd;
1076 /**********************************************************************
1077 * EVENT_DropFromOffix
1079 * don't know if it still works (last Changelog is from 96/11/04)
1081 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1083 struct x11drv_win_data *data;
1084 unsigned long data_length;
1085 unsigned long aux_long;
1086 unsigned char* p_data = NULL;
1087 Atom atom_aux;
1088 int x, y, dummy;
1089 BOOL bAccept;
1090 Window win, w_aux_root, w_aux_child;
1092 win = X11DRV_get_whole_window(hWnd);
1093 wine_tsx11_lock();
1094 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1095 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1096 x += virtual_screen_rect.left;
1097 y += virtual_screen_rect.top;
1098 wine_tsx11_unlock();
1100 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1102 /* find out drop point and drop window */
1103 if( x < 0 || y < 0 ||
1104 x > (data->whole_rect.right - data->whole_rect.left) ||
1105 y > (data->whole_rect.bottom - data->whole_rect.top) )
1107 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1108 x = 0;
1109 y = 0;
1111 else
1113 POINT pt = { x, y };
1114 HWND hwndDrop = find_drop_window( hWnd, &pt );
1115 if (hwndDrop)
1117 x = pt.x;
1118 y = pt.y;
1119 bAccept = TRUE;
1121 else
1123 bAccept = FALSE;
1127 if (!bAccept) return;
1129 wine_tsx11_lock();
1130 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1131 x11drv_atom(DndSelection), 0, 65535, FALSE,
1132 AnyPropertyType, &atom_aux, &dummy,
1133 &data_length, &aux_long, &p_data);
1134 wine_tsx11_unlock();
1136 if( !aux_long && p_data) /* don't bother if > 64K */
1138 char *p = (char *)p_data;
1139 char *p_drop;
1141 aux_long = 0;
1142 while( *p ) /* calculate buffer size */
1144 INT len = GetShortPathNameA( p, NULL, 0 );
1145 if (len) aux_long += len + 1;
1146 p += strlen(p) + 1;
1148 if( aux_long && aux_long < 65535 )
1150 HDROP hDrop;
1151 DROPFILES *lpDrop;
1153 aux_long += sizeof(DROPFILES) + 1;
1154 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1155 lpDrop = GlobalLock( hDrop );
1157 if( lpDrop )
1159 lpDrop->pFiles = sizeof(DROPFILES);
1160 lpDrop->pt.x = x;
1161 lpDrop->pt.y = y;
1162 lpDrop->fNC = FALSE;
1163 lpDrop->fWide = FALSE;
1164 p_drop = (char *)(lpDrop + 1);
1165 p = (char *)p_data;
1166 while(*p)
1168 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1169 p_drop += strlen( p_drop ) + 1;
1170 p += strlen(p) + 1;
1172 *p_drop = '\0';
1173 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1177 wine_tsx11_lock();
1178 if( p_data ) XFree(p_data);
1179 wine_tsx11_unlock();
1182 /**********************************************************************
1183 * EVENT_DropURLs
1185 * drop items are separated by \n
1186 * each item is prefixed by its mime type
1188 * event->data.l[3], event->data.l[4] contains drop x,y position
1190 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1192 struct x11drv_win_data *win_data;
1193 unsigned long data_length;
1194 unsigned long aux_long, drop_len = 0;
1195 unsigned char *p_data = NULL; /* property data */
1196 char *p_drop = NULL;
1197 char *p, *next;
1198 int x, y;
1199 DROPFILES *lpDrop;
1200 HDROP hDrop;
1201 union {
1202 Atom atom_aux;
1203 int i;
1204 Window w_aux;
1205 unsigned int u;
1206 } u; /* unused */
1208 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1210 wine_tsx11_lock();
1211 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1212 x11drv_atom(DndSelection), 0, 65535, FALSE,
1213 AnyPropertyType, &u.atom_aux, &u.i,
1214 &data_length, &aux_long, &p_data);
1215 wine_tsx11_unlock();
1216 if (aux_long)
1217 WARN("property too large, truncated!\n");
1218 TRACE("urls=%s\n", p_data);
1220 if( !aux_long && p_data) { /* don't bother if > 64K */
1221 /* calculate length */
1222 p = (char*) p_data;
1223 next = strchr(p, '\n');
1224 while (p) {
1225 if (next) *next=0;
1226 if (strncmp(p,"file:",5) == 0 ) {
1227 INT len = GetShortPathNameA( p+5, NULL, 0 );
1228 if (len) drop_len += len + 1;
1230 if (next) {
1231 *next = '\n';
1232 p = next + 1;
1233 next = strchr(p, '\n');
1234 } else {
1235 p = NULL;
1239 if( drop_len && drop_len < 65535 ) {
1240 wine_tsx11_lock();
1241 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1242 &x, &y, &u.i, &u.i, &u.u);
1243 x += virtual_screen_rect.left;
1244 y += virtual_screen_rect.top;
1245 wine_tsx11_unlock();
1247 drop_len += sizeof(DROPFILES) + 1;
1248 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1249 lpDrop = GlobalLock( hDrop );
1251 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1253 lpDrop->pFiles = sizeof(DROPFILES);
1254 lpDrop->pt.x = x;
1255 lpDrop->pt.y = y;
1256 lpDrop->fNC =
1257 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1258 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1259 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1260 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1261 lpDrop->fWide = FALSE;
1262 p_drop = (char*)(lpDrop + 1);
1265 /* create message content */
1266 if (p_drop) {
1267 p = (char*) p_data;
1268 next = strchr(p, '\n');
1269 while (p) {
1270 if (next) *next=0;
1271 if (strncmp(p,"file:",5) == 0 ) {
1272 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1273 if (len) {
1274 TRACE("drop file %s as %s\n", p+5, p_drop);
1275 p_drop += len+1;
1276 } else {
1277 WARN("can't convert file %s to dos name\n", p+5);
1279 } else {
1280 WARN("unknown mime type %s\n", p);
1282 if (next) {
1283 *next = '\n';
1284 p = next + 1;
1285 next = strchr(p, '\n');
1286 } else {
1287 p = NULL;
1289 *p_drop = '\0';
1292 GlobalUnlock(hDrop);
1293 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1296 wine_tsx11_lock();
1297 if( p_data ) XFree(p_data);
1298 wine_tsx11_unlock();
1302 /**********************************************************************
1303 * handle_dnd_protocol
1305 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1307 Window root, child;
1308 int root_x, root_y, child_x, child_y;
1309 unsigned int u;
1311 /* query window (drag&drop event contains only drag window) */
1312 wine_tsx11_lock();
1313 XQueryPointer( event->display, root_window, &root, &child,
1314 &root_x, &root_y, &child_x, &child_y, &u);
1315 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1316 wine_tsx11_unlock();
1317 if (!hwnd) return;
1318 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1319 EVENT_DropFromOffiX(hwnd, event);
1320 else if (event->data.l[0] == DndURL)
1321 EVENT_DropURLs(hwnd, event);
1325 struct client_message_handler
1327 int atom; /* protocol atom */
1328 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1331 static const struct client_message_handler client_messages[] =
1333 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1334 { XATOM_DndProtocol, handle_dnd_protocol },
1335 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1336 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1337 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1338 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1342 /**********************************************************************
1343 * X11DRV_ClientMessage
1345 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1347 XClientMessageEvent *event = &xev->xclient;
1348 unsigned int i;
1350 if (!hwnd) return;
1352 if (event->format != 32)
1354 WARN( "Don't know how to handle format %d\n", event->format );
1355 return;
1358 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1360 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1362 client_messages[i].handler( hwnd, event );
1363 return;
1366 TRACE( "no handler found for %ld\n", event->message_type );
1370 /***********************************************************************
1371 * X11DRV_SendInput (X11DRV.@)
1373 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1375 UINT i;
1377 for (i = 0; i < count; i++, inputs++)
1379 switch(inputs->type)
1381 case INPUT_MOUSE:
1382 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1383 inputs->u.mi.mouseData, inputs->u.mi.time,
1384 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1385 break;
1386 case INPUT_KEYBOARD:
1387 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1388 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1389 break;
1390 case INPUT_HARDWARE:
1391 FIXME( "INPUT_HARDWARE not supported\n" );
1392 break;
1395 return count;