Home | History | Annotate | Download | only in test
      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 "chrome_frame/test/simulate_input.h"
      6 
      7 #include <atlbase.h>
      8 #include <atlwin.h>
      9 
     10 #include "base/test/test_timeouts.h"
     11 #include "base/threading/platform_thread.h"
     12 #include "chrome_frame/utils.h"
     13 
     14 namespace simulate_input {
     15 
     16 class ForegroundHelperWindow : public CWindowImpl<ForegroundHelperWindow> {
     17  public:
     18 BEGIN_MSG_MAP(ForegroundHelperWindow)
     19   MESSAGE_HANDLER(WM_HOTKEY, OnHotKey)
     20 END_MSG_MAP()
     21 
     22   ForegroundHelperWindow() : window_(NULL) {}
     23 
     24   HRESULT SetForeground(HWND window) {
     25     DCHECK(::IsWindow(window));
     26     window_ = window;
     27     if (NULL == Create(NULL, NULL, NULL, WS_POPUP))
     28       return AtlHresultFromLastError();
     29 
     30     static const int kHotKeyId = 0x0000baba;
     31     static const int kHotKeyWaitTimeout = 2000;
     32 
     33     RegisterHotKey(m_hWnd, kHotKeyId, 0, VK_F22);
     34 
     35     MSG msg = {0};
     36     PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
     37 
     38     SendMnemonic(VK_F22, NONE, false, false, KEY_DOWN);
     39     // There are scenarios where the WM_HOTKEY is not dispatched by the
     40     // the corresponding foreground thread. To prevent us from indefinitely
     41     // waiting for the hotkey, we set a timer and exit the loop.
     42     SetTimer(kHotKeyId, kHotKeyWaitTimeout, NULL);
     43 
     44     while (GetMessage(&msg, NULL, 0, 0)) {
     45       TranslateMessage(&msg);
     46       DispatchMessage(&msg);
     47       if (msg.message == WM_HOTKEY) {
     48         break;
     49       }
     50       if (msg.message == WM_TIMER) {
     51         SetForegroundWindow(window);
     52         break;
     53       }
     54     }
     55 
     56     UnregisterHotKey(m_hWnd, kHotKeyId);
     57     KillTimer(kHotKeyId);
     58     DestroyWindow();
     59     return S_OK;
     60   }
     61 
     62   LRESULT OnHotKey(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) {  // NOLINT
     63     SetForegroundWindow(window_);
     64     return 1;
     65   }
     66  private:
     67   HWND window_;
     68 };
     69 
     70 bool ForceSetForegroundWindow(HWND window) {
     71   if (GetForegroundWindow() == window)
     72     return true;
     73   ForegroundHelperWindow foreground_helper_window;
     74   HRESULT hr = foreground_helper_window.SetForeground(window);
     75   return SUCCEEDED(hr);
     76 }
     77 
     78 struct PidAndWindow {
     79   base::ProcessId pid;
     80   HWND hwnd;
     81 };
     82 
     83 BOOL CALLBACK FindWindowInProcessCallback(HWND hwnd, LPARAM param) {
     84   PidAndWindow* paw = reinterpret_cast<PidAndWindow*>(param);
     85   base::ProcessId pid;
     86   GetWindowThreadProcessId(hwnd, &pid);
     87   if (pid == paw->pid && IsWindowVisible(hwnd)) {
     88     paw->hwnd = hwnd;
     89     return FALSE;
     90   }
     91 
     92   return TRUE;
     93 }
     94 
     95 bool EnsureProcessInForeground(base::ProcessId process_id) {
     96   HWND hwnd = GetForegroundWindow();
     97   base::ProcessId current_foreground_pid = 0;
     98   DWORD active_thread_id = GetWindowThreadProcessId(hwnd,
     99       &current_foreground_pid);
    100   if (current_foreground_pid == process_id)
    101     return true;
    102 
    103   PidAndWindow paw = { process_id };
    104   EnumWindows(FindWindowInProcessCallback, reinterpret_cast<LPARAM>(&paw));
    105   if (!IsWindow(paw.hwnd)) {
    106     LOG(ERROR) << "failed to find process window";
    107     return false;
    108   }
    109 
    110   bool ret = ForceSetForegroundWindow(paw.hwnd);
    111   LOG_IF(ERROR, !ret) << "ForceSetForegroundWindow: " << ret;
    112 
    113   return ret;
    114 }
    115 
    116 void SendScanCode(short scan_code, uint32 modifiers) {
    117   DCHECK(-1 != scan_code);
    118 
    119   // High order byte in |scan_code| is SHIFT/CTRL/ALT key state.
    120   modifiers = static_cast<Modifier>(modifiers | HIBYTE(scan_code));
    121   DCHECK(modifiers <= ALT);
    122 
    123   // Low order byte in |scan_code| is the actual scan code.
    124   SendMnemonic(LOBYTE(scan_code), modifiers, false, true, KEY_DOWN);
    125 }
    126 
    127 void SendCharA(char c, uint32 modifiers) {
    128   SendScanCode(VkKeyScanA(c), modifiers);
    129 }
    130 
    131 void SendCharW(wchar_t c, uint32 modifiers) {
    132   SendScanCode(VkKeyScanW(c), modifiers);
    133 }
    134 
    135 // Sends a keystroke to the currently active application with optional
    136 // modifiers set.
    137 void SendMnemonic(WORD mnemonic_char,
    138                   uint32 modifiers,
    139                   bool extended,
    140                   bool unicode,
    141                   KeyMode key_mode) {
    142   const int kMaxInputs = 4;
    143   INPUT keys[kMaxInputs] = {0};  // Keyboard events
    144   int key_count = 0;  // Number of generated events
    145 
    146   if (modifiers & SHIFT) {
    147     keys[key_count].type = INPUT_KEYBOARD;
    148     keys[key_count].ki.wVk = VK_SHIFT;
    149     keys[key_count].ki.wScan = MapVirtualKey(VK_SHIFT, 0);
    150     key_count++;
    151   }
    152 
    153   if (modifiers & CONTROL) {
    154     keys[key_count].type = INPUT_KEYBOARD;
    155     keys[key_count].ki.wVk = VK_CONTROL;
    156     keys[key_count].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
    157     key_count++;
    158   }
    159 
    160   if (modifiers & ALT) {
    161     keys[key_count].type = INPUT_KEYBOARD;
    162     keys[key_count].ki.wVk = VK_MENU;
    163     keys[key_count].ki.wScan = MapVirtualKey(VK_MENU, 0);
    164     key_count++;
    165   }
    166 
    167   if (mnemonic_char) {
    168     keys[key_count].type = INPUT_KEYBOARD;
    169     keys[key_count].ki.wVk = mnemonic_char;
    170     keys[key_count].ki.wScan = MapVirtualKey(mnemonic_char, 0);
    171 
    172     if (extended)
    173       keys[key_count].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
    174     if (unicode)
    175       keys[key_count].ki.dwFlags |= KEYEVENTF_UNICODE;
    176     key_count++;
    177   }
    178 
    179   DCHECK_LE(key_count, kMaxInputs);
    180 
    181   // Add the key up bit if needed.
    182   if (key_mode == KEY_UP) {
    183     for (int i = 0; i < key_count; i++) {
    184       keys[i].ki.dwFlags |= KEYEVENTF_KEYUP;
    185     }
    186   }
    187 
    188   SendInput(key_count, &keys[0], sizeof(keys[0]));
    189 }
    190 
    191 void SetKeyboardFocusToWindow(HWND window) {
    192   SendMouseClick(window, 1, 1, LEFT);
    193 }
    194 
    195 void SendMouseClick(int x, int y, MouseButton button) {
    196   const base::TimeDelta kMessageTimeout = TestTimeouts::tiny_timeout();
    197   // TODO(joshia): Fix this. GetSystemMetrics(SM_CXSCREEN) will
    198   // retrieve screen size of the primarary monitor only. And monitors
    199   // arrangement could be pretty arbitrary.
    200   double screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1;
    201   double screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1;
    202   double location_x =  x * (65535.0f / screen_width);
    203   double location_y =  y * (65535.0f / screen_height);
    204 
    205   // Take advantage of button flag bitmask layout
    206   unsigned int button_flag = MOUSEEVENTF_LEFTDOWN << (button + button);
    207 
    208   INPUT input_info = {0};
    209   input_info.type = INPUT_MOUSE;
    210   input_info.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
    211   input_info.mi.dx = static_cast<LONG>(location_x);
    212   input_info.mi.dy = static_cast<LONG>(location_y);
    213   ::SendInput(1, &input_info, sizeof(INPUT));
    214   base::PlatformThread::Sleep(kMessageTimeout);
    215 
    216   input_info.mi.dwFlags = button_flag | MOUSEEVENTF_ABSOLUTE;
    217   ::SendInput(1, &input_info, sizeof(INPUT));
    218   base::PlatformThread::Sleep(kMessageTimeout);
    219 
    220   input_info.mi.dwFlags = (button_flag << 1) | MOUSEEVENTF_ABSOLUTE;
    221   ::SendInput(1, &input_info, sizeof(INPUT));
    222   base::PlatformThread::Sleep(kMessageTimeout);
    223 }
    224 
    225 void SendMouseClick(HWND window, int x, int y, MouseButton button) {
    226   if (!IsWindow(window)) {
    227     NOTREACHED() << "Invalid window handle.";
    228     return;
    229   }
    230 
    231   HWND top_level_window = window;
    232   if (!IsTopLevelWindow(top_level_window)) {
    233     top_level_window = GetAncestor(window, GA_ROOT);
    234   }
    235 
    236   ForceSetForegroundWindow(top_level_window);
    237 
    238   POINT cursor_position = {x, y};
    239   ClientToScreen(window, &cursor_position);
    240   SendMouseClick(cursor_position.x, cursor_position.y, button);
    241 }
    242 
    243 void SendExtendedKey(WORD key, uint32 modifiers) {
    244   SendMnemonic(key, modifiers, true, false, KEY_UP);
    245 }
    246 
    247 void SendStringW(const std::wstring& s) {
    248   for (size_t i = 0; i < s.length(); i++) {
    249     SendCharW(s[i], NONE);
    250     Sleep(10);
    251   }
    252 }
    253 
    254 void SendStringA(const std::string& s) {
    255   for (size_t i = 0; i < s.length(); i++) {
    256     SendCharA(s[i], NONE);
    257     Sleep(10);
    258   }
    259 }
    260 
    261 }  // namespace simulate_input
    262