Error mem viewer almost finished
This commit is contained in:
		
							
								
								
									
										10
									
								
								error-mem-viewer/include/error-mem-viewer/crc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								error-mem-viewer/include/error-mem-viewer/crc.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
#ifndef _CRC_H_
 | 
			
		||||
#define _CRC_H_
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
uint32_t calculate_stm_crc(uint32_t *data, size_t len);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* _CRC_H_ */
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
#ifndef _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_
 | 
			
		||||
#define _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_
 | 
			
		||||
 | 
			
		||||
#include <gtk/gtk.h>
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
G_DECLARE_FINAL_TYPE(ErrMemViewAddressCellRenderer, err_mem_view_address_cell_renderer, ERR_MEM_VIEW, ADDRESS_CELL_RENDERER, GtkCellRendererText)
 | 
			
		||||
#define ERR_MEM_VIEW_TYPE_ADDRESS_CELL_RENDERER (err_mem_view_address_cell_renderer_get_type())
 | 
			
		||||
 | 
			
		||||
GtkCellRenderer *err_mem_view_address_cell_renderer_new();
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_ */
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
#ifndef _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_
 | 
			
		||||
#define _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_
 | 
			
		||||
 | 
			
		||||
#include <gtk/gtk.h>
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
G_DECLARE_FINAL_TYPE(ErrMemViewHexCellRenderer, err_mem_view_hex_cell_renderer, ERR_MEM_VIEW, HEX_CELL_RENDERER, GtkCellRendererText)
 | 
			
		||||
#define ERR_MEM_VIEW_TYPE_HEX_CELL_RENDERER (err_mem_view_address_cell_renderer_get_type())
 | 
			
		||||
 | 
			
		||||
GtkCellRenderer *err_mem_view_hex_cell_renderer_new();
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_ */
 | 
			
		||||
@@ -0,0 +1,57 @@
 | 
			
		||||
#ifndef _SAFETY_MEM_TYPES_H_
 | 
			
		||||
#define _SAFETY_MEM_TYPES_H_
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Magic number to signal a valid safety memory header.
 | 
			
		||||
 */
 | 
			
		||||
#define SAFETY_MEMORY_MAGIC 0x12AA5CB7
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Error memory NOP entry
 | 
			
		||||
 */
 | 
			
		||||
#define SAFETY_MEMORY_NOP_ENTRY 0xC1AA1222
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Safety memory header
 | 
			
		||||
 */
 | 
			
		||||
struct safety_memory_header {
 | 
			
		||||
        uint32_t magic;			/**< @brief Magic. Set to @ref SAFETY_MEMORY_MAGIC */
 | 
			
		||||
        uint32_t boot_status_offset;	/**< @brief Offset of the safety_memory_boot_status struct (in 32 bit words)*/
 | 
			
		||||
        uint32_t config_overrides_offset; /**< @brief Offset address of override entries */
 | 
			
		||||
        uint32_t config_overrides_len;	/**< @brief Length of override entries in words */
 | 
			
		||||
        uint32_t err_memory_offset;	/**< @brief Offset of the error memory */
 | 
			
		||||
        uint32_t err_memory_end;	/**< @brief End of the error memory. This points to the word after the error memory, containing the CRC of the whole backup RAM. */
 | 
			
		||||
        uint32_t crc;		/**< @brief  CRC of the header */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct safety_memory_boot_status {
 | 
			
		||||
        /**
 | 
			
		||||
         * @brief Reboot into the bootloader
 | 
			
		||||
         *
 | 
			
		||||
         * When this flag is set, the controller will load the bootloader to
 | 
			
		||||
         * memory and execute it.
 | 
			
		||||
         */
 | 
			
		||||
        uint32_t reboot_to_bootloader;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @brief Bootloader has updated the code
 | 
			
		||||
         *
 | 
			
		||||
         * This flag is set, if the firmware ahs been updated successfully
 | 
			
		||||
         */
 | 
			
		||||
        uint32_t code_updated;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @brief reset_from_panic
 | 
			
		||||
         *
 | 
			
		||||
         * This flag is set, when entering the panic mode.
 | 
			
		||||
         * Because the panic mode is reset by a watchdog reset,
 | 
			
		||||
         * this flag is needed, in order to ensure, that the panic is handled correcly after
 | 
			
		||||
         * the watchdog reset.
 | 
			
		||||
         */
 | 
			
		||||
        uint32_t reset_from_panic;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* _SAFETY_MEM_TYPES_H_ */
 | 
			
		||||
@@ -5,6 +5,70 @@
 | 
			
		||||
  <object class="GtkWindow" id="main-window">
 | 
			
		||||
    <property name="name">main-window</property>
 | 
			
		||||
    <property name="can-focus">False</property>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkBox">
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can-focus">False</property>
 | 
			
		||||
        <property name="orientation">vertical</property>
 | 
			
		||||
        <child>
 | 
			
		||||
          <object class="GtkInfoBar" id="info-bar">
 | 
			
		||||
            <property name="visible">True</property>
 | 
			
		||||
            <property name="can-focus">False</property>
 | 
			
		||||
            <property name="message-type">warning</property>
 | 
			
		||||
            <property name="show-close-button">True</property>
 | 
			
		||||
            <property name="revealed">False</property>
 | 
			
		||||
            <signal name="response" handler="info_bar_close_cb" swapped="no"/>
 | 
			
		||||
            <child internal-child="action_area">
 | 
			
		||||
              <object class="GtkButtonBox">
 | 
			
		||||
                <property name="can-focus">False</property>
 | 
			
		||||
                <property name="spacing">6</property>
 | 
			
		||||
                <property name="layout-style">end</property>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <placeholder/>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <placeholder/>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <placeholder/>
 | 
			
		||||
                </child>
 | 
			
		||||
              </object>
 | 
			
		||||
              <packing>
 | 
			
		||||
                <property name="expand">False</property>
 | 
			
		||||
                <property name="fill">False</property>
 | 
			
		||||
                <property name="position">0</property>
 | 
			
		||||
              </packing>
 | 
			
		||||
            </child>
 | 
			
		||||
            <child internal-child="content_area">
 | 
			
		||||
              <object class="GtkBox">
 | 
			
		||||
                <property name="can-focus">False</property>
 | 
			
		||||
                <property name="spacing">16</property>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <placeholder/>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <placeholder/>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <placeholder/>
 | 
			
		||||
                </child>
 | 
			
		||||
              </object>
 | 
			
		||||
              <packing>
 | 
			
		||||
                <property name="expand">False</property>
 | 
			
		||||
                <property name="fill">False</property>
 | 
			
		||||
                <property name="position">0</property>
 | 
			
		||||
              </packing>
 | 
			
		||||
            </child>
 | 
			
		||||
            <child>
 | 
			
		||||
              <placeholder/>
 | 
			
		||||
            </child>
 | 
			
		||||
          </object>
 | 
			
		||||
          <packing>
 | 
			
		||||
            <property name="expand">False</property>
 | 
			
		||||
            <property name="fill">True</property>
 | 
			
		||||
            <property name="position">0</property>
 | 
			
		||||
          </packing>
 | 
			
		||||
        </child>
 | 
			
		||||
        <child>
 | 
			
		||||
          <object class="GtkScrolledWindow">
 | 
			
		||||
            <property name="visible">True</property>
 | 
			
		||||
@@ -20,6 +84,13 @@
 | 
			
		||||
              </object>
 | 
			
		||||
            </child>
 | 
			
		||||
          </object>
 | 
			
		||||
          <packing>
 | 
			
		||||
            <property name="expand">True</property>
 | 
			
		||||
            <property name="fill">True</property>
 | 
			
		||||
            <property name="position">1</property>
 | 
			
		||||
          </packing>
 | 
			
		||||
        </child>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
    <child type="titlebar">
 | 
			
		||||
      <object class="GtkHeaderBar" id="header-bar">
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								error-mem-viewer/src/crc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								error-mem-viewer/src/crc.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#include <error-mem-viewer/crc.h>
 | 
			
		||||
 | 
			
		||||
uint32_t do_crc(uint32_t init, uint32_t data)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t crc = init;
 | 
			
		||||
	uint32_t cnt;
 | 
			
		||||
 | 
			
		||||
	for(cnt=0; cnt < 32; cnt++)
 | 
			
		||||
	{
 | 
			
		||||
		crc = ((int32_t)(crc ^ data))<0 ? (crc << 1) ^ 0x04C11DB7 : crc << 1;
 | 
			
		||||
		data <<=1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return crc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t calculate_stm_crc(uint32_t *data, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t crc = ~0U;
 | 
			
		||||
 | 
			
		||||
	while(len--) {
 | 
			
		||||
		crc = do_crc(crc, *data++);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return crc;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
#include <error-mem-viewer/err-mem-viewer-address-cell-renderer.h>
 | 
			
		||||
#include <error-mem-viewer/err-mem-viewer-hex-cell-renderer.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
struct _ErrMemViewAddressCellRenderer {
 | 
			
		||||
struct _ErrMemViewHexCellRenderer {
 | 
			
		||||
	GtkCellRendererText base;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -10,9 +10,9 @@ enum {
 | 
			
		||||
	PROP_COUNT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(ErrMemViewAddressCellRenderer, err_mem_view_address_cell_renderer, GTK_TYPE_CELL_RENDERER_TEXT)
 | 
			
		||||
G_DEFINE_TYPE(ErrMemViewHexCellRenderer, err_mem_view_address_cell_renderer, GTK_TYPE_CELL_RENDERER_TEXT)
 | 
			
		||||
 | 
			
		||||
static void err_mem_view_address_cell_renderer_init(ErrMemViewAddressCellRenderer *self)
 | 
			
		||||
static void err_mem_view_address_cell_renderer_init(ErrMemViewHexCellRenderer *self)
 | 
			
		||||
{
 | 
			
		||||
	(void)self;
 | 
			
		||||
}
 | 
			
		||||
@@ -28,7 +28,7 @@ static void convert_gvalue_uint_to_string(const GValue *in, GValue *out)
 | 
			
		||||
	g_value_init(out, G_TYPE_STRING);
 | 
			
		||||
 | 
			
		||||
	val = g_value_get_uint(in);
 | 
			
		||||
	snprintf(text, sizeof(text), "0x%08x", val);
 | 
			
		||||
	snprintf(text, sizeof(text), "0x%08X", val);
 | 
			
		||||
	g_value_set_string(out, text);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -64,7 +64,7 @@ static void err_mem_view_address_cell_renderer_get_property(GObject      *object
 | 
			
		||||
 | 
			
		||||
static GParamSpec *properties[PROP_COUNT];
 | 
			
		||||
 | 
			
		||||
void err_mem_view_address_cell_renderer_class_init(ErrMemViewAddressCellRendererClass *klass)
 | 
			
		||||
void err_mem_view_address_cell_renderer_class_init(ErrMemViewHexCellRendererClass *klass)
 | 
			
		||||
{
 | 
			
		||||
	GObjectClass *oclass = G_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
@@ -75,7 +75,7 @@ void err_mem_view_address_cell_renderer_class_init(ErrMemViewAddressCellRenderer
 | 
			
		||||
	g_object_class_install_properties(oclass, PROP_COUNT, properties);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GtkCellRenderer *err_mem_view_address_cell_renderer_new()
 | 
			
		||||
GtkCellRenderer *err_mem_view_hex_cell_renderer_new()
 | 
			
		||||
{
 | 
			
		||||
	return GTK_CELL_RENDERER(g_object_new(ERR_MEM_VIEW_TYPE_ADDRESS_CELL_RENDERER, NULL));
 | 
			
		||||
	return GTK_CELL_RENDERER(g_object_new(ERR_MEM_VIEW_TYPE_HEX_CELL_RENDERER, NULL));
 | 
			
		||||
}
 | 
			
		||||
@@ -3,15 +3,18 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
 | 
			
		||||
#include <error-mem-viewer/err-mem-viewer-address-cell-renderer.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <error-mem-viewer/err-mem-viewer-hex-cell-renderer.h>
 | 
			
		||||
#include <error-mem-viewer/safety-memory-types.h>
 | 
			
		||||
#include <error-mem-viewer/crc.h>
 | 
			
		||||
 | 
			
		||||
enum memory_view_columns {
 | 
			
		||||
	MEM_VIEW_COL_ADDRESS = 0,
 | 
			
		||||
	MEM_VIEW_COL_DATA,
 | 
			
		||||
	MEM_VIEW_COL_INTERPRETATION,
 | 
			
		||||
	MEM_VIEW_COLOR,
 | 
			
		||||
	MEM_VIEW_COLUMN_COUNT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -19,87 +22,427 @@ struct application_data {
 | 
			
		||||
	GtkWidget *borrowed_main_window;
 | 
			
		||||
	GtkTreeView *borrowed_tree_view;
 | 
			
		||||
	GtkHeaderBar *borrowed_header_bar;
 | 
			
		||||
	GtkInfoBar *borrowed_info_bar;
 | 
			
		||||
	const char *error_memory_data;
 | 
			
		||||
	size_t file_size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void tree_view_show_error_memory(GtkTreeView *tree_view, const char *memory, size_t len)
 | 
			
		||||
enum entry_state {
 | 
			
		||||
	ENTRY_STATE_VALID = 0,
 | 
			
		||||
	ENTRY_STATE_INVALID,
 | 
			
		||||
	ENTRY_STATE_NOT_PARSED,
 | 
			
		||||
	ENTRY_STATE_BLOCK
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void append_data(GtkTreeStore *store, GtkTreeIter *parent, GtkTreeIter *iter, GString *interpretation, enum entry_state state, uint32_t addr, uint32_t val)
 | 
			
		||||
{
 | 
			
		||||
	GtkListStore *store;
 | 
			
		||||
	GtkTreeIter _iter;
 | 
			
		||||
	GtkTreeIter *iter = &_iter;
 | 
			
		||||
	const GdkRGBA invalid_color = {
 | 
			
		||||
		.red = 1.0,
 | 
			
		||||
		.alpha = 1.0,
 | 
			
		||||
		.green = 0.0,
 | 
			
		||||
		.blue = 0.0,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	store = GTK_LIST_STORE(gtk_tree_view_get_model(tree_view));
 | 
			
		||||
	if (store) {
 | 
			
		||||
		for (unsigned int i = 0; i < 0xFFFF; i+=4) {
 | 
			
		||||
			gtk_list_store_append(store, iter);
 | 
			
		||||
			gtk_list_store_set(store, iter, MEM_VIEW_COL_ADDRESS, i, MEM_VIEW_COL_INTERPRETATION, "Test", MEM_VIEW_COL_DATA, i % 12, -1);
 | 
			
		||||
		}
 | 
			
		||||
	const GdkRGBA valid_color = {
 | 
			
		||||
		.red = 0.0,
 | 
			
		||||
		.alpha = 1.0,
 | 
			
		||||
		.green = 1.0,
 | 
			
		||||
		.blue = 0.0,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const GdkRGBA warn_color = {
 | 
			
		||||
		.red = 1.0,
 | 
			
		||||
		.alpha = 1.0,
 | 
			
		||||
		.green = 1.0,
 | 
			
		||||
		.blue = 0.0,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const GdkRGBA block_color = {
 | 
			
		||||
		.red = 1.0,
 | 
			
		||||
		.alpha = 1.0,
 | 
			
		||||
		.green = 0.3,
 | 
			
		||||
		.blue = 0.5,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const GdkRGBA *col;
 | 
			
		||||
 | 
			
		||||
	switch (state) {
 | 
			
		||||
	case ENTRY_STATE_VALID:
 | 
			
		||||
		col = &valid_color;
 | 
			
		||||
		break;
 | 
			
		||||
	case ENTRY_STATE_NOT_PARSED:
 | 
			
		||||
		col = &warn_color;
 | 
			
		||||
		break;
 | 
			
		||||
	case ENTRY_STATE_BLOCK:
 | 
			
		||||
		col = &block_color;
 | 
			
		||||
		break;
 | 
			
		||||
	case ENTRY_STATE_INVALID:
 | 
			
		||||
	default:
 | 
			
		||||
		col = &invalid_color;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gtk_tree_store_append(store, iter, parent);
 | 
			
		||||
	gtk_tree_store_set(store, iter, MEM_VIEW_COL_ADDRESS, addr, MEM_VIEW_COLOR, col, MEM_VIEW_COL_INTERPRETATION, (interpretation ? interpretation->str : ""), MEM_VIEW_COL_DATA, val, -1);
 | 
			
		||||
	if (interpretation)
 | 
			
		||||
		g_string_free(interpretation, TRUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void info_bar_show_message(GtkInfoBar *info_bar, GtkMessageType type, const char *format, ...)
 | 
			
		||||
{
 | 
			
		||||
	GString *string;
 | 
			
		||||
	va_list args;
 | 
			
		||||
	GtkWidget *content;
 | 
			
		||||
	GList *children, *iter;
 | 
			
		||||
	GtkWidget *label;
 | 
			
		||||
 | 
			
		||||
	va_start(args, format);
 | 
			
		||||
	string = g_string_new(NULL);
 | 
			
		||||
 | 
			
		||||
	g_string_vprintf(string, format, args);
 | 
			
		||||
	va_end(args);
 | 
			
		||||
 | 
			
		||||
	content = gtk_info_bar_get_content_area(info_bar);
 | 
			
		||||
	children = gtk_container_get_children(GTK_CONTAINER(content));
 | 
			
		||||
	for (iter = children; iter; iter = g_list_next(iter))
 | 
			
		||||
		gtk_widget_destroy(GTK_WIDGET(iter->data));
 | 
			
		||||
	g_list_free(children);
 | 
			
		||||
 | 
			
		||||
	label = gtk_label_new(string->str);
 | 
			
		||||
	gtk_container_add(GTK_CONTAINER(content), label);
 | 
			
		||||
	gtk_widget_show(label);
 | 
			
		||||
	gtk_info_bar_set_message_type(info_bar, type);
 | 
			
		||||
	gtk_widget_show(GTK_WIDGET(info_bar));
 | 
			
		||||
	gtk_info_bar_set_revealed(info_bar, TRUE);
 | 
			
		||||
 | 
			
		||||
	g_string_free(string, TRUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool check_err_mem_header(struct safety_memory_header *header, uint32_t *expected)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t crc;
 | 
			
		||||
 | 
			
		||||
	crc = calculate_stm_crc((uint32_t *)header, sizeof(*header) / sizeof(uint32_t) - 1);
 | 
			
		||||
	if (expected)
 | 
			
		||||
		*expected = crc;
 | 
			
		||||
	if (crc == header->crc)
 | 
			
		||||
		return true;
 | 
			
		||||
	else
 | 
			
		||||
		return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GString *new_string_printf(const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	GString *string;
 | 
			
		||||
	va_list args;
 | 
			
		||||
 | 
			
		||||
	va_start(args, fmt);
 | 
			
		||||
 | 
			
		||||
	string = g_string_new(NULL);
 | 
			
		||||
	g_string_vprintf(string, fmt, args);
 | 
			
		||||
 | 
			
		||||
	va_end(args);
 | 
			
		||||
 | 
			
		||||
	return string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void show_error_memory(GtkTreeView *tree_view, const unsigned char *memory, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	GtkTreeStore *store;
 | 
			
		||||
	GtkTreeIter iter;
 | 
			
		||||
	GtkTreeIter iter2;
 | 
			
		||||
	GtkTreeIter *parent_iter, *child_iter;
 | 
			
		||||
	enum entry_state state;
 | 
			
		||||
	GString *interpret;
 | 
			
		||||
	bool valid;
 | 
			
		||||
	bool analyze_further = true;
 | 
			
		||||
	struct safety_memory_header header;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	uint32_t dat, expected_value;
 | 
			
		||||
 | 
			
		||||
	store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
 | 
			
		||||
	gtk_tree_store_clear(store);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < len / 4; i++) {
 | 
			
		||||
		valid = false;
 | 
			
		||||
		interpret = NULL;
 | 
			
		||||
		dat = memory[i*4];
 | 
			
		||||
		dat |= ((uint32_t)memory[i*4+1]) << 8;
 | 
			
		||||
		dat |= ((uint32_t)memory[i*4+2]) << 16;
 | 
			
		||||
		dat |= ((uint32_t)memory[i*4+3]) << 24;
 | 
			
		||||
 | 
			
		||||
		state = ENTRY_STATE_NOT_PARSED;
 | 
			
		||||
 | 
			
		||||
		if (!analyze_further) {
 | 
			
		||||
			interpret = g_string_new("Not analyzed due to previous error");
 | 
			
		||||
			state = ENTRY_STATE_NOT_PARSED;
 | 
			
		||||
			parent_iter = NULL;
 | 
			
		||||
			child_iter = &iter;
 | 
			
		||||
			goto print;
 | 
			
		||||
		}
 | 
			
		||||
		/* Parse header */
 | 
			
		||||
		switch (i) {
 | 
			
		||||
		case 0: /* Magic */
 | 
			
		||||
			parent_iter = NULL;
 | 
			
		||||
			child_iter = &iter;
 | 
			
		||||
			append_data(store, parent_iter, child_iter, new_string_printf("Header"), ENTRY_STATE_BLOCK, i * 4, dat);
 | 
			
		||||
			parent_iter = child_iter;
 | 
			
		||||
			child_iter = &iter2;
 | 
			
		||||
			if (dat == SAFETY_MEMORY_MAGIC) {
 | 
			
		||||
				valid = true;
 | 
			
		||||
				interpret = g_string_new("Valid Header Magic");
 | 
			
		||||
			} else {
 | 
			
		||||
				interpret = g_string_new("Invalid Header Magic");
 | 
			
		||||
				analyze_further = false;
 | 
			
		||||
			}
 | 
			
		||||
			header.magic = dat;
 | 
			
		||||
			break;
 | 
			
		||||
		case 1:
 | 
			
		||||
			header.boot_status_offset = dat;
 | 
			
		||||
 | 
			
		||||
			valid = true;
 | 
			
		||||
			interpret = new_string_printf("Boot status offset addr: %u", dat);
 | 
			
		||||
			break;
 | 
			
		||||
		case 2:
 | 
			
		||||
			header.config_overrides_offset = dat;
 | 
			
		||||
			interpret = new_string_printf("Config override offset addr: %u", dat);
 | 
			
		||||
			valid = true;
 | 
			
		||||
			break;
 | 
			
		||||
		case 3:
 | 
			
		||||
			header.config_overrides_len = dat;
 | 
			
		||||
			interpret = new_string_printf("Config override length: %u", dat);
 | 
			
		||||
			valid = true;
 | 
			
		||||
			break;
 | 
			
		||||
		case 4:
 | 
			
		||||
			header.err_memory_offset = dat;
 | 
			
		||||
			interpret = new_string_printf("Error memory offset addr: %u", dat);
 | 
			
		||||
			valid = true;
 | 
			
		||||
			break;
 | 
			
		||||
		case 5:
 | 
			
		||||
			header.err_memory_end = dat;
 | 
			
		||||
			interpret = new_string_printf("Error memory end ptr: %u", dat);
 | 
			
		||||
			valid = true;
 | 
			
		||||
			break;
 | 
			
		||||
		case 6:
 | 
			
		||||
			header.crc = dat;
 | 
			
		||||
			valid = check_err_mem_header(&header, &expected_value);
 | 
			
		||||
			if (valid) {
 | 
			
		||||
				interpret = new_string_printf("Header CRC: 0x%08X", dat);
 | 
			
		||||
			} else {
 | 
			
		||||
 | 
			
		||||
				interpret = new_string_printf("Invalid CRC: 0x%08X | Expected: 0x%08X", dat, expected_value);
 | 
			
		||||
				analyze_further = false;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		if (i <= 6) {
 | 
			
		||||
			state = ENTRY_STATE_INVALID;
 | 
			
		||||
			goto print;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Check if we're in the boot status structure */
 | 
			
		||||
		if (i >= header.boot_status_offset && i < header.boot_status_offset + sizeof(struct safety_memory_boot_status) / 4) {
 | 
			
		||||
			if (i == header.boot_status_offset) {
 | 
			
		||||
				/* First entry of boot status struct */
 | 
			
		||||
				parent_iter = NULL;
 | 
			
		||||
				child_iter = &iter;
 | 
			
		||||
				interpret = new_string_printf("Boot Status Struct");
 | 
			
		||||
				append_data(store, parent_iter, child_iter, interpret, ENTRY_STATE_BLOCK, i * 4, dat);
 | 
			
		||||
				parent_iter = &iter;
 | 
			
		||||
				child_iter = &iter2;
 | 
			
		||||
				interpret = NULL;
 | 
			
		||||
			}
 | 
			
		||||
			switch (i - header.boot_status_offset) {
 | 
			
		||||
			case 0:
 | 
			
		||||
				interpret = new_string_printf("%s into bootloader", (dat ? "Boot" : "Do not boot"));
 | 
			
		||||
				valid = true;
 | 
			
		||||
				break;
 | 
			
		||||
			case 1:
 | 
			
		||||
				interpret = new_string_printf("Code %s been updated", (dat ? "has" : "hasn't"));
 | 
			
		||||
				valid = true;
 | 
			
		||||
				break;
 | 
			
		||||
			case 2:
 | 
			
		||||
				interpret = new_string_printf("Panic %s", (dat ? "occured" : "did not occur"));
 | 
			
		||||
				valid = true;
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				valid = false;
 | 
			
		||||
				interpret = new_string_printf("Value undefined!");
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			state = ENTRY_STATE_INVALID;
 | 
			
		||||
			goto print;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (i >= header.config_overrides_offset && i < (header.config_overrides_offset + header.config_overrides_len)) {
 | 
			
		||||
			if (i == header.config_overrides_offset) {
 | 
			
		||||
				parent_iter  = NULL;
 | 
			
		||||
				child_iter = &iter;
 | 
			
		||||
				append_data(store, parent_iter, child_iter,
 | 
			
		||||
					    new_string_printf("Config Overrides"),
 | 
			
		||||
					    ENTRY_STATE_BLOCK, i * 4, dat);
 | 
			
		||||
				parent_iter = child_iter;
 | 
			
		||||
				child_iter = &iter2;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			interpret = new_string_printf("Config Overrides not yet implemented");
 | 
			
		||||
			valid = true;
 | 
			
		||||
			state = ENTRY_STATE_INVALID;
 | 
			
		||||
			goto print;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (i >= header.err_memory_offset && i < header.err_memory_end) {
 | 
			
		||||
			if (i == header.err_memory_offset) {
 | 
			
		||||
				parent_iter  = NULL;
 | 
			
		||||
				child_iter = &iter;
 | 
			
		||||
				append_data(store, parent_iter, child_iter,
 | 
			
		||||
					    new_string_printf("Error Memory (length: %d)", header.err_memory_end - header.err_memory_offset),
 | 
			
		||||
					    ENTRY_STATE_BLOCK, i * 4, dat);
 | 
			
		||||
				parent_iter = child_iter;
 | 
			
		||||
				child_iter = &iter2;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch (dat) {
 | 
			
		||||
			case SAFETY_MEMORY_NOP_ENTRY:
 | 
			
		||||
				valid = true;
 | 
			
		||||
				interpret = new_string_printf("Error Memory NOP");
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				if ((dat & 0xFFU) == 0x51U) {
 | 
			
		||||
					valid = true;
 | 
			
		||||
					interpret = new_string_printf("Err memory Entry");
 | 
			
		||||
				} else
 | 
			
		||||
					interpret = new_string_printf("Invalid error memory entry");
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			state = ENTRY_STATE_INVALID;
 | 
			
		||||
			goto print;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (i == header.err_memory_end) {
 | 
			
		||||
			expected_value = calculate_stm_crc((uint32_t *)memory, header.err_memory_end);
 | 
			
		||||
			if (expected_value == dat) {
 | 
			
		||||
				interpret = new_string_printf("Valid Memory CRC");
 | 
			
		||||
				valid = true;
 | 
			
		||||
			} else {
 | 
			
		||||
				interpret = new_string_printf("Invalid CRC. Expected: 0x%08X", expected_value);
 | 
			
		||||
			}
 | 
			
		||||
			state = ENTRY_STATE_INVALID;
 | 
			
		||||
			parent_iter = NULL;
 | 
			
		||||
			child_iter = &iter;
 | 
			
		||||
			goto print;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		parent_iter = NULL;
 | 
			
		||||
		child_iter = &iter;
 | 
			
		||||
print:
 | 
			
		||||
		if (state != ENTRY_STATE_NOT_PARSED) {
 | 
			
		||||
			state = (valid ? ENTRY_STATE_VALID : ENTRY_STATE_INVALID);
 | 
			
		||||
		}
 | 
			
		||||
		append_data(store, parent_iter, child_iter, interpret, state, i * 4, dat);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (!store)
 | 
			
		||||
		return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void setup_tree_view(GtkTreeView *tree_view)
 | 
			
		||||
{
 | 
			
		||||
	GtkListStore *list_store;
 | 
			
		||||
	GtkTreeStore *tree_store;
 | 
			
		||||
	GtkCellRenderer *string_renderer;
 | 
			
		||||
	GtkCellRenderer *hex_renderer;
 | 
			
		||||
	GtkTreeViewColumn *column;
 | 
			
		||||
 | 
			
		||||
	g_return_if_fail(GTK_IS_TREE_VIEW(tree_view));
 | 
			
		||||
 | 
			
		||||
	list_store = gtk_list_store_new(MEM_VIEW_COLUMN_COUNT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
 | 
			
		||||
	gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(list_store));
 | 
			
		||||
	tree_store = gtk_tree_store_new(MEM_VIEW_COLUMN_COUNT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING, GDK_TYPE_RGBA);
 | 
			
		||||
	gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(tree_store));
 | 
			
		||||
 | 
			
		||||
	string_renderer = gtk_cell_renderer_text_new();
 | 
			
		||||
	hex_renderer = err_mem_view_address_cell_renderer_new();
 | 
			
		||||
	hex_renderer = err_mem_view_hex_cell_renderer_new();
 | 
			
		||||
 | 
			
		||||
	column = gtk_tree_view_column_new_with_attributes("Address", hex_renderer, "hex-num", MEM_VIEW_COL_ADDRESS, NULL);
 | 
			
		||||
	column = gtk_tree_view_column_new_with_attributes("Address", hex_renderer, "hex-num", MEM_VIEW_COL_ADDRESS, "foreground-rgba", MEM_VIEW_COLOR, NULL);
 | 
			
		||||
	gtk_tree_view_append_column(tree_view, column);
 | 
			
		||||
	column = gtk_tree_view_column_new_with_attributes("Data", hex_renderer, "hex-num", MEM_VIEW_COL_DATA, NULL);
 | 
			
		||||
	column = gtk_tree_view_column_new_with_attributes("Data", hex_renderer, "hex-num", MEM_VIEW_COL_DATA, "foreground-rgba", MEM_VIEW_COLOR, NULL);
 | 
			
		||||
	gtk_tree_view_append_column(tree_view, column);
 | 
			
		||||
	column = gtk_tree_view_column_new_with_attributes("Interpretation", string_renderer, "text", MEM_VIEW_COL_INTERPRETATION, NULL);
 | 
			
		||||
	column = gtk_tree_view_column_new_with_attributes("Interpretation", string_renderer, "text", MEM_VIEW_COL_INTERPRETATION, "foreground-rgba", MEM_VIEW_COLOR, NULL);
 | 
			
		||||
	gtk_tree_view_append_column(tree_view, column);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t get_file_size(int file)
 | 
			
		||||
static ptrdiff_t get_file_size(const char *filename)
 | 
			
		||||
{
 | 
			
		||||
	off_t size;
 | 
			
		||||
	struct stat file_stat;
 | 
			
		||||
	int res;
 | 
			
		||||
	ptrdiff_t size;
 | 
			
		||||
 | 
			
		||||
	res = stat(filename, &file_stat);
 | 
			
		||||
 | 
			
		||||
	if (!file)
 | 
			
		||||
		return -1L;
 | 
			
		||||
	if (res)
 | 
			
		||||
		size = -1UL;
 | 
			
		||||
	else
 | 
			
		||||
		size = (ptrdiff_t)file_stat.st_size;
 | 
			
		||||
 | 
			
		||||
	size = lseek(file, 0, SEEK_END);
 | 
			
		||||
	if (size == (off_t)-1)
 | 
			
		||||
		return -1;
 | 
			
		||||
	return size;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	return (int64_t)size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *map_file_to_memory(const char *name, size_t *detected_size)
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Loads a file to memory and decodes it using base64
 | 
			
		||||
 * @param name Filename
 | 
			
		||||
 * @param detected_size detected Size of the ouput generated
 | 
			
		||||
 * @return Buffer of decoded data with size \p detected_size, NULL in case of an error.
 | 
			
		||||
 * @note Return buffer is allocated on heap. Use free() to free the memory.
 | 
			
		||||
 */
 | 
			
		||||
static char *load_file_to_memory(const char *name, size_t *detected_size)
 | 
			
		||||
{
 | 
			
		||||
	const char *ret = NULL;
 | 
			
		||||
	int file;
 | 
			
		||||
	int64_t file_size;
 | 
			
		||||
	ptrdiff_t size;
 | 
			
		||||
	FILE *f;
 | 
			
		||||
	char *ret = NULL;
 | 
			
		||||
	size_t read_bytes;
 | 
			
		||||
	gsize decoded_size;
 | 
			
		||||
 | 
			
		||||
	file = open(name, O_RDONLY);
 | 
			
		||||
	if (file < 0)
 | 
			
		||||
	size = get_file_size(name);
 | 
			
		||||
	if (size <= 0)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	file_size = get_file_size(file);
 | 
			
		||||
	if (file_size < 0)
 | 
			
		||||
		goto exit;
 | 
			
		||||
	f = fopen(name, "rb");
 | 
			
		||||
	ret = (char *)malloc(size + 1);
 | 
			
		||||
	if (!ret)
 | 
			
		||||
		goto exit_close_file;
 | 
			
		||||
 | 
			
		||||
	read_bytes = fread(ret, 1UL, (size_t)size, f);
 | 
			
		||||
	if (read_bytes != (size_t)size) {
 | 
			
		||||
		free(ret);
 | 
			
		||||
		ret = NULL;
 | 
			
		||||
		goto exit_close_file;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret[size] = '\0';
 | 
			
		||||
 | 
			
		||||
	g_base64_decode_inplace(ret, &decoded_size);
 | 
			
		||||
	if (decoded_size == 0) {
 | 
			
		||||
		free(ret);
 | 
			
		||||
		ret = NULL;
 | 
			
		||||
		goto exit_close_file;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (detected_size)
 | 
			
		||||
		*detected_size = (size_t)file_size;
 | 
			
		||||
 | 
			
		||||
	ret = (const char *)mmap(NULL, (size_t)file_size, PROT_READ, MAP_SHARED, file, (off_t)0L);
 | 
			
		||||
		*detected_size = (size_t)decoded_size;
 | 
			
		||||
exit_close_file:
 | 
			
		||||
	fclose(f);
 | 
			
		||||
exit:
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
G_MODULE_EXPORT
 | 
			
		||||
void info_bar_close_cb(GtkInfoBar *info_bar, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	(void)user_data;
 | 
			
		||||
 | 
			
		||||
	gtk_info_bar_set_revealed(info_bar, FALSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
G_MODULE_EXPORT
 | 
			
		||||
void open_button_clicked_cb(GtkButton *button, gpointer *user_data)
 | 
			
		||||
{
 | 
			
		||||
@@ -109,6 +452,7 @@ void open_button_clicked_cb(GtkButton *button, gpointer *user_data)
 | 
			
		||||
	gint res;
 | 
			
		||||
	gchar *filename;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	dialog = GTK_DIALOG(gtk_file_chooser_dialog_new("Open File", GTK_WINDOW(data->borrowed_main_window),
 | 
			
		||||
							GTK_FILE_CHOOSER_ACTION_OPEN,
 | 
			
		||||
							"Cancel", GTK_RESPONSE_CANCEL,
 | 
			
		||||
@@ -117,7 +461,9 @@ void open_button_clicked_cb(GtkButton *button, gpointer *user_data)
 | 
			
		||||
	res = gtk_dialog_run(dialog);
 | 
			
		||||
	if (res == GTK_RESPONSE_ACCEPT) {
 | 
			
		||||
		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 | 
			
		||||
		data->error_memory_data = map_file_to_memory(filename, &data->file_size);
 | 
			
		||||
		if (data->error_memory_data)
 | 
			
		||||
			free((void *)data->error_memory_data);
 | 
			
		||||
		data->error_memory_data = load_file_to_memory(filename, &data->file_size);
 | 
			
		||||
		if (!data->error_memory_data) {
 | 
			
		||||
			g_warning("File could not be loaded");
 | 
			
		||||
			g_free(filename);
 | 
			
		||||
@@ -126,9 +472,19 @@ void open_button_clicked_cb(GtkButton *button, gpointer *user_data)
 | 
			
		||||
 | 
			
		||||
		gtk_header_bar_set_subtitle(data->borrowed_header_bar, filename);
 | 
			
		||||
		g_free(filename);
 | 
			
		||||
	} else {
 | 
			
		||||
		goto ret_destroy_dialog;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tree_view_show_error_memory(data->borrowed_tree_view, data->error_memory_data, data->file_size);
 | 
			
		||||
	if (data->file_size % 4 || data->file_size == 0) {
 | 
			
		||||
		free((void *)data->error_memory_data);
 | 
			
		||||
		data->error_memory_data = NULL;
 | 
			
		||||
		info_bar_show_message(data->borrowed_info_bar, GTK_MESSAGE_WARNING,
 | 
			
		||||
				      "Data must be base64 encoded and must contain full 32 bit words of data.");
 | 
			
		||||
		goto ret_destroy_dialog;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	show_error_memory(data->borrowed_tree_view, (const unsigned char *)data->error_memory_data, data->file_size);
 | 
			
		||||
 | 
			
		||||
ret_destroy_dialog:
 | 
			
		||||
	gtk_widget_destroy(GTK_WIDGET(dialog));
 | 
			
		||||
@@ -136,21 +492,21 @@ ret_destroy_dialog:
 | 
			
		||||
 | 
			
		||||
static void app_activated(GApplication *app, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	(void)user_data;
 | 
			
		||||
	struct application_data *data = (struct application_data *)user_data;
 | 
			
		||||
	GtkBuilder *builder;
 | 
			
		||||
	GtkWindow *main_window;
 | 
			
		||||
	static struct application_data data;
 | 
			
		||||
 | 
			
		||||
	builder = gtk_builder_new_from_resource("/gui/main");
 | 
			
		||||
	main_window = GTK_WINDOW(gtk_builder_get_object(builder, "main-window"));
 | 
			
		||||
	data.borrowed_main_window = GTK_WIDGET(main_window);
 | 
			
		||||
	data.error_memory_data = NULL;
 | 
			
		||||
	data.borrowed_tree_view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "error-mem-tree-view"));
 | 
			
		||||
	data.borrowed_header_bar = GTK_HEADER_BAR(gtk_builder_get_object(builder, "header-bar"));
 | 
			
		||||
	data->borrowed_main_window = GTK_WIDGET(main_window);
 | 
			
		||||
	data->error_memory_data = NULL;
 | 
			
		||||
	data->borrowed_tree_view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "error-mem-tree-view"));
 | 
			
		||||
	data->borrowed_header_bar = GTK_HEADER_BAR(gtk_builder_get_object(builder, "header-bar"));
 | 
			
		||||
	data->borrowed_info_bar = GTK_INFO_BAR(gtk_builder_get_object(builder, "info-bar"));
 | 
			
		||||
 | 
			
		||||
	setup_tree_view(data.borrowed_tree_view);
 | 
			
		||||
	setup_tree_view(data->borrowed_tree_view);
 | 
			
		||||
 | 
			
		||||
	gtk_builder_connect_signals(builder, &data);
 | 
			
		||||
	gtk_builder_connect_signals(builder, data);
 | 
			
		||||
	g_object_unref(builder);
 | 
			
		||||
	gtk_application_add_window(GTK_APPLICATION(app), main_window);
 | 
			
		||||
	gtk_widget_show(GTK_WIDGET(main_window));
 | 
			
		||||
@@ -160,14 +516,17 @@ static int start_gui(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
	GtkApplication *g_app;
 | 
			
		||||
	static struct application_data data;
 | 
			
		||||
 | 
			
		||||
	/* Create a new G application which will start a completely new process for each call to the program (NON_UNIQUE) instead
 | 
			
		||||
	 * of creating an additional window in the same process
 | 
			
		||||
	 */
 | 
			
		||||
	g_app = gtk_application_new("de.shimatta.reflow.error-mem-viewer", G_APPLICATION_NON_UNIQUE);
 | 
			
		||||
	g_signal_connect(g_app, "activate", G_CALLBACK(app_activated), NULL);
 | 
			
		||||
	g_signal_connect(g_app, "activate", G_CALLBACK(app_activated), &data);
 | 
			
		||||
	ret = g_application_run(G_APPLICATION(g_app), argc, argv);
 | 
			
		||||
	g_object_unref(g_app);
 | 
			
		||||
	if (data.error_memory_data)
 | 
			
		||||
		free((void *)data.error_memory_data);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user