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