1 // Copyright 2014 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/accelerators/key_hold_detector.h" 6 7 #include <X11/Xlib.h> 8 9 #undef RootWindow 10 #undef Status 11 12 #include "ash/shell.h" 13 #include "base/message_loop/message_loop.h" 14 #include "ui/aura/window_tracker.h" 15 #include "ui/aura/window_tree_host.h" 16 #include "ui/events/event_dispatcher.h" 17 #include "ui/events/event_processor.h" 18 19 namespace ash { 20 namespace { 21 DispatchPressedEvent(XEvent native_event,scoped_ptr<aura::WindowTracker> tracker)22void DispatchPressedEvent(XEvent native_event, 23 scoped_ptr<aura::WindowTracker> tracker) { 24 // The target window may be gone. 25 if (tracker->windows().empty()) 26 return; 27 aura::Window* target = *(tracker->windows().begin()); 28 ui::KeyEvent event(&native_event, false); 29 event.set_flags(event.flags() | ui::EF_IS_SYNTHESIZED); 30 ui::EventDispatchDetails result ALLOW_UNUSED = 31 target->GetHost()->event_processor()->OnEventFromSource(&event); 32 } 33 PostPressedEvent(ui::KeyEvent * event)34void PostPressedEvent(ui::KeyEvent* event) { 35 // Modify RELEASED event to PRESSED event. 36 XEvent xkey = *(event->native_event()); 37 xkey.xkey.type = KeyPress; 38 xkey.xkey.state |= ShiftMask; 39 scoped_ptr<aura::WindowTracker> tracker(new aura::WindowTracker); 40 tracker->Add(static_cast<aura::Window*>(event->target())); 41 42 base::MessageLoopForUI::current()->PostTask( 43 FROM_HERE, 44 base::Bind(&DispatchPressedEvent, xkey, base::Passed(&tracker))); 45 } 46 47 } // namespace 48 KeyHoldDetector(scoped_ptr<Delegate> delegate)49KeyHoldDetector::KeyHoldDetector(scoped_ptr<Delegate> delegate) 50 : state_(INITIAL), 51 delegate_(delegate.Pass()) {} 52 ~KeyHoldDetector()53KeyHoldDetector::~KeyHoldDetector() {} 54 OnKeyEvent(ui::KeyEvent * event)55void KeyHoldDetector::OnKeyEvent(ui::KeyEvent* event) { 56 if (!delegate_->ShouldProcessEvent(event)) 57 return; 58 59 if (delegate_->IsStartEvent(event)) { 60 switch (state_) { 61 case INITIAL: 62 // Pass through posted event. 63 if (event->flags() & ui::EF_IS_SYNTHESIZED) { 64 event->set_flags(event->flags() & ~ui::EF_IS_SYNTHESIZED); 65 return; 66 } 67 state_ = PRESSED; 68 // Don't process ET_KEY_PRESSED event yet. The ET_KEY_PRESSED 69 // event will be generated upon ET_KEY_RELEASEED event below. 70 event->StopPropagation(); 71 break; 72 case PRESSED: 73 state_ = HOLD; 74 // pass through 75 case HOLD: 76 delegate_->OnKeyHold(event); 77 event->StopPropagation(); 78 break; 79 } 80 } else if (event->type() == ui::ET_KEY_RELEASED) { 81 switch (state_) { 82 case INITIAL: 83 break; 84 case PRESSED: { 85 PostPressedEvent(event); 86 event->StopPropagation(); 87 break; 88 } 89 case HOLD: { 90 delegate_->OnKeyUnhold(event); 91 event->StopPropagation(); 92 break; 93 } 94 } 95 state_ = INITIAL; 96 } 97 } 98 99 } // namespace ash 100