LetalisDev

Untitled

May 18th, 2021
71
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include "win32_window.h"
  2.  
  3. #include <flutter_windows.h>
  4. #include <shellapi.h> //for systray
  5. #include <strsafe.h> //for systray text
  6. #include <tchar.h> //for systray menu
  7. #include <iostream>
  8. #include <string>
  9.  
  10. #include "resource.h"
  11.  
  12. namespace {
  13.  
  14. constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
  15.  
  16. // The number of Win32Window objects that currently exist.
  17. static int g_active_window_count = 0;
  18.  
  19. //For systray:
  20. HINSTANCE hInst;                    // current instance
  21. NOTIFYICONDATA nidApp;
  22. HMENU hPopMenu;
  23. BOOL bDisable = FALSE;              // keep application state
  24. #define WM_USER_SHELLICON WM_USER + 1
  25. #define IDM_SHOW 104
  26. #define IDM_EXIT 105
  27.  
  28. using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
  29.  
  30. // Scale helper to convert logical scaler values to physical using passed in
  31. // scale factor
  32. int Scale(int source, double scale_factor) {
  33.   return static_cast<int>(source * scale_factor);
  34. }
  35.  
  36. // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
  37. // This API is only needed for PerMonitor V1 awareness mode.
  38. void EnableFullDpiSupportIfAvailable(HWND hwnd) {
  39.   HMODULE user32_module = LoadLibraryA("User32.dll");
  40.   if (!user32_module) {
  41.     return;
  42.   }
  43.   auto enable_non_client_dpi_scaling =
  44.       reinterpret_cast<EnableNonClientDpiScaling*>(
  45.           GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
  46.   if (enable_non_client_dpi_scaling != nullptr) {
  47.     enable_non_client_dpi_scaling(hwnd);
  48.     FreeLibrary(user32_module);
  49.   }
  50. }
  51.  
  52. }  // namespace
  53.  
  54. // Manages the Win32Window's window class registration.
  55. class WindowClassRegistrar {
  56.  public:
  57.   ~WindowClassRegistrar() = default;
  58.  
  59.   // Returns the singleton registar instance.
  60.   static WindowClassRegistrar* GetInstance() {
  61.     if (!instance_) {
  62.       instance_ = new WindowClassRegistrar();
  63.     }
  64.     return instance_;
  65.   }
  66.  
  67.   // Returns the name of the window class, registering the class if it hasn't
  68.   // previously been registered.
  69.   const wchar_t* GetWindowClass();
  70.  
  71.   // Unregisters the window class. Should only be called if there are no
  72.   // instances of the window.
  73.   void UnregisterWindowClass();
  74.  
  75.  private:
  76.   WindowClassRegistrar() = default;
  77.  
  78.   static WindowClassRegistrar* instance_;
  79.  
  80.   bool class_registered_ = false;
  81. };
  82.  
  83. WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
  84.  
  85. const wchar_t* WindowClassRegistrar::GetWindowClass() {
  86.   if (!class_registered_) {
  87.     WNDCLASS window_class{};
  88.     window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
  89.     window_class.lpszClassName = kWindowClassName;
  90.     window_class.style = CS_HREDRAW | CS_VREDRAW;
  91.     window_class.cbClsExtra = 0;
  92.     window_class.cbWndExtra = 0;
  93.     window_class.hInstance = GetModuleHandle(nullptr);
  94.     window_class.hIcon =
  95.         LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
  96.     window_class.hbrBackground = 0;
  97.     window_class.lpszMenuName = nullptr;
  98.     window_class.lpfnWndProc = Win32Window::WndProc;
  99.     RegisterClass(&window_class);
  100.     class_registered_ = true;
  101.  
  102.     //For systray:
  103.     hInst = window_class.hInstance;
  104.   }
  105.   return kWindowClassName;
  106. }
  107.  
  108. void WindowClassRegistrar::UnregisterWindowClass() {
  109.   UnregisterClass(kWindowClassName, nullptr);
  110.   class_registered_ = false;
  111. }
  112.  
  113. Win32Window::Win32Window() {
  114.   ++g_active_window_count;
  115. }
  116.  
  117. Win32Window::~Win32Window() {
  118.   --g_active_window_count;
  119.   Destroy();
  120. }
  121.  
  122. bool Win32Window::CreateAndShow(const std::wstring& title,
  123.                                 const Point& origin,
  124.                                 const Size& size) {
  125.   Destroy();
  126.  
  127.   const wchar_t* window_class =
  128.       WindowClassRegistrar::GetInstance()->GetWindowClass();
  129.  
  130.   const POINT target_point = {static_cast<LONG>(origin.x),
  131.                               static_cast<LONG>(origin.y)};
  132.   HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
  133.   UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
  134.   double scale_factor = dpi / 96.0;
  135.  
  136.   //New code, create dwStyle and modify to make non-resizable:
  137.   DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
  138.   dwStyle &= ~WS_SIZEBOX; //applies NOT operator to WS_SIZEBOX bit to disable dragging to resize
  139.   dwStyle &= ~WS_MAXIMIZEBOX; //disables maximise button
  140.   HWND window = CreateWindow(
  141.       window_class, title.c_str(), dwStyle,
  142.       Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
  143.       Scale(size.width, scale_factor), Scale(size.height, scale_factor),
  144.       nullptr, nullptr, GetModuleHandle(nullptr), this);
  145.  
  146.   if (!window) {
  147.     return false;
  148.   }
  149.  
  150.   //Systray:
  151.   HICON hMainIcon;
  152.   hMainIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_APP_ICON));
  153.   nidApp.cbSize = sizeof(NOTIFYICONDATA); // sizeof the struct in bytes
  154.   nidApp.hWnd = (HWND) window;              //handle of the window which will process this app. messages
  155.   nidApp.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; //ORing of all the flags
  156.   nidApp.hIcon = hMainIcon; // handle of the Icon to be displayed, obtained from LoadIcon
  157.   nidApp.uCallbackMessage = WM_USER_SHELLICON;
  158.   StringCchCopy(nidApp.szTip, ARRAYSIZE(nidApp.szTip), L"All Platforms Test");
  159.   Shell_NotifyIcon(NIM_ADD, &nidApp);
  160.  
  161.   return OnCreate();
  162. }
  163.  
  164. // static
  165. LRESULT CALLBACK Win32Window::WndProc(HWND const window,
  166.                                       UINT const message,
  167.                                       WPARAM const wparam,
  168.                                       LPARAM const lparam) noexcept {
  169.   if (message == WM_NCCREATE) {
  170.     auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
  171.     SetWindowLongPtr(window, GWLP_USERDATA,
  172.                      reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
  173.  
  174.     auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
  175.     EnableFullDpiSupportIfAvailable(window);
  176.     that->window_handle_ = window;
  177.   }
  178.   else if (message == WM_USER_SHELLICON) { //interacting with systray icon
  179.     if (LOWORD(lparam) == WM_RBUTTONDOWN) { //right clicked
  180.         POINT lpClickPoint;
  181.         GetCursorPos(&lpClickPoint);
  182.         hPopMenu = CreatePopupMenu();
  183.         InsertMenu(hPopMenu,0xFFFFFFFF,MF_BYPOSITION|MF_STRING,IDM_SHOW,_T("Show"));
  184.         InsertMenu(hPopMenu,0xFFFFFFFF,MF_BYPOSITION|MF_STRING,IDM_EXIT,_T("Quit"));
  185.         SetForegroundWindow(window);
  186.         TrackPopupMenu(hPopMenu,TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_BOTTOMALIGN,lpClickPoint.x, lpClickPoint.y,0,window,NULL);
  187.     }
  188.     else if (LOWORD(lparam) == WM_LBUTTONDOWN) { //left clicked
  189.         ShowWindow(window, SW_NORMAL);
  190.         //LOOK: works but shows blank screen until is interacted with (mouse enters or key is pressed etc)
  191.     }
  192.   }
  193.   else if (message == WM_COMMAND) { //if message is a command event such as a click on the exit menu option
  194.       int wmId;
  195.       wmId = LOWORD(wparam);
  196.  
  197.       if (wmId == IDM_EXIT) { //if quit has been pressed
  198.           Shell_NotifyIcon(NIM_DELETE,&nidApp);
  199.           DestroyWindow(window);
  200.       }
  201.       else if (wmId == IDM_SHOW) {
  202.           ShowWindow(window, SW_NORMAL);
  203.           //LOOK: works but shows blank screen until is interacted with (mouse enters or key is pressed etc)
  204.       }
  205.   }
  206.   else if (Win32Window* that = GetThisFromHandle(window)) {
  207.     return that->MessageHandler(window, message, wparam, lparam);
  208.   }
  209.  
  210.   return DefWindowProc(window, message, wparam, lparam);
  211. }
  212.  
  213. LRESULT
  214. Win32Window::MessageHandler(HWND hwnd,
  215.                             UINT const message,
  216.                             WPARAM const wparam,
  217.                             LPARAM const lparam) noexcept {
  218.   switch (message) {
  219.     case WM_DESTROY:
  220.       window_handle_ = nullptr;
  221.       Destroy();
  222.       if (quit_on_close_) {
  223.         PostQuitMessage(0);
  224.       }
  225.       return 0;
  226.  
  227.     case WM_DPICHANGED: {
  228.       auto newRectSize = reinterpret_cast<RECT*>(lparam);
  229.       LONG newWidth = newRectSize->right - newRectSize->left;
  230.       LONG newHeight = newRectSize->bottom - newRectSize->top;
  231.  
  232.       SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
  233.                    newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
  234.  
  235.       return 0;
  236.     }
  237.     case WM_SIZE: {
  238.       RECT rect = GetClientArea();
  239.       if (child_content_ != nullptr) {
  240.         // Size and position the child window.
  241.         MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
  242.                    rect.bottom - rect.top, TRUE);
  243.       }
  244.       return 0;
  245.     }
  246.  
  247.     case WM_ACTIVATE:
  248.       if (child_content_ != nullptr) {
  249.         SetFocus(child_content_);
  250.       }
  251.       return 0;
  252.  
  253.     case WM_CLOSE: //stop window from closing normally, can only be closed when DestroyWindow() is run from systray
  254.       //Hide window and continue running in background.
  255.       ShowWindow(hwnd, SW_HIDE);
  256.       return 0;
  257.   }
  258.  
  259.   return DefWindowProc(window_handle_, message, wparam, lparam);
  260. }
  261.  
  262. void Win32Window::Destroy() {
  263.   //Remove systray icon:
  264.   Shell_NotifyIcon(NIM_DELETE,&nidApp);
  265.  
  266.   OnDestroy();
  267.  
  268.   if (window_handle_) {
  269.     DestroyWindow(window_handle_);
  270.     window_handle_ = nullptr;
  271.   }
  272.   if (g_active_window_count == 0) {
  273.     WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
  274.   }
  275. }
  276.  
  277. Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
  278.   return reinterpret_cast<Win32Window*>(
  279.       GetWindowLongPtr(window, GWLP_USERDATA));
  280. }
  281.  
  282. void Win32Window::SetChildContent(HWND content) {
  283.   child_content_ = content;
  284.   SetParent(content, window_handle_);
  285.   RECT frame = GetClientArea();
  286.  
  287.   MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
  288.              frame.bottom - frame.top, true);
  289.  
  290.   SetFocus(child_content_);
  291. }
  292.  
  293. RECT Win32Window::GetClientArea() {
  294.   RECT frame;
  295.   GetClientRect(window_handle_, &frame);
  296.   return frame;
  297. }
  298.  
  299. HWND Win32Window::GetHandle() {
  300.   return window_handle_;
  301. }
  302.  
  303. void Win32Window::SetQuitOnClose(bool quit_on_close) {
  304.   quit_on_close_ = quit_on_close;
  305. }
  306.  
  307. bool Win32Window::OnCreate() {
  308.   // No-op; provided for subclasses.
  309.   return true;
  310. }
  311.  
  312. void Win32Window::OnDestroy() {
  313.   // No-op; provided for subclasses.
  314. }
  315.  
RAW Paste Data