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
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 * * 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 * * 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:
#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); }
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?
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.