diff --git a/common/text_window.py b/common/text_window.py new file mode 100755 index 0000000000..fb9ed4f836 --- /dev/null +++ b/common/text_window.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +import os +import subprocess +from common.basedir import BASEDIR + + +class TextWindow(): + def __init__(self, s): + try: + self.text_proc = subprocess.Popen(["./text", s], + stdin=subprocess.PIPE, + cwd=os.path.join(BASEDIR, "selfdrive", "ui", "text"), + close_fds=True) + except OSError: + self.text_proc = None + + def get_status(self): + if self.text_proc is not None: + self.text_proc.poll() + return s.text_proc.returncode + + return None + + def __enter__(self): + return self + + def close(self): + if self.text_proc is not None: + self.text_proc.terminate() + self.text_proc = None + + def __del__(self): + self.close() + + def __exit__(self, type, value, traceback): + self.close() + + +class FakeTextWindow(): + def __init__(self): + pass + + def get_status(self): + return None + + def __enter__(self): + return self + + def update(self, _): + pass + + def __exit__(self, type, value, traceback): + pass + + +if __name__ == "__main__": + import time + text = """Traceback (most recent call last): + File "./controlsd.py", line 608, in + main() + File "./controlsd.py", line 604, in main + controlsd_thread(sm, pm, logcan) + File "./controlsd.py", line 455, in controlsd_thread + 1/0 +ZeroDivisionError: division by zero""" + print(text) + + with TextWindow(text) as s: + for _ in range(100): + if s.get_status() == 1: + print("Got exit button") + break + time.sleep(0.1) + print("gone") diff --git a/release/files_common b/release/files_common index 20fe7a2f7f..8f8b79c397 100644 --- a/release/files_common +++ b/release/files_common @@ -31,6 +31,7 @@ common/basedir.py common/filter_simple.py common/stat_live.py common/spinner.py +common/text_window.py common/cython_hacks.py common/apk.py common/SConscript @@ -331,6 +332,10 @@ selfdrive/ui/spinner/Makefile selfdrive/ui/spinner/spinner selfdrive/ui/spinner/spinner.c +selfdrive/ui/text/Makefile +selfdrive/ui/text/text +selfdrive/ui/text/text.c + selfdrive/camerad/SConscript selfdrive/camerad/main.cc selfdrive/camerad/bufs.h diff --git a/selfdrive/ui/text/Makefile b/selfdrive/ui/text/Makefile new file mode 100644 index 0000000000..7a011edb35 --- /dev/null +++ b/selfdrive/ui/text/Makefile @@ -0,0 +1,73 @@ +CC = clang +CXX = clang++ + +PHONELIBS = ../../../phonelibs + +WARN_FLAGS = -Werror=implicit-function-declaration \ + -Werror=incompatible-pointer-types \ + -Werror=int-conversion \ + -Werror=return-type \ + -Werror=format-extra-args + +CFLAGS = -std=gnu11 -fPIC -O2 $(WARN_FLAGS) +CXXFLAGS = -std=c++11 -fPIC -O2 $(WARN_FLAGS) + +NANOVG_FLAGS = -I$(PHONELIBS)/nanovg + +OPENGL_LIBS = -lGLESv3 + +FRAMEBUFFER_LIBS = -lutils -lgui -lEGL + +OBJS = text.o \ + ../../common/framebuffer.o \ + ../../../selfdrive/common/touch.o \ + $(PHONELIBS)/nanovg/nanovg.o \ + opensans_regular.o \ + +DEPS := $(OBJS:.o=.d) + +.PHONY: all +all: text + +text: $(OBJS) + @echo "[ LINK ] $@" + $(CXX) -fPIC -o '$@' $^ \ + -s \ + $(FRAMEBUFFER_LIBS) \ + -L/system/vendor/lib64 \ + $(OPENGL_LIBS) \ + -lm -llog + +opensans_regular.o: ../../assets/fonts/opensans_regular.ttf + @echo "[ bin2o ] $@" + cd '$(dir $<)' && ld -r -b binary '$(notdir $<)' -o '$(abspath $@)' + +%.o: %.c + mkdir -p $(@D) + @echo "[ CC ] $@" + $(CC) $(CPPFLAGS) $(CFLAGS) \ + -I../.. \ + -I$(PHONELIBS)/android_frameworks_native/include \ + -I$(PHONELIBS)/android_system_core/include \ + -I$(PHONELIBS)/android_hardware_libhardware/include \ + $(NANOVG_FLAGS) \ + -c -o '$@' '$<' + +%.o: %.cc + mkdir -p $(@D) + @echo "[ CXX ] $@" + $(CXX) $(CPPFLAGS) $(CXXFLAGS) \ + -I../../selfdrive \ + -I../../ \ + -I$(PHONELIBS)/android_frameworks_native/include \ + -I$(PHONELIBS)/android_system_core/include \ + -I$(PHONELIBS)/android_hardware_libhardware/include \ + $(NANOVG_FLAGS) \ + -c -o '$@' '$<' + + +.PHONY: clean +clean: + rm -f text $(OBJS) $(DEPS) + +-include $(DEPS) diff --git a/selfdrive/ui/text/text b/selfdrive/ui/text/text new file mode 100755 index 0000000000..c4a62baaa6 --- /dev/null +++ b/selfdrive/ui/text/text @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be30fb76fbf9a21853c5357757e126210254f46b758ec2fd7438c24604d2bd70 +size 494112 diff --git a/selfdrive/ui/text/text.c b/selfdrive/ui/text/text.c new file mode 100644 index 0000000000..cd910cc80b --- /dev/null +++ b/selfdrive/ui/text/text.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "nanovg.h" +#define NANOVG_GLES3_IMPLEMENTATION +#include "nanovg_gl.h" +#include "nanovg_gl_utils.h" + + +#include "common/framebuffer.h" +#include "common/touch.h" + + +#define COLOR_WHITE nvgRGBA(255, 255, 255, 255) +#define MAX_TEXT_SIZE 2048 + +extern const unsigned char _binary_opensans_regular_ttf_start[]; +extern const unsigned char _binary_opensans_regular_ttf_end[]; + +static void set_brightness(int brightness) { + FILE *f = fopen("/sys/class/leds/lcd-backlight/brightness", "wb"); + if (f != NULL) { + fprintf(f, "%d", brightness); + fclose(f); + } +} + +int main(int argc, char** argv) { + int err; + + // spinner + int fb_w, fb_h; + FramebufferState *fb = framebuffer_init("text", 0x00001000, false, + &fb_w, &fb_h); + assert(fb); + + NVGcontext *vg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES); + assert(vg); + + int font = nvgCreateFontMem(vg, "regular", (unsigned char*)_binary_opensans_regular_ttf_start, _binary_opensans_regular_ttf_end-_binary_opensans_regular_ttf_start, 0); +assert(font >= 0); + + // Awake + framebuffer_set_power(fb, HWC_POWER_MODE_NORMAL); + set_brightness(255); + + glClearColor(0.1, 0.1, 0.1, 1.0); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + nvgBeginFrame(vg, fb_w, fb_h, 1.0f); + + // background + nvgBeginPath(vg); + NVGpaint bg = nvgLinearGradient(vg, fb_w, 0, fb_w, fb_h, + nvgRGBA(0, 0, 0, 175), nvgRGBA(0, 0, 0, 255)); + nvgFillPaint(vg, bg); + nvgRect(vg, 0, 0, fb_w, fb_h); + nvgFill(vg); + + + // Text + nvgFillColor(vg, COLOR_WHITE); + nvgFontSize(vg, 75.0f); + + if (argc >= 2) { + float x = 150; + float y = 150; + + // Copy text + char * text = malloc(MAX_TEXT_SIZE); + strncpy(text, argv[1], MAX_TEXT_SIZE); + + float lineh; + nvgTextMetrics(vg, NULL, NULL, &lineh); + + // nvgTextBox strips leading whitespace. We have to reimplement + char * next = strtok(text, "\n"); + while (next != NULL){ + nvgText(vg, x, y, next, NULL); + y += lineh; + next = strtok(NULL, "\n"); + } + } + + // Button + int b_x = 1500; + int b_y = 800; + int b_w = 300; + int b_h = 150; + + nvgBeginPath(vg); + nvgFillColor(vg, nvgRGBA(8, 8, 8, 255)); + nvgRoundedRect(vg, b_x, b_y, b_w, b_h, 20); + nvgFill(vg); + + nvgFillColor(vg, nvgRGBA(255, 255, 255, 255)); + nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); + nvgText(vg, b_x+b_w/2, b_y+b_h/2, "Exit", NULL); + + nvgBeginPath(vg); + nvgStrokeColor(vg, nvgRGBA(255, 255, 255, 50)); + nvgStrokeWidth(vg, 5); + nvgRoundedRect(vg, b_x, b_y, b_w, b_h, 20); + nvgStroke(vg); + + // Draw to screen + nvgEndFrame(vg); + framebuffer_swap(fb); + assert(glGetError() == GL_NO_ERROR); + + + // Wait for button + TouchState touch; + touch_init(&touch); + + while (true){ + int touch_x = -1, touch_y = -1; + int res = touch_poll(&touch, &touch_x, &touch_y, 0); + if (res){ + + if (touch_x > b_x && touch_x < b_x + b_w){ + if (touch_y > b_y && touch_y < b_y + b_h){ + return 1; + } + } + } + + usleep(1000000 / 60); + } + + return 0; +}