mirror of https://github.com/xemu-project/xemu.git
ui: Add auto-updater feature for Windows
This commit is contained in:
parent
23766591c4
commit
90ddc5cce3
|
@ -37,6 +37,7 @@ xemu_ss.add(files(
|
|||
'xemu-hud.cc',
|
||||
'xemu-reporting.cc',
|
||||
))
|
||||
xemu_ss.add(when: 'CONFIG_WIN32', if_true: files('xemu-update.cc'))
|
||||
|
||||
imgui_flags = ['-DIMGUI_IMPL_OPENGL_LOADER_CUSTOM="epoxy/gl.h"']
|
||||
|
||||
|
|
182
ui/xemu-hud.cc
182
ui/xemu-hud.cc
|
@ -35,6 +35,10 @@
|
|||
#include "xemu-xbe.h"
|
||||
#include "xemu-reporting.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "xemu-update.h"
|
||||
#endif
|
||||
|
||||
#include "data/roboto_medium.ttf.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
@ -570,6 +574,9 @@ private:
|
|||
char eeprom_path[MAX_STRING_LEN];
|
||||
int memory_idx;
|
||||
bool short_animation;
|
||||
#if defined(_WIN32)
|
||||
bool check_for_update;
|
||||
#endif
|
||||
|
||||
public:
|
||||
SettingsWindow()
|
||||
|
@ -622,6 +629,11 @@ public:
|
|||
xemu_settings_get_bool(XEMU_SETTINGS_SYSTEM_SHORTANIM, &tmp_int);
|
||||
short_animation = !!tmp_int;
|
||||
|
||||
#if defined(_WIN32)
|
||||
xemu_settings_get_bool(XEMU_SETTINGS_MISC_CHECK_FOR_UPDATE, &tmp_int);
|
||||
check_for_update = !!tmp_int;
|
||||
#endif
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
|
@ -633,6 +645,9 @@ public:
|
|||
xemu_settings_set_string(XEMU_SETTINGS_SYSTEM_EEPROM_PATH, eeprom_path);
|
||||
xemu_settings_set_int(XEMU_SETTINGS_SYSTEM_MEMORY, 64+memory_idx*64);
|
||||
xemu_settings_set_bool(XEMU_SETTINGS_SYSTEM_SHORTANIM, short_animation);
|
||||
#if defined(_WIN32)
|
||||
xemu_settings_set_bool(XEMU_SETTINGS_MISC_CHECK_FOR_UPDATE, check_for_update);
|
||||
#endif
|
||||
xemu_settings_save();
|
||||
xemu_queue_notification("Settings saved! Restart to apply updates.");
|
||||
pending_restart = true;
|
||||
|
@ -716,6 +731,15 @@ public:
|
|||
}
|
||||
ImGui::NextColumn();
|
||||
|
||||
#if defined(_WIN32)
|
||||
ImGui::Dummy(ImVec2(0,0));
|
||||
ImGui::NextColumn();
|
||||
if (ImGui::Checkbox("Check for updates on startup", &check_for_update)) {
|
||||
dirty = true;
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
#endif
|
||||
|
||||
ImGui::Columns(1);
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, ImGui::GetStyle().WindowPadding.y));
|
||||
|
@ -1536,6 +1560,140 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
class AutoUpdateWindow
|
||||
{
|
||||
protected:
|
||||
Updater updater;
|
||||
|
||||
public:
|
||||
bool is_open;
|
||||
bool should_prompt_auto_update_selection;
|
||||
|
||||
AutoUpdateWindow()
|
||||
{
|
||||
is_open = false;
|
||||
should_prompt_auto_update_selection = false;
|
||||
}
|
||||
|
||||
~AutoUpdateWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void save_auto_update_selection(bool preference)
|
||||
{
|
||||
xemu_settings_set_bool(XEMU_SETTINGS_MISC_CHECK_FOR_UPDATE, preference);
|
||||
xemu_settings_save();
|
||||
should_prompt_auto_update_selection = false;
|
||||
}
|
||||
|
||||
void prompt_auto_update_selection()
|
||||
{
|
||||
ImGui::Text("Would you like xemu to check for updates on startup?");
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, ImGui::GetStyle().WindowPadding.y));
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy(ImVec2(0.0f, ImGui::GetStyle().WindowPadding.y));
|
||||
|
||||
float w = (130)*g_ui_scale;
|
||||
float bw = w + (10)*g_ui_scale;
|
||||
ImGui::SetCursorPosX(ImGui::GetWindowWidth()-2*bw);
|
||||
|
||||
if (ImGui::Button("No", ImVec2(w, 0))) {
|
||||
save_auto_update_selection(false);
|
||||
is_open = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Yes", ImVec2(w, 0))) {
|
||||
save_auto_update_selection(true);
|
||||
check_for_updates_and_prompt_if_available();
|
||||
}
|
||||
}
|
||||
|
||||
void check_for_updates_and_prompt_if_available()
|
||||
{
|
||||
updater.check_for_update([this](){
|
||||
is_open |= updater.is_update_available();
|
||||
});
|
||||
}
|
||||
|
||||
void Draw()
|
||||
{
|
||||
if (!is_open) return;
|
||||
ImGui::SetNextWindowContentSize(ImVec2(550.0f*g_ui_scale, 0.0f));
|
||||
if (!ImGui::Begin("Update", &is_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (should_prompt_auto_update_selection) {
|
||||
prompt_auto_update_selection();
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::IsWindowAppearing() && !updater.is_update_available()) {
|
||||
updater.check_for_update();
|
||||
}
|
||||
|
||||
const char *status_msg[] = {
|
||||
"",
|
||||
"An error has occured. Try again.",
|
||||
"Checking for update...",
|
||||
"Downloading update...",
|
||||
"Update successful! Restart to launch updated version of xemu."
|
||||
};
|
||||
const char *available_msg[] = {
|
||||
"Update availability unknown.",
|
||||
"This version of xemu is up to date.",
|
||||
"An updated version of xemu is available!",
|
||||
};
|
||||
|
||||
if (updater.get_status() == UPDATER_IDLE) {
|
||||
ImGui::Text(available_msg[updater.get_update_availability()]);
|
||||
} else {
|
||||
ImGui::Text(status_msg[updater.get_status()]);
|
||||
}
|
||||
|
||||
if (updater.is_updating()) {
|
||||
ImGui::ProgressBar(updater.get_update_progress_percentage()/100.0f,
|
||||
ImVec2(-1.0f, 0.0f));
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, ImGui::GetStyle().WindowPadding.y));
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy(ImVec2(0.0f, ImGui::GetStyle().WindowPadding.y));
|
||||
|
||||
float w = (130)*g_ui_scale;
|
||||
float bw = w + (10)*g_ui_scale;
|
||||
ImGui::SetCursorPosX(ImGui::GetWindowWidth()-bw);
|
||||
|
||||
if (updater.is_checking_for_update() || updater.is_updating()) {
|
||||
if (ImGui::Button("Cancel", ImVec2(w, 0))) {
|
||||
updater.cancel();
|
||||
}
|
||||
} else {
|
||||
if (updater.is_pending_restart()) {
|
||||
if (ImGui::Button("Restart", ImVec2(w, 0))) {
|
||||
updater.restart_to_updated();
|
||||
}
|
||||
} else if (updater.is_update_available()) {
|
||||
if (ImGui::Button("Update", ImVec2(w, 0))) {
|
||||
updater.update();
|
||||
}
|
||||
} else {
|
||||
if (ImGui::Button("Check for Update", ImVec2(w, 0))) {
|
||||
updater.check_for_update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
static MonitorWindow monitor_window;
|
||||
static DebugApuWindow apu_window;
|
||||
static DebugVideoWindow video_window;
|
||||
|
@ -1545,6 +1703,9 @@ static AboutWindow about_window;
|
|||
static SettingsWindow settings_window;
|
||||
static CompatibilityReporter compatibility_reporter_window;
|
||||
static NotificationManager notification_manager;
|
||||
#if defined(_WIN32)
|
||||
static AutoUpdateWindow update_window;
|
||||
#endif
|
||||
static std::deque<const char *> g_errors;
|
||||
|
||||
class FirstBootWindow
|
||||
|
@ -1776,12 +1937,16 @@ static void ShowMainMenu()
|
|||
|
||||
if (ImGui::BeginMenu("Help"))
|
||||
{
|
||||
ImGui::MenuItem("Report Compatibility", NULL, &compatibility_reporter_window.is_open);
|
||||
if (ImGui::MenuItem("Help", NULL))
|
||||
{
|
||||
xemu_open_web_browser("https://github.com/mborgerson/xemu/wiki");
|
||||
}
|
||||
|
||||
ImGui::MenuItem("Report Compatibility...", NULL, &compatibility_reporter_window.is_open);
|
||||
#if defined(_WIN32)
|
||||
ImGui::MenuItem("Check for Updates...", NULL, &update_window.is_open);
|
||||
#endif
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("About", NULL, &about_window.is_open);
|
||||
ImGui::EndMenu();
|
||||
|
@ -1911,6 +2076,18 @@ void xemu_hud_init(SDL_Window* window, void* sdl_gl_context)
|
|||
g_sdl_window = window;
|
||||
|
||||
ImPlot::CreateContext();
|
||||
|
||||
#if defined(_WIN32)
|
||||
int should_check_for_update;
|
||||
xemu_settings_get_bool(XEMU_SETTINGS_MISC_CHECK_FOR_UPDATE, &should_check_for_update);
|
||||
if (should_check_for_update == -1) {
|
||||
update_window.should_prompt_auto_update_selection =
|
||||
update_window.is_open = !xemu_settings_did_fail_to_load();
|
||||
|
||||
} else if (should_check_for_update) {
|
||||
update_window.check_for_updates_and_prompt_if_available();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void xemu_hud_cleanup(void)
|
||||
|
@ -2076,6 +2253,9 @@ void xemu_hud_render(void)
|
|||
network_window.Draw();
|
||||
compatibility_reporter_window.Draw();
|
||||
notification_manager.Draw();
|
||||
#if defined(_WIN32)
|
||||
update_window.Draw();
|
||||
#endif
|
||||
|
||||
// Very rudimentary error notification API
|
||||
if (g_errors.size() > 0) {
|
||||
|
|
|
@ -68,6 +68,7 @@ struct xemu_settings {
|
|||
|
||||
// [misc]
|
||||
char *user_token;
|
||||
int check_for_update; // Boolean
|
||||
};
|
||||
|
||||
struct enum_str_map {
|
||||
|
@ -125,7 +126,8 @@ struct config_offset_table {
|
|||
[XEMU_SETTINGS_NETWORK_LOCAL_ADDR] = { CONFIG_TYPE_STRING, "network", "local_addr", offsetof(struct xemu_settings, net_local_addr), { .default_str = "0.0.0.0:9368" } },
|
||||
[XEMU_SETTINGS_NETWORK_REMOTE_ADDR] = { CONFIG_TYPE_STRING, "network", "remote_addr", offsetof(struct xemu_settings, net_remote_addr), { .default_str = "1.2.3.4:9368" } },
|
||||
|
||||
[XEMU_SETTINGS_MISC_USER_TOKEN] = { CONFIG_TYPE_STRING, "misc", "user_token", offsetof(struct xemu_settings, user_token), { .default_str = "" } },
|
||||
[XEMU_SETTINGS_MISC_USER_TOKEN] = { CONFIG_TYPE_STRING, "misc", "user_token", offsetof(struct xemu_settings, user_token), { .default_str = "" } },
|
||||
[XEMU_SETTINGS_MISC_CHECK_FOR_UPDATE] = { CONFIG_TYPE_BOOL, "misc", "check_for_update", offsetof(struct xemu_settings, check_for_update), { .default_bool = -1 } },
|
||||
};
|
||||
|
||||
static const char *settings_path;
|
||||
|
@ -312,6 +314,8 @@ static int config_parse_callback(void *user, const char *section, const char *na
|
|||
int_val = 1;
|
||||
} else if (strcmp(value, "false") == 0) {
|
||||
int_val = 0;
|
||||
} else if (strcmp(value, "") == 0) {
|
||||
return 1;
|
||||
} else {
|
||||
fprintf(stderr, "Error parsing %s.%s as boolean. Got '%s'\n", section, name, value);
|
||||
return 0;
|
||||
|
@ -401,7 +405,11 @@ int xemu_settings_save(void)
|
|||
} else if (config_items[i].type == CONFIG_TYPE_BOOL) {
|
||||
int v;
|
||||
xemu_settings_get_bool(i, &v);
|
||||
fprintf(fd, "%s\n", !!(v) ? "true" : "false");
|
||||
if (v == 0 || v == 1) {
|
||||
fprintf(fd, "%s\n", !!(v) ? "true" : "false");
|
||||
} else {
|
||||
// Other values are considered unset
|
||||
}
|
||||
} else if (config_items[i].type == CONFIG_TYPE_ENUM) {
|
||||
int v;
|
||||
xemu_settings_get_enum(i, &v);
|
||||
|
|
|
@ -49,6 +49,7 @@ enum xemu_settings_keys {
|
|||
XEMU_SETTINGS_NETWORK_LOCAL_ADDR,
|
||||
XEMU_SETTINGS_NETWORK_REMOTE_ADDR,
|
||||
XEMU_SETTINGS_MISC_USER_TOKEN,
|
||||
XEMU_SETTINGS_MISC_CHECK_FOR_UPDATE,
|
||||
XEMU_SETTINGS__COUNT,
|
||||
XEMU_SETTINGS_INVALID = -1
|
||||
};
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* xemu Automatic Update
|
||||
*
|
||||
* Copyright (C) 2021 Matt Borgerson
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <SDL_filesystem.h>
|
||||
|
||||
#include "util/miniz/miniz.h"
|
||||
|
||||
#include "xemu-update.h"
|
||||
#include "xemu-version.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
const char *version_host = "raw.githubusercontent.com";
|
||||
const char *version_uri = "/mborgerson/xemu/ppa-snapshot/XEMU_VERSION";
|
||||
const char *download_host = "github.com";
|
||||
const char *download_uri = "/mborgerson/xemu/releases/latest/download/xemu-win-release.zip";
|
||||
#else
|
||||
FIXME
|
||||
#endif
|
||||
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT 1
|
||||
#include "httplib.h"
|
||||
|
||||
#define DPRINTF(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__);
|
||||
|
||||
Updater::Updater()
|
||||
{
|
||||
m_status = UPDATER_IDLE;
|
||||
m_update_availability = UPDATE_AVAILABILITY_UNKNOWN;
|
||||
m_update_percentage = 0;
|
||||
m_latest_version = "Unknown";
|
||||
m_should_cancel = false;
|
||||
}
|
||||
|
||||
void Updater::check_for_update(UpdaterCallback on_complete)
|
||||
{
|
||||
if (m_status == UPDATER_IDLE || m_status == UPDATER_ERROR) {
|
||||
m_on_complete = on_complete;
|
||||
qemu_thread_create(&m_thread, "update_worker",
|
||||
&Updater::checker_thread_worker_func,
|
||||
this, QEMU_THREAD_JOINABLE);
|
||||
}
|
||||
}
|
||||
|
||||
void *Updater::checker_thread_worker_func(void *updater)
|
||||
{
|
||||
((Updater *)updater)->check_for_update_internal();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Updater::check_for_update_internal()
|
||||
{
|
||||
httplib::SSLClient cli(version_host, 443);
|
||||
cli.set_follow_location(true);
|
||||
cli.set_timeout_sec(5);
|
||||
auto res = cli.Get(version_uri, [this](uint64_t len, uint64_t total) {
|
||||
m_update_percentage = len*100/total;
|
||||
return !m_should_cancel;
|
||||
});
|
||||
if (m_should_cancel) {
|
||||
m_should_cancel = false;
|
||||
m_status = UPDATER_IDLE;
|
||||
goto finished;
|
||||
} else if (!res || res->status != 200) {
|
||||
m_status = UPDATER_ERROR;
|
||||
goto finished;
|
||||
}
|
||||
|
||||
if (strcmp(xemu_version, res->body.c_str())) {
|
||||
m_update_availability = UPDATE_AVAILABLE;
|
||||
} else {
|
||||
m_update_availability = UPDATE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
m_latest_version = res->body;
|
||||
m_status = UPDATER_IDLE;
|
||||
finished:
|
||||
if (m_on_complete) {
|
||||
m_on_complete();
|
||||
}
|
||||
}
|
||||
|
||||
void Updater::update()
|
||||
{
|
||||
if (m_status == UPDATER_IDLE || m_status == UPDATER_ERROR) {
|
||||
m_status = UPDATER_UPDATING;
|
||||
qemu_thread_create(&m_thread, "update_worker",
|
||||
&Updater::update_thread_worker_func,
|
||||
this, QEMU_THREAD_JOINABLE);
|
||||
}
|
||||
}
|
||||
|
||||
void *Updater::update_thread_worker_func(void *updater)
|
||||
{
|
||||
((Updater *)updater)->update_internal();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Updater::update_internal()
|
||||
{
|
||||
httplib::SSLClient cli(download_host, 443);
|
||||
cli.set_follow_location(true);
|
||||
cli.set_timeout_sec(5);
|
||||
auto res = cli.Get(download_uri, [this](uint64_t len, uint64_t total) {
|
||||
m_update_percentage = len*100/total;
|
||||
return !m_should_cancel;
|
||||
});
|
||||
|
||||
if (m_should_cancel) {
|
||||
m_should_cancel = false;
|
||||
m_status = UPDATER_IDLE;
|
||||
return;
|
||||
} else if (!res || res->status != 200) {
|
||||
m_status = UPDATER_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
mz_zip_archive zip;
|
||||
mz_zip_zero_struct(&zip);
|
||||
if (!mz_zip_reader_init_mem(&zip, res->body.data(), res->body.size(), 0)) {
|
||||
DPRINTF("mz_zip_reader_init_mem failed\n");
|
||||
m_status = UPDATER_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
mz_uint num_files = mz_zip_reader_get_num_files(&zip);
|
||||
for (mz_uint file_idx = 0; file_idx < num_files; file_idx++) {
|
||||
mz_zip_archive_file_stat fstat;
|
||||
if (!mz_zip_reader_file_stat(&zip, file_idx, &fstat)) {
|
||||
DPRINTF("mz_zip_reader_file_stat failed for file #%d\n", file_idx);
|
||||
goto errored;
|
||||
}
|
||||
|
||||
if (fstat.m_filename[strlen(fstat.m_filename)-1] == '/') {
|
||||
/* FIXME: mkdirs */
|
||||
DPRINTF("FIXME: subdirs not handled yet\n");
|
||||
goto errored;
|
||||
}
|
||||
|
||||
char *dst_path = g_strdup_printf("%s%s", SDL_GetBasePath(), fstat.m_filename);
|
||||
DPRINTF("extracting %s to %s\n", fstat.m_filename, dst_path);
|
||||
|
||||
if (!strcmp(fstat.m_filename, "xemu.exe")) {
|
||||
// We cannot overwrite current executable, but we can move it
|
||||
char *renamed_path = g_strdup_printf("%s%s", SDL_GetBasePath(), "xemu-previous.exe");
|
||||
MoveFileExA(dst_path, renamed_path, MOVEFILE_REPLACE_EXISTING);
|
||||
g_free(renamed_path);
|
||||
}
|
||||
|
||||
if (!mz_zip_reader_extract_to_file(&zip, file_idx, dst_path, 0)) {
|
||||
DPRINTF("mz_zip_reader_extract_to_file failed to create %s\n", dst_path);
|
||||
g_free(dst_path);
|
||||
goto errored;
|
||||
}
|
||||
|
||||
g_free(dst_path);
|
||||
}
|
||||
|
||||
m_status = UPDATER_UPDATE_SUCCESSFUL;
|
||||
goto cleanup_zip;
|
||||
errored:
|
||||
m_status = UPDATER_ERROR;
|
||||
cleanup_zip:
|
||||
mz_zip_reader_end(&zip);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern char **gArgv;
|
||||
}
|
||||
|
||||
void Updater::restart_to_updated()
|
||||
{
|
||||
char *target_exec = g_strdup_printf("%s%s", SDL_GetBasePath(), "xemu.exe");
|
||||
DPRINTF("Restarting to updated executable %s\n", target_exec);
|
||||
_execv(target_exec, gArgv);
|
||||
DPRINTF("Launching updated executable failed\n");
|
||||
exit(1);
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* xemu Automatic Update
|
||||
*
|
||||
* Copyright (C) 2021 Matt Borgerson
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XEMU_UPDATE_H
|
||||
#define XEMU_UPDATE_H
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include <functional>
|
||||
|
||||
extern "C" {
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/thread.h"
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
UPDATE_AVAILABILITY_UNKNOWN,
|
||||
UPDATE_NOT_AVAILABLE,
|
||||
UPDATE_AVAILABLE
|
||||
} UpdateAvailability;
|
||||
|
||||
typedef enum {
|
||||
UPDATER_IDLE,
|
||||
UPDATER_ERROR,
|
||||
UPDATER_CHECKING_FOR_UPDATE,
|
||||
UPDATER_UPDATING,
|
||||
UPDATER_UPDATE_SUCCESSFUL
|
||||
} UpdateStatus;
|
||||
|
||||
using UpdaterCallback = std::function<void(void)>;
|
||||
|
||||
class Updater {
|
||||
private:
|
||||
UpdateAvailability m_update_availability;
|
||||
int m_update_percentage;
|
||||
QemuThread m_thread;
|
||||
std::string m_latest_version;
|
||||
bool m_should_cancel;
|
||||
UpdateStatus m_status;
|
||||
UpdaterCallback m_on_complete;
|
||||
|
||||
public:
|
||||
Updater();
|
||||
UpdateStatus get_status() { return m_status; }
|
||||
UpdateAvailability get_update_availability() { return m_update_availability; }
|
||||
bool is_errored() { return m_status == UPDATER_ERROR; }
|
||||
bool is_pending_restart() { return m_status == UPDATER_UPDATE_SUCCESSFUL; }
|
||||
bool is_update_available() { return m_update_availability == UPDATE_AVAILABLE; }
|
||||
bool is_checking_for_update() { return m_status == UPDATER_CHECKING_FOR_UPDATE; }
|
||||
bool is_updating() { return m_status == UPDATER_UPDATING; }
|
||||
std::string get_update_version() { return m_latest_version; }
|
||||
void cancel() { m_should_cancel = true; }
|
||||
void update();
|
||||
void update_internal();
|
||||
void check_for_update(UpdaterCallback on_complete = nullptr);
|
||||
void check_for_update_internal();
|
||||
int get_update_progress_percentage() { return m_update_percentage; }
|
||||
static void *update_thread_worker_func(void *updater);
|
||||
static void *checker_thread_worker_func(void *updater);
|
||||
void restart_to_updated(void);
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue