Inside Wine; surface

[目次] (https://qiita.com/Yutaka_Aoki/items/9d38ea98406f4096435b)

# Inside Wine; surface

## surface が存在するかどうか

### 結論

surface が存在するかどうかは、以下の関数で決定されている。この関数内で、surface != NULL になった場合、__wine_set_visible_region() は、その中から呼び出される dibdrv_set_window_surface() の内部にて、PHYSDEV dev を struct windrv_physdev *wnd_dev にし、ドライバーの関数セットを window_driver にする。詳細は、もう一度、ずっと下の方で述べる。

略して紹介:

```c:~/wine/dlls/user32/painting.c
static void update_visible_region( struct dce *dce )
{
    struct window_surface *surface = NULL;
    
    /* 中略 */
    
    /* don't use a surface to paint the client area of OpenGL windows */
    if (!(paint_flags & SET_WINPOS_PIXEL_FORMAT) || (flags & DCX_WINDOW)) {
        win = WIN_GetPtr( top_win );
        if (win && win != WND_DESKTOP && win != WND_OTHER_PROCESS) {
            surface = win->surface;
            if (surface) window_surface_add_ref( surface );
            WIN_ReleasePtr( win );
        }
    }
	
    if (!surface) top_rect = get_virtual_screen_rect();
    
    
    /*
    	以下の関数は、
    	
    	a. surface == NULL の時は、「デフォルト」のままなので、
    	
    	1. PHYSDEV dev の指す実体は、X11DRV_PDEVICE 型とする。
    	2. 関数セットであるところの、dev->funcs を &x11drv_funcs とする。
    	
    	b. surface != NULL の時は、
    	
    	デフォルトに関数群をオーバーライドするように（追加的に）、
    	
    	3. PHYSDEV dev の指す実体を struct windrv_physdev 型にする。
    	4. 関数セットであるところの、dev->funcs を &window_driver で
    	  「オーバーライド」する。
   	*/
    __wine_set_visible_region( dce->hdc, vis_rgn, &win_rect, &top_rect, surface );
    
    
    if (surface) window_surface_release( surface );
}
```

ここで、win->surface について、実験とソース解析の両方の見地から、多くの場合に当てはまることを述べておく。それは、

```c:
HWND  hwnd     = (自分の HWND);
WND   *my_win  = WIN_GetPtr( hwnd );
```

の時

```c:
my_win->surface != NULL : hwnd の親が Desktop Window の場合
                          (自分が Desktop に浮いている時)

my_win->surface == NULL : hwnd の親が Desktop Window でない場合
                          (自分が 他のウィンドウの子ウィンドウの時)
```

ということである。update_visible_region() においては、

```c:
WND *win;
/* 中略 */
HWND top_win;
win = WIN_GetPtr( top_win );
```

となっている。「top_win」は、名前から推定すると、自分の親のうち、Desktop Window を親とするような（画面に浮いているような）Window であるはずである。それと、上記の win->surface の話とを合わせれば、「必ず」win->surface != NULL と考えて「大体」正しい。

だから、update_visible_region() を見ての結論は、自分が Desktop に浮いている場合は、surface == NULL であり、逆に、そうではなくて、別の Window の中に入っている場合(=子ウィンドウ)には、surface != NULL になるということである。

そして、surface != NULL の場合の、surface の値は、最も外側の Window ( = Desktop に浮いている Window ) が同じであれば、共通になるということである。まとめると、こんな感じになる :


| 自分の親     | surface              | my_win->surface | win       |
|:-------------|:--------------------:|:---------------:|:---------:|
| Desktop      | NULL                 | 非NULL          | == my_win |
| 非Desktop    | win->surface(非NULL) | NULL            | != my_win |


## struct window_surface

```c:~/wine/include/wine/gdi_driver.h
/* support for window surfaces */
struct window_surface;

struct window_surface_funcs {
    void  (*lock)( struct window_surface *surface );
    void  (*unlock)( struct window_surface *surface );
    void* (*get_info)( struct window_surface *surface, BITMAPINFO *info );
    RECT* (*get_bounds)( struct window_surface *surface );
    void  (*set_region)( struct window_surface *surface, HRGN region );
    void  (*flush)( struct window_surface *surface );
    void  (*destroy)( struct window_surface *surface );
};

struct window_surface {
    const struct window_surface_funcs *funcs; /* driver-specific implementations  */
    struct list                        entry; /* entry in global list managed by user32 */
    LONG                               ref;   /* reference count */
    RECT                               rect;  /* constant, no locking needed */
    /* driver-specific fields here */
};
```

## struct x11drv_window_surface : struct window_surface を継承

```c:~/wine/dlls/winex11.drv/bitblt.c
struct x11drv_window_surface {
	struct window_surface header;
	Window                window;
	GC                    gc;
	XImage               *image;
	RECT                  bounds;
	BOOL                  byteswap;
	BOOL                  is_argb;
	DWORD                 alpha_bits;
	COLORREF              color_key;
	HRGN                  region;
	void                 *bits;
#ifdef HAVE_LIBXXSHM
	XShmSegmentInfo       shminfo;
#endif
	CRITICAL_SECTION      crit;
	BITMAPINFO            info;   /* variable size, must be last */
};
```

## surface データの相互関係

1. struct x11drv_window_surface は、先頭に struct window_surface そのものを、
header という名称のメンバとして持っている。だから、キャストすると、互いに移りあえる。
2. struct x11drv_win_data は、struct window_surface へのポインタを持っている。
3. struct windrv_physdev は、struct window_surface へのポインタを持っている。

## struct window_surface の動的型判定

WINE は、C 言語で書かれていて、C++ 言語ではないので、言語レベルでは「継承」の概念はない。しかし、struct window_surface と struct x11drv_window_surface の関係は、継承の概念と考えることが出来る。struct window_surface の「継承後の本当の型」は、struct x11drv_window_surface 以外に、struct android_window_surface や struct macdrv_window_surface がある。

継承後の型は、概念的には、以下のような関数で判定できる。ただし、x11drv_surface_funcs, android_surface_funcs, macdrv_surface_funcs は、全てそれぞれの環境用の、*.c ファイルの中で static 宣言されているので、他の *.c ファイルからは参照できない。そのため以下の関数は「擬似関数」であって、実際にコンパイルする事は出来ない。

ただし、x11(Linux), android, mac の違いは、かなりグローバルな話なので、おそらく絶対にどこかのグローバル変数か、マクロ定義によって、判断が付くはず。それよりも、struct window_surface が必ず継承されて使用されているかの方を調査する必要がある。


```c:擬似関数
int __get_type_of_surface( struct window_surface *p_window_surface )
{
	if ( p_window_surface->funcs == &x11drv_surface_funcs ) {
		// *p_window_surface は、struct x11drv_window_surface 型である。
		return	1;
	}
	else if ( p_window_surface->funcs == &android_surface_funcs ) {
		// *p_window_surface は、struct android_window_surface 型である。
		return	2;
	}
	else if ( p_window_surface->funcs == &macdrv_surface_funcs ) {
		// *p_window_surface は、struct macdrv_window_surface 型である。
		return	3;
	}
	else {
		// 多分、有り得ない :
		return	0;
	}
}
```

## struct window_surface から struct x11drv_window_surface の取得法

### 変換関数

struct window_surface から、struct x11drv_window_surface を取得するのは、関数 get_x11_surface() で行える。ただし、実際にやっている事は、「キャスト」するだけである。なぜなら、既に述べたように、後者は、前者を先頭部分に「含んでいる」ためである。これは、既に述べたように、C++ でいうところの、継承クラスと基本クラスの関係に他ならない。


```c:~/wine/dlls/winex11.drv/bitblt.c
static struct x11drv_window_surface *get_x11_surface( struct window_surface *surface )
{
	return (struct x11drv_window_surface *)surface;
}
```

### 実際に使われている例


```c:~/wine/dlls/winex11.drv/bitblt.c
static void x11drv_surface_flush( struct window_surface *window_surface )
{
	struct x11drv_window_surface *surface = get_x11_surface( window_surface );
	unsigned char *src = surface->bits;
	unsigned char *dst = (unsigned char *)surface->image->data;
```



## struct window_surface の作成

~/wine/dlls/user32/winpos.c の set_window_pos() の中で、

1. USER_Driver->pWindowPosChanging() : X11DRV_WindowPosChanging
2. SERVER_START_REQ( set_window_pos ), wine_server_call
3. USER_Driver->pWindowPosChanged() : X11DRV_WindowPosChanged

の順序で呼び出される。

X11DRV_WindowPosChanging() の中で、get_surface_rect で求めた surface_rect を引数にして、create_surface() が呼び出される：

```c:
*surface = create_surface( data->whole_window, &data->vis, &surface_rect, key, FALSE );
```

ここの部分は、X11DRV_WindowPosChanging の最後の引数の

```
 ( ・・・, struct window_surface **surface )
```

に対して書き込みを行っていることになる。


// 本質部分を抜粋 :

```c:~/wine/dlls/user32/winpos.c
BOOL set_window_pos( HWND hwnd, HWND insert_after,
					 UINT swp_flags,
                     const RECT *window_rect,
                     const RECT *client_rect,
                     const RECT *valid_rects )
{
	WND *win;
	struct window_surface *old_surface, *new_surface = NULL;
	
	visible_rect = *window_rect;
	USER_Driver->pWindowPosChanging( hwnd, insert_after, swp_flags,
	                                 window_rect, client_rect, &visible_rect,
	                                 &new_surface );
	
	win = WIN_GetPtr( hwnd )
	
	SERVER_START_REQ( set_window_pos ) {
	  wine_server_call( req );
	  win->surface      = new_surface;
	}
	
	USER_Driver->pWindowPosChanged( hwnd, insert_after, swp_flags,
									window_rect, client_rect, &visible_rect,
	                                valid_rects,
	                                new_surface );
}
```

```c:~/wine/dlls/winex11.drv/window.c
void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after,
									UINT swp_flags,
                                    const RECT *rectWindow,
                                    const RECT *rectClient,
                                    const RECT *visible_rect,
                                    const RECT *valid_rects,
                                    struct window_surface *surface )
{
	/*中略*/
    
    if (data->vis.visualid == default_visual.visualid) {
        if (surface) window_surface_add_ref( surface );
        if (data->surface) window_surface_release( data->surface );
        data->surface = surface;
    }
    
	/*中略*/
}
```

```c:~/wine/dlls/winex11.drv/window.c
static inline BOOL get_surface_rect( const RECT *visible_rect, RECT *surface_rect )
{
	*surface_rect = get_virtual_screen_rect();

	if (!IntersectRect( surface_rect, surface_rect, visible_rect )) return FALSE;
	OffsetRect( surface_rect, -visible_rect->left, -visible_rect->top );
	surface_rect->left &= ~31;
	surface_rect->top  &= ~31;
	surface_rect->right  = max( surface_rect->left + 32, (surface_rect->right + 31) & ~31 );
	surface_rect->bottom = max( surface_rect->top + 32, (surface_rect->bottom + 31) & ~31 );
	return TRUE;
}
```

```c:~/wine/dlls/winex11.drv/window.c
struct window_surface *create_surface( Window window, const XVisualInfo *vis, const RECT *rect,
									   COLORREF color_key, BOOL use_alpha )
{
	const XPixmapFormatValues *format = pixmap_formats[vis->depth];
	struct x11drv_window_surface *surface;
	int width = rect->right - rect->left, height = rect->bottom - rect->top;
	int colors = format->bits_per_pixel <= 8 ? 1 << format->bits_per_pixel : 3;

	surface = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
						 FIELD_OFFSET( struct x11drv_window_surface, info.bmiColors[colors] ));
	if (!surface) return NULL;
	surface->info.bmiHeader.biSize        = sizeof(surface->info.bmiHeader);
	surface->info.bmiHeader.biWidth       = width;
	surface->info.bmiHeader.biHeight      = -height; /* top-down */
	surface->info.bmiHeader.biPlanes      = 1;
	surface->info.bmiHeader.biBitCount    = format->bits_per_pixel;
	surface->info.bmiHeader.biSizeImage   = get_dib_image_size( &surface->info );
	set_color_info( vis, &surface->info, use_alpha );

	InitializeCriticalSection( &surface->crit );
	surface->crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": surface");

	surface->header.funcs = &x11drv_surface_funcs;
	surface->header.rect  = *rect;
	surface->header.ref   = 1;
	surface->window = window;
	surface->is_argb = (use_alpha && vis->depth == 32 && surface->info.bmiHeader.biCompression == BI_RGB);
	set_color_key( surface, color_key );
	reset_bounds( &surface->bounds );

#ifdef HAVE_LIBXXSHM
	surface->image = create_shm_image( vis, width, height, &surface->shminfo );
	if (!surface->image)
#endif
	{
		surface->image = XCreateImage( gdi_display, vis->visual, vis->depth, ZPixmap, 0, NULL,
									   width, height, 32, 0 );
		if (!surface->image) goto failed;
		surface->image->data = HeapAlloc( GetProcessHeap(), 0, surface->info.bmiHeader.biSizeImage );
		if (!surface->image->data) goto failed;
	}

	surface->gc = XCreateGC( gdi_display, window, 0, NULL );
	XSetSubwindowMode( gdi_display, surface->gc, IncludeInferiors );
	surface->byteswap = image_needs_byteswap( surface->image, is_r8g8b8(vis), format->bits_per_pixel );

	if (vis->depth == 32 && !surface->is_argb)
		surface->alpha_bits = ~(vis->red_mask | vis->green_mask | vis->blue_mask);

	if (surface->byteswap || format->bits_per_pixel == 4 || format->bits_per_pixel == 8)
	{
		/* allocate separate surface bits if byte swapping or palette mapping is required */
		if (!(surface->bits  = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
										  surface->info.bmiHeader.biSizeImage )))
			goto failed;
	}
	else surface->bits = surface->image->data;

	TRACE( "created %p for %lx %s bits %p-%p image %p\n", surface, window, wine_dbgstr_rect(rect),
		   surface->bits, (char *)surface->bits + surface->info.bmiHeader.biSizeImage,
		   surface->image->data );

	return &surface->header;

failed:
	x11drv_surface_destroy( &surface->header );
	return NULL;
}
```



## HWND から struct x11drv_window_surface を取得する方法

(X Window 上で動いていて、かつ、HWND の Window が surface を持っているときに限定)

```c:
HWND    hwnd = xxxx;   // 入力
struct x11drv_win_data *data			= get_win_data( hwnd );
struct window_surface *p_wnd_surface	= data->surface;
struct x11drv_window_surface *surface	= get_x11_surface( p_wnd_surface );
```

## HDC から struct x11drv_window_surface を取得する方法

実際に使うには、条件を満たしているかをあらかじめ知っている方が良い。ただし、条件が何なのかは今のところよく分かってない。以下の関数では、動的型判定を用いている。以下は、「pLineTo」を介しての場合。

(window_driver は、外部から参照できない。)

```c:独自関数
struct x11drv_window_surface __get_x11_wnd_surface_of_hdc( HDC hdc )
{
	DC		*dc    = get_dc_ptr( hdc );
	
	//update_dc( dc );
	PHYSDEV					dev			= GET_DC_PHYSDEV( dc, pLineTo );
	x11drv_window_surface	*surface	= NULL;
	
	if ( dev->funcs == &window_driver ) {
		// dev が struct windrv_physdev * 型である場合 :
		
		struct windrv_physdev *p_wnd_physdev    = get_windrv_physdev( dev );
		struct window_surface *p_wnd_surface	= p_wnd_physdev->surface;
		
		if ( p_wnd_surface != NULL ) {
			surface		= get_x11_surface( p_wnd_surface );
		}
    }
    
    release_dc_ptr( dc );
    
	return	surface;
}
```

## surface が存在するかどうか

### Win32 で描画系の全ての始まりは、HDC を取得することから。

Win32 で描画系 API を使用する際、全ては、HDC を取得することから始まる。

典型的な API としてよく使われる以下の関数達は、内部で、GetDCEx() を呼び出す :

```
1. HDC WINAPI GetDC( HWND hwnd );
2. HDC WINAPI GetWindowDC( HWND hwnd );
3. HDC WINAPI BeginPaint( HWND hwnd, PAINTSTRUCT *lps );
```

### BeginPaint() については

```
BeginPaint()
-->send_erase()
-->GetDCEx()
```

の順になる。

### では、肝心の GetDCEx() はどうなってるか。

[c:~/wine/dlls/user32/painting.c]
の GetDCEx() から始まる関数列

```c:
HDC WINAPI GetDCEx( HWND hwnd, HRGN hrgnClip, DWORD flags )
--> update_visible_region()
--> __wine_set_visible_region()
--> dibdrv_set_window_surface( DC *dc, struct window_surface *surface )
-->
  if ( surface != NULL ) {
    call window_driver.pCreateDC( &dc->physDev, NULL, NULL, NULL, NULL )
    --> windrv_CreateDC()
    --> push_dc_driver( dev, &physdev->dev, &window_driver );
    --> ドライバーの関数セットが window_driver になる。
  }
```

### windrv_CreateDC()

```c:~/wine/dlls/gdi32/dibdrv/dc.c
static BOOL windrv_CreateDC( PHYSDEV *dev, LPCWSTR driver, LPCWSTR device,
							 LPCWSTR output, const DEVMODEW *devmode )
{
	struct windrv_physdev *physdev = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physdev) );

	if (!physdev) return FALSE;

	if (!dib_driver.pCreateDC( dev, NULL, NULL, NULL, NULL ))
	{
		HeapFree( GetProcessHeap(), 0, physdev );
		return FALSE;
	}
	physdev->dibdrv = get_dibdrv_pdev( *dev );
	push_dc_driver( dev, &physdev->dev, &window_driver );
	return TRUE;
}
```

### update_visible_region()

略さずに全掲載 :

```c:~/wine/dlls/user32/painting.c

/***********************************************************************
 *		update_visible_region
 *
 * Set the visible region and X11 drawable for the DC associated to
 * a given window.
 */
static void update_visible_region( struct dce *dce )
{
    struct window_surface *surface = NULL;
    NTSTATUS status;
    HRGN vis_rgn = 0;
    HWND top_win = 0;
    DWORD flags = dce->flags;
    DWORD paint_flags = 0;
    size_t size = 256;
    RECT win_rect, top_rect;
    WND *win;

    /* don't clip siblings if using parent clip region */
    if (flags & DCX_PARENTCLIP) flags &= ~DCX_CLIPSIBLINGS;

    /* fetch the visible region from the server */
    do
    {
        RGNDATA *data = HeapAlloc( GetProcessHeap(), 0, sizeof(*data) + size - 1 );
        if (!data) return;

        SERVER_START_REQ( get_visible_region )
        {
            req->window  = wine_server_user_handle( dce->hwnd );
            req->flags   = flags;
            wine_server_set_reply( req, data->Buffer, size );
            if (!(status = wine_server_call( req )))
            {
                size_t reply_size = wine_server_reply_size( reply );
                data->rdh.dwSize   = sizeof(data->rdh);
                data->rdh.iType    = RDH_RECTANGLES;
                data->rdh.nCount   = reply_size / sizeof(RECT);
                data->rdh.nRgnSize = reply_size;
                vis_rgn = ExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );

                top_win         = wine_server_ptr_handle( reply->top_win );
                win_rect.left   = reply->win_rect.left;
                win_rect.top    = reply->win_rect.top;
                win_rect.right  = reply->win_rect.right;
                win_rect.bottom = reply->win_rect.bottom;
                top_rect.left   = reply->top_rect.left;
                top_rect.top    = reply->top_rect.top;
                top_rect.right  = reply->top_rect.right;
                top_rect.bottom = reply->top_rect.bottom;
                paint_flags     = reply->paint_flags;
            }
            else size = reply->total_size;
        }
        SERVER_END_REQ;
        HeapFree( GetProcessHeap(), 0, data );
    } while (status == STATUS_BUFFER_OVERFLOW);

    if (status || !vis_rgn) return;

    USER_Driver->pGetDC( dce->hdc, dce->hwnd, top_win, &win_rect, &top_rect, flags );

    if (dce->clip_rgn) CombineRgn( vis_rgn, vis_rgn, dce->clip_rgn,
                                   (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF );

    /* don't use a surface to paint the client area of OpenGL windows */
    if (!(paint_flags & SET_WINPOS_PIXEL_FORMAT) || (flags & DCX_WINDOW))
    {
        win = WIN_GetPtr( top_win );
        if (win && win != WND_DESKTOP && win != WND_OTHER_PROCESS)
        {
            surface = win->surface;
            if (surface) window_surface_add_ref( surface );
            WIN_ReleasePtr( win );
        }
    }

    if (!surface) top_rect = get_virtual_screen_rect();
    __wine_set_visible_region( dce->hdc, vis_rgn, &win_rect, &top_rect, surface );
    if (surface) window_surface_release( surface );
}
```


### dibdrv_set_window_surface()


略さずに全掲載 :

```c:~/wine/dlls/gdi32/dibdrv/dc.c
void dibdrv_set_window_surface( DC *dc, struct window_surface *surface )
{
	char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
	BITMAPINFO *info = (BITMAPINFO *)buffer;
	RECT rect;
	void *bits;
	PHYSDEV windev;
	struct windrv_physdev *physdev;
	struct dibdrv_physdev *dibdrv;

	TRACE( "%p %p\n", dc->hSelf, surface );

	windev = pop_dc_driver( dc, &window_driver );

	if (surface)
	{
		if (windev) push_dc_driver( &dc->physDev, windev, windev->funcs );
		else
		{
			if (!window_driver.pCreateDC( &dc->physDev, NULL, NULL, NULL, NULL )) return;
			windev = find_dc_driver( dc, &window_driver );
		}

		physdev = get_windrv_physdev( windev );
		window_surface_add_ref( surface );
		if (physdev->surface) window_surface_release( physdev->surface );
		physdev->surface = surface;

		dibdrv = physdev->dibdrv;
		bits = surface->funcs->get_info( surface, info );
		init_dib_info_from_bitmapinfo( &dibdrv->dib, info, bits );
		/* clip the device rect to the surface */
		rect = surface->rect;
		offset_rect( &rect, dc->device_rect.left, dc->device_rect.top );
		intersect_rect( &dc->device_rect, &dc->device_rect, &rect );
		dibdrv->dib.rect = dc->vis_rect;
		offset_rect( &dibdrv->dib.rect, -rect.left, -rect.top );
		dibdrv->bounds = surface->funcs->get_bounds( surface );
		DC_InitDC( dc );
	}
	else if (windev)
	{
		dib_driver.pDeleteDC( pop_dc_driver( dc, &dib_driver ));
		windev->funcs->pDeleteDC( windev );
		DC_InitDC( dc );
	}
}
```


### __wine_set_visible_region()

略さずに全掲載 :

```c:~/wine/dlls/gdi32/clipping.c
void CDECL __wine_set_visible_region( HDC hdc, HRGN hrgn, const RECT *vis_rect, const RECT *device_rect,
                                      struct window_surface *surface )
{
    DC * dc;

    if (!(dc = get_dc_ptr( hdc ))) return;

    TRACE( "%p %p %s %s %p\n", hdc, hrgn,
           wine_dbgstr_rect(vis_rect), wine_dbgstr_rect(device_rect), surface );

    /* map region to DC coordinates */
    OffsetRgn( hrgn, -vis_rect->left, -vis_rect->top );

    if (dc->hVisRgn) DeleteObject( dc->hVisRgn );
    dc->dirty = 0;
    dc->vis_rect = *vis_rect;
    dc->device_rect = *device_rect;
    dc->hVisRgn = hrgn;
    dibdrv_set_window_surface( dc, surface );
    DC_UpdateXforms( dc );
    update_dc_clipping( dc );
    release_dc_ptr( dc );
}
```


### set_window_pos()

略さずに全掲載 :

```c:~/wine/dlls/user32/winpos.c
/***********************************************************************
 *		set_window_pos
 *
 * Backend implementation of SetWindowPos.
 */
BOOL set_window_pos( HWND hwnd, HWND insert_after, UINT swp_flags,
					 const RECT *window_rect, const RECT *client_rect, const RECT *valid_rects )
{
	WND *win;
	HWND surface_win = 0, parent = GetAncestor( hwnd, GA_PARENT );
	BOOL ret, needs_update = FALSE;
	RECT visible_rect, old_visible_rect, old_window_rect, old_client_rect;
	struct window_surface *old_surface, *new_surface = NULL;

	if (!parent || parent == GetDesktopWindow())
	{
		new_surface = &dummy_surface;  /* provide a default surface for top-level windows */
		window_surface_add_ref( new_surface );
	}
	visible_rect = *window_rect;
	USER_Driver->pWindowPosChanging( hwnd, insert_after, swp_flags,
									 window_rect, client_rect, &visible_rect, &new_surface );

	WIN_GetRectangles( hwnd, COORDS_SCREEN, &old_window_rect, NULL );

	if (!(win = WIN_GetPtr( hwnd )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS)
	{
		if (new_surface) window_surface_release( new_surface );
		return FALSE;
	}
	old_visible_rect = win->visible_rect;
	old_client_rect = win->rectClient;
	old_surface = win->surface;
	if (old_surface != new_surface) swp_flags |= SWP_FRAMECHANGED;  /* force refreshing non-client area */

	SERVER_START_REQ( set_window_pos )
	{
		req->handle        = wine_server_user_handle( hwnd );
		req->previous      = wine_server_user_handle( insert_after );
		req->swp_flags     = swp_flags;
		req->window.left   = window_rect->left;
		req->window.top    = window_rect->top;
		req->window.right  = window_rect->right;
		req->window.bottom = window_rect->bottom;
		req->client.left   = client_rect->left;
		req->client.top    = client_rect->top;
		req->client.right  = client_rect->right;
		req->client.bottom = client_rect->bottom;
		if (!EqualRect( window_rect, &visible_rect ) || !IsRectEmpty( &valid_rects[0] ))
		{
			wine_server_add_data( req, &visible_rect, sizeof(visible_rect) );
			if (!IsRectEmpty( &valid_rects[0] ))
				wine_server_add_data( req, valid_rects, 2 * sizeof(*valid_rects) );
		}
		if (new_surface) req->paint_flags |= SET_WINPOS_PAINT_SURFACE;
		if (win->pixel_format) req->paint_flags |= SET_WINPOS_PIXEL_FORMAT;

		if ((ret = !wine_server_call( req )))
		{
			win->dwStyle    = reply->new_style;
			win->dwExStyle  = reply->new_ex_style;
			win->rectWindow = *window_rect;
			win->rectClient = *client_rect;
			win->visible_rect = visible_rect;
			win->surface      = new_surface;
			surface_win       = wine_server_ptr_handle( reply->surface_win );
			needs_update      = reply->needs_update;
			if (GetWindowLongW( win->parent, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
			{
				RECT client;
				GetClientRect( win->parent, &client );
				mirror_rect( &client, &win->rectWindow );
				mirror_rect( &client, &win->rectClient );
				mirror_rect( &client, &win->visible_rect );
			}
			/* if an RTL window is resized the children have moved */
			if (win->dwExStyle & WS_EX_LAYOUTRTL &&
				client_rect->right - client_rect->left != old_client_rect.right - old_client_rect.left)
				win->flags |= WIN_CHILDREN_MOVED;
		}
	}
	SERVER_END_REQ;

	if (ret)
	{
		if (needs_update) update_surface_region( surface_win );
		if (((swp_flags & SWP_AGG_NOPOSCHANGE) != SWP_AGG_NOPOSCHANGE) ||
			(swp_flags & (SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_STATECHANGED | SWP_FRAMECHANGED)))
			invalidate_dce( win, &old_window_rect );
	}

	WIN_ReleasePtr( win );

	if (ret)
	{
		TRACE( "win %p surface %p -> %p\n", hwnd, old_surface, new_surface );
		register_window_surface( old_surface, new_surface );
		if (old_surface)
		{
			if (!IsRectEmpty( valid_rects ))
			{
				move_window_bits( hwnd, old_surface, new_surface, &visible_rect,
								  &old_visible_rect, window_rect, valid_rects );
				valid_rects = NULL;  /* prevent the driver from trying to also move the bits */
			}
			window_surface_release( old_surface );
		}
		else if (surface_win && surface_win != hwnd)
		{
			if (!IsRectEmpty( valid_rects ))
			{
				RECT rects[2];
				int x_offset = old_visible_rect.left - visible_rect.left;
				int y_offset = old_visible_rect.top - visible_rect.top;

				/* if all that happened is that the whole window moved, copy everything */
				if (!(swp_flags & SWP_FRAMECHANGED) &&
					old_visible_rect.right  - visible_rect.right  == x_offset &&
					old_visible_rect.bottom - visible_rect.bottom == y_offset &&
					old_client_rect.left    - client_rect->left   == x_offset &&
					old_client_rect.right   - client_rect->right  == x_offset &&
					old_client_rect.top     - client_rect->top    == y_offset &&
					old_client_rect.bottom  - client_rect->bottom == y_offset &&
					EqualRect( &valid_rects[0], client_rect ))
				{
					rects[0] = visible_rect;
					rects[1] = old_visible_rect;
					valid_rects = rects;
				}
				move_window_bits_parent( hwnd, surface_win, window_rect, valid_rects );
				valid_rects = NULL;  /* prevent the driver from trying to also move the bits */
			}
		}

		USER_Driver->pWindowPosChanged( hwnd, insert_after, swp_flags, window_rect,
										client_rect, &visible_rect, valid_rects, new_surface );
	}
	else if (new_surface) window_surface_release( new_surface );

	return ret;
}
```

## X11DRV_WindowPosChanging

略さず掲載 :

```c:~/wine/dlls/winex11.drv/window.c
/***********************************************************************
 *		WindowPosChanging   (X11DRV.@)
 */
void CDECL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flags,
									 const RECT *window_rect, const RECT *client_rect, RECT *visible_rect,
									 struct window_surface **surface )
{
	struct x11drv_win_data *data = get_win_data( hwnd );
	RECT surface_rect;
	DWORD flags;
	COLORREF key;
	BOOL layered = GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED;

	if (!data && !(data = X11DRV_create_win_data( hwnd, window_rect, client_rect ))) return;

	/* check if we need to switch the window to managed */
	if (!data->managed && data->whole_window && is_window_managed( hwnd, swp_flags, window_rect ))
	{
		TRACE( "making win %p/%lx managed\n", hwnd, data->whole_window );
		release_win_data( data );
		unmap_window( hwnd );
		if (!(data = get_win_data( hwnd ))) return;
		data->managed = TRUE;
	}

	*visible_rect = *window_rect;
	X11DRV_window_to_X_rect( data, visible_rect );

	/* create the window surface if necessary */

	if (!data->whole_window && !data->embedded) goto done;
	if (swp_flags & SWP_HIDEWINDOW) goto done;
	if (data->use_alpha) goto done;
	if (!get_surface_rect( visible_rect, &surface_rect )) goto done;

	if (*surface) window_surface_release( *surface );
	*surface = NULL;  /* indicate that we want to draw directly to the window */

	if (data->embedded) goto done;
	if (data->whole_window == root_window) goto done;
	if (data->client_window) goto done;
	if (!client_side_graphics && !layered) goto done;

	if (data->surface)
	{
		if (EqualRect( &data->surface->rect, &surface_rect ))
		{
			/* existing surface is good enough */
			window_surface_add_ref( data->surface );
			*surface = data->surface;
			goto done;
		}
	}
	else if (!(swp_flags & SWP_SHOWWINDOW) && !(GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)) goto done;

	if (!layered || !GetLayeredWindowAttributes( hwnd, &key, NULL, &flags ) || !(flags & LWA_COLORKEY))
		key = CLR_INVALID;

	*surface = create_surface( data->whole_window, &data->vis, &surface_rect, key, FALSE );

done:
	release_win_data( data );
}
```

## X11DRV_WindowPosChanged

略さず掲載 :

```c:~/wine/dlls/winex11.drv/window.c
/***********************************************************************
 *		WindowPosChanged   (X11DRV.@)
 */
void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags,
									const RECT *rectWindow, const RECT *rectClient,
									const RECT *visible_rect, const RECT *valid_rects,
									struct window_surface *surface )
{
	struct x11drv_thread_data *thread_data;
	struct x11drv_win_data *data;
	DWORD new_style = GetWindowLongW( hwnd, GWL_STYLE );
	RECT old_window_rect, old_whole_rect, old_client_rect;
	int event_type;

	if (!(data = get_win_data( hwnd ))) return;

	thread_data = x11drv_thread_data();

	old_window_rect = data->window_rect;
	old_whole_rect  = data->whole_rect;
	old_client_rect = data->client_rect;
	data->window_rect = *rectWindow;
	data->whole_rect  = *visible_rect;
	data->client_rect = *rectClient;
	if (data->vis.visualid == default_visual.visualid)
	{
		if (surface) window_surface_add_ref( surface );
		if (data->surface) window_surface_release( data->surface );
		data->surface = surface;
	}

	TRACE( "win %p window %s client %s style %08x flags %08x\n",
		   hwnd, wine_dbgstr_rect(rectWindow), wine_dbgstr_rect(rectClient), new_style, swp_flags );

	if (!IsRectEmpty( &valid_rects[0] ))
	{
		Window window = data->whole_window;
		int x_offset = old_whole_rect.left - data->whole_rect.left;
		int y_offset = old_whole_rect.top - data->whole_rect.top;

		/* if all that happened is that the whole window moved, copy everything */
		if (!(swp_flags & SWP_FRAMECHANGED) &&
			old_whole_rect.right   - data->whole_rect.right   == x_offset &&
			old_whole_rect.bottom  - data->whole_rect.bottom  == y_offset &&
			old_client_rect.left   - data->client_rect.left   == x_offset &&
			old_client_rect.right  - data->client_rect.right  == x_offset &&
			old_client_rect.top    - data->client_rect.top    == y_offset &&
			old_client_rect.bottom - data->client_rect.bottom == y_offset &&
			EqualRect( &valid_rects[0], &data->client_rect ))
		{
			/* if we have an X window the bits will be moved by the X server */
			if (!window && (x_offset != 0 || y_offset != 0))
			{
				release_win_data( data );
				move_window_bits( hwnd, window, &old_whole_rect, visible_rect,
								  &old_client_rect, rectClient, rectWindow );
				if (!(data = get_win_data( hwnd ))) return;
			}
		}
		else
		{
			release_win_data( data );
			move_window_bits( hwnd, window, &valid_rects[1], &valid_rects[0],
							  &old_client_rect, rectClient, rectWindow );
			if (!(data = get_win_data( hwnd ))) return;
		}
	}

	XFlush( gdi_display );  /* make sure painting is done before we move the window */

	sync_client_position( data, &old_client_rect, &old_whole_rect );

	if (!data->whole_window)
	{
		BOOL needs_resize = (!data->client_window &&
							 (data->client_rect.right - data->client_rect.left !=
							  old_client_rect.right - old_client_rect.left ||
							  data->client_rect.bottom - data->client_rect.top !=
							  old_client_rect.bottom - old_client_rect.top));
		release_win_data( data );
		if (needs_resize) sync_gl_drawable( hwnd );
		return;
	}

	/* check if we are currently processing an event relevant to this window */
	event_type = 0;
	if (thread_data &&
		thread_data->current_event &&
		thread_data->current_event->xany.window == data->whole_window)
	{
		event_type = thread_data->current_event->type;
		if (event_type != ConfigureNotify && event_type != PropertyNotify &&
			event_type != GravityNotify && event_type != ReparentNotify)
			event_type = 0;  /* ignore other events */
	}

	if (data->mapped && event_type != ReparentNotify)
	{
		if (((swp_flags & SWP_HIDEWINDOW) && !(new_style & WS_VISIBLE)) ||
			(!event_type && !(new_style & WS_MINIMIZE) &&
			 !is_window_rect_mapped( rectWindow ) && is_window_rect_mapped( &old_window_rect )))
		{
			release_win_data( data );
			unmap_window( hwnd );
			if (is_window_rect_fullscreen( &old_window_rect )) reset_clipping_window();
			if (!(data = get_win_data( hwnd ))) return;
		}
	}

	/* don't change position if we are about to minimize or maximize a managed window */
	if (!event_type &&
		!(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE))))
		sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect );

	if ((new_style & WS_VISIBLE) &&
		((new_style & WS_MINIMIZE) || is_window_rect_mapped( rectWindow )))
	{
		if (!data->mapped)
		{
			BOOL needs_icon = !data->icon_pixmap;
			BOOL needs_map = TRUE;

			/* layered windows are mapped only once their attributes are set */
			if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED) needs_map = data->layered;
			release_win_data( data );
			if (needs_icon) fetch_icon_data( hwnd, 0, 0 );
			if (needs_map) map_window( hwnd, new_style );
			return;
		}
		else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE)))
		{
			set_wm_hints( data );
			data->iconic = (new_style & WS_MINIMIZE) != 0;
			TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic );
			if (data->iconic)
				XIconifyWindow( data->display, data->whole_window, data->vis.screen );
			else if (is_window_rect_mapped( rectWindow ))
				XMapWindow( data->display, data->whole_window );
			update_net_wm_states( data );
		}
		else
		{
			if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data );
			if (!event_type) update_net_wm_states( data );
		}
	}

	XFlush( data->display );  /* make sure changes are done before we start painting again */
	if (data->surface && data->vis.visualid != default_visual.visualid)
		data->surface->funcs->flush( data->surface );

	release_win_data( data );
}
```


## 重要メモ

```c:
void CDECL X11DRV_WindowPosChanging(
				HWND hwnd, HWND insert_after,UINT swp_flags,
				const RECT *window_rect, const RECT *client_rect,
				RECT *visible_rect,
				struct window_surface **surface )
```

の中で、

```c:
	struct x11drv_win_data *data = get_win_data( hwnd );
	if ( !data &&
		 !(data = X11DRV_create_win_data(
		 				hwnd,
		 				window_rect,
		 				client_rect ))) return;
```

の部分は、hwnd がまだ初期化されていない時には、X11DRV_create_win_data()
を呼び出して、struct x11drv_win_data *data; の実体を作る、ということであろう。

そして、一度作成された「data」は、get_win_data( hwnd ) で取得できると。そして、X11DRV_create_win_data() の中の、

```c:
	if (!(data = alloc_win_data( display, hwnd ))) return NULL;
```

の部分で、alloc_win_data() の中は、

```c:
	if ((data = HeapAlloc(GetProcessHeap(),
							HEAP_ZERO_MEMORY, sizeof(*data)))) {
	 /* 中略 */
	}
	/* 中略 */
	return data;
```

となっており、data の中は、data->vis, data->use_alpha 以外は、全て 0 クリアされるらしい。次に、

```c:
	if (parent == GetDesktopWindow()) {
		create_whole_window( data );
		TRACE( "win %p/%lx window %s whole %s client %s\n",
			   hwnd, data->whole_window,
			   wine_dbgstr_rect( &data->window_rect ),
			   wine_dbgstr_rect( &data->whole_rect ),
			   wine_dbgstr_rect( &data->client_rect ));
	}
```

となっている。create_whole_window() の中に、次のコードがある :

```c:
	data->whole_window = XCreateWindow(
					data->display, root_window,
					pos.x, pos.y,
					cx, cy, 0,
					data->vis.depth, InputOutput,
					data->vis.visual, mask, &attr );
```

だから、結局、基本的には、親が Desktop の時だけ、data->whole_window != NULL になると考えられる。さて、X11DRV_WindowPosChanging() の

```c:
	if ( !data &&
		 !(data = X11DRV_create_win_data(
		 				hwnd,
		 				window_rect,
		 				client_rect ))) return;
```

以降の行に話を戻す。何行か下に、

```c:
	if (!data->whole_window && !data->embedded) goto done;
```

というコードがある。これは、大体で言えば、親が Desktop 以外の時は(data->whole_window == NULL なので)、done ラベルに飛んで、関数から return する、ということである。その後、

```c:
	if (data->surface) {
		if (EqualRect( &data->surface->rect, &surface_rect )) {
			/* existing surface is good enough */
			window_surface_add_ref( data->surface );
			*surface = data->surface;
			
			FIXME( "c1, hwnd=%08X, goto done\n", (DWORD)hwnd );
			goto done;
		}
	}
	else if ( !(swp_flags & SWP_SHOWWINDOW) &&
			  !(GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)) goto done;
```

というところがあるが、大体の意味は、既に data->surface が NULL でなく、かつ、今までと矩形の位置と寸法に変化がない場合は、今までの data->surface を再利用して、*surface = data->surface としてしまって、そのまま関数から return する、ということである。逆に、矩形が変わった場合は、上記の if 文には入らないので最後の方まで行き、

```c:
	*surface = create_surface(
					data->whole_window,
					&data->vis,
					&surface_rect,
					key,
					FALSE );
```

となる。これは、親が Desktop Window の場合を大前提として、data->surface がまだないか、または、あっても、data->surface と surface_rect の矩形が異なっている場合には、create_surface() が呼び出される、ということである。



さらに、set_window_pos() 関数の中で、以下のようになっている :

```c:
BOOL set_window_pos( HWND hwnd, ・・・ )
{
	WND						*win;
	struct window_surface	*new_surface = NULL;
    
    /* 中略 */
	win = WIN_GetPtr( hwnd )
    
    /* 中略 */
	USER_Driver->pWindowPosChanging(
					hwnd, insert_after, swp_flags,
					window_rect, client_rect,
					&visible_rect, &new_surface );
    /* 中略 */
	win->surface      = new_surface;
    
    /* 中略 */
}
```

どういうことかというと、X11DRV_WindowPosChanging() の中で、

```
	*surface		= xxx;
```

とされると、`set_window_pos()` からみると、`new_surface = xxx` とされたのと同じ事になるから、結局、

```c:
	win->surface	= xxx;
```

とされたのと同じ事になる、ということである。





### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ

### aaa

あああ



## end