123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 |
- // Copyright 2013 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 "ui/base/test/ui_controls_internal_win.h"
- #include <windows.h>
- #include <algorithm>
- #include <cmath>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/location.h"
- #include "base/logging.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/test/test_timeouts.h"
- #include "base/threading/thread_checker.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/win/win_util.h"
- #include "ui/base/win/event_creation_utils.h"
- #include "ui/display/win/screen_win.h"
- #include "ui/events/keycodes/keyboard_code_conversion_win.h"
- #include "ui/events/keycodes/keyboard_codes.h"
- #include "ui/gfx/geometry/point.h"
- namespace {
- // InputDispatcher ------------------------------------------------------------
- // InputDispatcher is used to listen for a mouse/keyboard event. Only one
- // instance may be alive at a time. The callback is run when the appropriate
- // event is received.
- class InputDispatcher {
- public:
- // Constructs an InputDispatcher that will invoke |callback| when
- // |message_type| is received. This must be invoked on thread, after the input
- // is sent but before it is processed.
- static void CreateForMouseEvent(base::OnceClosure callback,
- WPARAM message_type);
- // Special case of CreateForMessage() for WM_KEYUP (can await multiple events
- // when modifiers are involved).
- static void CreateForKeyUp(base::OnceClosure callback,
- int num_keyups_awaited);
- // Special case of CreateForMessage() for WM_MOUSEMOVE. Upon receipt, an error
- // message is logged if the destination of the move is not |screen_point|.
- // |callback| is run regardless after a sufficiently long delay. This
- // generally happens when another process has a window over the test's window,
- // or if |screen_point| is not over a window owned by the test.
- static void CreateForMouseMove(base::OnceClosure callback,
- const gfx::Point& screen_point);
- InputDispatcher(const InputDispatcher&) = delete;
- InputDispatcher& operator=(const InputDispatcher&) = delete;
- private:
- // Generic message
- InputDispatcher(base::OnceClosure callback,
- WPARAM message_waiting_for,
- UINT system_queue_flag);
- // WM_KEYUP
- InputDispatcher(base::OnceClosure callback,
- WPARAM message_waiting_for,
- UINT system_queue_flag,
- int num_keyups_awaited);
- // WM_MOUSEMOVE
- InputDispatcher(base::OnceClosure callback,
- WPARAM message_waiting_for,
- UINT system_queue_flag,
- const gfx::Point& screen_point);
- ~InputDispatcher();
- // Installs the dispatcher as the current hook.
- void InstallHook();
- // Callback from hook when a mouse message is received.
- static LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param);
- // Callback from hook when a key message is received.
- static LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param);
- // Invoked from the hook. If |message_id| matches message_waiting_for_
- // MatchingMessageProcessed() is invoked. |mouse_hook_struct| contains extra
- // information about the mouse event.
- void DispatchedMessage(UINT message_id,
- const MOUSEHOOKSTRUCT* mouse_hook_struct);
- // Invoked when a matching event is found. Must be invoked through a task
- // posted from the hook so that the event, which is processed after the hook,
- // has already been handled.
- // |definitively_done| is set to true if this event is definitely the one we
- // were waiting for (i.e., we will resume regardless of the presence of
- // |system_queue_flag_| messages in the queue).
- void MatchingMessageProcessed(bool definitively_done);
- // Invoked when the hook for a mouse move is not called within a reasonable
- // time. This likely means that a window from another process is over a test
- // window, so the event does not reach this process.
- void OnTimeout();
- // The current dispatcher if a hook is installed; otherwise, nullptr;
- static InputDispatcher* current_dispatcher_;
- // Return value from SetWindowsHookEx.
- static HHOOK next_hook_;
- THREAD_CHECKER(thread_checker_);
- // The callback to run when the desired message is received.
- base::OnceClosure callback_;
- // The message on which the instance is waiting -- unused for WM_KEYUP
- // messages.
- const WPARAM message_waiting_for_;
- // The system queue flag (ref. ::GetQueueStatus) which the awaited event is
- // reflected in.
- const UINT system_queue_flag_;
- // The number of WM_KEYUP messages to receive before dispatching |callback_|.
- // Only relevant when |message_waiting_for_| is WM_KEYUP.
- int num_keyups_awaited_ = 0;
- // The desired mouse position for a mouse move event.
- const gfx::Point expected_mouse_location_;
- base::WeakPtrFactory<InputDispatcher> weak_factory_{this};
- };
- // static
- InputDispatcher* InputDispatcher::current_dispatcher_ = nullptr;
- // static
- HHOOK InputDispatcher::next_hook_ = nullptr;
- // static
- void InputDispatcher::CreateForMouseEvent(base::OnceClosure callback,
- WPARAM message_type) {
- DCHECK(message_type == WM_LBUTTONDOWN || message_type == WM_LBUTTONUP ||
- message_type == WM_MBUTTONDOWN || message_type == WM_MBUTTONUP ||
- message_type == WM_RBUTTONDOWN || message_type == WM_RBUTTONUP)
- << message_type;
- // Owns self.
- new InputDispatcher(std::move(callback), message_type, QS_MOUSEBUTTON);
- }
- // static
- void InputDispatcher::CreateForKeyUp(base::OnceClosure callback,
- int num_keyups_awaited) {
- // Owns self.
- new InputDispatcher(std::move(callback), WM_KEYUP, QS_KEY,
- num_keyups_awaited);
- }
- // static
- void InputDispatcher::CreateForMouseMove(base::OnceClosure callback,
- const gfx::Point& screen_point) {
- // Owns self.
- new InputDispatcher(std::move(callback), WM_MOUSEMOVE, QS_MOUSEMOVE,
- screen_point);
- }
- InputDispatcher::InputDispatcher(base::OnceClosure callback,
- WPARAM message_waiting_for,
- UINT system_queue_flag)
- : callback_(std::move(callback)),
- message_waiting_for_(message_waiting_for),
- system_queue_flag_(system_queue_flag) {
- InstallHook();
- }
- InputDispatcher::InputDispatcher(base::OnceClosure callback,
- WPARAM message_waiting_for,
- UINT system_queue_flag,
- int num_keyups_awaited)
- : callback_(std::move(callback)),
- message_waiting_for_(message_waiting_for),
- system_queue_flag_(system_queue_flag),
- num_keyups_awaited_(num_keyups_awaited) {
- DCHECK_EQ(message_waiting_for_, static_cast<WPARAM>(WM_KEYUP));
- InstallHook();
- }
- InputDispatcher::InputDispatcher(base::OnceClosure callback,
- WPARAM message_waiting_for,
- UINT system_queue_flag,
- const gfx::Point& screen_point)
- : callback_(std::move(callback)),
- message_waiting_for_(message_waiting_for),
- system_queue_flag_(system_queue_flag),
- expected_mouse_location_(screen_point) {
- DCHECK_EQ(message_waiting_for_, static_cast<WPARAM>(WM_MOUSEMOVE));
- InstallHook();
- }
- InputDispatcher::~InputDispatcher() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK_EQ(current_dispatcher_, this);
- current_dispatcher_ = nullptr;
- UnhookWindowsHookEx(next_hook_);
- next_hook_ = nullptr;
- }
- void InputDispatcher::InstallHook() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!current_dispatcher_);
- current_dispatcher_ = this;
- int hook_type;
- HOOKPROC hook_function;
- if (message_waiting_for_ == WM_KEYUP) {
- hook_type = WH_KEYBOARD;
- hook_function = &KeyHook;
- } else {
- // WH_CALLWNDPROCRET does not generate mouse messages for some reason.
- hook_type = WH_MOUSE;
- hook_function = &MouseHook;
- if (message_waiting_for_ == WM_MOUSEMOVE) {
- // Things don't go well with move events sometimes. Bail out if it takes
- // too long.
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&InputDispatcher::OnTimeout,
- weak_factory_.GetWeakPtr()),
- TestTimeouts::action_timeout());
- }
- }
- next_hook_ =
- SetWindowsHookEx(hook_type, hook_function, nullptr, GetCurrentThreadId());
- DPCHECK(next_hook_);
- }
- // static
- LRESULT CALLBACK InputDispatcher::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(
- static_cast<UINT>(w_param),
- reinterpret_cast<MOUSEHOOKSTRUCT*>(l_param));
- }
- return CallNextHookEx(next_hook, n_code, w_param, l_param);
- }
- // static
- LRESULT CALLBACK InputDispatcher::KeyHook(int n_code,
- WPARAM w_param,
- LPARAM l_param) {
- if ((n_code == HC_ACTION) && (HIWORD(l_param) & KF_UP)) {
- DCHECK(current_dispatcher_);
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&InputDispatcher::MatchingMessageProcessed,
- current_dispatcher_->weak_factory_.GetWeakPtr(), false));
- }
- return CallNextHookEx(next_hook_, n_code, w_param, l_param);
- }
- void InputDispatcher::DispatchedMessage(
- UINT message_id,
- const MOUSEHOOKSTRUCT* mouse_hook_struct) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (message_id == message_waiting_for_) {
- bool definitively_done = false;
- if (message_id == WM_MOUSEMOVE) {
- // Allow a slight offset, targets are never one pixel wide and pixel math
- // is imprecise (see SendMouseMoveImpl()).
- gfx::Point actual_location(mouse_hook_struct->pt);
- auto offset = expected_mouse_location_ - actual_location;
- definitively_done = std::abs(offset.x()) + std::abs(offset.y()) < 2;
- // Verify that the mouse ended up at the desired location.
- LOG_IF(ERROR, !definitively_done)
- << "Mouse moved to (" << mouse_hook_struct->pt.x << ", "
- << mouse_hook_struct->pt.y << ") rather than ("
- << expected_mouse_location_.x() << ", "
- << expected_mouse_location_.y()
- << "); check the math in SendMouseMoveImpl.";
- }
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&InputDispatcher::MatchingMessageProcessed,
- weak_factory_.GetWeakPtr(), definitively_done));
- } else if ((message_waiting_for_ == WM_LBUTTONDOWN &&
- message_id == WM_LBUTTONDBLCLK) ||
- (message_waiting_for_ == WM_MBUTTONDOWN &&
- message_id == WM_MBUTTONDBLCLK) ||
- (message_waiting_for_ == WM_RBUTTONDOWN &&
- message_id == WM_RBUTTONDBLCLK)) {
- LOG(WARNING) << "Double click event being treated as single-click. "
- << "This may result in different event processing behavior. "
- << "If you need a single click try moving the mouse between "
- << "down events.";
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&InputDispatcher::MatchingMessageProcessed,
- weak_factory_.GetWeakPtr(), false));
- }
- }
- void InputDispatcher::MatchingMessageProcessed(bool definitively_done) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (message_waiting_for_ == WM_KEYUP && --num_keyups_awaited_ > 0)
- return;
- // Unless specified otherwise by |definitively_done| : resume on the last
- // event of its type only (instead of the first one) to prevent flakes when
- // InputDispatcher is created while there are preexisting matching events
- // remaining in the queue. Emit a warning to help diagnose flakes should the
- // queue somehow never become empty of such events.
- if (HIWORD(::GetQueueStatus(system_queue_flag_)) && !definitively_done) {
- LOG(WARNING)
- << "Skipping message notification per remaining events in the queue "
- "(will try again shortly) : "
- << system_queue_flag_;
- // Check back on the next loop instead of relying on the remaining event
- // being caught by our hooks (all events don't seem to reliably make it
- // there).
- if (message_waiting_for_ == WM_KEYUP)
- ++num_keyups_awaited_;
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&InputDispatcher::MatchingMessageProcessed,
- weak_factory_.GetWeakPtr(), false));
- return;
- }
- // Delete |this| before running the callback to allow callers to chain input
- // events.
- auto callback = std::move(callback_);
- delete this;
- std::move(callback).Run();
- }
- void InputDispatcher::OnTimeout() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- LOG(ERROR) << "Timed out waiting for mouse move event. The test will now "
- "continue, but may fail.";
- auto callback = std::move(callback_);
- delete this;
- std::move(callback).Run();
- }
- // Private functions ----------------------------------------------------------
- UINT MapVirtualKeyToScanCode(UINT code) {
- UINT ret_code = MapVirtualKey(code, MAPVK_VK_TO_VSC);
- // We have to manually mark the following virtual
- // keys as extended or else their scancodes depend
- // on NumLock state.
- // For ex. VK_DOWN will be mapped onto either DOWN or NumPad2
- // depending on NumLock state which can lead to tests failures.
- switch (code) {
- case VK_INSERT:
- case VK_DELETE:
- case VK_HOME:
- case VK_END:
- case VK_NEXT:
- case VK_PRIOR:
- case VK_LEFT:
- case VK_RIGHT:
- case VK_UP:
- case VK_DOWN:
- case VK_NUMLOCK:
- ret_code |= KF_EXTENDED;
- break;
- default:
- break;
- }
- return ret_code;
- }
- // Whether scan code should be used for |key|.
- // When sending keyboard events by SendInput() function, Windows does not
- // "smartly" add scan code if virtual key-code is used. So these key events
- // won't have scan code or DOM UI Event code string.
- // But we cannot blindly send all events with scan code. For some layout
- // dependent keys, the Windows may not translate them to what they used to be,
- // because the test cases are usually running in headless environment with
- // default keyboard layout. So fall back to use virtual key code for these keys.
- bool ShouldSendThroughScanCode(ui::KeyboardCode key) {
- const DWORD native_code = ui::WindowsKeyCodeForKeyboardCode(key);
- const DWORD scan_code = MapVirtualKeyToScanCode(native_code);
- return native_code == MapVirtualKey(scan_code, MAPVK_VSC_TO_VK);
- }
- // Append an INPUT structure with the appropriate keyboard event
- // parameters required by SendInput
- void AppendKeyboardInput(ui::KeyboardCode key,
- bool key_up,
- std::vector<INPUT>* input) {
- INPUT key_input = {};
- key_input.type = INPUT_KEYBOARD;
- key_input.ki.wVk = ui::WindowsKeyCodeForKeyboardCode(key);
- if (ShouldSendThroughScanCode(key)) {
- key_input.ki.wScan = MapVirtualKeyToScanCode(key_input.ki.wVk);
- // When KEYEVENTF_SCANCODE is used, ki.wVk is ignored, so we do not need to
- // clear it.
- key_input.ki.dwFlags = KEYEVENTF_SCANCODE;
- if ((key_input.ki.wScan & 0xFF00) != 0)
- key_input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
- }
- if (key_up)
- key_input.ki.dwFlags |= KEYEVENTF_KEYUP;
- input->push_back(key_input);
- }
- // Append an INPUT structure with a simple mouse up or down event to be used
- // by SendInput.
- void AppendMouseInput(DWORD flags, std::vector<INPUT>* input) {
- INPUT mouse_input = {};
- mouse_input.type = INPUT_MOUSE;
- mouse_input.mi.dwFlags = flags;
- input->push_back(mouse_input);
- }
- // Append an INPUT array with optional accelerator keys that may be pressed
- // with a keyboard or mouse event. This array will be sent by SendInput.
- void AppendAcceleratorInputs(bool control,
- bool shift,
- bool alt,
- bool key_up,
- std::vector<INPUT>* input) {
- if (control)
- AppendKeyboardInput(ui::VKEY_CONTROL, key_up, input);
- if (alt)
- AppendKeyboardInput(ui::VKEY_LMENU, key_up, input);
- if (shift)
- AppendKeyboardInput(ui::VKEY_SHIFT, key_up, input);
- }
- } // namespace
- namespace ui_controls {
- namespace internal {
- bool SendKeyPressImpl(HWND window,
- ui::KeyboardCode key,
- bool control,
- bool shift,
- bool alt,
- base::OnceClosure task) {
- // SendInput only works as we expect it if one of our windows is the
- // foreground window already.
- HWND target_window = (::GetActiveWindow() &&
- ::GetWindow(::GetActiveWindow(), GW_OWNER) == window) ?
- ::GetActiveWindow() :
- window;
- if (window && ::GetForegroundWindow() != target_window)
- return false;
- // 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 (task)
- InputDispatcher::CreateForKeyUp(std::move(task), 1);
- return true;
- }
- std::vector<INPUT> input;
- AppendAcceleratorInputs(control, shift, alt, false, &input);
- AppendKeyboardInput(key, false, &input);
- AppendKeyboardInput(key, true, &input);
- AppendAcceleratorInputs(control, shift, alt, true, &input);
- if (input.size() > std::numeric_limits<UINT>::max())
- return false;
- if (::SendInput(static_cast<UINT>(input.size()), input.data(),
- sizeof(INPUT)) != input.size()) {
- return false;
- }
- if (task)
- InputDispatcher::CreateForKeyUp(std::move(task), input.size() / 2);
- return true;
- }
- bool SendMouseMoveImpl(int screen_x, int screen_y, base::OnceClosure task) {
- gfx::Point screen_point =
- display::win::ScreenWin::DIPToScreenPoint({screen_x, screen_y});
- // Check if the mouse is already there.
- POINT current_pos;
- ::GetCursorPos(¤t_pos);
- if (screen_point.x() == current_pos.x && screen_point.y() == current_pos.y) {
- if (task)
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(task));
- return true;
- }
- if (!ui::SendMouseEvent(screen_point,
- MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE)) {
- return false;
- }
- if (task)
- InputDispatcher::CreateForMouseMove(std::move(task),
- {screen_point.x(), screen_point.y()});
- return true;
- }
- bool SendMouseEventsImpl(MouseButton type,
- int button_state,
- base::OnceClosure task,
- int accelerator_state) {
- 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 = (button_state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN;
- break;
- case MIDDLE:
- down_flags |= MOUSEEVENTF_MIDDLEDOWN;
- up_flags |= MOUSEEVENTF_MIDDLEUP;
- last_event = (button_state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN;
- break;
- case RIGHT:
- down_flags |= MOUSEEVENTF_RIGHTDOWN;
- up_flags |= MOUSEEVENTF_RIGHTUP;
- last_event = (button_state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN;
- break;
- default:
- NOTREACHED();
- return false;
- }
- std::vector<INPUT> input;
- if (button_state & DOWN) {
- AppendAcceleratorInputs(accelerator_state & kControl,
- accelerator_state & kShift,
- accelerator_state & kAlt, false, &input);
- AppendMouseInput(down_flags, &input);
- }
- if (button_state & UP) {
- AppendMouseInput(up_flags, &input);
- AppendAcceleratorInputs(accelerator_state & kControl,
- accelerator_state & kShift,
- accelerator_state & kAlt, true, &input);
- }
- if (input.size() > std::numeric_limits<UINT>::max())
- return false;
- if (::SendInput(static_cast<UINT>(input.size()), input.data(),
- sizeof(INPUT)) != input.size()) {
- return false;
- }
- if (task)
- InputDispatcher::CreateForMouseEvent(std::move(task), last_event);
- return true;
- }
- bool SendTouchEventsImpl(int action, int num, int x, int y) {
- const int kTouchesLengthCap = 16;
- DCHECK_LE(num, kTouchesLengthCap);
- using InitializeTouchInjectionFn = BOOL(WINAPI*)(UINT32, DWORD);
- static const auto initialize_touch_injection =
- reinterpret_cast<InitializeTouchInjectionFn>(
- base::win::GetUser32FunctionPointer("InitializeTouchInjection"));
- if (!initialize_touch_injection ||
- !initialize_touch_injection(num, TOUCH_FEEDBACK_INDIRECT)) {
- return false;
- }
- using InjectTouchInputFn = BOOL(WINAPI*)(UINT32, POINTER_TOUCH_INFO*);
- static const auto inject_touch_input = reinterpret_cast<InjectTouchInputFn>(
- base::win::GetUser32FunctionPointer("InjectTouchInput"));
- if (!inject_touch_input)
- return false;
- POINTER_TOUCH_INFO pointer_touch_info[kTouchesLengthCap];
- for (int i = 0; i < num; i++) {
- POINTER_TOUCH_INFO& contact = pointer_touch_info[i];
- memset(&contact, 0, sizeof(POINTER_TOUCH_INFO));
- contact.pointerInfo.pointerType = PT_TOUCH;
- contact.pointerInfo.pointerId = i;
- contact.pointerInfo.ptPixelLocation.y = y;
- contact.pointerInfo.ptPixelLocation.x = x + 10 * i;
- contact.touchFlags = TOUCH_FLAG_NONE;
- contact.touchMask =
- TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION | TOUCH_MASK_PRESSURE;
- contact.orientation = 90;
- contact.pressure = 32000;
- // defining contact area
- contact.rcContact.top = contact.pointerInfo.ptPixelLocation.y - 2;
- contact.rcContact.bottom = contact.pointerInfo.ptPixelLocation.y + 2;
- contact.rcContact.left = contact.pointerInfo.ptPixelLocation.x - 2;
- contact.rcContact.right = contact.pointerInfo.ptPixelLocation.x + 2;
- contact.pointerInfo.pointerFlags =
- POINTER_FLAG_DOWN | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
- }
- // Injecting the touch down on screen
- if (!inject_touch_input(num, pointer_touch_info))
- return false;
- // Injecting the touch move on screen
- if (action & MOVE) {
- for (int i = 0; i < num; i++) {
- POINTER_TOUCH_INFO& contact = pointer_touch_info[i];
- contact.pointerInfo.ptPixelLocation.y = y + 10;
- contact.pointerInfo.ptPixelLocation.x = x + 10 * i + 30;
- contact.pointerInfo.pointerFlags =
- POINTER_FLAG_UPDATE | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
- }
- if (!inject_touch_input(num, pointer_touch_info))
- return false;
- }
- // Injecting the touch up on screen
- if (action & RELEASE) {
- for (int i = 0; i < num; i++) {
- POINTER_TOUCH_INFO& contact = pointer_touch_info[i];
- contact.pointerInfo.ptPixelLocation.y = y + 10;
- contact.pointerInfo.ptPixelLocation.x = x + 10 * i + 30;
- contact.pointerInfo.pointerFlags = POINTER_FLAG_UP | POINTER_FLAG_INRANGE;
- }
- if (!inject_touch_input(num, pointer_touch_info))
- return false;
- }
- return true;
- }
- } // namespace internal
- } // namespace ui_controls
|