1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2007 Novell, Inc.
23 // Geoff Norton (gnorton@novell.com)
27 using System
.Collections
;
29 using System
.Globalization
;
30 using System
.Runtime
.InteropServices
;
32 namespace System
.Windows
.Forms
.CarbonInternal
{
33 internal class KeyboardHandler
: EventHandlerBase
, IEventHandler
{
34 internal const uint kEventRawKeyDown
= 1;
35 internal const uint kEventRawKeyRepeat
= 2;
36 internal const uint kEventRawKeyUp
= 3;
37 internal const uint kEventRawKeyModifiersChanged
= 4;
38 internal const uint kEventHotKeyPressed
= 5;
39 internal const uint kEventHotKeyReleased
= 6;
41 internal const uint kEventParamKeyMacCharCodes
= 1801676914;
42 internal const uint kEventParamKeyCode
= 1801678692;
43 internal const uint kEventParamKeyModifiers
= 1802334052;
44 internal const uint kEventTextInputUnicodeForKeyEvent
= 2;
45 internal const uint kEventParamTextInputSendText
= 1953723512;
47 internal const uint typeChar
= 1413830740;
48 internal const uint typeUInt32
= 1835100014;
49 internal const uint typeUnicodeText
= 1970567284;
51 internal static byte [] key_filter_table
;
52 internal static byte [] key_modifier_table
;
53 internal static byte [] key_translation_table
;
54 internal static byte [] char_translation_table
;
56 internal static bool translate_modifier
= false;
58 internal string ComposedString
;
60 static KeyboardHandler () {
61 // our key filter table is a 256 byte array - if the corresponding byte
62 // is set the key should be filtered from WM_CHAR (apple pushes unicode events
63 // for some keys which win32 only handles as KEYDOWN
64 // currently filtered:
70 // Please update this list as well as the table as more keys are found
71 key_filter_table
= new byte [256] {
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
76 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
87 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
90 // our char translation table is a set of translations from mac char codes
91 // to win32 vkey codes
92 // most things map directly
93 char_translation_table
= new byte [256] {
94 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
95 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 0x25, 0x27, 0x26, 0x28,
96 32, 49, 34, 51, 52, 53, 55, 222, 57, 48, 56, 187, 188, 189, 190, 191,
97 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 186, 60, 61, 62, 63,
98 50, 65, 66, 67, 68, 187, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
99 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 219, 220, 221, 54, 189,
100 192, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
101 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 0x2e,
102 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
103 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
104 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
105 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
106 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
107 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
108 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
109 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
111 key_translation_table
= new byte [256] {
112 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
113 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
114 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
115 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
116 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
117 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
118 0x74, 0x75, 0x76, 0x72, 0x77, 0x78, 0x79, 103, 104, 105, 106, 107, 108, 109, 0x7a, 0x7b,
119 112, 113, 114, 115, 116, 117, 0x73, 119, 0x71, 121, 0x70, 123, 124, 125, 126, 127,
120 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
121 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
122 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
123 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
124 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
125 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
126 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
127 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
129 // the key modifier table is a state table of the possible modifier keys
130 // apple currently only goes up to 1 << 14 keys, we've extended this to 32
131 // bytes as thats the size that apple uses
132 key_modifier_table
= new byte [32];
135 internal KeyboardHandler (XplatUICarbon driver
) : base (driver
) {}
137 private void ModifierToVirtualKey (int i
, ref MSG msg
, bool down
) {
138 msg
.hwnd
= XplatUICarbon
.FocusWindow
;
140 if (i
== 9 || i
== 13) {
141 msg
.message
= (down
? Msg
.WM_KEYDOWN
: Msg
.WM_KEYUP
);
142 msg
.wParam
= (IntPtr
) VirtualKeys
.VK_SHIFT
;
143 msg
.lParam
= IntPtr
.Zero
;
146 if (i
== 12 || i
== 14) {
147 msg
.message
= (down
? Msg
.WM_KEYDOWN
: Msg
.WM_KEYUP
);
148 msg
.wParam
= (IntPtr
) VirtualKeys
.VK_CONTROL
;
149 msg
.lParam
= IntPtr
.Zero
;
153 msg
.message
= (down
? Msg
.WM_SYSKEYDOWN
: Msg
.WM_SYSKEYUP
);
154 msg
.wParam
= (IntPtr
) VirtualKeys
.VK_MENU
;
155 msg
.lParam
= new IntPtr (0x20000000);
162 public void ProcessModifiers (IntPtr eventref
, ref MSG msg
) {
163 // we get notified when modifiers change, but not specifically what changed
164 UInt32 modifiers
= 0;
166 GetEventParameter (eventref
, kEventParamKeyModifiers
, typeUInt32
, IntPtr
.Zero
, (uint)Marshal
.SizeOf (typeof (UInt32
)), IntPtr
.Zero
, ref modifiers
);
168 for (int i
= 0; i
< 32; i
++) {
169 if (key_modifier_table
[i
] == 0x01 && (modifiers
& (1 << i
)) == 0) {
170 ModifierToVirtualKey (i
, ref msg
, false);
171 key_modifier_table
[i
] = 0x00;
173 } else if (key_modifier_table
[i
] == 0x00 && (modifiers
& (1 << i
)) == (1 << i
)) {
174 ModifierToVirtualKey (i
, ref msg
, true);
175 key_modifier_table
[i
] = 0x01;
183 public void ProcessText (IntPtr eventref
, ref MSG msg
) {
185 IntPtr buffer
= IntPtr
.Zero
;
188 // get the size of the unicode buffer
189 GetEventParameter (eventref
, kEventParamTextInputSendText
, typeUnicodeText
, IntPtr
.Zero
, 0, ref size
, IntPtr
.Zero
);
191 buffer
= Marshal
.AllocHGlobal ((int) size
);
192 bdata
= new byte [size
];
194 // get the actual text buffer
195 GetEventParameter (eventref
, kEventParamTextInputSendText
, typeUnicodeText
, IntPtr
.Zero
, size
, IntPtr
.Zero
, buffer
);
197 Marshal
.Copy (buffer
, bdata
, 0, (int) size
);
198 Marshal
.FreeHGlobal (buffer
);
200 if (key_filter_table
[bdata
[0]] == 0x00) {
202 msg
.message
= Msg
.WM_CHAR
;
203 msg
.wParam
= BitConverter
.IsLittleEndian
? (IntPtr
) bdata
[0] : (IntPtr
) bdata
[size
-1];
204 msg
.lParam
= IntPtr
.Zero
;
205 msg
.hwnd
= XplatUICarbon
.FocusWindow
;
207 msg
.message
= Msg
.WM_IME_COMPOSITION
;
208 Encoding enc
= BitConverter
.IsLittleEndian
? Encoding
.Unicode
: Encoding
.BigEndianUnicode
;
209 ComposedString
= enc
.GetString (bdata
);
210 msg
.hwnd
= XplatUICarbon
.FocusWindow
;
215 public void ProcessKeyPress (IntPtr eventref
, ref MSG msg
) {
219 GetEventParameter (eventref
, kEventParamKeyMacCharCodes
, typeChar
, IntPtr
.Zero
, (uint)Marshal
.SizeOf (typeof (byte)), IntPtr
.Zero
, ref charCode
);
220 GetEventParameter (eventref
, kEventParamKeyCode
, typeUInt32
, IntPtr
.Zero
, (uint)Marshal
.SizeOf (typeof (byte)), IntPtr
.Zero
, ref keyCode
);
222 msg
.lParam
= (IntPtr
) charCode
;
223 msg
.wParam
= charCode
== 0x10 ? (IntPtr
) key_translation_table
[keyCode
] : (IntPtr
) char_translation_table
[charCode
];
224 msg
.hwnd
= XplatUICarbon
.FocusWindow
;
227 public bool ProcessEvent (IntPtr callref
, IntPtr eventref
, IntPtr handle
, uint kind
, ref MSG msg
) {
228 uint klass
= EventHandler
.GetEventClass (eventref
);
231 if (klass
== EventHandler
.kEventClassTextInput
) {
233 case kEventTextInputUnicodeForKeyEvent
:
234 ProcessText (eventref
, ref msg
);
237 Console
.WriteLine ("WARNING: KeyboardHandler.ProcessEvent default handler for kEventClassTextInput should not be reached");
240 } else if (klass
== EventHandler
.kEventClassKeyboard
) {
242 case kEventRawKeyDown
:
243 case kEventRawKeyRepeat
:
244 msg
.message
= Msg
.WM_KEYDOWN
;
245 ProcessKeyPress (eventref
, ref msg
);
248 msg
.message
= Msg
.WM_KEYUP
;
249 ProcessKeyPress (eventref
, ref msg
);
251 case kEventRawKeyModifiersChanged
:
252 ProcessModifiers (eventref
, ref msg
);
255 Console
.WriteLine ("WARNING: KeyboardHandler.ProcessEvent default handler for kEventClassKeyboard should not be reached");
259 Console
.WriteLine ("WARNING: KeyboardHandler.ProcessEvent default handler for kEventClassTextInput should not be reached");
265 public bool TranslateMessage (ref MSG msg
) {
268 if (msg
.message
>= Msg
.WM_KEYFIRST
&& msg
.message
<= Msg
.WM_KEYLAST
)
271 if (msg
.message
!= Msg
.WM_KEYDOWN
&& msg
.message
!= Msg
.WM_SYSKEYDOWN
&& msg
.message
!= Msg
.WM_KEYUP
&& msg
.message
!= Msg
.WM_SYSKEYUP
&& msg
.message
!= Msg
.WM_CHAR
&& msg
.message
!= Msg
.WM_SYSCHAR
)
274 if (key_modifier_table
[8] == 0x01 && key_modifier_table
[12] == 0x00 && key_modifier_table
[14] == 0x00) {
275 if (msg
.message
== Msg
.WM_KEYDOWN
) {
276 msg
.message
= Msg
.WM_SYSKEYDOWN
;
277 } else if (msg
.message
== Msg
.WM_CHAR
) {
278 msg
.message
= Msg
.WM_SYSCHAR
;
279 translate_modifier
= true;
280 } else if (msg
.message
== Msg
.WM_KEYUP
) {
281 msg
.message
= Msg
.WM_SYSKEYUP
;
286 msg
.lParam
= new IntPtr (0x20000000);
287 } else if (msg
.message
== Msg
.WM_SYSKEYUP
&& translate_modifier
&& msg
.wParam
== (IntPtr
) 18) {
288 msg
.message
= Msg
.WM_KEYUP
;
290 msg
.lParam
= IntPtr
.Zero
;
291 translate_modifier
= false;
297 internal Keys ModifierKeys
{
299 Keys keys
= Keys
.None
;
300 if (key_modifier_table
[9] == 0x01 || key_modifier_table
[13] == 0x01) { keys |= Keys.Shift; }
301 if (key_modifier_table
[8] == 0x01) { keys |= Keys.Alt; }
302 if (key_modifier_table
[12] == 0x01 || key_modifier_table
[14] == 0x01) { keys |= Keys.Control; }
307 [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
308 static extern int GetEventParameter (IntPtr eventref
, uint name
, uint type
, IntPtr outtype
, uint size
, ref UInt32 outsize
, IntPtr data
);
309 [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
310 static extern int GetEventParameter (IntPtr eventref
, uint name
, uint type
, IntPtr outtype
, uint size
, IntPtr outsize
, IntPtr data
);
311 [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
312 static extern int GetEventParameter (IntPtr eventref
, uint name
, uint type
, IntPtr outtype
, uint size
, IntPtr outsize
, ref byte data
);
313 [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
314 static extern int GetEventParameter (IntPtr eventref
, uint name
, uint type
, IntPtr outtype
, uint size
, IntPtr outsize
, ref UInt32 data
);
317 internal enum KeyboardModifiers
: uint {
324 controlKey
= 1 << 12,
325 rightShiftKey
= 1 << 13,
326 rightOptionKey
= 1 << 14,
327 rightControlKey
= 1 << 14,