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/base/win/hwnd_subclass.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_vector.h"
     11 #include "base/memory/singleton.h"
     12 #include "ui/gfx/win/dpi.h"
     13 #include "ui/gfx/win/hwnd_util.h"
     14 
     15 namespace {
     16 const char kHWNDSubclassKey[] = "__UI_BASE_WIN_HWND_SUBCLASS_PROC__";
     17 
     18 LRESULT CALLBACK WndProc(HWND hwnd,
     19                          UINT message,
     20                          WPARAM w_param,
     21                          LPARAM l_param) {
     22   ui::HWNDSubclass* wrapped_wnd_proc =
     23       reinterpret_cast<ui::HWNDSubclass*>(
     24           ui::ViewProp::GetValue(hwnd, kHWNDSubclassKey));
     25   return wrapped_wnd_proc ? wrapped_wnd_proc->OnWndProc(hwnd,
     26                                                         message,
     27                                                         w_param,
     28                                                         l_param)
     29                           : DefWindowProc(hwnd, message, w_param, l_param);
     30 }
     31 
     32 WNDPROC GetCurrentWndProc(HWND target) {
     33   return reinterpret_cast<WNDPROC>(GetWindowLongPtr(target, GWLP_WNDPROC));
     34 }
     35 
     36 // Not defined before Win7
     37 BOOL GetTouchInputInfoWrapper(HTOUCHINPUT handle, UINT count,
     38                               PTOUCHINPUT pointer, int size) {
     39   typedef BOOL(WINAPI *GetTouchInputInfoPtr)(HTOUCHINPUT, UINT,
     40                                              PTOUCHINPUT, int);
     41   GetTouchInputInfoPtr get_touch_input_info_func =
     42       reinterpret_cast<GetTouchInputInfoPtr>(
     43           GetProcAddress(GetModuleHandleA("user32.dll"), "GetTouchInputInfo"));
     44   if (get_touch_input_info_func)
     45     return get_touch_input_info_func(handle, count, pointer, size);
     46   return FALSE;
     47 }
     48 
     49 }  // namespace
     50 
     51 namespace ui {
     52 
     53 // Singleton factory that creates and manages the lifetime of all
     54 // ui::HWNDSubclass objects.
     55 class HWNDSubclass::HWNDSubclassFactory {
     56  public:
     57   static HWNDSubclassFactory* GetInstance() {
     58     return Singleton<HWNDSubclassFactory,
     59         LeakySingletonTraits<HWNDSubclassFactory> >::get();
     60   }
     61 
     62   // Returns a non-null HWNDSubclass corresponding to the HWND |target|. Creates
     63   // one if none exists. Retains ownership of the returned pointer.
     64   HWNDSubclass* GetHwndSubclassForTarget(HWND target) {
     65     DCHECK(target);
     66     HWNDSubclass* subclass = reinterpret_cast<HWNDSubclass*>(
     67         ui::ViewProp::GetValue(target, kHWNDSubclassKey));
     68     if (!subclass) {
     69       subclass = new ui::HWNDSubclass(target);
     70       hwnd_subclasses_.push_back(subclass);
     71     }
     72     return subclass;
     73   }
     74 
     75   const ScopedVector<HWNDSubclass>& hwnd_subclasses() {
     76     return hwnd_subclasses_;
     77   }
     78 
     79  private:
     80   friend struct DefaultSingletonTraits<HWNDSubclassFactory>;
     81 
     82   HWNDSubclassFactory() {}
     83 
     84   ScopedVector<HWNDSubclass> hwnd_subclasses_;
     85 
     86   DISALLOW_COPY_AND_ASSIGN(HWNDSubclassFactory);
     87 };
     88 
     89 // static
     90 void HWNDSubclass::AddFilterToTarget(HWND target, HWNDMessageFilter* filter) {
     91   HWNDSubclassFactory::GetInstance()->GetHwndSubclassForTarget(
     92       target)->AddFilter(filter);
     93 }
     94 
     95 // static
     96 void HWNDSubclass::RemoveFilterFromAllTargets(HWNDMessageFilter* filter) {
     97   HWNDSubclassFactory* factory = HWNDSubclassFactory::GetInstance();
     98   ScopedVector<ui::HWNDSubclass>::const_iterator it;
     99   for (it = factory->hwnd_subclasses().begin();
    100       it != factory->hwnd_subclasses().end(); ++it)
    101     (*it)->RemoveFilter(filter);
    102 }
    103 
    104 // static
    105 HWNDSubclass* HWNDSubclass::GetHwndSubclassForTarget(HWND target) {
    106   return HWNDSubclassFactory::GetInstance()->GetHwndSubclassForTarget(target);
    107 }
    108 
    109 void HWNDSubclass::AddFilter(HWNDMessageFilter* filter) {
    110   DCHECK(filter);
    111   if (std::find(filters_.begin(), filters_.end(), filter) == filters_.end())
    112     filters_.push_back(filter);
    113 }
    114 
    115 void HWNDSubclass::RemoveFilter(HWNDMessageFilter* filter) {
    116   std::vector<HWNDMessageFilter*>::iterator it =
    117       std::find(filters_.begin(), filters_.end(), filter);
    118   if (it != filters_.end())
    119     filters_.erase(it);
    120 }
    121 
    122 HWNDSubclass::HWNDSubclass(HWND target)
    123     : target_(target),
    124       original_wnd_proc_(GetCurrentWndProc(target)),
    125       prop_(target, kHWNDSubclassKey, this) {
    126   gfx::SetWindowProc(target_, &WndProc);
    127 }
    128 
    129 HWNDSubclass::~HWNDSubclass() {
    130 }
    131 
    132 LRESULT HWNDSubclass::OnWndProc(HWND hwnd,
    133                                 UINT message,
    134                                 WPARAM w_param,
    135                                 LPARAM l_param) {
    136 
    137   // Touch messages are always passed in screen coordinates. If the OS is
    138   // scaled, but the app is not DPI aware, then then WM_TOUCH might be
    139   // intended for a different window.
    140   if (message == WM_TOUCH) {
    141     TOUCHINPUT point;
    142 
    143     if (GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param), 1,
    144                                  &point, sizeof(TOUCHINPUT))) {
    145       POINT touch_location = {
    146           TOUCH_COORD_TO_PIXEL(point.x) /
    147           gfx::win::GetUndocumentedDPITouchScale(),
    148           TOUCH_COORD_TO_PIXEL(point.y) /
    149           gfx::win::GetUndocumentedDPITouchScale()};
    150       HWND actual_target = WindowFromPoint(touch_location);
    151       if (actual_target != hwnd) {
    152         return SendMessage(actual_target, message, w_param, l_param);
    153       }
    154     }
    155   }
    156 
    157   for (std::vector<HWNDMessageFilter*>::iterator it = filters_.begin();
    158       it != filters_.end(); ++it) {
    159     LRESULT l_result = 0;
    160     if ((*it)->FilterMessage(hwnd, message, w_param, l_param, &l_result))
    161       return l_result;
    162   }
    163 
    164   // In most cases, |original_wnd_proc_| will take care of calling
    165   // DefWindowProc.
    166   return CallWindowProc(original_wnd_proc_, hwnd, message, w_param, l_param);
    167 }
    168 
    169 HWNDMessageFilter::~HWNDMessageFilter() {
    170   HWNDSubclass::RemoveFilterFromAllTargets(this);
    171 }
    172 
    173 }  // namespace ui
    174