- Online Community Forums » Programming Questions » Is it possible to generate keyboard events when running in the background?

This thread is locked; no one can reply to it. rss feed Print
Is it possible to generate keyboard events when running in the background?
Member #8,794
July 2007

I'm trying to write a small program to record my input on another program. I decided to use Allegro 5's event system so that I don't lose events. It works great since even if I have an al_rest(1.0), the program catches all events. The problem is that it only works if the program window has the focus. If I switch to another window, the program no longer catches keyboard events even though it keeps running in the background.

I know this is the normal behavior but is there a way to force an Allegro 5.0.4 program to keep generating keyboard events even if it's window doesn't have the focus?.


[EDIT:] Forgot to mention, I want to do this on Windows XP.

Member #7,919
October 2006

This is a very key logger way to do things.

A typical message is passed through the programs that have focus. I'd pass a message to the foremost program and wait for its response to that key press. If it returns TRUE that it had handled it, nothing else receives that message. If it returns false, it means that it didn't understand how to process it and is giving it back so someone else "higher up" can try to process it.

In many cases, the OS itself blocks this message from reaching your background-suspended program. The only exceptions to this occur when you have drivers that place hooks on the system events. These always get processed, and CAN block the message from going any further, although key loggers are typically smart enough to let the key press actually reach their destination or the user would quickly know something was wrong when they start entering their bank information and nothing is appearing in their browser window.

Can you describe why you'd want to keep receiving those messages? If I play your game, then switch to write an email, I wouldn't want my WASD keys to start moving a ship around while I can't see it (and where it shouldn't even be accepting inputs).

Member #8,794
July 2007

OnlineCop said:

Can you describe why you'd want to keep receiving those messages?

I'm playing a game I downloaded and I want to record my input on that game so that I can replay it later (and probably fine tune it).

A while ago I tried to do the same thing with Allegro 4. On Allegro 4 the program keeps receiving keyboard input if you use set_display_switch_mode(SWITCH_BACKGROUND) but in order to not lose any key presses you must not rest() so the program takes 100% of the CPU and slows the actual game (maybe this wouldn't be an issue if I had a 2 cores CPU).

With Allegro 5's event system it seemed like catching the key presses would require practically no CPU usage but, unlike Allegro 4, it doesn't have an option like set_display_switch_mode().

I tried the Windows API function AttachThreadInput() but looks like it doesn't do what I expected it to. Right now I'm watching an example on how to set a keyboard hook.

Oscar Giner
Member #2,207
April 2002

If you're interesting just in the end result (you don't really care about learning how to do this), there are several programs, some free, that allow you to do this.

Member #8,794
July 2007

I want to be able to customize it for my needs while also learning how to do it.

After searching and trying some of the examples without luck, I decided to look at the code of MyHook, a really simple keylogger, and found that the keyboard hook code was OK but in order to actually get to the callback, I had to explicitly handle the Windows' messages (at least that's what MyHook does and it's the only method I got to work until now).

I made a few more changes to my code and I got it to work although not quite as I expected. Only key events performed when the Allegro 5 window is active are inserted into the Allegro event queue; key events performed when the Allegro 5 window is inactive are detected by the hook callback function but are not inserted into the Allegro event queue. CPU usage is low so I guess this might work.

I'll clean the mess in my code and post it here.

Member #12,491
January 2011

I think you might want to look into user events. You could have the hook callback function throw one of those.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Member #8,794
July 2007

Yes, that's what I did. This is the sample code I'm using for setting the keyboard hook. I marked the lines that do the actual work for the keyboard hook.

1#include <allegro5/allegro.h> 2#include <allegro5/allegro_opengl.h> 3#include <allegro5/allegro_windows.h> 4#include <allegro5/allegro_primitives.h> 5 6 7 8#define WMSG_EVENT ALLEGRO_GET_EVENT_TYPE('W','M','S','G') // Base identifier for Windows MSG events 9#define WMSG_KEYDOWN (WMSG_EVENT + 0) 10#define WMSG_KEYUP (WMSG_EVENT + 1) 11#define WMSG_OTHER (WMSG_EVENT + 2) 12 13 14 15ALLEGRO_DISPLAY *display = NULL; 16ALLEGRO_EVENT_QUEUE *events = NULL; 17ALLEGRO_TIMER *timer = NULL; 18ALLEGRO_EVENT event; 19ALLEGRO_KEYBOARD_STATE state; 20 21HHOOK kbHook; 22HINSTANCE appInstance; 23MSG msg; 24 25ALLEGRO_EVENT_QUEUE *uevents; // Event queue for user events 26ALLEGRO_EVENT_SOURCE usource; // User event source 27int redraw, quit = FALSE; 28 29 30 31LRESULT CALLBACK LowLevelKeyboardProc( int nCode, WPARAM wParam, LPARAM lParam ) { 32 KBDLLHOOKSTRUCT *pKeyBoard = (KBDLLHOOKSTRUCT *)lParam; 33 DWORD vkCode; 34 35 switch (wParam) { 36 case WM_KEYDOWN: 37 case WM_KEYUP: 38 vkCode = pKeyBoard->vkCode; 39 //printf("%c %d\n", vkCode, pKeyBoard->time); 40 event.user.type = wParam == WM_KEYDOWN ? WMSG_KEYDOWN : WMSG_KEYUP; 41 event.user.data1 = vkCode; 42 event.user.data2 = pKeyBoard->time; 43 event.user.data3 = MapVirtualKey(vkCode, 0); // MAPVK_VK_TO_VSC 44 event.user.data4 = pKeyBoard->scanCode; 45 if (!al_emit_user_event(&usource, &event, NULL)) puts("Event not delivered"); 46 break; 47 default: 48 return CallNextHookEx( NULL, nCode, wParam, lParam ); 49 } 50 51 return 0; 52} 53 54 55 56int main() { 57 assert(al_init()); 58 assert(al_install_keyboard()); 59 assert(al_install_mouse()); 60 al_set_new_display_flags(ALLEGRO_WINDOWED | ALLEGRO_OPENGL); 61 assert(display = al_create_display(640, 480)); 62 assert(al_init_primitives_addon()); 63 64 assert(timer = al_create_timer(ALLEGRO_BPS_TO_SECS(30))); 65 al_start_timer(timer); 66 assert(events = al_create_event_queue()); 67 al_register_event_source(events, al_get_timer_event_source(timer)); 68 al_register_event_source(events, al_get_keyboard_event_source()); 69 70 al_init_user_event_source(&usource); 71 assert(uevents = al_create_event_queue()); 72 al_register_event_source(uevents, &usource); 73 74// Sets a global keyboard hook
75 appInstance = GetModuleHandle(NULL);
76 kbHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, appInstance, 0);
77 if (kbHook != NULL) puts("Keyboard hook succesfully installed"); 78 else { 79 puts("Keyboard hook installation failed"); 80 quit = TRUE; 81 } 82 83 al_clear_to_color(al_map_rgb(0, 192, 192)); 84 while (!quit) { 85 // Wait for a timer event 86 al_wait_for_event(events, &event); 87 if(event.type == ALLEGRO_EVENT_TIMER) 88 redraw = TRUE; 89 else if (event.type == ALLEGRO_EVENT_KEY_UP && event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) 90 quit = TRUE; 91 // Keyboard hook logic
92 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) {
93 //while (GetMessage(&msg, NULL, 0, 0) > 0) { 94 //TranslateMessage(&msg);
95 DispatchMessage(&msg);
96 }
97 // Drawing 98 if (redraw && al_is_event_queue_empty(events)) { 99 al_flip_display(); 100 redraw = FALSE; 101 } 102 } 103 104 if (!UnhookWindowsHookEx(kbHook)) puts ("Can't remove keyboard hook"); 105 else puts("Keyboard hook removed successfully"); 106 107 while (!al_is_event_queue_empty(uevents)) { 108 int lParam; 109 char keyName[80]; 110 111 al_get_next_event(uevents, &event); 112 lParam = event.user.data4 << 16; 113 GetKeyNameText(lParam, keyName, 80); 114 // Print data: Event type | Timestamp | Windows Virtual Key Code | Windows key name 115 printf("%5d %d %d %s\n", event.type, event.user.data2, event.user.data1, keyName); 116 } 117 al_destroy_user_event_source(&usource); 118}

By using PeekMessage() the Allegro program keeps running normally so it's responsive to, for example, adding a GUI to start/stop recording input. GetMessage(), on the other hand, waits for a message. Both have it's pros and cons.

Some of the fields I'm filling in the user events are redundant; that's because I'm experimenting.

Now I want to save the input to a file. I'm planning to simply write the contents of the event variable as I go through the user event queue by saving sizeof(ALLEGRO_EVENT) bytes at a time. Then I should be able to read them back into an ALLEGRO_EVENT variable, right?.

Go to: