/* * 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); /* Some useful macros */ #define HAS_DLGFRAME(style,exStyle) \ ((!((style) & WS_THICKFRAME)) && (((style) & WS_DLGFRAME) || ((exStyle) & WS_EX_DLGMODALFRAME))) /**********************************************************************/ extern Cursor X11DRV_MOUSE_XCursor; /* Current X cursor */ extern BOOL X11DRV_CreateBitmap( HBITMAP ); extern Pixmap X11DRV_BITMAP_Pixmap( HBITMAP ); /**********************************************************************/ WND_DRIVER X11DRV_WND_Driver = { X11DRV_WND_Initialize, X11DRV_WND_Finalize, X11DRV_WND_CreateDesktopWindow, X11DRV_WND_CreateWindow, X11DRV_WND_DestroyWindow, X11DRV_WND_SetParent, X11DRV_WND_ForceWindowRaise, X11DRV_WND_SetWindowPos, X11DRV_WND_SetText, X11DRV_WND_SetFocus, X11DRV_WND_PreSizeMove, X11DRV_WND_PostSizeMove, X11DRV_WND_SurfaceCopy, X11DRV_WND_SetDrawable, X11DRV_WND_SetHostAttr, X11DRV_WND_IsSelfClipping, X11DRV_WND_SetWindowRgn }; /* X context to associate a hwnd to an X window */ XContext winContext = 0; Atom wmProtocols = None; Atom wmDeleteWindow = None; Atom dndProtocol = None; Atom dndSelection = None; Atom wmChangeState = None; Atom kwmDockWindow = None; Atom _net_kde_system_tray_window_for = None; /* KDE 2 Beta */ Atom _kde_net_wm_system_tray_window_for = None; /* KDE 2 Final */ /*********************************************************************** * 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_RegisterWindow * * Associate an X window to a HWND. */ static void X11DRV_WND_RegisterWindow(WND *wndPtr) { TSXSetWMProtocols( display, X11DRV_WND_GetXWindow(wndPtr), &wmDeleteWindow, 1 ); if (!winContext) winContext = TSXUniqueContext(); TSXSaveContext( display, X11DRV_WND_GetXWindow(wndPtr), winContext, (char *) wndPtr->hwndSelf ); } /*********************************************************************** * 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_Initialize */ void X11DRV_WND_Initialize(WND *wndPtr) { X11DRV_WND_DATA *pWndDriverData = HeapAlloc(GetProcessHeap(), 0, sizeof(X11DRV_WND_DATA)); wndPtr->pDriverData = (void *) pWndDriverData; pWndDriverData->window = 0; } /********************************************************************** * X11DRV_WND_Finalize */ void X11DRV_WND_Finalize(WND *wndPtr) { X11DRV_WND_DATA *pWndDriverData = (X11DRV_WND_DATA *) wndPtr->pDriverData; if (!wndPtr->pDriverData) { ERR("Trying to destroy window again. Not good.\n"); return; } if(pWndDriverData->window) { ERR( "WND destroyed without destroying " "the associated X Window (%ld)\n", pWndDriverData->window ); } HeapFree(GetProcessHeap(), 0, wndPtr->pDriverData); wndPtr->pDriverData = NULL; } /********************************************************************** * X11DRV_WND_CreateDesktopWindow */ BOOL X11DRV_WND_CreateDesktopWindow(WND *wndPtr) { if (wmProtocols == None) wmProtocols = TSXInternAtom( display, "WM_PROTOCOLS", True ); if (wmDeleteWindow == None) wmDeleteWindow = TSXInternAtom( display, "WM_DELETE_WINDOW", True ); if( dndProtocol == None ) dndProtocol = TSXInternAtom( display, "DndProtocol" , False ); if( dndSelection == None ) dndSelection = TSXInternAtom( display, "DndSelection" , False ); if( wmChangeState == None ) wmChangeState = TSXInternAtom (display, "WM_CHANGE_STATE", False); if (kwmDockWindow == None) kwmDockWindow = TSXInternAtom( display, "KWM_DOCKWINDOW", False ); if (_kde_net_wm_system_tray_window_for == None) _kde_net_wm_system_tray_window_for = TSXInternAtom( display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False ); if (_net_kde_system_tray_window_for == None) _net_kde_system_tray_window_for = TSXInternAtom( display, "_NET_KDE_SYSTEM_TRAY_WINDOW_FOR", False ); ((X11DRV_WND_DATA *) wndPtr->pDriverData)->window = X11DRV_GetXRootWindow(); if (X11DRV_GetXRootWindow() != DefaultRootWindow(display)) wndPtr->flags |= WIN_NATIVE; X11DRV_WND_RegisterWindow( wndPtr ); return TRUE; } /********************************************************************** * X11DRV_WND_IconChanged * * hIcon or hIconSm has changed (or is being initialised for the * first time). Complete the X11 driver-specific initialisation. * * This is not entirely correct, may need to create * an icon window and set the pixmap as a background */ static void X11DRV_WND_IconChanged(WND *wndPtr) { HICON16 hIcon = NC_IconForWindow(wndPtr); if( ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconBitmap ) DeleteObject( ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconBitmap ); if( ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconMask ) DeleteObject( ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconMask); if (!hIcon) { ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconBitmap= 0; ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconMask= 0; } else { HBITMAP hbmOrig; RECT rcMask; BITMAP bmMask; ICONINFO ii; HDC hDC; GetIconInfo(hIcon, &ii); X11DRV_CreateBitmap(ii.hbmMask); X11DRV_CreateBitmap(ii.hbmColor); GetObjectA(ii.hbmMask, sizeof(bmMask), &bmMask); rcMask.top = 0; rcMask.left = 0; rcMask.right = bmMask.bmWidth; rcMask.bottom = bmMask.bmHeight; hDC = CreateCompatibleDC(0); hbmOrig = SelectObject(hDC, ii.hbmMask); InvertRect(hDC, &rcMask); SelectObject(hDC, hbmOrig); DeleteDC(hDC); ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconBitmap = ii.hbmColor; ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconMask= ii.hbmMask; } return; } static void X11DRV_WND_SetIconHints(WND *wndPtr, XWMHints *hints) { if (((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconBitmap) { hints->icon_pixmap = X11DRV_BITMAP_Pixmap(((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconBitmap); hints->flags |= IconPixmapHint; } else hints->flags &= ~IconPixmapHint; if (((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconMask) { hints->icon_mask = X11DRV_BITMAP_Pixmap(((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconMask); hints->flags |= IconMaskHint; } else hints->flags &= ~IconMaskHint; } /********************************************************************** * X11DRV_WND_UpdateIconHints * * hIcon or hIconSm has changed (or is being initialised for the * first time). Complete the X11 driver-specific initialisation * and set the window hints. * * This is not entirely correct, may need to create * an icon window and set the pixmap as a background */ static void X11DRV_WND_UpdateIconHints(WND *wndPtr) { XWMHints* wm_hints; X11DRV_WND_IconChanged(wndPtr); wm_hints = TSXGetWMHints( display, X11DRV_WND_GetXWindow(wndPtr) ); if (!wm_hints) wm_hints = TSXAllocWMHints(); if (wm_hints) { X11DRV_WND_SetIconHints(wndPtr, wm_hints); TSXSetWMHints( display, X11DRV_WND_GetXWindow(wndPtr), wm_hints ); TSXFree( wm_hints ); } } /********************************************************************** * X11DRV_WND_CreateWindow */ BOOL X11DRV_WND_CreateWindow(WND *wndPtr, CREATESTRUCTA *cs, BOOL bUnicode) { /* Create the X window (only for top-level windows, and then only */ /* when there's no desktop window) */ if ((X11DRV_GetXRootWindow() == DefaultRootWindow(display)) && (wndPtr->parent->hwndSelf == GetDesktopWindow())) { Window wGroupLeader; XWMHints* wm_hints; XSetWindowAttributes win_attr; /* Create "managed" windows only if a title bar or resizable */ /* frame is required. */ if (WIN_WindowNeedsWMBorder(cs->style, cs->dwExStyle)) { win_attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask | StructureNotifyMask; win_attr.override_redirect = FALSE; wndPtr->dwExStyle |= WS_EX_MANAGED; } else { win_attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask; win_attr.override_redirect = TRUE; } wndPtr->flags |= WIN_NATIVE; win_attr.bit_gravity = (wndPtr->clsStyle & (CS_VREDRAW | CS_HREDRAW)) ? BGForget : BGNorthWest; win_attr.colormap = X11DRV_PALETTE_PaletteXColormap; win_attr.backing_store = NotUseful; win_attr.save_under = ((wndPtr->clsStyle & CS_SAVEBITS) != 0); win_attr.cursor = X11DRV_MOUSE_XCursor; ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconBitmap = 0; ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconMask = 0; ((X11DRV_WND_DATA *) wndPtr->pDriverData)->bit_gravity = win_attr.bit_gravity; /* Zero-size X11 window hack. X doesn't like them, and will crash */ /* with a BadValue unless we do something ugly like this. */ /* Zero size window won't be mapped */ if (cs->cx <= 0) cs->cx = 1; if (cs->cy <= 0) cs->cy = 1; ((X11DRV_WND_DATA *) wndPtr->pDriverData)->window = TSXCreateWindow( display, X11DRV_GetXRootWindow(), cs->x, cs->y, cs->cx, cs->cy, 0, screen_depth, InputOutput, visual, CWEventMask | CWOverrideRedirect | CWColormap | CWCursor | CWSaveUnder | CWBackingStore | CWBitGravity, &win_attr ); if(!(wGroupLeader = X11DRV_WND_GetXWindow(wndPtr))) return FALSE; /* If we are the systray, we need to be managed to be noticed by KWM */ if (wndPtr->dwExStyle & WS_EX_TRAYWINDOW) X11DRV_WND_DockWindow(wndPtr); if (wndPtr->dwExStyle & WS_EX_MANAGED) { XClassHint *class_hints = TSXAllocClassHint(); XSizeHints* size_hints = TSXAllocSizeHints(); if (class_hints) { class_hints->res_name = "wineManaged"; class_hints->res_class = "Wine"; TSXSetClassHint( display, ((X11DRV_WND_DATA *) wndPtr->pDriverData)->window, class_hints ); TSXFree (class_hints); } if (size_hints) { size_hints->win_gravity = StaticGravity; size_hints->x = cs->x; size_hints->y = cs->y; size_hints->flags = PWinGravity|PPosition; if (HAS_DLGFRAME(cs->style,cs->dwExStyle)) { size_hints->min_width = size_hints->max_width = cs->cx; size_hints->min_height = size_hints->max_height = cs->cy; size_hints->flags |= PMinSize | PMaxSize; } TSXSetWMSizeHints( display, X11DRV_WND_GetXWindow(wndPtr), size_hints, XA_WM_NORMAL_HINTS ); TSXFree(size_hints); } } if (cs->hwndParent) /* Get window owner */ { Window w; WND *tmpWnd = WIN_FindWndPtr(cs->hwndParent); w = X11DRV_WND_FindXWindow( tmpWnd ); if (w != None) { TSXSetTransientForHint( display, X11DRV_WND_GetXWindow(wndPtr), w ); wGroupLeader = w; } WIN_ReleaseWndPtr(tmpWnd); } if ((wm_hints = TSXAllocWMHints())) { wm_hints->flags = InputHint | StateHint | WindowGroupHint; wm_hints->input = True; if (wndPtr->dwExStyle & WS_EX_MANAGED) { X11DRV_WND_IconChanged(wndPtr); X11DRV_WND_SetIconHints(wndPtr, wm_hints); wm_hints->initial_state = (wndPtr->dwStyle & WS_MINIMIZE) ? IconicState : NormalState; } else wm_hints->initial_state = NormalState; wm_hints->window_group = wGroupLeader; TSXSetWMHints( display, X11DRV_WND_GetXWindow(wndPtr), wm_hints ); TSXFree(wm_hints); } X11DRV_WND_RegisterWindow( wndPtr ); } return TRUE; } /*********************************************************************** * X11DRV_WND_DestroyWindow */ BOOL X11DRV_WND_DestroyWindow(WND *wndPtr) { Window w; if ((w = X11DRV_WND_GetXWindow(wndPtr))) { XEvent xe; TSXDeleteContext( display, w, winContext ); TSXDestroyWindow( display, w ); while( TSXCheckWindowEvent(display, w, NoEventMask, &xe) ); ((X11DRV_WND_DATA *) wndPtr->pDriverData)->window = None; if( ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconBitmap ) { DeleteObject( ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconBitmap ); ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconBitmap = 0; } if( ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconMask ) { DeleteObject( ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconMask); ((X11DRV_WND_DATA *) wndPtr->pDriverData)->hWMIconMask= 0; } } return TRUE; } /***************************************************************** * X11DRV_WND_SetParent */ WND *X11DRV_WND_SetParent(WND *wndPtr, WND *pWndParent) { WND *pDesktop = WIN_GetDesktop(); if( wndPtr && pWndParent && (wndPtr != pDesktop) ) { WND* pWndPrev = wndPtr->parent; if( pWndParent != pWndPrev ) { if ( X11DRV_WND_GetXWindow(wndPtr) ) { /* Toplevel window needs to be reparented. Used by Tk 8.0 */ TSXDestroyWindow( display, X11DRV_WND_GetXWindow(wndPtr) ); ((X11DRV_WND_DATA *) wndPtr->pDriverData)->window = None; } WIN_UnlinkWindow(wndPtr->hwndSelf); wndPtr->parent = pWndParent; /* Create an X counterpart for reparented top-level windows * when not in the desktop mode. */ if( pWndParent == pDesktop ) { if( X11DRV_GetXRootWindow() == DefaultRootWindow(display) ) { CREATESTRUCTA cs; cs.lpCreateParams = NULL; cs.hInstance = 0; /* not used in following call */ cs.hMenu = 0; /* not used in following call */ cs.hwndParent = pWndParent->hwndSelf; cs.cy = wndPtr->rectWindow.bottom - wndPtr->rectWindow.top; if (!cs.cy) cs.cy = 1; cs.cx = wndPtr->rectWindow.right - wndPtr->rectWindow.left; if (!cs.cx) cs.cx = 1; cs.y = wndPtr->rectWindow.top; cs.x = wndPtr->rectWindow.left; cs.style = wndPtr->dwStyle; cs.lpszName = 0; /* not used in following call */ cs.lpszClass = 0; /*not used in following call */ cs.dwExStyle = wndPtr->dwExStyle; X11DRV_WND_CreateWindow(wndPtr, &cs, FALSE); } } else /* a child window */ { if( !( wndPtr->dwStyle & WS_CHILD ) ) { if( wndPtr->wIDmenu != 0) { DestroyMenu( (HMENU) wndPtr->wIDmenu ); wndPtr->wIDmenu = 0; } } } WIN_LinkWindow(wndPtr->hwndSelf, HWND_TOP); } WIN_ReleaseDesktop(); return pWndPrev; } /* failure */ WIN_ReleaseDesktop(); return 0; } /*********************************************************************** * 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_SetHostAttr( winposPtr, HAK_BITGRAVITY, BGForget ); } } /* 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_SetText */ void X11DRV_WND_SetText(WND *wndPtr, LPCWSTR text) { UINT count; char *buffer; static UINT text_cp = (UINT)-1; Window win; if (!(win = X11DRV_WND_GetXWindow(wndPtr))) return; if(text_cp == (UINT)-1) { text_cp = PROFILE_GetWineIniInt("x11drv", "TextCP", CP_ACP); TRACE("text_cp = %u\n", text_cp); } /* allocate new buffer for window text */ count = WideCharToMultiByte(text_cp, 0, text, -1, NULL, 0, NULL, NULL); if (!(buffer = HeapAlloc( GetProcessHeap(), 0, count * sizeof(WCHAR) ))) { ERR("Not enough memory for window text\n"); return; } WideCharToMultiByte(text_cp, 0, text, -1, buffer, count, NULL, NULL); TSXStoreName( display, win, buffer ); TSXSetIconName( display, win, buffer ); HeapFree( GetProcessHeap(), 0, buffer ); } /***************************************************************** * X11DRV_WND_SetFocus * * Set the X focus. * Explicit colormap management seems to work only with OLVWM. */ void X11DRV_WND_SetFocus(WND *wndPtr) { HWND hwnd = wndPtr->hwndSelf; XWindowAttributes win_attr; Window win; WND *w = wndPtr; if (X11DRV_WND_IsZeroSizeWnd(wndPtr)) return; /* Only mess with the X focus if there's */ /* no desktop window and if the window is not managed by the WM. */ if ((X11DRV_GetXRootWindow() != DefaultRootWindow(display))) return; while (w && !((X11DRV_WND_DATA *) w->pDriverData)->window) w = w->parent; if (!w) w = wndPtr; if (w->dwExStyle & WS_EX_MANAGED) return; if (!hwnd) /* If setting the focus to 0, uninstall the colormap */ { if (X11DRV_PALETTE_PaletteFlags & X11DRV_PALETTE_PRIVATE) TSXUninstallColormap( display, X11DRV_PALETTE_PaletteXColormap ); return; } /* Set X focus and install colormap */ if (!(win = X11DRV_WND_FindXWindow(wndPtr))) return; if (!TSXGetWindowAttributes( display, win, &win_attr ) || (win_attr.map_state != IsViewable)) return; /* If window is not viewable, don't change anything */ TSXSetInputFocus( display, win, RevertToParent, CurrentTime ); if (X11DRV_PALETTE_PaletteFlags & X11DRV_PALETTE_PRIVATE) TSXInstallColormap( display, X11DRV_PALETTE_PaletteXColormap ); EVENT_Synchronize(); } /***************************************************************** * X11DRV_WND_PreSizeMove */ void X11DRV_WND_PreSizeMove(WND *wndPtr) { /* Grab the server only when moving top-level windows without desktop */ if ((X11DRV_GetXRootWindow() == DefaultRootWindow(display)) && (wndPtr->parent->hwndSelf == GetDesktopWindow())) TSXGrabServer( display ); } /***************************************************************** * X11DRV_WND_PostSizeMove */ void X11DRV_WND_PostSizeMove(WND *wndPtr) { if ((X11DRV_GetXRootWindow() == DefaultRootWindow(display)) && (wndPtr->parent->hwndSelf == GetDesktopWindow())) TSXUngrabServer( display ); } /***************************************************************** * 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 */ EVENT_Synchronize(); } /*********************************************************************** * X11DRV_WND_SetDrawable * * Set the drawable, origin and dimensions for the DC associated to * a given window. */ void X11DRV_WND_SetDrawable(WND *wndPtr, HDC hdc, WORD flags, BOOL bSetClipOrigin) { DC *dc = DC_GetDCPtr( hdc ); X11DRV_PDEVICE *physDev; INT dcOrgXCopy = 0, dcOrgYCopy = 0; BOOL offsetClipRgn = FALSE; if (!dc) return; physDev = (X11DRV_PDEVICE *)dc->physDev; if (!wndPtr) /* Get a DC for the whole screen */ { dc->DCOrgX = 0; dc->DCOrgY = 0; physDev->drawable = X11DRV_GetXRootWindow(); TSXSetSubwindowMode( display, physDev->gc, IncludeInferiors ); } else { /* * This function change the coordinate system (DCOrgX,DCOrgY) * values. When it moves the origin, other data like the current clipping * region will not be moved to that new origin. In the case of DCs that are class * or window DCs that clipping region might be a valid value from a previous use * of the DC and changing the origin of the DC without moving the clip region * results in a clip region that is not placed properly in the DC. * This code will save the dc origin, let the SetDrawable * modify the origin and reset the clipping. When the clipping is set, * it is moved according to the new DC origin. */ if ( (wndPtr->clsStyle & (CS_OWNDC | CS_CLASSDC)) && (dc->hClipRgn > 0)) { dcOrgXCopy = dc->DCOrgX; dcOrgYCopy = dc->DCOrgY; offsetClipRgn = TRUE; } if (flags & DCX_WINDOW) { dc->DCOrgX = wndPtr->rectWindow.left; dc->DCOrgY = wndPtr->rectWindow.top; } else { dc->DCOrgX = wndPtr->rectClient.left; dc->DCOrgY = wndPtr->rectClient.top; } while (!X11DRV_WND_GetXWindow(wndPtr)) { wndPtr = wndPtr->parent; dc->DCOrgX += wndPtr->rectClient.left; dc->DCOrgY += wndPtr->rectClient.top; } dc->DCOrgX -= wndPtr->rectWindow.left; dc->DCOrgY -= wndPtr->rectWindow.top; /* reset the clip region, according to the new origin */ if ( offsetClipRgn ) { OffsetRgn(dc->hClipRgn, dc->DCOrgX - dcOrgXCopy,dc->DCOrgY - dcOrgYCopy); } physDev->drawable = X11DRV_WND_GetXWindow(wndPtr); #if 0 /* This is needed when we reuse a cached DC because * SetDCState() called by ReleaseDC() screws up DC * origins for child windows. */ if( bSetClipOrigin ) TSXSetClipOrigin( display, physDev->gc, dc->DCOrgX, dc->DCOrgY ); #endif } GDI_ReleaseObj( hdc ); } /*********************************************************************** * 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; } /*********************************************************************** * 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)) ) { XSetWindowAttributes win_attr; 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; case HAK_BITGRAVITY: /* called when a window is resized */ if( ((X11DRV_WND_DATA *) wnd->pDriverData)->bit_gravity != value ) { win_attr.bit_gravity = value; ((X11DRV_WND_DATA *) wnd->pDriverData)->bit_gravity = value; TSXChangeWindowAttributes( display, w, CWBitGravity, &win_attr ); } return TRUE; case HAK_ICONS: /* called when the icons change */ if ( (wnd->dwExStyle & WS_EX_MANAGED) ) X11DRV_WND_UpdateIconHints(wnd); return TRUE; case HAK_ACCEPTFOCUS: /* called when a window is disabled/enabled */ if( (wnd->dwExStyle & WS_EX_MANAGED) ) return X11DRV_SetWMHint( display, wnd, InputHint, value ); } } return FALSE; } /*********************************************************************** * X11DRV_WND_IsSelfClipping */ BOOL X11DRV_WND_IsSelfClipping(WND *wndPtr) { return X11DRV_WND_GetXWindow(wndPtr) != None; } /*********************************************************************** * X11DRV_WND_DockWindow * * Set the X Property of the window that tells the windowmanager we really * want to be in the systray * * KDE: set "KWM_DOCKWINDOW", type "KWM_DOCKWINDOW" to 1 before a window is * mapped. * * all others: to be added ;) */ void X11DRV_WND_DockWindow(WND *wndPtr) { int data = 1; Window win = X11DRV_WND_GetXWindow(wndPtr); if (kwmDockWindow != None) { TSXChangeProperty( display,win,kwmDockWindow,kwmDockWindow,32,PropModeReplace,(char*)&data,1 ); } if (_kde_net_wm_system_tray_window_for != None) { TSXChangeProperty( display, win, _kde_net_wm_system_tray_window_for, XA_WINDOW, 32, PropModeReplace, (char*)&win, 1 ); } if (_net_kde_system_tray_window_for != None) { TSXChangeProperty( display, win, _net_kde_system_tray_window_for, XA_WINDOW, 32, PropModeReplace, (char*)&win, 1 ); } } /*********************************************************************** * X11DRV_WND_SetWindowRgn * * Assign specified region to window (for non-rectangular windows) */ void X11DRV_WND_SetWindowRgn(WND *wndPtr, HRGN hrgnWnd) { #ifdef HAVE_LIBXSHAPE Window win = X11DRV_WND_GetXWindow(wndPtr); if (!win) return; if (!hrgnWnd) { TSXShapeCombineMask( display, win, ShapeBounding, 0, 0, None, ShapeSet ); } else { XRectangle *aXRect; DWORD size; DWORD dwBufferSize = GetRegionData(hrgnWnd, 0, NULL); PRGNDATA pRegionData = HeapAlloc(GetProcessHeap(), 0, dwBufferSize); if (!pRegionData) return; GetRegionData(hrgnWnd, dwBufferSize, pRegionData); size = pRegionData->rdh.nCount; /* convert region's "Windows rectangles" to XRectangles */ aXRect = HeapAlloc(GetProcessHeap(), 0, size * sizeof(*aXRect) ); if (aXRect) { XRectangle* pCurrRect = aXRect; RECT *pRect = (RECT*) pRegionData->Buffer; for (; pRect < ((RECT*) pRegionData->Buffer) + size ; ++pRect, ++pCurrRect) { pCurrRect->x = pRect->left; pCurrRect->y = pRect->top; pCurrRect->height = pRect->bottom - pRect->top; pCurrRect->width = pRect->right - pRect->left; TRACE("Rectangle %04d of %04ld data: X=%04d, Y=%04d, Height=%04d, Width=%04d.\n", pRect - (RECT*) pRegionData->Buffer, size, pCurrRect->x, pCurrRect->y, pCurrRect->height, pCurrRect->width); } /* shape = non-rectangular windows (X11/extensions) */ TSXShapeCombineRectangles( display, win, ShapeBounding, 0, 0, aXRect, pCurrRect - aXRect, ShapeSet, YXBanded ); HeapFree(GetProcessHeap(), 0, aXRect ); } HeapFree(GetProcessHeap(), 0, pRegionData); } #endif /* HAVE_LIBXSHAPE */ }