Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2010 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/chromeos/system_key_event_listener.h"
      6 
      7 #include <gdk/gdkx.h>
      8 #include <X11/XF86keysym.h>
      9 
     10 #include "chrome/browser/chromeos/audio_handler.h"
     11 #include "chrome/browser/chromeos/brightness_bubble.h"
     12 #include "chrome/browser/chromeos/volume_bubble.h"
     13 #include "chrome/browser/metrics/user_metrics.h"
     14 #include "third_party/cros/chromeos_wm_ipc_enums.h"
     15 
     16 namespace chromeos {
     17 
     18 namespace {
     19 
     20 const double kStepPercentage = 4.0;
     21 
     22 }  // namespace
     23 
     24 // static
     25 SystemKeyEventListener* SystemKeyEventListener::GetInstance() {
     26   return Singleton<SystemKeyEventListener>::get();
     27 }
     28 
     29 SystemKeyEventListener::SystemKeyEventListener()
     30     : stopped_(false),
     31       audio_handler_(AudioHandler::GetInstance()) {
     32   WmMessageListener::GetInstance()->AddObserver(this);
     33 
     34   key_volume_mute_ = XKeysymToKeycode(GDK_DISPLAY(), XF86XK_AudioMute);
     35   key_volume_down_ = XKeysymToKeycode(GDK_DISPLAY(), XF86XK_AudioLowerVolume);
     36   key_volume_up_ = XKeysymToKeycode(GDK_DISPLAY(), XF86XK_AudioRaiseVolume);
     37   key_f8_ = XKeysymToKeycode(GDK_DISPLAY(), XK_F8);
     38   key_f9_ = XKeysymToKeycode(GDK_DISPLAY(), XK_F9);
     39   key_f10_ = XKeysymToKeycode(GDK_DISPLAY(), XK_F10);
     40 
     41   if (key_volume_mute_)
     42     GrabKey(key_volume_mute_, 0);
     43   if (key_volume_down_)
     44     GrabKey(key_volume_down_, 0);
     45   if (key_volume_up_)
     46     GrabKey(key_volume_up_, 0);
     47   GrabKey(key_f8_, 0);
     48   GrabKey(key_f9_, 0);
     49   GrabKey(key_f10_, 0);
     50   gdk_window_add_filter(NULL, GdkEventFilter, this);
     51 }
     52 
     53 SystemKeyEventListener::~SystemKeyEventListener() {
     54   Stop();
     55 }
     56 
     57 void SystemKeyEventListener::Stop() {
     58   if (stopped_)
     59     return;
     60   WmMessageListener::GetInstance()->RemoveObserver(this);
     61   gdk_window_remove_filter(NULL, GdkEventFilter, this);
     62   audio_handler_->Disconnect();
     63   stopped_ = true;
     64 }
     65 
     66 
     67 void SystemKeyEventListener::ProcessWmMessage(const WmIpc::Message& message,
     68                                               GdkWindow* window) {
     69   if (message.type() != WM_IPC_MESSAGE_CHROME_NOTIFY_SYSKEY_PRESSED)
     70     return;
     71 
     72   switch (message.param(0)) {
     73     case WM_IPC_SYSTEM_KEY_VOLUME_MUTE:
     74       OnVolumeMute();
     75       break;
     76     case WM_IPC_SYSTEM_KEY_VOLUME_DOWN:
     77       OnVolumeDown();
     78       break;
     79     case WM_IPC_SYSTEM_KEY_VOLUME_UP:
     80       OnVolumeUp();
     81       break;
     82     default:
     83       DLOG(ERROR) << "SystemKeyEventListener: Unexpected message "
     84                   << message.param(0)
     85                   << " received";
     86   }
     87 }
     88 
     89 // static
     90 GdkFilterReturn SystemKeyEventListener::GdkEventFilter(GdkXEvent* gxevent,
     91                                                        GdkEvent* gevent,
     92                                                        gpointer data) {
     93   SystemKeyEventListener* listener = static_cast<SystemKeyEventListener*>(data);
     94   XEvent* xevent = static_cast<XEvent*>(gxevent);
     95 
     96   if (xevent->type == KeyPress) {
     97     int32 keycode = xevent->xkey.keycode;
     98     if (keycode) {
     99       // Only doing non-Alt/Shift/Ctrl modified keys
    100       if (!(xevent->xkey.state & (Mod1Mask | ShiftMask | ControlMask))) {
    101         if ((keycode == listener->key_f8_) ||
    102             (keycode == listener->key_volume_mute_)) {
    103           if (keycode == listener->key_f8_)
    104             UserMetrics::RecordAction(UserMetricsAction("Accel_VolumeMute_F8"));
    105           listener->OnVolumeMute();
    106           return GDK_FILTER_REMOVE;
    107         } else if ((keycode == listener->key_f9_) ||
    108                     keycode == listener->key_volume_down_) {
    109           if (keycode == listener->key_f9_)
    110             UserMetrics::RecordAction(UserMetricsAction("Accel_VolumeDown_F9"));
    111           listener->OnVolumeDown();
    112           return GDK_FILTER_REMOVE;
    113         } else if ((keycode == listener->key_f10_) ||
    114                    (keycode == listener->key_volume_up_)) {
    115           if (keycode == listener->key_f10_)
    116             UserMetrics::RecordAction(UserMetricsAction("Accel_VolumeUp_F10"));
    117           listener->OnVolumeUp();
    118           return GDK_FILTER_REMOVE;
    119         }
    120       }
    121     }
    122   }
    123   return GDK_FILTER_CONTINUE;
    124 }
    125 
    126 void SystemKeyEventListener::GrabKey(int32 key, uint32 mask) {
    127   uint32 num_lock_mask = Mod2Mask;
    128   uint32 caps_lock_mask = LockMask;
    129   Window root = DefaultRootWindow(GDK_DISPLAY());
    130   XGrabKey(GDK_DISPLAY(), key, mask, root, True, GrabModeAsync, GrabModeAsync);
    131   XGrabKey(GDK_DISPLAY(), key, mask | caps_lock_mask, root, True,
    132            GrabModeAsync, GrabModeAsync);
    133   XGrabKey(GDK_DISPLAY(), key, mask | num_lock_mask, root, True,
    134            GrabModeAsync, GrabModeAsync);
    135   XGrabKey(GDK_DISPLAY(), key, mask | caps_lock_mask | num_lock_mask, root,
    136            True, GrabModeAsync, GrabModeAsync);
    137 }
    138 
    139 // TODO(davej): Move the ShowBubble() calls in to AudioHandler so that this
    140 // function returns faster without blocking on GetVolumePercent(), and still
    141 // guarantees that the volume displayed will be that after the adjustment.
    142 
    143 // TODO(davej): The IsMute() check can also be made non-blocking by changing to
    144 // an AdjustVolumeByPercentOrUnmute() function which can do the steps off of
    145 // this thread when ShowBubble() is moved in to AudioHandler.
    146 
    147 void SystemKeyEventListener::OnVolumeMute() {
    148   // Always muting (and not toggling) as per final decision on
    149   // http://crosbug.com/3751
    150   audio_handler_->SetMute(true);
    151   VolumeBubble::GetInstance()->ShowBubble(0);
    152   BrightnessBubble::GetInstance()->HideBubble();
    153 }
    154 
    155 void SystemKeyEventListener::OnVolumeDown() {
    156   if (audio_handler_->IsMute()) {
    157     VolumeBubble::GetInstance()->ShowBubble(0);
    158   } else {
    159     audio_handler_->AdjustVolumeByPercent(-kStepPercentage);
    160     VolumeBubble::GetInstance()->ShowBubble(
    161         audio_handler_->GetVolumePercent());
    162   }
    163   BrightnessBubble::GetInstance()->HideBubble();
    164 }
    165 
    166 void SystemKeyEventListener::OnVolumeUp() {
    167   if (audio_handler_->IsMute())
    168     audio_handler_->SetMute(false);
    169   else
    170     audio_handler_->AdjustVolumeByPercent(kStepPercentage);
    171   VolumeBubble::GetInstance()->ShowBubble(
    172       audio_handler_->GetVolumePercent());
    173   BrightnessBubble::GetInstance()->HideBubble();
    174 }
    175 
    176 }  // namespace chromeos
    177