Refactor frame (#1192)
	
		
	
				
					
				
			* start drawing new sidebar, add assets * add thermal to ui, draw network_type and battery * draw sidebar metrics, add freeSpace and paTemp * draw static panda metric and network strength, start ubloxGnss messaging * use array for network_img * start sidebar touch events * prevent multiple touch events with touch_timeout * filter old touches, isolate sidebar events * add hwType check with timeout for panda metric * cleanup touch poll, handle vision touch, remove frame and black apks * cleanup per willem comments * update offroad, only read active_app from cereal * tweak sidebar behavior, show active app status * update offroad apk * read networkstrength from thermal in sidebarpull/214/head
@ -1,3 +0,0 @@ | 
				
			||||
version https://git-lfs.github.com/spec/v1 | 
				
			||||
oid sha256:309b46b7c38f10da52b18b0340eb3c57b633558a9a27c3ca4116474969ebb456 | 
				
			||||
size 84675 | 
				
			||||
@ -1,3 +0,0 @@ | 
				
			||||
version https://git-lfs.github.com/spec/v1 | 
				
			||||
oid sha256:d563bc9d43408f78d859538b50999cd7703487bdbe1fba1b11a3810205cd26c4 | 
				
			||||
size 2856327 | 
				
			||||
@ -1,3 +1,3 @@ | 
				
			||||
version https://git-lfs.github.com/spec/v1 | 
				
			||||
oid sha256:bf9cdba9b1f10d7b76573d1f78d208bc58a7273ae5d5e6f171be6c00c67863a6 | 
				
			||||
size 13678787 | 
				
			||||
oid sha256:deb70c5a5b284e73d4960b60ac7a6dcc1263597762554749adf675c23efc383e | 
				
			||||
size 13688133 | 
				
			||||
 | 
				
			||||
| 
		 After Width: | Height: | Size: 1.7 KiB  | 
| 
		 After Width: | Height: | Size: 2.1 KiB  | 
| 
		 After Width: | Height: | Size: 1.5 KiB  | 
| 
		 After Width: | Height: | Size: 957 B  | 
| 
		 After Width: | Height: | Size: 416 B  | 
| 
		 After Width: | Height: | Size: 462 B  | 
| 
		 After Width: | Height: | Size: 593 B  | 
| 
		 After Width: | Height: | Size: 524 B  | 
| 
		 After Width: | Height: | Size: 541 B  | 
| 
		 After Width: | Height: | Size: 503 B  | 
@ -0,0 +1,234 @@ | 
				
			||||
#include <stdio.h> | 
				
			||||
#include <string.h> | 
				
			||||
#include <math.h> | 
				
			||||
#include "ui.hpp" | 
				
			||||
 | 
				
			||||
static void ui_draw_sidebar_background(UIState *s, bool hasSidebar) { | 
				
			||||
  int sbr_x = hasSidebar ? 0 : -(sbr_w) + bdr_s * 2; | 
				
			||||
 | 
				
			||||
  nvgBeginPath(s->vg); | 
				
			||||
  nvgRect(s->vg, sbr_x, 0, sbr_w, vwp_h); | 
				
			||||
  nvgFillColor(s->vg, COLOR_BLACK_ALPHA); | 
				
			||||
  nvgFill(s->vg); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void ui_draw_sidebar_settings_button(UIState *s, bool hasSidebar) { | 
				
			||||
  bool settingsActive = s->active_app == cereal_UiLayoutState_App_settings; | 
				
			||||
  const int settings_btn_xr = hasSidebar ? settings_btn_x : -(sbr_w); | 
				
			||||
 | 
				
			||||
  nvgBeginPath(s->vg); | 
				
			||||
  NVGpaint imgPaint = nvgImagePattern(s->vg, settings_btn_xr, settings_btn_y, | 
				
			||||
    settings_btn_w, settings_btn_h, 0, s->img_button_settings, settingsActive ? 1.0f : 0.65f); | 
				
			||||
  nvgRect(s->vg, settings_btn_xr, settings_btn_y, settings_btn_w, settings_btn_h); | 
				
			||||
  nvgFillPaint(s->vg, imgPaint); | 
				
			||||
  nvgFill(s->vg); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void ui_draw_sidebar_home_button(UIState *s, bool hasSidebar) { | 
				
			||||
  bool homeActive = s->active_app == cereal_UiLayoutState_App_home; | 
				
			||||
  const int home_btn_xr = hasSidebar ? home_btn_x : -(sbr_w); | 
				
			||||
 | 
				
			||||
  nvgBeginPath(s->vg); | 
				
			||||
  NVGpaint imgPaint = nvgImagePattern(s->vg, home_btn_xr, home_btn_y, | 
				
			||||
    home_btn_w, home_btn_h, 0, s->img_button_home, homeActive ? 1.0f : 0.65f); | 
				
			||||
  nvgRect(s->vg, home_btn_xr, home_btn_y, home_btn_w, home_btn_h); | 
				
			||||
  nvgFillPaint(s->vg, imgPaint); | 
				
			||||
  nvgFill(s->vg); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void ui_draw_sidebar_network_strength(UIState *s, bool hasSidebar) { | 
				
			||||
  const int network_img_h = 27; | 
				
			||||
  const int network_img_w = 176; | 
				
			||||
  const int network_img_x = hasSidebar ? 58 : -(sbr_w); | 
				
			||||
  const int network_img_y = 196; | 
				
			||||
  const int network_img = s->scene.networkType == cereal_ThermalData_NetworkType_none ? | 
				
			||||
                          s->img_network[0] : s->img_network[s->scene.networkStrength + 1]; | 
				
			||||
 | 
				
			||||
  nvgBeginPath(s->vg); | 
				
			||||
  NVGpaint imgPaint = nvgImagePattern(s->vg, network_img_x, network_img_y, | 
				
			||||
    network_img_w, network_img_h, 0, network_img, 1.0f); | 
				
			||||
  nvgRect(s->vg, network_img_x, network_img_y, network_img_w, network_img_h); | 
				
			||||
  nvgFillPaint(s->vg, imgPaint); | 
				
			||||
  nvgFill(s->vg); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void ui_draw_sidebar_battery_icon(UIState *s, bool hasSidebar) { | 
				
			||||
  const int battery_img_h = 36; | 
				
			||||
  const int battery_img_w = 76; | 
				
			||||
  const int battery_img_x = hasSidebar ? 160 : -(sbr_w); | 
				
			||||
  const int battery_img_y = 255; | 
				
			||||
 | 
				
			||||
  int battery_img = strcmp(s->scene.batteryStatus, "Charging") == 0 ? | 
				
			||||
    s->img_battery_charging : s->img_battery; | 
				
			||||
 | 
				
			||||
  nvgBeginPath(s->vg); | 
				
			||||
  nvgRect(s->vg, battery_img_x + 6, battery_img_y + 5, | 
				
			||||
    ((battery_img_w - 19) * (s->scene.batteryPercent * 0.01)), battery_img_h - 11); | 
				
			||||
  nvgFillColor(s->vg, COLOR_WHITE); | 
				
			||||
  nvgFill(s->vg); | 
				
			||||
 | 
				
			||||
  nvgBeginPath(s->vg); | 
				
			||||
  NVGpaint imgPaint = nvgImagePattern(s->vg, battery_img_x, battery_img_y, | 
				
			||||
    battery_img_w, battery_img_h, 0, battery_img, 1.0f); | 
				
			||||
  nvgRect(s->vg, battery_img_x, battery_img_y, battery_img_w, battery_img_h); | 
				
			||||
  nvgFillPaint(s->vg, imgPaint); | 
				
			||||
  nvgFill(s->vg); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void ui_draw_sidebar_network_type(UIState *s, bool hasSidebar) { | 
				
			||||
  const int network_x = hasSidebar ? 50 : -(sbr_w); | 
				
			||||
  const int network_y = 273; | 
				
			||||
  const int network_w = 100; | 
				
			||||
  const int network_h = 100; | 
				
			||||
  const char *network_types[6] = {"--", "WiFi", "2G", "3G", "4G", "5G"}; | 
				
			||||
  char network_type_str[32]; | 
				
			||||
 | 
				
			||||
  if (s->scene.networkType <= 5) { | 
				
			||||
    snprintf(network_type_str, sizeof(network_type_str), "%s", network_types[s->scene.networkType]); | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  nvgFillColor(s->vg, COLOR_WHITE); | 
				
			||||
  nvgFontSize(s->vg, 48); | 
				
			||||
  nvgFontFace(s->vg, "sans-regular"); | 
				
			||||
  nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); | 
				
			||||
  nvgTextBox(s->vg, network_x, network_y, network_w, network_type_str, NULL); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void ui_draw_sidebar_metric(UIState *s, const char* label_str, const char* value_str, const int severity, const int y_offset, const char* message_str, bool hasSidebar) { | 
				
			||||
  const int metric_x = hasSidebar ? 30 : -(sbr_w); | 
				
			||||
  const int metric_y = 338 + y_offset; | 
				
			||||
  const int metric_w = 240; | 
				
			||||
  const int metric_h = message_str ? strlen(message_str) > 8 ? 124 : 100 : 148; | 
				
			||||
  NVGcolor status_color; | 
				
			||||
 | 
				
			||||
  if (severity == 0) { | 
				
			||||
    status_color = COLOR_WHITE; | 
				
			||||
  } else if (severity == 1) { | 
				
			||||
    status_color = COLOR_YELLOW; | 
				
			||||
  } else if (severity > 1) { | 
				
			||||
    status_color = COLOR_RED; | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  nvgBeginPath(s->vg); | 
				
			||||
  nvgRoundedRect(s->vg, metric_x, metric_y, metric_w, metric_h, 20); | 
				
			||||
  nvgStrokeColor(s->vg, severity > 0 ? COLOR_WHITE : COLOR_WHITE_ALPHA); | 
				
			||||
  nvgStrokeWidth(s->vg, 2); | 
				
			||||
  nvgStroke(s->vg); | 
				
			||||
 | 
				
			||||
  nvgBeginPath(s->vg); | 
				
			||||
  nvgRoundedRectVarying(s->vg, metric_x + 6, metric_y + 6, 18, metric_h - 12, 25, 0, 0, 25); | 
				
			||||
  nvgFillColor(s->vg, status_color); | 
				
			||||
  nvgFill(s->vg); | 
				
			||||
 | 
				
			||||
  if (!message_str) { | 
				
			||||
    nvgFillColor(s->vg, COLOR_WHITE); | 
				
			||||
    nvgFontSize(s->vg, 78); | 
				
			||||
    nvgFontFace(s->vg, "sans-bold"); | 
				
			||||
    nvgTextAlign(s->vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); | 
				
			||||
    nvgTextBox(s->vg, metric_x + 50, metric_y + 50, metric_w - 60, value_str, NULL); | 
				
			||||
 | 
				
			||||
    nvgFillColor(s->vg, COLOR_WHITE); | 
				
			||||
    nvgFontSize(s->vg, 48); | 
				
			||||
    nvgFontFace(s->vg, "sans-regular"); | 
				
			||||
    nvgTextAlign(s->vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); | 
				
			||||
    nvgTextBox(s->vg, metric_x + 50, metric_y + 50 + 66, metric_w - 60, label_str, NULL); | 
				
			||||
  } else { | 
				
			||||
    nvgFillColor(s->vg, COLOR_WHITE); | 
				
			||||
    nvgFontSize(s->vg, 48); | 
				
			||||
    nvgFontFace(s->vg, "sans-bold"); | 
				
			||||
    nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); | 
				
			||||
    nvgTextBox(s->vg, metric_x + 35, metric_y + (strlen(message_str) > 8 ? 40 : 50), metric_w - 50, message_str, NULL); | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void ui_draw_sidebar_storage_metric(UIState *s, bool hasSidebar) { | 
				
			||||
  int storage_severity; | 
				
			||||
  char storage_label_str[32]; | 
				
			||||
  char storage_value_str[32]; | 
				
			||||
  char storage_value_unit[32]; | 
				
			||||
  const int storage_y_offset = 0; | 
				
			||||
  const float storage_pct = ceilf((1.0 - s->scene.freeSpace) * 100); | 
				
			||||
 | 
				
			||||
  if (storage_pct < 75.0) { | 
				
			||||
    storage_severity = 0; | 
				
			||||
  } else if (storage_pct >= 75.0 && storage_pct < 87.0) { | 
				
			||||
    storage_severity = 1; | 
				
			||||
  } else if (storage_pct >= 87.0) { | 
				
			||||
    storage_severity = 2; | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  snprintf(storage_value_str, sizeof(storage_value_str), "%d", (int)storage_pct); | 
				
			||||
  snprintf(storage_value_unit, sizeof(storage_value_unit), "%s", "%"); | 
				
			||||
  snprintf(storage_label_str, sizeof(storage_label_str), "%s", "STORAGE"); | 
				
			||||
  strcat(storage_value_str, storage_value_unit); | 
				
			||||
 | 
				
			||||
  ui_draw_sidebar_metric(s, storage_label_str, storage_value_str, storage_severity, storage_y_offset, NULL, hasSidebar); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void ui_draw_sidebar_temp_metric(UIState *s, bool hasSidebar) { | 
				
			||||
  int temp_severity; | 
				
			||||
  char temp_label_str[32]; | 
				
			||||
  char temp_value_str[32]; | 
				
			||||
  char temp_value_unit[32]; | 
				
			||||
  const int temp_y_offset = 148 + 32; | 
				
			||||
 | 
				
			||||
  if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_green) { | 
				
			||||
    temp_severity = 0; | 
				
			||||
  } else if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_yellow) { | 
				
			||||
    temp_severity = 1; | 
				
			||||
  } else if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_red) { | 
				
			||||
    temp_severity = 2; | 
				
			||||
  } else if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_danger) { | 
				
			||||
    temp_severity = 3; | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  snprintf(temp_value_str, sizeof(temp_value_str), "%d", s->scene.paTemp); | 
				
			||||
  snprintf(temp_value_unit, sizeof(temp_value_unit), "%s", "°C"); | 
				
			||||
  snprintf(temp_label_str, sizeof(temp_label_str), "%s", "TEMP"); | 
				
			||||
  strcat(temp_value_str, temp_value_unit); | 
				
			||||
 | 
				
			||||
  ui_draw_sidebar_metric(s, temp_label_str, temp_value_str, temp_severity, temp_y_offset, NULL, hasSidebar); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void ui_draw_sidebar_panda_metric(UIState *s, bool hasSidebar) { | 
				
			||||
  int panda_severity; | 
				
			||||
  char panda_message_str[32]; | 
				
			||||
  const int panda_y_offset = (148 + 32) * 2; | 
				
			||||
 | 
				
			||||
  if (s->scene.hwType == cereal_HealthData_HwType_unknown) { | 
				
			||||
    panda_severity = 2; | 
				
			||||
    snprintf(panda_message_str, sizeof(panda_message_str), "%s", "NO PANDA"); | 
				
			||||
  } else if (s->scene.hwType == cereal_HealthData_HwType_whitePanda) { | 
				
			||||
    panda_severity = 0; | 
				
			||||
    snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA ACTIVE"); | 
				
			||||
  } else if ( | 
				
			||||
      (s->scene.hwType == cereal_HealthData_HwType_greyPanda) || | 
				
			||||
      (s->scene.hwType == cereal_HealthData_HwType_blackPanda) || | 
				
			||||
      (s->scene.hwType == cereal_HealthData_HwType_uno)) { | 
				
			||||
      if (s->scene.satelliteCount == -1) { | 
				
			||||
        panda_severity = 0; | 
				
			||||
        snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA ACTIVE"); | 
				
			||||
      } else if (s->scene.satelliteCount < 6) { | 
				
			||||
        panda_severity = 1; | 
				
			||||
        snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA\nNO GPS"); | 
				
			||||
      } else if (s->scene.satelliteCount >= 6) { | 
				
			||||
        panda_severity = 0; | 
				
			||||
        snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA GOOD GPS"); | 
				
			||||
      } | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  ui_draw_sidebar_metric(s, NULL, NULL, panda_severity, panda_y_offset, panda_message_str, hasSidebar); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void ui_draw_sidebar(UIState *s) { | 
				
			||||
  bool hasSidebar = !s->scene.uilayout_sidebarcollapsed; | 
				
			||||
  ui_draw_sidebar_background(s, hasSidebar); | 
				
			||||
  ui_draw_sidebar_settings_button(s, hasSidebar); | 
				
			||||
  ui_draw_sidebar_home_button(s, hasSidebar); | 
				
			||||
  ui_draw_sidebar_network_strength(s, hasSidebar); | 
				
			||||
  ui_draw_sidebar_battery_icon(s, hasSidebar); | 
				
			||||
  ui_draw_sidebar_network_type(s, hasSidebar); | 
				
			||||
  ui_draw_sidebar_storage_metric(s, hasSidebar); | 
				
			||||
  ui_draw_sidebar_temp_metric(s, hasSidebar); | 
				
			||||
  ui_draw_sidebar_panda_metric(s, hasSidebar); | 
				
			||||
} | 
				
			||||