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 int process_events( Display *display )
208 x11drv_event_handler handler;
211 for (count = 0; XPending(display); count++)
213 XNextEvent( display, &event );
214 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
216 if (!(handler = find_handler( event.type )))
218 TRACE( "%s, ignoring\n", dbgstr_event( event.type ));
219 continue; /* no handler, ignore it */
222 if (XFindContext( display, event.xany.window, winContext, (char **)&hwnd ) != 0)
223 hwnd = 0; /* not for a registered window */
224 if (!hwnd && event.xany.window == root_window) hwnd = GetDesktopWindow();
227 TRACE( "%s for hwnd/window %p/%lx\n",
228 dbgstr_event( event.type ), hwnd, event.xany.window );
229 handler( hwnd, &event );
233 if (count) TRACE( "processed %d events\n", count );
238 /***********************************************************************
239 * MsgWaitForMultipleObjectsEx (X11DRV.@)
241 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
242 DWORD timeout, DWORD mask, DWORD flags )
244 HANDLE new_handles[MAXIMUM_WAIT_OBJECTS+1]; /* FIXME! */
246 struct x11drv_thread_data *data = NtCurrentTeb()->driver_data;
248 if (!data || data->process_event_count)
249 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
250 timeout, flags & MWMO_ALERTABLE );
252 /* check whether only server queue handle was passed in */
253 if (count < 2) flags &= ~MWMO_WAITALL;
255 for (i = 0; i < count; i++) new_handles[i] = handles[i];
256 new_handles[count] = data->display_fd;
259 XFlush( gdi_display );
260 XFlush( data->display );
263 data->process_event_count++;
264 if (process_events( data->display )) ret = count;
267 ret = WaitForMultipleObjectsEx( count+1, new_handles, flags & MWMO_WAITALL,
268 timeout, flags & MWMO_ALERTABLE );
269 if (ret == count) process_events( data->display );
271 data->process_event_count--;
275 /***********************************************************************
276 * EVENT_x11_time_to_win32_time
278 * Make our timer and the X timer line up as best we can
279 * Pass 0 to retrieve the current adjustment value (times -1)
281 DWORD EVENT_x11_time_to_win32_time(Time time)
283 static DWORD adjust = 0;
284 DWORD now = GetTickCount();
287 if (! adjust && time != 0)
294 /* If we got an event in the 'future', then our clock is clearly wrong.
295 If we got it more than 10000 ms in the future, then it's most likely
296 that the clock has wrapped. */
299 if (ret > now && ((ret - now) < 10000) && time != 0)
310 /*******************************************************************
311 * can_activate_window
313 * Check if we can activate the specified window.
315 inline static BOOL can_activate_window( HWND hwnd )
317 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
318 if (!(style & WS_VISIBLE)) return FALSE;
319 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
320 return !(style & WS_DISABLED);
324 /**********************************************************************
327 static void set_focus( HWND hwnd, Time time )
332 TRACE( "setting foreground window to %p\n", hwnd );
333 SetForegroundWindow( hwnd );
336 if (focus) focus = GetAncestor( focus, GA_ROOT );
337 win = X11DRV_get_whole_window(focus);
341 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
343 XSetInputFocus( thread_display(), win, RevertToParent, time );
349 /**********************************************************************
350 * handle_wm_protocols
352 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
354 Atom protocol = (Atom)event->data.l[0];
356 if (!protocol) return;
358 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
360 /* Ignore the delete window request if the window has been disabled
361 * and we are in managed mode. This is to disallow applications from
362 * being closed by the window manager while in a modal state.
364 if (IsWindowEnabled(hwnd)) PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
366 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
368 Time event_time = (Time)event->data.l[1];
369 HWND last_focus = x11drv_thread_data()->last_focus;
371 TRACE( "got take focus msg for %p, enabled=%d, focus=%p, active=%p, fg=%p, last=%p\n",
372 hwnd, IsWindowEnabled(hwnd), GetFocus(), GetActiveWindow(),
373 GetForegroundWindow(), last_focus );
375 if (can_activate_window(hwnd))
377 /* simulate a mouse click on the caption to find out
378 * whether the window wants to be activated */
379 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
380 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
381 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
382 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE) set_focus( hwnd, event_time );
383 else TRACE( "not setting focus to %p (%lx), ma=%ld\n", hwnd, event->window, ma );
388 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
389 if (!hwnd) hwnd = GetActiveWindow();
390 if (!hwnd) hwnd = last_focus;
391 if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, event_time );
394 else if (protocol == x11drv_atom(_NET_WM_PING))
396 XClientMessageEvent xev;
399 TRACE("NET_WM Ping\n");
400 xev.window = DefaultRootWindow(xev.display);
401 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
402 /* this line is semi-stolen from gtk2 */
403 TRACE("NET_WM Pong\n");
408 static const char * const focus_details[] =
414 "NotifyNonlinearVirtual",
420 /**********************************************************************
423 static void EVENT_FocusIn( HWND hwnd, XEvent *xev )
425 XFocusChangeEvent *event = &xev->xfocus;
430 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
432 if (event->detail == NotifyPointer) return;
434 if ((xic = X11DRV_get_ic( hwnd )))
440 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
442 if (!can_activate_window(hwnd))
444 HWND hwnd = GetFocus();
445 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
446 if (!hwnd) hwnd = GetActiveWindow();
447 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
448 if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, CurrentTime );
450 else SetForegroundWindow( hwnd );
454 /**********************************************************************
457 * Note: only top-level windows get FocusOut events.
459 static void EVENT_FocusOut( HWND hwnd, XEvent *xev )
461 XFocusChangeEvent *event = &xev->xfocus;
469 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
471 if (event->detail == NotifyPointer) return;
472 if (ximInComposeMode) return;
474 x11drv_thread_data()->last_focus = hwnd;
475 if ((xic = X11DRV_get_ic( hwnd )))
478 XUnsetICFocus( xic );
481 if (hwnd != GetForegroundWindow()) return;
482 SendMessageA( hwnd, WM_CANCELMODE, 0, 0 );
484 /* don't reset the foreground window, if the window which is
485 getting the focus is a Wine window */
488 XGetInputFocus( thread_display(), &focus_win, &revert );
491 if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0)
498 /* Abey : 6-Oct-99. Check again if the focus out window is the
499 Foreground window, because in most cases the messages sent
500 above must have already changed the foreground window, in which
501 case we don't have to change the foreground window to 0 */
502 if (hwnd == GetForegroundWindow())
504 TRACE( "lost focus, setting fg to 0\n" );
505 SetForegroundWindow( 0 );
511 /***********************************************************************
512 * EVENT_PropertyNotify
513 * We use this to release resources like Pixmaps when a selection
514 * client no longer needs them.
516 static void EVENT_PropertyNotify( HWND hwnd, XEvent *xev )
518 XPropertyEvent *event = &xev->xproperty;
519 /* Check if we have any resources to free */
520 TRACE("Received PropertyNotify event: \n");
526 TRACE("\tPropertyDelete for atom %ld on window %ld\n",
527 event->atom, (long)event->window);
531 case PropertyNewValue:
533 TRACE("\tPropertyNewValue for atom %ld on window %ld\n\n",
534 event->atom, (long)event->window);
543 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
547 if (!IsWindowEnabled(hQueryWnd)) return 0;
549 GetWindowRect(hQueryWnd, &tempRect);
551 if(!PtInRect(&tempRect, *lpPt)) return 0;
553 if (!IsIconic( hQueryWnd ))
555 GetClientRect( hQueryWnd, &tempRect );
556 MapWindowPoints( hQueryWnd, 0, (LPPOINT)&tempRect, 2 );
558 if (PtInRect( &tempRect, *lpPt))
560 HWND *list = WIN_ListChildren( hQueryWnd );
567 for (i = 0; list[i]; i++)
569 if (GetWindowLongW( list[i], GWL_STYLE ) & WS_VISIBLE)
571 GetWindowRect( list[i], &tempRect );
572 if (PtInRect( &tempRect, *lpPt )) break;
577 if (IsWindowEnabled( list[i] ))
578 bResult = find_drop_window( list[i], lpPt );
580 HeapFree( GetProcessHeap(), 0, list );
582 if(bResult) return bResult;
586 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
588 ScreenToClient(hQueryWnd, lpPt);
593 /**********************************************************************
594 * EVENT_DropFromOffix
596 * don't know if it still works (last Changlog is from 96/11/04)
598 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
600 unsigned long data_length;
601 unsigned long aux_long;
602 unsigned char* p_data = NULL;
613 Window win, w_aux_root, w_aux_child;
617 win = X11DRV_get_whole_window(hWnd);
619 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
620 &x, &y, (int *) &u.pt_aux.x, (int *) &u.pt_aux.y,
621 (unsigned int*)&aux_long);
624 pWnd = WIN_GetPtr(hWnd);
626 /* find out drop point and drop window */
627 if( x < 0 || y < 0 ||
628 x > (pWnd->rectWindow.right - pWnd->rectWindow.left) ||
629 y > (pWnd->rectWindow.bottom - pWnd->rectWindow.top) )
631 bAccept = pWnd->dwExStyle & WS_EX_ACCEPTFILES;
638 HWND hwndDrop = find_drop_window( hWnd, &pt );
651 WIN_ReleasePtr(pWnd);
653 if (!bAccept) return;
656 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
657 x11drv_atom(DndSelection), 0, 65535, FALSE,
658 AnyPropertyType, &u.atom_aux, (int *) &u.pt_aux.y,
659 &data_length, &aux_long, &p_data);
662 if( !aux_long && p_data) /* don't bother if > 64K */
664 signed char *p = (signed char*) p_data;
668 while( *p ) /* calculate buffer size */
671 if((u.i = *p) != -1 )
673 INT len = GetShortPathNameA( p, NULL, 0 );
674 if (len) aux_long += len + 1;
679 if( aux_long && aux_long < 65535 )
684 aux_long += sizeof(DROPFILES) + 1;
685 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
686 lpDrop = (DROPFILES*)GlobalLock( hDrop );
690 WND *pDropWnd = WIN_GetPtr( hScope );
691 lpDrop->pFiles = sizeof(DROPFILES);
695 ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left) ||
696 y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top) ||
697 x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
698 y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
699 lpDrop->fWide = FALSE;
700 WIN_ReleasePtr(pDropWnd);
701 p_drop = (char *)(lpDrop + 1);
705 if( *p != -1 ) /* use only "good" entries */
707 GetShortPathNameA( p, p_drop, 65535 );
708 p_drop += strlen( p_drop ) + 1;
713 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
718 if( p_data ) XFree(p_data);
722 /**********************************************************************
725 * drop items are separated by \n
726 * each item is prefixed by its mime type
728 * event->data.l[3], event->data.l[4] contains drop x,y position
730 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
732 unsigned long data_length;
733 unsigned long aux_long, drop_len = 0;
734 unsigned char *p_data = NULL; /* property data */
746 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
749 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
750 x11drv_atom(DndSelection), 0, 65535, FALSE,
751 AnyPropertyType, &u.atom_aux, &u.i,
752 &data_length, &aux_long, &p_data);
755 WARN("property too large, truncated!\n");
756 TRACE("urls=%s\n", p_data);
758 if( !aux_long && p_data) { /* don't bother if > 64K */
759 /* calculate length */
761 next = strchr(p, '\n');
764 if (strncmp(p,"file:",5) == 0 ) {
765 INT len = GetShortPathNameA( p+5, NULL, 0 );
766 if (len) drop_len += len + 1;
771 next = strchr(p, '\n');
777 if( drop_len && drop_len < 65535 ) {
779 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
780 &x, &y, &u.i, &u.i, &u.i);
783 drop_len += sizeof(DROPFILES) + 1;
784 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
785 lpDrop = (DROPFILES *) GlobalLock( hDrop );
788 WND *pDropWnd = WIN_GetPtr( hWnd );
789 lpDrop->pFiles = sizeof(DROPFILES);
790 lpDrop->pt.x = (INT)x;
791 lpDrop->pt.y = (INT)y;
793 ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left) ||
794 y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top) ||
795 x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
796 y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
797 lpDrop->fWide = FALSE;
798 p_drop = (char*)(lpDrop + 1);
799 WIN_ReleasePtr(pDropWnd);
802 /* create message content */
805 next = strchr(p, '\n');
808 if (strncmp(p,"file:",5) == 0 ) {
809 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
811 TRACE("drop file %s as %s\n", p+5, p_drop);
814 WARN("can't convert file %s to dos name \n", p+5);
817 WARN("unknown mime type %s\n", p);
822 next = strchr(p, '\n');
830 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
834 if( p_data ) XFree(p_data);
839 /**********************************************************************
840 * handle_dnd_protocol
842 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
845 int root_x, root_y, child_x, child_y;
848 /* query window (drag&drop event contains only drag window) */
850 XQueryPointer( event->display, root_window, &root, &child,
851 &root_x, &root_y, &child_x, &child_y, &u);
852 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
855 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
856 EVENT_DropFromOffiX(hwnd, event);
857 else if (event->data.l[0] == DndURL)
858 EVENT_DropURLs(hwnd, event);
862 struct client_message_handler
864 int atom; /* protocol atom */
865 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
868 static const struct client_message_handler client_messages[] =
870 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
871 { XATOM_DndProtocol, handle_dnd_protocol },
872 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
873 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
874 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
875 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
879 /**********************************************************************
880 * EVENT_ClientMessage
882 static void EVENT_ClientMessage( HWND hwnd, XEvent *xev )
884 XClientMessageEvent *event = &xev->xclient;
889 if (event->format != 32)
891 WARN( "Don't know how to handle format %d\n", event->format );
895 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
897 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
899 client_messages[i].handler( hwnd, event );
903 TRACE( "no handler found for %ld\n", event->message_type );