]> git.ipfire.org Git - fireperf.git/commitdiff
tui: Add scaffolding for a basic TUI
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 19 Sep 2024 12:53:40 +0000 (12:53 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 19 Sep 2024 12:53:40 +0000 (12:53 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/ctx.h
src/main.c
src/tui.c [new file with mode: 0644]
src/tui.h [new file with mode: 0644]

index d92097908c09d02cbcfb6ce8430a6e241817b4d1..4c320b1fc636e20a235465dc42f5aae5f7b2a243 100644 (file)
@@ -75,6 +75,8 @@ fireperf_SOURCES = \
        src/server.h \
        src/stats.c \
        src/stats.h \
+       src/tui.c \
+       src/tui.h \
        src/util.c \
        src/util.h \
        src/worker.c \
index e8fceb104e0864c6d290f505ab050fec96514c75..2bc601ff32885556f589be6fb54de476d4e9be43 100644 (file)
--- a/src/ctx.h
+++ b/src/ctx.h
 
 #include "constants.h"
 #include "stats.h"
+#include "tui.h"
 
 // Forward declarations
 struct fireperf_worker;
 
 struct fireperf_ctx {
+       // TUI
+       struct fireperf_tui* tui;
+
        int terminated;
        int loglevel;
        enum {
index d03017bfc4e126fbb5ef876c0ae583bcbdad5473..c7262379bba2ea2828055a4d5f3fc0ab22e4be68 100644 (file)
@@ -28,6 +28,8 @@
 #include <sys/timerfd.h>
 #include <unistd.h>
 
+#include <ncurses.h>
+
 #include "client.h"
 #include "main.h"
 #include "logging.h"
@@ -98,6 +100,7 @@ int main(int argc, char* argv[]) {
        void* data = NULL;
        int epollfd = -1;
        int timerfd = -1;
+       int stdinfd = -1;
        int ready;
        int r;
 
@@ -117,6 +120,11 @@ int main(int argc, char* argv[]) {
                        goto ERROR;
        }
 
+       // Setup the TUI
+       r = fireperf_tui_init(&ctx->tui, ctx);
+       if (r)
+               goto ERROR;
+
        // Set limits
        r = set_limits(ctx);
        if (r)
@@ -130,6 +138,13 @@ int main(int argc, char* argv[]) {
                goto ERROR;
        }
 
+       // Register the TUI with the event loop
+       stdinfd = fireperf_tui_register(ctx->tui, epollfd);
+       if (stdinfd < 0) {
+               ERROR(ctx, "Could not register the TUI: %s\n", strerror(-stdinfd));
+               goto ERROR;
+       }
+
        // Create a timer that fires once a second
        timerfd = setup_timer(ctx, epollfd);
        if (timerfd < 0) {
@@ -187,6 +202,12 @@ int main(int argc, char* argv[]) {
                                if (r)
                                        goto ERROR;
 
+                       // Handle user input
+                       } else if (fd == stdinfd) {
+                               r = fireperf_tui_action(ctx->tui);
+                               if (r)
+                                       goto ERROR;
+
                        // Handle everything else
                        } else {
                                switch (ctx->mode) {
@@ -210,6 +231,9 @@ int main(int argc, char* argv[]) {
        }
 
 ERROR:
+       // Terminate the TUI
+       fireperf_tui_finish(ctx->tui);
+
        switch (ctx->mode) {
                case FIREPERF_MODE_CLIENT:
                        fireperf_client_free(ctx, data);
diff --git a/src/tui.c b/src/tui.c
new file mode 100644 (file)
index 0000000..20540d7
--- /dev/null
+++ b/src/tui.c
@@ -0,0 +1,217 @@
+/*#############################################################################
+#                                                                             #
+# fireperf - A network benchmarking tool                                      #
+# Copyright (C) 2024 IPFire Development Team                                  #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+#include <ncurses.h>
+
+#include "ctx.h"
+#include "tui.h"
+
+struct fireperf_tui {
+       struct fireperf_ctx* ctx;
+
+       // The main screen
+       WINDOW* screen;
+
+       // The frame around the screen
+       WINDOW* frame;
+
+       // The status bar
+       WINDOW* status;
+};
+
+static int fireperf_tui_setup_frame(struct fireperf_tui* tui) {
+       int r;
+
+       // Create the window
+       tui->frame = newwin(LINES - 1, COLS, 0, 0);
+       if (!tui->frame)
+               return 1;
+
+       // Make my brush blue
+       wattron(tui->frame, COLOR_PAIR(3));
+
+       // Draw a box around the frame
+       box(tui->frame, 0, 0);
+
+       // Reset my brush
+       wattroff(tui->frame, COLOR_PAIR(3));
+
+       // Refresh
+       wrefresh(tui->frame);
+
+       return 0;
+}
+
+static int fireperf_tui_setup_status(struct fireperf_tui* tui) {
+       int r;
+
+       // Create the window
+       tui->status = newwin(1, COLS, LINES - 1, 0);
+       if (!tui->status)
+               return 1;
+
+       // Create status line
+       move(LINES - 1, 0);
+
+       const char* help = "Press 'q' to quit";
+
+       // Write in black & white
+       wattron(tui->status, COLOR_PAIR(4));
+
+       // Push the help text to the right
+       for (int i = 0; i < COLS - strlen(help); i++)
+               waddch(tui->status, ' ');
+
+       // Write the help text
+       waddstr(tui->status, help);
+
+       // Reset the colour
+       wattroff(tui->status, COLOR_PAIR(4));
+
+       // Refresh
+       wrefresh(tui->status);
+
+       return 0;
+}
+
+static int fireperf_tui_setup(struct fireperf_tui* tui) {
+       int r;
+
+       // Start ncurses
+       tui->screen = initscr();
+       if (!tui->screen)
+               return -errno;
+
+       // We would like to see colours
+       start_color();
+
+       // Configure a few colours
+       init_pair(1, COLOR_RED,    COLOR_BLACK);
+       init_pair(2, COLOR_YELLOW, COLOR_BLACK);
+       init_pair(3, COLOR_BLUE,   COLOR_BLACK);
+       init_pair(4, COLOR_BLACK,  COLOR_WHITE);
+
+       // Enable raw mode
+       raw();
+
+       // Disable character echoing
+       noecho();
+
+       // Set the cursor to the top
+       curs_set(0);
+
+       // Configure the keyboard
+       keypad(stdscr, 1);
+
+       // ???
+       halfdelay(1);
+
+       // Refresh!
+       refresh();
+
+       // Setup the frame
+       r = fireperf_tui_setup_frame(tui);
+       if (r)
+               return r;
+
+       // Setup the status bar
+       r = fireperf_tui_setup_status(tui);
+       if (r)
+               return r;
+
+       // Refresh!
+       refresh();
+
+       return 0;
+}
+
+int fireperf_tui_init(struct fireperf_tui** tui, struct fireperf_ctx* ctx) {
+       struct fireperf_tui* t = NULL;
+       int r;
+
+       // Allocate the TUI
+       t = calloc(1, sizeof(*t));
+       if (!t)
+               return -errno;
+
+       // Store a reference to the context
+       t->ctx = ctx;
+
+       // Perform the basic setup
+       r = fireperf_tui_setup(t);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+void fireperf_tui_finish(struct fireperf_tui* tui) {
+       endwin();
+
+       free(tui);
+}
+
+int fireperf_tui_register(struct fireperf_tui* tui, int epollfd) {
+       const int fd = STDIN_FILENO;
+       int r;
+
+       struct epoll_event ev = {
+               .events  = EPOLLIN|EPOLLET,
+               .data.fd = fd,
+       };
+
+       // Register the timer with the event loop
+       r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
+       if (r)
+               return -errno;
+
+       return fd;
+}
+
+/*
+       Called when the user presses a button
+*/
+int fireperf_tui_action(struct fireperf_tui* tui) {
+       int c;
+
+       for (;;) {
+               // Fetch the next character
+               c = getch();
+               if (c < 0)
+                       break;
+
+               switch (c) {
+                       // Quit
+                       case 'q':
+                               return -ESHUTDOWN;
+
+                       default:
+                               break;
+               }
+       }
+
+       return 0;
+}
diff --git a/src/tui.h b/src/tui.h
new file mode 100644 (file)
index 0000000..6c82266
--- /dev/null
+++ b/src/tui.h
@@ -0,0 +1,34 @@
+/*#############################################################################
+#                                                                             #
+# fireperf - A network benchmarking tool                                      #
+# Copyright (C) 2024 IPFire Development Team                                  #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#ifndef FIREPERF_TUI_H
+#define FIREPERF_TUI_H
+
+struct fireperf_tui;
+
+#include "ctx.h"
+
+int fireperf_tui_init(struct fireperf_tui** tui, struct fireperf_ctx* ctx);
+void fireperf_tui_finish(struct fireperf_tui* tui);
+
+int fireperf_tui_register(struct fireperf_tui* tui, int epollfd);
+int fireperf_tui_action(struct fireperf_tui* tui);
+
+#endif /* FIREPERF_TUI_H */