Input Logging

Forum for software developers to discuss BCI2000 software development
Locked
timo.veldt
Posts: 23
Joined: 12 Feb 2010, 04:08

Input Logging

Post by timo.veldt » 01 Apr 2010, 07:25

I've looking through the tutorial here, because I would like to be able to log a button press by the user. So I only want a keyboard logger.

In another project, I've worked with OIS, which I liked. So I started creating my own OSThread which would make use of OIS to capture keyboard activity. Now I see that OIS needs a handle to the application window during initialization.

My visual component of the application is created using the DisplayWindow of BCI2000.

The Question:
How can I obtain the window handle of a DisplayWindow (if it is even possible)?

mellinger
Posts: 1065
Joined: 12 Feb 2003, 11:06

Post by mellinger » 01 Apr 2010, 08:35

The DisplayWindow class inherits from GraphDisplay, which allows access to its DrawContext. The DrawContext, in turn, contains a handle or pointer to the underlying GUI drawing object. In the case of Qt, the underlying GUI drawing object is a QPaintDevice, which may be dynamically cast into a QWidget, allowing access to the native window handle through QWidget::winId().

So the code would be (assuming that pDisplay is a pointer to the DisplayWindow object):

Code: Select all

HWND hwnd = NULL;
QPaintDevice* pDevice = pDisplay->Context().handle;
QWidget* pWidget = dynamic_cast<QWidget*>( pDevice );
if ( pWidget )
  hwnd = pWidget->window()->winId();
if ( hwnd )
{
  // do stuff
  ...
}
If there are any further questions, let me know.
--Juergen

gschalk
Posts: 615
Joined: 28 Jan 2003, 12:37

keyboard

Post by gschalk » 01 Apr 2010, 19:17

Hi,

BCI2000 already has a keyboard logger implemented:

http://www.bci2000.org/wiki/index.php/U ... ging_Input

Gerv

timo.veldt
Posts: 23
Joined: 12 Feb 2010, 04:08

Post by timo.veldt » 07 Apr 2010, 09:38

Thanks to you both!

Unfortunately I already implemented quite a lot for OIS before I saw the message from Gerv. I'll keep it in mind!

Another question on the implementation.

I've created my own application(-filter) that makes an instance of my OISKeyboardListener. OISKeyboardListener inherits from OSThread and captures keyboard event every millisecond (I've already reduced this to every sample, but I don't think it matters(?)).

When I debug the application and I press a button the program should execute this code:

Code: Select all

mKeyboard->capture();
if(mKeyboard->isKeyDown(OIS::KC_SPACE)){
	bcievent << "SpacePressed " << 1 << std::endl;
}
else{
	bcievent << "SpacePressed " << 0 << std::endl;
}
Sleep(mSleepTime);
When I place a breakpoint at the the line that sets the state to 1, it immediately hits the breakpoint when I press the space bar (KC_SPACE) during a run. However, when opening the datafile, there is no record of the State SpacePressed ever being one (it's always zero).

[Edit]
When adding a line in the if-statement that prints "Space Pressed" to bciout (

Code: Select all

bciout << "Space pressed" << std::endl;
) my System log is flooded with the text (resulting in an unresponsive system in a worsed case scenario).
[/Edit]

Any suggestions?

mellinger
Posts: 1065
Joined: 12 Feb 2003, 11:06

Post by mellinger » 07 Apr 2010, 12:50

Recording of events currently only works within source modules. This is because event and signal timestamps need to share a common timebase, which is not the case for other modules when BCI2000 is being run distributed over a network. For more information, see this wiki page.

--Juergen

timo.veldt
Posts: 23
Joined: 12 Feb 2010, 04:08

Post by timo.veldt » 08 Apr 2010, 04:50

I gave up :) When implementing the OISKeyboardListener, I could not access the states so I tried an Event Pattern on it, making the application(-filter) listen to the KeyboardListener. Unfortunately this approach caused the KeyboardListener to access states at a time when this is not allowed.
Let alone the hassle of ensuring thread-safety between the application and the KeyboardListener.

So I tried to run my application with the built-in keyboardlogging. Following the link posted by Gerv, I found that I had to create the State variables myself (i.e. "KeyDown" and "KeyUp") before I could access the data in the data file.
When I fixed this, I noticed that the keycodes do not correspond with the codes given in the wiki (see the quoted link), so I looked on the internet and found a PDF file that does match the recorded data for my keyboard.

[Edit]
Is it possible to prevent mistakes when starting the program and turning keyboard logging on through code? I am aware that I can achieve the same effect by creating a batch file, but that still doesn't provide a guarantee that Keyboard logging is turned on.
[/Edit]

mellinger
Posts: 1065
Joined: 12 Feb 2003, 11:06

Post by mellinger » 12 Apr 2010, 09:00

Timo,

you don't need to create the state variables yourself. Once you specify --LogKeyboard=1 from the source module's command line, these states will be created. Otherwise, they will be omitted (see the KeyLogger's source code).

Also, the list of virtual key codes referenced by the wiki is correct. The numbers are given in hexadecimal, that's why they look different from the table in your PDF file. E.g., the space character has code 0x20=2*16+0*1=32. The reason why we link out to Microsoft's MSDN site rather than to an ASCII table elsewhere is that ASCII tables give codes for characters, whereas we are interested in keys on a keyboard, and their encoding is OS dependent. For keys that produce characters, the key code just happens to match the corresponding ASCII character code.

To ensure keyboard logging to be switched on, you could just check for the LogKeyboard parameter to be nonzero in your application filter's Preflight() function:

Code: Select all

PreflightCondition( Parameter( "LogKeyboard" ) != 0 );
--Juergen

Locked

Who is online

Users browsing this forum: No registered users and 2 guests