Mouse Addon causing program crash
Doctor Cop

Hello friends, how are you all?

I am trying to figure out the mouse click event for every button in my program,
the mouse events were working fine until I wrote code for ImageButton. Now my program is crashing whenever I hover over or mouse out of the 6th or 2nd button.

I tried debugging but even after lots of tries I couldn't find the problem.

Here's my code for Buttons:

#SelectExpand
1#pragma once 2 3#include <stdio.h> 4#include <allegro5/allegro.h> 5#include <allegro5/allegro_primitives.h> 6#include <allegro5/allegro_font.h> 7#include <allegro5/allegro_ttf.h> 8#include "base.h" 9 10 11ALLEGRO_FONT* font; 12 13typedef struct 14{ 15 Model M; 16 int radius; 17 char* text; 18 ALLEGRO_FONT* font; 19 int text_align; 20} Button; 21 22typedef struct 23{ 24 Model M; 25 int radius; 26 char* text; 27 ALLEGRO_BITMAP* image; 28 int flip; 29} ImageButton; 30 31 32void view_button(Button* btn) 33{ 34 // Button background 35 if (btn->M.clicked == CLICKED) 36 al_draw_filled_rounded_rectangle(btn->M.x, btn->M.y, btn->M.x + btn->M.width, btn->M.y+btn->M.height, btn->radius, btn->radius, btn->M.C.primary_variant_color); 37 else 38 al_draw_filled_rounded_rectangle(btn->M.x, btn->M.y, btn->M.x + btn->M.width, btn->M.y+btn->M.height, btn->radius, btn->radius, btn->M.C.primary_color); 39 // Button border 40 al_draw_rounded_rectangle(btn->M.x, btn->M.y, btn->M.x + btn->M.width, btn->M.y+btn->M.height, btn->radius, btn->radius, btn->M.C.border_color, btn->M.border); 41 // Button text 42 al_draw_text(btn->font, btn->M.C.on_primary_color, btn->M.x + btn->M.padding, btn->M.y + btn->M.padding, btn->text_align, btn->text); 43} 44 45void view_image_button(ImageButton* btn) 46{ 47 // Button background 48 al_draw_filled_rounded_rectangle(btn->M.x, btn->M.y, btn->M.x + btn->M.width, btn->M.y+btn->M.height, btn->radius, btn->radius, btn->M.C.primary_color); 49 // Button border 50 al_draw_rounded_rectangle(btn->M.x, btn->M.y, btn->M.x + btn->M.width, btn->M.y+btn->M.height, btn->radius, btn->radius, btn->M.C.border_color, btn->M.border); 51 // Button image 52 al_draw_bitmap(btn->image, btn->M.x, btn->M.y, btn->flip); 53} 54 55void controller_click_down(Model* btn, ALLEGRO_MOUSE_STATE* state) 56{ 57 printf("Mouse x = %d \nMouse y = %d", state->x, state->y); 58 if (state != NULL) 59 if (state->buttons & 1) 60 if (state->x > btn->x && 61 state->y > btn->y && 62 state->x < btn->x + btn->width && 63 state->y < btn->y + btn->height) { 64 btn->clicked = PRESSED; 65 printf("controller_click_down\n"); 66 } 67} 68void controller_click_up(Model* btn, ALLEGRO_MOUSE_STATE* state) 69{ 70 if (state != NULL) 71 if (state->buttons & 1) 72 if (state->x > btn->x && 73 state->y > btn->y && 74 state->x < btn->x + btn->width && 75 state->y < btn->y + btn->height) { 76 btn->clicked = CLICKED; 77 printf("controller_click_up\n"); 78 } 79} 80 81/*void controller_image_button(ImageButton* btn) 82{ 83 ALLEGRO_MOUSE_STATE state; 84 al_get_mouse_state(&state); 85 if (state.buttons & 1) 86 if (state.x > btn->M.x && 87 state.y > btn->M.y && 88 state.x < btn->M.x + btn->M.width && 89 state.y < btn->M.y + btn->M.height) 90 btn->clicked = true; 91}*/ 92 93void init_buttons(Model* parent, Button* btns, int length, ALLEGRO_FONT* font) 94{ 95 materialize(); 96 97 Model BUTTON; 98 int positionin = parent->x + parent->padding; 99 int b_width = 20; 100 int endin = parent->y + parent->padding; 101 int b_height = 20; 102 char temp_text[] = "a"; 103 printf("parent pointer size : %d", sizeof(Model*)); 104 105 for (int i=0; i<length; i++) 106 { 107 BUTTON.id = i; 108 BUTTON.C = MATERIAL; 109 BUTTON.x = positionin; 110 BUTTON.y = endin; 111 BUTTON.width = b_width; 112 BUTTON.height = b_height; 113 BUTTON.offset_x = b_width; 114 BUTTON.offset_y = b_height; 115 BUTTON.padding = 6; 116 BUTTON.border = 2; 117 BUTTON.visibility = VISIBLE; 118 BUTTON.clicked = UNSET; 119 120 btns[i].M = BUTTON; 121 btns[i].radius = 2; 122 temp_text[0] = 'a'+i; 123 btns[i].text = malloc(2); 124 strcpy(btns[i].text, temp_text); 125 btns[i].text_align = ALLEGRO_ALIGN_LEFT; 126 btns[i].font = font; 127 128 if ((b_width)*2 + positionin + parent->padding <= parent->width+parent->x) { 129 positionin += b_width + parent->padding; 130 } else { 131 endin += b_height + parent->padding; 132 positionin = parent->x + parent->padding; 133 } 134 } 135} 136 137void init_image_buttons(Model* parent, ImageButton* btns, int length) 138{ 139 font = al_create_builtin_font(); 140 materialize(); 141 142 Model BUTTON; 143 int positionin = parent->x + parent->padding; 144 int b_width = 20; 145 int endin = parent->y + parent->padding; 146 int b_height = 20; 147 char filename[] = "../images/Sprites/flatLight/flatLight00.png"; 148 printf("parent pointer size : %d", sizeof(Model*)); 149 150 for (int i=0; i<length; i++) 151 { 152 BUTTON.id = i; 153 BUTTON.C = MATERIAL; 154 BUTTON.x = positionin; 155 BUTTON.y = endin; 156 BUTTON.width = b_width; 157 BUTTON.height = b_height; 158 BUTTON.offset_x = b_width; 159 BUTTON.offset_y = b_height; 160 BUTTON.padding = 6; 161 BUTTON.border = 2; 162 BUTTON.visibility = VISIBLE; 163 BUTTON.clicked = UNSET; 164 165 btns[i].M = BUTTON; 166 btns[i].radius = 2; 167 filename[38] = '0'+i; 168 printf("Load image %s\n", filename); 169 btns[i].image = al_load_bitmap(filename); 170 btns[i].flip = ALLEGRO_FLIP_HORIZONTAL; 171 172 if ((b_width)*2 + positionin + parent->padding <= parent->width+parent->x) { 173 positionin += b_width + parent->padding; 174 } else { 175 endin += b_height + parent->padding; 176 positionin = parent->x + parent->padding; 177 } 178 } 179}

I have attached the complete code.
I hope you are all safe and sound.

Arthur Kalliokoski

I compiled it by hand with

gcc -g main.c -o t -lallegro -lallegro_primitives -lallegro_font -lallegro_image -lallegro_ttf
and it immediately crashed because there wasn't any icon2.png. Grabbing a random png icon let it run, but mouseing over and clicking on the blue numbered things didn't have any effect. Upon exit it'd crash because you had two lines of
al_destroy_font(font);
at the end of main. I dunno <shrug>

Peter Hull

Good to hear from you Doctor!

Have a look at this, main.c, line 87 (approx)

        // close the display
        else if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE || event.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
            break;

Checking for keycode being 'escape' but you didn't check first if that event was a key event. The event structure is a union and all the different possibilities are laid over each other and you need to check the event.type to figure out which one is valid.
Here, a mouse event with x=59, when interpreted (incorrectly) as a key event, just happens to have a key code of ALLEGRO_KEY_ESCAPE.
Here it is in LLDB

(ALLEGRO_EVENT) $0 = {
  type = 20
  any = {
    type = 20
    source = 0x0000000100199890
    timestamp = 2.275083
  }

...skip a bit...

keyboard = {
    type = 20
    source = 0x0000000100199890
    timestamp = 2.275083
    display = 0x0000000100675dd0
    keycode = 59
    unichar = 165
    modifiers = 0
    repeat = false
  }
  mouse = {
    type = 20
    source = 0x0000000100199890
    timestamp = 2.275083
    display = 0x0000000100675dd0
    x = 59
    y = 165
    z = 0
    w = 0
    dx = -1
    dy = 0
    dz = 0
    dw = 0
    button = 0
    pressure = 0
  }

So, when you move the mouse to x=59, the app will try to exit.

Now, in the last part of main:

You've got al_destroy_font twice and that is what causes the crash (segfault)

That's as far as I got, there may be more.

What I did:
0. Ran in the debugger
1. Noted the segfault, found out where by doing a backtrace.
2. fixed that
3. Noted the app was now exiting 'for no reason'
4. There's only one line which could possibly cause an exit from the loop
5. Looked at that line and saw the logic error.

Hope that helps!
Peter

[edit] forgot to say, in your code you'd commented out al_install_mouse() and al_register_event_source(queue, al_get_mouse_event_source()) so I uncommented those.

Edgar Reynaldo

Excellent debugging Peter. You get a gold sticker. :)

{"name":"kawaii_face_gold_star_sticker-r6754b0139f8949e9ae01004fc4af8279_v9w09_8byvr_512.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/4\/9\/49a213c9deaa84857576b6998f75c947.jpg","w":512,"h":512,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/4\/9\/49a213c9deaa84857576b6998f75c947"}kawaii_face_gold_star_sticker-r6754b0139f8949e9ae01004fc4af8279_v9w09_8byvr_512.jpg

Doctor Cop

Hi Peter, thanks for the detailed solution.
I can't thank you enough, it solved my problem and gave me a new perspective on ALLEGRO EVENTS. Now I look at it I can think of chaining events instead of checking a single event to do everything.

One more thing, can you tell me if using a single event queue is enough for everything or I should use different queues for different things such as drawing UI and App logic?

@Arthur Kalliokoski, I see you linked -lallegro -lallegro_primitives -lallegro_font -lallegro_image -lallegro_ttf in the compilation phase, I just link the monolith, does it make any difference by linking all the shared lib files separately?

Upon exit it'd crash because you had two lines of
al_destroy_font(font);
at the end of main. I dunno <shrug>

It's a side-effect of cargo cult programming that I developed a habit of at my previous workplace, I'm trying to leave this bad habit but it still affects me one way or other. I'm trying to improve by making templates off of my previous projects so that I do not write Frankenstein code anymore.

I have attached the png file now.
It's from asset jesus's miracle pot. (Kenny Assets)

Edit:
I was wondering why my buttons weren't "clicking". So I changed the
controller_click_up(&buttons[i].M, &state);
to
controller_click_up(&buttons[i].M, &event.mouse);

IDK why it fixed it or what was causing the problem. If anybody can tell me, I would appreciate that.

Arthur Kalliokoski

I just link the monolith, does it make any difference by linking all the shared lib files separately?

I suppose not. I mostly didn't do it because the makefile wouldn't work as is (didn't have the correct directory structure) so I either had to futz with makedir or build it by hand. And it turned out I didn't have a monolith lib built, and didn't want to fiddle around with cmake and stuff trying to build it.

I'd suggest if you do this sort of thing again, make a parent directory and copy your program with sub directories into it, along with any needed data and compress the parent directory. Then uncompress it somewhere else and try to build it with a simple make command or whatever.

Don't go too far with this however, having build libraries such as monolith would be more the testers responsibility.

Peter Hull

One more thing, can you tell me if using a single event queue is enough for everything

I don't believe I've ever used more that one queue. Not sure what that would be useful for, maybe if you were doing multi-threaded apps?

Quote:

I was wondering why my buttons weren't "clicking".

In controller_click_up you've got

    if (state != NULL)
    if (state->buttons & 1)

I think state->buttons is always zero for the button you've just released in a mouse up event, therefore the following code never runs.

One more thing, it's normal to put the bulk of the code in .c files and just have the declarations in .h files (in C++, exceptions for very short functions which can be inlined)

Doctor Cop

@Peter, I'm trying to keep the number of files as less as possible, as I don't write lengthy functions for anything and I keep my header files small and always create a new file if the functionality can be extended to other files. It just feels convenient and keeps me sane during debugging. But I agree that I should probably change my habits.

Thanks for the solution, but I still don't get what's the point of getting the mouse and keyboard state when there are events for them, like I can just directly pass the event.mouse in function and it works, just have to change from state.buttons to event.button .

Edgar Reynaldo

Event queues are immensely useful in multi-threaded applications. Each thread gets its own synchronized access to the queue handled by a mutex. That's how my library does it anyway.

Rant:
I'm tired of seeing people struggle with things like keyboard and mouse events and state. My library has a full input handler that can handle anything you want. It is both event based and a state engine. Each event is like a tick of the clock for input. You can get presses, holds, releases, opens, up state down state, for each button joysticks and mice included. And then there are actual Input objects that can store a reference to a button or key and be checked automatically using a boolean conversion. I will support touch as soon as I have a device that has touch. ;P

What's more, the InputGroup class can hold combinations of keys and button states. It makes configuring your program immensely easy because you have a map of keys to actions you can change anytime.

Doctor Cop

Hi Edgar,

Actually I have tried to compile your library many times before, but failed. The last time I tried to compile Eagle the CMake version needed an update and after that my compiler didn't compile Eagle because it's Mingw32 and Eagle needs Mingw64, so I downloaded Mingw64, but guess what, Allegro 5 library wasn't setup in my new compiler and I couldn't recall how I managed to install Allegro in my Mingw32 in the first place. If you could provide binaries then I would be really grateful.

I have looked into the source code of Eagle library and have learned a lot of things from it, It's amazing and not complicated as code of other GUI libraries I have looked into. I hope I will succeed in compiling the Eagle library this time.

Edgar Reynaldo

You should have said something to me, I would have helped you immediately.

I can make some binaries for you, if you like, or help you get it to compile. Either is fine. Are you still on Discord? I am # 2 6 0 1 ,
M o d u l o 5 k .

I will send you a link to my eagle discord channel, where we can discuss your problems. The cmake version is probably too high for normal use. I always use the latest cmake, so it never bothers me.

Doctor Cop

Edgar, a part of me really wants to compile the Eagle project, but I really need those binaries. If you make them available as github packages then it'll be so convenient. I urge you to release the binaries as packages when every you compile a new major version. I'll make life of us noobs very easier.

Edgar Reynaldo

Let me put together a clean release. I want to brush up the docs and make sure they're ready before I release anything.

As an aside, I have no idea what version to call it. I've been working on it so long, it's got to be close to version 1. Or maybe I should adopt the YY-MM dating that is popular these days.

Doctor Cop

Edgar, the YY-MM format can be confusing as it happened in the case of Angular, but you do you, just that the version numbers don't be years apart.

Thread #618284. Printed from Allegro.cc