Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Still trying to debug ManyMouse and al_set_target_bitmap

Credits go to SiegeLord for helping out!
This thread is locked; no one can reply to it. rss feed Print
Still trying to debug ManyMouse and al_set_target_bitmap
Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Hi guys.

I am still having a crash in al_set_target_bitmap on line 444 of tls.c from latest Allegro 5.2 in GIT.

tls.c#SelectExpand
444 old_shader->vt->unuse_shader(old_shader, old_display);

It happens when I try to change my mouse pointer from one type to another in my ManyMouse program. When the mouse pointer changes size I destroy the display that the mouse pointer is using and re-create it, but when I am merely changing the icon I only re-draw the buffer. Somehow when I go to draw the new mouse pointer on my buffer, the old target bitmap is trash, and when it goes to unuse the old shader it crashes because the old shader is trash too.

So my main question is, how is it possible for the old target bitmap to be trash? When a display is destroyed, the target bitmap is unset, except on Android, but I am not on Android.

I have 4 displays. One for a screen wide transparent overlay to capture mouse input. One for a visual logger. And atm, two for mouse pointers (one for each mouse present on the system). So there is a thread for each display and there is a main thread that is controlling it all. The main thread controls when to re-create the mouse pointers and re-draw the mouse buffers, but the mouse pointers are all drawn on the screen via InvalidateRect and UpdateWindow, so they are drawn through a WM_PAINT message on the window thread that allegro controls for each display.

Edit
From looking at my code, the code that controls re-drawing the mouse image buffers is called from the window callback process of my main overlay window.

Each thread has its own drawing target. The main window has its own thread provided by allegro. That thread is re-drawing the mouse images. However, each mouse window has its own display, and therefore its own callback thread. The mouse images are stored on that display's thread.

So the question is, is it because I am re-drawing the mouse images from a different thread? Is that why the drawing target is being trashed? I am having a hard time wrapping my head around all this.

SiegeLord
Member #7,827
October 2006
avatar

I'm confused by what you mean when you say that mouse pointer has a separate display... is this something I can compile and play around with?

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

EDIT
Here's a binary if anyone wants to test and or debug this. It's built with MinGW 4.8.1 and Allegro 5.2 from my binary distros. It's a debugging build so you can use it in gdb or you can try to build it yourself. All the source files in the src folder belong to the ManyMouseHooks dll project except for RawInputMain.cpp and RawInputHandler.hpp and RawInputHandler.cpp. Those files are the source for the executable program ManyMouse.

Links :
ManyMouseDistro.7z

In case anyone wants to try building ManyMouse with MinGW and Allegro 5.2 :
MinGW 4.8.1 binary
Allegro 5.2 binary for MinGW 4.8.1

Yes, I can give you an invitation to the repo. Right now I am trying to adapt it to build with MSVC, but there are CodeBlocks projects for MinGW. It uses Allegro 5.2 but that is the only dependency aside from Windows.

PM me your email address and I'll invite you to the repo. It's a GIT repo on BitBucket. There are two projects. One is for the Hooks DLL, and the other is for the program, which depends on the Hooks DLL. I'll answer any questions you might have.

SiegeLord said:

I'm confused by what you mean when you say that mouse pointer has a separate display... is this something I can compile and play around with?

Each mouse pointer active on the system has its own ALLEGRO_DISPLAY, as well as one for the transparent overlay that captures mouse input, and the visual logger.

It looks like I am creating the mouse image buffer on the thread that runs main. But I am trying to draw to it on the main window's callback thread. That might be the cause of a crash in FCFS mouse mode (First come first serve mouse mode, activate by pressing F) where it says it fails an assertion that !al_is_bitmap_drawing_held for some reason.

But that doesn't explain the crash in Heavy mouse mode (Key H) when I try to draw different mouse pointers (the crash I explained above).I am only trying to draw the stored mouse image to the window's buffer bitmap in preparation to draw it to the window's DIB and then from the DIB to the window.

Keys F,G, and H control the mouse mode. F is for first come first serve mouse strategy. G is for normal mouse mode. H is for heavy mouse mode, where you have to have someone help you pick something up. In case of F and H, it crashes when I try to click on something, but only intermittently. ESC quits. You might have to restart it a few times or click a few different times to get it to crash. It doesn't always do it.

To test properly you probably need at least two mice installed on your system. A trackpad counts as a mouse. USB mice work too.

Here's a full backtrace of when it crashes in Heavy mouse mode if it might help you navigate the code.

#SelectExpand
1(gdb) bt 2#0 0x675b64c5 in al_set_target_bitmap (bitmap=0x283e738) at C:\LIBS\A5GIT\allegro5\src\tls.c:444 3#1 0x675b660f in al_set_target_backbuffer (display=0x283e4d8) at C:\LIBS\A5GIT\allegro5\src\tls.c:493 4#2 0x64887685 in TransparentWindow::SetWindowImage (this=0x2833560, img=0xfeec00) at C:\ctwoplus\progcode\ManyMouse\src\TransparentWindow.cpp:248 5#3 0x64882a11 in Mouse::SetImage (this=0x2833560, mouse_image=0xfeec00) at C:\ctwoplus\progcode\ManyMouse\src\Mouse.cpp:114 6#4 0x6488596a in HeavyMouseStrategy::SetHeavyMouseStrategyState (this=0x68fab0, new_state=HEAVY_MOUSE_STATE_GRABBING) at C:\ctwoplus\progcode\ManyMouse\src\MouseStrategyHeavy.cpp:239 7#5 0x64884f40 in HeavyMouseStrategy::HandleInput (this=0x68fab0, input=..., process_input=true) at C:\ctwoplus\progcode\ManyMouse\src\MouseStrategyHeavy.cpp:81 8#6 0x64884d00 in HeavyMouseStrategy::HandleInput (this=0x68fab0, input=...) at C:\ctwoplus\progcode\ManyMouse\src\MouseStrategyHeavy.cpp:26 9#7 0x6488359e in MouseController::HandleRawInput (this=0x68fa68, rawinput=...) at C:\ctwoplus\progcode\ManyMouse\src\MouseController.cpp:156 10#8 0x004045d5 in _fu68___ZN9ManyMouse3logE () at C:\ctwoplus\progcode\ManyMouse\src\RawInputHandler.cpp:1399 11#9 0x004041e8 in _fu60___ZN9ManyMouse3logE () at C:\ctwoplus\progcode\ManyMouse\src\RawInputHandler.cpp:1201 12#10 0x0040177a in _fu0___ZN9ManyMouse3logE () at C:\ctwoplus\progcode\ManyMouse\src\RawInputHandler.cpp:44 13#11 0x67624b7e in window_callback (hWnd=0x46424a, message=255, wParam=0, lParam=155666555) at C:\LIBS\A5GIT\allegro5\src\win\wwindow.c:438 14#12 0x749384f3 in USER32!SetManipulationInputTarget () from C:\WINDOWS\SysWOW64\user32.dll 15#13 0x74916c40 in USER32!CallWindowProcW () from C:\WINDOWS\SysWOW64\user32.dll 16#14 0x74916541 in USER32!DispatchMessageW () from C:\WINDOWS\SysWOW64\user32.dll 17#15 0x74916300 in USER32!DispatchMessageW () from C:\WINDOWS\SysWOW64\user32.dll 18#16 0x6762ed38 in d3d_display_thread_proc (arg=0x68f698) at C:\LIBS\A5GIT\allegro5\src\win\d3d_disp.cpp:1468 19#17 0x675b4554 in detached_thread_func_trampoline (inner=0x27451e0, _outer=0x27451e0) at C:\LIBS\A5GIT\allegro5\src\threads.c:94 20#18 0x6762773c in thread_proc_trampoline (data=0x27451e0) at C:\LIBS\A5GIT\allegro5\src\win\wxthread.c:33 21#19 0x73e471e6 in msvcrt!_beginthreadex () from C:\WINDOWS\SysWOW64\msvcrt.dll 22#20 0x73e472b1 in msvcrt!_endthreadex () from C:\WINDOWS\SysWOW64\msvcrt.dll 23#21 0x747038f4 in KERNEL32!BaseThreadInitThunk () from C:\WINDOWS\SysWOW64\kernel32.dll 24#22 0x77095de3 in ?? () 25#23 0x77095dae in ?? () 26#24 0x00000000 in ?? () 27(gdb)

Here's the SetWindowImage function of the TransparentWindow class. I use it for the mouse pointers. It's crashing on the lines highlighted below when I try to set the backbuffer for the display. For some reason the old target bitmap and its shader are trash as I explained above.

TransparentWindow.cpp#SelectExpand
231bool TransparentWindow::SetWindowImage(ALLEGRO_BITMAP* img) { 232 233/* 234 if (image) { 235 al_destroy_bitmap(image); 236 image = 0; 237 } 238*/ 239 if (img) { 240 int nw = al_get_bitmap_width(img); 241 int nh = al_get_bitmap_height(img); 242 if ((nw != w) || (nh != h)) { 243 if (!CreateTheWindow(img)) { 244 return false; 245 } 246 } 247 else { 248 al_set_target_backbuffer(display); 249 al_set_blender(ALLEGRO_ADD , ALLEGRO_ONE , ALLEGRO_ZERO); 250 al_set_target_bitmap(image); 251 al_draw_bitmap(img , 0 , 0 , 0); 252 } 253 DrawImageToDIB(); 254 } 255 else { 256 if (image) { 257 al_destroy_bitmap(image); 258 image = 0; 259 } 260 dib_buffer.ClearToColor(trans_color); 261 } 262 263 if (ready) { 264/// dib_buffer.BlendBufferToWindowDC(); 265 dib_buffer.BlitBufferToWindowDC(); 266 QueuePaintMessage(); 267 } 268 269 return true; 270}

I am sorry if this is all confusing. It confuses me too, and I wrote it. :/

I really need to completely re-factor this. It's gotten a bit out of hand.

Edit
SiegeLord, I'll upload an archive you can try here in a bit. PM me if you want access to the repo.

SiegeLord
Member #7,827
October 2006
avatar

Whoops, sorry forgout about this. Bumping the thread until I have something better to say.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

No problem. ;) I'm really at my wits end right now. Not sure what is going on, but somehow something is not cleaning up the old target bitmap properly, and that's why when I try to set it the old one causes a segfault.

Also related is my msvc build. I'm trying to get a dll with ManyMouse built with MSVC but I can't get it to work right.

See here :
http://stackoverflow.com/questions/37999725/msvs-2015-building-a-dll-unresolved-external-symbol-main-in-invoke-main-in-ms
(EDIT - I solved this problem - I needed ALLEGRO_NO_MAGIC_MAIN to be defined before including Allegro).

Edit
You may have to run the program multiple times to get it to crash. In FCFS mouse mode, just click the mouse and move it. In Heavy mouse mode, click one mouse and then move it, and then move the other mouse near it and click with that mouse. That should be enough to get it to crash. Sometimes it works though.

SiegeLord
Member #7,827
October 2006
avatar

I played around with this a bit, and here's what I noticed. The crash seems always to be because the 'old_display' variable points to the display you just destroyed. Perhaps all you need to do is make sure to do the display destruction in your mouse callback thread.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Okay, so here's how it's all set up.

The main thread creates the transparent overlay window for capturing mouse input, as well as the visual logging display window. This thread is also responsible for changing mouse states in it's InputLoop function. This calls SetImage on the mice windows, which controls creation and destruction of mouse windows.

The main display has a callback function associated with it, which runs on Allegro's thread for the transparent overlay window's thread process. This thread handles all window input in the form of windows messages, including the WM_INPUT message, which triggers the transition of mouse states within each mouse mode.

So right now there are two threads controlling the mouse images. One, from InputLoop which controls mouse mode, and the WindowProcCallback, which controls transition of mouse states within each mouse mode.

So somehow I need to move one or the other into the other thread, right? I assume I need to keep everything on the same thread to prevent issues with TLS states getting messed up. I think that's what the problem is anyway.

Advice? Should I move input handling out of main and the InputLoop into the WindowProcCallback, or should I move the mouse state transitions out of WindowProcCallback into the main thread? I think I favor the first option somehow.

SiegeLord
Member #7,827
October 2006
avatar

Yeah, the first option is what I'd suggest. If you keep everything that deals with the associated displays and images into the WindowsProcCallback thread, everything should be fine. You could move the event handling in that thread, or use some signalling mechanism from the main thread to the callback thread.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I moved the mouse display creation and destruction to the callback thread, but there is still the same crash when a display is destroyed.

I am trying to narrow it down and make a simple example program to show the behavior.

EDIT
I double checked a few things and I found the issue, but I'm not sure why it was causing it.

In the drawing section of the main thread in the function InputLoop I was calling mouse_controller.Draw(), which sets the target bitmap to the backbuffer of the mouse display (I don't know why I did that, because mouse drawing is handled by calling SendMessage(WM_PAINT) to the mouse window's callback thread, but I did).

So by commenting out the following line in Mouse::Draw, I got it to stop crashing.

MMDECLSPEC void Mouse::Draw() {
   if (!transparent_window.GetAllegroDisplay()) {
      ManyMouse::log.Log("Mouse::Draw - transparent window's display is NULL.\n");
      return;
   }

   /// We don't paint the allegro display directly. We draw to the DIB and then use Windows to post WM_PAINT asynchronously
///   al_set_target_backbuffer(transparent_window.GetAllegroDisplay());
   transparent_window.PaintTheWindow();
}

Maybe Allegro doesn't like having a display created on a different thread be current for another calling thread.

So that solved that problem at least. There still remains an difficult to reproduce deadlock issue, and an intermittent crash on close, but for the most part the program generally works now.

Elias
Member #358
May 2000

It should work, but can be tricky. The rule is basically that only one thread can use a display at a time. So if whatever you do ends up making two different threads access the same display at the same time then it will crash. So maybe when you make your "transparent_window" the target bitmap, it is still the target bitmap on another thread?

Keeping each display to a single thread as you said is the easiest way to avoid this issue.

--
"Either help out or stop whining" - Evert

Go to: