diff options
24 files changed, 770 insertions, 441 deletions
diff --git a/chrome/browser/automation/ui_controls_aura.cc b/chrome/browser/automation/ui_controls_aura.cc deleted file mode 100644 index d3bbb96c..0000000 --- a/chrome/browser/automation/ui_controls_aura.cc +++ /dev/null @@ -1,70 +0,0 @@ -// 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 "chrome/browser/automation/ui_controls.h" - -#include "base/logging.h" -#include "views/view.h" - -namespace ui_controls { - -bool SendKeyPress(gfx::NativeWindow window, - ui::KeyboardCode key, - bool control, - bool shift, - bool alt, - bool command) { - // http://crbug.com/104396 - NOTIMPLEMENTED(); - return true; -} - -bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, - ui::KeyboardCode key, - bool control, - bool shift, - bool alt, - bool command, - const base::Closure& task) { - // http://crbug.com/104396 - NOTIMPLEMENTED(); - return true; -} - -bool SendMouseMove(long x, long y) { - // http://crbug.com/104396 - NOTIMPLEMENTED(); - return true; -} - -bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { - // http://crbug.com/104396 - NOTIMPLEMENTED(); - return true; -} - -bool SendMouseEvents(MouseButton type, int state) { - // http://crbug.com/104396 - NOTIMPLEMENTED(); - return true; -} - -bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, - const base::Closure& task) { - // http://crbug.com/104396 - NOTIMPLEMENTED(); - return true; -} - -bool SendMouseClick(MouseButton type) { - return SendMouseEvents(type, UP | DOWN); -} - -void MoveMouseToCenterAndPress(views::View* view, MouseButton button, - int state, const base::Closure& task) { - // http://crbug.com/104396 - NOTIMPLEMENTED(); -} - -} // namespace ui_controls diff --git a/chrome/browser/automation/ui_controls_aurawin.cc b/chrome/browser/automation/ui_controls_aurawin.cc new file mode 100644 index 0000000..d23b3e7 --- /dev/null +++ b/chrome/browser/automation/ui_controls_aurawin.cc @@ -0,0 +1,72 @@ +// 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 "chrome/browser/automation/ui_controls.h" + +#include "base/logging.h" +#include "chrome/browser/automation/ui_controls_internal.h" +#include "ui/aura/desktop.h" +#include "views/view.h" + +namespace ui_controls { + +bool SendKeyPress(gfx::NativeWindow window, + ui::KeyboardCode key, + bool control, + bool shift, + bool alt, + bool command) { + DCHECK(!command); // No command key on Aura + return internal::SendKeyPressImpl(key, control, shift, alt, base::Closure()); +} + +bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, + ui::KeyboardCode key, + bool control, + bool shift, + bool alt, + bool command, + const base::Closure& task) { + DCHECK(!command); // No command key on Aura + return internal::SendKeyPressImpl(key, control, shift, alt, task); +} + +bool SendMouseMove(long x, long y) { + gfx::Point point(x, y); + aura::Desktop::GetInstance()->ConvertPointToNativeScreen(&point); + return internal::SendMouseMoveImpl(point.x(), point.y(), base::Closure()); +} + +bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { + gfx::Point point(x, y); + aura::Desktop::GetInstance()->ConvertPointToNativeScreen(&point); + return internal::SendMouseMoveImpl(point.x(), point.y(), task); +} + +bool SendMouseEvents(MouseButton type, int state) { + return internal::SendMouseEventsImpl(type, state, base::Closure()); +} + +bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, + const base::Closure& task) { + return internal::SendMouseEventsImpl(type, state, task); +} + +bool SendMouseClick(MouseButton type) { + return SendMouseEvents(type, UP | DOWN); +} + +void MoveMouseToCenterAndPress(views::View* view, + MouseButton button, + int state, + const base::Closure& task) { + DCHECK(view); + DCHECK(view->GetWidget()); + gfx::Point view_center(view->width() / 2, view->height() / 2); + views::View::ConvertPointToScreen(view, &view_center); + SendMouseMove(view_center.x(), view_center.y()); + SendMouseEventsNotifyWhenDone(button, state, task); +} + +} // namespace ui_controls diff --git a/chrome/browser/automation/ui_controls_aurax11.cc b/chrome/browser/automation/ui_controls_aurax11.cc new file mode 100644 index 0000000..43bc219c --- /dev/null +++ b/chrome/browser/automation/ui_controls_aurax11.cc @@ -0,0 +1,227 @@ +// 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 "chrome/browser/automation/ui_controls.h" + +#include <X11/keysym.h> +#include <X11/Xlib.h> + +#include "base/callback.h" +#include "base/logging.h" +#include "base/message_pump_x.h" +#include "chrome/browser/automation/ui_controls_internal.h" +#include "ui/aura/desktop.h" +#include "ui/base/keycodes/keyboard_code_conversion_x.h" +#include "views/view.h" + +namespace { + +// Event waiter executes the specified closure|when a matching event +// is found. +// TODO(oshima): Move this to base. +class EventWaiter : public MessageLoopForUI::Observer { + public: + typedef bool (*EventWaiterMatcher)(const base::NativeEvent& event); + + EventWaiter(const base::Closure& closure, EventWaiterMatcher matcher) + : closure_(closure), + matcher_(matcher) { + MessageLoopForUI::current()->AddObserver(this); + } + + virtual ~EventWaiter() { + MessageLoopForUI::current()->RemoveObserver(this); + } + + // MessageLoop::Observer implementation: + virtual base::EventStatus WillProcessEvent( + const base::NativeEvent& event) OVERRIDE { + if ((*matcher_)(event)) { + MessageLoop::current()->PostTask(FROM_HERE, closure_); + delete this; + } + return base::EVENT_CONTINUE; + } + + virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { + } + + private: + base::Closure closure_; + EventWaiterMatcher matcher_; + DISALLOW_COPY_AND_ASSIGN(EventWaiter); +}; + +// Latest mouse pointer location set by SendMouseMoveNotifyWhenDone. +int g_current_x = -1000; +int g_current_y = -1000; + +// Returns atom that indidates that the XEvent is marker event. +Atom MarkerEventAtom() { + return XInternAtom(base::MessagePumpX::GetDefaultXDisplay(), + "marker_event", + False); +} + +// Returns true when the event is a marker event. +bool Matcher(const base::NativeEvent& event) { + return event->xany.type == ClientMessage && + event->xclient.message_type == MarkerEventAtom(); +} + +void RunClosureAfterEvents(const base::Closure closure) { + if (closure.is_null()) + return; + static XEvent* marker_event = NULL; + if (!marker_event) { + marker_event = new XEvent(); + marker_event->xclient.type = ClientMessage; + marker_event->xclient.display = NULL; + marker_event->xclient.window = None; + marker_event->xclient.format = 8; + } + marker_event->xclient.message_type = MarkerEventAtom(); + aura::Desktop::GetInstance()->PostNativeEvent(marker_event); + new EventWaiter(closure, &Matcher); +} + +} // namespace + +namespace ui_controls { + +bool SendKeyPress(gfx::NativeWindow window, + ui::KeyboardCode key, + bool control, + bool shift, + bool alt, + bool command) { + DCHECK(!command); // No command key on Aura + return SendKeyPressNotifyWhenDone( + window, key, control, shift, alt, command, base::Closure()); +} + +void SetMaskAndKeycodeThenSend(XEvent* xevent, + unsigned int mask, + unsigned int keycode) { + xevent->xkey.state |= mask; + xevent->xkey.keycode = keycode; + aura::Desktop::GetInstance()->PostNativeEvent(xevent); +} + +void SetKeycodeAndSendThenUnmask(XEvent* xevent, + unsigned int mask, + unsigned int keycode) { + xevent->xkey.keycode = keycode; + aura::Desktop::GetInstance()->PostNativeEvent(xevent); + xevent->xkey.state ^= mask; +} + +bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, + ui::KeyboardCode key, + bool control, + bool shift, + bool alt, + bool command, + const base::Closure& closure) { + DCHECK(!command); // No command key on Aura + XEvent xevent = {0}; + xevent.xkey.type = KeyPress; + if (control) + SetMaskAndKeycodeThenSend(&xevent, ControlMask, XK_Control_L); + if (shift) + SetMaskAndKeycodeThenSend(&xevent, ShiftMask, XK_Shift_L); + if (alt) + SetMaskAndKeycodeThenSend(&xevent, Mod1Mask, XK_Alt_L); + xevent.xkey.keycode = ui::XKeysymForWindowsKeyCode(key, shift); + aura::Desktop::GetInstance()->PostNativeEvent(&xevent); + + // Send key release events. + xevent.xkey.type = KeyRelease; + aura::Desktop::GetInstance()->PostNativeEvent(&xevent); + if (alt) + SetKeycodeAndSendThenUnmask(&xevent, Mod1Mask, XK_Alt_L); + if (shift) + SetKeycodeAndSendThenUnmask(&xevent, ShiftMask, XK_Shift_L); + if (control) + SetKeycodeAndSendThenUnmask(&xevent, ControlMask, XK_Control_L); + DCHECK(!xevent.xkey.state); + RunClosureAfterEvents(closure); + return true; +} + +bool SendMouseMove(long x, long y) { + return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); +} + +bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& closure) { + XEvent xevent = {0}; + XMotionEvent* xmotion = &xevent.xmotion; + xmotion->type = MotionNotify; + g_current_x = xmotion->x = x; + g_current_y = xmotion->y = y; + xmotion->same_screen = True; + // Desktop will take care of other necessary fields. + aura::Desktop::GetInstance()->PostNativeEvent(&xevent); + RunClosureAfterEvents(closure); + return false; +} + +bool SendMouseEvents(MouseButton type, int state) { + return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); +} + +bool SendMouseEventsNotifyWhenDone(MouseButton type, + int state, + const base::Closure& closure) { + XEvent xevent = {0}; + XButtonEvent* xbutton = &xevent.xbutton; + DCHECK_NE(g_current_x, -1000); + DCHECK_NE(g_current_y, -1000); + xbutton->x = g_current_x; + xbutton->y = g_current_y; + xbutton->same_screen = True; + switch (type) { + case LEFT: + xbutton->button = Button1; + xbutton->state = Button1Mask; + break; + case MIDDLE: + xbutton->button = Button2; + xbutton->state = Button2Mask; + break; + case RIGHT: + xbutton->button = Button3; + xbutton->state = Button3Mask; + break; + } + // Desktop will take care of other necessary fields. + + aura::Desktop* desktop = aura::Desktop::GetInstance(); + if (state & DOWN) { + xevent.xbutton.type = ButtonPress; + desktop->PostNativeEvent(&xevent); + } + if (state & UP) { + xevent.xbutton.type = ButtonRelease; + desktop->PostNativeEvent(&xevent); + } + RunClosureAfterEvents(closure); + return false; +} + +bool SendMouseClick(MouseButton type) { + return SendMouseEvents(type, UP | DOWN); +} + +void MoveMouseToCenterAndPress(views::View* view, MouseButton button, + int state, const base::Closure& closure) { + DCHECK(view); + DCHECK(view->GetWidget()); + gfx::Point view_center(view->width() / 2, view->height() / 2); + views::View::ConvertPointToScreen(view, &view_center); + SendMouseMove(view_center.x(), view_center.y()); + SendMouseEventsNotifyWhenDone(button, state, closure); +} + +} // namespace ui_controls diff --git a/chrome/browser/automation/ui_controls_gtk.cc b/chrome/browser/automation/ui_controls_gtk.cc index d9b02d1..1422031 100644 --- a/chrome/browser/automation/ui_controls_gtk.cc +++ b/chrome/browser/automation/ui_controls_gtk.cc @@ -44,7 +44,7 @@ class EventWaiter : public MessageLoopForUI::Observer { } // MessageLoop::Observer implementation: - virtual void WillProcessEvent(GdkEvent* event) { + virtual void WillProcessEvent(GdkEvent* event) OVERRIDE { if ((event->type == type_) && (--count_ == 0)) { // At the time we're invoked the event has not actually been processed. // Use PostTask to make sure the event has been processed before @@ -57,7 +57,7 @@ class EventWaiter : public MessageLoopForUI::Observer { } } - virtual void DidProcessEvent(GdkEvent* event) { + virtual void DidProcessEvent(GdkEvent* event) OVERRIDE { // No-op. } @@ -230,7 +230,8 @@ bool SendMouseEvents(MouseButton type, int state) { return false; } -bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, +bool SendMouseEventsNotifyWhenDone(MouseButton type, + int state, const base::Closure& task) { bool rv = SendMouseEvents(type, state); GdkEventType wait_type; @@ -280,8 +281,10 @@ void SynchronizeWidgetSize(views::Widget* widget) { } #endif -void MoveMouseToCenterAndPress(views::View* view, MouseButton button, - int state, const base::Closure& task) { +void MoveMouseToCenterAndPress(views::View* view, + MouseButton button, + int state, + const base::Closure& task) { #if defined(OS_LINUX) // X is asynchronous and we need to wait until the window gets // resized to desired size. @@ -292,7 +295,7 @@ void MoveMouseToCenterAndPress(views::View* view, MouseButton button, views::View::ConvertPointToScreen(view, &view_center); SendMouseMoveNotifyWhenDone( view_center.x(), view_center.y(), - base::Bind(&ui_controls::ClickTask, button, state, task)); + base::Bind(&ui_controls::internal::ClickTask, button, state, task)); } #else void MoveMouseToCenterAndPress(GtkWidget* widget, @@ -303,7 +306,7 @@ void MoveMouseToCenterAndPress(GtkWidget* widget, SendMouseMoveNotifyWhenDone( bounds.x() + bounds.width() / 2, bounds.y() + bounds.height() / 2, - base::Bind(&ui_controls::ClickTask, button, state, task)); + base::Bind(&ui_controls::internal::ClickTask, button, state, task)); } #endif diff --git a/chrome/browser/automation/ui_controls_internal.cc b/chrome/browser/automation/ui_controls_internal.cc index 8cb5ec3..1263230 100644 --- a/chrome/browser/automation/ui_controls_internal.cc +++ b/chrome/browser/automation/ui_controls_internal.cc @@ -1,10 +1,11 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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 "chrome/browser/automation/ui_controls_internal.h" namespace ui_controls { +namespace internal { void ClickTask(MouseButton button, int state, const base::Closure& followup) { if (!followup.is_null()) @@ -13,4 +14,5 @@ void ClickTask(MouseButton button, int state, const base::Closure& followup) { SendMouseEvents(button, state); } -} // ui_controls +} // namespace internal +} // namespace ui_controls diff --git a/chrome/browser/automation/ui_controls_internal.h b/chrome/browser/automation/ui_controls_internal.h index 39d0c7df..d28f361 100644 --- a/chrome/browser/automation/ui_controls_internal.h +++ b/chrome/browser/automation/ui_controls_internal.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -9,11 +9,27 @@ #include "chrome/browser/automation/ui_controls.h" namespace ui_controls { +namespace internal { // A utility function to send a mouse click event in a closure. It's shared by // ui_controls_linux.cc and ui_controls_mac.cc void ClickTask(MouseButton button, int state, const base::Closure& followup); +#if defined(OS_WIN) +// A utility functions for windows to send key or mouse events and +// run the task. +bool SendKeyPressImpl(ui::KeyboardCode key, + bool control, + bool shift, + bool alt, + const base::Closure& task); +bool SendMouseMoveImpl(long x, long y, const base::Closure& task); +bool SendMouseEventsImpl(MouseButton type, + int state, + const base::Closure& task); +#endif + +} // namespace internal } // namespace ui_controls #endif // CHROME_BROWSER_AUTOMATION_UI_CONTROLS_INTERNAL_H_ diff --git a/chrome/browser/automation/ui_controls_internal_win.cc b/chrome/browser/automation/ui_controls_internal_win.cc new file mode 100644 index 0000000..76be27f --- /dev/null +++ b/chrome/browser/automation/ui_controls_internal_win.cc @@ -0,0 +1,331 @@ +// 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 "chrome/browser/automation/ui_controls_internal.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop.h" +#include "base/task.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/base/keycodes/keyboard_code_conversion_win.h" + +namespace { + +// InputDispatcher ------------------------------------------------------------ + +// InputDispatcher is used to listen for a mouse/keyboard event. When the +// appropriate event is received the task is notified. +class InputDispatcher : public base::RefCounted<InputDispatcher> { + public: + InputDispatcher(const base::Closure& task, WPARAM message_waiting_for); + + // Invoked from the hook. If mouse_message matches message_waiting_for_ + // MatchingMessageFound is invoked. + void DispatchedMessage(WPARAM mouse_message); + + // Invoked when a matching event is found. Uninstalls the hook and schedules + // an event that notifies the task. + void MatchingMessageFound(); + + private: + friend class base::RefCounted<InputDispatcher>; + + ~InputDispatcher(); + + // Notifies the task and release this (which should delete it). + void NotifyTask(); + + // The task we notify. + base::Closure task_; + + // Message we're waiting for. Not used for keyboard events. + const WPARAM message_waiting_for_; + + DISALLOW_COPY_AND_ASSIGN(InputDispatcher); +}; + +// Have we installed the hook? +bool installed_hook_ = false; + +// Return value from SetWindowsHookEx. +HHOOK next_hook_ = NULL; + +// If a hook is installed, this is the dispatcher. +InputDispatcher* current_dispatcher_ = NULL; + +// Callback from hook when a mouse message is received. +LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param) { + HHOOK next_hook = next_hook_; + if (n_code == HC_ACTION) { + DCHECK(current_dispatcher_); + current_dispatcher_->DispatchedMessage(w_param); + } + return CallNextHookEx(next_hook, n_code, w_param, l_param); +} + +// Callback from hook when a key message is received. +LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param) { + HHOOK next_hook = next_hook_; + if (n_code == HC_ACTION) { + DCHECK(current_dispatcher_); + if (l_param & (1 << 30)) { + // Only send on key up. + current_dispatcher_->MatchingMessageFound(); + } + } + return CallNextHookEx(next_hook, n_code, w_param, l_param); +} + +// Installs dispatcher as the current hook. +void InstallHook(InputDispatcher* dispatcher, bool key_hook) { + DCHECK(!installed_hook_); + current_dispatcher_ = dispatcher; + installed_hook_ = true; + if (key_hook) { + next_hook_ = SetWindowsHookEx(WH_KEYBOARD, &KeyHook, NULL, + GetCurrentThreadId()); + } else { + // NOTE: I originally tried WH_CALLWNDPROCRET, but for some reason I + // didn't get a mouse message like I do with MouseHook. + next_hook_ = SetWindowsHookEx(WH_MOUSE, &MouseHook, NULL, + GetCurrentThreadId()); + } + DCHECK(next_hook_); +} + +// Uninstalls the hook set in InstallHook. +void UninstallHook(InputDispatcher* dispatcher) { + if (current_dispatcher_ == dispatcher) { + installed_hook_ = false; + current_dispatcher_ = NULL; + UnhookWindowsHookEx(next_hook_); + } +} + +InputDispatcher::InputDispatcher(const base::Closure& task, + UINT message_waiting_for) + : task_(task), message_waiting_for_(message_waiting_for) { + InstallHook(this, message_waiting_for == WM_KEYUP); +} + +InputDispatcher::~InputDispatcher() { + // Make sure the hook isn't installed. + UninstallHook(this); +} + +void InputDispatcher::DispatchedMessage(WPARAM message) { + if (message == message_waiting_for_) + MatchingMessageFound(); +} + +void InputDispatcher::MatchingMessageFound() { + UninstallHook(this); + // At the time we're invoked the event has not actually been processed. + // Use PostTask to make sure the event has been processed before notifying. + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&InputDispatcher::NotifyTask, this)); +} + +void InputDispatcher::NotifyTask() { + task_.Run(); + Release(); +} + +// Private functions ---------------------------------------------------------- + +// Populate the INPUT structure with the appropriate keyboard event +// parameters required by SendInput +bool FillKeyboardInput(ui::KeyboardCode key, INPUT* input, bool key_up) { + memset(input, 0, sizeof(INPUT)); + input->type = INPUT_KEYBOARD; + input->ki.wVk = ui::WindowsKeyCodeForKeyboardCode(key); + input->ki.dwFlags = key_up ? KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP : + KEYEVENTF_EXTENDEDKEY; + + return true; +} + +// Send a key event (up/down) +bool SendKeyEvent(ui::KeyboardCode key, bool up) { + INPUT input = { 0 }; + + if (!FillKeyboardInput(key, &input, up)) + return false; + + if (!::SendInput(1, &input, sizeof(INPUT))) + return false; + + return true; +} + +} // namespace + +namespace ui_controls { +namespace internal { + +bool SendKeyPressImpl(ui::KeyboardCode key, + bool control, bool shift, bool alt, + const base::Closure& task) { + scoped_refptr<InputDispatcher> dispatcher( + !task.is_null() ? new InputDispatcher(task, WM_KEYUP) : NULL); + + // If a pop-up menu is open, it won't receive events sent using SendInput. + // Check for a pop-up menu using its window class (#32768) and if one + // exists, send the key event directly there. + HWND popup_menu = ::FindWindow(L"#32768", 0); + if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) { + WPARAM w_param = ui::WindowsKeyCodeForKeyboardCode(key); + LPARAM l_param = 0; + ::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param); + ::SendMessage(popup_menu, WM_KEYUP, w_param, l_param); + + if (dispatcher.get()) + dispatcher->AddRef(); + return true; + } + + INPUT input[8] = { 0 }; // 8, assuming all the modifiers are activated. + + UINT i = 0; + if (control) { + if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], false)) + return false; + i++; + } + + if (shift) { + if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], false)) + return false; + i++; + } + + if (alt) { + if (!FillKeyboardInput(ui::VKEY_MENU, &input[i], false)) + return false; + i++; + } + + if (!FillKeyboardInput(key, &input[i], false)) + return false; + i++; + + if (!FillKeyboardInput(key, &input[i], true)) + return false; + i++; + + if (alt) { + if (!FillKeyboardInput(ui::VKEY_MENU, &input[i], true)) + return false; + i++; + } + + if (shift) { + if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], true)) + return false; + i++; + } + + if (control) { + if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], true)) + return false; + i++; + } + + if (::SendInput(i, input, sizeof(INPUT)) != i) + return false; + + if (dispatcher.get()) + dispatcher->AddRef(); + + return true; +} + +bool SendMouseMoveImpl(long x, long y, const base::Closure& task) { + // First check if the mouse is already there. + POINT current_pos; + ::GetCursorPos(¤t_pos); + if (x == current_pos.x && y == current_pos.y) { + if (!task.is_null()) + MessageLoop::current()->PostTask(FROM_HERE, task); + return true; + } + + INPUT input = { 0 }; + + int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1; + int screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1; + LONG pixel_x = static_cast<LONG>(x * (65535.0f / screen_width)); + LONG pixel_y = static_cast<LONG>(y * (65535.0f / screen_height)); + + input.type = INPUT_MOUSE; + input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; + input.mi.dx = pixel_x; + input.mi.dy = pixel_y; + + scoped_refptr<InputDispatcher> dispatcher( + !task.is_null() ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL); + + if (!::SendInput(1, &input, sizeof(INPUT))) + return false; + + if (dispatcher.get()) + dispatcher->AddRef(); + + return true; +} + +bool SendMouseEventsImpl(MouseButton type, int state, + const base::Closure& task) { + DWORD down_flags = MOUSEEVENTF_ABSOLUTE; + DWORD up_flags = MOUSEEVENTF_ABSOLUTE; + UINT last_event; + + switch (type) { + case LEFT: + down_flags |= MOUSEEVENTF_LEFTDOWN; + up_flags |= MOUSEEVENTF_LEFTUP; + last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN; + break; + + case MIDDLE: + down_flags |= MOUSEEVENTF_MIDDLEDOWN; + up_flags |= MOUSEEVENTF_MIDDLEUP; + last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN; + break; + + case RIGHT: + down_flags |= MOUSEEVENTF_RIGHTDOWN; + up_flags |= MOUSEEVENTF_RIGHTUP; + last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN; + break; + + default: + NOTREACHED(); + return false; + } + + scoped_refptr<InputDispatcher> dispatcher( + !task.is_null() ? new InputDispatcher(task, last_event) : NULL); + + INPUT input = { 0 }; + input.type = INPUT_MOUSE; + input.mi.dwFlags = down_flags; + if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT))) + return false; + + input.mi.dwFlags = up_flags; + if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT))) + return false; + + if (dispatcher.get()) + dispatcher->AddRef(); + + return true; +} + +} // namespace internal +} // namespace ui_controls diff --git a/chrome/browser/automation/ui_controls_mac.mm b/chrome/browser/automation/ui_controls_mac.mm index 41a4fdd..110f467 100644 --- a/chrome/browser/automation/ui_controls_mac.mm +++ b/chrome/browser/automation/ui_controls_mac.mm @@ -381,7 +381,7 @@ void MoveMouseToCenterAndPress( SendMouseMoveNotifyWhenDone( center.x, center.y, - base::Bind(&ui_controls::ClickTask, button, state, task)); + base::Bind(&ui_controls::internal::ClickTask, button, state, task)); } } // ui_controls diff --git a/chrome/browser/automation/ui_controls_win.cc b/chrome/browser/automation/ui_controls_win.cc index 9385df5..6db0e4a 100644 --- a/chrome/browser/automation/ui_controls_win.cc +++ b/chrome/browser/automation/ui_controls_win.cc @@ -4,331 +4,12 @@ #include "chrome/browser/automation/ui_controls.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop.h" -#include "base/task.h" -#include "ui/base/keycodes/keyboard_codes.h" -#include "ui/base/keycodes/keyboard_code_conversion_win.h" +#include "chrome/browser/automation/ui_controls_internal.h" +#include "ui/gfx/point.h" #include "views/view.h" namespace ui_controls { -namespace { - -// InputDispatcher ------------------------------------------------------------ - -// InputDispatcher is used to listen for a mouse/keyboard event. When the -// appropriate event is received the task is notified. -class InputDispatcher : public base::RefCounted<InputDispatcher> { - public: - InputDispatcher(const base::Closure& task, WPARAM message_waiting_for); - - // Invoked from the hook. If mouse_message matches message_waiting_for_ - // MatchingMessageFound is invoked. - void DispatchedMessage(WPARAM mouse_message); - - // Invoked when a matching event is found. Uninstalls the hook and schedules - // an event that notifies the task. - void MatchingMessageFound(); - - private: - friend class base::RefCounted<InputDispatcher>; - - ~InputDispatcher(); - - // Notifies the task and release this (which should delete it). - void NotifyTask(); - - // The task we notify. - base::Closure task_; - - // Message we're waiting for. Not used for keyboard events. - const WPARAM message_waiting_for_; - - DISALLOW_COPY_AND_ASSIGN(InputDispatcher); -}; - -// Have we installed the hook? -bool installed_hook_ = false; - -// Return value from SetWindowsHookEx. -HHOOK next_hook_ = NULL; - -// If a hook is installed, this is the dispatcher. -InputDispatcher* current_dispatcher_ = NULL; - -// Callback from hook when a mouse message is received. -LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param) { - HHOOK next_hook = next_hook_; - if (n_code == HC_ACTION) { - DCHECK(current_dispatcher_); - current_dispatcher_->DispatchedMessage(w_param); - } - return CallNextHookEx(next_hook, n_code, w_param, l_param); -} - -// Callback from hook when a key message is received. -LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param) { - HHOOK next_hook = next_hook_; - if (n_code == HC_ACTION) { - DCHECK(current_dispatcher_); - if (l_param & (1 << 30)) { - // Only send on key up. - current_dispatcher_->MatchingMessageFound(); - } - } - return CallNextHookEx(next_hook, n_code, w_param, l_param); -} - -// Installs dispatcher as the current hook. -void InstallHook(InputDispatcher* dispatcher, bool key_hook) { - DCHECK(!installed_hook_); - current_dispatcher_ = dispatcher; - installed_hook_ = true; - if (key_hook) { - next_hook_ = SetWindowsHookEx(WH_KEYBOARD, &KeyHook, NULL, - GetCurrentThreadId()); - } else { - // NOTE: I originally tried WH_CALLWNDPROCRET, but for some reason I - // didn't get a mouse message like I do with MouseHook. - next_hook_ = SetWindowsHookEx(WH_MOUSE, &MouseHook, NULL, - GetCurrentThreadId()); - } - DCHECK(next_hook_); -} - -// Uninstalls the hook set in InstallHook. -void UninstallHook(InputDispatcher* dispatcher) { - if (current_dispatcher_ == dispatcher) { - installed_hook_ = false; - current_dispatcher_ = NULL; - UnhookWindowsHookEx(next_hook_); - } -} - -InputDispatcher::InputDispatcher(const base::Closure& task, - UINT message_waiting_for) - : task_(task), message_waiting_for_(message_waiting_for) { - InstallHook(this, message_waiting_for == WM_KEYUP); -} - -InputDispatcher::~InputDispatcher() { - // Make sure the hook isn't installed. - UninstallHook(this); -} - -void InputDispatcher::DispatchedMessage(WPARAM message) { - if (message == message_waiting_for_) - MatchingMessageFound(); -} - -void InputDispatcher::MatchingMessageFound() { - UninstallHook(this); - // At the time we're invoked the event has not actually been processed. - // Use PostTask to make sure the event has been processed before notifying. - MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&InputDispatcher::NotifyTask, this)); -} - -void InputDispatcher::NotifyTask() { - task_.Run(); - Release(); -} - -// Private functions ---------------------------------------------------------- - -// Populate the INPUT structure with the appropriate keyboard event -// parameters required by SendInput -bool FillKeyboardInput(ui::KeyboardCode key, INPUT* input, bool key_up) { - memset(input, 0, sizeof(INPUT)); - input->type = INPUT_KEYBOARD; - input->ki.wVk = ui::WindowsKeyCodeForKeyboardCode(key); - input->ki.dwFlags = key_up ? KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP : - KEYEVENTF_EXTENDEDKEY; - - return true; -} - -// Send a key event (up/down) -bool SendKeyEvent(ui::KeyboardCode key, bool up) { - INPUT input = { 0 }; - - if (!FillKeyboardInput(key, &input, up)) - return false; - - if (!::SendInput(1, &input, sizeof(INPUT))) - return false; - - return true; -} - -bool SendKeyPressImpl(ui::KeyboardCode key, - bool control, bool shift, bool alt, - const base::Closure& task) { - scoped_refptr<InputDispatcher> dispatcher( - !task.is_null() ? new InputDispatcher(task, WM_KEYUP) : NULL); - - // If a pop-up menu is open, it won't receive events sent using SendInput. - // Check for a pop-up menu using its window class (#32768) and if one - // exists, send the key event directly there. - HWND popup_menu = ::FindWindow(L"#32768", 0); - if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) { - WPARAM w_param = ui::WindowsKeyCodeForKeyboardCode(key); - LPARAM l_param = 0; - ::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param); - ::SendMessage(popup_menu, WM_KEYUP, w_param, l_param); - - if (dispatcher.get()) - dispatcher->AddRef(); - return true; - } - - INPUT input[8] = { 0 }; // 8, assuming all the modifiers are activated. - - UINT i = 0; - if (control) { - if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], false)) - return false; - i++; - } - - if (shift) { - if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], false)) - return false; - i++; - } - - if (alt) { - if (!FillKeyboardInput(ui::VKEY_MENU, &input[i], false)) - return false; - i++; - } - - if (!FillKeyboardInput(key, &input[i], false)) - return false; - i++; - - if (!FillKeyboardInput(key, &input[i], true)) - return false; - i++; - - if (alt) { - if (!FillKeyboardInput(ui::VKEY_MENU, &input[i], true)) - return false; - i++; - } - - if (shift) { - if (!FillKeyboardInput(ui::VKEY_SHIFT, &input[i], true)) - return false; - i++; - } - - if (control) { - if (!FillKeyboardInput(ui::VKEY_CONTROL, &input[i], true)) - return false; - i++; - } - - if (::SendInput(i, input, sizeof(INPUT)) != i) - return false; - - if (dispatcher.get()) - dispatcher->AddRef(); - - return true; -} - -bool SendMouseMoveImpl(long x, long y, const base::Closure& task) { - // First check if the mouse is already there. - POINT current_pos; - ::GetCursorPos(¤t_pos); - if (x == current_pos.x && y == current_pos.y) { - if (!task.is_null()) - MessageLoop::current()->PostTask(FROM_HERE, task); - return true; - } - - INPUT input = { 0 }; - - int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1; - int screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1; - LONG pixel_x = static_cast<LONG>(x * (65535.0f / screen_width)); - LONG pixel_y = static_cast<LONG>(y * (65535.0f / screen_height)); - - input.type = INPUT_MOUSE; - input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; - input.mi.dx = pixel_x; - input.mi.dy = pixel_y; - - scoped_refptr<InputDispatcher> dispatcher( - !task.is_null() ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL); - - if (!::SendInput(1, &input, sizeof(INPUT))) - return false; - - if (dispatcher.get()) - dispatcher->AddRef(); - - return true; -} - -bool SendMouseEventsImpl(MouseButton type, int state, - const base::Closure& task) { - DWORD down_flags = MOUSEEVENTF_ABSOLUTE; - DWORD up_flags = MOUSEEVENTF_ABSOLUTE; - UINT last_event; - - switch (type) { - case LEFT: - down_flags |= MOUSEEVENTF_LEFTDOWN; - up_flags |= MOUSEEVENTF_LEFTUP; - last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN; - break; - - case MIDDLE: - down_flags |= MOUSEEVENTF_MIDDLEDOWN; - up_flags |= MOUSEEVENTF_MIDDLEUP; - last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN; - break; - - case RIGHT: - down_flags |= MOUSEEVENTF_RIGHTDOWN; - up_flags |= MOUSEEVENTF_RIGHTUP; - last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN; - break; - - default: - NOTREACHED(); - return false; - } - - scoped_refptr<InputDispatcher> dispatcher( - !task.is_null() ? new InputDispatcher(task, last_event) : NULL); - - INPUT input = { 0 }; - input.type = INPUT_MOUSE; - input.mi.dwFlags = down_flags; - if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT))) - return false; - - input.mi.dwFlags = up_flags; - if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT))) - return false; - - if (dispatcher.get()) - dispatcher->AddRef(); - - return true; -} - -} // namespace - -// public functions ----------------------------------------------------------- - bool SendKeyPress(gfx::NativeWindow window, ui::KeyboardCode key, bool control, @@ -336,7 +17,7 @@ bool SendKeyPress(gfx::NativeWindow window, bool alt, bool command) { DCHECK(!command); // No command key on Windows - return SendKeyPressImpl(key, control, shift, alt, base::Closure()); + return internal::SendKeyPressImpl(key, control, shift, alt, base::Closure()); } bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, @@ -347,28 +28,28 @@ bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, bool command, const base::Closure& task) { DCHECK(!command); // No command key on Windows - return SendKeyPressImpl(key, control, shift, alt, task); + return internal::SendKeyPressImpl(key, control, shift, alt, task); } bool SendMouseMove(long x, long y) { - return SendMouseMoveImpl(x, y, base::Closure()); + return internal::SendMouseMoveImpl(x, y, base::Closure()); } bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { - return SendMouseMoveImpl(x, y, task); + return internal::SendMouseMoveImpl(x, y, task); } bool SendMouseEvents(MouseButton type, int state) { - return SendMouseEventsImpl(type, state, base::Closure()); + return internal::SendMouseEventsImpl(type, state, base::Closure()); } bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, const base::Closure& task) { - return SendMouseEventsImpl(type, state, task); + return internal::SendMouseEventsImpl(type, state, task); } bool SendMouseClick(MouseButton type) { - return SendMouseEventsImpl(type, UP | DOWN, base::Closure()); + return internal::SendMouseEventsImpl(type, UP | DOWN, base::Closure()); } void MoveMouseToCenterAndPress(views::View* view, diff --git a/chrome/browser/printing/print_dialog_cloud_uitest.cc b/chrome/browser/printing/print_dialog_cloud_uitest.cc index 0f93e8e..33aa437 100644 --- a/chrome/browser/printing/print_dialog_cloud_uitest.cc +++ b/chrome/browser/printing/print_dialog_cloud_uitest.cc @@ -227,9 +227,8 @@ net::URLRequestJob* PrintDialogCloudTest::Factory(net::URLRequest* request, return new SimpleTestJob(request); } -#if defined(OS_WIN) || defined(USE_AURA) +#if defined(OS_WIN) && !defined(USE_AURA) // http://crbug.com/94864 for OS_WIN issue -// http://crbug.com/103497 for USE_AURA issue #define MAYBE_HandlersRegistered DISABLED_HandlersRegistered #else #define MAYBE_HandlersRegistered HandlersRegistered diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 02ab0d1..5ea8bba 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -238,9 +238,11 @@ 'browser/automation/testing_automation_provider_views.cc', 'browser/automation/testing_automation_provider_win.cc', 'browser/automation/ui_controls.h', + 'browser/automation/ui_controls_aurawin.cc', + 'browser/automation/ui_controls_aurax11.cc', 'browser/automation/ui_controls_internal.cc', 'browser/automation/ui_controls_internal.h', - 'browser/automation/ui_controls_aura.cc', + 'browser/automation/ui_controls_internal_win.cc', 'browser/automation/ui_controls_gtk.cc', 'browser/automation/ui_controls_mac.mm', 'browser/automation/ui_controls_win.cc', diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc index 27de278..bfa4ade 100644 --- a/chrome/test/automation/automation_proxy.cc +++ b/chrome/test/automation/automation_proxy.cc @@ -490,7 +490,7 @@ scoped_refptr<TabProxy> AutomationProxy::CreateExternalTab( return NULL; } -#if defined(OS_WIN) +#if defined(OS_WIN) && !defined(USE_AURA) DCHECK(IsWindow(*external_tab_container)); #else // defined(OS_WIN) DCHECK(*external_tab_container); diff --git a/chrome/test/base/view_event_test_base.cc b/chrome/test/base/view_event_test_base.cc index cc414b2..91cced3 100644 --- a/chrome/test/base/view_event_test_base.cc +++ b/chrome/test/base/view_event_test_base.cc @@ -88,8 +88,7 @@ void ViewEventTestBase::TearDown() { DestroyWindow(window_->GetNativeWindow()); #else window_->Close(); - MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); - ui_test_utils::RunMessageLoop(); + ui_test_utils::RunAllPendingInMessageLoop(); #endif window_ = NULL; } @@ -134,7 +133,7 @@ void ViewEventTestBase::StartMessageLoopAndRunTest() { #endif // Flush any pending events to make sure we start with a clean slate. - MessageLoop::current()->RunAllPending(); + ui_test_utils::RunAllPendingInMessageLoop(); // Schedule a task that starts the test. Need to do this as we're going to // run the message loop. @@ -142,7 +141,7 @@ void ViewEventTestBase::StartMessageLoopAndRunTest() { FROM_HERE, base::Bind(&ViewEventTestBase::DoTestOnMessageLoop, this)); - MessageLoop::current()->Run(); + ui_test_utils::RunMessageLoop(); } gfx::Size ViewEventTestBase::GetPreferredSize() { diff --git a/content/browser/renderer_host/test_render_view_host.cc b/content/browser/renderer_host/test_render_view_host.cc index abad243..84611a0 100644 --- a/content/browser/renderer_host/test_render_view_host.cc +++ b/content/browser/renderer_host/test_render_view_host.cc @@ -234,7 +234,7 @@ void TestRenderWidgetHostView::AcceleratedSurfaceBuffersSwapped( int gpu_host_id) { } -#elif defined(OS_WIN) +#elif defined(OS_WIN) && !defined(USE_AURA) void TestRenderWidgetHostView::WillWmDestroy() { } diff --git a/content/browser/renderer_host/test_render_view_host.h b/content/browser/renderer_host/test_render_view_host.h index 27468dd..7b531af 100644 --- a/content/browser/renderer_host/test_render_view_host.h +++ b/content/browser/renderer_host/test_render_view_host.h @@ -119,7 +119,7 @@ class TestRenderWidgetHostView : public RenderWidgetHostView { int renderer_id, int32 route_id, int gpu_host_id) OVERRIDE; -#elif defined(OS_WIN) +#elif defined(OS_WIN) && !defined(USE_AURA) virtual void WillWmDestroy() OVERRIDE; #endif #if defined(OS_POSIX) || defined(USE_AURA) diff --git a/ui/aura/desktop.cc b/ui/aura/desktop.cc index a4aa58e..b3a8b8a 100644 --- a/ui/aura/desktop.cc +++ b/ui/aura/desktop.cc @@ -377,6 +377,11 @@ void Desktop::PostNativeEvent(const base::NativeEvent& native_event) { host_->PostNativeEvent(native_event); } +void Desktop::ConvertPointToNativeScreen(gfx::Point* point) const { + gfx::Point location = host_->GetLocationOnNativeScreen(); + point->Offset(location.x(), location.y()); +} + void Desktop::SetCapture(Window* window) { if (capture_window_ == window) return; diff --git a/ui/aura/desktop.h b/ui/aura/desktop.h index 7e8d0dc..40963f4 100644 --- a/ui/aura/desktop.h +++ b/ui/aura/desktop.h @@ -131,6 +131,9 @@ class AURA_EXPORT Desktop : public ui::CompositorDelegate, // Posts |native_event| to the platform's event queue. void PostNativeEvent(const base::NativeEvent& native_event); + // Converts |point| from the desktop's coordinate system to native screen's. + void ConvertPointToNativeScreen(gfx::Point* point) const; + // Capture ------------------------------------------------------------------- // Sets capture to the specified window. diff --git a/ui/aura/desktop_host.h b/ui/aura/desktop_host.h index b276dea..0ac7be1 100644 --- a/ui/aura/desktop_host.h +++ b/ui/aura/desktop_host.h @@ -50,6 +50,9 @@ class DesktopHost : public MessageLoop::Dispatcher { virtual gfx::Size GetSize() const = 0; virtual void SetSize(const gfx::Size& size) = 0; + // Returns the location of the desktop on native screen. + virtual gfx::Point GetLocationOnNativeScreen() const = 0; + // Sets the currently displayed cursor. virtual void SetCursor(gfx::NativeCursor cursor) = 0; diff --git a/ui/aura/desktop_host_linux.cc b/ui/aura/desktop_host_linux.cc index 2de31e6..7e9a5b1 100644 --- a/ui/aura/desktop_host_linux.cc +++ b/ui/aura/desktop_host_linux.cc @@ -238,6 +238,7 @@ class DesktopHostLinux : public DesktopHost { virtual void ToggleFullScreen() OVERRIDE; virtual gfx::Size GetSize() const OVERRIDE; virtual void SetSize(const gfx::Size& size) OVERRIDE; + virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE; virtual void SetCursor(gfx::NativeCursor cursor_type) OVERRIDE; virtual gfx::Point QueryMouseLocation() OVERRIDE; virtual void PostNativeEvent(const base::NativeEvent& event) OVERRIDE; @@ -256,8 +257,8 @@ class DesktopHostLinux : public DesktopHost { // Current Aura cursor. gfx::NativeCursor current_cursor_; - // The size of |xwindow_|. - gfx::Size size_; + // The bounds of |xwindow_|. + gfx::Rect bounds_; DISALLOW_COPY_AND_ASSIGN(DesktopHostLinux); }; @@ -267,7 +268,7 @@ DesktopHostLinux::DesktopHostLinux(const gfx::Rect& bounds) xdisplay_(base::MessagePumpX::GetDefaultXDisplay()), xwindow_(0), current_cursor_(aura::kCursorNull), - size_(bounds.size()) { + bounds_(bounds) { xwindow_ = XCreateSimpleWindow(xdisplay_, DefaultRootWindow(xdisplay_), bounds.x(), bounds.y(), bounds.width(), bounds.height(), @@ -331,11 +332,12 @@ base::MessagePumpDispatcher::DispatchStatus DesktopHostLinux::Dispatch( // It's possible that the X window may be resized by some other means than // from within aura (e.g. the X window manager can change the size). Make // sure the desktop size is maintained properly. - gfx::Size size(xev->xconfigure.width, xev->xconfigure.height); - if (size_ != size) { - size_ = size; - desktop_->OnHostResized(size); - } + gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y, + xev->xconfigure.width, xev->xconfigure.height); + bool size_changed = bounds_.size() != bounds.size(); + bounds_ = bounds; + if (size_changed) + desktop_->OnHostResized(bounds.size()); handled = true; break; } @@ -437,11 +439,11 @@ void DesktopHostLinux::ToggleFullScreen() { } gfx::Size DesktopHostLinux::GetSize() const { - return size_; + return bounds_.size(); } void DesktopHostLinux::SetSize(const gfx::Size& size) { - if (size == size_) + if (size == bounds_.size()) return; XResizeWindow(xdisplay_, xwindow_, size.width(), size.height()); @@ -450,11 +452,15 @@ void DesktopHostLinux::SetSize(const gfx::Size& size) { // case if we're running without a window manager. If there's a window // manager, it can modify or ignore the request, but (per ICCCM) we'll get a // (possibly synthetic) ConfigureNotify about the actual size and correct - // |size_| later. - size_ = size; + // |bounds_| later. + bounds_.set_size(size); desktop_->OnHostResized(size); } +gfx::Point DesktopHostLinux::GetLocationOnNativeScreen() const { + return bounds_.origin(); +} + void DesktopHostLinux::SetCursor(gfx::NativeCursor cursor) { if (current_cursor_ == cursor) return; @@ -478,8 +484,8 @@ gfx::Point DesktopHostLinux::QueryMouseLocation() { &root_x_return, &root_y_return, &win_x_return, &win_y_return, &mask_return); - return gfx::Point(max(0, min(size_.width(), win_x_return)), - max(0, min(size_.height(), win_y_return))); + return gfx::Point(max(0, min(bounds_.width(), win_x_return)), + max(0, min(bounds_.height(), win_y_return))); } void DesktopHostLinux::PostNativeEvent(const base::NativeEvent& native_event) { @@ -488,7 +494,30 @@ void DesktopHostLinux::PostNativeEvent(const base::NativeEvent& native_event) { XEvent xevent = *native_event; xevent.xany.display = xdisplay_; xevent.xany.window = xwindow_; - ::XPutBackEvent(xdisplay_, &xevent); + + switch (xevent.type) { + case EnterNotify: + case LeaveNotify: + case MotionNotify: + case KeyPress: + case KeyRelease: + case ButtonPress: + case ButtonRelease: { + // The fields used below are in the same place for all of events + // above. Using xmotion from XEvent's unions to avoid repeating + // the code. + xevent.xmotion.root = DefaultRootWindow(xdisplay_); + xevent.xmotion.time = CurrentTime; + + gfx::Point point(xevent.xmotion.x, xevent.xmotion.y); + desktop_->ConvertPointToNativeScreen(&point); + xevent.xmotion.x_root = point.x(); + xevent.xmotion.y_root = point.y(); + } + default: + break; + } + XSendEvent(xdisplay_, xwindow_, False, 0, &xevent); } bool DesktopHostLinux::IsWindowManagerPresent() { diff --git a/ui/aura/desktop_host_win.cc b/ui/aura/desktop_host_win.cc index 36399e2..3ad4f66 100644 --- a/ui/aura/desktop_host_win.cc +++ b/ui/aura/desktop_host_win.cc @@ -206,6 +206,13 @@ void DesktopHostWin::SetSize(const gfx::Size& size) { SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOREPOSITION); } +gfx::Point DesktopHostWin::GetLocationOnNativeScreen() const { + RECT r; + GetClientRect(hwnd(), &r); + return gfx::Point(r.left, r.top); +} + + void DesktopHostWin::SetCursor(gfx::NativeCursor native_cursor) { // Custom web cursors are handled directly. if (native_cursor == kCursorCustom) diff --git a/ui/aura/desktop_host_win.h b/ui/aura/desktop_host_win.h index 9309d2b..cd9c136 100644 --- a/ui/aura/desktop_host_win.h +++ b/ui/aura/desktop_host_win.h @@ -27,6 +27,7 @@ class DesktopHostWin : public DesktopHost, public ui::WindowImpl { virtual void ToggleFullScreen() OVERRIDE; virtual gfx::Size GetSize() const OVERRIDE; virtual void SetSize(const gfx::Size& size) OVERRIDE; + virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE; virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE; virtual gfx::Point QueryMouseLocation() OVERRIDE; virtual void PostNativeEvent(const base::NativeEvent& native_event) OVERRIDE; diff --git a/ui/base/x/events_x.cc b/ui/base/x/events_x.cc index 966fec2..765aed3 100644 --- a/ui/base/x/events_x.cc +++ b/ui/base/x/events_x.cc @@ -6,12 +6,17 @@ #include <X11/Xlib.h> #include <X11/extensions/XInput2.h> +#include <string.h> #include "base/logging.h" #include "ui/base/keycodes/keyboard_code_conversion_x.h" #include "ui/base/touch/touch_factory.h" #include "ui/gfx/point.h" +#if !defined(TOOLKIT_USES_GTK) +#include "base/message_pump_x.h" +#endif + namespace { // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+. @@ -356,12 +361,25 @@ float GetTouchForce(const base::NativeEvent& native_event) { } base::NativeEvent CreateNoopEvent() { - static XEvent* noop = new XEvent(); - noop->xclient.type = ClientMessage; - noop->xclient.display = NULL; - noop->xclient.window = None; - noop->xclient.message_type = 0; - noop->xclient.format = 0; + static XEvent* noop = NULL; + if (!noop) { + noop = new XEvent(); + memset(noop, 0, sizeof(XEvent)); + noop->xclient.type = ClientMessage; + noop->xclient.window = None; + noop->xclient.format = 8; + DCHECK(!noop->xclient.display); + } + // TODO(oshima): Remove ifdef once gtk is removed from views. +#if defined(TOOLKIT_USES_GTK) + NOTREACHED(); +#else + // Make sure we use atom from current xdisplay, which may + // change during the test. + noop->xclient.message_type = XInternAtom( + base::MessagePumpX::GetDefaultXDisplay(), + "noop", False); +#endif return noop; } @@ -572,6 +572,7 @@ 'gfx/render_text_linux.h', 'gfx/render_text_win.cc', 'gfx/render_text_win.h', + 'base/x/events_x.cc', ], }], ['OS=="android"', { diff --git a/ui/ui_unittests.gypi b/ui/ui_unittests.gypi index 5a4a8c4..cab407c 100644 --- a/ui/ui_unittests.gypi +++ b/ui/ui_unittests.gypi @@ -121,7 +121,7 @@ ], }, }], - ['OS == "linux"', { + ['OS == "linux" and toolkit_views==1', { 'sources': [ 'base/x/events_x_unittest.cc', ], |