221 lines
5.9 KiB
C
221 lines
5.9 KiB
C
/*
|
|
* JoeyDev
|
|
* Copyright (C) 2018-2023 Scott Duensing <scott@kangaroopunch.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
|
|
#include <pthread.h>
|
|
|
|
#include "common.h"
|
|
#include "messages.h"
|
|
#include "utils.h"
|
|
|
|
|
|
static GtkWidget *_lstMessages = NULL;
|
|
static char **_pendingMessages = NULL;
|
|
static pthread_mutex_t _mtxMessage;
|
|
|
|
|
|
static gboolean messageScroll(gpointer userData);
|
|
static gboolean messagesUpdate(gpointer userData);
|
|
EVENT gboolean winMessagesClose(GtkWidget *object, gpointer userData);
|
|
static void winMessagesDelete(gpointer userData);
|
|
|
|
|
|
void message(MessageTypesT level, char *format, ...) {
|
|
va_list args;
|
|
char *string;
|
|
char *msg;
|
|
char *tok;
|
|
char *labels[MSG_COUNT] = {
|
|
"<span foreground=\"gray\"> Info:</span>",
|
|
"<span foreground=\"yellow\">Warning:</span>",
|
|
"<span foreground=\"red\"> Error:</span>",
|
|
"<span foreground=\"red\"><b> Severe:</b></span>"
|
|
};
|
|
|
|
// Construct message.
|
|
va_start(args, format);
|
|
msg = utilCreateStringVArgs(format, args);
|
|
va_end(args);
|
|
|
|
debug("%s\n", msg);
|
|
|
|
// Break multiline messages down into individual lines.
|
|
tok = strtok(msg, "\n");
|
|
while (tok != NULL) {
|
|
//***TODO*** Filter out things that could be mistaken as markup tags.
|
|
string = utilCreateString("<tt>%s %s</tt>", labels[level], tok);
|
|
|
|
// Save for UI thread. message() is not always called from the UI thread!
|
|
pthread_mutex_lock(&_mtxMessage);
|
|
arrput(_pendingMessages, string); // Do not free string.
|
|
pthread_mutex_unlock(&_mtxMessage);
|
|
|
|
tok = strtok(NULL, "\n");
|
|
}
|
|
|
|
DEL(msg);
|
|
}
|
|
|
|
|
|
static gboolean messageScroll(gpointer userData) {
|
|
GtkAdjustment *adjustment;
|
|
static int count = 100; // Try this many times to scroll.
|
|
|
|
(void)userData;
|
|
|
|
//***TODO*** This doesn't always scroll to the very end.
|
|
adjustment = gtk_list_box_get_adjustment(GTK_LIST_BOX(_lstMessages));
|
|
gtk_adjustment_set_value(adjustment, gtk_adjustment_get_upper(adjustment));
|
|
gtk_widget_show_all(_lstMessages);
|
|
utilForceUpdate();
|
|
|
|
count--;
|
|
if (count > 0) return G_SOURCE_CONTINUE;
|
|
|
|
count = 100;
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
|
|
void messageShutdown(void) {
|
|
g_idle_remove_by_data(messagesUpdate);
|
|
pthread_mutex_destroy(&_mtxMessage);
|
|
}
|
|
|
|
|
|
void messageStartup(void) {
|
|
if (pthread_mutex_init(&_mtxMessage, NULL) != 0) {
|
|
debug("Message mutex init failed!\n");
|
|
exit(1);
|
|
}
|
|
|
|
// Set up updates on the UI thread.
|
|
g_idle_add(messagesUpdate, messagesUpdate);
|
|
}
|
|
|
|
|
|
static gboolean messagesUpdate(gpointer userData) {
|
|
WindowDataT *self = NULL;
|
|
char *widgetNames[] = {
|
|
"winMessages",
|
|
"lstMessages",
|
|
NULL
|
|
};
|
|
GtkWidget **widgets[] = {
|
|
NULL,
|
|
&_lstMessages
|
|
};
|
|
GtkWidget *row;
|
|
GtkWidget *box;
|
|
GtkWidget *label;
|
|
char *string = NULL;
|
|
|
|
(void)userData;
|
|
|
|
// Lock and access the pending message list.
|
|
pthread_mutex_lock(&_mtxMessage);
|
|
if (arrlen(_pendingMessages) > 0) {
|
|
string = _pendingMessages[0]; // We just need the pointer.
|
|
arrdel(_pendingMessages, 0);
|
|
}
|
|
pthread_mutex_unlock(&_mtxMessage);
|
|
|
|
// Are there pending messages?
|
|
if (string != NULL) {
|
|
|
|
// Do we need to open the message window?
|
|
if (!_lstMessages) {
|
|
// Set up instance data. We only need WindowDataT since this is a "singleton" window.
|
|
self = NEW(WindowDataT);
|
|
self->closeWindow = winMessagesClose;
|
|
|
|
widgets[0] = &self->window;
|
|
utilGetWidgetsFromMemory("/com/kangaroopunch/joeydev/Messages.glade", widgetNames, widgets, self);
|
|
|
|
// Register window.
|
|
utilWindowRegister(self);
|
|
|
|
// Show window.
|
|
gtk_widget_show_all(self->window);
|
|
}
|
|
|
|
// Create new row with the message string.
|
|
row = gtk_list_box_row_new();
|
|
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_widget_set_hexpand(box, TRUE);
|
|
label = gtk_label_new(string);
|
|
gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
|
|
|
|
// Add new row to the message box.
|
|
gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
|
|
gtk_container_add(GTK_CONTAINER(row), box);
|
|
gtk_list_box_insert(GTK_LIST_BOX(_lstMessages), row, -1);
|
|
gtk_widget_show_all(_lstMessages);
|
|
utilForceUpdate();
|
|
|
|
// Scroll to show new row.
|
|
g_idle_add(messageScroll, NULL);
|
|
|
|
DEL(string); // Finally free the string allocated in message().
|
|
}
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
|
|
EVENT void toolMessagesClearClicked(GtkWidget *widget, gpointer userData) {
|
|
GList *children;
|
|
GList *iter;
|
|
|
|
(void)widget;
|
|
(void)userData;
|
|
|
|
children = gtk_container_get_children(GTK_CONTAINER(_lstMessages));
|
|
for (iter = children; iter != NULL; iter = g_list_next(iter)) {
|
|
gtk_widget_destroy(GTK_WIDGET(iter->data));
|
|
}
|
|
g_list_free(children);
|
|
}
|
|
|
|
|
|
EVENT gboolean winMessagesClose(GtkWidget *object, gpointer userData) {
|
|
// userData is not reliable due to util indirectly calling us.
|
|
WindowDataT *self = (WindowDataT *)utilGetWindowData(object);
|
|
|
|
(void)userData;
|
|
|
|
winMessagesDelete(self);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void winMessagesDelete(gpointer userData) {
|
|
// Delete any pending messages.
|
|
while (arrlen(_pendingMessages) > 0) {
|
|
DEL(_pendingMessages[0]);
|
|
arrdel(_pendingMessages, 0);
|
|
}
|
|
//ARRFREE(_pendingMessages); // This is making Memwatch mad.
|
|
|
|
utilWindowUnRegister(userData);
|
|
_lstMessages = NULL;
|
|
DEL(userData);
|
|
}
|