Moving Window as Image

Hi everyone,

I'm trying to move a whole window (with a lot of widgets in it) as quickly as possible. To achieve this I believe one would need to take a picture of it and move this picture, since the gx_widget_shift commands will move the while widget structure and every underlying widget will be updated, which slows this process down.

Is there a possibility to create an image (or use the framebuffer?) of the screen and then move this image?

Secondly, is it possible to draw a whole window off-screen on another canvas and then move this new canvas as an image into the display?

Regards,

Christoph

  • Good day, Christoph!

    Have you figured this one out already?

    Sai
    RenesasRulz Forum Moderator
    https://renesasrulz.com/
    https://academy.renesas.com/
    en-us.knowledgebase.renesas.com/
  • In reply to Sai:

    Hello Sai,

    I have not and some help would be greatly appreciated.

    Kind Regards,

    Christoph
  • In reply to cbismark:

    Hi Chris,

    At the application level, moving window as a bitmap is not supported however I know that GUIX does make some use of "block move" feature in D/AVE2D engine on Synergy. I don't think this is used by widget move however, because other than visual translation, changes to the control block of each child are required to ensure correct widget behavior (such as touch input registration).

    You can possibly "cheat" and implement this feature using second, unused display layer (typically foreground layer). Contents of this layer can be managed directly by user. Common use case for this is to perform smooth window transitions:
    1. Copy existing base (background) frame buffer to the foreground frame buffer.
    2. Make foreground buffer visible.
    3. Trigger changes to the GUI so that background frame buffer is updated.
    4. Move/fade foreground buffer out of view to reveal updated base frame buffer.

    Unfortunately this cannot be auto-managed by current version of GUIX and SSP.

    Regards
  • Hello Christoph,

    Has Karol's explanation answered your questions or do you need any additional support?

    Kind regards,
  • In reply to Sergey Sokol:

    Hello Karol, Hello Sergey,

    I will have to try this out and see if I can adapt the functionality Karol mentioned for my use case. I'll write again, if I'm having trouble and also if it works.

    Kind Regards,

    Christoph
  • In reply to cbismark:

    Hi Christoph,

    Have you already tried out Karol's answer? Did it work for you?

    Herald
    RenesasRulz Forum Moderator

    https://renesasrulz.com/
    https://academy.renesas.com/
    en-us.knowledgebase.renesas.com/
  • In reply to Herald:

    Hi Herald,

    do you or Karol have got any more hints how to achieve what she suggested? I haven't been able to find the correct functions for doing what she has in mind? Or is it meant to be copied with basic functions like memcpy?`

    Regards,

    Christoph
  • In reply to cbismark:

    Hello Christoph,

    I haven't got a code to perform exactly what you're looking for, but I do have something similar you might find useful. I wrote following function set to provide sliding effect when changing windows. It's meant to run on 800x480 so you'll probably need to amend all hard-coded references to "800" and "480". I'm providing this code as is.

    gx_usr_screen_slide.c:

    gx_usr_screen_slide.c.txt
    /*
     * gx_usr_screen_slide.c
     *
     *  Created on: Dec 12, 2018
     *      Author: Karol.Saja
     */
    
    #include "hal_data.h"
    #include "gx_usr_screen_slide.h"
    
    static inline void fbcopy(void);
    
    static INT slide_distance;
    static gx_usr_screen_slide_type slide_type;
    
    UINT gx_usr_screen_slide_start(GX_WINDOW * p_old, GX_WINDOW * p_new,
                                   gx_usr_screen_slide_type type)
    {
        UINT status = GX_SUCCESS;
    
        if (0 != slide_distance)
        {
            status = GX_FAILURE;
        }
    
        else
        {
            slide_type = type;
    
            fbcopy();
    
            switch (type)
            {
                case SLIDE_FROM_LEFT:
                {
                    INT shift_current = SCREEN_SLIDE_STEP_FX(800);
                    slide_distance = 800 - shift_current;
                    g_display_runtime_cfg_fg.layer.coordinate.x = (SHORT) (0 + shift_current); // move to the right
                    g_display_runtime_cfg_fg.layer.coordinate.y = 0;
                    g_display_runtime_cfg_fg.input.p_base = (ULONG *) g_display_fb_foreground[0];
                    while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_fg,
                                                        DISPLAY_FRAME_LAYER_2));
    
                    g_display_runtime_cfg_bg.layer.coordinate.x = (SHORT) (-800 + shift_current); // move to the right
                    while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_bg,
                                                        DISPLAY_FRAME_LAYER_1));
                }
                break;
    
                case SLIDE_FROM_RIGHT:
                {
    
                    INT shift_current = SCREEN_SLIDE_STEP_FX(800);
                    slide_distance = 800 - shift_current;
                    g_display_runtime_cfg_fg.layer.coordinate.x = (SHORT) (0 - shift_current); // move to the left
                    g_display_runtime_cfg_fg.layer.coordinate.y = 0;
                    g_display_runtime_cfg_fg.input.p_base = (ULONG *) g_display_fb_foreground[0];
                    while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_fg,
                                                        DISPLAY_FRAME_LAYER_2));
    
                    g_display_runtime_cfg_bg.layer.coordinate.x = (SHORT) (800 - shift_current); // move to the left
                    while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_bg,
                                                        DISPLAY_FRAME_LAYER_1));
                }
                break;
    
                case SLIDE_FROM_TOP:
                {
                    INT shift_current = SCREEN_SLIDE_STEP_FX(480);
                    slide_distance = 480 - shift_current;
                    g_display_runtime_cfg_fg.layer.coordinate.y = (SHORT) (0 + shift_current); // move to the bottom
                    g_display_runtime_cfg_fg.layer.coordinate.x = 0;
                    g_display_runtime_cfg_fg.input.p_base = (ULONG *) g_display_fb_foreground[0];
                    while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_fg,
                                                        DISPLAY_FRAME_LAYER_2));
    
                    g_display_runtime_cfg_bg.layer.coordinate.y = (SHORT) (-480 + shift_current); // move to the bottom
                    while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_bg,
                                                        DISPLAY_FRAME_LAYER_1));
                }
                break;
    
                case SLIDE_FROM_BOTTOM:
                {
    
                    INT shift_current = SCREEN_SLIDE_STEP_FX(480);
                    slide_distance = 480 - shift_current;
                    g_display_runtime_cfg_fg.layer.coordinate.y = (SHORT) (0 - shift_current); // move to the top
                    g_display_runtime_cfg_fg.layer.coordinate.x = 0;
                    g_display_runtime_cfg_fg.input.p_base = (ULONG *) g_display_fb_foreground[0];
                    while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_fg,
                                                        DISPLAY_FRAME_LAYER_2));
    
                    g_display_runtime_cfg_bg.layer.coordinate.y = (SHORT) (480 - shift_current); // move to the top
                    while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_bg,
                                                        DISPLAY_FRAME_LAYER_1));
                }
                break;
            }
    
            gx_widget_attach(p_old->gx_widget_parent, p_new);
            gx_widget_detach(p_old);
    
            gx_system_timer_start(p_old, SCREEN_SLIDE_TIMER_ID, 1, 1);
        }
    
        return status;
    }
    
    
    VOID gx_usr_screen_slide_step(GX_WINDOW * p_old)
    {
        switch (slide_type)
        {
            case SLIDE_FROM_LEFT:
            {
                INT shift_current = SCREEN_SLIDE_STEP_FX(slide_distance);
                shift_current = shift_current > slide_distance ? slide_distance : shift_current;
                slide_distance = slide_distance - shift_current;
                g_display_runtime_cfg_bg.layer.coordinate.x =
                        (SHORT) (g_display_runtime_cfg_bg.layer.coordinate.x + shift_current);
                while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_bg,
                                                    DISPLAY_FRAME_LAYER_1));
    
                if (0 == g_display_runtime_cfg_bg.layer.coordinate.x)
                {
                    g_display_runtime_cfg_fg.input.p_base = NULL;
    
                    gx_system_timer_stop(p_old, SCREEN_SLIDE_TIMER_ID);
                }
                else
                {
                    g_display_runtime_cfg_fg.layer.coordinate.x =
                            (SHORT) (g_display_runtime_cfg_fg.layer.coordinate.x + shift_current);
                }
    
                while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_fg,
                                                    DISPLAY_FRAME_LAYER_2));
            }
            break;
    
            case SLIDE_FROM_RIGHT:
            {
                INT shift_current = SCREEN_SLIDE_STEP_FX(slide_distance);
                shift_current = shift_current > slide_distance ? slide_distance : shift_current;
                slide_distance = slide_distance - shift_current;
                g_display_runtime_cfg_bg.layer.coordinate.x =
                        (SHORT) (g_display_runtime_cfg_bg.layer.coordinate.x - shift_current);
                while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_bg,
                                                    DISPLAY_FRAME_LAYER_1));
    
                if (0 == g_display_runtime_cfg_bg.layer.coordinate.x)
                {
                    g_display_runtime_cfg_fg.input.p_base = NULL;
    
                    gx_system_timer_stop(p_old, SCREEN_SLIDE_TIMER_ID);
                }
                else
                {
                    g_display_runtime_cfg_fg.layer.coordinate.x =
                            (SHORT) (g_display_runtime_cfg_fg.layer.coordinate.x - shift_current);
                }
    
                while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_fg,
                                                    DISPLAY_FRAME_LAYER_2));
            }
            break;
    
            case SLIDE_FROM_TOP:
            {
                INT shift_current = SCREEN_SLIDE_STEP_FX(slide_distance);
                shift_current = shift_current > slide_distance ? slide_distance : shift_current;
                slide_distance = slide_distance - shift_current;
                g_display_runtime_cfg_bg.layer.coordinate.y =
                        (SHORT) (g_display_runtime_cfg_bg.layer.coordinate.y + shift_current);
                while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_bg,
                                                    DISPLAY_FRAME_LAYER_1));
    
                if (0 == g_display_runtime_cfg_bg.layer.coordinate.y)
                {
                    g_display_runtime_cfg_fg.input.p_base = NULL;
    
                    gx_system_timer_stop(p_old, SCREEN_SLIDE_TIMER_ID);
                }
                else
                {
                    g_display_runtime_cfg_fg.layer.coordinate.y =
                            (SHORT) (g_display_runtime_cfg_fg.layer.coordinate.y + shift_current);
                }
    
                while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_fg,
                                                    DISPLAY_FRAME_LAYER_2));
            }
            break;
    
            case SLIDE_FROM_BOTTOM:
            {
                INT shift_current = SCREEN_SLIDE_STEP_FX(slide_distance);
                shift_current = shift_current > slide_distance ? slide_distance : shift_current;
                slide_distance = slide_distance - shift_current;
                g_display_runtime_cfg_bg.layer.coordinate.y =
                        (SHORT) (g_display_runtime_cfg_bg.layer.coordinate.y - shift_current);
                while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_bg,
                                                    DISPLAY_FRAME_LAYER_1));
    
                if (0 == g_display_runtime_cfg_bg.layer.coordinate.y)
                {
                    g_display_runtime_cfg_fg.input.p_base = NULL;
    
                    gx_system_timer_stop(p_old, SCREEN_SLIDE_TIMER_ID);
                }
                else
                {
                    g_display_runtime_cfg_fg.layer.coordinate.y =
                            (SHORT) (g_display_runtime_cfg_fg.layer.coordinate.y - shift_current);
                }
    
                while (g_display.p_api->layerChange(g_display.p_ctrl, &g_display_runtime_cfg_fg,
                                                    DISPLAY_FRAME_LAYER_2));
            }
            break;
        }
    }
    
    
    static inline void fbcopy(void)
    {
        ULONG * p_s = g_display_runtime_cfg_bg.input.p_base;
        ULONG * p_d = (ULONG *) g_display_fb_foreground[0];
        ULONG * p_e = p_d + (sizeof(g_display_fb_foreground[0]) / sizeof(ULONG));
    
        while (p_d < p_e)
        {
            /* x32 */
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
            * p_d++ = * p_s++;
        }
    }
    

    gx_usr_screen_slide.h:

    gx_usr_screen_slide.h.txt
    /*
     * gx_usr_screen_slide.h
     *
     *  Created on: Dec 12, 2018
     *      Author: Karol.Saja
     */
    
    #ifndef GX_USR_SCREEN_SLIDE_H_
    #define GX_USR_SCREEN_SLIDE_H_
    
    #define SCREEN_SLIDE_TIMER_ID       (199)
    #define SCREEN_SLIDE_STEP_FX(a)     (((a) / 10) + 15)
    
    typedef enum
    {
        SLIDE_FROM_LEFT,
        SLIDE_FROM_RIGHT,
        SLIDE_FROM_TOP,
        SLIDE_FROM_BOTTOM,
    } gx_usr_screen_slide_type;
    
    UINT gx_usr_screen_slide_start(GX_WINDOW *, GX_WINDOW *, gx_usr_screen_slide_type);
    VOID gx_usr_screen_slide_step(GX_WINDOW *);
    
    #endif /* GX_USR_SCREEN_SLIDE_H_ */
    

    Here's an example on how this is used. gx_usr_screen_slide_start sets everything up (here it uses button press) and GX_EVENT_TIMER case must be provided to handle position updates using gx_usr_screen_slide_step:

    w_home_functions.c.txt
    #include "hal_data.h"
    #include "guix/gxs_specifications.h"
    
    #include "gx_usr_screen_slide.h"
    
    UINT w_home_ev(GX_WINDOW * p_self, GX_EVENT * p_ev)
    {
        switch (p_ev->gx_event_type)
        {
            case GX_SIGNAL(ID_MBTN_COOK, GX_EVENT_CLICKED):
            {
                /* Slide away to the right */
                gx_usr_screen_slide_start(p_self, (GX_WINDOW *) &w_cook_s0, SLIDE_FROM_LEFT);
            }
            break;
    
            case GX_SIGNAL(ID_MBTN_TIME, GX_EVENT_CLICKED):
            {
                /* Slide away to the top */
    
                /* Slide in time screen from the bottom */
            }
            break;
    
            case GX_SIGNAL(ID_MBTN_UNUSED, GX_EVENT_CLICKED):
            {
    
            }
            break;
    
            case GX_EVENT_TIMER:
            {
                if (SCREEN_SLIDE_TIMER_ID ==
                        p_ev->gx_event_payload.gx_event_timer_id)
                {
                    gx_usr_screen_slide_step(p_self);
                }
            }
        }
    
        return gx_window_event_process(p_self, p_ev);
    }
    

  • In reply to Renesas Karol:

    Hi Karol,

    thank you very much for your suggestion. As far as I can see, the speed of this solution is far superior to simply shifting the widgets.
    However, I needed to add a second screen in GUIX configuration, because I got the error that "g_display0_runtime_cfg_fg" was an undefined reference. When I start the application the screen is all messed up. When I click the correct position where my button for starting the slide is, the slide works correctly and afterwards the screen looks good again. I assume GUIX boots up with the wrong screen.

    Is there a way to tell it to boot with the correct screen? Or am I doing something wrong?

    Regards,

    Christoph

     

    EDIT: I solved this via invoking

    g_display0_runtime_cfg_fg.input.p_base = (ULONG *) g_display0_fb_background[0];
        while (g_display0.p_api->layerChange(g_display0.p_ctrl, &g_display0_runtime_cfg_fg,
                                                            DISPLAY_FRAME_LAYER_2));

    in the gui_thread_entry. But there still remains one problem: When I e.g. SLIDE_FROM_LEFT, the window that is supposed to be moved out is overwritten by the window that is moved in. To clarify: I SLIDE_FROM_LEFT in window 2, window 1 moves in from the left and the window 2 which moves out is overwritten by window 1. I suppose this happens in g_display0_runtime_cfg_fg.input.p_base = (ULONG *) g_display0_fb_background[0];.

    Is there a way to solve this properly?

  • In reply to cbismark:

    Okay final reply:

    I created another buffer in SDRAM, where I copied the current visible framebuffer window content into. Then I set
    g_display0_runtime_cfg_fg.input.p_base = (ULONG*)slide_buf; to let the foreground buffer point to my new buffer.
    When swiping the foreground buffer (old window) is moved out and the new window is moved in.

    Regards,

    Christoph
  • In reply to cbismark:

    Hello Chris,

    To hide the foreground buffer, it's recommended to instead set p_base to NULL - that way GLCDC will know not to display this buffer.

    When using foreground display layer, you don't need to create an additional buffer as Synergy Configurator will allocate one for you anyway (g_display_fb_foreground). You should also make sure to set number of foreground buffers to 1 since the buffer will only be used for transitions using canvas offset rather than updating the actual frame buffer.

    Regards
  • In reply to Renesas Karol:

    Hello Karol,

    thank you again for your reply. After doing as you recommended it works now. It also solved a problem I had with my screensaver where a logo was moved across the screen and the screen was suddenly flickering, as if double buffering was turned off. If anyone has the same problem, I will reiterate exactly what I did:

    in the gui_thread_entry:

    g_display0_runtime_cfg_fg.input.p_base = NULL;
        while (g_display0.p_api->layerChange(g_display0.p_ctrl, &g_display0_runtime_cfg_fg,
                                                                DISPLAY_FRAME_LAYER_2));

    The sliding function is the one you provided, only that I start it not with a click on a button, but when a PEN_DRAG event is raised and enough distance was recorded. The sliding motion looks great, up to par with modern smartphone levels. Thank you very much for you help!

    One thing I wonder however, why does GUIX not implement such smooth transitions in the standard APIs? Even with a predefined animation sliding windows in and out stutters and just doesn't feel good.

    Regards,

    Christoph

  • In reply to cbismark:

    Hi Christoph,

    Support for second hardware layer is on the roadmap however the way secondary canvases are allocated and used in GUIX is quite different to how Synergy hardware does it.

    Regards
  • In reply to Renesas Karol:

    Hi Karol,

    When there are new version I'll see when and if that happens.

    Regards,

    Christoph