Home | History | Annotate | Download | only in test
      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 "ui/base/test/ui_controls.h"
      6 
      7 #include <gdk/gdkkeysyms.h>
      8 #include <gtk/gtk.h>
      9 
     10 #include "base/bind.h"
     11 #include "base/logging.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "ui/base/gtk/event_synthesis_gtk.h"
     14 #include "ui/base/gtk/gtk_screen_util.h"
     15 #include "ui/gfx/rect.h"
     16 
     17 namespace {
     18 bool g_ui_controls_enabled = false;
     19 
     20 // static
     21 guint32 XTimeNow() {
     22   struct timespec ts;
     23   clock_gettime(CLOCK_MONOTONIC, &ts);
     24   return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
     25 }
     26 
     27 class EventWaiter : public base::MessageLoopForUI::Observer {
     28  public:
     29   EventWaiter(const base::Closure& task, GdkEventType type, int count)
     30       : task_(task),
     31         type_(type),
     32         count_(count) {
     33     base::MessageLoopForUI::current()->AddObserver(this);
     34   }
     35 
     36   virtual ~EventWaiter() {
     37     base::MessageLoopForUI::current()->RemoveObserver(this);
     38   }
     39 
     40   // MessageLoop::Observer implementation:
     41   virtual void WillProcessEvent(GdkEvent* event) OVERRIDE {
     42     if ((event->type == type_) && (--count_ == 0)) {
     43       // At the time we're invoked the event has not actually been processed.
     44       // Use PostTask to make sure the event has been processed before
     45       // notifying.
     46       // NOTE: if processing a message results in running a nested message
     47       // loop, then DidProcessEvent isn't immediately sent. As such, we do
     48       // the processing in WillProcessEvent rather than DidProcessEvent.
     49       base::MessageLoop::current()->PostTask(FROM_HERE, task_);
     50       delete this;
     51     }
     52   }
     53 
     54   virtual void DidProcessEvent(GdkEvent* event) OVERRIDE {
     55     // No-op.
     56   }
     57 
     58  private:
     59   base::Closure task_;
     60   GdkEventType type_;
     61   // The number of events of this type to wait for.
     62   int count_;
     63 };
     64 
     65 void FakeAMouseMotionEvent(gint x, gint y) {
     66   GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY);
     67 
     68   event->motion.send_event = false;
     69   event->motion.time = XTimeNow();
     70 
     71   GtkWidget* grab_widget = gtk_grab_get_current();
     72   if (grab_widget) {
     73     // If there is a grab, we need to target all events at it regardless of
     74     // what widget the mouse is over.
     75     event->motion.window = gtk_widget_get_window(grab_widget);
     76   } else {
     77     event->motion.window = gdk_window_at_pointer(&x, &y);
     78   }
     79   g_object_ref(event->motion.window);
     80   gint window_x, window_y;
     81   gdk_window_get_origin(event->motion.window, &window_x, &window_y);
     82   event->motion.x = x - window_x;
     83   event->motion.y = y - window_y;
     84   event->motion.x_root = x;
     85   event->motion.y_root = y;
     86 
     87   event->motion.device = gdk_device_get_core_pointer();
     88   event->type = GDK_MOTION_NOTIFY;
     89 
     90   gdk_event_put(event);
     91   gdk_event_free(event);
     92 }
     93 
     94 }  // namespace
     95 
     96 namespace ui_controls {
     97 
     98 void EnableUIControls() {
     99   g_ui_controls_enabled = true;
    100 }
    101 
    102 bool SendKeyPress(gfx::NativeWindow window,
    103                   ui::KeyboardCode key,
    104                   bool control,
    105                   bool shift,
    106                   bool alt,
    107                   bool command) {
    108   CHECK(g_ui_controls_enabled);
    109   DCHECK(!command);  // No command key on Linux
    110   GdkWindow* event_window = NULL;
    111   GtkWidget* grab_widget = gtk_grab_get_current();
    112   if (grab_widget) {
    113     // If there is a grab, send all events to the grabbed widget.
    114     event_window = gtk_widget_get_window(grab_widget);
    115   } else if (window) {
    116     event_window = gtk_widget_get_window(GTK_WIDGET(window));
    117   } else {
    118     // No target was specified. Send the events to the active toplevel.
    119     GList* windows = gtk_window_list_toplevels();
    120     for (GList* element = windows; element; element = g_list_next(element)) {
    121       GtkWindow* this_window = GTK_WINDOW(element->data);
    122       if (gtk_window_is_active(this_window)) {
    123         event_window = gtk_widget_get_window(GTK_WIDGET(this_window));
    124         break;
    125       }
    126     }
    127     g_list_free(windows);
    128   }
    129   if (!event_window) {
    130     NOTREACHED() << "Window not specified and none is active";
    131     return false;
    132   }
    133 
    134   std::vector<GdkEvent*> events;
    135   ui::SynthesizeKeyPressEvents(event_window, key, control, shift, alt, &events);
    136   for (std::vector<GdkEvent*>::iterator iter = events.begin();
    137        iter != events.end(); ++iter) {
    138     gdk_event_put(*iter);
    139     // gdk_event_put appends a copy of the event.
    140     gdk_event_free(*iter);
    141   }
    142 
    143   return true;
    144 }
    145 
    146 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
    147                                 ui::KeyboardCode key,
    148                                 bool control,
    149                                 bool shift,
    150                                 bool alt,
    151                                 bool command,
    152                                 const base::Closure& task) {
    153   CHECK(g_ui_controls_enabled);
    154   DCHECK(!command);  // No command key on Linux
    155   int release_count = 1;
    156   if (control)
    157     release_count++;
    158   if (shift)
    159     release_count++;
    160   if (alt)
    161     release_count++;
    162   // This object will delete itself after running |task|.
    163   new EventWaiter(task, GDK_KEY_RELEASE, release_count);
    164   return SendKeyPress(window, key, control, shift, alt, command);
    165 }
    166 
    167 bool SendMouseMove(long x, long y) {
    168   CHECK(g_ui_controls_enabled);
    169   gdk_display_warp_pointer(gdk_display_get_default(), gdk_screen_get_default(),
    170                            x, y);
    171   // Sometimes gdk_display_warp_pointer fails to send back any indication of
    172   // the move, even though it succesfully moves the server cursor. We fake it in
    173   // order to get drags to work.
    174   FakeAMouseMotionEvent(x, y);
    175   return true;
    176 }
    177 
    178 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) {
    179   CHECK(g_ui_controls_enabled);
    180   bool rv = SendMouseMove(x, y);
    181   new EventWaiter(task, GDK_MOTION_NOTIFY, 1);
    182   return rv;
    183 }
    184 
    185 bool SendMouseEvents(MouseButton type, int state) {
    186   CHECK(g_ui_controls_enabled);
    187   GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS);
    188 
    189   event->button.send_event = false;
    190   event->button.time = XTimeNow();
    191 
    192   gint x, y;
    193   GtkWidget* grab_widget = gtk_grab_get_current();
    194   if (grab_widget) {
    195     // If there is a grab, we need to target all events at it regardless of
    196     // what widget the mouse is over.
    197     event->button.window = gtk_widget_get_window(grab_widget);
    198     gdk_window_get_pointer(event->button.window, &x, &y, NULL);
    199   } else {
    200     event->button.window = gdk_window_at_pointer(&x, &y);
    201     CHECK(event->button.window);
    202   }
    203 
    204   g_object_ref(event->button.window);
    205   event->button.x = x;
    206   event->button.y = y;
    207   gint origin_x, origin_y;
    208   gdk_window_get_origin(event->button.window, &origin_x, &origin_y);
    209   event->button.x_root = x + origin_x;
    210   event->button.y_root = y + origin_y;
    211 
    212   event->button.axes = NULL;
    213   GdkModifierType modifier;
    214   gdk_window_get_pointer(event->button.window, NULL, NULL, &modifier);
    215   event->button.state = modifier;
    216   event->button.button = type == LEFT ? 1 : (type == MIDDLE ? 2 : 3);
    217   event->button.device = gdk_device_get_core_pointer();
    218 
    219   event->button.type = GDK_BUTTON_PRESS;
    220   if (state & DOWN)
    221     gdk_event_put(event);
    222 
    223   // Also send a release event.
    224   GdkEvent* release_event = gdk_event_copy(event);
    225   release_event->button.type = GDK_BUTTON_RELEASE;
    226   release_event->button.time++;
    227   if (state & UP)
    228     gdk_event_put(release_event);
    229 
    230   gdk_event_free(event);
    231   gdk_event_free(release_event);
    232 
    233   return false;
    234 }
    235 
    236 bool SendMouseEventsNotifyWhenDone(MouseButton type,
    237                                    int state,
    238                                    const base::Closure& task) {
    239   CHECK(g_ui_controls_enabled);
    240   bool rv = SendMouseEvents(type, state);
    241   GdkEventType wait_type;
    242   if (state & UP) {
    243     wait_type = GDK_BUTTON_RELEASE;
    244   } else {
    245     if (type == LEFT)
    246       wait_type = GDK_BUTTON_PRESS;
    247     else if (type == MIDDLE)
    248       wait_type = GDK_2BUTTON_PRESS;
    249     else
    250       wait_type = GDK_3BUTTON_PRESS;
    251   }
    252   new EventWaiter(task, wait_type, 1);
    253   return rv;
    254 }
    255 
    256 bool SendMouseClick(MouseButton type) {
    257   CHECK(g_ui_controls_enabled);
    258   return SendMouseEvents(type, UP | DOWN);
    259 }
    260 
    261 }  // namespace ui_controls
    262