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