Skip to content

Commit

Permalink
Merge branch 'version_check_separate_thread' into 'main'
Browse files Browse the repository at this point in the history
Version check should be done in a separate thread

See merge request melroy/winegui!34
  • Loading branch information
melroy89 committed May 21, 2024
2 parents 318ee22 + e108bc3 commit 3bbb9cb
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 27 deletions.
26 changes: 22 additions & 4 deletions include/main_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <iostream>
#include <list>
#include <string>
#include <thread>

using std::cout;
using std::endl;
Expand Down Expand Up @@ -91,10 +92,13 @@ class MainWindow : public Gtk::Window
protected:
// Signal handlers
void on_startup_version_update();
bool delete_window(GdkEventAny* any_event);
Glib::RefPtr<Gio::Settings> window_settings; /*!< Window settings to store our window settings, even during restarts */
void on_error_message_check_version();
void on_info_message_check_version();
void on_new_version_available();
bool on_delete_window(GdkEventAny* any_event);

AppListModelColumns app_list_columns; /*!< Application list model columns for app tree view */
Glib::RefPtr<Gio::Settings> window_settings; /*!< Window settings to store our window settings, even during restarts */
AppListModelColumns app_list_columns; /*!< Application list model columns for app tree view */

// Child widgets
Gtk::Box vbox; /*!< The main vertical box */
Expand Down Expand Up @@ -154,8 +158,20 @@ class MainWindow : public Gtk::Window
// Busy dialog
BusyDialog busy_dialog_; /*!< Busy dialog, when the user should wait until install is finished */
private:
mutable std::mutex info_message_mutex_; /*!< Synchronizes access to info message using mutex */
mutable std::mutex error_message_mutex_; /*!< Synchronizes access to error message using mutex */
mutable std::mutex new_version_mutex_; /*!< Synchronizes access to new version using mutex */
Glib::ustring info_message_;
Glib::ustring error_message_;
string new_version_;
BottleNewAssistant new_bottle_assistant_; /*!< New bottle wizard (behind the "new" toolbar button) */
GeneralConfigData general_config_data_;
std::thread* thread_check_version_; /*!< Thread for checking version */
// Dispatchers for handling signals from the thread towards a GUI thread
Glib::Dispatcher error_message_check_version_dispatcher_;
Glib::Dispatcher info_message_check_version_dispatcher_;
Glib::Dispatcher new_version_available_dispatcher_;
Glib::Dispatcher check_version_finished_dispatcher_;

// Signal handlers
virtual void on_bottle_row_clicked(Gtk::ListBoxRow* row);
Expand All @@ -167,7 +183,9 @@ class MainWindow : public Gtk::Window
void set_detailed_info(const BottleItem& bottle);
void set_application_list(const string& prefix_path, const std::map<int, ApplicationData>& app_List);
void add_application(const string& name, const string& description, const string& command, const string& icon_name, bool is_icon_full_path = false);
void check_version_update(bool show_equal = false);
void cleanup_check_version_thread();
void check_version_update(bool show_equal_or_error = false);
void check_version(bool show_equal_or_error);
void load_stored_window_settings();
void create_left_panel();
void create_right_panel();
Expand Down
2 changes: 1 addition & 1 deletion include/signal_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class SignalController : public Gtk::Window
AddAppWindow& add_app_window_;
RemoveAppWindow& remove_app_window_;

// Dispatcher for handling signals from the thread towards a GUI thread
// Dispatchers for handling signals from the thread towards a GUI thread
Glib::Dispatcher bottle_created_dispatcher_;
Glib::Dispatcher bottle_updated_dispatcher_;
Glib::Dispatcher bottle_cloned_dispatcher_;
Expand Down
141 changes: 121 additions & 20 deletions src/main_window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ MainWindow::MainWindow(Menu& menu)
app_list_top_hbox(Gtk::Orientation::ORIENTATION_HORIZONTAL),
container_paned(Gtk::Orientation::ORIENTATION_HORIZONTAL),
separator1(Gtk::Orientation::ORIENTATION_HORIZONTAL),
busy_dialog_(*this)
busy_dialog_(*this),
thread_check_version_(nullptr)
{
// Set some Window properties
set_title("WineGUI - WINE Manager");
Expand Down Expand Up @@ -118,10 +119,16 @@ MainWindow::MainWindow(Menu& menu)
remove_app_list_button.signal_clicked().connect(show_remove_app_window);
refresh_app_list_button.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_refresh_app_list_button_clicked));

// Dispatch signals
error_message_check_version_dispatcher_.connect(sigc::mem_fun(this, &MainWindow::on_error_message_check_version));
info_message_check_version_dispatcher_.connect(sigc::mem_fun(this, &MainWindow::on_info_message_check_version));
new_version_available_dispatcher_.connect(sigc::mem_fun(this, &MainWindow::on_new_version_available));
check_version_finished_dispatcher_.connect(sigc::mem_fun(this, &MainWindow::cleanup_check_version_thread));

// Check for update (when GTK is idle)
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &MainWindow::on_startup_version_update), Glib::PRIORITY_DEFAULT_IDLE);
// Window closed signal
signal_delete_event().connect(sigc::mem_fun(this, &MainWindow::delete_window));
signal_delete_event().connect(sigc::mem_fun(this, &MainWindow::on_delete_window));

// Show the widget children
show_all_children();
Expand All @@ -132,6 +139,8 @@ MainWindow::MainWindow(Menu& menu)
*/
MainWindow::~MainWindow()
{
// Avoid zombies
this->cleanup_check_version_thread();
}

/**
Expand Down Expand Up @@ -498,13 +507,62 @@ void MainWindow::on_new_bottle_apply()
*/
void MainWindow::on_startup_version_update()
{
check_version_update(); // Without message when versions are equal
// Silent check
check_version_update();
}

/**
* \brief Show error messages from the version check thread
*/
void MainWindow::on_error_message_check_version()
{
this->cleanup_check_version_thread();

{
std::lock_guard<std::mutex> lock(error_message_mutex_);
show_error_message(error_message_);
}
}

/**
* \brief Show info messages from the version check thread
*/
void MainWindow::on_info_message_check_version()
{
this->cleanup_check_version_thread();

{
std::lock_guard<std::mutex> lock(info_message_mutex_);
show_info_message(info_message_);
}
}

/**
* \brief Show new version available dialog from the version check thread
*/
void MainWindow::on_new_version_available()
{
this->cleanup_check_version_thread();

// Show dialog
{
std::lock_guard<std::mutex> lock(new_version_mutex_);
string message = "<b>New WineGUI release is out.</b> Please, <i>update</i> WineGUI to the latest release.\n"
"You are using: v" +
std::string(PROJECT_VER) + ". Latest version: v" + new_version_ + ".";
Gtk::MessageDialog dialog(*this, message, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK);
dialog.set_secondary_text("<big><a href=\"https://gitlab.melroy.org/melroy/winegui/-/releases\">Download the latest release now!</a></big>",
true);
dialog.set_title("New WineGUI Release!");
dialog.set_modal(true);
dialog.run();
}
}

/**
* \brief Called when Window is closed/exited
*/
bool MainWindow::delete_window(GdkEventAny* any_event __attribute__((unused)))
bool MainWindow::on_delete_window(GdkEventAny* any_event __attribute__((unused)))
{
if (window_settings)
{
Expand Down Expand Up @@ -755,45 +813,88 @@ void MainWindow::add_application(const string& name, const string& description,
}
}

/**
* \brief Helper method for cleaning the manage thread.
*/
void MainWindow::cleanup_check_version_thread()
{
if (thread_check_version_)
{
if (thread_check_version_->joinable())
thread_check_version_->join();
delete thread_check_version_;
thread_check_version_ = nullptr;
}
}

/**
* \brief Check for WineGUI version, is there an update?
* \param show_equal Also show user message when the versions matches.
* \param show_equal_or_error Also show message when the versions matches or an error occurs.
*/
void MainWindow::check_version_update(bool show_equal)
void MainWindow::check_version_update(bool show_equal_or_error)
{
if (thread_check_version_)
{
if (show_equal_or_error)
{
show_error_message("WineGUI version check is stilling running. Please try again later.");
}
}
else
{
// Start the check version thread
thread_check_version_ = new std::thread([this, show_equal_or_error] { check_version(show_equal_or_error); });
}
}

/**
* \brief Check WineGUI version (runs in thread)
* \param[in] show_equal_or_error Also show message when the versions matches or an error occurs.
*/
void MainWindow::check_version(bool show_equal_or_error)
{
string version = Helper::open_file_from_uri("https://winegui.melroy.org/latest_release.txt");
// Remove new lines
version.erase(std::remove(version.begin(), version.end(), '\n'), version.end());
if (!version.empty())
{
// Is there a different version? Show the user the message to update to the latest release.
// Is there a different version? Signal a new version available.
if (version.compare(PROJECT_VER) != 0)
{
string message = "<b>New WineGUI release is out.</b> Please, <i>update</i> WineGUI to the latest release.\n"
"You are using: v" +
std::string(PROJECT_VER) + ". Latest version: v" + version + ".";
Gtk::MessageDialog dialog(*this, message, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK);
dialog.set_secondary_text("<big><a href=\"https://gitlab.melroy.org/melroy/winegui/-/releases\">Download the latest release now!</a></big>",
true);
dialog.set_title("New WineGUI Release!");
dialog.set_modal(true);
dialog.run();
{
std::lock_guard<std::mutex> lock(new_version_mutex_);
new_version_ = version;
}
this->new_version_available_dispatcher_.emit(); // Will eventually show a dialog
return;
}
else
{
if (show_equal)
if (show_equal_or_error)
{
show_info_message("WineGUI release is up-to-date. Well done!");

{
std::lock_guard<std::mutex> lock(info_message_mutex_);
info_message_ = "WineGUI release is up-to-date. Well done!";
}
this->info_message_check_version_dispatcher_.emit();
return;
}
}
}
else
{
if (show_equal)
if (show_equal_or_error)
{
show_error_message("We could determine the latest WineGUI version. Try again later.");
{
std::lock_guard<std::mutex> lock(error_message_mutex_);
error_message_ = "We could not determine the latest WineGUI version. Try again later.";
}
this->error_message_check_version_dispatcher_.emit();
return;
}
}
this->check_version_finished_dispatcher_.emit(); // Clean-up the thread pointer
}

/**
Expand Down
2 changes: 0 additions & 2 deletions src/signal_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ SignalController::SignalController(BottleManager& manager,
configure_window_(configure_window),
add_app_window_(add_app_window),
remove_app_window_(remove_app_window),
bottle_created_dispatcher_(),
error_message_created_dispatcher_(),
thread_bottle_manager_(nullptr)
{
// Nothing
Expand Down

0 comments on commit 3bbb9cb

Please sign in to comment.