1 // Copyright 2018 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 "ash/public/cpp/accelerators.h"
6
7 #include "base/callback.h"
8 #include "base/no_destructor.h"
9 #include "base/stl_util.h"
10
11 namespace ash {
12
13 namespace {
14
15 AcceleratorController* g_instance = nullptr;
16
GetVolumeAdjustmentCallback()17 base::RepeatingClosure* GetVolumeAdjustmentCallback() {
18 static base::NoDestructor<base::RepeatingClosure> callback;
19 return callback.get();
20 }
21
22 } // namespace
23
24 const AcceleratorData kAcceleratorData[] = {
25 {true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, SWITCH_TO_LAST_USED_IME},
26 {false, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, SWITCH_TO_LAST_USED_IME},
27 {true, ui::VKEY_TAB, ui::EF_ALT_DOWN, CYCLE_FORWARD_MRU},
28 {true, ui::VKEY_TAB, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
29 CYCLE_BACKWARD_MRU},
30 {true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_NONE, TOGGLE_OVERVIEW},
31 {true, ui::VKEY_BROWSER_SEARCH, ui::EF_NONE, TOGGLE_APP_LIST},
32 {true, ui::VKEY_BROWSER_SEARCH, ui::EF_SHIFT_DOWN,
33 TOGGLE_APP_LIST_FULLSCREEN},
34 {true, ui::VKEY_WLAN, ui::EF_NONE, TOGGLE_WIFI},
35 {true, ui::VKEY_PRIVACY_SCREEN_TOGGLE, ui::EF_NONE, PRIVACY_SCREEN_TOGGLE},
36 {true, ui::VKEY_KBD_BRIGHTNESS_DOWN, ui::EF_NONE, KEYBOARD_BRIGHTNESS_DOWN},
37 {true, ui::VKEY_KBD_BRIGHTNESS_UP, ui::EF_NONE, KEYBOARD_BRIGHTNESS_UP},
38 // Maximize button.
39 {true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_CONTROL_DOWN, TOGGLE_MIRROR_MODE},
40 {true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_ALT_DOWN, SWAP_PRIMARY_DISPLAY},
41 // Cycle windows button.
42 {true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_CONTROL_DOWN, TAKE_SCREENSHOT},
43 {true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
44 TAKE_PARTIAL_SCREENSHOT},
45 {true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
46 TAKE_WINDOW_SCREENSHOT},
47 {true, ui::VKEY_BRIGHTNESS_DOWN, ui::EF_NONE, BRIGHTNESS_DOWN},
48 {true, ui::VKEY_BRIGHTNESS_DOWN, ui::EF_ALT_DOWN, KEYBOARD_BRIGHTNESS_DOWN},
49 {true, ui::VKEY_BRIGHTNESS_UP, ui::EF_NONE, BRIGHTNESS_UP},
50 {true, ui::VKEY_BRIGHTNESS_UP, ui::EF_ALT_DOWN, KEYBOARD_BRIGHTNESS_UP},
51 {true, ui::VKEY_BRIGHTNESS_DOWN, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
52 MAGNIFIER_ZOOM_OUT},
53 {true, ui::VKEY_BRIGHTNESS_UP, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
54 MAGNIFIER_ZOOM_IN},
55 {true, ui::VKEY_L, ui::EF_COMMAND_DOWN, LOCK_SCREEN},
56 {true, ui::VKEY_L, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN, SUSPEND},
57 // The lock key on Chrome OS keyboards produces F13 scancodes.
58 {true, ui::VKEY_F13, ui::EF_NONE, LOCK_PRESSED},
59 {false, ui::VKEY_F13, ui::EF_NONE, LOCK_RELEASED},
60 // Generic keyboards can use VKEY_SLEEP to mimic ChromeOS keyboard's lock
61 // key.
62 {true, ui::VKEY_SLEEP, ui::EF_NONE, LOCK_PRESSED},
63 {false, ui::VKEY_SLEEP, ui::EF_NONE, LOCK_RELEASED},
64 {true, ui::VKEY_POWER, ui::EF_NONE, POWER_PRESSED},
65 {false, ui::VKEY_POWER, ui::EF_NONE, POWER_RELEASED},
66 {true, ui::VKEY_M, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, OPEN_FILE_MANAGER},
67 {true, ui::VKEY_OEM_2, ui::EF_CONTROL_DOWN, OPEN_GET_HELP},
68 {true, ui::VKEY_OEM_2, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
69 OPEN_GET_HELP},
70 {true, ui::VKEY_T, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, OPEN_CROSH},
71 {true, ui::VKEY_I, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
72 TOUCH_HUD_MODE_CHANGE},
73 {true, ui::VKEY_I,
74 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN,
75 TOUCH_HUD_CLEAR},
76 {true, ui::VKEY_H, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
77 TOGGLE_HIGH_CONTRAST},
78 {true, ui::VKEY_Z, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
79 TOGGLE_SPOKEN_FEEDBACK},
80 {true, ui::VKEY_D, ui::EF_COMMAND_DOWN, TOGGLE_DICTATION},
81 {true, ui::VKEY_OEM_COMMA, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
82 SWITCH_TO_PREVIOUS_USER},
83 {true, ui::VKEY_OEM_PERIOD, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
84 SWITCH_TO_NEXT_USER},
85 // Single shift release turns off caps lock.
86 {false, ui::VKEY_LSHIFT, ui::EF_NONE, DISABLE_CAPS_LOCK},
87 {false, ui::VKEY_SHIFT, ui::EF_NONE, DISABLE_CAPS_LOCK},
88 {false, ui::VKEY_RSHIFT, ui::EF_NONE, DISABLE_CAPS_LOCK},
89 // Accelerators to toggle Caps Lock.
90 // The following is triggered when Search is released while Alt is still
91 // down. The key_code here is LWIN (for search) and Alt is a modifier.
92 {false, ui::VKEY_LWIN, ui::EF_ALT_DOWN, TOGGLE_CAPS_LOCK},
93 // The following is triggered when Alt is released while search is still
94 // down. The key_code here is MENU (for Alt) and Search is a modifier
95 // (EF_COMMAND_DOWN is used for Search as a modifier).
96 {false, ui::VKEY_MENU, ui::EF_COMMAND_DOWN, TOGGLE_CAPS_LOCK},
97 {true, ui::VKEY_VOLUME_MUTE, ui::EF_NONE, VOLUME_MUTE},
98 {true, ui::VKEY_VOLUME_DOWN, ui::EF_NONE, VOLUME_DOWN},
99 {true, ui::VKEY_VOLUME_UP, ui::EF_NONE, VOLUME_UP},
100 {true, ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN, SHOW_TASK_MANAGER},
101 {true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
102 SWITCH_TO_NEXT_IME},
103 {true, ui::VKEY_I, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, OPEN_FEEDBACK_PAGE},
104 {true, ui::VKEY_Q, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, EXIT},
105 {true, ui::VKEY_N, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
106 NEW_INCOGNITO_WINDOW},
107 {true, ui::VKEY_N, ui::EF_CONTROL_DOWN, NEW_WINDOW},
108 {true, ui::VKEY_T, ui::EF_CONTROL_DOWN, NEW_TAB},
109 {true, ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
110 SCALE_UI_UP},
111 {true, ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
112 SCALE_UI_DOWN},
113 {true, ui::VKEY_0, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, SCALE_UI_RESET},
114 {true, ui::VKEY_BROWSER_REFRESH, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
115 ROTATE_SCREEN},
116 {true, ui::VKEY_BROWSER_REFRESH,
117 ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ROTATE_WINDOW},
118 {true, ui::VKEY_T, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, RESTORE_TAB},
119 // This corresponds to the "Print Screen" key.
120 {true, ui::VKEY_SNAPSHOT, ui::EF_NONE, TAKE_SCREENSHOT},
121 {true, ui::VKEY_SNAPSHOT, ui::EF_ALT_DOWN, TAKE_PARTIAL_SCREENSHOT},
122 // On Chrome OS, Search key is mapped to LWIN. The Search key binding should
123 // act on release instead of press when using Search as a modifier key for
124 // extended keyboard shortcuts.
125 {false, ui::VKEY_LWIN, ui::EF_NONE, TOGGLE_APP_LIST},
126 {false, ui::VKEY_LWIN, ui::EF_SHIFT_DOWN, TOGGLE_APP_LIST_FULLSCREEN},
127 {true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_NONE, TOGGLE_FULLSCREEN},
128 {true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_SHIFT_DOWN, TOGGLE_FULLSCREEN},
129 {true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN, UNPIN},
130 {true, ui::VKEY_L, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, FOCUS_SHELF},
131 {true, ui::VKEY_V, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, FOCUS_PIP},
132 {true, ui::VKEY_HELP, ui::EF_NONE, SHOW_SHORTCUT_VIEWER},
133 {true, ui::VKEY_OEM_2, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
134 SHOW_SHORTCUT_VIEWER},
135 {true, ui::VKEY_OEM_2,
136 ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
137 SHOW_SHORTCUT_VIEWER},
138 {true, ui::VKEY_F14, ui::EF_NONE, SHOW_SHORTCUT_VIEWER},
139 {true, ui::VKEY_N, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
140 TOGGLE_MESSAGE_CENTER_BUBBLE},
141 {true, ui::VKEY_P, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, SHOW_STYLUS_TOOLS},
142 {true, ui::VKEY_S, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
143 TOGGLE_SYSTEM_TRAY_BUBBLE},
144 // Until we have unified settings and notifications the "hamburger"
145 // key opens quick settings.
146 {true, ui::VKEY_SETTINGS, ui::EF_NONE, TOGGLE_SYSTEM_TRAY_BUBBLE},
147 {true, ui::VKEY_K, ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN,
148 SHOW_IME_MENU_BUBBLE},
149 {true, ui::VKEY_1, ui::EF_ALT_DOWN, LAUNCH_APP_0},
150 {true, ui::VKEY_2, ui::EF_ALT_DOWN, LAUNCH_APP_1},
151 {true, ui::VKEY_3, ui::EF_ALT_DOWN, LAUNCH_APP_2},
152 {true, ui::VKEY_4, ui::EF_ALT_DOWN, LAUNCH_APP_3},
153 {true, ui::VKEY_5, ui::EF_ALT_DOWN, LAUNCH_APP_4},
154 {true, ui::VKEY_6, ui::EF_ALT_DOWN, LAUNCH_APP_5},
155 {true, ui::VKEY_7, ui::EF_ALT_DOWN, LAUNCH_APP_6},
156 {true, ui::VKEY_8, ui::EF_ALT_DOWN, LAUNCH_APP_7},
157 {true, ui::VKEY_9, ui::EF_ALT_DOWN, LAUNCH_LAST_APP},
158
159 // Window management shortcuts.
160 {true, ui::VKEY_OEM_4, ui::EF_ALT_DOWN, WINDOW_CYCLE_SNAP_LEFT},
161 {true, ui::VKEY_OEM_6, ui::EF_ALT_DOWN, WINDOW_CYCLE_SNAP_RIGHT},
162 {true, ui::VKEY_OEM_MINUS, ui::EF_ALT_DOWN, WINDOW_MINIMIZE},
163 {true, ui::VKEY_OEM_PLUS, ui::EF_ALT_DOWN, TOGGLE_MAXIMIZED},
164 {true, ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN, FOCUS_NEXT_PANE},
165 {true, ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN, FOCUS_PREVIOUS_PANE},
166 {true, ui::VKEY_BROWSER_BACK, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
167 FOCUS_NEXT_PANE},
168 {true, ui::VKEY_BROWSER_BACK, ui::EF_NONE, MINIMIZE_TOP_WINDOW_ON_BACK},
169
170 // Moving active window between displays shortcut.
171 {true, ui::VKEY_M, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
172 MOVE_ACTIVE_WINDOW_BETWEEN_DISPLAYS},
173
174 // Magnifiers shortcuts.
175 {true, ui::VKEY_D, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
176 TOGGLE_DOCKED_MAGNIFIER},
177 {true, ui::VKEY_M, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
178 TOGGLE_FULLSCREEN_MAGNIFIER},
179
180 // Media Player shortcuts.
181 {true, ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE, MEDIA_NEXT_TRACK},
182 {true, ui::VKEY_MEDIA_PAUSE, ui::EF_NONE, MEDIA_PAUSE},
183 {true, ui::VKEY_MEDIA_PLAY, ui::EF_NONE, MEDIA_PLAY},
184 {true, ui::VKEY_MEDIA_PLAY_PAUSE, ui::EF_NONE, MEDIA_PLAY_PAUSE},
185 {true, ui::VKEY_MEDIA_PREV_TRACK, ui::EF_NONE, MEDIA_PREV_TRACK},
186 {true, ui::VKEY_MEDIA_STOP, ui::EF_NONE, MEDIA_STOP},
187 {true, ui::VKEY_OEM_103, ui::EF_NONE, MEDIA_REWIND},
188 {true, ui::VKEY_OEM_104, ui::EF_NONE, MEDIA_FAST_FORWARD},
189
190 // Assistant shortcuts.
191 {true, ui::VKEY_A, ui::EF_COMMAND_DOWN, START_ASSISTANT},
192 {true, ui::VKEY_ASSISTANT, ui::EF_NONE, START_ASSISTANT},
193
194 // IME mode change key.
195 {true, ui::VKEY_MODECHANGE, ui::EF_NONE, SWITCH_TO_NEXT_IME},
196
197 // Debugging shortcuts that need to be available to end-users in
198 // release builds.
199 {true, ui::VKEY_U, kDebugModifier, PRINT_UI_HIERARCHIES},
200
201 // Virtual Desks shortcuts.
202 // Desk activation:
203 {true, ui::VKEY_OEM_4, ui::EF_COMMAND_DOWN, DESKS_ACTIVATE_DESK},
204 {true, ui::VKEY_OEM_6, ui::EF_COMMAND_DOWN, DESKS_ACTIVATE_DESK},
205 // Moving windows to desks:
206 {true, ui::VKEY_OEM_4, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN,
207 DESKS_MOVE_ACTIVE_ITEM},
208 {true, ui::VKEY_OEM_6, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN,
209 DESKS_MOVE_ACTIVE_ITEM},
210 // TODO(afakhry): Implement activating and moving windows to a desk by
211 // its index directly.
212
213 // TODO(yusukes): Handle VKEY_MEDIA_STOP, and
214 // VKEY_MEDIA_LAUNCH_MAIL.
215 };
216
217 const size_t kAcceleratorDataLength = base::size(kAcceleratorData);
218
219 const AcceleratorData kDisableWithNewMappingAcceleratorData[] = {
220 // Desk creation and removal:
221 // Due to https://crbug.com/976487, Search + "=" is always automatically
222 // rewritten to F12, and so is Search + "-" to F11. So we had to implement
223 // the following two shortcuts as Shift + F11/F12 until we resolve the above
224 // issue, accepting the fact that these two shortcuts might sometimes be
225 // consumed by apps and pages (since they're not search-based).
226 // TODO(afakhry): Change the following to Search+Shift+"+"/"-" once
227 // https://crbug.com/976487 is fixed.
228 {true, ui::VKEY_F12, ui::EF_SHIFT_DOWN, DESKS_NEW_DESK},
229 {true, ui::VKEY_F11, ui::EF_SHIFT_DOWN, DESKS_REMOVE_CURRENT_DESK},
230 };
231
232 const size_t kDisableWithNewMappingAcceleratorDataLength =
233 base::size(kDisableWithNewMappingAcceleratorData);
234
235 const AcceleratorData kEnableWithNewMappingAcceleratorData[] = {
236 // Desk creation and removal:
237 {true, ui::VKEY_OEM_PLUS, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN,
238 DESKS_NEW_DESK},
239 {true, ui::VKEY_OEM_MINUS, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN,
240 DESKS_REMOVE_CURRENT_DESK},
241
242 // Desk activation:
243 {true, ui::VKEY_LEFT, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
244 DESKS_ACTIVATE_DESK},
245 {true, ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
246 DESKS_ACTIVATE_DESK},
247
248 // Moving windows to desks:
249 {true, ui::VKEY_LEFT, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
250 DESKS_MOVE_ACTIVE_ITEM},
251 {true, ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
252 DESKS_MOVE_ACTIVE_ITEM},
253
254 // Snap
255 {true, ui::VKEY_OEM_COMMA,
256 ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
257 WINDOW_CYCLE_SNAP_LEFT},
258 {true, ui::VKEY_OEM_PERIOD,
259 ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
260 WINDOW_CYCLE_SNAP_RIGHT},
261
262 // Zoom
263 {true, ui::VKEY_UP,
264 ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
265 SCALE_UI_UP},
266 {true, ui::VKEY_DOWN,
267 ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
268 SCALE_UI_DOWN},
269 {true, ui::VKEY_BACK,
270 ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
271 SCALE_UI_RESET},
272
273 // Shortcut Viewer
274 {true, ui::VKEY_OEM_2, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN,
275 SHOW_SHORTCUT_VIEWER},
276 };
277
278 const size_t kEnableWithNewMappingAcceleratorDataLength =
279 base::size(kEnableWithNewMappingAcceleratorData);
280
281 // static
Get()282 AcceleratorController* AcceleratorController::Get() {
283 return g_instance;
284 }
285
286 // static
SetVolumeAdjustmentSoundCallback(const base::RepeatingClosure & closure)287 void AcceleratorController::SetVolumeAdjustmentSoundCallback(
288 const base::RepeatingClosure& closure) {
289 DCHECK(GetVolumeAdjustmentCallback()->is_null() || closure.is_null());
290 *GetVolumeAdjustmentCallback() = std::move(closure);
291 }
292
293 // static
PlayVolumeAdjustmentSound()294 void AcceleratorController::PlayVolumeAdjustmentSound() {
295 if (*GetVolumeAdjustmentCallback())
296 GetVolumeAdjustmentCallback()->Run();
297 }
298
AcceleratorController()299 AcceleratorController::AcceleratorController() {
300 DCHECK_EQ(nullptr, g_instance);
301 g_instance = this;
302 }
303
~AcceleratorController()304 AcceleratorController::~AcceleratorController() {
305 DCHECK_EQ(this, g_instance);
306 g_instance = nullptr;
307 }
308
309 } // namespace ash
310