wine
Surface
x11drv_window_surface
window_surface

目次

Inside Wine; surface

surface が存在するかどうか

結論

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

略して紹介:

~/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 について、実験とソース解析の両方の見地から、多くの場合に当てはまることを述べておく。それは、

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

の時

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

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

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

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

~/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 を継承

~/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 が必ず継承されて使用されているかの方を調査する必要がある。

擬似関数
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++ でいうところの、継承クラスと基本クラスの関係に他ならない。

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

実際に使われている例

~/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() が呼び出される:

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

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

 ( ・・・, struct window_surface **surface )

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

// 本質部分を抜粋 :

~/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 );
}
~/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;
    }

    /*中略*/
}
~/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;
}
~/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 を持っているときに限定)

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 は、外部から参照できない。)

独自関数
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() から始まる関数列

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()

~/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()

略さずに全掲載 :

~/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()

略さずに全掲載 :

~/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_visibleregion()

略さずに全掲載 :

~/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()

略さずに全掲載 :

~/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

略さず掲載 :

~/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

略さず掲載 :

~/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 );
}

重要メモ

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 );
    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() の中の、

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

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

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

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

    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() の中に、次のコードがある :

    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() の

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

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

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

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

    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 文には入らないので最後の方まで行き、

    *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() 関数の中で、以下のようになっている :

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 とされたのと同じ事になるから、結局、

    win->surface    = xxx;

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

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

aaa

あああ

end