• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
WineDXGIBackend_BeginPresent(IGalliumDXGIBackend * This,HWND hwnd,void ** ppresent_cookie,void ** pwindow,RECT * prect,RGNDATA ** prgndata,BOOL * ppreserve_aspect_ratio)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 
WineDXGIBackend_EndPresent(IGalliumDXGIBackend * This,HWND hwnd,void * present_cookie)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 
WineDXGIBackend_TestPresent(IGalliumDXGIBackend * This,HWND hwnd)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 
WineDXGIBackend_GetPresentSize(IGalliumDXGIBackend * This,HWND hwnd,unsigned * width,unsigned * height)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 */
WineDXGIBackend_AddRef(IGalliumDXGIBackend * This)181 static ULONG STDMETHODCALLTYPE WineDXGIBackend_AddRef(IGalliumDXGIBackend* This)
182 {
183 	return InterlockedIncrement(&((struct WineDXGIBackend*)&This)->ref);
184 }
185 
WineDXGIBackend_Release(IGalliumDXGIBackend * This)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 
WineDXGIBackend_QueryInterface(IGalliumDXGIBackend * iface,REFIID riid,void ** ppvObject)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 
new_WineDXGIBackend()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 
install_wine_dxgi_backend()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 
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)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