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