From b4372119c09564774055800bfd4e1525111ebce0 Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Tue, 18 Jan 2022 20:23:12 +0100 Subject: [PATCH 01/19] Adding non-threaded search. --- src/view/search.c | 49 +++++++---------------------------------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/src/view/search.c b/src/view/search.c index c0e4de2..47219e4 100644 --- a/src/view/search.c +++ b/src/view/search.c @@ -6,16 +6,7 @@ #include static void -search_text_changed (GtkEditable *self, - gpointer user_data); -static void -async_search_manga (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable); -static void -set_mangas_to_list_view (GObject *source_object, - GAsyncResult *res, +search_text_changed (GtkEntry *entry, gpointer user_data); GtkWidget * @@ -30,7 +21,7 @@ create_search_view (ControlsAdwaita *controls) { gtk_box_append (GTK_BOX (search_view), search_entry); list_view_mangas = create_list_view_mangas (mangas, controls); - g_signal_connect (search_entry, "changed", + g_signal_connect (search_entry, "activate", G_CALLBACK (search_text_changed), list_view_mangas); gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scroll), @@ -42,43 +33,17 @@ create_search_view (ControlsAdwaita *controls) { return search_view; } -static GCancellable *cancellable = NULL; - static void -search_text_changed (GtkEditable *editable, +search_text_changed (GtkEntry *entry, gpointer user_data) { - if (cancellable) { - g_cancellable_cancel (cancellable); - } - cancellable = g_cancellable_new (); GtkListView *list_view_mangas = GTK_LIST_VIEW (user_data); - GTask *task = g_task_new (editable, cancellable, set_mangas_to_list_view,list_view_mangas); - g_task_set_return_on_cancel (task, true); - g_task_run_in_thread (task, async_search_manga); -} - -static void -set_mangas_to_list_view (GObject *source_object, - GAsyncResult *res, - gpointer user_data) { - GListStore *mangas = G_LIST_STORE (g_task_propagate_pointer (G_TASK (res), NULL)); + GtkEntryBuffer *buffer = gtk_entry_get_buffer (entry); + MgBackendReadmng *readmng = mg_backend_readmng_new (); + const char *search_string = gtk_entry_buffer_get_text (buffer); + GListStore *mangas = mg_backend_readmng_search (readmng, search_string); if (!mangas) return; - GtkListView *list_view_mangas = GTK_LIST_VIEW (user_data); GtkSingleSelection *selection = GTK_SINGLE_SELECTION ( gtk_list_view_get_model (list_view_mangas)); gtk_single_selection_set_model (selection, G_LIST_MODEL (mangas)); } - -static void -async_search_manga (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) { - GtkEntry *entry = GTK_ENTRY (source_object); - GtkEntryBuffer *buffer = gtk_entry_get_buffer (entry); - MgBackendReadmng *readmng = mg_backend_readmng_new (); - const char *search_string = gtk_entry_buffer_get_text (buffer); - GListStore *mangas = mg_backend_readmng_search (readmng, search_string); - g_task_return_pointer (task, mangas, g_object_unref); -} From 46ef8f9df35b9a61acd665472f4223ebfdfa400c Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Tue, 18 Jan 2022 21:00:39 +0100 Subject: [PATCH 02/19] Fix to avoid threading a lot with url pictures. --- include/openmg/view/picture.h | 2 +- src/view/chapter_view.c | 8 ++++- src/view/detail_manga.c | 5 +++- src/view/list_view_manga.c | 5 +++- src/view/picture.c | 55 ++++++++++++++++++++++++----------- 5 files changed, 54 insertions(+), 21 deletions(-) diff --git a/include/openmg/view/picture.h b/include/openmg/view/picture.h index 6ab6451..b2e6d4e 100644 --- a/include/openmg/view/picture.h +++ b/include/openmg/view/picture.h @@ -1,6 +1,6 @@ #pragma once #include -void +GtkPicture * create_picture_from_url (const char *const url, gint picture_size, GAsyncReadyCallback ready, gpointer source_object, gpointer callback_data); diff --git a/src/view/chapter_view.c b/src/view/chapter_view.c index eed3269..4d5f414 100644 --- a/src/view/chapter_view.c +++ b/src/view/chapter_view.c @@ -167,8 +167,14 @@ set_image_zoomable_picture_container (ChapterVisorData *chapter_visor_data) { strlen(url_image_not_owned) + 1, 0, strlen (url_image_not_owned)); - create_picture_from_url (url_image, 0, picture_ready_manga_page, + GtkPicture *picture = create_picture_from_url (url_image, 0, picture_ready_manga_page, zoomable_picture_container, chapter_visor_data); + if (picture) { + chapter_visor_data->current_picture = GTK_PICTURE (picture); + g_signal_connect (G_OBJECT (picture), "map", + G_CALLBACK (image_page_show), chapter_visor_data); + gtk_scrolled_window_set_child (zoomable_picture_container, GTK_WIDGET (picture)); + } g_free (url_image); g_clear_object (&string_util); } diff --git a/src/view/detail_manga.c b/src/view/detail_manga.c index 37e7378..141fabf 100644 --- a/src/view/detail_manga.c +++ b/src/view/detail_manga.c @@ -89,7 +89,7 @@ create_detail_view (MgManga *manga, ControlsAdwaita *controls) { ("network-transmit-receive-symbolic")); GtkListView *chapter_list = NULL; char *url_image = mg_manga_get_image_url(manga); - create_picture_from_url (url_image, 200, + GtkPicture *picture = create_picture_from_url (url_image, 200, picture_ready_manga_detail, avatar_title_box, NULL); char *manga_title_text = mg_manga_get_title (manga); char *title_text = mg_util_xml_get_title_text ( @@ -116,6 +116,9 @@ create_detail_view (MgManga *manga, ControlsAdwaita *controls) { gtk_widget_set_size_request (GTK_WIDGET (manga_description), 200, -1); gtk_label_set_use_markup (GTK_LABEL (manga_title), 1); + if (picture) { + gtk_box_append (avatar_title_box, GTK_WIDGET (picture)); + } gtk_box_append (avatar_title_box, GTK_WIDGET (manga_title)); gtk_box_append (foldable_manga_data, GTK_WIDGET (avatar_title_box)); diff --git a/src/view/list_view_manga.c b/src/view/list_view_manga.c index 1273cd5..6cf1978 100644 --- a/src/view/list_view_manga.c +++ b/src/view/list_view_manga.c @@ -69,10 +69,13 @@ setup_list_view_mangas (GtkSignalListItemFactory *factory, char *image_url = mg_manga_get_image_url (manga); GtkWidget *label = gtk_label_new (manga_title); - create_picture_from_url (image_url, 100, + GtkPicture *picture = create_picture_from_url (image_url, 100, picture_ready_manga_preview, box, NULL); g_object_set_property_int (G_OBJECT(box), "height-request", 100); + if (picture) { + gtk_box_append (box, GTK_WIDGET (picture)); + } gtk_box_append (box, label); gtk_list_item_set_child (list_item, GTK_WIDGET (box)); diff --git a/src/view/picture.c b/src/view/picture.c index 9e2d3e1..136e5f9 100644 --- a/src/view/picture.c +++ b/src/view/picture.c @@ -16,6 +16,7 @@ const char *const IMAGE_CACHE_FORMAT_STRING = typedef struct { char *url; gint picture_size; + GFile *image; } PictureThreadAttributes; static char * @@ -30,7 +31,7 @@ threaded_picture_recover (GTask *task, gpointer source_object, const char *url = attrs->url; gint picture_size = attrs->picture_size; GFileIOStream *iostream = NULL; - GFile *image = NULL; + GFile *image = attrs->image; GError *error = NULL; GdkTexture *texture = NULL; GtkPicture *picture = NULL; @@ -39,15 +40,11 @@ threaded_picture_recover (GTask *task, gpointer source_object, char *downloaded_image = NULL; MgUtilSoup *util_soup = mg_util_soup_new (); + downloaded_image = mg_util_soup_get_request (util_soup, + url, &size_downloaded_image); static GMutex mutex; g_mutex_lock (&mutex); - image = get_image_for_url (url); - g_mutex_unlock (&mutex); if (!g_file_query_exists (image, NULL)) { - downloaded_image = - mg_util_soup_get_request - (util_soup, - url, &size_downloaded_image); iostream = g_file_create_readwrite (image, G_FILE_CREATE_NONE, NULL, &error); if (error) { @@ -63,9 +60,11 @@ threaded_picture_recover (GTask *task, gpointer source_object, goto cleanup_create_picture_from_url; } } + g_mutex_unlock (&mutex); texture = gdk_texture_new_from_file (image, &error); if (error) { - fprintf (stderr, "Texture malformed."); + g_warning ("Texture malformed."); + g_clear_error (&error); goto cleanup_create_picture_from_url; } picture = GTK_PICTURE (gtk_picture_new_for_paintable (GDK_PAINTABLE (texture))); @@ -93,21 +92,43 @@ free_picture_thread_attributes (gpointer user_data) { g_free (attrs); } -void +GtkPicture * create_picture_from_url (const char *const url, gint picture_size, GAsyncReadyCallback ready, gpointer source_object, gpointer callback_data) { - GTask *task = g_task_new (source_object, NULL, ready, callback_data); + GtkPicture *picture = NULL; + GFile *image = NULL; + GdkTexture *texture = NULL; + GError *error = NULL; + + image = get_image_for_url (url); size_t url_len = strlen (url) + 1; - PictureThreadAttributes *attrs = g_malloc (sizeof *attrs); - - attrs->url = g_malloc (url_len * sizeof *url); - snprintf (attrs->url, url_len, "%s", url); - attrs->picture_size = picture_size; - g_task_set_task_data (task, attrs, free_picture_thread_attributes); - g_task_run_in_thread (task, threaded_picture_recover); + if (g_file_query_exists (image, NULL)) { + texture = gdk_texture_new_from_file (image, &error); + if (error) { + g_warning ("Texture malformed."); + g_clear_error (&error); + goto cleanup_create_picture_from_url; + } + picture = GTK_PICTURE (gtk_picture_new_for_paintable (GDK_PAINTABLE (texture))); + if (GTK_IS_WIDGET (picture)) { + g_object_set_property_int (G_OBJECT(picture), "height-request", picture_size); + g_object_set_property_int (G_OBJECT(picture), "width-request", picture_size); + } + } else { + GTask *task = g_task_new (source_object, NULL, ready, callback_data); + PictureThreadAttributes *attrs = g_malloc (sizeof *attrs); + attrs->url = g_malloc (url_len * sizeof *url); + snprintf (attrs->url, url_len, "%s", url); + attrs->image = image; + attrs->picture_size = picture_size; + g_task_set_task_data (task, attrs, free_picture_thread_attributes); + g_task_run_in_thread (task, threaded_picture_recover); + } +cleanup_create_picture_from_url: + return picture; } GFile * From 023fbf7e4d6320c39900c73bf957ddc1fdc659da Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Tue, 18 Jan 2022 21:06:02 +0100 Subject: [PATCH 03/19] Fixing cygwin64 build. --- src/view/main_view.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/view/main_view.c b/src/view/main_view.c index 0d3bcbd..b57ebab 100644 --- a/src/view/main_view.c +++ b/src/view/main_view.c @@ -1,4 +1,6 @@ +#ifndef __CYGWIN__ #include +#endif #include #include @@ -37,6 +39,7 @@ activate (AdwApplication *app, AdwLeaflet *views_leaflet_search; AdwHeaderBar *header_bar; +#ifndef __CYGWIN__ swipe_back_t swipe_back = (swipe_back_t) dlsym (NULL, "adw_leaflet_set_can_navigate_back"); @@ -44,6 +47,9 @@ activate (AdwApplication *app, swipe_back = (swipe_back_t) dlsym (NULL, "adw_leaflet_set_can_swipe_back"); } +#else + swipe_back_t swipe_back = adw_leaflet_set_can_navigate_back; +#endif controls->is_set_previous = 0; controls->header = NULL; From d6200919d0cb1d668a15da9d3c52a68238edeffc Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Tue, 18 Jan 2022 21:17:33 +0100 Subject: [PATCH 04/19] Fixing again windows build. --- src/view/main_view.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/view/main_view.c b/src/view/main_view.c index b57ebab..468c171 100644 --- a/src/view/main_view.c +++ b/src/view/main_view.c @@ -1,4 +1,4 @@ -#ifndef __CYGWIN__ +#ifndef __CYGWIN64__ #include #endif @@ -39,7 +39,7 @@ activate (AdwApplication *app, AdwLeaflet *views_leaflet_search; AdwHeaderBar *header_bar; -#ifndef __CYGWIN__ +#ifndef __CYGWIN64__ swipe_back_t swipe_back = (swipe_back_t) dlsym (NULL, "adw_leaflet_set_can_navigate_back"); From 10e7ccab2bafa77be3bc64b8e7b1e023d899a93b Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Tue, 18 Jan 2022 21:20:58 +0100 Subject: [PATCH 05/19] Using _WIN32 instead. --- src/view/main_view.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/view/main_view.c b/src/view/main_view.c index 468c171..a70a4cf 100644 --- a/src/view/main_view.c +++ b/src/view/main_view.c @@ -1,4 +1,4 @@ -#ifndef __CYGWIN64__ +#ifndef _WIN32 #include #endif @@ -39,7 +39,7 @@ activate (AdwApplication *app, AdwLeaflet *views_leaflet_search; AdwHeaderBar *header_bar; -#ifndef __CYGWIN64__ +#ifndef _WIN32 swipe_back_t swipe_back = (swipe_back_t) dlsym (NULL, "adw_leaflet_set_can_navigate_back"); From 03ca0461806808f1220f712eb418e9d29d9cce6c Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Wed, 19 Jan 2022 00:29:44 +0100 Subject: [PATCH 06/19] Multiple fixes, allowing to compile without images in lists. (Too expensive for some computers) Logging image writes. --- meson.build | 11 +++++++++-- meson_options.txt | 1 + src/backend/readmng.c | 21 ++++++++++++++------- src/view/list_view_manga.c | 6 ++++++ src/view/picture.c | 1 + 5 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 meson_options.txt diff --git a/meson.build b/meson.build index 9753e8b..24dbca9 100644 --- a/meson.build +++ b/meson.build @@ -39,11 +39,18 @@ link_arguments = [ '-ldl', '-lm' ] - + +images_on_lists = get_option('images') + +cArgs = '' +if images_on_lists + cArgs = cArgs + '-DLIST_IMAGES' +endif executable('openmg', sources, dependencies : openmgdeps, include_directories : inc, install : true, - link_args : link_arguments + link_args : link_arguments, + c_args: cArgs ) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..5bfbecd --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('images', type : 'boolean', value : true) diff --git a/src/backend/readmng.c b/src/backend/readmng.c index 74fdd96..b225e30 100644 --- a/src/backend/readmng.c +++ b/src/backend/readmng.c @@ -66,7 +66,8 @@ mg_backend_readmng_loop_li_chapter ( MgBackendReadmng *self, xmlNodePtr li); static char * -mg_backend_readmng_fetch_search (MgBackendReadmng *self, const char *search_query); +mg_backend_readmng_fetch_search (MgBackendReadmng *self, + const char *search_query, size_t *response_len); static GListModel * mg_backend_readmng_parse_page (MgBackendReadmng *self, xmlDocPtr html_document); @@ -235,15 +236,21 @@ mg_backend_readmng_fetch_page_url (MgBackendReadmng *self, GListStore * mg_backend_readmng_search (MgBackendReadmng *self, const char *search_query) { - char *response = mg_backend_readmng_fetch_search (self, search_query); + size_t response_len = 0; + char *response = mg_backend_readmng_fetch_search (self, search_query, + &response_len); JsonParser *parser = json_parser_new (); - GListStore *mangas = g_list_store_new(MG_TYPE_MANGA); + GListStore *mangas = g_list_store_new (MG_TYPE_MANGA); GError *error = NULL; JsonNode *root = NULL; JsonArray *mangas_json_array = NULL; guint mangas_json_array_len = 0; - json_parser_load_from_data (parser, response, -1, &error); + if (!response) { + g_warning ("Json search response is null."); + goto cleanup_mg_backend_readmng_search; + } + json_parser_load_from_data (parser, response, response_len, &error); if (error) { g_warning ("Unable to parse json: %s.", error->message); g_clear_error (&error); @@ -280,14 +287,14 @@ cleanup_mg_backend_readmng_search: } static char * -mg_backend_readmng_fetch_search (MgBackendReadmng *self, const char *search_query) { +mg_backend_readmng_fetch_search (MgBackendReadmng *self, + const char *search_query, size_t *response_len) { MgUtilSoup *util_soup; MgUtilString *string_util; char *request_url; size_t request_url_len; - size_t response_len = 0; util_soup = mg_util_soup_new (); string_util = mg_util_string_new (); @@ -328,7 +335,7 @@ mg_backend_readmng_fetch_search (MgBackendReadmng *self, const char *search_quer size_t body_len = sizeof body / sizeof *body; char *text_response = mg_util_soup_post_request_url_encoded (util_soup, - request_url, body, body_len, headers, headers_len, &response_len); + request_url, body, body_len, headers, headers_len, response_len); g_free (request_url); g_free (phrase); diff --git a/src/view/list_view_manga.c b/src/view/list_view_manga.c index 6cf1978..ea4f9ad 100644 --- a/src/view/list_view_manga.c +++ b/src/view/list_view_manga.c @@ -46,6 +46,7 @@ manga_selected (GtkListView *list_view, adw_leaflet_navigate (views_leaflet, ADW_NAVIGATION_DIRECTION_FORWARD); } +#ifdef LIST_IMAGES static void picture_ready_manga_preview (GObject *source_object, GAsyncResult *res, @@ -58,6 +59,7 @@ picture_ready_manga_preview (GObject *source_object, gtk_box_prepend (box, picture); } } +#endif static void setup_list_view_mangas (GtkSignalListItemFactory *factory, @@ -69,13 +71,17 @@ setup_list_view_mangas (GtkSignalListItemFactory *factory, char *image_url = mg_manga_get_image_url (manga); GtkWidget *label = gtk_label_new (manga_title); +#ifdef LIST_IMAGES GtkPicture *picture = create_picture_from_url (image_url, 100, picture_ready_manga_preview, box, NULL); +#endif g_object_set_property_int (G_OBJECT(box), "height-request", 100); +#ifdef LIST_IMAGES if (picture) { gtk_box_append (box, GTK_WIDGET (picture)); } +#endif gtk_box_append (box, label); gtk_list_item_set_child (list_item, GTK_WIDGET (box)); diff --git a/src/view/picture.c b/src/view/picture.c index 136e5f9..9d47137 100644 --- a/src/view/picture.c +++ b/src/view/picture.c @@ -45,6 +45,7 @@ threaded_picture_recover (GTask *task, gpointer source_object, static GMutex mutex; g_mutex_lock (&mutex); if (!g_file_query_exists (image, NULL)) { + g_warning ("Storing %s", url); iostream = g_file_create_readwrite (image, G_FILE_CREATE_NONE, NULL, &error); if (error) { From eb19a7bd0bc5e57f3ffd6fca9a43a8ae71c36833 Mon Sep 17 00:00:00 2001 From: broken Date: Thu, 20 Jan 2022 14:17:33 +0100 Subject: [PATCH 07/19] fixed memory leaks --- src/backend/readmng.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/backend/readmng.c b/src/backend/readmng.c index b225e30..7ab3f88 100644 --- a/src/backend/readmng.c +++ b/src/backend/readmng.c @@ -409,6 +409,7 @@ cleanup_mg_backend_readmng_retrieve_manga_details: if (movie_detail) { g_free (movie_detail); } + xmlFreeDoc(html_document); } static GListStore * @@ -453,6 +454,9 @@ cleanup_mg_backend_readmng_recover_chapter_list: xmlXPathFreeObject(xpath_result); } if (uls) { + for (size_t i = 0; i < ul_len; i++) { + xmlFreeNode(uls[i]); + } g_free (uls); } return return_value; @@ -588,6 +592,7 @@ mg_backend_readmng_parse_main_page (MgBackendReadmng *self, const xmlDocPtr html xmlFreeNode (current_li); li[i] = NULL; } + xmlFreeNode(slides); g_free (li); return mangas; } @@ -643,6 +648,11 @@ cleanup_mg_backend_readmng_retrieve_slides: xmlXPathFreeObject(xpath_result); } if (nodes) { + for (size_t i = 1; i < matching_classes_len; i++) + { + xmlFreeNode(nodes[i]); + } + g_free (nodes); } return slides; From a49e12154f77c50b9ac5f2fae95cb7b572313411 Mon Sep 17 00:00:00 2001 From: broken Date: Thu, 20 Jan 2022 14:22:47 +0100 Subject: [PATCH 08/19] Added valgrind supression list --- openmg.supp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 openmg.supp diff --git a/openmg.supp b/openmg.supp new file mode 100644 index 0000000..97e8055 --- /dev/null +++ b/openmg.supp @@ -0,0 +1,36 @@ +# +# Valgrind suppression file for mangareader. +# Warning: This file is very generic, so a real memory leak might be suppressed. +# Also, some false positives are still captured +# +# Format specification: +# http://valgrind.org/docs/manual/manual-core.html#manual-core.suppress +# +{ + gtk4 + Memcheck:Leak + ... + obj:/usr/lib/x86_64-linux-gnu/libgtk-4.so.1.600.0 + ... +} +{ + glib + Memcheck:Leak + ... + obj:/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7100.0 + ... +} +{ + fontconfig + Memcheck:Leak + ... + obj:/usr/lib/x86_64-linux-gnu/libfontconfig.so.1.12.0 + ... +} +{ + gallium + Memcheck:Leak + ... + obj:/usr/lib/x86_64-linux-gnu/GL/default/lib/dri/libgallium_dri.so + ... +} From 14c671c78b63f102558edc50632c6c908d5c7c80 Mon Sep 17 00:00:00 2001 From: endes Date: Thu, 20 Jan 2022 17:42:40 +0100 Subject: [PATCH 09/19] updated AUTHORS.md --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index bb44edc..7b70e33 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,2 +1,3 @@ Sergiotarxz sergiotarxz@posteo.net Project leader and developer [Gitea](https://gitea.sergiotarxz.freemyip.com/sergiotarxz) Germe.db FOSSgerme.deb@tuta.io Author of the icon openmg.svg. [sr.ht](https://sr.ht/~germe-fur/) +Endes endes@disroot.org Some small fixes [Github](https://github.com/endes0) From 44689c95616a2ccbcf72d5483874bbc32bcbd19c Mon Sep 17 00:00:00 2001 From: endes Date: Thu, 20 Jan 2022 17:45:50 +0100 Subject: [PATCH 10/19] corrected 'AUTHORS.md' --- AUTHORS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS.md b/AUTHORS.md index 7b70e33..5efa8be 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,3 +1,3 @@ Sergiotarxz sergiotarxz@posteo.net Project leader and developer [Gitea](https://gitea.sergiotarxz.freemyip.com/sergiotarxz) Germe.db FOSSgerme.deb@tuta.io Author of the icon openmg.svg. [sr.ht](https://sr.ht/~germe-fur/) -Endes endes@disroot.org Some small fixes [Github](https://github.com/endes0) +Endes endes@disroot.org Developer [Github](https://github.com/endes0) From a17f9c1f2514e4c1286420dc7e8df3f67b5806c0 Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Sun, 23 Jan 2022 09:58:01 +0100 Subject: [PATCH 11/19] Fix to endes patch (Unitialized html_document.) and default to build flatpak without images to work better on low resources computers. --- AUTHORS.md | 8 +++++--- README.md | 4 ++++ me.sergiotarxz.openmg.yml | 20 -------------------- meson.build | 13 +++++++++---- meson_options.txt | 1 + src/backend/readmng.c | 6 ++++-- 6 files changed, 23 insertions(+), 29 deletions(-) delete mode 100644 me.sergiotarxz.openmg.yml diff --git a/AUTHORS.md b/AUTHORS.md index 5efa8be..9f5b816 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,3 +1,5 @@ -Sergiotarxz sergiotarxz@posteo.net Project leader and developer [Gitea](https://gitea.sergiotarxz.freemyip.com/sergiotarxz) -Germe.db FOSSgerme.deb@tuta.io Author of the icon openmg.svg. [sr.ht](https://sr.ht/~germe-fur/) -Endes endes@disroot.org Developer [Github](https://github.com/endes0) +Sergiotarxz sergiotarxz@posteo.net Project leader and developer. [Gitea](https://gitea.sergiotarxz.freemyip.com/sergiotarxz) + +Germe.db FOSSgerme.deb@tuta.io Designer and icons creator. [sr.ht](https://sr.ht/~germe-fur/) + +Endes endes@disroot.org Developer. [Github](https://github.com/endes0) diff --git a/README.md b/README.md index 6b4b284..c4ecbaf 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ ## Installing the app. +First fine tune the options in `me.sergiotarxz.openmg.json` for +meson you want to have, for example preview images, complete list is +on `meson_options.txt` + ```shell flatpak --user remote-add --if-not-exists gnome-nightly https://nightly.gnome.org/gnome-nightly.flatpakrepo flatpak install org.gnome.Sdk//master diff --git a/me.sergiotarxz.openmg.yml b/me.sergiotarxz.openmg.yml deleted file mode 100644 index 889076e..0000000 --- a/me.sergiotarxz.openmg.yml +++ /dev/null @@ -1,20 +0,0 @@ - -app-id: me.sergiotarxz.openmg -runtime: org.gnome.Platform -runtime-version: master -sdk: org.gnome.Sdk -sdk-version: master -command: openmg -finish-args: - - "--share=ipc" - - "--socket=x11" - - "--socket=wayland" - - "--socket=session-bus" - - "--share=network" - - "--device=dri" -modules: - - name: openmg - buildsystem: meson - sources: - - type: dir - path: . diff --git a/meson.build b/meson.build index 24dbca9..e193194 100644 --- a/meson.build +++ b/meson.build @@ -36,16 +36,21 @@ sources = [ ] link_arguments = [ - '-ldl', '-lm' ] images_on_lists = get_option('images') +is_windows = get_option('windows') -cArgs = '' -if images_on_lists - cArgs = cArgs + '-DLIST_IMAGES' +if not is_windows + link_arguments += ['-ldl'] endif + +cArgs = [] +if images_on_lists + cArgs += ['-DLIST_IMAGES'] +endif + executable('openmg', sources, dependencies : openmgdeps, diff --git a/meson_options.txt b/meson_options.txt index 5bfbecd..084c599 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ option('images', type : 'boolean', value : true) +option('windows', type : 'boolean', value : false) diff --git a/src/backend/readmng.c b/src/backend/readmng.c index 7ab3f88..f952bda 100644 --- a/src/backend/readmng.c +++ b/src/backend/readmng.c @@ -364,7 +364,7 @@ mg_backend_readmng_retrieve_manga_details (MgBackendReadmng *self, MgManga *manga) { MgUtilXML *xml_utils; - xmlDocPtr html_document; + xmlDocPtr html_document = NULL; xmlNodePtr *movie_detail = NULL; xmlXPathObjectPtr xpath_result = NULL; xmlNodeSetPtr node_set = NULL; @@ -409,7 +409,9 @@ cleanup_mg_backend_readmng_retrieve_manga_details: if (movie_detail) { g_free (movie_detail); } - xmlFreeDoc(html_document); + if (html_document) { + xmlFreeDoc(html_document); + } } static GListStore * From 2c2e1d550e56e6134809dd16e56b6dca7c55ee14 Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Sun, 23 Jan 2022 10:53:46 +0100 Subject: [PATCH 12/19] Improving the image logic. --- include/openmg/view/controls.h | 2 ++ sqlite | 1 - src/view/list_view_manga.c | 19 ++++++++++++++++--- src/view/main_view.c | 3 ++- src/view/search.c | 22 ++++++++++++++++++++-- 5 files changed, 40 insertions(+), 7 deletions(-) delete mode 160000 sqlite diff --git a/include/openmg/view/controls.h b/include/openmg/view/controls.h index 422f791..b8b01ad 100644 --- a/include/openmg/view/controls.h +++ b/include/openmg/view/controls.h @@ -9,6 +9,8 @@ typedef struct { AdwHeaderBar *header; AdwLeaflet *views_leaflet; AdwViewStack *view_stack; + GCancellable **image_threads; + size_t image_threads_len; GtkButton *previous; gboolean is_set_previous; } ControlsAdwaita; diff --git a/sqlite b/sqlite deleted file mode 160000 index 855a165..0000000 --- a/sqlite +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 855a165fecc25da50cb20525ddf5b9e60a67d18f diff --git a/src/view/list_view_manga.c b/src/view/list_view_manga.c index ea4f9ad..9a7c1fd 100644 --- a/src/view/list_view_manga.c +++ b/src/view/list_view_manga.c @@ -26,6 +26,14 @@ manga_selected (GtkListView *list_view, guint position, gpointer user_data) { ControlsAdwaita *controls = (ControlsAdwaita *) user_data; + for (size_t i = 0; i < controls->image_threads_len; i++) { + g_cancellable_cancel (controls->image_threads[i]); + } + if (controls->image_threads) { + g_free (controls->image_threads); + } + controls->image_threads = NULL; + controls->image_threads_len = 0; AdwLeaflet *views_leaflet = controls->views_leaflet; GtkSingleSelection *selection = GTK_SINGLE_SELECTION (gtk_list_view_get_model (list_view)); @@ -65,6 +73,7 @@ static void setup_list_view_mangas (GtkSignalListItemFactory *factory, GtkListItem *list_item, gpointer user_data) { + ControlsAdwaita *controls = (ControlsAdwaita *) user_data; MgManga *manga = gtk_list_item_get_item (list_item); GtkBox *box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0)); char *manga_title = mg_manga_get_title (manga); @@ -72,8 +81,13 @@ setup_list_view_mangas (GtkSignalListItemFactory *factory, GtkWidget *label = gtk_label_new (manga_title); #ifdef LIST_IMAGES + GCancellable *cancellable = g_cancellable_new (); GtkPicture *picture = create_picture_from_url (image_url, 100, - picture_ready_manga_preview, box, NULL); + picture_ready_manga_preview, box, cancellable); + controls->image_threads_len++; + controls->image_threads = g_realloc (controls->image_threads, + controls->image_threads_len * sizeof *(controls->image_threads)); + controls->image_threads[controls->image_threads_len-1] = cancellable; #endif g_object_set_property_int (G_OBJECT(box), "height-request", 100); @@ -91,14 +105,13 @@ setup_list_view_mangas (GtkSignalListItemFactory *factory, GtkListView * create_list_view_mangas (GListStore *mangas, ControlsAdwaita *controls) { - AdwLeaflet *views_leaflet = controls->views_leaflet; GtkSingleSelection *selection = gtk_single_selection_new (G_LIST_MODEL (mangas)); GtkListItemFactory *factory = gtk_signal_list_item_factory_new (); GtkListView *list_view_manga = NULL; g_signal_connect (G_OBJECT (factory), "bind", G_CALLBACK (setup_list_view_mangas), - views_leaflet); + controls); list_view_manga = GTK_LIST_VIEW (gtk_list_view_new (GTK_SELECTION_MODEL (selection), factory)); diff --git a/src/view/main_view.c b/src/view/main_view.c index a70a4cf..f05ac0d 100644 --- a/src/view/main_view.c +++ b/src/view/main_view.c @@ -54,7 +54,8 @@ activate (AdwApplication *app, controls->is_set_previous = 0; controls->header = NULL; controls->view_stack = view_stack; - + controls->image_threads_len = 0; + controls->image_threads = NULL; views_leaflet_explore = create_explore_leaflet (controls, swipe_back); views_leaflet_search = create_search_leaflet (controls, swipe_back); diff --git a/src/view/search.c b/src/view/search.c index 47219e4..2bc67c6 100644 --- a/src/view/search.c +++ b/src/view/search.c @@ -9,6 +9,11 @@ static void search_text_changed (GtkEntry *entry, gpointer user_data); +typedef struct { + GtkListView *list_view_mangas; + ControlsAdwaita *controls; +} SearchTextData; + GtkWidget * create_search_view (ControlsAdwaita *controls) { GtkWidget *search_view = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10); @@ -17,12 +22,15 @@ create_search_view (ControlsAdwaita *controls) { GtkWidget *scroll = gtk_scrolled_window_new (); GListStore *mangas = g_list_store_new(MG_TYPE_MANGA); GtkListView *list_view_mangas; + SearchTextData *search_text_data = g_malloc (sizeof *search_text_data); gtk_box_append (GTK_BOX (search_view), search_entry); list_view_mangas = create_list_view_mangas (mangas, controls); + search_text_data->list_view_mangas = list_view_mangas; + search_text_data ->controls = controls; g_signal_connect (search_entry, "activate", - G_CALLBACK (search_text_changed), list_view_mangas); + G_CALLBACK (search_text_changed), search_text_data); gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scroll), GTK_WIDGET (list_view_mangas)); @@ -36,11 +44,21 @@ create_search_view (ControlsAdwaita *controls) { static void search_text_changed (GtkEntry *entry, gpointer user_data) { - GtkListView *list_view_mangas = GTK_LIST_VIEW (user_data); + SearchTextData *search_text_data = (SearchTextData *) user_data; + ControlsAdwaita *controls = search_text_data->controls; + GtkListView *list_view_mangas = search_text_data->list_view_mangas; GtkEntryBuffer *buffer = gtk_entry_get_buffer (entry); MgBackendReadmng *readmng = mg_backend_readmng_new (); const char *search_string = gtk_entry_buffer_get_text (buffer); GListStore *mangas = mg_backend_readmng_search (readmng, search_string); + for (size_t i = 0; i < controls->image_threads_len; i++) { + g_cancellable_cancel (controls->image_threads[i]); + } + if (controls->image_threads) { + g_free (controls->image_threads); + } + controls->image_threads = NULL; + controls->image_threads_len = 0; if (!mangas) return; GtkSingleSelection *selection = GTK_SINGLE_SELECTION ( gtk_list_view_get_model (list_view_mangas)); From 84c47861ada7b045a88653b19b3d785dd1b59dae Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Sun, 23 Jan 2022 11:03:40 +0100 Subject: [PATCH 13/19] Adding g_task_set_return_on_cancel. --- src/util/soup.c | 1 - src/view/picture.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/soup.c b/src/util/soup.c index bdc8342..5841a2f 100644 --- a/src/util/soup.c +++ b/src/util/soup.c @@ -54,7 +54,6 @@ mg_util_soup_get_request (MgUtilSoup *self, const char *url, gsize *size_respons g_value_unset (&response); g_clear_object (&soup_session); g_clear_object (&msg); - return return_value; } diff --git a/src/view/picture.c b/src/view/picture.c index 9d47137..5c01dbe 100644 --- a/src/view/picture.c +++ b/src/view/picture.c @@ -126,6 +126,7 @@ create_picture_from_url (const char *const url, gint picture_size, attrs->image = image; attrs->picture_size = picture_size; g_task_set_task_data (task, attrs, free_picture_thread_attributes); + g_task_set_return_on_cancel (task, true); g_task_run_in_thread (task, threaded_picture_recover); } cleanup_create_picture_from_url: From e0c837642b5db5aa54ee6185e4fd513503b0736d Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Sun, 23 Jan 2022 11:35:31 +0100 Subject: [PATCH 14/19] Better yet image download. --- include/openmg/view/controls.h | 1 + include/openmg/view/picture.h | 2 +- src/view/chapter_view.c | 2 +- src/view/detail_manga.c | 2 +- src/view/list_view_manga.c | 9 +++++++-- src/view/main_view.c | 1 + src/view/picture.c | 4 ++-- src/view/search.c | 2 ++ 8 files changed, 16 insertions(+), 7 deletions(-) diff --git a/include/openmg/view/controls.h b/include/openmg/view/controls.h index b8b01ad..9b85571 100644 --- a/include/openmg/view/controls.h +++ b/include/openmg/view/controls.h @@ -11,6 +11,7 @@ typedef struct { AdwViewStack *view_stack; GCancellable **image_threads; size_t image_threads_len; + bool avoid_list_image_downloads; GtkButton *previous; gboolean is_set_previous; } ControlsAdwaita; diff --git a/include/openmg/view/picture.h b/include/openmg/view/picture.h index b2e6d4e..69e273a 100644 --- a/include/openmg/view/picture.h +++ b/include/openmg/view/picture.h @@ -3,4 +3,4 @@ GtkPicture * create_picture_from_url (const char *const url, gint picture_size, GAsyncReadyCallback ready, gpointer source_object, - gpointer callback_data); + gpointer callback_data, bool do_not_download); diff --git a/src/view/chapter_view.c b/src/view/chapter_view.c index 4d5f414..e773de5 100644 --- a/src/view/chapter_view.c +++ b/src/view/chapter_view.c @@ -168,7 +168,7 @@ set_image_zoomable_picture_container (ChapterVisorData *chapter_visor_data) { strlen (url_image_not_owned)); GtkPicture *picture = create_picture_from_url (url_image, 0, picture_ready_manga_page, - zoomable_picture_container, chapter_visor_data); + zoomable_picture_container, chapter_visor_data, false); if (picture) { chapter_visor_data->current_picture = GTK_PICTURE (picture); g_signal_connect (G_OBJECT (picture), "map", diff --git a/src/view/detail_manga.c b/src/view/detail_manga.c index 141fabf..5089218 100644 --- a/src/view/detail_manga.c +++ b/src/view/detail_manga.c @@ -90,7 +90,7 @@ create_detail_view (MgManga *manga, ControlsAdwaita *controls) { GtkListView *chapter_list = NULL; char *url_image = mg_manga_get_image_url(manga); GtkPicture *picture = create_picture_from_url (url_image, 200, - picture_ready_manga_detail, avatar_title_box, NULL); + picture_ready_manga_detail, avatar_title_box, NULL, false); char *manga_title_text = mg_manga_get_title (manga); char *title_text = mg_util_xml_get_title_text ( xml_util, manga_title_text); diff --git a/src/view/list_view_manga.c b/src/view/list_view_manga.c index 9a7c1fd..81047c1 100644 --- a/src/view/list_view_manga.c +++ b/src/view/list_view_manga.c @@ -26,6 +26,7 @@ manga_selected (GtkListView *list_view, guint position, gpointer user_data) { ControlsAdwaita *controls = (ControlsAdwaita *) user_data; + controls->avoid_list_image_downloads = true;; for (size_t i = 0; i < controls->image_threads_len; i++) { g_cancellable_cancel (controls->image_threads[i]); } @@ -52,6 +53,7 @@ manga_selected (GtkListView *list_view, GtkBox *detail_view = create_detail_view (manga, controls); adw_leaflet_append (views_leaflet, GTK_WIDGET (detail_view)); adw_leaflet_navigate (views_leaflet, ADW_NAVIGATION_DIRECTION_FORWARD); + controls->avoid_list_image_downloads = false; } #ifdef LIST_IMAGES @@ -80,10 +82,13 @@ setup_list_view_mangas (GtkSignalListItemFactory *factory, char *image_url = mg_manga_get_image_url (manga); GtkWidget *label = gtk_label_new (manga_title); + GtkPicture *picture = NULL; #ifdef LIST_IMAGES + printf ("%d\n", controls->avoid_list_image_downloads); GCancellable *cancellable = g_cancellable_new (); - GtkPicture *picture = create_picture_from_url (image_url, 100, - picture_ready_manga_preview, box, cancellable); + picture = create_picture_from_url (image_url, 100, + picture_ready_manga_preview, box, cancellable, + controls->avoid_list_image_downloads); controls->image_threads_len++; controls->image_threads = g_realloc (controls->image_threads, controls->image_threads_len * sizeof *(controls->image_threads)); diff --git a/src/view/main_view.c b/src/view/main_view.c index f05ac0d..675566a 100644 --- a/src/view/main_view.c +++ b/src/view/main_view.c @@ -56,6 +56,7 @@ activate (AdwApplication *app, controls->view_stack = view_stack; controls->image_threads_len = 0; controls->image_threads = NULL; + controls->avoid_list_image_downloads = false; views_leaflet_explore = create_explore_leaflet (controls, swipe_back); views_leaflet_search = create_search_leaflet (controls, swipe_back); diff --git a/src/view/picture.c b/src/view/picture.c index 5c01dbe..fc52004 100644 --- a/src/view/picture.c +++ b/src/view/picture.c @@ -96,7 +96,7 @@ free_picture_thread_attributes (gpointer user_data) { GtkPicture * create_picture_from_url (const char *const url, gint picture_size, GAsyncReadyCallback ready, gpointer source_object, - gpointer callback_data) { + gpointer callback_data, bool do_not_download) { GtkPicture *picture = NULL; GFile *image = NULL; GdkTexture *texture = NULL; @@ -118,7 +118,7 @@ create_picture_from_url (const char *const url, gint picture_size, g_object_set_property_int (G_OBJECT(picture), "width-request", picture_size); } - } else { + } else if (!do_not_download) { GTask *task = g_task_new (source_object, NULL, ready, callback_data); PictureThreadAttributes *attrs = g_malloc (sizeof *attrs); attrs->url = g_malloc (url_len * sizeof *url); diff --git a/src/view/search.c b/src/view/search.c index 2bc67c6..dfb1343 100644 --- a/src/view/search.c +++ b/src/view/search.c @@ -59,9 +59,11 @@ search_text_changed (GtkEntry *entry, } controls->image_threads = NULL; controls->image_threads_len = 0; + controls->avoid_list_image_downloads = true; if (!mangas) return; GtkSingleSelection *selection = GTK_SINGLE_SELECTION ( gtk_list_view_get_model (list_view_mangas)); gtk_single_selection_set_model (selection, G_LIST_MODEL (mangas)); + controls->avoid_list_image_downloads = false; } From 81a266fe3a83ca3433b9c26dc16e4570084bc2e8 Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Sun, 23 Jan 2022 11:42:08 +0100 Subject: [PATCH 15/19] Remooving debug statement. --- src/view/list_view_manga.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/view/list_view_manga.c b/src/view/list_view_manga.c index 81047c1..57c370a 100644 --- a/src/view/list_view_manga.c +++ b/src/view/list_view_manga.c @@ -84,7 +84,6 @@ setup_list_view_mangas (GtkSignalListItemFactory *factory, GtkWidget *label = gtk_label_new (manga_title); GtkPicture *picture = NULL; #ifdef LIST_IMAGES - printf ("%d\n", controls->avoid_list_image_downloads); GCancellable *cancellable = g_cancellable_new (); picture = create_picture_from_url (image_url, 100, picture_ready_manga_preview, box, cancellable, From d78d687f6d3f1312f4d2baee4191511dae7ebeed Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Sun, 23 Jan 2022 15:27:37 +0100 Subject: [PATCH 16/19] Fixing unblocking the image download after setting the model. --- src/view/search.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/search.c b/src/view/search.c index dfb1343..9157615 100644 --- a/src/view/search.c +++ b/src/view/search.c @@ -63,7 +63,7 @@ search_text_changed (GtkEntry *entry, if (!mangas) return; GtkSingleSelection *selection = GTK_SINGLE_SELECTION ( gtk_list_view_get_model (list_view_mangas)); + controls->avoid_list_image_downloads = false; gtk_single_selection_set_model (selection, G_LIST_MODEL (mangas)); - controls->avoid_list_image_downloads = false; } From 52378d5623118e636c0013ad087a0a8ff73e089b Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Sun, 23 Jan 2022 15:43:50 +0100 Subject: [PATCH 17/19] Limiting search to 20 mangas to improve performance. --- src/backend/readmng.c | 2 +- src/view/picture.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/readmng.c b/src/backend/readmng.c index f952bda..243bcfa 100644 --- a/src/backend/readmng.c +++ b/src/backend/readmng.c @@ -263,7 +263,7 @@ mg_backend_readmng_search (MgBackendReadmng *self, mangas_json_array = json_node_get_array (root); mangas_json_array_len = json_array_get_length ( mangas_json_array); - for (guint i = 0; i < mangas_json_array_len; i++) { + for (guint i = 0; i < mangas_json_array_len && i < 19; i++) { JsonObject *manga_json_object = json_array_get_object_element (mangas_json_array, i); char *id_manga = NULL; diff --git a/src/view/picture.c b/src/view/picture.c index fc52004..07615f2 100644 --- a/src/view/picture.c +++ b/src/view/picture.c @@ -45,7 +45,7 @@ threaded_picture_recover (GTask *task, gpointer source_object, static GMutex mutex; g_mutex_lock (&mutex); if (!g_file_query_exists (image, NULL)) { - g_warning ("Storing %s", url); + g_info ("Storing %s", url); iostream = g_file_create_readwrite (image, G_FILE_CREATE_NONE, NULL, &error); if (error) { From c54fa2b2e48613e8825db2147637dac0be8440d6 Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Tue, 25 Jan 2022 01:45:27 +0100 Subject: [PATCH 18/19] Adding me.sergiotarxz.openmg.json --- me.sergiotarxz.openmg.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 me.sergiotarxz.openmg.json diff --git a/me.sergiotarxz.openmg.json b/me.sergiotarxz.openmg.json new file mode 100644 index 0000000..e441792 --- /dev/null +++ b/me.sergiotarxz.openmg.json @@ -0,0 +1,30 @@ +{ + "app-id": "me.sergiotarxz.openmg", + "runtime": "org.gnome.Platform", + "runtime-version": "master", + "sdk": "org.gnome.Sdk", + "command": "openmg", + "finish-args": [ + "--share=ipc", + "--socket=x11", + "--socket=wayland", + "--socket=session-bus", + "--share=network", + "--device=dri" + ], + "modules": [ + { + "name": "openmg", + "buildsystem": "meson", + "config-opts": [ + "-Dimages=true" + ], + "sources": [ + { + "type": "dir", + "path": "." + } + ] + } + ] +} From b3cf226554b4d3b3f4f511a7f33a74c70ac700b7 Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Tue, 1 Feb 2022 19:52:58 +0100 Subject: [PATCH 19/19] Adding a donations bitcoin address. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c4ecbaf..01b9257 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,7 @@ flatpak-builder --install --user build me.sergiotarxz.openmg.yml me.sergiotarxz. ```shell flatpak run me.sergiotarxz.openmg ``` + +## Donations welcome: + +btc: `bc1q0apxdedrm5vjn3zr0hxswnruk2x2uecwqrusmj`