diff --git a/lib/gks/gkscore.h b/lib/gks/gkscore.h index 60573a526..31f5168f2 100644 --- a/lib/gks/gkscore.h +++ b/lib/gks/gkscore.h @@ -234,10 +234,9 @@ void gks_lookup_afm(int font, int chr, stroke_data_t *buffer); DLLEXPORT char *gks_malloc(int size); DLLEXPORT char *gks_realloc(void *ptr, int size); DLLEXPORT void gks_free(void *ptr); - DLLEXPORT char *gks_strdup(const char *str); -void gks_perror(const char *, ...); +DLLEXPORT void gks_perror(const char *, ...); void gks_fatal_error(const char *, ...); const char *gks_function_name(int routine); void gks_report_error(int routine, int errnum); diff --git a/lib/gks/socket.c b/lib/gks/socket.c index 25b725b3f..27bf5f4e4 100644 --- a/lib/gks/socket.c +++ b/lib/gks/socket.c @@ -21,6 +21,7 @@ #include #else #define __STRSAFE__NO_INLINE +#define STRSAFE_NO_DEPRECATE #define _WIN32_WINNT 0x0602 #include #include diff --git a/lib/gr/contourf.c b/lib/gr/contourf.c index e27fcc1ca..4ffdc4dd6 100644 --- a/lib/gr/contourf.c +++ b/lib/gr/contourf.c @@ -530,7 +530,7 @@ void gr_draw_contourf(int nx, int ny, int nh, double *px, double *py, double *h, { marching_squares(px, py, pz, nx, ny, h, nh, first_color, last_color, major_h == 0); } - if (major_h) + if (major_h && major_h != -1) { gr_inqspace(&z_space_min, &z_space_max, &rotation, &tilt); gr_setspace(zmin, zmax, 0, 90); diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 444e26694..538dc3083 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -9209,7 +9209,8 @@ void gr_contour(int nx, int ny, int nh, double *px, double *py, double *h, doubl * \param[in] major_h Directs GR to label contour lines. For example, a value of * 3 would label every third line. A value of 1 will label * every line. A value of 0 produces no labels. To produce - * colored contour lines, add an offset of 1000 to major_h + * colored contour lines, add an offset of 1000 to major_h. + * Use a value of -1 to disable contour lines and labels. */ void gr_contourf(int nx, int ny, int nh, double *px, double *py, double *h, double *pz, int major_h) { diff --git a/lib/gr/gr.h b/lib/gr/gr.h index bb964acc3..1d338b58c 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -212,6 +212,7 @@ DLLEXPORT void gr_begingraphics(char *); DLLEXPORT void gr_endgraphics(void); DLLEXPORT char *gr_getgraphics(void); DLLEXPORT int gr_drawgraphics(char *); +DLLEXPORT int gr_startlistener(void); DLLEXPORT void gr_mathtex(double, double, char *); DLLEXPORT void gr_inqmathtex(double, double, char *, double *, double *); DLLEXPORT void gr_beginselection(int, int); diff --git a/lib/gr/stream.c b/lib/gr/stream.c index 643e97814..e8b6dbe29 100644 --- a/lib/gr/stream.c +++ b/lib/gr/stream.c @@ -1,31 +1,43 @@ -#if defined(__unix__) && !defined(__FreeBSD__) -#define _POSIX_C_SOURCE 200112L +#ifndef __FreeBSD__ +#ifdef __unix__ +#define _POSIX_C_SOURCE 200809L +#endif #endif #include +#include #include +#include #include -#include #ifndef _WIN32 -#include -#include -#include #include #include #include #include +#include +#include +#include +#include +#include #else #define _WIN32_WINNT 0x0602 #include #include #include +#include #endif #include "gr.h" -#include "stream.h" +#include "gks.h" #include "gkscore.h" +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#define PORT "8002" + static int status = EXIT_SUCCESS; static FILE *stream = NULL; @@ -40,6 +52,8 @@ static char *port = "4660"; /* 0x1234 */ static int nbytes = 0, size = 0, static_size = 0; +static int is_running = 0; + static void close_socket(int s) { #ifndef _WIN32 @@ -60,7 +74,11 @@ static void save(char *string, int nbytes) static_buffer = (char *)realloc(static_buffer, nbytes + 1); static_size = nbytes + 1; } +#ifdef _WIN32 + StringCchCopyA(static_buffer, static_size, string); +#else strcpy(static_buffer, string); +#endif } static int sendstream(char *string) @@ -285,3 +303,266 @@ char *gr_getgraphics(void) { return static_buffer; } + +#ifdef _WIN32 + +#define CMD_LINE_LEN (32767 + 10) +/* + * The maximum length of an environment variable is 32767 characters plus 10 characters for 'cmd /c ""' + */ + +static DWORD WINAPI grplot_thread(LPVOID parm) +{ + wchar_t *cmd = (char *)parm; + wchar_t w_cmd_line[CMD_LINE_LEN]; + STARTUPINFOW startupInfo; + PROCESS_INFORMATION processInformation; + + StringCbPrintfW(w_cmd_line, CMD_LINE_LEN, L"cmd /c \"%ls\" --listen", cmd); + + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + ZeroMemory(&processInformation, sizeof(processInformation)); + + is_running = 1; + CreateProcessW(NULL, w_cmd_line, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | CREATE_NO_WINDOW | DETACHED_PROCESS, + NULL, NULL, &startupInfo, &processInformation); + WaitForSingleObject(processInformation.hThread, INFINITE); + is_running = 0; + + CloseHandle(processInformation.hProcess); + CloseHandle(processInformation.hThread); + + return 0; +} + +#else + +static void *grplot_thread(void *arg) +{ + int retstat = 0; +#ifdef __APPLE__ + sigset_t blockMask, origMask; + struct sigaction saIgnore, saOrigQuit, saOrigInt, saDefault; + pid_t pid; + + sigemptyset(&blockMask); + sigaddset(&blockMask, SIGCHLD); + sigprocmask(SIG_BLOCK, &blockMask, &origMask); + + saIgnore.sa_handler = SIG_IGN; + saIgnore.sa_flags = 0; + sigemptyset(&saIgnore.sa_mask); + sigaction(SIGINT, &saIgnore, &saOrigInt); + sigaction(SIGQUIT, &saIgnore, &saOrigQuit); + + is_running = 1; + if ((pid = fork()) == 0) + { + saDefault.sa_handler = SIG_DFL; + saDefault.sa_flags = 0; + sigemptyset(&saDefault.sa_mask); + + if (saOrigInt.sa_handler != SIG_IGN) sigaction(SIGINT, &saDefault, NULL); + if (saOrigQuit.sa_handler != SIG_IGN) sigaction(SIGQUIT, &saDefault, NULL); + + sigprocmask(SIG_SETMASK, &origMask, NULL); + execl("/bin/sh", "sh", "-c", (char *)arg, (char *)NULL); + + _exit(127); + } + if (pid == -1) + { + fprintf(stderr, "Fork failed\n"); + retstat = -1; + } + else + { + int status; + while (waitpid(pid, &status, 0) == -1) + { + if (errno != EINTR) + { + retstat = WIFEXITED(status) != 0 ? WEXITSTATUS(status) : -1; + break; + } + } + } + is_running = 0; + + sigprocmask(SIG_SETMASK, &origMask, NULL); + sigaction(SIGINT, &saOrigInt, NULL); + sigaction(SIGQUIT, &saOrigQuit, NULL); +#else + is_running = 1; + retstat = system((char *)arg); + is_running = 0; +#endif + + return retstat == 0 ? arg : NULL; +} + +#endif + +static int start(void *cmd) +{ +#ifdef _WIN32 + DWORD thread; + + if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)grplot_thread, cmd, 0, &thread) == NULL) return -1; +#else + pthread_t thread; + + if (pthread_create(&thread, NULL, grplot_thread, cmd)) return -1; +#endif + return 0; +} + +static int connect_socket(int quiet) +{ + int rc, s; + struct addrinfo hints, *res = NULL; + int opt; + +#if defined(_WIN32) + WORD wVersionRequested = MAKEWORD(1, 1); + WSADATA wsaData; + + if (WSAStartup(wVersionRequested, &wsaData) != 0) + { + fprintf(stderr, "Can't find a usable WinSock DLL\n"); + return -1; + } +#endif + + memset(&hints, 0x00, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + if ((rc = getaddrinfo("localhost", PORT, &hints, &res)) != 0) + { + hints.ai_family = AF_INET6; + if ((rc = getaddrinfo("localhost", PORT, &hints, &res)) != 0) + { + if (!quiet) + { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rc)); + } + return -1; + } + } + + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s < 0) + { + if (!quiet) perror("socket"); + freeaddrinfo(res); + return -1; + } + + opt = 1; +#ifdef SO_REUSEADDR + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)); +#endif + + if (connect(s, res->ai_addr, res->ai_addrlen) < 0) + { + if (!quiet) perror("connect"); + freeaddrinfo(res); + return -1; + } + + freeaddrinfo(res); + + return s; +} + +int gr_startlistener(void) +{ +#ifdef _WIN32 + wchar_t command[CMD_LINE_LEN], w_env[MAXPATHLEN]; +#else + const char *command = NULL, *env; + char *cmd = NULL; +#endif + size_t retry_count, max_retry_count = 50; + int s; + + /* In order to not sleep an excessive amount start with a short sleep time and then ramp + it up to `max_sleep_time` */ + int sleep_ms; + int ms_to_ns = 1000000; + int initial_sleep_time_ms[] = {5, 10, 25, 50, 100}; + int max_sleep_time_ms = 300; + size_t n_initial_times = sizeof(initial_sleep_time_ms) / sizeof(initial_sleep_time_ms[0]); + max_retry_count += n_initial_times; + +#ifdef _WIN32 + if (!GetEnvironmentVariableW(L"GR_PLOT", command, CMD_LINE_LEN)) + { + if (!GetEnvironmentVariableW(L"GRDIR", w_env, MAXPATHLEN)) + { + StringCbPrintfW(command, CMD_LINE_LEN, L"%wS\\bin\\grplot.exe --listen", GRDIR); + } + else + { + StringCbPrintfW(command, CMD_LINE_LEN, L"%ws\\bin\\grplot.exe --listen", w_env); + } + } +#else + command = gks_getenv("GR_PLOT"); + if (command == NULL) + { + env = gks_getenv("GRDIR"); + if (env == NULL) env = GRDIR; + + cmd = (char *)gks_malloc(MAXPATHLEN); +#ifdef __APPLE__ + snprintf(cmd, MAXPATHLEN, "%s/Applications/grplot.app/Contents/MacOS/grplot --listen", env); +#else + snprintf(cmd, MAXPATHLEN, "%s/bin/grplot --listen", env); +#endif + command = cmd; + } +#endif + + for (retry_count = 1; retry_count <= max_retry_count; retry_count++) + { + if ((s = connect_socket(retry_count != max_retry_count)) == -1) + { + if (command != NULL && retry_count == 1) + { + /* For Julia BinaryBuilder environments the command string can be set to "" + because in this case grplot is started by the GR.jl wrapper script */ + if (*command) + { + if (start((void *)command) != 0) gks_perror("could not auto-start GR Plot application"); + } + } + sleep_ms = retry_count <= n_initial_times ? initial_sleep_time_ms[retry_count - 1] : max_sleep_time_ms; +#ifndef _WIN32 + { + struct timespec delay; + delay.tv_sec = 0; + delay.tv_nsec = sleep_ms * ms_to_ns; + while (nanosleep(&delay, &delay) == -1) + ; + } +#else + Sleep(sleep_ms); +#endif + } + else + break; + } + + close_socket(s); + + is_running = (retry_count <= max_retry_count); + +#ifndef _WIN32 + if (cmd != NULL) free(cmd); +#endif + + return s; +} diff --git a/lib/gr/stream.h b/lib/gr/stream.h index 26ba7ba83..2422022aa 100644 --- a/lib/gr/stream.h +++ b/lib/gr/stream.h @@ -9,6 +9,7 @@ int gr_openstream(const char *path); void gr_writestream(char *string, ...); void gr_flushstream(int discard); void gr_closestream(void); +int gr_startlistener(void); #ifdef __cplusplus } diff --git a/lib/grm/grplot/grplot.cxx b/lib/grm/grplot/grplot.cxx index ebc49ce2d..a619835cc 100644 --- a/lib/grm/grplot/grplot.cxx +++ b/lib/grm/grplot/grplot.cxx @@ -74,10 +74,9 @@ int main(int argc, char **argv) QApplication app(argc, argv); GRPlotMainWindow window(argc, argv); - window.show(); - if (strcmp(argv[1], "--listen") == 0) + if (strcmp(argv[1], "--listen") != 0) { - window.hide(); + window.show(); } return app.exec(); diff --git a/lib/grm/grplot/grplot.pro b/lib/grm/grplot/grplot.pro index 36db0885a..74c48bb98 100644 --- a/lib/grm/grplot/grplot.pro +++ b/lib/grm/grplot/grplot.pro @@ -8,21 +8,21 @@ QMAKE_CXXLAGS += $$(EXTRA_CXXFLAGS) QMAKE_LFLAGS += $$(EXTRA_LDFLAGS) HEADERS += grplot_widget.hxx grplot_mainwindow.hxx util.hxx qtterm/grm_args_t_wrapper.h qtterm/receiver_thread.h SOURCES += grplot_widget.cxx grplot.cxx grplot_mainwindow.cxx util.cxx qtterm/grm_args_t_wrapper.cpp qtterm/receiver_thread.cpp -INCLUDEPATH += ../include +INCLUDEPATH += ../include ../../gr if (macx) { if (exists(../libGRM.dylib)) { - LIBS += -L.. -lGRM + LIBS += -L.. -L../../gr -lGRM -lGR } else { - LIBS += -L$(GRDIR)/lib -lGRM + LIBS += -L$(GRDIR)/lib -lGRM -lGR } # On macOS, the grplot executable is located in `$(GRDIR)/Applications/grplot.app/Contents/MacOS` # and we need to resolve `libGRM.dylib` in `$(GRDIR)/lib` QMAKE_RPATHDIR += ../../../../lib } else { if (exists(../libGRM.so)) { - LIBS += -L.. -lGRM -Wl,-rpath-link,../../gr -Wl,-rpath-link,../../gr3 + LIBS += -L.. -L../../gr -lGRM -lGR -Wl,-rpath-link,../../gr -Wl,-rpath-link,../../gr3 } else { - LIBS += -L$(GRDIR)/lib -lGRM -Wl,-rpath-link,$(GRDIR)/lib + LIBS += -L$(GRDIR)/lib -lGRM -lGR -Wl,-rpath-link,$(GRDIR)/lib } # On every other system, the grplot executable is located in `$(GRDIR)/bin` # and we need to resolve `libGRM.so` in `$(GRDIR)/lib` diff --git a/lib/grm/grplot/grplot_mainwindow.cxx b/lib/grm/grplot/grplot_mainwindow.cxx index cb9d96c4a..27bde673e 100644 --- a/lib/grm/grplot/grplot_mainwindow.cxx +++ b/lib/grm/grplot/grplot_mainwindow.cxx @@ -27,15 +27,16 @@ GRPlotMainWindow::GRPlotMainWindow(int argc, char **argv) : QMainWindow() fprintf(stderr, "No plot type with the name %s was found.\n", kind.c_str()); } setCentralWidget(message); + resize(600, 450); } else { grplot_widget_ = new GRPlotWidget(this, argc, argv); setCentralWidget(grplot_widget_); + grplot_widget_->resize(600, 450); } setWindowTitle("GR Plot"); - resize(600, 450); } GRPlotMainWindow::~GRPlotMainWindow() = default; diff --git a/lib/grm/grplot/grplot_widget.cxx b/lib/grm/grplot/grplot_widget.cxx index a50794c76..cd31670bc 100644 --- a/lib/grm/grplot/grplot_widget.cxx +++ b/lib/grm/grplot/grplot_widget.cxx @@ -15,6 +15,8 @@ #include #include +#include + #include "grplot_widget.hxx" #include "util.hxx" @@ -42,15 +44,21 @@ void getWheelPos(QWheelEvent *event, int *x, int *y) #endif } -std::function callback; -extern "C" void wrapper(const grm_event_t *cb) +std::function size_callback; +extern "C" void size_callback_wrapper(const grm_event_t *cb) +{ + size_callback(cb); +} + +std::function cmd_callback; +extern "C" void cmd_callback_wrapper(const grm_event_t *event) { - callback(cb); + cmd_callback(reinterpret_cast(event)); } GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) - : QWidget(parent), args_(nullptr), rubberBand(nullptr), pixmap(nullptr), tooltip(nullptr) + : QWidget(parent), args_(nullptr), rubberBand(nullptr), pixmap(), redraw_pixmap(false), tooltip(nullptr) { const char *kind; unsigned int z_length; @@ -93,17 +101,19 @@ GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) qRegisterMetaType("grm_args_t_wrapper"); receiver_thread = new Receiver_Thread(); QObject::connect(receiver_thread, SIGNAL(resultReady(grm_args_t_wrapper)), this, - SLOT(received(grm_args_t_wrapper))); - QObject::connect(receiver_thread, SIGNAL(resultReady(grm_args_t_wrapper)), this->topLevelWidget(), SLOT(show())); + SLOT(received(grm_args_t_wrapper)), Qt::QueuedConnection); receiver_thread->start(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - callback = [this](auto &&PH1) { size_callback(std::forward(PH1)); }; + ::size_callback = [this](auto &&PH1) { size_callback(std::forward(PH1)); }; + ::cmd_callback = [this](auto &&PH1) { cmd_callback(std::forward(PH1)); }; #else - callback = std::bind(&GRPlotWidget::size_callback, this, std::placeholders::_1); + ::size_callback = std::bind(&GRPlotWidget::size_callback, this, std::placeholders::_1); + ::cmd_callback = std::bind(&GRPlotWidget::cmd_callback, this, std::placeholders::_1); #endif - grm_register(GRM_EVENT_SIZE, wrapper); + grm_register(GRM_EVENT_SIZE, size_callback_wrapper); + grm_register(GRM_EVENT_CMD, cmd_callback_wrapper); grm_args_t_wrapper configuration; configuration.set_wrapper(grm_args_new()); grm_args_push(configuration.get_wrapper(), "hold_plots", "i", 0); @@ -256,13 +266,9 @@ void GRPlotWidget::draw() void GRPlotWidget::redraw() { - if (pixmap != nullptr) - { - delete pixmap; - pixmap = nullptr; - } + redraw_pixmap = true; - repaint(); + update(); } #define style \ @@ -293,12 +299,18 @@ void GRPlotWidget::paintEvent(QPaintEvent *event) std::stringstream addresses; const char *kind; - if (!pixmap) + QSize needed_pixmap_size = QSize((int)(geometry().width() * this->devicePixelRatioF()), + (int)(geometry().height() * this->devicePixelRatioF())); + + if (pixmap.isNull() || pixmap.size() != needed_pixmap_size) { - pixmap = new QPixmap((int)(geometry().width() * this->devicePixelRatioF()), - (int)(geometry().height() * this->devicePixelRatioF())); - pixmap->setDevicePixelRatio(this->devicePixelRatioF()); + pixmap = QPixmap(needed_pixmap_size); + pixmap.setDevicePixelRatio(this->devicePixelRatioF()); + redraw_pixmap = true; + } + if (redraw_pixmap) + { #ifdef _WIN32 addresses << "GKS_CONID="; #endif @@ -309,63 +321,61 @@ void GRPlotWidget::paintEvent(QPaintEvent *event) setenv("GKS_CONID", addresses.str().c_str(), 1); #endif - painter.begin(pixmap); + painter.begin(&pixmap); painter.fillRect(0, 0, width(), height(), QColor("white")); draw(); painter.end(); + redraw_pixmap = false; } - if (pixmap) + + painter.begin(this); + painter.drawPixmap(0, 0, pixmap); + if (tooltip != nullptr) { - painter.begin(this); - painter.drawPixmap(0, 0, *pixmap); - if (tooltip != nullptr) + if (tooltip->x_px > 0 && tooltip->y_px > 0) { - if (tooltip->x_px > 0 && tooltip->y_px > 0) + QColor background(224, 224, 224, 128); + char c_info[BUFSIZ]; + QPainterPath triangle; + std::string x_label = tooltip->xlabel, y_label = tooltip->ylabel; + + if (util::startsWith(x_label, "$") && util::endsWith(x_label, "$")) + { + x_label = "x"; + } + if (util::startsWith(y_label, "$") && util::endsWith(y_label, "$")) { - QColor background(224, 224, 224, 128); - char c_info[BUFSIZ]; - QPainterPath triangle; - std::string x_label = tooltip->xlabel, y_label = tooltip->ylabel; - - if (util::startsWith(x_label, "$") && util::endsWith(x_label, "$")) - { - x_label = "x"; - } - if (util::startsWith(y_label, "$") && util::endsWith(y_label, "$")) - { - y_label = "y"; - } - std::snprintf(c_info, BUFSIZ, tooltipTemplate, tooltip->label, x_label.c_str(), tooltip->x, - y_label.c_str(), tooltip->y); - std::string info(c_info); - label.setDefaultStyleSheet(style); - label.setHtml(info.c_str()); - grm_args_values(args_, "kind", "s", &kind); - if (strcmp(kind, "heatmap") == 0 || strcmp(kind, "marginalheatmap") == 0) - { - background.setAlpha(224); - } - painter.fillRect(tooltip->x_px + 8, (int)(tooltip->y_px - label.size().height() / 2), - (int)label.size().width(), (int)label.size().height(), - QBrush(background, Qt::SolidPattern)); - - triangle.moveTo(tooltip->x_px, tooltip->y_px); - triangle.lineTo(tooltip->x_px + 8, tooltip->y_px + 6); - triangle.lineTo(tooltip->x_px + 8, tooltip->y_px - 6); - triangle.closeSubpath(); - background.setRgb(128, 128, 128, 128); - painter.fillPath(triangle, QBrush(background, Qt::SolidPattern)); - - painter.save(); - painter.translate(tooltip->x_px + 8, tooltip->y_px - label.size().height() / 2); - label.drawContents(&painter); - painter.restore(); + y_label = "y"; } + std::snprintf(c_info, BUFSIZ, tooltipTemplate, tooltip->label, x_label.c_str(), tooltip->x, y_label.c_str(), + tooltip->y); + std::string info(c_info); + label.setDefaultStyleSheet(style); + label.setHtml(info.c_str()); + grm_args_values(args_, "kind", "s", &kind); + if (strcmp(kind, "heatmap") == 0 || strcmp(kind, "marginalheatmap") == 0) + { + background.setAlpha(224); + } + painter.fillRect(tooltip->x_px + 8, (int)(tooltip->y_px - label.size().height() / 2), + (int)label.size().width(), (int)label.size().height(), QBrush(background, Qt::SolidPattern)); + + triangle.moveTo(tooltip->x_px, tooltip->y_px); + triangle.lineTo(tooltip->x_px + 8, tooltip->y_px + 6); + triangle.lineTo(tooltip->x_px + 8, tooltip->y_px - 6); + triangle.closeSubpath(); + background.setRgb(128, 128, 128, 128); + painter.fillPath(triangle, QBrush(background, Qt::SolidPattern)); + + painter.save(); + painter.translate(tooltip->x_px + 8, tooltip->y_px - label.size().height() / 2); + label.drawContents(&painter); + painter.restore(); } - painter.end(); } + painter.end(); } void GRPlotWidget::keyPressEvent(QKeyEvent *event) @@ -480,30 +490,26 @@ void GRPlotWidget::mouseReleaseEvent(QMouseEvent *event) void GRPlotWidget::resizeEvent(QResizeEvent *event) { grm_args_push(args_, "size", "dd", (double)event->size().width(), (double)event->size().height()); - grm_merge(args_); - - if (pixmap != nullptr) - { - delete pixmap; - pixmap = nullptr; - } + grm_merge_hold(args_); redraw(); } void GRPlotWidget::wheelEvent(QWheelEvent *event) { - int x, y; - getWheelPos(event, &x, &y); - - grm_args_t *args = grm_args_new(); - grm_args_push(args, "x", "i", x); - grm_args_push(args, "y", "i", y); - grm_args_push(args, "angle_delta", "d", (double)event->angleDelta().y()); - grm_input(args); - grm_args_delete(args); + if (event->angleDelta().y() != 0) + { + int x, y; + getWheelPos(event, &x, &y); + grm_args_t *args = grm_args_new(); + grm_args_push(args, "x", "i", x); + grm_args_push(args, "y", "i", y); + grm_args_push(args, "angle_delta", "d", (double)event->angleDelta().y()); + grm_input(args); + grm_args_delete(args); - redraw(); + redraw(); + } } void GRPlotWidget::mouseDoubleClickEvent(QMouseEvent *event) @@ -715,7 +721,7 @@ void GRPlotWidget::received(grm_args_t_wrapper args) { if (!isVisible()) { - show(); + window()->show(); } if (args_) { @@ -725,7 +731,7 @@ void GRPlotWidget::received(grm_args_t_wrapper args) args_ = args.get_wrapper(); grm_merge(args_); - reset_pixmap(); + redraw(); } void GRPlotWidget::closeEvent(QCloseEvent *event) @@ -746,26 +752,23 @@ void GRPlotWidget::showEvent(QShowEvent *) void GRPlotWidget::screenChanged() { - reset_pixmap(); + gr_configurews(); + redraw(); } -void GRPlotWidget::reset_pixmap() +void GRPlotWidget::size_callback(const grm_event_t *new_size_object) { - if (pixmap != nullptr) + // TODO: Get Plot ID + if (this->size() != QSize(new_size_object->size_event.width, new_size_object->size_event.height)) { - delete pixmap; - pixmap = nullptr; + this->window()->resize(new_size_object->size_event.width, new_size_object->size_event.height); } - - update(); } -void GRPlotWidget::size_callback(const grm_event_t *new_size_object) +void GRPlotWidget::cmd_callback(const grm_cmd_event_t *event) { - // TODO: Get Plot ID - if (this->size() != QSize(new_size_object->size_event.width, new_size_object->size_event.height)) + if (strcmp(event->cmd, "close") == 0) { - this->topLevelWidget()->show(); - this->resize(new_size_object->size_event.width, new_size_object->size_event.height); + QApplication::quit(); } } diff --git a/lib/grm/grplot/grplot_widget.hxx b/lib/grm/grplot/grplot_widget.hxx index 6eac1c8fe..7cd448394 100644 --- a/lib/grm/grplot/grplot_widget.hxx +++ b/lib/grm/grplot/grplot_widget.hxx @@ -77,7 +77,8 @@ private: QPoint pressed; QPoint anchor; }; - QPixmap *pixmap; + QPixmap pixmap; + bool redraw_pixmap; grm_args_t *args_; MouseState mouseState; QRubberBand *rubberBand; @@ -118,10 +119,10 @@ private: QAction *JpegAct; QAction *SvgAct; - void reset_pixmap(); void showEvent(QShowEvent *) override; void closeEvent(QCloseEvent *event) override; void size_callback(const grm_event_t *); + void cmd_callback(const grm_cmd_event_t *); }; #endif /* ifndef GRPLOT_WIDGET_H_INCLUDED */ diff --git a/lib/grm/grplot/qtterm/receiver_thread.cpp b/lib/grm/grplot/qtterm/receiver_thread.cpp index 1d557d070..b564e6ff6 100644 --- a/lib/grm/grplot/qtterm/receiver_thread.cpp +++ b/lib/grm/grplot/qtterm/receiver_thread.cpp @@ -4,7 +4,9 @@ void Receiver_Thread::run() { void *handle = nullptr; grm_args_t_wrapper args; - while (running) + bool ready = false; + + while (true) { fflush(stdout); if (handle == nullptr) @@ -15,19 +17,24 @@ void Receiver_Thread::run() qCritical() << "receiver could not be created"; qCritical() << "Retrying in 5 seconds"; QThread::sleep(5); + ready = false; continue; } } args.set_wrapper(grm_recv(handle, nullptr)); if (args.get_wrapper() == nullptr) { - qCritical() << "data could not be received from stream"; + if (ready) + { + qCritical() << "data could not be received from stream"; + } grm_close(handle); handle = nullptr; } else { emit resultReady(args); + ready = true; } } if (handle != nullptr) diff --git a/lib/grm/grplot/qtterm/receiver_thread.h b/lib/grm/grplot/qtterm/receiver_thread.h index 19432b51e..8042fe7bd 100644 --- a/lib/grm/grplot/qtterm/receiver_thread.h +++ b/lib/grm/grplot/qtterm/receiver_thread.h @@ -15,8 +15,6 @@ class Receiver_Thread : public QThread private: void run() override; - - bool running = true; }; #endif /* ifndef RECEIVER_THREAD_H_INCLUDED */ diff --git a/lib/grm/include/grm/event.h b/lib/grm/include/grm/event.h index 9911971d8..da481e65b 100644 --- a/lib/grm/include/grm/event.h +++ b/lib/grm/include/grm/event.h @@ -22,6 +22,7 @@ typedef enum GRM_EVENT_UPDATE_PLOT, GRM_EVENT_SIZE, GRM_EVENT_MERGE_END, + GRM_EVENT_CMD, _GRM_EVENT_TYPE_COUNT /* helper entry to store how many different event types exist */ } grm_event_type_t; @@ -51,12 +52,19 @@ typedef struct const char *identificator; } grm_merge_end_event_t; +typedef struct +{ + grm_event_type_t type; + const char *cmd; +} grm_cmd_event_t; + typedef union { grm_new_plot_event_t new_plot_event; grm_size_event_t size_event; grm_update_plot_event_t update_plot_event; grm_merge_end_event_t merge_end_event; + grm_cmd_event_t cmd_event; } grm_event_t; typedef void (*grm_event_callback_t)(const grm_event_t *); diff --git a/lib/grm/src/grm/event.c b/lib/grm/src/grm/event.c index 1e413e9d3..227f3aa02 100644 --- a/lib/grm/src/grm/event.c +++ b/lib/grm/src/grm/event.c @@ -249,6 +249,28 @@ err_t event_queue_enqueue_merge_end_event(event_queue_t *queue, const char *iden return error; } +err_t event_queue_enqueue_cmd_event(event_queue_t *queue, const char *cmd) +{ + grm_cmd_event_t *cmd_event = NULL; + err_t error = ERROR_NONE; + + cmd_event = malloc(sizeof(grm_cmd_event_t)); + error_cleanup_and_set_error_if(cmd_event == NULL, ERROR_MALLOC); + cmd_event->type = GRM_EVENT_CMD; + cmd_event->cmd = cmd; /* TODO: should `cmd` be copied? */ + error = event_reflist_enqueue(queue->queue, (grm_event_t *)cmd_event); + error_cleanup_if_error; + + return ERROR_NONE; + +error_cleanup: + if (cmd_event != NULL) + { + free(cmd_event); + } + + return error; +} #undef DEFINE_LIST_METHODS diff --git a/lib/grm/src/grm/event_int.h b/lib/grm/src/grm/event_int.h index 2cbd24818..38c314527 100644 --- a/lib/grm/src/grm/event_int.h +++ b/lib/grm/src/grm/event_int.h @@ -54,6 +54,7 @@ err_t event_queue_enqueue_new_plot_event(event_queue_t *queue, int plot_id); err_t event_queue_enqueue_update_plot_event(event_queue_t *queue, int plot_id); err_t event_queue_enqueue_size_event(event_queue_t *queue, int plot_id, int width, int height); err_t event_queue_enqueue_merge_end_event(event_queue_t *queue, const char *identificator); +err_t event_queue_enqueue_cmd_event(event_queue_t *queue, const char *cmd); #undef DECLARE_LIST_METHODS diff --git a/lib/grm/src/grm/json.c b/lib/grm/src/grm/json.c index 692f84ef1..0ecdc87bb 100644 --- a/lib/grm/src/grm/json.c +++ b/lib/grm/src/grm/json.c @@ -5,6 +5,7 @@ /* ######################### includes ############################################################################### */ #include +#include #include #include #include @@ -77,6 +78,7 @@ err_t fromjson_parse(grm_args_t *args, const char *json_string, fromjson_shared_ fromjson_state_t state; int allocated_shared_state_mem = 0; err_t error = ERROR_NONE; + char *saved_locale; state.datatype = JSON_DATATYPE_UNKNOWN; state.value_buffer = NULL; @@ -115,6 +117,9 @@ err_t fromjson_parse(grm_args_t *args, const char *json_string, fromjson_shared_ ++state.shared_state->json_ptr; } + saved_locale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + while (strchr("}", *state.shared_state->json_ptr) == NULL) { const char *current_key = NULL; @@ -192,6 +197,11 @@ err_t fromjson_parse(grm_args_t *args, const char *json_string, fromjson_shared_ free(shared_state); } + if (saved_locale) + { + setlocale(LC_NUMERIC, saved_locale); + } + return error; } diff --git a/lib/grm/src/grm/plot.cxx b/lib/grm/src/grm/plot.cxx index b2692a819..79772d502 100644 --- a/lib/grm/src/grm/plot.cxx +++ b/lib/grm/src/grm/plot.cxx @@ -8639,6 +8639,13 @@ int grm_merge_extended(const grm_args_t *args, int hold, const char *identificat } if (args != nullptr) { + const char *cmd = nullptr; + if (grm_args_values(args, "cmd", "s", &cmd)) + { + event_queue_enqueue_cmd_event(event_queue, cmd); + process_events(); + return 1; + } if (plot_merge_args(global_root_args, args, nullptr, nullptr, hold) != ERROR_NONE) { return 0;