- Added bunch of screens, fonts and images - Added script to read out frame buffer (function currently disabled in Firmware)
947 lines
29 KiB
C
947 lines
29 KiB
C
/*
|
|
* Display.c
|
|
*
|
|
* Created: Sun Mar 21 2021 15:52:12
|
|
* Author Chris
|
|
*/
|
|
#include "Display.h"
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include "hardware/dma.h"
|
|
|
|
#include "Screens.h"
|
|
#include "Display_SPI.h"
|
|
#include "Display_Init.h"
|
|
#include "Display_Font.h"
|
|
#include "Display_Color.h"
|
|
#include "Display_Image.h"
|
|
#include "Display_Touch.h"
|
|
#include "Display_Shapes.h"
|
|
#include "Display_Objects.h"
|
|
#include "Display_Render_Simple.h"
|
|
#include "Display_Render_Complex.h"
|
|
#include "Display_Message_Box_Icons.h"
|
|
|
|
#include "Easings.h"
|
|
|
|
|
|
// ============================================================================================
|
|
// Defines
|
|
#define DEG2RAD (float)(M_PI / 180)
|
|
|
|
|
|
// ============================================================================================
|
|
// Variables
|
|
static Display_Image_Buffer _Image_Buffer;
|
|
static Display_Image_Buffer _Image_Buffer_Backup;
|
|
static Display_Image_Buffer* _Current_Buffer;
|
|
|
|
static int _DMA_Channel_Copy_Buffer;
|
|
static dma_channel_config _DMA_Config_Copy_Buffer;
|
|
|
|
static int _Touched_Button_Return_Value;
|
|
|
|
static bool _Object_Selected = false;
|
|
static uint _Frame_Counter = 0;
|
|
static bool _Touch_Initialized = false;
|
|
static bool _Draw_Touch_Reference_Points = false;
|
|
static bool _Draw_Center_Lines = false;
|
|
|
|
static Screen_Transition_Settings_t _Transition_Settings;
|
|
|
|
|
|
// ============================================================================================
|
|
// Function Declarations
|
|
void Display_Draw_Style (Coordinates* coordinates, Style* style, uint32_t content_width, uint32_t content_height, bool do_draw);
|
|
void Display_Draw_Focused (Coordinates* coordinates, uint32_t width, uint32_t height);
|
|
|
|
Animation_State Display_Animation_Tick(Display_Object* object);
|
|
|
|
void Display_Draw_Touch_Refernce_Points(Display_Color color);
|
|
void Display_Draw_Touch_Marker(Display_Color color);
|
|
void Display_Draw_Center_Lines(Display_Color color);
|
|
|
|
void Display_Check_Button_Touch(int16_t x, int16_t y);
|
|
void Display_Object_Select_Next(void);
|
|
void Display_Object_Select_Previous(void);
|
|
Display_Object* Display_Get_Selected_Object(void);
|
|
int Display_Get_Index_Of_Object(Display_Object* object);
|
|
|
|
void Display_Screen_Transition_Tick();
|
|
void Display_Buffer_Shift_Left(uint32_t steps);
|
|
void Display_Buffer_Shift_Right(uint32_t steps);
|
|
void Display_Buffer_Shift_Up(uint32_t steps);
|
|
void Display_Buffer_Shift_Down(uint32_t steps);
|
|
|
|
void Display_Set_Current_Image_Buffer(Display_Image_Buffer* buffer);
|
|
void Display_Copy_Buffer(Display_Image_Buffer *src, Display_Image_Buffer *dest);
|
|
|
|
|
|
/*******************************************************************
|
|
Functions
|
|
*******************************************************************/
|
|
void Display_Init(Display_Color initial_color, bool send_buffer, bool init_touch)
|
|
{
|
|
_Touch_Initialized = init_touch;
|
|
|
|
_Touched_Button_Return_Value = -1;
|
|
|
|
Display_Set_Current_Image_Buffer(&_Image_Buffer);
|
|
|
|
|
|
_Transition_Settings.Direction_Out = TRANSITION_NONE;
|
|
_Transition_Settings.Direction_In = TRANSITION_NONE;
|
|
_Transition_Settings.Offset.X = 0;
|
|
_Transition_Settings.Offset.Y = 0;
|
|
_Transition_Settings.Type = LINEAR;
|
|
_Transition_Settings.Frame_Duration = 0;
|
|
_Transition_Settings.Step = 0;
|
|
_Transition_Settings.Position_In = 0;
|
|
_Transition_Settings.Position_Out = 0;
|
|
|
|
|
|
_DMA_Channel_Copy_Buffer = dma_claim_unused_channel(true);
|
|
_DMA_Config_Copy_Buffer = dma_channel_get_default_config(_DMA_Channel_Copy_Buffer);
|
|
channel_config_set_transfer_data_size(&_DMA_Config_Copy_Buffer, DMA_SIZE_32);
|
|
channel_config_set_read_increment(&_DMA_Config_Copy_Buffer, true);
|
|
channel_config_set_write_increment(&_DMA_Config_Copy_Buffer, true);
|
|
|
|
|
|
Display_Shapes_Init(&_Current_Buffer);
|
|
Display_Font_Init();
|
|
Display_Image_Init(&_Current_Buffer, initial_color);
|
|
Display_Render_Simple_Init(&_Transition_Settings);
|
|
|
|
Display_Init_GPIOs();
|
|
Display_SPI_Init(init_touch);
|
|
|
|
Display_Init_Reset();
|
|
Display_Init_Registers();
|
|
Display_Init_WakeUp();
|
|
|
|
if(init_touch) {
|
|
Display_Touch_Init();
|
|
}
|
|
|
|
Display_Objects_Init(initial_color);
|
|
|
|
if(send_buffer) {
|
|
Display_Render_Objects();
|
|
}
|
|
}
|
|
|
|
void Display_Issue_Touch_Event(int16_t x_screen, int16_t y_screen)
|
|
{
|
|
// Display_Check_Button_Touch(x_screen, y_screen);
|
|
_Screen_Touch_Event(x_screen, y_screen);
|
|
}
|
|
|
|
void Display_Set_Draw_Touch_Reference_Points(bool do_draw)
|
|
{
|
|
_Draw_Touch_Reference_Points = do_draw;
|
|
}
|
|
|
|
void Display_Set_Draw_Center_Lines(bool do_draw)
|
|
{
|
|
_Draw_Center_Lines = do_draw;
|
|
}
|
|
|
|
void Display_Screen_Transition_Start(Screen_Transition_Direction direction_out, Screen_Transition_Direction direction_in, Easing type, uint32_t frame_duration)
|
|
{
|
|
_Transition_Settings.Direction_Out = direction_out;
|
|
_Transition_Settings.Direction_In = TRANSITION_NONE;
|
|
_Transition_Settings.Offset.X = 0;
|
|
_Transition_Settings.Offset.Y = 0;
|
|
_Transition_Settings.Type = type;
|
|
_Transition_Settings.Frame_Duration = frame_duration;
|
|
_Transition_Settings.Step = 0;
|
|
_Transition_Settings.Position_In = 0;
|
|
_Transition_Settings.Position_Out = 0;
|
|
|
|
if(_Transition_Settings.Frame_Duration == 0) {
|
|
return;
|
|
}
|
|
|
|
switch (direction_in)
|
|
{
|
|
case TRANSITION_NONE:
|
|
break;
|
|
|
|
case TRANSITION_LEFT:
|
|
_Transition_Settings.Offset.X = +DISPLAY_WIDTH;
|
|
_Transition_Settings.Direction_In = direction_in;
|
|
break;
|
|
|
|
case TRANSITION_RIGHT:
|
|
_Transition_Settings.Offset.X = -DISPLAY_WIDTH;
|
|
_Transition_Settings.Direction_In = direction_in;
|
|
break;
|
|
|
|
case TRANSITION_UP:
|
|
_Transition_Settings.Offset.Y = +DISPLAY_HEIGHT;
|
|
_Transition_Settings.Direction_In = direction_in;
|
|
break;
|
|
|
|
case TRANSITION_DOWN:
|
|
_Transition_Settings.Offset.Y = -DISPLAY_HEIGHT;
|
|
_Transition_Settings.Direction_In = direction_in;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(_Transition_Settings.Direction_Out != TRANSITION_NONE) {
|
|
Display_Copy_Buffer(&_Image_Buffer, &_Image_Buffer_Backup);
|
|
}
|
|
}
|
|
|
|
void Display_Screen_Transition_Tick()
|
|
{
|
|
if(_Transition_Settings.Direction_Out == TRANSITION_NONE && _Transition_Settings.Direction_In == TRANSITION_NONE) {
|
|
return;
|
|
}
|
|
|
|
float Transition_X = ((float)_Transition_Settings.Step) / ((float)_Transition_Settings.Frame_Duration);
|
|
float New_Percent = 0.0f;
|
|
|
|
New_Percent = ApplyEasing1(Transition_X, _Transition_Settings.Type);
|
|
|
|
// Calculate position targets separately for incoming and outgoing directions
|
|
int16_t Position_Target_In = 0;
|
|
int16_t Position_Target_Out = 0;
|
|
|
|
// Set position target for incoming screen (only if there is an incoming transition)
|
|
if (_Transition_Settings.Direction_In != TRANSITION_NONE)
|
|
{
|
|
switch (_Transition_Settings.Direction_In)
|
|
{
|
|
case TRANSITION_LEFT:
|
|
case TRANSITION_RIGHT:
|
|
Position_Target_In = DISPLAY_WIDTH;
|
|
break;
|
|
|
|
case TRANSITION_UP:
|
|
case TRANSITION_DOWN:
|
|
Position_Target_In = DISPLAY_HEIGHT;
|
|
break;
|
|
|
|
default:
|
|
_Transition_Settings.Offset.X = 0;
|
|
_Transition_Settings.Offset.Y = 0;
|
|
_Transition_Settings.Direction_In = TRANSITION_NONE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Set position target for outgoing screen (only if there is an outgoing transition)
|
|
if (_Transition_Settings.Direction_Out != TRANSITION_NONE)
|
|
{
|
|
switch (_Transition_Settings.Direction_Out)
|
|
{
|
|
case TRANSITION_LEFT:
|
|
case TRANSITION_RIGHT:
|
|
Position_Target_Out = DISPLAY_WIDTH;
|
|
break;
|
|
|
|
case TRANSITION_UP:
|
|
case TRANSITION_DOWN:
|
|
Position_Target_Out = DISPLAY_HEIGHT;
|
|
break;
|
|
|
|
case TRANSITION_NONE:
|
|
Position_Target_Out = 0; // No outgoing transition
|
|
break;
|
|
|
|
default:
|
|
Position_Target_Out = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Calculate new positions and shift steps
|
|
int16_t New_Position_In = 0;
|
|
int16_t Shift_Step_In = 0;
|
|
|
|
// Only calculate incoming position if there's an incoming transition
|
|
if (_Transition_Settings.Direction_In != TRANSITION_NONE) {
|
|
New_Position_In = (uint)(New_Percent * Position_Target_In);
|
|
Shift_Step_In = New_Position_In - _Transition_Settings.Position_In;
|
|
}
|
|
|
|
// Handle incoming screen movement
|
|
if (_Transition_Settings.Direction_In != TRANSITION_NONE)
|
|
{
|
|
switch (_Transition_Settings.Direction_In)
|
|
{
|
|
case TRANSITION_LEFT: _Transition_Settings.Offset.X -= Shift_Step_In; break;
|
|
case TRANSITION_RIGHT: _Transition_Settings.Offset.X += Shift_Step_In; break;
|
|
case TRANSITION_UP: _Transition_Settings.Offset.Y -= Shift_Step_In; break;
|
|
case TRANSITION_DOWN: _Transition_Settings.Offset.Y += Shift_Step_In; break;
|
|
}
|
|
|
|
_Transition_Settings.Position_In += Shift_Step_In;
|
|
}
|
|
|
|
int16_t New_Position_Out = 0;
|
|
int16_t Shift_Step_Out = 0;
|
|
|
|
// Only calculate outgoing position if there's an outgoing transition
|
|
if (_Transition_Settings.Direction_Out != TRANSITION_NONE) {
|
|
New_Position_Out = (uint)(New_Percent * Position_Target_Out);
|
|
Shift_Step_Out = New_Position_Out - _Transition_Settings.Position_Out;
|
|
}
|
|
|
|
// Handle outgoing screen movement (if there is one)
|
|
if (_Transition_Settings.Direction_Out != TRANSITION_NONE)
|
|
{
|
|
Display_Set_Current_Image_Buffer(&_Image_Buffer_Backup);
|
|
switch (_Transition_Settings.Direction_Out)
|
|
{
|
|
case TRANSITION_LEFT: Display_Buffer_Shift_Left(Shift_Step_Out); break;
|
|
case TRANSITION_RIGHT: Display_Buffer_Shift_Right(Shift_Step_Out); break;
|
|
case TRANSITION_UP: Display_Buffer_Shift_Up(Shift_Step_Out); break;
|
|
case TRANSITION_DOWN: Display_Buffer_Shift_Down(Shift_Step_Out); break;
|
|
}
|
|
Display_Set_Current_Image_Buffer(&_Image_Buffer);
|
|
|
|
_Transition_Settings.Position_Out += Shift_Step_Out;
|
|
}
|
|
|
|
// Update step counter and position
|
|
if (_Transition_Settings.Step < _Transition_Settings.Frame_Duration) {
|
|
_Transition_Settings.Step++;
|
|
}
|
|
else {
|
|
_Transition_Settings.Offset.X = 0;
|
|
_Transition_Settings.Offset.Y = 0;
|
|
_Transition_Settings.Direction_In = TRANSITION_NONE;
|
|
|
|
_Transition_Settings.Direction_Out = TRANSITION_NONE;
|
|
}
|
|
}
|
|
|
|
bool Display_Screen_Transition_Ongoing()
|
|
{
|
|
if(_Transition_Settings.Direction_Out == TRANSITION_NONE && _Transition_Settings.Direction_In == TRANSITION_NONE) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Display_Render_Objects(void)
|
|
{
|
|
Object_Menu_Select* MS;
|
|
Object_Menu_Icon_Row* MI;
|
|
Object_Select_YesNo* YN;
|
|
Object_Select_List* SL;
|
|
Object_Select_Value* SV;
|
|
Object_Entry_Indicator* EI;
|
|
|
|
Animation_State Animation_State = COMPLETE;
|
|
|
|
if(_Transition_Settings.Direction_Out != TRANSITION_NONE) {
|
|
Display_Copy_Buffer(&_Image_Buffer_Backup, &_Image_Buffer);
|
|
}
|
|
else {
|
|
Display_Shapes_Fill_Screen(Display_Objects_Background_Color_Get());
|
|
}
|
|
|
|
for(uint i=0;i<Display_Objects_Count();i++)
|
|
{
|
|
Display_Object* Object = Display_Objects_Get_By_ID(i);
|
|
Style* Style = Object->Style;
|
|
Animation* Animation = Object->Animation;
|
|
|
|
if(Animation != NULL) {
|
|
if(Animation->Animation_Start == START_AFTER_PREVIOUS && Animation_State != COMPLETE) {
|
|
continue;
|
|
}
|
|
|
|
Animation_State = Display_Animation_Tick(Object);
|
|
|
|
if(Animation_State == NO_STARTED || Animation_State == DELAYING) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if(Object->Enabled == false) {
|
|
continue;
|
|
}
|
|
|
|
Coordinates Coordinates_Object = Object->Coordinates;
|
|
Coordinates_Object.X += Object->Content_Offset.X;
|
|
Coordinates_Object.Y += Object->Content_Offset.Y;
|
|
|
|
Coordinates_Object.X += _Transition_Settings.Offset.X;
|
|
Coordinates_Object.Y += _Transition_Settings.Offset.Y;
|
|
|
|
if(Style != NULL) {
|
|
bool Do_Draw_Style = true;
|
|
|
|
Coordinates Coordinates_Style = Object->Coordinates;
|
|
Coordinates_Style.X += _Transition_Settings.Offset.X;
|
|
Coordinates_Style.Y += _Transition_Settings.Offset.Y;
|
|
Display_Draw_Style(&Coordinates_Style, Style, Object->Dimension.Width, Object->Dimension.Height, Do_Draw_Style);
|
|
}
|
|
|
|
if(Object->Focused == true) {
|
|
Coordinates Coordinates_Focus = Object->Coordinates;
|
|
Coordinates_Focus.X += _Transition_Settings.Offset.X;
|
|
Coordinates_Focus.Y += _Transition_Settings.Offset.Y;
|
|
|
|
Display_Draw_Focused(&Coordinates_Focus, Object->Dimension.Width, Object->Dimension.Height);
|
|
}
|
|
|
|
switch (Object->Type)
|
|
{
|
|
///////////////////
|
|
// Render Simple //
|
|
///////////////////
|
|
case FLOAT: Display_Render_Simple_Float(&Coordinates_Object, (Object_Float*)Object->Data); break;
|
|
case INTEGER: Display_Render_Simple_Integer(&Coordinates_Object, (Object_Integer*)Object->Data); break;
|
|
case TEXT: Display_Render_Simple_Text(&Coordinates_Object, (Object_Text*)Object->Data); break;
|
|
case IMAGE: Display_Render_Simple_Image(&Coordinates_Object, (Object_Image_Color*)Object->Data); break;
|
|
case BOOLEAN: Display_Render_Simple_Bool(&Coordinates_Object, (Object_Bool*)Object->Data); break;
|
|
case SHAPE: Display_Render_Simple_Shape(&Coordinates_Object, (Object_Shape*)Object->Data); break;
|
|
|
|
////////////////////
|
|
// Render Complex //
|
|
////////////////////
|
|
case VALUE_BAR_RECT: Display_Render_Complex_Value_Bar_Rect(&Coordinates_Object, (Object_Value_Bar_Rect*)Object->Data); break;
|
|
case VALUE_BAR_ARC: Display_Render_Complex_Value_Bar_Arc(&Coordinates_Object, (Object_Value_Bar_Arc*)Object->Data); break;
|
|
case GRAPH: Display_Render_Complex_Graph(&Coordinates_Object, (Object_Graph*)Object->Data); break;
|
|
case BUTTON: Display_Render_Complex_Button(&Coordinates_Object, (Object_Button*)Object->Data); break;
|
|
case CANVAS: Display_Render_Complex_Canvas(&Coordinates_Object, (Object_Canvas*)(Object->Data)); break;
|
|
|
|
case MESSAGE_BOX:
|
|
Display_Render_Complex_Message_Box(&Coordinates_Object, (Object_Message_Box*)(Object->Data), Object->Dimension.Width, Object->Dimension.Height);
|
|
break;
|
|
|
|
case MENU_SELECT:
|
|
MS = (Object_Menu_Select*)(Object->Data);
|
|
Display_Render_Complex_Menu_Select(&Coordinates_Object, MS->Menu_Titles, MS->Menu_Entry_Count, MS->Title_Char_Length, *MS->Selected_Entry, MS->Config);
|
|
break;
|
|
|
|
case MENU_ICON_ROW:
|
|
MI = (Object_Menu_Icon_Row*)(Object->Data);
|
|
Display_Render_Complex_Menu_Icon_Row(&Coordinates_Object, MI->Items, MI->Item_Count, *MI->Selected_Item, MI->Config);
|
|
break;
|
|
|
|
case MENU_RING:
|
|
Display_Render_Complex_Menu_Ring(&Coordinates_Object, (Object_Menu_Ring*)(Object->Data));
|
|
break;
|
|
|
|
case MENU_HIERARCHICAL:
|
|
Display_Render_Complex_Menu_Hierarchical(&Coordinates_Object, (Object_Menu_Hierarchical*)(Object->Data));
|
|
break;
|
|
|
|
case SELECT_YESNO:
|
|
YN = (Object_Select_YesNo*)(Object->Data);
|
|
Display_Render_Complex_Select_YesNo(&Coordinates_Object, YN->Title, YN->Title_Length, *YN->Value, YN->Config);
|
|
break;
|
|
|
|
case SELECT_LIST:
|
|
SL = (Object_Select_List*)(Object->Data);
|
|
Display_Render_Complex_Select_List(&Coordinates_Object, SL->List_Titles, SL->List_Entry_Count, SL->List_Char_Length, *SL->Selected_Entry, SL->Initial_Entry, SL->Config);
|
|
break;
|
|
|
|
case SELECT_VALUE:
|
|
SV = (Object_Select_Value*)(Object->Data);
|
|
Display_Render_Complex_Select_Value(&Coordinates_Object, SV->Title, SV->Title_Length, *SV->Value, SV->Max, SV->Min, SV->Format, SV->Config);
|
|
break;
|
|
|
|
case SELECT_RGB:
|
|
Display_Render_Complex_Select_RGB(&Coordinates_Object, (Object_Select_RGB*)(Object->Data));
|
|
break;
|
|
|
|
case ENTRY_INDICATOR:
|
|
EI = (Object_Entry_Indicator*)(Object->Data);
|
|
Display_Render_Complex_Entry_Indicator(&Coordinates_Object, EI->Entry_Count, *EI->Entry_Value, EI->Config);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(_Draw_Touch_Reference_Points) {
|
|
Display_Draw_Touch_Refernce_Points(DISPLAY_COLOR_GREEN);
|
|
}
|
|
|
|
if(_Draw_Center_Lines) {
|
|
Display_Draw_Center_Lines(DISPLAY_COLOR_GREENYELLOW);
|
|
}
|
|
|
|
Display_Draw_Touch_Marker(DISPLAY_COLOR_BLUE);
|
|
}
|
|
|
|
void Display_Send_Buffer(void)
|
|
{
|
|
Display_SPI_Start_Command(DISPLAY_MEMORY_WRITE);
|
|
Display_SPI_Send_Data((uint8_t *)_Image_Buffer.Dim_1, DISPLAY_IMAGE_BUFFER_BYTE_SIZE, true);
|
|
}
|
|
|
|
bool Display_Send_Buffer_Completed(void)
|
|
{
|
|
return Display_SPI_DMA_Transfer_Completed();
|
|
}
|
|
|
|
void Display_Show_Test_Screen(void)
|
|
{
|
|
int16_t Width = DISPLAY_WIDTH / 5;
|
|
|
|
Display_Shapes_Draw_Rect_Filled(0*Width, 0, Width, DISPLAY_HEIGHT, DISPLAY_COLOR_RED);
|
|
Display_Shapes_Draw_Rect_Filled(1*Width, 0, Width, DISPLAY_HEIGHT, DISPLAY_COLOR_GREEN);
|
|
Display_Shapes_Draw_Rect_Filled(2*Width, 0, Width, DISPLAY_HEIGHT, DISPLAY_COLOR_BLUE);
|
|
Display_Shapes_Draw_Rect_Filled(3*Width, 0, Width, DISPLAY_HEIGHT, DISPLAY_COLOR_BLACK);
|
|
Display_Shapes_Draw_Rect_Filled(4*Width, 0, Width, DISPLAY_HEIGHT, DISPLAY_COLOR_WHITE);
|
|
}
|
|
|
|
int Display_Get_Button_Touch_Return_Value(void)
|
|
{
|
|
int Return_Value = _Touched_Button_Return_Value;
|
|
|
|
_Touched_Button_Return_Value = -1;
|
|
|
|
return Return_Value;
|
|
}
|
|
|
|
void Display_Select_First_Object(void)
|
|
{
|
|
Display_Action_CW();
|
|
}
|
|
|
|
void Display_Action_CW(void)
|
|
{
|
|
if(_Object_Selected == false)
|
|
{
|
|
(*_Screen_On_Objects_Defocused)(Display_Get_Index_Of_Object(Display_Get_Selected_Object()));
|
|
Display_Object_Select_Next();
|
|
(*_Screen_On_Objects_Focused)(Display_Get_Index_Of_Object(Display_Get_Selected_Object()));
|
|
}
|
|
else
|
|
{
|
|
(*_Screen_Action_CW)(Display_Get_Index_Of_Object(Display_Get_Selected_Object()));
|
|
}
|
|
}
|
|
|
|
void Display_Action_CCW(void)
|
|
{
|
|
if(_Object_Selected == false)
|
|
{
|
|
(*_Screen_On_Objects_Defocused)(Display_Get_Index_Of_Object(Display_Get_Selected_Object()));
|
|
Display_Object_Select_Previous();
|
|
(*_Screen_On_Objects_Focused)(Display_Get_Index_Of_Object(Display_Get_Selected_Object()));
|
|
}
|
|
else
|
|
{
|
|
(*_Screen_Action_CCW)(Display_Get_Index_Of_Object(Display_Get_Selected_Object()));
|
|
}
|
|
}
|
|
|
|
void Display_Action_SW(void)
|
|
{
|
|
if(_Object_Selected == false)
|
|
{
|
|
_Object_Selected = true;
|
|
(*_Screen_On_Object_Select)(Display_Get_Index_Of_Object(Display_Get_Selected_Object()));
|
|
}
|
|
else
|
|
{
|
|
_Object_Selected = false;
|
|
(*_Screen_On_Object_Deselect)(Display_Get_Index_Of_Object(Display_Get_Selected_Object()));
|
|
}
|
|
}
|
|
|
|
void Display_Select_Object(void)
|
|
{
|
|
_Object_Selected = true;
|
|
}
|
|
|
|
void Display_Unselect_Object(void)
|
|
{
|
|
_Object_Selected = false;
|
|
}
|
|
|
|
void Display_Menu_Icon_Row_Set(uint32_t initially_selected_item, uint32_t icon_space_width)
|
|
{
|
|
Display_Render_Complex_Menu_Icon_Row_Set(initially_selected_item, icon_space_width);
|
|
}
|
|
|
|
void Display_Inc_Frame_Counter(void)
|
|
{
|
|
_Frame_Counter++;
|
|
}
|
|
|
|
uint* Display_Get_Frame_Counter_Reference(void)
|
|
{
|
|
return &_Frame_Counter;
|
|
}
|
|
|
|
Display_Color Display_Get_Pixel(uint32_t pixel_number)
|
|
{
|
|
if(pixel_number >= DISPLAY_HEIGHT * DISPLAY_WIDTH) {
|
|
return 0;
|
|
}
|
|
|
|
return _Image_Buffer.Dim_1[pixel_number];
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
Internal Functions
|
|
*******************************************************************/
|
|
|
|
|
|
void Display_Draw_Style(Coordinates* coordinates, Style* style, uint32_t content_width, uint32_t content_height, bool do_draw)
|
|
{
|
|
/*
|
|
INFO (2021-11-11):
|
|
Width and Height information from the Style-Struct is currently not used. The dimension is stored in the Object
|
|
itself and does not change during runtime. A width or height adapted to the contect size is currently not available.
|
|
It is also not possible to align the content within the style. This has to be done by setting the Padding values
|
|
properly.
|
|
I am not sure if the above mentioned functionality is needed or will be used by at all. This is why I will not
|
|
implement these functions and keep things simple until I see I really need it.
|
|
*/
|
|
|
|
uint16_t Height = content_height;
|
|
uint16_t Width = content_width;
|
|
Display_Color Border_Color = style->Border_Color;
|
|
uint16_t Border_Thickness = style->Border_Thickness;
|
|
uint16_t Border_Radius = style->Border_Radius;
|
|
|
|
if(!do_draw) {
|
|
return;
|
|
}
|
|
|
|
if(style->Background_Color != Display_Objects_Background_Color_Get()) {
|
|
if(Border_Radius > 0) {
|
|
Display_Shapes_Draw_Round_Rect_Filled(coordinates->X + Border_Thickness, coordinates->Y + Border_Thickness, Width - 2 * Border_Thickness, Height - 2 * Border_Thickness, Border_Radius, style->Background_Color);
|
|
} else {
|
|
Display_Shapes_Draw_Rect_Filled(coordinates->X + Border_Thickness, coordinates->Y + Border_Thickness, Width - 2 * Border_Thickness, Height - 2 * Border_Thickness, style->Background_Color);
|
|
}
|
|
}
|
|
|
|
if(style->Border_Color != Display_Objects_Background_Color_Get() && style->Border_Thickness > 0) {
|
|
Display_Shapes_Draw_Round_Rect_Frame(coordinates->X, coordinates->Y, Width, Height, Border_Radius, Border_Thickness, Border_Color);
|
|
}
|
|
}
|
|
|
|
void Display_Draw_Focused(Coordinates* coordinates, uint32_t width, uint32_t height)
|
|
{
|
|
// No focus indicator needed for this projects
|
|
|
|
if(_Object_Selected == true) {
|
|
// Display_Shapes_Draw_Rect_Filled(coordinates->X, coordinates->Y, 3, 3, DISPLAY_COLOR_RED);
|
|
}
|
|
else {
|
|
// Display_Shapes_Draw_Rect_Filled(coordinates->X, coordinates->Y, 3, 3, DISPLAY_COLOR_GREEN);
|
|
}
|
|
}
|
|
|
|
Animation_State Display_Animation_Tick(Display_Object* object)
|
|
{
|
|
Animation* Animation = object->Animation;
|
|
Animation_Status* Animation_Status = object->Animation_Status;
|
|
|
|
if(Animation == NULL || Animation_Status == NULL) {
|
|
return COMPLETE;
|
|
}
|
|
|
|
switch (Animation_Status->State)
|
|
{
|
|
case NO_STARTED:
|
|
Animation_Status->Target.X = object->Coordinates.X;
|
|
Animation_Status->Target.Y = object->Coordinates.Y;
|
|
|
|
// break; // Skipped on purpose
|
|
|
|
case DELAYING:
|
|
if(Animation_Status->State == DELAYING) {
|
|
Animation_Status->Tick_Counter--;
|
|
} else {
|
|
Animation_Status->Tick_Counter = Animation->Tick_Delay;
|
|
Animation_Status->State = DELAYING;
|
|
}
|
|
|
|
if(Animation_Status->Tick_Counter > 0) {
|
|
break;
|
|
}
|
|
|
|
// break; // Skipped on purpose
|
|
|
|
case MOVING:
|
|
if(Animation_Status->State == MOVING) {
|
|
Animation_Status->Tick_Counter--;
|
|
} else {
|
|
Animation_Status->Tick_Counter = Animation->Tick_Duration;
|
|
Animation_Status->State = MOVING;
|
|
}
|
|
|
|
float Progress = (float)Animation_Status->Tick_Counter / (float)Animation->Tick_Duration;
|
|
|
|
object->Coordinates.X = Animation_Status->Target.X + (int16_t)((float)Animation->Offset.X * Progress);
|
|
object->Coordinates.Y = Animation_Status->Target.Y + (int16_t)((float)Animation->Offset.Y * Progress);
|
|
|
|
if(Animation_Status->Tick_Counter == 0) {
|
|
Animation_Status->State = COMPLETE;
|
|
}
|
|
break;
|
|
|
|
case COMPLETE:
|
|
default:
|
|
Animation_Status->State = COMPLETE;
|
|
break;
|
|
}
|
|
|
|
return Animation_Status->State;
|
|
}
|
|
|
|
void Display_Draw_Touch_Refernce_Points(Display_Color color)
|
|
{
|
|
for(int i=0;i<Display_Touch_Get_Reference_Point_Count();i++)
|
|
{
|
|
Coordinates* Point = Display_Touch_Get_Reference_Point(i);
|
|
if(Point != NULL) {
|
|
Display_Shapes_Draw_Circle_Filled(Point->X-5, Point->Y-5, 5, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Display_Draw_Touch_Marker(Display_Color color)
|
|
{
|
|
int Radius = 5;
|
|
uint Marker_Count = Display_Touch_Get_Marker_Count();
|
|
|
|
for(uint i=0;i<Marker_Count;i++) {
|
|
Coordinates* Coordinates = Display_Touch_Get_Marker_Coordinates(i);
|
|
if(Coordinates == NULL) {
|
|
continue;
|
|
}
|
|
|
|
Display_Shapes_Draw_Circle_Filled(Coordinates->X-Radius, Coordinates->Y-Radius, Radius, color);
|
|
}
|
|
}
|
|
|
|
void Display_Draw_Center_Lines(Display_Color color)
|
|
{
|
|
Display_Shapes_Draw_HLine(0, DISPLAY_HEIGHT/2 - 1, DISPLAY_WIDTH, 2, color);
|
|
Display_Shapes_Draw_VLine(DISPLAY_WIDTH/2 - 1, 0, DISPLAY_HEIGHT, 2, color);
|
|
}
|
|
|
|
void Display_Check_Button_Touch(int16_t x, int16_t y)
|
|
{
|
|
for(uint i=0;i<Display_Objects_Count();i++)
|
|
{
|
|
Display_Object* Object = Display_Objects_Get_By_ID(i);
|
|
|
|
if(Object->Type != BUTTON) { continue; }
|
|
|
|
Coordinates Coordinates = Object->Coordinates;
|
|
Style* Style = Object->Style;
|
|
Object_Button* N = (Object_Button*)Object->Data;
|
|
|
|
if(Style != NULL) {
|
|
Display_Draw_Style(&Coordinates, Style, N->Dimension.Width, N->Dimension.Height, false);
|
|
}
|
|
|
|
if(x >= Coordinates.X && x <= Coordinates.X + N->Dimension.Width && y >= Coordinates.Y && y <= Coordinates.Y + N->Dimension.Height)
|
|
{
|
|
_Touched_Button_Return_Value = N->Return_Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Display_Object_Select_Next(void)
|
|
{
|
|
Display_Object* Current_Object = Display_Get_Selected_Object();
|
|
int Current_Object_Index = Display_Get_Index_Of_Object(Current_Object);
|
|
|
|
if(Current_Object_Index == -1)
|
|
{
|
|
Current_Object_Index = 0;
|
|
Current_Object = Display_Objects_Get_By_ID(Current_Object_Index);
|
|
if(Current_Object->Selectable == true && Current_Object->Enabled == true)
|
|
{
|
|
Current_Object->Focused = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
for(uint i=0;i<Display_Objects_Count();i++)
|
|
{
|
|
uint Index = i + Current_Object_Index + 1;
|
|
if(Index >= Display_Objects_Count())
|
|
{
|
|
Index -= Display_Objects_Count();
|
|
}
|
|
|
|
Display_Object* Next_Possible_Object = Display_Objects_Get_By_ID(Index);
|
|
|
|
if(Next_Possible_Object->Selectable == true && Next_Possible_Object->Enabled == true)
|
|
{
|
|
Current_Object->Focused = false;
|
|
Next_Possible_Object->Focused = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Display_Object_Select_Previous(void)
|
|
{
|
|
Display_Object* Current_Object = Display_Get_Selected_Object();
|
|
int Current_Object_Index = Display_Get_Index_Of_Object(Current_Object);
|
|
|
|
if(Current_Object_Index == -1)
|
|
{
|
|
Current_Object_Index = 0;
|
|
}
|
|
|
|
for(uint i=0;i<Display_Objects_Count();i++)
|
|
{
|
|
uint Index = (Display_Objects_Count() - i - 1) + Current_Object_Index;
|
|
if(Index >= Display_Objects_Count())
|
|
{
|
|
Index -= Display_Objects_Count();
|
|
}
|
|
|
|
Display_Object* Next_Possible_Object = Display_Objects_Get_By_ID(Index);
|
|
|
|
if(Next_Possible_Object->Selectable == true && Next_Possible_Object->Enabled == true)
|
|
{
|
|
Current_Object->Focused = false;
|
|
Next_Possible_Object->Focused = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(Current_Object->Selectable == true && Current_Object->Enabled == true)
|
|
{
|
|
Current_Object->Focused = true;
|
|
}
|
|
}
|
|
|
|
Display_Object* Display_Get_Selected_Object(void)
|
|
{
|
|
for(uint i=0;i<Display_Objects_Count();i++)
|
|
{
|
|
Display_Object* Object = Display_Objects_Get_By_ID(i);
|
|
if(Object->Selectable == true && Object->Focused == true)
|
|
{
|
|
return Object;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int Display_Get_Index_Of_Object(Display_Object* object)
|
|
{
|
|
if(object == NULL) { return -1; }
|
|
|
|
for(uint i=0;i<Display_Objects_Count();i++)
|
|
{
|
|
if(Display_Objects_Get_By_ID(i) == object)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Display_Buffer_Shift_Left(uint32_t steps)
|
|
{
|
|
if (steps >= DISPLAY_WIDTH) {
|
|
// Clear entire buffer
|
|
Display_Shapes_Fill_Screen(Display_Objects_Background_Color_Get());
|
|
return;
|
|
}
|
|
|
|
Display_Color BG_Color = Display_Objects_Background_Color_Get();
|
|
|
|
// Move columns to the left
|
|
for (int16_t row = 0; row < DISPLAY_HEIGHT; row++)
|
|
{
|
|
// Move the row data left
|
|
memmove(&_Current_Buffer->Dim_2[row][0], &_Current_Buffer->Dim_2[row][steps], (DISPLAY_WIDTH - steps) * sizeof(Display_Color));
|
|
|
|
// Fill the rightmost area with background color
|
|
for (int16_t col = DISPLAY_WIDTH - steps; col < DISPLAY_WIDTH; col++) {
|
|
_Current_Buffer->Dim_2[row][col] = BG_Color;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Display_Buffer_Shift_Right(uint32_t steps)
|
|
{
|
|
if (steps >= DISPLAY_WIDTH) {
|
|
Display_Shapes_Fill_Screen(Display_Objects_Background_Color_Get());
|
|
return;
|
|
}
|
|
|
|
Display_Color BG_Color = Display_Objects_Background_Color_Get();
|
|
|
|
// Move columns to the right (start from rightmost to avoid overwriting)
|
|
for (int16_t row = 0; row < DISPLAY_HEIGHT; row++)
|
|
{
|
|
// Move the row data right
|
|
memmove(&_Current_Buffer->Dim_2[row][steps], &_Current_Buffer->Dim_2[row][0], (DISPLAY_WIDTH - steps) * sizeof(Display_Color));
|
|
|
|
// Fill the leftmost area with background color
|
|
for (int16_t col = 0; col < steps; col++) {
|
|
_Current_Buffer->Dim_2[row][col] = BG_Color;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Display_Buffer_Shift_Up(uint32_t steps)
|
|
{
|
|
if (steps >= DISPLAY_HEIGHT) {
|
|
Display_Shapes_Fill_Screen(Display_Objects_Background_Color_Get());
|
|
return;
|
|
}
|
|
|
|
// Calculate how many pixels to move
|
|
uint32_t Pixels_To_Move = DISPLAY_WIDTH * (DISPLAY_HEIGHT - steps);
|
|
|
|
// Single memmove for the entire shift operation
|
|
memmove(&_Current_Buffer->Dim_1[0], &_Current_Buffer->Dim_1[DISPLAY_WIDTH * steps], Pixels_To_Move * sizeof(Display_Color));
|
|
|
|
// Fill the bottom area using optimized rectangle fill
|
|
Display_Shapes_Draw_Rect_Filled(0, DISPLAY_HEIGHT - steps, DISPLAY_WIDTH, steps, Display_Objects_Background_Color_Get());
|
|
}
|
|
|
|
void Display_Buffer_Shift_Down(uint32_t steps)
|
|
{
|
|
if (steps >= DISPLAY_HEIGHT) {
|
|
Display_Shapes_Fill_Screen(Display_Objects_Background_Color_Get());
|
|
return;
|
|
}
|
|
|
|
// Calculate how many pixels to move
|
|
uint32_t Pixels_To_Move = DISPLAY_WIDTH * (DISPLAY_HEIGHT - steps);
|
|
|
|
// Single memmove for the entire shift operation
|
|
memmove(&_Current_Buffer->Dim_1[DISPLAY_WIDTH * steps], &_Current_Buffer->Dim_1[0], Pixels_To_Move * sizeof(Display_Color));
|
|
|
|
// Fill the top area using optimized rectangle fill
|
|
Display_Shapes_Draw_Rect_Filled(0, 0, DISPLAY_WIDTH, steps, Display_Objects_Background_Color_Get());
|
|
}
|
|
|
|
void Display_Set_Current_Image_Buffer(Display_Image_Buffer* buffer)
|
|
{
|
|
if(buffer != NULL) {
|
|
_Current_Buffer = buffer;
|
|
}
|
|
}
|
|
|
|
void Display_Copy_Buffer(Display_Image_Buffer *src, Display_Image_Buffer *dest)
|
|
{
|
|
dma_channel_configure(_DMA_Channel_Copy_Buffer, &_DMA_Config_Copy_Buffer, dest, src, DISPLAY_IMAGE_BUFFER_PIXEL_SIZE/2, false);
|
|
dma_channel_start(_DMA_Channel_Copy_Buffer);
|
|
dma_channel_wait_for_finish_blocking(_DMA_Channel_Copy_Buffer);
|
|
} |