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