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