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 sidebar
albatross
Andrew Valish 2020-03-09 19:13:32 -07:00 committed by GitHub
parent 8eff974a41
commit 5ba6beed17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 427 additions and 73 deletions

BIN
apk/ai.comma.plus.black.apk (Stored with Git LFS)

Binary file not shown.

BIN
apk/ai.comma.plus.frame.apk (Stored with Git LFS)

Binary file not shown.

BIN
apk/ai.comma.plus.offroad.apk (Stored with Git LFS)

Binary file not shown.

View File

@ -6,7 +6,7 @@ import shutil
from common.basedir import BASEDIR
from selfdrive.swaglog import cloudlog
android_packages = ("ai.comma.plus.offroad", "ai.comma.plus.frame")
android_packages = ("ai.comma.plus.offroad",)
def get_installed_apks():
dat = subprocess.check_output(["pm", "list", "packages", "-f"], encoding='utf8').strip().split("\n")
@ -26,9 +26,9 @@ def install_apk(path):
os.remove(install_path)
return ret == 0
def start_frame():
def start_offroad():
set_package_permissions()
system("am start -n ai.comma.plus.frame/.MainActivity")
system("am start -n ai.comma.plus.offroad/.MainActivity")
def set_package_permissions():
pm_grant("ai.comma.plus.offroad", "android.permission.ACCESS_FINE_LOCATION")
@ -95,4 +95,3 @@ def pm_apply_packages(cmd):
if __name__ == "__main__":
update_apks()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

View File

@ -129,7 +129,7 @@ from selfdrive.version import version, dirty
from selfdrive.loggerd.config import ROOT
from selfdrive.launcher import launcher
from common import android
from common.apk import update_apks, pm_apply_packages, start_frame
from common.apk import update_apks, pm_apply_packages, start_offroad
from common.manager_helpers import print_cpu_usage
ThermalStatus = cereal.log.ThermalData.ThermalStatus
@ -406,10 +406,10 @@ def manager_thread():
for p in persistent_processes:
start_managed_process(p)
# start frame
# start offroad
if ANDROID:
pm_apply_packages('enable')
start_frame()
start_offroad()
if os.getenv("NOBOARD") is None:
start_managed_process("pandad")

View File

@ -2,7 +2,7 @@
import os
os.environ['FAKEUPLOAD'] = "1"
from common.apk import update_apks, start_frame, pm_apply_packages, android_packages
from common.apk import update_apks, start_offroad, pm_apply_packages, android_packages
from common.params import Params
from common.testing import phone_only
from selfdrive.manager import manager_init, manager_prepare
@ -57,7 +57,7 @@ def with_apks():
update_apks()
pm_apply_packages('enable')
start_frame()
start_offroad()
func()

View File

@ -1,6 +1,6 @@
Import('env', 'arch', 'common', 'messaging', 'gpucommon', 'visionipc', 'cereal')
src = ['ui.cc', 'paint.cc', '#phonelibs/nanovg/nanovg.c']
src = ['ui.cc', 'paint.cc', 'sidebar.cc', '#phonelibs/nanovg/nanovg.c']
libs = [common, 'zmq', 'czmq', 'capnp', 'capnp_c', 'm', cereal, messaging, gpucommon, visionipc]
if arch == "aarch64":

View File

@ -877,6 +877,7 @@ static void ui_draw_blank(UIState *s) {
}
void ui_draw(UIState *s) {
ui_draw_sidebar(s);
if (s->vision_connected && s->active_app == cereal_UiLayoutState_App_home && s->status != STATUS_STOPPED) {
ui_draw_vision(s);
} else {
@ -985,6 +986,25 @@ void ui_nvg_init(UIState *s) {
assert(s->img_map >= 0);
s->img_map = nvgCreateImage(s->vg, "../assets/img_map.png", 1);
assert(s->img_button_settings >= 0);
s->img_button_settings = nvgCreateImage(s->vg, "../assets/images/button_settings.png", 1);
assert(s->img_button_home >= 0);
s->img_button_home = nvgCreateImage(s->vg, "../assets/images/button_home.png", 1);
assert(s->img_battery >= 0);
s->img_battery = nvgCreateImage(s->vg, "../assets/images/battery.png", 1);
assert(s->img_battery_charging >= 0);
s->img_battery_charging = nvgCreateImage(s->vg, "../assets/images/battery_charging.png", 1);
for(int i=0;i<=5;++i) {
assert(s->img_network[i] >= 0);
char network_asset[32];
snprintf(network_asset, sizeof(network_asset), "../assets/images/network_%d.png", i);
s->img_network[i] = nvgCreateImage(s->vg, network_asset, 1);
}
// init gl
s->frame_program = load_program(frame_vertex_shader, frame_fragment_shader);
assert(s->frame_program);

View File

@ -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);
}

View File

@ -57,6 +57,45 @@ static void set_awake(UIState *s, bool awake) {
#endif
}
static void navigate_to_settings(UIState *s) {
#ifdef QCOM
system("am broadcast -a 'ai.comma.plus.SidebarSettingsTouchUpInside'");
#else
// computer UI doesn't have offroad settings
#endif
}
static void navigate_to_home(UIState *s) {
#ifdef QCOM
system("am broadcast -a 'ai.comma.plus.HomeButtonTouchUpInside'");
#else
// computer UI doesn't have offroad home
#endif
}
static void handle_sidebar_touch(UIState *s, int touch_x, int touch_y) {
if (!s->scene.uilayout_sidebarcollapsed && touch_x <= sbr_w) {
if (touch_x >= settings_btn_x && touch_x < (settings_btn_x + settings_btn_w)
&& touch_y >= settings_btn_y && touch_y < (settings_btn_y + settings_btn_h)) {
navigate_to_settings(s);
}
if (touch_x >= home_btn_x && touch_x < (home_btn_x + home_btn_w)
&& touch_y >= home_btn_y && touch_y < (home_btn_y + home_btn_h)) {
navigate_to_home(s);
if (s->vision_connected) {
s->scene.uilayout_sidebarcollapsed = true;
}
}
}
}
static void handle_vision_touch(UIState *s, int touch_x, int touch_y) {
if (s->vision_connected && (touch_x >= s->scene.ui_viz_rx - bdr_s)
&& (s->active_app != cereal_UiLayoutState_App_settings)) {
s->scene.uilayout_sidebarcollapsed = !s->scene.uilayout_sidebarcollapsed;
}
}
volatile sig_atomic_t do_exit = 0;
static void set_do_exit(int sig) {
do_exit = 1;
@ -110,19 +149,28 @@ static void ui_init(UIState *s) {
s->uilayout_sock = SubSocket::create(s->ctx, "uiLayoutState");
s->livecalibration_sock = SubSocket::create(s->ctx, "liveCalibration");
s->radarstate_sock = SubSocket::create(s->ctx, "radarState");
s->thermal_sock = SubSocket::create(s->ctx, "thermal");
s->health_sock = SubSocket::create(s->ctx, "health");
s->ubloxgnss_sock = SubSocket::create(s->ctx, "ubloxGnss");
assert(s->model_sock != NULL);
assert(s->controlsstate_sock != NULL);
assert(s->uilayout_sock != NULL);
assert(s->livecalibration_sock != NULL);
assert(s->radarstate_sock != NULL);
assert(s->thermal_sock != NULL);
assert(s->health_sock != NULL);
assert(s->ubloxgnss_sock != NULL);
s->poller = Poller::create({
s->model_sock,
s->controlsstate_sock,
s->uilayout_sock,
s->livecalibration_sock,
s->radarstate_sock
s->radarstate_sock,
s->thermal_sock,
s->health_sock,
s->ubloxgnss_sock
});
#ifdef SHOW_SPEEDLIMIT
@ -404,27 +452,56 @@ void handle_message(UIState *s, Message * msg) {
cereal_read_UiLayoutState(&datad, eventd.uiLayoutState);
s->active_app = datad.activeApp;
s->scene.uilayout_sidebarcollapsed = datad.sidebarCollapsed;
s->scene.uilayout_mapenabled = datad.mapEnabled;
bool hasSidebar = !s->scene.uilayout_sidebarcollapsed;
bool mapEnabled = s->scene.uilayout_mapenabled;
if (mapEnabled) {
s->scene.ui_viz_rx = hasSidebar ? (box_x+nav_w) : (box_x+nav_w-(bdr_s*4));
s->scene.ui_viz_rw = hasSidebar ? (box_w-nav_w) : (box_w-nav_w+(bdr_s*4));
s->scene.ui_viz_ro = -(sbr_w + 4*bdr_s);
} else {
s->scene.ui_viz_rx = hasSidebar ? box_x : (box_x-sbr_w+bdr_s*2);
s->scene.ui_viz_rw = hasSidebar ? box_w : (box_w+sbr_w-(bdr_s*2));
s->scene.ui_viz_ro = hasSidebar ? -(sbr_w - 6*bdr_s) : 0;
}
} else if (eventd.which == cereal_Event_liveMapData) {
struct cereal_LiveMapData datad;
cereal_read_LiveMapData(&datad, eventd.liveMapData);
s->scene.map_valid = datad.mapValid;
} else if (eventd.which == cereal_Event_thermal) {
struct cereal_ThermalData datad;
cereal_read_ThermalData(&datad, eventd.thermal);
s->scene.networkType = datad.networkType;
s->scene.networkStrength = datad.networkStrength;
s->scene.batteryPercent = datad.batteryPercent;
snprintf(s->scene.batteryStatus, sizeof(s->scene.batteryStatus), "%s", datad.batteryStatus.str);
s->scene.freeSpace = datad.freeSpace;
s->scene.thermalStatus = datad.thermalStatus;
s->scene.paTemp = datad.pa0;
} else if (eventd.which == cereal_Event_ubloxGnss) {
struct cereal_UbloxGnss datad;
cereal_read_UbloxGnss(&datad, eventd.ubloxGnss);
struct cereal_UbloxGnss_MeasurementReport reportdatad;
cereal_read_UbloxGnss_MeasurementReport(&reportdatad, datad.measurementReport);
s->scene.satelliteCount = reportdatad.numMeas;
} else if (eventd.which == cereal_Event_health) {
struct cereal_HealthData datad;
cereal_read_HealthData(&datad, eventd.health);
s->scene.hwType = datad.hwType;
s->hardware_timeout = 5*30; // 5 seconds at 30 fps
}
capn_free(&ctx);
}
static void check_messages(UIState *s) {
while(true) {
auto polls = s->poller->poll(0);
if (polls.size() == 0)
break;
for (auto sock : polls){
Message * msg = sock->receive();
if (msg == NULL) continue;
handle_message(s, msg);
delete msg;
}
}
}
static void ui_update(UIState *s) {
int err;
@ -494,7 +571,7 @@ static void ui_update(UIState *s) {
assert(glGetError() == GL_NO_ERROR);
// Default UI Measurements (Assumes sidebar collapsed)
s->scene.uilayout_sidebarcollapsed = true;
s->scene.ui_viz_rx = (box_x-sbr_w+bdr_s*2);
s->scene.ui_viz_rw = (box_w+sbr_w-(bdr_s*2));
s->scene.ui_viz_ro = 0;
@ -575,23 +652,7 @@ static void ui_update(UIState *s) {
break;
}
// peek and consume all events in the zmq queue, then return.
while(true) {
auto polls = s->poller->poll(0);
if (polls.size() == 0)
return;
for (auto sock : polls){
Message * msg = sock->receive();
if (msg == NULL) continue;
set_awake(s, true);
handle_message(s, msg);
delete msg;
}
}
check_messages(s);
}
static int vision_subscribe(int fd, VisionPacket *rp, VisionStreamType type) {
@ -732,7 +793,6 @@ fail:
return NULL;
}
static void* bg_thread(void* args) {
UIState *s = (UIState*)args;
set_thread_name("bg");
@ -813,7 +873,6 @@ int main(int argc, char* argv[]) {
TouchState touch = {0};
touch_init(&touch);
s->touch_fd = touch.fd;
ui_sound_init();
// light sensor scaling params
@ -830,6 +889,9 @@ int main(int argc, char* argv[]) {
set_volume(MIN_VOLUME);
s->volume_timeout = 5 * UI_FREQ;
int draws = 0;
s->scene.satelliteCount = -1;
while (!do_exit) {
bool should_swap = false;
if (!s->vision_connected) {
@ -847,34 +909,37 @@ int main(int argc, char* argv[]) {
if (smooth_brightness > 255) smooth_brightness = 255;
set_brightness(s, (int)smooth_brightness);
// resize vision for collapsing sidebar
const bool hasSidebar = !s->scene.uilayout_sidebarcollapsed;
s->scene.ui_viz_rx = hasSidebar ? box_x : (box_x - sbr_w + (bdr_s * 2));
s->scene.ui_viz_rw = hasSidebar ? box_w : (box_w + sbr_w - (bdr_s * 2));
s->scene.ui_viz_ro = hasSidebar ? -(sbr_w - 6 * bdr_s) : 0;
// poll for touch events
int touch_x = -1, touch_y = -1;
int touched = touch_poll(&touch, &touch_x, &touch_y, 0);
if (touched == 1) {
set_awake(s, true);
handle_sidebar_touch(s, touch_x, touch_y);
handle_vision_touch(s, touch_x, touch_y);
}
if (!s->vision_connected) {
// Car is not started, keep in idle state and awake on touch events
zmq_pollitem_t polls[1] = {{0}};
polls[0].fd = s->touch_fd;
polls[0].events = ZMQ_POLLIN;
int ret = zmq_poll(polls, 1, 0);
if (ret < 0){
if (errno == EINTR) continue;
LOGW("poll failed (%d)", ret);
} else if (ret > 0) {
// awake on any touch
int touch_x = -1, touch_y = -1;
int touched = touch_read(&touch, &touch_x, &touch_y);
if (touched == 1) {
set_awake(s, true);
}
}
if (s->status != STATUS_STOPPED) {
update_status(s, STATUS_STOPPED);
}
check_messages(s);
} else {
set_awake(s, true);
if (s->status == STATUS_STOPPED) {
update_status(s, STATUS_DISENGAGED);
}
// Car started, fetch a new rgb image from ipc and peek for zmq events.
ui_update(s);
if(!s->vision_connected) {
if (!s->vision_connected) {
// Visiond process is just stopped, force a redraw to make screen blank again.
s->scene.satelliteCount = -1;
s->scene.uilayout_sidebarcollapsed = false;
ui_draw(s);
glFinish();
should_swap = true;
@ -888,8 +953,15 @@ int main(int argc, char* argv[]) {
set_awake(s, false);
}
// Don't waste resources on drawing in case screen is off or car is not started.
if (s->awake && s->vision_connected) {
// manage hardware disconnect
if (s->hardware_timeout > 0) {
s->hardware_timeout--;
} else {
s->scene.hwType = cereal_HealthData_HwType_unknown;
}
// Don't waste resources on drawing in case screen is off
if (s->awake) {
ui_draw(s);
glFinish();
should_swap = true;

View File

@ -38,6 +38,12 @@
#define ALERTSIZE_MID 2
#define ALERTSIZE_FULL 3
#define COLOR_BLACK_ALPHA nvgRGBA(0, 0, 0, 85)
#define COLOR_WHITE nvgRGBA(255, 255, 255, 255)
#define COLOR_WHITE_ALPHA nvgRGBA(255, 255, 255, 85)
#define COLOR_YELLOW nvgRGBA(218, 202, 37, 255)
#define COLOR_RED nvgRGBA(201, 34, 49, 255)
#ifndef QCOM
#define UI_60FPS
#endif
@ -60,6 +66,14 @@ const int viz_w = vwp_w-(bdr_s*2);
const int header_h = 420;
const int footer_h = 280;
const int footer_y = vwp_h-bdr_s-footer_h;
const int settings_btn_h = 117;
const int settings_btn_w = 200;
const int settings_btn_x = 50;
const int settings_btn_y = 35;
const int home_btn_h = 180;
const int home_btn_w = 180;
const int home_btn_x = 60;
const int home_btn_y = vwp_h - home_btn_h - 40;
const int UI_FREQ = 30; // Hz
@ -115,7 +129,7 @@ typedef struct UIScene {
int lead_status;
float lead_d_rel, lead_y_rel, lead_v_rel;
int lead_status2;
float lead_d_rel2, lead_y_rel2, lead_v_rel2;
@ -131,6 +145,16 @@ typedef struct UIScene {
// Used to show gps planner status
bool gps_planner_active;
uint8_t networkType;
uint8_t networkStrength;
int batteryPercent;
char batteryStatus[64];
float freeSpace;
uint8_t thermalStatus;
int paTemp;
int hwType;
int satelliteCount;
} UIScene;
typedef struct {
@ -168,6 +192,11 @@ typedef struct UIState {
int img_turn;
int img_face;
int img_map;
int img_button_settings;
int img_button_home;
int img_battery;
int img_battery_charging;
int img_network[6];
// sockets
Context *ctx;
@ -177,7 +206,11 @@ typedef struct UIState {
SubSocket *radarstate_sock;
SubSocket *map_data_sock;
SubSocket *uilayout_sock;
SubSocket *thermal_sock;
SubSocket *health_sock;
SubSocket *ubloxgnss_sock;
Poller * poller;
Poller * ublox_poller;
int active_app;
@ -221,6 +254,7 @@ typedef struct UIState {
int is_metric_timeout;
int longitudinal_control_timeout;
int limit_set_speed_timeout;
int hardware_timeout;
bool controls_seen;
@ -254,8 +288,9 @@ typedef struct UIState {
// API
void ui_draw_vision_alert(UIState *s, int va_size, int va_color,
const char* va_text1, const char* va_text2);
const char* va_text1, const char* va_text2);
void ui_draw(UIState *s);
void ui_draw_sidebar(UIState *s);
void ui_nvg_init(UIState *s);
#endif