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