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
55struct WineDXGIBackend
56{
57		const IGalliumDXGIBackendVtbl *vtbl_IGalliumDXGIBackend;
58		LONG ref;
59};
60
61static 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
135static void STDMETHODCALLTYPE WineDXGIBackend_EndPresent(
136	IGalliumDXGIBackend* This,
137	HWND hwnd,
138	void *present_cookie)
139{
140	HeapFree(GetProcessHeap(), 0, present_cookie);
141}
142
143static 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
163static 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 */
181static ULONG STDMETHODCALLTYPE WineDXGIBackend_AddRef(IGalliumDXGIBackend* This)
182{
183	return InterlockedIncrement(&((struct WineDXGIBackend*)&This)->ref);
184}
185
186static 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
194static 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
210static 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
221IGalliumDXGIBackend* 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
229static 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
247BOOL 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