OLD | NEW |
| (Empty) |
1 // Copyright 2013 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/wm/sticky_keys.h" | |
6 | |
7 #if defined(USE_X11) | |
8 #include <X11/extensions/XInput2.h> | |
9 #include <X11/Xlib.h> | |
10 #undef RootWindow | |
11 #endif | |
12 | |
13 #include "base/basictypes.h" | |
14 #include "base/debug/stack_trace.h" | |
15 #include "ui/aura/root_window.h" | |
16 #include "ui/aura/window.h" | |
17 #include "ui/aura/window_tracker.h" | |
18 #include "ui/events/event.h" | |
19 #include "ui/events/keycodes/keyboard_code_conversion.h" | |
20 | |
21 namespace ash { | |
22 | |
23 namespace { | |
24 | |
25 // Returns true if the type of mouse event should be modified by sticky keys. | |
26 bool ShouldModifyMouseEvent(ui::MouseEvent* event) { | |
27 ui::EventType type = event->type(); | |
28 return type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED || | |
29 type == ui::ET_MOUSEWHEEL; | |
30 } | |
31 | |
32 // An implementation of StickyKeysHandler::StickyKeysHandlerDelegate. | |
33 class StickyKeysHandlerDelegateImpl : | |
34 public StickyKeysHandler::StickyKeysHandlerDelegate { | |
35 public: | |
36 StickyKeysHandlerDelegateImpl(); | |
37 virtual ~StickyKeysHandlerDelegateImpl(); | |
38 | |
39 // StickyKeysHandlerDelegate overrides. | |
40 virtual void DispatchKeyEvent(ui::KeyEvent* event, | |
41 aura::Window* target) OVERRIDE; | |
42 | |
43 virtual void DispatchMouseEvent(ui::MouseEvent* event, | |
44 aura::Window* target) OVERRIDE; | |
45 | |
46 virtual void DispatchScrollEvent(ui::ScrollEvent* event, | |
47 aura::Window* target) OVERRIDE; | |
48 private: | |
49 DISALLOW_COPY_AND_ASSIGN(StickyKeysHandlerDelegateImpl); | |
50 }; | |
51 | |
52 StickyKeysHandlerDelegateImpl::StickyKeysHandlerDelegateImpl() { | |
53 } | |
54 | |
55 StickyKeysHandlerDelegateImpl::~StickyKeysHandlerDelegateImpl() { | |
56 } | |
57 | |
58 void StickyKeysHandlerDelegateImpl::DispatchKeyEvent(ui::KeyEvent* event, | |
59 aura::Window* target) { | |
60 DCHECK(target); | |
61 target->GetDispatcher()->AsRootWindowHostDelegate()->OnHostKeyEvent(event); | |
62 } | |
63 | |
64 void StickyKeysHandlerDelegateImpl::DispatchMouseEvent(ui::MouseEvent* event, | |
65 aura::Window* target) { | |
66 DCHECK(target); | |
67 // We need to send a new, untransformed mouse event to the host. | |
68 if (event->IsMouseWheelEvent()) { | |
69 ui::MouseWheelEvent new_event(*static_cast<ui::MouseWheelEvent*>(event)); | |
70 target->GetDispatcher()->AsRootWindowHostDelegate() | |
71 ->OnHostMouseEvent(&new_event); | |
72 } else { | |
73 ui::MouseEvent new_event(*event, target, target->GetRootWindow()); | |
74 target->GetDispatcher()->AsRootWindowHostDelegate() | |
75 ->OnHostMouseEvent(&new_event); | |
76 } | |
77 } | |
78 | |
79 void StickyKeysHandlerDelegateImpl::DispatchScrollEvent( | |
80 ui::ScrollEvent* event, | |
81 aura::Window* target) { | |
82 DCHECK(target); | |
83 target->GetDispatcher()->AsRootWindowHostDelegate() | |
84 ->OnHostScrollEvent(event); | |
85 } | |
86 | |
87 } // namespace | |
88 | |
89 /////////////////////////////////////////////////////////////////////////////// | |
90 // StickyKeys | |
91 StickyKeys::StickyKeys() | |
92 : enabled_(false), | |
93 shift_sticky_key_( | |
94 new StickyKeysHandler(ui::EF_SHIFT_DOWN, | |
95 new StickyKeysHandlerDelegateImpl())), | |
96 alt_sticky_key_( | |
97 new StickyKeysHandler(ui::EF_ALT_DOWN, | |
98 new StickyKeysHandlerDelegateImpl())), | |
99 ctrl_sticky_key_( | |
100 new StickyKeysHandler(ui::EF_CONTROL_DOWN, | |
101 new StickyKeysHandlerDelegateImpl())) { | |
102 } | |
103 | |
104 StickyKeys::~StickyKeys() { | |
105 } | |
106 | |
107 void StickyKeys::Enable(bool enabled) { | |
108 if (enabled_ != enabled) { | |
109 enabled_ = enabled; | |
110 | |
111 // Reset key handlers when activating sticky keys to ensure all | |
112 // the handlers' states are reset. | |
113 if (enabled_) { | |
114 shift_sticky_key_.reset( | |
115 new StickyKeysHandler(ui::EF_SHIFT_DOWN, | |
116 new StickyKeysHandlerDelegateImpl())); | |
117 alt_sticky_key_.reset( | |
118 new StickyKeysHandler(ui::EF_ALT_DOWN, | |
119 new StickyKeysHandlerDelegateImpl())); | |
120 ctrl_sticky_key_.reset( | |
121 new StickyKeysHandler(ui::EF_CONTROL_DOWN, | |
122 new StickyKeysHandlerDelegateImpl())); | |
123 } | |
124 } | |
125 } | |
126 | |
127 bool StickyKeys::HandleKeyEvent(ui::KeyEvent* event) { | |
128 return shift_sticky_key_->HandleKeyEvent(event) || | |
129 alt_sticky_key_->HandleKeyEvent(event) || | |
130 ctrl_sticky_key_->HandleKeyEvent(event); | |
131 return ctrl_sticky_key_->HandleKeyEvent(event); | |
132 } | |
133 | |
134 bool StickyKeys::HandleMouseEvent(ui::MouseEvent* event) { | |
135 return shift_sticky_key_->HandleMouseEvent(event) || | |
136 alt_sticky_key_->HandleMouseEvent(event) || | |
137 ctrl_sticky_key_->HandleMouseEvent(event); | |
138 } | |
139 | |
140 bool StickyKeys::HandleScrollEvent(ui::ScrollEvent* event) { | |
141 return shift_sticky_key_->HandleScrollEvent(event) || | |
142 alt_sticky_key_->HandleScrollEvent(event) || | |
143 ctrl_sticky_key_->HandleScrollEvent(event); | |
144 } | |
145 | |
146 void StickyKeys::OnKeyEvent(ui::KeyEvent* event) { | |
147 // Do not consume a translated key event which is generated by an IME. | |
148 if (event->type() == ui::ET_TRANSLATED_KEY_PRESS || | |
149 event->type() == ui::ET_TRANSLATED_KEY_RELEASE) { | |
150 return; | |
151 } | |
152 | |
153 if (enabled_ && HandleKeyEvent(event)) | |
154 event->StopPropagation(); | |
155 } | |
156 | |
157 void StickyKeys::OnMouseEvent(ui::MouseEvent* event) { | |
158 if (enabled_ && HandleMouseEvent(event)) | |
159 event->StopPropagation(); | |
160 } | |
161 | |
162 void StickyKeys::OnScrollEvent(ui::ScrollEvent* event) { | |
163 if (enabled_ && HandleScrollEvent(event)) | |
164 event->StopPropagation(); | |
165 } | |
166 | |
167 /////////////////////////////////////////////////////////////////////////////// | |
168 // StickyKeysHandler | |
169 StickyKeysHandler::StickyKeysHandler(ui::EventFlags target_modifier_flag, | |
170 StickyKeysHandlerDelegate* delegate) | |
171 : modifier_flag_(target_modifier_flag), | |
172 current_state_(DISABLED), | |
173 event_from_myself_(false), | |
174 preparing_to_enable_(false), | |
175 scroll_delta_(0), | |
176 delegate_(delegate) { | |
177 } | |
178 | |
179 StickyKeysHandler::~StickyKeysHandler() { | |
180 } | |
181 | |
182 StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() { | |
183 } | |
184 | |
185 StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() { | |
186 } | |
187 | |
188 bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent* event) { | |
189 if (event_from_myself_) | |
190 return false; // Do not handle self-generated key event. | |
191 switch (current_state_) { | |
192 case DISABLED: | |
193 return HandleDisabledState(event); | |
194 case ENABLED: | |
195 return HandleEnabledState(event); | |
196 case LOCKED: | |
197 return HandleLockedState(event); | |
198 } | |
199 NOTREACHED(); | |
200 return false; | |
201 } | |
202 | |
203 bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) { | |
204 preparing_to_enable_ = false; | |
205 if (event_from_myself_ || current_state_ == DISABLED | |
206 || !ShouldModifyMouseEvent(event)) { | |
207 return false; | |
208 } | |
209 DCHECK(current_state_ == ENABLED || current_state_ == LOCKED); | |
210 | |
211 AppendModifier(event); | |
212 // Only disable on the mouse released event in normal, non-locked mode. | |
213 if (current_state_ == ENABLED && event->type() != ui::ET_MOUSE_PRESSED) { | |
214 current_state_ = DISABLED; | |
215 DispatchEventAndReleaseModifier(event); | |
216 return true; | |
217 } | |
218 | |
219 return false; | |
220 } | |
221 | |
222 bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) { | |
223 preparing_to_enable_ = false; | |
224 if (event_from_myself_ || current_state_ == DISABLED) | |
225 return false; | |
226 DCHECK(current_state_ == ENABLED || current_state_ == LOCKED); | |
227 | |
228 // We detect a direction change if the current |scroll_delta_| is assigned | |
229 // and the offset of the current scroll event has the opposing sign. | |
230 bool direction_changed = false; | |
231 if (current_state_ == ENABLED && event->type() == ui::ET_SCROLL) { | |
232 int offset = event->y_offset(); | |
233 if (scroll_delta_) | |
234 direction_changed = offset * scroll_delta_ <= 0; | |
235 scroll_delta_ = offset; | |
236 } | |
237 | |
238 if (!direction_changed) | |
239 AppendModifier(event); | |
240 | |
241 // We want to modify all the scroll events in the scroll sequence, which ends | |
242 // with a fling start event. We also stop when the scroll sequence changes | |
243 // direction. | |
244 if (current_state_ == ENABLED && | |
245 (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) { | |
246 current_state_ = DISABLED; | |
247 scroll_delta_ = 0; | |
248 DispatchEventAndReleaseModifier(event); | |
249 return true; | |
250 } | |
251 | |
252 return false; | |
253 } | |
254 | |
255 StickyKeysHandler::KeyEventType | |
256 StickyKeysHandler::TranslateKeyEvent(ui::KeyEvent* event) { | |
257 bool is_target_key = false; | |
258 if (event->key_code() == ui::VKEY_SHIFT || | |
259 event->key_code() == ui::VKEY_LSHIFT || | |
260 event->key_code() == ui::VKEY_RSHIFT) { | |
261 is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN); | |
262 } else if (event->key_code() == ui::VKEY_CONTROL || | |
263 event->key_code() == ui::VKEY_LCONTROL || | |
264 event->key_code() == ui::VKEY_RCONTROL) { | |
265 is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN); | |
266 } else if (event->key_code() == ui::VKEY_MENU || | |
267 event->key_code() == ui::VKEY_LMENU || | |
268 event->key_code() == ui::VKEY_RMENU) { | |
269 is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN); | |
270 } else { | |
271 return event->type() == ui::ET_KEY_PRESSED ? | |
272 NORMAL_KEY_DOWN : NORMAL_KEY_UP; | |
273 } | |
274 | |
275 if (is_target_key) { | |
276 return event->type() == ui::ET_KEY_PRESSED ? | |
277 TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP; | |
278 } | |
279 return event->type() == ui::ET_KEY_PRESSED ? | |
280 OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP; | |
281 } | |
282 | |
283 bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) { | |
284 switch (TranslateKeyEvent(event)) { | |
285 case TARGET_MODIFIER_UP: | |
286 if (preparing_to_enable_) { | |
287 preparing_to_enable_ = false; | |
288 scroll_delta_ = 0; | |
289 current_state_ = ENABLED; | |
290 modifier_up_event_.reset(new ui::KeyEvent(*event)); | |
291 return true; | |
292 } | |
293 return false; | |
294 case TARGET_MODIFIER_DOWN: | |
295 preparing_to_enable_ = true; | |
296 return false; | |
297 case NORMAL_KEY_DOWN: | |
298 preparing_to_enable_ = false; | |
299 return false; | |
300 case NORMAL_KEY_UP: | |
301 case OTHER_MODIFIER_DOWN: | |
302 case OTHER_MODIFIER_UP: | |
303 return false; | |
304 } | |
305 NOTREACHED(); | |
306 return false; | |
307 } | |
308 | |
309 bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) { | |
310 switch (TranslateKeyEvent(event)) { | |
311 case NORMAL_KEY_UP: | |
312 case TARGET_MODIFIER_DOWN: | |
313 return true; | |
314 case TARGET_MODIFIER_UP: | |
315 current_state_ = LOCKED; | |
316 modifier_up_event_.reset(); | |
317 return true; | |
318 case NORMAL_KEY_DOWN: { | |
319 current_state_ = DISABLED; | |
320 AppendModifier(event); | |
321 DispatchEventAndReleaseModifier(event); | |
322 return true; | |
323 } | |
324 case OTHER_MODIFIER_DOWN: | |
325 case OTHER_MODIFIER_UP: | |
326 return false; | |
327 } | |
328 NOTREACHED(); | |
329 return false; | |
330 } | |
331 | |
332 bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) { | |
333 switch (TranslateKeyEvent(event)) { | |
334 case TARGET_MODIFIER_DOWN: | |
335 return true; | |
336 case TARGET_MODIFIER_UP: | |
337 current_state_ = DISABLED; | |
338 return false; | |
339 case NORMAL_KEY_DOWN: | |
340 case NORMAL_KEY_UP: | |
341 AppendModifier(event); | |
342 return false; | |
343 case OTHER_MODIFIER_DOWN: | |
344 case OTHER_MODIFIER_UP: | |
345 return false; | |
346 } | |
347 NOTREACHED(); | |
348 return false; | |
349 } | |
350 | |
351 void StickyKeysHandler::DispatchEventAndReleaseModifier(ui::Event* event) { | |
352 DCHECK(event->IsKeyEvent() || | |
353 event->IsMouseEvent() || | |
354 event->IsScrollEvent()); | |
355 DCHECK(modifier_up_event_.get()); | |
356 aura::Window* target = static_cast<aura::Window*>(event->target()); | |
357 DCHECK(target); | |
358 aura::Window* root_window = target->GetRootWindow(); | |
359 DCHECK(root_window); | |
360 | |
361 aura::WindowTracker window_tracker; | |
362 window_tracker.Add(target); | |
363 | |
364 event_from_myself_ = true; | |
365 if (event->IsKeyEvent()) { | |
366 delegate_->DispatchKeyEvent(static_cast<ui::KeyEvent*>(event), target); | |
367 } else if (event->IsMouseEvent()) { | |
368 delegate_->DispatchMouseEvent(static_cast<ui::MouseEvent*>(event), target); | |
369 } else { | |
370 delegate_->DispatchScrollEvent( | |
371 static_cast<ui::ScrollEvent*>(event), target); | |
372 } | |
373 | |
374 // The action triggered above may have destroyed the event target, in which | |
375 // case we will dispatch the modifier up event to the root window instead. | |
376 aura::Window* modifier_up_target = | |
377 window_tracker.Contains(target) ? target : root_window; | |
378 delegate_->DispatchKeyEvent(modifier_up_event_.get(), modifier_up_target); | |
379 event_from_myself_ = false; | |
380 } | |
381 | |
382 void StickyKeysHandler::AppendNativeEventMask(unsigned int* state) { | |
383 unsigned int& state_ref = *state; | |
384 switch (modifier_flag_) { | |
385 case ui::EF_CONTROL_DOWN: | |
386 state_ref |= ControlMask; | |
387 break; | |
388 case ui::EF_ALT_DOWN: | |
389 state_ref |= Mod1Mask; | |
390 break; | |
391 case ui::EF_SHIFT_DOWN: | |
392 state_ref |= ShiftMask; | |
393 break; | |
394 default: | |
395 NOTREACHED(); | |
396 } | |
397 } | |
398 | |
399 void StickyKeysHandler::AppendModifier(ui::KeyEvent* event) { | |
400 #if defined(USE_X11) | |
401 XEvent* xev = event->native_event(); | |
402 if (xev) { | |
403 XKeyEvent* xkey = &(xev->xkey); | |
404 AppendNativeEventMask(&xkey->state); | |
405 } | |
406 #elif defined(USE_OZONE) | |
407 NOTIMPLEMENTED() << "Modifier key is not handled"; | |
408 #endif | |
409 event->set_flags(event->flags() | modifier_flag_); | |
410 event->set_character(ui::GetCharacterFromKeyCode(event->key_code(), | |
411 event->flags())); | |
412 event->NormalizeFlags(); | |
413 } | |
414 | |
415 void StickyKeysHandler::AppendModifier(ui::MouseEvent* event) { | |
416 #if defined(USE_X11) | |
417 XEvent* xev = event->native_event(); | |
418 if (xev) { | |
419 XButtonEvent* xkey = &(xev->xbutton); | |
420 AppendNativeEventMask(&xkey->state); | |
421 } | |
422 #elif defined(USE_OZONE) | |
423 NOTIMPLEMENTED() << "Modifier key is not handled"; | |
424 #endif | |
425 event->set_flags(event->flags() | modifier_flag_); | |
426 } | |
427 | |
428 void StickyKeysHandler::AppendModifier(ui::ScrollEvent* event) { | |
429 #if defined(USE_X11) | |
430 XEvent* xev = event->native_event(); | |
431 if (xev) { | |
432 XIDeviceEvent* xievent = | |
433 static_cast<XIDeviceEvent*>(xev->xcookie.data); | |
434 if (xievent) { | |
435 AppendNativeEventMask(reinterpret_cast<unsigned int*>( | |
436 &xievent->mods.effective)); | |
437 } | |
438 } | |
439 #elif defined(USE_OZONE) | |
440 NOTIMPLEMENTED() << "Modifier key is not handled"; | |
441 #endif | |
442 event->set_flags(event->flags() | modifier_flag_); | |
443 } | |
444 | |
445 } // namespace ash | |
OLD | NEW |