sdawans Posted November 10, 2016 Report Share Posted November 10, 2016 Hello, I'm currently developing a µGFX based GUI on ATSAMG55J19 running FreeRTOS, with a ILI9431 controller in SPI 4-wire mode. I've started prototyping the GUI in baremetal first to split the development into smaller problems. I'll integrate it in a FreeRTOS task later once I'm happy with the result. The application has multiple screens, for which I'm using containers. Not relevant to my current question but maybe worth mentioning is that I have a sidebar that uses a unique instance of a container that doesn't need to be refreshed when changing pages, while the pages themselves are implemented in the remaining 230x240 portion of the screen (which is 320x240). Option #2 of this this answer was helpful in deciding how to implement this static sidebar without duplicating the widgets and it works fine. I'm also using this trick to make clickable zones over multiple widgets within the sidebar as I didn't want simple buttons but rather a combination of icons and dynamic labels in each clickable area. Again, not completely relevant to what I want to ask next. The issue I'm facing is that although the page transitions are functional, the refresh of the screen is done twice. Since refresh operations are not very fast, this is quite an annoying glitch and I'm trying to figure out what could be causing two refreshes. The total wait time is actually 4 page loads, because the pages are first cleared before displaying a new one. I can clearly see that the page displays all widgets correctly after the first clear + redraw, but it gets cleared and re-drawn a second time. Here is are the relevant portions of the code, you'll notice I used a uGFX Studio-generated project as initial template. I tried removing as much noise as possible to limit this post's length: /* Called from mainloop after other platform initialisations * and a call to gfxInit(); */ int ugfx_test(void){ gdispSetBacklight(100); gdispSetContrast(100); geventListenerInit(&glistener); gwinAttachListener(&glistener); guiCreate(); while (1) { guiEventLoop(); } } void guiCreate(void) { GWidgetInit wi; // Prepare fonts dejavu_sans_16 = gdispOpenFont("DejaVuSans16"); dejavu_sans_24 = gdispOpenFont("DejaVuSans24"); dejavu_sans_32 = gdispOpenFont("DejaVuSans32"); // GWIN settings gwinWidgetClearInit(&wi); gwinSetDefaultFont(dejavu_sans_16); gwinSetDefaultStyle(&white, FALSE); gwinSetDefaultColor(white_studio); gwinSetDefaultBgColor(white_studio); /* Create 6 pages */ createPage00_standby(); createPage01_main(); createPage02_important(); createPage03_info(); createPage04_measure_menu(); createPage05_measure_pressure(); /* Create the sidebar in a unique container */ create_sidebar(); /* Display page 0 at the start */ guiShowPage(0); gwinShow(ghContainer_sidebar); } void guiShowPage(unsigned pageIndex) { // Hide all pages gwinHide(ghContainer00_standby); gwinHide(ghContainer01_main); gwinHide(ghContainer02_important); gwinHide(ghContainer03_info); gwinHide(ghContainer04_measure_menu); gwinHide(ghContainer05_measure_pressure); // Show page selected page switch (pageIndex) { case 0: gwinShow(ghContainer00_standby); break; case 1: gwinShow(ghContainer01_main); break; /* etc... cases 2 to 5 also handled here for remaining pages */ /* ... */ default: break; } } void guiEventLoop(void) { GEvent* pe; while (1) { // Get an event pe = geventEventWait(&glistener, 10); switch (pe->type) { case GEVENT_GWIN_BUTTON: if ( ((GEventGWinButton*)pe)->gwin == ghButton1) { guiShowPage(1); } else if ( ((GEventGWinButton*)pe)->gwin == ghButton2) { guiShowPage(2); } /* etc, same thing for other pages */ /* ... */ break; } } } I left out the createPagexx functions as they are quite long, but the general idea for all of them is pretty much identical to what uGFX generates in a typical display page, for example: static void createPage01_main(void) { GWidgetInit wi; gwinWidgetClearInit(&wi); // create container widget: ghContainer01_main wi.g.show = FALSE; wi.g.x = 0; wi.g.y = 0; wi.g.width = 230; wi.g.height = 240; wi.g.parent = 0; wi.text = "Container"; wi.customDraw = 0; wi.customParam = 0; wi.customStyle = 0; ghContainer01_main = gwinContainerCreate(0, &wi, 0); // Create label widget: ghLabel_1_date wi.g.show = TRUE; wi.g.x = 10; wi.g.y = 10; wi.g.width = 100; wi.g.height = 20; wi.g.parent = ghContainer01_main; wi.text = "12/03/2016"; wi.customDraw = gwinLabelDrawJustifiedCenter; wi.customParam = 0; wi.customStyle = 0; ghLabel_1_date = gwinLabelCreate(0, &wi); gwinLabelSetBorder(ghLabel_1_date, FALSE); gwinSetFont(ghLabel_1_date, dejavu_sans_16); gwinRedraw(ghLabel_1_date); /* Other child widgets of ghContainer01_main here, not shown */ /* ... */ } I'm trying to understand the source of the double refresh: Could be due to the actual button event being generated twice due to some sort of button debounce problem, althoughit seems unlikely. As I understand it, there is no queue for events so if multiple events are fired all subsequent events will be dropped until we come back to a blocking wait in geventEventWait(...). Could be due to some child widgets needing to be redrawn after the initial redraw of the page. Could child widgets needing to be redraw trigger a redraw of the parent container? Seems unlikely again, especially since I get this with even the most basic container with a single label as child. Brain fart from my part, highly likely Thanks for the great software and tools you are putting together, and for any support on this matter. By the way, are there any plans to open source the uGFX Studio? I'm willing to contribute some minor bugfixes. Have a nice day Sébastien Link to comment Share on other sites More sharing options...
Joel Bodenmann Posted November 10, 2016 Report Share Posted November 10, 2016 Hello Sébastien and welcome to the community! First to of all, let me thank you for your very nice post. If everybody would post their questions like this, life would be a whole lot easier for us. The background information you give along the minimized examples are really helpful. I'm on the road right now so I can't run your example - this might have to wait until tomorrow. I also don't know anything on top of my head that might help you - we're going to have a look at this and report back ASAP. Would it be possible for you to attach your simplified code as a ready-to-run projects as a ZIP archive? This would further simplify the process of tracking down & fixing the issue for us. 9 minutes ago, sdawans said: By the way, are there any plans to open source the uGFX Studio? I'm willing to contribute some minor bugfixes. The current version of the uGFX-Studio is pretty much dead. We're not putting any more time into it unless a company requests bug fixes or new features as part of a paid support plan. We started working on a completely new version of the uGFX-Studio which will be a lot better in almost all aspects. You can have a look at that here: Whether or not that tool will be open-sourced has not been decided yet. Link to comment Share on other sites More sharing options...
sdawans Posted November 10, 2016 Author Report Share Posted November 10, 2016 Hi Joel, That was quick Well, I was mostly interested in a "top of your head" answer for something blatantly obvious that I was missing or misusing, or for a referral to a similar problem experienced by someone else that I may have missed when reading past posts in this forum. If this isn't the case and you are willing to dive into the code I can certainly share the project but since it's custom hardware that I'm designing you won't be able to run it as-is. It's also currently formatted as an Atmel Studio solution so you would need that to build it out of the box. Or maybe you were talking about me sharing an x86 compatible version - that I can definately do as well. In fact, for debugging reasons I will go ahead with that idea and see if the problem also appear in the x86 version (although maybe there the refresh rate will be too fast to see anything). Thanks for your time, Sébastien Link to comment Share on other sites More sharing options...
Joel Bodenmann Posted November 10, 2016 Report Share Posted November 10, 2016 Hello Sébastien, If you can share a stripped down version of the project that runs as an x86 project so we can run it on Windows that would be great. Other than that, if you want to dive into it yourself you can simply put a breakpoint at the redrawing function and look at the backtrace to figure out what causes the 2nd redraw. Link to comment Share on other sites More sharing options...
sdawans Posted November 10, 2016 Author Report Share Posted November 10, 2016 (edited) Here is a working build on Win32 Some notes on usability: Make sure to change the path to your ugfx library in Makefile (sorry for stating the obvious, this message is for anyone else in the future). The project has 6 pages numbered 00 to 05. Page 00 is displayed at startup Pages 01 to 04 are displayed using the 4 clickable zones in the sidebar on the right. These zones are not currently delimited graphically, it's something I will fix later by applying a texture to the container background but it is low priority. Page 05 is not reachable currently. The battery icon changes between 25% and 100% based on the page number, obviously later I must connect this to a real notification of battery % coming from another peripheral. The battery % text is not implemented. Just saying so you don't think it's a bug. Since this is for an embedded platform I kept images in the C-style format, not using the .bmp directly. These were generated using file2c with -d option. As you'll notice, there is no visible glitch between page transitions. Something I noticed for the x86 version is that geventEventWait returns a null pointer on timeout when there is no event, triggering a segfault. I made a small modification to avoid this: pe = geventEventWait(&glistener, 0); if(pe == NULL){ continue; } 05-ugfx-template-x86.zip Thanks for bearing with me, I'll admit I hacked this together overnight (and the x86 version just now) so not properly documented but I think you'll get the general idea of what this is about. best regards, Sébastien Edited November 10, 2016 by sdawans Link to comment Share on other sites More sharing options...
sdawans Posted November 10, 2016 Author Report Share Posted November 10, 2016 (edited) I didn't step through the code yet simply because I don't have a proper debugging setup yet, having some issues with the Atmel EDBG thing that I'll look into now. In the meantime I did browse through some of the code involved in redrawing and I noticed that #define GWIN_REDRAW_IMMEDIATE TRUE fixes my problem, although other glitches appear such as the sidebar widgets being hidden under the buttons. Will get back when I get a working debugging setup (should have done that before posting, sorry). Edited November 10, 2016 by sdawans Link to comment Share on other sites More sharing options...
inmarket Posted November 11, 2016 Report Share Posted November 11, 2016 The reason your geventEventWait is returning NULL is that you have passed in 0 which is TIME_IMMEDIATE. You are effectively telling it to not wait for an event, so if there is no event ready it returns 0. If you pass TIME_FOREVER (Ithink that is what it is called) you will not get back a NULL, instead it will wait for an event. Any value in between those numbers is the maximum milliseconds to wait for the event. Link to comment Share on other sites More sharing options...
Joel Bodenmann Posted November 11, 2016 Report Share Posted November 11, 2016 40 minutes ago, inmarket said: If you pass TIME_FOREVER (Ithink that is what it is called) you will not get back a NULL, instead it will wait for an event. It's TIME_INFINITE Link to comment Share on other sites More sharing options...
sdawans Posted November 11, 2016 Author Report Share Posted November 11, 2016 Hi Joel, I did realize the second parameter was a timeout and there is no reason for me to use anything other than TIME_INFINITE in my case. What I meant to say is that accessing the null pointer on the x86 build does not behave in the same way as the embedded platform so I added the safeguard to allow it to run. I mentioned it to point exhaustively all the differences between my embedded code and this one. Cheers, Sébastien Link to comment Share on other sites More sharing options...
inmarket Posted November 11, 2016 Report Share Posted November 11, 2016 0 = TIME_IMMEDIATE Link to comment Share on other sites More sharing options...
David Kibble Posted May 2, 2017 Report Share Posted May 2, 2017 Hi Guys. Going back to the original question - did you solve the problem of the double redraw? I'm having something similar on multi-container UIs. Switching between 'screens' using the guiShowPage(...) type approach generates two redraws, the first after the gwinHide, the second after the gwinShow I think. You mention trying different GWIN_REDRAW_IMMEDIATE modes, what did you conclude? Many thanks Link to comment Share on other sites More sharing options...
Matteo Vigni Posted July 30, 2020 Report Share Posted July 30, 2020 (edited) Hello, I found a workaround for the double redraw issue and thought I would share. I'm working on an STM32L4 MCU running FreeRTOS. My workaround basically consist in modifying the guiShowPage function this way: void guiShowPage(unsigned pageIndex) { // Hide all pages gwinHide( page1 ); gwinHide( page2 ); gwinHide( page3 ); /* ... */ // Allow the rendering thread to clear the screen gfxYield(); // ADD THIS LINE! // Show page selected page switch ( pageIndex ) { case 0: gwinRaise( page1 ); // ADD THIS LINE! gwinShow( page1 ); break; case 1: gwinRaise( page2 ); // ADD THIS LINE! gwinShow( page2 ); break; /* ... */ default: break; } } I hope this will help someone else in the future Edited July 30, 2020 by Matteo Vigni Link to comment Share on other sites More sharing options...
Joel Bodenmann Posted July 31, 2020 Report Share Posted July 31, 2020 Welcome to the µGFX community & welcome for sharing your workaround, @Matteo Vigni! Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now