/ src / modules / ShortcutGuide / ShortcutGuide / d2d_window.cpp
d2d_window.cpp
  1  #include "pch.h"
  2  #include "d2d_window.h"
  3  
  4  #include <common/utils/resources.h>
  5  
  6  D2DWindow::D2DWindow()
  7  {
  8      static const WCHAR* class_name = L"PToyD2DPopup";
  9      WNDCLASS wc = {};
 10      wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
 11      wc.hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
 12      wc.lpszClassName = class_name;
 13      wc.style = CS_HREDRAW | CS_VREDRAW;
 14      wc.lpfnWndProc = d2d_window_proc;
 15      RegisterClass(&wc);
 16      hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOREDIRECTIONBITMAP | WS_EX_LAYERED,
 17                             wc.lpszClassName,
 18                             L"PToyD2DPopup",
 19                             WS_POPUP | WS_VISIBLE,
 20                             CW_USEDEFAULT,
 21                             CW_USEDEFAULT,
 22                             CW_USEDEFAULT,
 23                             CW_USEDEFAULT,
 24                             nullptr,
 25                             nullptr,
 26                             wc.hInstance,
 27                             this);
 28      WINRT_VERIFY(hwnd);
 29  }
 30  
 31  void D2DWindow::show(UINT x, UINT y, UINT width, UINT height)
 32  {
 33      if (!initialized)
 34      {
 35          base_init();
 36      }
 37      base_resize(width, height);
 38      render_empty();
 39      hidden = false;
 40      on_show();
 41      SetWindowPos(hwnd, HWND_TOPMOST, x, y, width, height, 0);
 42      ShowWindow(hwnd, SW_SHOWNORMAL);
 43      SetForegroundWindow(hwnd);
 44      UpdateWindow(hwnd);
 45  }
 46  
 47  void D2DWindow::hide()
 48  {
 49      hidden = true;
 50      ShowWindow(hwnd, SW_HIDE);
 51      on_hide();
 52  }
 53  
 54  void D2DWindow::initialize()
 55  {
 56      base_init();
 57  }
 58  
 59  void D2DWindow::base_init()
 60  {
 61      std::unique_lock lock(mutex);
 62      // D2D1Factory is independent from the device, no need to recreate it if we need to recreate the device.
 63      if (!d2d_factory)
 64      {
 65  #ifdef _DEBUG
 66          D2D1_FACTORY_OPTIONS options = { D2D1_DEBUG_LEVEL_INFORMATION };
 67  #else
 68          D2D1_FACTORY_OPTIONS options = {};
 69  #endif
 70          winrt::check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
 71                                                 __uuidof(d2d_factory),
 72                                                 &options,
 73                                                 d2d_factory.put_void()));
 74      }
 75      // For all other stuff - assign nullptr first to release the object, to reset the com_ptr.
 76      d2d_dc = nullptr;
 77      d2d_device = nullptr;
 78      dxgi_factory = nullptr;
 79      dxgi_device = nullptr;
 80      d3d_device = nullptr;
 81      winrt::check_hresult(D3D11CreateDevice(nullptr,
 82                                             D3D_DRIVER_TYPE_HARDWARE,
 83                                             nullptr,
 84                                             D3D11_CREATE_DEVICE_BGRA_SUPPORT,
 85                                             nullptr,
 86                                             0,
 87                                             D3D11_SDK_VERSION,
 88                                             d3d_device.put(),
 89                                             nullptr,
 90                                             nullptr));
 91      winrt::check_hresult(d3d_device->QueryInterface(__uuidof(dxgi_device), dxgi_device.put_void()));
 92      winrt::check_hresult(CreateDXGIFactory2(0, __uuidof(dxgi_factory), dxgi_factory.put_void()));
 93      winrt::check_hresult(d2d_factory->CreateDevice(dxgi_device.get(), d2d_device.put()));
 94      winrt::check_hresult(d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, d2d_dc.put()));
 95      init();
 96      initialized = true;
 97  }
 98  
 99  void D2DWindow::base_resize(UINT width, UINT height)
100  {
101      std::unique_lock lock(mutex);
102      if (!initialized)
103      {
104          return;
105      }
106      window_width = width;
107      window_height = height;
108      if (window_width == 0 || window_height == 0)
109      {
110          return;
111      }
112      DXGI_SWAP_CHAIN_DESC1 sc_description = {};
113      sc_description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
114      sc_description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
115      sc_description.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
116      sc_description.BufferCount = 2;
117      sc_description.SampleDesc.Count = 1;
118      sc_description.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
119      sc_description.Width = window_width;
120      sc_description.Height = window_height;
121      dxgi_swap_chain = nullptr;
122      winrt::check_hresult(dxgi_factory->CreateSwapChainForComposition(dxgi_device.get(),
123                                                                       &sc_description,
124                                                                       nullptr,
125                                                                       dxgi_swap_chain.put()));
126      composition_device = nullptr;
127      winrt::check_hresult(DCompositionCreateDevice(dxgi_device.get(),
128                                                    __uuidof(composition_device),
129                                                    composition_device.put_void()));
130  
131      composition_target = nullptr;
132      winrt::check_hresult(composition_device->CreateTargetForHwnd(hwnd, true, composition_target.put()));
133  
134      composition_visual = nullptr;
135      winrt::check_hresult(composition_device->CreateVisual(composition_visual.put()));
136      winrt::check_hresult(composition_visual->SetContent(dxgi_swap_chain.get()));
137      winrt::check_hresult(composition_target->SetRoot(composition_visual.get()));
138  
139      dxgi_surface = nullptr;
140      winrt::check_hresult(dxgi_swap_chain->GetBuffer(0, __uuidof(dxgi_surface), dxgi_surface.put_void()));
141      D2D1_BITMAP_PROPERTIES1 properties = {};
142      properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
143      properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
144      properties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
145  
146      d2d_bitmap = nullptr;
147      winrt::check_hresult(d2d_dc->CreateBitmapFromDxgiSurface(dxgi_surface.get(),
148                                                               properties,
149                                                               d2d_bitmap.put()));
150      d2d_dc->SetTarget(d2d_bitmap.get());
151      resize();
152  }
153  
154  void D2DWindow::base_render()
155  {
156      std::unique_lock lock(mutex);
157      if (!initialized || !d2d_dc || !d2d_bitmap)
158          return;
159      d2d_dc->BeginDraw();
160      render(d2d_dc.get());
161      winrt::check_hresult(d2d_dc->EndDraw());
162      winrt::check_hresult(dxgi_swap_chain->Present(1, 0));
163      winrt::check_hresult(composition_device->Commit());
164  }
165  
166  void D2DWindow::render_empty()
167  {
168      std::unique_lock lock(mutex);
169      if (!initialized || !d2d_dc || !d2d_bitmap)
170          return;
171      d2d_dc->BeginDraw();
172      d2d_dc->Clear();
173      winrt::check_hresult(d2d_dc->EndDraw());
174      winrt::check_hresult(dxgi_swap_chain->Present(1, 0));
175      winrt::check_hresult(composition_device->Commit());
176  }
177  
178  D2DWindow::~D2DWindow()
179  {
180      ShowWindow(hwnd, SW_HIDE);
181      DestroyWindow(hwnd);
182  }
183  
184  D2DWindow* D2DWindow::this_from_hwnd(HWND window)
185  {
186      return reinterpret_cast<D2DWindow*>(GetWindowLongPtr(window, GWLP_USERDATA));
187  }
188  
189  LRESULT __stdcall D2DWindow::d2d_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
190  {
191      auto self = this_from_hwnd(window);
192      switch (message)
193      {
194      case WM_NCCREATE:
195      {
196          auto create_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
197          SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(create_struct->lpCreateParams));
198          return TRUE;
199      }
200      case WM_MOVE:
201      case WM_SIZE:
202          self->base_resize(static_cast<unsigned>(lparam) & 0xFFFF, static_cast<unsigned>(lparam) >> 16);
203          [[fallthrough]];
204      case WM_PAINT:
205          self->base_render();
206          return 0;
207  
208      default:
209          return DefWindowProc(window, message, wparam, lparam);
210      }
211  }