4 * Copyright 1993 Alexandre Julliard
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #define COM_NO_WINDOWS_H
25 #include <X11/Xatom.h>
26 #include <X11/keysym.h>
28 #include <X11/Xresource.h>
29 #include <X11/Xutil.h>
30 #ifdef HAVE_LIBXXF86DGA2
31 #include <X11/extensions/xf86dga.h>
37 #include "wine/winuser16.h"
42 #include "shlobj.h" /* DROPFILES */
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(event);
51 WINE_DECLARE_DEBUG_CHANNEL(clipboard);
53 extern BOOL ximInComposeMode;
55 #define DndNotDnd -1 /* OffiX drag&drop */
67 #define DndURL 128 /* KDE drag&drop */
70 static void EVENT_FocusIn( HWND hwnd, XEvent *event );
71 static void EVENT_FocusOut( HWND hwnd, XEvent *event );
72 static void EVENT_PropertyNotify( HWND hwnd, XEvent *event );
73 static void EVENT_ClientMessage( HWND hwnd, XEvent *event );
77 int type; /* event type */
78 x11drv_event_handler handler; /* corresponding handler function */
81 #define MAX_EVENT_HANDLERS 64
83 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
85 /* list must be sorted by event type */
86 { KeyPress, X11DRV_KeyEvent },
87 { KeyRelease, X11DRV_KeyEvent },
88 { ButtonPress, X11DRV_ButtonPress },
89 { ButtonRelease, X11DRV_ButtonRelease },
90 { MotionNotify, X11DRV_MotionNotify },
91 { EnterNotify, X11DRV_EnterNotify },
93 { FocusIn, EVENT_FocusIn },
94 { FocusOut, EVENT_FocusOut },
95 { KeymapNotify, X11DRV_KeymapNotify },
96 { Expose, X11DRV_Expose },
99 /* VisibilityNotify */
102 { UnmapNotify, X11DRV_UnmapNotify },
103 { MapNotify, X11DRV_MapNotify },
106 { ConfigureNotify, X11DRV_ConfigureNotify },
107 /* ConfigureRequest */
110 /* CirculateNotify */
111 /* CirculateRequest */
112 { PropertyNotify, EVENT_PropertyNotify },
113 { SelectionClear, X11DRV_SelectionClear },
114 { SelectionRequest, X11DRV_SelectionRequest },
115 /* SelectionNotify */
117 { ClientMessage, EVENT_ClientMessage },
118 { MappingNotify, X11DRV_MappingNotify },
121 static int nb_event_handlers = 18; /* change this if you add handlers above */
124 /* return the name of an X event */
125 static const char *dbgstr_event( int type )
127 static const char * const event_names[] =
129 "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
130 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
131 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
132 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
133 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
134 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
135 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
136 "ClientMessage", "MappingNotify"
139 if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
140 return wine_dbg_sprintf( "Extension event %d", type );
144 /***********************************************************************
147 * Find the handler for a given event type. Caller must hold the x11 lock.
149 static inline x11drv_event_handler find_handler( int type )
151 int min = 0, max = nb_event_handlers - 1;
155 int pos = (min + max) / 2;
156 if (handlers[pos].type == type) return handlers[pos].handler;
157 if (handlers[pos].type > type) max = pos - 1;
164 /***********************************************************************
165 * X11DRV_register_event_handler
167 * Register a handler for a given event type.
168 * If already registered, overwrite the previous handler.
170 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
176 max = nb_event_handlers - 1;
179 int pos = (min + max) / 2;
180 if (handlers[pos].type == type)
182 handlers[pos].handler = handler;
185 if (handlers[pos].type > type) max = pos - 1;
188 /* insert it between max and min */
189 memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
190 handlers[min].type = type;
191 handlers[min].handler = handler;
193 assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
196 TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
200 /***********************************************************************
203 static Bool filter_event( Display *display, XEvent *event, char *arg )
205 DWORD mask = (ULONG_PTR)arg;
207 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
214 return (mask & QS_KEY) != 0;
217 return (mask & QS_MOUSEBUTTON) != 0;
221 return (mask & QS_MOUSEMOVE) != 0;
223 return (mask & QS_PAINT) != 0;
225 return (mask & QS_POSTMESSAGE) != 0;
227 return (mask & QS_SENDMESSAGE) != 0;
232 /***********************************************************************
235 static int process_events( Display *display, DWORD mask )
240 x11drv_event_handler handler;
243 while (XCheckIfEvent( display, &event, filter_event, (char *)mask ))
246 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
248 if (!(handler = find_handler( event.type )))
250 TRACE( "%s, ignoring\n", dbgstr_event( event.type ));
251 continue; /* no handler, ignore it */
254 if (XFindContext( display, event.xany.window, winContext, (char **)&hwnd ) != 0)
255 hwnd = 0; /* not for a registered window */
256 if (!hwnd && event.xany.window == root_window) hwnd = GetDesktopWindow();
259 TRACE( "%s for hwnd/window %p/%lx\n",
260 dbgstr_event( event.type ), hwnd, event.xany.window );
261 handler( hwnd, &event );
265 if (count) TRACE( "processed %d events\n", count );
270 /***********************************************************************
271 * MsgWaitForMultipleObjectsEx (X11DRV.@)
273 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
274 DWORD timeout, DWORD mask, DWORD flags )
276 HANDLE new_handles[MAXIMUM_WAIT_OBJECTS+1]; /* FIXME! */
278 struct x11drv_thread_data *data = NtCurrentTeb()->driver_data;
280 if (!data || data->process_event_count)
281 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
282 timeout, flags & MWMO_ALERTABLE );
284 /* check whether only server queue handle was passed in */
285 if (count < 2) flags &= ~MWMO_WAITALL;
287 for (i = 0; i < count; i++) new_handles[i] = handles[i];
288 new_handles[count] = data->display_fd;
291 XFlush( gdi_display );
292 XFlush( data->display );
295 data->process_event_count++;
296 if (process_events( data->display, mask )) ret = count;
299 ret = WaitForMultipleObjectsEx( count+1, new_handles, flags & MWMO_WAITALL,
300 timeout, flags & MWMO_ALERTABLE );
301 if (ret == count) process_events( data->display, mask );
303 data->process_event_count--;
307 /***********************************************************************
308 * EVENT_x11_time_to_win32_time
310 * Make our timer and the X timer line up as best we can
311 * Pass 0 to retrieve the current adjustment value (times -1)
313 DWORD EVENT_x11_time_to_win32_time(Time time)
315 static DWORD adjust = 0;
316 DWORD now = GetTickCount();
319 if (! adjust && time != 0)
326 /* If we got an event in the 'future', then our clock is clearly wrong.
327 If we got it more than 10000 ms in the future, then it's most likely
328 that the clock has wrapped. */
331 if (ret > now && ((ret - now) < 10000) && time != 0)
342 /*******************************************************************
343 * can_activate_window
345 * Check if we can activate the specified window.
347 inline static BOOL can_activate_window( HWND hwnd )
349 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
350 if (!(style & WS_VISIBLE)) return FALSE;
351 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
352 return !(style & WS_DISABLED);
356 /**********************************************************************
359 static void set_focus( HWND hwnd, Time time )
364 TRACE( "setting foreground window to %p\n", hwnd );
365 SetForegroundWindow( hwnd );
368 if (focus) focus = GetAncestor( focus, GA_ROOT );
369 win = X11DRV_get_whole_window(focus);
373 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
375 XSetInputFocus( thread_display(), win, RevertToParent, time );
381 /**********************************************************************
382 * handle_wm_protocols
384 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
386 Atom protocol = (Atom)event->data.l[0];
388 if (!protocol) return;
390 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
392 /* Ignore the delete window request if the window has been disabled
393 * and we are in managed mode. This is to disallow applications from
394 * being closed by the window manager while in a modal state.
396 if (IsWindowEnabled(hwnd)) PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
398 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
400 Time event_time = (Time)event->data.l[1];
401 HWND last_focus = x11drv_thread_data()->last_focus;
403 TRACE( "got take focus msg for %p, enabled=%d, focus=%p, active=%p, fg=%p, last=%p\n",
404 hwnd, IsWindowEnabled(hwnd), GetFocus(), GetActiveWindow(),
405 GetForegroundWindow(), last_focus );
407 if (can_activate_window(hwnd))
409 /* simulate a mouse click on the caption to find out
410 * whether the window wants to be activated */
411 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
412 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
413 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
414 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE) set_focus( hwnd, event_time );
415 else TRACE( "not setting focus to %p (%lx), ma=%ld\n", hwnd, event->window, ma );
420 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
421 if (!hwnd) hwnd = GetActiveWindow();
422 if (!hwnd) hwnd = last_focus;
423 if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, event_time );
426 else if (protocol == x11drv_atom(_NET_WM_PING))
428 XClientMessageEvent xev;
431 TRACE("NET_WM Ping\n");
432 xev.window = DefaultRootWindow(xev.display);
433 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
434 /* this line is semi-stolen from gtk2 */
435 TRACE("NET_WM Pong\n");
440 static const char * const focus_details[] =
446 "NotifyNonlinearVirtual",
452 /**********************************************************************
455 static void EVENT_FocusIn( HWND hwnd, XEvent *xev )
457 XFocusChangeEvent *event = &xev->xfocus;
462 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
464 if (event->detail == NotifyPointer) return;
466 if ((xic = X11DRV_get_ic( hwnd )))
472 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
474 if (!can_activate_window(hwnd))
476 HWND hwnd = GetFocus();
477 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
478 if (!hwnd) hwnd = GetActiveWindow();
479 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
480 if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, CurrentTime );
482 else SetForegroundWindow( hwnd );
486 /**********************************************************************
489 * Note: only top-level windows get FocusOut events.
491 static void EVENT_FocusOut( HWND hwnd, XEvent *xev )
493 XFocusChangeEvent *event = &xev->xfocus;
501 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
503 if (event->detail == NotifyPointer) return;
504 if (ximInComposeMode) return;
506 x11drv_thread_data()->last_focus = hwnd;
507 if ((xic = X11DRV_get_ic( hwnd )))
510 XUnsetICFocus( xic );
513 if (hwnd != GetForegroundWindow()) return;
514 SendMessageA( hwnd, WM_CANCELMODE, 0, 0 );
516 /* don't reset the foreground window, if the window which is
517 getting the focus is a Wine window */
520 XGetInputFocus( thread_display(), &focus_win, &revert );
523 if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0)
530 /* Abey : 6-Oct-99. Check again if the focus out window is the
531 Foreground window, because in most cases the messages sent
532 above must have already changed the foreground window, in which
533 case we don't have to change the foreground window to 0 */
534 if (hwnd == GetForegroundWindow())
536 TRACE( "lost focus, setting fg to 0\n" );
537 SetForegroundWindow( 0 );
543 /***********************************************************************
544 * EVENT_PropertyNotify
545 * We use this to release resources like Pixmaps when a selection
546 * client no longer needs them.
548 static void EVENT_PropertyNotify( HWND hwnd, XEvent *xev )
550 XPropertyEvent *event = &xev->xproperty;
551 /* Check if we have any resources to free */
552 TRACE("Received PropertyNotify event: \n");
558 TRACE("\tPropertyDelete for atom %ld on window %ld\n",
559 event->atom, (long)event->window);
563 case PropertyNewValue:
565 TRACE("\tPropertyNewValue for atom %ld on window %ld\n\n",
566 event->atom, (long)event->window);
575 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
579 if (!IsWindowEnabled(hQueryWnd)) return 0;
581 GetWindowRect(hQueryWnd, &tempRect);
583 if(!PtInRect(&tempRect, *lpPt)) return 0;
585 if (!IsIconic( hQueryWnd ))
587 GetClientRect( hQueryWnd, &tempRect );
588 MapWindowPoints( hQueryWnd, 0, (LPPOINT)&tempRect, 2 );
590 if (PtInRect( &tempRect, *lpPt))
592 HWND *list = WIN_ListChildren( hQueryWnd );
599 for (i = 0; list[i]; i++)
601 if (GetWindowLongW( list[i], GWL_STYLE ) & WS_VISIBLE)
603 GetWindowRect( list[i], &tempRect );
604 if (PtInRect( &tempRect, *lpPt )) break;
609 if (IsWindowEnabled( list[i] ))
610 bResult = find_drop_window( list[i], lpPt );
612 HeapFree( GetProcessHeap(), 0, list );
614 if(bResult) return bResult;
618 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
620 ScreenToClient(hQueryWnd, lpPt);
625 /**********************************************************************
626 * EVENT_DropFromOffix
628 * don't know if it still works (last Changlog is from 96/11/04)
630 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
632 unsigned long data_length;
633 unsigned long aux_long;
634 unsigned char* p_data = NULL;
645 Window win, w_aux_root, w_aux_child;
649 win = X11DRV_get_whole_window(hWnd);
651 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
652 &x, &y, (int *) &u.pt_aux.x, (int *) &u.pt_aux.y,
653 (unsigned int*)&aux_long);
656 pWnd = WIN_GetPtr(hWnd);
658 /* find out drop point and drop window */
659 if( x < 0 || y < 0 ||
660 x > (pWnd->rectWindow.right - pWnd->rectWindow.left) ||
661 y > (pWnd->rectWindow.bottom - pWnd->rectWindow.top) )
663 bAccept = pWnd->dwExStyle & WS_EX_ACCEPTFILES;
670 HWND hwndDrop = find_drop_window( hWnd, &pt );
683 WIN_ReleasePtr(pWnd);
685 if (!bAccept) return;
688 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
689 x11drv_atom(DndSelection), 0, 65535, FALSE,
690 AnyPropertyType, &u.atom_aux, (int *) &u.pt_aux.y,
691 &data_length, &aux_long, &p_data);
694 if( !aux_long && p_data) /* don't bother if > 64K */
696 signed char *p = (signed char*) p_data;
700 while( *p ) /* calculate buffer size */
703 if((u.i = *p) != -1 )
705 INT len = GetShortPathNameA( p, NULL, 0 );
706 if (len) aux_long += len + 1;
711 if( aux_long && aux_long < 65535 )
716 aux_long += sizeof(DROPFILES) + 1;
717 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
718 lpDrop = (DROPFILES*)GlobalLock( hDrop );
722 WND *pDropWnd = WIN_GetPtr( hScope );
723 lpDrop->pFiles = sizeof(DROPFILES);
727 ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left) ||
728 y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top) ||
729 x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
730 y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
731 lpDrop->fWide = FALSE;
732 WIN_ReleasePtr(pDropWnd);
733 p_drop = (char *)(lpDrop + 1);
737 if( *p != -1 ) /* use only "good" entries */
739 GetShortPathNameA( p, p_drop, 65535 );
740 p_drop += strlen( p_drop ) + 1;
745 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
750 if( p_data ) XFree(p_data);
754 /**********************************************************************
757 * drop items are separated by \n
758 * each item is prefixed by its mime type
760 * event->data.l[3], event->data.l[4] contains drop x,y position
762 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
764 unsigned long data_length;
765 unsigned long aux_long, drop_len = 0;
766 unsigned char *p_data = NULL; /* property data */
778 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
781 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
782 x11drv_atom(DndSelection), 0, 65535, FALSE,
783 AnyPropertyType, &u.atom_aux, &u.i,
784 &data_length, &aux_long, &p_data);
787 WARN("property too large, truncated!\n");
788 TRACE("urls=%s\n", p_data);
790 if( !aux_long && p_data) { /* don't bother if > 64K */
791 /* calculate length */
793 next = strchr(p, '\n');
796 if (strncmp(p,"file:",5) == 0 ) {
797 INT len = GetShortPathNameA( p+5, NULL, 0 );
798 if (len) drop_len += len + 1;
803 next = strchr(p, '\n');
809 if( drop_len && drop_len < 65535 ) {
811 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
812 &x, &y, &u.i, &u.i, &u.i);
815 drop_len += sizeof(DROPFILES) + 1;
816 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
817 lpDrop = (DROPFILES *) GlobalLock( hDrop );
820 WND *pDropWnd = WIN_GetPtr( hWnd );
821 lpDrop->pFiles = sizeof(DROPFILES);
822 lpDrop->pt.x = (INT)x;
823 lpDrop->pt.y = (INT)y;
825 ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left) ||
826 y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top) ||
827 x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
828 y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
829 lpDrop->fWide = FALSE;
830 p_drop = (char*)(lpDrop + 1);
831 WIN_ReleasePtr(pDropWnd);
834 /* create message content */
837 next = strchr(p, '\n');
840 if (strncmp(p,"file:",5) == 0 ) {
841 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
843 TRACE("drop file %s as %s\n", p+5, p_drop);
846 WARN("can't convert file %s to dos name \n", p+5);
849 WARN("unknown mime type %s\n", p);
854 next = strchr(p, '\n');
862 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
866 if( p_data ) XFree(p_data);
871 /**********************************************************************
872 * handle_dnd_protocol
874 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
877 int root_x, root_y, child_x, child_y;
880 /* query window (drag&drop event contains only drag window) */
882 XQueryPointer( event->display, root_window, &root, &child,
883 &root_x, &root_y, &child_x, &child_y, &u);
884 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
887 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
888 EVENT_DropFromOffiX(hwnd, event);
889 else if (event->data.l[0] == DndURL)
890 EVENT_DropURLs(hwnd, event);
894 struct client_message_handler
896 int atom; /* protocol atom */
897 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
900 static const struct client_message_handler client_messages[] =
902 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
903 { XATOM_DndProtocol, handle_dnd_protocol },
904 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
905 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
906 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
907 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
911 /**********************************************************************
912 * EVENT_ClientMessage
914 static void EVENT_ClientMessage( HWND hwnd, XEvent *xev )
916 XClientMessageEvent *event = &xev->xclient;
921 if (event->format != 32)
923 WARN( "Don't know how to handle format %d\n", event->format );
927 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
929 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
931 client_messages[i].handler( hwnd, event );
935 TRACE( "no handler found for %ld\n", event->message_type );
939 /**********************************************************************
940 * X11DRV_WindowMessage (X11DRV.@)
942 LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
946 case WM_X11DRV_ACQUIRE_SELECTION:
947 X11DRV_AcquireClipboard( hwnd );
950 FIXME( "got window msg %x hwnd %p wp %x lp %lx\n", msg, hwnd, wp, lp );