diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:51:45 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:51:45 +0300 |
| commit | d3261e64152397db2dca4d691a990c6bc2a6f4dd (patch) | |
| tree | fac2f7be638651181a72453d714f0f96675c2b8b /archived/projt-launcher/launcher/updater/MacSparkleUpdater.mm | |
| parent | 31b9a8949ed0a288143e23bf739f2eb64fdc63be (diff) | |
| download | Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.tar.gz Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.zip | |
NOISSUE add archived projects
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'archived/projt-launcher/launcher/updater/MacSparkleUpdater.mm')
| -rw-r--r-- | archived/projt-launcher/launcher/updater/MacSparkleUpdater.mm | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/archived/projt-launcher/launcher/updater/MacSparkleUpdater.mm b/archived/projt-launcher/launcher/updater/MacSparkleUpdater.mm new file mode 100644 index 0000000000..82de1074ee --- /dev/null +++ b/archived/projt-launcher/launcher/updater/MacSparkleUpdater.mm @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * Copyright (C) 2026 Project Tick + * + * 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, version 3. + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * === Upstream License Block (Do Not Modify) ============================== + * + * + * + * + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Kenneth Chew <kenneth.c0@protonmail.com> + * + * 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, version 3. + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + * + * ======================================================================== */ + +#include "MacSparkleUpdater.h" + +#include "Application.h" +#include "BuildConfig.h" + +#include <Cocoa/Cocoa.h> +#include <Sparkle/Sparkle.h> + +@interface UpdaterObserver : NSObject + +@property(nonatomic, readonly) SPUUpdater* updater; + +/// A callback to run when the state of `canCheckForUpdates` for the `updater` changes. +@property(nonatomic, copy) void (^callback)(bool); + +- (id)initWithUpdater:(SPUUpdater*)updater; + +@end + +@implementation UpdaterObserver + +- (id)initWithUpdater:(SPUUpdater*)updater { + self = [super init]; + _updater = updater; + [self addObserver:self forKeyPath:@"updater.canCheckForUpdates" options:NSKeyValueObservingOptionNew context:nil]; + + return self; +} + +- (void)observeValueForKeyPath:(NSString*)keyPath + ofObject:(id)object + change:(NSDictionary<NSKeyValueChangeKey, id>*)change + context:(void*)context { + if ([keyPath isEqualToString:@"updater.canCheckForUpdates"]) { + bool canCheck = [change[NSKeyValueChangeNewKey] boolValue]; + self.callback(canCheck); + } +} + +@end + +@interface UpdaterDelegate : NSObject <SPUUpdaterDelegate> + +@property(nonatomic, copy) NSSet<NSString*>* allowedChannels; + +@end + +@implementation UpdaterDelegate + +- (NSSet<NSString*>*)allowedChannelsForUpdater:(SPUUpdater*)updater { + return _allowedChannels; +} + +@end + +class MacSparkleUpdater::Private { + public: + SPUStandardUpdaterController* updaterController; + UpdaterObserver* updaterObserver; + UpdaterDelegate* updaterDelegate; + NSAutoreleasePool* autoReleasePool; + QString lineChannel; + QString migrationChannel; +}; + +MacSparkleUpdater::MacSparkleUpdater() { + priv = new MacSparkleUpdater::Private(); + + // Enable Cocoa's memory management. + NSApplicationLoad(); + priv->autoReleasePool = [[NSAutoreleasePool alloc] init]; + + // Delegate is used for setting/getting allowed update channels. + priv->updaterDelegate = [[UpdaterDelegate alloc] init]; + + // Controller is the interface for actually doing the updates. + priv->updaterController = [[SPUStandardUpdaterController alloc] initWithStartingUpdater:true + updaterDelegate:priv->updaterDelegate + userDriverDelegate:nil]; + + priv->updaterObserver = [[UpdaterObserver alloc] initWithUpdater:priv->updaterController.updater]; + // Use KVO to run a callback that emits a Qt signal when `canCheckForUpdates` changes, so the UI can respond + // accordingly. + priv->updaterObserver.callback = ^(bool canCheck) { + emit canCheckForUpdatesChanged(canCheck); + }; + + auto extractLineChannel = [](QString version) -> QString { + version = version.trimmed(); + if (version.startsWith(QLatin1Char('v'), Qt::CaseInsensitive)) { + version.remove(0, 1); + } + while (version.endsWith(QLatin1Char('.'))) { + version.chop(1); + } + + if (version.contains(QLatin1Char('-'))) { + return version.section(QLatin1Char('-'), 0, 0); + } + + const auto parts = version.split(QLatin1Char('.'), Qt::KeepEmptyParts); + if (parts.size() >= 3) { + return QString("%1.%2.%3").arg(parts.at(0), parts.at(1), parts.at(2)); + } + return QString(); + }; + + priv->lineChannel = extractLineChannel(BuildConfig.printableVersionString()); + if (!priv->lineChannel.isEmpty()) { + const auto parts = priv->lineChannel.split(QLatin1Char('.'), Qt::KeepEmptyParts); + if (parts.size() == 3) { + bool ok = false; + const auto z = parts.at(2).toInt(&ok); + if (ok) { + priv->migrationChannel = QString("%1.%2.%3").arg(parts.at(0), parts.at(1)).arg(z + 1); + } + } + + QSet<QString> channels{priv->lineChannel}; + if (!priv->migrationChannel.isEmpty()) { + channels.insert(priv->migrationChannel); + } + setAllowedChannels(channels); + } +} + +MacSparkleUpdater::~MacSparkleUpdater() { + [priv->updaterObserver removeObserver:priv->updaterObserver forKeyPath:@"updater.canCheckForUpdates"]; + + [priv->updaterController release]; + [priv->updaterObserver release]; + [priv->updaterDelegate release]; + [priv->autoReleasePool release]; + delete priv; +} + +void MacSparkleUpdater::checkForUpdates() { + [priv->updaterController checkForUpdates:nil]; +} + +bool MacSparkleUpdater::getAutomaticallyChecksForUpdates() { + return priv->updaterController.updater.automaticallyChecksForUpdates; +} + +double MacSparkleUpdater::getUpdateCheckInterval() { + return priv->updaterController.updater.updateCheckInterval; +} + +QSet<QString> MacSparkleUpdater::getAllowedChannels() { + // Convert NSSet<NSString> -> QSet<QString> + __block QSet<QString> channels; + [priv->updaterDelegate.allowedChannels enumerateObjectsUsingBlock:^(NSString* channel, BOOL* stop) { + channels.insert(QString::fromNSString(channel)); + }]; + return channels; +} + +bool MacSparkleUpdater::getBetaAllowed() { + return getAllowedChannels().contains("beta"); +} + +void MacSparkleUpdater::setAutomaticallyChecksForUpdates(bool check) { + priv->updaterController.updater.automaticallyChecksForUpdates = check ? YES : NO; // make clang-tidy happy +} + +void MacSparkleUpdater::setUpdateCheckInterval(double seconds) { + priv->updaterController.updater.updateCheckInterval = seconds; +} + +void MacSparkleUpdater::clearAllowedChannels() { + priv->updaterDelegate.allowedChannels = [NSSet set]; +} + +void MacSparkleUpdater::setAllowedChannel(const QString& channel) { + if (channel.isEmpty()) { + clearAllowedChannels(); + return; + } + + NSSet<NSString*>* nsChannels = [NSSet setWithObject:channel.toNSString()]; + priv->updaterDelegate.allowedChannels = nsChannels; +} + +void MacSparkleUpdater::setAllowedChannels(const QSet<QString>& channels) { + if (channels.isEmpty()) { + clearAllowedChannels(); + return; + } + + QString channelsConfig = ""; + // Convert QSet<QString> -> NSSet<NSString> + NSMutableSet<NSString*>* nsChannels = [NSMutableSet setWithCapacity:channels.count()]; + for (const QString& channel : channels) { + [nsChannels addObject:channel.toNSString()]; + channelsConfig += channel + " "; + } + + priv->updaterDelegate.allowedChannels = nsChannels; +} + +void MacSparkleUpdater::setBetaAllowed(bool allowed) { + if (priv->lineChannel.isEmpty()) { + if (allowed) { + setAllowedChannel("beta"); + } else { + clearAllowedChannels(); + } + return; + } + + QSet<QString> channels{priv->lineChannel}; + if (!priv->migrationChannel.isEmpty()) { + channels.insert(priv->migrationChannel); + } + if (allowed) { + channels.insert("beta"); + } + setAllowedChannels(channels); +} |
