Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Options
Go to last post Go to first unread
Offline fool  
#1 Posted : Thursday, May 3, 2012 11:59:03 AM(UTC)
fool

Joined: 8/24/2011(UTC)
Posts: 34
Location: New Zealand

Thanks: 4 times
I'm playing around with keyboard input and KeyboardTests.TestCtrlAltShift seems to be broken on Windows. It doesn't differentiate Left and Right variants of Ctrl, Alt, and Shift. i.e. press any of them and they're all pressed. Presumably a bug in BaseKeyboard.HandleCombinedKeys?

Wanna join the discussion?! Login to your forum accountregister a new account. Or Connect via Facebook Twitter Google

Offline Benjamin  
#2 Posted : Thursday, May 3, 2012 3:44:44 PM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
Have you checked the InputButton enum?
http://help.deltaengine....utSystem_InputButton.htm

Do you mean you get all 3 button states as pressed once you press ANY shift key? Shift, LeftShift and RightShift?

I just tested it with the test you mentioned: TestCtrlAltShift. It seems to be a bug I am not aware of. The code has been tested and used for a long time, something must have broken it ..

Update: It seems some simplification code in the GetInputButtonFromKeyCode method is the problem, which handles the Shift, Alt and Ctrl keys, but OnFormsWindowKeyDown does not receive LShift, LMenu, etc. but just ShiftKey as the KeyCode.

The value is however correctly returned in the LowLevelCaptureKey method, which is also used. I have added all left and right key combinations there and now it works fine. The updated code can be found in the next nightly release (it is all in Delta.InputSystem.Windows if you want to check it out).

Edited by user Thursday, May 3, 2012 4:44:21 PM(UTC)  | Reason: Not specified

thanks 1 user thanked Benjamin for this useful post.
fool on 5/3/2012(UTC)
Offline fool  
#3 Posted : Thursday, May 3, 2012 10:05:59 PM(UTC)
fool

Joined: 8/24/2011(UTC)
Posts: 34
Location: New Zealand

Thanks: 4 times
Thanks Ben. I thought there was a bug somewhere. FWIW, I do look at the code I have access to before posting.
Offline fool  
#4 Posted : Sunday, May 6, 2012 4:31:11 AM(UTC)
fool

Joined: 8/24/2011(UTC)
Posts: 34
Location: New Zealand

Thanks: 4 times
I've had a play with the changes above and although it now differentiates left and right key combinations, I'm seeing a strange behaviour when running the test, e.g.

* press and hold Left Ctrl for a few seconds - stable
* release Left Ctrl
* press and hold Right Ctrl for a few seconds - stable
* release Right Ctrl
* press and hold Left Ctrl for a while - unstable (state is toggling)

For some reason, the sequence above causes Control to then appear to toggle between IsPressed and NotPressed while Left Ctrl is being held down. The same happens for Alt and Shift. Very strange...

Edit: From subsequent tests this appears to be something to do with BaseKeyboard.HandleCombinedKeys.

Edited by user Sunday, May 6, 2012 11:54:12 AM(UTC)  | Reason: Not specified

Offline Benjamin  
#5 Posted : Sunday, May 6, 2012 12:25:06 PM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
Yeah, you are right, this is a strange issue. The code in Update was actually fine, the problem is that capturing the new keys in LowLevelCaptureKey was doing things twice. I just commented out the two lines in LowLevelCaptureKey and now it works fine:
Code:

					//done in update already: HandleCombinedKeys((int)key);

					// Also add this key to the usedKeyIndices list
					//done in update already: AddKeyIndex((int)key);


For easier testing I also limited the framerate to 30fps in TestCtrlAltShift because I was unable to see any toggling at high fps rates (6000fps ^^):
Code:

			// Limit to 30 fps for easier state checking.
			Settings.Extra.LimitFramerateNumber = 30;
			Settings.Extra.ReduceInputLag = true;
Offline fool  
#6 Posted : Sunday, May 6, 2012 2:01:22 PM(UTC)
fool

Joined: 8/24/2011(UTC)
Posts: 34
Location: New Zealand

Thanks: 4 times
Okay that fixes it, apart from two observations... using the sequence above there is a noticeable lag (of about 1 second) when pressing and holding Left Ctrl before 'Control' is reported as 'IsPressed'. The same lag does not happen when pressing and holding Right Ctrl. Actually, if I change the order of the sequence to be Right Ctrl, then Left Ctrl, then Right Ctrl... the lag to update 'Control' status happens with Right Ctrl and not Left Ctrl. WTF?

Also, If AddKeyIndex should not be called from LowLevelCaptureKey, then presumably it should not be called from OnWPFWindowKeyDown and OnWPFWindowKeyUp either - it isn't called from OnFormsWindowKeyUp and OnFormsWindowKeyDown. But currently it is being called in the WPF event handlers. Albeit the WPF variants aren't being used, but the inconsistency is confusing and presumably another bug.

Edited by user Monday, May 7, 2012 12:21:39 AM(UTC)  | Reason: Not specified

Offline Benjamin  
#7 Posted : Sunday, May 6, 2012 11:52:37 PM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
I noticed some heavy lag at 5fps or 10fps too, but it went away with 30fps and is not noticeable with high fps (6000). I am not sure why you would experience it so strongly (1 second, wtf). I tested a bit with uncommenting Log lines to see whats going on in the console.

AddKeyIndex is mostly used for keyboards on other platforms, e.g. in XNA everything can be handled in the update loop (polling). It shouldn't matter much if it is added in the key up/down events of WindowsKeyboard.cs because the keys are added to the keysUp and keysDowns lists anyway and those will call the AddKeyIndex method anyway. I have commented them out and see no difference in WPF, so it will stay commented out (simpler code is always better).

Please note that I tried to test the keyboard with logging enabled and with 5fps to see whats going on, I also reduced input lag because my GPU rendered several frames ahead and this makes things confusing ^^
Code:

			// Limit to 5 fps for easier state checking.
			Settings.Extra.LimitFramerateNumber = 5;
			Settings.Extra.ReduceInputLag = true;


After testing for another hour this evening and fixing some minor issues along the way it turned out the Update code was not broken, neither was the LowLevelCaptureKey hook function. The problem was apparent with 1 fps as way more (10-20x) events arrived at the LowLevelCaptureKey function than the app could handle. It seemed to lag 20-30 seconds behind, so this is the same issue you were experiencing just a much worse delay with 1 update tick per second. The trick here is to not let Windows call the LowLevelCaptureKey hook function so often. I just limited it to the current executing input thread and the problem went fully away and the code works fine now Cool
Code:

				// Set Hook of Keyboard Process for current module and thread.
				lowLevelKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,
					keyboardProcessObject, GetModuleHandle(currentModule.ModuleName),
					(uint)GetCurrentThreadId());


Hope this helps.

Edited by user Sunday, May 6, 2012 11:53:24 PM(UTC)  | Reason: Not specified

Offline fool  
#8 Posted : Monday, May 7, 2012 12:45:16 AM(UTC)
fool

Joined: 8/24/2011(UTC)
Posts: 34
Location: New Zealand

Thanks: 4 times
Without waiting for the nightly checkin, I added the GetCurrentThreadId change, which makes the toggling go away, but it's now lost the left and right differentiation, so I presume you made other changes as well - I'll test again when I get the nightly changes.

Excuse me for saying, but the above change is suspicious - as in it smells more like a multi threading issue to me. ;)
Offline Benjamin  
#9 Posted : Monday, May 7, 2012 1:12:28 AM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
Dunno how you implemented GetCurrentThreadId, maybe you are not passing the actual native thread id?

Maybe just check the nightly release, which is coming in 2 hours anyway. It works fine for me (left and right keys, plus combined keys, with 5 fps or thousnands of fps).
Offline fool  
#10 Posted : Monday, May 7, 2012 1:17:35 AM(UTC)
fool

Joined: 8/24/2011(UTC)
Posts: 34
Location: New Zealand

Thanks: 4 times
Originally Posted by: Benjamin Nitschke (DeltaEngine) Go to Quoted Post
Dunno how you implemented GetCurrentThreadId, maybe you are not passing the actual native thread id?


P/Invoke:

Code:

[DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId")]
private static extern UInt32 GetCurrentThreadId();


I look forward to the nightly... ;)
Offline Benjamin  
#11 Posted : Monday, May 7, 2012 2:29:47 AM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
I tested the issues a bit more and some of the code was not very stable. The GetCurrentThreadId method was also not very reliable because it only works in certain conditions, so I reverted to global hocks again. I prevented alt up key hocks to go through, which caused all applications to be in the alt state and stay there because they did only get the alt down key, not the up key.

So instead I tested each key and figured out the best code path for them. Windows keys need to be prevented globally and still have the lag issue, but this is no biggy because I don't think anyone is going to use the Windows key for anything with high accuracy. The alt keys were the most problematic because they also cause the app to lose focus, but the lag issue was even worse. Instead I let the CallNextHookEx do its work, which gets rid of any lag. Now I only needed to figure out when to set which key state and lots of if else code and testing was done .. finally I found out some other issues with adding keysUp and keysDown too many times in one update tick, which happened often for very low fps situations (5 fps) and caused keys not to be in the correct state.

Hopefully the code is now good enough, spend way too much time on this already (as always when native code is involved ^^).

Here is the body of the LowLevelCaptureKey in all its glory (you can check it out in v0.9.5.18):
Code:

		#region LowLevelCaptureKey
		/// <summary>
		/// Low level capture key for the keyboard hook to handle Windows, Alt,
		/// Shift and Control keys, which are not caputured very well by normal
		/// Windows forms or WPF keyboard handlers (they don't differentiate between
		/// left and right keys and they cannot prevent losing app focus).
		/// </summary>
		/// <param name="nCode">
		/// Capture code to handle (we handle all above 0).
		/// </param>
		/// <param name="wp">Pointer to the first pointer (unused here)</param>
		/// <param name="lp">Pointer to the KBDLLHOOKSTRUCT</param>
		/// <returns>
		/// Pointer, but basically 0 if not successful, otherwise this method
		/// handled the capture key.
		/// </returns>
		private IntPtr LowLevelCaptureKey(int nCode, IntPtr wp, IntPtr lp)
		{
			if (nCode >= 0)
			{
				KBDLLHOOKSTRUCT objKeyInfo = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(
					lp, typeof(KBDLLHOOKSTRUCT));

				// Handle RShiftKey, LShiftKey, LMenu, RMenu, LControlKey, RControlKey
				// directly here and skip them in OnFormsWindowKeyDown, which does not
				// differentiate between left and right.
				//Log.Info(
				//  "LowLevelCaptureKey nCode=" + nCode + ", wp=" + wp + ", lp=" + lp +
				//  ", objKeyInfo.key=" + objKeyInfo.key + ", GetState(key)" +
				//  GetState(GetInputButtonFromKeyCode(objKeyInfo.key)), false);
				if (objKeyInfo.key == Keys.RShiftKey ||
						objKeyInfo.key == Keys.LShiftKey ||
						objKeyInfo.key == Keys.LMenu ||
						objKeyInfo.key == Keys.RMenu ||
						objKeyInfo.key == Keys.LControlKey ||
						objKeyInfo.key == Keys.RControlKey ||
						// Also Disable the Windows keys from stealing our app focus
						objKeyInfo.key == Keys.RWin ||
						objKeyInfo.key == Keys.LWin)
				{
					// Mark that we have data for the Update method.
					HasData = true;
					// Grab and convert the key code, which maps with our InputButton
					InputButton key = GetInputButtonFromKeyCode(objKeyInfo.key);
					// Is the key up or down?
					if ((objKeyInfo.flags & 0x80) != 0)
					{
						// Alt is handled in OnFormsWindowKeyUp!
						if (objKeyInfo.key != Keys.RMenu &&
							objKeyInfo.key != Keys.LMenu)
						{
							if (keysUp.Contains(key) == false)
							{
								keysUp.Add(key);
							}
						}
					}
					else
					{
						if (keysDown.Contains(key) == false)
						{
							keysDown.Add(key);
						}
					}

					// And don't handle Windows keys it any further (will not be handled
					// by Windows and also won't get to the keyboard events above).
					// Note: When using global hocks this produces a lot of lag!
					if (objKeyInfo.key == Keys.RWin ||
					    objKeyInfo.key == Keys.LWin)
					{
						return (IntPtr)1;
					}
				}
			} // if

			// Else use the normal keyboard handling hook
			return CallNextHookEx(lowLevelKeyboardHook, nCode, wp, lp);
		}
		#endregion
Offline fool  
#12 Posted : Monday, May 7, 2012 5:21:44 AM(UTC)
fool

Joined: 8/24/2011(UTC)
Posts: 34
Location: New Zealand

Thanks: 4 times
So I've had a play with the new nightly and it still has the following problem:

Originally Posted by: fool Go to Quoted Post
Using the sequence above there is a noticeable lag (of about 1 second) when pressing and holding Left Ctrl before 'Control' is reported as 'IsPressed'. The same lag does not happen when pressing and holding Right Ctrl. Actually, if I change the order of the sequence to be Right Ctrl, then Left Ctrl, then Right Ctrl... the lag to update 'Control' status happens with Right Ctrl and not Left Ctrl. WTF?


But it's slightly different. With the much slower frame rate I can clearly see the state transitions. When Left Ctrl is pressed for the third time it makes the following transitions:

NotPressed -> Pressed -> IsPressed

but Control makes the following transitions:

NotPressed -> Pressed -> NotPressed -> IsPressed

and so appears to lag Left Ctrl by about 1 second.

This happens for Ctrl, Alt, and Shift. The mystery to me is why it only happens after I use both Left and Right keys.

Oh and the Windows keys seem to be stuffed now. Pressing and holding them causes what seems to be massive lag, leaving the app and Windows in general to be unusable.

Sorry to bring it up again, but could you post the code for BaseKeyboard.HandleCombinedKeys so we can eliminate it from the suspect list?
Rss Feed  Atom Feed
Users browsing this topic
OceanSpiders 2.0
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

Powered by YAF.NET | YAF.NET © 2003-2023, Yet Another Forum.NET
This page was generated in 0.124 seconds.