Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,3 @@ Standard: "c++17"
TabWidth: 4
UseTab: Never
ForEachMacros: [ EL_FEATURES_FOREACH, BOOST_FOREACH ]

---
Language: ObjC
BasedOnStyle: Chromium
AlignTrailingComments: true
BreakBeforeBraces: Allman
ColumnLimit: 0
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PointerAlignment: Left
SpacesBeforeTrailingComments: 1
TabWidth: 4
UseTab: Never
...
18 changes: 18 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@
- Maintain consistency with the existing codebase style and patterns.
- Consider maintainability and future developers who will read the code.

## Documentation

- Use Doxygen-style comments with `/** ... */` for documenting classes, functions, and methods.
- Include a brief description, parameter documentation with `@param`, and return value documentation with `@return`.
- Document what the function does, not how it does it (implementation details belong in inline comments).
- Example:
```cpp
/** Checks if the application can safely shut down.

Determines whether there are any unsaved changes in the current session
that would be lost on shutdown.

@return true if there are no pending session changes and shutdown can proceed,
false if the session has unsaved changes
*/
static bool canShutdown();
```

## Audio Graph Architecture

- **IONodes** (audio/MIDI input/output) require a parent `GraphNode` to be set before ports can be properly initialized.
Expand Down
2 changes: 1 addition & 1 deletion cmake/FindSparkle.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ string(APPEND ELEMENT_APP_PLIST_TO_MERGE "<key>SUFeedURL</key>")
string(APPEND ELEMENT_APP_PLIST_TO_MERGE "<string>${SPARKLE_FEED_URL}</string>")
string(APPEND ELEMENT_APP_PLIST_TO_MERGE "<key>SUPublicEDKey</key>")
string(APPEND ELEMENT_APP_PLIST_TO_MERGE "<string>${SPARKLE_PUBLIC_KEY}</string>")
string(APPEND ELEMENT_APP_PLIST_TO_MERGE "</dict></plist>")
string(APPEND ELEMENT_APP_PLIST_TO_MERGE "<key>SUEnableAutomaticChecks</key>")
string(APPEND ELEMENT_APP_PLIST_TO_MERGE "<false/>")
string(APPEND ELEMENT_APP_PLIST_TO_MERGE "<key>SUScheduledCheckInterval</key>")
string(APPEND ELEMENT_APP_PLIST_TO_MERGE "<integer>0</integer>")
string(APPEND ELEMENT_APP_PLIST_TO_MERGE "</dict></plist>")
25 changes: 25 additions & 0 deletions docs/sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Sparkle Development
Tips and tricks when working on the Sparkle updater for macOS

## Setup

## Clearning Settings

**Forcing Info.plist generation**

Some of the Sparkle settings live in Element's `Info.plist`. To force it's recreation and be assured it actually did so. Delete the artefacts created by JUCE, run cmake, and rebuild.

```sh
cd build
rm -rf element_app_artefacts && cmake .. && ninja
```

**Make sparkle think it is a "first run"**

```sh
defaults delete net.kushview.Element
rm -rf ~/Library/Caches/net.kushview.Element
```

# Win Sparkle Development
Tips and tricks when working on the WinSparkle updater for Windows
11 changes: 10 additions & 1 deletion include/element/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ class Application : public juce::JUCEApplication,
*/
void finishLaunching();

/** Checks if the application can safely shut down.

Determines whether there are any unsaved changes in the current session
that would be lost on shutdown.

@return true if there are no pending session changes and shutdown can proceed,
false if the session has unsaved changes
*/
static bool canShutdown();

protected:
Context& context() { return *world; }
virtual std::unique_ptr<ContentFactory> createContentFactory() { return nullptr; }
Expand All @@ -118,7 +128,6 @@ class Application : public juce::JUCEApplication,
std::unique_ptr<Context> world; ///< The application context and services
std::unique_ptr<Startup> startup; ///< Handles startup initialization
juce::OwnedArray<juce::ChildProcessWorker> workers; ///< Worker processes (e.g., plugin scanner)

#if JUCE_LINUX
class MidiSettingsApply {
public:
Expand Down
8 changes: 1 addition & 7 deletions include/element/ui.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,8 @@ class GuiService : public Service,
void run();
Commands& commands();

/** Change updater info. */
void setUpdaterPackage (const std::string_view package, std::string_view version);

/** Check for a newer version and show alert, if available. */
void checkUpdates();

/** Launch the updater tool, if available and enabled. */
void launchUpdater();
void checkUpdates (bool background);

Services& services() const { return controller; }
juce::KeyListener* getKeyListener() const;
Expand Down
98 changes: 45 additions & 53 deletions include/element/ui/updater.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@

#include <memory>
#include <string>
#include <vector>

#include <element/signals.hpp>
#include <element/element.hpp>

#ifndef ELEMENT_UPDATES_HOST
#define ELEMENT_UPDATES_HOST "https://repo.kushview.net"
Expand All @@ -29,70 +28,63 @@

namespace element {

/** An update package. */
struct UpdatePackage {
/** Package (component) Identifier */
std::string ID;
/** Version number of the package
can be 3 or 4 segements.
*/
std::string version;
};

/** Updater helper for checking available updates */
/** Application update checker and installer.

Provides functionality to check for, download, and install application updates.
Uses platform-specific update mechanisms (Sparkle on macOS, WinSparkle on Windows).
*/
class Updater {
public:
/** Creates a platform-specific updater instance.

@return A unique pointer to the created updater, or nullptr if updates
are not supported on the current platform
*/
static std::unique_ptr<Updater> create();

virtual ~Updater();

//==========================================================================
/** Triggered when updates are found. Only fired when checking async. */
Signal<void()> sigUpdatesAvailable;

/** Check for updates in the background */
void clear();

/** Check for updates now or later. */
virtual void check (bool async);

/** Returns all package updates listed in the repo. */
std::vector<UpdatePackage> packages() const noexcept;

/** Returns available packages matching this updater's ID
and also is a greater version.
*/
std::vector<UpdatePackage> available() const noexcept;

//==========================================================================
/** Change updater / package / repo information */
void setInfo (const std::string& package, const std::string& version, const std::string& url);

/** Change updater / package information */
void setInfo (const std::string& package, const std::string& version);

//==========================================================================
/** Returns the repository URL or file:/// path */
std::string repository() const noexcept;

/** Set the base URL to the repository to check for Updates with.
This can also be a file:///path/to/folder on the system.
*/
void setRepository (const std::string& url);
/** Checks for available updates.

When background is true, the check is performed silently without showing UI
unless an update is found. When background is false, a dialog is presented
to the user immediately to show the check progress.

If an update is available, a dialog will be shown to the user regardless of
the background parameter, allowing them to review release notes and choose
whether to install the update.

This method returns immediately; the actual check happens asynchronously.

@param background If true, checks silently in the background. If false,
shows immediate UI feedback to the user.
*/
virtual void check (bool background);

//==========================================================================
/** Override Online XML with local xml
Call clear() to wipe it out.
/** Returns the currently configured update feed URL.

@return The repository URL (http/https) or file system path (file:///)
where the appcast feed is located
*/
virtual std::string feedUrl() const noexcept;

/** Sets the update feed URL.

Configures the base URL or file path to check for updates. This should point
to the location of the appcast XML file that describes available updates.

@param url The repository URL (e.g., "https://example.com/appcast.xml") or
local file path (e.g., "file:///path/to/appcast.xml"). Note that
local file may or may not work depending on platform.
*/
void setUpdatesXml (const std::string& xml);
virtual void setFeedUrl (const std::string& url);

protected:
/** Protected constructor. Use create() to instantiate. */
Updater();
Updater (const std::string& package, const std::string& version, const std::string& url);

private:
class Updates;
std::unique_ptr<Updates> updates;
EL_DISABLE_COPY (Updater)
};

} // namespace element
26 changes: 23 additions & 3 deletions src/application.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright (C) Kushview, LLC.
// SPDX-License-Identifier: GPL-3.0-or-later

#include <atomic>
#include <element/application.hpp>
#include <element/services.hpp>
#include <element/version.hpp>
Expand Down Expand Up @@ -37,6 +38,8 @@

namespace element {

static std::atomic<int> sCanShutdown { 0 };

class Startup : public ActionBroadcaster
{
public:
Expand Down Expand Up @@ -227,6 +230,23 @@ void Application::actionListenerCallback (const String& message)
finishLaunching();
}

bool Application::canShutdown()
{
if (! MessageManager::getInstance()->isThisTheMessageThread())
{
auto result = MessageManager::getInstance()->callSync (Application::canShutdown);
return result.has_value() ? * result : true;
}

if (auto app = dynamic_cast<Application*> (getInstance())) {
auto& services = app->world->services();
auto ssvc = services.find<SessionService>();
return !ssvc->hasSessionChanged();
}

return true;
}

void Application::shutdown()
{
if (! world)
Expand All @@ -250,6 +270,7 @@ void Application::shutdown()

srvs.deactivate();
srvs.shutdown();
sCanShutdown.store (1, std::memory_order_release);

plugins.saveUserPlugins (settings);
midi.writeSettings (settings);
Expand Down Expand Up @@ -356,7 +377,7 @@ void Application::finishLaunching()
world->services().run();

if (world->settings().checkForUpdates())
startTimer (5000);
startTimer (10 * 1000);

maybeOpenCommandLineFile (getCommandLineParameters());
}
Expand Down Expand Up @@ -427,8 +448,7 @@ void Application::initializeModulePath()

void Application::timerCallback()
{
world->logger().logMessage ("[element] checking updates...");
world->services().find<UI>()->checkUpdates();
world->services().find<UI>()->checkUpdates (true);
stopTimer();
}

Expand Down
Loading
Loading