text: /*
   *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
   *
   *  Use of this source code is governed by a BSD-style license
   *  that can be found in the LICENSE file in the root of the source
   *  tree. An additional intellectual property rights grant can be found
   *  in the file PATENTS.  All contributing project authors may
   *  be found in the AUTHORS file in the root of the source tree.
   */
  
  #include "modules/desktop_capture/win/window_capture_utils.h"
  
  #include "modules/desktop_capture/win/scoped_gdi_object.h"
  #include BOSS_WEBRTC_U_rtc_base__checks_h //original-code:"rtc_base/checks.h"
  #include BOSS_WEBRTC_U_rtc_base__logging_h //original-code:"rtc_base/logging.h"
  #include BOSS_WEBRTC_U_rtc_base__win32_h //original-code:"rtc_base/win32.h"
  
  namespace webrtc {
  
  // Prefix used to match the window class for Chrome windows.
  const wchar_t kChromeWindowClassPrefix[] = L"Chrome_WidgetWin_";
  
  bool GetWindowRect(HWND window, DesktopRect* result) {
    RECT rect;
    if (!::GetWindowRect(window, &rect)) {
      return false;
    }
    *result = DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
    return true;
  }
  
  bool GetCroppedWindowRect(HWND window,
                            DesktopRect* cropped_rect,
                            DesktopRect* original_rect) {
    DesktopRect window_rect;
    if (!GetWindowRect(window, &window_rect)) {
      return false;
    }
  
    if (original_rect) {
      *original_rect = window_rect;
    }
    *cropped_rect = window_rect;
  
    bool is_maximized = false;
    if (!IsWindowMaximized(window, &is_maximized)) {
      return false;
    }
  
    // After Windows8, transparent borders will be added by OS at
    // left/bottom/right sides of a window. If the cropped window
    // doesn't remove these borders, the background will be exposed a bit.
    if (rtc::IsWindows8OrLater() || is_maximized) {
      const int width = GetSystemMetrics(SM_CXSIZEFRAME);
      const int height = GetSystemMetrics(SM_CYSIZEFRAME);
      cropped_rect->Extend(-width, 0, -width, -height);
    }
  
    return true;
  }
  
  bool GetWindowContentRect(HWND window, DesktopRect* result) {
    if (!GetWindowRect(window, result)) {
      return false;
    }
  
    RECT rect;
    if (!::GetClientRect(window, &rect)) {
      return false;
    }
  
    const int width = rect.right - rect.left;
    // The GetClientRect() is not expected to return a larger area than
    // GetWindowRect().
    if (width > 0 && width < result->width()) {
      // - GetClientRect() always set the left / top of RECT to 0. So we need to
      //   estimate the border width from GetClientRect() and GetWindowRect().
      // - Border width of a window varies according to the window type.
      // - GetClientRect() excludes the title bar, which should be considered as
      //   part of the content and included in the captured frame. So we always
      //   estimate the border width according to the window width.
      // - We assume a window has same border width in each side.
      // So we shrink half of the width difference from all four sides.
      const int shrink = ((width - result->width()) / 2);
      // When |shrink| is negative, DesktopRect::Extend() shrinks itself.
      result->Extend(shrink, 0, shrink, 0);
      // Usually this should not happen, just in case we have received a strange
      // window, which has only left and right borders.
      if (result->height() > shrink * 2) {
        result->Extend(0, shrink, 0, shrink);
      }
      RTC_DCHECK(!result->is_empty());
    }
  
    return true;
  }
  
  int GetWindowRegionTypeWithBoundary(HWND window, DesktopRect* result) {
    win::ScopedGDIObject<HRGN, win::DeleteObjectTraits<HRGN>> scoped_hrgn(
        CreateRectRgn(0, 0, 0, 0));
    const int region_type = GetWindowRgn(window, scoped_hrgn.Get());
  
    if (region_type == SIMPLEREGION) {
      RECT rect;
      GetRgnBox(scoped_hrgn.Get(), &rect);
      *result =
          DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
    }
    return region_type;
  }
  
  bool GetDcSize(HDC hdc, DesktopSize* size) {
    win::ScopedGDIObject<HGDIOBJ, win::DeleteObjectTraits<HGDIOBJ>> scoped_hgdi(
        GetCurrentObject(hdc, OBJ_BITMAP));
    BITMAP bitmap;
    memset(&bitmap, 0, sizeof(BITMAP));
    if (GetObject(scoped_hgdi.Get(), sizeof(BITMAP), &bitmap) == 0) {
      return false;
    }
    size->set(bitmap.bmWidth, bitmap.bmHeight);
    return true;
  }
  
  bool IsWindowMaximized(HWND window, bool* result) {
    WINDOWPLACEMENT placement;
    memset(&placement, 0, sizeof(WINDOWPLACEMENT));
    placement.length = sizeof(WINDOWPLACEMENT);
    if (!::GetWindowPlacement(window, &placement)) {
      return false;
    }
  
    *result = (placement.showCmd == SW_SHOWMAXIMIZED);
    return true;
  }
  
  // WindowCaptureHelperWin implementation.
  WindowCaptureHelperWin::WindowCaptureHelperWin()
      : dwmapi_library_(nullptr),
        func_(nullptr),
        virtual_desktop_manager_(nullptr) {
    // Try to load dwmapi.dll dynamically since it is not available on XP.
    dwmapi_library_ = LoadLibrary(L"dwmapi.dll");
    if (dwmapi_library_) {
      func_ = reinterpret_cast<DwmIsCompositionEnabledFunc>(
          GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled"));
    }
  
    if (rtc::IsWindows10OrLater()) {
      if (FAILED(::CoCreateInstance(__uuidof(VirtualDesktopManager), nullptr,
                                    CLSCTX_ALL,
                                    IID_PPV_ARGS(&virtual_desktop_manager_)))) {
        RTC_LOG(LS_WARNING) << "Fail to create instance of VirtualDesktopManager";
      }
    }
  }
  
  WindowCaptureHelperWin::~WindowCaptureHelperWin() {
    if (dwmapi_library_) {
      FreeLibrary(dwmapi_library_);
    }
  }
  
  bool WindowCaptureHelperWin::IsAeroEnabled() {
    BOOL result = FALSE;
    if (func_) {
      func_(&result);
    }
    return result != FALSE;
  }
  
  // This is just a best guess of a notification window. Chrome uses the Windows
  // native framework for showing notifications. So far what we know about such a
  // window includes: no title, class name with prefix "Chrome_WidgetWin_" and
  // with certain extended styles.
  bool WindowCaptureHelperWin::IsWindowChromeNotification(HWND hwnd) {
    const size_t kTitleLength = 32;
    WCHAR window_title[kTitleLength];
    GetWindowText(hwnd, window_title, kTitleLength);
    if (wcsnlen_s(window_title, kTitleLength) != 0) {
      return false;
    }
  
    const size_t kClassLength = 256;
    WCHAR class_name[kClassLength];
    const int class_name_length = GetClassName(hwnd, class_name, kClassLength);
    RTC_DCHECK(class_name_length)
        << "Error retrieving the application's class name";
    if (wcsncmp(class_name, kChromeWindowClassPrefix,
                wcsnlen_s(kChromeWindowClassPrefix, kClassLength)) != 0) {
      return false;
    }
  
    const LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
    if ((exstyle & WS_EX_NOACTIVATE) && (exstyle & WS_EX_TOOLWINDOW) &&
        (exstyle & WS_EX_TOPMOST)) {
      return true;
    }
  
    return false;
  }
  
  bool WindowCaptureHelperWin::IsWindowOnCurrentDesktop(HWND hwnd) {
    // Make sure the window is on the current virtual desktop.
    if (virtual_desktop_manager_) {
      BOOL on_current_desktop;
      if (SUCCEEDED(virtual_desktop_manager_->IsWindowOnCurrentVirtualDesktop(
              hwnd, &on_current_desktop)) &&
          !on_current_desktop) {
        return false;
      }
    }
    return true;
  }
  
  bool WindowCaptureHelperWin::IsWindowVisibleOnCurrentDesktop(HWND hwnd) {
    return !::IsIconic(hwnd) && ::IsWindowVisible(hwnd) &&
           IsWindowOnCurrentDesktop(hwnd);
  }
  
  }  // namespace webrtc
  