Jump to content

crteensy

Members
  • Posts

    111
  • Joined

  • Last visited

Posts posted by crteensy

  1. Some more findings for the example attached to this reply:

    • the label's text is not updated until the second call to updateHeatUpLabel(), which calls gwinSetText(). The first call does not have any apparent effect.
    • funny behavior with different fonts is still there. UI2 narrow seems to cause a hang after a while, fixed 10x20 is not displayed on the button and hangs as well

    I should probably describe what to do with that round widget:

    Click the small dark gray filled circle (the grip) and drag it around in the round widget centered on the screen. It will travel in the inner or the outer pair of circles. The label will be updated with a value depending on the grip's angle and a unit depending on the grip's traveling radius around the central widget. After some movement, the application hangs.

    heatUp_dTdt_dial_wButton.zip

  2. My previous example crashed with a segfault, I could now resolve that by *not* passing NULL to the label's init routine. Initializing a label apparently requires an initial non-null pointer (so I can't get around dynamic memory allocation). The attached example now hangs as described.

    Old reply:

    Here's a test project using your build system, stripped down to a custom widget and a label. It compiles fine but crashes with a segfault. ugfx is expected in ../ugfx just like in the build example in the wiki.

    gcc barfs at a forward declaration in gwinContainer.h (I think) so I commented that out. Nothing happens between the forward declaration and the following actual typedef so that's safe.

    I couldn't reply earlier because replying from within my university's network didn't work.

    heatUp_dTdt_dial_simple.zip

  3. I recompiled everything with -pthread, my libc is thread-safe.

    These are the compiler flags from my build log:

    g++ -O0 -Wall -fexceptions -pthread -g -Iugfx -Iugfx/src/gdisp/mcufont -Iugfx/drivers/multiple/X_480x272 -Iugfx_user -I.

    (yes, there are C++ flags in there. g++ ignores them for C files)

    Here is the output from gcc -v:

    Using built-in specs.
    COLLECT_GCC=gcc
    COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-linux-gnu/4.8/lto-wrapper
    Target: i686-linux-gnu
    Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.2-19ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-i386/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-i386 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-i386 --with-arch-directory=i386 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-targets=all --enable-multiarch --disable-werror --with-arch-32=i686 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=i686-linux-gnu --host=i686-linux-gnu --target=i686-linux-gnu
    Thread model: posix

    Still hangs after a seemingly random number of updates. Might this be related to button labels not being shown when I pick a different font?

    This is frustrating, really. Why do I need that multithreading again? It comes with so many problems that I'm really considering writing my own widgets, because widgets are what forced me to activate ugfx's multithreading in the first place. Code size skyrocketed, I need an extra heap for ugfx on my target hardware, things crash when running on linux.

  4. The buffer is not on the stack, but allocated statically. Regardless of that, I can pass whatever I want to gwinSetText - it hangs.

    I also have not yet understood what I can do to avoid dynamic allocation of character arrays. When I set the text in an init struct, it will be copied into a dynamically allocated character array so the whole point of gwinSetText having a parameter to select between static and dynamic memory seems moot.

  5. My app repeatedly calls this function:

    void run()
    {
    // handle baking state
    switch(_state)
    {
    case EPassive:
    break;
    case EBaking:
    break;
    }
    // get events
    GEvent* pe = geventEventWait(&_gl, 0);
    if (pe != NULL)
    {
    switch(pe->type) {
    case GEVENT_GWIN_BUTTON:
    if (((GEventGWinButton*)pe)->gwin == _ghBtnBack) {
    leave();
    }
    else if (((GEventGWinButton*)pe)->gwin == _ghBtnStartStop) {
    startOrStop();
    }
    break;
    case GEVENT_GWIN_SLIDER:
    if (((GEventGWinSlider*)pe)->gwin == _ghHeatUpDial) {
    updateHeatUpLabel();
    }
    }
    }
    gfxYield();
    }

    When updateHeatUpLabel() is called, a text is updated. I do this in two ways which show different behavior. The first (as shown here, drawing text manually), does not result in a hang:

    void updateHeatUpLabel()
    {
    static char buf[7];
    GHeatUp_dTdt_DialObject* p = (GHeatUp_dTdt_DialObject*)_ghHeatUpDial;
    if (p->unit == EdTdt_KperHour)
    {
    snprintf(buf, 7, "%2u K/h", (unsigned int)p->value);
    }
    else
    {
    snprintf(buf, 7, "%2u K/m", (unsigned int)p->value);
    }
    buf[6] = 0;
    gdispFillString(100, 20, buf, p->w.g.font, p->w.pstyle->enabled.text, p->w.pstyle->background);
    // gwinSetText(_ghHeatUpLabel, buf, FALSE);
    }

    When I swap in the call to gwinSetText() call, I get a hang. My debugger shows this call stack:

    #0 0xb7fdd424	__kernel_vsyscall () (??:??)
    #1 0xb7c29a1c sched_yield() (../sysdeps/unix/syscall-template.S:81)
    #2 0x80511fb gfxYield() (/ugfx/src/gos/gos_linux.c:47)
    #3 ?? 0x08049d19 in BakeMode::run (this=0x8063208 ) (/mode/bakeMode.h:155)
    #4 0x80497b8 Mode::runCurrent() (/mode/abstractMode.h:19)
    #5 0x80496d2 loop() (/main.cpp:19)
    #6 0x8049709 main(argc=1, argv=0xbffff5a4) (/main.cpp:27)

    I'm compiling with gcc -O0.

    Edit: This doesn't happen as soon as the label is updated, but after a seemingly random number of label updates.

  6. I just compiled the graph demo for playing with it and noticed that the plots are drawn correctly, but the grids and axes are invisible. This can be fixed by calling gwinClear(gh) before doing any further init, but that's not documented and it's also missing in the demo code. So the correct init code should be

    	gwinClear(gh); // <------ add this, line 80
    gwinGraphSetOrigin(gh, gwinGetWidth(gh)/2, gwinGetHeight(gh)/2);
    gwinGraphSetStyle(gh, &GraphStyle1);
    gwinGraphDrawAxis(gh);

    instead of

    	gwinGraphSetOrigin(gh, gwinGetWidth(gh)/2, gwinGetHeight(gh)/2);
    gwinGraphSetStyle(gh, &GraphStyle1);
    gwinGraphDrawAxis(gh);

    Edit: The documentation says "The graph is not automatically cleared. You must do that first by calling gwinClear()." but to me that doesn't indicate that the axes and grid won't be drawn when the graph is not cleared.

  7. Regarding the memory allocation problem I faced:

    The widget constructor functions provide an optional pointer to a pre-allocated widget structure. Shouldn't I be able to get around using ugfx's own memory management by simple passing a poitner to pre-allocated memory there? What happens to dynamically allocated strings in that case?

  8. I though it might be something like that. OTOH, getwidth() in gwin_label.c adds 2 pixels of extra width to the returned width, for the label to use for padding. I think this padding should be enough, but gdispFillStringBox doesn't shift the text right by one pixel, or does it?

  9. btw sorry for the background flicker!

    So why do the labels leave a trace when moving from left to right, but not when moving in any other direction? Those traces force me to do a full redraw of the parent container as soon as all labels have settled in their final position.

  10. Nothing earth-shattering, but I thought I might share my draggable button code. It might be interesting for others who want to implement their own custom widgets.

    It's based on uGFX's button and can be dragged around while the mouse is pressed over the button. It uses styles just like the original button. I have not tested the fancier dawing functions (arc, ellipse, polygons).

    There are two MouseMove functions in the .c file, the first (commented out) one doesn't add a dragging threshold. This is fine when used with a mouse. The second one (not commented out) adds a 5 pixel threshold before the button actually starts moving. This is a bit more convenient when used with flaky touch input.

    draggableButton.h:

    #include "gfx.h"

    #ifdef __cplusplus
    extern "C" {
    #endif

    typedef struct GDraggableButtonObject {
    GWidgetObject w;
    #if GINPUT_NEED_TOGGLE
    uint16_t toggle;
    #endif
    coord_t hotspot_x;
    coord_t hotspot_y;
    } GDraggableButtonObject;


    GHandle gwinGDraggableButtonCreate(GDisplay *g, GDraggableButtonObject *gb, const GWidgetInit *pInit);
    #define gwinDraggableButtonCreate(gb, pInit) gwinGDraggableButtonCreate(GDISP, gb, pInit)

    void gwinDraggableButtonDraw_Normal(GWidgetObject *gw, void *param); // @< A standard button
    #if GDISP_NEED_ARC || defined(__DOXYGEN__)
    void gwinDraggableButtonDraw_Rounded(GWidgetObject *gw, void *param); // @< A rounded rectangle button
    #endif
    #if GDISP_NEED_ELLIPSE || defined(__DOXYGEN__)
    void gwinDraggableButtonDraw_Ellipse(GWidgetObject *gw, void *param); // @< A circular button
    #endif
    #if GDISP_NEED_CONVEX_POLYGON || defined(__DOXYGEN__)
    void gwinDraggableButtonDraw_ArrowUp(GWidgetObject *gw, void *param); // @< An up arrow button
    void gwinDraggableButtonDraw_ArrowDown(GWidgetObject *gw, void *param); // @< A down arrow button
    void gwinDraggableButtonDraw_ArrowLeft(GWidgetObject *gw, void *param); // @< A left arrow button
    void gwinDraggableButtonDraw_ArrowRight(GWidgetObject *gw, void *param); // @< A right arrow button
    #endif
    #if GDISP_NEED_IMAGE || defined(__DOXYGEN__)
    void gwinDraggableButtonDraw_Image(GWidgetObject *gw, void *param); // @< An image button - see the notes above on the param.
    #endif

    #ifdef __cplusplus
    }
    #endif

    draggableButton.c:

    #include "draggableButton.h"

    #include

    #include "src/gwin/gwin_class.h"

    // Our pressed state
    #define GBUTTON_FLG_PRESSED (GWIN_FIRST_CONTROL_FLAG<<0)
    #define GBUTTON_FLG_MOVED (GWIN_FIRST_CONTROL_FLAG<<1)

    #if GINPUT_NEED_MOUSE
    // A mouse down has occurred over the button
    static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) {
    GDraggableButtonObject* p = (GDraggableButtonObject*)gw;
    gw->g.flags |= GBUTTON_FLG_PRESSED;
    p->hotspot_x = x;
    p->hotspot_y = y;
    _gwinUpdate((GHandle)gw);
    }

    // A mouse up has occurred (it is over the button, because the button can be dragged)
    static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) {
    (void) x; (void) y;
    gw->g.flags &= ~GBUTTON_FLG_PRESSED;
    _gwinUpdate((GHandle)gw);

    if(!(gw->g.flags & GBUTTON_FLG_MOVED))
    {
    _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON);
    }
    else // dropped
    {
    gw->g.flags &= ~GBUTTON_FLG_MOVED;
    gwinRedrawDisplay(GDISP, FALSE);
    }
    }

    // // A mouse move has occurred
    // static void MouseMove(GWidgetObject *gw, coord_t x, coord_t y) {
    // GDraggableButtonObject* p = (GDraggableButtonObject*)gw;
    // coord_t winx = gwinGetScreenX((GWindowObject*)gw);
    // coord_t winy = gwinGetScreenY((GWindowObject*)gw);
    // gw->g.flags |= GBUTTON_FLG_MOVED;
    // #if(GWIN_NEED_CONTAINERS)
    // if (gw->g.parent)
    // {
    // GWindowObject* parent = gw->g.parent;
    // winx -= parent->x;
    // winy -= parent->y;
    // }
    // #endif
    //
    // gwinMove((GWindowObject *)gw, winx+x-p->hotspot_x, winy+y-p->hotspot_y);
    // }

    // A mouse move has occurred
    static void MouseMove(GWidgetObject *gw, coord_t x, coord_t y) {
    GDraggableButtonObject* p = (GDraggableButtonObject*)gw;
    coord_t winx = gwinGetScreenX((GWindowObject*)gw);
    coord_t winy = gwinGetScreenY((GWindowObject*)gw);
    // not moving yet? calculate displacement
    if (!(gw->g.flags & GBUTTON_FLG_MOVED))
    {
    coord_t dx = abs(x - p->hotspot_x);
    coord_t dy = abs(y - p->hotspot_y);
    // displacement > 5 pixels in any direction: start moving
    if((dx > 5) || (dy > 5))
    {
    gw->g.flags |= GBUTTON_FLG_MOVED;
    }
    }

    #if(GWIN_NEED_CONTAINERS)
    if (gw->g.parent)
    {
    GWindowObject* parent = gw->g.parent;
    winx -= parent->x;
    winy -= parent->y;
    }
    #endif

    if(gw->g.flags & GBUTTON_FLG_MOVED)
    {
    gwinMove((GWindowObject *)gw, winx+x-p->hotspot_x, winy+y-p->hotspot_y);
    }
    }
    #endif

    #if GINPUT_NEED_TOGGLE
    // A toggle off has occurred
    static void ToggleOff(GWidgetObject *gw, uint16_t role) {
    (void) role;
    gw->g.flags &= ~GBUTTON_FLG_PRESSED;
    _gwinUpdate((GHandle)gw);
    }

    // A toggle on has occurred
    static void ToggleOn(GWidgetObject *gw, uint16_t role) {
    (void) role;
    gw->g.flags |= GBUTTON_FLG_PRESSED;
    _gwinUpdate((GHandle)gw);
    // Trigger the event on button down (different than for mouse/touch)
    _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON);
    }

    static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) {
    (void) role;
    ((GButtonObject *)gw)->toggle = instance;
    }

    static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) {
    (void) role;
    return ((GButtonObject *)gw)->toggle;
    }
    #endif

    // The button VMT table
    static const gwidgetVMT draggableButtonVMT = {
    {
    "Draggable Button", // The classname
    sizeof(GButtonObject), // The object size
    _gwidgetDestroy, // The destroy routine
    _gwidgetRedraw, // The redraw routine
    0, // The after-clear routine
    },
    gwinDraggableButtonDraw_Normal, // The default drawing routine
    #if GINPUT_NEED_MOUSE
    {
    MouseDown, // Process mouse down events
    MouseUp, // Process mouse up events
    MouseMove, // Process mouse move events
    },
    #endif
    #if GINPUT_NEED_TOGGLE
    {
    1, // 1 toggle role
    ToggleAssign, // Assign Toggles
    ToggleGet, // Get Toggles
    ToggleOff, // Process toggle off events
    ToggleOn, // Process toggle on events
    },
    #endif
    #if GINPUT_NEED_DIAL
    {
    0, // No dial roles
    0, // Assign Dials (NOT USED)
    0, // Get Dials (NOT USED)
    0, // Process dial move events (NOT USED)
    },
    #endif
    };

    GHandle gwinGDraggableButtonCreate(GDisplay *g, GDraggableButtonObject *gw, const GWidgetInit *pInit) {
    if (!(gw = (GDraggableButtonObject *)_gwidgetCreate(g, &gw->w, pInit, &draggableButtonVMT)))
    return 0;

    #if GINPUT_NEED_TOGGLE
    gw->toggle = GWIDGET_NO_INSTANCE;
    #endif
    gwinSetVisible((GHandle)gw, pInit->g.show);
    return (GHandle)gw;
    }

    /*----------------------------------------------------------
    * Custom Draw Routines
    *----------------------------------------------------------*/

    static const GColorSet *getDrawColors(GWidgetObject *gw) {
    if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled;
    if ((gw->g.flags & GBUTTON_FLG_PRESSED)) return &gw->pstyle->pressed;
    return &gw->pstyle->enabled;
    }

    #if GWIN_FLAT_STYLING
    void gwinDraggableButtonDraw_Normal(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter);
    gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge);
    gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge);
    }
    #else
    void gwinDraggableButtonDraw_Normal(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    fixed alpha;
    fixed dalpha;
    coord_t i;
    color_t tcol, bcol;
    (void) param;

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    /* Fill the box blended from variants of the fill color */
    tcol = gdispBlendColor(White, pcol->fill, TOP_FADE);
    bcol = gdispBlendColor(Black, pcol->fill, BOTTOM_FADE);
    dalpha = FIXED(255)/gw->g.height;
    for(alpha = 0, i = 0; i < gw->g.height; i++, alpha += dalpha)
    gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+i, gw->g.x+gw->g.width-2, gw->g.y+i, gdispBlendColor(bcol, tcol, NONFIXED(alpha)));

    gdispGDrawStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, justifyCenter);
    gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge);
    gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge);
    }
    #endif

    #if GDISP_NEED_ARC
    void gwinDraggableButtonDraw_Rounded(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    if (gw->g.width >= 2*RND_CNR_SIZE+10) {
    gdispGFillRoundedBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, RND_CNR_SIZE-1, pcol->fill);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+RND_CNR_SIZE, gw->g.width-2, gw->g.height-(2*RND_CNR_SIZE), gw->text, gw->g.font, pcol->text, justifyCenter);
    gdispGDrawRoundedBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, RND_CNR_SIZE, pcol->edge);
    } else {
    gdispGFillStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter);
    gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge);
    }
    }
    #endif

    #if GDISP_NEED_ELLIPSE
    void gwinDraggableButtonDraw_Ellipse(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gdispGFillEllipse(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width/2-1, gw->g.height/2-1, pcol->fill);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    gdispGDrawEllipse(gw->g.display, gw->g.x, gw->g.y, gw->g.width/2, gw->g.height/2, pcol->edge);
    }
    #endif

    #if GDISP_NEED_CONVEX_POLYGON
    void gwinDraggableButtonDraw_ArrowUp(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;
    point arw[7];

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    arw[0].x = gw->g.width/2; arw[0].y = 0;
    arw[1].x = gw->g.width-1; arw[1].y = gw->g.height/ARROWHEAD_DIVIDER;
    arw[2].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[2].y = gw->g.height/ARROWHEAD_DIVIDER;
    arw[3].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[3].y = gw->g.height-1;
    arw[4].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[4].y = gw->g.height-1;
    arw[5].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[5].y = gw->g.height/ARROWHEAD_DIVIDER;
    arw[6].x = 0; arw[6].y = gw->g.height/ARROWHEAD_DIVIDER;

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill);
    gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    }

    void gwinDraggableButtonDraw_ArrowDown(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;
    point arw[7];

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    arw[0].x = gw->g.width/2; arw[0].y = gw->g.height-1;
    arw[1].x = gw->g.width-1; arw[1].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER;
    arw[2].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[2].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER;
    arw[3].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[3].y = 0;
    arw[4].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[4].y = 0;
    arw[5].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[5].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER;
    arw[6].x = 0; arw[6].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER;

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill);
    gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    }

    void gwinDraggableButtonDraw_ArrowLeft(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;
    point arw[7];

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    arw[0].x = 0; arw[0].y = gw->g.height/2;
    arw[1].x = gw->g.width/ARROWHEAD_DIVIDER; arw[1].y = 0;
    arw[2].x = gw->g.width/ARROWHEAD_DIVIDER; arw[2].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[3].x = gw->g.width-1; arw[3].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[4].x = gw->g.width-1; arw[4].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[5].x = gw->g.width/ARROWHEAD_DIVIDER; arw[5].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[6].x = gw->g.width/ARROWHEAD_DIVIDER; arw[6].y = gw->g.height-1;

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill);
    gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    }

    void gwinDraggableButtonDraw_ArrowRight(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;
    point arw[7];

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    arw[0].x = gw->g.width-1; arw[0].y = gw->g.height/2;
    arw[1].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[1].y = 0;
    arw[2].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[2].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[3].x = 0; arw[3].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[4].x = 0; arw[4].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[5].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[5].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[6].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[6].y = gw->g.height-1;

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill);
    gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    }
    #endif

    #if GDISP_NEED_IMAGE || defined(__DOXYGEN__)
    void gwinDraggableButtonDraw_Image(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    coord_t sy;

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) {
    sy = 2 * gw->g.height;
    } else if ((gw->g.flags & GBUTTON_FLG_PRESSED)) {
    sy = gw->g.height;
    } else {
    sy = 0;
    }

    gdispGImageDraw(gw->g.display, (gdispImage *)param, gw->g.x, gw->g.y, gw->g.width, gw->g.height, 0, sy);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    }
    #endif

  11. Great that you found a solution. I will put that back into the repository for the arduino port of gos.

    Keep in mind that arduino is not a single piece of hardware. There are *many* different boards, architectures and compilers and the malloc problems I'm apparently facing might be a non-issue for different hardware.

    Just another thought for you...

    In your main loop you are doing a timer comparison to run update every 100ms. Let me suggest to you to use gtimer to run your update every 100ms. The geventEventWait can then use INFINITE for the timeout period and that loop does nothing but handle events.

    The multi threading is not going to be an issue for two reasons, 1) gwin is designed for that, and 2) your class should be safe as the threading on your platform is cooperative.

    I might try that, but to be honest I don't like the fact that gwin forces me to use a multi-threading environment in the first place. gdisp was fine without multiple threads, but having widgets doesn't work without them. That adds a lot of potential problems and given that I've already faced many problems with this project I think that there are many many more for others who are doing different things.

  12. Yup, that's it. I gave ugfx 4096 bytes and it now happily destroys and recreates widgets. I could now take this problem to the teensyduino people, but I think that's quite a rabbit hole to explore - I wouldn't even know how to start explaining this in such a way that someone else can even recreate the problem.

  13. Just a note on the gfxYield, if you replace the 0 in the geventEventWait call with a 1 I suspect the gfxYield will not be needed.

    Yes indeed, thanks for that! I kinda hoped that this would also make my other problem disappear, but it's still there. Thanks for having a look even if it's next week.

  14. I'm making frequent use of gwinDestroy(). Is it possible that under certain circumstances ugfx tries to access or redraw a widget that has been destroyed previously? Can widget desctruction have any other side effects?

    my application does not crash/hang when I replace gwinDestroy() with gwinHide() (that's an obvious memory leak, but I wanted to test the behavior with that).

  15. The purpose of gfxYield() is now quite clear to me in theory, it just surprised me that the touch panel driver wouldn't get polled without it while mouse input (when using X11) "just worked".

    Nonetheless, the problem remains. At the moment I can add items withoug problems, and drag them around. However, when I have removed one by clicking on it, the system hangs when I try to add the next item. This is not 100% reproducible, sometimes adding a new item works fine after having removed one previously. FWIW, here's my code if anyone wants to have a look:

    ContainerButton is basically a container, but it sends a button event when its background is clicked. One of these is my main container that I add "Items" to.

    containerButton.h:

    #ifndef CONTAINER_BUTTON_H
    #define CONTAINER_BUTTON_H

    #include "gfx.h"

    //typedef GWidgetObject GContainerButtonObject;

    #ifdef __cplusplus
    extern "C" {
    #endif

    typedef struct GContainerButtonObject {
    GWidgetObject w;
    coord_t click_x;
    coord_t click_y;
    } GContainerButtonObject;

    GHandle gwinGContainerButtonCreate(GDisplay *g, GContainerButtonObject *gc, const GWidgetInit *pInit, uint32_t flags);
    #define gwinContainerButtonCreate(gc, pInit, flags) gwinGContainerButtonCreate(GDISP, gc, pInit, flags)

    void gwinContainerButtonDraw_Std(GWidgetObject *gw, void *param);
    void gwinContainerButtonDraw_Transparent(GWidgetObject *gw, void *param);
    void gwinContainerButtonDraw_Image(GWidgetObject *gw, void *param);

    #ifdef __cplusplus
    }
    #endif


    #endif // CONTAINER_BUTTON_H

    containerButton.c:

    #include "containerButton.h"

    #if GFX_USE_GWIN && GWIN_NEED_CONTAINER

    #include "src/gwin/gwin_class.h"

    #define GBUTTON_FLG_PRESSED (GWIN_FIRST_CONTROL_FLAG<<0)

    #if GINPUT_NEED_MOUSE
    // A mouse down has occurred over the button
    static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) {
    (void) x; (void) y;
    gw->g.flags |= GBUTTON_FLG_PRESSED;
    _gwinUpdate((GHandle)gw);
    }

    // A mouse up has occurred (it may or may not be over the button)
    static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) {
    (void) x; (void) y;
    gw->g.flags &= ~GBUTTON_FLG_PRESSED;
    _gwinUpdate((GHandle)gw);

    #if !GWIN_BUTTON_LAZY_RELEASE
    // If the mouse up was not over the button then cancel the event
    if (x < 0 || y < 0 || x >= gw->g.width || y >= gw->g.height)
    return;
    #endif
    ((GContainerButtonObject*)gw)->click_x = x;
    ((GContainerButtonObject*)gw)->click_y = y;

    _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON);
    }
    #endif // GINPUT_NEED_MOUSE

    #if GWIN_CONTAINER_BORDER != GWIN_FIRST_CONTROL_FLAG
    #error "GWIN Container: - Flag definitions don't match"
    #endif

    #define BORDER_WIDTH 2

    static coord_t BorderSize(GHandle gh) { return (gh->flags & GWIN_CONTAINER_BORDER) ? BORDER_WIDTH : 0; }

    // The container VMT table
    static const gcontainerVMT containerButtonVMT = {
    {
    {
    "Container", // The classname
    sizeof(GContainerObject), // The object size
    _gcontainerDestroy, // The destroy routine
    _gcontainerRedraw, // The redraw routine
    0, // The after-clear routine
    },
    gwinContainerButtonDraw_Std, // The default drawing routine
    #if GINPUT_NEED_MOUSE
    {
    MouseUp,
    MouseDown,
    0, // not used
    },
    #endif
    #if GINPUT_NEED_TOGGLE
    {
    0, 0, 0, 0, 0, // No toggles
    },
    #endif
    #if GINPUT_NEED_DIAL
    {
    0, 0, 0, 0, // No dials
    },
    #endif
    },
    BorderSize, // The size of the left border (mandatory)
    BorderSize, // The size of the top border (mandatory)
    BorderSize, // The size of the right border (mandatory)
    BorderSize, // The size of the bottom border (mandatory)
    0, // A child has been added (optional)
    0, // A child has been deleted (optional)
    };

    GHandle gwinGContainerButtonCreate(GDisplay *g, GContainerButtonObject *gc, const GWidgetInit *pInit, uint32_t flags)
    {
    if (!(gc = (GContainerButtonObject *)_gcontainerCreate(g, &gc->w, pInit, &containerButtonVMT)))
    return 0;
    GWidgetObject* gw = (GWidgetObject*)gc;
    gw->g.flags |= (flags & GWIN_CONTAINER_BORDER);

    gwinSetVisible((GHandle)gw, pInit->g.show);
    return (GHandle)gc;
    }

    void gwinContainerButtonDraw_Transparent(GWidgetObject *gw, void *param) {
    (void)param;
    if (gw->g.vmt != (gwinVMT *)&containerButtonVMT)
    return;

    if ((gw->g.flags & GWIN_CONTAINER_BORDER))
    gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge);

    // Don't touch the client area
    }

    void gwinContainerButtonDraw_Std(GWidgetObject *gw, void *param) {
    (void)param;
    if (gw->g.vmt != (gwinVMT *)&containerButtonVMT)
    return;

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gwinContainerDraw_Transparent(gw, param);
    }

    #if GDISP_NEED_IMAGE
    void gwinContainerButtonDraw_Image(GWidgetObject *gw, void *param) {
    #define gi ((gdispImage *)param)
    coord_t x, y, iw, ih, mx, my;

    if (gw->g.vmt != (gwinVMT *)&containerButtonVMT)
    return;

    // Draw the frame
    gwinContainerDraw_Transparent(gw, param);

    // Draw the client area by tiling the image
    mx = gw->g.x+gw->g.width;
    my = gw->g.y+gw->g.height;
    y = gw->g.y;
    if ((gw->g.flags & GWIN_CONTAINER_BORDER)) {
    mx--;
    my--;
    y++;
    }
    for(ih=gi->height; y < my; y += ih) {
    if (ih > my - y)
    ih = my - y;
    x = gw->g.x;
    if ((gw->g.flags & GWIN_CONTAINER_BORDER))
    x++;
    for(iw=gi->width; x < mx; x += iw) {
    if (iw > mx - x)
    iw = mx - x;
    gdispGImageDraw(gw->g.display, gi, x, y, ih, iw, 0, 0);
    }
    }

    #undef gi
    }
    #endif

    #endif

    DraggableButton is a button, but it can also be dragged around with the mouse.

    draggableButton.h:

    #include "gfx.h"

    #ifdef __cplusplus
    extern "C" {
    #endif

    typedef struct GDraggableButtonObject {
    GWidgetObject w;
    #if GINPUT_NEED_TOGGLE
    uint16_t toggle;
    #endif
    coord_t hotspot_x;
    coord_t hotspot_y;
    } GDraggableButtonObject;


    GHandle gwinGDraggableButtonCreate(GDisplay *g, GDraggableButtonObject *gb, const GWidgetInit *pInit);
    #define gwinDraggableButtonCreate(gb, pInit) gwinGDraggableButtonCreate(GDISP, gb, pInit)

    void gwinDraggableButtonDraw_Normal(GWidgetObject *gw, void *param); // @< A standard button
    #if GDISP_NEED_ARC || defined(__DOXYGEN__)
    void gwinDraggableButtonDraw_Rounded(GWidgetObject *gw, void *param); // @< A rounded rectangle button
    #endif
    #if GDISP_NEED_ELLIPSE || defined(__DOXYGEN__)
    void gwinDraggableButtonDraw_Ellipse(GWidgetObject *gw, void *param); // @< A circular button
    #endif
    #if GDISP_NEED_CONVEX_POLYGON || defined(__DOXYGEN__)
    void gwinDraggableButtonDraw_ArrowUp(GWidgetObject *gw, void *param); // @< An up arrow button
    void gwinDraggableButtonDraw_ArrowDown(GWidgetObject *gw, void *param); // @< A down arrow button
    void gwinDraggableButtonDraw_ArrowLeft(GWidgetObject *gw, void *param); // @< A left arrow button
    void gwinDraggableButtonDraw_ArrowRight(GWidgetObject *gw, void *param); // @< A right arrow button
    #endif
    #if GDISP_NEED_IMAGE || defined(__DOXYGEN__)
    void gwinDraggableButtonDraw_Image(GWidgetObject *gw, void *param); // @< An image button - see the notes above on the param.
    #endif

    #ifdef __cplusplus
    }
    #endif

    draggableButton.h:

    #include "draggableButton.h"

    #include

    #include "src/gwin/gwin_class.h"

    // Our pressed state
    #define GBUTTON_FLG_PRESSED (GWIN_FIRST_CONTROL_FLAG<<0)
    #define GBUTTON_FLG_MOVED (GWIN_FIRST_CONTROL_FLAG<<1)

    #if GINPUT_NEED_MOUSE
    // A mouse down has occurred over the button
    static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) {
    GDraggableButtonObject* p = (GDraggableButtonObject*)gw;
    gw->g.flags |= GBUTTON_FLG_PRESSED;
    p->hotspot_x = x;
    p->hotspot_y = y;
    _gwinUpdate((GHandle)gw);
    }

    // A mouse up has occurred (it is over the button, because the button can be dragged)
    static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) {
    (void) x; (void) y;
    gw->g.flags &= ~GBUTTON_FLG_PRESSED;
    _gwinUpdate((GHandle)gw);

    if(!(gw->g.flags & GBUTTON_FLG_MOVED))
    {
    _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON);
    }
    else // dropped
    {
    gw->g.flags &= ~GBUTTON_FLG_MOVED;
    gwinRedrawDisplay(GDISP, FALSE);
    }
    }

    // A mouse move has occurred
    static void MouseMove(GWidgetObject *gw, coord_t x, coord_t y) {
    GDraggableButtonObject* p = (GDraggableButtonObject*)gw;
    coord_t winx = gwinGetScreenX((GWindowObject*)gw);
    coord_t winy = gwinGetScreenY((GWindowObject*)gw);
    gw->g.flags |= GBUTTON_FLG_MOVED;
    #if(GWIN_NEED_CONTAINERS)
    if (gw->g.parent)
    {
    GWindowObject* parent = gw->g.parent;
    winx -= parent->x;
    winy -= parent->y;
    }
    #endif

    gwinMove((GWindowObject *)gw, winx+x-p->hotspot_x, winy+y-p->hotspot_y);
    }
    #endif

    #if GINPUT_NEED_TOGGLE
    // A toggle off has occurred
    static void ToggleOff(GWidgetObject *gw, uint16_t role) {
    (void) role;
    gw->g.flags &= ~GBUTTON_FLG_PRESSED;
    _gwinUpdate((GHandle)gw);
    }

    // A toggle on has occurred
    static void ToggleOn(GWidgetObject *gw, uint16_t role) {
    (void) role;
    gw->g.flags |= GBUTTON_FLG_PRESSED;
    _gwinUpdate((GHandle)gw);
    // Trigger the event on button down (different than for mouse/touch)
    _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON);
    }

    static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) {
    (void) role;
    ((GButtonObject *)gw)->toggle = instance;
    }

    static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) {
    (void) role;
    return ((GButtonObject *)gw)->toggle;
    }
    #endif

    // The button VMT table
    static const gwidgetVMT draggableButtonVMT = {
    {
    "Draggable Button", // The classname
    sizeof(GButtonObject), // The object size
    _gwidgetDestroy, // The destroy routine
    _gwidgetRedraw, // The redraw routine
    0, // The after-clear routine
    },
    gwinDraggableButtonDraw_Normal, // The default drawing routine
    #if GINPUT_NEED_MOUSE
    {
    MouseDown, // Process mouse down events
    MouseUp, // Process mouse up events
    MouseMove, // Process mouse move events
    },
    #endif
    #if GINPUT_NEED_TOGGLE
    {
    1, // 1 toggle role
    ToggleAssign, // Assign Toggles
    ToggleGet, // Get Toggles
    ToggleOff, // Process toggle off events
    ToggleOn, // Process toggle on events
    },
    #endif
    #if GINPUT_NEED_DIAL
    {
    0, // No dial roles
    0, // Assign Dials (NOT USED)
    0, // Get Dials (NOT USED)
    0, // Process dial move events (NOT USED)
    },
    #endif
    };

    GHandle gwinGDraggableButtonCreate(GDisplay *g, GDraggableButtonObject *gw, const GWidgetInit *pInit) {
    if (!(gw = (GDraggableButtonObject *)_gwidgetCreate(g, &gw->w, pInit, &draggableButtonVMT)))
    return 0;

    #if GINPUT_NEED_TOGGLE
    gw->toggle = GWIDGET_NO_INSTANCE;
    #endif
    gwinSetVisible((GHandle)gw, pInit->g.show);
    return (GHandle)gw;
    }

    /*----------------------------------------------------------
    * Custom Draw Routines
    *----------------------------------------------------------*/

    static const GColorSet *getDrawColors(GWidgetObject *gw) {
    if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled;
    if ((gw->g.flags & GBUTTON_FLG_PRESSED)) return &gw->pstyle->pressed;
    return &gw->pstyle->enabled;
    }

    #if GWIN_FLAT_STYLING
    void gwinDraggableButtonDraw_Normal(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter);
    gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge);
    gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge);
    }
    #else
    void gwinDraggableButtonDraw_Normal(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    fixed alpha;
    fixed dalpha;
    coord_t i;
    color_t tcol, bcol;
    (void) param;

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    /* Fill the box blended from variants of the fill color */
    tcol = gdispBlendColor(White, pcol->fill, TOP_FADE);
    bcol = gdispBlendColor(Black, pcol->fill, BOTTOM_FADE);
    dalpha = FIXED(255)/gw->g.height;
    for(alpha = 0, i = 0; i < gw->g.height; i++, alpha += dalpha)
    gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+i, gw->g.x+gw->g.width-2, gw->g.y+i, gdispBlendColor(bcol, tcol, NONFIXED(alpha)));

    gdispGDrawStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, justifyCenter);
    gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge);
    gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge);
    }
    #endif

    #if GDISP_NEED_ARC
    void gwinDraggableButtonDraw_Rounded(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    if (gw->g.width >= 2*RND_CNR_SIZE+10) {
    gdispGFillRoundedBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, RND_CNR_SIZE-1, pcol->fill);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+RND_CNR_SIZE, gw->g.width-2, gw->g.height-(2*RND_CNR_SIZE), gw->text, gw->g.font, pcol->text, justifyCenter);
    gdispGDrawRoundedBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, RND_CNR_SIZE, pcol->edge);
    } else {
    gdispGFillStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter);
    gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge);
    }
    }
    #endif

    #if GDISP_NEED_ELLIPSE
    void gwinDraggableButtonDraw_Ellipse(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gdispGFillEllipse(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width/2-1, gw->g.height/2-1, pcol->fill);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    gdispGDrawEllipse(gw->g.display, gw->g.x, gw->g.y, gw->g.width/2, gw->g.height/2, pcol->edge);
    }
    #endif

    #if GDISP_NEED_CONVEX_POLYGON
    void gwinDraggableButtonDraw_ArrowUp(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;
    point arw[7];

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    arw[0].x = gw->g.width/2; arw[0].y = 0;
    arw[1].x = gw->g.width-1; arw[1].y = gw->g.height/ARROWHEAD_DIVIDER;
    arw[2].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[2].y = gw->g.height/ARROWHEAD_DIVIDER;
    arw[3].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[3].y = gw->g.height-1;
    arw[4].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[4].y = gw->g.height-1;
    arw[5].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[5].y = gw->g.height/ARROWHEAD_DIVIDER;
    arw[6].x = 0; arw[6].y = gw->g.height/ARROWHEAD_DIVIDER;

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill);
    gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    }

    void gwinDraggableButtonDraw_ArrowDown(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;
    point arw[7];

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    arw[0].x = gw->g.width/2; arw[0].y = gw->g.height-1;
    arw[1].x = gw->g.width-1; arw[1].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER;
    arw[2].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[2].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER;
    arw[3].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[3].y = 0;
    arw[4].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[4].y = 0;
    arw[5].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[5].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER;
    arw[6].x = 0; arw[6].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER;

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill);
    gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    }

    void gwinDraggableButtonDraw_ArrowLeft(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;
    point arw[7];

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    arw[0].x = 0; arw[0].y = gw->g.height/2;
    arw[1].x = gw->g.width/ARROWHEAD_DIVIDER; arw[1].y = 0;
    arw[2].x = gw->g.width/ARROWHEAD_DIVIDER; arw[2].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[3].x = gw->g.width-1; arw[3].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[4].x = gw->g.width-1; arw[4].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[5].x = gw->g.width/ARROWHEAD_DIVIDER; arw[5].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[6].x = gw->g.width/ARROWHEAD_DIVIDER; arw[6].y = gw->g.height-1;

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill);
    gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    }

    void gwinDraggableButtonDraw_ArrowRight(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    (void) param;
    point arw[7];

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    arw[0].x = gw->g.width-1; arw[0].y = gw->g.height/2;
    arw[1].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[1].y = 0;
    arw[2].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[2].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[3].x = 0; arw[3].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[4].x = 0; arw[4].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[5].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[5].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2;
    arw[6].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[6].y = gw->g.height-1;

    gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background);
    gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill);
    gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    }
    #endif

    #if GDISP_NEED_IMAGE || defined(__DOXYGEN__)
    void gwinDraggableButtonDraw_Image(GWidgetObject *gw, void *param) {
    const GColorSet * pcol;
    coord_t sy;

    if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return;
    pcol = getDrawColors(gw);

    if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) {
    sy = 2 * gw->g.height;
    } else if ((gw->g.flags & GBUTTON_FLG_PRESSED)) {
    sy = gw->g.height;
    } else {
    sy = 0;
    }

    gdispGImageDraw(gw->g.display, (gdispImage *)param, gw->g.x, gw->g.y, gw->g.width, gw->g.height, 0, sy);
    gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
    }
    #endif

    and my main.cpp, including the "Item" class that takes care of adding buttons, labels, and moving them around:

    #include 
    #include

    #include
    #include

    #include
    #include "gfx.h"
    #include "src/gwin/gwin_class.h"
    #include "ugfx_user/containerButton/containerButton.h"
    #include "ugfx_user/draggableButton/draggableButton.h"

    #define DEBUG(x) Serial.printf x ;Serial.flush()
    //#define DEBUG(x)

    static GHandle ghContainer;

    static GWidgetStyle myStyle = {
    White, // background
    {Black, SkyBlue, SkyBlue, Black}, // enabled (text edge fill progress)
    {White, Gray, Gray, Black}, // disabled
    {White, Orange, Orange, Black} // pressed
    };

    static void createWidgets(void) {
    coord_t height = gdispGetHeight();
    coord_t width = gdispGetWidth();
    GWidgetInit wi;

    // Apply some default values for GWIN
    gwinWidgetClearInit(&wi);

    // Apply the container parameters
    wi.g.show = FALSE;
    wi.g.width = width;
    wi.g.height = height;
    wi.g.x = 0;
    wi.g.y = 0;
    wi.text = "Container";
    ghContainer = gwinContainerButtonCreate(0, &wi, 0);//GWIN_CONTAINER_BORDER);
    wi.g.show = TRUE;

    gwinShow(ghContainer);
    }

    class Item
    {
    public:
    static void add(coord_t x, coord_t y)
    {
    // create a new button
    DEBUG(("Item::create (%d,%d)\n", x, y));
    Item* p = new Item;
    if(p == NULL)
    {
    DEBUG(("Could not allocate space for a new item\n"));
    while(1);
    }
    DEBUG(("Initializing item @%p\n", p));
    p->init(x,y);
    DEBUG(("Prepending item @%p to list\n", p));
    p->next = pItems;
    pItems = p;
    DEBUG(("add: done\n"));
    }
    static void remove(GHandle gh)
    {
    // find item with btn handle gh
    DEBUG(("remove(%p)\n", gh));
    Item* p = pItems;
    while((p != NULL) && (p->_btnHandle != gh))
    {
    p = p->next;
    }
    if(p == NULL)
    {
    // not found
    DEBUG(("remove: item not found\n"));
    return;
    }
    // now we're sure that an object with this button handle exists and it is at p
    DEBUG(("remove p @%p\n", p));
    DEBUG(("p->_btnHandle = %p\n", p->_btnHandle));
    // p points to the item that is to be removed
    if(pItems == p)
    {
    DEBUG(("Removing root item, next = %p\n", p->next));
    pItems = p->next;
    }
    else
    {
    DEBUG(("Removing non-root item, next = %p\n", p->next));
    Item* prev = pItems;
    while((prev != NULL) && (prev->next != p))
    {
    prev = prev->next;
    }
    if(prev->next == p)
    {
    // found
    DEBUG(("Found previous item @%p\n",prev));
    DEBUG((" prev->next = %p\n",prev->next));
    prev->next = p->next;
    }
    else
    {
    DEBUG(("your list is borked\n"));
    }
    }
    p->deinit();
    DEBUG(("remove: item deinitialized\n"));
    delete p;
    }
    Item()
    {
    DEBUG(("Item being constructed @%p\n", this));
    _btnHandle = NULL;
    _lblHandle = NULL;
    next = NULL;
    }
    ~Item()
    {
    DEBUG(("Item @%p being deleted\n", this));
    }
    static bool update()
    {
    coord_t left = 0;
    coord_t right = gdispGetWidth()-1;
    coord_t top = 0;
    coord_t bottom = gdispGetHeight()-1;
    // calculate forces on labels
    Item* it = pItems;
    while(it != NULL)
    {
    // reset
    double fx = 0;
    double fy = 0;
    coord_t labelWidth = gwinGetWidth(it->_lblHandle);
    coord_t labelHeight = gwinGetHeight(it->_lblHandle);
    double label_x = gwinGetScreenX(it->_lblHandle) + labelWidth/2.;
    double label_y = gwinGetScreenY(it->_lblHandle) + labelHeight/2.;
    double btn_x = gwinGetScreenX(it->_btnHandle) + gwinGetWidth(it->_btnHandle)/2.;
    double btn_y = gwinGetScreenY(it->_btnHandle) + gwinGetHeight(it->_btnHandle)/2.;
    // add forces for screen boundary
    fx -= c3/pow(right-label_x, 2);
    fx += c3/pow(label_x - left, 2);
    fy -= c3/pow(bottom-label_y, 2);
    fy += c3/pow(label_y - top, 2);
    // add force for this item's button
    double dx = btn_x - label_x;
    double dy = btn_y - label_y;
    double dist_sq = pow(dx,2) + pow(dy,2);
    double dist = sqrt(dist_sq);
    double f = c1*log(dist/c2);
    fx += f*dx/dist;
    fy += f*dy/dist;
    // add forces for each other item
    Item* other = pItems;
    while(other != NULL)
    {
    if(other == it)
    {
    other = other->next;
    continue;
    }
    coord_t other_x = gwinGetScreenX(other->_lblHandle) + gwinGetWidth(other->_lblHandle)/2;
    coord_t other_y = gwinGetScreenY(other->_lblHandle) + gwinGetHeight(other->_lblHandle)/2;
    dx = other_x - label_x;
    dy = other_y - label_y;
    dist_sq = pow(dx,2) + pow(dy,2);
    dist = sqrt(dist_sq);
    f = c3/dist_sq;
    fx -= dx/dist*f;
    fy -= dy/dist*f;
    other_x = gwinGetScreenX(other->_btnHandle) + gwinGetWidth(other->_btnHandle)/2;
    other_y = gwinGetScreenY(other->_btnHandle) + gwinGetHeight(other->_btnHandle)/2;
    dx = other_x - label_x;
    dy = other_y - label_y;
    dist_sq = pow(dx,2) + pow(dy,2);
    dist = sqrt(dist_sq);
    f = c3/dist_sq;
    fx -= dx/dist*f;
    fy -= dy/dist*f;
    other = other->next;
    }
    it->force_x = fx;
    it->force_y = fy;

    it = it->next;
    }

    // move labels
    it = pItems;
    bool somethingMoved = false;
    while(it != NULL)
    {
    int dx = round(c4*it->force_x);
    int dy = round(c4*it->force_y);
    bool thisMoved = ((dx != 0) || (dy != 0));
    somethingMoved |= thisMoved;
    coord_t x = gwinGetScreenX(it->_lblHandle) + dx;
    coord_t y = gwinGetScreenY(it->_lblHandle) + dy;
    it->_lblHandle->x = x;
    it->_lblHandle->y = y;
    if(thisMoved)
    {
    gwinRedraw(it->_lblHandle);
    }
    it = it->next;
    }

    return somethingMoved;
    }
    void init(coord_t x, coord_t y)
    {
    DEBUG(("Item::init (%d,%d)\n", x, y));
    GWidgetInit wi;
    gwinWidgetClearInit(&wi);
    wi.g.x = x - btnSize/2;
    wi.g.y = y - btnSize/2;
    wi.g.width = btnSize;
    wi.g.height = btnSize;
    wi.g.parent = ghContainer;
    wi.text = " ";
    wi.g.show = TRUE;
    _btnHandle = gwinDraggableButtonCreate(0, &wi);
    if(_btnHandle == NULL)
    {
    DEBUG(("invalid button handle!\n"));
    while(1);
    }
    wi.g.x = x + btnSize/2 + 5;
    wi.g.y = y + btnSize/2 + 5;
    wi.g.width = 0;
    wi.g.height = 0;
    wi.text = " ";
    _lblHandle = gwinLabelCreate(0, &wi);
    if(_lblHandle == NULL)
    {
    DEBUG(("invalid label handle!\n"));
    while(1);
    }
    char str[4];
    snprintf(str, 4, "%u", (unsigned int)nextNumber);
    gwinSetText(_lblHandle, str, TRUE);
    nextNumber++;
    DEBUG(("Item::init done\n", x, y));
    }
    void deinit()
    {
    gwinDestroy(_btnHandle);
    gwinDestroy(_lblHandle);
    next = NULL;
    }
    private:
    static Item* lastItem()
    {
    Item* p = pItems;
    while((p != NULL ) && (p->next != NULL))
    {
    p = p->next;
    }
    return p;
    }
    static Item* previousItem(const Item* i)
    {
    // fint item such that item->next = i
    Item* p = pItems;
    while(p != NULL)
    {
    if(p->next == i)
    {
    DEBUG(("previousItem found: p->next = %p, i = %p\n", p->next, i));
    break;
    }
    p = p->next;
    }
    return p;
    }

    static Item* pItems;
    static uint8_t nextNumber;
    static const uint8_t btnSize = 32;
    static constexpr double c1 = 2;
    static constexpr double c2 = 1.5*btnSize;
    static constexpr double c3 = 2560;
    static constexpr double c4 = 1;

    Item* next;
    GHandle _btnHandle;
    GHandle _lblHandle;
    double force_x;
    double force_y;
    };

    Item* Item::pItems = NULL;
    uint8_t Item::nextNumber = 0;

    void setup()
    {
    // while(!Serial.available()); // wait for serial input before starting the app
    DEBUG(("Start"));
    SPI.begin();
    }

    static GListener gl;
    void loop(void) {

    gfxInit();

    // Set the widget defaults
    gwinSetDefaultFont(gdispOpenFont("UI2 Narrow"));
    gwinSetDefaultStyle(&myStyle, FALSE);

    gdispClear(White);
    createWidgets();

    geventListenerInit(&gl);
    gwinAttachListener(&gl);
    GEvent* pe;

    while(1) {
    // alive-check over serial
    if (Serial.available())
    {
    Serial.read();
    Serial.printf("alive!\n");
    }

    pe = geventEventWait(&gl, 0);
    if (pe != NULL)
    {
    switch(pe->type) {
    case GEVENT_GWIN_BUTTON:
    if(((GEventGWinButton*)pe)->gwin == ghContainer) {
    GContainerButtonObject* p = (GContainerButtonObject*)((GEventGWinButton*)pe)->gwin;
    coord_t x = p->click_x;
    coord_t y = p->click_y;
    DEBUG(("add @ %d %d\n", x, y));
    Item::add(x,y);
    }
    else
    {
    // if one of the buttons was clicked, remove it
    Item::remove(((GEventGWinButton*)pe)->gwin);
    gwinRedraw(ghContainer);
    }
    break;

    default:
    DEBUG(("unhandled event type\n"));
    break;
    }
    DEBUG(("Event handler done\n"));
    }
    static bool keepUpdating = false;
    static systemticks_t last = gfxSystemTicks();
    systemticks_t now = gfxSystemTicks();
    if (((now - last) >= 100) || keepUpdating)
    {
    last = now;
    if (Item::update())
    {
    keepUpdating = true;
    }
    else
    {
    keepUpdating = false;
    }
    }
    gfxYield();
    }
    }

    Still no video of the real hardware, sorry!

  16. When my application crashes/hangs, it seems to be stuck in gfxYield(). Without the call to gfxYield() I don't get any touch events, that's why I added it in the first place. I'm not sure why it hangs there, but it's after I touched the touch panel.

  17. Running on real hardware with a touch panel now. The Cortex-M0 I wanted to use simply doesn't have enough flash space to hold the application, so I switched to a larger Cortex-M4 board. It's still using floating point calculations for the forces acting on my labels and turns out the be quite responsive and fast.

    Crappy calibration turns out to be a problem when I try to drag items around, because not hitting an item results in another one being added, making the place quite crowded at times. Many force directed items are quite entertaining to look at, especially if their initial position is off screen. They are pushed into a place far far away that somewhen "rolls over" to zero, making the label appear on screen again and then trying to make its way to the button it belongs to.

    GWIN_REDRAW_IMMEDIATE (the only option I tried so far) results in far less drawing artifacts. I just don't know what exactly it does and what the tradeoff is. The problem is that at some (seemingly random) point my app crashes, which might be related to my memory management.

    I'll try to make a video later today!

  18. I'm targeting a Cortex-M0 platform with an RA8875 controlled TFT (you know my other threads). I'll try to get it to run on that platform today. My impression regarding real hardware vs X11 drawing is that X11 looks more sluggish and less "crisp". When I draw a character on my TFT it is "just there", whereas it looks like being built on X11.

    My force directed graph code is probably too CPU hungry and I'd probably have to use fixed point calculations instead of floating point. Using std::list is probably also a very bad idea...

    I'll also have a look at the different window manager options, thanks for that hint!

×
×
  • Create New...