Code:
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#define WIN_32_LEAN_AND_MEAN
#include <windows.h>
#include <strsafe.h>
#define MAX_THREADS 6
/* Key-input macro and usage credit goes to : C pong program ( need help smoothing animations ) */
#define VKEY_IS_PRESSED(vk) ( GetAsyncKeyState(vk) & 0x8000 )
enum v_key
{
up_arrow = VK_UP,
down_arrow = VK_DOWN,
right_arrow = VK_RIGHT,
left_arrow = VK_LEFT,
clearscreen = VK_BACK,
escape = VK_ESCAPE
};
typedef struct shared_game_data
{
DWORD value;
} shared_game_data;
void * data = NULL;
HANDLE h_threads[MAX_THREADS] = {0};
HANDLE up_arrow_event = 0;
HANDLE down_arrow_event = 0;
HANDLE right_arrow_event = 0;
HANDLE left_arrow_event = 0;
HANDLE clear_screen_event = 0;
HANDLE escape_event = 0;
CRITICAL_SECTION critical_section;
/* Credit to Microsoft : http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx */
void cls( HANDLE hConsole )
{
COORD coordScreen = { 0, 0 }; // home for the cursor
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
// Get the number of character cells in the current buffer.
if( !GetConsoleScreenBufferInfo( hConsole, &csbi ))
{
return;
}
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
// Fill the entire screen with blanks.
if( !FillConsoleOutputCharacter( hConsole, // Handle to console screen buffer
(TCHAR) ' ', // Character to write to the buffer
dwConSize, // Number of cells to write
coordScreen, // Coordinates of first cell
&cCharsWritten ))// Receive number of characters written
{
return;
}
// Get the current text attribute.
if( !GetConsoleScreenBufferInfo( hConsole, &csbi ))
{
return;
}
// Set the buffer's attributes accordingly.
if( !FillConsoleOutputAttribute( hConsole, // Handle to console screen buffer
csbi.wAttributes, // Character attributes to use
dwConSize, // Number of cells to set attribute
coordScreen, // Coordinates of first cell
&cCharsWritten )) // Receive number of characters written
{
return;
}
// Put the cursor at its home coordinates.
SetConsoleCursorPosition( hConsole, coordScreen );
}
/* Credit to Microsoft : http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx */
void ErrorExit(LPTSTR lpszFunction)
{
LPVOID lp_msg_buf;
LPVOID lp_display_buf;
DWORD dw = GetLastError();
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lp_msg_buf,
0,
NULL
);
lp_display_buf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lp_msg_buf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lp_display_buf, LocalSize(lp_display_buf) / sizeof(TCHAR), TEXT("%s failed with error %d: %s"), lpszFunction, dw, lp_msg_buf);
MessageBox(NULL, (LPCTSTR)lp_display_buf, TEXT("Error"), MB_OK);
LocalFree(lp_msg_buf);
LocalFree(lp_display_buf);
ExitProcess(dw);
}
void close_events( )
{
CloseHandle(up_arrow_event);
CloseHandle(down_arrow_event);
CloseHandle(right_arrow_event);
CloseHandle(left_arrow_event);
CloseHandle(escape_event);
return;
}
void close_threads( )
{
unsigned index;
for (index = 0; index < MAX_THREADS; index++)
{
CloseHandle(h_threads[index]);
}
return;
}
unsigned __stdcall event_loop( void * m_sleep )
{
const DWORD msleep = *(DWORD *)m_sleep;
for (;;)
{
if ( VKEY_IS_PRESSED(escape) )
{
if ( !SetEvent(escape_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-Escape"));
}
return 0;
}
if ( VKEY_IS_PRESSED(up_arrow) )
{
if ( !SetEvent(up_arrow_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-UpArrow"));
}
}
if ( VKEY_IS_PRESSED(down_arrow) )
{
if ( !SetEvent(down_arrow_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-DownArrow"));
}
}
if ( VKEY_IS_PRESSED(right_arrow) )
{
if ( !SetEvent(right_arrow_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-RightArrow"));
}
}
if ( VKEY_IS_PRESSED(left_arrow) )
{
if ( !SetEvent(left_arrow_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-LeftArrow"));
}
}
if ( VKEY_IS_PRESSED(clearscreen) )
{
if ( !SetEvent(clear_screen_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-ClearScreen"));
}
}
Sleep(msleep);
}
return 1;
}
unsigned __stdcall left_arrow_handler( void * parameter )
{
DWORD return_value = 0;
HANDLE objects[2] = {left_arrow_event, escape_event};
shared_game_data * my_data = (shared_game_data *)parameter;
for ( ; ; )
{
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
EnterCriticalSection(&critical_section);
printf("Left-arrow-handler, I.D. : %d\n", (int)GetCurrentThreadId());
my_data->value -= 10;
printf("Value : %d\n", (int)my_data->value);
LeaveCriticalSection(&critical_section);
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
unsigned __stdcall right_arrow_handler( void * parameter )
{
DWORD return_value = 0;
HANDLE objects[2] = {right_arrow_event, escape_event};
shared_game_data * my_data = (shared_game_data *)parameter;
for ( ; ; )
{
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
EnterCriticalSection(&critical_section);
printf("Right-arrow-handler, I.D. : %d\n", (int)GetCurrentThreadId());
my_data->value += 10;
printf("Value : %d\n", (int)my_data->value);
LeaveCriticalSection(&critical_section);
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
unsigned __stdcall down_arrow_handler( void * parameter )
{
DWORD return_value = 0;
HANDLE objects[2] = {down_arrow_event, escape_event};
shared_game_data * my_data = (shared_game_data *)parameter;
for ( ; ; )
{
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
EnterCriticalSection(&critical_section);
printf("Down-arrow-handler, I.D. : %d\n", (int)GetCurrentThreadId());
my_data->value /= 10;
printf("Value : %d\n", (int)my_data->value);
LeaveCriticalSection(&critical_section);
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
unsigned __stdcall up_arrow_handler( void * parameter )
{
DWORD return_value = 0;
HANDLE objects[2] = {up_arrow_event, escape_event};
shared_game_data * my_data = (shared_game_data *)parameter;
for ( ; ; )
{
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
EnterCriticalSection(&critical_section);
printf("Up-arrow-handler, I.D. : %d\n", (int)GetCurrentThreadId());
my_data->value *= 10;
printf("Value : %d\n", (int)my_data->value);
LeaveCriticalSection(&critical_section);
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
unsigned __stdcall clear_screen_handler( void * parameter )
{
UNREFERENCED_PARAMETER(parameter);
HANDLE h_stdout = NULL;
h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD return_value = 0;
HANDLE objects[2] = {clear_screen_event, escape_event};
for ( ; ; )
{
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
cls(h_stdout);
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
void init_events ( )
{
/* Create our keyboard events */
up_arrow_event = CreateEvent( NULL, TRUE, FALSE, TEXT("UpArrowEvent") );
if (!up_arrow_event)
ErrorExit(TEXT("CreateEvent 1"));
down_arrow_event = CreateEvent( NULL, TRUE, FALSE, TEXT("DownArrowEvent") );
if (!down_arrow_event)
ErrorExit(TEXT("CreateEvent 2"));
right_arrow_event = CreateEvent( NULL, TRUE, FALSE, TEXT("RightArrowEvent") );
if (!right_arrow_event)
ErrorExit(TEXT("CreateEvent 3"));
left_arrow_event = CreateEvent( NULL, TRUE, FALSE, TEXT("LeftArrowEvent") );
if (!left_arrow_event)
ErrorExit(TEXT("CreateEvent 4"));
escape_event = CreateEvent( NULL, TRUE, FALSE, TEXT("EscapeEvent") );
if (!escape_event)
ErrorExit(TEXT("CreateEvent 5"));
clear_screen_event = CreateEvent( NULL, TRUE, FALSE, TEXT("ClearScrenEvent") );
if(!clear_screen_event)
ErrorExit(TEXT("CreateEvent 6"));
return;
}
inline shared_game_data * create_shared_game_data_node( shared_game_data * data )
{
data = malloc(sizeof(shared_game_data));
if (!data)
{
perror("Malloc");
ExitProcess(1);
}
return data;
}
inline shared_game_data * inigame_data_node( shared_game_data * data, DWORD value )
{
data->value = value;
return data;
}
inline void create_threads( void * data, unsigned * thread_id )
{
DWORD msleep = 50;
void * sleep = &msleep;
h_threads[0] = (HANDLE)_beginthreadex(NULL, 0, &event_loop, sleep, 0, &thread_id[0] );
h_threads[1] = (HANDLE)_beginthreadex(NULL, 0, &left_arrow_handler, data, 0, &thread_id[1] );
h_threads[2] = (HANDLE)_beginthreadex(NULL, 0, &right_arrow_handler, data, 0, &thread_id[2] );
h_threads[3] = (HANDLE)_beginthreadex(NULL, 0, &up_arrow_handler, data, 0, &thread_id[3] );
h_threads[4] = (HANDLE)_beginthreadex(NULL, 0, &down_arrow_handler, data, 0, &thread_id[4] );
h_threads[5] = (HANDLE)_beginthreadex(NULL, 0, &clear_screen_handler, data, 0, &thread_id[5] );
return;
}
void cleanup( )
{
close_threads();
DeleteCriticalSection(&critical_section);
close_events();
free(data);
return;
}
int main( )
{
if (atexit(cleanup))
{
perror("Atexit");
ExitProcess(1);
}
unsigned thread_id[MAX_THREADS] = {0};
if (!InitializeCriticalSectionAndSpinCount(&critical_section, 0x0000050))
ErrorExit(TEXT("InitialCriticalSectionAndSpinCount"));
init_events();
shared_game_data * game_data = create_shared_game_data_node( data );
game_data = inigame_data_node( game_data, 2 );
data = game_data;
create_threads( data, thread_id );
WaitForMultipleObjects(MAX_THREADS, h_threads, TRUE, INFINITE);
return 0;
}
The code I made above basically makes 6 threads that handle and coordinate keyboard events, and edit a value in shared memory according to the key pressed. It works, but it's super sensitive to keyboard input, and fires the event 2-3 times if I'm not careful. Is there any good way to make it not fire the event as much?