1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/system_key_event_listener.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include <gdk/gdkx.h>
8513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include <X11/XF86keysym.h>
9513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/audio_handler.h"
1121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/chromeos/brightness_bubble.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/volume_bubble.h"
133f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "chrome/browser/metrics/user_metrics.h"
1421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "third_party/cros/chromeos_wm_ipc_enums.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace chromeos {
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst double kStepPercentage = 4.0;
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
2521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenSystemKeyEventListener* SystemKeyEventListener::GetInstance() {
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return Singleton<SystemKeyEventListener>::get();
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochSystemKeyEventListener::SystemKeyEventListener()
3072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : stopped_(false),
3172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      audio_handler_(AudioHandler::GetInstance()) {
3221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  WmMessageListener::GetInstance()->AddObserver(this);
33513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
34513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  key_volume_mute_ = XKeysymToKeycode(GDK_DISPLAY(), XF86XK_AudioMute);
35513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  key_volume_down_ = XKeysymToKeycode(GDK_DISPLAY(), XF86XK_AudioLowerVolume);
36513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  key_volume_up_ = XKeysymToKeycode(GDK_DISPLAY(), XF86XK_AudioRaiseVolume);
37513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  key_f8_ = XKeysymToKeycode(GDK_DISPLAY(), XK_F8);
38513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  key_f9_ = XKeysymToKeycode(GDK_DISPLAY(), XK_F9);
39513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  key_f10_ = XKeysymToKeycode(GDK_DISPLAY(), XK_F10);
40513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
41513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (key_volume_mute_)
42513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    GrabKey(key_volume_mute_, 0);
43513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (key_volume_down_)
44513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    GrabKey(key_volume_down_, 0);
45513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (key_volume_up_)
46513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    GrabKey(key_volume_up_, 0);
47513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  GrabKey(key_f8_, 0);
48513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  GrabKey(key_f9_, 0);
49513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  GrabKey(key_f10_, 0);
50513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  gdk_window_add_filter(NULL, GdkEventFilter, this);
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochSystemKeyEventListener::~SystemKeyEventListener() {
5472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  Stop();
5572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
5672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
5772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid SystemKeyEventListener::Stop() {
5872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (stopped_)
5972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return;
6021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  WmMessageListener::GetInstance()->RemoveObserver(this);
61513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  gdk_window_remove_filter(NULL, GdkEventFilter, this);
6272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  audio_handler_->Disconnect();
6372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  stopped_ = true;
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid SystemKeyEventListener::ProcessWmMessage(const WmIpc::Message& message,
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                              GdkWindow* window) {
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (message.type() != WM_IPC_MESSAGE_CHROME_NOTIFY_SYSKEY_PRESSED)
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (message.param(0)) {
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case WM_IPC_SYSTEM_KEY_VOLUME_MUTE:
74513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      OnVolumeMute();
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case WM_IPC_SYSTEM_KEY_VOLUME_DOWN:
77513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      OnVolumeDown();
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case WM_IPC_SYSTEM_KEY_VOLUME_UP:
80513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      OnVolumeUp();
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      DLOG(ERROR) << "SystemKeyEventListener: Unexpected message "
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  << message.param(0)
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  << " received";
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
89513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch// static
90513209b27ff55e2841eac0e4120199c23acce758Ben MurdochGdkFilterReturn SystemKeyEventListener::GdkEventFilter(GdkXEvent* gxevent,
91513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                                       GdkEvent* gevent,
92513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                                       gpointer data) {
93513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  SystemKeyEventListener* listener = static_cast<SystemKeyEventListener*>(data);
94513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  XEvent* xevent = static_cast<XEvent*>(gxevent);
95513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
96513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (xevent->type == KeyPress) {
97513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    int32 keycode = xevent->xkey.keycode;
98513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    if (keycode) {
994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      // Only doing non-Alt/Shift/Ctrl modified keys
1004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if (!(xevent->xkey.state & (Mod1Mask | ShiftMask | ControlMask))) {
1014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        if ((keycode == listener->key_f8_) ||
1024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            (keycode == listener->key_volume_mute_)) {
1033f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen          if (keycode == listener->key_f8_)
1043f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen            UserMetrics::RecordAction(UserMetricsAction("Accel_VolumeMute_F8"));
1054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          listener->OnVolumeMute();
1064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          return GDK_FILTER_REMOVE;
1074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        } else if ((keycode == listener->key_f9_) ||
1084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                    keycode == listener->key_volume_down_) {
1093f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen          if (keycode == listener->key_f9_)
1103f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen            UserMetrics::RecordAction(UserMetricsAction("Accel_VolumeDown_F9"));
1114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          listener->OnVolumeDown();
1124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          return GDK_FILTER_REMOVE;
1134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        } else if ((keycode == listener->key_f10_) ||
1144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                   (keycode == listener->key_volume_up_)) {
1153f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen          if (keycode == listener->key_f10_)
1163f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen            UserMetrics::RecordAction(UserMetricsAction("Accel_VolumeUp_F10"));
1174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          listener->OnVolumeUp();
1184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          return GDK_FILTER_REMOVE;
1194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        }
120513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      }
121513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    }
122513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  }
123513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  return GDK_FILTER_CONTINUE;
124513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
125513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
126513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid SystemKeyEventListener::GrabKey(int32 key, uint32 mask) {
127513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  uint32 num_lock_mask = Mod2Mask;
128513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  uint32 caps_lock_mask = LockMask;
129513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  Window root = DefaultRootWindow(GDK_DISPLAY());
130513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  XGrabKey(GDK_DISPLAY(), key, mask, root, True, GrabModeAsync, GrabModeAsync);
131513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  XGrabKey(GDK_DISPLAY(), key, mask | caps_lock_mask, root, True,
132513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch           GrabModeAsync, GrabModeAsync);
133513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  XGrabKey(GDK_DISPLAY(), key, mask | num_lock_mask, root, True,
134513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch           GrabModeAsync, GrabModeAsync);
135513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  XGrabKey(GDK_DISPLAY(), key, mask | caps_lock_mask | num_lock_mask, root,
136513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch           True, GrabModeAsync, GrabModeAsync);
137513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
138513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
13921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// TODO(davej): Move the ShowBubble() calls in to AudioHandler so that this
14021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// function returns faster without blocking on GetVolumePercent(), and still
14121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// guarantees that the volume displayed will be that after the adjustment.
142513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
14321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// TODO(davej): The IsMute() check can also be made non-blocking by changing to
14421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// an AdjustVolumeByPercentOrUnmute() function which can do the steps off of
14521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// this thread when ShowBubble() is moved in to AudioHandler.
146513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
147513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid SystemKeyEventListener::OnVolumeMute() {
148513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // Always muting (and not toggling) as per final decision on
149513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // http://crosbug.com/3751
150513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  audio_handler_->SetMute(true);
15121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  VolumeBubble::GetInstance()->ShowBubble(0);
15221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  BrightnessBubble::GetInstance()->HideBubble();
153513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
154513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
155513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid SystemKeyEventListener::OnVolumeDown() {
156513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (audio_handler_->IsMute()) {
15721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    VolumeBubble::GetInstance()->ShowBubble(0);
158513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  } else {
159513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    audio_handler_->AdjustVolumeByPercent(-kStepPercentage);
16021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    VolumeBubble::GetInstance()->ShowBubble(
161513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch        audio_handler_->GetVolumePercent());
162513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  }
16321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  BrightnessBubble::GetInstance()->HideBubble();
164513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
165513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
166513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid SystemKeyEventListener::OnVolumeUp() {
167513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (audio_handler_->IsMute())
168513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    audio_handler_->SetMute(false);
169513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  else
170513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    audio_handler_->AdjustVolumeByPercent(kStepPercentage);
17121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  VolumeBubble::GetInstance()->ShowBubble(
172513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      audio_handler_->GetVolumePercent());
17321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  BrightnessBubble::GetInstance()->HideBubble();
174513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
175513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace chromeos
177