dxgicapture: reinitialize duplication interface on ERROR_ACCESS_LOST
[platform/upstream/gstreamer.git] / sys / winscreencap / dxgicapture.c
1 /* GStreamer
2  * Copyright (C) 2019 OKADA Jun-ichi <okada@abt.jp>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /* This code captures the screen using "Desktop Duplication API".
21  * For more information
22  * https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api */
23
24 #include "dxgicapture.h"
25
26 #include <d3dcompiler.h>
27 #include <gmodule.h>
28
29 GST_DEBUG_CATEGORY_EXTERN (gst_dxgi_screen_cap_src_debug);
30 #define GST_CAT_DEFAULT gst_dxgi_screen_cap_src_debug
31
32 #define PTR_RELEASE(p) {if(NULL!=(p)){IUnknown_Release((IUnknown *)(p)); (p) = NULL;}}
33 #define BYTE_PER_PIXEL (4)
34
35 /* vertex structures */
36 typedef struct _vector3d
37 {
38   float x;
39   float y;
40   float z;
41 } vector3d;
42
43 typedef struct _vector2d
44 {
45   float x;
46   float y;
47 } vector2d;
48
49 typedef struct _vertex
50 {
51   vector3d pos;
52   vector2d texcoord;
53 } vertex;
54 #define VERTEX_NUM (6);
55
56 typedef struct _DxgiCapture
57 {
58   GstDXGIScreenCapSrc *src;
59
60   /*Direct3D pointers */
61   ID3D11Device *d3d11_device;
62   ID3D11DeviceContext *d3d11_context;
63   IDXGIOutput1 *dxgi_output1;
64   IDXGIOutputDuplication *dxgi_dupl;
65
66   /* Texture that has been rotated and combined fragments. */
67   ID3D11Texture2D *work_texture;
68   D3D11_TEXTURE2D_DESC work_texture_desc;
69   D3D11_VIEWPORT view_port;
70   /* Textures that can be read by the CPU.
71    * CPU-accessible textures are required separately from work_texture
72    * because shaders cannot be executed. */
73   ID3D11Texture2D *readable_texture;
74   ID3D11VertexShader *vertex_shader;
75   ID3D11PixelShader *pixel_shader;
76   ID3D11SamplerState *sampler_state;
77   ID3D11RenderTargetView *target_view;
78   /* Screen output dimensions and rotation status.
79    * The texture acquired by AcquireNextFrame has a non-rotated region. */
80   DXGI_OUTDUPL_DESC dupl_desc;
81
82   /* mouse pointer image */
83   guint8 *pointer_buffer;
84   gsize pointer_buffer_capacity;
85
86   /* The movement rectangular regions and the movement
87    * destination position from the previous frame. */
88   DXGI_OUTDUPL_MOVE_RECT *move_rects;
89   gsize move_rects_capacity;
90
91   /* Array of dirty rectangular region for the desktop frame. */
92   RECT *dirty_rects;
93   gsize dirty_rects_capacity;
94
95   /* Vertex buffer created from array of dirty rectangular region. */
96   vertex *dirty_verteces;
97   gsize verteces_capacity;
98
99   /* Array of rectangular region to copy to readable_texture. */
100   RECT *copy_rects;
101   gsize copy_rects_capacity;
102
103   /* latest mouse pointer info */
104   DXGI_OUTDUPL_POINTER_SHAPE_INFO pointer_shape_info;
105   DXGI_OUTDUPL_POINTER_POSITION last_pointer_position;
106
107 } DxgiCapture;
108
109 /* Vertex shader for texture rotation by HLSL. */
110 static const char STR_VERTEX_SHADER[] =
111     "struct vs_input  { float4 pos : POSITION; float2 tex : TEXCOORD; }; "
112     "struct vs_output { float4 pos : SV_POSITION; float2 tex : TEXCOORD; }; "
113     "vs_output vs_main(vs_input input){return input;}";
114
115 /* Pixel shader for texture rotation by HLSL. */
116 static const char STR_PIXEL_SHADER[] =
117     "Texture2D tx : register( t0 ); "
118     "SamplerState samp : register( s0 ); "
119     "struct ps_input { float4 pos : SV_POSITION; float2 tex : TEXCOORD;}; "
120     "float4 ps_main(ps_input input) : "
121     "SV_Target{ return tx.Sample( samp, input.tex ); }";
122
123 /* initial buffer size */
124 const int INITIAL_POINTER_BUFFER_CAPACITY = 64 * 64 * BYTE_PER_PIXEL;
125 const int INITIAL_MOVE_RECTS_CAPACITY = 100;
126 const int INITIAL_DIRTY_RECTS_CAPACITY = 100;
127 const int INITIAL_VERTICES_CAPACITY = 100 * VERTEX_NUM;
128 const int INITIAL_COPY_RECTS_CAPACITY = 100;
129
130 static D3D_FEATURE_LEVEL feature_levels[] = {
131   D3D_FEATURE_LEVEL_11_0,
132   D3D_FEATURE_LEVEL_10_1,
133   D3D_FEATURE_LEVEL_10_0,
134   D3D_FEATURE_LEVEL_9_3,
135   D3D_FEATURE_LEVEL_9_2,
136   D3D_FEATURE_LEVEL_9_1,
137 };
138
139 static D3D11_INPUT_ELEMENT_DESC vertex_layout[] = {
140   {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
141       D3D11_INPUT_PER_VERTEX_DATA, 0},
142   {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA,
143       0}
144 };
145
146 static void _draw_pointer (DxgiCapture * self, LPBYTE buffer, LPRECT dst_rect,
147     int stride);
148 static ID3D11Texture2D *_create_texture (DxgiCapture * self,
149     enum D3D11_USAGE usage, UINT bindFlags, UINT cpuAccessFlags);
150
151 static gboolean _setup_texture (DxgiCapture * self);
152
153 static HRESULT _update_work_texture (DxgiCapture * self,
154     IDXGIResource * desktop_resource);
155
156 static HRESULT _copy_dirty_fragment (DxgiCapture * self,
157     ID3D11Texture2D * src_texture, const D3D11_TEXTURE2D_DESC * src_desc,
158     guint move_count, guint dirty_count, RECT ** dst_rect);
159
160 static void _set_verteces (DxgiCapture * self, vertex * verteces,
161     RECT * dest_rect, const D3D11_TEXTURE2D_DESC * dst_desc, RECT * rect,
162     const D3D11_TEXTURE2D_DESC * src_desc);
163
164 static GModule *d3d_compiler_module = NULL;
165 static pD3DCompile GstD3DCompileFunc = NULL;
166
167 gboolean
168 gst_dxgicap_shader_init (void)
169 {
170   static gsize _init = 0;
171   static const gchar *d3d_compiler_names[] = {
172     "d3dcompiler_47.dll",
173     "d3dcompiler_46.dll",
174     "d3dcompiler_45.dll",
175     "d3dcompiler_44.dll",
176     "d3dcompiler_43.dll",
177   };
178
179   if (g_once_init_enter (&_init)) {
180     gint i;
181     for (i = 0; i < G_N_ELEMENTS (d3d_compiler_names); i++) {
182       d3d_compiler_module =
183           g_module_open (d3d_compiler_names[i], G_MODULE_BIND_LAZY);
184
185       if (d3d_compiler_module) {
186         GST_INFO ("D3D compiler %s is available", d3d_compiler_names[i]);
187         if (!g_module_symbol (d3d_compiler_module, "D3DCompile",
188                 (gpointer *) & GstD3DCompileFunc)) {
189           GST_ERROR ("Cannot load D3DCompile symbol from %s",
190               d3d_compiler_names[i]);
191           g_module_close (d3d_compiler_module);
192           d3d_compiler_module = NULL;
193           GstD3DCompileFunc = NULL;
194         } else {
195           break;
196         }
197       }
198     }
199
200     if (!GstD3DCompileFunc)
201       GST_WARNING ("D3D11 compiler library is unavailable");
202
203     g_once_init_leave (&_init, 1);
204   }
205
206   return ! !GstD3DCompileFunc;
207 }
208
209 static gboolean
210 initialize_output_duplication (DxgiCapture * self)
211 {
212   HDESK hdesk;
213   HRESULT hr;
214   GstDXGIScreenCapSrc *src = self->src;
215
216   PTR_RELEASE (self->dxgi_dupl);
217
218   hdesk = OpenInputDesktop (0, FALSE, GENERIC_ALL);
219   if (hdesk) {
220     if (!SetThreadDesktop (hdesk)) {
221       GST_WARNING_OBJECT (src, "SetThreadDesktop() failed. Error code: %lu",
222           GetLastError ());
223     }
224
225     CloseDesktop (hdesk);
226   } else {
227     GST_WARNING_OBJECT (src, "OpenInputDesktop() failed. Error code: %lu",
228         GetLastError ());
229   }
230
231   hr = IDXGIOutput1_DuplicateOutput (self->dxgi_output1,
232       (IUnknown *) (self->d3d11_device), &self->dxgi_dupl);
233   if (hr != S_OK) {
234     gchar *msg = get_hresult_to_string (hr);
235     GST_WARNING_OBJECT (src, "IDXGIOutput1::DuplicateOutput() failed (%x): %s",
236         (guint) hr, msg);
237     g_free (msg);
238     return FALSE;
239   }
240
241   return TRUE;
242 }
243
244 DxgiCapture *
245 dxgicap_new (HMONITOR monitor, GstDXGIScreenCapSrc * src)
246 {
247   int i, j;
248   HRESULT hr;
249   IDXGIFactory1 *dxgi_factory1 = NULL;
250   IDXGIAdapter1 *dxgi_adapter1 = NULL;
251   ID3D11InputLayout *vertex_input_layout = NULL;
252   ID3DBlob *vertex_shader_blob = NULL;
253   ID3DBlob *pixel_shader_blob = NULL;
254   D3D11_SAMPLER_DESC sampler_desc;
255
256   DxgiCapture *self = g_new0 (DxgiCapture, 1);
257   if (NULL == self) {
258     return NULL;
259   }
260
261   self->src = src;
262   hr = CreateDXGIFactory1 (&IID_IDXGIFactory1, (void **) &dxgi_factory1);
263   HR_FAILED_GOTO (hr, CreateDXGIFactory1, new_error);
264
265   for (i = 0;
266       IDXGIFactory1_EnumAdapters1 (dxgi_factory1, i,
267           &dxgi_adapter1) != DXGI_ERROR_NOT_FOUND; ++i) {
268     IDXGIOutput *dxgi_output = NULL;
269     D3D_FEATURE_LEVEL feature_level;
270
271     hr = D3D11CreateDevice ((IDXGIAdapter *) dxgi_adapter1,
272         D3D_DRIVER_TYPE_UNKNOWN, NULL, 0,
273         feature_levels, G_N_ELEMENTS (feature_levels),
274         D3D11_SDK_VERSION, &self->d3d11_device, &feature_level,
275         &self->d3d11_context);
276     if (FAILED (hr)) {
277       HR_FAILED_INFO (hr, D3D11CreateDevice);
278       PTR_RELEASE (dxgi_adapter1);
279       continue;
280     }
281
282     for (j = 0; IDXGIAdapter1_EnumOutputs (dxgi_adapter1, j, &dxgi_output) !=
283         DXGI_ERROR_NOT_FOUND; ++j) {
284       DXGI_OUTPUT_DESC output_desc;
285       hr = IDXGIOutput_QueryInterface (dxgi_output, &IID_IDXGIOutput1,
286           (void **) &self->dxgi_output1);
287       PTR_RELEASE (dxgi_output);
288       HR_FAILED_GOTO (hr, IDXGIOutput::QueryInterface, new_error);
289
290       hr = IDXGIOutput1_GetDesc (self->dxgi_output1, &output_desc);
291       HR_FAILED_GOTO (hr, IDXGIOutput1::GetDesc, new_error);
292
293       if (output_desc.Monitor == monitor) {
294         GST_DEBUG_OBJECT (src, "found monitor");
295         break;
296       }
297
298       PTR_RELEASE (self->dxgi_output1);
299     }
300
301     PTR_RELEASE (dxgi_adapter1);
302
303     if (NULL != self->dxgi_output1) {
304       break;
305     }
306
307     PTR_RELEASE (self->d3d11_device);
308     PTR_RELEASE (self->d3d11_context);
309   }
310
311   if (NULL == self->dxgi_output1) {
312     goto new_error;
313   }
314
315   PTR_RELEASE (dxgi_factory1);
316
317   if (!initialize_output_duplication (self)) {
318     goto new_error;
319   }
320
321   IDXGIOutputDuplication_GetDesc (self->dxgi_dupl, &self->dupl_desc);
322   self->pointer_buffer_capacity = INITIAL_POINTER_BUFFER_CAPACITY;
323   self->pointer_buffer = g_malloc (self->pointer_buffer_capacity);
324   if (NULL == self->pointer_buffer) {
325     goto new_error;
326   }
327
328   self->move_rects_capacity = INITIAL_MOVE_RECTS_CAPACITY;
329   self->move_rects = g_new0 (DXGI_OUTDUPL_MOVE_RECT, self->move_rects_capacity);
330   if (NULL == self->move_rects) {
331     goto new_error;
332   }
333
334   self->dirty_rects_capacity = INITIAL_DIRTY_RECTS_CAPACITY;
335   self->dirty_rects = g_new0 (RECT, self->dirty_rects_capacity);
336   if (NULL == self->dirty_rects) {
337     goto new_error;
338   }
339
340   self->verteces_capacity = INITIAL_VERTICES_CAPACITY;
341   self->dirty_verteces = g_new0 (vertex, self->verteces_capacity);
342   if (NULL == self->dirty_verteces) {
343     goto new_error;
344   }
345
346   self->copy_rects_capacity = INITIAL_COPY_RECTS_CAPACITY;
347   self->copy_rects = g_new0 (RECT, self->copy_rects_capacity);
348   if (NULL == self->copy_rects) {
349     goto new_error;
350   }
351
352   if (DXGI_MODE_ROTATION_IDENTITY != self->dupl_desc.Rotation) {
353     g_assert (GstD3DCompileFunc);
354
355     /* For a rotated display, create a shader. */
356     hr = GstD3DCompileFunc (STR_VERTEX_SHADER, sizeof (STR_VERTEX_SHADER),
357         NULL, NULL, NULL, "vs_main", "vs_4_0_level_9_1",
358         0, 0, &vertex_shader_blob, NULL);
359     HR_FAILED_GOTO (hr, D3DCompile, new_error);
360
361     hr = GstD3DCompileFunc (STR_PIXEL_SHADER, sizeof (STR_PIXEL_SHADER),
362         NULL, NULL, NULL, "ps_main", "ps_4_0_level_9_1",
363         0, 0, &pixel_shader_blob, NULL);
364     HR_FAILED_GOTO (hr, D3DCompile, new_error);
365
366     hr = ID3D11Device_CreateVertexShader (self->d3d11_device,
367         ID3D10Blob_GetBufferPointer (vertex_shader_blob),
368         ID3D10Blob_GetBufferSize (vertex_shader_blob), NULL,
369         &self->vertex_shader);
370     HR_FAILED_GOTO (hr, ID3D11Device::CreateVertexShader, new_error);
371
372     hr = ID3D11Device_CreateInputLayout (self->d3d11_device, vertex_layout,
373         G_N_ELEMENTS (vertex_layout),
374         ID3D10Blob_GetBufferPointer (vertex_shader_blob),
375         ID3D10Blob_GetBufferSize (vertex_shader_blob), &vertex_input_layout);
376     PTR_RELEASE (vertex_shader_blob)
377         HR_FAILED_GOTO (hr, ID3D11Device::CreateInputLayout, new_error);
378
379     ID3D11DeviceContext_IASetInputLayout (self->d3d11_context,
380         vertex_input_layout);
381     PTR_RELEASE (vertex_input_layout);
382
383     hr = ID3D11Device_CreatePixelShader (self->d3d11_device,
384         ID3D10Blob_GetBufferPointer (pixel_shader_blob),
385         ID3D10Blob_GetBufferSize (pixel_shader_blob), NULL,
386         &self->pixel_shader);
387     PTR_RELEASE (pixel_shader_blob);
388     HR_FAILED_GOTO (hr, ID3D11Device::CreatePixelShader, new_error);
389
390     memset (&sampler_desc, 0, sizeof (sampler_desc));
391     sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
392     sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
393     sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
394     sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
395     sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
396     sampler_desc.MinLOD = 0;
397     sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
398
399     hr = ID3D11Device_CreateSamplerState (self->d3d11_device, &sampler_desc,
400         &self->sampler_state);
401     HR_FAILED_GOTO (hr, ID3D11Device::CreateSamplerState, new_error);
402   }
403
404   return self;
405
406 new_error:
407   PTR_RELEASE (vertex_input_layout);
408   PTR_RELEASE (vertex_shader_blob);
409   PTR_RELEASE (pixel_shader_blob);
410
411   dxgicap_destory (self);
412   return NULL;
413 }
414
415 void
416 dxgicap_destory (DxgiCapture * self)
417 {
418   if (!self)
419     return;
420   PTR_RELEASE (self->target_view);
421   PTR_RELEASE (self->readable_texture);
422   PTR_RELEASE (self->work_texture);
423   PTR_RELEASE (self->dxgi_output1);
424   PTR_RELEASE (self->dxgi_dupl);
425   PTR_RELEASE (self->d3d11_context);
426   PTR_RELEASE (self->d3d11_device);
427   PTR_RELEASE (self->vertex_shader);
428   PTR_RELEASE (self->pixel_shader);
429   PTR_RELEASE (self->sampler_state);
430
431   g_free (self->pointer_buffer);
432   g_free (self->move_rects);
433   g_free (self->dirty_rects);
434   g_free (self->dirty_verteces);
435   g_free (self->copy_rects);
436
437   g_free (self);
438 }
439
440 gboolean
441 dxgicap_start (DxgiCapture * self)
442 {
443   return _setup_texture (self);
444 }
445
446 void
447 dxgicap_stop (DxgiCapture * self)
448 {
449   PTR_RELEASE (self->target_view);
450   PTR_RELEASE (self->readable_texture);
451   PTR_RELEASE (self->work_texture);
452 }
453
454 gboolean
455 dxgicap_acquire_next_frame (DxgiCapture * self, gboolean show_cursor,
456     guint timeout)
457 {
458   gboolean ret = FALSE;
459   HRESULT hr;
460   GstDXGIScreenCapSrc *src = self->src;
461
462   DXGI_OUTDUPL_FRAME_INFO frame_info;
463   IDXGIResource *desktop_resource = NULL;
464
465   if (!self->dxgi_dupl) {
466     /* Desktop duplication interface became invalid due to desktop switch,
467      * UAC prompt popping up, or similar event. Try to reinitialize. */
468     if (!initialize_output_duplication (self)) {
469       ret = TRUE;
470       goto end;
471     }
472   }
473
474   /* Get the latest desktop frames. */
475   hr = IDXGIOutputDuplication_AcquireNextFrame (self->dxgi_dupl,
476       timeout, &frame_info, &desktop_resource);
477   if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
478     /* In case of DXGI_ERROR_WAIT_TIMEOUT,
479      * it has not changed from the last time. */
480     GST_LOG_OBJECT (src, "DXGI_ERROR_WAIT_TIMEOUT");
481     ret = TRUE;
482     goto end;
483   } else if (hr == DXGI_ERROR_ACCESS_LOST) {
484     GST_LOG_OBJECT (src, "DXGI_ERROR_ACCESS_LOST; reinitializing output "
485         "duplication...");
486     PTR_RELEASE (self->dxgi_dupl);
487     ret = TRUE;
488     goto end;
489   }
490   HR_FAILED_GOTO (hr, IDXGIOutputDuplication::AcquireNextFrame, end);
491
492   if (0 != frame_info.LastPresentTime.QuadPart) {
493     /* The desktop frame has changed since last time. */
494     hr = _update_work_texture (self, desktop_resource);
495     if (FAILED (hr)) {
496       GST_DEBUG_OBJECT (src, "failed to _update_work_texture");
497       goto end;
498     }
499   }
500
501   if (show_cursor && 0 != frame_info.LastMouseUpdateTime.QuadPart) {
502     /* The mouse pointer has changed since last time. */
503     self->last_pointer_position = frame_info.PointerPosition;
504
505     if (0 < frame_info.PointerShapeBufferSize) {
506       /* A valid mouse cursor shape exists. */
507       DXGI_OUTDUPL_POINTER_SHAPE_INFO pointer_shape_info;
508       guint pointer_shape_size_required;
509       /* Get the mouse cursor shape. */
510       hr = IDXGIOutputDuplication_GetFramePointerShape (self->dxgi_dupl,
511           self->pointer_buffer_capacity,
512           self->pointer_buffer,
513           &pointer_shape_size_required, &pointer_shape_info);
514       if (DXGI_ERROR_MORE_DATA == hr) {
515         /* not enough buffers */
516         self->pointer_buffer_capacity = pointer_shape_size_required * 2;
517         self->pointer_buffer =
518             g_realloc (self->pointer_buffer, self->pointer_buffer_capacity);
519
520         hr = IDXGIOutputDuplication_GetFramePointerShape (self->dxgi_dupl,
521             self->pointer_buffer_capacity,
522             self->pointer_buffer,
523             &pointer_shape_size_required, &pointer_shape_info);
524       }
525       HR_FAILED_GOTO (hr, IDXGIOutputDuplication::GetFramePointerShape, end);
526       self->pointer_shape_info = pointer_shape_info;
527       ret = TRUE;
528     } else {
529       ret = TRUE;
530     }
531   } else {
532     ret = TRUE;
533   }
534 end:
535   if (self->dxgi_dupl) {
536     IDXGIOutputDuplication_ReleaseFrame (self->dxgi_dupl);
537   }
538   PTR_RELEASE (desktop_resource);
539   return ret;
540 }
541
542 gboolean
543 dxgicap_copy_buffer (DxgiCapture * self, gboolean show_cursor, LPRECT dst_rect,
544     GstVideoInfo * video_info, GstBuffer * buf)
545 {
546   HRESULT hr;
547   int i;
548   GstDXGIScreenCapSrc *src = self->src;
549   D3D11_MAPPED_SUBRESOURCE readable_map;
550   GstVideoFrame vframe;
551   gint height = RECT_HEIGHT ((*dst_rect));
552   gint width = RECT_WIDTH ((*dst_rect));
553
554   if (NULL == self->readable_texture) {
555     GST_DEBUG_OBJECT (src, "readable_texture is null");
556     goto flow_error;
557   }
558
559   hr = ID3D11DeviceContext_Map (self->d3d11_context,
560       (ID3D11Resource *) self->readable_texture, 0,
561       D3D11_MAP_READ, 0, &readable_map);
562   HR_FAILED_GOTO (hr, IDXGISurface1::Map, flow_error);
563   GST_DEBUG_OBJECT (src, "copy size width:%d height:%d", width, height);
564
565   /* Copy from readable_texture to GstVideFrame. */
566   if (gst_video_frame_map (&vframe, video_info, buf, GST_MAP_WRITE)) {
567     gint line_size;
568     gint stride_dst;
569     PBYTE frame_buffer;
570     PBYTE p_dst;
571     PBYTE p_src;
572
573     frame_buffer = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0);
574     p_src = (PBYTE) readable_map.pData +
575         (dst_rect->top * readable_map.RowPitch) +
576         (dst_rect->left * BYTE_PER_PIXEL);
577     p_dst = frame_buffer;
578
579     line_size = width * BYTE_PER_PIXEL;
580     stride_dst = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
581
582     if (line_size > stride_dst) {
583       GST_ERROR_OBJECT (src, "not enough stride in video frame");
584       ID3D11DeviceContext_Unmap (self->d3d11_context,
585           (ID3D11Resource *) self->readable_texture, 0);
586       gst_video_frame_unmap (&vframe);
587       goto flow_error;
588     }
589
590     for (i = 0; i < height; ++i) {
591       memcpy (p_dst, p_src, line_size);
592       p_dst += stride_dst;
593       p_src += readable_map.RowPitch;
594     }
595     ID3D11DeviceContext_Unmap (self->d3d11_context,
596         (ID3D11Resource *) self->readable_texture, 0);
597     HR_FAILED_GOTO (hr, IDXGISurface1::Unmap, flow_error);
598
599     if (show_cursor && self->last_pointer_position.Visible) {
600       _draw_pointer (self, frame_buffer, dst_rect, stride_dst);
601     }
602     gst_video_frame_unmap (&vframe);
603     return TRUE;
604   }
605
606 flow_error:
607   return FALSE;
608 }
609
610 static void
611 _draw_pointer (DxgiCapture * self, PBYTE buffer, LPRECT dst_rect, int stride)
612 {
613   RECT pointer_rect;
614   RECT clip_pointer_rect;
615   int offset_x;
616   int offset_y;
617   PBYTE p_dst;
618   /* For DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME, halve the height. */
619   int pointer_height =
620       (DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME ==
621       self->pointer_shape_info.Type)
622       ? self->pointer_shape_info.Height / 2 : self->pointer_shape_info.Height;
623
624   /* A rectangular area containing the mouse pointer shape */
625   SetRect (&pointer_rect,
626       self->last_pointer_position.Position.x,
627       self->last_pointer_position.Position.y,
628       self->last_pointer_position.Position.x +
629       self->pointer_shape_info.Width,
630       self->last_pointer_position.Position.y + pointer_height);
631
632   if (!IntersectRect (&clip_pointer_rect, dst_rect, &pointer_rect)) {
633     return;
634   }
635
636   /* Draw a pointer if it overlaps the destination rectangle range.
637    * There are three ways to draw the mouse cursor.
638    * see  https://docs.microsoft.com/ja-jp/windows/win32/api/dxgi1_2/ne-dxgi1_2-dxgi_outdupl_pointer_shape_type */
639   offset_x = clip_pointer_rect.left - pointer_rect.left;
640   offset_y = clip_pointer_rect.top - pointer_rect.top;
641   p_dst =
642       ((PBYTE) buffer) + ((clip_pointer_rect.top -
643           dst_rect->top) * stride) +
644       ((clip_pointer_rect.left - dst_rect->left) * BYTE_PER_PIXEL);
645
646   if (DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR ==
647       self->pointer_shape_info.Type
648       || DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR ==
649       self->pointer_shape_info.Type) {
650     gboolean mask_mode =
651         DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR ==
652         self->pointer_shape_info.Type;
653     PBYTE p_src =
654         (PBYTE) self->pointer_buffer +
655         (offset_y * self->pointer_shape_info.Pitch) +
656         (offset_x * BYTE_PER_PIXEL);
657
658     int y, x;
659     for (y = 0; y < RECT_HEIGHT (clip_pointer_rect); ++y) {
660       for (x = 0; x < RECT_WIDTH (clip_pointer_rect); ++x) {
661         PBYTE p1 = p_dst + (x * BYTE_PER_PIXEL);
662         PBYTE p2 = p_src + (x * BYTE_PER_PIXEL);
663         int alpha = *(p2 + 3);
664         int i;
665         for (i = 0; i < 3; ++i) {
666           if (mask_mode) {
667             /* case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
668              * If the alpha channel of a pixel in the mouse image is 0, copy it.
669              * Otherwise, xor each pixel. */
670             if (0 == alpha) {
671               *p1 = *p2;
672             } else {
673               *p1 = *p2 ^ *p1;
674             }
675           } else {
676             /* case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
677              * Copies the mouse cursor image with alpha channel composition. */
678             *p1 = min (255, max (0, *p1 + ((*p2 - *p1) * alpha / 255)));
679           }
680           ++p1;
681           ++p2;
682         }
683       }
684       p_dst += stride;
685       p_src += self->pointer_shape_info.Pitch;
686     }
687   } else if (DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME ==
688       self->pointer_shape_info.Type) {
689     guint mask_bit = 0x80;
690     /* AND MASK pointer
691      * It is stored in 1 bit per pixel from the beginning. */
692     PBYTE p_src_and =
693         (PBYTE) self->pointer_buffer +
694         (offset_y * self->pointer_shape_info.Pitch);
695     /* XOR MASK pointer
696      * The XOR MASK is stored after the AND mask. */
697     PBYTE p_src_xor =
698         (PBYTE) self->pointer_buffer +
699         ((offset_y + pointer_height) * self->pointer_shape_info.Pitch);
700
701     int y, x;
702     for (y = 0; y < RECT_HEIGHT (clip_pointer_rect); ++y) {
703       guint32 *p_dst_32 = ((guint32 *) (p_dst));
704       for (x = offset_x; x < RECT_WIDTH (clip_pointer_rect); ++x) {
705         int bit_pos = x % 8;
706         gboolean and_bit =
707             0 != (*(p_src_and + (x / 8)) & (mask_bit >> bit_pos));
708         gboolean xor_bit =
709             0 != (*(p_src_xor + (x / 8)) & (mask_bit >> bit_pos));
710
711         if (and_bit) {
712           if (xor_bit) {
713             *p_dst_32 = *p_dst_32 ^ 0x00ffffff;
714           }
715         } else {
716           if (xor_bit) {
717             *p_dst_32 = 0xffffffff;
718           } else {
719             *p_dst_32 = 0xff000000;
720           }
721         }
722         ++p_dst_32;
723       }
724       p_dst += stride;
725       p_src_and += self->pointer_shape_info.Pitch;
726       p_src_xor += self->pointer_shape_info.Pitch;
727     }
728   }
729 }
730
731 static ID3D11Texture2D *
732 _create_texture (DxgiCapture * self,
733     enum D3D11_USAGE usage, UINT bindFlags, UINT cpuAccessFlags)
734 {
735   HRESULT hr;
736   GstDXGIScreenCapSrc *src = self->src;
737   D3D11_TEXTURE2D_DESC new_desc;
738   ID3D11Texture2D *new_texture = NULL;
739
740   ZeroMemory (&new_desc, sizeof (new_desc));
741   new_desc.Width = self->dupl_desc.ModeDesc.Width;
742   new_desc.Height = self->dupl_desc.ModeDesc.Height;
743   new_desc.MipLevels = 1;
744   new_desc.ArraySize = 1;
745   new_desc.SampleDesc.Count = 1;
746   new_desc.SampleDesc.Quality = 0;
747   new_desc.Usage = usage;
748   new_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
749   new_desc.BindFlags = bindFlags;
750   new_desc.CPUAccessFlags = cpuAccessFlags;
751   new_desc.MiscFlags = 0;
752
753   hr = ID3D11Device_CreateTexture2D (self->d3d11_device, &new_desc, NULL,
754       &new_texture);
755   HR_FAILED_RET (hr, ID3D11Device::CreateTexture2D, NULL);
756
757   return new_texture;
758 }
759
760 static gboolean
761 _setup_texture (DxgiCapture * self)
762 {
763   HRESULT hr;
764   ID3D11Texture2D *new_texture = NULL;
765   GstDXGIScreenCapSrc *src = self->src;
766
767   if (NULL == self->readable_texture) {
768     new_texture = _create_texture (self, D3D11_USAGE_STAGING, 0,
769         D3D11_CPU_ACCESS_READ);
770     if (NULL == new_texture) {
771       return FALSE;
772     }
773     self->readable_texture = new_texture;
774   }
775
776   if (DXGI_MODE_ROTATION_IDENTITY != self->dupl_desc.Rotation) {
777     /* For rotated displays, create work_texture. */
778     if (NULL == self->work_texture) {
779       new_texture =
780           _create_texture (self, D3D11_USAGE_DEFAULT,
781           D3D11_BIND_RENDER_TARGET, 0);
782       if (NULL == new_texture) {
783         return FALSE;
784       }
785
786       self->work_texture = new_texture;
787       ID3D11Texture2D_GetDesc (self->work_texture, &self->work_texture_desc);
788       hr = ID3D11Device_CreateRenderTargetView (self->d3d11_device,
789           (ID3D11Resource *) self->work_texture, NULL, &self->target_view);
790       HR_FAILED_RET (hr, ID3D11Device::CreateRenderTargetView, FALSE);
791
792       self->view_port.Width = (float) self->work_texture_desc.Width;
793       self->view_port.Height = (float) self->work_texture_desc.Height;
794       self->view_port.MinDepth = 0.0f;
795       self->view_port.MaxDepth = 1.0f;
796       self->view_port.TopLeftX = 0.0f;
797       self->view_port.TopLeftY = 0.0f;
798     }
799   }
800
801   return TRUE;
802 }
803
804 /* Update work_texture to the latest desktop frame from the update information
805  * that can be obtained from IDXGIOutputDuplication.
806  * Then copy to readable_texture.
807  */
808 static HRESULT
809 _update_work_texture (DxgiCapture * self, IDXGIResource * desktop_resource)
810 {
811   HRESULT hr = S_OK;
812   GstDXGIScreenCapSrc *src = self->src;
813   int i;
814   ID3D11Texture2D *desktop_texture = NULL;
815   guint required_size;
816   guint move_count;
817   guint dirty_rects_capacity_size;
818   guint dirty_count;
819   guint copy_count;
820   D3D11_TEXTURE2D_DESC src_desc;
821   RECT *dst_rect;
822   ID3D11Texture2D *work_src;
823   guint move_rects_capacity_size =
824       sizeof (DXGI_OUTDUPL_MOVE_RECT) * self->move_rects_capacity;
825
826   hr = IDXGIResource_QueryInterface (desktop_resource, &IID_ID3D11Texture2D,
827       (void **) &desktop_texture);
828   HR_FAILED_GOTO (hr, IDXGIResource::QueryInterface, end);
829
830   /* Get the rectangular regions that was moved from the last time.
831    * However, I have never obtained a valid value in GetFrameMoveRects.
832    * It seems to depend on the implementation of the GPU driver.
833    * see https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutputduplication-getframemoverects
834    */
835   hr = IDXGIOutputDuplication_GetFrameMoveRects (self->dxgi_dupl,
836       move_rects_capacity_size, self->move_rects, &required_size);
837   if (DXGI_ERROR_MORE_DATA == hr) {
838     /* not enough buffers */
839     self->move_rects_capacity =
840         (required_size / sizeof (DXGI_OUTDUPL_MOVE_RECT)) * 2;
841     self->move_rects =
842         g_renew (DXGI_OUTDUPL_MOVE_RECT, self->move_rects,
843         self->move_rects_capacity);
844
845     hr = IDXGIOutputDuplication_GetFrameMoveRects (self->dxgi_dupl,
846         required_size, self->move_rects, &required_size);
847   }
848   HR_FAILED_GOTO (hr, IDXGIOutputDuplication::GetFrameMoveRects, end);
849   move_count = required_size / sizeof (DXGI_OUTDUPL_MOVE_RECT);
850
851   dirty_rects_capacity_size = sizeof (RECT) * self->dirty_rects_capacity;
852   /* Gets the rectangular regions that has changed since the last time.
853      see https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutputduplication-getframedirtyrects
854    */
855   hr = IDXGIOutputDuplication_GetFrameDirtyRects (self->dxgi_dupl,
856       dirty_rects_capacity_size, self->dirty_rects, &required_size);
857
858   if (DXGI_ERROR_MORE_DATA == hr) {
859     /* not enough buffers */
860     self->dirty_rects_capacity = (required_size / sizeof (RECT)) * 2;
861     self->dirty_rects =
862         g_renew (RECT, self->dirty_rects, self->dirty_rects_capacity);
863
864     hr = IDXGIOutputDuplication_GetFrameDirtyRects (self->dxgi_dupl,
865         required_size, self->dirty_rects, &required_size);
866   }
867   HR_FAILED_GOTO (hr, IDXGIOutputDuplication::GetFrameDirtyRects, end);
868
869   dirty_count = required_size / sizeof (RECT);
870
871   /* The number of rectangular regions to copy to the readable_texture. */
872   copy_count = move_count + dirty_count;
873
874   if (self->copy_rects_capacity < copy_count) {
875     /* not enough buffers */
876     self->copy_rects_capacity = copy_count * 2;
877     self->copy_rects =
878         g_renew (RECT, self->copy_rects, self->copy_rects_capacity);
879   }
880
881   if (DXGI_MODE_ROTATION_IDENTITY == self->dupl_desc.Rotation) {
882     /* For a non-rotating display, copy it directly into readable_texture. */
883     RECT *p = self->copy_rects;
884     for (i = 0; i < move_count; ++i) {
885       *p = self->move_rects[i].DestinationRect;
886       ++p;
887     }
888     for (i = 0; i < dirty_count; ++i) {
889       *p = self->dirty_rects[i];
890       ++p;
891     }
892     work_src = desktop_texture;
893   } else {
894     /* For rotated displays, rotate to work_texture and copy. */
895     ID3D11Texture2D_GetDesc (desktop_texture, &src_desc);
896     dst_rect = self->copy_rects;
897     /* Copy the dirty rectangular and moved rectangular regions from desktop frame to work_texture. */
898     hr = _copy_dirty_fragment (self, desktop_texture, &src_desc, move_count,
899         dirty_count, &dst_rect);
900     work_src = self->work_texture;
901     if (FAILED (hr)) {
902       goto end;
903     }
904   }
905
906   /* Copy the updated rectangular regions to readable_texture. */
907   for (i = 0; i < copy_count; ++i) {
908     RECT *p = (self->copy_rects + i);
909     D3D11_BOX box;
910     box.left = p->left;
911     box.top = p->top;
912     box.front = 0;
913     box.right = p->right;
914     box.bottom = p->bottom;
915     box.back = 1;
916
917     ID3D11DeviceContext_CopySubresourceRegion (self->d3d11_context,
918         (ID3D11Resource *) self->readable_texture,
919         0, p->left, p->top, 0, (ID3D11Resource *) work_src, 0, &box);
920   }
921
922 end:
923   PTR_RELEASE (desktop_texture);
924   return hr;
925 }
926
927 static void
928 _rotate_rect (DXGI_MODE_ROTATION rotation, RECT * dst, const RECT * src,
929     gint dst_width, gint dst_height)
930 {
931   switch (rotation) {
932     case DXGI_MODE_ROTATION_ROTATE90:
933       dst->left = dst_width - src->bottom;
934       dst->top = src->left;
935       dst->right = dst_width - src->top;
936       dst->bottom = src->right;
937       break;
938     case DXGI_MODE_ROTATION_ROTATE180:
939       dst->left = dst_width - src->right;
940       dst->top = dst_height - src->bottom;
941       dst->right = dst_width - src->left;
942       dst->bottom = dst_height - src->top;
943       break;
944     case DXGI_MODE_ROTATION_ROTATE270:
945       dst->left = src->top;
946       dst->top = dst_height - src->right;
947       dst->right = src->bottom;
948       dst->bottom = dst_height - src->left;
949       break;
950     default:
951       *dst = *src;
952       break;
953   }
954 }
955
956 /* Copy the rectangular area specified by dirty_rects and move_rects from src_texture to work_texture. */
957 static HRESULT
958 _copy_dirty_fragment (DxgiCapture * self, ID3D11Texture2D * src_texture,
959     const D3D11_TEXTURE2D_DESC * src_desc, guint move_count, guint dirty_count,
960     RECT ** dst_rect)
961 {
962   HRESULT hr = S_OK;
963   GstDXGIScreenCapSrc *src = self->src;
964   int i;
965   RECT *dst_rect_p;
966   vertex *vp;
967   UINT stride;
968   UINT offset;
969   guint verteces_count;
970   ID3D11Buffer *verteces_buffer = NULL;
971   ID3D11ShaderResourceView *shader_resource = NULL;
972   D3D11_SUBRESOURCE_DATA subresource_data;
973   D3D11_BUFFER_DESC buffer_desc;
974   D3D11_SHADER_RESOURCE_VIEW_DESC shader_desc;
975
976   shader_desc.Format = src_desc->Format;
977   shader_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
978   shader_desc.Texture2D.MostDetailedMip = src_desc->MipLevels - 1;
979   shader_desc.Texture2D.MipLevels = src_desc->MipLevels;
980   hr = ID3D11Device_CreateShaderResourceView (self->d3d11_device,
981       (ID3D11Resource *) src_texture, &shader_desc, &shader_resource);
982   HR_FAILED_GOTO (hr, ID3D11Device::CreateShaderResourceView, end);
983
984   ID3D11DeviceContext_OMSetRenderTargets (self->d3d11_context, 1,
985       &self->target_view, NULL);
986
987   ID3D11DeviceContext_VSSetShader (self->d3d11_context, self->vertex_shader,
988       NULL, 0);
989
990   ID3D11DeviceContext_PSSetShader (self->d3d11_context, self->pixel_shader,
991       NULL, 0);
992
993   ID3D11DeviceContext_PSSetShaderResources (self->d3d11_context, 0, 1,
994       &shader_resource);
995
996   ID3D11DeviceContext_PSSetSamplers (self->d3d11_context, 0, 1,
997       &self->sampler_state);
998
999   ID3D11DeviceContext_IASetPrimitiveTopology (self->d3d11_context,
1000       D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1001
1002   verteces_count = (move_count + dirty_count) * VERTEX_NUM;
1003   if (verteces_count > self->verteces_capacity) {
1004     /* not enough buffers */
1005     self->verteces_capacity = verteces_count * 2;
1006     self->dirty_verteces =
1007         g_renew (vertex, self->dirty_verteces, self->verteces_capacity);
1008     if (NULL == self->dirty_verteces) {
1009       hr = S_FALSE;
1010       goto end;
1011     }
1012   }
1013
1014   dst_rect_p = *dst_rect;
1015   vp = self->dirty_verteces;
1016   /* Create a vertex buffer to move and rotate from the move_rects.
1017    * And set the rectangular region to be copied to readable_texture. */
1018   for (i = 0; i < move_count; ++i) {
1019     /* Copy the area to be moved.
1020      * The source of the move is included in dirty_rects. */
1021     _set_verteces (self, vp, dst_rect_p, &self->work_texture_desc,
1022         &(self->move_rects[i].DestinationRect), src_desc);
1023     vp += VERTEX_NUM;
1024     ++dst_rect_p;
1025   }
1026   /* Create a vertex buffer to move and rotate from the dirty_rects.
1027    * And set the rectangular region to be copied to readable_texture. */
1028   for (i = 0; i < dirty_count; ++i) {
1029     _set_verteces (self, vp, dst_rect_p, &self->work_texture_desc,
1030         &(self->dirty_rects[i]), src_desc);
1031     vp += VERTEX_NUM;
1032     ++dst_rect_p;
1033   }
1034   *dst_rect = dst_rect_p;
1035
1036   memset (&buffer_desc, 0, sizeof (buffer_desc));
1037   buffer_desc.Usage = D3D11_USAGE_IMMUTABLE;
1038   buffer_desc.ByteWidth = verteces_count * sizeof (vertex);
1039   buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
1040   buffer_desc.CPUAccessFlags = 0;
1041
1042   memset (&subresource_data, 0, sizeof (subresource_data));
1043   subresource_data.pSysMem = self->dirty_verteces;
1044
1045   hr = ID3D11Device_CreateBuffer (self->d3d11_device, &buffer_desc,
1046       &subresource_data, &verteces_buffer);
1047   HR_FAILED_GOTO (hr, ID3D11Device::CreateBuffer, end);
1048
1049   stride = sizeof (vertex);
1050   offset = 0;
1051   ID3D11DeviceContext_IASetVertexBuffers (self->d3d11_context, 0, 1,
1052       &verteces_buffer, &stride, &offset);
1053
1054   ID3D11DeviceContext_RSSetViewports (self->d3d11_context, 1, &self->view_port);
1055
1056   /* Copy the rectangular region indicated by dirty_rects from the desktop frame to work_texture. */
1057   ID3D11DeviceContext_Draw (self->d3d11_context, verteces_count, 0);
1058
1059 end:
1060   PTR_RELEASE (verteces_buffer);
1061   PTR_RELEASE (shader_resource);
1062
1063   return hr;
1064 }
1065
1066 static void
1067 _set_verteces (DxgiCapture * self, vertex * verteces, RECT * dst_rect,
1068     const D3D11_TEXTURE2D_DESC * dst_desc, RECT * rect,
1069     const D3D11_TEXTURE2D_DESC * src_desc)
1070 {
1071   int center_x;
1072   int center_y;
1073
1074   /* Rectangular area is moved according to the rotation of the display. */
1075   _rotate_rect (self->dupl_desc.Rotation, dst_rect, rect, dst_desc->Width,
1076       dst_desc->Height);
1077
1078   /* Set the vertex buffer from the rotation of the display. */
1079   switch (self->dupl_desc.Rotation) {
1080     case DXGI_MODE_ROTATION_ROTATE90:
1081     verteces[0].texcoord = (vector2d) {
1082     (float) rect->right / (float) src_desc->Width,
1083           (float) rect->bottom / (float) src_desc->Height};
1084       verteces[1].texcoord = (vector2d) {
1085       (float) rect->left / (float) src_desc->Width,
1086             (float) rect->bottom / (float) src_desc->Height};
1087       verteces[2].texcoord = (vector2d) {
1088       (float) rect->right / (float) src_desc->Width,
1089             (float) rect->top / (float) src_desc->Height};
1090       verteces[5].texcoord = (vector2d) {
1091       (float) rect->left / (float) src_desc->Width,
1092             (float) rect->top / (float) src_desc->Height};
1093       break;
1094     case DXGI_MODE_ROTATION_ROTATE180:
1095     verteces[0].texcoord = (vector2d) {
1096     (float) rect->right / (float) src_desc->Width,
1097           (float) rect->top / (float) src_desc->Height};
1098       verteces[1].texcoord = (vector2d) {
1099       (float) rect->right / (float) src_desc->Width,
1100             (float) rect->bottom / (float) src_desc->Height};
1101       verteces[2].texcoord = (vector2d) {
1102       (float) rect->left / (float) src_desc->Width,
1103             (float) rect->top / (float) src_desc->Height};
1104       verteces[5].texcoord = (vector2d) {
1105       (float) rect->left / (float) src_desc->Width,
1106             (float) rect->bottom / (float) src_desc->Height};
1107       break;
1108     case DXGI_MODE_ROTATION_ROTATE270:
1109     verteces[0].texcoord = (vector2d) {
1110     (float) rect->left / (float) src_desc->Width,
1111           (float) rect->top / (float) src_desc->Height};
1112       verteces[1].texcoord = (vector2d) {
1113       (float) rect->right / (float) src_desc->Width,
1114             (float) rect->top / (float) src_desc->Height};
1115       verteces[2].texcoord = (vector2d) {
1116       (float) rect->left / (float) src_desc->Width,
1117             (float) rect->bottom / (float) src_desc->Height};
1118       verteces[5].texcoord = (vector2d) {
1119       (float) rect->right / (float) src_desc->Width,
1120             (float) rect->bottom / (float) src_desc->Height};
1121       break;
1122     default:
1123     verteces[0].texcoord = (vector2d) {
1124     (float) rect->left / (float) src_desc->Width,
1125           (float) rect->bottom / (float) src_desc->Height};
1126       verteces[1].texcoord = (vector2d) {
1127       (float) rect->left / (float) src_desc->Width,
1128             (float) rect->top / (float) src_desc->Height};
1129       verteces[2].texcoord = (vector2d) {
1130       (float) rect->right / (float) src_desc->Width,
1131             (float) rect->bottom / (float) src_desc->Height};
1132       verteces[5].texcoord = (vector2d) {
1133       (float) rect->right / (float) src_desc->Width,
1134             (float) rect->top / (float) src_desc->Height};
1135       break;
1136   }
1137   verteces[3].texcoord = verteces[2].texcoord;
1138   verteces[4].texcoord = verteces[1].texcoord;
1139
1140   center_x = (int) dst_desc->Width / 2;
1141   center_y = (int) dst_desc->Height / 2;
1142
1143   verteces[0].pos = (vector3d) {
1144   (float) (dst_rect->left - center_x) / (float) center_x,
1145         (float) (dst_rect->bottom - center_y) / (float) center_y *-1.0f, 0.0f};
1146   verteces[1].pos = (vector3d) {
1147   (float) (dst_rect->left - center_x) / (float) center_x,
1148         (float) (dst_rect->top - center_y) / (float) center_y *-1.0f, 0.0f};
1149   verteces[2].pos = (vector3d) {
1150   (float) (dst_rect->right - center_x) / (float) center_x,
1151         (float) (dst_rect->bottom - center_y) / (float) center_y *-1.0f, 0.0f};
1152   verteces[3].pos = verteces[2].pos;
1153   verteces[4].pos = verteces[1].pos;
1154   verteces[5].pos = (vector3d) {
1155   (float) (dst_rect->right - center_x) / (float) center_x,
1156         (float) (dst_rect->top - center_y) / (float) center_y *-1.0f, 0.0f};
1157 }
1158
1159 typedef struct _monitor_param_by_name
1160 {
1161   const gchar *device_name;
1162   HMONITOR hmonitor;
1163 } monitor_param_by_name;
1164
1165 static BOOL CALLBACK
1166 monitor_enum_proc_by_name (HMONITOR hmonitor, HDC hdc, LPRECT rect,
1167     LPARAM lparam)
1168 {
1169   MONITORINFOEXA monitor_info;
1170   monitor_param_by_name *param = (monitor_param_by_name *) lparam;
1171
1172   monitor_info.cbSize = sizeof (monitor_info);
1173   if (GetMonitorInfoA (hmonitor, (MONITORINFO *) & monitor_info)) {
1174     if (0 == g_strcmp0 (monitor_info.szDevice, param->device_name)) {
1175       param->hmonitor = hmonitor;
1176       return FALSE;
1177     }
1178   }
1179   return TRUE;
1180 }
1181
1182 HMONITOR
1183 get_hmonitor_by_device_name (const gchar * device_name)
1184 {
1185   monitor_param_by_name monitor = { device_name, NULL, };
1186   EnumDisplayMonitors (NULL, NULL, monitor_enum_proc_by_name,
1187       (LPARAM) & monitor);
1188   return monitor.hmonitor;
1189 }
1190
1191 static BOOL CALLBACK
1192 monitor_enum_proc_primary (HMONITOR hmonitor, HDC hdc, LPRECT rect,
1193     LPARAM lparam)
1194 {
1195   MONITORINFOEXA monitor_info;
1196   monitor_param_by_name *param = (monitor_param_by_name *) lparam;
1197
1198   monitor_info.cbSize = sizeof (monitor_info);
1199   if (GetMonitorInfoA (hmonitor, (MONITORINFO *) & monitor_info)) {
1200     if (MONITORINFOF_PRIMARY == monitor_info.dwFlags) {
1201       param->hmonitor = hmonitor;
1202       return FALSE;
1203     }
1204   }
1205   return TRUE;
1206 }
1207
1208 HMONITOR
1209 get_hmonitor_primary (void)
1210 {
1211   monitor_param_by_name monitor = { NULL, NULL, };
1212   EnumDisplayMonitors (NULL, NULL, monitor_enum_proc_primary,
1213       (LPARAM) & monitor);
1214   return monitor.hmonitor;
1215 }
1216
1217 typedef struct _monitor_param_by_index
1218 {
1219   int target;
1220   int counter;
1221   HMONITOR hmonitor;
1222 } monitor_param_by_index;
1223
1224 static BOOL CALLBACK
1225 monitor_enum_proc_by_index (HMONITOR hmonitor, HDC hdc, LPRECT rect,
1226     LPARAM lparam)
1227 {
1228   MONITORINFOEXA monitor_info;
1229   monitor_param_by_index *param = (monitor_param_by_index *) lparam;
1230
1231   monitor_info.cbSize = sizeof (monitor_info);
1232   if (GetMonitorInfoA (hmonitor, (MONITORINFO *) & monitor_info)) {
1233     if (param->target == param->counter) {
1234       param->hmonitor = hmonitor;
1235       return FALSE;
1236     }
1237   }
1238   ++param->counter;
1239   return TRUE;
1240 }
1241
1242 HMONITOR
1243 get_hmonitor_by_index (int index)
1244 {
1245   monitor_param_by_index monitor = { index, 0, NULL, };
1246   EnumDisplayMonitors (NULL, NULL, monitor_enum_proc_by_index,
1247       (LPARAM) & monitor);
1248   return monitor.hmonitor;
1249 }
1250
1251
1252 gboolean
1253 get_monitor_physical_size (HMONITOR hmonitor, LPRECT rect)
1254 {
1255   MONITORINFOEXW monitor_info;
1256   DEVMODEW dev_mode;
1257
1258   monitor_info.cbSize = sizeof (monitor_info);
1259   if (!GetMonitorInfoW (hmonitor, (LPMONITORINFO) & monitor_info)) {
1260     return FALSE;
1261   }
1262
1263   dev_mode.dmSize = sizeof (dev_mode);
1264   dev_mode.dmDriverExtra = sizeof (POINTL);
1265   dev_mode.dmFields = DM_POSITION;
1266   if (!EnumDisplaySettingsW
1267       (monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode)) {
1268     return FALSE;
1269   }
1270
1271   SetRect (rect, 0, 0, dev_mode.dmPelsWidth, dev_mode.dmPelsHeight);
1272   return TRUE;
1273 }
1274
1275 static const gchar *
1276 _hresult_to_string_fallback (HRESULT hr)
1277 {
1278   const gchar *s = "unknown error";
1279
1280   switch (hr) {
1281     case DXGI_ERROR_ACCESS_DENIED:
1282       s = "DXGI_ERROR_ACCESS_DENIED";
1283       break;
1284     case DXGI_ERROR_ACCESS_LOST:
1285       s = "DXGI_ERROR_ACCESS_LOST";
1286       break;
1287     case DXGI_ERROR_CANNOT_PROTECT_CONTENT:
1288       s = "DXGI_ERROR_CANNOT_PROTECT_CONTENT";
1289       break;
1290     case DXGI_ERROR_DEVICE_HUNG:
1291       s = "DXGI_ERROR_DEVICE_HUNG";
1292       break;
1293     case DXGI_ERROR_DEVICE_REMOVED:
1294       s = "DXGI_ERROR_DEVICE_REMOVED";
1295       break;
1296     case DXGI_ERROR_DEVICE_RESET:
1297       s = "DXGI_ERROR_DEVICE_RESET";
1298       break;
1299     case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
1300       s = "DXGI_ERROR_DRIVER_INTERNAL_ERROR";
1301       break;
1302     case DXGI_ERROR_FRAME_STATISTICS_DISJOINT:
1303       s = "DXGI_ERROR_FRAME_STATISTICS_DISJOINT";
1304       break;
1305     case DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE:
1306       s = "DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE";
1307       break;
1308     case DXGI_ERROR_INVALID_CALL:
1309       s = "DXGI_ERROR_INVALID_CALL";
1310       break;
1311     case DXGI_ERROR_MORE_DATA:
1312       s = "DXGI_ERROR_MORE_DATA";
1313       break;
1314     case DXGI_ERROR_NAME_ALREADY_EXISTS:
1315       s = "DXGI_ERROR_NAME_ALREADY_EXISTS";
1316       break;
1317     case DXGI_ERROR_NONEXCLUSIVE:
1318       s = "DXGI_ERROR_NONEXCLUSIVE";
1319       break;
1320     case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
1321       s = "DXGI_ERROR_NOT_CURRENTLY_AVAILABLE";
1322       break;
1323     case DXGI_ERROR_NOT_FOUND:
1324       s = "DXGI_ERROR_NOT_FOUND";
1325       break;
1326     case DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED:
1327       s = "DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED";
1328       break;
1329     case DXGI_ERROR_REMOTE_OUTOFMEMORY:
1330       s = "DXGI_ERROR_REMOTE_OUTOFMEMORY";
1331       break;
1332     case DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE:
1333       s = "DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE";
1334       break;
1335     case DXGI_ERROR_SDK_COMPONENT_MISSING:
1336       s = "DXGI_ERROR_SDK_COMPONENT_MISSING";
1337       break;
1338     case DXGI_ERROR_SESSION_DISCONNECTED:
1339       s = "DXGI_ERROR_SESSION_DISCONNECTED";
1340       break;
1341     case DXGI_ERROR_UNSUPPORTED:
1342       s = "DXGI_ERROR_UNSUPPORTED";
1343       break;
1344     case DXGI_ERROR_WAIT_TIMEOUT:
1345       s = "DXGI_ERROR_WAIT_TIMEOUT";
1346       break;
1347     case DXGI_ERROR_WAS_STILL_DRAWING:
1348       s = "DXGI_ERROR_WAS_STILL_DRAWING";
1349       break;
1350     case E_FAIL:
1351       s = "E_FAIL";
1352       break;
1353     case E_OUTOFMEMORY:
1354       s = "E_OUTOFMEMORY";
1355       break;
1356     case E_NOTIMPL:
1357       s = "E_NOTIMPL";
1358       break;
1359     case E_ACCESSDENIED:
1360       s = "E_ACCESSDENIED";
1361       break;
1362     case E_POINTER:
1363       s = "E_POINTER";
1364       break;
1365     case E_INVALIDARG:
1366       s = "E_INVALIDARG";
1367       break;
1368 #if defined(_MSC_VER) && (_MSC_VER >= 1800)
1369     case DXGI_ERROR_ALREADY_EXISTS:
1370       s = "DXGI_ERROR_ALREADY_EXISTS";
1371       break;
1372     case D3D11_ERROR_FILE_NOT_FOUND:
1373       s = "D3D11_ERROR_FILE_NOT_FOUND";
1374       break;
1375     case D3D11_ERROR_TOO_MANY_UNIQUE_STATE_OBJECTS:
1376       s = "D3D11_ERROR_TOO_MANY_UNIQUE_STATE_OBJECTS";
1377       break;
1378     case D3D11_ERROR_TOO_MANY_UNIQUE_VIEW_OBJECTS:
1379       s = "D3D11_ERROR_TOO_MANY_UNIQUE_VIEW_OBJECTS";
1380       break;
1381     case D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD:
1382       s = "D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD";
1383       break;
1384 #endif
1385   }
1386   return s;
1387 }
1388
1389 gchar *
1390 get_hresult_to_string (HRESULT hr)
1391 {
1392   gchar *error_text = NULL;
1393
1394   error_text = g_win32_error_message ((gint) hr);
1395   /* g_win32_error_message() doesn't cover all HERESULT return code,
1396    * so it could be empty string, or null if there was an error
1397    * in g_utf16_to_utf8() */
1398   if (!error_text || strlen (error_text) == 0) {
1399     g_free (error_text);
1400     error_text = g_strdup (_hresult_to_string_fallback (hr));
1401   }
1402
1403   return error_text;
1404 }