Home | History | Annotate | Download | only in dxgi
      1 /**************************************************************************
      2  *
      3  * Copyright 2010 Luca Barbieri
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining
      6  * a copy of this software and associated documentation files (the
      7  * "Software"), to deal in the Software without restriction, including
      8  * without limitation the rights to use, copy, modify, merge, publish,
      9  * distribute, sublicense, and/or sell copies of the Software, and to
     10  * permit persons to whom the Software is furnished to do so, subject to
     11  * the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the
     14  * next paragraph) shall be included in all copies or substantial
     15  * portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     20  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
     21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     24  *
     25  **************************************************************************/
     26 
     27 #include <windows.h>
     28 #include <winnt.h>
     29 #include <X11/Xlib.h>
     30 #include <galliumdxgi.h>
     31 
     32 #define DLL_WINE_PREATTACH 8
     33 
     34 #define X11DRV_ESCAPE 6789
     35 #define X11DRV_GET_DISPLAY 0
     36 #define X11DRV_GET_DRAWABLE 1
     37 
     38 /* Wine works in this way: wineserver stores the all window positions
     39  * in (somewhat fictitious) "screen coordinates", and does not itself
     40  * interact with X11.
     41  *
     42  * Instead, it is the responsibliity of the owner of the X window to
     43  * handle ConfigureNotify and inform wineserver that the window
     44  * moved.
     45  *
     46  * This means that we can freely look at window positions non-atomically,
     47  * since they won't get updated until we return and the application
     48  * processes the Win32 message queue.
     49  *
     50  * Of course, if this thread doesn't own the window, we are screwed.
     51  *
     52  * It might be a good idea to integrate this code in winex11.drv.
     53  */
     54 
     55 struct WineDXGIBackend
     56 {
     57 		const IGalliumDXGIBackendVtbl *vtbl_IGalliumDXGIBackend;
     58 		LONG ref;
     59 };
     60 
     61 static HRESULT STDMETHODCALLTYPE WineDXGIBackend_BeginPresent(
     62 	IGalliumDXGIBackend* This,
     63 	HWND hwnd,
     64 	void** ppresent_cookie,
     65 	void** pwindow,
     66 	RECT* prect,
     67 	RGNDATA** prgndata,
     68 	BOOL* ppreserve_aspect_ratio)
     69 {
     70 	/* this is the parent HWND which actually has an X11 window associated */
     71 	HWND x11_hwnd;
     72 	HDC hdc;
     73 	RECT client_rect;
     74 	POINT x11_hwnd_origin_from_screen;
     75 	Drawable drawable;
     76 	POINT hwnd_origin_from_screen;
     77 	HRGN hrgn;
     78 	unsigned code = X11DRV_GET_DRAWABLE;
     79 	unsigned rgndata_size;
     80 	RGNDATA* rgndata;
     81 	RECT rgn_box;
     82 	int rgn_box_type;
     83 
     84 	hdc = GetDC(hwnd);
     85 	GetDCOrgEx(hdc, &hwnd_origin_from_screen);
     86 	hrgn = CreateRectRgn(0, 0, 0, 0);
     87 	GetRandomRgn(hdc, hrgn, SYSRGN);
     88 	rgn_box_type = GetRgnBox(hrgn, &rgn_box);
     89 
     90 	/* the coordinate system differs depending on whether Wine is
     91 	 * pretending to be Win9x or WinNT, so match that behavior.
     92 	 */
     93 	if (!(GetVersion() & 0x80000000))
     94 		OffsetRgn(hrgn, -hwnd_origin_from_screen.x, -hwnd_origin_from_screen.y);
     95 	ReleaseDC(hwnd, hdc);
     96 
     97 	if(rgn_box_type == NULLREGION)
     98 	{
     99 		DeleteObject(hrgn);
    100 		return DXGI_STATUS_OCCLUDED;
    101 	}
    102 
    103 	rgndata_size = GetRegionData(hrgn, 0, NULL);
    104 	rgndata = HeapAlloc(GetProcessHeap(), 0, rgndata_size);
    105 	GetRegionData(hrgn, rgndata_size, rgndata);
    106 	DeleteObject(hrgn);
    107 	*prgndata = rgndata;
    108 
    109 	x11_hwnd = GetAncestor(hwnd, GA_ROOT);
    110 	hdc = GetDC(x11_hwnd);
    111 	ExtEscape(hdc, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(drawable), (LPTSTR)&drawable);
    112 
    113 	GetDCOrgEx(hdc, &x11_hwnd_origin_from_screen);
    114 	ReleaseDC(x11_hwnd, hdc);
    115 
    116 	*pwindow = (void*)drawable;
    117 	GetClientRect(hwnd, &client_rect);
    118 
    119 	prect->left = hwnd_origin_from_screen.x - x11_hwnd_origin_from_screen.x;
    120 	prect->top = hwnd_origin_from_screen.y - x11_hwnd_origin_from_screen.y;
    121 
    122 	prect->right = prect->left + client_rect.right;
    123 	prect->bottom = prect->top + client_rect.bottom;
    124 
    125 	// Windows doesn't preserve the aspect ratio
    126 	// TODO: maybe let the user turn this on somehow
    127 	*ppreserve_aspect_ratio = FALSE;
    128 
    129 	*ppresent_cookie = rgndata;
    130 
    131 	// TODO: check for errors and return them
    132 	return S_OK;
    133 }
    134 
    135 static void STDMETHODCALLTYPE WineDXGIBackend_EndPresent(
    136 	IGalliumDXGIBackend* This,
    137 	HWND hwnd,
    138 	void *present_cookie)
    139 {
    140 	HeapFree(GetProcessHeap(), 0, present_cookie);
    141 }
    142 
    143 static HRESULT STDMETHODCALLTYPE WineDXGIBackend_TestPresent(
    144 	IGalliumDXGIBackend* This,
    145 	HWND hwnd)
    146 {
    147 	HDC hdc;
    148 	HRGN hrgn;
    149 	RECT rgn_box;
    150 	int rgn_box_type;
    151 
    152 	// TODO: is there a simpler way to check this?
    153 	hdc = GetDC(hwnd);
    154 	hrgn = CreateRectRgn(0, 0, 0, 0);
    155 	GetRandomRgn(hdc, hrgn, SYSRGN);
    156 	rgn_box_type = GetRgnBox(hrgn, &rgn_box);
    157 	DeleteObject(hrgn);
    158 	ReleaseDC(hwnd, hdc);
    159 
    160 	return rgn_box_type == NULLREGION ? DXGI_STATUS_OCCLUDED : S_OK;
    161 }
    162 
    163 static HRESULT STDMETHODCALLTYPE WineDXGIBackend_GetPresentSize(
    164 	IGalliumDXGIBackend* This,
    165 	HWND hwnd,
    166 	unsigned* width,
    167 	unsigned* height)
    168 {
    169 	RECT client_rect;
    170 	GetClientRect(hwnd, &client_rect);
    171 	*width = client_rect.right - client_rect.left;
    172 	*height = client_rect.bottom - client_rect.top;
    173 
    174 	// TODO: check for errors and return them
    175 	return S_OK;
    176 }
    177 
    178 /* Wine should switch to C++ at least to be able to implement COM interfaces in a sensible way,
    179  * instead of this ridiculous amount of clumsy duplicated code everywhere
    180  * C++ exists exactly to avoid having to write the following code */
    181 static ULONG STDMETHODCALLTYPE WineDXGIBackend_AddRef(IGalliumDXGIBackend* This)
    182 {
    183 	return InterlockedIncrement(&((struct WineDXGIBackend*)&This)->ref);
    184 }
    185 
    186 static ULONG STDMETHODCALLTYPE WineDXGIBackend_Release(IGalliumDXGIBackend* This)
    187 {
    188 	ULONG v = InterlockedDecrement(&((struct WineDXGIBackend*)&This)->ref);
    189 	if(!v)
    190 		HeapFree(GetProcessHeap(), 0, This);
    191 	return v;
    192 }
    193 
    194 static HRESULT WINAPI WineDXGIBackend_QueryInterface(
    195 	IGalliumDXGIBackend* iface,
    196 	REFIID riid,
    197 	void** ppvObject)
    198 {
    199 	if (IsEqualGUID(riid, &IID_IUnknown)
    200 		|| IsEqualGUID(riid, &IID_IGalliumDXGIBackend))
    201 	{
    202 		WineDXGIBackend_AddRef(iface);
    203 		*ppvObject = iface;
    204 		return S_OK;
    205 	}
    206 
    207 	return E_NOINTERFACE;
    208 }
    209 
    210 static IGalliumDXGIBackendVtbl WineDXGIBackend_vtbl =
    211 {
    212 	WineDXGIBackend_QueryInterface,
    213 	WineDXGIBackend_AddRef,
    214 	WineDXGIBackend_Release,
    215 	WineDXGIBackend_BeginPresent,
    216 	WineDXGIBackend_EndPresent,
    217 	WineDXGIBackend_TestPresent,
    218 	WineDXGIBackend_GetPresentSize
    219 };
    220 
    221 IGalliumDXGIBackend* new_WineDXGIBackend()
    222 {
    223 	struct WineDXGIBackend* backend = HeapAlloc(GetProcessHeap(), 0, sizeof(struct WineDXGIBackend));
    224 	backend->ref = 1;
    225 	backend->vtbl_IGalliumDXGIBackend = &WineDXGIBackend_vtbl;
    226 	return (IGalliumDXGIBackend*)backend;
    227 }
    228 
    229 static void install_wine_dxgi_backend()
    230 {
    231 	IGalliumDXGIBackend* backend = new_WineDXGIBackend();
    232 	HWND root = GetDesktopWindow();
    233 	unsigned code = X11DRV_GET_DISPLAY;
    234 	Display* dpy;
    235 	HDC hdc;
    236 
    237 	hdc = GetDC(root);
    238 	ExtEscape(hdc, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(dpy), (LPTSTR)&dpy);
    239 	ReleaseDC(root, hdc);
    240 
    241 	GalliumDXGIUseX11Display(dpy, backend);
    242 	GalliumDXGIMakeDefault();
    243 	GalliumDXGIUseNothing();
    244 	backend->lpVtbl->Release(backend);
    245 }
    246 
    247 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
    248 {
    249 	switch (fdwReason)
    250 	{
    251 	case DLL_WINE_PREATTACH:
    252 		return TRUE;
    253 	case DLL_PROCESS_ATTACH:
    254 		DisableThreadLibraryCalls(hinstDLL);
    255 		install_wine_dxgi_backend();
    256 		break;
    257         case DLL_PROCESS_DETACH:
    258         	break;
    259 	default:
    260         	break;
    261 	}
    262 
    263 	return TRUE;
    264 }
    265