OLD | NEW |
| (Empty) |
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 "ui/aura/remote_window_tree_host_win.h" | |
6 | |
7 #include <windows.h> | |
8 #include <stddef.h> | |
9 | |
10 #include <algorithm> | |
11 | |
12 #include "base/message_loop/message_loop.h" | |
13 #include "ipc/ipc_message.h" | |
14 #include "ipc/ipc_sender.h" | |
15 #include "ui/aura/client/cursor_client.h" | |
16 #include "ui/aura/window_event_dispatcher.h" | |
17 #include "ui/aura/window_property.h" | |
18 #include "ui/base/cursor/cursor_loader_win.h" | |
19 #include "ui/base/ime/composition_text.h" | |
20 #include "ui/base/ime/input_method.h" | |
21 #include "ui/base/ime/remote_input_method_win.h" | |
22 #include "ui/base/ime/text_input_client.h" | |
23 #include "ui/base/view_prop.h" | |
24 #include "ui/events/event_utils.h" | |
25 #include "ui/events/keycodes/keyboard_code_conversion_win.h" | |
26 #include "ui/gfx/geometry/insets.h" | |
27 #include "ui/gfx/win/dpi.h" | |
28 #include "ui/metro_viewer/metro_viewer_messages.h" | |
29 | |
30 namespace aura { | |
31 | |
32 namespace { | |
33 | |
34 const char* kWindowTreeHostWinKey = "__AURA_REMOTE_WINDOW_TREE_HOST_WIN__"; | |
35 | |
36 // Sets the keystate for the virtual key passed in to down or up. | |
37 void SetKeyState(uint8_t* key_states, | |
38 bool key_down, | |
39 uint32_t virtual_key_code) { | |
40 DCHECK(key_states); | |
41 | |
42 if (key_down) | |
43 key_states[virtual_key_code] |= 0x80; | |
44 else | |
45 key_states[virtual_key_code] &= 0x7F; | |
46 } | |
47 | |
48 // Sets the keyboard states for the Shift/Control/Alt/Caps lock keys. | |
49 void SetVirtualKeyStates(uint32_t flags) { | |
50 uint8_t keyboard_state[256] = {0}; | |
51 ::GetKeyboardState(keyboard_state); | |
52 | |
53 SetKeyState(keyboard_state, !!(flags & ui::EF_SHIFT_DOWN), VK_SHIFT); | |
54 SetKeyState(keyboard_state, !!(flags & ui::EF_CONTROL_DOWN), VK_CONTROL); | |
55 SetKeyState(keyboard_state, !!(flags & ui::EF_ALT_DOWN), VK_MENU); | |
56 SetKeyState(keyboard_state, !!(flags & ui::EF_CAPS_LOCK_ON), VK_CAPITAL); | |
57 SetKeyState(keyboard_state, !!(flags & ui::EF_LEFT_MOUSE_BUTTON), VK_LBUTTON); | |
58 SetKeyState(keyboard_state, !!(flags & ui::EF_RIGHT_MOUSE_BUTTON), | |
59 VK_RBUTTON); | |
60 SetKeyState(keyboard_state, !!(flags & ui::EF_MIDDLE_MOUSE_BUTTON), | |
61 VK_MBUTTON); | |
62 | |
63 ::SetKeyboardState(keyboard_state); | |
64 } | |
65 | |
66 void FillCompositionText( | |
67 const base::string16& text, | |
68 int32_t selection_start, | |
69 int32_t selection_end, | |
70 const std::vector<metro_viewer::UnderlineInfo>& underlines, | |
71 ui::CompositionText* composition_text) { | |
72 composition_text->Clear(); | |
73 composition_text->text = text; | |
74 composition_text->selection.set_start(selection_start); | |
75 composition_text->selection.set_end(selection_end); | |
76 composition_text->underlines.resize(underlines.size()); | |
77 for (size_t i = 0; i < underlines.size(); ++i) { | |
78 composition_text->underlines[i].start_offset = underlines[i].start_offset; | |
79 composition_text->underlines[i].end_offset = underlines[i].end_offset; | |
80 composition_text->underlines[i].color = SK_ColorBLACK; | |
81 composition_text->underlines[i].thick = underlines[i].thick; | |
82 composition_text->underlines[i].background_color = SK_ColorTRANSPARENT; | |
83 } | |
84 } | |
85 | |
86 } // namespace | |
87 | |
88 RemoteWindowTreeHostWin* g_instance = NULL; | |
89 | |
90 // static | |
91 RemoteWindowTreeHostWin* RemoteWindowTreeHostWin::Instance() { | |
92 return g_instance; | |
93 } | |
94 | |
95 RemoteWindowTreeHostWin::RemoteWindowTreeHostWin() | |
96 : remote_window_(NULL), | |
97 host_(NULL), | |
98 ignore_mouse_moves_until_set_cursor_ack_(0), | |
99 event_flags_(0), | |
100 window_size_(GetSystemMetrics(SM_CXSCREEN), | |
101 GetSystemMetrics(SM_CYSCREEN)) { | |
102 CHECK(!g_instance); | |
103 g_instance = this; | |
104 prop_.reset(new ui::ViewProp(NULL, kWindowTreeHostWinKey, this)); | |
105 CreateCompositor(); | |
106 OnAcceleratedWidgetAvailable(); | |
107 } | |
108 | |
109 RemoteWindowTreeHostWin::~RemoteWindowTreeHostWin() { | |
110 DestroyCompositor(); | |
111 DestroyDispatcher(); | |
112 DCHECK_EQ(g_instance, this); | |
113 g_instance = NULL; | |
114 } | |
115 | |
116 // static | |
117 bool RemoteWindowTreeHostWin::IsValid() { | |
118 return Instance()->remote_window_ != NULL; | |
119 } | |
120 | |
121 void RemoteWindowTreeHostWin::SetRemoteWindowHandle(HWND remote_window) { | |
122 remote_window_ = remote_window; | |
123 } | |
124 | |
125 void RemoteWindowTreeHostWin::Connected(IPC::Sender* host) { | |
126 CHECK(host_ == NULL); | |
127 DCHECK(remote_window_); | |
128 host_ = host; | |
129 // Recreate the compositor for the target surface represented by the | |
130 // remote_window HWND. | |
131 CreateCompositor(); | |
132 OnAcceleratedWidgetAvailable(); | |
133 InitCompositor(); | |
134 } | |
135 | |
136 void RemoteWindowTreeHostWin::Disconnected() { | |
137 // Don't CHECK here, Disconnected is called on a channel error which can | |
138 // happen before we're successfully Connected. | |
139 if (!host_) | |
140 return; | |
141 ui::RemoteInputMethodPrivateWin* remote_input_method_private = | |
142 GetRemoteInputMethodPrivate(); | |
143 if (remote_input_method_private) | |
144 remote_input_method_private->SetRemoteDelegate(NULL); | |
145 host_ = NULL; | |
146 remote_window_ = NULL; | |
147 } | |
148 | |
149 bool RemoteWindowTreeHostWin::OnMessageReceived(const IPC::Message& message) { | |
150 bool handled = true; | |
151 IPC_BEGIN_MESSAGE_MAP(RemoteWindowTreeHostWin, message) | |
152 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MouseMoved, OnMouseMoved) | |
153 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MouseButton, OnMouseButton) | |
154 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_KeyDown, OnKeyDown) | |
155 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_KeyUp, OnKeyUp) | |
156 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_Character, OnChar) | |
157 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_WindowActivated, | |
158 OnWindowActivated) | |
159 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_EdgeGesture, OnEdgeGesture) | |
160 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchDown, | |
161 OnTouchDown) | |
162 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchUp, | |
163 OnTouchUp) | |
164 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchMoved, | |
165 OnTouchMoved) | |
166 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursorPosAck, | |
167 OnSetCursorPosAck) | |
168 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeCandidatePopupChanged, | |
169 OnImeCandidatePopupChanged) | |
170 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeCompositionChanged, | |
171 OnImeCompositionChanged) | |
172 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeTextCommitted, | |
173 OnImeTextCommitted) | |
174 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeInputSourceChanged, | |
175 OnImeInputSourceChanged) | |
176 IPC_MESSAGE_UNHANDLED(handled = false) | |
177 IPC_END_MESSAGE_MAP() | |
178 return handled; | |
179 } | |
180 | |
181 void RemoteWindowTreeHostWin::HandleOpenURLOnDesktop( | |
182 const base::FilePath& shortcut, | |
183 const base::string16& url) { | |
184 if (!host_) | |
185 return; | |
186 host_->Send(new MetroViewerHostMsg_OpenURLOnDesktop(shortcut, url)); | |
187 } | |
188 | |
189 void RemoteWindowTreeHostWin::HandleWindowSizeChanged(uint32_t width, | |
190 uint32_t height) { | |
191 SetBounds(gfx::Rect(0, 0, width, height)); | |
192 } | |
193 | |
194 bool RemoteWindowTreeHostWin::IsForegroundWindow() { | |
195 return ::GetForegroundWindow() == remote_window_; | |
196 } | |
197 | |
198 Window* RemoteWindowTreeHostWin::GetAshWindow() { | |
199 return window(); | |
200 } | |
201 | |
202 ui::EventSource* RemoteWindowTreeHostWin::GetEventSource() { | |
203 return this; | |
204 } | |
205 | |
206 gfx::AcceleratedWidget RemoteWindowTreeHostWin::GetAcceleratedWidget() { | |
207 if (remote_window_) | |
208 return remote_window_; | |
209 // Getting here should only happen for ash_unittests.exe and related code. | |
210 return ::GetDesktopWindow(); | |
211 } | |
212 | |
213 void RemoteWindowTreeHostWin::ShowImpl() { | |
214 ui::RemoteInputMethodPrivateWin* remote_input_method_private = | |
215 GetRemoteInputMethodPrivate(); | |
216 if (remote_input_method_private) | |
217 remote_input_method_private->SetRemoteDelegate(this); | |
218 } | |
219 | |
220 void RemoteWindowTreeHostWin::HideImpl() { | |
221 NOTIMPLEMENTED(); | |
222 } | |
223 | |
224 gfx::Rect RemoteWindowTreeHostWin::GetBounds() const { | |
225 return gfx::Rect(window_size_); | |
226 } | |
227 | |
228 void RemoteWindowTreeHostWin::SetBounds(const gfx::Rect& bounds) { | |
229 window_size_ = bounds.size(); | |
230 OnHostResized(bounds.size()); | |
231 } | |
232 | |
233 gfx::Point RemoteWindowTreeHostWin::GetLocationOnNativeScreen() const { | |
234 return gfx::Point(0, 0); | |
235 } | |
236 | |
237 void RemoteWindowTreeHostWin::SetCapture() { | |
238 } | |
239 | |
240 void RemoteWindowTreeHostWin::ReleaseCapture() { | |
241 } | |
242 | |
243 void RemoteWindowTreeHostWin::SetCursorNative(gfx::NativeCursor native_cursor) { | |
244 if (!host_) | |
245 return; | |
246 host_->Send( | |
247 new MetroViewerHostMsg_SetCursor(uint64_t(native_cursor.platform()))); | |
248 } | |
249 | |
250 void RemoteWindowTreeHostWin::MoveCursorToNative(const gfx::Point& location) { | |
251 VLOG(1) << "In MoveCursorTo: " << location.x() << ", " << location.y(); | |
252 if (!host_) | |
253 return; | |
254 | |
255 // This function can be called in cases like when the mouse cursor is | |
256 // restricted within a viewport (For e.g. LockCursor) which assumes that | |
257 // subsequent mouse moves would be received starting with the new cursor | |
258 // coordinates. This is a challenge for Windows ASH for the reasons | |
259 // outlined below. | |
260 // Other cases which don't expect this behavior should continue to work | |
261 // without issues. | |
262 | |
263 // The mouse events are received by the viewer process and sent to the | |
264 // browser. If we invoke the SetCursor API here we continue to receive | |
265 // mouse messages from the viewer which were posted before the SetCursor | |
266 // API executes which messes up the state in the browser. To workaround | |
267 // this we invoke the SetCursor API in the viewer process and ignore | |
268 // mouse messages until we received an ACK from the viewer indicating that | |
269 // the SetCursor operation completed. | |
270 ignore_mouse_moves_until_set_cursor_ack_++; | |
271 VLOG(1) << "In MoveCursorTo. Sending IPC"; | |
272 host_->Send(new MetroViewerHostMsg_SetCursorPos(location.x(), location.y())); | |
273 } | |
274 | |
275 void RemoteWindowTreeHostWin::OnCursorVisibilityChangedNative(bool show) { | |
276 NOTIMPLEMENTED(); | |
277 } | |
278 | |
279 void RemoteWindowTreeHostWin::CancelComposition() { | |
280 if (!host_) | |
281 return; | |
282 host_->Send(new MetroViewerHostMsg_ImeCancelComposition); | |
283 } | |
284 | |
285 void RemoteWindowTreeHostWin::OnTextInputClientUpdated( | |
286 const std::vector<int32_t>& input_scopes, | |
287 const std::vector<gfx::Rect>& composition_character_bounds) { | |
288 if (!host_) | |
289 return; | |
290 std::vector<metro_viewer::CharacterBounds> character_bounds; | |
291 for (size_t i = 0; i < composition_character_bounds.size(); ++i) { | |
292 const gfx::Rect& rect = composition_character_bounds[i]; | |
293 metro_viewer::CharacterBounds bounds; | |
294 bounds.left = rect.x(); | |
295 bounds.top = rect.y(); | |
296 bounds.right = rect.right(); | |
297 bounds.bottom = rect.bottom(); | |
298 character_bounds.push_back(bounds); | |
299 } | |
300 host_->Send(new MetroViewerHostMsg_ImeTextInputClientUpdated( | |
301 input_scopes, character_bounds)); | |
302 } | |
303 | |
304 gfx::Point PointFromNativeEvent(int32_t x, int32_t y) { | |
305 static float scale_factor = gfx::GetDPIScale(); | |
306 gfx::Point result( x * scale_factor, y * scale_factor); | |
307 return result; | |
308 } | |
309 | |
310 void RemoteWindowTreeHostWin::OnMouseMoved(int32_t x, | |
311 int32_t y, | |
312 int32_t flags) { | |
313 if (ignore_mouse_moves_until_set_cursor_ack_) | |
314 return; | |
315 | |
316 gfx::Point location = PointFromNativeEvent(x, y); | |
317 ui::MouseEvent event(ui::ET_MOUSE_MOVED, location, location, | |
318 ui::EventTimeForNow(), flags, 0); | |
319 SendEventToProcessor(&event); | |
320 } | |
321 | |
322 void RemoteWindowTreeHostWin::OnMouseButton( | |
323 const MetroViewerHostMsg_MouseButtonParams& params) { | |
324 gfx::Point location = PointFromNativeEvent(params.x, params.y); | |
325 ui::MouseEvent mouse_event( | |
326 params.event_type, location, location, ui::EventTimeForNow(), | |
327 static_cast<int>(params.flags), static_cast<int>(params.changed_button)); | |
328 | |
329 SetEventFlags(params.flags | key_event_flags()); | |
330 if (params.event_type == ui::ET_MOUSEWHEEL) { | |
331 int x_offset = params.is_horizontal_wheel ? params.extra : 0; | |
332 int y_offset = !params.is_horizontal_wheel ? params.extra : 0; | |
333 ui::MouseWheelEvent wheel_event(mouse_event, x_offset, y_offset); | |
334 SendEventToProcessor(&wheel_event); | |
335 } else if (params.event_type == ui::ET_MOUSE_PRESSED) { | |
336 // TODO(shrikant): Ideally modify code in event.cc by adding automatic | |
337 // tracking of double clicks in synthetic MouseEvent constructor code. | |
338 // Non-synthetic MouseEvent constructor code does automatically track | |
339 // this. Need to use some caution while modifying synthetic constructor | |
340 // as many tests and other code paths depend on it and apparently | |
341 // specifically depend on non implicit tracking of previous mouse event. | |
342 if (last_mouse_click_event_ && | |
343 ui::MouseEvent::IsRepeatedClickEvent(mouse_event, | |
344 *last_mouse_click_event_)) { | |
345 mouse_event.SetClickCount(2); | |
346 } else { | |
347 mouse_event.SetClickCount(1); | |
348 } | |
349 last_mouse_click_event_ .reset(new ui::MouseEvent(mouse_event)); | |
350 SendEventToProcessor(&mouse_event); | |
351 } else { | |
352 SendEventToProcessor(&mouse_event); | |
353 } | |
354 } | |
355 | |
356 void RemoteWindowTreeHostWin::OnKeyDown(uint32_t vkey, | |
357 uint32_t repeat_count, | |
358 uint32_t scan_code, | |
359 uint32_t flags) { | |
360 DispatchKeyboardMessage(ui::ET_KEY_PRESSED, vkey, repeat_count, scan_code, | |
361 flags, false); | |
362 } | |
363 | |
364 void RemoteWindowTreeHostWin::OnKeyUp(uint32_t vkey, | |
365 uint32_t repeat_count, | |
366 uint32_t scan_code, | |
367 uint32_t flags) { | |
368 DispatchKeyboardMessage(ui::ET_KEY_RELEASED, vkey, repeat_count, scan_code, | |
369 flags, false); | |
370 } | |
371 | |
372 void RemoteWindowTreeHostWin::OnChar(uint32_t key_code, | |
373 uint32_t repeat_count, | |
374 uint32_t scan_code, | |
375 uint32_t flags) { | |
376 DispatchKeyboardMessage(ui::ET_KEY_PRESSED, key_code, repeat_count, | |
377 scan_code, flags, true); | |
378 } | |
379 | |
380 void RemoteWindowTreeHostWin::OnWindowActivated(bool repaint) { | |
381 OnHostActivated(); | |
382 if (repaint && compositor()) | |
383 compositor()->ScheduleFullRedraw(); | |
384 } | |
385 | |
386 void RemoteWindowTreeHostWin::OnEdgeGesture() { | |
387 ui::GestureEvent event( | |
388 0, | |
389 0, | |
390 0, | |
391 ui::EventTimeForNow(), | |
392 ui::GestureEventDetails(ui::ET_GESTURE_WIN8_EDGE_SWIPE)); | |
393 SendEventToProcessor(&event); | |
394 } | |
395 | |
396 void RemoteWindowTreeHostWin::OnTouchDown(int32_t x, | |
397 int32_t y, | |
398 uint64_t timestamp, | |
399 uint32_t pointer_id) { | |
400 gfx::Point location = PointFromNativeEvent(x, y); | |
401 ui::TouchEvent event(ui::ET_TOUCH_PRESSED, | |
402 location, | |
403 pointer_id, | |
404 base::TimeDelta::FromMicroseconds(timestamp)); | |
405 SendEventToProcessor(&event); | |
406 } | |
407 | |
408 void RemoteWindowTreeHostWin::OnTouchUp(int32_t x, | |
409 int32_t y, | |
410 uint64_t timestamp, | |
411 uint32_t pointer_id) { | |
412 gfx::Point location = PointFromNativeEvent(x, y); | |
413 ui::TouchEvent event(ui::ET_TOUCH_RELEASED, | |
414 location, | |
415 pointer_id, | |
416 base::TimeDelta::FromMicroseconds(timestamp)); | |
417 SendEventToProcessor(&event); | |
418 } | |
419 | |
420 void RemoteWindowTreeHostWin::OnTouchMoved(int32_t x, | |
421 int32_t y, | |
422 uint64_t timestamp, | |
423 uint32_t pointer_id) { | |
424 gfx::Point location = PointFromNativeEvent(x, y); | |
425 ui::TouchEvent event(ui::ET_TOUCH_MOVED, | |
426 location, | |
427 pointer_id, | |
428 base::TimeDelta::FromMicroseconds(timestamp)); | |
429 SendEventToProcessor(&event); | |
430 } | |
431 | |
432 void RemoteWindowTreeHostWin::OnSetCursorPosAck() { | |
433 DCHECK_GT(ignore_mouse_moves_until_set_cursor_ack_, 0); | |
434 ignore_mouse_moves_until_set_cursor_ack_--; | |
435 } | |
436 | |
437 ui::RemoteInputMethodPrivateWin* | |
438 RemoteWindowTreeHostWin::GetRemoteInputMethodPrivate() { | |
439 return ui::RemoteInputMethodPrivateWin::Get(GetInputMethod()); | |
440 } | |
441 | |
442 void RemoteWindowTreeHostWin::OnImeCandidatePopupChanged(bool visible) { | |
443 ui::RemoteInputMethodPrivateWin* remote_input_method_private = | |
444 GetRemoteInputMethodPrivate(); | |
445 if (!remote_input_method_private) | |
446 return; | |
447 remote_input_method_private->OnCandidatePopupChanged(visible); | |
448 } | |
449 | |
450 void RemoteWindowTreeHostWin::OnImeCompositionChanged( | |
451 const base::string16& text, | |
452 int32_t selection_start, | |
453 int32_t selection_end, | |
454 const std::vector<metro_viewer::UnderlineInfo>& underlines) { | |
455 ui::RemoteInputMethodPrivateWin* remote_input_method_private = | |
456 GetRemoteInputMethodPrivate(); | |
457 if (!remote_input_method_private) | |
458 return; | |
459 ui::CompositionText composition_text; | |
460 FillCompositionText( | |
461 text, selection_start, selection_end, underlines, &composition_text); | |
462 remote_input_method_private->OnCompositionChanged(composition_text); | |
463 } | |
464 | |
465 void RemoteWindowTreeHostWin::OnImeTextCommitted(const base::string16& text) { | |
466 ui::RemoteInputMethodPrivateWin* remote_input_method_private = | |
467 GetRemoteInputMethodPrivate(); | |
468 if (!remote_input_method_private) | |
469 return; | |
470 remote_input_method_private->OnTextCommitted(text); | |
471 } | |
472 | |
473 void RemoteWindowTreeHostWin::OnImeInputSourceChanged(uint16_t language_id, | |
474 bool is_ime) { | |
475 ui::RemoteInputMethodPrivateWin* remote_input_method_private = | |
476 GetRemoteInputMethodPrivate(); | |
477 if (!remote_input_method_private) | |
478 return; | |
479 remote_input_method_private->OnInputSourceChanged(language_id, is_ime); | |
480 } | |
481 | |
482 void RemoteWindowTreeHostWin::DispatchKeyboardMessage(ui::EventType type, | |
483 uint32_t vkey, | |
484 uint32_t repeat_count, | |
485 uint32_t scan_code, | |
486 uint32_t flags, | |
487 bool is_character) { | |
488 SetEventFlags(flags | mouse_event_flags()); | |
489 if (base::MessageLoop::current()->IsNested()) { | |
490 int index = (flags & ui::EF_ALT_DOWN) ? 1 : 0; | |
491 const int char_message[] = {WM_CHAR, WM_SYSCHAR}; | |
492 const int keydown_message[] = {WM_KEYDOWN, WM_SYSKEYDOWN}; | |
493 const int keyup_message[] = {WM_KEYUP, WM_SYSKEYUP}; | |
494 uint32_t message = | |
495 is_character ? char_message[index] | |
496 : (type == ui::ET_KEY_PRESSED ? keydown_message[index] | |
497 : keyup_message[index]); | |
498 ::PostThreadMessage(::GetCurrentThreadId(), | |
499 message, | |
500 vkey, | |
501 repeat_count | scan_code >> 15); | |
502 } else if (is_character) { | |
503 ui::KeyEvent event(static_cast<base::char16>(vkey), | |
504 ui::KeyboardCodeForWindowsKeyCode(vkey), | |
505 flags); | |
506 SendEventToProcessor(&event); | |
507 } else { | |
508 ui::KeyEvent event(type, | |
509 ui::KeyboardCodeForWindowsKeyCode(vkey), | |
510 flags); | |
511 SendEventToProcessor(&event); | |
512 } | |
513 } | |
514 | |
515 void RemoteWindowTreeHostWin::SetEventFlags(uint32_t flags) { | |
516 if (flags == event_flags_) | |
517 return; | |
518 event_flags_ = flags; | |
519 SetVirtualKeyStates(event_flags_); | |
520 } | |
521 | |
522 } // namespace aura | |
OLD | NEW |