0032638: Draw Harness, ViewerTest - HTML input range misbehavior in WebAssembly
[occt.git] / src / Wasm / Wasm_Window.cxx
1 // Created by: Kirill Gavrilov
2 // Copyright (c) 2021 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #include <Wasm_Window.hxx>
16
17 #include <Aspect_ScrollDelta.hxx>
18 #include <Aspect_WindowInputListener.hxx>
19
20 #if defined(__EMSCRIPTEN__)
21   #include <emscripten.h>
22   #include <emscripten/html5.h>
23   #include <emscripten/key_codes.h>
24 #endif
25
26 IMPLEMENT_STANDARD_RTTIEXT(Wasm_Window, Aspect_Window)
27
28 // =======================================================================
29 // function : Wasm_Window
30 // purpose  :
31 // =======================================================================
32 Wasm_Window::Wasm_Window (const TCollection_AsciiString& theCanvasId,
33                           const bool theToScaleBacking)
34 : myCanvasId (theCanvasId),
35   mySize (0),
36   myDevicePixelRatio (1.0),
37   myToScaleBacking (theToScaleBacking),
38   myIsMapped (true)
39 {
40 #if defined(__EMSCRIPTEN__)
41   myDevicePixelRatio = emscripten_get_device_pixel_ratio();
42   emscripten_get_canvas_element_size (myCanvasId.ToCString(), &mySize.x(), &mySize.y());
43   if (myToScaleBacking)
44   {
45     myDevicePixelRatio = emscripten_get_device_pixel_ratio();
46     Graphic3d_Vec2d aCssSize;
47     emscripten_get_element_css_size (myCanvasId.ToCString(), &aCssSize.x(), &aCssSize.y());
48     Graphic3d_Vec2i aCanvasSize = Graphic3d_Vec2i (aCssSize * myDevicePixelRatio);
49     if (aCanvasSize != mySize)
50     {
51       mySize = aCanvasSize;
52       emscripten_set_canvas_element_size (myCanvasId.ToCString(), aCanvasSize.x(), aCanvasSize.y());
53       emscripten_set_element_css_size    (myCanvasId.ToCString(), aCssSize.x(),    aCssSize.y());
54     }
55   }
56 #endif
57 }
58
59 // =======================================================================
60 // function : ~Wasm_Window
61 // purpose  :
62 // =======================================================================
63 Wasm_Window::~Wasm_Window()
64 {
65   //
66 }
67
68 // =======================================================================
69 // function : DoResize
70 // purpose  :
71 // =======================================================================
72 Aspect_TypeOfResize Wasm_Window::DoResize()
73 {
74   if (IsVirtual())
75   {
76     return Aspect_TOR_UNKNOWN;
77   }
78
79 #if defined(__EMSCRIPTEN__)
80   emscripten_get_canvas_element_size (myCanvasId.ToCString(), &mySize.x(), &mySize.y());
81   if (myToScaleBacking)
82   {
83     myDevicePixelRatio = emscripten_get_device_pixel_ratio();
84     Graphic3d_Vec2d aCssSize;
85     emscripten_get_element_css_size (myCanvasId.ToCString(), &aCssSize.x(), &aCssSize.y());
86     Graphic3d_Vec2i aCanvasSize = Graphic3d_Vec2i (aCssSize * myDevicePixelRatio);
87     if (aCanvasSize != mySize)
88     {
89       mySize = aCanvasSize;
90       emscripten_set_canvas_element_size (myCanvasId.ToCString(), aCanvasSize.x(), aCanvasSize.y());
91       emscripten_set_element_css_size    (myCanvasId.ToCString(), aCssSize.x(),    aCssSize.y());
92     }
93   }
94 #endif
95   return Aspect_TOR_UNKNOWN;
96 }
97
98 // =======================================================================
99 // function : Ratio
100 // purpose  :
101 // =======================================================================
102 Standard_Real Wasm_Window::Ratio() const
103 {
104   Graphic3d_Vec2i aCanvasSize = mySize;
105   if (!IsVirtual())
106   {
107   #if defined(__EMSCRIPTEN__)
108     emscripten_get_canvas_element_size (myCanvasId.ToCString(), &aCanvasSize.x(), &aCanvasSize.y());
109   #endif
110   }
111
112   return (aCanvasSize.x() != 0 && aCanvasSize.y() != 0)
113         ? Standard_Real(aCanvasSize.x()) / Standard_Real(aCanvasSize.y())
114         : 1.0;
115 }
116
117 // =======================================================================
118 // function : Position
119 // purpose  :
120 // =======================================================================
121 void Wasm_Window::Position (Standard_Integer& theX1, Standard_Integer& theY1,
122                             Standard_Integer& theX2, Standard_Integer& theY2) const
123 {
124   theX1 = 0;
125   theY1 = 0;
126   if (IsVirtual())
127   {
128     theX2 = mySize.x();
129     theY2 = mySize.y();
130     return;
131   }
132
133 #if defined(__EMSCRIPTEN__)
134   emscripten_get_canvas_element_size (myCanvasId.ToCString(), &theX2, &theY2);
135 #endif
136 }
137
138 // =======================================================================
139 // function : Size
140 // purpose  :
141 // =======================================================================
142 void Wasm_Window::Size (Standard_Integer& theWidth,
143                         Standard_Integer& theHeight) const
144 {
145   if (IsVirtual())
146   {
147     theWidth  = mySize.x();
148     theHeight = mySize.y();
149     return;
150   }
151
152 #if defined(__EMSCRIPTEN__)
153   emscripten_get_canvas_element_size (myCanvasId.ToCString(), &theWidth, &theHeight);
154 #endif
155 }
156
157 // =======================================================================
158 // function : SetSizeLogical
159 // purpose  :
160 // =======================================================================
161 void Wasm_Window::SetSizeLogical (const Graphic3d_Vec2d& theSize)
162 {
163   mySize = Graphic3d_Vec2i (theSize * myDevicePixelRatio);
164   if (IsVirtual())
165   {
166     return;
167   }
168
169 #if defined(__EMSCRIPTEN__)
170   emscripten_set_canvas_element_size (myCanvasId.ToCString(), mySize.x(),  mySize.y());
171   emscripten_set_element_css_size    (myCanvasId.ToCString(), theSize.x(), theSize.y());
172 #endif
173 }
174
175 // =======================================================================
176 // function : SetSizeBacking
177 // purpose  :
178 // =======================================================================
179 void Wasm_Window::SetSizeBacking (const Graphic3d_Vec2i& theSize)
180 {
181   mySize = theSize;
182   if (IsVirtual())
183   {
184     return;
185   }
186
187 #if defined(__EMSCRIPTEN__)
188   Graphic3d_Vec2i aCanvasSize = mySize;
189   Graphic3d_Vec2d aCssSize = Graphic3d_Vec2d (mySize) / myDevicePixelRatio;
190   emscripten_set_canvas_element_size (myCanvasId.ToCString(), aCanvasSize.x(), aCanvasSize.y());
191   emscripten_set_element_css_size    (myCanvasId.ToCString(), aCssSize.x(),    aCssSize.y());
192 #endif
193 }
194
195 // =======================================================================
196 // function : InvalidateContent
197 // purpose  :
198 // =======================================================================
199 void Wasm_Window::InvalidateContent (const Handle(Aspect_DisplayConnection)& )
200 {
201   //
202 }
203
204 // =======================================================================
205 // function : ProcessMessage
206 // purpose  :
207 // =======================================================================
208 bool Wasm_Window::ProcessMessage (Aspect_WindowInputListener& theListener,
209                                   int theEventType, const void* theEvent)
210 {
211 #if defined(__EMSCRIPTEN__)
212   switch (theEventType)
213   {
214     case EMSCRIPTEN_EVENT_MOUSEMOVE:
215     case EMSCRIPTEN_EVENT_MOUSEDOWN:
216     case EMSCRIPTEN_EVENT_MOUSEUP:
217     case EMSCRIPTEN_EVENT_CLICK:
218     case EMSCRIPTEN_EVENT_DBLCLICK:
219     case EMSCRIPTEN_EVENT_MOUSEENTER:
220     case EMSCRIPTEN_EVENT_MOUSELEAVE:
221     {
222       return ProcessMouseEvent (theListener, theEventType, (const EmscriptenMouseEvent* )theEvent);
223     }
224     case EMSCRIPTEN_EVENT_TOUCHSTART:
225     case EMSCRIPTEN_EVENT_TOUCHMOVE:
226     case EMSCRIPTEN_EVENT_TOUCHEND:
227     case EMSCRIPTEN_EVENT_TOUCHCANCEL:
228     {
229       return ProcessTouchEvent (theListener, theEventType, (const EmscriptenTouchEvent* )theEvent);
230     }
231     case EMSCRIPTEN_EVENT_WHEEL:
232     {
233       return ProcessWheelEvent (theListener, theEventType, (const EmscriptenWheelEvent* )theEvent);
234     }
235     case EMSCRIPTEN_EVENT_KEYDOWN:
236     case EMSCRIPTEN_EVENT_KEYUP:
237     case EMSCRIPTEN_EVENT_KEYPRESS:
238     {
239       return ProcessKeyEvent (theListener, theEventType, (const EmscriptenKeyboardEvent* )theEvent);
240     }
241     case EMSCRIPTEN_EVENT_RESIZE:
242     case EMSCRIPTEN_EVENT_CANVASRESIZED:
243     {
244       return ProcessUiEvent (theListener, theEventType, (const EmscriptenUiEvent* )theEvent);
245     }
246     case EMSCRIPTEN_EVENT_FOCUS:
247     case EMSCRIPTEN_EVENT_FOCUSIN:
248     case EMSCRIPTEN_EVENT_FOCUSOUT:
249     {
250       return ProcessFocusEvent (theListener, theEventType, (const EmscriptenFocusEvent* )theEvent);
251     }
252   }
253   return false;
254 #else
255   (void )theListener;
256   (void )theEventType;
257   (void )theEvent;
258   return false;
259 #endif
260 }
261
262 // =======================================================================
263 // function : ProcessMouseEvent
264 // purpose  :
265 // =======================================================================
266 bool Wasm_Window::ProcessMouseEvent (Aspect_WindowInputListener& theListener,
267                                      int theEventType, const EmscriptenMouseEvent* theEvent)
268 {
269 #if defined(__EMSCRIPTEN__)
270   const Graphic3d_Vec2d aNewPos2d = ConvertPointToBacking (Graphic3d_Vec2d (theEvent->targetX, theEvent->targetY));
271   const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i (aNewPos2d + Graphic3d_Vec2d (0.5));
272   Aspect_VKeyFlags aFlags = 0;
273   if (theEvent->ctrlKey  == EM_TRUE) { aFlags |= Aspect_VKeyFlags_CTRL;  }
274   if (theEvent->shiftKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_SHIFT; }
275   if (theEvent->altKey   == EM_TRUE) { aFlags |= Aspect_VKeyFlags_ALT;   }
276   if (theEvent->metaKey  == EM_TRUE) { aFlags |= Aspect_VKeyFlags_META;  }
277
278   const bool isEmulated = false;
279   const Aspect_VKeyMouse aButtons = Wasm_Window::MouseButtonsFromNative (theEvent->buttons);
280   switch (theEventType)
281   {
282     case EMSCRIPTEN_EVENT_MOUSEMOVE:
283     {
284       if ((aNewPos2i.x() < 0 || aNewPos2i.x() > mySize.x()
285         || aNewPos2i.y() < 0 || aNewPos2i.y() > mySize.y())
286         && theListener.PressedMouseButtons() == Aspect_VKeyMouse_NONE)
287       {
288         return false;
289       }
290       if (theListener.UpdateMousePosition (aNewPos2i, aButtons, aFlags, isEmulated))
291       {
292         theListener.ProcessInput();
293       }
294       break;
295     }
296     case EMSCRIPTEN_EVENT_MOUSEDOWN:
297     case EMSCRIPTEN_EVENT_MOUSEUP:
298     {
299       if (theEventType == EMSCRIPTEN_EVENT_MOUSEDOWN)
300       {
301         if (aNewPos2i.x() < 0 || aNewPos2i.x() > mySize.x()
302          || aNewPos2i.y() < 0 || aNewPos2i.y() > mySize.y())
303         {
304           return false;
305         }
306       }
307       if (theListener.UpdateMouseButtons (aNewPos2i, aButtons, aFlags, isEmulated))
308       {
309         theListener.ProcessInput();
310       }
311       break;
312     }
313     case EMSCRIPTEN_EVENT_CLICK:
314     case EMSCRIPTEN_EVENT_DBLCLICK:
315     {
316       if (aNewPos2i.x() < 0 || aNewPos2i.x() > mySize.x()
317        || aNewPos2i.y() < 0 || aNewPos2i.y() > mySize.y())
318       {
319         return false;
320       }
321       break;
322     }
323     case EMSCRIPTEN_EVENT_MOUSEENTER:
324     {
325       break;
326     }
327     case EMSCRIPTEN_EVENT_MOUSELEAVE:
328     {
329       // there is no SetCapture() support, so that mouse unclick events outside canvas will not arrive,
330       // so we have to forget current state...
331       if (theListener.UpdateMouseButtons (aNewPos2i, Aspect_VKeyMouse_NONE, aFlags, isEmulated))
332       {
333         theListener.ProcessInput();
334       }
335       break;
336     }
337   }
338   return true;
339 #else
340   (void )theListener;
341   (void )theEventType;
342   (void )theEvent;
343   return false;
344 #endif
345 }
346
347 // =======================================================================
348 // function : ProcessWheelEvent
349 // purpose  :
350 // =======================================================================
351 bool Wasm_Window::ProcessWheelEvent (Aspect_WindowInputListener& theListener,
352                                      int theEventType, const EmscriptenWheelEvent* theEvent)
353 {
354 #if defined(__EMSCRIPTEN__)
355   if (theEventType != EMSCRIPTEN_EVENT_WHEEL)
356   {
357     return false;
358   }
359
360   const Graphic3d_Vec2d aNewPos2d = ConvertPointToBacking (Graphic3d_Vec2d (theEvent->mouse.targetX, theEvent->mouse.targetY));
361   const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i (aNewPos2d + Graphic3d_Vec2d (0.5));
362   if (aNewPos2i.x() < 0 || aNewPos2i.x() > mySize.x()
363    || aNewPos2i.y() < 0 || aNewPos2i.y() > mySize.y())
364   {
365     return false;
366   }
367
368   double aDelta = 0.0;
369   switch (theEvent->deltaMode)
370   {
371     case DOM_DELTA_PIXEL:
372     {
373       aDelta = theEvent->deltaY / (5.0 * DevicePixelRatio());
374       break;
375     }
376     case DOM_DELTA_LINE:
377     {
378       aDelta = theEvent->deltaY * 8.0;
379       break;
380     }
381     case DOM_DELTA_PAGE:
382     {
383       aDelta = theEvent->deltaY >= 0.0 ? 24.0 : -24.0;
384       break;
385     }
386   }
387   aDelta /= 15.0;
388
389   if (theListener.UpdateMouseScroll (Aspect_ScrollDelta (aNewPos2i, -aDelta)))
390   {
391     theListener.ProcessInput();
392   }
393   return true;
394 #else
395   (void )theListener;
396   (void )theEventType;
397   (void )theEvent;
398   return false;
399 #endif
400 }
401
402 // =======================================================================
403 // function : ProcessTouchEvent
404 // purpose  :
405 // =======================================================================
406 bool Wasm_Window::ProcessTouchEvent (Aspect_WindowInputListener& theListener,
407                                      int theEventType, const EmscriptenTouchEvent* theEvent)
408 {
409   bool hasUpdates = false;
410 #if defined(__EMSCRIPTEN__)
411   if (theEventType != EMSCRIPTEN_EVENT_TOUCHSTART
412    && theEventType != EMSCRIPTEN_EVENT_TOUCHMOVE
413    && theEventType != EMSCRIPTEN_EVENT_TOUCHEND
414    && theEventType != EMSCRIPTEN_EVENT_TOUCHCANCEL)
415   {
416     return false;
417   }
418
419   for (int aTouchIter = 0; aTouchIter < theEvent->numTouches; ++aTouchIter)
420   {
421     const EmscriptenTouchPoint& aTouch = theEvent->touches[aTouchIter];
422     if (!aTouch.isChanged)
423     {
424       continue;
425     }
426
427     const Standard_Size aTouchId = (Standard_Size )aTouch.identifier;
428
429     const Graphic3d_Vec2d aNewPos2d = ConvertPointToBacking (Graphic3d_Vec2d (aTouch.targetX, aTouch.targetY));
430     const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i (aNewPos2d + Graphic3d_Vec2d (0.5));
431     switch (theEventType)
432     {
433       case EMSCRIPTEN_EVENT_TOUCHSTART:
434       {
435         if (aNewPos2i.x() >= 0 && aNewPos2i.x() < mySize.x()
436          && aNewPos2i.y() >= 0 && aNewPos2i.y() < mySize.y())
437         {
438           hasUpdates = true;
439           theListener.AddTouchPoint (aTouchId, aNewPos2d);
440         }
441         break;
442       }
443       case EMSCRIPTEN_EVENT_TOUCHMOVE:
444       {
445         const int anOldIndex = theListener.TouchPoints().FindIndex (aTouchId);
446         if (anOldIndex != 0)
447         {
448           hasUpdates = true;
449           theListener.UpdateTouchPoint (aTouchId, aNewPos2d);
450         }
451         break;
452       }
453       case EMSCRIPTEN_EVENT_TOUCHEND:
454       case EMSCRIPTEN_EVENT_TOUCHCANCEL:
455       {
456         if (theListener.RemoveTouchPoint (aTouchId))
457         {
458           hasUpdates = true;
459         }
460         break;
461       }
462     }
463   }
464   if (hasUpdates)
465   {
466     theListener.ProcessInput();
467   }
468 #else
469   (void )theEventType;
470   (void )theEvent;
471 #endif
472   return hasUpdates || theListener.HasTouchPoints();
473 }
474
475 // =======================================================================
476 // function : ProcessKeyEvent
477 // purpose  :
478 // =======================================================================
479 bool Wasm_Window::ProcessKeyEvent (Aspect_WindowInputListener& theListener,
480                                    int theEventType, const EmscriptenKeyboardEvent* theEvent)
481 {
482 #if defined(__EMSCRIPTEN__)
483   if (theEventType != EMSCRIPTEN_EVENT_KEYDOWN
484    && theEventType != EMSCRIPTEN_EVENT_KEYUP
485    && theEventType != EMSCRIPTEN_EVENT_KEYPRESS)
486   {
487     return false;
488   }
489
490   const double aTimeStamp = theListener.EventTime();
491   const Aspect_VKey aVKey = Wasm_Window::VirtualKeyFromNative (theEvent->keyCode);
492   if (aVKey == Aspect_VKey_UNKNOWN)
493   {
494     return false;
495   }
496
497   switch (theEventType)
498   {
499     case EMSCRIPTEN_EVENT_KEYDOWN:
500     {
501       if (theEvent->repeat == EM_TRUE)
502       {
503         return false;
504       }
505
506       theListener.KeyDown (aVKey, aTimeStamp);
507       theListener.ProcessInput();
508       return false;
509     }
510     case EMSCRIPTEN_EVENT_KEYUP:
511     {
512       theListener.KeyUp (aVKey, aTimeStamp);
513       theListener.ProcessInput();
514       return false;
515     }
516   }
517 #else
518   (void )theListener;
519   (void )theEventType;
520   (void )theEvent;
521 #endif
522   return false;
523 }
524
525 // =======================================================================
526 // function : ProcessUiEvent
527 // purpose  :
528 // =======================================================================
529 bool Wasm_Window::ProcessUiEvent (Aspect_WindowInputListener& theListener,
530                                   int theEventType, const EmscriptenUiEvent* )
531 {
532 #if defined(__EMSCRIPTEN__)
533   if (theEventType != EMSCRIPTEN_EVENT_RESIZE
534    && theEventType != EMSCRIPTEN_EVENT_CANVASRESIZED)
535   {
536     return false;
537   }
538 #else
539   (void )theEventType;
540 #endif
541   theListener.ProcessConfigure (true);
542   return true;
543 }
544
545 // =======================================================================
546 // function : ProcessFocusEvent
547 // purpose  :
548 // =======================================================================
549 bool Wasm_Window::ProcessFocusEvent (Aspect_WindowInputListener& theListener,
550                                      int theEventType, const EmscriptenFocusEvent* )
551 {
552   bool isActivated = false;
553 #if defined(__EMSCRIPTEN__)
554   if (theEventType != EMSCRIPTEN_EVENT_FOCUS
555    && theEventType != EMSCRIPTEN_EVENT_FOCUSIN // about to receive focus
556    && theEventType != EMSCRIPTEN_EVENT_FOCUSOUT)
557   {
558     return false;
559   }
560   isActivated = theEventType == EMSCRIPTEN_EVENT_FOCUS;
561 #else
562   (void )theEventType;
563 #endif
564   theListener.ProcessFocus (isActivated);
565   return true;
566 }
567
568 // =======================================================================
569 // function : MouseButtonsFromNative
570 // purpose  :
571 // =======================================================================
572 Aspect_VKeyMouse Wasm_Window::MouseButtonsFromNative (unsigned short theButtons)
573 {
574   Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
575   if ((theButtons & 0x1) != 0)
576   {
577     aButtons |= Aspect_VKeyMouse_LeftButton;
578   }
579   if ((theButtons & 0x2) != 0)
580   {
581     aButtons |= Aspect_VKeyMouse_RightButton;
582   }
583   if ((theButtons & 0x4) != 0)
584   {
585     aButtons |= Aspect_VKeyMouse_MiddleButton;
586   }
587   return aButtons;
588 }
589
590 // =======================================================================
591 // function : VirtualKeyFromNative
592 // purpose  :
593 // =======================================================================
594 Aspect_VKey Wasm_Window::VirtualKeyFromNative (Standard_Integer theKey)
595 {
596 #if defined(__EMSCRIPTEN__)
597   if (theKey >= DOM_VK_0
598    && theKey <= DOM_VK_9)
599   {
600     // numpad keys
601     return Aspect_VKey((theKey - DOM_VK_0) + Aspect_VKey_0);
602   }
603   if (theKey >= DOM_VK_A
604    && theKey <= DOM_VK_Z)
605   {
606     // main latin alphabet keys
607     return Aspect_VKey((theKey - DOM_VK_A) + Aspect_VKey_A);
608   }
609   if (theKey >= DOM_VK_F1
610    && theKey <= DOM_VK_F24)
611   {
612     // special keys
613     if (theKey <= DOM_VK_F12)
614     {
615       return Aspect_VKey((theKey - DOM_VK_F1) + Aspect_VKey_F1);
616     }
617     return Aspect_VKey_UNKNOWN;
618   }
619   if (theKey >= DOM_VK_NUMPAD0
620    && theKey <= DOM_VK_NUMPAD9)
621   {
622     // numpad keys
623     return Aspect_VKey((theKey - DOM_VK_NUMPAD0) + Aspect_VKey_Numpad0);
624   }
625
626   switch (theKey)
627   {
628     case DOM_VK_CANCEL:
629     case DOM_VK_HELP:
630       return Aspect_VKey_UNKNOWN;
631     case DOM_VK_BACK_SPACE:
632       return Aspect_VKey_Backspace;
633     case DOM_VK_TAB:
634       return Aspect_VKey_Tab;
635     case DOM_VK_CLEAR:
636       return Aspect_VKey_UNKNOWN;
637     case DOM_VK_RETURN:
638     case DOM_VK_ENTER:
639       return Aspect_VKey_Enter;
640     case DOM_VK_SHIFT:
641       return Aspect_VKey_Shift;
642     case DOM_VK_CONTROL:
643       return Aspect_VKey_Control;
644     case DOM_VK_ALT:
645       return Aspect_VKey_Alt;
646     case DOM_VK_PAUSE:
647     case DOM_VK_CAPS_LOCK:
648     case DOM_VK_KANA:
649     //case DOM_VK_HANGUL:
650     case DOM_VK_EISU:
651     case DOM_VK_JUNJA:
652     case DOM_VK_FINAL:
653     case DOM_VK_HANJA:
654     //case DOM_VK_KANJI:
655       return Aspect_VKey_UNKNOWN;
656     case DOM_VK_ESCAPE:
657       return Aspect_VKey_Escape;
658     case DOM_VK_CONVERT:
659     case DOM_VK_NONCONVERT:
660     case DOM_VK_ACCEPT:
661     case DOM_VK_MODECHANGE:
662       return Aspect_VKey_UNKNOWN;
663     case DOM_VK_SPACE:
664       return Aspect_VKey_Space;
665     case DOM_VK_PAGE_UP:
666       return Aspect_VKey_PageUp;
667     case DOM_VK_PAGE_DOWN:
668       return Aspect_VKey_PageDown;
669     case DOM_VK_END:
670       return Aspect_VKey_End;
671     case DOM_VK_HOME:
672       return Aspect_VKey_Home;
673     case DOM_VK_LEFT:
674       return Aspect_VKey_Left;
675     case DOM_VK_UP:
676       return Aspect_VKey_Up;
677     case DOM_VK_RIGHT:
678       return Aspect_VKey_Right;
679     case DOM_VK_DOWN:
680       return Aspect_VKey_Down;
681     case DOM_VK_SELECT:
682     case DOM_VK_PRINT:
683     case DOM_VK_EXECUTE:
684     case DOM_VK_PRINTSCREEN:
685     case DOM_VK_INSERT:
686       return Aspect_VKey_UNKNOWN;
687     case DOM_VK_DELETE:
688       return Aspect_VKey_Delete;
689     case DOM_VK_COLON:
690       return Aspect_VKey_Comma;
691     case DOM_VK_SEMICOLON:
692       return Aspect_VKey_Semicolon;
693     case DOM_VK_LESS_THAN:
694       return Aspect_VKey_UNKNOWN;
695     case DOM_VK_EQUALS:
696       return Aspect_VKey_Equal;
697     case DOM_VK_GREATER_THAN:
698       return Aspect_VKey_UNKNOWN;
699     case DOM_VK_QUESTION_MARK:
700       return Aspect_VKey_Slash;
701     case DOM_VK_AT: // @ key
702       return Aspect_VKey_UNKNOWN;
703     case DOM_VK_WIN:
704       return Aspect_VKey_Meta;
705     case DOM_VK_CONTEXT_MENU:
706     case DOM_VK_SLEEP:
707       return Aspect_VKey_UNKNOWN;
708     case DOM_VK_MULTIPLY:
709       return Aspect_VKey_NumpadMultiply;
710     case DOM_VK_ADD:
711       return Aspect_VKey_NumpadAdd;
712     case DOM_VK_SEPARATOR:
713       return Aspect_VKey_UNKNOWN;
714     case DOM_VK_SUBTRACT:
715       return Aspect_VKey_NumpadSubtract;
716     case DOM_VK_DECIMAL:
717       return Aspect_VKey_UNKNOWN;
718     case DOM_VK_DIVIDE:
719       return Aspect_VKey_NumpadDivide;
720     case DOM_VK_NUM_LOCK:
721       return Aspect_VKey_Numlock;
722     case DOM_VK_SCROLL_LOCK:
723       return Aspect_VKey_Scroll;
724     case DOM_VK_WIN_OEM_FJ_JISHO:
725     case DOM_VK_WIN_OEM_FJ_MASSHOU:
726     case DOM_VK_WIN_OEM_FJ_TOUROKU:
727     case DOM_VK_WIN_OEM_FJ_LOYA:
728     case DOM_VK_WIN_OEM_FJ_ROYA:
729     case DOM_VK_CIRCUMFLEX:
730       return Aspect_VKey_UNKNOWN;
731     case DOM_VK_EXCLAMATION:
732     case DOM_VK_DOUBLE_QUOTE:
733     //case DOM_VK_HASH:
734     case DOM_VK_DOLLAR:
735     case DOM_VK_PERCENT:
736     case DOM_VK_AMPERSAND:
737     case DOM_VK_UNDERSCORE:
738     case DOM_VK_OPEN_PAREN:
739     case DOM_VK_CLOSE_PAREN:
740     case DOM_VK_ASTERISK:
741       return Aspect_VKey_UNKNOWN;
742     case DOM_VK_PLUS:
743       return Aspect_VKey_Plus;
744     case DOM_VK_PIPE:
745     case DOM_VK_HYPHEN_MINUS:
746       return Aspect_VKey_UNKNOWN;
747     case DOM_VK_OPEN_CURLY_BRACKET:
748       return Aspect_VKey_BracketLeft;
749     case DOM_VK_CLOSE_CURLY_BRACKET:
750       return Aspect_VKey_BracketRight;
751     case DOM_VK_TILDE:
752       return Aspect_VKey_Tilde;
753     case DOM_VK_VOLUME_MUTE:
754       return Aspect_VKey_VolumeMute;
755     case DOM_VK_VOLUME_DOWN:
756       return Aspect_VKey_VolumeDown;
757     case DOM_VK_VOLUME_UP:
758       return Aspect_VKey_VolumeUp;
759     case DOM_VK_COMMA:
760       return Aspect_VKey_Comma;
761     case DOM_VK_PERIOD:
762       return Aspect_VKey_Period;
763     case DOM_VK_SLASH:
764       return Aspect_VKey_Slash;
765     case DOM_VK_BACK_QUOTE:
766       return Aspect_VKey_UNKNOWN;
767     case DOM_VK_OPEN_BRACKET:
768       return Aspect_VKey_BracketLeft;
769     case DOM_VK_BACK_SLASH:
770       return Aspect_VKey_Backslash;
771     case DOM_VK_CLOSE_BRACKET:
772       return Aspect_VKey_BracketRight;
773     case DOM_VK_QUOTE:
774       return Aspect_VKey_UNKNOWN;
775     case DOM_VK_META:
776       return Aspect_VKey_Meta;
777     case DOM_VK_ALTGR:
778       return Aspect_VKey_Alt;
779     case DOM_VK_WIN_ICO_HELP:
780     case DOM_VK_WIN_ICO_00:
781     case DOM_VK_WIN_ICO_CLEAR:
782     case DOM_VK_WIN_OEM_RESET:
783     case DOM_VK_WIN_OEM_JUMP:
784     case DOM_VK_WIN_OEM_PA1:
785     case DOM_VK_WIN_OEM_PA2:
786     case DOM_VK_WIN_OEM_PA3:
787     case DOM_VK_WIN_OEM_WSCTRL:
788     case DOM_VK_WIN_OEM_CUSEL:
789     case DOM_VK_WIN_OEM_ATTN:
790     case DOM_VK_WIN_OEM_FINISH:
791     case DOM_VK_WIN_OEM_COPY:
792     case DOM_VK_WIN_OEM_AUTO:
793     case DOM_VK_WIN_OEM_ENLW:
794     case DOM_VK_WIN_OEM_BACKTAB:
795     case DOM_VK_ATTN:
796     case DOM_VK_CRSEL:
797     case DOM_VK_EXSEL:
798     case DOM_VK_EREOF:
799       return Aspect_VKey_UNKNOWN;
800     case DOM_VK_PLAY:
801       return Aspect_VKey_MediaPlayPause;
802     case DOM_VK_ZOOM:
803     case DOM_VK_PA1:
804     case DOM_VK_WIN_OEM_CLEAR:
805       return Aspect_VKey_UNKNOWN;
806   }
807 #else
808   (void )theKey;
809 #endif
810   return Aspect_VKey_UNKNOWN;
811 }