Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2013 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/browser/extensions/global_shortcut_listener_win.h"
      6 
      7 #include "base/win/win_util.h"
      8 #include "content/public/browser/browser_thread.h"
      9 #include "ui/base/accelerators/accelerator.h"
     10 #include "ui/events/event_constants.h"
     11 #include "ui/events/keycodes/keyboard_code_conversion_win.h"
     12 
     13 using content::BrowserThread;
     14 
     15 namespace extensions {
     16 
     17 // static
     18 GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
     19   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     20   static GlobalShortcutListenerWin* instance =
     21       new GlobalShortcutListenerWin();
     22   return instance;
     23 }
     24 
     25 GlobalShortcutListenerWin::GlobalShortcutListenerWin()
     26     : is_listening_(false) {
     27   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     28 }
     29 
     30 GlobalShortcutListenerWin::~GlobalShortcutListenerWin() {
     31   if (is_listening_)
     32     StopListening();
     33 }
     34 
     35 void GlobalShortcutListenerWin::StartListening() {
     36   DCHECK(!is_listening_);  // Don't start twice.
     37   DCHECK(!hotkey_ids_.empty());  // Also don't start if no hotkey is registered.
     38   gfx::SingletonHwnd::GetInstance()->AddObserver(this);
     39   is_listening_ = true;
     40 }
     41 
     42 void GlobalShortcutListenerWin::StopListening() {
     43   DCHECK(is_listening_);  // No point if we are not already listening.
     44   DCHECK(hotkey_ids_.empty());  // Make sure the map is clean before ending.
     45   gfx::SingletonHwnd::GetInstance()->RemoveObserver(this);
     46   is_listening_ = false;
     47 }
     48 
     49 void GlobalShortcutListenerWin::OnWndProc(HWND hwnd,
     50                                           UINT message,
     51                                           WPARAM wparam,
     52                                           LPARAM lparam) {
     53   if (message != WM_HOTKEY)
     54     return;
     55 
     56   int key_code = HIWORD(lparam);
     57   int modifiers = 0;
     58   modifiers |= (LOWORD(lparam) & MOD_SHIFT) ? ui::EF_SHIFT_DOWN : 0;
     59   modifiers |= (LOWORD(lparam) & MOD_ALT) ? ui::EF_ALT_DOWN : 0;
     60   modifiers |= (LOWORD(lparam) & MOD_CONTROL) ? ui::EF_CONTROL_DOWN : 0;
     61   ui::Accelerator accelerator(
     62       ui::KeyboardCodeForWindowsKeyCode(key_code), modifiers);
     63 
     64   NotifyKeyPressed(accelerator);
     65 }
     66 
     67 bool GlobalShortcutListenerWin::RegisterAcceleratorImpl(
     68     const ui::Accelerator& accelerator) {
     69   DCHECK(hotkey_ids_.find(accelerator) == hotkey_ids_.end());
     70 
     71   int modifiers = 0;
     72   modifiers |= accelerator.IsShiftDown() ? MOD_SHIFT : 0;
     73   modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0;
     74   modifiers |= accelerator.IsAltDown() ? MOD_ALT : 0;
     75   static int hotkey_id = 0;
     76   bool success = !!RegisterHotKey(
     77       gfx::SingletonHwnd::GetInstance()->hwnd(),
     78       hotkey_id,
     79       modifiers,
     80       accelerator.key_code());
     81 
     82   if (!success) {
     83     // Most likely error: 1409 (Hotkey already registered).
     84     return false;
     85   }
     86 
     87   hotkey_ids_[accelerator] = hotkey_id++;
     88   return true;
     89 }
     90 
     91 void GlobalShortcutListenerWin::UnregisterAcceleratorImpl(
     92     const ui::Accelerator& accelerator) {
     93   HotkeyIdMap::iterator it = hotkey_ids_.find(accelerator);
     94   DCHECK(it != hotkey_ids_.end());
     95 
     96   bool success = !!UnregisterHotKey(
     97       gfx::SingletonHwnd::GetInstance()->hwnd(), it->second);
     98   // This call should always succeed, as long as we pass in the right HWND and
     99   // an id we've used to register before.
    100   DCHECK(success);
    101 
    102   hotkey_ids_.erase(it);
    103 }
    104 
    105 }  // namespace extensions
    106