blob: 99dd049b04d98c4f700c6b2ef05d9de1256e132e [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "views/focus/accelerator_handler.h"
#include <bitset>
#include <gtk/gtk.h>
#include <X11/extensions/XInput2.h>
#include "ui/base/touch/touch_factory.h"
#include "views/accelerator.h"
#include "views/events/event.h"
#include "views/focus/focus_manager.h"
#include "views/ime/input_method.h"
#include "views/view.h"
#include "views/widget/native_widget.h"
namespace views {
namespace {
Widget* FindWidgetForGdkWindow(GdkWindow* gdk_window) {
gpointer data = NULL;
gdk_window_get_user_data(gdk_window, &data);
GtkWidget* gtk_widget = reinterpret_cast<GtkWidget*>(data);
if (!gtk_widget || !GTK_IS_WIDGET(gtk_widget)) {
DLOG(WARNING) << "no GtkWidget found for that GdkWindow";
return NULL;
}
Widget* widget = Widget::GetWidgetForNativeView(gtk_widget);
if (!widget) {
DLOG(WARNING) << "no NativeWidgetGtk found for that GtkWidget";
return NULL;
}
return widget;
}
} // namespace
bool DispatchX2Event(Widget* widget, XEvent* xev) {
XGenericEventCookie* cookie = &xev->xcookie;
switch (cookie->evtype) {
case XI_KeyPress:
case XI_KeyRelease: {
// TODO(sad): We don't capture XInput2 events from keyboard yet.
break;
}
#if defined(USE_XI2_MT)
case XI_TouchBegin:
case XI_TouchEnd:
case XI_TouchUpdate: {
// Hide the cursor when a touch event comes in.
ui::TouchFactory::GetInstance()->SetCursorVisible(false, false);
// If the TouchEvent is processed by |widget|, then return.
TouchEvent touch(xev);
if (widget->OnTouchEvent(touch) != ui::TOUCH_STATUS_UNKNOWN)
return true;
// We do not want to generate a mouse event for an unprocessed touch
// event here. That is already done by the gesture manager in
// RootView::OnTouchEvent.
return false;
}
#endif
case XI_ButtonPress:
case XI_ButtonRelease:
case XI_Motion: {
XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(cookie->data);
// Scrolling the wheel generates press/release events with button id's 4
// and 5. In case of a wheelscroll, we do not want to show the cursor.
if (xievent->detail == 4 || xievent->detail == 5) {
MouseWheelEvent wheelev(xev);
return widget->OnMouseEvent(wheelev);
}
ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
// Is the event coming from a touch device?
if (factory->IsTouchDevice(xievent->sourceid)) {
// Hide the cursor when a touch event comes in.
factory->SetCursorVisible(false, false);
// With XInput 2.0, XI_ButtonPress and XI_ButtonRelease events are
// ignored, as XI_Motion events contain enough data to detect finger
// press and release. See more notes in TouchFactory::TouchParam.
if ((cookie->evtype == XI_ButtonPress ||
cookie->evtype == XI_ButtonRelease) &&
factory->IsRealTouchDevice(xievent->sourceid))
return false;
// If the TouchEvent is processed by |widget|, then return. Otherwise
// let it fall through so it can be used as a MouseEvent, if desired.
TouchEvent touch(xev);
if (widget->OnTouchEvent(touch) != ui::TOUCH_STATUS_UNKNOWN)
return true;
// We do not want to generate a mouse event for an unprocessed touch
// event here. That is already done by the gesture manager in
// RootView::OnTouchEvent.
return false;
} else {
MouseEvent mouseev(xev);
// Show the cursor. Start a timer to hide the cursor after a delay on
// move (not drag) events, or if the only button pressed is released.
bool start_timer = mouseev.type() == ui::ET_MOUSE_MOVED;
start_timer |= mouseev.type() == ui::ET_MOUSE_RELEASED &&
(mouseev.IsOnlyLeftMouseButton() ||
mouseev.IsOnlyMiddleMouseButton() ||
mouseev.IsOnlyRightMouseButton());
factory->SetCursorVisible(true, start_timer);
return widget->OnMouseEvent(mouseev);
}
}
}
return false;
}
bool DispatchXEvent(XEvent* xev) {
GdkDisplay* gdisp = gdk_display_get_default();
XID xwindow = xev->xany.window;
if (xev->type == GenericEvent) {
if (!ui::TouchFactory::GetInstance()->ShouldProcessXI2Event(xev))
return true; // Consume the event.
XGenericEventCookie* cookie = &xev->xcookie;
if (cookie->evtype == XI_HierarchyChanged) {
ui::TouchFactory::GetInstance()->UpdateDeviceList(cookie->display);
return true;
}
XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(cookie->data);
xwindow = xiev->event;
}
GdkWindow* gwind = gdk_window_lookup_for_display(gdisp, xwindow);
Widget* widget = FindWidgetForGdkWindow(gwind);
if (widget) {
switch (xev->type) {
case KeyPress:
case KeyRelease: {
KeyEvent keyev(xev);
InputMethod* ime = widget->GetInputMethod();
// Always dispatch key events to the input method first, to make sure
// that the input method's hotkeys work all time.
if (ime) {
ime->DispatchKeyEvent(keyev);
return true;
}
return widget->OnKeyEvent(keyev);
}
case ButtonPress:
case ButtonRelease:
if (xev->xbutton.button == 4 || xev->xbutton.button == 5) {
// Scrolling the wheel triggers button press/release events.
MouseWheelEvent wheelev(xev);
return widget->OnMouseEvent(wheelev);
}
// fallthrough
case MotionNotify: {
MouseEvent mouseev(xev);
return widget->OnMouseEvent(mouseev);
}
case GenericEvent: {
return DispatchX2Event(widget, xev);
}
}
}
return false;
}
AcceleratorHandler::AcceleratorHandler() {}
base::MessagePumpDispatcher::DispatchStatus
AcceleratorHandler::Dispatch(XEvent* xev) {
return DispatchXEvent(xev) ?
base::MessagePumpDispatcher::EVENT_PROCESSED :
base::MessagePumpDispatcher::EVENT_IGNORED;
}
} // namespace views