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