public override void Update(GameTime gameTime) { base.Update(gameTime); // Get current keystroke and check if this is a new keystroke or // if it is a repeated one. KeyboardState state = Keyboard.GetState(); VirtualKeyValue[] keystroke = keyboardLayout.ProcessKeys(state); Keys[] filteredStroke = keyboardLayout.FilteredPressedKeys; bool isNewStroke = true; if (previousStroke != null) { // Check scan codes to handle modifier+char to char transitions. // If two or more keys are pressed, the last one is used and when // this key is released, no keystroke is generated. int newKeyIndex = -1; int i; int j = 0; for (i = 0; i < filteredStroke.Length; i++) { // KeyboardLayout.FilteredPressedKeys is sorted while (j < previousStroke.Length && previousStroke[j] < filteredStroke[i]) { j++; } if (j >= previousStroke.Length) { newKeyIndex = i; break; } else if (previousStroke[j] > filteredStroke[i]) { newKeyIndex = i; } } if (newKeyIndex > -1) { isNewStroke = currentKeyValue != keystroke[newKeyIndex]; currentKeyValue = keystroke[newKeyIndex]; } else { isNewStroke = false; if (keystroke.Length == 0) { currentKeyValue = VirtualKeyValue.Empty; } } } for (int i = 0; i < keyStates.Length; i++) { keyStates[i] = KeyStateFlags.Up; } // If the keystroke is a new stroke, reset start time and update // key state flags to notify that the key was just released if (isNewStroke == true) { strokeStartTime = gameTime.TotalGameTime.TotalMilliseconds; repeatTimeCount = -1; if (previousStroke != null) { foreach (Keys key in previousStroke) { keyStates[(int)key] = KeyStateFlags.Released; } } } // Apply delay and repeat-speed to stroke age double dt = gameTime.TotalGameTime.TotalMilliseconds - strokeStartTime; bool doSetString = false; KeyStateFlags newKeyStateFlags = KeyStateFlags.Down; if (dt <= float.Epsilon) { // First stroke doSetString = true; } else if (repeatTimeCount == -1 && delayTime - dt < float.Epsilon) { // Repeat delay time reached doSetString = true; repeatTimeCount = 0; newKeyStateFlags |= KeyStateFlags.Repeat; } else if (repeatTimeCount > -1) { dt -= delayTime; // Count stroke repeats int i; for (i = 0; dt > 0; i++) { dt -= repeatTime; } if (i > repeatTimeCount) { doSetString = true; repeatTimeCount++; } newKeyStateFlags |= KeyStateFlags.Repeat; } // Build the current key strokes output characters = ""; if (doSetString == true && keystroke.Length > 0) { VirtualKeyValue keyValue = currentKeyValue; if (deadKey != null) { char baseChar = keyValue.Characters[0]; if (deadKey.ContainsBaseCharacter(baseChar) == true) { characters = deadKey.GetCombinedCharacter(baseChar).ToString(); } else { characters = String.Format("{0}{1}", deadKey.DeadCharacter, baseChar); } deadKey = null; } else if (keyValue.IsDeadKey == true) { keyboardLayout.DeadKeys.TryGetValue(keyValue.Characters[0], out deadKey); } else if (keyValue.Characters != null && Char.IsControl(keyValue.Characters, 0) == false) { // Add non-control characters only characters = keyValue.Characters; } } if (doSetString == true && filteredStroke.Length > 0) { // Update key state flags foreach (Keys key in filteredStroke) { keyStates[(int)key] = newKeyStateFlags; } } previousStroke = filteredStroke; }
/// <summary> /// /// </summary> /// <param name="iKeyDead">The index into the VirtualKey of the dead key</param> /// <param name="shiftStateDead">The shiftstate that contains the dead key</param> /// <param name="lpKeyStateDead">The key state for the dead key</param> /// <param name="rgKey">Our array of dead keys</param> /// <param name="fCapsLock">Was the caps lock key pressed?</param> /// <param name="hkl">The keyboard layout</param> /// <returns></returns> internal static DeadKey ProcessDeadKey(KeyboardLayoutContent kbl, uint iKeyDead, ShiftState shiftStateDead, KeysEx[] lpKeyStateDead, VirtualKeyContent[] rgKey, bool fCapsLock, IntPtr hkl) { KeysEx[] lpKeyState = new KeysEx[256]; String dkShiftState = rgKey[iKeyDead].GetShiftState(shiftStateDead, fCapsLock).Characters; DeadKey deadKey = new DeadKey(dkShiftState[0]); for (uint iKey = 0; iKey < rgKey.Length; iKey++) { if (rgKey[iKey] != null) { StringBuilder sbBuffer = new StringBuilder(10); // Scratchpad we use many places for (ShiftState ss = ShiftState.Base; ss <= kbl.MaxShiftState; ss++) { int rc = 0; if (ss == ShiftState.Menu || ss == ShiftState.ShftMenu) { // Alt and Shift+Alt don't work, so skip them continue; } for (int caps = 0; caps <= 1; caps++) { // First the dead key while (rc >= 0) { // We know that this is a dead key coming up, otherwise // this function would never have been called. If we do // *not* get a dead key then that means the state is // messed up so we run again and again to clear it up. // Risk is technically an infinite loop but per Hiroyama // that should be impossible here. rc = NativeMethods.User32.ToUnicodeEx((uint)rgKey[iKeyDead].VK, rgKey[iKeyDead].SC, lpKeyStateDead, sbBuffer, sbBuffer.Capacity, 0, hkl); } // Now fill the key state for the potential base character FillKeyState(kbl, lpKeyState, ss, (caps != 0)); sbBuffer = new StringBuilder(10); rc = NativeMethods.User32.ToUnicodeEx((uint)rgKey[iKey].VK, rgKey[iKey].SC, lpKeyState, sbBuffer, sbBuffer.Capacity, 0, hkl); if (rc == 1) { // That was indeed a base character for our dead key. // And we now have a composite character. Let's run // through one more time to get the actual base // character that made it all possible? char combchar = sbBuffer[0]; sbBuffer = new StringBuilder(10); rc = NativeMethods.User32.ToUnicodeEx((uint)rgKey[iKey].VK, rgKey[iKey].SC, lpKeyState, sbBuffer, sbBuffer.Capacity, 0, hkl); char basechar = sbBuffer[0]; if (deadKey.DeadCharacter == combchar) { // Since the combined character is the same as the dead key, // we must clear out the keyboard buffer. ClearKeyboardBuffer((uint)KeysEx.VK_DECIMAL, rgKey[(uint)KeysEx.VK_DECIMAL].SC, hkl); } if ((((ss == ShiftState.Ctrl) || (ss == ShiftState.ShftCtrl)) && (char.IsControl(basechar))) || (basechar.Equals(combchar))) { // ToUnicodeEx has an internal knowledge about those // VK_A ~ VK_Z keys to produce the control characters, // when the conversion rule is not provided in keyboard // layout files // Additionally, dead key state is lost for some of these // character combinations, for unknown reasons. // Therefore, if the base character and combining are equal, // and its a CTRL or CTRL+SHIFT state, and a control character // is returned, then we do not add this "dead key" (which // is not really a dead key). continue; } if (!deadKey.ContainsBaseCharacter(basechar)) { deadKey.AddDeadKeyRow(basechar, combchar); } } else if (rc > 1) { // Not a valid dead key combination, sorry! We just ignore it. } else if (rc < 0) { // It's another dead key, so we ignore it (other than to flush it from the state) ClearKeyboardBuffer((uint)KeysEx.VK_DECIMAL, rgKey[(uint)KeysEx.VK_DECIMAL].SC, hkl); } } } } } return(deadKey); }