/* * X11 windows driver * * Copyright 1993, 1994, 1995, 1996 Alexandre Julliard * 1993 David Metcalfe * 1995, 1996 Alex Korobka */ #include "config.h" #include #include "ts_xlib.h" #include "ts_xutil.h" #include "ts_shape.h" #include #include #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( thread_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( thread_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; Display *display = thread_display(); 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; wine_tsx11_lock(); if (bUpdate) /* handles non-Wine windows hanging over the copied area */ XSetGraphicsExposures( gdi_display, physDev->gc, True ); XSetFunction( gdi_display, physDev->gc, GXcopy ); XCopyArea( gdi_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) XSetGraphicsExposures( gdi_display, physDev->gc, False ); wine_tsx11_unlock(); 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( thread_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)) ) { Display *display = thread_display(); 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, DefaultRootWindow(display), 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; }