Home | History | Annotate | Download | only in win
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/gfx/win/hwnd_util.h"
      6 
      7 #include "base/i18n/rtl.h"
      8 #include "base/strings/string_util.h"
      9 #include "base/win/metro.h"
     10 #include "base/win/win_util.h"
     11 #include "ui/gfx/point.h"
     12 #include "ui/gfx/rect.h"
     13 #include "ui/gfx/size.h"
     14 
     15 namespace gfx {
     16 
     17 namespace {
     18 
     19 // Adjust the window to fit.
     20 void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) {
     21   if (fit_to_monitor) {
     22     // Get the monitor.
     23     HMONITOR hmon = MonitorFromRect(&bounds, MONITOR_DEFAULTTONEAREST);
     24     if (hmon) {
     25       MONITORINFO mi;
     26       mi.cbSize = sizeof(mi);
     27       GetMonitorInfo(hmon, &mi);
     28       Rect window_rect(bounds);
     29       Rect monitor_rect(mi.rcWork);
     30       Rect new_window_rect = window_rect;
     31       new_window_rect.AdjustToFit(monitor_rect);
     32       if (new_window_rect != window_rect) {
     33         // Window doesn't fit on monitor, move and possibly resize.
     34         SetWindowPos(hwnd, 0, new_window_rect.x(), new_window_rect.y(),
     35                      new_window_rect.width(), new_window_rect.height(),
     36                      SWP_NOACTIVATE | SWP_NOZORDER);
     37         return;
     38       }
     39       // Else fall through.
     40     } else {
     41       NOTREACHED() << "Unable to find default monitor";
     42       // Fall through.
     43     }
     44   }  // Else fall through.
     45 
     46   // The window is not being fit to monitor, or the window fits on the monitor
     47   // as is, or we have no monitor info; reset the bounds.
     48   ::SetWindowPos(hwnd, 0, bounds.left, bounds.top,
     49                  bounds.right - bounds.left, bounds.bottom - bounds.top,
     50                  SWP_NOACTIVATE | SWP_NOZORDER);
     51 }
     52 
     53 // Turn off optimizations for these functions so they show up in crash reports.
     54 MSVC_DISABLE_OPTIMIZE();
     55 
     56 void CrashOutOfMemory() {
     57   PLOG(FATAL);
     58 }
     59 
     60 void CrashAccessDenied() {
     61   PLOG(FATAL);
     62 }
     63 
     64 // Crash isn't one of the ones we commonly see.
     65 void CrashOther() {
     66   PLOG(FATAL);
     67 }
     68 
     69 MSVC_ENABLE_OPTIMIZE();
     70 
     71 }  // namespace
     72 
     73 base::string16 GetClassName(HWND window) {
     74   // GetClassNameW will return a truncated result (properly null terminated) if
     75   // the given buffer is not large enough.  So, it is not possible to determine
     76   // that we got the entire class name if the result is exactly equal to the
     77   // size of the buffer minus one.
     78   DWORD buffer_size = MAX_PATH;
     79   while (true) {
     80     std::wstring output;
     81     DWORD size_ret =
     82         GetClassNameW(window, WriteInto(&output, buffer_size), buffer_size);
     83     if (size_ret == 0)
     84       break;
     85     if (size_ret < (buffer_size - 1)) {
     86       output.resize(size_ret);
     87       return output;
     88     }
     89     buffer_size *= 2;
     90   }
     91   return std::wstring();  // error
     92 }
     93 
     94 #pragma warning(push)
     95 #pragma warning(disable:4312 4244)
     96 
     97 WNDPROC SetWindowProc(HWND hwnd, WNDPROC proc) {
     98   // The reason we don't return the SetwindowLongPtr() value is that it returns
     99   // the orignal window procedure and not the current one. I don't know if it is
    100   // a bug or an intended feature.
    101   WNDPROC oldwindow_proc =
    102       reinterpret_cast<WNDPROC>(GetWindowLongPtr(hwnd, GWLP_WNDPROC));
    103   SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(proc));
    104   return oldwindow_proc;
    105 }
    106 
    107 void* SetWindowUserData(HWND hwnd, void* user_data) {
    108   return
    109       reinterpret_cast<void*>(SetWindowLongPtr(hwnd, GWLP_USERDATA,
    110           reinterpret_cast<LONG_PTR>(user_data)));
    111 }
    112 
    113 void* GetWindowUserData(HWND hwnd) {
    114   DWORD process_id = 0;
    115   DWORD thread_id = GetWindowThreadProcessId(hwnd, &process_id);
    116   // A window outside the current process needs to be ignored.
    117   if (process_id != ::GetCurrentProcessId())
    118     return NULL;
    119   return reinterpret_cast<void*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
    120 }
    121 
    122 #pragma warning(pop)
    123 
    124 bool DoesWindowBelongToActiveWindow(HWND window) {
    125   DCHECK(window);
    126   HWND top_window = ::GetAncestor(window, GA_ROOT);
    127   if (!top_window)
    128     return false;
    129 
    130   HWND active_top_window = ::GetAncestor(::GetForegroundWindow(), GA_ROOT);
    131   return (top_window == active_top_window);
    132 }
    133 
    134 void CenterAndSizeWindow(HWND parent,
    135                          HWND window,
    136                          const Size& pref) {
    137   DCHECK(window && pref.width() > 0 && pref.height() > 0);
    138 
    139   // Calculate the ideal bounds.
    140   RECT window_bounds;
    141   RECT center_bounds = {0};
    142   if (parent) {
    143     // If there is a parent, center over the parents bounds.
    144     ::GetWindowRect(parent, &center_bounds);
    145   }
    146 
    147   if (::IsRectEmpty(&center_bounds)) {
    148     // No parent or no parent rect. Center over the monitor the window is on.
    149     HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);
    150     if (monitor) {
    151       MONITORINFO mi = {0};
    152       mi.cbSize = sizeof(mi);
    153       GetMonitorInfo(monitor, &mi);
    154       center_bounds = mi.rcWork;
    155     } else {
    156       NOTREACHED() << "Unable to get default monitor";
    157     }
    158   }
    159 
    160   window_bounds.left = center_bounds.left;
    161   if (pref.width() < (center_bounds.right - center_bounds.left)) {
    162     window_bounds.left +=
    163         (center_bounds.right - center_bounds.left - pref.width()) / 2;
    164   }
    165   window_bounds.right = window_bounds.left + pref.width();
    166 
    167   window_bounds.top = center_bounds.top;
    168   if (pref.height() < (center_bounds.bottom - center_bounds.top)) {
    169     window_bounds.top +=
    170         (center_bounds.bottom - center_bounds.top - pref.height()) / 2;
    171   }
    172   window_bounds.bottom = window_bounds.top + pref.height();
    173 
    174   // If we're centering a child window, we are positioning in client
    175   // coordinates, and as such we need to offset the target rectangle by the
    176   // position of the parent window.
    177   if (::GetWindowLong(window, GWL_STYLE) & WS_CHILD) {
    178     DCHECK(parent && ::GetParent(window) == parent);
    179     POINT topleft = { window_bounds.left, window_bounds.top };
    180     ::MapWindowPoints(HWND_DESKTOP, parent, &topleft, 1);
    181     window_bounds.left = topleft.x;
    182     window_bounds.top = topleft.y;
    183     window_bounds.right = window_bounds.left + pref.width();
    184     window_bounds.bottom = window_bounds.top + pref.height();
    185   }
    186 
    187   AdjustWindowToFit(window, window_bounds, !parent);
    188 }
    189 
    190 void CheckWindowCreated(HWND hwnd) {
    191   if (!hwnd) {
    192     switch (GetLastError()) {
    193       case ERROR_NOT_ENOUGH_MEMORY:
    194         CrashOutOfMemory();
    195         break;
    196       case ERROR_ACCESS_DENIED:
    197         CrashAccessDenied();
    198         break;
    199       default:
    200         CrashOther();
    201         break;
    202     }
    203     PLOG(FATAL);
    204   }
    205 }
    206 
    207 void ShowSystemMenu(HWND window) {
    208   RECT rect;
    209   GetWindowRect(window, &rect);
    210   Point point = Point(base::i18n::IsRTL() ? rect.right : rect.left, rect.top);
    211   static const int kSystemMenuOffset = 10;
    212   point.Offset(base::i18n::IsRTL() ? -kSystemMenuOffset : kSystemMenuOffset,
    213                kSystemMenuOffset);
    214   ShowSystemMenuAtPoint(window, point);
    215 }
    216 
    217 void ShowSystemMenuAtPoint(HWND window, const Point& point) {
    218   // In the Metro process, we never want to show the system menu.
    219   if (base::win::IsMetroProcess())
    220     return;
    221   UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD;
    222   if (base::i18n::IsRTL())
    223     flags |= TPM_RIGHTALIGN;
    224   HMENU menu = GetSystemMenu(window, FALSE);
    225   const int command =
    226       TrackPopupMenu(menu, flags, point.x(), point.y(), 0, window, NULL);
    227   if (command)
    228     SendMessage(window, WM_SYSCOMMAND, command, 0);
    229 }
    230 
    231 extern "C" {
    232   typedef HWND (*RootWindow)();
    233 }
    234 
    235 HWND GetWindowToParentTo(bool get_real_hwnd) {
    236   HMODULE metro = base::win::GetMetroModule();
    237   if (!metro)
    238     return get_real_hwnd ? ::GetDesktopWindow() : HWND_DESKTOP;
    239   // In windows 8 metro-mode the root window is not the desktop.
    240   RootWindow root_window =
    241       reinterpret_cast<RootWindow>(::GetProcAddress(metro, "GetRootWindow"));
    242   return root_window();
    243 }
    244 
    245 }  // namespace gfx
    246