/* * Window related functions * * Copyright 1993, 1994, 1995, 1996, 2001 Alexandre Julliard * Copyright 1993 David Metcalfe * Copyright 1995, 1996 Alex Korobka */ #include "config.h" #include "ts_xlib.h" #include "ts_xutil.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "debugtools.h" #include "x11drv.h" #include "win.h" #include "options.h" DEFAULT_DEBUG_CHANNEL(win); extern Cursor X11DRV_MOUSE_XCursor; /* current X cursor */ extern Pixmap X11DRV_BITMAP_Pixmap( HBITMAP ); #define HAS_DLGFRAME(style,exStyle) \ ((!((style) & WS_THICKFRAME)) && (((style) & WS_DLGFRAME) || ((exStyle) & WS_EX_DLGMODALFRAME))) /* 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 _kde_net_wm_system_tray_window_for = None; /* KDE 2 Final */ /*********************************************************************** * register_window * * Associate an X window to a HWND. */ static void register_window( HWND hwnd, Window win ) { if (!winContext) winContext = TSXUniqueContext(); TSXSaveContext( display, win, winContext, (char *)hwnd ); TSXSetWMProtocols( display, win, &wmDeleteWindow, 1 ); } /*********************************************************************** * set_wm_hint * * Set a window manager hint. */ static void set_wm_hint( Window win, int hint, int val ) { XWMHints* wm_hints = TSXGetWMHints( display, win ); 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, win, wm_hints ); TSXFree(wm_hints); } } /*********************************************************************** * set_icon_hints * * Set the icon wm hints */ static void set_icon_hints( WND *wndPtr, XWMHints *hints ) { X11DRV_WND_DATA *data = wndPtr->pDriverData; HICON hIcon = GetClassLongA( wndPtr->hwndSelf, GCL_HICON ); if (data->hWMIconBitmap) DeleteObject( data->hWMIconBitmap ); if (data->hWMIconMask) DeleteObject( data->hWMIconMask); if (!hIcon) { data->hWMIconBitmap = 0; data->hWMIconMask = 0; hints->flags &= ~(IconPixmapHint | IconMaskHint); } 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); data->hWMIconBitmap = ii.hbmColor; data->hWMIconMask = ii.hbmMask; hints->icon_pixmap = X11DRV_BITMAP_Pixmap(data->hWMIconBitmap); hints->icon_mask = X11DRV_BITMAP_Pixmap(data->hWMIconMask); hints->flags |= IconPixmapHint | IconMaskHint; } } /*********************************************************************** * dock_window * * 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 ;) */ inline static void dock_window( Window win ) { int data = 1; 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 ); } /********************************************************************** * create_desktop */ static void create_desktop(WND *wndPtr) { X11DRV_WND_DATA *data = wndPtr->pDriverData; wmProtocols = TSXInternAtom( display, "WM_PROTOCOLS", True ); wmDeleteWindow = TSXInternAtom( display, "WM_DELETE_WINDOW", True ); dndProtocol = TSXInternAtom( display, "DndProtocol" , False ); dndSelection = TSXInternAtom( display, "DndSelection" , False ); wmChangeState = TSXInternAtom (display, "WM_CHANGE_STATE", False); kwmDockWindow = TSXInternAtom( display, "KWM_DOCKWINDOW", False ); _kde_net_wm_system_tray_window_for = TSXInternAtom( display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False ); data->window = root_window; if (root_window != DefaultRootWindow(display)) wndPtr->flags |= WIN_NATIVE; register_window( wndPtr->hwndSelf, root_window ); } /********************************************************************** * CreateWindow (X11DRV.@) */ BOOL X11DRV_CreateWindow( HWND hwnd ) { X11DRV_WND_DATA *data; WND *wndPtr = WIN_FindWndPtr( hwnd ); int x = wndPtr->rectWindow.left; int y = wndPtr->rectWindow.top; int cx = wndPtr->rectWindow.right - wndPtr->rectWindow.left; int cy = wndPtr->rectWindow.bottom - wndPtr->rectWindow.top; if (!(data = HeapAlloc(GetProcessHeap(), 0, sizeof(X11DRV_WND_DATA)))) { WIN_ReleaseWndPtr( wndPtr ); return FALSE; } data->window = 0; wndPtr->pDriverData = data; if (!wndPtr->parent) { create_desktop( wndPtr ); WIN_ReleaseWndPtr( wndPtr ); return TRUE; } /* Create the X window (only for top-level windows, and then only */ /* when there's no desktop window) */ if ((root_window == 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(wndPtr->dwStyle, wndPtr->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)) ? ForgetGravity : NorthWestGravity; 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; data->hWMIconBitmap = 0; data->hWMIconMask = 0; data->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 (cx <= 0) cx = 1; if (cy <= 0) cy = 1; data->window = TSXCreateWindow( display, root_window, x, y, cx, cy, 0, screen_depth, InputOutput, visual, CWEventMask | CWOverrideRedirect | CWColormap | CWCursor | CWSaveUnder | CWBackingStore | CWBitGravity, &win_attr ); if(!(wGroupLeader = X11DRV_WND_GetXWindow(wndPtr))) { HeapFree( GetProcessHeap(), 0, data ); WIN_ReleaseWndPtr( wndPtr ); return FALSE; } /* If we are the systray, we need to be managed to be noticed by KWM */ if (wndPtr->dwExStyle & WS_EX_TRAYWINDOW) dock_window( data->window ); 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, data->window, class_hints ); TSXFree (class_hints); } if (size_hints) { size_hints->win_gravity = StaticGravity; size_hints->x = x; size_hints->y = y; size_hints->flags = PWinGravity|PPosition; if (HAS_DLGFRAME(wndPtr->dwStyle,wndPtr->dwExStyle)) { size_hints->min_width = size_hints->max_width = cx; size_hints->min_height = size_hints->max_height = cy; size_hints->flags |= PMinSize | PMaxSize; } TSXSetWMSizeHints( display, X11DRV_WND_GetXWindow(wndPtr), size_hints, XA_WM_NORMAL_HINTS ); TSXFree(size_hints); } } if (wndPtr->owner) /* Get window owner */ { Window w = X11DRV_WND_FindXWindow( wndPtr->owner ); if (w != None) { TSXSetTransientForHint( display, X11DRV_WND_GetXWindow(wndPtr), w ); wGroupLeader = w; } } if ((wm_hints = TSXAllocWMHints())) { wm_hints->flags = InputHint | StateHint | WindowGroupHint; wm_hints->input = True; if (wndPtr->dwExStyle & WS_EX_MANAGED) { set_icon_hints( 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); } register_window( hwnd, data->window ); } WIN_ReleaseWndPtr( wndPtr ); return TRUE; } /*********************************************************************** * DestroyWindow (X11DRV.@) */ BOOL X11DRV_DestroyWindow( HWND hwnd ) { WND *wndPtr = WIN_FindWndPtr( hwnd ); X11DRV_WND_DATA *data = wndPtr->pDriverData; Window w; if (data && (w = data->window)) { XEvent xe; TSXDeleteContext( display, w, winContext ); TSXDestroyWindow( display, w ); while( TSXCheckWindowEvent(display, w, NoEventMask, &xe) ); data->window = None; if( data->hWMIconBitmap ) { DeleteObject( data->hWMIconBitmap ); data->hWMIconBitmap = 0; } if( data->hWMIconMask ) { DeleteObject( data->hWMIconMask); data->hWMIconMask= 0; } } HeapFree( GetProcessHeap(), 0, data ); wndPtr->pDriverData = NULL; WIN_ReleaseWndPtr( wndPtr ); return TRUE; } /***************************************************************** * SetParent (X11DRV.@) */ HWND X11DRV_SetParent( HWND hwnd, HWND parent ) { WND *wndPtr; WND *pWndParent; DWORD dwStyle; HWND retvalue; if (!(wndPtr = WIN_FindWndPtr(hwnd))) return 0; dwStyle = wndPtr->dwStyle; pWndParent = parent ? WIN_FindWndPtr(parent) : WIN_GetDesktop(); if (!pWndParent) { WIN_ReleaseWndPtr( wndPtr ); return 0; } /* Windows hides the window first, then shows it again * including the WM_SHOWWINDOW messages and all */ if (dwStyle & WS_VISIBLE) ShowWindow( hwnd, SW_HIDE ); retvalue = wndPtr->parent->hwndSelf; /* old parent */ if (pWndParent != wndPtr->parent) { 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 (parent == GetDesktopWindow()) { if(root_window == DefaultRootWindow(display)) X11DRV_CreateWindow(wndPtr->hwndSelf); } 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_ReleaseWndPtr( pWndParent ); WIN_ReleaseWndPtr( wndPtr ); /* SetParent additionally needs to make hwnd the topmost window in the x-order and send the expected WM_WINDOWPOSCHANGING and WM_WINDOWPOSCHANGED notification messages. */ SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE| ((dwStyle & WS_VISIBLE)?SWP_SHOWWINDOW:0)); /* FIXME: a WM_MOVE is also generated (in the DefWindowProc handler * for WM_WINDOWPOSCHANGED) in Windows, should probably remove SWP_NOMOVE */ return retvalue; } /******************************************************************* * EnableWindow (X11DRV.@) */ BOOL X11DRV_EnableWindow( HWND hwnd, BOOL enable ) { WND *wndPtr; BOOL retvalue; Window w; if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE; retvalue = ((wndPtr->dwStyle & WS_DISABLED) != 0); if (enable && (wndPtr->dwStyle & WS_DISABLED)) { /* Enable window */ wndPtr->dwStyle &= ~WS_DISABLED; if ((wndPtr->dwExStyle & WS_EX_MANAGED) && (w = X11DRV_WND_GetXWindow( wndPtr ))) set_wm_hint( w, InputHint, TRUE ); SendMessageA( hwnd, WM_ENABLE, TRUE, 0 ); } else if (!enable && !(wndPtr->dwStyle & WS_DISABLED)) { SendMessageA( wndPtr->hwndSelf, WM_CANCELMODE, 0, 0 ); /* Disable window */ wndPtr->dwStyle |= WS_DISABLED; if ((wndPtr->dwExStyle & WS_EX_MANAGED) && (w = X11DRV_WND_GetXWindow( wndPtr ))) set_wm_hint( w, InputHint, FALSE ); if (hwnd == GetFocus()) SetFocus( 0 ); /* A disabled window can't have the focus */ if (hwnd == GetCapture()) ReleaseCapture(); /* A disabled window can't capture the mouse */ SendMessageA( hwnd, WM_ENABLE, FALSE, 0 ); } WIN_ReleaseWndPtr(wndPtr); return retvalue; } /***************************************************************** * SetFocus (X11DRV.@) * * Set the X focus. * Explicit colormap management seems to work only with OLVWM. */ void X11DRV_SetFocus( HWND hwnd ) { XWindowAttributes win_attr; Window win; WND *wndPtr = WIN_FindWndPtr( hwnd ); WND *w = wndPtr; if (!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 (root_window != DefaultRootWindow(display)) goto done; while (w && !((X11DRV_WND_DATA *) w->pDriverData)->window) w = w->parent; if (!w) w = wndPtr; if (w->dwExStyle & WS_EX_MANAGED) goto done; if (!hwnd) /* If setting the focus to 0, uninstall the colormap */ { if (X11DRV_PALETTE_PaletteFlags & X11DRV_PALETTE_PRIVATE) TSXUninstallColormap( display, X11DRV_PALETTE_PaletteXColormap ); } else if ((win = X11DRV_WND_FindXWindow(wndPtr))) { /* Set X focus and install colormap */ if (TSXGetWindowAttributes( display, win, &win_attr ) && (win_attr.map_state == IsViewable)) { /* 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 ); X11DRV_Synchronize(); } } done: WIN_ReleaseWndPtr( wndPtr ); } /***************************************************************** * SetWindowText (X11DRV.@) */ BOOL X11DRV_SetWindowText( HWND hwnd, LPCWSTR text ) { UINT count; char *buffer; static UINT text_cp = (UINT)-1; Window win; WND *wndPtr = WIN_FindWndPtr( hwnd ); if (!wndPtr) return FALSE; if ((win = X11DRV_WND_GetXWindow(wndPtr))) { 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"); WIN_ReleaseWndPtr( wndPtr ); return FALSE; } WideCharToMultiByte(text_cp, 0, text, -1, buffer, count, NULL, NULL); TSXStoreName( display, win, buffer ); TSXSetIconName( display, win, buffer ); HeapFree( GetProcessHeap(), 0, buffer ); } WIN_ReleaseWndPtr( wndPtr ); return TRUE; } /********************************************************************** * X11DRV_SetWindowIcon * * 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 */ HICON X11DRV_SetWindowIcon( HWND hwnd, HICON icon, BOOL small ) { WND *wndPtr = WIN_FindWndPtr( hwnd ); int index = small ? GCL_HICONSM : GCL_HICON; HICON old; if (!wndPtr) return 0; old = GetClassLongW( hwnd, index ); SetClassLongW( hwnd, index, icon ); SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER ); if (wndPtr->dwExStyle & WS_EX_MANAGED) { Window win = X11DRV_WND_GetXWindow(wndPtr); XWMHints* wm_hints = TSXGetWMHints( display, win ); if (!wm_hints) wm_hints = TSXAllocWMHints(); if (wm_hints) { set_icon_hints( wndPtr, wm_hints ); TSXSetWMHints( display, win, wm_hints ); TSXFree( wm_hints ); } } WIN_ReleaseWndPtr( wndPtr ); return old; }