diff --git a/driver/Makefile b/driver/Makefile index 6978a7d..df21db9 100644 --- a/driver/Makefile +++ b/driver/Makefile @@ -13,17 +13,18 @@ OUT_CLI = cli OBJ_CLI = cli.o CFLAGS = -std=c++14 -O3 -Wall -Wextra LDFLAGS = -lcurses -OBJECTS = drv/usart.o drv/b15f.o drv/plottyfile.o drv/dot.o +OBJECTS_DRV = drv/usart.o drv/b15f.o drv/plottyfile.o drv/dot.o +OBJECTS_UI = ui/view.o ui/view_main.o ui/view_info.o COMPILE = $(COMPILER_PATH) $(CFLAGS) -main: $(OBJECTS) $(OBJ_MAIN) +main: $(OBJECTS_DRV) $(OBJ_MAIN) @echo "Linking..." - $(COMPILE) $(OBJ_MAIN) $(OBJECTS) -o $(OUT_MAIN) $(LDFLAGS) + $(COMPILE) $(OBJ_MAIN) $(OBJECTS_DRV) -o $(OUT_MAIN) $(LDFLAGS) -cli: $(OBJECTS) $(OBJ_CLI) +cli: $(OBJECTS_DRV) $(OBJECTS_UI) $(OBJ_CLI) @echo "Linking..." - $(COMPILE) $(OBJ_CLI) $(OBJECTS) -o $(OUT_CLI) $(LDFLAGS) + $(COMPILE) $(OBJ_CLI) $(OBJECTS_DRV) $(OBJECTS_UI) -o $(OUT_CLI) $(LDFLAGS) help: @echo "This Makefile has the following rules:" @@ -33,7 +34,7 @@ help: clean: @echo "Cleaning..." - rm -f $(OBJECTS) $(OUTPUT) + rm -f $(OBJECTS_DRV) $(OBJECTS_UI) $(OUT_CLI) $(OUT_MAIN) .cpp.o: $(COMPILE) -c $< -o $@ diff --git a/driver/cli b/driver/cli index 664d5db..86ca550 100755 Binary files a/driver/cli and b/driver/cli differ diff --git a/driver/cli.cpp b/driver/cli.cpp index dd77457..51083aa 100644 --- a/driver/cli.cpp +++ b/driver/cli.cpp @@ -5,112 +5,92 @@ #include #include #include - -#define width 30 -#define height 10 - -WINDOW *win_menu; - -std::vector choices = { - "Choice 1", - "Choice 2", - "Choice 3", - "Choice 4", - "Exit", -}; +#include "ui/view_main.h" +#include "ui/view_info.h" -void init_win(WINDOW*& win) +std::vector win_stack; +ViewMain* view_main = nullptr; +ViewInfo* view_info = nullptr; + +void init() { - struct winsize size; - if (ioctl(0, TIOCGWINSZ, (char *) &size) < 0) - throw std::runtime_error("TIOCGWINSZ error"); - - int start_x = (size.ws_col - width); - int start_y = (size.ws_row - height); - if(start_x % 2) - start_x++; - if(start_y % 2) - start_y++; - start_x /= 2; - start_y /= 2; - - win = newwin(height, width, start_y, start_x); - keypad(win, TRUE); - refresh(); -} - -void print_menu(WINDOW *win_menu, int highlight) -{ - - - - int x, y, i; - - x = 2; - y = 2; - box(win_menu, 0, 0); - for(i = 0; i < choices.size(); ++i) - { if(highlight == i + 1) /* High light the present choice */ - { wattron(win_menu, A_REVERSE); - mvwprintw(win_menu, y, x, "%s", choices[i].c_str()); - wattroff(win_menu, A_REVERSE); - } - else - mvwprintw(win_menu, y, x, "%s", choices[i].c_str()); - ++y; - } - wrefresh(win_menu); -} - -int main() -{ - int highlight = 1; - int choice = 0; - int c; - initscr(); start_color(); curs_set(0); // 0: invisible, 1: normal, 2: very visible clear(); noecho(); - cbreak(); /* Line buffering disabled. pass on everything */ - - init_win(win_menu); - mvprintw(0, 0, "Use arrow keys to go up and down, Press enter to select a choice"); - print_menu(win_menu, highlight); - refresh(); + cbreak(); // Line buffering disabled. pass on everything + mousemask(ALL_MOUSE_EVENTS, NULL); - while(1) - { c = wgetch(win_menu); - switch(c) - { case KEY_UP: - if(highlight == 1) - highlight = choices.size(); - else - --highlight; - break; - case KEY_DOWN: - if(highlight == choices.size()) - highlight = 1; - else - ++highlight; - break; - case 10: - choice = highlight; - break; - default: - mvprintw(24, 0, "Charcter pressed is = %3d Hopefully it can be printed as '%c'", c, c); - refresh(); - break; - } - print_menu(win_menu, highlight); - if(choice != 0) /* User did a choice come out of the infinite loop */ - break; - } - //mvprintw(23, 0, "You chose choice %d with choice string %s\n", choice, choices[choice - 1].c_str()); + View::setWinContext(newwin(32, 128, 0, 0)); +} + +void cleanup() +{ clrtoeol(); refresh(); endwin(); - return 0; +} + +void finish(int key) +{ + cleanup(); + exit(EXIT_SUCCESS); +} + +void input(int prev_key) +{ + std::function nextCall; + int key; + do + { + key = wgetch(View::getWinContext()); + nextCall = win_stack.back()->keypress(key); + + if(key == -1) + { + win_stack.pop_back(); + if(win_stack.size()) + win_stack.back()->repaint(); + return; + } + + if(nextCall) + nextCall(key); + } + while(!false); +} + +void show_info(int key) +{ + View* view = new ViewInfo(); + view->setTitle("Info"); + view->repaint(); + + win_stack.push_back(view); + input(0); +} + +void show_main(int key) +{ + View* view = new ViewMain(); + view->setTitle("B15F - Command Line Interface"); + view->addCall(&show_info); + view->addCall(&finish); + view->repaint(); + + win_stack.push_back(view); + input(0); +} + +int main() +{ + + init(); + + show_main(0); + + cleanup(); + return EXIT_SUCCESS; } diff --git a/driver/main b/driver/main deleted file mode 100755 index 0cb40b6..0000000 Binary files a/driver/main and /dev/null differ diff --git a/driver/ui/view.cpp b/driver/ui/view.cpp new file mode 100644 index 0000000..9ec8dda --- /dev/null +++ b/driver/ui/view.cpp @@ -0,0 +1,58 @@ +#include "view.h" + +WINDOW* View::win = nullptr; + +View::View() +{ + if(!win) + throw std::runtime_error("View::win not initialized, missing context!"); + getmaxyx(win, height, width); // init width and height + keypad(win, TRUE); +} + +void View::setWinContext(WINDOW* win) +{ + View::win = win; +} + +WINDOW* View::getWinContext() +{ + return win; +} + +void View::addCall(std::function call) +{ + calls.push_back(call); +} + +void View::setTitle(std::string title) +{ + this->title = title; +} + +void View::repaint() +{ + // get screen size + struct winsize size; + if (ioctl(0, TIOCGWINSZ, (char *) &size) < 0) + throw std::runtime_error("TIOCGWINSZ error"); + + + start_x = floor((size.ws_col - width) / 2.); + start_y = floor((size.ws_row - height) / 2.); + + mvwin(win, start_y, start_x); + + wclear(win); + + // generic draw + box(win, 0, 0); + int offset_x = (width - title.length()) / 2; + mvwprintw(win, 1, offset_x, "%s", title.c_str()); + + // specific draw + draw(); + + refresh(); + wrefresh(win); +} diff --git a/driver/ui/view.h b/driver/ui/view.h new file mode 100644 index 0000000..8cb620e --- /dev/null +++ b/driver/ui/view.h @@ -0,0 +1,42 @@ +#ifndef VIEW_H +#define VIEW_H + +#include +#include +#include +#include +#include // sudo apt-get install libncurses5-dev +#include +#include + +class View +{ +public: + View(void); + + static void setWinContext(WINDOW* win); + static WINDOW* getWinContext(void); + + virtual void addCall(std::function call); + + void setSize(int width, int height); + void setTitle(std::string title); + + virtual void repaint(void); + + virtual void draw(void) = 0; + virtual std::function keypress(int& key) = 0; + + +protected: + int width, height; + int start_x = 0, start_y = 0; + std::string title; + std::vector> calls; + + constexpr static int KEY_ENT = 10; + + static WINDOW* win; +}; + +#endif // VIEW_H diff --git a/driver/ui/view_info.cpp b/driver/ui/view_info.cpp new file mode 100644 index 0000000..74fa330 --- /dev/null +++ b/driver/ui/view_info.cpp @@ -0,0 +1,33 @@ +#include "view_info.h" + +ViewInfo::ViewInfo() +{ +} + +void ViewInfo::draw() +{ + mvwprintw(win, text_offset_y, text_offset_x, "%s", "hello"); +} + +std::function ViewInfo::keypress(int& key) +{ + std::function ret = nullptr; + switch(key) + { + + case KEY_MOUSE: + { + // http://pronix.linuxdelta.de/C/Linuxprogrammierung/Linuxsystemprogrammieren_C_Kurs_Kapitel10b.shtml + MEVENT event; + if(getmouse(&event) == OK && event.bstate & BUTTON1_CLICKED) + key = -1; // do return from view + break; + } + case KEY_ENT: + key = -1; // do return from view + break; + default: + break; + } + return ret; +} diff --git a/driver/ui/view_info.h b/driver/ui/view_info.h new file mode 100644 index 0000000..22d4f27 --- /dev/null +++ b/driver/ui/view_info.h @@ -0,0 +1,18 @@ +#ifndef VIEW_INFO +#define VIEW_INFO + +#include "view.h" + +class ViewInfo : public View +{ +public: + ViewInfo(void); + virtual void draw(void) override; + virtual std::function keypress(int& key) override; + +private: + constexpr static int text_offset_x = 2; + constexpr static int text_offset_y = 3; +}; + +#endif // VIEW_INFO diff --git a/driver/ui/view_main.cpp b/driver/ui/view_main.cpp new file mode 100644 index 0000000..e1348e4 --- /dev/null +++ b/driver/ui/view_main.cpp @@ -0,0 +1,62 @@ +#include "view_main.h" + +void ViewMain::draw() +{ + + for(size_t i = 0; i < choices.size(); i++) + { + if(selection == i) + wattron(win, A_REVERSE); + mvwprintw(win, i + choice_offset_y, choice_offset_x, "%s", choices[i].c_str()); + if(selection == i) + wattroff(win, A_REVERSE); + } +} + +std::function ViewMain::keypress(int& key) +{ + std::function ret = nullptr; + switch(key) + { + case KEY_UP: + selection = (selection - 1) % choices.size(); + break; + case KEY_DOWN: + selection = (selection + 1) % choices.size(); + break; + case KEY_MOUSE: + { + // http://pronix.linuxdelta.de/C/Linuxprogrammierung/Linuxsystemprogrammieren_C_Kurs_Kapitel10b.shtml + MEVENT event; + bool hit = false; + if(getmouse(&event) == OK && event.bstate & (BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED)) + { + size_t column_start = start_x + choice_offset_x; + size_t row_start = start_y + choice_offset_y; + size_t mouse_x = event.x, mouse_y = event.y; + for(size_t i = 0; i < choices.size(); i++) + if(mouse_y == row_start + i && mouse_x >= column_start && mouse_x < column_start + choices[i].length()) + { + if(selection == i || event.bstate & BUTTON1_DOUBLE_CLICKED) + hit = true; + selection = i; + } + } + if(!hit) + break; + + // fall through to next case + __attribute__ ((fallthrough)); + } + case KEY_ENT: + if(selection == choices.size() - 1) // exit + key = -1; // do return from view + else + ret = calls[selection]; + break; + default: + break; + } + repaint(); + return ret; +} diff --git a/driver/ui/view_main.h b/driver/ui/view_main.h new file mode 100644 index 0000000..7792575 --- /dev/null +++ b/driver/ui/view_main.h @@ -0,0 +1,24 @@ +#ifndef VIEW_MAIN_H +#define VIEW_MAIN_H + +#include +#include +#include "view.h" + +class ViewMain : public View +{ +public: + virtual void draw(void) override; + virtual std::function keypress(int& key) override; + +private: + constexpr static int choice_offset_x = 2; + constexpr static int choice_offset_y = 3; + size_t selection = 0; + const std::vector choices = { + "Beobachten", + "Exit" + }; +}; + +#endif // VIEW_MAIN_H