1// Copyright (c) 2012 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 <windowsx.h>
6
7#include "ui/events/event_constants.h"
8
9#include "base/logging.h"
10#include "base/time/time.h"
11#include "base/win/win_util.h"
12#include "ui/events/event_utils.h"
13#include "ui/events/keycodes/keyboard_code_conversion_win.h"
14#include "ui/gfx/point.h"
15#include "ui/gfx/win/dpi.h"
16
17namespace ui {
18
19namespace {
20
21// From MSDN: "Mouse" events are flagged with 0xFF515700 if they come
22// from a touch or stylus device.  In Vista or later, they are also flagged
23// with 0x80 if they come from touch.
24#define MOUSEEVENTF_FROMTOUCH (0xFF515700 | 0x80)
25
26// Get the native mouse key state from the native event message type.
27int GetNativeMouseKey(const base::NativeEvent& native_event) {
28  switch (native_event.message) {
29    case WM_LBUTTONDBLCLK:
30    case WM_LBUTTONDOWN:
31    case WM_LBUTTONUP:
32    case WM_NCLBUTTONDBLCLK:
33    case WM_NCLBUTTONDOWN:
34    case WM_NCLBUTTONUP:
35      return MK_LBUTTON;
36    case WM_MBUTTONDBLCLK:
37    case WM_MBUTTONDOWN:
38    case WM_MBUTTONUP:
39    case WM_NCMBUTTONDBLCLK:
40    case WM_NCMBUTTONDOWN:
41    case WM_NCMBUTTONUP:
42      return MK_MBUTTON;
43    case WM_RBUTTONDBLCLK:
44    case WM_RBUTTONDOWN:
45    case WM_RBUTTONUP:
46    case WM_NCRBUTTONDBLCLK:
47    case WM_NCRBUTTONDOWN:
48    case WM_NCRBUTTONUP:
49      return MK_RBUTTON;
50    case WM_NCXBUTTONDBLCLK:
51    case WM_NCXBUTTONDOWN:
52    case WM_NCXBUTTONUP:
53    case WM_XBUTTONDBLCLK:
54    case WM_XBUTTONDOWN:
55    case WM_XBUTTONUP:
56      return MK_XBUTTON1;
57  }
58  return 0;
59}
60
61bool IsButtonDown(const base::NativeEvent& native_event) {
62  return ((MK_LBUTTON | MK_MBUTTON | MK_RBUTTON | MK_XBUTTON1 | MK_XBUTTON2) &
63          native_event.wParam) != 0;
64}
65
66bool IsClientMouseEvent(const base::NativeEvent& native_event) {
67  return native_event.message == WM_MOUSELEAVE ||
68         native_event.message == WM_MOUSEHOVER ||
69        (native_event.message >= WM_MOUSEFIRST &&
70         native_event.message <= WM_MOUSELAST);
71}
72
73bool IsNonClientMouseEvent(const base::NativeEvent& native_event) {
74  return native_event.message == WM_NCMOUSELEAVE ||
75         native_event.message == WM_NCMOUSEHOVER ||
76        (native_event.message >= WM_NCMOUSEMOVE &&
77         native_event.message <= WM_NCXBUTTONDBLCLK);
78}
79
80bool IsMouseWheelEvent(const base::NativeEvent& native_event) {
81  return native_event.message == WM_MOUSEWHEEL ||
82         native_event.message == WM_MOUSEHWHEEL;
83}
84
85bool IsKeyEvent(const base::NativeEvent& native_event) {
86  return native_event.message == WM_KEYDOWN ||
87         native_event.message == WM_SYSKEYDOWN ||
88         native_event.message == WM_CHAR ||
89         native_event.message == WM_KEYUP ||
90         native_event.message == WM_SYSKEYUP;
91}
92
93bool IsScrollEvent(const base::NativeEvent& native_event) {
94  return native_event.message == WM_VSCROLL ||
95         native_event.message == WM_HSCROLL;
96}
97
98// Returns a mask corresponding to the set of pressed modifier keys.
99// Checks the current global state and the state sent by client mouse messages.
100int KeyStateFlagsFromNative(const base::NativeEvent& native_event) {
101  int flags = 0;
102  flags |= base::win::IsAltPressed() ? EF_ALT_DOWN : EF_NONE;
103  flags |= base::win::IsShiftPressed() ? EF_SHIFT_DOWN : EF_NONE;
104  flags |= base::win::IsCtrlPressed() ? EF_CONTROL_DOWN : EF_NONE;
105
106  // Check key messages for the extended key flag.
107  if (IsKeyEvent(native_event))
108    flags |= (HIWORD(native_event.lParam) & KF_EXTENDED) ? EF_EXTENDED : 0;
109
110  // Most client mouse messages include key state information.
111  if (IsClientMouseEvent(native_event)) {
112    int win_flags = GET_KEYSTATE_WPARAM(native_event.wParam);
113    flags |= (win_flags & MK_SHIFT) ? EF_SHIFT_DOWN : 0;
114    flags |= (win_flags & MK_CONTROL) ? EF_CONTROL_DOWN : 0;
115  }
116
117  return flags;
118}
119
120// Returns a mask corresponding to the set of pressed mouse buttons.
121// This includes the button of the given message, even if it is being released.
122int MouseStateFlagsFromNative(const base::NativeEvent& native_event) {
123  int win_flags = GetNativeMouseKey(native_event);
124
125  // Client mouse messages provide key states in their WPARAMs.
126  if (IsClientMouseEvent(native_event))
127    win_flags |= GET_KEYSTATE_WPARAM(native_event.wParam);
128
129  int flags = 0;
130  flags |= (win_flags & MK_LBUTTON) ? EF_LEFT_MOUSE_BUTTON : 0;
131  flags |= (win_flags & MK_MBUTTON) ? EF_MIDDLE_MOUSE_BUTTON : 0;
132  flags |= (win_flags & MK_RBUTTON) ? EF_RIGHT_MOUSE_BUTTON : 0;
133  flags |= IsNonClientMouseEvent(native_event) ? EF_IS_NON_CLIENT : 0;
134  return flags;
135}
136
137}  // namespace
138
139void UpdateDeviceList() {
140  NOTIMPLEMENTED();
141}
142
143EventType EventTypeFromNative(const base::NativeEvent& native_event) {
144  switch (native_event.message) {
145    case WM_KEYDOWN:
146    case WM_SYSKEYDOWN:
147    case WM_CHAR:
148      return ET_KEY_PRESSED;
149    case WM_KEYUP:
150    case WM_SYSKEYUP:
151      return ET_KEY_RELEASED;
152    case WM_LBUTTONDBLCLK:
153    case WM_LBUTTONDOWN:
154    case WM_MBUTTONDBLCLK:
155    case WM_MBUTTONDOWN:
156    case WM_NCLBUTTONDBLCLK:
157    case WM_NCLBUTTONDOWN:
158    case WM_NCMBUTTONDBLCLK:
159    case WM_NCMBUTTONDOWN:
160    case WM_NCRBUTTONDBLCLK:
161    case WM_NCRBUTTONDOWN:
162    case WM_NCXBUTTONDBLCLK:
163    case WM_NCXBUTTONDOWN:
164    case WM_RBUTTONDBLCLK:
165    case WM_RBUTTONDOWN:
166    case WM_XBUTTONDBLCLK:
167    case WM_XBUTTONDOWN:
168      return ET_MOUSE_PRESSED;
169    case WM_LBUTTONUP:
170    case WM_MBUTTONUP:
171    case WM_NCLBUTTONUP:
172    case WM_NCMBUTTONUP:
173    case WM_NCRBUTTONUP:
174    case WM_NCXBUTTONUP:
175    case WM_RBUTTONUP:
176    case WM_XBUTTONUP:
177      return ET_MOUSE_RELEASED;
178    case WM_MOUSEMOVE:
179      return IsButtonDown(native_event) ? ET_MOUSE_DRAGGED : ET_MOUSE_MOVED;
180    case WM_NCMOUSEMOVE:
181      return ET_MOUSE_MOVED;
182    case WM_MOUSEWHEEL:
183    case WM_MOUSEHWHEEL:
184      return ET_MOUSEWHEEL;
185    case WM_MOUSELEAVE:
186    case WM_NCMOUSELEAVE:
187      return ET_MOUSE_EXITED;
188    case WM_VSCROLL:
189    case WM_HSCROLL:
190      return ET_SCROLL;
191    default:
192      // We can't NOTREACHED() here, since this function can be called for any
193      // message.
194      break;
195  }
196  return ET_UNKNOWN;
197}
198
199int EventFlagsFromNative(const base::NativeEvent& native_event) {
200  int flags = KeyStateFlagsFromNative(native_event);
201  if (IsMouseEvent(native_event))
202    flags |= MouseStateFlagsFromNative(native_event);
203
204  return flags;
205}
206
207base::TimeDelta EventTimeFromNative(const base::NativeEvent& native_event) {
208  return base::TimeDelta::FromMilliseconds(native_event.time);
209}
210
211gfx::Point EventLocationFromNative(const base::NativeEvent& native_event) {
212  POINT native_point;
213  if ((native_event.message == WM_MOUSELEAVE ||
214       native_event.message == WM_NCMOUSELEAVE) ||
215      IsScrollEvent(native_event)) {
216    // These events have no coordinates. For sanity with rest of events grab
217    // coordinates from the OS.
218    ::GetCursorPos(&native_point);
219  } else if (IsClientMouseEvent(native_event) &&
220             !IsMouseWheelEvent(native_event)) {
221    // Note: Wheel events are considered client, but their position is in screen
222    //       coordinates.
223    // Client message. The position is contained in the LPARAM.
224    return gfx::Point(native_event.lParam);
225  } else {
226    DCHECK(IsNonClientMouseEvent(native_event) ||
227           IsMouseWheelEvent(native_event) || IsScrollEvent(native_event));
228    // Non-client message. The position is contained in a POINTS structure in
229    // LPARAM, and is in screen coordinates so we have to convert to client.
230    native_point.x = GET_X_LPARAM(native_event.lParam);
231    native_point.y = GET_Y_LPARAM(native_event.lParam);
232  }
233  ScreenToClient(native_event.hwnd, &native_point);
234  return gfx::win::ScreenToDIPPoint(gfx::Point(native_point));
235}
236
237gfx::Point EventSystemLocationFromNative(
238    const base::NativeEvent& native_event) {
239  // TODO(ben): Needs to always return screen position here. Returning normal
240  // origin for now since that's obviously wrong.
241  return gfx::Point(0, 0);
242}
243
244KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) {
245  return KeyboardCodeForWindowsKeyCode(native_event.wParam);
246}
247
248const char* CodeFromNative(const base::NativeEvent& native_event) {
249  const uint16 scan_code = GetScanCodeFromLParam(native_event.lParam);
250  return CodeForWindowsScanCode(scan_code);
251}
252
253bool IsMouseEvent(const base::NativeEvent& native_event) {
254  return IsClientMouseEvent(native_event) ||
255         IsNonClientMouseEvent(native_event);
256}
257
258int GetChangedMouseButtonFlagsFromNative(
259    const base::NativeEvent& native_event) {
260  switch (GetNativeMouseKey(native_event)) {
261    case MK_LBUTTON:
262      return EF_LEFT_MOUSE_BUTTON;
263    case MK_MBUTTON:
264      return EF_MIDDLE_MOUSE_BUTTON;
265    case MK_RBUTTON:
266      return EF_RIGHT_MOUSE_BUTTON;
267    // TODO: add support for MK_XBUTTON1.
268    default:
269      break;
270  }
271  return 0;
272}
273
274gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) {
275  DCHECK(native_event.message == WM_MOUSEWHEEL ||
276         native_event.message == WM_MOUSEHWHEEL);
277  if (native_event.message == WM_MOUSEWHEEL)
278    return gfx::Vector2d(0, GET_WHEEL_DELTA_WPARAM(native_event.wParam));
279  return gfx::Vector2d(GET_WHEEL_DELTA_WPARAM(native_event.wParam), 0);
280}
281
282void ClearTouchIdIfReleased(const base::NativeEvent& xev) {
283  NOTIMPLEMENTED();
284}
285
286int GetTouchId(const base::NativeEvent& xev) {
287  NOTIMPLEMENTED();
288  return 0;
289}
290
291float GetTouchRadiusX(const base::NativeEvent& native_event) {
292  NOTIMPLEMENTED();
293  return 1.0;
294}
295
296float GetTouchRadiusY(const base::NativeEvent& native_event) {
297  NOTIMPLEMENTED();
298  return 1.0;
299}
300
301float GetTouchAngle(const base::NativeEvent& native_event) {
302  NOTIMPLEMENTED();
303  return 0.0;
304}
305
306float GetTouchForce(const base::NativeEvent& native_event) {
307  NOTIMPLEMENTED();
308  return 0.0;
309}
310
311bool GetScrollOffsets(const base::NativeEvent& native_event,
312                      float* x_offset,
313                      float* y_offset,
314                      float* x_offset_ordinal,
315                      float* y_offset_ordinal,
316                      int* finger_count) {
317  // TODO(ananta)
318  // Support retrieving the scroll offsets from the scroll event.
319  if (native_event.message == WM_VSCROLL || native_event.message == WM_HSCROLL)
320    return true;
321  return false;
322}
323
324bool GetFlingData(const base::NativeEvent& native_event,
325                  float* vx,
326                  float* vy,
327                  float* vx_ordinal,
328                  float* vy_ordinal,
329                  bool* is_cancel) {
330  // Not supported in Windows.
331  NOTIMPLEMENTED();
332  return false;
333}
334
335bool GetGestureTimes(const base::NativeEvent& native_event,
336                     double* start_time,
337                     double* end_time) {
338  // Not supported in Windows.
339  *start_time = 0;
340  *end_time = 0;
341  return false;
342}
343
344void SetNaturalScroll(bool enabled) {
345  NOTIMPLEMENTED();
346}
347
348bool IsNaturalScrollEnabled() {
349  NOTIMPLEMENTED();
350  return false;
351}
352
353bool IsTouchpadEvent(const base::NativeEvent& event) {
354  NOTIMPLEMENTED();
355  return false;
356}
357
358bool IsNoopEvent(const base::NativeEvent& event) {
359  return event.message == WM_USER + 310;
360}
361
362base::NativeEvent CreateNoopEvent() {
363  MSG event = { NULL };
364  event.message = WM_USER + 310;
365  return event;
366}
367
368int GetModifiersFromACCEL(const ACCEL& accel) {
369  int modifiers = EF_NONE;
370  if (accel.fVirt & FSHIFT)
371    modifiers |= EF_SHIFT_DOWN;
372  if (accel.fVirt & FCONTROL)
373    modifiers |= EF_CONTROL_DOWN;
374  if (accel.fVirt & FALT)
375    modifiers |= EF_ALT_DOWN;
376  return modifiers;
377}
378
379int GetModifiersFromKeyState() {
380  int modifiers = EF_NONE;
381  if (base::win::IsShiftPressed())
382    modifiers |= EF_SHIFT_DOWN;
383  if (base::win::IsCtrlPressed())
384    modifiers |= EF_CONTROL_DOWN;
385  if (base::win::IsAltPressed())
386    modifiers |= EF_ALT_DOWN;
387  if (base::win::IsAltGrPressed())
388    modifiers |= EF_ALTGR_DOWN;
389  return modifiers;
390}
391
392// Windows emulates mouse messages for touch events.
393bool IsMouseEventFromTouch(UINT message) {
394  return (message >= WM_MOUSEFIRST) && (message <= WM_MOUSELAST) &&
395      (GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) ==
396      MOUSEEVENTF_FROMTOUCH;
397}
398
399// Conversion scan_code and LParam each other.
400// uint16 scan_code:
401//     ui/events/keycodes/dom4/keycode_converter_data.h
402// 0 - 15bits: represetns the scan code.
403// 28 - 30 bits (0xE000): represents whether this is an extended key or not.
404//
405// LPARAM lParam:
406//     http://msdn.microsoft.com/en-us/library/windows/desktop/ms644984.aspx
407// 16 - 23bits: represetns the scan code.
408// 24bit (0x0100): represents whether this is an extended key or not.
409uint16 GetScanCodeFromLParam(LPARAM l_param) {
410  uint16 scan_code = ((l_param >> 16) & 0x00FF);
411  if (l_param & (1 << 24))
412    scan_code |= 0xE000;
413  return scan_code;
414}
415
416LPARAM GetLParamFromScanCode(uint16 scan_code) {
417  LPARAM l_param = static_cast<LPARAM>(scan_code & 0x00FF) << 16;
418  if ((scan_code & 0xE000) == 0xE000)
419    l_param |= (1 << 24);
420  return l_param;
421}
422
423}  // namespace ui
424