/* * X11 event driver * * Copyright 1993 Alexandre Julliard */ #include "config.h" #ifndef X_DISPLAY_MISSING #include #include #include "ts_xlib.h" #include "ts_xresource.h" #include "ts_xutil.h" #include #include #include #include "callback.h" #include "class.h" #include "clipboard.h" #include "dce.h" #include "dde_proc.h" #include "debug.h" #include "drive.h" #include "heap.h" #include "keyboard.h" #include "message.h" #include "mouse.h" #include "options.h" #include "queue.h" #include "shell.h" #include "winpos.h" #include "winsock.h" #include "windef.h" #include "x11drv.h" /* X context to associate a hwnd to an X window */ extern XContext winContext; extern Atom wmProtocols; extern Atom wmDeleteWindow; extern Atom dndProtocol; extern Atom dndSelection; extern void X11DRV_KEYBOARD_UpdateState(void); extern void X11DRV_KEYBOARD_HandleEvent(WND *pWnd, XKeyEvent *event); #define NB_BUTTONS 3 /* Windows can handle 3 buttons */ #define DndNotDnd -1 /* OffiX drag&drop */ #define DndUnknown 0 #define DndRawData 1 #define DndFile 2 #define DndFiles 3 #define DndText 4 #define DndDir 5 #define DndLink 6 #define DndExe 7 #define DndEND 8 #define DndURL 128 /* KDE drag&drop */ /* EVENT_WaitNetEvent() master fd sets */ static fd_set __event_io_set[3]; static int __event_max_fd = 0; static int __event_x_connection = 0; static int __wakeup_pipe[2]; static const char * const event_names[] = { "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease", "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify", "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest", "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify", "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify" }; static void EVENT_ProcessEvent( XEvent *event ); /* Event handlers */ static void EVENT_Key( HWND hWnd, XKeyEvent *event ); static void EVENT_ButtonPress( HWND hWnd, XButtonEvent *event ); static void EVENT_ButtonRelease( HWND hWnd, XButtonEvent *event ); static void EVENT_MotionNotify( HWND hWnd, XMotionEvent *event ); static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event ); static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event ); static void EVENT_Expose( HWND hWnd, XExposeEvent *event ); static void EVENT_GraphicsExpose( HWND hWnd, XGraphicsExposeEvent *event ); static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *event ); static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event); static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event); static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event ); static void EVENT_MapNotify( HWND pWnd, XMapEvent *event ); static void EVENT_UnmapNotify( HWND pWnd, XUnmapEvent *event ); /* Usable only with OLVWM - compile option perhaps? static void EVENT_EnterNotify( HWND hWnd, XCrossingEvent *event ); */ static void EVENT_GetGeometry( Window win, int *px, int *py, unsigned int *pwidth, unsigned int *pheight ); /*********************************************************************** * EVENT_Init * * Initialize network IO. */ BOOL X11DRV_EVENT_Init(void) { int i; for( i = 0; i < 3; i++ ) FD_ZERO( __event_io_set + i ); __event_max_fd = __event_x_connection = ConnectionNumber(display); FD_SET( __event_x_connection, &__event_io_set[EVENT_IO_READ] ); /* this pipe is used to be able to wake-up the scheduler(WaitNetEvent) by a 32 bit thread, this will become obsolete when the input thread will be implemented */ pipe(__wakeup_pipe); /* make the pipe non-blocking */ fcntl(__wakeup_pipe[0], F_SETFL, O_NONBLOCK); fcntl(__wakeup_pipe[1], F_SETFL, O_NONBLOCK); FD_SET( __wakeup_pipe[0], &__event_io_set[EVENT_IO_READ] ); if (__wakeup_pipe[0] > __event_max_fd) __event_max_fd = __wakeup_pipe[0]; __event_max_fd++; return TRUE; } /*********************************************************************** * X11DRV_EVENT_AddIO */ void X11DRV_EVENT_AddIO( int fd, unsigned io_type ) { FD_SET( fd, &__event_io_set[io_type] ); if( __event_max_fd <= fd ) __event_max_fd = fd + 1; } /*********************************************************************** * X11DRV_EVENT_DeleteIO */ void X11DRV_EVENT_DeleteIO( int fd, unsigned io_type ) { FD_CLR( fd, &__event_io_set[io_type] ); } /*********************************************************************** * X11DRV_EVENT_IsUserIdle */ BOOL16 X11DRV_EVENT_IsUserIdle(void) { struct timeval timeout = {0, 0}; fd_set check_set; FD_ZERO(&check_set); FD_SET(__event_x_connection, &check_set); if( select(__event_x_connection + 1, &check_set, NULL, NULL, &timeout) > 0 ) return TRUE; return FALSE; } /*********************************************************************** * EVENT_ReadWakeUpPipe * * Empty the wake up pipe */ void EVENT_ReadWakeUpPipe(void) { char tmpBuf[10]; ssize_t ret; EnterCriticalSection(&X11DRV_CritSection); /* Flush the wake-up pipe, it's just dummy data for waking-up this thread. This will be obsolete when the input thread will be done */ while ( (ret = read(__wakeup_pipe[0], &tmpBuf, 10)) == 10 ); LeaveCriticalSection(&X11DRV_CritSection); } /*********************************************************************** * X11DRV_EVENT_WaitNetEvent * * Wait for a network event, optionally sleeping until one arrives. * Returns TRUE if an event is pending that cannot be processed in * 'peek' mode, FALSE otherwise. */ BOOL X11DRV_EVENT_WaitNetEvent( BOOL sleep, BOOL peek ) { XEvent event; LONG maxWait = sleep ? TIMER_GetNextExpiration() : 0; int pending = TSXPending(display); /* Wait for an event or a timeout. If maxWait is -1, we have no timeout; * in this case, we fall through directly to the XNextEvent loop. */ if ((maxWait != -1) && !pending) { int num_pending; struct timeval timeout; fd_set io_set[3]; memcpy( io_set, __event_io_set, sizeof(io_set) ); timeout.tv_usec = (maxWait % 1000) * 1000; timeout.tv_sec = maxWait / 1000; #ifdef CONFIG_IPC sigsetjmp(env_wait_x, 1); stop_wait_op= CONT; if (DDE_GetRemoteMessage()) { while(DDE_GetRemoteMessage()) ; return FALSE; } stop_wait_op = STOP_WAIT_X; /* The code up to the next "stop_wait_op = CONT" must be reentrant */ num_pending = select( __event_max_fd, &io_set[EVENT_IO_READ], &io_set[EVENT_IO_WRITE], &io_set[EVENT_IO_EXCEPT], &timeout ); if ( num_pending == 0 ) { stop_wait_op = CONT; TIMER_ExpireTimers(); return FALSE; } else stop_wait_op = CONT; #else /* CONFIG_IPC */ num_pending = select( __event_max_fd, &io_set[EVENT_IO_READ], &io_set[EVENT_IO_WRITE], &io_set[EVENT_IO_EXCEPT], &timeout ); if ( num_pending == 0) { /* Timeout or error */ TIMER_ExpireTimers(); return FALSE; } #endif /* CONFIG_IPC */ /* Flush the wake-up pipe, it's just dummy data for waking-up this thread. This will be obsolete when the input thread will be done */ if ( FD_ISSET( __wakeup_pipe[0], &io_set[EVENT_IO_READ] ) ) { num_pending--; EVENT_ReadWakeUpPipe(); } /* Winsock asynchronous services */ if( FD_ISSET( __event_x_connection, &io_set[EVENT_IO_READ]) ) { num_pending--; if( num_pending ) WINSOCK_HandleIO( &__event_max_fd, num_pending, io_set, __event_io_set ); } else /* no X events */ { WINSOCK_HandleIO( &__event_max_fd, num_pending, io_set, __event_io_set ); return FALSE; } } else if(!pending) { /* Wait for X11 input. */ fd_set set; int max_fd; FD_ZERO(&set); FD_SET(__event_x_connection, &set); /* wait on wake-up pipe also */ FD_SET(__wakeup_pipe[0], &set); if (__event_x_connection > __wakeup_pipe[0]) max_fd = __event_x_connection + 1; else max_fd = __wakeup_pipe[0] + 1; select(max_fd, &set, 0, 0, 0 ); /* Flush the wake-up pipe, it's just dummy data for waking-up this thread. This will be obsolete when the input thread will be done */ if ( FD_ISSET( __wakeup_pipe[0], &set ) ) EVENT_ReadWakeUpPipe(); } /* Process current X event (and possibly others that occurred in the meantime) */ EnterCriticalSection(&X11DRV_CritSection); while (XPending( display )) { #ifdef CONFIG_IPC if (DDE_GetRemoteMessage()) { LeaveCriticalSection(&X11DRV_CritSection); while(DDE_GetRemoteMessage()) ; return FALSE; } #endif /* CONFIG_IPC */ XNextEvent( display, &event ); if( peek ) { /* Check only for those events which can be processed * internally. */ if( event.type == MotionNotify || event.type == ButtonPress || event.type == ButtonRelease || event.type == KeyPress || event.type == KeyRelease || event.type == SelectionRequest || event.type == SelectionClear || event.type == ClientMessage ) { LeaveCriticalSection(&X11DRV_CritSection); EVENT_ProcessEvent( &event ); EnterCriticalSection(&X11DRV_CritSection); continue; } if ( event.type == NoExpose ) continue; XPutBackEvent(display, &event); LeaveCriticalSection(&X11DRV_CritSection); return TRUE; } else { LeaveCriticalSection(&X11DRV_CritSection); EVENT_ProcessEvent( &event ); EnterCriticalSection(&X11DRV_CritSection); } } LeaveCriticalSection(&X11DRV_CritSection); return FALSE; } /*********************************************************************** * EVENT_Synchronize * * Synchronize with the X server. Should not be used too often. */ void X11DRV_EVENT_Synchronize() { XEvent event; /* Use of the X critical section is needed or we have a small * race between XPending() and XNextEvent(). */ EnterCriticalSection( &X11DRV_CritSection ); XSync( display, False ); while (XPending( display )) { XNextEvent( display, &event ); /* unlock X critsection for EVENT_ProcessEvent() might switch tasks */ LeaveCriticalSection( &X11DRV_CritSection ); EVENT_ProcessEvent( &event ); EnterCriticalSection( &X11DRV_CritSection ); } LeaveCriticalSection( &X11DRV_CritSection ); } /*********************************************************************** * EVENT_ProcessEvent * * Process an X event. */ static void EVENT_ProcessEvent( XEvent *event ) { WND *pWnd; HWND hWnd; switch (event->type) { case SelectionNotify: /* all of these should be caught by XCheckTypedWindowEvent() */ FIXME(event,"Got SelectionNotify - must not happen!\n"); /* fall through */ /* We get all these because of StructureNotifyMask. This check is placed here to avoid getting error messages below, as X might send some of these even for windows that have already been deleted ... */ case CirculateNotify: case CreateNotify: case DestroyNotify: case GravityNotify: case ReparentNotify: return; } if ( TSXFindContext( display, event->xany.window, winContext, (char **)&pWnd ) != 0) { if ( event->type == ClientMessage) { /* query window (drag&drop event contains only drag window) */ Window root, child; int root_x, root_y, child_x, child_y; unsigned u; TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child, &root_x, &root_y, &child_x, &child_y, &u); if (TSXFindContext( display, child, winContext, (char **)&pWnd ) != 0) return; } else { pWnd = NULL; /* Not for a registered window */ } } WIN_LockWndPtr(pWnd); if(!pWnd) hWnd = 0; else hWnd = pWnd->hwndSelf; if ( !pWnd && event->xany.window != X11DRV_GetXRootWindow() ) ERR( event, "Got event %s for unknown Window %08lx\n", event_names[event->type], event->xany.window ); else TRACE( event, "Got event %s for hwnd %04x\n", event_names[event->type], hWnd ); WIN_ReleaseWndPtr(pWnd); switch(event->type) { case KeyPress: case KeyRelease: EVENT_Key( hWnd, (XKeyEvent*)event ); break; case ButtonPress: EVENT_ButtonPress( hWnd, (XButtonEvent*)event ); break; case ButtonRelease: EVENT_ButtonRelease( hWnd, (XButtonEvent*)event ); break; case MotionNotify: /* Wine between two fast machines across the overloaded campus ethernet gets very boged down in MotionEvents. The following simply finds the last motion event in the queue and drops the rest. On a good link events are servered before they build up so this doesn't take place. On a slow link this may cause problems if the event order is important. I'm not yet seen of any problems. Jon 7/6/96. */ while (TSXCheckTypedWindowEvent(display,((XAnyEvent *)event)->window, MotionNotify, event)); EVENT_MotionNotify( hWnd, (XMotionEvent*)event ); break; case FocusIn: if (!hWnd) return; EVENT_FocusIn( hWnd, (XFocusChangeEvent*)event ); break; case FocusOut: if (!hWnd) return; EVENT_FocusOut( hWnd, (XFocusChangeEvent*)event ); break; case Expose: EVENT_Expose( hWnd, (XExposeEvent *)event ); break; case GraphicsExpose: EVENT_GraphicsExpose( hWnd, (XGraphicsExposeEvent *)event ); break; case ConfigureNotify: if (!hWnd) return; EVENT_ConfigureNotify( hWnd, (XConfigureEvent*)event ); break; case SelectionRequest: if (!hWnd) return; EVENT_SelectionRequest( hWnd, (XSelectionRequestEvent *)event ); break; case SelectionClear: if (!hWnd) return; EVENT_SelectionClear( hWnd, (XSelectionClearEvent*) event ); break; case ClientMessage: if (!hWnd) return; EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event ); break; #if 0 case EnterNotify: EVENT_EnterNotify( hWnd, (XCrossingEvent *) event ); break; #endif case NoExpose: break; case MapNotify: if (!hWnd) return; EVENT_MapNotify( hWnd, (XMapEvent *)event ); break; case UnmapNotify: if (!hWnd) return; EVENT_UnmapNotify( hWnd, (XUnmapEvent *)event ); break; default: WARN(event, "Unprocessed event %s for hwnd %04x\n", event_names[event->type], hWnd ); break; } } /*********************************************************************** * EVENT_QueryZOrder * * Synchronize internal z-order with the window manager's. */ static BOOL __check_query_condition( WND** pWndA, WND** pWndB ) { /* return TRUE if we have at least two managed windows */ for( *pWndB = NULL; *pWndA; *pWndA = (*pWndA)->next ) if( (*pWndA)->flags & WIN_MANAGED && (*pWndA)->dwStyle & WS_VISIBLE ) break; if( *pWndA ) for( *pWndB = (*pWndA)->next; *pWndB; *pWndB = (*pWndB)->next ) if( (*pWndB)->flags & WIN_MANAGED && (*pWndB)->dwStyle & WS_VISIBLE ) break; return ((*pWndB) != NULL); } static Window __get_common_ancestor( Window A, Window B, Window** children, unsigned* total ) { /* find the real root window */ Window root, *childrenB; unsigned totalB; do { TSXQueryTree( display, A, &root, &A, children, total ); TSXQueryTree( display, B, &root, &B, &childrenB, &totalB ); if( childrenB ) TSXFree( childrenB ); if( *children ) TSXFree( *children ); } while( A != B && A && B ); if( A && B ) { TSXQueryTree( display, A, &root, &B, children, total ); return A; } return 0 ; } static Window __get_top_decoration( Window w, Window ancestor ) { Window* children, root, prev = w, parent = w; unsigned total; do { w = parent; TSXQueryTree( display, w, &root, &parent, &children, &total ); if( children ) TSXFree( children ); } while( parent && parent != ancestor ); TRACE(event, "\t%08x -> %08x\n", (unsigned)prev, (unsigned)w ); return ( parent ) ? w : 0 ; } static unsigned __td_lookup( Window w, Window* list, unsigned max ) { unsigned i; for( i = max - 1; i >= 0; i-- ) if( list[i] == w ) break; return i; } static BOOL EVENT_QueryZOrder( HWND hWndCheck) { BOOL bRet = FALSE; HWND hwndInsertAfter = HWND_TOP; WND *pWndCheck = WIN_FindWndPtr(hWndCheck); WND *pDesktop = WIN_GetDesktop(); WND *pWnd, *pWndZ = WIN_LockWndPtr(pDesktop->child); Window w, parent, *children = NULL; unsigned total, check, pos, best; if( !__check_query_condition(&pWndZ, &pWnd) ) { WIN_ReleaseWndPtr(pWndCheck); WIN_ReleaseWndPtr(pDesktop->child); WIN_ReleaseDesktop(); return TRUE; } WIN_LockWndPtr(pWndZ); WIN_LockWndPtr(pWnd); WIN_ReleaseWndPtr(pDesktop->child); WIN_ReleaseDesktop(); parent = __get_common_ancestor( X11DRV_WND_GetXWindow(pWndZ), X11DRV_WND_GetXWindow(pWnd), &children, &total ); if( parent && children ) { /* w is the ancestor if pWndCheck that is a direct descendant of 'parent' */ w = __get_top_decoration( X11DRV_WND_GetXWindow(pWndCheck), parent ); if( w != children[total-1] ) /* check if at the top */ { /* X child at index 0 is at the bottom, at index total-1 is at the top */ check = __td_lookup( w, children, total ); best = total; for( WIN_UpdateWndPtr(&pWnd,pWndZ); pWnd;WIN_UpdateWndPtr(&pWnd,pWnd->next)) { /* go through all windows in Wine z-order... */ if( pWnd != pWndCheck ) { if( !(pWnd->flags & WIN_MANAGED) || !(w = __get_top_decoration( X11DRV_WND_GetXWindow(pWnd), parent )) ) continue; pos = __td_lookup( w, children, total ); if( pos < best && pos > check ) { /* find a nearest Wine window precedes * pWndCheck in the real z-order... */ best = pos; hwndInsertAfter = pWnd->hwndSelf; } if( best - check == 1 ) break; } } /* and link pWndCheck right behind it in the local z-order */ } WIN_UnlinkWindow( pWndCheck->hwndSelf ); WIN_LinkWindow( pWndCheck->hwndSelf, hwndInsertAfter); } if( children ) TSXFree( children ); WIN_ReleaseWndPtr(pWnd); WIN_ReleaseWndPtr(pWndZ); WIN_ReleaseWndPtr(pWndCheck); return bRet; } /*********************************************************************** * EVENT_XStateToKeyState * * Translate a X event state (Button1Mask, ShiftMask, etc...) to * a Windows key state (MK_SHIFT, MK_CONTROL, etc...) */ static WORD EVENT_XStateToKeyState( int state ) { int kstate = 0; if (state & Button1Mask) kstate |= MK_LBUTTON; if (state & Button2Mask) kstate |= MK_MBUTTON; if (state & Button3Mask) kstate |= MK_RBUTTON; if (state & ShiftMask) kstate |= MK_SHIFT; if (state & ControlMask) kstate |= MK_CONTROL; return kstate; } /*********************************************************************** * X11DRV_EVENT_QueryPointer */ BOOL X11DRV_EVENT_QueryPointer(DWORD *posX, DWORD *posY, DWORD *state) { Window root, child; int rootX, rootY, winX, winY; unsigned int xstate; if (!TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child, &rootX, &rootY, &winX, &winY, &xstate )) return FALSE; if(posX) *posX = (DWORD)winX; if(posY) *posY = (DWORD)winY; if(state) *state = EVENT_XStateToKeyState( xstate ); return TRUE; } /*********************************************************************** * EVENT_Expose */ static void EVENT_Expose( HWND hWnd, XExposeEvent *event ) { RECT rect; WND *pWnd = WIN_FindWndPtr(hWnd); /* Make position relative to client area instead of window */ rect.left = event->x - (pWnd? (pWnd->rectClient.left - pWnd->rectWindow.left) : 0); rect.top = event->y - (pWnd? (pWnd->rectClient.top - pWnd->rectWindow.top) : 0); rect.right = rect.left + event->width; rect.bottom = rect.top + event->height; WIN_ReleaseWndPtr(pWnd); Callout.RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASE | (event->count ? 0 : RDW_ERASENOW) ); } /*********************************************************************** * EVENT_GraphicsExpose * * This is needed when scrolling area is partially obscured * by non-Wine X window. */ static void EVENT_GraphicsExpose( HWND hWnd, XGraphicsExposeEvent *event ) { RECT rect; WND *pWnd = WIN_FindWndPtr(hWnd); /* Make position relative to client area instead of window */ rect.left = event->x - (pWnd? (pWnd->rectClient.left - pWnd->rectWindow.left) : 0); rect.top = event->y - (pWnd? (pWnd->rectClient.top - pWnd->rectWindow.top) : 0); rect.right = rect.left + event->width; rect.bottom = rect.top + event->height; WIN_ReleaseWndPtr(pWnd); Callout.RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE | (event->count ? 0 : RDW_ERASENOW) ); } /*********************************************************************** * EVENT_Key * * Handle a X key event */ static void EVENT_Key( HWND hWnd, XKeyEvent *event ) { WND *pWnd = WIN_FindWndPtr(hWnd); X11DRV_KEYBOARD_HandleEvent( pWnd, event ); WIN_ReleaseWndPtr(pWnd); } /*********************************************************************** * EVENT_MotionNotify */ static void EVENT_MotionNotify( HWND hWnd, XMotionEvent *event ) { WND *pWnd = WIN_FindWndPtr(hWnd); int xOffset = pWnd? pWnd->rectWindow.left : 0; int yOffset = pWnd? pWnd->rectWindow.top : 0; WIN_ReleaseWndPtr(pWnd); MOUSE_SendEvent( MOUSEEVENTF_MOVE, xOffset + event->x, yOffset + event->y, EVENT_XStateToKeyState( event->state ), event->time - MSG_WineStartTicks, hWnd); } /*********************************************************************** * X11DRV_EVENT_DummyMotionNotify * * Generate a dummy MotionNotify event. Used to force a WM_SETCURSOR message. */ void X11DRV_EVENT_DummyMotionNotify(void) { DWORD winX, winY, state; if ( EVENT_QueryPointer( &winX, &winY, &state ) ) { MOUSE_SendEvent( MOUSEEVENTF_MOVE, winX, winY, state, GetTickCount(), 0 ); } } /*********************************************************************** * EVENT_ButtonPress */ static void EVENT_ButtonPress( HWND hWnd, XButtonEvent *event ) { static WORD statusCodes[NB_BUTTONS] = { MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN }; int buttonNum = event->button - 1; WND *pWnd = WIN_FindWndPtr(hWnd); int xOffset = pWnd? pWnd->rectWindow.left : 0; int yOffset = pWnd? pWnd->rectWindow.top : 0; WORD keystate; WIN_ReleaseWndPtr(pWnd); if (buttonNum >= NB_BUTTONS) return; /* * Get the compatible keystate */ keystate = EVENT_XStateToKeyState( event->state ); /* * Make sure that the state of the button that was just * pressed is "down". */ switch (buttonNum) { case 0: keystate |= MK_LBUTTON; break; case 1: keystate |= MK_MBUTTON; break; case 2: keystate |= MK_RBUTTON; break; } MOUSE_SendEvent( statusCodes[buttonNum], xOffset + event->x, yOffset + event->y, keystate, event->time - MSG_WineStartTicks, hWnd); } /*********************************************************************** * EVENT_ButtonRelease */ static void EVENT_ButtonRelease( HWND hWnd, XButtonEvent *event ) { static WORD statusCodes[NB_BUTTONS] = { MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP }; int buttonNum = event->button - 1; WND *pWnd = WIN_FindWndPtr(hWnd); int xOffset = pWnd? pWnd->rectWindow.left : 0; int yOffset = pWnd? pWnd->rectWindow.top : 0; WORD keystate; WIN_ReleaseWndPtr(pWnd); if (buttonNum >= NB_BUTTONS) return; /* * Get the compatible keystate */ keystate = EVENT_XStateToKeyState( event->state ); /* * Make sure that the state of the button that was just * released is "up". */ switch (buttonNum) { case 0: keystate &= ~MK_LBUTTON; break; case 1: keystate &= ~MK_MBUTTON; break; case 2: keystate &= ~MK_RBUTTON; break; } MOUSE_SendEvent( statusCodes[buttonNum], xOffset + event->x, yOffset + event->y, keystate, event->time - MSG_WineStartTicks, hWnd); } /********************************************************************** * EVENT_FocusIn */ static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event ) { if (Options.managed) EVENT_QueryZOrder( hWnd ); if (event->detail != NotifyPointer) { if (hWnd != GetActiveWindow()) { WINPOS_ChangeActiveWindow( hWnd, FALSE ); X11DRV_KEYBOARD_UpdateState(); } if ((hWnd != GetFocus()) && !IsChild( hWnd, GetFocus())) SetFocus( hWnd ); } } /********************************************************************** * EVENT_FocusOut * * Note: only top-level override-redirect windows get FocusOut events. */ static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event ) { if (event->detail != NotifyPointer) { if (hWnd == GetActiveWindow()) { SendMessageA( hWnd, WM_CANCELMODE, 0, 0 ); WINPOS_ChangeActiveWindow( 0, FALSE ); } if ((hWnd == GetFocus()) || IsChild( hWnd, GetFocus())) SetFocus( 0 ); } } /********************************************************************** * X11DRV_EVENT_CheckFocus */ BOOL X11DRV_EVENT_CheckFocus(void) { WND* pWnd; Window xW; int state; TSXGetInputFocus(display, &xW, &state); if( xW == None || TSXFindContext(display, xW, winContext, (char **)&pWnd) ) return FALSE; return TRUE; } /********************************************************************** * EVENT_GetGeometry * * Helper function for ConfigureNotify handling. * Get the new geometry of a window relative to the root window. */ static void EVENT_GetGeometry( Window win, int *px, int *py, unsigned int *pwidth, unsigned int *pheight ) { Window root, parent, *children; int xpos, ypos; unsigned int width, height, border, depth, nb_children; if (!TSXGetGeometry( display, win, &root, px, py, pwidth, pheight, &border, &depth )) return; if (win == X11DRV_GetXRootWindow()) { *px = *py = 0; return; } for (;;) { if (!TSXQueryTree(display, win, &root, &parent, &children, &nb_children)) return; TSXFree( children ); if (parent == X11DRV_GetXRootWindow()) break; win = parent; if (!TSXGetGeometry( display, win, &root, &xpos, &ypos, &width, &height, &border, &depth )) return; *px += xpos; *py += ypos; } } /********************************************************************** * EVENT_ConfigureNotify * * The ConfigureNotify event is only selected on top-level windows * when the -managed flag is used. */ static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *event ) { WINDOWPOS winpos; RECT newWindowRect, newClientRect; RECT oldWindowRect, oldClientRect; Window above = event->above; int x, y; unsigned int width, height; WND *pWnd = WIN_FindWndPtr(hWnd); assert (pWnd->flags & WIN_MANAGED); /* We don't rely on the event geometry info, because it is relative * to parent and not to root, and it may be wrong (XFree sets x,y to 0,0 * if the window hasn't moved). */ EVENT_GetGeometry( event->window, &x, &y, &width, &height ); TRACE(win, "%04x adjusted to (%i,%i)-(%i,%i)\n", pWnd->hwndSelf, x, y, x + width, y + height ); /* Fill WINDOWPOS struct */ winpos.flags = SWP_NOACTIVATE | SWP_NOZORDER; winpos.hwnd = hWnd; winpos.x = x; winpos.y = y; winpos.cx = width; winpos.cy = height; /* Check for unchanged attributes */ if (winpos.x == pWnd->rectWindow.left && winpos.y == pWnd->rectWindow.top) winpos.flags |= SWP_NOMOVE; if ((winpos.cx == pWnd->rectWindow.right - pWnd->rectWindow.left) && (winpos.cy == pWnd->rectWindow.bottom - pWnd->rectWindow.top)) winpos.flags |= SWP_NOSIZE; else { RECT rect = { 0, 0, pWnd->rectWindow.right - pWnd->rectWindow.left, pWnd->rectWindow.bottom - pWnd->rectWindow.top }; DCE_InvalidateDCE( pWnd, &rect ); } /* Send WM_WINDOWPOSCHANGING */ SendMessageA( winpos.hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)&winpos ); if (!IsWindow( winpos.hwnd )) { WIN_ReleaseWndPtr(pWnd); return; } /* Calculate new position and size */ newWindowRect.left = x; newWindowRect.right = x + width; newWindowRect.top = y; newWindowRect.bottom = y + height; WINPOS_SendNCCalcSize( winpos.hwnd, TRUE, &newWindowRect, &pWnd->rectWindow, &pWnd->rectClient, &winpos, &newClientRect ); if (!IsWindow( winpos.hwnd )) { WIN_ReleaseWndPtr(pWnd); return; } oldWindowRect = pWnd->rectWindow; oldClientRect = pWnd->rectClient; /* Set new size and position */ pWnd->rectWindow = newWindowRect; pWnd->rectClient = newClientRect; /* FIXME: Copy valid bits */ if( oldClientRect.top - oldWindowRect.top != newClientRect.top - newWindowRect.top || oldClientRect.left - oldWindowRect.left != newClientRect.left - newWindowRect.left ) RedrawWindow( winpos.hwnd, NULL, 0, RDW_FRAME | RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_ERASE | RDW_ERASENOW ); WIN_ReleaseWndPtr(pWnd); SendMessageA( winpos.hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)&winpos ); if (!IsWindow( winpos.hwnd )) return; if( above == None ) /* absolute bottom */ { WIN_UnlinkWindow( winpos.hwnd ); WIN_LinkWindow( winpos.hwnd, HWND_BOTTOM); } else EVENT_QueryZOrder( hWnd ); /* try to outsmart window manager */ } /*********************************************************************** * EVENT_SelectionRequest */ static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event ) { XSelectionEvent result; Atom rprop = None; Window request = event->requestor; if(event->target == XA_STRING) { HANDLE16 hText; LPSTR text; int size,i,j; rprop = event->property; if( rprop == None ) rprop = event->target; if( event->selection != XA_PRIMARY ) rprop = None; else if( !CLIPBOARD_IsPresent(CF_OEMTEXT) ) rprop = None; else { /* open to make sure that clipboard is available */ BOOL couldOpen = OpenClipboard( hWnd ); char* lpstr = 0; hText = GetClipboardData16(CF_TEXT); text = GlobalLock16(hText); size = GlobalSize16(hText); /* remove carriage returns */ lpstr = (char*)HEAP_xalloc( GetProcessHeap(), 0, size-- ); for(i=0,j=0; i < size && text[i]; i++ ) { if( text[i] == '\r' && (text[i+1] == '\n' || text[i+1] == '\0') ) continue; lpstr[j++] = text[i]; } lpstr[j]='\0'; TSXChangeProperty(display, request, rprop, XA_STRING, 8, PropModeReplace, lpstr, j); HeapFree( GetProcessHeap(), 0, lpstr ); /* close only if we opened before */ if(couldOpen) CloseClipboard(); } } if( rprop == None) TRACE(event,"Request for %s ignored\n", TSXGetAtomName(display,event->target)); /* reply to sender */ result.type = SelectionNotify; result.display = display; result.requestor = request; result.selection = event->selection; result.property = rprop; result.target = event->target; result.time = event->time; TSXSendEvent(display,event->requestor,False,NoEventMask,(XEvent*)&result); } /*********************************************************************** * EVENT_SelectionClear */ static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event ) { if (event->selection != XA_PRIMARY) return; X11DRV_CLIPBOARD_ReleaseSelection( event->window, hWnd ); } /********************************************************************** * EVENT_DropFromOffix * * don't know if it still works (last Changlog is from 96/11/04) */ static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event ) { unsigned long data_length; unsigned long aux_long; unsigned char* p_data = NULL; union { Atom atom_aux; struct { int x; int y; } pt_aux; int i; } u; int x, y; BOOL16 bAccept; HGLOBAL16 hDragInfo = GlobalAlloc16( GMEM_SHARE | GMEM_ZEROINIT, sizeof(DRAGINFO)); LPDRAGINFO lpDragInfo = (LPDRAGINFO) GlobalLock16(hDragInfo); SEGPTR spDragInfo = (SEGPTR) WIN16_GlobalLock16(hDragInfo); Window w_aux_root, w_aux_child; WND* pDropWnd; WND* pWnd; if( !lpDragInfo || !spDragInfo ) return; pWnd = WIN_FindWndPtr(hWnd); TSXQueryPointer( display, X11DRV_WND_GetXWindow(pWnd), &w_aux_root, &w_aux_child, &x, &y, (int *) &u.pt_aux.x, (int *) &u.pt_aux.y, (unsigned int*)&aux_long); lpDragInfo->hScope = hWnd; lpDragInfo->pt.x = (INT16)x; lpDragInfo->pt.y = (INT16)y; /* find out drop point and drop window */ if( x < 0 || y < 0 || x > (pWnd->rectWindow.right - pWnd->rectWindow.left) || y > (pWnd->rectWindow.bottom - pWnd->rectWindow.top) ) { bAccept = pWnd->dwExStyle & WS_EX_ACCEPTFILES; x = y = 0; } else { bAccept = DRAG_QueryUpdate( hWnd, spDragInfo, TRUE ); x = lpDragInfo->pt.x; y = lpDragInfo->pt.y; } pDropWnd = WIN_FindWndPtr( lpDragInfo->hScope ); WIN_ReleaseWndPtr(pWnd); GlobalFree16( hDragInfo ); if( bAccept ) { TSXGetWindowProperty( display, DefaultRootWindow(display), dndSelection, 0, 65535, FALSE, AnyPropertyType, &u.atom_aux, (int *) &u.pt_aux.y, &data_length, &aux_long, &p_data); if( !aux_long && p_data) /* don't bother if > 64K */ { char *p = (char*) p_data; char *p_drop; aux_long = 0; while( *p ) /* calculate buffer size */ { p_drop = p; if((u.i = *p) != -1 ) u.i = DRIVE_FindDriveRoot( (const char **)&p_drop ); if( u.i == -1 ) *p = -1; /* mark as "bad" */ else { INT len = GetShortPathNameA( p, NULL, 0 ); if (len) aux_long += len + 1; else *p = -1; } p += strlen(p) + 1; } if( aux_long && aux_long < 65535 ) { HDROP16 hDrop; LPDROPFILESTRUCT16 lpDrop; aux_long += sizeof(DROPFILESTRUCT16) + 1; hDrop = (HDROP16)GlobalAlloc16( GMEM_SHARE, aux_long ); lpDrop = (LPDROPFILESTRUCT16) GlobalLock16( hDrop ); if( lpDrop ) { lpDrop->wSize = sizeof(DROPFILESTRUCT16); lpDrop->ptMousePos.x = (INT16)x; lpDrop->ptMousePos.y = (INT16)y; lpDrop->fInNonClientArea = (BOOL16) ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left) || y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top) || x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) || y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) ); p_drop = ((char*)lpDrop) + sizeof(DROPFILESTRUCT16); p = p_data; while(*p) { if( *p != -1 ) /* use only "good" entries */ { GetShortPathNameA( p, p_drop, 65535 ); p_drop += strlen( p_drop ) + 1; } p += strlen(p) + 1; } *p_drop = '\0'; PostMessage16( hWnd, WM_DROPFILES, (WPARAM16)hDrop, 0L ); } } } if( p_data ) TSXFree(p_data); } /* WS_EX_ACCEPTFILES */ WIN_ReleaseWndPtr(pDropWnd); } /********************************************************************** * EVENT_DropURLs * * drop items are separated by \n * each item is prefixed by its mime type * * event->data.l[3], event->data.l[4] contains drop x,y position */ static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event ) { WND *pDropWnd; WND *pWnd; unsigned long data_length; unsigned long aux_long, drop_len = 0; unsigned char *p_data = NULL; /* property data */ char *p_drop = NULL; char *p, *next; int x, y, drop32 = FALSE ; union { Atom atom_aux; int i; Window w_aux; } u; /* unused */ union { HDROP16 h16; HDROP h32; } hDrop; pWnd = WIN_FindWndPtr(hWnd); drop32 = pWnd->flags & WIN_ISWIN32; if (!(pWnd->dwExStyle & WS_EX_ACCEPTFILES)) { WIN_ReleaseWndPtr(pWnd); return; } WIN_ReleaseWndPtr(pWnd); TSXGetWindowProperty( display, DefaultRootWindow(display), dndSelection, 0, 65535, FALSE, AnyPropertyType, &u.atom_aux, &u.i, &data_length, &aux_long, &p_data); if (aux_long) WARN(event,"property too large, truncated!\n"); TRACE(event,"urls=%s\n", p_data); if( !aux_long && p_data) { /* don't bother if > 64K */ /* calculate length */ p = p_data; next = strchr(p, '\n'); while (p) { if (next) *next=0; if (strncmp(p,"file:",5) == 0 ) { INT len = GetShortPathNameA( p+5, NULL, 0 ); if (len) drop_len += len + 1; } if (next) { *next = '\n'; p = next + 1; next = strchr(p, '\n'); } else { p = NULL; } } if( drop_len && drop_len < 65535 ) { TSXQueryPointer( display, X11DRV_GetXRootWindow(), &u.w_aux, &u.w_aux, &x, &y, &u.i, &u.i, &u.i); pDropWnd = WIN_FindWndPtr( hWnd ); if (drop32) { LPDROPFILESTRUCT lpDrop; drop_len += sizeof(DROPFILESTRUCT) + 1; hDrop.h32 = (HDROP)GlobalAlloc( GMEM_SHARE, drop_len ); lpDrop = (LPDROPFILESTRUCT) GlobalLock( hDrop.h32 ); if( lpDrop ) { lpDrop->lSize = sizeof(DROPFILESTRUCT); lpDrop->ptMousePos.x = (INT)x; lpDrop->ptMousePos.y = (INT)y; lpDrop->fInNonClientArea = (BOOL) ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left) || y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top) || x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) || y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) ); lpDrop->fWideChar = FALSE; p_drop = ((char*)lpDrop) + sizeof(DROPFILESTRUCT); } } else { LPDROPFILESTRUCT16 lpDrop; drop_len += sizeof(DROPFILESTRUCT16) + 1; hDrop.h16 = (HDROP16)GlobalAlloc16( GMEM_SHARE, drop_len ); lpDrop = (LPDROPFILESTRUCT16) GlobalLock16( hDrop.h16 ); if( lpDrop ) { lpDrop->wSize = sizeof(DROPFILESTRUCT16); lpDrop->ptMousePos.x = (INT16)x; lpDrop->ptMousePos.y = (INT16)y; lpDrop->fInNonClientArea = (BOOL16) ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left) || y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top) || x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) || y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) ); p_drop = ((char*)lpDrop) + sizeof(DROPFILESTRUCT16); } } /* create message content */ if (p_drop) { p = p_data; next = strchr(p, '\n'); while (p) { if (next) *next=0; if (strncmp(p,"file:",5) == 0 ) { INT len = GetShortPathNameA( p+5, p_drop, 65535 ); if (len) { TRACE(event, "drop file %s as %s\n", p+5, p_drop); p_drop += len+1; } else { WARN(event, "can't convert file %s to dos name \n", p+5); } } else { WARN(event, "unknown mime type %s\n", p); } if (next) { *next = '\n'; p = next + 1; next = strchr(p, '\n'); } else { p = NULL; } *p_drop = '\0'; } if (drop32) { /* can not use PostMessage32A because it is currently based on * PostMessage16 and WPARAM32 would be truncated to WPARAM16 */ GlobalUnlock(hDrop.h32); SendMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop.h32, 0L ); } else { GlobalUnlock16(hDrop.h16); PostMessage16( hWnd, WM_DROPFILES, (WPARAM16)hDrop.h16, 0L ); } } WIN_ReleaseWndPtr(pDropWnd); } if( p_data ) TSXFree(p_data); } } /********************************************************************** * EVENT_ClientMessage */ static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event ) { if (event->message_type != None && event->format == 32) { if ((event->message_type == wmProtocols) && (((Atom) event->data.l[0]) == wmDeleteWindow)) SendMessage16( hWnd, WM_SYSCOMMAND, SC_CLOSE, 0 ); else if ( event->message_type == dndProtocol && (event->data.l[0] == DndFile || event->data.l[0] == DndFiles) ) EVENT_DropFromOffiX(hWnd, event); else if ( event->message_type == dndProtocol && event->data.l[0] == DndURL ) EVENT_DropURLs(hWnd, event); else { #if 0 /* enable this if you want to see the message */ unsigned char* p_data = NULL; union { unsigned long l; int i; Atom atom; } u; /* unused */ TSXGetWindowProperty( display, DefaultRootWindow(display), dndSelection, 0, 65535, FALSE, AnyPropertyType, &u.atom, &u.i, &u.l, &u.l, &p_data); TRACE(event, "message_type=%ld, data=%ld,%ld,%ld,%ld,%ld, msg=%s\n", event->message_type, event->data.l[0], event->data.l[1], event->data.l[2], event->data.l[3], event->data.l[4], p_data); #endif TRACE(event, "unrecognized ClientMessage\n" ); } } } /********************************************************************** * EVENT_EnterNotify * * Install colormap when Wine window is focused in * self-managed mode with private colormap */ #if 0 void EVENT_EnterNotify( HWND hWnd, XCrossingEvent *event ) { if( !Options.managed && X11DRV_GetXRootWindow() == DefaultRootWindow(display) && (COLOR_GetSystemPaletteFlags() & COLOR_PRIVATE) && GetFocus() ) TSXInstallColormap( display, X11DRV_PALETTE_GetColormap() ); } #endif /********************************************************************** * EVENT_MapNotify */ void EVENT_MapNotify( HWND hWnd, XMapEvent *event ) { HWND hwndFocus = GetFocus(); WND *wndFocus = WIN_FindWndPtr(hwndFocus); WND *pWnd = WIN_FindWndPtr(hWnd); if (pWnd->flags & WIN_MANAGED) pWnd->dwStyle &= ~WS_MINIMIZE; WIN_ReleaseWndPtr(pWnd); if (hwndFocus && IsChild( hWnd, hwndFocus )) X11DRV_WND_SetFocus(wndFocus); WIN_ReleaseWndPtr(wndFocus); return; } /********************************************************************** * EVENT_MapNotify */ void EVENT_UnmapNotify( HWND hWnd, XUnmapEvent *event ) { WND *pWnd = WIN_FindWndPtr(hWnd); if (pWnd->flags & WIN_MANAGED) { EndMenu(); pWnd->dwStyle |= WS_MINIMIZE; } WIN_ReleaseWndPtr(pWnd); } /********************************************************************** * X11DRV_EVENT_Pending */ BOOL X11DRV_EVENT_Pending() { return TSXPending(display); } /********************************************************************** * X11DRV_EVENT_WakeUp */ void X11DRV_EVENT_WakeUp(void) { /* wake-up EVENT_WaitNetEvent function, a 32 bit thread post an event for a 16 bit task */ if (write (__wakeup_pipe[1], "A", 1) != 1) ERR(event, "unable to write in wakeup_pipe\n"); } #endif /* !defined(X_DISPLAY_MISSING) */