Home | History | Annotate | Download | only in accelerators
      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 
     22 void DispatchPressedEvent(const ui::KeyEvent& key_event,
     23                           scoped_ptr<aura::WindowTracker> tracker) {
     24   // The target window may be gone.
     25   if (tracker->windows().empty())
     26     return;
     27   ui::KeyEvent event(key_event);
     28   aura::Window* target = *(tracker->windows().begin());
     29   ui::EventDispatchDetails result ALLOW_UNUSED =
     30       target->GetHost()->event_processor()->OnEventFromSource(&event);
     31 }
     32 
     33 void PostPressedEvent(ui::KeyEvent* event) {
     34   // Modify RELEASED event to PRESSED event.
     35   const ui::KeyEvent pressed_event(
     36       ui::ET_KEY_PRESSED,
     37       event->key_code(),
     38       event->code(),
     39       event->flags() | ui::EF_SHIFT_DOWN | ui::EF_IS_SYNTHESIZED);
     40   scoped_ptr<aura::WindowTracker> tracker(new aura::WindowTracker);
     41   tracker->Add(static_cast<aura::Window*>(event->target()));
     42 
     43   base::MessageLoopForUI::current()->PostTask(
     44       FROM_HERE,
     45       base::Bind(&DispatchPressedEvent, pressed_event, base::Passed(&tracker)));
     46 }
     47 
     48 }  // namespace
     49 
     50 KeyHoldDetector::KeyHoldDetector(scoped_ptr<Delegate> delegate)
     51     : state_(INITIAL),
     52       delegate_(delegate.Pass()) {}
     53 
     54 KeyHoldDetector::~KeyHoldDetector() {}
     55 
     56 void KeyHoldDetector::OnKeyEvent(ui::KeyEvent* event) {
     57   if (!delegate_->ShouldProcessEvent(event))
     58     return;
     59 
     60   if (delegate_->IsStartEvent(event)) {
     61     switch (state_) {
     62       case INITIAL:
     63         // Pass through posted event.
     64         if (event->flags() & ui::EF_IS_SYNTHESIZED) {
     65           event->set_flags(event->flags() & ~ui::EF_IS_SYNTHESIZED);
     66           return;
     67         }
     68         state_ = PRESSED;
     69         // Don't process ET_KEY_PRESSED event yet. The ET_KEY_PRESSED
     70         // event will be generated upon ET_KEY_RELEASEED event below.
     71         event->StopPropagation();
     72         break;
     73       case PRESSED:
     74         state_ = HOLD;
     75         // pass through
     76       case HOLD:
     77         delegate_->OnKeyHold(event);
     78         event->StopPropagation();
     79         break;
     80       }
     81   } else if (event->type() == ui::ET_KEY_RELEASED) {
     82     switch (state_) {
     83       case INITIAL:
     84         break;
     85       case PRESSED: {
     86         PostPressedEvent(event);
     87         event->StopPropagation();
     88         break;
     89       }
     90       case HOLD: {
     91         delegate_->OnKeyUnhold(event);
     92         event->StopPropagation();
     93         break;
     94       }
     95     }
     96     state_ = INITIAL;
     97   }
     98 }
     99 
    100 }  // namespace ash
    101