Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Multiple threads/displays, Event handling question

This thread is locked; no one can reply to it. rss feed Print
Multiple threads/displays, Event handling question
Aksel Huff
Member #16,201
February 2016

EDIT: See bottom of post for latest problem solving.

Hey all,

I am new to using threads, and I am trying to develop an application that has two displays. Following the ex_threads.c example, I have two displays working, one in the main thread and one in an ALLEGRO_THREAD. They each have their own ALLEGRO_TIMER, and ALLEGRO_EVENT_QUEUE.

My confusion is about how events are generated. I have my main display registered to accept mouse and keyboard events, and my second display not registered for either. For some reason that I do not understand, keyboard and mouse events that happen while my second display is in focus gets handled by my main thread and so affect my main display.

Is this behaviour correct?

My only thoughts about how to correct this would be to somehow return the display pointer from the thread so I can make sure not to handle any events where ev.display or ev.keyboard.display, etc. are the second display.

Thanks for any help.

My thread function:

#SelectExpand
1static void *thread_func(ALLEGRO_THREAD* thr, void* arg) 2{ 3 ALLEGRO_DISPLAY* display = nullptr; 4 ALLEGRO_EVENT_QUEUE* evq = nullptr; 5 ALLEGRO_TIMER* timer = nullptr; 6 ALLEGRO_EVENT ev; 7 bool running = true; 8 bool redraw = true; 9 10 display = createDisplay("Viewer", DEFAULT_WIND_WIDTH, DEFAULT_WIND_HEIGHT, ALLEGRO_RESIZABLE | ALLEGRO_WINDOWED); 11 timer = al_create_timer(1.0 / 60.0); 12 evq = al_create_event_queue(); 13 14 al_register_event_source(evq, al_get_timer_event_source(timer)); 15 al_register_event_source(evq, al_get_display_event_source(display)); 16 17 al_start_timer(timer); 18 while (running) 19 { 20 if (al_get_thread_should_stop(thr)) 21 { 22 break; 23 } 24 25 al_wait_for_event(evq, &ev); 26 27 switch (ev.type) 28 { 29 case ALLEGRO_EVENT_TIMER: 30 redraw = true; 31 break; 32 33 case ALLEGRO_EVENT_DISPLAY_CLOSE: 34 running = false; 35 break; 36 37 case ALLEGRO_EVENT_DISPLAY_RESIZE: 38 al_acknowledge_resize(display); 39 break; 40 41 default: 42 break; 43 } 44 45 if (al_event_queue_is_empty(evq) && redraw) 46 { 47 al_clear_to_color(al_map_rgb(45, 45, 45)); 48 al_flip_display(); 49 redraw = false; 50 } 51 } 52 53 al_destroy_timer(timer); 54 al_destroy_event_queue(evq); 55 al_destroy_display(display); 56 57 return NULL; 58}

Main:

#SelectExpand
1int main(int argc, char ** argv) 2{ 3 ALLEGRO_DISPLAY *main_display = nullptr; 4 ALLEGRO_EVENT_QUEUE *ev_queue = nullptr; 5 ALLEGRO_TIMER *timer = nullptr; 6 ALLEGRO_THREAD *view_thread = nullptr; 7 8 bool redraw = true; 9 bool quit = false; 10 11 if (!al_init()) 12 { 13 std::cerr << "Failed to load Allegro!" << std::endl; 14 exit(EXIT_FAILURE); 15 } 16 17 main_display = createDisplay("Editor", DEFAULT_WIND_WIDTH, DEFAULT_WIND_HEIGHT, ALLEGRO_WINDOWED | ALLEGRO_RESIZABLE); 18 19 al_init_image_addon(); 20 al_init_font_addon(); 21 al_init_ttf_addon(); 22 al_init_primitives_addon(); 23 al_init_native_dialog_addon(); 24 25 timer = al_create_timer(1.0 / 60.0); 26 ev_queue = al_create_event_queue(); 27 28 InputHandler m_input; // Installs keyboard and mouse 29 30 al_register_event_source(ev_queue, al_get_keyboard_event_source()); 31 al_register_event_source(ev_queue, al_get_mouse_event_source()); 32 al_register_event_source(ev_queue, al_get_timer_event_source(timer)); 33 al_register_event_source(ev_queue, al_get_display_event_source(main_display)); 34 35 MapEditor map_editor(m_input, {0, 0}, { getScreenSize().x, getScreenSize().y - CONSOLE_HEIGHT }); 36 37 // Set program lifetime keybinds 38 m_input.setKeybind(ALLEGRO_KEY_ESCAPE, [&quit](){ quit = true; }); 39 40 al_start_timer(timer); 41 auto last_time = std_clk::now(); 42 while (!quit) 43 { 44 ALLEGRO_EVENT ev; 45 std_clk::time_point current_time; 46 double delta_time; 47 48 al_wait_for_event(ev_queue, &ev); 49 50 m_input.getInput(ev); 51 map_editor.handleEvents(ev); 52 53 switch (ev.type) 54 { 55 case ALLEGRO_EVENT_KEY_DOWN: 56 if (ev.keyboard.keycode == ALLEGRO_KEY_F1) 57 { 58 al_destroy_thread(view_thread); // al_destroy_thread() returns null if supplied pointer is null 59 view_thread = nullptr; 60 view_thread = al_create_thread(thread_func, nullptr); 61 al_start_thread(view_thread); 62 } 63 break; 64 65 case ALLEGRO_EVENT_DISPLAY_RESIZE: 66 al_acknowledge_resize(ev.display.source); 67 map_editor.resizeView({0, 0}, { getScreenSize().x, getScreenSize().y - CONSOLE_HEIGHT }); 68 break; 69 70 case ALLEGRO_EVENT_DISPLAY_CLOSE: 71 quit = true; 72 break; 73 74 case ALLEGRO_EVENT_TIMER: 75 current_time = std_clk::now(); 76 delta_time = std::chrono::duration<double>(current_time - last_time).count(); 77 last_time = current_time; 78 map_editor.update(delta_time); 79 redraw = true; 80 break; 81 82 default: 83 break; 84 } 85 86 //Drawing 87 88 if (al_event_queue_is_empty(ev_queue) && redraw) 89 { 90 al_clear_to_color(al_map_rgb(0, 0, 0)); 91 92 map_editor.draw(); 93 94 al_flip_display(); 95 96 redraw = false; 97 } 98 } 99 100 al_set_thread_should_stop(view_thread); 101 102 al_destroy_timer(timer); 103 al_destroy_event_queue(ev_queue); 104 al_destroy_display(main_display); 105 al_destroy_thread(view_thread); 106 107 return 0; 108}

Update:

I have done a bunch for experimenting and I've realized the following:

Keyboard (and mouse) events happen regardless of which displays exist or are in focus. Any event queue registered for these events will receive them.

I have figured out how to skip any keyboard and mouse events where the in focus display is not the main display:

#SelectExpand
1 while (!quit) 2 { 3 al_wait_for_event(ev_queue, &ev); 4 5 if (ev.any.source == al_get_mouse_event_source()) 6 { 7 if (ev.mouse.display != main_display) 8 { 9 continue; 10 } 11 } 12 else if (ev.any.source == al_get_keyboard_event_source()) 13 { 14 if (ev.keyboard.display != main_display) 15 { 16 continue; 17 } 18 } 19 /* Rest of loop logic goes here */ 20 }

This does not completely stop input from my second display from affecting my main display however. This is because my input logic doesn't care about events! I currently just store a previous keyboard state and the current keyboard state and query my input hgandler to see if theres a difference between the states and therefore a press or release.

So now it's just a matter of refactoring my input code to rely on events, and to store its own keyboard state so that I don't handle input where an event was skipped.

As usual, as soon as I post on a forum asking for help, I make a bunch of progress and figure it out. I am leaving this post here in case someone else has a similar issue and this helps them figure it out as well!

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

if ((ev.type == ALLEGRO_EVENT_KEY_UP || ev.type == ALLEGRO_EVENT_KEY_DOWN) {
   if (ev.keyboard.display == our_display) {
      // use key
   }
   else {
      continue;
   }
}

Which is basically what you did.

Just use one event loop to handle the logic for both windows.

I solved this by giving each window its own thread and event queue. Both threads and both windows are registered with their own event queue and there is a thread process function that runs them.

Go to: