From 15d1d2c6da6052a8299de27b7433d8cd7ecf6a8c Mon Sep 17 00:00:00 2001 From: Alistair Buxton Date: Thu, 14 Dec 2017 00:20:02 +0000 Subject: [PATCH 1/5] Scan for boot files at run time. This patch allows usbbootgui to dynamically load boot files at run time. XDG standard directories are searched. Usually this means: /usr/share/rpiboot /usr/local/share/rpiboot ~/.local/share/rpiboot The code first checks if the rpiboot/ subdirectory exists. If it does, every subdirectory is checked for a bootcode.bin file. If found, the directory is added to the list store. If a description.txt is found in the directory, it is used as the "friendly name" of the image. If an icon.png is found, it is loaded as the icon. If not found, the directory name and a default icon are used instead. Both the description and path of the image are displayed in the GUI. This is to distinguish between the same image in /usr/share and /usr/local/share for example. Images are now always refered to by their full paths. I have not checked how this interacts with the "always use image" code, but I think it should work. Old config files might have to be deleted. This patch also allows the TreeView to have a scrollbar, and fixes the "expand" property on a few items in the GUI, so that only the TreeView expands when the window is resized. Signed-off-by: Alistair Buxton --- data/usbbootgui.ui | 77 +++++++++++++------------- src/usbbootgui.c | 132 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 157 insertions(+), 52 deletions(-) diff --git a/data/usbbootgui.ui b/data/usbbootgui.ui index 11a6d49..b9b781a 100644 --- a/data/usbbootgui.ui +++ b/data/usbbootgui.ui @@ -17,16 +17,6 @@ - - gpio.png - GPIO expansion board - gpioexpand - - - sdcard.png - eMMC / SD card reader - msd - document-open.png Custom application @@ -78,7 +68,7 @@ - True + False True 0 @@ -142,43 +132,50 @@ Type: - - 200 + True - True - liststore - False - 7 + automatic + automatic - - 64 - 64 - icon + + 200 + True + True + liststore + False + 7 - - - 0 - + + 64 + 64 + icon + + + + 0 + + + - - - - - description - - - 1 - + + description + + + + 1 + + + + + True + True + 2 + - - True - True - 2 - @@ -189,7 +186,7 @@ Type: True - True + False True 3 diff --git a/src/usbbootgui.c b/src/usbbootgui.c index d8dcfd2..3a0a7b3 100644 --- a/src/usbbootgui.c +++ b/src/usbbootgui.c @@ -29,9 +29,6 @@ #define MODEL_IDX_NAME 1 /* Friendly name */ #define MODEL_IDX_IMAGE 2 /* Image folder name */ -/* Prefix to image folders */ -#define IMAGE_PREFIX "/usr/share/rpiboot" - #define SETTINGS_FILE "/etc/usbbootgui.conf" /* USB device ids of interest */ @@ -303,7 +300,7 @@ static void showDialog() } else { - imagepath = g_strdup_printf ("%s/%s", IMAGE_PREFIX, image); + imagepath = g_strdup(image); } if (imagepath) @@ -441,14 +438,7 @@ static gboolean pollForPi() showDialog(); else { - if (image[0] == '/') - success = usbboot (image); - else - { - imagepath = g_strdup_printf ("%s/%s", IMAGE_PREFIX, image); - success = usbboot (imagepath); - g_free (imagepath); - } + success = usbboot (image); if (!success) { @@ -477,8 +467,119 @@ static void showTrayIcon() } } +static void addImage(const gchar *name, const gchar *folder) +{ + GtkListStore *store = GTK_LIST_STORE (gtk_builder_get_object (builder, "liststore")); + GtkTreeIter iter; + gchar *description, *descpath, *iconpath; + GdkPixbuf *icon; + + gchar format[] = "%s\n\n%s"; + + descpath = g_strdup_printf ("%s/%s", folder, "description.txt"); + if (g_file_test (descpath, G_FILE_TEST_EXISTS)) + { + gchar *desc; + g_file_get_contents (descpath, &desc, NULL, NULL); + description = g_strdup_printf (format, desc, folder); + g_free (desc); + } else { + if (g_strcmp0 (name, "gpioexpand") == 0) + description = g_strdup_printf (format, _("GPIO expansion board"), folder); + else if (g_strcmp0 (name, "msd") == 0) + description = g_strdup_printf (format, _("eMMC / SD card reader"), folder); + else + description = g_strdup_printf (format, name, folder); + } + g_free (descpath); + + iconpath = g_strdup_printf ("%s/%s", folder, "icon.png"); + if (g_file_test (iconpath, G_FILE_TEST_EXISTS)) { + icon = gdk_pixbuf_new_from_file(iconpath, NULL); + } else { + if (g_strcmp0 (name, "gpioexpand") == 0) + icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/gpio.png", NULL); + else if (g_strcmp0 (name, "msd") == 0) + icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/sdcard.png", NULL); + else + icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/document-open.png", NULL); // TODO: make an "unknown" icon + } + g_free (iconpath); + + gtk_list_store_append(store, &iter); + + gtk_list_store_set(store, &iter, + MODEL_IDX_ICON, icon, + MODEL_IDX_NAME, description, + MODEL_IDX_IMAGE, g_strdup (folder), + -1); +} + +static void scanDirectory(const gchar *path) +{ + gchar *rpibootpath, *imagepath, *bootcodepath; + const gchar *name; + GDir *rpibootdir; + + printf("scanning %s\n", path); + + rpibootpath = g_strdup_printf ("%s/rpiboot", path); + rpibootdir = g_dir_open(rpibootpath, 0, NULL); + if (!rpibootdir) + return; + + while(name = g_dir_read_name(rpibootdir)) { + imagepath = g_strdup_printf ("%s/%s", rpibootpath, name); + if (g_file_test (imagepath, G_FILE_TEST_IS_DIR)) { + bootcodepath = g_strdup_printf ("%s/bootcode.bin", imagepath); + if (g_file_test (bootcodepath, G_FILE_TEST_EXISTS)) { + addImage(name, imagepath); + } + g_free (bootcodepath); + } + g_free(imagepath); + } + g_dir_close (rpibootdir); + g_free (rpibootpath); +} + +gboolean stringInList(GList *list, const gchar *string) +{ + GList *l; + for (l=list; l!=NULL; l = l->next) + if (g_strcmp0 (string, l->data) == 0) + return TRUE; + return FALSE; +} + +static void populateImageList() +{ + const gchar *path; + const gchar *var; + const gchar * const *xdg_data_dirs; + gint i; + + GList *scanned_dirs = NULL; + GList *l; + + xdg_data_dirs = g_get_system_data_dirs (); + for (i = 0; xdg_data_dirs[i]; i++) { + if (stringInList(scanned_dirs, xdg_data_dirs[i])) + continue; + scanned_dirs = g_list_prepend(scanned_dirs, (gpointer)xdg_data_dirs[i]); + scanDirectory(xdg_data_dirs[i]); + } + path = g_get_user_data_dir (); + if (stringInList(scanned_dirs, path)) + return; + scanDirectory(path); +} + int main(int argc, char *argv[]) { + GtkListStore *store; + GtkTreeIter iter; + #ifdef ENABLE_NLS bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); @@ -498,6 +599,13 @@ int main(int argc, char *argv[]) builder = gtk_builder_new(); gtk_builder_add_from_file (builder, PACKAGE_DATA_DIR "/usbbootgui.ui", NULL); + populateImageList(); + + /* Move "Custom application" to the end of the list. */ + store = GTK_LIST_STORE (gtk_builder_get_object (builder, "liststore")); + gtk_tree_model_get_iter_first(GTK_TREE_MODEL (store), &iter); + gtk_list_store_move_before(store, &iter, NULL); + if ( !hasAlwaysUseImage() ) showDialog(); if ( hasAlwaysUseImage() ) From 0d0fb77c36c8001a5867673b404df89589b23cb6 Mon Sep 17 00:00:00 2001 From: Alistair Buxton Date: Thu, 14 Dec 2017 00:32:10 +0000 Subject: [PATCH 2/5] Remove a debugging printf(). Signed-off-by: Alistair Buxton --- src/usbbootgui.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/usbbootgui.c b/src/usbbootgui.c index 3a0a7b3..2ac1e90 100644 --- a/src/usbbootgui.c +++ b/src/usbbootgui.c @@ -521,8 +521,6 @@ static void scanDirectory(const gchar *path) const gchar *name; GDir *rpibootdir; - printf("scanning %s\n", path); - rpibootpath = g_strdup_printf ("%s/rpiboot", path); rpibootdir = g_dir_open(rpibootpath, 0, NULL); if (!rpibootdir) From cdc1113977d76e95d2d1a538bdee8dbbb651b302 Mon Sep 17 00:00:00 2001 From: Alistair Buxton Date: Thu, 14 Dec 2017 01:31:10 +0000 Subject: [PATCH 3/5] Use g_build_filename() instead of g_strdup_printf(). As well as being cleaner and more portable, this also fixes any double separators in displayed strings. Signed-off-by: Alistair Buxton --- src/usbbootgui.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/usbbootgui.c b/src/usbbootgui.c index 2ac1e90..e137dc3 100644 --- a/src/usbbootgui.c +++ b/src/usbbootgui.c @@ -76,7 +76,7 @@ static gchar *promptForFolder() gtk_widget_destroy (dialog); - bootcodepath = g_strdup_printf ("%s/bootcode.bin", folder); + bootcodepath = g_build_filename (folder, "bootcode.bin", NULL); if (!g_file_test (bootcodepath, G_FILE_TEST_EXISTS)) { @@ -306,7 +306,7 @@ static void showDialog() if (imagepath) { /* Sanity check: check that image folder is present, and contains at least bootcode.bin */ - bootcodepath = g_strdup_printf ("%s/bootcode.bin", imagepath); + bootcodepath = g_build_filename (imagepath, "bootcode.bin", NULL); if (!g_file_test (imagepath, G_FILE_TEST_EXISTS)) { @@ -476,7 +476,7 @@ static void addImage(const gchar *name, const gchar *folder) gchar format[] = "%s\n\n%s"; - descpath = g_strdup_printf ("%s/%s", folder, "description.txt"); + descpath = g_build_filename (folder, "description.txt", NULL); if (g_file_test (descpath, G_FILE_TEST_EXISTS)) { gchar *desc; @@ -493,7 +493,7 @@ static void addImage(const gchar *name, const gchar *folder) } g_free (descpath); - iconpath = g_strdup_printf ("%s/%s", folder, "icon.png"); + iconpath = g_build_filename (folder, "icon.png", NULL); if (g_file_test (iconpath, G_FILE_TEST_EXISTS)) { icon = gdk_pixbuf_new_from_file(iconpath, NULL); } else { @@ -521,15 +521,15 @@ static void scanDirectory(const gchar *path) const gchar *name; GDir *rpibootdir; - rpibootpath = g_strdup_printf ("%s/rpiboot", path); + rpibootpath = g_build_filename (path, "rpiboot", NULL); rpibootdir = g_dir_open(rpibootpath, 0, NULL); if (!rpibootdir) return; while(name = g_dir_read_name(rpibootdir)) { - imagepath = g_strdup_printf ("%s/%s", rpibootpath, name); + imagepath = g_build_filename (rpibootpath, name, NULL); if (g_file_test (imagepath, G_FILE_TEST_IS_DIR)) { - bootcodepath = g_strdup_printf ("%s/bootcode.bin", imagepath); + bootcodepath = g_build_filename (imagepath, "bootcode.bin", NULL); if (g_file_test (bootcodepath, G_FILE_TEST_EXISTS)) { addImage(name, imagepath); } From 4b0c6bc3df1186fc3b5c675ef281866e59c14883 Mon Sep 17 00:00:00 2001 From: Alistair Buxton Date: Thu, 14 Dec 2017 01:34:14 +0000 Subject: [PATCH 4/5] These packing properties aren't needed when using a GtkScrolledWindow. They just cause Gtk warnings, so remove them. --- data/usbbootgui.ui | 5 ----- 1 file changed, 5 deletions(-) diff --git a/data/usbbootgui.ui b/data/usbbootgui.ui index b9b781a..b581ab3 100644 --- a/data/usbbootgui.ui +++ b/data/usbbootgui.ui @@ -169,11 +169,6 @@ Type: - - True - True - 2 - From 359fdc6a86b700db0050e4592468d4f0853b64c9 Mon Sep 17 00:00:00 2001 From: Alistair Buxton Date: Thu, 14 Dec 2017 15:45:18 +0000 Subject: [PATCH 5/5] Strip leading and trailing whitespace (and newlines) from description. Signed-off-by: Alistair Buxton --- src/usbbootgui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usbbootgui.c b/src/usbbootgui.c index e137dc3..a2d78c0 100644 --- a/src/usbbootgui.c +++ b/src/usbbootgui.c @@ -481,7 +481,7 @@ static void addImage(const gchar *name, const gchar *folder) { gchar *desc; g_file_get_contents (descpath, &desc, NULL, NULL); - description = g_strdup_printf (format, desc, folder); + description = g_strdup_printf (format, g_strstrip (desc), folder); g_free (desc); } else { if (g_strcmp0 (name, "gpioexpand") == 0)