/* * JoeyDev * Copyright (C) 2018-2023 Scott Duensing * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #pragma clang diagnostic push #pragma ide diagnostic ignored "cert-err34-c" // atoi warnings #include "common.h" #include "results.h" #include "utils.h" typedef struct ResultsDataS { WindowDataT windowData; ProjectDataT *project; GtkWidget *gridResults; GtkWidget *notebookResults; GtkWidget *lstDebug; GtkWidget *lstRelease; GtkWidget *lstRawDebug; GtkWidget *lstRawRelease; } ResultsDataT; static ResultsDataT **_resultWindows = NULL; EVENT void resultClicked(GtkButton *widget, gpointer userData); static void resultLoadRaw(gboolean release, int target, int arch, ResultsDataT *self); static void resultParseRaw(gboolean release, char *line, ResultsDataT *self); static void resultsUpdate(ResultsDataT *self); EVENT gboolean winBuildResultsClose(GtkWidget *object, gpointer userData); static void winBuildResultsDelete(gpointer userData); EVENT void resultClicked(GtkButton *widget, gpointer userData) { const char *name = gtk_widget_get_name(GTK_WIDGET(widget)); int t; int a; gboolean r; // ***TODO*** Highlight the button somehow so we know what we're viewing. // Extract button info from widget name. r = (name[0] == 'r'); t = atoi(&name[1]); a = atoi(strstr(name, "a") + 1); resultLoadRaw(r, t, a, userData); } static void resultLoadRaw(gboolean release, int target, int arch, ResultsDataT *self) { FILE *in; char *temp; char *line = NULL; size_t len = 0; TargetT *t; // Clear existing contents. utilClearContainer(GTK_CONTAINER(release ? self->lstRelease : self->lstDebug)); utilClearContainer(GTK_CONTAINER(release ? self->lstRawRelease : self->lstRawDebug)); // Load raw output results. t = self->project->targets[target]; temp = utilCreateString("%sresults%cbuild.%s.%s.%s", self->project->windowData.path, UTIL_PATH_CHAR, t->name, t->archs[arch]->name, release ? "release" : "debug"); in = fopen(temp, "rt"); if (in) { while (!feof(in)) { utilEnsureBufferSize((unsigned char **)&line, (int *)&len, 1024); // Not technically needed, but fixes a pointer warning from memmaker. while (utilGetLine(&line, &len, in) != -1) { if (strlen(line) > 0) line[strlen(line) - 1] = 0; utilAddTextToListBox(GTK_LIST_BOX(release ? self->lstRawRelease : self->lstRawDebug), line, FALSE); resultParseRaw(release, line, self); } } fclose(in); DEL(line); } DEL(temp); // Display appropriate tab. gtk_notebook_set_current_page(GTK_NOTEBOOK(self->notebookResults), release ? 1 : 0); } static void resultParseRaw(gboolean release, char *line, ResultsDataT *self) { GtkWidget *messages; GtkWidget *row; GtkWidget *box; GtkWidget *label; GtkWidget *button; char *temp; char *text = NULL; gboolean isWarning = FALSE; messages = release ? self->lstRelease : self->lstDebug; // ***TODO*** This is going to need to be a lot more complicated. // We need the line number and filename from multiple compilers. if (strstr(line, " warning: ") != NULL) { text = line; isWarning = TRUE; } if (strstr(line, " error: ") != NULL) { text = line; } if (text) { // New listbox row. row = gtk_list_box_row_new(); box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); gtk_widget_set_hexpand(box, TRUE); // Icon button. button = gtk_button_new_from_icon_name(isWarning ? "dialog-warning" : "dialog-error", GTK_ICON_SIZE_BUTTON); temp = utilCreateString("%c", isWarning ? 'w' : 'e'); // We store data about this button in its name. gtk_widget_set_name(button, temp); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0); DEL(temp); // Message. label = gtk_label_new(text); gtk_label_set_use_markup(GTK_LABEL(label), FALSE); gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); // Add to list. gtk_container_add(GTK_CONTAINER(row), box); gtk_list_box_insert(GTK_LIST_BOX(messages), row, -1); gtk_widget_show_all(GTK_WIDGET(messages)); } } static void resultsUpdate(ResultsDataT *self) { int i; int j; int x; int gridLine = 0; TargetT *t; GtkWidget *w; char *temp; int firstSystem = -1; gboolean debugFound; gboolean releaseFound; DIR *directory; struct dirent *entry; utilClearContainer(GTK_CONTAINER(self->gridResults)); //***TODO*** This should probably iterate over what files exist not what we expect. // We also need to figure out why the system list is empty some times and display an error/message. // Iterate over target data and load results. for (i=0; iproject->targets); i++) { t = self->project->targets[i]; for (j=0; jarchs); j++) { if (t->archs[j]->selected) { if (firstSystem < 0) { firstSystem = i; } // Add to grid. Name. w = gtk_label_new(t->longName); gtk_label_set_line_wrap(GTK_LABEL(w), FALSE); gtk_label_set_xalign(GTK_LABEL(w), 1.0f); gtk_grid_attach(GTK_GRID(self->gridResults), w, 0, gridLine, 1, 1); // Add to grid. Arch. w = gtk_label_new(t->archs[j]->name); gtk_label_set_line_wrap(GTK_LABEL(w), FALSE); gtk_label_set_xalign(GTK_LABEL(w), 1.0f); gtk_grid_attach(GTK_GRID(self->gridResults), w, 1, gridLine, 1, 1); // See if we got binaries for debug and release. debugFound = FALSE; releaseFound = FALSE; // This for loop is an ugly way to check both debug and release without making a function for the enclosed code. for (x=0; x<2; x++) { temp = utilCreateString("%sresults%c%s-%s%c%s", self->project->windowData.path, UTIL_PATH_CHAR, t->name, t->archs[j]->name, UTIL_PATH_CHAR, x == 0 ? "debug" : "release"); directory = opendir(temp); DEL(temp); if (directory) { // Anything in here that's not a directory is a binary. while ((entry = readdir(directory))) { if (entry->d_type == DT_REG) { if (x == 0) { debugFound = TRUE; } else { releaseFound = TRUE; } break; } } closedir(directory); } } // Add debug button. w = gtk_button_new_from_icon_name(debugFound ? "mail-mark-notjunk" : "mail-mark-junk", GTK_ICON_SIZE_BUTTON); temp = utilCreateString("d%da%d", i, j); // We store data about this button in its name. gtk_widget_set_name(w, temp); DEL(temp); gtk_grid_attach(GTK_GRID(self->gridResults), w, 2, gridLine, 1, 1); g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(resultClicked), self); // Add release button. w = gtk_button_new_from_icon_name(releaseFound ? "mail-mark-notjunk" : "mail-mark-junk", GTK_ICON_SIZE_BUTTON); temp = utilCreateString("r%da%d", i, j); // We store data about this button in its name. gtk_widget_set_name(w, temp); DEL(temp); gtk_grid_attach(GTK_GRID(self->gridResults), w, 3, gridLine, 1, 1); g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(resultClicked), self); // Next result! gridLine++; } } } // Did we load anything? if (gridLine > 0) { resultLoadRaw(FALSE, firstSystem, 0, self); resultLoadRaw(TRUE, firstSystem, 0, self); } } EVENT gboolean winBuildResultsClose(GtkWidget *object, gpointer userData) { // userData is not reliable due to util indirectly calling us. WindowDataT *self = (WindowDataT *)utilGetWindowData(object); (void)userData; winBuildResultsDelete(self); return FALSE; } void winBuildResultsCreate(ProjectDataT *project) { int i; ResultsDataT *self; char *widgetNames[] = { "winBuildResults", "gridBuildResults", "lstBuildMessagesDebug", "lstBuildMessagesRelease", "lstBuildOutputDebug", "lstBuildOutputRelease", "notebookResults", NULL };static GtkWidget **widgets[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; char *temp; // Is there already a results window open for this project? for (i=0; iproject == project) { // Focus the existing window. gtk_window_present_with_time(GTK_WINDOW(_resultWindows[i]->windowData.window), GDK_CURRENT_TIME); resultsUpdate(_resultWindows[i]); return; } } // Create new window and associate it with the project. self = NEW(ResultsDataT); self->windowData.closeWindow = winBuildResultsClose; self->project = project; project->buildResults = self; // Load widgets from XML. widgets[0] = &self->windowData.window; widgets[1] = &self->gridResults; widgets[2] = &self->lstDebug; widgets[3] = &self->lstRelease; widgets[4] = &self->lstRawDebug; widgets[5] = &self->lstRawRelease; widgets[6] = &self->notebookResults; utilGetWidgetsFromMemory("/com/kangaroopunch/joeydev/BuildResults.glade", widgetNames, widgets, self); // Register window. utilWindowRegister(self); // Adjust title. temp = utilCreateString("%s - %s", self->windowData.title, self->project->projectName); gtk_window_set_title(GTK_WINDOW(self->windowData.window), temp); DEL(temp); // Draw contents. resultsUpdate(self); // Show window. gtk_widget_show_all(self->windowData.window); // Remember window. arrput(_resultWindows, self); } static void winBuildResultsDelete(gpointer userData) { ResultsDataT *self = (ResultsDataT *)userData; int i; // Remove from open results window list. for (i=0; i