wine/windows/x11drv/wnd.c
Alexandre Julliard 4323004bf1 Create an X connection for each thread, and process X events in the
thread that created the corresponding X window.
Spawn a separate thread to run the desktop message loop in desktop
mode.
2001-05-16 19:52:29 +00:00

471 lines
14 KiB
C

/*
* 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( 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;
}