Jump to content

crteensy

Members
  • Posts

    111
  • Joined

  • Last visited

Everything 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. test - seems to work again from my university's network
  3. 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
  4. 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.
  5. 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.
  6. I'm not editing this into the original post because it's kind of a new facette to the problem: The application doesn't hang when I use a different font. I had UI2 Narrow, but the hang does not happen with fixed 10x20. Instead, buttons simply don't display any text.
  7. 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.
  8. 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.
  9. 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?
  10. That sounds a bit complicated and somewhat makes the whole point of encapsulation moot, doesn't it?
  11. 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?
  12. 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.
  13. 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
  14. The items were a bit hard to delete because any movement while pressed would be treated as a drag operation. I've added a 5 pixel threshold to that and now they're much easier to remove. New video, now running on target hardware: http://youtu.be/nasV63EVM2I Surprisingly smooth!
  15. 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. 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.
  16. 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.
  17. Is that done by setting GOS_RAW_HEAP_SIZE to some non-zero value in gfxconf.h? I can probably give ugfx a few kB as heap to play with and see how it goes.
  18. 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.
  19. 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).
  20. 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!
  21. 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.
  22. 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!
  23. 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!
  24. I'm ignoring that issue now because drawing with screen absolute coordinates works quite well (set x and y in the widget struct and then redraw). Here's a video: http://youtu.be/vMinMt-Qn8A
×
×
  • Create New...