| /* | |
| * X11 windows driver | |
| * | |
| * Copyright 1993, 1994, 1995, 1996 Alexandre Julliard | |
| * 1993 David Metcalfe | |
| * 1995, 1996 Alex Korobka | |
| */ | |
| #include "config.h" | |
| #include <X11/Xatom.h> | |
| #include "ts_xlib.h" | |
| #include "ts_xutil.h" | |
| #include "ts_shape.h" | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "bitmap.h" | |
| #include "debugtools.h" | |
| #include "gdi.h" | |
| #include "options.h" | |
| #include "message.h" | |
| #include "win.h" | |
| #include "windef.h" | |
| #include "x11drv.h" | |
| #include "wingdi.h" | |
| #include "winnls.h" | |
| #include "wine/winuser16.h" | |
| DEFAULT_DEBUG_CHANNEL(win); | |
| extern Atom wmChangeState; | |
| #define HAS_DLGFRAME(style,exStyle) \ | |
| ((!((style) & WS_THICKFRAME)) && (((style) & WS_DLGFRAME) || ((exStyle) & WS_EX_DLGMODALFRAME))) | |
| /**********************************************************************/ | |
| WND_DRIVER X11DRV_WND_Driver = | |
| { | |
| X11DRV_WND_ForceWindowRaise, | |
| X11DRV_WND_SetHostAttr | |
| }; | |
| /*********************************************************************** | |
| * X11DRV_WND_GetXWindow | |
| * | |
| * Return the X window associated to a window. | |
| */ | |
| Window X11DRV_WND_GetXWindow(WND *wndPtr) | |
| { | |
| return wndPtr && wndPtr->pDriverData ? | |
| ((X11DRV_WND_DATA *) wndPtr->pDriverData)->window : 0; | |
| } | |
| /*********************************************************************** | |
| * X11DRV_WND_FindXWindow | |
| * | |
| * Return the the first X window associated to a window chain. | |
| */ | |
| Window X11DRV_WND_FindXWindow(WND *wndPtr) | |
| { | |
| while (wndPtr && | |
| !((X11DRV_WND_DATA *) wndPtr->pDriverData)->window) | |
| wndPtr = wndPtr->parent; | |
| return wndPtr ? | |
| ((X11DRV_WND_DATA *) wndPtr->pDriverData)->window : 0; | |
| } | |
| /*********************************************************************** | |
| * X11DRV_WND_IsZeroSizeWnd | |
| * | |
| * Return TRUE if the window has a height or widht less or equal to 0 | |
| */ | |
| static BOOL X11DRV_WND_IsZeroSizeWnd(WND *wndPtr) | |
| { | |
| if ( (wndPtr->rectWindow.left >= wndPtr->rectWindow.right) || | |
| (wndPtr->rectWindow.top >= wndPtr->rectWindow.bottom) ) | |
| return TRUE; | |
| else | |
| return FALSE; | |
| } | |
| /*********************************************************************** | |
| * X11DRV_WND_ForceWindowRaise | |
| * | |
| * Raise a window on top of the X stacking order, while preserving | |
| * the correct Windows Z order. | |
| */ | |
| void X11DRV_WND_ForceWindowRaise(WND *wndPtr) | |
| { | |
| XWindowChanges winChanges; | |
| WND *wndPrev,*pDesktop = WIN_GetDesktop(); | |
| if (X11DRV_WND_IsZeroSizeWnd(wndPtr)) | |
| { | |
| WIN_ReleaseDesktop(); | |
| return; | |
| } | |
| if( !wndPtr || !X11DRV_WND_GetXWindow(wndPtr) || (wndPtr->dwExStyle & WS_EX_MANAGED) ) | |
| { | |
| WIN_ReleaseDesktop(); | |
| return; | |
| } | |
| /* Raise all windows up to wndPtr according to their Z order. | |
| * (it would be easier with sibling-related Below but it doesn't | |
| * work very well with SGI mwm for instance) | |
| */ | |
| winChanges.stack_mode = Above; | |
| while (wndPtr) | |
| { | |
| if ( !X11DRV_WND_IsZeroSizeWnd(wndPtr) && X11DRV_WND_GetXWindow(wndPtr) ) | |
| TSXReconfigureWMWindow( display, X11DRV_WND_GetXWindow(wndPtr), 0, | |
| CWStackMode, &winChanges ); | |
| wndPrev = pDesktop->child; | |
| if (wndPrev == wndPtr) break; | |
| while (wndPrev && (wndPrev->next != wndPtr)) wndPrev = wndPrev->next; | |
| wndPtr = wndPrev; | |
| } | |
| WIN_ReleaseDesktop(); | |
| } | |
| /*********************************************************************** | |
| * X11DRV_WND_FindDesktopXWindow [Internal] | |
| * | |
| * Find the actual X window which needs be restacked. | |
| * Used by X11DRV_WND_SetWindowPos(). | |
| */ | |
| static Window X11DRV_WND_FindDesktopXWindow( WND *wndPtr ) | |
| { | |
| if (!(wndPtr->dwExStyle & WS_EX_MANAGED)) | |
| return X11DRV_WND_GetXWindow(wndPtr); | |
| else | |
| { | |
| Window window, root, parent, *children; | |
| int nchildren; | |
| window = X11DRV_WND_GetXWindow(wndPtr); | |
| for (;;) | |
| { | |
| TSXQueryTree( display, window, &root, &parent, | |
| &children, &nchildren ); | |
| TSXFree( children ); | |
| if (parent == root) | |
| return window; | |
| window = parent; | |
| } | |
| } | |
| } | |
| /*********************************************************************** | |
| * WINPOS_SetXWindowPos | |
| * | |
| * SetWindowPos() for an X window. Used by the real SetWindowPos(). | |
| */ | |
| void X11DRV_WND_SetWindowPos(WND *wndPtr, const WINDOWPOS *winpos, BOOL bChangePos) | |
| { | |
| XWindowChanges winChanges; | |
| int changeMask = 0; | |
| BOOL isZeroSizeWnd = FALSE; | |
| BOOL forceMapWindow = FALSE; | |
| WND *winposPtr = WIN_FindWndPtr( winpos->hwnd ); | |
| if ( !winposPtr ) return; | |
| /* find out if after this function we will end out with a zero-size window */ | |
| if (X11DRV_WND_IsZeroSizeWnd(winposPtr)) | |
| { | |
| /* if current size is 0, and no resizing */ | |
| if (winpos->flags & SWP_NOSIZE) | |
| isZeroSizeWnd = TRUE; | |
| else if ((winpos->cx > 0) && (winpos->cy > 0)) | |
| { | |
| /* if this function is setting a new size > 0 for the window, we | |
| should map the window if WS_VISIBLE is set */ | |
| if ((winposPtr->dwStyle & WS_VISIBLE) && !(winpos->flags & SWP_HIDEWINDOW)) | |
| forceMapWindow = TRUE; | |
| } | |
| } | |
| /* if resizing to 0 */ | |
| if ( !(winpos->flags & SWP_NOSIZE) && ((winpos->cx <= 0) || (winpos->cy <= 0)) ) | |
| isZeroSizeWnd = TRUE; | |
| if(!wndPtr->hwndSelf) wndPtr = NULL; /* FIXME: WND destroyed, shouldn't happen!!! */ | |
| if (!(winpos->flags & SWP_SHOWWINDOW) && (winpos->flags & SWP_HIDEWINDOW)) | |
| { | |
| if(X11DRV_WND_GetXWindow(wndPtr)) | |
| TSXUnmapWindow( display, X11DRV_WND_GetXWindow(wndPtr) ); | |
| } | |
| if(bChangePos) | |
| { | |
| if ( !(winpos->flags & SWP_NOSIZE)) | |
| { | |
| winChanges.width = (winpos->cx > 0 ) ? winpos->cx : 1; | |
| winChanges.height = (winpos->cy > 0 ) ? winpos->cy : 1; | |
| changeMask |= CWWidth | CWHeight; | |
| /* Tweak dialog window size hints */ | |
| if ((winposPtr->dwExStyle & WS_EX_MANAGED) && | |
| HAS_DLGFRAME(winposPtr->dwStyle,winposPtr->dwExStyle)) | |
| { | |
| XSizeHints *size_hints = TSXAllocSizeHints(); | |
| if (size_hints) | |
| { | |
| long supplied_return; | |
| TSXGetWMSizeHints( display, X11DRV_WND_GetXWindow(winposPtr), size_hints, | |
| &supplied_return, XA_WM_NORMAL_HINTS); | |
| size_hints->min_width = size_hints->max_width = winpos->cx; | |
| size_hints->min_height = size_hints->max_height = winpos->cy; | |
| TSXSetWMSizeHints( display, X11DRV_WND_GetXWindow(winposPtr), size_hints, | |
| XA_WM_NORMAL_HINTS ); | |
| TSXFree(size_hints); | |
| } | |
| } | |
| } | |
| if (!(winpos->flags & SWP_NOMOVE)) | |
| { | |
| winChanges.x = winpos->x; | |
| winChanges.y = winpos->y; | |
| changeMask |= CWX | CWY; | |
| } | |
| if (!(winpos->flags & SWP_NOZORDER) && !isZeroSizeWnd) | |
| { | |
| winChanges.stack_mode = Below; | |
| changeMask |= CWStackMode; | |
| if (winpos->hwndInsertAfter == HWND_TOP) winChanges.stack_mode = Above; | |
| else if (winpos->hwndInsertAfter != HWND_BOTTOM) | |
| { | |
| WND* insertPtr = WIN_FindWndPtr( winpos->hwndInsertAfter ); | |
| Window stack[2]; | |
| /* If the window where we should do the insert is zero-sized (not mapped) | |
| don't used this window since it will possibly crash the X server, | |
| use the "non zero-sized" window above */ | |
| if (X11DRV_WND_IsZeroSizeWnd(insertPtr)) | |
| { | |
| /* find the window on top of the zero sized window */ | |
| WND *pDesktop = WIN_GetDesktop(); | |
| WND *wndPrev = pDesktop->child; | |
| WND *wndZeroSized = insertPtr; | |
| while (1) | |
| { | |
| if (wndPrev == wndZeroSized) | |
| break; /* zero-sized window is on top */ | |
| while (wndPrev && (wndPrev->next != wndZeroSized)) | |
| wndPrev = wndPrev->next; | |
| /* check if the window found is not zero-sized */ | |
| if (X11DRV_WND_IsZeroSizeWnd(wndPrev)) | |
| { | |
| wndZeroSized = wndPrev; /* restart the search */ | |
| wndPrev = pDesktop->child; | |
| } | |
| else | |
| break; /* "above" window is found */ | |
| } | |
| WIN_ReleaseDesktop(); | |
| if (wndPrev == wndZeroSized) | |
| { | |
| /* the zero-sized window is on top */ | |
| /* so set the window on top */ | |
| winChanges.stack_mode = Above; | |
| } | |
| else | |
| { | |
| stack[0] = X11DRV_WND_FindDesktopXWindow( wndPrev ); | |
| stack[1] = X11DRV_WND_FindDesktopXWindow( winposPtr ); | |
| TSXRestackWindows(display, stack, 2); | |
| changeMask &= ~CWStackMode; | |
| } | |
| } | |
| else /* Normal behavior, windows are not zero-sized */ | |
| { | |
| stack[0] = X11DRV_WND_FindDesktopXWindow( insertPtr ); | |
| stack[1] = X11DRV_WND_FindDesktopXWindow( winposPtr ); | |
| TSXRestackWindows(display, stack, 2); | |
| changeMask &= ~CWStackMode; | |
| } | |
| WIN_ReleaseWndPtr(insertPtr); | |
| } | |
| } | |
| if (changeMask && X11DRV_WND_GetXWindow(winposPtr)) | |
| { | |
| TSXReconfigureWMWindow( display, X11DRV_WND_GetXWindow(winposPtr), 0, changeMask, &winChanges ); | |
| if( winposPtr->clsStyle & (CS_VREDRAW | CS_HREDRAW) ) | |
| X11DRV_WND_SetGravity( winposPtr, ForgetGravity ); | |
| } | |
| } | |
| /* don't map the window if it's a zero size window */ | |
| if ( ((winpos->flags & SWP_SHOWWINDOW) && !isZeroSizeWnd) || forceMapWindow ) | |
| { | |
| if(X11DRV_WND_GetXWindow(wndPtr)) | |
| TSXMapWindow( display, X11DRV_WND_GetXWindow(wndPtr) ); | |
| } | |
| WIN_ReleaseWndPtr(winposPtr); | |
| } | |
| /***************************************************************** | |
| * X11DRV_WND_SurfaceCopy | |
| * | |
| * Copies rect to (rect.left + dx, rect.top + dy). | |
| */ | |
| void X11DRV_WND_SurfaceCopy(WND* wndPtr, HDC hdc, INT dx, INT dy, | |
| const RECT *rect, BOOL bUpdate) | |
| { | |
| X11DRV_PDEVICE *physDev; | |
| POINT dst, src; | |
| DC *dcPtr = DC_GetDCPtr( hdc ); | |
| if (!dcPtr) return; | |
| physDev = (X11DRV_PDEVICE *)dcPtr->physDev; | |
| dst.x = (src.x = dcPtr->DCOrgX + rect->left) + dx; | |
| dst.y = (src.y = dcPtr->DCOrgY + rect->top) + dy; | |
| if (bUpdate) /* handles non-Wine windows hanging over the copied area */ | |
| TSXSetGraphicsExposures( display, physDev->gc, True ); | |
| TSXSetFunction( display, physDev->gc, GXcopy ); | |
| TSXCopyArea( display, physDev->drawable, physDev->drawable, | |
| physDev->gc, src.x, src.y, | |
| rect->right - rect->left, | |
| rect->bottom - rect->top, | |
| dst.x, dst.y ); | |
| if (bUpdate) | |
| TSXSetGraphicsExposures( display, physDev->gc, False ); | |
| GDI_ReleaseObj( hdc ); | |
| if (bUpdate) /* Make sure exposure events have been processed */ | |
| X11DRV_Synchronize(); | |
| } | |
| /*********************************************************************** | |
| * X11DRV_SetWMHint | |
| */ | |
| static BOOL X11DRV_SetWMHint(Display* display, WND* wndPtr, int hint, int val) | |
| { | |
| XWMHints* wm_hints = TSXGetWMHints( display, X11DRV_WND_GetXWindow(wndPtr) ); | |
| if (!wm_hints) wm_hints = TSXAllocWMHints(); | |
| if (wm_hints) | |
| { | |
| wm_hints->flags = hint; | |
| switch( hint ) | |
| { | |
| case InputHint: | |
| wm_hints->input = val; | |
| break; | |
| case StateHint: | |
| wm_hints->initial_state = val; | |
| break; | |
| case IconPixmapHint: | |
| wm_hints->icon_pixmap = (Pixmap)val; | |
| break; | |
| case IconWindowHint: | |
| wm_hints->icon_window = (Window)val; | |
| break; | |
| } | |
| TSXSetWMHints( display, X11DRV_WND_GetXWindow(wndPtr), wm_hints ); | |
| TSXFree(wm_hints); | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| void X11DRV_WND_SetGravity( WND* wnd, int value ) | |
| { | |
| X11DRV_WND_DATA *data = wnd->pDriverData; | |
| if (data && data->window && data->bit_gravity != value ) | |
| { | |
| XSetWindowAttributes win_attr; | |
| win_attr.bit_gravity = value; | |
| data->bit_gravity = value; | |
| TSXChangeWindowAttributes( display, data->window, CWBitGravity, &win_attr ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * X11DRV_WND_SetHostAttr | |
| * | |
| * This function returns TRUE if the attribute is supported and the | |
| * action was successful. Otherwise it should return FALSE and Wine will try | |
| * to get by without the functionality provided by the host window system. | |
| */ | |
| BOOL X11DRV_WND_SetHostAttr(WND* wnd, INT ha, INT value) | |
| { | |
| Window w; | |
| if( (w = X11DRV_WND_GetXWindow(wnd)) ) | |
| { | |
| switch( ha ) | |
| { | |
| case HAK_ICONICSTATE: /* called when a window is minimized/restored */ | |
| /* don't do anything if it'a zero size window */ | |
| if (X11DRV_WND_IsZeroSizeWnd(wnd)) | |
| return TRUE; | |
| if( (wnd->dwExStyle & WS_EX_MANAGED) ) | |
| { | |
| if( value ) | |
| { | |
| if( wnd->dwStyle & WS_VISIBLE ) | |
| { | |
| XClientMessageEvent ev; | |
| /* FIXME: set proper icon */ | |
| ev.type = ClientMessage; | |
| ev.display = display; | |
| ev.message_type = wmChangeState; | |
| ev.format = 32; | |
| ev.data.l[0] = IconicState; | |
| ev.window = w; | |
| if( TSXSendEvent (display, | |
| RootWindow( display, XScreenNumberOfScreen(X11DRV_GetXScreen()) ), | |
| True, (SubstructureRedirectMask | SubstructureNotifyMask), (XEvent*)&ev)) | |
| { | |
| XEvent xe; | |
| TSXFlush (display); | |
| while( !TSXCheckTypedWindowEvent( display, w, UnmapNotify, &xe) ); | |
| } | |
| else | |
| break; | |
| } | |
| else | |
| X11DRV_SetWMHint( display, wnd, StateHint, IconicState ); | |
| } | |
| else | |
| { | |
| if( !(wnd->flags & WS_VISIBLE) ) | |
| X11DRV_SetWMHint( display, wnd, StateHint, NormalState ); | |
| else | |
| { | |
| XEvent xe; | |
| TSXMapWindow(display, w ); | |
| while( !TSXCheckTypedWindowEvent( display, w, MapNotify, &xe) ); | |
| } | |
| } | |
| return TRUE; | |
| } | |
| break; | |
| } | |
| } | |
| return FALSE; | |
| } |