Allegro.cc - Online Community
Post Reply

Allegro.cc Forums » Programming Questions » Creating a Allegro 5 equivalent to _kbhit()

rss feed Print
Creating a Allegro 5 equivalent to _kbhit()
nshade
Member #4,372
February 2004

I'm in the process of replacing Microsoft CRT functions with allegro equivalents to remove my dependencies on console functions. One of them is _kbhit which is an ancient legacy command from conio.h

Here is the function definition from Microsoft:
The _kbhit function checks the console for a recent keystroke. If the function returns a nonzero value, a keystroke is waiting in the buffer. The program can then
call _getch or _getche to get the keystroke.

Here's my stab to do the same thing:

int keyboardhit()
{
  
  if (al_peek_next_event(kbdQueue, &kbdState))
  {
    return 1;  //doesn't really matter what was hit
  }
  return 0;
}

I then will write my own _getch() to get the next key, but it's not quite working the way it's supposed to.

I tried an attempt using microsoft's exmaple.

//keyboardhit() replaced _kbhit()
  while (!keyboardhit())
    _cputs("Hit me!! ");

and it's not detecting the keypress. Am I missing something here?

Here's my whole example code

#SelectExpand
1#include <stdio.h> 2#include <allegro5/allegro.h> 3 4ALLEGRO_EVENT_QUEUE *kbdQueue = NULL; //!< Keyboard Queue 5ALLEGRO_KEYBOARD_STATE kbdState; //!< Keybord State 6 7 8int keyboardhit() 9{ 10 11 if (al_peek_next_event(kbdQueue, &kbdState)) { return 1; } 12 else return 0; 13} 14 15 16int main(int argc, char **argv) { 17 18 19 if (!al_init()) { 20 fprintf(stderr, "failed to initialize allegro!\n"); 21 return -1; 22 } 23 if (!al_install_keyboard()) { 24 fprintf(stderr, "Error installing keyboard.\n"); 25 return 1; 26 } 27 28 //Create Keybord queue 29 kbdQueue = al_create_event_queue(); 30 if (!kbdQueue) { 31 fprintf(stderr, "failed to create keyboard queue!\n"); 32 return 1; 33 } 34 al_register_event_source(kbdQueue, al_get_keyboard_event_source()); 35 36 while (!keyboardhit()) 37 _cputs("Hit me!! "); 38 39 return 0; 40}

Neil Roy
Member #2,229
April 2002
avatar

I use the following code when I need to wait for a keypress... this is an Allegro 5 function I created for Deluxe Pacman 2 (see link below), it will also respond to a mouse click or joystick input (see comments in code).

I added on additional functionality to this where you can send a wait_time in milliseconds so it will wait for a keypress OR however many seconds you want. So if you call it with wait_for_keypress(5000.0) It will wait 5000ms or 5 seconds and then continue on. If you want it to wait indefinitely, pass a zero to it and it will wait for input and not move.

#SelectExpand
1// Waits a specified time for a key to be pressed. 2// Will also respond if a mouse or joystick button is clicked. 3// If the time passes before a key/mouse/joystick is pressed, the function returns 4// If the time specified is zero, the function will wait indefinitely. 5bool wait_for_keypress(double wait_time) 6{ 7 double ts = al_get_time(); // time stamp 8 double elapsed_time; 9 10 al_flush_event_queue(event_queue); 11 frames = 0; 12 13 bool done = false; 14 15 // Wait until the specified time passes or a key is pressed. 16 do { 17 al_wait_for_event(event_queue, &event); 18 19 if(event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { 20 shut_down(); 21 exit(0); 22 } 23 24 else if(wait_time && event.type == ALLEGRO_EVENT_TIMER) { 25 elapsed_time = al_get_time() - ts; 26 if(elapsed_time >= wait_time) return false; 27 } 28 29 // Wait for key to be pressed and released to continue 30 else if(event.type == ALLEGRO_EVENT_KEY_UP) { 31 if(event.keyboard.keycode == ALLEGRO_KEY_F12) { 32 if(setting.sound_on) 33 al_play_sample(sfx_camera, setting.sound_volume, 0, 1, ALLEGRO_PLAYMODE_ONCE, NULL); 34 a5_screenshot("Screenshots/DeluxePacman2"); 35 } 36 else if(event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) return true; 37 else done = true; 38 } 39 40 // And if a key wasn't pressed, maybe a mouse button was... 41 else if(event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) { 42 done = true; 43 } 44 45 /// JOYSTICK 46 else if(event.type == ALLEGRO_EVENT_JOYSTICK_BUTTON_UP) { 47 if(event.joystick.button == 0) done = true; // Button 0 = A (green, like pressing ENTER) 48 if(event.joystick.button == 1) return true; // Button 1 = B (red, used like pressing ESC) 49 // Button 7 = Start (unused here) 50 } 51 52 else if(event.type == ALLEGRO_EVENT_JOYSTICK_AXIS) { 53 done = true; 54 } 55 56 else if(event.type == ALLEGRO_EVENT_JOYSTICK_CONFIGURATION) { 57 al_reconfigure_joysticks(); 58 } 59 } 60 while(!done); 61 62 return false; 63}

Edgar Reynaldo
Member #8,592
May 2007
avatar

nshade said:

ALLEGRO_EVENT_QUEUE *kbdQueue = NULL; //!< Keyboard Queue 
ALLEGRO_KEYBOARD_STATE kbdState; //!< Keybord State 

int keyboardhit() {
 if (al_peek_next_event(kbdQueue, &kbdState)) { return 1; }
 else return 0;
}

That code shouldn't even compile. You're passing an ALLEGRO_KEYBOARD_STATE* to a function parameter that is expecting an ALLEGRO_EVENT*.

To keep in the fashion of _kbhit, you can use functions like these, in the same way you were attempting to. You just need to keep a separate queue of events, or at least store and remove them when they are received.

#SelectExpand
1 2#include "allegro5/allegro.h" 3 4#include <deque> 5#include <string> 6 7ALLEGRO_EVENT_QUEUE* q; 8 9std::deque<ALLEGRO_EVENT> kb_events; 10std::deque<ALLEGRO_EVENT> events; 11 12void poll_for_events() { 13 ALLEGRO_EVENT ev; 14 while (al_get_next_event(q , &ev)) { 15 if (ev.type == ALLEGRO_EVENT_KEY_DOWN || ev.type == ALLEGRO_EVENT_KEY_UP || ev.type == ALLEGRO_EVENT_KEY_CHAR) { 16 kb_events.push_back(ev); 17 } 18 else { 19 events.push_back(ev); 20 } 21 } 22} 23 24int poll_kb_for_events() { 25 poll_for_events(); 26 return kb_events.size(); 27} 28 29int poll_for_non_kb_events() { 30 poll_for_events(); 31 return events.size(); 32} 33 34int main(int argc , char** argv) { 35 36 if (!al_init()) {return 1;} 37 if (!al_install_keyboard()) {return 2;} 38 39/// al_set_new_window_position(-500,-400); 40 al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_WINDOWED); 41 ALLEGRO_DISPLAY* d = al_create_display(400,300); 42 43 q = al_create_event_queue(); 44 45 al_register_event_source(q , al_get_keyboard_event_source()); 46 al_register_event_source(q , al_get_display_event_source(d)); 47 48 while (1) { 49 al_rest(0.015); 50 if (poll_kb_for_events()) { 51 ALLEGRO_EVENT ev = kb_events.back(); 52 if (ev.type == ALLEGRO_EVENT_KEY_UP && ev.keyboard.keycode == ALLEGRO_KEY_ENTER) { 53 break; 54 } 55 } 56 } 57 58 std::string s = ""; 59 while (!kb_events.empty()) { 60 ALLEGRO_EVENT ev = kb_events.front(); 61 kb_events.pop_front(); 62 if (ev.type == ALLEGRO_EVENT_KEY_CHAR && ev.keyboard.keycode != ALLEGRO_KEY_ENTER) { 63 s += ev.keyboard.unichar; 64 } 65 } 66 67 printf("You typed : '%s'\n" , s.c_str()); 68 69 al_uninstall_system(); 70 71 return 0; 72}

nshade
Member #4,372
February 2004

Thanks for the push in the right direction - I was sick yesterday... Also I'm not using C++, so I can't use namespaces.

I was writing kind of off the cuff. I'm making a little lab project where I can rewrite replace getch and kbhit with a little test unit.

==EDIT==

Something is up, I don't know if it's my foggy head and leftover flu, but I can't seem to get any keyboard input wot work anymore. Even your examples. I have a very basic system her just to get something out of the event queue and it's not working at all. Is there something I'm missing?

I'm trying to do a "drop in" replacement for these two commands. Keep in mind, the program I'm porting does not exactly have a "main loop", but a bunch of machine states that are executed depending on globals. There is a lot of pausing and waiting for input without rendering anything. My code is below.

Why was the original keypress() functions removed from allegro 5? They seem really useful!

#SelectExpand
1 2#include <stdio.h> 3#include <allegro5/allegro.h> 4 5ALLEGRO_EVENT master_event; //!< Master Allegro event handler 6ALLEGRO_EVENT_QUEUE *kbdQueue; //!< Keyboard Queue 7ALLEGRO_KEYBOARD_STATE kbdState; //!< Keybord State 8 9int al_kbhit(void) 10{ 11 12 if (al_peek_next_event(kbdQueue, &master_event)) 13 { 14 return 1; //doesn't really matter what was hit 15 } 16 return 0; 17 // return _kbhit(); 18} 19 20int al_getch(void) 21{ 22 return _getch(); 23} 24 25 26void init_keyboard() 27{ 28 if (!al_init()) { 29 fprintf(stderr, "failed to initialize allegro!\n"); 30 exit(1); 31 } 32 if (!al_install_keyboard()) { 33 fprintf(stderr, "Error installing keyboard.\n"); 34 exit(1); 35 } 36 37 al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_WINDOWED); 38 d = al_create_display(400, 300); 39 40 //Create Keybord queue 41 kbdQueue = al_create_event_queue(); 42 if (!kbdQueue) { 43 fprintf(stderr, "failed to create keyboard queue!\n"); 44 exit(1); 45 } 46 al_register_event_source(kbdQueue, al_get_keyboard_event_source()); 47 48} 49 50 51 52int main(int argc, char **argv) { 53 54 char ch; 55 56 init_keyboard(); 57 while (!al_kbhit()) 58 printf("Hit me!\n"); 59 60 ch = al_getch(); 61 printf("Key struck was %d\n", ch); 62 63 while (1) {}; //Loop forever 64 65}

++ ANOTHER EDIT ++
My original code was not making an allegro display. It doesn't work in the terminal. Makes sense as I'm trying to get rid of the terminal :P Now the keyboard is working (eyeroll)

LAST EDIT

here is the code for others to use. Mind you, It's not 100% compatible, but works well enough for all. I woudn't mind this in the actual Allegro library if it was cleaned up enough :)

#SelectExpand
1#include <stdio.h> 2#include <allegro5/allegro.h> 3 4 5int keymap[] = { 6// NULL, A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 7 0, 65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90, 8 9// 0 1 2 3 4 5 6 7 8 9 K0 K1 K2 K3 K4 K5 K6 K7 K8 K9 F1 F2 F3 F4 F5 10 48, 49,50,51,52,53,54,55,56,57,48,49,50,51,52,53,54,55,56,57,1000,1001,1002,1003,1004, 11 12// F6 F7 F8 F9 F10 F11 F12 ESC ~ - = BS TAB [ ] ENT ; " \ \2 , . / SP 13 1005,1006,1007,1008,1009, 0, 0, 0, 0,45,43,8, 9, 91,93,13, 59,39,92, 0, 44,46,47,32, 14 15// INS DEL HOME END PGUP PGDN LFT RGT UP DWN K/ K* K- K+ K. KE PRSCN PAUSE 16 4000, 4001, 2007, 2006, 2004, 2005, 2003, 2001, 2000, 2002, 47, 42, 45, 43, 46, 13, 0, 0, 17 18// Scancodes 94-127 : Non-PC keys. Listing in Allegro documentation, ALLEGRO_KEY_UNKNOWN (128) 19 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0 20}; //!< Keymapping from scancodes to ASCII or function codes 21 22 23ALLEGRO_EVENT master_event; //!< Master Allegro event handler 24ALLEGRO_EVENT_QUEUE *kbdQueue; //!< Keyboard Queue 25ALLEGRO_KEYBOARD_STATE kbdState; //!< Keybord State 26 27ALLEGRO_DISPLAY *d; 28int key_capture; 29 30int al_kbhit(void) 31{ 32 // return _kbhit(); 33 34 if (al_peek_next_event(kbdQueue, &master_event)) 35 { 36 if (master_event.type == ALLEGRO_EVENT_KEY_DOWN || master_event.type == ALLEGRO_EVENT_KEY_UP) 37 { 38 al_drop_next_event(kbdQueue); //remove all events but the keys 39 return 0; 40 } 41 if (master_event.type == ALLEGRO_EVENT_KEY_CHAR) 42 { 43 key_capture = keymap[master_event.keyboard.keycode]; 44 return 1; 45 } 46 } 47 return 0; 48} 49 50int al_getch(void) 51{ 52 //return _getch(); 53 54 int key = key_capture; 55 key_capture = 0; 56 return key; 57} 58 59 60void startup() 61{ 62 if (!al_init()) { 63 fprintf(stderr, "failed to initialize allegro!\n"); 64 exit(1); 65 } 66 if (!al_install_keyboard()) { 67 fprintf(stderr, "Error installing keyboard.\n"); 68 exit(1); 69 } 70 71 d = al_create_display(400, 300); 72 73 //Create Keybord queue 74 kbdQueue = al_create_event_queue(); 75 if (!kbdQueue) { 76 fprintf(stderr, "failed to create keyboard queue!\n"); 77 exit(1); 78 } 79 al_register_event_source(kbdQueue, al_get_keyboard_event_source()); 80 81} 82 83 84 85int main(int argc, char **argv) { 86 87 int ch; 88 89 startup(); 90 while (!al_kbhit()) 91 printf("Hit me!\n"); 92 93 ch = al_getch(); 94 printf("Key struck was %d\n", ch); 95 96 while (1) {}; //Loop forever 97 98}

Neil Roy
Member #2,229
April 2002
avatar

Okay, you need to read through your code, and follow it along logically...

#SelectExpand
1int al_kbhit(void) 2{ 3 // return _kbhit(); 4 5 if (al_peek_next_event(kbdQueue, &master_event)) 6 { 7 if (master_event.type == ALLEGRO_EVENT_KEY_DOWN || master_event.type == ALLEGRO_EVENT_KEY_UP) 8 { 9 al_drop_next_event(kbdQueue); //remove all events but the keys 10 return 0; 11 } 12 if (master_event.type == ALLEGRO_EVENT_KEY_CHAR) 13 { 14 key_capture = keymap[master_event.keyboard.keycode]; 15 return 1; 16 } 17 } 18 return 0; 19}

If a person presses a key, what line will this code return at?
If a person releases a key, what line will this code return at?

You are checking for a key down event, then clearing the queue and exiting with a return 0. You are also checking for a key up event in the same code. So no matter what happens, no matter what key is pressed, it will ALWAYS return 0 with nothing.

WHat you want to do, is to check if a key is pressed, or released, up to you (you can respond as soon as it is pressed, or wait for the person to release it). Anyhow, once it is pressed, you do not return right away, you then need to check which key was pressed INSIDE that same code, and respond to it accordingly... like this... in pseudo code.

Was a key pressed?
{
Get key that was pressed and respond to it.
return
}
else no key pressed, check for other events
{
...code here...
}

You're checking if a key was pressed and then returning without doing anything at all. Check the example I showed you. It is in C, I clipped it straight out of my Deluxe Pacman 2 code, unedited, so it works. You will notice when I check to see if a key is released, if one was, than I immediately extract the key that was pressed and respond inside that loop.

Edgar Reynaldo
Member #8,592
May 2007
avatar

If you simply must do it in C, then just take advantage of allegro. If you get a key up or down event, discard it. If you get a char event leave it on the queue until _getch is called.

#SelectExpand
1ALLEGRO_EVENT_QUEUE* q;// only register the kb to this queue 2 3int _kbhit() { 4 ALLEGRO_EVENT ev; 5 while (al_peek_next_event(q , &ev)) { 6 if (ev.type != ALLEGRO_EVENT_KEY_CHAR) { 7 al_drop_next_event(q); 8 } 9 else { 10 return 1; 11 } 12 } 13 return 0; 14} 15 16int _getch() { 17 ALLEGRO_EVENT ev; 18 al_get_next_event(q , &ev); 19 if (ev.type != ALLEGRO_EVENT_KEY_CHAR) {return 0;} 20 return ev.keyboard.unichar; 21} 22 23int main(int argc , char** argv) { 24 25 if (!al_init()) {return 1;} 26 if (!al_install_keyboard()) {return 2;} 27 28 q = al_create_event_queue(); 29 al_register_event_source(q , al_get_keyboard_event_source()); 30 31 ALLEGRO_DISPLAY* d = al_create_display(400,300); 32 al_clear_to_color(al_map_rgb(255,255,255)); 33 al_flip_display(); 34 35 while (!_kbhit()) { 36 al_rest(0.001); 37 } 38 39 printf("You typed %p\n" , (void*)_getch()); 40 41 return 0; 42}

nshade
Member #4,372
February 2004

Thanks for all your input guys! You were right Neil, I wasn't thinking the logic though. But thanks especially to you Edgar! You have been overly helpful with all my silly questions to the point I feel like I'm being a bother. I guess it doesn't hurt I'm showing off my work and demonstrating I'm at least trying to get things right. I have gained such a larger grasp on Allegro with this project than I imagined I would.. Maybe I should be giving back to the community and helping out the newbies too.

Edgar Reynaldo
Member #8,592
May 2007
avatar

I gave you bad code. Let me fix it.

int _getch() {
   ALLEGRO_EVENT ev;

   if (ev.type != ALLEGRO_EVENT_KEY_CHAR) {return 0;}
   return ev.keyboard.unichar;
}

The above code could have passed back an uninitialized value for ev.keyboard.unichar in the case of ev.type being KEY_CHAR left over in memory.

int _getch() {
   ALLEGRO_EVENT ev;
   if (al_get_next_event(q , &ev)) {
      if (ev.type != ALLEGRO_EVENT_KEY_CHAR) {return 0;}
      return ev.keyboard.unichar;
   }
   return 0;
}

This makes sure you actually received an event before checking its fields.

Post Reply
Go to: