diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt
index a108f2f2..dd34ce09 100644
--- a/.github/actions/spelling/allow.txt
+++ b/.github/actions/spelling/allow.txt
@@ -49,6 +49,7 @@ BBBA
BBD
bcdefghi
beffdb
+bhref
bindir
bir
blargh
@@ -181,6 +182,7 @@ GPOs
grecord
groupinstall
gshared
+gtk
GVariant
hdrs
hideonindex
@@ -215,11 +217,14 @@ itimerspec
journalctl
jsvar
kdbx
+kde
keepass
keyboxd
keyutils
kotlin
krb
+kservices
+kubuntu
lalen
lbl
Lcode
@@ -257,6 +262,7 @@ libnghttp
libnotify
libphobos
libpsl
+libqt
libsepol
libsqlite
libssh
@@ -512,6 +518,7 @@ wpath
writefln
wrt
wtf
+xbel
xca
xcbac
xdeadbeef
diff --git a/Makefile.in b/Makefile.in
index cdfa38e3..67391e1a 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -18,6 +18,12 @@ docdir = $(datadir)/doc/$(package)
VPATH = @srcdir@
INSTALL = @INSTALL@
+# Icon install locations (system-wide hicolor theme)
+ICON_THEMEDIR = $(datadir)/icons/hicolor
+ICON_PLACES_DIR = $(ICON_THEMEDIR)/scalable/places
+ICON_SOURCE_SVG = contrib/images/onedrive.svg
+ICON_TARGET_SVG = onedrive.svg
+
NOTIFICATIONS = @NOTIFICATIONS@
HAVE_SYSTEMD = @HAVE_SYSTEMD@
systemduserunitdir = @systemduserunitdir@
@@ -140,6 +146,16 @@ ifeq ($(COMPLETIONS),yes)
mkdir -p $(DESTDIR)$(FISH_COMPLETION_DIR)
$(INSTALL) -m 0644 contrib/completions/complete.fish $(DESTDIR)$(FISH_COMPLETION_DIR)/onedrive.fish
endif
+ # --- OneDrive folder icon (hicolor) ---
+ mkdir -p $(DESTDIR)$(ICON_PLACES_DIR)
+ $(INSTALL) -m 0644 $(ICON_SOURCE_SVG) $(DESTDIR)$(ICON_PLACES_DIR)/$(ICON_TARGET_SVG)
+ # Refresh icon cache only when installing to the live system (not during staged DESTDIR installs)
+ # and only if the theme directory is a proper theme (has index.theme)
+ if [ -z "$(DESTDIR)" ] && command -v gtk-update-icon-cache >/dev/null 2>&1 \
+ && [ -f "$(ICON_THEMEDIR)/index.theme" ]; then \
+ gtk-update-icon-cache -q "$(ICON_THEMEDIR)"; \
+ fi
+
uninstall:
rm -f $(DESTDIR)$(bindir)/onedrive
@@ -164,3 +180,10 @@ ifeq ($(COMPLETIONS),yes)
rm -f $(DESTDIR)$(BASH_COMPLETION_DIR)/onedrive
rm -f $(DESTDIR)$(FISH_COMPLETION_DIR)/onedrive.fish
endif
+ # --- OneDrive folder icon (hicolor) ---
+ rm -f $(DESTDIR)$(ICON_PLACES_DIR)/$(ICON_TARGET_SVG)
+ # Refresh icon cache if removing from the live system and index.theme exists
+ if [ -z "$(DESTDIR)" ] && command -v gtk-update-icon-cache >/dev/null 2>&1 \
+ && [ -f "$(ICON_THEMEDIR)/index.theme" ]; then \
+ gtk-update-icon-cache -q "$(ICON_THEMEDIR)"; \
+ fi
diff --git a/contrib/images/onedrive.svg b/contrib/images/onedrive.svg
new file mode 100644
index 00000000..499a4ade
--- /dev/null
+++ b/contrib/images/onedrive.svg
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/docs/application-config-options.md b/docs/application-config-options.md
index 32379a8d..319d3a01 100644
--- a/docs/application-config-options.md
+++ b/docs/application-config-options.md
@@ -22,6 +22,7 @@ Before reading this document, please ensure you are running application version
- [disable_notifications](#disable_notifications)
- [disable_permission_set](#disable_permission_set)
- [disable_upload_validation](#disable_upload_validation)
+ - [display_manager_integration](#display_manager_integration)
- [display_running_config](#display_running_config)
- [display_transfer_metrics](#display_transfer_metrics)
- [dns_timeout](#dns_timeout)
@@ -343,6 +344,17 @@ _**Config Example:**_ `disable_websocket_support = "false"` or `disable_websocke
_**CLI Option Use:**_ *None - this is a config file option only*
+### display_manager_integration
+_**Description:**_ Controls whether the client integrates the configured 'sync_dir' with the desktop’s file manager (e.g. Nautilus for GNOME, Dolphin for KDE), adding it as a “special place” in the sidebar and setting a custom OneDrive folder icon where supported.
+
+_**Value Type:**_ Boolean
+
+_**Default Value:**_ False
+
+_**Config Example:**_ `display_manager_integration = "false"` or `display_manager_integration = "true"`
+
+_**CLI Option Use:**_ *None - this is a config file option only*
+
### display_running_config
_**Description:**_ This option will include the running config of the application at application startup. This may be desirable to enable when running in containerised environments so that any application logging that is occurring, will have the application configuration being consumed at startup, written out to any applicable log file.
diff --git a/docs/images/fedora_integration.png b/docs/images/fedora_integration.png
new file mode 100644
index 00000000..c513f2ce
Binary files /dev/null and b/docs/images/fedora_integration.png differ
diff --git a/docs/images/kubuntu_integration.png b/docs/images/kubuntu_integration.png
new file mode 100644
index 00000000..1b8c4bc5
Binary files /dev/null and b/docs/images/kubuntu_integration.png differ
diff --git a/docs/images/ubuntu_integration.png b/docs/images/ubuntu_integration.png
new file mode 100644
index 00000000..d5bf7385
Binary files /dev/null and b/docs/images/ubuntu_integration.png differ
diff --git a/docs/usage.md b/docs/usage.md
index bdd2298c..580c9f0a 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -38,6 +38,7 @@ Before reading this document, please ensure you are running application version
- [Handling a Microsoft OneDrive Account Password Change](#handling-a-microsoft-onedrive-account-password-change)
- [Determining the synchronisation result](#determining-the-synchronisation-result)
- [Resumable Transfers](#resumable-transfers)
+ - [Display Manager Integration](#display-manager-integration)
- [Frequently Asked Configuration Questions](#frequently-asked-configuration-questions)
- [How to change the default configuration of the client?](#how-to-change-the-default-configuration-of-the-client)
- [How to change where my data from Microsoft OneDrive is stored?](#how-to-change-where-my-data-from-microsoft-onedrive-is-stored)
@@ -1181,6 +1182,69 @@ If `--resync` is used, all resumable data is discarded intentionally.
> [!NOTE]
> Resumable transfer support is built-in and requires no special configuration. It is automatically applied during both standalone and monitor operational modes when applicable.
+### Display Manager Integration
+Modern desktop environments such as GNOME and KDE Plasma provide graphical file managers — Nautilus (GNOME Files) and Dolphin, respectively — to help users navigate their local and remote storage.
+
+#### What “Display Manager Integration” means
+Display Manager Integration refers to an ability to integrate your configured Microsoft OneDrive synchronisation directory (`sync_dir`) with the desktop’s file manager environment. Depending on the platform and desktop environment, this may include:
+
+1. **Sidebar registration** — Adding the OneDrive folder as a “special place” within the sidebar of Nautilus (GNOME) or Dolphin (KDE), providing easy access without manual navigation.
+2. **Custom folder icon** — Applying a dedicated OneDrive icon to visually distinguish the synchronised directory within the file manager.
+3. **Context-menu extensions** — Adding right-click actions such as “Upload to OneDrive” or “Share via OneDrive” directly inside Nautilus or Dolphin.
+4. **File overlay badges** — Displaying icons (check-marks, sync arrows, or cloud symbols) to represent file synchronisation state.
+5. **System tray or application indicator** — Presenting sync status, pause/resume controls, or notifications via a tray icon.
+
+#### What display manager integration is available in the OneDrive Client for Linux
+The OneDrive Client for Linux currently supports the following integration features:
+
+1. **Sidebar registration** — The client automatically registers the OneDrive folder as a “special place” within the sidebar of Nautilus (GNOME) or Dolphin (KDE).
+2. **Custom folder icon** — The client applies a OneDrive-specific icon to the synchronisation directory where supported by the installed icon theme.
+
+This behaviour is controlled by the configuration option:
+```text
+display_manager_integration = "true"
+```
+When enabled, the client detects the active desktop session and applies the corresponding integration automatically when the client is running in `--monitor` mode only.
+
+> [!NOTE]
+> Display Manager Integration remains active only while the OneDrive client or its systemd service is running. If the client stops or the service is stopped, the desktop integration is automatically cleared. It is re-applied the next time the client starts.
+
+#### Fedora Display Manager Integration Example
+
+
+#### Ubuntu Display Manager Integration Example
+
+
+#### Kubuntu Display Manager Integration Example
+
+
+#### What about context menu integration?
+Context-menu integration is a desktop-specific capability, not part of the core OneDrive Client. It can be achieved through desktop-provided extension mechanisms:
+
+1. **Shell-script bridge** — A simple shell script can be registered as a KDE ServiceMenu or a GNOME Nautilus Script to trigger local actions (for example, creating a symbolic link in `~/OneDrive` to upload a file).
+2. **Python + Nautilus API (GNOME)** — Implemented via nautilus-python bindings by registering a subclass of `Nautilus.MenuProvider`.
+3. **Qt/KIO Plugins (KDE)** — Implemented using C++ or declarative .desktop ServiceMenu definitions under `/usr/share/kservices5/ServiceMenus/`.
+
+These methods are optional and operate independently of the core OneDrive Client. They can be used by advanced users or system integrators to provide additional right-click functionality.
+
+#### What about file overlay badges?
+File overlay badges are typically associated with Microsoft’s Files-On-Demand feature, which allows selective file downloads and visual state indicators (online-only, available offline, etc.).
+
+Because Files-On-Demand is currently a feature request for this client, overlay badges are not implemented and remain out of scope for now.
+
+#### What about a system tray or application indicator?
+While the core OneDrive Client for Linux does not include its own tray icon or GUI dashboard, the community provides complementary tools that plug into it — exposing sync status, pause/resume controls, tray menus, and GUI configuration front-ends. Below are two popular options:
+
+**1. OneDriveGUI** - https://github.com/bpozdena/OneDriveGUI
+* A full-featured graphical user interface built for the OneDrive Linux client.
+* Key features include: multi-account support, asynchronous real-time monitoring of multiple OneDrive profiles, a setup wizard for profile creation/import, automatic sync on GUI startup, and GUI-based login.
+* Includes tray icon support when the desktop environment allows it.
+* Intended to simplify one-click configuration of the CLI client, help users visualise current operations (uploads/downloads), and manage advanced features such as SharePoint libraries and multiple profiles.
+
+**2. onedrive_tray** - https://github.com/DanielBorgesOliveira/onedrive_tray
+* A lightweight system tray utility written in Qt (using libqt5 or later) that monitors the running OneDrive Linux client and displays status via a tray icon.
+* Left-click the tray icon to view sync progress; right-click to access a menu of available actions; middle-click shows the PID of the running client.
+* Ideal for users who just want visual status cues (e.g., “sync in progress”, “idle”, “error”) without a full GUI configuration tool.
## Frequently Asked Configuration Questions
diff --git a/onedrive.1.in b/onedrive.1.in
index 1c4c4263..a463a639 100644
--- a/onedrive.1.in
+++ b/onedrive.1.in
@@ -61,6 +61,8 @@ Designed for maximum flexibility and reliability, this powerful and highly confi
* Manages traffic bandwidth use with rate limiting
.br
* Supports sending desktop alerts using libnotify
+.br
+* Provides desktop file-manager integration by registering the OneDrive folder as a sidebar location with a distinctive icon
.SH CONFIGURATION
By default, the OneDrive Client for Linux uses a sensible set of built-in defaults to interact with the Microsoft OneDrive service.
diff --git a/readme.md b/readme.md
index 483d3003..08a53e16 100644
--- a/readme.md
+++ b/readme.md
@@ -39,6 +39,7 @@ Since forking in early 2018, this client has evolved into a clean re-imagining o
* Enhanced synchronisation speed with multi-threaded file transfers
* Manages traffic bandwidth use with rate limiting
* Supports sending desktop alerts using libnotify
+* Provides desktop file-manager integration by registering the OneDrive folder as a sidebar location with a distinctive icon.
## What's missing
diff --git a/src/config.d b/src/config.d
index 065b1d41..525c9602 100644
--- a/src/config.d
+++ b/src/config.d
@@ -19,6 +19,8 @@ import std.format;
import std.ascii;
import std.datetime;
import std.exception;
+import core.sys.posix.unistd : geteuid, getuid;
+import std.process : spawnProcess, wait;
// What other modules that we have created do we need to import?
import log;
@@ -444,6 +446,9 @@ class ApplicationConfig {
// Use authentication via OAuth2 Device Authorisation Flow
boolValues["use_device_auth"] = false;
+ // GUI | Display Manager Integration
+ boolValues["display_manager_integration"] = false;
+
// EXPAND USERS HOME DIRECTORY
// Determine the users home directory.
// Need to avoid using ~ here as expandTilde() below does not interpret correctly when running under init.d or systemd scripts
@@ -2821,6 +2826,290 @@ class ApplicationConfig {
recycleBinFilePath = basePath ~ "files/";
recycleBinInfoPath = basePath ~ "info/";
}
+
+ // Is the client running under a GUI session?
+ // - GNOME
+ // - KDE
+ bool isGuiSessionDetected() {
+
+ bool hasDisplay = false;
+ bool hasRuntime = false;
+ bool uidMatches = false;
+ bool homeOK = false;
+ string xdgType;
+
+ try {
+ xdgType = environment.get("XDG_SESSION_TYPE", "");
+ } catch (Exception e) {
+ xdgType = "";
+ }
+
+ try {
+ hasDisplay = environment.get("WAYLAND_DISPLAY", "").length > 0 || environment.get("DISPLAY", "").length > 0;
+ } catch (Exception e) {
+ hasDisplay = false;
+ }
+
+ try {
+ hasRuntime = environment.get("XDG_RUNTIME_DIR", "").length > 0;
+ } catch (Exception e) {
+ hasRuntime = false;
+ }
+
+ try {
+ uidMatches = (geteuid() == getuid());
+ } catch (Exception e) {
+ uidMatches = false;
+ }
+
+ try {
+ homeOK = environment.get("HOME", "").length > 0;
+ } catch (Exception e) {
+ homeOK = false;
+ }
+
+ bool hasGuiElements = hasDisplay || (xdgType == "wayland" || xdgType == "x11");
+
+ return hasGuiElements && hasRuntime && uidMatches && homeOK;
+ }
+
+ // Attempt to detect the running display manager
+ DesktopHints detectDesktop() {
+ string all = ( environment.get("XDG_CURRENT_DESKTOP","") ~ ":" ~
+ environment.get("XDG_SESSION_DESKTOP","") ~ ":" ~
+ environment.get("DESKTOP_SESSION","") ~ ":" ~
+ environment.get("GDMSESSION","") ~ ":" ~
+ environment.get("KDE_FULL_SESSION","")).toLower();
+
+ DesktopHints hints;
+ hints.gnome = all.canFind("gnome");
+ hints.kde = all.canFind("kde") || all.canFind("plasma");
+ return hints;
+ }
+
+ string fileUriFor(string absPath) {
+ // Basic, safe URI for local file
+ return "file://" ~ expandTilde(absPath);
+ }
+
+ void addGnomeBookmark() {
+ // Configure required variables
+ string uri = fileUriFor(getValueString("sync_dir"));
+ string bookmarksPath = buildPath(expandTilde(environment.get("HOME", "")), ".config", "gtk-3.0", "bookmarks");
+
+ // Ensure the bookmarks path exists
+ mkdirRecurse(dirName(bookmarksPath));
+
+ // Does the bookmark already exist?
+ string content = exists(bookmarksPath) ? readText(bookmarksPath) : "";
+ bool present = false;
+ foreach (line; content.splitLines()) {
+ if (line.strip == uri) { present = true; break; }
+ }
+ if (present) return;
+
+ // Append newline if needed, then our URI
+ string newline = content.length && !content.endsWith("\n") ? "\n" : "";
+ string updated = content ~ newline ~ uri ~ "\n";
+
+ // Atomic write
+ string tmp = bookmarksPath ~ ".tmp";
+ std.file.write(tmp, updated);
+ rename(tmp, bookmarksPath);
+
+ // Log outcome
+ addLogEntry("GNOME Desktop Integration: Bookmark added successfully", ["info"]);
+ }
+
+ void setOneDriveFolderIcon() {
+ // Get the sync directory
+ string syncDir = expandTilde(getValueString("sync_dir"));
+
+ // Build gio command
+ string[] gioCmd = [
+ "gio",
+ "set",
+ syncDir,
+ "metadata::custom-icon-name",
+ "onedrive"
+ ];
+
+ // Try and set folder icon
+ try {
+ auto p = spawnProcess(gioCmd);
+ int status = p.wait();
+ if (status == 0) {
+ addLogEntry("GNOME Desktop Integration: Set folder icon to 'onedrive' for " ~ syncDir, ["info"]);
+ } else {
+ addLogEntry("GNOME Desktop Integration: Failed to set folder icon for " ~ syncDir ~ " (gio exit " ~ status.to!string ~ ")", ["info"]);
+ }
+ } catch (Exception e) {
+ addLogEntry("GNOME Desktop Integration: Exception setting folder icon: " ~ e.msg, ["info"]);
+ }
+ }
+
+ void removeGnomeBookmark() {
+ // Configure required variables
+ string uri = fileUriFor(getValueString("sync_dir"));
+ string bookmarksPath = buildPath(expandTilde(environment.get("HOME", "")), ".config", "gtk-3.0", "bookmarks");
+
+ // Does the bookmark path exist?
+ if (!exists(bookmarksPath)) {
+ return;
+ }
+
+ // Read existing bookmarks
+ string content = readText(bookmarksPath);
+ auto lines = content.splitLines();
+
+ bool changed = false;
+ string[] kept;
+ kept.reserve(lines.length);
+
+ foreach (line; lines) {
+ // Remove every line that exactly matches the URI (after stripping whitespace)
+ if (line.strip == uri) {
+ changed = true;
+ continue;
+ }
+ kept ~= line;
+ }
+
+ if (!changed) {
+ return;
+ }
+
+ // Rebuild file (ensure trailing newline if non-empty)
+ string updated = kept.length ? kept.join("\n") ~ "\n" : "";
+
+ // Atomic write
+ const string tmp = bookmarksPath ~ ".tmp";
+ std.file.write(tmp, updated);
+ rename(tmp, bookmarksPath);
+
+ // Log outcome
+ addLogEntry("GNOME Desktop Integration: Bookmark removed successfully", ["info"]);
+ }
+
+ void removeOneDriveFolderIcon() {
+ // Get the sync directory
+ string syncDir = expandTilde(getValueString("sync_dir"));
+
+ // Build gio command
+ string[] gioCmd = [
+ "gio",
+ "set",
+ syncDir,
+ "metadata::custom-icon-name",
+ "folder"
+ ];
+
+ // Try and set folder icon
+ try {
+ auto p = spawnProcess(gioCmd);
+ int status = p.wait();
+ if (status == 0) {
+ addLogEntry("GNOME Desktop Integration: Reset folder icon to 'default' for " ~ syncDir, ["info"]);
+ } else {
+ addLogEntry("GNOME Desktop Integration: Failed to reset folder icon for " ~ syncDir ~ " (gio exit " ~ status.to!string ~ ")", ["info"]);
+ }
+ } catch (Exception e) {
+ addLogEntry("GNOME Desktop Integration: Exception setting folder icon: " ~ e.msg, ["info"]);
+ }
+ }
+
+ void addKDEPlacesEntry() {
+ // Configure required variables
+ string uri = fileUriFor(getValueString("sync_dir"));
+ string xbelPath = buildPath(expandTilde(environment.get("HOME", "")), ".local", "share", "user-places.xbel");
+ string content;
+
+ // Ensure the xbelPath path exists
+ mkdirRecurse(dirName(xbelPath));
+
+ // Does the xbel file exist?
+ if (exists(xbelPath)) {
+ // Path exists - read the file
+ content = readText(xbelPath);
+
+ // Does the 'sync_dir' path exist in the xbel file?
+ if (content.canFind(`href="` ~ uri ~ `"`)) {
+ return; // already present
+ }
+ } else {
+ // xbel path does not exist, create minimal XBEL skeleton
+ content = "
+
+ ";
+ }
+
+ // Insert xbel bookmark before closing tag
+ string bookmark = `
+ OneDrive
+
+
+
+
+
+ `;
+
+ // Update xbel file with Microsoft OneDrive Bookmark
+ string updated;
+ auto idx = content.lastIndexOf("");
+ if (idx >= 0) {
+ updated = content[0 .. idx] ~ bookmark ~ "\n" ~ content[idx .. $];
+ } else {
+ // Fallback: append (still valid for many parsers)
+ updated = content ~ "\n" ~ bookmark ~ "\n\n";
+ }
+
+ string tmp = xbelPath ~ ".tmp";
+ std.file.write(tmp, updated);
+ rename(tmp, xbelPath);
+
+ // Log outcome
+ addLogEntry("KDE Desktop Integration: KDE/Plasma place added successfully", ["info"]);
+ }
+
+ void removeKDEPlacesEntry() {
+ // Compute paths/values
+ const string uri = fileUriFor(getValueString("sync_dir"));
+ const string xbelPath = buildPath(expandTilde(environment.get("HOME", "")), ".local", "share", "user-places.xbel");
+
+ if (!exists(xbelPath)) {
+ return;
+ }
+
+ string content = readText(xbelPath);
+ auto before = content;
+
+ // Build a regex that matches:
+ // ...
+ // - tolerate attribute order/whitespace
+ // - accept single or double quotes around URI
+ // - non-greedy body match
+ const esc = regexEscape(uri);
+ auto re = regex(`(?s)]*\bhref\s*=\s*["']` ~ esc ~ `["'][^>]*>.*?`);
+
+ // Remove all matches
+ content = replaceAll(content, re, "");
+
+ // Optional: tidy up multiple blank lines left behind
+ auto cleanup = regex(`\n{3,}`);
+ content = replaceAll(content, cleanup, "\n\n");
+
+ // If nothing changed, exit quietly
+ if (content == before) {
+ return;
+ }
+
+ // Atomic write
+ string tmp = xbelPath ~ ".tmp";
+ std.file.write(tmp, content);
+ rename(tmp, xbelPath);
+
+ addLogEntry("KDE Desktop Integration: KDE/Plasma place removed successfully", ["info"]);
+ }
}
// Output the full application help when --help is passed in
diff --git a/src/main.d b/src/main.d
index a8e114aa..db5e7203 100644
--- a/src/main.d
+++ b/src/main.d
@@ -953,6 +953,12 @@ int main(string[] cliArgs) {
if (appConfig.getValueBool("monitor")) {
// Update the flag given we are running with --monitor
performFileSystemMonitoring = true;
+
+ // Is Display Manager Integration enabled?
+ if (appConfig.getValueBool("display_manager_integration")) {
+ // Attempt to configure the desktop integration whilst the client is running in --monitor mode
+ attemptFileManagerIntegration();
+ }
// If 'webhooks' are enabled, this is going to conflict with 'websockets' if the OS cURL library supports websockets
if (appConfig.getValueBool("webhook_enabled") && appConfig.curlSupportsWebSockets) {
@@ -1858,6 +1864,14 @@ void performSynchronisedExitProcess(string scopeCaller = null) {
try {
// Log who called this function
if (debugLogging) {addLogEntry("performSynchronisedExitProcess called by: " ~ scopeCaller, ["debug"]);}
+ // Remove Desktop integration
+ if(performFileSystemMonitoring) {
+ // Was desktop integration enabled?
+ if (appConfig.getValueBool("display_manager_integration")) {
+ // Attempt removal
+ attemptFileManagerIntegrationRemoval();
+ }
+ }
// Shutdown the OneDrive Webhook instance
shutdownOneDriveWebhook();
// Shutdown the OneDrive WebSocket instance
@@ -1994,4 +2008,50 @@ string compilerDetails() {
else enum compiler = "Unknown compiler";
string compilerString = compiler ~ " " ~ to!string(__VERSION__);
return compilerString;
-}
\ No newline at end of file
+}
+
+void attemptFileManagerIntegration() {
+ // Are we running under a Desktop Manager (GNOME or KDE)?
+ if (appConfig.isGuiSessionDetected()) {
+ // Generate desktop hints
+ auto hints = appConfig.detectDesktop();
+
+ // GNOME Desktop File Manager integration
+ if (hints.gnome) {
+ // Attempt integration
+ appConfig.addGnomeBookmark();
+ appConfig.setOneDriveFolderIcon();
+ return;
+ }
+
+ // KDE Desktop File Manager integration
+ if (hints.kde) {
+ // Attempt integration
+ appConfig.addKDEPlacesEntry();
+ return;
+ }
+ }
+}
+
+void attemptFileManagerIntegrationRemoval() {
+ // Are we running under a Desktop Manager (GNOME or KDE)?
+ if (appConfig.isGuiSessionDetected()) {
+ // Generate desktop hints
+ auto hints = appConfig.detectDesktop();
+
+ // GNOME Desktop File Manager integration removal
+ if (hints.gnome) {
+ // Attempt integration removal
+ appConfig.removeGnomeBookmark();
+ appConfig.removeOneDriveFolderIcon();
+ return;
+ }
+
+ // KDE Desktop File Manager integration removal
+ if (hints.kde) {
+ // Attempt integration removal
+ appConfig.removeKDEPlacesEntry();
+ return;
+ }
+ }
+}
diff --git a/src/util.d b/src/util.d
index ee2f390d..3e09c656 100644
--- a/src/util.d
+++ b/src/util.d
@@ -51,6 +51,11 @@ __gshared bool exitHandlerTriggered = false;
// util module variable
ulong previousRSS;
+struct DesktopHints {
+ bool gnome;
+ bool kde;
+}
+
shared static this() {
deviceName = Socket.hostName;
}
@@ -1943,3 +1948,15 @@ bool isDirEmpty(string dir) {
}
return true;
}
+
+// Escape a string for literal use inside a regex
+string regexEscape(string s) {
+ auto b = appender!string();
+ foreach (c; s) {
+ // characters with special meaning in regex
+ immutable specials = "\\.^$|?*+()[]{}";
+ if (specials.canFind(c)) b.put('\\');
+ b.put(c);
+ }
+ return b.data;
+}