diff options
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services')
51 files changed, 0 insertions, 22041 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/AppSearchService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/AppSearchService.qml deleted file mode 100644 index 1c8413f..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/AppSearchService.qml +++ /dev/null @@ -1,985 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import qs.Common - -Singleton { - id: root - - property var applications: [] - property var _cachedCategories: null - property var _cachedVisibleApps: null - property var _hiddenAppsSet: new Set() - - property var _transformCache: ({}) - property var _cachedDefaultSections: [] - property var _cachedDefaultFlatModel: [] - property bool _defaultCacheValid: false - property int cacheVersion: 0 - - readonly property int maxResults: 10 - readonly property int frecencySampleSize: 10 - - readonly property var timeBuckets: [ - { - "maxDays": 4, - "weight": 100 - }, - { - "maxDays": 14, - "weight": 70 - }, - { - "maxDays": 31, - "weight": 50 - }, - { - "maxDays": 90, - "weight": 30 - }, - { - "maxDays": 99999, - "weight": 10 - } - ] - - function refreshApplications() { - applications = DesktopEntries.applications.values; - _cachedCategories = null; - _cachedVisibleApps = null; - invalidateLauncherCache(); - } - - function invalidateLauncherCache() { - _transformCache = {}; - _defaultCacheValid = false; - _cachedDefaultSections = []; - _cachedDefaultFlatModel = []; - cacheVersion++; - } - - function getOrTransformApp(app, transformFn) { - const id = app.id || app.execString || app.exec || ""; - if (!id) - return transformFn(app); - const cached = _transformCache[id]; - if (cached) { - const currentIcon = app.icon || ""; - const cachedSourceIcon = cached._sourceIcon || ""; - if (currentIcon === cachedSourceIcon) - return cached; - } - const transformed = transformFn(app); - transformed._sourceIcon = app.icon || ""; - _transformCache[id] = transformed; - return transformed; - } - - function getCachedDefaultSections() { - if (!_defaultCacheValid) - return null; - return _cachedDefaultSections; - } - - function setCachedDefaultSections(sections, flatModel) { - _cachedDefaultSections = sections.map(function (s) { - return Object.assign({}, s, { - items: s.items ? s.items.slice() : [] - }); - }); - _cachedDefaultFlatModel = flatModel.slice(); - _defaultCacheValid = true; - } - - function isCacheValid() { - return _defaultCacheValid; - } - - function _rebuildHiddenSet() { - _hiddenAppsSet = new Set(SessionData.hiddenApps || []); - _cachedVisibleApps = null; - } - - function isAppHidden(app) { - if (!app) - return false; - const appId = app.id || app.execString || app.exec || ""; - return _hiddenAppsSet.has(appId); - } - - function getVisibleApplications() { - if (_cachedVisibleApps === null) { - const seen = new Set(); - _cachedVisibleApps = applications.filter(app => { - if (isAppHidden(app)) - return false; - const id = app.id; - if (id && seen.has(id)) - return false; - if (id) - seen.add(id); - return true; - }); - } - return _cachedVisibleApps.map(app => applyAppOverride(app)); - } - - Connections { - target: SessionData - function onHiddenAppsChanged() { - root._rebuildHiddenSet(); - root.invalidateLauncherCache(); - } - function onAppOverridesChanged() { - root._cachedVisibleApps = null; - root.invalidateLauncherCache(); - } - } - - Connections { - target: AppUsageHistoryData - function onAppUsageRankingChanged() { - root.invalidateLauncherCache(); - } - } - - function applyAppOverride(app) { - if (!app) - return app; - const appId = app.id || app.execString || app.exec || ""; - const override = SessionData.getAppOverride(appId); - if (!override) - return app; - return Object.assign({}, app, { - name: override.name || app.name, - icon: override.icon || app.icon, - comment: override.comment || app.comment, - _override: override - }); - } - - readonly property string dmsLogoPath: Qt.resolvedUrl("../assets/danklogo2.svg") - - readonly property var builtInPlugins: ({ - "dms_settings": { - id: "dms_settings", - name: I18n.tr("Settings", "settings window title"), - icon: "svg+corner:" + dmsLogoPath + "|settings", - cornerIcon: "settings", - comment: "DMS", - action: "ipc:settings", - categories: ["Settings", "System"], - defaultTrigger: "", - isLauncher: false - }, - "dms_notepad": { - id: "dms_notepad", - name: I18n.tr("Notepad", "Notepad"), - icon: "svg+corner:" + dmsLogoPath + "|description", - cornerIcon: "description", - comment: "DMS", - action: "ipc:notepad", - categories: ["Office", "Utility"], - defaultTrigger: "", - isLauncher: false - }, - "dms_sysmon": { - id: "dms_sysmon", - name: I18n.tr("System Monitor", "sysmon window title"), - icon: "svg+corner:" + dmsLogoPath + "|monitor_heart", - cornerIcon: "monitor_heart", - comment: "DMS", - action: "ipc:processlist", - categories: ["System", "Monitor"], - defaultTrigger: "", - isLauncher: false - }, - "dms_colorpicker": { - id: "dms_colorpicker", - name: I18n.tr("Color Picker"), - icon: "svg+corner:" + dmsLogoPath + "|palette", - cornerIcon: "palette", - comment: "DMS", - action: "ipc:color-picker", - categories: ["Graphics", "Utility"], - defaultTrigger: "", - isLauncher: false - }, - "dms_settings_search": { - id: "dms_settings_search", - name: I18n.tr("Settings", "settings window title"), - cornerIcon: "search", - comment: "DMS", - defaultTrigger: "?", - isLauncher: true - } - }) - - function getBuiltInPluginTrigger(pluginId) { - const plugin = builtInPlugins[pluginId]; - if (!plugin) - return null; - return SettingsData.getBuiltInPluginSetting(pluginId, "trigger", plugin.defaultTrigger); - } - - readonly property var coreApps: { - SettingsData.builtInPluginSettings; - const apps = []; - for (const pluginId in builtInPlugins) { - if (!SettingsData.getBuiltInPluginSetting(pluginId, "enabled", true)) - continue; - const plugin = builtInPlugins[pluginId]; - if (plugin.isLauncher) - continue; - apps.push({ - name: plugin.name, - icon: plugin.icon, - comment: plugin.comment, - action: plugin.action, - categories: plugin.categories, - isCore: true, - builtInPluginId: pluginId, - cornerIcon: plugin.cornerIcon - }); - } - return apps; - } - - function getBuiltInLauncherPlugins() { - const result = {}; - for (const pluginId in builtInPlugins) { - const plugin = builtInPlugins[pluginId]; - if (!plugin.isLauncher) - continue; - if (!SettingsData.getBuiltInPluginSetting(pluginId, "enabled", true)) - continue; - result[pluginId] = plugin; - } - return result; - } - - function getBuiltInLauncherTriggers() { - const triggers = {}; - const launchers = getBuiltInLauncherPlugins(); - for (const pluginId in launchers) { - const trigger = getBuiltInPluginTrigger(pluginId); - if (trigger && trigger.trim() !== "") - triggers[trigger] = pluginId; - } - return triggers; - } - - function getBuiltInLauncherPluginsWithEmptyTrigger() { - const result = []; - const launchers = getBuiltInLauncherPlugins(); - for (const pluginId in launchers) { - const trigger = getBuiltInPluginTrigger(pluginId); - if (!trigger || trigger.trim() === "") - result.push(pluginId); - } - return result; - } - - function getBuiltInLauncherItems(pluginId, query) { - if (pluginId !== "dms_settings_search") - return []; - - SettingsSearchService.search(query); - const results = SettingsSearchService.results; - const items = []; - for (let i = 0; i < results.length; i++) { - const r = results[i]; - items.push({ - name: r.label, - icon: "material:" + r.icon, - comment: r.category, - action: "settings_nav:" + r.tabIndex + ":" + r.section, - categories: ["Settings"], - isCore: true, - isBuiltInLauncher: true, - builtInPluginId: pluginId - }); - } - return items; - } - - function executeBuiltInLauncherItem(item) { - if (!item?.action) - return false; - - const parts = item.action.split(":"); - if (parts[0] !== "settings_nav") - return false; - - const tabIndex = parseInt(parts[1]); - const section = parts.slice(2).join(":"); - SettingsSearchService.navigateToSection(section); - PopoutService.openSettingsWithTabIndex(tabIndex); - return true; - } - - function getCoreApps(query) { - if (!query || query.length === 0) - return coreApps; - const lowerQuery = query.toLowerCase(); - return coreApps.filter(app => app.name.toLowerCase().includes(lowerQuery) || app.comment.toLowerCase().includes(lowerQuery)); - } - - function executeCoreApp(app) { - if (!app?.action) - return false; - - const parts = app.action.split(":"); - if (parts[0] !== "ipc") - return false; - - switch (parts[1]) { - case "settings": - PopoutService.focusOrToggleSettings(); - return true; - case "notepad": - PopoutService.toggleNotepad(); - return true; - case "processlist": - PopoutService.toggleProcessListModal(); - return true; - case "color-picker": - PopoutService.showColorPicker(); - return true; - } - return false; - } - - Connections { - target: DesktopEntries - function onApplicationsChanged() { - root.refreshApplications(); - } - } - - Component.onCompleted: { - _rebuildHiddenSet(); - refreshApplications(); - } - - function tokenize(text) { - return text.toLowerCase().trim().split(/[\s\-_]+/).filter(w => w.length > 0); - } - - function wordBoundaryMatch(text, query) { - const textWords = tokenize(text); - const queryWords = tokenize(query); - - if (queryWords.length === 0) - return false; - if (queryWords.length > textWords.length) - return false; - - for (var i = 0; i <= textWords.length - queryWords.length; i++) { - let allMatch = true; - for (var j = 0; j < queryWords.length; j++) { - if (!textWords[i + j].startsWith(queryWords[j])) { - allMatch = false; - break; - } - } - if (allMatch) - return true; - } - return false; - } - - function levenshteinDistance(s1, s2) { - const len1 = s1.length; - const len2 = s2.length; - const matrix = []; - - for (var i = 0; i <= len1; i++) { - matrix[i] = [i]; - } - for (var j = 0; j <= len2; j++) { - matrix[0][j] = j; - } - - for (var i = 1; i <= len1; i++) { - for (var j = 1; j <= len2; j++) { - const cost = s1[i - 1] === s2[j - 1] ? 0 : 1; - matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost); - } - } - return matrix[len1][len2]; - } - - function fuzzyMatchScore(text, query) { - const queryLower = query.toLowerCase(); - const maxDistance = query.length <= 2 ? 0 : query.length === 3 ? 1 : query.length <= 6 ? 2 : 3; - - let bestScore = 0; - - const distance = levenshteinDistance(text.toLowerCase(), queryLower); - if (distance <= maxDistance) { - const maxLen = Math.max(text.length, query.length); - bestScore = 1 - (distance / maxLen); - } - - const words = tokenize(text); - for (const word of words) { - const wordDistance = levenshteinDistance(word, queryLower); - if (wordDistance <= maxDistance) { - const maxLen = Math.max(word.length, query.length); - const score = 1 - (wordDistance / maxLen); - bestScore = Math.max(bestScore, score); - } - } - - return bestScore; - } - - function calculateFrecency(app) { - const usageRanking = AppUsageHistoryData.appUsageRanking || {}; - const appId = app.id || (app.execString || app.exec || ""); - const idVariants = [appId, appId.replace(".desktop", ""), app.id, app.id ? app.id.replace(".desktop", "") : null].filter(id => id); - - let usageData = null; - for (const variant of idVariants) { - if (usageRanking[variant]) { - usageData = usageRanking[variant]; - break; - } - } - - if (!usageData || !usageData.usageCount) { - return { - "frecency": 0, - "daysSinceUsed": 999999 - }; - } - - const usageCount = usageData.usageCount || 0; - const lastUsed = usageData.lastUsed || 0; - const now = Date.now(); - const daysSinceUsed = (now - lastUsed) / (1000 * 60 * 60 * 24); - - let timeBucketWeight = 10; - for (const bucket of timeBuckets) { - if (daysSinceUsed <= bucket.maxDays) { - timeBucketWeight = bucket.weight; - break; - } - } - - const contextBonus = 100; - const sampleSize = Math.min(usageCount, frecencySampleSize); - const frecency = (timeBucketWeight * contextBonus * sampleSize) / 100; - - return { - "frecency": frecency, - "daysSinceUsed": daysSinceUsed - }; - } - - function searchApplications(query) { - if (!query || query.length === 0) - return getVisibleApplications(); - if (applications.length === 0) - return []; - - const queryLower = query.toLowerCase().trim(); - const scoredApps = []; - const results = []; - const visibleApps = getVisibleApplications(); - - for (const app of visibleApps) { - const name = (app.name || "").toLowerCase(); - const genericName = (app.genericName || "").toLowerCase(); - const comment = (app.comment || "").toLowerCase(); - const id = (app.id || "").toLowerCase(); - const keywords = app.keywords ? app.keywords.map(k => k.toLowerCase()) : []; - - let textScore = 0; - let matchType = "none"; - - if (name === queryLower) { - textScore = 10000; - matchType = "exact"; - } else if (name.startsWith(queryLower)) { - textScore = 5000; - matchType = "prefix"; - } else if (wordBoundaryMatch(name, queryLower)) { - textScore = 3000; - matchType = "word_boundary"; - } else if (name.includes(queryLower)) { - textScore = 500; - matchType = "substring"; - } else if (genericName && genericName.startsWith(queryLower)) { - textScore = 800; - matchType = "generic_prefix"; - } else if (genericName && genericName.includes(queryLower)) { - textScore = 400; - matchType = "generic"; - } else if (id && id.includes(queryLower)) { - textScore = 350; - matchType = "id"; - } - - if (matchType === "none" && keywords.length > 0) { - for (const keyword of keywords) { - if (keyword.startsWith(queryLower)) { - textScore = 300; - matchType = "keyword_prefix"; - break; - } else if (keyword.includes(queryLower)) { - textScore = 150; - matchType = "keyword"; - break; - } - } - } - - if (matchType === "none" && comment && comment.includes(queryLower)) { - textScore = 50; - matchType = "comment"; - } - - if (matchType === "none") { - const fuzzyScore = fuzzyMatchScore(name, queryLower); - if (fuzzyScore > 0) { - textScore = fuzzyScore * 100; - matchType = "fuzzy"; - } - } - - if (matchType !== "none") { - const frecencyData = calculateFrecency(app); - - results.push({ - "app": app, - "textScore": textScore, - "frecency": frecencyData.frecency, - "daysSinceUsed": frecencyData.daysSinceUsed, - "matchType": matchType - }); - } - } - - for (const result of results) { - const frecencyBonus = result.frecency > 0 ? Math.min(result.frecency, 2000) : 0; - const recencyBonus = result.daysSinceUsed < 1 ? 1500 : result.daysSinceUsed < 7 ? 1000 : result.daysSinceUsed < 30 ? 500 : 0; - - const finalScore = result.textScore + frecencyBonus + recencyBonus; - - scoredApps.push({ - "app": result.app, - "score": finalScore - }); - } - - if (SessionData.searchAppActions) { - const actionResults = searchAppActions(queryLower, visibleApps); - for (const actionResult of actionResults) { - scoredApps.push({ - app: actionResult.app, - score: actionResult.score - }); - } - } - - scoredApps.sort((a, b) => b.score - a.score); - return scoredApps.slice(0, maxResults).map(item => item.app); - } - - function searchAppActions(query, apps) { - const results = []; - for (const app of apps) { - if (!app.actions || app.actions.length === 0) - continue; - for (const action of app.actions) { - const actionName = (action.name || "").toLowerCase(); - if (!actionName) - continue; - - let score = 0; - if (actionName === query) { - score = 8000; - } else if (actionName.startsWith(query)) { - score = 4000; - } else if (actionName.includes(query)) { - score = 400; - } - - if (score > 0) { - results.push({ - app: { - name: action.name, - icon: action.icon || app.icon, - comment: app.name, - categories: app.categories || [], - isAction: true, - parentApp: app, - actionData: action - }, - score: score - }); - } - } - } - return results; - } - - function getCategoriesForApp(app) { - if (!app?.categories) - return []; - - const categoryMap = { - "AudioVideo": I18n.tr("Media"), - "Audio": I18n.tr("Media"), - "Video": I18n.tr("Media"), - "Development": I18n.tr("Development"), - "TextEditor": I18n.tr("Development"), - "IDE": I18n.tr("Development"), - "Education": I18n.tr("Education"), - "Game": I18n.tr("Games"), - "Graphics": I18n.tr("Graphics"), - "Photography": I18n.tr("Graphics"), - "Network": I18n.tr("Internet"), - "WebBrowser": I18n.tr("Internet"), - "Email": I18n.tr("Internet"), - "Office": I18n.tr("Office"), - "WordProcessor": I18n.tr("Office"), - "Spreadsheet": I18n.tr("Office"), - "Presentation": I18n.tr("Office"), - "Science": I18n.tr("Science"), - "Settings": I18n.tr("Settings"), - "System": I18n.tr("System"), - "Utility": I18n.tr("Utilities"), - "Accessories": I18n.tr("Utilities"), - "FileManager": I18n.tr("Utilities"), - "TerminalEmulator": I18n.tr("Utilities") - }; - - const mappedCategories = new Set(); - - for (const cat of app.categories) { - if (categoryMap[cat]) - mappedCategories.add(categoryMap[cat]); - } - - return Array.from(mappedCategories); - } - - property var categoryIcons: ({ - "All": "apps", - "Media": "music_video", - "Development": "code", - "Games": "sports_esports", - "Graphics": "photo_library", - "Internet": "web", - "Office": "content_paste", - "Settings": "settings", - "System": "host", - "Utilities": "build" - }) - - function getCategoryIcon(category) { - // Check if it's a plugin category - const pluginIcon = getPluginCategoryIcon(category); - if (pluginIcon) { - return pluginIcon; - } - return categoryIcons[category] || "folder"; - } - - function getAllCategories() { - if (_cachedCategories) - return _cachedCategories; - - const categories = new Set([I18n.tr("All")]); - for (const app of applications) { - const appCategories = getCategoriesForApp(app); - appCategories.forEach(cat => categories.add(cat)); - } - - // Include categories from core apps (e.g. DMS Settings) - for (const app of coreApps) { - const appCategories = getCategoriesForApp(app); - appCategories.forEach(cat => categories.add(cat)); - } - - const pluginCategories = getPluginCategories(); - pluginCategories.forEach(cat => categories.add(cat)); - - _cachedCategories = Array.from(categories).sort(); - return _cachedCategories; - } - - function getAppsInCategory(category) { - const visibleApps = getVisibleApplications(); - if (category === I18n.tr("All")) - return visibleApps; - - const pluginItems = getPluginItems(category, ""); - if (pluginItems.length > 0) - return pluginItems; - - return visibleApps.filter(app => { - const appCategories = getCategoriesForApp(app); - return appCategories.includes(category); - }); - } - - // Plugin launcher support functions - function getPluginCategories() { - if (typeof PluginService === "undefined") { - return []; - } - - const categories = []; - const launchers = PluginService.getLauncherPlugins(); - - for (const pluginId in launchers) { - const plugin = launchers[pluginId]; - const categoryName = plugin.name || pluginId; - categories.push(categoryName); - } - - return categories; - } - - function getPluginCategoryIcon(category) { - if (typeof PluginService === "undefined") - return null; - - const launchers = PluginService.getLauncherPlugins(); - for (const pluginId in launchers) { - const plugin = launchers[pluginId]; - if ((plugin.name || pluginId) === category) { - return plugin.icon || "extension"; - } - } - return null; - } - - function getAllPluginItems() { - if (typeof PluginService === "undefined") { - return []; - } - - let allItems = []; - const launchers = PluginService.getLauncherPlugins(); - - for (const pluginId in launchers) { - const categoryName = launchers[pluginId].name || pluginId; - const items = getPluginItems(categoryName, ""); - allItems = allItems.concat(items); - } - - return allItems; - } - - function getPluginItems(category, query) { - if (typeof PluginService === "undefined") - return []; - - const launchers = PluginService.getLauncherPlugins(); - for (const pluginId in launchers) { - const plugin = launchers[pluginId]; - if ((plugin.name || pluginId) === category) { - return getPluginItemsForPlugin(pluginId, query); - } - } - return []; - } - - function getPluginItemsForPlugin(pluginId, query) { - if (typeof PluginService === "undefined") { - return []; - } - - let instance = PluginService.pluginInstances[pluginId]; - let isPersistent = true; - - if (!instance) { - const component = PluginService.pluginLauncherComponents[pluginId]; - if (!component) - return []; - - try { - instance = component.createObject(root, { - "pluginService": PluginService - }); - isPersistent = false; - } catch (e) { - console.warn("AppSearchService: Error creating temporary plugin instance", pluginId, ":", e); - return []; - } - } - - if (!instance) - return []; - - try { - if (typeof instance.getItems === "function") { - const items = instance.getItems(query || ""); - if (!isPersistent) - instance.destroy(); - return items || []; - } - - if (!isPersistent) { - instance.destroy(); - } - } catch (e) { - console.warn("AppSearchService: Error getting items from plugin", pluginId, ":", e); - if (!isPersistent) - instance.destroy(); - } - - return []; - } - - function executePluginItem(item, pluginId) { - if (typeof PluginService === "undefined") - return false; - - let instance = PluginService.pluginInstances[pluginId]; - let isPersistent = true; - - if (!instance) { - const component = PluginService.pluginLauncherComponents[pluginId]; - if (!component) - return false; - - try { - instance = component.createObject(root, { - "pluginService": PluginService - }); - isPersistent = false; - } catch (e) { - console.warn("AppSearchService: Error creating temporary plugin instance for execution", pluginId, ":", e); - return false; - } - } - - if (!instance) - return false; - - try { - if (typeof instance.executeItem === "function") { - instance.executeItem(item); - if (!isPersistent) - instance.destroy(); - return true; - } - - if (!isPersistent) { - instance.destroy(); - } - } catch (e) { - console.warn("AppSearchService: Error executing item from plugin", pluginId, ":", e); - if (!isPersistent) - instance.destroy(); - } - - return false; - } - - function getPluginPasteText(pluginId, item) { - if (typeof PluginService === "undefined") - return null; - - const instance = PluginService.pluginInstances[pluginId]; - if (!instance) - return null; - - if (typeof instance.getPasteText === "function") { - return instance.getPasteText(item); - } - - return null; - } - - function getPluginPasteArgs(pluginId, item) { - if (typeof PluginService === "undefined") - return null; - - const instance = PluginService.pluginInstances[pluginId]; - if (!instance) - return null; - - if (typeof instance.getPasteArgs === "function") - return instance.getPasteArgs(item); - - if (typeof instance.getPasteText === "function") { - const text = instance.getPasteText(item); - if (text) - return ["dms", "cl", "copy", text]; - } - - return null; - } - - function searchPluginItems(query) { - if (typeof PluginService === "undefined") - return []; - - let allItems = []; - const launchers = PluginService.getLauncherPlugins(); - - for (const pluginId in launchers) { - const items = getPluginItemsForPlugin(pluginId, query); - allItems = allItems.concat(items); - } - - return allItems; - } - - function getPluginLauncherCategories(pluginId) { - if (typeof PluginService === "undefined") - return []; - - const instance = PluginService.pluginInstances[pluginId]; - if (!instance) - return []; - - if (typeof instance.getCategories !== "function") - return []; - - try { - return instance.getCategories() || []; - } catch (e) { - console.warn("AppSearchService: Error getting categories from plugin", pluginId, ":", e); - return []; - } - } - - function setPluginLauncherCategory(pluginId, categoryId) { - if (typeof PluginService === "undefined") - return; - - const instance = PluginService.pluginInstances[pluginId]; - if (!instance) - return; - - if (typeof instance.setCategory !== "function") - return; - - try { - instance.setCategory(categoryId); - } catch (e) { - console.warn("AppSearchService: Error setting category on plugin", pluginId, ":", e); - } - } - - function pluginHasCategories(pluginId) { - if (typeof PluginService === "undefined") - return false; - - const instance = PluginService.pluginInstances[pluginId]; - if (!instance) - return false; - - return typeof instance.getCategories === "function"; - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/AudioService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/AudioService.qml deleted file mode 100644 index 015fedc..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/AudioService.qml +++ /dev/null @@ -1,1075 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtCore -import QtQuick -import Quickshell -import Quickshell.Io -import Quickshell.Services.Pipewire -import qs.Common -import qs.Services - -Singleton { - id: root - - readonly property PwNode sink: Pipewire.defaultAudioSink - readonly property PwNode source: Pipewire.defaultAudioSource - - readonly property bool soundsAvailable: MultimediaService.available - property bool gsettingsAvailable: false - property var availableSoundThemes: [] - property string currentSoundTheme: "" - property var soundFilePaths: ({}) - - property var volumeChangeSound: null - property var powerPlugSound: null - property var powerUnplugSound: null - property var normalNotificationSound: null - property var criticalNotificationSound: null - property var loginSound: null - property real notificationsVolume: 1.0 - property bool notificationsAudioMuted: false - - property var mediaDevices: null - property var mediaDevicesConnections: null - - property var deviceAliases: ({}) - property string wireplumberConfigPath: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/wireplumber/wireplumber.conf.d/51-dms-audio-aliases.conf" - property bool wireplumberReloading: false - - readonly property int sinkMaxVolume: { - const name = sink?.name ?? ""; - if (!name) - return 100; - return SessionData.deviceMaxVolumes[name] ?? 100; - } - - signal micMuteChanged - signal audioOutputCycled(string deviceName, string deviceIcon) - signal deviceAliasChanged(string nodeName, string newAlias) - signal wireplumberReloadStarted - signal wireplumberReloadCompleted(bool success) - - function getMaxVolumePercent(node) { - if (!node?.name) - return 100; - return SessionData.deviceMaxVolumes[node.name] ?? 100; - } - - Connections { - target: SessionData - function onDeviceMaxVolumesChanged() { - if (!root.sink?.audio) - return; - const maxVol = root.sinkMaxVolume; - const currentPercent = Math.round(root.sink.audio.volume * 100); - if (currentPercent > maxVol) - root.sink.audio.volume = maxVol / 100; - } - } - - // Used in playLoginSoundIfApplicable() - Process { - id: loginSoundChecker - onExited: (exitCode) => { - if (exitCode === 0) { - playLoginSound(); - } - } -} - - function getAvailableSinks() { - const hidden = SessionData.hiddenOutputDeviceNames ?? []; - return Pipewire.nodes.values.filter(node => node.audio && node.isSink && !node.isStream && !hidden.includes(node.name)); - } - - function cycleAudioOutput() { - const sinks = getAvailableSinks(); - if (sinks.length < 2) - return null; - - const currentName = root.sink?.name ?? ""; - const currentIndex = sinks.findIndex(s => s.name === currentName); - const nextIndex = (currentIndex + 1) % sinks.length; - const nextSink = sinks[nextIndex]; - Pipewire.preferredDefaultAudioSink = nextSink; - const name = displayName(nextSink); - audioOutputCycled(name, sinkIcon(nextSink)); - return name; - } - - function getDeviceAlias(nodeName) { - if (!nodeName) - return null; - return deviceAliases[nodeName] || null; - } - - function hasDeviceAlias(nodeName) { - if (!nodeName) - return false; - return deviceAliases.hasOwnProperty(nodeName) && deviceAliases[nodeName] !== null && deviceAliases[nodeName] !== ""; - } - - function setDeviceAlias(nodeName, customAlias) { - if (!nodeName) { - console.error("AudioService: Cannot set alias - nodeName is empty"); - return false; - } - - if (!customAlias || customAlias.trim() === "") { - return removeDeviceAlias(nodeName); - } - - const trimmedAlias = customAlias.trim(); - - const updated = Object.assign({}, deviceAliases); - updated[nodeName] = trimmedAlias; - deviceAliases = updated; - - writeWireplumberConfig(); - deviceAliasChanged(nodeName, trimmedAlias); - return true; - } - - function removeDeviceAlias(nodeName) { - if (!nodeName) - return false; - - if (!hasDeviceAlias(nodeName)) - return false; - - const updated = Object.assign({}, deviceAliases); - delete updated[nodeName]; - deviceAliases = updated; - - writeWireplumberConfig(); - deviceAliasChanged(nodeName, ""); - return true; - } - - function writeWireplumberConfig() { - const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/wireplumber/wireplumber.conf.d"; - const configContent = generateWireplumberConfig(); - - const shellCmd = `mkdir -p "${configDir}" && cat > "${wireplumberConfigPath}" << 'EOFCONFIG' -${configContent} -EOFCONFIG -`; - - Proc.runCommand("writeWireplumberConfig", ["sh", "-c", shellCmd], (output, exitCode) => { - if (exitCode !== 0) { - console.error("AudioService: Failed to write WirePlumber config. Exit code:", exitCode); - console.error("AudioService: Error output:", output); - ToastService.showError(I18n.tr("Failed to save audio config"), output || ""); - return; - } - - reloadWireplumberConfig(); - }, 0); - } - - function generateWireplumberConfig() { - let config = "# Generated by DankMaterialShell - Audio Device Aliases\n"; - config += "# Do not edit manually - changes will be overwritten\n"; - config += "# Last updated: " + new Date().toISOString() + "\n\n"; - - const aliasKeys = Object.keys(deviceAliases); - if (aliasKeys.length === 0) { - config += "# No device aliases configured\n"; - return config; - } - - const alsaAliases = []; - const bluezAliases = []; - const otherAliases = []; - - for (const nodeName of aliasKeys) { - const alias = deviceAliases[nodeName]; - if (!alias) - continue; - - const rule = { - nodeName: nodeName, - alias: alias - }; - - if (nodeName.includes("alsa")) { - alsaAliases.push(rule); - } else if (nodeName.includes("bluez")) { - bluezAliases.push(rule); - } else { - otherAliases.push(rule); - } - } - - if (alsaAliases.length > 0) { - config += "monitor.alsa.rules = [\n"; - for (let i = 0; i < alsaAliases.length; i++) { - const rule = alsaAliases[i]; - config += " {\n"; - config += ` matches = [ { "node.name" = "${rule.nodeName}" } ]\n`; - config += ` actions = { update-props = { "node.description" = "${rule.alias}" } }\n`; - config += " }"; - if (i < alsaAliases.length - 1) - config += ","; - config += "\n"; - } - config += "]\n\n"; - } - - if (bluezAliases.length > 0) { - config += "monitor.bluez.rules = [\n"; - for (let i = 0; i < bluezAliases.length; i++) { - const rule = bluezAliases[i]; - config += " {\n"; - config += ` matches = [ { "node.name" = "${rule.nodeName}" } ]\n`; - config += ` actions = { update-props = { "node.description" = "${rule.alias}" } }\n`; - config += " }"; - if (i < bluezAliases.length - 1) - config += ","; - config += "\n"; - } - config += "]\n\n"; - } - - if (otherAliases.length > 0) { - config += "# Other device aliases (RAOP, USB, and other devices)\n"; - config += "wireplumber.rules = [\n"; - for (let i = 0; i < otherAliases.length; i++) { - const rule = otherAliases[i]; - config += " {\n"; - config += ` matches = [\n`; - config += ` { "node.name" = "${rule.nodeName}" }\n`; - config += ` ]\n`; - config += ` actions = {\n`; - config += ` update-props = {\n`; - config += ` "node.description" = "${rule.alias}"\n`; - config += ` "node.nick" = "${rule.alias}"\n`; - config += ` "device.description" = "${rule.alias}"\n`; - config += ` }\n`; - config += ` }\n`; - config += " }"; - if (i < otherAliases.length - 1) - config += ","; - config += "\n"; - } - config += "]\n"; - } - - return config; - } - - function reloadWireplumberConfig() { - if (wireplumberReloading) { - return; - } - - wireplumberReloading = true; - wireplumberReloadStarted(); - - Proc.runCommand("restartWireplumber", ["systemctl", "--user", "restart", "wireplumber"], (output, exitCode) => { - wireplumberReloading = false; - - if (exitCode === 0) { - ToastService.showInfo(I18n.tr("Audio system restarted"), I18n.tr("Device names updated")); - wireplumberReloadCompleted(true); - } else { - console.error("AudioService: Failed to restart WirePlumber:", output); - ToastService.showError(I18n.tr("Failed to restart audio system"), output); - wireplumberReloadCompleted(false); - } - }, 5000); - } - - function loadDeviceAliases() { - const configPath = wireplumberConfigPath; - - Proc.runCommand("readWireplumberConfig", ["cat", configPath], (output, exitCode) => { - if (exitCode !== 0) { - console.log("AudioService: No existing WirePlumber config found"); - return; - } - - const aliases = {}; - const lines = output.split('\n'); - let currentNodeName = null; - - for (const line of lines) { - const nodeNameMatch = line.match(/"node\.name"\s*=\s*"([^"]+)"/); - if (nodeNameMatch) { - currentNodeName = nodeNameMatch[1]; - } - - const descriptionMatch = line.match(/"node\.description"\s*=\s*"([^"]+)"/); - if (descriptionMatch && currentNodeName) { - aliases[currentNodeName] = descriptionMatch[1]; - currentNodeName = null; - } - } - - if (Object.keys(aliases).length > 0) { - deviceAliases = aliases; - console.log("AudioService: Loaded", Object.keys(aliases).length, "device aliases"); - } - }, 0); - } - - Connections { - target: root.sink?.audio ?? null - - function onVolumeChanged() { - if (SessionData.suppressOSD) - return; - root.playVolumeChangeSoundIfEnabled(); - } - } - - function checkGsettings() { - Proc.runCommand("checkGsettings", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null"], (output, exitCode) => { - gsettingsAvailable = (exitCode === 0); - if (gsettingsAvailable) { - scanSoundThemes(); - getCurrentSoundTheme(); - } - }, 0); - } - - function scanSoundThemes() { - const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS"); - const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== "" ? xdgDataDirs.split(":").concat(Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))) : ["/usr/share", "/usr/local/share", Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))]; - - const basePaths = searchPaths.map(p => p + "/sounds").join(" "); - const script = ` - for base_dir in ${basePaths}; do - [ -d "$base_dir" ] || continue - for theme_dir in "$base_dir"/*; do - [ -d "$theme_dir/stereo" ] || continue - basename "$theme_dir" - done - done | sort -u - `; - - Proc.runCommand("scanSoundThemes", ["sh", "-c", script], (output, exitCode) => { - if (exitCode === 0 && output.trim()) { - const themes = output.trim().split('\n').filter(t => t && t.length > 0); - availableSoundThemes = themes; - } else { - availableSoundThemes = []; - } - }, 0); - } - - function getCurrentSoundTheme() { - Proc.runCommand("getCurrentSoundTheme", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null | sed \"s/'//g\""], (output, exitCode) => { - if (exitCode === 0 && output.trim()) { - currentSoundTheme = output.trim(); - console.log("AudioService: Current system sound theme:", currentSoundTheme); - if (SettingsData.useSystemSoundTheme) { - discoverSoundFiles(currentSoundTheme); - } - } else { - currentSoundTheme = ""; - console.log("AudioService: No system sound theme found"); - } - }, 0); - } - - function setSoundTheme(themeName) { - if (!themeName || themeName === currentSoundTheme) { - return; - } - - Proc.runCommand("setSoundTheme", ["sh", "-c", `gsettings set org.gnome.desktop.sound theme-name '${themeName}'`], (output, exitCode) => { - if (exitCode === 0) { - currentSoundTheme = themeName; - if (SettingsData.useSystemSoundTheme) { - discoverSoundFiles(themeName); - } - } - }, 0); - } - - function discoverSoundFiles(themeName) { - if (!themeName) { - soundFilePaths = {}; - if (soundsAvailable) { - destroySoundPlayers(); - createSoundPlayers(); - } - return; - } - - const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS"); - const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== "" ? xdgDataDirs.split(":").concat(Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))) : ["/usr/share", "/usr/local/share", Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))]; - - const extensions = ["oga", "ogg", "wav", "mp3", "flac"]; - const themesToSearch = themeName !== "freedesktop" ? `${themeName} freedesktop` : themeName; - - const script = ` - for event_key in audio-volume-change power-plug power-unplug message message-new-instant desktop-login; do - found=0 - - case "$event_key" in - message) - names="dialog-information message message-lowpriority bell" - ;; - message-new-instant) - names="dialog-warning message-new-instant message-highlight" - ;; - *) - names="$event_key" - ;; - esac - - for theme in ${themesToSearch}; do - for event_name in $names; do - for base_path in ${searchPaths.join(" ")}; do - sounds_path="$base_path/sounds" - for ext in ${extensions.join(" ")}; do - file_path="$sounds_path/$theme/stereo/$event_name.$ext" - if [ -f "$file_path" ]; then - echo "$event_key=$file_path" - found=1 - break - fi - done - [ $found -eq 1 ] && break - done - [ $found -eq 1 ] && break - done - [ $found -eq 1 ] && break - done - done - `; - - Proc.runCommand("discoverSoundFiles", ["sh", "-c", script], (output, exitCode) => { - const paths = {}; - if (exitCode === 0 && output.trim()) { - const lines = output.trim().split('\n'); - for (let line of lines) { - const parts = line.split('='); - if (parts.length === 2) { - paths[parts[0]] = "file://" + parts[1]; - } - } - } - soundFilePaths = paths; - - if (soundsAvailable) { - destroySoundPlayers(); - createSoundPlayers(); - } - }, 0); - } - - function getSoundPath(soundEvent) { - const soundMap = { - "audio-volume-change": "../assets/sounds/freedesktop/audio-volume-change.wav", - "power-plug": "../assets/sounds/plasma/power-plug.wav", - "power-unplug": "../assets/sounds/plasma/power-unplug.wav", - "message": "../assets/sounds/freedesktop/message.wav", - "message-new-instant": "../assets/sounds/freedesktop/message-new-instant.wav", - "desktop-login": "../assets/sounds/freedesktop/desktop-login.wav" - }; - - const specialConditions = { - "smooth": ["audio-volume-change"] - }; - - const themeLower = currentSoundTheme.toLowerCase(); - if (SettingsData.useSystemSoundTheme && specialConditions[themeLower]?.includes(soundEvent)) { - const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav"); - console.log("AudioService: Using bundled sound (special condition) for", soundEvent, ":", bundledPath); - return bundledPath; - } - - if (SettingsData.useSystemSoundTheme && soundFilePaths[soundEvent]) { - console.log("AudioService: Using system sound for", soundEvent, ":", soundFilePaths[soundEvent]); - return soundFilePaths[soundEvent]; - } - - const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav"); - console.log("AudioService: Using bundled sound for", soundEvent, ":", bundledPath); - return bundledPath; - } - - function reloadSounds() { - console.log("AudioService: Reloading sounds, useSystemSoundTheme:", SettingsData.useSystemSoundTheme, "currentSoundTheme:", currentSoundTheme); - if (SettingsData.useSystemSoundTheme && currentSoundTheme) { - discoverSoundFiles(currentSoundTheme); - } else { - soundFilePaths = {}; - if (soundsAvailable) { - destroySoundPlayers(); - createSoundPlayers(); - } - } - } - - function setupMediaDevices() { - if (!soundsAvailable || mediaDevices) { - return; - } - - try { - mediaDevices = Qt.createQmlObject(` - import QtQuick - import QtMultimedia - MediaDevices { - id: devices - Component.onCompleted: { - console.log("AudioService: MediaDevices initialized, default output:", defaultAudioOutput?.description) - } - } - `, root, "AudioService.MediaDevices"); - - if (mediaDevices) { - mediaDevicesConnections = Qt.createQmlObject(` - import QtQuick - Connections { - target: root.mediaDevices - function onDefaultAudioOutputChanged() { - console.log("AudioService: Default audio output changed, recreating sound players") - root.destroySoundPlayers() - root.createSoundPlayers() - } - } - `, root, "AudioService.MediaDevicesConnections"); - } - } catch (e) { - console.log("AudioService: MediaDevices not available, using default audio output"); - mediaDevices = null; - } - } - - function destroySoundPlayers() { - if (volumeChangeSound) { - volumeChangeSound.destroy(); - volumeChangeSound = null; - } - if (powerPlugSound) { - powerPlugSound.destroy(); - powerPlugSound = null; - } - if (powerUnplugSound) { - powerUnplugSound.destroy(); - powerUnplugSound = null; - } - if (normalNotificationSound) { - normalNotificationSound.destroy(); - normalNotificationSound = null; - } - if (criticalNotificationSound) { - criticalNotificationSound.destroy(); - criticalNotificationSound = null; - } - if (loginSound) { - loginSound.destroy(); - loginSound = null; - } - } - - function createSoundPlayers() { - if (!soundsAvailable) { - return; - } - - setupMediaDevices(); - - try { - const deviceProperty = mediaDevices ? `device: root.mediaDevices.defaultAudioOutput\n ` : ""; - - const volumeChangePath = getSoundPath("audio-volume-change"); - volumeChangeSound = Qt.createQmlObject(` - import QtQuick - import QtMultimedia - MediaPlayer { - source: "${volumeChangePath}" - audioOutput: AudioOutput { - ${deviceProperty}volume: notificationsVolume - } - } - `, root, "AudioService.VolumeChangeSound"); - - const powerPlugPath = getSoundPath("power-plug"); - powerPlugSound = Qt.createQmlObject(` - import QtQuick - import QtMultimedia - MediaPlayer { - source: "${powerPlugPath}" - audioOutput: AudioOutput { - ${deviceProperty}volume: notificationsVolume - } - } - `, root, "AudioService.PowerPlugSound"); - - const powerUnplugPath = getSoundPath("power-unplug"); - powerUnplugSound = Qt.createQmlObject(` - import QtQuick - import QtMultimedia - MediaPlayer { - source: "${powerUnplugPath}" - audioOutput: AudioOutput { - ${deviceProperty}volume: notificationsVolume - } - } - `, root, "AudioService.PowerUnplugSound"); - - const messagePath = getSoundPath("message"); - normalNotificationSound = Qt.createQmlObject(` - import QtQuick - import QtMultimedia - MediaPlayer { - source: "${messagePath}" - audioOutput: AudioOutput { - ${deviceProperty}volume: notificationsVolume - } - } - `, root, "AudioService.NormalNotificationSound"); - - const messageNewInstantPath = getSoundPath("message-new-instant"); - criticalNotificationSound = Qt.createQmlObject(` - import QtQuick - import QtMultimedia - MediaPlayer { - source: "${messageNewInstantPath}" - audioOutput: AudioOutput { - ${deviceProperty}volume: notificationsVolume - } - } - `, root, "AudioService.CriticalNotificationSound"); - - const loginPath = getSoundPath("desktop-login"); - loginSound = Qt.createQmlObject(` - import QtQuick - import QtMultimedia - MediaPlayer { - source: "${loginPath}" - audioOutput: AudioOutput { - ${deviceProperty}volume: notificationsVolume - } - } - `, root, "AudioService.LoginSound"); - - } catch (e) { - console.warn("AudioService: Error creating sound players:", e); - } - } - - function isMediaPlaying() { - return MprisController.activePlayer?.isPlaying ?? false; - } - - function playVolumeChangeSound() { - if (!soundsAvailable || !volumeChangeSound || notificationsAudioMuted || isMediaPlaying()) - return; - volumeChangeSound.play(); - } - - function playPowerPlugSound() { - if (!soundsAvailable || !powerPlugSound || notificationsAudioMuted || isMediaPlaying()) - return; - powerPlugSound.play(); - } - - function playPowerUnplugSound() { - if (!soundsAvailable || !powerUnplugSound || notificationsAudioMuted || isMediaPlaying()) - return; - powerUnplugSound.play(); - } - - function playNormalNotificationSound() { - if (!soundsAvailable || !normalNotificationSound || SessionData.doNotDisturb || notificationsAudioMuted || isMediaPlaying()) - return; - normalNotificationSound.play(); - } - - function playCriticalNotificationSound() { - if (!soundsAvailable || !criticalNotificationSound || SessionData.doNotDisturb || notificationsAudioMuted || isMediaPlaying()) - return; - criticalNotificationSound.play(); - } - - function playLoginSound() { - if (!soundsAvailable || !loginSound || notificationsAudioMuted || isMediaPlaying()) { - return; - } - loginSound.play(); - } - - function playLoginSoundIfApplicable() { - if (SettingsData.soundsEnabled && SettingsData.soundLogin && !notificationsAudioMuted) { - // plays login sound on session start, but only if a specific file doesn't exist, - // to prevent it from playing on every DMS restart during the session - const runtimeDir = Quickshell.env("XDG_RUNTIME_DIR"); - const sessionId = Quickshell.env("XDG_SESSION_ID") || "0"; - - if (!runtimeDir) return; - - const loginFile = `${runtimeDir}/danklinux.login-${sessionId}`; - - // if file doesn't exist, touch it (0) - // If it exists, do nothing (1) - loginSoundChecker.command = ["sh", "-c", `[ ! -f ${loginFile} ] && touch ${loginFile}`]; - loginSoundChecker.running = true; - } - } - - function playVolumeChangeSoundIfEnabled() { - if (SettingsData.soundsEnabled && SettingsData.soundVolumeChanged && !notificationsAudioMuted) { - playVolumeChangeSound(); - } - } - - function sinkIcon(node) { - if (!node) - return "speaker"; - - const props = node.properties || {}; - const formFactor = (props["device.form-factor"] || "").toLowerCase(); - - switch (formFactor) { - case "headphone": - case "headset": - case "hands-free": - case "handset": - return "headset"; - case "tv": - case "monitor": - return "tv"; - case "speaker": - case "computer": - case "hifi": - case "portable": - case "car": - return "speaker"; - } - - const bus = (props["device.bus"] || "").toLowerCase(); - if (bus === "bluetooth") - return "headset"; - - const name = (node.name || "").toLowerCase(); - if (name.includes("hdmi")) - return "tv"; - if (name.includes("iec958") || name.includes("spdif")) - return "speaker"; - - if (bus === "usb") - return "headset"; - - return "speaker"; - } - - function displayName(node) { - if (!node) { - return ""; - } - - // FIRST: Check if we have a custom alias in our deviceAliases map - // This ensures we always show the user's custom name, regardless of - // whether WirePlumber has applied it to the node properties yet - if (node.name && deviceAliases[node.name]) { - return deviceAliases[node.name]; - } - - // Check node.properties["node.description"] for WirePlumber-applied aliases - // This is the live property updated by WirePlumber rules - if (node.properties && node.properties["node.description"]) { - const desc = node.properties["node.description"]; - if (desc !== node.name) { - return desc; - } - } - - // Check cached description as fallback - if (node.description && node.description !== node.name) { - return node.description; - } - - // Fallback to device description property - if (node.properties && node.properties["device.description"]) { - return node.properties["device.description"]; - } - - // Fallback to nickname - if (node.nickname && node.nickname !== node.name) { - return node.nickname; - } - - // Fallback to friendly names based on node name patterns - if (node.name.includes("analog-stereo")) { - return "Built-in Audio Analog Stereo"; - } - if (node.name.includes("bluez")) { - return "Bluetooth Audio"; - } - if (node.name.includes("usb")) { - return "USB Audio"; - } - if (node.name.includes("hdmi")) { - return "HDMI Audio"; - } - - return node.name; - } - - function originalName(node) { - if (!node) { - return ""; - } - - // Get the original name without checking for custom aliases - // Check pattern-based friendly names FIRST (before device.description) - // This ensures we show user-friendly names like "Built-in Audio Analog Stereo" - // instead of hardware chip names like "ALC274 Analog" - if (node.name.includes("analog-stereo")) { - return "Built-in Audio Analog Stereo"; - } - if (node.name.includes("bluez")) { - return "Bluetooth Audio"; - } - if (node.name.includes("usb")) { - return "USB Audio"; - } - if (node.name.includes("hdmi")) { - return "HDMI Audio"; - } - if (node.name.includes("raop_sink")) { - // Extract friendly name from RAOP node name - const match = node.name.match(/raop_sink\.([^.]+)/); - if (match) { - return match[1].replace(/-/g, " "); - } - } - - // Fallback to device.description property - if (node.properties && node.properties["device.description"]) { - return node.properties["device.description"]; - } - - // Fallback to nickname - if (node.nickname && node.nickname !== node.name) { - return node.nickname; - } - - return node.name; - } - - function subtitle(name) { - if (!name) { - return ""; - } - - if (name.includes('usb-')) { - if (name.includes('SteelSeries')) { - return "USB Gaming Headset"; - } - if (name.includes('Generic')) { - return "USB Audio Device"; - } - return "USB Audio"; - } - - if (name.includes('pci-')) { - if (name.includes('01_00.1') || name.includes('01:00.1')) { - return "NVIDIA GPU Audio"; - } - return "PCI Audio"; - } - - if (name.includes('bluez')) { - return "Bluetooth Audio"; - } - if (name.includes('analog')) { - return "Built-in Audio"; - } - if (name.includes('hdmi')) { - return "HDMI Audio"; - } - - return ""; - } - - PwObjectTracker { - objects: Pipewire.nodes.values.filter(node => node.audio && !node.isStream) - } - - Connections { - target: Pipewire - function onDefaultAudioSinkChanged() { - if (soundsAvailable) { - Qt.callLater(root.destroySoundPlayers); - Qt.callLater(root.createSoundPlayers); - } - } - } - - function setVolume(percentage) { - if (!root.sink?.audio) - return "No audio sink available"; - - const maxVol = root.sinkMaxVolume; - const clampedVolume = Math.max(0, Math.min(maxVol, percentage)); - root.sink.audio.volume = clampedVolume / 100; - return `Volume set to ${clampedVolume}%`; - } - - function toggleMute() { - if (!root.sink?.audio) { - return "No audio sink available"; - } - - root.sink.audio.muted = !root.sink.audio.muted; - return root.sink.audio.muted ? "Audio muted" : "Audio unmuted"; - } - - function setMicVolume(percentage) { - if (!root.source?.audio) { - return "No audio source available"; - } - - const clampedVolume = Math.max(0, Math.min(100, percentage)); - root.source.audio.volume = clampedVolume / 100; - return `Microphone volume set to ${clampedVolume}%`; - } - - function toggleMicMute() { - if (!root.source?.audio) { - return "No audio source available"; - } - - root.source.audio.muted = !root.source.audio.muted; - return root.source.audio.muted ? "Microphone muted" : "Microphone unmuted"; - } - - IpcHandler { - target: "audio" - - function setvolume(percentage: string): string { - return root.setVolume(parseInt(percentage)); - } - - function increment(step: string): string { - if (!root.sink?.audio) - return "No audio sink available"; - - if (root.sink.audio.muted) - root.sink.audio.muted = false; - - const maxVol = root.sinkMaxVolume; - const currentVolume = Math.round(root.sink.audio.volume * 100); - const stepValue = parseInt(step || "5"); - const newVolume = Math.max(0, Math.min(maxVol, currentVolume + stepValue)); - - root.sink.audio.volume = newVolume / 100; - return `Volume increased to ${newVolume}%`; - } - - function decrement(step: string): string { - if (!root.sink?.audio) - return "No audio sink available"; - - if (root.sink.audio.muted) - root.sink.audio.muted = false; - - const maxVol = root.sinkMaxVolume; - const currentVolume = Math.round(root.sink.audio.volume * 100); - const stepValue = parseInt(step || "5"); - const newVolume = Math.max(0, Math.min(maxVol, currentVolume - stepValue)); - - root.sink.audio.volume = newVolume / 100; - return `Volume decreased to ${newVolume}%`; - } - - function mute(): string { - return root.toggleMute(); - } - - function setmic(percentage: string): string { - return root.setMicVolume(parseInt(percentage)); - } - - function micmute(): string { - const result = root.toggleMicMute(); - root.micMuteChanged(); - return result; - } - - function status(): string { - let result = "Audio Status:\n"; - - if (root.sink?.audio) { - const volume = Math.round(root.sink.audio.volume * 100); - const muteStatus = root.sink.audio.muted ? " (muted)" : ""; - const maxVol = root.sinkMaxVolume; - result += `Output: ${volume}%${muteStatus} (max: ${maxVol}%)\n`; - } else { - result += "Output: No sink available\n"; - } - - if (root.source?.audio) { - const micVolume = Math.round(root.source.audio.volume * 100); - const muteStatus = root.source.audio.muted ? " (muted)" : ""; - result += `Input: ${micVolume}%${muteStatus}`; - } else { - result += "Input: No source available"; - } - - return result; - } - - function getmaxvolume(): string { - return `${root.sinkMaxVolume}`; - } - - function setmaxvolume(percent: string): string { - if (!root.sink?.name) - return "No audio sink available"; - const val = parseInt(percent); - if (isNaN(val)) - return "Invalid percentage"; - SessionData.setDeviceMaxVolume(root.sink.name, val); - return `Max volume set to ${SessionData.getDeviceMaxVolume(root.sink.name)}%`; - } - - function getmaxvolumefor(nodeName: string): string { - if (!nodeName) - return "No node name specified"; - return `${SessionData.getDeviceMaxVolume(nodeName)}`; - } - - function setmaxvolumefor(nodeName: string, percent: string): string { - if (!nodeName) - return "No node name specified"; - const val = parseInt(percent); - if (isNaN(val)) - return "Invalid percentage"; - SessionData.setDeviceMaxVolume(nodeName, val); - return `Max volume for ${nodeName} set to ${SessionData.getDeviceMaxVolume(nodeName)}%`; - } - - function cycleoutput(): string { - const result = root.cycleAudioOutput(); - if (!result) - return "Only one audio output available"; - return `Switched to: ${result}`; - } - } - - Connections { - target: SettingsData - function onUseSystemSoundThemeChanged() { - reloadSounds(); - } - } - - Component.onCompleted: { - if (soundsAvailable) { - checkGsettings(); - Qt.callLater(createSoundPlayers); - } - - loadDeviceAliases(); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BarWidgetService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BarWidgetService.qml deleted file mode 100644 index 0ae2450..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BarWidgetService.qml +++ /dev/null @@ -1,171 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Hyprland -import Quickshell.I3 - -Singleton { - id: root - - property var widgetRegistry: ({}) - property var dankBarRepeater: null - - signal widgetRegistered(string widgetId, string screenName) - signal widgetUnregistered(string widgetId, string screenName) - - function registerWidget(widgetId, screenName, widgetRef) { - if (!widgetId || !screenName || !widgetRef) - return; - if (typeof widgetRegistry !== "object" || widgetRegistry === null) - widgetRegistry = ({}); - - if (!widgetRegistry[widgetId]) - widgetRegistry[widgetId] = {}; - - widgetRegistry[widgetId][screenName] = widgetRef; - widgetRegistered(widgetId, screenName); - } - - function unregisterWidget(widgetId, screenName) { - if (!widgetId || !screenName) - return; - if (typeof widgetRegistry !== "object" || widgetRegistry === null) - return; - if (!widgetRegistry[widgetId]) - return; - - delete widgetRegistry[widgetId][screenName]; - if (Object.keys(widgetRegistry[widgetId]).length === 0) - delete widgetRegistry[widgetId]; - - widgetUnregistered(widgetId, screenName); - } - - function getWidget(widgetId, screenName) { - if (!widgetRegistry[widgetId]) - return null; - if (screenName) - return widgetRegistry[widgetId][screenName] || null; - - const screens = Object.keys(widgetRegistry[widgetId]); - return screens.length > 0 ? widgetRegistry[widgetId][screens[0]] : null; - } - - function getWidgetOnFocusedScreen(widgetId) { - if (!widgetRegistry[widgetId]) - return null; - - const focusedScreen = getFocusedScreenName(); - if (focusedScreen && widgetRegistry[widgetId][focusedScreen]) - return widgetRegistry[widgetId][focusedScreen]; - - const screens = Object.keys(widgetRegistry[widgetId]); - return screens.length > 0 ? widgetRegistry[widgetId][screens[0]] : null; - } - - function getFocusedScreenName() { - if (CompositorService.isHyprland && Hyprland.focusedWorkspace?.monitor) - return Hyprland.focusedWorkspace.monitor.name; - if (CompositorService.isNiri && NiriService.currentOutput) - return NiriService.currentOutput; - if (CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) { - const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true); - return focusedWs?.monitor?.name || ""; - } - return ""; - } - - function getRegisteredWidgetIds() { - return Object.keys(widgetRegistry); - } - - function hasWidget(widgetId) { - return widgetRegistry[widgetId] && Object.keys(widgetRegistry[widgetId]).length > 0; - } - - function triggerWidgetPopout(widgetId) { - const widget = getWidgetOnFocusedScreen(widgetId); - if (!widget) - return false; - - if (typeof widget.triggerPopout === "function") { - widget.triggerPopout(); - return true; - } - - const signalMap = { - "battery": "toggleBatteryPopup", - "vpn": "toggleVpnPopup", - "layout": "toggleLayoutPopup", - "clock": "clockClicked", - "cpuUsage": "cpuClicked", - "memUsage": "ramClicked", - "cpuTemp": "cpuTempClicked", - "gpuTemp": "gpuTempClicked" - }; - - const signalName = signalMap[widgetId]; - if (signalName && typeof widget[signalName] === "function") { - widget[signalName](); - return true; - } - - if (typeof widget.clicked === "function") { - widget.clicked(); - return true; - } - - if (widget.popoutTarget?.toggle) { - widget.popoutTarget.toggle(); - return true; - } - - return false; - } - - function getBarWindowForScreen(screenName) { - if (!dankBarRepeater) - return null; - - for (var i = 0; i < dankBarRepeater.count; i++) { - const loader = dankBarRepeater.itemAt(i); - if (!loader?.item) - continue; - - const barItem = loader.item; - if (!barItem.barVariants?.instances) - continue; - - for (var j = 0; j < barItem.barVariants.instances.length; j++) { - const barInstance = barItem.barVariants.instances[j]; - if (barInstance.modelData?.name === screenName) - return barInstance; - } - } - return null; - } - - function getBarWindowOnFocusedScreen() { - const focusedScreen = getFocusedScreenName(); - if (!focusedScreen) - return getFirstBarWindow(); - return getBarWindowForScreen(focusedScreen) || getFirstBarWindow(); - } - - function getFirstBarWindow() { - if (!dankBarRepeater || dankBarRepeater.count === 0) - return null; - - const loader = dankBarRepeater.itemAt(0); - if (!loader?.item) - return null; - - const barItem = loader.item; - if (!barItem.barVariants?.instances || barItem.barVariants.instances.length === 0) - return null; - - return barItem.barVariants.instances[0]; - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BatteryService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BatteryService.qml deleted file mode 100644 index e7cea56..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BatteryService.qml +++ /dev/null @@ -1,271 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Services.UPower -import qs.Common - -Singleton { - id: root - - property bool suppressSound: true - property bool previousPluggedState: false - - readonly property var scale: 100 / SettingsData.batteryChargeLimit - - Timer { - id: startupTimer - interval: 500 - repeat: false - running: true - onTriggered: root.suppressSound = false - } - - readonly property string preferredBatteryOverride: Quickshell.env("DMS_PREFERRED_BATTERY") - - // List of laptop batteries - readonly property var batteries: UPower.devices.values.filter(dev => dev.isLaptopBattery) - - readonly property bool usePreferred: preferredBatteryOverride && preferredBatteryOverride.length > 0 - - // Main battery (for backward compatibility) - readonly property UPowerDevice device: { - var preferredDev; - if (usePreferred) { - preferredDev = batteries.find(dev => dev.nativePath.toLowerCase().includes(preferredBatteryOverride.toLowerCase())); - } - return preferredDev || batteries[0] || null; - } - // Whether at least one battery is available - readonly property bool batteryAvailable: batteries.length > 0 - // Aggregated charge level (percentage) - readonly property real batteryLevel: { - if (!batteryAvailable) - return 0; - if (batteryCapacity === 0) { - if (usePreferred && device && device.ready) - return Math.round(device.percentage * 100 * scale); - const validBatteries = batteries.filter(b => b.ready && b.percentage >= 0); - if (validBatteries.length === 0) - return 0; - const avgPercentage = validBatteries.reduce((sum, b) => sum + b.percentage, 0) / validBatteries.length; - return Math.round(avgPercentage * 100 * scale); - } - return Math.round((batteryEnergy * 100) / batteryCapacity * scale); - } - readonly property bool isCharging: batteryAvailable && batteries.some(b => b.state === UPowerDeviceState.Charging) - - // Is the system plugged in (Is not running on battery) - readonly property bool isPluggedIn: !UPower.onBattery - readonly property bool isLowBattery: batteryAvailable && batteryLevel <= 20 - - onIsPluggedInChanged: { - if (suppressSound || !batteryAvailable) { - previousPluggedState = isPluggedIn; - return; - } - - if (SettingsData.soundsEnabled && SettingsData.soundPluggedIn) { - if (isPluggedIn && !previousPluggedState) { - AudioService.playPowerPlugSound(); - } else if (!isPluggedIn && previousPluggedState) { - AudioService.playPowerUnplugSound(); - } - } - - const profileValue = BatteryService.isPluggedIn ? SettingsData.acProfileName : SettingsData.batteryProfileName; - - if (profileValue !== "") { - const targetProfile = parseInt(profileValue); - if (!isNaN(targetProfile) && PowerProfiles.profile !== targetProfile) { - PowerProfiles.profile = targetProfile; - } - } - - previousPluggedState = isPluggedIn; - } - - // Aggregated charge/discharge rate - readonly property real changeRate: { - if (!batteryAvailable) - return 0; - if (usePreferred && device && device.ready) - return device.changeRate; - return batteries.length > 0 ? batteries.reduce((sum, b) => sum + b.changeRate, 0) : 0; - } - - // Aggregated battery health - readonly property string batteryHealth: { - if (!batteryAvailable) - return "N/A"; - - // If a preferred battery is selected and ready - if (usePreferred && device && device.ready && device.healthSupported) - return `${Math.round(device.healthPercentage)}%`; - - // Otherwise, calculate the average health of all laptop batteries - const validBatteries = batteries.filter(b => b.healthSupported && b.healthPercentage > 0); - if (validBatteries.length === 0) - return "N/A"; - - const avgHealth = validBatteries.reduce((sum, b) => sum + b.healthPercentage, 0) / validBatteries.length; - return `${Math.round(avgHealth)}%`; - } - - readonly property real batteryEnergy: { - if (!batteryAvailable) - return 0; - if (usePreferred && device && device.ready) - return device.energy; - return batteries.length > 0 ? batteries.reduce((sum, b) => sum + b.energy, 0) : 0; - } - - // Total battery capacity (Wh) - readonly property real batteryCapacity: { - if (!batteryAvailable) - return 0; - if (usePreferred && device && device.ready) - return device.energyCapacity; - return batteries.length > 0 ? batteries.reduce((sum, b) => sum + b.energyCapacity, 0) : 0; - } - - function translateBatteryState(state) { - switch (state) { - case UPowerDeviceState.Charging: - return I18n.tr("Charging", "battery status"); - case UPowerDeviceState.Discharging: - return I18n.tr("Discharging", "battery status"); - case UPowerDeviceState.Empty: - return I18n.tr("Empty", "battery status"); - case UPowerDeviceState.FullyCharged: - return I18n.tr("Fully Charged", "battery status"); - case UPowerDeviceState.PendingCharge: - return I18n.tr("Pending Charge", "battery status"); - case UPowerDeviceState.PendingDischarge: - return I18n.tr("Pending Discharge", "battery status"); - default: - return I18n.tr("Unknown", "battery status"); - } - } - - // Aggregated battery status - readonly property string batteryStatus: { - if (!batteryAvailable) { - return I18n.tr("No Battery", "battery status"); - } - - if (isCharging && !batteries.some(b => b.changeRate > 0)) - return I18n.tr("Plugged In", "battery status"); - - const states = batteries.map(b => b.state); - if (states.every(s => s === states[0])) - return translateBatteryState(states[0]); - - return isCharging ? I18n.tr("Charging", "battery status") : (isPluggedIn ? I18n.tr("Plugged In", "battery status") : I18n.tr("Discharging", "battery status")); - } - - readonly property bool suggestPowerSaver: false - - readonly property var bluetoothDevices: { - const btDevices = []; - const bluetoothTypes = [UPowerDeviceType.BluetoothGeneric, UPowerDeviceType.Headphones, UPowerDeviceType.Headset, UPowerDeviceType.Keyboard, UPowerDeviceType.Mouse, UPowerDeviceType.Speakers]; - - for (var i = 0; i < UPower.devices.count; i++) { - const dev = UPower.devices.get(i); - if (dev && dev.ready && bluetoothTypes.includes(dev.type)) { - btDevices.push({ - "name": dev.model || UPowerDeviceType.toString(dev.type), - "percentage": Math.round(dev.percentage * 100), - "type": dev.type - }); - } - } - return btDevices; - } - - // Format time remaining for charge/discharge - function formatTimeRemaining() { - if (!batteryAvailable) { - return "Unknown"; - } - - let totalTime = 0; - totalTime = (isCharging) ? ((batteryCapacity - batteryEnergy) / changeRate) : (batteryEnergy / changeRate); - const avgTime = Math.abs(totalTime * 3600); - if (!avgTime || avgTime <= 0 || avgTime > 86400) - return "Unknown"; - - const hours = Math.floor(avgTime / 3600); - const minutes = Math.floor((avgTime % 3600) / 60); - return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`; - } - - function getBatteryIcon() { - if (!batteryAvailable) { - return "power"; - } - - if (isCharging) { - if (batteryLevel >= 90) { - return "battery_charging_full"; - } - if (batteryLevel >= 80) { - return "battery_charging_90"; - } - if (batteryLevel >= 60) { - return "battery_charging_80"; - } - if (batteryLevel >= 50) { - return "battery_charging_60"; - } - if (batteryLevel >= 30) { - return "battery_charging_50"; - } - if (batteryLevel >= 20) { - return "battery_charging_30"; - } - return "battery_charging_20"; - } - if (isPluggedIn) { - if (batteryLevel >= 90) { - return "battery_charging_full"; - } - if (batteryLevel >= 80) { - return "battery_charging_90"; - } - if (batteryLevel >= 60) { - return "battery_charging_80"; - } - if (batteryLevel >= 50) { - return "battery_charging_60"; - } - if (batteryLevel >= 30) { - return "battery_charging_50"; - } - if (batteryLevel >= 20) { - return "battery_charging_30"; - } - return "battery_charging_20"; - } - if (batteryLevel >= 95) { - return "battery_full"; - } - if (batteryLevel >= 85) { - return "battery_6_bar"; - } - if (batteryLevel >= 70) { - return "battery_5_bar"; - } - if (batteryLevel >= 55) { - return "battery_4_bar"; - } - if (batteryLevel >= 40) { - return "battery_3_bar"; - } - if (batteryLevel >= 25) { - return "battery_2_bar"; - } - return "battery_1_bar"; - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BluetoothService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BluetoothService.qml deleted file mode 100644 index 4de300e..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BluetoothService.qml +++ /dev/null @@ -1,523 +0,0 @@ -pragma Singleton - -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import Quickshell.Bluetooth -import qs.Services - -Singleton { - id: root - - readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter - readonly property bool available: adapter !== null - readonly property bool enabled: (adapter && adapter.enabled) ?? false - readonly property bool discovering: (adapter && adapter.discovering) ?? false - readonly property var devices: adapter ? adapter.devices : null - readonly property bool enhancedPairingAvailable: DMSService.dmsAvailable && DMSService.apiVersion >= 9 && DMSService.capabilities.includes("bluetooth") - readonly property bool connected: { - if (!adapter || !adapter.devices) { - return false - } - - let isConnected = false - adapter.devices.values.forEach(dev => { if (dev.connected) isConnected = true }) - return isConnected - } - readonly property var pairedDevices: { - if (!adapter || !adapter.devices) { - return [] - } - - return adapter.devices.values.filter(dev => { - return dev && (dev.paired || dev.trusted) - }) - } - readonly property var allDevicesWithBattery: { - if (!adapter || !adapter.devices) { - return [] - } - - return adapter.devices.values.filter(dev => { - return dev && dev.batteryAvailable && dev.battery > 0 - }) - } - - function sortDevices(devices) { - return devices.sort((a, b) => { - const aName = a.name || a.deviceName || "" - const bName = b.name || b.deviceName || "" - const aAddr = a.address || "" - const bAddr = b.address || "" - - const aHasRealName = aName.includes(" ") && aName.length > 3 - const bHasRealName = bName.includes(" ") && bName.length > 3 - - if (aHasRealName && !bHasRealName) return -1 - if (!aHasRealName && bHasRealName) return 1 - - if (aHasRealName && bHasRealName) { - return aName.localeCompare(bName) - } - - return aAddr.localeCompare(bAddr) - }) - } - - function getDeviceIcon(device) { - if (!device) { - return "bluetooth" - } - - const name = (device.name || device.deviceName || "").toLowerCase() - const icon = (device.icon || "").toLowerCase() - - const audioKeywords = ["headset", "audio", "headphone", "airpod", "arctis"] - if (audioKeywords.some(keyword => icon.includes(keyword) || name.includes(keyword))) { - return "headset" - } - - if (icon.includes("mouse") || name.includes("mouse")) { - return "mouse" - } - - if (icon.includes("keyboard") || name.includes("keyboard")) { - return "keyboard" - } - - const phoneKeywords = ["phone", "iphone", "android", "samsung"] - if (phoneKeywords.some(keyword => icon.includes(keyword) || name.includes(keyword))) { - return "smartphone" - } - - if (icon.includes("watch") || name.includes("watch")) { - return "watch" - } - - if (icon.includes("speaker") || name.includes("speaker")) { - return "speaker" - } - - if (icon.includes("display") || name.includes("tv")) { - return "tv" - } - - return "bluetooth" - } - - function canConnect(device) { - if (!device) { - return false - } - - return !device.paired && !device.pairing && !device.blocked - } - - function getSignalStrength(device) { - if (!device || device.signalStrength === undefined || device.signalStrength <= 0) { - return "Unknown" - } - - const signal = device.signalStrength - if (signal >= 80) { - return "Excellent" - } - if (signal >= 60) { - return "Good" - } - if (signal >= 40) { - return "Fair" - } - if (signal >= 20) { - return "Poor" - } - - return "Very Poor" - } - - function getSignalIcon(device) { - if (!device || device.signalStrength === undefined || device.signalStrength <= 0) { - return "signal_cellular_null" - } - - const signal = device.signalStrength - if (signal >= 80) { - return "signal_cellular_4_bar" - } - if (signal >= 60) { - return "signal_cellular_3_bar" - } - if (signal >= 40) { - return "signal_cellular_2_bar" - } - if (signal >= 20) { - return "signal_cellular_1_bar" - } - - return "signal_cellular_0_bar" - } - - function isDeviceBusy(device) { - if (!device) { - return false - } - return device.pairing || device.state === BluetoothDeviceState.Disconnecting || device.state === BluetoothDeviceState.Connecting - } - - function connectDeviceWithTrust(device) { - if (!device) { - return - } - - device.trusted = true - device.connect() - } - - function pairDevice(device, callback) { - if (!device) { - if (callback) callback({error: "Invalid device"}) - return - } - - // The DMS backend actually implements a bluez agent, so we can pair anything - if (enhancedPairingAvailable) { - const devicePath = getDevicePath(device) - DMSService.bluetoothPair(devicePath, callback) - return - } - - // Quickshell does not implement a bluez agent, so we can try to pair but only with devices that don't require a passcode - device.trusted = true - device.connect() - if (callback) callback({success: true}) - } - - function getCardName(device) { - if (!device) { - return "" - } - return `bluez_card.${device.address.replace(/:/g, "_")}` - } - - function getDevicePath(device) { - if (!device || !device.address) { - return "" - } - const adapterPath = adapter ? "/org/bluez/hci0" : "/org/bluez/hci0" - return `${adapterPath}/dev_${device.address.replace(/:/g, "_")}` - } - - function isAudioDevice(device) { - if (!device) { - return false - } - const icon = getDeviceIcon(device) - return icon === "headset" || icon === "speaker" - } - - function getCodecInfo(codecName) { - const codec = codecName.replace(/-/g, "_").toUpperCase() - - const codecMap = { - "LDAC": { - "name": "LDAC", - "description": "Highest quality • Higher battery usage", - "qualityColor": "#4CAF50" - }, - "APTX_HD": { - "name": "aptX HD", - "description": "High quality • Balanced battery", - "qualityColor": "#FF9800" - }, - "APTX": { - "name": "aptX", - "description": "Good quality • Low latency", - "qualityColor": "#FF9800" - }, - "AAC": { - "name": "AAC", - "description": "Balanced quality and battery", - "qualityColor": "#2196F3" - }, - "SBC_XQ": { - "name": "SBC-XQ", - "description": "Enhanced SBC • Better compatibility", - "qualityColor": "#2196F3" - }, - "SBC": { - "name": "SBC", - "description": "Basic quality • Universal compatibility", - "qualityColor": "#9E9E9E" - }, - "MSBC": { - "name": "mSBC", - "description": "Modified SBC • Optimized for speech", - "qualityColor": "#9E9E9E" - }, - "CVSD": { - "name": "CVSD", - "description": "Basic speech codec • Legacy compatibility", - "qualityColor": "#9E9E9E" - } - } - - return codecMap[codec] || { - "name": codecName, - "description": "Unknown codec", - "qualityColor": "#9E9E9E" - } - } - - property var deviceCodecs: ({}) - - function updateDeviceCodec(deviceAddress, codec) { - deviceCodecs[deviceAddress] = codec - deviceCodecsChanged() - } - - function refreshDeviceCodec(device) { - if (!device || !device.connected || !isAudioDevice(device)) { - return - } - - const cardName = getCardName(device) - codecQueryProcess.cardName = cardName - codecQueryProcess.deviceAddress = device.address - codecQueryProcess.availableCodecs = [] - codecQueryProcess.parsingTargetCard = false - codecQueryProcess.detectedCodec = "" - codecQueryProcess.running = true - } - - function getCurrentCodec(device, callback) { - if (!device || !device.connected || !isAudioDevice(device)) { - callback("") - return - } - - const cardName = getCardName(device) - codecQueryProcess.cardName = cardName - codecQueryProcess.callback = callback - codecQueryProcess.availableCodecs = [] - codecQueryProcess.parsingTargetCard = false - codecQueryProcess.detectedCodec = "" - codecQueryProcess.running = true - } - - function getAvailableCodecs(device, callback) { - if (!device || !device.connected || !isAudioDevice(device)) { - callback([], "") - return - } - - const cardName = getCardName(device) - codecFullQueryProcess.cardName = cardName - codecFullQueryProcess.callback = callback - codecFullQueryProcess.availableCodecs = [] - codecFullQueryProcess.parsingTargetCard = false - codecFullQueryProcess.detectedCodec = "" - codecFullQueryProcess.running = true - } - - function switchCodec(device, profileName, callback) { - if (!device || !isAudioDevice(device)) { - callback(false, "Invalid device") - return - } - - const cardName = getCardName(device) - codecSwitchProcess.cardName = cardName - codecSwitchProcess.profile = profileName - codecSwitchProcess.callback = callback - codecSwitchProcess.running = true - } - - Process { - id: codecQueryProcess - - property string cardName: "" - property string deviceAddress: "" - property var callback: null - property bool parsingTargetCard: false - property string detectedCodec: "" - property var availableCodecs: [] - - command: ["pactl", "list", "cards"] - - onExited: (exitCode, exitStatus) => { - if (exitCode === 0 && detectedCodec) { - if (deviceAddress) { - root.updateDeviceCodec(deviceAddress, detectedCodec) - } - if (callback) { - callback(detectedCodec) - } - } else if (callback) { - callback("") - } - - parsingTargetCard = false - detectedCodec = "" - availableCodecs = [] - deviceAddress = "" - callback = null - } - - stdout: SplitParser { - splitMarker: "\n" - onRead: data => { - let line = data.trim() - - if (line.includes(`Name: ${codecQueryProcess.cardName}`)) { - codecQueryProcess.parsingTargetCard = true - return - } - - if (codecQueryProcess.parsingTargetCard && line.startsWith("Name: ") && !line.includes(codecQueryProcess.cardName)) { - codecQueryProcess.parsingTargetCard = false - return - } - - if (codecQueryProcess.parsingTargetCard) { - if (line.startsWith("Active Profile:")) { - let profile = line.split(": ")[1] || "" - let activeCodec = codecQueryProcess.availableCodecs.find(c => { - return c.profile === profile - }) - if (activeCodec) { - codecQueryProcess.detectedCodec = activeCodec.name - } - return - } - if (line.includes("codec") && line.includes("available: yes")) { - let parts = line.split(": ") - if (parts.length >= 2) { - let profile = parts[0].trim() - let description = parts[1] - let codecMatch = description.match(/codec ([^\)\s]+)/i) - let codecName = codecMatch ? codecMatch[1].toUpperCase() : "UNKNOWN" - let codecInfo = root.getCodecInfo(codecName) - if (codecInfo && !codecQueryProcess.availableCodecs.some(c => { - return c.profile === profile - })) { - let newCodecs = codecQueryProcess.availableCodecs.slice() - newCodecs.push({ - "name": codecInfo.name, - "profile": profile, - "description": codecInfo.description, - "qualityColor": codecInfo.qualityColor - }) - codecQueryProcess.availableCodecs = newCodecs - } - } - } - } - } - } - } - - Process { - id: codecFullQueryProcess - - property string cardName: "" - property var callback: null - property bool parsingTargetCard: false - property string detectedCodec: "" - property var availableCodecs: [] - - command: ["pactl", "list", "cards"] - - onExited: function (exitCode, exitStatus) { - if (callback) { - callback(exitCode === 0 ? availableCodecs : [], exitCode === 0 ? detectedCodec : "") - } - parsingTargetCard = false - detectedCodec = "" - availableCodecs = [] - callback = null - } - - stdout: SplitParser { - splitMarker: "\n" - onRead: data => { - let line = data.trim() - - if (line.includes(`Name: ${codecFullQueryProcess.cardName}`)) { - codecFullQueryProcess.parsingTargetCard = true - return - } - - if (codecFullQueryProcess.parsingTargetCard && line.startsWith("Name: ") && !line.includes(codecFullQueryProcess.cardName)) { - codecFullQueryProcess.parsingTargetCard = false - return - } - - if (codecFullQueryProcess.parsingTargetCard) { - if (line.startsWith("Active Profile:")) { - let profile = line.split(": ")[1] || "" - let activeCodec = codecFullQueryProcess.availableCodecs.find(c => { - return c.profile === profile - }) - if (activeCodec) { - codecFullQueryProcess.detectedCodec = activeCodec.name - } - return - } - if (line.includes("codec") && line.includes("available: yes")) { - let parts = line.split(": ") - if (parts.length >= 2) { - let profile = parts[0].trim() - let description = parts[1] - let codecMatch = description.match(/codec ([^\)\s]+)/i) - let codecName = codecMatch ? codecMatch[1].toUpperCase() : "UNKNOWN" - let codecInfo = root.getCodecInfo(codecName) - if (codecInfo && !codecFullQueryProcess.availableCodecs.some(c => { - return c.profile === profile - })) { - let newCodecs = codecFullQueryProcess.availableCodecs.slice() - newCodecs.push({ - "name": codecInfo.name, - "profile": profile, - "description": codecInfo.description, - "qualityColor": codecInfo.qualityColor - }) - codecFullQueryProcess.availableCodecs = newCodecs - } - } - } - } - } - } - } - - Process { - id: codecSwitchProcess - - property string cardName: "" - property string profile: "" - property var callback: null - - command: ["pactl", "set-card-profile", cardName, profile] - - onExited: function (exitCode, exitStatus) { - if (callback) { - callback(exitCode === 0, exitCode === 0 ? "Codec switched successfully" : "Failed to switch codec") - } - - // If successful, refresh the codec for this device - if (exitCode === 0) { - if (root.adapter && root.adapter.devices) { - root.adapter.devices.values.forEach(device => { - if (device && root.getCardName(device) === cardName) { - Qt.callLater(() => root.refreshDeviceCodec(device)) - } - }) - } - } - - callback = null - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BlurService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BlurService.qml deleted file mode 100644 index 40a3aeb..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/BlurService.qml +++ /dev/null @@ -1,113 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import Quickshell.Wayland // ! Import is needed despite what qmlls says -import qs.Common - -Singleton { - id: root - - property bool quickshellSupported: false - property bool compositorSupported: false - property bool available: quickshellSupported && compositorSupported - readonly property bool enabled: available && (SettingsData.blurEnabled ?? false) - - readonly property color borderColor: { - if (!enabled) - return "transparent"; - const opacity = SettingsData.blurBorderOpacity ?? 0.5; - switch (SettingsData.blurBorderColor ?? "outline") { - case "primary": - return Theme.withAlpha(Theme.primary, opacity); - case "secondary": - return Theme.withAlpha(Theme.secondary, opacity); - case "surfaceText": - return Theme.withAlpha(Theme.surfaceText, opacity); - case "custom": - return Theme.withAlpha(SettingsData.blurBorderCustomColor ?? "#ffffff", opacity); - default: - return Theme.withAlpha(Theme.outline, opacity); - } - } - readonly property int borderWidth: enabled ? 1 : 0 - - function hoverColor(baseColor, hoverAlpha) { - if (!enabled) - return baseColor; - return Theme.withAlpha(baseColor, hoverAlpha ?? 0.15); - } - - function createBlurRegion(targetWindow) { - if (!available) - return null; - - try { - const region = Qt.createQmlObject(` - import Quickshell - Region {} - `, targetWindow, "BlurRegion"); - targetWindow.BackgroundEffect.blurRegion = region; - return region; - } catch (e) { - console.warn("BlurService: Failed to create blur region:", e); - return null; - } - } - - function reapplyBlurRegion(targetWindow, region) { - if (!region || !available) - return; - try { - targetWindow.BackgroundEffect.blurRegion = region; - region.changed(); - } catch (e) {} - } - - function destroyBlurRegion(targetWindow, region) { - if (!region) - return; - try { - targetWindow.BackgroundEffect.blurRegion = null; - } catch (e) {} - region.destroy(); - } - - Process { - id: blurProbe - running: false - command: ["dms", "blur", "check"] - - stdout: StdioCollector { - onStreamFinished: { - root.compositorSupported = text.trim() === "supported"; - if (root.compositorSupported) - console.info("BlurService: Compositor supports ext-background-effect-v1"); - else - console.info("BlurService: Compositor does not support ext-background-effect-v1"); - } - } - - onExited: exitCode => { - if (exitCode !== 0) - console.warn("BlurService: blur probe failed with code:", exitCode); - } - } - - Component.onCompleted: { - try { - const test = Qt.createQmlObject(` - import Quickshell - Region { radius: 0 } - `, root, "BlurAvailabilityTest"); - test.destroy(); - quickshellSupported = true; - console.info("BlurService: Quickshell blur support available"); - blurProbe.running = true; - } catch (e) { - console.info("BlurService: BackgroundEffect not available - blur disabled. Requires a newer version of Quickshell."); - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CalendarService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CalendarService.qml deleted file mode 100644 index 1146f7b..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CalendarService.qml +++ /dev/null @@ -1,317 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property bool khalAvailable: false - property var eventsByDate: ({}) - property bool isLoading: false - property string lastError: "" - property date lastStartDate - property date lastEndDate - property string khalDateFormat: "MM/dd/yyyy" - - function checkKhalAvailability() { - if (!khalCheckProcess.running) - khalCheckProcess.running = true - } - - function detectKhalDateFormat() { - if (!khalFormatProcess.running) - khalFormatProcess.running = true - } - - function parseKhalDateFormat(formatExample) { - let qtFormat = formatExample.replace("12", "MM").replace("21", "dd").replace("2013", "yyyy") - return { format: qtFormat, parser: null } - } - - - function loadCurrentMonth() { - if (!root.khalAvailable) - return - - let today = new Date() - let firstDay = new Date(today.getFullYear(), today.getMonth(), 1) - let lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0) - // Add padding - let startDate = new Date(firstDay) - startDate.setDate(startDate.getDate() - firstDay.getDay() - 7) - let endDate = new Date(lastDay) - endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()) + 7) - loadEvents(startDate, endDate) - } - - function loadEvents(startDate, endDate) { - if (!root.khalAvailable) { - return - } - if (eventsProcess.running) { - return - } - // Store last requested date range for refresh timer - root.lastStartDate = startDate - root.lastEndDate = endDate - root.isLoading = true - // Format dates for khal using detected format - let startDateStr = Qt.formatDate(startDate, root.khalDateFormat) - let endDateStr = Qt.formatDate(endDate, root.khalDateFormat) - eventsProcess.requestStartDate = startDate - eventsProcess.requestEndDate = endDate - eventsProcess.command = ["khal", "list", "--json", "title", "--json", "description", "--json", "start-date", "--json", "start-time", "--json", "end-date", "--json", "end-time", "--json", "all-day", "--json", "location", "--json", "url", startDateStr, endDateStr] - eventsProcess.running = true - } - - function getEventsForDate(date) { - let dateKey = Qt.formatDate(date, "yyyy-MM-dd") - return root.eventsByDate[dateKey] || [] - } - - function hasEventsForDate(date) { - let events = getEventsForDate(date) - return events.length > 0 - } - - // Initialize on component completion - Component.onCompleted: { - detectKhalDateFormat() - } - - // Process for detecting khal date format - Process { - id: khalFormatProcess - - command: ["khal", "printformats"] - running: false - onExited: exitCode => { - if (exitCode !== 0) { - checkKhalAvailability() - } - } - - stdout: StdioCollector { - onStreamFinished: { - let lines = text.split('\n') - for (let line of lines) { - if (line.startsWith('dateformat:')) { - let formatExample = line.substring(line.indexOf(':') + 1).trim() - let formatInfo = parseKhalDateFormat(formatExample) - root.khalDateFormat = formatInfo.format - break - } - } - checkKhalAvailability() - } - } - } - - // Process for checking khal configuration - Process { - id: khalCheckProcess - - command: ["khal", "list", "today"] - running: false - onExited: exitCode => { - root.khalAvailable = (exitCode === 0) - if (exitCode === 0) { - loadCurrentMonth() - } - } - } - - // Process for loading events - Process { - id: eventsProcess - - property date requestStartDate - property date requestEndDate - property string rawOutput: "" - - running: false - onExited: exitCode => { - root.isLoading = false - if (exitCode !== 0) { - root.lastError = "Failed to load events (exit code: " + exitCode + ")" - return - } - try { - let newEventsByDate = {} - let lines = eventsProcess.rawOutput.split('\n') - for (let line of lines) { - line = line.trim() - if (!line || line === "[]") - continue - - // Parse JSON line - let dayEvents = JSON.parse(line) - // Process each event in this day's array - for (let event of dayEvents) { - if (!event.title) - continue - - // Parse start and end dates using detected format - let startDate, endDate - if (event['start-date']) { - startDate = Date.fromLocaleString(I18n.locale(), event['start-date'], root.khalDateFormat) - } else { - startDate = new Date() - } - if (event['end-date']) { - endDate = Date.fromLocaleString(I18n.locale(), event['end-date'], root.khalDateFormat) - } else { - endDate = new Date(startDate) - } - // Create start/end times - let startTime = new Date(startDate) - let endTime = new Date(endDate) - if (event['start-time'] - && event['all-day'] !== "True") { - // Parse time if available and not all-day - let timeStr = event['start-time'] - if (timeStr) { - // Match time with optional seconds and AM/PM - let timeParts = timeStr.match(/(\d+):(\d+)(?::\d+)?\s*(AM|PM)?/i) - if (timeParts) { - let hours = parseInt(timeParts[1]) - let minutes = parseInt(timeParts[2]) - - // Handle AM/PM conversion if present - if (timeParts[3]) { - let period = timeParts[3].toUpperCase() - if (period === 'PM' && hours !== 12) { - hours += 12 - } else if (period === 'AM' && hours === 12) { - hours = 0 - } - } - - startTime.setHours(hours, minutes) - if (event['end-time']) { - let endTimeParts = event['end-time'].match( - /(\d+):(\d+)(?::\d+)?\s*(AM|PM)?/i) - if (endTimeParts) { - let endHours = parseInt(endTimeParts[1]) - let endMinutes = parseInt(endTimeParts[2]) - - // Handle AM/PM conversion if present - if (endTimeParts[3]) { - let endPeriod = endTimeParts[3].toUpperCase() - if (endPeriod === 'PM' && endHours !== 12) { - endHours += 12 - } else if (endPeriod === 'AM' && endHours === 12) { - endHours = 0 - } - } - - endTime.setHours(endHours, endMinutes) - } - } else { - // Default to 1 hour duration on same day - endTime = new Date(startTime) - endTime.setHours( - startTime.getHours() + 1) - } - } - } - } - // Create unique ID for this event (to track multi-day events) - let eventId = event.title + "_" + event['start-date'] - + "_" + (event['start-time'] || 'allday') - // Create event object template - let extractedUrl = "" - if (!event.url && event.description) { - let urlMatch = event.description.match(/https?:\/\/[^\s]+/) - if (urlMatch) { - extractedUrl = urlMatch[0] - } - } - let eventTemplate = { - "id": eventId, - "title": event.title || "Untitled Event", - "start": startTime, - "end": endTime, - "location": event.location || "", - "description": event.description || "", - "url": event.url || extractedUrl, - "calendar": "", - "color": "", - "allDay": event['all-day'] === "True", - "isMultiDay": startDate.toDateString( - ) !== endDate.toDateString() - } - // Add event to each day it spans - let currentDate = new Date(startDate) - while (currentDate <= endDate) { - let dateKey = Qt.formatDate(currentDate, - "yyyy-MM-dd") - if (!newEventsByDate[dateKey]) - newEventsByDate[dateKey] = [] - - // Check if this exact event is already added to this date (prevent duplicates) - let existingEvent = newEventsByDate[dateKey].find( - e => { - return e.id === eventId - }) - if (existingEvent) { - // Move to next day without adding duplicate - currentDate.setDate(currentDate.getDate() + 1) - continue - } - // Create a copy of the event for this date - let dayEvent = Object.assign({}, eventTemplate) - // For multi-day events, adjust the display time for this specific day - if (currentDate.getTime() === startDate.getTime()) { - // First day - use original start time - dayEvent.start = new Date(startTime) - } else { - // Subsequent days - start at beginning of day for all-day events - dayEvent.start = new Date(currentDate) - if (!dayEvent.allDay) - dayEvent.start.setHours(0, 0, 0, 0) - } - if (currentDate.getTime() === endDate.getTime()) { - // Last day - use original end time - dayEvent.end = new Date(endTime) - } else { - // Earlier days - end at end of day for all-day events - dayEvent.end = new Date(currentDate) - if (!dayEvent.allDay) - dayEvent.end.setHours(23, 59, 59, 999) - } - newEventsByDate[dateKey].push(dayEvent) - // Move to next day - currentDate.setDate(currentDate.getDate() + 1) - } - } - } - // Sort events by start time within each date - for (let dateKey in newEventsByDate) { - newEventsByDate[dateKey].sort((a, b) => { - return a.start.getTime( - ) - b.start.getTime() - }) - } - root.eventsByDate = newEventsByDate - root.lastError = "" - } catch (error) { - root.lastError = "Failed to parse events JSON: " + error.toString() - root.eventsByDate = {} - } - // Reset for next run - eventsProcess.rawOutput = "" - } - - stdout: SplitParser { - splitMarker: "\n" - onRead: data => { - eventsProcess.rawOutput += data + "\n" - } - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CavaService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CavaService.qml deleted file mode 100644 index d3e5717..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CavaService.qml +++ /dev/null @@ -1,76 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io - -Singleton { - id: root - - property list<int> values: Array(6) - property int refCount: 0 - property bool cavaAvailable: false - - Process { - id: cavaCheck - - command: ["which", "cava"] - running: false - onExited: exitCode => { - root.cavaAvailable = exitCode === 0 && Quickshell.env("DMS_DISABLE_CAVA") !== "1"; - } - } - - Component.onCompleted: { - cavaCheck.running = true; - } - - Process { - id: cavaProcess - - running: root.cavaAvailable && root.refCount > 0 - command: ["sh", "-c", `cat <<'CAVACONF' | cava -p /dev/stdin -[general] -framerate=25 -bars=6 -autosens=0 -sensitivity=30 -lower_cutoff_freq=50 -higher_cutoff_freq=12000 - -[output] -method=raw -raw_target=/dev/stdout -data_format=ascii -channels=mono -mono_option=average - -[smoothing] -noise_reduction=35 -integral=90 -gravity=95 -ignore=2 -monstercat=1.5 -CAVACONF`] - - onRunningChanged: { - if (!running) { - root.values = Array(6).fill(0); - } - } - - stdout: SplitParser { - splitMarker: "\n" - onRead: data => { - if (root.refCount > 0 && data.length > 0) { - const parts = data.split(";"); - if (parts.length >= 6) { - const points = [parseInt(parts[0], 10), parseInt(parts[1], 10), parseInt(parts[2], 10), parseInt(parts[3], 10), parseInt(parts[4], 10), parseInt(parts[5], 10)]; - root.values = points; - } - } - } - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ChangelogService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ChangelogService.qml deleted file mode 100644 index 562a513..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ChangelogService.qml +++ /dev/null @@ -1,108 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtCore -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - readonly property string currentVersion: "1.4" - readonly property bool changelogEnabled: false - - readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/DankMaterialShell" - readonly property string changelogMarkerPath: configDir + "/.changelog-" + currentVersion - - property bool checkComplete: false - property bool changelogDismissed: false - - readonly property bool shouldShowChangelog: { - if (!checkComplete) - return false; - if (!changelogEnabled) - return false; - if (changelogDismissed) - return false; - if (typeof FirstLaunchService !== "undefined" && FirstLaunchService.isFirstLaunch) - return false; - return true; - } - - signal changelogRequested - signal changelogCompleted - - Component.onCompleted: { - if (!changelogEnabled) - return; - if (FirstLaunchService.checkComplete) - handleFirstLaunchResult(); - } - - function handleFirstLaunchResult() { - if (FirstLaunchService.isFirstLaunch) { - checkComplete = true; - changelogDismissed = true; - touchMarkerProcess.running = true; - } else { - changelogCheckProcess.running = true; - } - } - - Connections { - target: FirstLaunchService - - function onCheckCompleteChanged() { - if (FirstLaunchService.checkComplete && root.changelogEnabled && !root.checkComplete) - root.handleFirstLaunchResult(); - } - } - - function showChangelog() { - changelogRequested(); - } - - function dismissChangelog() { - changelogDismissed = true; - touchMarkerProcess.running = true; - changelogCompleted(); - } - - Process { - id: changelogCheckProcess - - command: ["sh", "-c", "[ -f '" + changelogMarkerPath + "' ] && echo 'seen' || echo 'show'"] - running: false - - stdout: SplitParser { - onRead: data => { - const result = data.trim(); - root.checkComplete = true; - - switch (result) { - case "seen": - root.changelogDismissed = true; - break; - case "show": - root.changelogRequested(); - break; - } - } - } - } - - Process { - id: touchMarkerProcess - - command: ["sh", "-c", "mkdir -p '" + configDir + "' && touch '" + changelogMarkerPath + "'"] - running: false - - onExited: exitCode => { - if (exitCode !== 0) { - console.warn("ChangelogService: Failed to create changelog marker"); - } - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ClipboardService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ClipboardService.qml deleted file mode 100644 index 4c09255..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ClipboardService.qml +++ /dev/null @@ -1,275 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - readonly property int longTextThreshold: 200 - - readonly property bool clipboardAvailable: DMSService.isConnected && (DMSService.capabilities.length === 0 || DMSService.capabilities.includes("clipboard")) - readonly property bool wtypeAvailable: SessionService.wtypeAvailable - - property var internalEntries: [] - property var clipboardEntries: [] - property var unpinnedEntries: [] - property var pinnedEntries: [] - property int pinnedCount: 0 - property int totalCount: 0 - property string searchText: "" - property int selectedIndex: 0 - property bool keyboardNavigationActive: false - property int refCount: 0 - - signal historyCopied - signal historyCleared - - Process { - id: wtypeProcess - command: ["wtype", "-M", "ctrl", "-P", "v", "-p", "v", "-m", "ctrl"] - running: false - } - - Timer { - id: pasteTimer - interval: 200 - repeat: false - onTriggered: wtypeProcess.running = true - } - - function updateFilteredModel() { - const query = searchText.trim(); - let filtered = []; - - if (query.length === 0) { - filtered = internalEntries; - } else { - const lowerQuery = query.toLowerCase(); - filtered = internalEntries.filter(entry => entry.preview.toLowerCase().includes(lowerQuery)); - } - - filtered.sort((a, b) => { - if (a.pinned !== b.pinned) - return b.pinned ? 1 : -1; - return b.id - a.id; - }); - - clipboardEntries = filtered; - unpinnedEntries = filtered.filter(e => !e.pinned); - totalCount = clipboardEntries.length; - - if (unpinnedEntries.length === 0) { - keyboardNavigationActive = false; - selectedIndex = 0; - return; - } - if (selectedIndex >= unpinnedEntries.length) { - selectedIndex = unpinnedEntries.length - 1; - } - } - - function refresh() { - if (!clipboardAvailable) { - return; - } - DMSService.sendRequest("clipboard.getHistory", null, function (response) { - if (response.error) { - console.warn("ClipboardService: Failed to get history:", response.error); - return; - } - internalEntries = response.result || []; - pinnedEntries = internalEntries.filter(e => e.pinned); - pinnedCount = pinnedEntries.length; - updateFilteredModel(); - }); - } - - function reset() { - searchText = ""; - selectedIndex = 0; - keyboardNavigationActive = false; - internalEntries = []; - clipboardEntries = []; - unpinnedEntries = []; - } - - function copyEntry(entry, closeCallback) { - DMSService.sendRequest("clipboard.copyEntry", { - "id": entry.id - }, function (response) { - if (response.error) { - ToastService.showError(I18n.tr("Failed to copy entry")); - return; - } - ToastService.showInfo(entry.isImage ? I18n.tr("Image copied to clipboard") : I18n.tr("Copied to clipboard")); - historyCopied(); - if (closeCallback) { - closeCallback(); - } - }); - } - - function pasteEntry(entry, closeCallback) { - if (!wtypeAvailable) { - ToastService.showError(I18n.tr("wtype not available - install wtype for paste support")); - return; - } - DMSService.sendRequest("clipboard.copyEntry", { - "id": entry.id - }, function (response) { - if (response.error) { - ToastService.showError(I18n.tr("Failed to copy entry")); - return; - } - if (closeCallback) { - closeCallback(); - } - pasteTimer.start(); - }); - } - - function pasteSelected(closeCallback) { - if (!keyboardNavigationActive || clipboardEntries.length === 0 || selectedIndex < 0 || selectedIndex >= clipboardEntries.length) { - return; - } - pasteEntry(clipboardEntries[selectedIndex], closeCallback); - } - - function deleteEntry(entry) { - DMSService.sendRequest("clipboard.deleteEntry", { - "id": entry.id - }, function (response) { - if (response.error) { - console.warn("ClipboardService: Failed to delete entry:", response.error); - return; - } - internalEntries = internalEntries.filter(e => e.id !== entry.id); - updateFilteredModel(); - if (clipboardEntries.length === 0) { - keyboardNavigationActive = false; - selectedIndex = 0; - return; - } - if (selectedIndex >= clipboardEntries.length) { - selectedIndex = clipboardEntries.length - 1; - } - }); - } - - function deletePinnedEntry(entry, confirmDialog) { - if (!confirmDialog) { - return; - } - confirmDialog.show(I18n.tr("Delete Saved Item?"), I18n.tr("This will permanently remove this saved clipboard item. This action cannot be undone."), function () { - DMSService.sendRequest("clipboard.deleteEntry", { - "id": entry.id - }, function (response) { - if (response.error) { - console.warn("ClipboardService: Failed to delete entry:", response.error); - return; - } - internalEntries = internalEntries.filter(e => e.id !== entry.id); - updateFilteredModel(); - ToastService.showInfo(I18n.tr("Saved item deleted")); - }); - }, function () {}); - } - - function pinEntry(entry) { - DMSService.sendRequest("clipboard.getPinnedCount", null, function (countResponse) { - if (countResponse.error) { - ToastService.showError(I18n.tr("Failed to check pin limit")); - return; - } - - const maxPinned = 25; - if (countResponse.result.count >= maxPinned) { - ToastService.showError(I18n.tr("Maximum pinned entries reached") + " (" + maxPinned + ")"); - return; - } - - DMSService.sendRequest("clipboard.pinEntry", { - "id": entry.id - }, function (response) { - if (response.error) { - ToastService.showError(I18n.tr("Failed to pin entry")); - return; - } - ToastService.showInfo(I18n.tr("Entry pinned")); - refresh(); - }); - }); - } - - function unpinEntry(entry) { - DMSService.sendRequest("clipboard.unpinEntry", { - "id": entry.id - }, function (response) { - if (response.error) { - ToastService.showError(I18n.tr("Failed to unpin entry")); - return; - } - ToastService.showInfo(I18n.tr("Entry unpinned")); - refresh(); - }); - } - - function clearAll() { - const hasPinned = pinnedCount > 0; - const savedCount = pinnedCount; - DMSService.sendRequest("clipboard.clearHistory", null, function (response) { - if (response.error) { - console.warn("ClipboardService: Failed to clear history:", response.error); - return; - } - refresh(); - historyCleared(); - if (hasPinned) { - ToastService.showInfo(I18n.tr("History cleared. %1 pinned entries kept.").arg(savedCount)); - } - }); - } - - function getEntryPreview(entry) { - return entry.preview || ""; - } - - function getEntryType(entry) { - if (entry.isImage) { - return "image"; - } - if (entry.size > longTextThreshold) { - return "long_text"; - } - return "text"; - } - - function hashedPinnedEntry(entryHash) { - if (!entryHash) { - return false; - } - return pinnedEntries.some(pinnedEntry => pinnedEntry.hash === entryHash); - } - - onClipboardAvailableChanged: { - if (!clipboardAvailable || refCount <= 0) - return; - refresh(); - } - - Connections { - target: DMSService - enabled: root.refCount > 0 - function onClipboardStateUpdate(data) { - const newHistory = data.history || []; - internalEntries = newHistory; - pinnedEntries = newHistory.filter(e => e.pinned); - pinnedCount = pinnedEntries.length; - updateFilteredModel(); - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CompositorService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CompositorService.qml deleted file mode 100644 index d327ea2..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CompositorService.qml +++ /dev/null @@ -1,677 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.I3 -import Quickshell.Wayland -import Quickshell.Hyprland -import qs.Common - -Singleton { - id: root - - property bool isHyprland: false - property bool isNiri: false - property bool isDwl: false - property bool isSway: false - property bool isScroll: false - property bool isMiracle: false - property bool isLabwc: false - property string compositor: "unknown" - readonly property bool useHyprlandFocusGrab: isHyprland && Quickshell.env("DMS_HYPRLAND_EXCLUSIVE_FOCUS") !== "1" - - readonly property string hyprlandSignature: Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE") - readonly property string niriSocket: Quickshell.env("NIRI_SOCKET") - readonly property string swaySocket: Quickshell.env("SWAYSOCK") - readonly property string scrollSocket: Quickshell.env("SWAYSOCK") - readonly property string miracleSocket: Quickshell.env("MIRACLESOCK") - readonly property string labwcPid: Quickshell.env("LABWC_PID") - property bool useNiriSorting: isNiri && NiriService - - property var randrScales: ({}) - property bool randrReady: false - signal randrDataReady - - property var sortedToplevels: [] - property bool _sortScheduled: false - - signal toplevelsChanged - - function fetchRandrData() { - Proc.runCommand("randr", ["dms", "randr", "--json"], (output, exitCode) => { - if (exitCode === 0 && output) { - try { - const data = JSON.parse(output.trim()); - if (data.outputs && Array.isArray(data.outputs)) { - const scales = {}; - for (const out of data.outputs) { - if (out.name && out.scale > 0) - scales[out.name] = out.scale; - } - randrScales = scales; - } - } catch (e) { - console.warn("CompositorService: failed to parse randr data:", e); - } - } - randrReady = true; - randrDataReady(); - }, 0, 3000); - } - - function getScreenScale(screen) { - if (!screen) - return 1; - - if (Quickshell.env("QT_WAYLAND_FORCE_DPI") || Quickshell.env("QT_SCALE_FACTOR")) { - return screen.devicePixelRatio || 1; - } - - const randrScale = randrScales[screen.name]; - if (randrScale !== undefined && randrScale > 0) - return Math.round(randrScale * 20) / 20; - - if (WlrOutputService.wlrOutputAvailable && screen) { - const wlrOutput = WlrOutputService.getOutput(screen.name); - if (wlrOutput?.enabled && wlrOutput.scale !== undefined && wlrOutput.scale > 0) { - return Math.round(wlrOutput.scale * 20) / 20; - } - } - - if (isNiri && screen) { - const niriScale = NiriService.displayScales[screen.name]; - if (niriScale !== undefined) - return niriScale; - } - - if (isHyprland && screen) { - const hyprlandMonitor = Hyprland.monitors.values.find(m => m.name === screen.name); - if (hyprlandMonitor?.scale !== undefined) - return hyprlandMonitor.scale; - } - - if (isDwl && screen) { - const dwlScale = DwlService.getOutputScale(screen.name); - if (dwlScale !== undefined && dwlScale > 0) - return dwlScale; - } - - return screen?.devicePixelRatio || 1; - } - - function getFocusedScreen() { - let screenName = ""; - if (isHyprland && Hyprland.focusedWorkspace?.monitor) - screenName = Hyprland.focusedWorkspace.monitor.name; - else if (isNiri && NiriService.currentOutput) - screenName = NiriService.currentOutput; - else if (isSway || isScroll || isMiracle) { - const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true); - screenName = focusedWs?.monitor?.name || ""; - } else if (isDwl && DwlService.activeOutput) - screenName = DwlService.activeOutput; - - if (!screenName) - return Quickshell.screens.length > 0 ? Quickshell.screens[0] : null; - - for (let i = 0; i < Quickshell.screens.length; i++) { - if (Quickshell.screens[i].name === screenName) - return Quickshell.screens[i]; - } - return Quickshell.screens.length > 0 ? Quickshell.screens[0] : null; - } - - Timer { - id: sortDebounceTimer - interval: 100 - repeat: false - onTriggered: { - _sortScheduled = false; - sortedToplevels = computeSortedToplevels(); - toplevelsChanged(); - } - } - - function scheduleSort() { - if (_sortScheduled) - return; - _sortScheduled = true; - sortDebounceTimer.restart(); - } - - Connections { - target: ToplevelManager.toplevels - function onValuesChanged() { - root.scheduleSort(); - } - } - Connections { - target: isHyprland ? Hyprland : null - enabled: isHyprland - - function onRawEvent(event) { - if (event.name === "openwindow" || event.name === "closewindow" || event.name === "movewindow" || event.name === "movewindowv2" || event.name === "workspace" || event.name === "workspacev2" || event.name === "focusedmon" || event.name === "focusedmonv2" || event.name === "activewindow" || event.name === "activewindowv2" || event.name === "changefloatingmode" || event.name === "fullscreen" || event.name === "moveintogroup" || event.name === "moveoutofgroup") { - try { - Hyprland.refreshToplevels(); - } catch (e) {} - root.scheduleSort(); - } - } - } - Connections { - target: NiriService - function onWindowsChanged() { - root.scheduleSort(); - } - } - - Component.onCompleted: { - fetchRandrData(); - detectCompositor(); - scheduleSort(); - Qt.callLater(() => { - NiriService.generateNiriLayoutConfig(); - HyprlandService.generateLayoutConfig(); - DwlService.generateLayoutConfig(); - }); - } - - Connections { - target: DwlService - function onStateChanged() { - if (isDwl && !isHyprland && !isNiri) { - scheduleSort(); - } - } - } - - function computeSortedToplevels() { - if (!ToplevelManager.toplevels || !ToplevelManager.toplevels.values) - return []; - - if (useNiriSorting) - return NiriService.sortToplevels(ToplevelManager.toplevels.values); - - if (isHyprland) - return sortHyprlandToplevelsSafe(); - - return Array.from(ToplevelManager.toplevels.values); - } - - function _get(o, path, fallback) { - try { - let v = o; - for (let i = 0; i < path.length; i++) { - if (v === null || v === undefined) - return fallback; - v = v[path[i]]; - } - return (v === undefined || v === null) ? fallback : v; - } catch (e) { - return fallback; - } - } - - function sortHyprlandToplevelsSafe() { - if (!Hyprland.toplevels || !Hyprland.toplevels.values) - return []; - - const items = Array.from(Hyprland.toplevels.values); - - function _get(o, path, fb) { - try { - let v = o; - for (let k of path) { - if (v == null) - return fb; - v = v[k]; - } - return (v == null) ? fb : v; - } catch (e) { - return fb; - } - } - - let snap = []; - for (let i = 0; i < items.length; i++) { - const t = items[i]; - if (!t) - continue; - const addr = t.address || ""; - if (!addr) - continue; - const li = t.lastIpcObject || null; - - const monName = _get(li, ["monitor"], null) ?? _get(t, ["monitor", "name"], ""); - const monX = _get(t, ["monitor", "x"], Number.MAX_SAFE_INTEGER); - const monY = _get(t, ["monitor", "y"], Number.MAX_SAFE_INTEGER); - - const wsId = _get(li, ["workspace", "id"], null) ?? _get(t, ["workspace", "id"], Number.MAX_SAFE_INTEGER); - - const at = _get(li, ["at"], null); - let atX = (at !== null && at !== undefined && typeof at[0] === "number") ? at[0] : 1e9; - let atY = (at !== null && at !== undefined && typeof at[1] === "number") ? at[1] : 1e9; - - const relX = Number.isFinite(monX) ? (atX - monX) : atX; - const relY = Number.isFinite(monY) ? (atY - monY) : atY; - - snap.push({ - monKey: String(monName), - monOrderX: Number.isFinite(monX) ? monX : Number.MAX_SAFE_INTEGER, - monOrderY: Number.isFinite(monY) ? monY : Number.MAX_SAFE_INTEGER, - wsId: (typeof wsId === "number") ? wsId : Number.MAX_SAFE_INTEGER, - x: relX, - y: relY, - title: t.title || "", - address: addr, - wayland: t.wayland - }); - } - - const groups = new Map(); - for (const it of snap) { - const key = it.monKey + "::" + it.wsId; - if (!groups.has(key)) - groups.set(key, []); - groups.get(key).push(it); - } - - let groupList = []; - for (const [key, arr] of groups) { - const repr = arr[0]; - groupList.push({ - key, - monKey: repr.monKey, - monOrderX: repr.monOrderX, - monOrderY: repr.monOrderY, - wsId: repr.wsId, - items: arr - }); - } - - groupList.sort((a, b) => { - if (a.monOrderX !== b.monOrderX) - return a.monOrderX - b.monOrderX; - if (a.monOrderY !== b.monOrderY) - return a.monOrderY - b.monOrderY; - if (a.monKey !== b.monKey) - return a.monKey.localeCompare(b.monKey); - if (a.wsId !== b.wsId) - return a.wsId - b.wsId; - return 0; - }); - - const COLUMN_THRESHOLD = 48; - const JITTER_Y = 6; - - let ordered = []; - for (const g of groupList) { - const arr = g.items; - - const xs = arr.map(it => it.x).filter(x => Number.isFinite(x)).sort((a, b) => a - b); - let colCenters = []; - if (xs.length > 0) { - for (const x of xs) { - if (colCenters.length === 0) { - colCenters.push(x); - } else { - const last = colCenters[colCenters.length - 1]; - if (x - last >= COLUMN_THRESHOLD) { - colCenters.push(x); - } - } - } - } else { - colCenters = [0]; - } - - for (const it of arr) { - let bestCol = 0; - let bestDist = Number.POSITIVE_INFINITY; - for (let ci = 0; ci < colCenters.length; ci++) { - const d = Math.abs(it.x - colCenters[ci]); - if (d < bestDist) { - bestDist = d; - bestCol = ci; - } - } - it._col = bestCol; - } - - arr.sort((a, b) => { - if (a._col !== b._col) - return a._col - b._col; - - const dy = a.y - b.y; - if (Math.abs(dy) > JITTER_Y) - return dy; - - if (a.title !== b.title) - return a.title.localeCompare(b.title); - if (a.address !== b.address) - return a.address.localeCompare(b.address); - return 0; - }); - - ordered.push.apply(ordered, arr); - } - - return ordered.map(x => x.wayland).filter(w => w !== null && w !== undefined); - } - - function filterCurrentWorkspace(toplevels, screen) { - if (useNiriSorting) - return NiriService.filterCurrentWorkspace(toplevels, screen); - if (isHyprland) - return filterHyprlandCurrentWorkspaceSafe(toplevels, screen); - return toplevels; - } - - function filterCurrentDisplay(toplevels, screenName) { - if (!toplevels || toplevels.length === 0 || !screenName) - return toplevels; - if (useNiriSorting) - return NiriService.filterCurrentDisplay(toplevels, screenName); - if (isHyprland) - return filterHyprlandCurrentDisplaySafe(toplevels, screenName); - return toplevels; - } - - function filterHyprlandCurrentDisplaySafe(toplevels, screenName) { - if (!toplevels || toplevels.length === 0 || !Hyprland.toplevels) - return toplevels; - - let monitorWindows = new Set(); - try { - const hy = Array.from(Hyprland.toplevels.values); - for (const t of hy) { - const mon = _get(t, ["monitor", "name"], ""); - if (mon === screenName && t.wayland) - monitorWindows.add(t.wayland); - } - } catch (e) {} - - return toplevels.filter(w => monitorWindows.has(w)); - } - - function filterHyprlandCurrentWorkspaceSafe(toplevels, screenName) { - if (!toplevels || toplevels.length === 0 || !Hyprland.toplevels) - return toplevels; - - let currentWorkspaceId = null; - try { - if (Hyprland.monitors) { - const monitor = Hyprland.monitors.values.find(m => m.name === screenName); - if (monitor) - currentWorkspaceId = _get(monitor, ["activeWorkspace", "id"], null); - } - - if (currentWorkspaceId === null) { - const hy = Array.from(Hyprland.toplevels.values); - for (const t of hy) { - const mon = _get(t, ["monitor", "name"], ""); - const wsId = _get(t, ["workspace", "id"], null); - const active = !!_get(t, ["activated"], false); - if (mon === screenName && wsId !== null) { - if (active) { - currentWorkspaceId = wsId; - break; - } - if (currentWorkspaceId === null) - currentWorkspaceId = wsId; - } - } - } - - if (currentWorkspaceId === null && Hyprland.workspaces) { - const wss = Array.from(Hyprland.workspaces.values); - const focusedId = _get(Hyprland, ["focusedWorkspace", "id"], null); - for (const ws of wss) { - const monName = _get(ws, ["monitor", "name"], ""); - const wsId = _get(ws, ["id"], null); - if (monName === screenName && wsId !== null) { - if (focusedId !== null && wsId === focusedId) { - currentWorkspaceId = wsId; - break; - } - if (currentWorkspaceId === null) - currentWorkspaceId = wsId; - } - } - } - } catch (e) { - console.warn("CompositorService: workspace snapshot failed:", e); - } - - if (currentWorkspaceId === null) - return toplevels; - - let map = new Map(); - try { - const hy = Array.from(Hyprland.toplevels.values); - for (const t of hy) { - const wsId = _get(t, ["workspace", "id"], null); - if (t && t.wayland && wsId !== null) - map.set(t.wayland, wsId); - } - } catch (e) {} - - return toplevels.filter(w => map.get(w) === currentWorkspaceId); - } - - Timer { - id: compositorInitTimer - interval: 100 - running: true - repeat: false - onTriggered: { - detectCompositor(); - Qt.callLater(() => { - NiriService.generateNiriLayoutConfig(); - HyprlandService.generateLayoutConfig(); - DwlService.generateLayoutConfig(); - }); - } - } - - function detectCompositor() { - if (hyprlandSignature && hyprlandSignature.length > 0 && !niriSocket && !swaySocket && !scrollSocket && !miracleSocket && !labwcPid) { - isHyprland = true; - isNiri = false; - isDwl = false; - isSway = false; - isScroll = false; - isMiracle = false; - isLabwc = false; - compositor = "hyprland"; - console.info("CompositorService: Detected Hyprland"); - return; - } - - if (niriSocket && niriSocket.length > 0) { - Proc.runCommand("niriSocketCheck", ["test", "-S", niriSocket], (output, exitCode) => { - if (exitCode === 0) { - isNiri = true; - isHyprland = false; - isDwl = false; - isSway = false; - isScroll = false; - isMiracle = false; - isLabwc = false; - compositor = "niri"; - console.info("CompositorService: Detected Niri with socket:", niriSocket); - NiriService.generateNiriBlurrule(); - } - }, 0); - return; - } - - if (swaySocket && swaySocket.length > 0 && !scrollSocket && scrollSocket.length == 0 && !miracleSocket) { - Proc.runCommand("swaySocketCheck", ["test", "-S", swaySocket], (output, exitCode) => { - if (exitCode === 0) { - isNiri = false; - isHyprland = false; - isDwl = false; - isSway = true; - isScroll = false; - isMiracle = false; - isLabwc = false; - compositor = "sway"; - console.info("CompositorService: Detected Sway with socket:", swaySocket); - } - }, 0); - return; - } - - if (miracleSocket && miracleSocket.length > 0) { - Proc.runCommand("miracleSocketCheck", ["test", "-S", miracleSocket], (output, exitCode) => { - if (exitCode === 0) { - isNiri = false; - isHyprland = false; - isDwl = false; - isSway = false; - isScroll = false; - isMiracle = true; - isLabwc = false; - compositor = "miracle"; - console.info("CompositorService: Detected Miracle WM with socket:", miracleSocket); - } - }, 0); - return; - } - - if (scrollSocket && scrollSocket.length > 0 && !miracleSocket) { - Proc.runCommand("scrollSocketCheck", ["test", "-S", scrollSocket], (output, exitCode) => { - if (exitCode === 0) { - isNiri = false; - isHyprland = false; - isDwl = false; - isSway = false; - isScroll = true; - isMiracle = false; - isLabwc = false; - compositor = "scroll"; - console.info("CompositorService: Detected Scroll with socket:", scrollSocket); - } - }, 0); - return; - } - - if (labwcPid && labwcPid.length > 0) { - isHyprland = false; - isNiri = false; - isDwl = false; - isSway = false; - isScroll = false; - isMiracle = false; - isLabwc = true; - compositor = "labwc"; - console.info("CompositorService: Detected LabWC with PID:", labwcPid); - return; - } - - if (DMSService.dmsAvailable) { - Qt.callLater(checkForDwl); - } else { - isHyprland = false; - isNiri = false; - isDwl = false; - isSway = false; - isScroll = false; - isMiracle = false; - isLabwc = false; - compositor = "unknown"; - console.warn("CompositorService: No compositor detected"); - } - } - - Connections { - target: DMSService - function onCapabilitiesReceived() { - if (!isHyprland && !isNiri && !isDwl && !isLabwc) { - checkForDwl(); - } - } - } - - function checkForDwl() { - if (DMSService.apiVersion >= 12 && DMSService.capabilities.includes("dwl")) { - isHyprland = false; - isNiri = false; - isDwl = true; - isSway = false; - isScroll = false; - isMiracle = false; - isLabwc = false; - compositor = "dwl"; - console.info("CompositorService: Detected DWL via DMS capability"); - } - } - - function powerOffMonitors() { - if (isNiri) - return NiriService.powerOffMonitors(); - if (isHyprland) - return Hyprland.dispatch("dpms off"); - if (isDwl) - return _dwlPowerOffMonitors(); - if (isSway || isScroll || isMiracle) { - try { - I3.dispatch("output * dpms off"); - } catch (_) {} - return; - } - if (isLabwc) { - Quickshell.execDetached(["dms", "dpms", "off"]); - } - console.warn("CompositorService: Cannot power off monitors, unknown compositor"); - } - - function powerOnMonitors() { - if (isNiri) - return NiriService.powerOnMonitors(); - if (isHyprland) - return Hyprland.dispatch("dpms on"); - if (isDwl) - return _dwlPowerOnMonitors(); - if (isSway || isScroll || isMiracle) { - try { - I3.dispatch("output * dpms on"); - } catch (_) {} - return; - } - if (isLabwc) { - Quickshell.execDetached(["dms", "dpms", "on"]); - } - console.warn("CompositorService: Cannot power on monitors, unknown compositor"); - } - - function _dwlPowerOffMonitors() { - if (!Quickshell.screens || Quickshell.screens.length === 0) { - console.warn("CompositorService: No screens available for DWL power off"); - return; - } - - for (let i = 0; i < Quickshell.screens.length; i++) { - const screen = Quickshell.screens[i]; - if (screen && screen.name) { - Quickshell.execDetached(["mmsg", "-d", "disable_monitor," + screen.name]); - } - } - } - - function _dwlPowerOnMonitors() { - if (!Quickshell.screens || Quickshell.screens.length === 0) { - console.warn("CompositorService: No screens available for DWL power on"); - return; - } - - for (let i = 0; i < Quickshell.screens.length; i++) { - const screen = Quickshell.screens[i]; - if (screen && screen.name) { - Quickshell.execDetached(["mmsg", "-d", "enable_monitor," + screen.name]); - } - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CupsService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CupsService.qml deleted file mode 100644 index a88655d..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/CupsService.qml +++ /dev/null @@ -1,860 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import qs.Common - -Singleton { - id: root - - property int refCount: 0 - - onRefCountChanged: { - if (refCount > 0) { - ensureSubscription(); - } else if (refCount === 0 && DMSService.activeSubscriptions.includes("cups")) { - DMSService.removeSubscription("cups"); - } - } - - function ensureSubscription() { - if (refCount <= 0) - return; - if (!DMSService.isConnected) - return; - if (DMSService.activeSubscriptions.includes("cups")) - return; - if (DMSService.activeSubscriptions.includes("all")) - return; - DMSService.addSubscription("cups"); - if (cupsAvailable) { - getState(); - } - } - - property var printerNames: [] - property var printers: [] - property string selectedPrinter: "" - property string expandedPrinter: "" - - property bool cupsAvailable: false - property bool stateInitialized: false - - property var devices: [] - property var ppds: [] - property var printerClasses: [] - - readonly property var filteredDevices: { - if (!devices || devices.length === 0) - return []; - const bareProtocols = ["ipp", "ipps", "http", "https", "lpd", "socket", "beh", "dnssd", "mdns", "smb", "file", "cups-brf"]; - - // First pass: filter out invalid/bare protocol entries - const validDevices = devices.filter(d => { - if (!d.uri) - return false; - const uriLower = d.uri.toLowerCase(); - for (let proto of bareProtocols) { - if (uriLower === proto || uriLower === proto + ":") - return false; - } - if (d.class === "network" && d.info === "Backend Error Handler") - return false; - return true; - }); - - // Second pass: prefer IPP over LPD for the same printer - // _printer._tcp (LPD) doesn't work well with driverless printing - // _ipp._tcp or _ipps._tcp (IPP) should be preferred - const ippDeviceHosts = new Set(); - for (const d of validDevices) { - if (!d.uri) - continue; - // Extract hostname from dnssd URIs like dnssd://Name%20[mac]._ipp._tcp.local - const ippMatch = d.uri.match(/dnssd:\/\/[^/]*\._ipps?\._tcp/); - if (ippMatch) { - // Extract the unique identifier (usually MAC address in brackets) - const macMatch = d.uri.match(/\[([a-f0-9]+)\]/i); - if (macMatch) - ippDeviceHosts.add(macMatch[1].toLowerCase()); - } - } - - // Filter out _printer._tcp devices when we have _ipp._tcp for the same printer - return validDevices.filter(d => { - if (!d.uri) - return true; - // If this is an LPD device, check if we have an IPP alternative - if (d.uri.includes("._printer._tcp")) { - const macMatch = d.uri.match(/\[([a-f0-9]+)\]/i); - if (macMatch && ippDeviceHosts.has(macMatch[1].toLowerCase())) { - return false; // Skip LPD device, we have IPP - } - } - return true; - }); - } - - function decodeUri(str) { - if (!str) - return ""; - try { - return decodeURIComponent(str.replace(/\+/g, " ")); - } catch (e) { - return str; - } - } - - function getDeviceDisplayName(device) { - if (!device) - return ""; - let name = ""; - if (device.info && device.info.length > 0) { - name = decodeUri(device.info); - } else if (device.makeModel && device.makeModel.length > 0) { - name = decodeUri(device.makeModel); - } else { - return decodeUri(device.uri); - } - if (device.ip) - return name + " (" + device.ip + ")"; - return name; - } - - function getDeviceSubtitle(device) { - if (!device) - return ""; - const parts = []; - switch (device.class) { - case "direct": - parts.push(I18n.tr("Local")); - break; - case "network": - parts.push(I18n.tr("Network")); - break; - case "file": - parts.push(I18n.tr("File")); - break; - default: - if (device.class) - parts.push(device.class); - } - if (device.location) - parts.push(decodeUri(device.location)); - return parts.join(" • "); - } - - function suggestPrinterName(device) { - if (!device) - return ""; - let name = device.info || device.makeModel || ""; - name = name.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, ""); - return name.substring(0, 32) || "Printer"; - } - - function getMatchingPPDs(device) { - if (!device || !ppds || ppds.length === 0) - return []; - const isDnssd = device.uri && (device.uri.startsWith("dnssd://") || device.uri.startsWith("ipp://") || device.uri.startsWith("ipps://")); - if (isDnssd) { - const driverless = ppds.filter(p => p.name === "driverless" || p.name === "everywhere" || (p.makeModel && p.makeModel.toLowerCase().includes("driverless"))); - if (driverless.length > 0) - return driverless; - } - if (!device.makeModel) - return []; - const makeModelLower = device.makeModel.toLowerCase(); - const words = makeModelLower.split(/[\s_-]+/).filter(w => w.length > 2); - return ppds.filter(p => { - if (!p.makeModel) - return false; - const ppdLower = p.makeModel.toLowerCase(); - return words.some(w => ppdLower.includes(w)); - }).slice(0, 10); - } - - property bool loadingDevices: false - property bool loadingPPDs: false - property bool loadingClasses: false - property bool creatingPrinter: false - - signal cupsStateUpdate - - readonly property string socketPath: Quickshell.env("DMS_SOCKET") - - Component.onCompleted: { - if (socketPath && socketPath.length > 0) { - checkDMSCapabilities(); - } - } - - Connections { - target: DMSService - - function onConnectionStateChanged() { - if (DMSService.isConnected) { - checkDMSCapabilities(); - ensureSubscription(); - } - } - } - - Connections { - target: DMSService - enabled: DMSService.isConnected - - function onCupsStateUpdate(data) { - console.log("CupsService: Subscription update received"); - getState(); - } - - function onCapabilitiesChanged() { - checkDMSCapabilities(); - } - } - - function checkDMSCapabilities() { - if (!DMSService.isConnected) - return; - if (DMSService.capabilities.length === 0) - return; - cupsAvailable = DMSService.capabilities.includes("cups"); - - if (cupsAvailable && !stateInitialized) { - stateInitialized = true; - getState(); - } - } - - function getState() { - if (!cupsAvailable) - return; - DMSService.sendRequest("cups.getPrinters", null, response => { - if (response.result) { - updatePrinters(response.result); - fetchAllJobs(); - } - }); - } - - function updatePrinters(printersData) { - printerNames = printersData.map(p => p.name); - - let printersObj = {}; - for (var i = 0; i < printersData.length; i++) { - let printer = printersData[i]; - printersObj[printer.name] = { - "name": printer.name, - "uri": printer.uri || "", - "state": printer.state, - "stateReason": printer.stateReason, - "location": printer.location || "", - "info": printer.info || "", - "makeModel": printer.makeModel || "", - "accepting": printer.accepting !== false, - "jobs": [] - }; - } - printers = printersObj; - - if (printerNames.length > 0) { - if (selectedPrinter.length > 0) { - if (!printerNames.includes(selectedPrinter)) { - selectedPrinter = printerNames[0]; - } - } else { - selectedPrinter = printerNames[0]; - } - } - } - - function fetchAllJobs() { - for (var i = 0; i < printerNames.length; i++) { - fetchJobsForPrinter(printerNames[i]); - } - } - - function fetchJobsForPrinter(printerName) { - const params = { - "printerName": printerName - }; - - DMSService.sendRequest("cups.getJobs", params, response => { - if (response.result && printers[printerName]) { - let updatedPrinters = Object.assign({}, printers); - updatedPrinters[printerName].jobs = response.result; - printers = updatedPrinters; - } - }); - } - - function getSelectedPrinter() { - return selectedPrinter; - } - - function setSelectedPrinter(printerName) { - if (printerNames.length > 0) { - if (printerNames.includes(printerName)) { - selectedPrinter = printerName; - } else { - selectedPrinter = printerNames[0]; - } - } - } - - function getPrintersNum() { - if (!cupsAvailable) - return 0; - - return printerNames.length; - } - - function getPrintersNames() { - if (!cupsAvailable) - return []; - - return printerNames; - } - - function getTotalJobsNum() { - if (!cupsAvailable) - return 0; - - var result = 0; - for (var i = 0; i < printerNames.length; i++) { - var printerName = printerNames[i]; - if (printers[printerName] && printers[printerName].jobs) { - result += printers[printerName].jobs.length; - } - } - return result; - } - - function getCurrentPrinterState() { - if (!cupsAvailable || !selectedPrinter) - return ""; - - var printer = printers[selectedPrinter]; - return printer.state; - } - - function getCurrentPrinterStatePrettyShort() { - if (!cupsAvailable || !selectedPrinter) - return ""; - - var printer = printers[selectedPrinter]; - return getPrinterStateTranslation(printer.state) + " (" + getPrinterStateReasonTranslation(printer.stateReason) + ")"; - } - - function getCurrentPrinterStatePretty() { - if (!cupsAvailable || !selectedPrinter) - return ""; - - var printer = printers[selectedPrinter]; - return getPrinterStateTranslation(printer.state) + " (" + I18n.tr("Reason") + ": " + getPrinterStateReasonTranslation(printer.stateReason) + ")"; - } - - function getCurrentPrinterJobs() { - if (!cupsAvailable || !selectedPrinter) - return []; - - return getJobs(selectedPrinter); - } - - function getJobs(printerName) { - if (!cupsAvailable) - return ""; - - var printer = printers[printerName]; - return printer.jobs; - } - - function getJobsNum(printerName) { - if (!cupsAvailable) - return 0; - - var printer = printers[printerName]; - return printer.jobs.length; - } - - function pausePrinter(printerName) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName - }; - - DMSService.sendRequest("cups.pausePrinter", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to pause printer"), response.error); - } else { - getState(); - } - }); - } - - function resumePrinter(printerName) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName - }; - - DMSService.sendRequest("cups.resumePrinter", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to resume printer"), response.error); - } else { - getState(); - } - }); - } - - function cancelJob(printerName, jobID) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName, - "jobID": jobID - }; - - DMSService.sendRequest("cups.cancelJob", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to cancel selected job"), response.error); - } else { - fetchJobsForPrinter(printerName); - } - }); - } - - function purgeJobs(printerName) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName - }; - - DMSService.sendRequest("cups.purgeJobs", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to cancel all jobs"), response.error); - } else { - fetchJobsForPrinter(printerName); - } - }); - } - - function getDevices() { - if (!cupsAvailable) - return; - loadingDevices = true; - DMSService.sendRequest("cups.getDevices", null, response => { - loadingDevices = false; - if (response.result) { - devices = response.result; - } - }); - } - - function getPPDs() { - if (!cupsAvailable) - return; - loadingPPDs = true; - DMSService.sendRequest("cups.getPPDs", null, response => { - loadingPPDs = false; - if (response.result) { - ppds = response.result; - } - }); - } - - function getClasses() { - if (!cupsAvailable) - return; - loadingClasses = true; - DMSService.sendRequest("cups.getClasses", null, response => { - loadingClasses = false; - if (response.result) { - printerClasses = response.result; - } - }); - } - - function testConnection(host, port, protocol, callback) { - if (!cupsAvailable) - return; - const params = { - "host": host, - "port": port, - "protocol": protocol - }; - - DMSService.sendRequest("cups.testConnection", params, response => { - if (callback) - callback(response); - }); - } - - function createPrinter(name, deviceURI, ppd, options) { - if (!cupsAvailable) - return; - creatingPrinter = true; - const params = { - "name": name, - "deviceURI": deviceURI, - "ppd": ppd - }; - if (options) { - if (options.shared !== undefined) - params.shared = options.shared; - if (options.location) - params.location = options.location; - if (options.information) - params.information = options.information; - if (options.errorPolicy) - params.errorPolicy = options.errorPolicy; - } - - DMSService.sendRequest("cups.createPrinter", params, response => { - creatingPrinter = false; - if (response.error) { - ToastService.showError(I18n.tr("Failed to create printer"), response.error); - } else { - ToastService.showInfo(I18n.tr("Printer created successfully")); - getState(); - } - }); - } - - function deletePrinter(printerName) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName - }; - - DMSService.sendRequest("cups.deletePrinter", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to delete printer"), response.error); - } else { - ToastService.showInfo(I18n.tr("Printer deleted")); - if (selectedPrinter === printerName) { - selectedPrinter = ""; - } - getState(); - } - }); - } - - function acceptJobs(printerName) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName - }; - - DMSService.sendRequest("cups.acceptJobs", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to enable job acceptance"), response.error); - } else { - getState(); - } - }); - } - - function rejectJobs(printerName) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName - }; - - DMSService.sendRequest("cups.rejectJobs", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to disable job acceptance"), response.error); - } else { - getState(); - } - }); - } - - function setPrinterShared(printerName, shared) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName, - "shared": shared - }; - - DMSService.sendRequest("cups.setPrinterShared", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to update sharing"), response.error); - } else { - getState(); - } - }); - } - - function setPrinterLocation(printerName, location) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName, - "location": location - }; - - DMSService.sendRequest("cups.setPrinterLocation", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to update location"), response.error); - } else { - getState(); - } - }); - } - - function setPrinterInfo(printerName, info) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName, - "info": info - }; - - DMSService.sendRequest("cups.setPrinterInfo", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to update description"), response.error); - } else { - getState(); - } - }); - } - - function printTestPage(printerName) { - if (!cupsAvailable) - return; - const params = { - "printerName": printerName - }; - - DMSService.sendRequest("cups.printTestPage", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to print test page"), response.error); - } else { - ToastService.showInfo(I18n.tr("Test page sent to printer")); - fetchJobsForPrinter(printerName); - } - }); - } - - function moveJob(jobID, destPrinter) { - if (!cupsAvailable) - return; - const params = { - "jobID": jobID, - "destPrinter": destPrinter - }; - - DMSService.sendRequest("cups.moveJob", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to move job"), response.error); - } else { - fetchAllJobs(); - } - }); - } - - function restartJob(jobID) { - if (!cupsAvailable) - return; - const params = { - "jobID": jobID - }; - - DMSService.sendRequest("cups.restartJob", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to restart job"), response.error); - } else { - fetchAllJobs(); - } - }); - } - - function holdJob(jobID, holdUntil) { - if (!cupsAvailable) - return; - const params = { - "jobID": jobID - }; - if (holdUntil) { - params.holdUntil = holdUntil; - } - - DMSService.sendRequest("cups.holdJob", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to hold job"), response.error); - } else { - fetchAllJobs(); - } - }); - } - - function addPrinterToClass(className, printerName) { - if (!cupsAvailable) - return; - const params = { - "className": className, - "printerName": printerName - }; - - DMSService.sendRequest("cups.addPrinterToClass", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to add printer to class"), response.error); - } else { - getClasses(); - } - }); - } - - function removePrinterFromClass(className, printerName) { - if (!cupsAvailable) - return; - const params = { - "className": className, - "printerName": printerName - }; - - DMSService.sendRequest("cups.removePrinterFromClass", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to remove printer from class"), response.error); - } else { - getClasses(); - } - }); - } - - function deleteClass(className) { - if (!cupsAvailable) - return; - const params = { - "className": className - }; - - DMSService.sendRequest("cups.deleteClass", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to delete class"), response.error); - } else { - getClasses(); - } - }); - } - - function getPrinterData(printerName) { - if (!printers || !printers[printerName]) - return null; - return printers[printerName]; - } - - function getJobStateTranslation(state) { - switch (state) { - case "pending": - return I18n.tr("Pending"); - case "pending-held": - return I18n.tr("Held"); - case "processing": - return I18n.tr("Processing"); - case "processing-stopped": - return I18n.tr("Stopped"); - case "canceled": - return I18n.tr("Canceled"); - case "aborted": - return I18n.tr("Aborted"); - case "completed": - return I18n.tr("Completed"); - default: - return state; - } - } - - readonly property var states: ({ - "idle": I18n.tr("Idle"), - "processing": I18n.tr("Processing"), - "stopped": I18n.tr("Stopped") - }) - - readonly property var reasonsGeneral: ({ - "none": I18n.tr("None"), - "other": I18n.tr("Other") - }) - - readonly property var reasonsSupplies: ({ - "toner-low": I18n.tr("Toner Low"), - "toner-empty": I18n.tr("Toner Empty"), - "marker-supply-low": I18n.tr("Marker Supply Low"), - "marker-supply-empty": I18n.tr("Marker Supply Empty"), - "marker-waste-almost-full": I18n.tr("Marker Waste Almost Full"), - "marker-waste-full": I18n.tr("Marker Waste Full") - }) - - readonly property var reasonsMedia: ({ - "media-low": I18n.tr("Media Low"), - "media-empty": I18n.tr("Media Empty"), - "media-needed": I18n.tr("Media Needed"), - "media-jam": I18n.tr("Media Jam") - }) - - readonly property var reasonsParts: ({ - "cover-open": I18n.tr("Cover Open"), - "door-open": I18n.tr("Door Open"), - "interlock-open": I18n.tr("Interlock Open"), - "output-tray-missing": I18n.tr("Output Tray Missing"), - "output-area-almost-full": I18n.tr("Output Area Almost Full"), - "output-area-full": I18n.tr("Output Area Full") - }) - - readonly property var reasonsErrors: ({ - "paused": I18n.tr("Paused"), - "shutdown": I18n.tr("Shutdown"), - "connecting-to-device": I18n.tr("Connecting to Device"), - "timed-out": I18n.tr("Timed Out"), - "stopping": I18n.tr("Stopping"), - "stopped-partly": I18n.tr("Stopped Partly") - }) - - readonly property var reasonsService: ({ - "spool-area-full": I18n.tr("Spool Area Full"), - "cups-missing-filter-warning": I18n.tr("CUPS Missing Filter Warning"), - "cups-insecure-filter-warning": I18n.tr("CUPS Insecure Filter Warning") - }) - - readonly property var reasonsConnectivity: ({ - "offline-report": I18n.tr("Offline Report"), - "moving-to-paused": I18n.tr("Moving to Paused") - }) - - readonly property var severitySuffixes: ({ - "-error": I18n.tr("Error"), - "-warning": I18n.tr("Warning"), - "-report": I18n.tr("Report") - }) - - function getPrinterStateTranslation(state) { - return states[state] || state; - } - - function getPrinterStateReasonTranslation(reason) { - let allReasons = Object.assign({}, reasonsGeneral, reasonsSupplies, reasonsMedia, reasonsParts, reasonsErrors, reasonsService, reasonsConnectivity); - - let basReason = reason; - let suffix = ""; - - for (let s in severitySuffixes) { - if (reason.endsWith(s)) { - basReason = reason.slice(0, -s.length); - suffix = severitySuffixes[s]; - break; - } - } - - let translation = allReasons[basReason] || basReason; - return suffix ? translation + " (" + suffix + ")" : translation; - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DMSNetworkService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DMSNetworkService.qml deleted file mode 100644 index e234cf4..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DMSNetworkService.qml +++ /dev/null @@ -1,944 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import qs.Common - -Singleton { - id: root - - property bool networkAvailable: false - property string backend: "" - - property string networkStatus: "disconnected" - property string primaryConnection: "" - - property string ethernetIP: "" - property string ethernetInterface: "" - property bool ethernetConnected: false - property string ethernetConnectionUuid: "" - property var ethernetDevices: [] - - property var wiredConnections: [] - - property string wifiIP: "" - property string wifiInterface: "" - property bool wifiConnected: false - property bool wifiEnabled: true - property string wifiConnectionUuid: "" - property string wifiDevicePath: "" - property string activeAccessPointPath: "" - property var wifiDevices: [] - property string wifiDeviceOverride: SessionData.wifiDeviceOverride || "" - property string connectingDevice: "" - - property string currentWifiSSID: "" - property int wifiSignalStrength: 0 - property var wifiNetworks: [] - property var savedConnections: [] - property var ssidToConnectionName: ({}) - property var wifiSignalIcon: { - if (!wifiConnected) { - return "wifi_off"; - } - if (wifiSignalStrength >= 50) { - return "wifi"; - } - if (wifiSignalStrength >= 25) { - return "wifi_2_bar"; - } - return "wifi_1_bar"; - } - - property string userPreference: "auto" - property bool isConnecting: false - property string connectingSSID: "" - property string connectionError: "" - - property bool isScanning: false - property bool autoScan: false - - property bool wifiAvailable: true - property bool wifiToggling: false - property bool changingPreference: false - property string targetPreference: "" - property var savedWifiNetworks: [] - property string connectionStatus: "" - property string lastConnectionError: "" - property bool passwordDialogShouldReopen: false - property bool autoRefreshEnabled: false - property string wifiPassword: "" - property string forgetSSID: "" - - property var vpnProfiles: [] - property var vpnActive: [] - property bool vpnAvailable: false - property bool vpnIsBusy: false - property string lastConnectedVpnUuid: "" - property string pendingVpnUuid: "" - property var vpnBusyStartTime: 0 - - property alias profiles: root.vpnProfiles - property alias activeConnections: root.vpnActive - property var activeUuids: vpnActive.map(v => v.uuid).filter(u => !!u) - property var activeNames: vpnActive.map(v => v.name).filter(n => !!n) - property string activeUuid: activeUuids.length > 0 ? activeUuids[0] : "" - property string activeName: activeNames.length > 0 ? activeNames[0] : "" - property string activeDevice: vpnActive.length > 0 ? (vpnActive[0].device || "") : "" - property string activeState: vpnActive.length > 0 ? (vpnActive[0].state || "") : "" - property bool vpnConnected: activeUuids.length > 0 - property alias available: root.vpnAvailable - property alias isBusy: root.vpnIsBusy - property alias connected: root.vpnConnected - - property string networkInfoSSID: "" - property string networkInfoDetails: "" - property bool networkInfoLoading: false - - property string networkWiredInfoUUID: "" - property string networkWiredInfoDetails: "" - property bool networkWiredInfoLoading: false - - property int refCount: 0 - property bool stateInitialized: false - - property string credentialsToken: "" - property string credentialsSSID: "" - property string credentialsSetting: "" - property var credentialsFields: [] - property var credentialsHints: [] - property string credentialsReason: "" - property bool credentialsRequested: false - - property string pendingConnectionSSID: "" - property var pendingConnectionStartTime: 0 - property bool wasConnecting: false - - signal networksUpdated - signal connectionChanged - signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason, string connType, string connName, string vpnService, var fieldsInfo) - - readonly property string socketPath: Quickshell.env("DMS_SOCKET") - - readonly property string effectiveWifiDevice: { - if (!wifiDeviceOverride) - return ""; - const deviceExists = wifiDevices.some(d => d.name === wifiDeviceOverride); - return deviceExists ? wifiDeviceOverride : ""; - } - - Component.onCompleted: { - root.userPreference = SettingsData.networkPreference; - lastConnectedVpnUuid = SessionData.vpnLastConnected || ""; - if (socketPath && socketPath.length > 0) { - checkDMSCapabilities(); - } - } - - Connections { - target: DMSService - - function onNetworkStateUpdate(data) { - const networksCount = data.wifiNetworks?.length ?? "null"; - console.log("DMSNetworkService: Subscription update received, networks:", networksCount); - updateState(data); - } - } - - Connections { - target: DMSService - - function onConnectionStateChanged() { - if (DMSService.isConnected) { - checkDMSCapabilities(); - } - } - } - - Connections { - target: DMSService - enabled: DMSService.isConnected - - function onCapabilitiesChanged() { - checkDMSCapabilities(); - } - - function onCredentialsRequest(data) { - handleCredentialsRequest(data); - } - } - - function checkDMSCapabilities() { - if (!DMSService.isConnected) { - return; - } - - if (DMSService.capabilities.length === 0) { - return; - } - - networkAvailable = DMSService.capabilities.includes("network"); - - if (networkAvailable && !stateInitialized) { - stateInitialized = true; - getState(); - } - } - - function handleCredentialsRequest(data) { - credentialsToken = data.token || ""; - credentialsSSID = data.ssid || ""; - credentialsSetting = data.setting || "802-11-wireless-security"; - credentialsFields = data.fields || ["psk"]; - credentialsHints = data.hints || []; - credentialsReason = data.reason || "Credentials required"; - credentialsRequested = true; - - const connType = data.connType || ""; - const connName = data.name || data.connectionId || ""; - const vpnService = data.vpnService || ""; - const fInfo = data.fieldsInfo || []; - - credentialsNeeded(credentialsToken, credentialsSSID, credentialsSetting, credentialsFields, credentialsHints, credentialsReason, connType, connName, vpnService, fInfo); - } - - function addRef() { - refCount++; - if (refCount === 1 && networkAvailable) { - startAutoScan(); - } - } - - function removeRef() { - refCount = Math.max(0, refCount - 1); - if (refCount === 0) { - stopAutoScan(); - } - } - - property bool initialStateFetched: false - - function getState() { - if (!networkAvailable) - return; - DMSService.sendRequest("network.getState", null, response => { - if (response.result) { - updateState(response.result); - if (!initialStateFetched && response.result.wifiEnabled && (!response.result.wifiNetworks || response.result.wifiNetworks.length === 0)) { - initialStateFetched = true; - Qt.callLater(() => scanWifi()); - } - } - }); - } - - function updateState(state) { - const previousConnecting = isConnecting; - const previousConnectingSSID = connectingSSID; - - backend = state.backend || ""; - vpnAvailable = networkAvailable && backend === "networkmanager"; - networkStatus = state.networkStatus || "disconnected"; - primaryConnection = state.primaryConnection || ""; - - ethernetIP = state.ethernetIP || ""; - ethernetInterface = state.ethernetDevice || ""; - ethernetConnected = state.ethernetConnected || false; - ethernetConnectionUuid = state.ethernetConnectionUuid || ""; - ethernetDevices = state.ethernetDevices || []; - - wiredConnections = state.wiredConnections || []; - - wifiIP = state.wifiIP || ""; - wifiInterface = state.wifiDevice || ""; - wifiConnected = state.wifiConnected || false; - wifiEnabled = state.wifiEnabled !== undefined ? state.wifiEnabled : true; - wifiConnectionUuid = state.wifiConnectionUuid || ""; - wifiDevicePath = state.wifiDevicePath || ""; - activeAccessPointPath = state.activeAccessPointPath || ""; - wifiDevices = state.wifiDevices || []; - connectingDevice = state.connectingDevice || ""; - - currentWifiSSID = state.wifiSSID || ""; - wifiSignalStrength = state.wifiSignal || 0; - - if (state.wifiNetworks) { - wifiNetworks = state.wifiNetworks; - - const saved = []; - const mapping = {}; - for (const network of state.wifiNetworks) { - if (network.saved) { - saved.push({ - ssid: network.ssid, - saved: true - }); - mapping[network.ssid] = network.ssid; - } - } - savedConnections = saved; - savedWifiNetworks = saved; - ssidToConnectionName = mapping; - - networksUpdated(); - } - - if (state.vpnProfiles) { - vpnProfiles = state.vpnProfiles; - } - - const previousVpnActive = vpnActive; - vpnActive = state.vpnActive || []; - - if (vpnConnected && activeUuid) { - lastConnectedVpnUuid = activeUuid; - SessionData.setVpnLastConnected(activeUuid); - } - - if (vpnIsBusy) { - const busyDuration = Date.now() - vpnBusyStartTime; - const timeout = 30000; - - if (busyDuration > timeout) { - console.warn("DMSNetworkService: VPN operation timed out after", timeout, "ms"); - vpnIsBusy = false; - pendingVpnUuid = ""; - vpnBusyStartTime = 0; - } else if (pendingVpnUuid) { - const isPendingVpnActive = activeUuids.includes(pendingVpnUuid); - if (isPendingVpnActive) { - vpnIsBusy = false; - pendingVpnUuid = ""; - vpnBusyStartTime = 0; - } - } else { - const previousCount = previousVpnActive ? previousVpnActive.length : 0; - const currentCount = vpnActive ? vpnActive.length : 0; - - if (previousCount !== currentCount) { - vpnIsBusy = false; - vpnBusyStartTime = 0; - } - } - } - - isConnecting = state.isConnecting || false; - connectingSSID = state.connectingSSID || ""; - connectionError = state.lastError || ""; - lastConnectionError = state.lastError || ""; - - if (pendingConnectionSSID) { - if (wifiConnected && currentWifiSSID === pendingConnectionSSID && wifiIP) { - const elapsed = Date.now() - pendingConnectionStartTime; - console.info("DMSNetworkService: Successfully connected to", pendingConnectionSSID, "in", elapsed, "ms"); - ToastService.showInfo(`Connected to ${pendingConnectionSSID}`); - - if (userPreference === "wifi" || userPreference === "auto") { - setConnectionPriority("wifi"); - } - - pendingConnectionSSID = ""; - connectionStatus = "connected"; - } else if (previousConnecting && !isConnecting && !wifiConnected) { - const isCancellationError = connectionError === "user-canceled"; - const isBadCredentials = connectionError === "bad-credentials"; - - if (isCancellationError) { - connectionStatus = "cancelled"; - pendingConnectionSSID = ""; - } else if (isBadCredentials) { - connectionStatus = "invalid_password"; - pendingConnectionSSID = ""; - } else { - if (connectionError) { - ToastService.showError(I18n.tr("Failed to connect to %1").arg(pendingConnectionSSID)); - } - connectionStatus = "failed"; - pendingConnectionSSID = ""; - } - } - } - - wasConnecting = isConnecting; - - connectionChanged(); - } - - function connectToSpecificWiredConfig(uuid) { - if (!networkAvailable || isConnecting) - return; - isConnecting = true; - connectionError = ""; - connectionStatus = "connecting"; - - const params = { - uuid: uuid - }; - - DMSService.sendRequest("network.ethernet.connect.config", params, response => { - if (response.error) { - connectionError = response.error; - lastConnectionError = response.error; - connectionStatus = "failed"; - ToastService.showError(I18n.tr("Failed to activate configuration"), response.error); - } else { - connectionError = ""; - connectionStatus = "connected"; - ToastService.showInfo(I18n.tr("Configuration activated")); - } - - isConnecting = false; - }); - } - - function scanWifi() { - if (!networkAvailable || isScanning || !wifiEnabled) - return; - isScanning = true; - const params = effectiveWifiDevice ? { - device: effectiveWifiDevice - } : null; - DMSService.sendRequest("network.wifi.scan", params, response => { - isScanning = false; - if (response.error) { - console.warn("DMSNetworkService: WiFi scan failed:", response.error); - } else { - Qt.callLater(() => getState()); - } - }); - } - - function scanWifiNetworks() { - scanWifi(); - } - - function connectToWifi(ssid, password = "", username = "", anonymousIdentity = "", domainSuffixMatch = "", hidden = false) { - if (!networkAvailable || isConnecting) - return; - pendingConnectionSSID = ssid; - pendingConnectionStartTime = Date.now(); - connectionError = ""; - connectionStatus = "connecting"; - credentialsRequested = false; - - const params = { - ssid: ssid - }; - if (effectiveWifiDevice) - params.device = effectiveWifiDevice; - if (hidden) - params.hidden = true; - - if (DMSService.apiVersion >= 7) { - if (password || username) { - params.password = password; - if (username) - params.username = username; - if (anonymousIdentity) - params.anonymousIdentity = anonymousIdentity; - if (domainSuffixMatch) - params.domainSuffixMatch = domainSuffixMatch; - params.interactive = false; - } else { - params.interactive = true; - } - } else { - if (password) - params.password = password; - if (username) - params.username = username; - if (anonymousIdentity) - params.anonymousIdentity = anonymousIdentity; - if (domainSuffixMatch) - params.domainSuffixMatch = domainSuffixMatch; - } - - DMSService.sendRequest("network.wifi.connect", params, response => { - if (response.error) { - if (connectionStatus === "cancelled") - return; - connectionError = response.error; - lastConnectionError = response.error; - pendingConnectionSSID = ""; - connectionStatus = "failed"; - ToastService.showError(I18n.tr("Failed to start connection to %1").arg(ssid)); - } - }); - } - - function disconnectWifi() { - if (!networkAvailable || !wifiInterface) - return; - const params = effectiveWifiDevice ? { - device: effectiveWifiDevice - } : null; - DMSService.sendRequest("network.wifi.disconnect", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to disconnect WiFi"), response.error); - } else { - ToastService.showInfo(I18n.tr("Disconnected from WiFi")); - currentWifiSSID = ""; - connectionStatus = ""; - } - }); - } - - function submitCredentials(token, secrets, save) { - console.log("submitCredentials: networkAvailable=" + networkAvailable + " apiVersion=" + DMSService.apiVersion); - - if (!networkAvailable || DMSService.apiVersion < 7) { - console.warn("submitCredentials: Aborting - networkAvailable=" + networkAvailable + " apiVersion=" + DMSService.apiVersion); - return; - } - - const params = { - token: token, - secrets: secrets, - save: save || false - }; - - credentialsRequested = false; - - DMSService.sendRequest("network.credentials.submit", params, response => { - if (response.error) { - console.warn("DMSNetworkService: Failed to submit credentials:", response.error); - } - }); - } - - function cancelCredentials(token) { - if (!networkAvailable || DMSService.apiVersion < 7) - return; - const params = { - token: token - }; - - credentialsRequested = false; - pendingConnectionSSID = ""; - connectionStatus = "cancelled"; - - DMSService.sendRequest("network.credentials.cancel", params, response => { - if (response.error) { - console.warn("DMSNetworkService: Failed to cancel credentials:", response.error); - } - }); - } - - function forgetWifiNetwork(ssid) { - if (!networkAvailable) - return; - forgetSSID = ssid; - DMSService.sendRequest("network.wifi.forget", { - ssid: ssid - }, response => { - if (response.error) { - console.warn("Failed to forget network:", response.error); - } else { - ToastService.showInfo(I18n.tr("Forgot network %1").arg(ssid)); - - savedConnections = savedConnections.filter(s => s.ssid !== ssid); - savedWifiNetworks = savedWifiNetworks.filter(s => s.ssid !== ssid); - - const updated = [...wifiNetworks]; - for (const network of updated) { - if (network.ssid === ssid) { - network.saved = false; - if (network.connected) { - network.connected = false; - currentWifiSSID = ""; - } - } - } - wifiNetworks = updated; - networksUpdated(); - } - forgetSSID = ""; - }); - } - - function toggleWifiRadio() { - if (!networkAvailable || wifiToggling) - return; - wifiToggling = true; - DMSService.sendRequest("network.wifi.toggle", null, response => { - wifiToggling = false; - - if (response.error) { - console.warn("Failed to toggle WiFi:", response.error); - } else if (response.result) { - wifiEnabled = response.result.enabled; - ToastService.showInfo(wifiEnabled ? I18n.tr("WiFi enabled") : I18n.tr("WiFi disabled")); - } - }); - } - - function enableWifiDevice() { - if (!networkAvailable) - return; - DMSService.sendRequest("network.wifi.enable", null, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to enable WiFi"), response.error); - } else { - ToastService.showInfo(I18n.tr("WiFi enabled")); - } - }); - } - - function setNetworkPreference(preference) { - if (!networkAvailable) - return; - userPreference = preference; - changingPreference = true; - targetPreference = preference; - SettingsData.set("networkPreference", preference); - - DMSService.sendRequest("network.preference.set", { - preference: preference - }, response => { - changingPreference = false; - targetPreference = ""; - - if (response.error) { - console.warn("Failed to set network preference:", response.error); - } - }); - } - - function setConnectionPriority(type) { - if (type === "wifi") { - setNetworkPreference("wifi"); - } else if (type === "ethernet") { - setNetworkPreference("ethernet"); - } - } - - function connectToWifiAndSetPreference(ssid, password, username = "", anonymousIdentity = "", domainSuffixMatch = "", hidden = false) { - connectToWifi(ssid, password, username, anonymousIdentity, domainSuffixMatch, hidden); - setNetworkPreference("wifi"); - } - - function toggleNetworkConnection(type) { - if (!networkAvailable) - return; - if (type === "ethernet") { - if (ethernetConnected) { - DMSService.sendRequest("network.ethernet.disconnect", null, null); - } else { - DMSService.sendRequest("network.ethernet.connect", null, null); - } - } - } - - function disconnectEthernetDevice(deviceName) { - if (!networkAvailable) - return; - DMSService.sendRequest("network.ethernet.disconnect", { - device: deviceName - }, null); - } - - function startAutoScan() { - autoScan = true; - autoRefreshEnabled = true; - if (networkAvailable && wifiEnabled) { - scanWifi(); - } - } - - function stopAutoScan() { - autoScan = false; - autoRefreshEnabled = false; - } - - function fetchWiredNetworkInfo(uuid) { - if (!networkAvailable) - return; - networkWiredInfoUUID = uuid; - networkWiredInfoLoading = true; - networkWiredInfoDetails = "Loading network information..."; - - DMSService.sendRequest("network.ethernet.info", { - uuid: uuid - }, response => { - networkWiredInfoLoading = false; - - if (response.error) { - networkWiredInfoDetails = "Failed to fetch network information"; - } else if (response.result) { - formatWiredNetworkInfo(response.result); - } - }); - } - - function formatWiredNetworkInfo(info) { - let details = ""; - - if (!info) { - details = "Network information not found or network not available."; - } else { - details += "Interface: " + info.iface + "\\n"; - details += "Driver: " + info.driver + "\\n"; - details += "MAC Addr: " + info.hwAddr + "\\n"; - details += "Speed: " + info.speed + " Mb/s\\n\\n"; - - details += "IPv4 information:\\n"; - - for (const ip4 of info.IPv4s.ips) { - details += " IPv4 address: " + ip4 + "\\n"; - } - details += " Gateway: " + info.IPv4s.gateway + "\\n"; - details += " DNS: " + info.IPv4s.dns + "\\n"; - - if (info.IPv6s.ips) { - details += "\\nIPv6 information:\\n"; - - for (const ip6 of info.IPv6s.ips) { - details += " IPv6 address: " + ip6 + "\\n"; - } - if (info.IPv6s.gateway.length > 0) { - details += " Gateway: " + info.IPv6s.gateway + "\\n"; - } - if (info.IPv6s.dns.length > 0) { - details += " DNS: " + info.IPv6s.dns + "\\n"; - } - } - } - - networkWiredInfoDetails = details; - } - - function fetchNetworkInfo(ssid) { - if (!networkAvailable) - return; - networkInfoSSID = ssid; - networkInfoLoading = true; - networkInfoDetails = "Loading network information..."; - - DMSService.sendRequest("network.info", { - ssid: ssid - }, response => { - networkInfoLoading = false; - - if (response.error) { - networkInfoDetails = "Failed to fetch network information"; - } else if (response.result) { - formatNetworkInfo(response.result); - } - }); - } - - function formatNetworkInfo(info) { - let details = ""; - - if (!info || !info.bands || info.bands.length === 0) { - details = "Network information not found or network not available."; - } else { - for (const band of info.bands) { - const freqGHz = band.frequency / 1000; - let bandName = "Unknown"; - if (band.frequency >= 2400 && band.frequency <= 2500) { - bandName = "2.4 GHz"; - } else if (band.frequency >= 5000 && band.frequency <= 6000) { - bandName = "5 GHz"; - } else if (band.frequency >= 6000) { - bandName = "6 GHz"; - } - - const statusPrefix = band.connected ? "● " : " "; - const statusSuffix = band.connected ? " (Connected)" : ""; - - details += statusPrefix + bandName + statusSuffix + " - " + band.signal + "%\\n"; - details += " Channel " + band.channel + " (" + freqGHz.toFixed(1) + " GHz) • " + band.rate + " Mbit/s\\n"; - details += " BSSID: " + band.bssid + "\\n"; - details += " Mode: " + band.mode + "\\n"; - details += " Security: " + (band.secured ? "Secured" : "Open") + "\\n"; - if (band.saved) { - details += " Status: Saved network\\n"; - } - details += "\\n"; - } - } - - networkInfoDetails = details; - } - - function getNetworkInfo(ssid) { - const network = wifiNetworks.find(n => n.ssid === ssid); - if (!network) { - return null; - } - - return { - "ssid": network.ssid, - "signal": network.signal, - "secured": network.secured, - "saved": network.saved, - "connected": network.connected, - "bssid": network.bssid - }; - } - - function getWiredNetworkInfo(uuid) { - const network = wiredConnections.find(n => n.uuid === uuid); - if (!network) { - return null; - } - - return { - "uuid": uuid - }; - } - - function refreshVpnProfiles() { - if (!vpnAvailable) - return; - DMSService.sendRequest("network.vpn.profiles", null, response => { - if (response.result) { - vpnProfiles = response.result; - } - }); - } - - function refreshVpnActive() { - if (!vpnAvailable) - return; - DMSService.sendRequest("network.vpn.active", null, response => { - if (response.result) { - vpnActive = response.result; - } - }); - } - - function connectVpn(uuidOrName, singleActive = false) { - if (!vpnAvailable || vpnIsBusy) - return; - vpnIsBusy = true; - pendingVpnUuid = uuidOrName; - vpnBusyStartTime = Date.now(); - - const params = { - uuidOrName: uuidOrName, - singleActive: singleActive - }; - - DMSService.sendRequest("network.vpn.connect", params, response => { - if (response.error) { - vpnIsBusy = false; - pendingVpnUuid = ""; - vpnBusyStartTime = 0; - ToastService.showError(I18n.tr("Failed to connect VPN"), response.error); - } - }); - } - - function connect(uuidOrName, singleActive = false) { - connectVpn(uuidOrName, singleActive); - } - - function disconnectVpn(uuidOrName) { - if (!vpnAvailable || vpnIsBusy) - return; - vpnIsBusy = true; - pendingVpnUuid = ""; - vpnBusyStartTime = Date.now(); - - const params = { - uuidOrName: uuidOrName - }; - - DMSService.sendRequest("network.vpn.disconnect", params, response => { - if (response.error) { - vpnIsBusy = false; - vpnBusyStartTime = 0; - ToastService.showError(I18n.tr("Failed to disconnect VPN"), response.error); - } - }); - } - - function disconnect(uuidOrName) { - disconnectVpn(uuidOrName); - } - - function disconnectAllVpns() { - if (!vpnAvailable || vpnIsBusy) - return; - vpnIsBusy = true; - pendingVpnUuid = ""; - - DMSService.sendRequest("network.vpn.disconnectAll", null, response => { - if (response.error) { - vpnIsBusy = false; - ToastService.showError(I18n.tr("Failed to disconnect VPNs"), response.error); - } - }); - } - - function disconnectAllActive() { - disconnectAllVpns(); - } - - function toggleVpn(uuid) { - if (uuid) { - if (isActiveVpnUuid(uuid)) { - disconnectVpn(uuid); - } else { - connectVpn(uuid); - } - return; - } - - if (vpnConnected) { - disconnectAllVpns(); - return; - } - - const targetUuid = lastConnectedVpnUuid || (vpnProfiles.length > 0 ? vpnProfiles[0].uuid : ""); - if (targetUuid) { - connectVpn(targetUuid); - } - } - - function toggle(uuid) { - toggleVpn(uuid); - } - - function isActiveVpnUuid(uuid) { - return activeUuids && activeUuids.indexOf(uuid) !== -1; - } - - function isActiveUuid(uuid) { - return isActiveVpnUuid(uuid); - } - - function refreshNetworkState() { - if (networkAvailable) { - getState(); - } - } - - function setWifiDeviceOverride(deviceName) { - SessionData.setWifiDeviceOverride(deviceName || ""); - if (networkAvailable && wifiEnabled) { - scanWifi(); - } - } - - function setWifiAutoconnect(ssid, autoconnect) { - if (!networkAvailable || DMSService.apiVersion <= 13) - return; - const params = { - ssid: ssid, - autoconnect: autoconnect - }; - - DMSService.sendRequest("network.wifi.setAutoconnect", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to update autoconnect"), response.error); - } else { - ToastService.showInfo(autoconnect ? I18n.tr("Autoconnect enabled") : I18n.tr("Autoconnect disabled")); - Qt.callLater(() => getState()); - } - }); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DMSService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DMSService.qml deleted file mode 100644 index 217b96a..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DMSService.qml +++ /dev/null @@ -1,757 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property bool dmsAvailable: false - property var capabilities: [] - property int apiVersion: 0 - property string cliVersion: "" - readonly property int expectedApiVersion: 1 - property var availablePlugins: [] - property var installedPlugins: [] - property var availableThemes: [] - property var installedThemes: [] - property bool isConnected: false - property bool isConnecting: false - property bool subscribeConnected: false - readonly property bool forceExtWorkspace: false - - readonly property string socketPath: Quickshell.env("DMS_SOCKET") - - property var pendingRequests: ({}) - property var clipboardRequestIds: ({}) - property int requestIdCounter: 0 - property bool shownOutdatedError: false - property string updateCommand: "dms update" - property bool checkingUpdateCommand: false - - signal pluginsListReceived(var plugins) - signal installedPluginsReceived(var plugins) - signal searchResultsReceived(var plugins) - signal themesListReceived(var themes) - signal installedThemesReceived(var themes) - signal themeSearchResultsReceived(var themes) - signal operationSuccess(string message) - signal operationError(string error) - signal connectionStateChanged - - signal networkStateUpdate(var data) - signal cupsStateUpdate(var data) - signal loginctlStateUpdate(var data) - signal loginctlEvent(var event) - signal capabilitiesReceived - signal credentialsRequest(var data) - signal bluetoothPairingRequest(var data) - signal dwlStateUpdate(var data) - signal brightnessStateUpdate(var data) - signal brightnessDeviceUpdate(var device) - signal extWorkspaceStateUpdate(var data) - signal wlrOutputStateUpdate(var data) - signal evdevStateUpdate(var data) - signal gammaStateUpdate(var data) - signal themeAutoStateUpdate(var data) - signal openUrlRequested(string url) - signal appPickerRequested(var data) - signal screensaverStateUpdate(var data) - signal clipboardStateUpdate(var data) - signal locationStateUpdate(var data) - - property bool capsLockState: false - property bool screensaverInhibited: false - property var screensaverInhibitors: [] - - property var activeSubscriptions: ["network", "network.credentials", "loginctl", "freedesktop", "freedesktop.screensaver", "gamma", "theme.auto", "bluetooth", "bluetooth.pairing", "dwl", "brightness", "wlroutput", "evdev", "browser", "dbus", "clipboard", "location"] - - Component.onCompleted: { - if (socketPath && socketPath.length > 0) { - detectUpdateCommand(); - } - } - - function detectUpdateCommand() { - checkingUpdateCommand = true; - checkAurHelper.running = true; - } - - function startSocketConnection() { - if (socketPath && socketPath.length > 0) { - testProcess.running = true; - } - } - - Process { - id: checkAurHelper - command: ["sh", "-c", "command -v paru || command -v yay"] - running: false - - stdout: StdioCollector { - onStreamFinished: { - const helper = text.trim(); - if (helper.includes("paru")) { - checkDmsPackage.helper = "paru"; - checkDmsPackage.running = true; - } else if (helper.includes("yay")) { - checkDmsPackage.helper = "yay"; - checkDmsPackage.running = true; - } else { - updateCommand = "dms update"; - checkingUpdateCommand = false; - startSocketConnection(); - } - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - updateCommand = "dms update"; - checkingUpdateCommand = false; - startSocketConnection(); - } - } - } - - Process { - id: checkDmsPackage - property string helper: "" - command: ["sh", "-c", "pacman -Qi dms-shell-git 2>/dev/null || pacman -Qi dms-shell-bin 2>/dev/null"] - running: false - - stdout: StdioCollector { - onStreamFinished: { - if (text.includes("dms-shell-git")) { - updateCommand = checkDmsPackage.helper + " -S dms-shell-git"; - } else if (text.includes("dms-shell-bin")) { - updateCommand = checkDmsPackage.helper + " -S dms-shell-bin"; - } else { - updateCommand = "dms update"; - } - checkingUpdateCommand = false; - startSocketConnection(); - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - updateCommand = "dms update"; - checkingUpdateCommand = false; - startSocketConnection(); - } - } - } - - Process { - id: testProcess - command: ["test", "-S", root.socketPath] - - onExited: exitCode => { - if (exitCode === 0) { - root.dmsAvailable = true; - connectSocket(); - } else { - root.dmsAvailable = false; - } - } - } - - function connectSocket() { - if (!dmsAvailable || isConnected || isConnecting) { - return; - } - - isConnecting = true; - requestSocket.connected = true; - } - - DankSocket { - id: requestSocket - path: root.socketPath - connected: false - - onConnectionStateChanged: { - if (connected) { - root.isConnected = true; - root.isConnecting = false; - root.connectionStateChanged(); - subscribeSocket.connected = true; - } else { - root.isConnected = false; - root.isConnecting = false; - root.apiVersion = 0; - root.capabilities = []; - root.connectionStateChanged(); - } - } - - parser: SplitParser { - onRead: line => { - if (!line || line.length === 0) - return; - - let response; - try { - response = JSON.parse(line); - } catch (e) { - console.warn("DMSService: Failed to parse request response:", line.substring(0, 100)); - return; - } - const isClipboard = clipboardRequestIds[response.id]; - if (isClipboard) - delete clipboardRequestIds[response.id]; - else - console.log("DMSService: Request socket <<", line); - handleResponse(response); - } - } - } - - DankSocket { - id: subscribeSocket - path: root.socketPath - connected: false - - onConnectionStateChanged: { - root.subscribeConnected = connected; - if (connected) { - sendSubscribeRequest(); - } - } - - parser: SplitParser { - onRead: line => { - if (!line || line.length === 0) - return; - - let response; - try { - response = JSON.parse(line); - } catch (e) { - console.warn("DMSService: Failed to parse subscription event:", line.substring(0, 100)); - return; - } - if (!line.includes("clipboard")) - console.log("DMSService: Subscribe socket <<", line); - handleSubscriptionEvent(response); - } - } - } - - function sendSubscribeRequest() { - const request = { - "method": "subscribe" - }; - - if (activeSubscriptions.length > 0) { - request.params = { - "services": activeSubscriptions - }; - console.log("DMSService: Subscribing to services:", JSON.stringify(activeSubscriptions)); - } else { - console.log("DMSService: Subscribing to all services"); - } - - subscribeSocket.send(request); - } - - function subscribe(services) { - if (!Array.isArray(services)) { - services = [services]; - } - - activeSubscriptions = services; - - if (subscribeConnected) { - subscribeSocket.connected = false; - Qt.callLater(() => { - subscribeSocket.connected = true; - }); - } - } - - function addSubscription(service) { - if (activeSubscriptions.includes("all")) - return; - if (!activeSubscriptions.includes(service)) { - const newSubs = [...activeSubscriptions, service]; - subscribe(newSubs); - } - } - - function removeSubscription(service) { - if (activeSubscriptions.includes("all")) { - const allServices = ["network", "loginctl", "freedesktop", "gamma", "bluetooth", "dwl", "brightness", "extworkspace", "browser", "location"]; - const filtered = allServices.filter(s => s !== service); - subscribe(filtered); - } else { - const filtered = activeSubscriptions.filter(s => s !== service); - if (filtered.length === 0) { - console.warn("DMSService: Cannot remove last subscription"); - return; - } - subscribe(filtered); - } - } - - function subscribeAll() { - subscribe(["all"]); - } - - function subscribeAllExcept(excludeServices) { - if (!Array.isArray(excludeServices)) { - excludeServices = [excludeServices]; - } - - const allServices = ["network", "loginctl", "freedesktop", "gamma", "theme.auto", "bluetooth", "cups", "dwl", "brightness", "extworkspace", "browser", "dbus", "location"]; - const filtered = allServices.filter(s => !excludeServices.includes(s)); - subscribe(filtered); - } - - function handleSubscriptionEvent(response) { - if (response.error) { - if (response.error.includes("unknown method") && response.error.includes("subscribe")) { - if (!shownOutdatedError) { - console.error("DMSService: Server does not support subscribe method"); - ToastService.showError(I18n.tr("DMS out of date"), I18n.tr("To update, run the following command:"), updateCommand); - shownOutdatedError = true; - } - } - return; - } - - if (!response.result) { - return; - } - - const service = response.result.service; - const data = response.result.data; - - if (service === "server") { - apiVersion = data.apiVersion || 0; - cliVersion = data.cliVersion || ""; - capabilities = data.capabilities || []; - - console.info("DMSService: Connected (API v" + apiVersion + ", CLI " + cliVersion + ") -", JSON.stringify(capabilities)); - - if (apiVersion < expectedApiVersion) { - ToastService.showError("DMS server is outdated (API v" + apiVersion + ", expected v" + expectedApiVersion + ")"); - } - - capabilitiesReceived(); - } else if (service === "network") { - networkStateUpdate(data); - } else if (service === "network.credentials") { - credentialsRequest(data); - } else if (service === "loginctl") { - if (data.event) { - loginctlEvent(data); - } else { - loginctlStateUpdate(data); - } - } else if (service === "bluetooth.pairing") { - bluetoothPairingRequest(data); - } else if (service === "cups") { - cupsStateUpdate(data); - } else if (service === "dwl") { - dwlStateUpdate(data); - } else if (service === "brightness") { - brightnessStateUpdate(data); - } else if (service === "brightness.update") { - if (data.device) { - brightnessDeviceUpdate(data.device); - } - } else if (service === "extworkspace") { - extWorkspaceStateUpdate(data); - } else if (service === "wlroutput") { - wlrOutputStateUpdate(data); - } else if (service === "evdev") { - if (data.capsLock !== undefined) { - capsLockState = data.capsLock; - } - evdevStateUpdate(data); - } else if (service === "gamma") { - gammaStateUpdate(data); - } else if (service === "theme.auto") { - themeAutoStateUpdate(data); - } else if (service === "browser.open_requested") { - if (data.target) { - if (data.requestType === "url" || !data.requestType) { - openUrlRequested(data.target); - } else { - appPickerRequested(data); - } - } else if (data.url) { - openUrlRequested(data.url); - } - } else if (service === "freedesktop.screensaver") { - screensaverInhibited = data.inhibited || false; - screensaverInhibitors = data.inhibitors || []; - screensaverStateUpdate(data); - } else if (service === "dbus") { - dbusSignalReceived(data.subscriptionId || "", data); - } else if (service === "clipboard") { - clipboardStateUpdate(data); - } else if (service === "location") { - locationStateUpdate(data); - } - } - - function sendRequest(method, params, callback) { - if (!isConnected) { - console.warn("DMSService.sendRequest: Not connected, method:", method); - if (callback) { - callback({ - "error": "not connected to DMS socket" - }); - } - return; - } - - requestIdCounter++; - const id = Date.now() + requestIdCounter; - const request = { - "id": id, - "method": method - }; - - if (params) { - request.params = params; - } - - if (callback) - pendingRequests[id] = callback; - - if (method.startsWith("clipboard")) { - clipboardRequestIds[id] = true; - } else { - console.log("DMSService.sendRequest: Sending request id=" + id + " method=" + method); - } - requestSocket.send(request); - } - - function handleResponse(response) { - const callback = pendingRequests[response.id]; - - if (callback) { - delete pendingRequests[response.id]; - callback(response); - } - } - - function ping(callback) { - sendRequest("ping", null, callback); - } - - function listPlugins(callback) { - sendRequest("plugins.list", null, response => { - if (response.result) { - availablePlugins = response.result; - pluginsListReceived(response.result); - } - if (callback) { - callback(response); - } - }); - } - - function listInstalled(callback) { - sendRequest("plugins.listInstalled", null, response => { - if (response.result) { - installedPlugins = response.result; - installedPluginsReceived(response.result); - } - if (callback) { - callback(response); - } - }); - } - - function search(query, category, compositor, capability, callback) { - const params = { - "query": query - }; - if (category) { - params.category = category; - } - if (compositor) { - params.compositor = compositor; - } - if (capability) { - params.capability = capability; - } - - sendRequest("plugins.search", params, response => { - if (response.result) { - searchResultsReceived(response.result); - } - if (callback) { - callback(response); - } - }); - } - - function install(pluginName, callback) { - sendRequest("plugins.install", { - "name": pluginName - }, response => { - if (callback) { - callback(response); - } - if (!response.error) { - listInstalled(); - } - }); - } - - function uninstall(pluginName, callback) { - sendRequest("plugins.uninstall", { - "name": pluginName - }, response => { - if (callback) { - callback(response); - } - if (!response.error) { - listInstalled(); - } - }); - } - - function update(pluginName, callback) { - sendRequest("plugins.update", { - "name": pluginName - }, response => { - if (callback) { - callback(response); - } - if (!response.error) { - listInstalled(); - } - }); - } - - function listThemes(callback) { - sendRequest("themes.list", null, response => { - if (response.result) { - availableThemes = response.result; - themesListReceived(response.result); - } - if (callback) { - callback(response); - } - }); - } - - function listInstalledThemes(callback) { - sendRequest("themes.listInstalled", null, response => { - if (response.result) { - installedThemes = response.result; - installedThemesReceived(response.result); - } - if (callback) { - callback(response); - } - }); - } - - function searchThemes(query, callback) { - sendRequest("themes.search", { - "query": query - }, response => { - if (response.result) { - themeSearchResultsReceived(response.result); - } - if (callback) { - callback(response); - } - }); - } - - function installTheme(themeName, callback) { - sendRequest("themes.install", { - "name": themeName - }, response => { - if (callback) { - callback(response); - } - if (!response.error) { - listInstalledThemes(); - } - }); - } - - function uninstallTheme(themeName, callback) { - sendRequest("themes.uninstall", { - "name": themeName - }, response => { - if (callback) { - callback(response); - } - if (!response.error) { - listInstalledThemes(); - } - }); - } - - function updateTheme(themeName, callback) { - sendRequest("themes.update", { - "name": themeName - }, response => { - if (callback) { - callback(response); - } - if (!response.error) { - listInstalledThemes(); - } - }); - } - - function lockSession(callback) { - sendRequest("loginctl.lock", null, callback); - } - - function unlockSession(callback) { - sendRequest("loginctl.unlock", null, callback); - } - - function bluetoothPair(devicePath, callback) { - sendRequest("bluetooth.pair", { - "device": devicePath - }, callback); - } - - function bluetoothConnect(devicePath, callback) { - sendRequest("bluetooth.connect", { - "device": devicePath - }, callback); - } - - function bluetoothDisconnect(devicePath, callback) { - sendRequest("bluetooth.disconnect", { - "device": devicePath - }, callback); - } - - function bluetoothRemove(devicePath, callback) { - sendRequest("bluetooth.remove", { - "device": devicePath - }, callback); - } - - function bluetoothTrust(devicePath, callback) { - sendRequest("bluetooth.trust", { - "device": devicePath - }, callback); - } - - function bluetoothSubmitPairing(token, secrets, accept, callback) { - sendRequest("bluetooth.pairing.submit", { - "token": token, - "secrets": secrets, - "accept": accept - }, callback); - } - - function bluetoothCancelPairing(token, callback) { - sendRequest("bluetooth.pairing.cancel", { - "token": token - }, callback); - } - - signal dbusSignalReceived(string subscriptionId, var data) - - property var dbusSubscriptions: ({}) - - function dbusCall(bus, dest, path, iface, method, args, callback) { - sendRequest("dbus.call", { - "bus": bus, - "dest": dest, - "path": path, - "interface": iface, - "method": method, - "args": args || [] - }, callback); - } - - function dbusGetProperty(bus, dest, path, iface, property, callback) { - sendRequest("dbus.getProperty", { - "bus": bus, - "dest": dest, - "path": path, - "interface": iface, - "property": property - }, callback); - } - - function dbusSetProperty(bus, dest, path, iface, property, value, callback) { - sendRequest("dbus.setProperty", { - "bus": bus, - "dest": dest, - "path": path, - "interface": iface, - "property": property, - "value": value - }, callback); - } - - function dbusGetAllProperties(bus, dest, path, iface, callback) { - sendRequest("dbus.getAllProperties", { - "bus": bus, - "dest": dest, - "path": path, - "interface": iface - }, callback); - } - - function dbusIntrospect(bus, dest, path, callback) { - sendRequest("dbus.introspect", { - "bus": bus, - "dest": dest, - "path": path || "/" - }, callback); - } - - function dbusListNames(bus, callback) { - sendRequest("dbus.listNames", { - "bus": bus - }, callback); - } - - function dbusSubscribe(bus, sender, path, iface, member, callback) { - sendRequest("dbus.subscribe", { - "bus": bus, - "sender": sender || "", - "path": path || "", - "interface": iface || "", - "member": member || "" - }, response => { - if (!response.error && response.result?.subscriptionId) { - dbusSubscriptions[response.result.subscriptionId] = true; - } - if (callback) - callback(response); - }); - } - - function dbusUnsubscribe(subscriptionId, callback) { - sendRequest("dbus.unsubscribe", { - "subscriptionId": subscriptionId - }, response => { - if (!response.error) { - delete dbusSubscriptions[subscriptionId]; - } - if (callback) - callback(response); - }); - } - - function renameWorkspace(name, callback) { - sendRequest("extworkspace.renameWorkspace", { - "name": name - }, callback); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DSearchService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DSearchService.qml deleted file mode 100644 index 63efcb0..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DSearchService.qml +++ /dev/null @@ -1,184 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property bool dsearchAvailable: false - property int searchIdCounter: 0 - property int indexVersion: 0 - property bool supportsTypeFilter: false - property bool versionChecked: false - - signal searchResultsReceived(var results) - signal statsReceived(var stats) - signal errorOccurred(string error) - - Process { - id: checkProcess - command: ["sh", "-c", "command -v dsearch"] - running: true - - stdout: SplitParser { - onRead: line => { - if (line && line.trim().length > 0) { - root.dsearchAvailable = true; - } - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - root.dsearchAvailable = false; - } else { - root._checkVersion(); - } - } - } - - function _checkVersion() { - Proc.runCommand("dsearch-version", ["dsearch", "version", "--json"], (stdout, exitCode) => { - root.versionChecked = true; - if (exitCode !== 0) - return; - const response = JSON.parse(stdout); - root.indexVersion = response.index_schema || 0; - root.supportsTypeFilter = root.indexVersion >= 2; - }); - } - - function ping(callback) { - if (!dsearchAvailable) { - if (callback) { - callback({ - "error": "dsearch not available" - }); - } - return; - } - - Proc.runCommand("dsearch-ping", ["dsearch", "ping", "--json"], (stdout, exitCode) => { - if (callback) { - if (exitCode === 0) { - try { - const response = JSON.parse(stdout); - callback({ - "result": response - }); - } catch (e) { - callback({ - "error": "failed to parse ping response" - }); - } - } else { - callback({ - "error": "ping failed" - }); - } - } - }); - } - - function search(query, params, callback) { - if (!query || query.length === 0) { - if (callback) { - callback({ - "error": "query is required" - }); - } - return; - } - - if (!dsearchAvailable) { - if (callback) { - callback({ - "error": "dsearch not available" - }); - } - return; - } - - const args = ["dsearch", "search", query, "--json"]; - - if (params) { - if (params.limit !== undefined) { - args.push("-n", String(params.limit)); - } - if (params.type) { - args.push("-t", params.type); - } - if (params.ext) { - args.push("-e", params.ext); - } - if (params.folder) { - args.push("--folder", params.folder); - } - if (params.field) { - args.push("-f", params.field); - } - if (params.fuzzy) { - args.push("--fuzzy"); - } - if (params.sort) { - args.push("--sort", params.sort); - } - if (params.desc !== undefined) { - args.push("--desc=" + (params.desc ? "true" : "false")); - } - if (params.minSize !== undefined) { - args.push("--min-size", String(params.minSize)); - } - if (params.maxSize !== undefined) { - args.push("--max-size", String(params.maxSize)); - } - } - - const procId = "dsearch-search-" + (params?.type || "all"); - Proc.runCommand(procId, args, (stdout, exitCode) => { - if (exitCode === 0) { - try { - const response = JSON.parse(stdout); - searchResultsReceived(response); - if (callback) { - callback({ - "result": response - }); - } - } catch (e) { - const error = "failed to parse search response"; - errorOccurred(error); - if (callback) { - callback({ - "error": error - }); - } - } - } else if (exitCode === 124) { - const error = "search timed out"; - errorOccurred(error); - if (callback) { - callback({ - "error": error - }); - } - } else { - const error = "search failed"; - errorOccurred(error); - if (callback) { - callback({ - "error": error - }); - } - } - }, 100, 5000); - } - - function rediscover() { - checkProcess.running = true; - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DesktopService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DesktopService.qml deleted file mode 100644 index c9913e9..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DesktopService.qml +++ /dev/null @@ -1,94 +0,0 @@ -pragma Singleton - -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io - -Singleton { - id: root - - property var _cache: ({}) - - function resolveIconPath(moddedAppId) { - if (!moddedAppId) - return ""; - - if (_cache[moddedAppId] !== undefined) - return _cache[moddedAppId]; - - const result = (function() { - // 1. Try heuristic lookup (standard) - const entry = DesktopEntries.heuristicLookup(moddedAppId); - let icon = Quickshell.iconPath(entry?.icon, true); - if (icon && icon !== "") - return icon; - - // 2. Try the appId itself as an icon name - icon = Quickshell.iconPath(moddedAppId, true); - if (icon && icon !== "") - return icon; - - // 3. Try variations of the appId (lowercase, last part) - const appIds = [moddedAppId.toLowerCase()]; - const lastPart = moddedAppId.split('.').pop(); - if (lastPart && lastPart !== moddedAppId) { - appIds.push(lastPart); - appIds.push(lastPart.toLowerCase()); - } - - for (const id of appIds) { - icon = Quickshell.iconPath(id, true); - if (icon && icon !== "") - return icon; - } - - // 4. Deep search in all desktop entries (if the above fail) - // This is slow-ish but only happens once for failed icons - const strippedId = moddedAppId.replace(/-bin$/, "").toLowerCase(); - const allEntries = DesktopEntries.applications.values; - for (let i = 0; i < allEntries.length; i++) { - const e = allEntries[i]; - const eId = (e.id || "").toLowerCase(); - const eName = (e.name || "").toLowerCase(); - const eExec = (e.execString || "").toLowerCase(); - - if (eId.includes(strippedId) || eName.includes(strippedId) || eExec.includes(strippedId)) { - icon = Quickshell.iconPath(e.icon, true); - if (icon && icon !== "") - return icon; - } - } - - // 5. Nix/Guix specific store check (as a last resort) - for (const appId of appIds) { - let execPath = entry?.execString?.replace(/\/bin.*/, ""); - if (!execPath) - continue; - - if (execPath.startsWith("/nix/store/") || execPath.startsWith("/gnu/store/")) { - const basePath = execPath; - const sizes = ["256x256", "128x128", "64x64", "48x48", "32x32", "24x24", "16x16"]; - - let iconPath = `${basePath}/share/icons/hicolor/scalable/apps/${appId}.svg`; - icon = Quickshell.iconPath(iconPath, true); - if (icon && icon !== "") - return icon; - - for (const size of sizes) { - iconPath = `${basePath}/share/icons/hicolor/${size}/apps/${appId}.png`; - icon = Quickshell.iconPath(iconPath, true); - if (icon && icon !== "") - return icon; - } - } - } - - return ""; - })(); - - _cache[moddedAppId] = result; - return result; - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DesktopWidgetRegistry.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DesktopWidgetRegistry.qml deleted file mode 100644 index 0e2ea6f..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DesktopWidgetRegistry.qml +++ /dev/null @@ -1,232 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import qs.Common - -Singleton { - id: root - - property var registeredWidgets: ({}) - property var registeredWidgetsList: [] - - signal registryChanged - - Component.onCompleted: { - registerBuiltins(); - Qt.callLater(syncPluginWidgets); - } - - Connections { - target: PluginService - function onPluginLoaded(pluginId) { - const plugin = PluginService.availablePlugins[pluginId]; - if (plugin?.type === "desktop") - syncPluginWidgets(); - } - function onPluginUnloaded(pluginId) { - syncPluginWidgets(); - } - function onPluginListUpdated() { - syncPluginWidgets(); - } - } - - function registerBuiltins() { - registerWidget({ - id: "desktopClock", - name: I18n.tr("Desktop Clock", "Desktop clock widget name"), - icon: "schedule", - description: I18n.tr("Analog, digital, or stacked clock display", "Desktop clock widget description"), - type: "builtin", - component: "qs.Modules.BuiltinDesktopPlugins.DesktopClockWidget", - settingsComponent: "qs.Modules.Settings.DesktopWidgetSettings.ClockSettings", - defaultConfig: getDefaultClockConfig(), - defaultSize: { - width: 280, - height: 180 - } - }); - - registerWidget({ - id: "systemMonitor", - name: I18n.tr("System Monitor", "System monitor widget name"), - icon: "monitoring", - description: I18n.tr("CPU, memory, network, and disk monitoring", "System monitor widget description"), - type: "builtin", - component: "qs.Modules.BuiltinDesktopPlugins.SystemMonitorWidget", - settingsComponent: "qs.Modules.Settings.DesktopWidgetSettings.SystemMonitorSettings", - defaultConfig: getDefaultSystemMonitorConfig(), - defaultSize: { - width: 320, - height: 480 - } - }); - } - - function getDefaultClockConfig() { - return { - style: "analog", - transparency: 0.8, - colorMode: "primary", - customColor: "#ffffff", - showDate: true, - showAnalogNumbers: false, - showAnalogSeconds: true, - displayPreferences: ["all"] - }; - } - - function getDefaultSystemMonitorConfig() { - return { - showHeader: true, - transparency: 0.8, - colorMode: "primary", - customColor: "#ffffff", - showCpu: true, - showCpuGraph: true, - showCpuTemp: true, - showGpuTemp: false, - gpuPciId: "", - showMemory: true, - showMemoryGraph: true, - showNetwork: true, - showNetworkGraph: true, - showDisk: true, - showTopProcesses: false, - topProcessCount: 3, - topProcessSortBy: "cpu", - layoutMode: "auto", - graphInterval: 60, - displayPreferences: ["all"] - }; - } - - function registerWidget(widgetDef) { - if (!widgetDef?.id) - return; - - const newMap = Object.assign({}, registeredWidgets); - newMap[widgetDef.id] = widgetDef; - registeredWidgets = newMap; - _updateWidgetsList(); - registryChanged(); - } - - function unregisterWidget(widgetId) { - if (!registeredWidgets[widgetId]) - return; - - const newMap = Object.assign({}, registeredWidgets); - delete newMap[widgetId]; - registeredWidgets = newMap; - _updateWidgetsList(); - registryChanged(); - } - - function getWidget(widgetType) { - return registeredWidgets[widgetType] ?? null; - } - - function getDefaultConfig(widgetType) { - const widget = getWidget(widgetType); - if (!widget) - return {}; - - if (widget.type === "builtin") { - switch (widgetType) { - case "desktopClock": - return getDefaultClockConfig(); - case "systemMonitor": - return getDefaultSystemMonitorConfig(); - default: - return widget.defaultConfig ?? {}; - } - } - - return widget.defaultConfig ?? {}; - } - - function getDefaultSize(widgetType) { - const widget = getWidget(widgetType); - return widget?.defaultSize ?? { - width: 200, - height: 200 - }; - } - - function syncPluginWidgets() { - const desktopPlugins = PluginService.pluginDesktopComponents; - const availablePlugins = PluginService.availablePlugins; - const currentPluginIds = []; - - for (const pluginId in desktopPlugins) { - currentPluginIds.push(pluginId); - const plugin = availablePlugins[pluginId]; - if (!plugin) - continue; - - if (registeredWidgets[pluginId]?.type === "plugin") - continue; - - registerWidget({ - id: pluginId, - name: plugin.name || pluginId, - icon: plugin.icon || "extension", - description: plugin.description || "", - type: "plugin", - component: null, - settingsComponent: plugin.settingsPath || null, - defaultConfig: { - displayPreferences: ["all"] - }, - defaultSize: { - width: 200, - height: 200 - }, - pluginInfo: plugin - }); - } - - const toRemove = []; - for (const widgetId in registeredWidgets) { - const widget = registeredWidgets[widgetId]; - if (widget.type !== "plugin") - continue; - if (!currentPluginIds.includes(widgetId)) - toRemove.push(widgetId); - } - - for (const widgetId of toRemove) { - unregisterWidget(widgetId); - } - } - - function _updateWidgetsList() { - const result = []; - for (const key in registeredWidgets) { - result.push(registeredWidgets[key]); - } - result.sort((a, b) => { - if (a.type === "builtin" && b.type !== "builtin") - return -1; - if (a.type !== "builtin" && b.type === "builtin") - return 1; - return (a.name || "").localeCompare(b.name || ""); - }); - registeredWidgetsList = result; - } - - function getBuiltinWidgets() { - return registeredWidgetsList.filter(w => w.type === "builtin"); - } - - function getPluginWidgets() { - return registeredWidgetsList.filter(w => w.type === "plugin"); - } - - function getAllWidgets() { - return registeredWidgetsList; - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DgopService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DgopService.qml deleted file mode 100644 index e63c5c5..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DgopService.qml +++ /dev/null @@ -1,778 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property int refCount: 0 - property int updateInterval: refCount > 0 ? 3000 : 30000 - property bool isUpdating: false - property bool dgopAvailable: false - - property var moduleRefCounts: ({}) - property var enabledModules: [] - property var gpuPciIds: [] - property var gpuPciIdRefCounts: ({}) - property int processLimit: 20 - property string processSort: "cpu" - property bool noCpu: false - property int dgopProcessPid: 0 - - // Cursor data for accurate CPU calculations - property string cpuCursor: "" - property string procCursor: "" - property int cpuSampleCount: 0 - property int processSampleCount: 0 - - property real cpuUsage: 0 - property real cpuFrequency: 0 - property real cpuTemperature: 0 - property int cpuCores: 1 - property string cpuModel: "" - property var perCoreCpuUsage: [] - - property real memoryUsage: 0 - property real totalMemoryMB: 0 - property real usedMemoryMB: 0 - property real freeMemoryMB: 0 - property real availableMemoryMB: 0 - property int totalMemoryKB: 0 - property int usedMemoryKB: 0 - property int totalSwapKB: 0 - property int usedSwapKB: 0 - - property real networkRxRate: 0 - property real networkTxRate: 0 - property var lastNetworkStats: null - property var networkInterfaces: [] - - property real diskReadRate: 0 - property real diskWriteRate: 0 - property var lastDiskStats: null - property var diskMounts: [] - property var diskDevices: [] - - property var processes: [] - property var allProcesses: [] - property string currentSort: "cpu" - property bool sortAscending: false - property var availableGpus: [] - - property string kernelVersion: "" - property string distribution: "" - property string hostname: "" - property string architecture: "" - property string loadAverage: "" - property int processCount: 0 - property int threadCount: 0 - property string bootTime: "" - property string motherboard: "" - property string biosVersion: "" - property string uptime: "" - property string shortUptime: "" - - property int historySize: 60 - property var cpuHistory: [] - property var memoryHistory: [] - property var networkHistory: ({ - "rx": [], - "tx": [] - }) - property var diskHistory: ({ - "read": [], - "write": [] - }) - - function addRef(modules = null) { - refCount++; - let modulesChanged = false; - - if (modules) { - const modulesToAdd = Array.isArray(modules) ? modules : [modules]; - for (const module of modulesToAdd) { - const currentCount = moduleRefCounts[module] || 0; - moduleRefCounts[module] = currentCount + 1; - - // Add to enabled modules if not already there - if (enabledModules.indexOf(module) === -1) { - enabledModules.push(module); - modulesChanged = true; - } - } - } - - if (modulesChanged || refCount === 1) { - enabledModules = enabledModules.slice(); // Force property change - moduleRefCounts = Object.assign({}, moduleRefCounts); // Force property change - updateAllStats(); - } else if (gpuPciIds.length > 0 && refCount > 0) { - // If we have GPU PCI IDs and active modules, make sure to update - // This handles the case where PCI IDs were loaded after modules were added - updateAllStats(); - } - } - - function removeRef(modules = null) { - refCount = Math.max(0, refCount - 1); - let modulesChanged = false; - - if (modules) { - const modulesToRemove = Array.isArray(modules) ? modules : [modules]; - for (const module of modulesToRemove) { - const currentCount = moduleRefCounts[module] || 0; - if (currentCount > 1) { - moduleRefCounts[module] = currentCount - 1; - } else if (currentCount === 1) { - delete moduleRefCounts[module]; - const index = enabledModules.indexOf(module); - if (index > -1) { - enabledModules.splice(index, 1); - modulesChanged = true; - } - } - } - } - - if (modulesChanged) { - enabledModules = enabledModules.slice(); // Force property change - moduleRefCounts = Object.assign({}, moduleRefCounts); // Force property change - - // Clear cursor data when CPU or process modules are no longer active - if (!enabledModules.includes("cpu")) { - cpuCursor = ""; - cpuSampleCount = 0; - } - if (!enabledModules.includes("processes")) { - procCursor = ""; - processSampleCount = 0; - } - } - } - - function setGpuPciIds(pciIds) { - gpuPciIds = Array.isArray(pciIds) ? pciIds : []; - } - - function addGpuPciId(pciId) { - const currentCount = gpuPciIdRefCounts[pciId] || 0; - gpuPciIdRefCounts[pciId] = currentCount + 1; - - // Add to gpuPciIds array if not already there - if (!gpuPciIds.includes(pciId)) { - gpuPciIds = gpuPciIds.concat([pciId]); - } - - gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts); - } - - function removeGpuPciId(pciId) { - const currentCount = gpuPciIdRefCounts[pciId] || 0; - if (currentCount > 1) { - gpuPciIdRefCounts[pciId] = currentCount - 1; - } else if (currentCount === 1) { - // Remove completely when count reaches 0 - delete gpuPciIdRefCounts[pciId]; - const index = gpuPciIds.indexOf(pciId); - if (index > -1) { - gpuPciIds = gpuPciIds.slice(); - gpuPciIds.splice(index, 1); - } - - // Clear temperature data for this GPU when no longer monitored - if (availableGpus && availableGpus.length > 0) { - const updatedGpus = availableGpus.slice(); - for (var i = 0; i < updatedGpus.length; i++) { - if (updatedGpus[i].pciId === pciId) { - updatedGpus[i] = Object.assign({}, updatedGpus[i], { - "temperature": 0 - }); - } - } - availableGpus = updatedGpus; - } - } - - // Force property change notification - gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts); - } - - function setProcessOptions(limit = 20, sort = "cpu", disableCpu = false) { - processLimit = limit; - processSort = sort; - noCpu = disableCpu; - } - - function updateAllStats() { - if (dgopAvailable && refCount > 0 && enabledModules.length > 0) { - isUpdating = true; - dgopProcess.running = true; - } else { - isUpdating = false; - } - } - - function initializeGpuMetadata() { - if (!dgopAvailable) - return; - gpuInitProcess.running = true; - } - - function initializeSystemMetadata() { - if (!dgopAvailable) - return; - systemInitProcess.running = true; - } - - function buildDgopCommand() { - const cmd = ["dgop", "meta", "--json"]; - - if (enabledModules.length === 0) { - // Don't run if no modules are needed - return []; - } - - // Replace 'gpu' with 'gpu-temp' when we have PCI IDs to monitor - const finalModules = []; - for (const module of enabledModules) { - if (module === "gpu" && gpuPciIds.length > 0) { - finalModules.push("gpu-temp"); - } else if (module !== "gpu") { - finalModules.push(module); - } - } - - // Add gpu-temp module automatically when we have PCI IDs to monitor - if (gpuPciIds.length > 0 && finalModules.indexOf("gpu-temp") === -1) { - finalModules.push("gpu-temp"); - } - - if (enabledModules.indexOf("all") !== -1) { - cmd.push("--modules", "all"); - } else if (finalModules.length > 0) { - const moduleList = finalModules.join(","); - cmd.push("--modules", moduleList); - } else { - return []; - } - - // Add cursor data if available for accurate CPU percentages - if ((enabledModules.includes("cpu") || enabledModules.includes("all")) && cpuCursor) { - cmd.push("--cpu-cursor", cpuCursor); - } - if ((enabledModules.includes("processes") || enabledModules.includes("all")) && procCursor) { - cmd.push("--proc-cursor", procCursor); - } - - if (gpuPciIds.length > 0) { - cmd.push("--gpu-pci-ids", gpuPciIds.join(",")); - } - - if (enabledModules.indexOf("processes") !== -1 || enabledModules.indexOf("all") !== -1) { - cmd.push("--limit", "100"); // Get more data for client sorting - cmd.push("--sort", "cpu"); // Always get CPU sorted data - if (noCpu) { - cmd.push("--no-cpu"); - } - } - - return cmd; - } - - function parseData(data) { - if (data.cpu) { - const cpu = data.cpu; - cpuSampleCount++; - - cpuUsage = Math.round((cpu.usage || 0) * 10) / 10; - cpuFrequency = Math.round(cpu.frequency || 0); - cpuTemperature = Math.round(cpu.temperature || 0); - cpuCores = cpu.count || 1; - cpuModel = cpu.model || ""; - perCoreCpuUsage = cpu.coreUsage || []; - addToHistory(cpuHistory, cpuUsage); - - if (cpu.cursor) { - cpuCursor = cpu.cursor; - } - } - - if (data.memory) { - const mem = data.memory; - const totalKB = mem.total || 0; - const availableKB = mem.available || 0; - const freeKB = mem.free || 0; - const usedKB = mem.used !== undefined ? mem.used : (totalKB - availableKB); - - totalMemoryMB = Math.round(totalKB / 1024); - availableMemoryMB = Math.round(availableKB / 1024); - freeMemoryMB = Math.round(freeKB / 1024); - usedMemoryMB = Math.round(usedKB / 1024); - const rawMemUsage = mem.usedPercent !== undefined ? mem.usedPercent : (totalKB > 0 ? ((totalKB - availableKB) / totalKB) * 100 : 0); - memoryUsage = Math.round(rawMemUsage * 10) / 10; - - totalMemoryKB = totalKB; - usedMemoryKB = usedKB; - totalSwapKB = mem.swaptotal || 0; - usedSwapKB = (mem.swaptotal || 0) - (mem.swapfree || 0); - - addToHistory(memoryHistory, memoryUsage); - } - - if (data.network && Array.isArray(data.network)) { - networkInterfaces = data.network; - - let totalRx = 0; - let totalTx = 0; - for (const iface of data.network) { - totalRx += iface.rx || 0; - totalTx += iface.tx || 0; - } - - if (lastNetworkStats) { - const timeDiff = updateInterval / 1000; - const rxDiff = totalRx - lastNetworkStats.rx; - const txDiff = totalTx - lastNetworkStats.tx; - networkRxRate = Math.max(0, rxDiff / timeDiff); - networkTxRate = Math.max(0, txDiff / timeDiff); - addToHistory(networkHistory.rx, networkRxRate / 1024); - addToHistory(networkHistory.tx, networkTxRate / 1024); - } - lastNetworkStats = { - "rx": totalRx, - "tx": totalTx - }; - } - - if (data.disk && Array.isArray(data.disk)) { - diskDevices = data.disk; - - let totalRead = 0; - let totalWrite = 0; - for (const disk of data.disk) { - totalRead += (disk.read || 0) * 512; - totalWrite += (disk.write || 0) * 512; - } - - if (lastDiskStats) { - const timeDiff = updateInterval / 1000; - const readDiff = totalRead - lastDiskStats.read; - const writeDiff = totalWrite - lastDiskStats.write; - diskReadRate = Math.max(0, readDiff / timeDiff); - diskWriteRate = Math.max(0, writeDiff / timeDiff); - addToHistory(diskHistory.read, diskReadRate / (1024 * 1024)); - addToHistory(diskHistory.write, diskWriteRate / (1024 * 1024)); - } - lastDiskStats = { - "read": totalRead, - "write": totalWrite - }; - } - - if (data.diskmounts) { - diskMounts = data.diskmounts || []; - } - - if (data.processes && Array.isArray(data.processes)) { - const newProcesses = []; - processSampleCount++; - const ourPid = dgopProcessPid; - - for (const proc of data.processes) { - if (ourPid > 0 && proc.pid === ourPid) - continue; - - const cpuUsage = processSampleCount >= 2 ? (proc.cpu || 0) : 0; - - newProcesses.push({ - "pid": proc.pid || 0, - "ppid": proc.ppid || 0, - "cpu": cpuUsage, - "memoryPercent": proc.memoryPercent || proc.pssPercent || 0, - "memoryKB": proc.memoryKB || proc.pssKB || 0, - "command": proc.command || "", - "fullCommand": proc.fullCommand || "", - "username": proc.username || "", - "displayName": (proc.command && proc.command.length > 15) ? proc.command.substring(0, 15) + "..." : (proc.command || "") - }); - } - allProcesses = newProcesses; - applySorting(); - - if (data.cursor) { - procCursor = data.cursor; - } - } - - const gpuData = (data.gpu && data.gpu.gpus) || data.gpus; - if (gpuData && Array.isArray(gpuData)) { - // Check if this is temperature update data (has PCI IDs being monitored) - if (gpuPciIds.length > 0 && availableGpus && availableGpus.length > 0) { - // This is temperature data - merge with existing GPU metadata - const updatedGpus = availableGpus.slice(); - for (var i = 0; i < updatedGpus.length; i++) { - const existingGpu = updatedGpus[i]; - const tempGpu = gpuData.find(g => g.pciId === existingGpu.pciId); - // Only update temperature if this GPU's PCI ID is being monitored - if (tempGpu && gpuPciIds.includes(existingGpu.pciId)) { - updatedGpus[i] = Object.assign({}, existingGpu, { - "temperature": tempGpu.temperature || 0 - }); - } - } - availableGpus = updatedGpus; - } else { - // This is initial GPU metadata - set the full list - const gpuList = []; - for (const gpu of gpuData) { - let displayName = gpu.displayName || gpu.name || "Unknown GPU"; - let fullName = gpu.fullName || gpu.name || "Unknown GPU"; - - gpuList.push({ - "driver": gpu.driver || "", - "vendor": gpu.vendor || "", - "displayName": displayName, - "fullName": fullName, - "pciId": gpu.pciId || "", - "temperature": gpu.temperature || 0 - }); - } - availableGpus = gpuList; - } - } - - if (data.system) { - const sys = data.system; - loadAverage = sys.loadavg || ""; - processCount = sys.processes || 0; - threadCount = sys.threads || 0; - bootTime = sys.boottime || ""; - updateUptime(); - } - - const hwData = data.hardware || (data.hostname || data.kernel || data.distro || data.arch) ? data : null; - if (hwData) { - hostname = hwData.hostname || ""; - kernelVersion = hwData.kernel || ""; - distribution = hwData.distro || ""; - architecture = hwData.arch || ""; - motherboard = (hwData.bios && hwData.bios.motherboard) || ""; - biosVersion = (hwData.bios && hwData.bios.version) || ""; - } - - isUpdating = false; - } - - function addToHistory(array, value) { - array.push(value); - if (array.length > historySize) { - array.shift(); - } - } - - function getProcessIcon(command) { - const cmd = command.toLowerCase(); - if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser") || cmd.includes("chromium")) { - return "web"; - } - if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim")) { - return "code"; - } - if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh")) { - return "terminal"; - } - if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify")) { - return "music_note"; - } - if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv")) { - return "play_circle"; - } - if (cmd.includes("systemd") || cmd.includes("elogind") || cmd.includes("kernel") || cmd.includes("kthread") || cmd.includes("kworker")) { - return "settings"; - } - return "memory"; - } - - function formatCpuUsage(cpu) { - return (cpu || 0).toFixed(1) + "%"; - } - - function formatMemoryUsage(memoryKB) { - const mem = memoryKB || 0; - if (mem < 1024) { - return mem.toFixed(0) + " KB"; - } else if (mem < 1024 * 1024) { - return (mem / 1024).toFixed(1) + " MB"; - } else { - return (mem / (1024 * 1024)).toFixed(1) + " GB"; - } - } - - function formatSystemMemory(memoryKB) { - const mem = memoryKB || 0; - if (mem === 0) { - return "--"; - } - if (mem < 1024 * 1024) { - return (mem / 1024).toFixed(0) + " MB"; - } else { - return (mem / (1024 * 1024)).toFixed(1) + " GB"; - } - } - - function killProcess(pid) { - if (pid > 0) { - Quickshell.execDetached("kill", [pid.toString()]); - } - } - - function updateUptime() { - if (!bootTime) { - uptime = ""; - shortUptime = ""; - return; - } - - const bootDate = new Date(bootTime.replace(" ", "T")); - if (isNaN(bootDate.getTime())) { - uptime = ""; - shortUptime = ""; - return; - } - - const now = new Date(); - const seconds = Math.floor((now - bootDate) / 1000); - const days = Math.floor(seconds / 86400); - const hours = Math.floor((seconds % 86400) / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - - const parts = []; - if (days > 0) - parts.push(`${days} day${days === 1 ? "" : "s"}`); - if (hours > 0) - parts.push(`${hours} hour${hours === 1 ? "" : "s"}`); - if (minutes > 0) - parts.push(`${minutes} minute${minutes === 1 ? "" : "s"}`); - - uptime = parts.length > 0 ? `up ${parts.join(", ")}` : `up ${seconds} seconds`; - - var shortStr = "up"; - if (days > 0) - shortStr += ` ${days}d`; - if (hours > 0) - shortStr += ` ${hours}h`; - if (minutes > 0) - shortStr += ` ${minutes}m`; - shortUptime = shortStr; - } - - function setSortBy(newSortBy) { - if (newSortBy !== currentSort) { - currentSort = newSortBy; - sortAscending = false; - applySorting(); - } - } - - function toggleSort(column) { - if (column === currentSort) { - sortAscending = !sortAscending; - } else { - currentSort = column; - sortAscending = false; - } - applySorting(); - } - - function applySorting() { - if (!allProcesses || allProcesses.length === 0) - return; - - const asc = sortAscending; - const sorted = allProcesses.slice(); - sorted.sort((a, b) => { - let valueA, valueB, result; - - switch (currentSort) { - case "cpu": - valueA = a.cpu || 0; - valueB = b.cpu || 0; - result = valueB - valueA; - break; - case "memory": - valueA = a.memoryKB || 0; - valueB = b.memoryKB || 0; - result = valueB - valueA; - break; - case "name": - valueA = (a.command || "").toLowerCase(); - valueB = (b.command || "").toLowerCase(); - result = valueA.localeCompare(valueB); - break; - case "pid": - valueA = a.pid || 0; - valueB = b.pid || 0; - result = valueA - valueB; - break; - default: - return 0; - } - return asc ? -result : result; - }); - - processes = sorted.slice(0, processLimit); - } - - Timer { - id: updateTimer - interval: root.updateInterval - running: root.dgopAvailable && root.refCount > 0 && root.enabledModules.length > 0 - repeat: true - triggeredOnStart: true - onTriggered: root.updateAllStats() - } - - Process { - id: dgopProcess - command: root.buildDgopCommand() - running: false - onStarted: dgopProcessPid = processId ?? 0 - onExited: exitCode => { - if (exitCode !== 0) { - console.warn("Dgop process failed with exit code:", exitCode); - isUpdating = false; - } - } - stdout: StdioCollector { - onStreamFinished: { - if (text.trim()) { - try { - const data = JSON.parse(text.trim()); - parseData(data); - } catch (e) { - console.warn("Failed to parse dgop JSON:", e); - console.warn("Raw text was:", text.substring(0, 200)); - isUpdating = false; - } - } - } - } - } - - Process { - id: gpuInitProcess - command: ["dgop", "gpu", "--json"] - running: false - onExited: exitCode => { - if (exitCode !== 0) { - console.warn("GPU init process failed with exit code:", exitCode); - } - } - stdout: StdioCollector { - onStreamFinished: { - if (text.trim()) { - try { - const data = JSON.parse(text.trim()); - parseData(data); - } catch (e) { - console.warn("Failed to parse GPU init JSON:", e); - } - } - } - } - } - - Process { - id: systemInitProcess - command: ["dgop", "hardware", "--json"] - running: false - onExited: exitCode => { - if (exitCode !== 0) { - console.warn("System init process failed with exit code:", exitCode); - } - } - stdout: StdioCollector { - onStreamFinished: { - if (text.trim()) { - try { - const data = JSON.parse(text.trim()); - parseData(data); - } catch (e) { - console.warn("Failed to parse system init JSON:", e); - } - } - } - } - } - - Process { - id: dgopCheckProcess - command: ["which", "dgop"] - running: false - onExited: exitCode => { - dgopAvailable = (exitCode === 0); - if (dgopAvailable) { - initializeGpuMetadata(); - initializeSystemMetadata(); - if (SessionData.enabledGpuPciIds && SessionData.enabledGpuPciIds.length > 0) { - for (const pciId of SessionData.enabledGpuPciIds) { - addGpuPciId(pciId); - } - if (refCount > 0 && enabledModules.length > 0) { - updateAllStats(); - } - } - } else { - console.warn("dgop is not installed or not in PATH"); - } - } - } - - Process { - id: osReleaseProcess - command: ["cat", "/etc/os-release"] - running: false - onExited: exitCode => { - if (exitCode !== 0) { - console.warn("Failed to read /etc/os-release"); - } - } - stdout: StdioCollector { - onStreamFinished: { - if (text.trim()) { - try { - const lines = text.trim().split('\n'); - let prettyName = ""; - let name = ""; - - for (const line of lines) { - const trimmedLine = line.trim(); - if (trimmedLine.startsWith('PRETTY_NAME=')) { - prettyName = trimmedLine.substring(12).replace(/^["']|["']$/g, ''); - } else if (trimmedLine.startsWith('NAME=')) { - name = trimmedLine.substring(5).replace(/^["']|["']$/g, ''); - } - } - - // Prefer PRETTY_NAME, fallback to NAME - const distroName = prettyName || name || "Linux"; - distribution = distroName; - console.info("Detected distribution:", distroName); - } catch (e) { - console.warn("Failed to parse /etc/os-release:", e); - distribution = "Linux"; - } - } - } - } - } - - Component.onCompleted: { - dgopCheckProcess.running = true; - osReleaseProcess.running = true; - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DisplayService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DisplayService.qml deleted file mode 100644 index 553dd26..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DisplayService.qml +++ /dev/null @@ -1,1170 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property bool brightnessAvailable: devices.length > 0 - property var devices: [] - property var deviceBrightness: ({}) - property var deviceBrightnessUserSet: ({}) - property var deviceMaxCache: ({}) - property var userControlledDevices: ({}) - property var pendingOsdDevices: ({}) - property int brightnessVersion: 0 - property string currentDevice: "" - property string lastIpcDevice: "" - property int brightnessLevel: { - brightnessVersion; - const deviceToUse = lastIpcDevice === "" ? getDefaultDevice() : (lastIpcDevice || currentDevice); - if (!deviceToUse) { - return 50; - } - - return getDeviceBrightness(deviceToUse); - } - property int maxBrightness: 100 - property bool brightnessInitialized: false - property bool suppressOsd: true - - signal brightnessChanged(bool showOsd) - signal deviceSwitched - - property bool nightModeActive: nightModeEnabled - - property bool nightModeEnabled: false - property bool automationAvailable: false - property bool gammaControlAvailable: false - - property var gammaState: ({}) - property int gammaCurrentTemp: gammaState?.currentTemp ?? 0 - property string gammaNextTransition: gammaState?.nextTransition ?? "" - property string gammaSunriseTime: gammaState?.sunriseTime ?? "" - property string gammaSunsetTime: gammaState?.sunsetTime ?? "" - property string gammaDawnTime: gammaState?.dawnTime ?? "" - property string gammaNightTime: gammaState?.nightTime ?? "" - property bool gammaIsDay: gammaState?.isDay ?? true - property real gammaSunPosition: gammaState?.sunPosition ?? 0 - property int gammaLowTemp: gammaState?.config?.LowTemp ?? 0 - property int gammaHighTemp: gammaState?.config?.HighTemp ?? 0 - - function markDeviceUserControlled(deviceId) { - const newControlled = Object.assign({}, userControlledDevices); - newControlled[deviceId] = Date.now(); - userControlledDevices = newControlled; - } - - function isDeviceUserControlled(deviceId) { - const controlTime = userControlledDevices[deviceId]; - if (!controlTime) { - return false; - } - return (Date.now() - controlTime) < 1000; - } - - function clearDeviceUserControlled(deviceId) { - const newControlled = Object.assign({}, userControlledDevices); - delete newControlled[deviceId]; - userControlledDevices = newControlled; - } - - function markDevicePendingOsd(deviceId) { - const newPending = Object.assign({}, pendingOsdDevices); - newPending[deviceId] = true; - pendingOsdDevices = newPending; - } - - function clearDevicePendingOsd(deviceId) { - const newPending = Object.assign({}, pendingOsdDevices); - delete newPending[deviceId]; - pendingOsdDevices = newPending; - } - - function updateSingleDevice(device) { - if (device.class === "leds") { - return; - } - - const isUserControlled = isDeviceUserControlled(device.id); - if (isUserControlled) { - return; - } - - const deviceIndex = devices.findIndex(d => d.id === device.id); - if (deviceIndex !== -1) { - const newDevices = [...devices]; - const cachedMax = deviceMaxCache[device.id]; - - let displayMax = cachedMax || (device.class === "ddc" ? device.max : 100); - if (displayMax > 0 && !cachedMax) { - const newCache = Object.assign({}, deviceMaxCache); - newCache[device.id] = displayMax; - deviceMaxCache = newCache; - } - - newDevices[deviceIndex] = { - "id": device.id, - "name": device.id, - "class": device.class, - "current": device.current, - "percentage": device.currentPercent, - "max": device.max, - "backend": device.backend, - "displayMax": displayMax - }; - devices = newDevices; - } - - const isExponential = SessionData.getBrightnessExponential(device.id); - const userSetValue = deviceBrightnessUserSet[device.id]; - - let displayValue = device.currentPercent; - if (isExponential) { - if (userSetValue !== undefined) { - const exponent = SessionData.getBrightnessExponent(device.id); - const expectedHardware = Math.round(Math.pow(userSetValue / 100.0, exponent) * 100.0); - if (Math.abs(device.currentPercent - expectedHardware) > 2) { - const newUserSet = Object.assign({}, deviceBrightnessUserSet); - delete newUserSet[device.id]; - deviceBrightnessUserSet = newUserSet; - SessionData.clearBrightnessUserSetValue(device.id); - displayValue = linearToExponential(device.currentPercent, device.id); - } else { - displayValue = userSetValue; - } - } else { - displayValue = linearToExponential(device.currentPercent, device.id); - } - } - - const oldValue = deviceBrightness[device.id]; - const newBrightness = Object.assign({}, deviceBrightness); - newBrightness[device.id] = displayValue; - deviceBrightness = newBrightness; - brightnessVersion++; - - const isPendingOsd = pendingOsdDevices[device.id] === true; - if (isPendingOsd) { - clearDevicePendingOsd(device.id); - if (!suppressOsd) { - brightnessChanged(true); - } - return; - } - - if (!brightnessInitialized || oldValue === displayValue) { - return; - } - if (suppressOsd) { - return; - } - brightnessChanged(true); - } - - function updateFromBrightnessState(state) { - if (!state || !state.devices) { - return; - } - - const newMaxCache = Object.assign({}, deviceMaxCache); - devices = state.devices.map(d => { - const cachedMax = deviceMaxCache[d.id]; - let displayMax = cachedMax || (d.class === "ddc" ? d.max : 100); - if (displayMax > 0 && !cachedMax) { - newMaxCache[d.id] = displayMax; - } - return { - "id": d.id, - "name": d.id, - "class": d.class, - "current": d.current, - "percentage": d.currentPercent, - "max": d.max, - "backend": d.backend, - "displayMax": displayMax - }; - }); - deviceMaxCache = newMaxCache; - - const newBrightness = {}; - let anyDeviceBrightnessChanged = false; - - for (const device of state.devices) { - const isExponential = SessionData.getBrightnessExponential(device.id); - const userSetValue = deviceBrightnessUserSet[device.id]; - const oldValue = deviceBrightness[device.id]; - - if (isExponential) { - if (userSetValue !== undefined) { - newBrightness[device.id] = userSetValue; - } else { - newBrightness[device.id] = linearToExponential(device.currentPercent, device.id); - } - } else { - newBrightness[device.id] = device.currentPercent; - } - - const newValue = newBrightness[device.id]; - if (oldValue !== undefined && oldValue !== newValue) { - anyDeviceBrightnessChanged = true; - } - } - deviceBrightness = newBrightness; - brightnessVersion++; - - brightnessAvailable = devices.length > 0; - - if (devices.length > 0 && !currentDevice) { - const lastDevice = SessionData.lastBrightnessDevice || ""; - const deviceExists = devices.some(d => d.id === lastDevice); - if (deviceExists) { - setCurrentDevice(lastDevice, false); - } else { - const backlight = devices.find(d => d.class === "backlight"); - const nonKbdDevice = devices.find(d => !d.id.includes("kbd")); - const defaultDevice = backlight || nonKbdDevice || devices[0]; - setCurrentDevice(defaultDevice.id, false); - } - } - - const shouldShowOsd = brightnessInitialized && anyDeviceBrightnessChanged; - - if (!brightnessInitialized) { - brightnessInitialized = true; - } - - if (shouldShowOsd) { - brightnessChanged(true); - } - } - - function setBrightness(percentage, device, suppressOsd) { - const actualDevice = device === "" ? getDefaultDevice() : (device || currentDevice || getDefaultDevice()); - if (!actualDevice) { - console.warn("DisplayService: No device selected for brightness change"); - return; - } - - if (actualDevice !== lastIpcDevice) { - lastIpcDevice = actualDevice; - } - - const deviceInfo = getCurrentDeviceInfoByName(actualDevice); - const isExponential = SessionData.getBrightnessExponential(actualDevice); - - let minValue = 0; - let maxValue = 100; - - switch (true) { - case isExponential: - minValue = 1; - maxValue = 100; - break; - default: - minValue = (deviceInfo && (deviceInfo.class === "backlight" || deviceInfo.class === "ddc")) ? 1 : 0; - maxValue = deviceInfo?.displayMax || 100; - break; - } - - if (maxValue <= 0) { - console.warn("DisplayService: Invalid max value for device", actualDevice, "- skipping brightness change"); - return; - } - - const clampedValue = Math.max(minValue, Math.min(maxValue, percentage)); - - if (!DMSService.isConnected) { - console.warn("DisplayService: Not connected to DMS"); - return; - } - - const isLedDevice = deviceInfo?.class === "leds"; - - if (suppressOsd) { - markDeviceUserControlled(actualDevice); - } else if (!isLedDevice) { - markDevicePendingOsd(actualDevice); - } - - const newBrightness = Object.assign({}, deviceBrightness); - newBrightness[actualDevice] = clampedValue; - deviceBrightness = newBrightness; - brightnessVersion++; - - if (isLedDevice && !suppressOsd) { - brightnessChanged(true); - } - - if (isExponential) { - const newUserSet = Object.assign({}, deviceBrightnessUserSet); - newUserSet[actualDevice] = clampedValue; - deviceBrightnessUserSet = newUserSet; - SessionData.setBrightnessUserSetValue(actualDevice, clampedValue); - } - - const params = { - "device": actualDevice, - "percent": clampedValue - }; - if (isExponential) { - params.exponential = true; - params.exponent = SessionData.getBrightnessExponent(actualDevice); - } - - DMSService.sendRequest("brightness.setBrightness", params, response => { - if (response.error) { - console.error("DisplayService: Failed to set brightness:", response.error); - ToastService.showError(I18n.tr("Failed to set brightness"), response.error, "", "brightness"); - } else { - ToastService.dismissCategory("brightness"); - } - }); - } - - function setCurrentDevice(deviceName, saveToSession = false) { - if (currentDevice === deviceName) { - return; - } - - currentDevice = deviceName; - lastIpcDevice = deviceName; - - if (saveToSession) { - SessionData.setLastBrightnessDevice(deviceName); - } - - deviceSwitched(); - } - - function getDeviceBrightness(deviceName) { - if (!deviceName) { - return 50; - } - - if (deviceName in deviceBrightness) { - return deviceBrightness[deviceName]; - } - - return 50; - } - - function linearToExponential(linearPercent, deviceName) { - const exponent = SessionData.getBrightnessExponent(deviceName); - const hardwarePercent = linearPercent / 100.0; - const normalizedPercent = Math.pow(hardwarePercent, 1.0 / exponent); - return Math.round(normalizedPercent * 100.0); - } - - function getDefaultDevice() { - for (const device of devices) { - if (device.class === "backlight") { - return device.id; - } - } - return devices.length > 0 ? devices[0].id : ""; - } - - function getPinnedDeviceForFocusedScreen() { - const focusedScreen = CompositorService.getFocusedScreen(); - if (!focusedScreen) - return ""; - - const pins = SettingsData.brightnessDevicePins || {}; - const screenKey = SettingsData.getScreenDisplayName(focusedScreen); - if (!screenKey) - return ""; - - const pinnedDevice = pins[screenKey]; - if (!pinnedDevice) - return ""; - - const deviceExists = devices.some(d => d.id === pinnedDevice); - if (!deviceExists) - return ""; - - return pinnedDevice; - } - - function getPreferredDevice() { - const pinned = getPinnedDeviceForFocusedScreen(); - if (pinned) - return pinned; - - return getDefaultDevice(); - } - - function getCurrentDeviceInfo() { - const deviceToUse = lastIpcDevice === "" ? getDefaultDevice() : (lastIpcDevice || currentDevice); - if (!deviceToUse) { - return null; - } - - for (const device of devices) { - if (device.id === deviceToUse) { - return device; - } - } - return null; - } - - function isCurrentDeviceReady() { - const deviceToUse = lastIpcDevice === "" ? getDefaultDevice() : (lastIpcDevice || currentDevice); - return deviceToUse !== ""; - } - - function getCurrentDeviceInfoByName(deviceName) { - if (!deviceName) { - return null; - } - - for (const device of devices) { - if (device.id === deviceName) { - return device; - } - } - return null; - } - - function getDeviceMax(deviceName) { - const deviceInfo = getCurrentDeviceInfoByName(deviceName); - if (!deviceInfo) { - return 100; - } - return deviceInfo.displayMax || 100; - } - - // Night Mode Functions - Simplified - function enableNightMode() { - if (!gammaControlAvailable) { - ToastService.showWarning("Night mode failed: DMS gamma control not available"); - return; - } - - nightModeEnabled = true; - SessionData.setNightModeEnabled(true); - - DMSService.sendRequest("wayland.gamma.setEnabled", { - "enabled": true - }, response => { - if (response.error) { - console.error("DisplayService: Failed to enable gamma control:", response.error); - ToastService.showError(I18n.tr("Failed to enable night mode"), response.error, "", "night-mode"); - nightModeEnabled = false; - SessionData.setNightModeEnabled(false); - return; - } - ToastService.dismissCategory("night-mode"); - - if (SessionData.nightModeAutoEnabled) { - startAutomation(); - } else { - applyNightModeDirectly(); - } - }); - } - - function disableNightMode() { - nightModeEnabled = false; - SessionData.setNightModeEnabled(false); - - if (!gammaControlAvailable) { - return; - } - - DMSService.sendRequest("wayland.gamma.setEnabled", { - "enabled": false - }, response => { - if (response.error) { - console.error("DisplayService: Failed to disable gamma control:", response.error); - ToastService.showError(I18n.tr("Failed to disable night mode"), response.error, "", "night-mode"); - } else { - ToastService.dismissCategory("night-mode"); - } - }); - } - - function toggleNightMode() { - if (nightModeEnabled) { - disableNightMode(); - } else { - enableNightMode(); - } - } - - function applyNightModeDirectly() { - const temperature = SessionData.nightModeTemperature || 4000; - - DMSService.sendRequest("wayland.gamma.setManualTimes", { - "sunrise": null, - "sunset": null - }, response => { - if (response.error) { - console.error("DisplayService: Failed to clear manual times:", response.error); - return; - } - - DMSService.sendRequest("wayland.gamma.setUseIPLocation", { - "use": false - }, response => { - if (response.error) { - console.error("DisplayService: Failed to disable IP location:", response.error); - return; - } - - DMSService.sendRequest("wayland.gamma.setTemperature", { - "low": temperature, - "high": temperature - }, response => { - if (response.error) { - console.error("DisplayService: Failed to set temperature:", response.error); - ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode"); - } else { - ToastService.dismissCategory("night-mode"); - } - }); - }); - }); - } - - function startAutomation() { - if (!automationAvailable) { - return; - } - - const mode = SessionData.nightModeAutoMode || "time"; - - switch (mode) { - case "time": - startTimeBasedMode(); - break; - case "location": - startLocationBasedMode(); - break; - } - } - - function startTimeBasedMode() { - const temperature = SessionData.nightModeTemperature || 4000; - const highTemp = SessionData.nightModeHighTemperature || 6500; - const sunriseHour = SessionData.nightModeEndHour; - const sunriseMinute = SessionData.nightModeEndMinute; - const sunsetHour = SessionData.nightModeStartHour; - const sunsetMinute = SessionData.nightModeStartMinute; - - const sunrise = `${String(sunriseHour).padStart(2, '0')}:${String(sunriseMinute).padStart(2, '0')}`; - const sunset = `${String(sunsetHour).padStart(2, '0')}:${String(sunsetMinute).padStart(2, '0')}`; - - DMSService.sendRequest("wayland.gamma.setUseIPLocation", { - "use": false - }, response => { - if (response.error) { - console.error("DisplayService: Failed to disable IP location:", response.error); - return; - } - - DMSService.sendRequest("wayland.gamma.setTemperature", { - "low": temperature, - "high": highTemp - }, response => { - if (response.error) { - console.error("DisplayService: Failed to set temperature:", response.error); - ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode"); - return; - } - - DMSService.sendRequest("wayland.gamma.setManualTimes", { - "sunrise": sunrise, - "sunset": sunset - }, response => { - if (response.error) { - console.error("DisplayService: Failed to set manual times:", response.error); - ToastService.showError(I18n.tr("Failed to set night mode schedule"), response.error, "", "night-mode"); - } else { - ToastService.dismissCategory("night-mode"); - } - }); - }); - }); - } - - function startLocationBasedMode() { - const temperature = SessionData.nightModeTemperature || 4000; - const highTemp = SessionData.nightModeHighTemperature || 6500; - - DMSService.sendRequest("wayland.gamma.setManualTimes", { - "sunrise": null, - "sunset": null - }, response => { - if (response.error) { - console.error("DisplayService: Failed to clear manual times:", response.error); - return; - } - - DMSService.sendRequest("wayland.gamma.setTemperature", { - "low": temperature, - "high": highTemp - }, response => { - if (response.error) { - console.error("DisplayService: Failed to set temperature:", response.error); - ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode"); - return; - } - - if (SessionData.nightModeUseIPLocation) { - DMSService.sendRequest("wayland.gamma.setUseIPLocation", { - "use": true - }, response => { - if (response.error) { - console.error("DisplayService: Failed to enable IP location:", response.error); - ToastService.showError(I18n.tr("Failed to enable IP location"), response.error, "", "night-mode"); - } else { - ToastService.dismissCategory("night-mode"); - } - }); - } else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) { - DMSService.sendRequest("wayland.gamma.setUseIPLocation", { - "use": false - }, response => { - if (response.error) { - console.error("DisplayService: Failed to disable IP location:", response.error); - return; - } - - DMSService.sendRequest("wayland.gamma.setLocation", { - "latitude": SessionData.latitude, - "longitude": SessionData.longitude - }, response => { - if (response.error) { - console.error("DisplayService: Failed to set location:", response.error); - ToastService.showError(I18n.tr("Failed to set night mode location"), response.error, "", "night-mode"); - } else { - ToastService.dismissCategory("night-mode"); - } - }); - }); - } else { - console.warn("DisplayService: Location mode selected but no coordinates set and IP location disabled"); - } - }); - }); - } - - function setNightModeAutomationMode(mode) { - SessionData.setNightModeAutoMode(mode); - } - - function evaluateNightMode() { - if (!nightModeEnabled) { - return; - } - - if (SessionData.nightModeAutoEnabled) { - restartTimer.nextAction = "automation"; - restartTimer.start(); - } else { - restartTimer.nextAction = "direct"; - restartTimer.start(); - } - } - - function checkGammaControlAvailability() { - if (!DMSService.isConnected) { - return; - } - - if (DMSService.apiVersion < 6) { - gammaControlAvailable = false; - automationAvailable = false; - return; - } - - if (!DMSService.capabilities.includes("gamma")) { - gammaControlAvailable = false; - automationAvailable = false; - return; - } - - DMSService.sendRequest("wayland.gamma.getState", null, response => { - if (response.error) { - gammaControlAvailable = false; - automationAvailable = false; - console.error("DisplayService: Gamma control not available:", response.error); - } else { - gammaControlAvailable = true; - automationAvailable = true; - - if (nightModeEnabled) { - DMSService.sendRequest("wayland.gamma.setEnabled", { - "enabled": true - }, enableResponse => { - if (enableResponse.error) { - console.error("DisplayService: Failed to enable gamma control on startup:", enableResponse.error); - return; - } - - evaluateNightMode(); - }); - } - } - }); - } - - Timer { - id: restartTimer - property string nextAction: "" - interval: 250 - repeat: false - - onTriggered: { - if (nextAction === "automation") { - startAutomation(); - } else if (nextAction === "direct") { - applyNightModeDirectly(); - } - nextAction = ""; - } - } - - function rescanDevices() { - if (!DMSService.isConnected) { - return; - } - - DMSService.sendRequest("brightness.rescan", null, response => { - if (response.error) { - console.error("DisplayService: Failed to rescan brightness devices:", response.error); - } - }); - } - - function updateDeviceBrightnessDisplay(deviceName) { - brightnessVersion++; - brightnessChanged(); - } - - Timer { - id: osdSuppressTimer - interval: 2000 - running: true - onTriggered: suppressOsd = false - } - - Component.onCompleted: { - nightModeEnabled = SessionData.nightModeEnabled; - deviceBrightnessUserSet = Object.assign({}, SessionData.brightnessUserSetValues); - if (DMSService.isConnected) { - checkGammaControlAvailability(); - } - } - - Timer { - id: screenChangeRescanTimer - property int rescanAttempt: 0 - interval: 3000 - repeat: false - onTriggered: { - rescanDevices(); - rescanAttempt++; - if (rescanAttempt < 3) { - interval = rescanAttempt === 1 ? 5000 : 8000; - restart(); - } else { - rescanAttempt = 0; - interval = 3000; - } - } - } - - Connections { - target: Quickshell - - function onScreensChanged() { - screenChangeRescanTimer.rescanAttempt = 0; - screenChangeRescanTimer.interval = 3000; - screenChangeRescanTimer.restart(); - } - } - - Connections { - target: DMSService - - function onConnectionStateChanged() { - if (DMSService.isConnected) { - checkGammaControlAvailability(); - } else { - brightnessAvailable = false; - gammaControlAvailable = false; - automationAvailable = false; - } - } - - function onCapabilitiesReceived() { - checkGammaControlAvailability(); - } - - function onBrightnessStateUpdate(data) { - updateFromBrightnessState(data); - } - - function onBrightnessDeviceUpdate(device) { - updateSingleDevice(device); - } - - function onLoginctlEvent(event) { - if (event.event === "unlock" || event.event === "resume") { - suppressOsd = true; - osdSuppressTimer.restart(); - evaluateNightMode(); - } - } - - function onGammaStateUpdate(data) { - root.gammaState = data; - } - } - - // Session Data Connections - Connections { - target: SessionData - - function onNightModeEnabledChanged() { - nightModeEnabled = SessionData.nightModeEnabled; - evaluateNightMode(); - } - - function onNightModeAutoEnabledChanged() { - evaluateNightMode(); - } - function onNightModeAutoModeChanged() { - evaluateNightMode(); - } - function onNightModeStartHourChanged() { - evaluateNightMode(); - } - function onNightModeStartMinuteChanged() { - evaluateNightMode(); - } - function onNightModeEndHourChanged() { - evaluateNightMode(); - } - function onNightModeEndMinuteChanged() { - evaluateNightMode(); - } - function onNightModeTemperatureChanged() { - evaluateNightMode(); - } - function onNightModeHighTemperatureChanged() { - evaluateNightMode(); - } - function onLatitudeChanged() { - evaluateNightMode(); - } - function onLongitudeChanged() { - evaluateNightMode(); - } - function onNightModeUseIPLocationChanged() { - evaluateNightMode(); - } - } - - // IPC Handler for external control - IpcHandler { - function set(percentage: string, device: string): string { - if (!root.brightnessAvailable) - return "Brightness control not available"; - - const value = parseInt(percentage); - if (isNaN(value)) - return "Invalid brightness value: " + percentage; - - const actualDevice = device || root.getPreferredDevice(); - - if (actualDevice && !root.devices.some(d => d.id === actualDevice)) - return "Device not found: " + actualDevice; - - const deviceInfo = actualDevice ? root.getCurrentDeviceInfoByName(actualDevice) : null; - const minValue = (deviceInfo && (deviceInfo.class === "backlight" || deviceInfo.class === "ddc")) ? 1 : 0; - const clampedValue = Math.max(minValue, Math.min(100, value)); - - root.lastIpcDevice = actualDevice; - if (actualDevice && actualDevice !== root.currentDevice) - root.setCurrentDevice(actualDevice, false); - - root.setBrightness(clampedValue, actualDevice); - - return actualDevice ? "Brightness set to " + clampedValue + "% on " + actualDevice : "Brightness set to " + clampedValue + "%"; - } - - function increment(step: string, device: string): string { - if (!root.brightnessAvailable) - return "Brightness control not available"; - - const actualDevice = device || root.getPreferredDevice(); - - if (actualDevice && !root.devices.some(d => d.id === actualDevice)) - return "Device not found: " + actualDevice; - - const stepValue = parseInt(step || "5"); - - root.lastIpcDevice = actualDevice; - if (actualDevice && actualDevice !== root.currentDevice) - root.setCurrentDevice(actualDevice, false); - - const isExponential = SessionData.getBrightnessExponential(actualDevice); - const currentBrightness = root.getDeviceBrightness(actualDevice); - const deviceInfo = root.getCurrentDeviceInfoByName(actualDevice); - - const maxValue = isExponential ? 100 : (deviceInfo?.displayMax || 100); - const newBrightness = Math.min(maxValue, currentBrightness + stepValue); - - root.setBrightness(newBrightness, actualDevice); - - return "Brightness increased by " + stepValue + "%" + (device ? " on " + actualDevice : ""); - } - - function decrement(step: string, device: string): string { - if (!root.brightnessAvailable) - return "Brightness control not available"; - - const actualDevice = device || root.getPreferredDevice(); - - if (actualDevice && !root.devices.some(d => d.id === actualDevice)) - return "Device not found: " + actualDevice; - - const stepValue = parseInt(step || "5"); - - root.lastIpcDevice = actualDevice; - if (actualDevice && actualDevice !== root.currentDevice) - root.setCurrentDevice(actualDevice, false); - - const isExponential = SessionData.getBrightnessExponential(actualDevice); - const currentBrightness = root.getDeviceBrightness(actualDevice); - const deviceInfo = root.getCurrentDeviceInfoByName(actualDevice); - - let minValue = 0; - switch (true) { - case isExponential: - minValue = 1; - break; - case deviceInfo && (deviceInfo.class === "backlight" || deviceInfo.class === "ddc"): - minValue = 1; - break; - default: - minValue = 0; - break; - } - - const newBrightness = Math.max(minValue, currentBrightness - stepValue); - - root.setBrightness(newBrightness, actualDevice); - - return "Brightness decreased by " + stepValue + "%" + (device ? " on " + actualDevice : ""); - } - - function status(): string { - if (!root.brightnessAvailable) { - return "Brightness control not available"; - } - - return "Device: " + root.currentDevice + " - Brightness: " + root.brightnessLevel + "%"; - } - - function list(): string { - if (!root.brightnessAvailable) { - return "No brightness devices available"; - } - - let result = "Available devices:\n"; - for (const device of root.devices) { - const isExp = SessionData.getBrightnessExponential(device.id); - result += device.id + " (" + device.class + ")" + (isExp ? " [exponential]" : "") + "\n"; - } - return result; - } - - function enableExponential(device: string): string { - const targetDevice = device || root.currentDevice; - if (!targetDevice) { - return "No device specified"; - } - - if (!root.devices.some(d => d.id === targetDevice)) { - return "Device not found: " + targetDevice; - } - - SessionData.setBrightnessExponential(targetDevice, true); - return "Exponential mode enabled for " + targetDevice; - } - - function disableExponential(device: string): string { - const targetDevice = device || root.currentDevice; - if (!targetDevice) { - return "No device specified"; - } - - if (!root.devices.some(d => d.id === targetDevice)) { - return "Device not found: " + targetDevice; - } - - SessionData.setBrightnessExponential(targetDevice, false); - return "Exponential mode disabled for " + targetDevice; - } - - function toggleExponential(device: string): string { - const targetDevice = device || root.currentDevice; - if (!targetDevice) { - return "No device specified"; - } - - if (!root.devices.some(d => d.id === targetDevice)) { - return "Device not found: " + targetDevice; - } - - const currentState = SessionData.getBrightnessExponential(targetDevice); - SessionData.setBrightnessExponential(targetDevice, !currentState); - return "Exponential mode " + (!currentState ? "enabled" : "disabled") + " for " + targetDevice; - } - - target: "brightness" - } - - IpcHandler { - function toggle(): string { - root.toggleNightMode(); - return root.nightModeEnabled ? "Night mode enabled" : "Night mode disabled"; - } - - function enable(): string { - root.enableNightMode(); - return "Night mode enabled"; - } - - function disable(): string { - root.disableNightMode(); - return "Night mode disabled"; - } - - function status(): string { - if (!root.gammaControlAvailable) - return "Night mode: unavailable (no gamma control)"; - - const parts = ["Night mode: " + (root.nightModeEnabled ? "enabled" : "disabled")]; - - if (root.gammaCurrentTemp > 0) - parts.push("Current temperature: " + root.gammaCurrentTemp + "K"); - - parts.push("Target night temperature: " + SessionData.nightModeTemperature + "K"); - - if (SessionData.nightModeAutoEnabled) { - parts.push("Target day temperature: " + SessionData.nightModeHighTemperature + "K"); - parts.push("Automation: " + SessionData.nightModeAutoMode); - parts.push("Period: " + (root.gammaIsDay ? "day" : "night")); - - if (root.gammaNextTransition) - parts.push("Next transition: " + root.gammaNextTransition); - if (root.gammaSunriseTime) - parts.push("Sunrise: " + root.gammaSunriseTime); - if (root.gammaSunsetTime) - parts.push("Sunset: " + root.gammaSunsetTime); - } - - return parts.join("\n"); - } - - function getCurrentTemp(): string { - if (!root.gammaControlAvailable) - return "Gamma control not available"; - if (root.gammaCurrentTemp <= 0) - return "No current temperature reported"; - return root.gammaCurrentTemp.toString(); - } - - function getTargetTemp(): string { - return SessionData.nightModeTemperature.toString(); - } - - function getDayTemp(): string { - return SessionData.nightModeHighTemperature.toString(); - } - - function setTargetTemp(value: string): string { - if (!value) - return "Usage: night setTargetTemp <2500-6000>"; - - const temp = parseInt(value); - if (isNaN(temp)) - return "Invalid temperature: " + value; - if (temp < 2500 || temp > 6000) - return "Temperature must be between 2500K and 6000K"; - - const rounded = Math.round(temp / 500) * 500; - SessionData.setNightModeTemperature(rounded); - - if (root.nightModeEnabled) { - switch (true) { - case SessionData.nightModeAutoEnabled: - root.startAutomation(); - break; - default: - root.applyNightModeDirectly(); - break; - } - } - - if (rounded !== temp) - return "Night temperature set to " + rounded + "K (rounded from " + temp + "K)"; - return "Night temperature set to " + rounded + "K"; - } - - function setDayTemp(value: string): string { - if (!value) - return "Usage: night setDayTemp <2500-6500>"; - - const temp = parseInt(value); - if (isNaN(temp)) - return "Invalid temperature: " + value; - if (temp < 2500 || temp > 6500) - return "Temperature must be between 2500K and 6500K"; - - const rounded = Math.round(temp / 500) * 500; - SessionData.setNightModeHighTemperature(rounded); - - if (root.nightModeEnabled && SessionData.nightModeAutoEnabled) - root.startAutomation(); - - if (rounded !== temp) - return "Day temperature set to " + rounded + "K (rounded from " + temp + "K)"; - return "Day temperature set to " + rounded + "K"; - } - - function getSchedule(): string { - if (!SessionData.nightModeAutoEnabled) - return "Automation disabled"; - - const parts = ["Mode: " + SessionData.nightModeAutoMode]; - parts.push("Period: " + (root.gammaIsDay ? "day" : "night")); - - if (root.gammaDawnTime) - parts.push("Dawn: " + root.gammaDawnTime); - if (root.gammaSunriseTime) - parts.push("Sunrise: " + root.gammaSunriseTime); - if (root.gammaSunsetTime) - parts.push("Sunset: " + root.gammaSunsetTime); - if (root.gammaNightTime) - parts.push("Night: " + root.gammaNightTime); - if (root.gammaNextTransition) - parts.push("Next transition: " + root.gammaNextTransition); - if (root.gammaSunPosition > 0) - parts.push("Sun position: " + root.gammaSunPosition.toFixed(2) + "°"); - - return parts.join("\n"); - } - - target: "night" - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DwlService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DwlService.qml deleted file mode 100644 index 564e561..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/DwlService.qml +++ /dev/null @@ -1,454 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtCore -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) - readonly property string mangoDmsDir: configDir + "/mango/dms" - readonly property string outputsPath: mangoDmsDir + "/outputs.conf" - readonly property string layoutPath: mangoDmsDir + "/layout.conf" - readonly property string cursorPath: mangoDmsDir + "/cursor.conf" - - property int _lastGapValue: -1 - - property bool dwlAvailable: false - property var outputs: ({}) - property var tagCount: 9 - property var layouts: [] - property string activeOutput: "" - property var outputScales: ({}) - property string currentKeyboardLayout: { - if (!outputs || !activeOutput) - return ""; - const output = outputs[activeOutput]; - return (output && output.kbLayout) || ""; - } - - signal stateChanged - - Connections { - target: SettingsData - function onBarConfigsChanged() { - if (!CompositorService.isDwl) - return; - const newGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)); - if (newGaps === root._lastGapValue) - return; - root._lastGapValue = newGaps; - generateLayoutConfig(); - } - } - - Connections { - target: CompositorService - function onIsDwlChanged() { - if (CompositorService.isDwl) - generateLayoutConfig(); - } - } - - Connections { - target: DMSService - function onCapabilitiesReceived() { - checkCapabilities(); - } - function onConnectionStateChanged() { - if (DMSService.isConnected) { - checkCapabilities(); - } else { - dwlAvailable = false; - } - } - function onDwlStateUpdate(data) { - if (dwlAvailable) { - handleStateUpdate(data); - } - } - } - - Component.onCompleted: { - if (DMSService.dmsAvailable) - checkCapabilities(); - if (dwlAvailable) - refreshOutputScales(); - if (CompositorService.isDwl) - Qt.callLater(generateLayoutConfig); - } - - function checkCapabilities() { - if (!DMSService.capabilities || !Array.isArray(DMSService.capabilities)) { - dwlAvailable = false; - return; - } - - const hasDwl = DMSService.capabilities.includes("dwl"); - if (hasDwl && !dwlAvailable) { - dwlAvailable = true; - console.info("DwlService: DWL capability detected"); - requestState(); - refreshOutputScales(); - } else if (!hasDwl) { - dwlAvailable = false; - } - } - - function requestState() { - if (!DMSService.isConnected || !dwlAvailable) { - return; - } - - DMSService.sendRequest("dwl.getState", null, response => { - if (response.result) { - handleStateUpdate(response.result); - } - }); - } - - function handleStateUpdate(state) { - outputs = state.outputs || {}; - tagCount = state.tagCount || 9; - layouts = state.layouts || []; - activeOutput = state.activeOutput || ""; - stateChanged(); - } - - function setTags(outputName, tagmask, toggleTagset) { - if (!DMSService.isConnected || !dwlAvailable) { - return; - } - - DMSService.sendRequest("dwl.setTags", { - "output": outputName, - "tagmask": tagmask, - "toggleTagset": toggleTagset - }, response => { - if (response.error) { - console.warn("DwlService: setTags error:", response.error); - } - }); - } - - function setClientTags(outputName, andTags, xorTags) { - if (!DMSService.isConnected || !dwlAvailable) { - return; - } - - DMSService.sendRequest("dwl.setClientTags", { - "output": outputName, - "andTags": andTags, - "xorTags": xorTags - }, response => { - if (response.error) { - console.warn("DwlService: setClientTags error:", response.error); - } - }); - } - - function setLayout(outputName, index) { - if (!DMSService.isConnected || !dwlAvailable) { - return; - } - - DMSService.sendRequest("dwl.setLayout", { - "output": outputName, - "index": index - }, response => { - if (response.error) { - console.warn("DwlService: setLayout error:", response.error); - } - }); - } - - function getOutputState(outputName) { - if (!outputs || !outputs[outputName]) { - return null; - } - return outputs[outputName]; - } - - function getActiveTags(outputName) { - const output = getOutputState(outputName); - if (!output || !output.tags) { - return []; - } - return output.tags.filter(tag => tag.state === 1).map(tag => tag.tag); - } - - function getTagsWithClients(outputName) { - const output = getOutputState(outputName); - if (!output || !output.tags) { - return []; - } - return output.tags.filter(tag => tag.clients > 0).map(tag => tag.tag); - } - - function getUrgentTags(outputName) { - const output = getOutputState(outputName); - if (!output || !output.tags) { - return []; - } - return output.tags.filter(tag => tag.state === 2).map(tag => tag.tag); - } - - function switchToTag(outputName, tagIndex) { - const tagmask = 1 << tagIndex; - setTags(outputName, tagmask, 0); - } - - function toggleTag(outputName, tagIndex) { - const output = getOutputState(outputName); - if (!output || !output.tags) { - console.log("toggleTag: no output or tags for", outputName); - return; - } - - let currentMask = 0; - output.tags.forEach(tag => { - if (tag.state === 1) { - currentMask |= (1 << tag.tag); - } - }); - - const clickedMask = 1 << tagIndex; - const newMask = currentMask ^ clickedMask; - - console.log("toggleTag:", outputName, "tag:", tagIndex, "currentMask:", currentMask.toString(2), "clickedMask:", clickedMask.toString(2), "newMask:", newMask.toString(2)); - - if (newMask === 0) { - console.log("toggleTag: newMask is 0, switching to tag", tagIndex); - setTags(outputName, 1 << tagIndex, 0); - } else { - console.log("toggleTag: setting combined mask", newMask); - setTags(outputName, newMask, 0); - } - } - - function quit() { - Quickshell.execDetached(["mmsg", "-d", "quit"]); - } - - Process { - id: scaleQueryProcess - command: ["mmsg", "-A"] - running: false - - stdout: StdioCollector { - onStreamFinished: { - try { - const newScales = {}; - const lines = text.trim().split('\n'); - for (const line of lines) { - const parts = line.trim().split(/\s+/); - if (parts.length >= 3 && parts[1] === "scale_factor") { - const outputName = parts[0]; - const scale = parseFloat(parts[2]); - if (!isNaN(scale)) { - newScales[outputName] = scale; - } - } - } - outputScales = newScales; - } catch (e) { - console.warn("DwlService: Failed to parse mmsg output:", e); - } - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - console.warn("DwlService: mmsg failed with exit code:", exitCode); - } - } - } - - function refreshOutputScales() { - if (!dwlAvailable) - return; - scaleQueryProcess.running = true; - } - - function getOutputScale(outputName) { - return outputScales[outputName]; - } - - function getVisibleTags(outputName) { - const output = getOutputState(outputName); - if (!output || !output.tags) { - return []; - } - - const visibleTags = new Set(); - - output.tags.forEach(tag => { - if (tag.state === 1 || tag.clients > 0) { - visibleTags.add(tag.tag); - } - }); - - return Array.from(visibleTags).sort((a, b) => a - b); - } - - function generateOutputsConfig(outputsData) { - if (!outputsData || Object.keys(outputsData).length === 0) - return; - let lines = ["# Auto-generated by DMS - do not edit manually", ""]; - - for (const outputName in outputsData) { - const output = outputsData[outputName]; - if (!output) - continue; - let width = 1920; - let height = 1080; - let refreshRate = 60; - if (output.modes && output.current_mode !== undefined) { - const mode = output.modes[output.current_mode]; - if (mode) { - width = mode.width || 1920; - height = mode.height || 1080; - refreshRate = Math.round((mode.refresh_rate || 60000) / 1000); - } - } - - const x = output.logical?.x ?? 0; - const y = output.logical?.y ?? 0; - const scale = output.logical?.scale ?? 1.0; - const transform = transformToMango(output.logical?.transform ?? "Normal"); - const vrr = output.vrr_enabled ? 1 : 0; - - const rule = ["name:" + outputName, "width:" + width, "height:" + height, "refresh:" + refreshRate, "x:" + x, "y:" + y, "scale:" + scale, "rr:" + transform, "vrr:" + vrr].join(","); - - lines.push("monitorrule=" + rule); - } - - lines.push(""); - - const content = lines.join("\n"); - - Proc.runCommand("mango-write-outputs", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => { - if (exitCode !== 0) { - console.warn("DwlService: Failed to write outputs config:", output); - return; - } - console.info("DwlService: Generated outputs config at", outputsPath); - if (CompositorService.isDwl) - reloadConfig(); - }); - } - - function reloadConfig() { - Proc.runCommand("mango-reload", ["mmsg", "-d", "reload_config"], (output, exitCode) => { - if (exitCode !== 0) - console.warn("DwlService: mmsg reload_config failed:", output); - }); - } - - function generateLayoutConfig() { - if (!CompositorService.isDwl) - return; - - const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12; - const defaultGaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4; - const defaultBorderSize = 2; - - const cornerRadius = (typeof SettingsData !== "undefined" && SettingsData.mangoLayoutRadiusOverride >= 0) ? SettingsData.mangoLayoutRadiusOverride : defaultRadius; - const gaps = (typeof SettingsData !== "undefined" && SettingsData.mangoLayoutGapsOverride >= 0) ? SettingsData.mangoLayoutGapsOverride : defaultGaps; - const borderSize = (typeof SettingsData !== "undefined" && SettingsData.mangoLayoutBorderSize >= 0) ? SettingsData.mangoLayoutBorderSize : defaultBorderSize; - - let content = `# Auto-generated by DMS - do not edit manually -border_radius=${cornerRadius} -gappih=${gaps} -gappiv=${gaps} -gappoh=${gaps} -gappov=${gaps} -borderpx=${borderSize} -`; - - Proc.runCommand("mango-write-layout", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && cat > "${layoutPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => { - if (exitCode !== 0) { - console.warn("DwlService: Failed to write layout config:", output); - return; - } - console.info("DwlService: Generated layout config at", layoutPath); - reloadConfig(); - }); - } - - function transformToMango(transform) { - switch (transform) { - case "Normal": - return 0; - case "90": - return 1; - case "180": - return 2; - case "270": - return 3; - case "Flipped": - return 4; - case "Flipped90": - return 5; - case "Flipped180": - return 6; - case "Flipped270": - return 7; - default: - return 0; - } - } - - function generateCursorConfig() { - if (!CompositorService.isDwl) - return; - - console.log("DwlService: Generating cursor config..."); - - const settings = typeof SettingsData !== "undefined" ? SettingsData.cursorSettings : null; - if (!settings) { - Proc.runCommand("mango-write-cursor", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => { - if (exitCode !== 0) - console.warn("DwlService: Failed to write cursor config:", output); - }); - return; - } - - const themeName = settings.theme === "System Default" ? (SettingsData.systemDefaultCursorTheme || "") : settings.theme; - const size = settings.size || 24; - const hideTimeout = settings.dwl?.cursorHideTimeout || 0; - - const isDefaultConfig = !themeName && size === 24 && hideTimeout === 0; - if (isDefaultConfig) { - Proc.runCommand("mango-write-cursor", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => { - if (exitCode !== 0) - console.warn("DwlService: Failed to write cursor config:", output); - }); - return; - } - - let content = `# Auto-generated by DMS - do not edit manually -cursor_size=${size}`; - - if (themeName) - content += `\ncursor_theme=${themeName}`; - - if (hideTimeout > 0) - content += `\ncursor_hide_timeout=${hideTimeout}`; - - content += `\n`; - - Proc.runCommand("mango-write-cursor", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && cat > "${cursorPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => { - if (exitCode !== 0) { - console.warn("DwlService: Failed to write cursor config:", output); - return; - } - console.info("DwlService: Generated cursor config at", cursorPath); - reloadConfig(); - }); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ExtWorkspaceService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ExtWorkspaceService.qml deleted file mode 100644 index 48a8f9a..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ExtWorkspaceService.qml +++ /dev/null @@ -1,277 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell - -Singleton { - id: root - - property bool extWorkspaceAvailable: false - property var groups: [] - property var _cachedWorkspaces: ({}) - - signal stateChanged - - Connections { - target: DMSService - function onCapabilitiesReceived() { - checkCapabilities(); - } - function onConnectionStateChanged() { - if (DMSService.isConnected) { - checkCapabilities(); - } else { - extWorkspaceAvailable = false; - } - } - function onExtWorkspaceStateUpdate(data) { - if (extWorkspaceAvailable) { - handleStateUpdate(data); - } - } - } - - Component.onCompleted: { - if (DMSService.dmsAvailable) { - checkCapabilities(); - } - } - - function checkCapabilities() { - if (!DMSService.capabilities || !Array.isArray(DMSService.capabilities)) { - extWorkspaceAvailable = false; - return; - } - - const hasExtWorkspace = DMSService.capabilities.includes("extworkspace"); - if (hasExtWorkspace && !extWorkspaceAvailable) { - if (typeof CompositorService !== "undefined") { - const useExtWorkspace = DMSService.forceExtWorkspace || (!CompositorService.isNiri && !CompositorService.isHyprland && !CompositorService.isDwl && !CompositorService.isSway && !CompositorService.isScroll && !CompositorService.isMiracle); - if (!useExtWorkspace) { - console.info("ExtWorkspaceService: ext-workspace available but compositor has native support"); - extWorkspaceAvailable = false; - return; - } - } - extWorkspaceAvailable = true; - console.info("ExtWorkspaceService: ext-workspace capability detected"); - DMSService.addSubscription("extworkspace"); - requestState(); - } else if (!hasExtWorkspace) { - extWorkspaceAvailable = false; - } - } - - function requestState() { - if (!DMSService.isConnected || !extWorkspaceAvailable) { - return; - } - - DMSService.sendRequest("extworkspace.getState", null, response => { - if (response.result) { - handleStateUpdate(response.result); - } - }); - } - - function handleStateUpdate(state) { - groups = state.groups || []; - if (groups.length === 0) { - console.warn("ExtWorkspaceService: Received empty workspace groups from backend"); - } else { - console.log("ExtWorkspaceService: Updated with", groups.length, "workspace groups"); - } - stateChanged(); - } - - function activateWorkspace(workspaceID, groupID = "") { - if (!DMSService.isConnected || !extWorkspaceAvailable) { - return; - } - - DMSService.sendRequest("extworkspace.activateWorkspace", { - "workspaceID": workspaceID, - "groupID": groupID - }, response => { - if (response.error) { - console.warn("ExtWorkspaceService: activateWorkspace error:", response.error); - } - }); - } - - function deactivateWorkspace(workspaceID, groupID = "") { - if (!DMSService.isConnected || !extWorkspaceAvailable) { - return; - } - - DMSService.sendRequest("extworkspace.deactivateWorkspace", { - "workspaceID": workspaceID, - "groupID": groupID - }, response => { - if (response.error) { - console.warn("ExtWorkspaceService: deactivateWorkspace error:", response.error); - } - }); - } - - function removeWorkspace(workspaceID, groupID = "") { - if (!DMSService.isConnected || !extWorkspaceAvailable) { - return; - } - - DMSService.sendRequest("extworkspace.removeWorkspace", { - "workspaceID": workspaceID, - "groupID": groupID - }, response => { - if (response.error) { - console.warn("ExtWorkspaceService: removeWorkspace error:", response.error); - } - }); - } - - function createWorkspace(groupID, name) { - if (!DMSService.isConnected || !extWorkspaceAvailable) { - return; - } - - DMSService.sendRequest("extworkspace.createWorkspace", { - "groupID": groupID, - "name": name - }, response => { - if (response.error) { - console.warn("ExtWorkspaceService: createWorkspace error:", response.error); - } - }); - } - - function getGroupForOutput(outputName) { - for (const group of groups) { - if (group.outputs && group.outputs.includes(outputName)) { - return group; - } - } - return null; - } - - function getWorkspacesForOutput(outputName) { - const group = getGroupForOutput(outputName); - return group ? (group.workspaces || []) : []; - } - - function getActiveWorkspaces() { - const active = []; - for (const group of groups) { - if (!group.workspaces) - continue; - for (const ws of group.workspaces) { - if (ws.active) { - active.push({ - workspace: ws, - group: group, - outputs: group.outputs || [] - }); - } - } - } - return active; - } - - function getActiveWorkspaceForOutput(outputName) { - const group = getGroupForOutput(outputName); - if (!group || !group.workspaces) - return null; - - for (const ws of group.workspaces) { - if (ws.active) { - return ws; - } - } - return null; - } - - function getVisibleWorkspaces(outputName) { - const workspaces = getWorkspacesForOutput(outputName); - let visible = workspaces.filter(ws => !ws.hidden); - - const hasValidCoordinates = visible.some(ws => ws.coordinates && ws.coordinates.length > 0); - if (hasValidCoordinates) { - visible = visible.sort((a, b) => { - const coordsA = a.coordinates || [0, 0]; - const coordsB = b.coordinates || [0, 0]; - if (coordsA[0] !== coordsB[0]) - return coordsA[0] - coordsB[0]; - return coordsA[1] - coordsB[1]; - }); - } - - const cacheKey = outputName; - if (!_cachedWorkspaces[cacheKey]) { - _cachedWorkspaces[cacheKey] = { - workspaces: [], - lastNames: [] - }; - } - - const cache = _cachedWorkspaces[cacheKey]; - const currentNames = visible.map(ws => ws.name || ws.id); - const namesChanged = JSON.stringify(cache.lastNames) !== JSON.stringify(currentNames); - - if (namesChanged || cache.workspaces.length !== visible.length) { - cache.workspaces = visible.map(ws => ({ - id: ws.id, - name: ws.name, - coordinates: ws.coordinates, - state: ws.state, - active: ws.active, - urgent: ws.urgent, - hidden: ws.hidden - })); - cache.lastNames = currentNames; - return cache.workspaces; - } - - for (let i = 0; i < visible.length; i++) { - const src = visible[i]; - const dst = cache.workspaces[i]; - dst.id = src.id; - dst.name = src.name; - dst.coordinates = src.coordinates; - dst.state = src.state; - dst.active = src.active; - dst.urgent = src.urgent; - dst.hidden = src.hidden; - } - - return cache.workspaces; - } - - function getUrgentWorkspaces() { - const urgent = []; - for (const group of groups) { - if (!group.workspaces) - continue; - for (const ws of group.workspaces) { - if (ws.urgent) { - urgent.push({ - workspace: ws, - group: group, - outputs: group.outputs || [] - }); - } - } - } - return urgent; - } - - function switchToWorkspace(outputName, workspaceName) { - const workspaces = getWorkspacesForOutput(outputName); - for (const ws of workspaces) { - if (ws.name === workspaceName || ws.id === workspaceName) { - activateWorkspace(ws.name || ws.id); - return; - } - } - console.warn("ExtWorkspaceService: workspace not found:", workspaceName); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/FirstLaunchService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/FirstLaunchService.qml deleted file mode 100644 index 8b7acd3..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/FirstLaunchService.qml +++ /dev/null @@ -1,111 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtCore -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/DankMaterialShell" - readonly property string settingsPath: configDir + "/settings.json" - readonly property string firstLaunchMarkerPath: configDir + "/.firstlaunch" - - property bool isFirstLaunch: false - property bool checkComplete: false - property bool greeterDismissed: false - property int requestedStartPage: 0 - - readonly property bool shouldShowGreeter: checkComplete && isFirstLaunch && !greeterDismissed - - signal greeterRequested - signal greeterCompleted - - function showGreeter(startPage) { - requestedStartPage = startPage || 0; - greeterRequested(); - } - - function showWelcome() { - showGreeter(0); - } - - function showDoctor() { - showGreeter(1); - } - - Component.onCompleted: { - checkFirstLaunch(); - } - - function checkFirstLaunch() { - firstLaunchCheckProcess.running = true; - } - - function markFirstLaunchComplete() { - greeterDismissed = true; - touchMarkerProcess.running = true; - greeterCompleted(); - } - - function dismissGreeter() { - greeterDismissed = true; - } - - Process { - id: firstLaunchCheckProcess - - command: ["sh", "-c", ` - SETTINGS='` + settingsPath + `' - MARKER='` + firstLaunchMarkerPath + `' - if [ -f "$MARKER" ]; then - echo 'skip' - elif [ -f "$SETTINGS" ]; then - echo 'existing_user' - else - echo 'first' - fi - `] - running: false - - stdout: SplitParser { - onRead: data => { - const result = data.trim(); - - if (result === "first") { - root.isFirstLaunch = true; - console.info("FirstLaunchService: First launch detected, greeter will be shown"); - } else if (result === "existing_user") { - root.isFirstLaunch = false; - console.info("FirstLaunchService: Existing user detected, silently creating marker"); - touchMarkerProcess.running = true; - } else { - root.isFirstLaunch = false; - } - - root.checkComplete = true; - - if (root.isFirstLaunch) - root.greeterRequested(); - } - } - } - - Process { - id: touchMarkerProcess - - command: ["sh", "-c", "mkdir -p '" + configDir + "' && touch '" + firstLaunchMarkerPath + "'"] - running: false - - onExited: exitCode => { - if (exitCode === 0) { - console.info("FirstLaunchService: First launch marker created"); - } else { - console.warn("FirstLaunchService: Failed to create first launch marker"); - } - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/HyprlandService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/HyprlandService.qml deleted file mode 100644 index 20096ef..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/HyprlandService.qml +++ /dev/null @@ -1,338 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtCore -import QtQuick -import Quickshell -import Quickshell.Hyprland -import qs.Common - -Singleton { - id: root - - readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) - readonly property string hyprDmsDir: configDir + "/hypr/dms" - readonly property string outputsPath: hyprDmsDir + "/outputs.conf" - readonly property string layoutPath: hyprDmsDir + "/layout.conf" - readonly property string cursorPath: hyprDmsDir + "/cursor.conf" - readonly property string windowrulesPath: hyprDmsDir + "/windowrules.conf" - - property int _lastGapValue: -1 - - Component.onCompleted: { - if (CompositorService.isHyprland) { - Qt.callLater(generateLayoutConfig); - ensureWindowrulesConfig(); - } - } - - function ensureWindowrulesConfig() { - Proc.runCommand("hypr-ensure-windowrules", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && [ ! -f "${windowrulesPath}" ] && touch "${windowrulesPath}" || true`], (output, exitCode) => { - if (exitCode !== 0) - console.warn("HyprlandService: Failed to ensure windowrules.conf:", output); - }); - } - - Connections { - target: SettingsData - function onBarConfigsChanged() { - if (!CompositorService.isHyprland) - return; - const newGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)); - if (newGaps === root._lastGapValue) - return; - root._lastGapValue = newGaps; - generateLayoutConfig(); - } - } - - Connections { - target: CompositorService - function onIsHyprlandChanged() { - if (CompositorService.isHyprland) - generateLayoutConfig(); - } - } - - function getOutputIdentifier(output, outputName) { - if (SettingsData.displayNameMode === "model" && output.make && output.model) - return "desc:" + output.make + " " + output.model + " " + (output.serial || "Unknown"); - return outputName; - } - - function generateOutputsConfig(outputsData, hyprlandSettings) { - if (!outputsData || Object.keys(outputsData).length === 0) - return; - - const settings = hyprlandSettings || SettingsData.hyprlandOutputSettings; - let lines = ["# Auto-generated by DMS - do not edit manually", ""]; - let monitorv2Blocks = []; - - for (const outputName in outputsData) { - const output = outputsData[outputName]; - if (!output) - continue; - - const identifier = getOutputIdentifier(output, outputName); - const outputSettings = settings[identifier] || {}; - - if (outputSettings.disabled) { - lines.push("monitor = " + identifier + ", disable"); - continue; - } - - let resolution = "preferred"; - if (output.modes && output.current_mode !== undefined) { - const mode = output.modes[output.current_mode]; - if (mode) - resolution = mode.width + "x" + mode.height + "@" + (mode.refresh_rate / 1000).toFixed(3); - } - - const x = output.logical?.x ?? 0; - const y = output.logical?.y ?? 0; - const position = x + "x" + y; - const scale = output.logical?.scale ?? 1.0; - - let monitorLine = "monitor = " + identifier + ", " + resolution + ", " + position + ", " + scale; - - const transform = transformToHyprland(output.logical?.transform ?? "Normal"); - if (transform !== 0) - monitorLine += ", transform, " + transform; - - if (output.vrr_supported) { - const vrrMode = outputSettings.vrrFullscreenOnly ? 2 : (output.vrr_enabled ? 1 : 0); - monitorLine += ", vrr, " + vrrMode; - } - - if (output.mirror && output.mirror.length > 0) - monitorLine += ", mirror, " + output.mirror; - - if (outputSettings.bitdepth && outputSettings.bitdepth !== 8) - monitorLine += ", bitdepth, " + outputSettings.bitdepth; - - if (outputSettings.colorManagement && outputSettings.colorManagement !== "auto") - monitorLine += ", cm, " + outputSettings.colorManagement; - - if (outputSettings.sdrBrightness !== undefined && outputSettings.sdrBrightness !== 1.0) - monitorLine += ", sdrbrightness, " + outputSettings.sdrBrightness; - - if (outputSettings.sdrSaturation !== undefined && outputSettings.sdrSaturation !== 1.0) - monitorLine += ", sdrsaturation, " + outputSettings.sdrSaturation; - - lines.push(monitorLine); - - const needsMonitorv2 = outputSettings.supportsHdr || outputSettings.supportsWideColor || outputSettings.sdrMinLuminance !== undefined || outputSettings.sdrMaxLuminance !== undefined || outputSettings.minLuminance !== undefined || outputSettings.maxLuminance !== undefined || outputSettings.maxAvgLuminance !== undefined; - - if (needsMonitorv2) { - let block = "monitorv2 {\n"; - block += " output = " + identifier + "\n"; - - if (outputSettings.supportsWideColor) - block += " supports_wide_color = true\n"; - if (outputSettings.supportsHdr) - block += " supports_hdr = true\n"; - if (outputSettings.sdrMinLuminance !== undefined) - block += " sdr_min_luminance = " + outputSettings.sdrMinLuminance + "\n"; - if (outputSettings.sdrMaxLuminance !== undefined) - block += " sdr_max_luminance = " + outputSettings.sdrMaxLuminance + "\n"; - if (outputSettings.minLuminance !== undefined) - block += " min_luminance = " + outputSettings.minLuminance + "\n"; - if (outputSettings.maxLuminance !== undefined) - block += " max_luminance = " + outputSettings.maxLuminance + "\n"; - if (outputSettings.maxAvgLuminance !== undefined) - block += " max_avg_luminance = " + outputSettings.maxAvgLuminance + "\n"; - - block += "}"; - monitorv2Blocks.push(block); - } - } - - if (monitorv2Blocks.length > 0) { - lines.push(""); - for (const block of monitorv2Blocks) - lines.push(block); - } - - lines.push(""); - - const content = lines.join("\n"); - - Proc.runCommand("hypr-write-outputs", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => { - if (exitCode !== 0) { - console.warn("HyprlandService: Failed to write outputs config:", output); - return; - } - console.info("HyprlandService: Generated outputs config at", outputsPath); - if (CompositorService.isHyprland) - reloadConfig(); - }); - } - - function reloadConfig() { - Proc.runCommand("hyprctl-reload", ["hyprctl", "reload"], (output, exitCode) => { - if (exitCode !== 0) - console.warn("HyprlandService: hyprctl reload failed:", output); - }); - } - - function generateLayoutConfig() { - if (!CompositorService.isHyprland) - return; - - const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12; - const defaultGaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4; - const defaultBorderSize = 2; - - const cornerRadius = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutRadiusOverride >= 0) ? SettingsData.hyprlandLayoutRadiusOverride : defaultRadius; - const gaps = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutGapsOverride >= 0) ? SettingsData.hyprlandLayoutGapsOverride : defaultGaps; - const borderSize = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutBorderSize >= 0) ? SettingsData.hyprlandLayoutBorderSize : defaultBorderSize; - - let content = `# Auto-generated by DMS - do not edit manually - -general { - gaps_in = ${gaps} - gaps_out = ${gaps} - border_size = ${borderSize} -} - -decoration { - rounding = ${cornerRadius} -} -`; - - Proc.runCommand("hypr-write-layout", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${layoutPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => { - if (exitCode !== 0) { - console.warn("HyprlandService: Failed to write layout config:", output); - return; - } - console.info("HyprlandService: Generated layout config at", layoutPath); - reloadConfig(); - }); - } - - function transformToHyprland(transform) { - switch (transform) { - case "Normal": - return 0; - case "90": - return 1; - case "180": - return 2; - case "270": - return 3; - case "Flipped": - return 4; - case "Flipped90": - return 5; - case "Flipped180": - return 6; - case "Flipped270": - return 7; - default: - return 0; - } - } - - function hyprlandToTransform(value) { - switch (value) { - case 0: - return "Normal"; - case 1: - return "90"; - case 2: - return "180"; - case 3: - return "270"; - case 4: - return "Flipped"; - case 5: - return "Flipped90"; - case 6: - return "Flipped180"; - case 7: - return "Flipped270"; - default: - return "Normal"; - } - } - - function generateCursorConfig() { - if (!CompositorService.isHyprland) - return; - - const settings = typeof SettingsData !== "undefined" ? SettingsData.cursorSettings : null; - if (!settings) { - Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => { - if (exitCode !== 0) - console.warn("HyprlandService: Failed to write cursor config:", output); - }); - return; - } - - const themeName = settings.theme === "System Default" ? (SettingsData.systemDefaultCursorTheme || "") : settings.theme; - const size = settings.size || 24; - const hideOnKeyPress = settings.hyprland?.hideOnKeyPress || false; - const hideOnTouch = settings.hyprland?.hideOnTouch || false; - const inactiveTimeout = settings.hyprland?.inactiveTimeout || 0; - - const hasTheme = themeName && themeName.length > 0; - const hasNonDefaultSize = size !== 24; - const hasCursorSettings = hideOnKeyPress || hideOnTouch || inactiveTimeout > 0; - - if (!hasTheme && !hasNonDefaultSize && !hasCursorSettings) { - Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => { - if (exitCode !== 0) - console.warn("HyprlandService: Failed to write cursor config:", output); - }); - return; - } - - let lines = ["# Auto-generated by DMS - do not edit manually", ""]; - - if (hasTheme) { - lines.push(`env = HYPRCURSOR_THEME,${themeName}`); - lines.push(`env = XCURSOR_THEME,${themeName}`); - } - lines.push(`env = HYPRCURSOR_SIZE,${size}`); - lines.push(`env = XCURSOR_SIZE,${size}`); - - if (hasCursorSettings) { - lines.push(""); - lines.push("cursor {"); - if (hideOnKeyPress) - lines.push(" hide_on_key_press = true"); - if (hideOnTouch) - lines.push(" hide_on_touch = true"); - if (inactiveTimeout > 0) - lines.push(` inactive_timeout = ${inactiveTimeout}`); - lines.push("}"); - } - - lines.push(""); - const content = lines.join("\n"); - - Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${cursorPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => { - if (exitCode !== 0) { - console.warn("HyprlandService: Failed to write cursor config:", output); - return; - } - if (hasTheme) - Proc.runCommand("hyprctl-setcursor", ["hyprctl", "setcursor", themeName, String(size)], () => {}); - reloadConfig(); - }); - } - - function renameWorkspace(newName) { - if (!Hyprland.focusedWorkspace) - return; - const wsId = Hyprland.focusedWorkspace.id; - if (!wsId) - return; - const fullName = wsId + " " + newName; - Proc.runCommand("hyprland-rename-ws", ["hyprctl", "dispatch", "renameworkspace", String(wsId), fullName], (output, exitCode) => { - if (exitCode !== 0) { - console.warn("HyprlandService: Failed to rename workspace:", output); - } - }); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/IdleService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/IdleService.qml deleted file mode 100644 index 9965e47..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/IdleService.qml +++ /dev/null @@ -1,191 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Wayland -import qs.Common - -Singleton { - id: root - - readonly property bool idleMonitorAvailable: { - try { - return typeof IdleMonitor !== "undefined"; - } catch (e) { - return false; - } - } - - readonly property bool idleInhibitorAvailable: { - try { - return typeof IdleInhibitor !== "undefined"; - } catch (e) { - return false; - } - } - - property bool enabled: true - property bool respectInhibitors: true - property bool _enableGate: true - - readonly property bool externalInhibitActive: DMSService.screensaverInhibited - - readonly property bool isOnBattery: BatteryService.batteryAvailable && !BatteryService.isPluggedIn - readonly property int monitorTimeout: isOnBattery ? SettingsData.batteryMonitorTimeout : SettingsData.acMonitorTimeout - readonly property int lockTimeout: isOnBattery ? SettingsData.batteryLockTimeout : SettingsData.acLockTimeout - readonly property int suspendTimeout: isOnBattery ? SettingsData.batterySuspendTimeout : SettingsData.acSuspendTimeout - readonly property int suspendBehavior: isOnBattery ? SettingsData.batterySuspendBehavior : SettingsData.acSuspendBehavior - - readonly property bool mediaPlaying: MprisController.activePlayer !== null && MprisController.activePlayer.isPlaying - - onMonitorTimeoutChanged: _rearmIdleMonitors() - onLockTimeoutChanged: _rearmIdleMonitors() - onSuspendTimeoutChanged: _rearmIdleMonitors() - - function _rearmIdleMonitors() { - _enableGate = false; - Qt.callLater(() => { - _enableGate = true; - }); - } - - signal lockRequested - signal fadeToLockRequested - signal cancelFadeToLock - signal fadeToDpmsRequested - signal cancelFadeToDpms - signal requestMonitorOff - signal requestMonitorOn - signal requestSuspend - - property var monitorOffMonitor: null - property var lockMonitor: null - property var suspendMonitor: null - property var lockComponent: null - property bool monitorsOff: false - property bool isShellLocked: false - - function wake() { - requestMonitorOn(); - } - - function reapplyDpmsIfNeeded() { - if (monitorsOff) - CompositorService.powerOffMonitors(); - } - - function createIdleMonitors() { - if (!idleMonitorAvailable) { - console.info("IdleService: IdleMonitor not available, skipping creation"); - return; - } - - try { - const qmlString = ` - import QtQuick - import Quickshell.Wayland - - IdleMonitor { - enabled: false - respectInhibitors: true - timeout: 0 - } - `; - - monitorOffMonitor = Qt.createQmlObject(qmlString, root, "IdleService.MonitorOffMonitor"); - monitorOffMonitor.timeout = Qt.binding(() => root.monitorTimeout > 0 ? root.monitorTimeout : 86400); - monitorOffMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors); - monitorOffMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.monitorTimeout > 0); - monitorOffMonitor.isIdleChanged.connect(function () { - if (monitorOffMonitor.isIdle) { - if (SettingsData.fadeToDpmsEnabled) { - root.fadeToDpmsRequested(); - } else { - root.requestMonitorOff(); - } - } else { - if (SettingsData.fadeToDpmsEnabled) { - root.cancelFadeToDpms(); - } - root.requestMonitorOn(); - } - }); - - lockMonitor = Qt.createQmlObject(qmlString, root, "IdleService.LockMonitor"); - lockMonitor.timeout = Qt.binding(() => root.lockTimeout > 0 ? root.lockTimeout : 86400); - lockMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors); - lockMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.lockTimeout > 0); - lockMonitor.isIdleChanged.connect(function () { - if (lockMonitor.isIdle) { - if (SettingsData.fadeToLockEnabled) { - root.fadeToLockRequested(); - } else { - root.lockRequested(); - } - } else { - if (SettingsData.fadeToLockEnabled) { - root.cancelFadeToLock(); - } - } - }); - - suspendMonitor = Qt.createQmlObject(qmlString, root, "IdleService.SuspendMonitor"); - suspendMonitor.timeout = Qt.binding(() => root.suspendTimeout > 0 ? root.suspendTimeout : 86400); - suspendMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors); - suspendMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.suspendTimeout > 0); - suspendMonitor.isIdleChanged.connect(function () { - if (suspendMonitor.isIdle) { - root.requestSuspend(); - } - }); - } catch (e) { - console.warn("IdleService: Error creating IdleMonitors:", e); - } - } - - Connections { - target: root - function onRequestMonitorOff() { - monitorsOff = true; - CompositorService.powerOffMonitors(); - } - - function onRequestMonitorOn() { - monitorsOff = false; - CompositorService.powerOnMonitors(); - } - - function onRequestSuspend() { - SessionService.suspendWithBehavior(root.suspendBehavior); - } - } - - onExternalInhibitActiveChanged: { - if (externalInhibitActive) { - const apps = DMSService.screensaverInhibitors.map(i => i.appName).join(", "); - console.info("IdleService: External idle inhibit active from:", apps || "unknown"); - SessionService.idleInhibited = true; - SessionService.inhibitReason = "External app: " + (apps || "unknown"); - } else { - console.info("IdleService: External idle inhibit released"); - SessionService.idleInhibited = false; - SessionService.inhibitReason = "Keep system awake"; - } - } - - Component.onCompleted: { - if (!idleMonitorAvailable) { - console.warn("IdleService: IdleMonitor not available - power management disabled. This requires a newer version of Quickshell."); - } else { - console.info("IdleService: Initialized with idle monitoring support"); - createIdleMonitors(); - } - - if (externalInhibitActive) { - const apps = DMSService.screensaverInhibitors.map(i => i.appName).join(", "); - SessionService.idleInhibited = true; - SessionService.inhibitReason = "External app: " + (apps || "unknown"); - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/KeybindsService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/KeybindsService.qml deleted file mode 100644 index 470831b..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/KeybindsService.qml +++ /dev/null @@ -1,528 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtCore -import QtQuick -import Quickshell -import Quickshell.Io -import Quickshell.Wayland -// ! Even though qmlls says this is unused, it is wrong -import qs.Common -import "../Common/KeybindActions.js" as Actions - -Singleton { - id: root - - Component.onCompleted: { - if (!shortcutInhibitorAvailable) { - console.warn("[KeybindsService] ShortcutInhibitor is not available in this environment, keybinds editor disabled."); - } - } - - readonly property bool shortcutInhibitorAvailable: { - try { - return typeof ShortcutInhibitor !== "undefined"; - } catch (e) { - return false; - } - } - - property bool available: (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl) && shortcutInhibitorAvailable - property string currentProvider: { - if (CompositorService.isNiri) - return "niri"; - if (CompositorService.isHyprland) - return "hyprland"; - if (CompositorService.isDwl) - return "mangowc"; - return ""; - } - - readonly property string cheatsheetProvider: { - if (CompositorService.isNiri) - return "niri"; - if (CompositorService.isHyprland) - return "hyprland"; - if (CompositorService.isDwl) - return "mangowc"; - return ""; - } - property bool cheatsheetAvailable: cheatsheetProvider !== "" - property bool cheatsheetLoading: false - property var cheatsheet: ({}) - - property bool loading: false - property bool saving: false - property bool fixing: false - property string lastError: "" - property bool dmsBindsIncluded: true - - property var dmsStatus: ({ - "exists": true, - "included": true, - "includePosition": -1, - "totalIncludes": 0, - "bindsAfterDms": 0, - "effective": true, - "overriddenBy": 0, - "statusMessage": "" - }) - - property var _rawData: null - property var keybinds: ({}) - property var _allBinds: ({}) - property var _categories: [] - property var _flatCache: [] - property var displayList: [] - property int _dataVersion: 0 - property string _pendingSavedKey: "" - - readonly property var categoryOrder: Actions.getCategoryOrder() - readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) - readonly property string compositorConfigDir: { - switch (currentProvider) { - case "niri": - return configDir + "/niri"; - case "hyprland": - return configDir + "/hypr"; - case "mangowc": - return configDir + "/mango"; - default: - return ""; - } - } - readonly property string dmsBindsPath: { - switch (currentProvider) { - case "niri": - return compositorConfigDir + "/dms/binds.kdl"; - case "hyprland": - case "mangowc": - return compositorConfigDir + "/dms/binds.conf"; - default: - return ""; - } - } - readonly property string mainConfigPath: { - switch (currentProvider) { - case "niri": - return compositorConfigDir + "/config.kdl"; - case "hyprland": - return compositorConfigDir + "/hyprland.conf"; - case "mangowc": - return compositorConfigDir + "/config.conf"; - default: - return ""; - } - } - readonly property var actionTypes: Actions.getActionTypes() - readonly property var dmsActions: getDmsActions() - - signal bindsLoaded - signal bindSaved(string key) - signal bindSaveCompleted(bool success) - signal bindRemoved(string key) - signal dmsBindsFixed - signal cheatsheetLoaded - - Connections { - target: CompositorService - function onCompositorChanged() { - if (!CompositorService.isNiri) - return; - Qt.callLater(root.loadBinds); - } - } - - Connections { - target: NiriService - enabled: CompositorService.isNiri - function onConfigReloaded() { - Qt.callLater(root.loadBinds, false); - } - } - - Process { - id: cheatsheetProcess - running: false - - stdout: StdioCollector { - onStreamFinished: { - try { - root.cheatsheet = JSON.parse(text); - } catch (e) { - console.error("[KeybindsService] Failed to parse cheatsheet:", e); - root.cheatsheet = {}; - } - root.cheatsheetLoading = false; - root.cheatsheetLoaded(); - } - } - - onExited: exitCode => { - if (exitCode === 0) - return; - console.warn("[KeybindsService] Cheatsheet load failed with code:", exitCode); - root.cheatsheetLoading = false; - } - } - - Process { - id: loadProcess - running: false - - stdout: StdioCollector { - onStreamFinished: { - try { - root._rawData = JSON.parse(text); - root._processData(); - } catch (e) { - console.error("[KeybindsService] Failed to parse binds:", e); - } - root.loading = false; - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - console.warn("[KeybindsService] Load process failed with code:", exitCode); - root.loading = false; - } - } - } - - Process { - id: saveProcess - running: false - - stderr: StdioCollector { - onStreamFinished: { - if (!text.trim()) - return; - root.lastError = text.trim(); - ToastService.showError(I18n.tr("Failed to save keybind"), "", root.lastError, "keybinds"); - } - } - - onExited: exitCode => { - root.saving = false; - if (exitCode !== 0) { - console.error("[KeybindsService] Save failed with code:", exitCode); - root.bindSaveCompleted(false); - return; - } - root.lastError = ""; - root.bindSaveCompleted(true); - root.loadBinds(false); - } - } - - Process { - id: removeProcess - running: false - - stderr: StdioCollector { - onStreamFinished: { - if (!text.trim()) - return; - root.lastError = text.trim(); - ToastService.showError(I18n.tr("Failed to remove keybind"), "", root.lastError, "keybinds"); - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - console.error("[KeybindsService] Remove failed with code:", exitCode); - return; - } - root.lastError = ""; - root.loadBinds(false); - } - } - - Process { - id: fixProcess - running: false - - stderr: StdioCollector { - onStreamFinished: { - if (!text.trim()) - return; - root.lastError = text.trim(); - ToastService.showError(I18n.tr("Failed to add binds include"), "", root.lastError, "keybinds"); - } - } - - onExited: exitCode => { - root.fixing = false; - if (exitCode !== 0) { - console.error("[KeybindsService] Fix failed with code:", exitCode); - return; - } - root.lastError = ""; - root.dmsBindsIncluded = true; - root.dmsBindsFixed(); - const bindsFile = root.currentProvider === "niri" ? "dms/binds.kdl" : "dms/binds.conf"; - ToastService.showInfo(I18n.tr("Binds include added"), I18n.tr("%1 is now included in config").arg(bindsFile), "", "keybinds"); - Qt.callLater(root.forceReload); - } - } - - function fixDmsBindsInclude() { - if (fixing || dmsBindsIncluded || !compositorConfigDir) - return; - fixing = true; - const timestamp = Math.floor(Date.now() / 1000); - const backupPath = `${mainConfigPath}.dmsbackup${timestamp}`; - let script; - switch (currentProvider) { - case "niri": - script = `mkdir -p "${compositorConfigDir}/dms" && touch "${compositorConfigDir}/dms/binds.kdl" && cp "${mainConfigPath}" "${backupPath}" && echo 'include "dms/binds.kdl"' >> "${mainConfigPath}"`; - break; - case "hyprland": - script = `mkdir -p "${compositorConfigDir}/dms" && touch "${compositorConfigDir}/dms/binds.conf" && cp "${mainConfigPath}" "${backupPath}" && echo 'source = ./dms/binds.conf' >> "${mainConfigPath}"`; - break; - case "mangowc": - script = `mkdir -p "${compositorConfigDir}/dms" && touch "${compositorConfigDir}/dms/binds.conf" && cp "${mainConfigPath}" "${backupPath}" && echo 'source = ./dms/binds.conf' >> "${mainConfigPath}"`; - break; - default: - fixing = false; - return; - } - fixProcess.command = ["sh", "-c", script]; - fixProcess.running = true; - } - - function forceReload() { - _allBinds = {}; - _flatCache = []; - _categories = []; - loadBinds(true); - } - - function loadCheatsheet(provider) { - if (cheatsheetProcess.running) - return; - const target = provider || cheatsheetProvider; - if (!target) - return; - cheatsheetLoading = true; - cheatsheetProcess.command = ["dms", "keybinds", "show", target]; - cheatsheetProcess.running = true; - } - - function loadBinds(showLoading) { - if (loadProcess.running || !available) - return; - const hasData = Object.keys(_allBinds).length > 0; - loading = showLoading !== false && !hasData; - loadProcess.command = ["dms", "keybinds", "show", currentProvider]; - loadProcess.running = true; - } - - function _processData() { - keybinds = _rawData || {}; - dmsBindsIncluded = _rawData?.dmsBindsIncluded ?? true; - const status = _rawData?.dmsStatus; - if (status) { - dmsStatus = { - "exists": status.exists ?? true, - "included": status.included ?? true, - "includePosition": status.includePosition ?? -1, - "totalIncludes": status.totalIncludes ?? 0, - "bindsAfterDms": status.bindsAfterDms ?? 0, - "effective": status.effective ?? true, - "overriddenBy": status.overriddenBy ?? 0, - "statusMessage": status.statusMessage ?? "" - }; - } - - if (!_rawData?.binds) { - _allBinds = {}; - _categories = []; - _flatCache = []; - displayList = []; - _dataVersion++; - bindsLoaded(); - if (_pendingSavedKey) { - bindSaved(_pendingSavedKey); - _pendingSavedKey = ""; - } - return; - } - - const processed = {}; - const bindsData = _rawData.binds; - for (const cat in bindsData) { - const binds = bindsData[cat]; - for (var i = 0; i < binds.length; i++) { - const bind = binds[i]; - const targetCat = Actions.isDmsAction(bind.action) ? "DMS" : cat; - if (!processed[targetCat]) - processed[targetCat] = []; - processed[targetCat].push(bind); - } - } - - const sortedCats = Object.keys(processed).sort((a, b) => { - const ai = categoryOrder.indexOf(a); - const bi = categoryOrder.indexOf(b); - return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi); - }); - - const grouped = []; - const actionMap = {}; - for (var ci = 0; ci < sortedCats.length; ci++) { - const category = sortedCats[ci]; - const binds = processed[category]; - if (!binds) - continue; - for (var i = 0; i < binds.length; i++) { - const bind = binds[i]; - const action = bind.action || ""; - const keyData = { - "key": bind.key || "", - "source": bind.source || "config", - "isOverride": bind.source === "dms", - "cooldownMs": bind.cooldownMs || 0, - "flags": bind.flags || "", - "allowWhenLocked": bind.allowWhenLocked || false, - "allowInhibiting": bind.allowInhibiting, - "repeat": bind.repeat - }; - if (actionMap[action]) { - actionMap[action].keys.push(keyData); - if (!actionMap[action].desc && bind.desc) - actionMap[action].desc = bind.desc; - if (!actionMap[action].conflict && bind.conflict) - actionMap[action].conflict = bind.conflict; - } else { - const entry = { - "category": category, - "action": action, - "desc": bind.desc || "", - "keys": [keyData], - "conflict": bind.conflict || null - }; - actionMap[action] = entry; - grouped.push(entry); - } - } - } - - const list = []; - for (const cat of sortedCats) { - list.push({ - "id": "cat:" + cat, - "type": "category", - "name": cat - }); - const binds = processed[cat]; - if (!binds) - continue; - for (const bind of binds) - list.push({ - "id": "bind:" + bind.key, - "type": "bind", - "key": bind.key, - "desc": bind.desc - }); - } - - _allBinds = processed; - _categories = sortedCats; - _flatCache = grouped; - displayList = list; - _dataVersion++; - bindsLoaded(); - if (_pendingSavedKey) { - bindSaved(_pendingSavedKey); - _pendingSavedKey = ""; - } - } - - function getCategories() { - return _categories; - } - - function getFlatBinds() { - return _flatCache; - } - - function saveBind(originalKey, bindData) { - if (!bindData.key || !Actions.isValidAction(bindData.action)) - return; - saving = true; - const cmd = ["dms", "keybinds", "set", currentProvider, bindData.key, bindData.action, "--desc", bindData.desc || ""]; - if (originalKey && originalKey !== bindData.key) - cmd.push("--replace-key", originalKey); - if (bindData.cooldownMs > 0) - cmd.push("--cooldown-ms", String(bindData.cooldownMs)); - if (bindData.allowWhenLocked) - cmd.push("--allow-when-locked"); - if (bindData.repeat === false) - cmd.push("--no-repeat"); - if (bindData.allowInhibiting === false) - cmd.push("--no-inhibiting"); - if (bindData.flags) - cmd.push("--flags", bindData.flags); - saveProcess.command = cmd; - saveProcess.running = true; - _pendingSavedKey = bindData.key; - } - - function removeBind(key) { - if (!key) - return; - removeProcess.command = ["dms", "keybinds", "remove", currentProvider, key]; - removeProcess.running = true; - bindRemoved(key); - } - - function isDmsAction(action) { - return Actions.isDmsAction(action); - } - - function isValidAction(action) { - return Actions.isValidAction(action); - } - - function getActionType(action) { - return Actions.getActionType(action); - } - - function getActionLabel(action) { - return Actions.getActionLabel(action, currentProvider); - } - - function getCompositorCategories() { - return Actions.getCompositorCategories(currentProvider); - } - - function getCompositorActions(category) { - return Actions.getCompositorActions(currentProvider, category); - } - - function getDmsActions() { - return Actions.getDmsActions(CompositorService.isNiri, CompositorService.isHyprland); - } - - function buildSpawnAction(command, args) { - return Actions.buildSpawnAction(command, args); - } - - function buildShellAction(shellCmd, shell) { - return Actions.buildShellAction(currentProvider, shellCmd, shell); - } - - function getShellFromAction(action) { - return Actions.getShellFromAction(action); - } - - function parseSpawnCommand(action) { - return Actions.parseSpawnCommand(action); - } - - function parseShellCommand(action) { - return Actions.parseShellCommand(action); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/LegacyNetworkService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/LegacyNetworkService.qml deleted file mode 100644 index 97d8745..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/LegacyNetworkService.qml +++ /dev/null @@ -1,987 +0,0 @@ -pragma Singleton - -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property bool isActive: false - property string networkStatus: "disconnected" - property string primaryConnection: "" - - property string ethernetIP: "" - property string ethernetInterface: "" - property bool ethernetConnected: false - property string ethernetConnectionUuid: "" - - property var wiredConnections: [] - - property string wifiIP: "" - property string wifiInterface: "" - property bool wifiConnected: false - property bool wifiEnabled: true - property string wifiConnectionUuid: "" - property string wifiDevicePath: "" - property string activeAccessPointPath: "" - - property string currentWifiSSID: "" - property int wifiSignalStrength: 0 - property var wifiNetworks: [] - property var savedConnections: [] - property var ssidToConnectionName: { - - } - property var wifiSignalIcon: { - if (!wifiConnected || networkStatus !== "wifi") { - return "wifi_off" - } - if (wifiSignalStrength >= 50) { - return "wifi" - } - if (wifiSignalStrength >= 25) { - return "wifi_2_bar" - } - return "wifi_1_bar" - } - - property string userPreference: "auto" // "auto", "wifi", "ethernet" - property bool isConnecting: false - property string connectingSSID: "" - property string connectionError: "" - - property bool isScanning: false - property bool wifiAvailable: true - property bool wifiToggling: false - property bool changingPreference: false - property string targetPreference: "" - property var savedWifiNetworks: [] - property string connectionStatus: "" - property string lastConnectionError: "" - property bool passwordDialogShouldReopen: false - property string wifiPassword: "" - property string forgetSSID: "" - - readonly property var lowPriorityCmd: ["nice", "-n", "19", "ionice", "-c3"] - - property string networkInfoSSID: "" - property string networkInfoDetails: "" - property bool networkInfoLoading: false - - property string networkWiredInfoUUID: "" - property string networkWiredInfoDetails: "" - property bool networkWiredInfoLoading: false - - signal networksUpdated - signal connectionChanged - - function splitNmcliFields(line) { - const parts = [] - let cur = "" - let escape = false - for (var i = 0; i < line.length; i++) { - const ch = line[i] - if (escape) { - cur += ch - escape = false - } else if (ch === '\\') { - escape = true - } else if (ch === ':') { - parts.push(cur) - cur = "" - } else { - cur += ch - } - } - parts.push(cur) - return parts - } - - Component.onCompleted: { - root.userPreference = SettingsData.networkPreference - } - - function activate() { - if (!isActive) { - isActive = true - console.info("LegacyNetworkService: Activating...") - doRefreshNetworkState() - } - } - - - function doRefreshNetworkState() { - updatePrimaryConnection() - updateDeviceStates() - updateActiveConnections() - updateWifiState() - } - - function updatePrimaryConnection() { - primaryConnectionQuery.running = true - } - - Process { - id: primaryConnectionQuery - command: lowPriorityCmd.concat(["gdbus", "call", "--system", "--dest", "org.freedesktop.NetworkManager", "--object-path", "/org/freedesktop/NetworkManager", "--method", "org.freedesktop.DBus.Properties.Get", "org.freedesktop.NetworkManager", "PrimaryConnection"]) - running: false - - stdout: StdioCollector { - onStreamFinished: { - const match = text.match(/objectpath '([^']+)'/) - if (match && match[1] !== '/') { - root.primaryConnection = match[1] - getPrimaryConnectionType.running = true - } else { - root.primaryConnection = "" - root.networkStatus = "disconnected" - } - } - } - } - - Process { - id: getPrimaryConnectionType - command: root.primaryConnection ? lowPriorityCmd.concat(["gdbus", "call", "--system", "--dest", "org.freedesktop.NetworkManager", "--object-path", root.primaryConnection, "--method", "org.freedesktop.DBus.Properties.Get", "org.freedesktop.NetworkManager.Connection.Active", "Type"]) : [] - running: false - - stdout: StdioCollector { - onStreamFinished: { - if (text.includes("802-3-ethernet")) { - root.networkStatus = "ethernet" - } else if (text.includes("802-11-wireless")) { - root.networkStatus = "wifi" - } - root.connectionChanged() - } - } - } - - function updateDeviceStates() { - getEthernetDevice.running = true - getWifiDevice.running = true - } - - Process { - id: getEthernetDevice - command: lowPriorityCmd.concat(["nmcli", "-t", "-f", "DEVICE,TYPE", "device"]) - running: false - - stdout: StdioCollector { - onStreamFinished: { - const lines = text.trim().split('\n') - let ethernetInterface = "" - - for (const line of lines) { - const splitParts = line.split(':') - const device = splitParts[0] - const type = splitParts.length > 1 ? splitParts[1] : "" - if (type === "ethernet") { - ethernetInterface = device - break - } - } - - if (ethernetInterface) { - root.ethernetInterface = ethernetInterface - getEthernetDevicePath.command = lowPriorityCmd.concat(["gdbus", "call", "--system", "--dest", "org.freedesktop.NetworkManager", "--object-path", "/org/freedesktop/NetworkManager", "--method", "org.freedesktop.NetworkManager.GetDeviceByIpIface", ethernetInterface]) - getEthernetDevicePath.running = true - } else { - root.ethernetInterface = "" - root.ethernetConnected = false - } - } - } - } - - Process { - id: getEthernetDevicePath - running: false - - stdout: StdioCollector { - onStreamFinished: { - const match = text.match(/objectpath '([^']+)'/) - if (match && match[1] !== '/') { - checkEthernetState.command = lowPriorityCmd.concat(["gdbus", "call", "--system", "--dest", "org.freedesktop.NetworkManager", "--object-path", match[1], "--method", "org.freedesktop.DBus.Properties.Get", "org.freedesktop.NetworkManager.Device", "State"]) - checkEthernetState.running = true - } else { - root.ethernetInterface = "" - root.ethernetConnected = false - } - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - root.ethernetInterface = "" - root.ethernetConnected = false - } - } - } - - Process { - id: checkEthernetState - running: false - - stdout: StdioCollector { - onStreamFinished: { - const isConnected = text.includes("uint32 100") - root.ethernetConnected = isConnected - if (isConnected) { - getEthernetIP.running = true - } else { - root.ethernetIP = "" - } - } - } - } - - Process { - id: getEthernetIP - command: root.ethernetInterface ? lowPriorityCmd.concat(["ip", "-4", "addr", "show", root.ethernetInterface]) : [] - running: false - - stdout: StdioCollector { - onStreamFinished: { - const match = text.match(/inet (\d+\.\d+\.\d+\.\d+)/) - if (match) { - root.ethernetIP = match[1] - } - } - } - } - - Process { - id: getWifiDevice - command: lowPriorityCmd.concat(["nmcli", "-t", "-f", "DEVICE,TYPE", "device"]) - running: false - - stdout: StdioCollector { - onStreamFinished: { - const lines = text.trim().split('\n') - let wifiInterface = "" - - for (const line of lines) { - const splitParts = line.split(':') - const device = splitParts[0] - const type = splitParts.length > 1 ? splitParts[1] : "" - if (type === "wifi") { - wifiInterface = device - break - } - } - - if (wifiInterface) { - root.wifiInterface = wifiInterface - getWifiDevicePath.command = lowPriorityCmd.concat(["gdbus", "call", "--system", "--dest", "org.freedesktop.NetworkManager", "--object-path", "/org/freedesktop/NetworkManager", "--method", "org.freedesktop.NetworkManager.GetDeviceByIpIface", wifiInterface]) - getWifiDevicePath.running = true - } else { - root.wifiInterface = "" - root.wifiConnected = false - } - } - } - } - - Process { - id: getWifiDevicePath - running: false - - stdout: StdioCollector { - onStreamFinished: { - const match = text.match(/objectpath '([^']+)'/) - if (match && match[1] !== '/') { - root.wifiDevicePath = match[1] - checkWifiState.command = lowPriorityCmd.concat(["gdbus", "call", "--system", "--dest", "org.freedesktop.NetworkManager", "--object-path", match[1], "--method", "org.freedesktop.DBus.Properties.Get", "org.freedesktop.NetworkManager.Device", "State"]) - checkWifiState.running = true - } else { - root.wifiInterface = "" - root.wifiConnected = false - root.wifiDevicePath = "" - root.activeAccessPointPath = "" - } - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - root.wifiInterface = "" - root.wifiConnected = false - } - } - } - - Process { - id: checkWifiState - running: false - - stdout: StdioCollector { - onStreamFinished: { - root.wifiConnected = text.includes("uint32 100") - if (root.wifiConnected) { - getWifiIP.running = true - getCurrentWifiInfo.running = true - getActiveAccessPoint.running = true - if (root.currentWifiSSID === "") { - if (root.wifiConnectionUuid) { - resolveWifiSSID.running = true - } - if (root.wifiInterface) { - resolveWifiSSIDFromDevice.running = true - } - } - } else { - root.wifiIP = "" - root.currentWifiSSID = "" - root.wifiSignalStrength = 0 - root.activeAccessPointPath = "" - } - } - } - } - - Process { - id: getWifiIP - command: root.wifiInterface ? lowPriorityCmd.concat(["ip", "-4", "addr", "show", root.wifiInterface]) : [] - running: false - - stdout: StdioCollector { - onStreamFinished: { - const match = text.match(/inet (\d+\.\d+\.\d+\.\d+)/) - if (match) { - root.wifiIP = match[1] - } - } - } - } - - Process { - id: getActiveAccessPoint - command: root.wifiDevicePath ? lowPriorityCmd.concat(["gdbus", "call", "--system", "--dest", "org.freedesktop.NetworkManager", "--object-path", root.wifiDevicePath, "--method", "org.freedesktop.DBus.Properties.Get", "org.freedesktop.NetworkManager.Device.Wireless", "ActiveAccessPoint"]) : [] - running: false - - stdout: StdioCollector { - onStreamFinished: { - const match = text.match(/objectpath '([^']+)'/) - if (match && match[1] !== '/') { - root.activeAccessPointPath = match[1] - } else { - root.activeAccessPointPath = "" - } - } - } - } - - Process { - id: getCurrentWifiInfo - command: root.wifiInterface ? lowPriorityCmd.concat(["nmcli", "-t", "-f", "ACTIVE,SIGNAL,SSID", "device", "wifi", "list", "ifname", root.wifiInterface, "--rescan", "no"]) : [] - running: false - - stdout: SplitParser { - splitMarker: "\n" - onRead: line => { - if (line.startsWith("yes:")) { - const rest = line.substring(4) - const parts = root.splitNmcliFields(rest) - if (parts.length >= 2) { - const signal = parseInt(parts[0]) - console.log("Current WiFi signal strength:", signal) - root.wifiSignalStrength = isNaN(signal) ? 0 : signal - root.currentWifiSSID = parts[1] - console.log("Current WiFi SSID:", root.currentWifiSSID) - } - return - } - } - } - } - - function updateActiveConnections() { - getActiveConnections.running = true - } - - Process { - id: getActiveConnections - command: lowPriorityCmd.concat(["nmcli", "-t", "-f", "UUID,TYPE,DEVICE,STATE", "connection", "show", "--active"]) - running: false - - stdout: StdioCollector { - onStreamFinished: { - const lines = text.trim().split('\n') - for (const line of lines) { - const parts = line.split(':') - if (parts.length >= 4) { - const uuid = parts[0] - const type = parts[1] - const device = parts[2] - const state = parts[3] - if (type === "802-3-ethernet" && state === "activated") { - root.ethernetConnectionUuid = uuid - } else if (type === "802-11-wireless" && state === "activated") { - root.wifiConnectionUuid = uuid - } - } - } - } - } - } - - // Resolve SSID from active WiFi connection UUID when scans don't mark any row as ACTIVE. - Process { - id: resolveWifiSSID - command: root.wifiConnectionUuid ? lowPriorityCmd.concat(["nmcli", "-g", "802-11-wireless.ssid", "connection", "show", "uuid", root.wifiConnectionUuid]) : [] - running: false - - stdout: StdioCollector { - onStreamFinished: { - const ssid = text.trim() - if (ssid) { - root.currentWifiSSID = ssid - } - } - } - } - - // Fallback 2: Resolve SSID from device info (GENERAL.CONNECTION usually matches SSID for WiFi) - Process { - id: resolveWifiSSIDFromDevice - command: root.wifiInterface ? lowPriorityCmd.concat(["nmcli", "-t", "-f", "GENERAL.CONNECTION", "device", "show", root.wifiInterface]) : [] - running: false - - stdout: StdioCollector { - onStreamFinished: { - if (!root.currentWifiSSID) { - const name = text.trim() - if (name) { - root.currentWifiSSID = name - } - } - } - } - } - - function updateWifiState() { - checkWifiEnabled.running = true - } - - Process { - id: checkWifiEnabled - command: lowPriorityCmd.concat(["gdbus", "call", "--system", "--dest", "org.freedesktop.NetworkManager", "--object-path", "/org/freedesktop/NetworkManager", "--method", "org.freedesktop.DBus.Properties.Get", "org.freedesktop.NetworkManager", "WirelessEnabled"]) - running: false - - stdout: StdioCollector { - onStreamFinished: { - root.wifiEnabled = text.includes("true") - root.wifiAvailable = true // Always available if we can check it - } - } - } - - function scanWifi() { - if (root.isScanning || !root.wifiEnabled) { - return - } - - root.isScanning = true - requestWifiScan.running = true - } - - Process { - id: requestWifiScan - command: root.wifiInterface ? lowPriorityCmd.concat(["nmcli", "dev", "wifi", "rescan", "ifname", root.wifiInterface]) : [] - running: false - - onExited: exitCode => { - if (exitCode === 0) { - scanWifiNetworks() - } else { - console.warn("WiFi scan request failed") - root.isScanning = false - } - } - } - - function scanWifiNetworks() { - if (!root.wifiInterface) { - root.isScanning = false - return - } - - getWifiNetworks.running = true - getSavedConnections.running = true - } - - Process { - id: getWifiNetworks - command: lowPriorityCmd.concat(["nmcli", "-t", "-f", "SSID,SIGNAL,SECURITY,BSSID", "dev", "wifi", "list", "ifname", root.wifiInterface]) - running: false - - stdout: StdioCollector { - onStreamFinished: { - const networks = [] - const lines = text.trim().split('\n') - const seen = new Set() - - for (const line of lines) { - const parts = root.splitNmcliFields(line) - if (parts.length >= 4 && parts[0]) { - const ssid = parts[0] - if (!seen.has(ssid)) { - seen.add(ssid) - const signal = parseInt(parts[1]) || 0 - - networks.push({ - "ssid": ssid, - "signal": signal, - "secured": parts[2] !== "", - "bssid": parts[3], - "connected": ssid === root.currentWifiSSID, - "saved": false - }) - } - } - } - - networks.sort((a, b) => b.signal - a.signal) - root.wifiNetworks = networks - root.isScanning = false - root.networksUpdated() - } - } - } - - Process { - id: getSavedConnections - command: lowPriorityCmd.concat(["bash", "-c", "nmcli -t -f NAME,TYPE connection show | grep ':802-11-wireless$' | cut -d: -f1 | while read name; do ssid=$(nmcli -g 802-11-wireless.ssid connection show \"$name\"); echo \"$ssid:$name\"; done"]) - running: false - - stdout: StdioCollector { - onStreamFinished: { - const saved = [] - const mapping = {} - const lines = text.trim().split('\n') - - for (const line of lines) { - const parts = line.trim().split(':') - if (parts.length >= 2) { - const ssid = parts[0] - const connectionName = parts[1] - if (ssid && ssid.length > 0 && connectionName && connectionName.length > 0) { - saved.push({ - "ssid": ssid, - "saved": true - }) - mapping[ssid] = connectionName - } - } - } - - root.savedConnections = saved - root.savedWifiNetworks = saved - root.ssidToConnectionName = mapping - - const updated = [...root.wifiNetworks] - for (const network of updated) { - network.saved = saved.some(s => s.ssid === network.ssid) - } - root.wifiNetworks = updated - } - } - } - - function connectToWifi(ssid, password = "", username = "") { - if (root.isConnecting) { - return - } - - root.isConnecting = true - root.connectingSSID = ssid - root.connectionError = "" - root.connectionStatus = "connecting" - - if (!password && root.ssidToConnectionName[ssid]) { - const connectionName = root.ssidToConnectionName[ssid] - wifiConnector.command = lowPriorityCmd.concat(["nmcli", "connection", "up", connectionName]) - } else if (password) { - wifiConnector.command = lowPriorityCmd.concat(["nmcli", "dev", "wifi", "connect", ssid, "password", password]) - } else { - wifiConnector.command = lowPriorityCmd.concat(["nmcli", "dev", "wifi", "connect", ssid]) - } - wifiConnector.running = true - } - - Process { - id: wifiConnector - running: false - - property bool connectionSucceeded: false - - stdout: StdioCollector { - onStreamFinished: { - if (text.includes("successfully")) { - wifiConnector.connectionSucceeded = true - ToastService.showInfo(`Connected to ${root.connectingSSID}`) - root.connectionError = "" - root.connectionStatus = "connected" - - if (root.userPreference === "wifi" || root.userPreference === "auto") { - setConnectionPriority("wifi") - } - } - } - } - - stderr: StdioCollector { - onStreamFinished: { - root.connectionError = text - root.lastConnectionError = text - if (!wifiConnector.connectionSucceeded && text.trim() !== "") { - if (text.includes("password") || text.includes("authentication")) { - root.connectionStatus = "invalid_password" - root.passwordDialogShouldReopen = true - } else { - root.connectionStatus = "failed" - } - } - } - } - - onExited: exitCode => { - if (exitCode === 0 || wifiConnector.connectionSucceeded) { - if (!wifiConnector.connectionSucceeded) { - ToastService.showInfo(`Connected to ${root.connectingSSID}`) - root.connectionStatus = "connected" - } - } else { - if (root.connectionStatus === "") { - root.connectionStatus = "failed" - } - if (root.connectionStatus === "invalid_password") { - ToastService.showError(`Invalid password for ${root.connectingSSID}`) - } else { - ToastService.showError(`Failed to connect to ${root.connectingSSID}`) - } - } - - wifiConnector.connectionSucceeded = false - root.isConnecting = false - root.connectingSSID = "" - doRefreshNetworkState() - } - } - - function disconnectWifi() { - if (!root.wifiInterface) { - return - } - - wifiDisconnector.command = lowPriorityCmd.concat(["nmcli", "dev", "disconnect", root.wifiInterface]) - wifiDisconnector.running = true - } - - Process { - id: wifiDisconnector - running: false - - onExited: exitCode => { - if (exitCode === 0) { - ToastService.showInfo("Disconnected from WiFi") - root.currentWifiSSID = "" - root.connectionStatus = "" - } - doRefreshNetworkState() - } - } - - function forgetWifiNetwork(ssid) { - root.forgetSSID = ssid - const connectionName = root.ssidToConnectionName[ssid] || ssid - networkForgetter.command = lowPriorityCmd.concat(["nmcli", "connection", "delete", connectionName]) - networkForgetter.running = true - } - - Process { - id: networkForgetter - running: false - - onExited: exitCode => { - if (exitCode === 0) { - ToastService.showInfo(`Forgot network ${root.forgetSSID}`) - - root.savedConnections = root.savedConnections.filter(s => s.ssid !== root.forgetSSID) - root.savedWifiNetworks = root.savedWifiNetworks.filter(s => s.ssid !== root.forgetSSID) - - const updated = [...root.wifiNetworks] - for (const network of updated) { - if (network.ssid === root.forgetSSID) { - network.saved = false - if (network.connected) { - network.connected = false - root.currentWifiSSID = "" - } - } - } - root.wifiNetworks = updated - root.networksUpdated() - doRefreshNetworkState() - } - root.forgetSSID = "" - } - } - - function toggleWifiRadio() { - if (root.wifiToggling) { - return - } - - root.wifiToggling = true - const targetState = root.wifiEnabled ? "off" : "on" - wifiRadioToggler.targetState = targetState - wifiRadioToggler.command = lowPriorityCmd.concat(["nmcli", "radio", "wifi", targetState]) - wifiRadioToggler.running = true - } - - Process { - id: wifiRadioToggler - running: false - - property string targetState: "" - - onExited: exitCode => { - root.wifiToggling = false - if (exitCode === 0) { - ToastService.showInfo(targetState === "on" ? "WiFi enabled" : "WiFi disabled") - } - doRefreshNetworkState() - } - } - - function setNetworkPreference(preference) { - root.userPreference = preference - root.changingPreference = true - root.targetPreference = preference - SettingsData.set("networkPreference", preference) - - if (preference === "wifi") { - setConnectionPriority("wifi") - } else if (preference === "ethernet") { - setConnectionPriority("ethernet") - } - } - - function setConnectionPriority(type) { - if (type === "wifi") { - setRouteMetrics.command = lowPriorityCmd.concat(["bash", "-c", "nmcli -t -f NAME,TYPE connection show | grep 802-11-wireless | cut -d: -f1 | " + "xargs -I {} bash -c 'nmcli connection modify \"{}\" ipv4.route-metric 50 ipv6.route-metric 50'; " + "nmcli -t -f NAME,TYPE connection show | grep 802-3-ethernet | cut -d: -f1 | " + "xargs -I {} bash -c 'nmcli connection modify \"{}\" ipv4.route-metric 100 ipv6.route-metric 100'"]) - } else if (type === "ethernet") { - setRouteMetrics.command = lowPriorityCmd.concat(["bash", "-c", "nmcli -t -f NAME,TYPE connection show | grep 802-3-ethernet | cut -d: -f1 | " + "xargs -I {} bash -c 'nmcli connection modify \"{}\" ipv4.route-metric 50 ipv6.route-metric 50'; " + "nmcli -t -f NAME,TYPE connection show | grep 802-11-wireless | cut -d: -f1 | " + "xargs -I {} bash -c 'nmcli connection modify \"{}\" ipv4.route-metric 100 ipv6.route-metric 100'"]) - } - setRouteMetrics.running = true - } - - Process { - id: setRouteMetrics - running: false - - onExited: exitCode => { - console.log("Set route metrics process exited with code:", exitCode) - if (exitCode === 0) { - restartConnections.running = true - } - } - } - - Process { - id: restartConnections - command: lowPriorityCmd.concat(["bash", "-c", "nmcli -t -f UUID,TYPE connection show --active | " + "grep -E '802-11-wireless|802-3-ethernet' | cut -d: -f1 | " + "xargs -I {} sh -c 'nmcli connection down {} && nmcli connection up {}'"]) - running: false - - onExited: { - root.changingPreference = false - root.targetPreference = "" - doRefreshNetworkState() - } - } - - - function fetchNetworkInfo(ssid) { - root.networkInfoSSID = ssid - root.networkInfoLoading = true - root.networkInfoDetails = "Loading network information..." - wifiInfoFetcher.running = true - } - - Process { - id: wifiInfoFetcher - command: lowPriorityCmd.concat(["nmcli", "-t", "-f", "SSID,SIGNAL,SECURITY,FREQ,RATE,MODE,CHAN,WPA-FLAGS,RSN-FLAGS,ACTIVE,BSSID", "dev", "wifi", "list"]) - running: false - - stdout: StdioCollector { - onStreamFinished: { - let details = "" - if (text.trim()) { - const lines = text.trim().split('\n') - const bands = [] - - for (const line of lines) { - const parts = line.split(':') - if (parts.length >= 11 && parts[0] === root.networkInfoSSID) { - const signal = parts[1] || "0" - const security = parts[2] || "Open" - const freq = parts[3] || "Unknown" - const rate = parts[4] || "Unknown" - const channel = parts[6] || "Unknown" - const isActive = parts[9] === "yes" - let colonCount = 0 - let bssidStart = -1 - for (var i = 0; i < line.length; i++) { - if (line[i] === ':') { - colonCount++ - if (colonCount === 10) { - bssidStart = i + 1 - break - } - } - } - const bssid = bssidStart >= 0 ? line.substring(bssidStart).replace(/\\:/g, ":") : "" - - let band = "Unknown" - const freqNum = parseInt(freq) - if (freqNum >= 2400 && freqNum <= 2500) { - band = "2.4 GHz" - } else if (freqNum >= 5000 && freqNum <= 6000) { - band = "5 GHz" - } else if (freqNum >= 6000) { - band = "6 GHz" - } - - bands.push({ - "band": band, - "freq": freq, - "channel": channel, - "signal": signal, - "rate": rate, - "security": security, - "isActive": isActive, - "bssid": bssid - }) - } - } - - if (bands.length > 0) { - bands.sort((a, b) => { - if (a.isActive && !b.isActive) { - return -1 - } - if (!a.isActive && b.isActive) { - return 1 - } - return parseInt(b.signal) - parseInt(a.signal) - }) - - for (var i = 0; i < bands.length; i++) { - const b = bands[i] - if (b.isActive) { - details += "● " + b.band + " (Connected) - " + b.signal + "%\\n" - } else { - details += " " + b.band + " - " + b.signal + "%\\n" - } - details += " Channel " + b.channel + " (" + b.freq + " MHz) • " + b.rate + " Mbit/s\\n" - details += " " + b.bssid - if (i < bands.length - 1) { - details += "\\n\\n" - } - } - } - } - - if (details === "") { - details = "Network information not found or network not available." - } - - root.networkInfoDetails = details - root.networkInfoLoading = false - } - } - - onExited: exitCode => { - root.networkInfoLoading = false - if (exitCode !== 0) { - root.networkInfoDetails = "Failed to fetch network information" - } - } - } - - function enableWifiDevice() { - wifiDeviceEnabler.running = true - } - - Process { - id: wifiDeviceEnabler - command: lowPriorityCmd.concat(["sh", "-c", "WIFI_DEV=$(nmcli -t -f DEVICE,TYPE device | grep wifi | cut -d: -f1 | head -1); if [ -n \"$WIFI_DEV\" ]; then nmcli device connect \"$WIFI_DEV\"; else echo \"No WiFi device found\"; exit 1; fi"]) - running: false - - onExited: exitCode => { - if (exitCode === 0) { - ToastService.showInfo("WiFi enabled") - } else { - ToastService.showError("Failed to enable WiFi") - } - doRefreshNetworkState() - } - } - - function connectToWifiAndSetPreference(ssid, password) { - connectToWifi(ssid, password) - setNetworkPreference("wifi") - } - - function toggleNetworkConnection(type) { - if (type === "ethernet") { - if (root.networkStatus === "ethernet") { - ethernetDisconnector.running = true - } else { - ethernetConnector.running = true - } - } - } - - Process { - id: ethernetDisconnector - command: lowPriorityCmd.concat(["sh", "-c", "nmcli device disconnect $(nmcli -t -f DEVICE,TYPE device | grep ethernet | cut -d: -f1 | head -1)"]) - running: false - - onExited: function (exitCode) { - doRefreshNetworkState() - } - } - - Process { - id: ethernetConnector - command: lowPriorityCmd.concat(["sh", "-c", "ETH_DEV=$(nmcli -t -f DEVICE,TYPE device | grep ethernet | cut -d: -f1 | head -1); if [ -n \"$ETH_DEV\" ]; then nmcli device connect \"$ETH_DEV\"; else echo \"No ethernet device found\"; exit 1; fi"]) - running: false - - onExited: function (exitCode) { - doRefreshNetworkState() - } - } - - function getNetworkInfo(ssid) { - const network = root.wifiNetworks.find(n => n.ssid === ssid) - if (!network) { - return null - } - - return { - "ssid": network.ssid, - "signal": network.signal, - "secured": network.secured, - "saved": network.saved, - "connected": network.connected, - "bssid": network.bssid - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/LocationService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/LocationService.qml deleted file mode 100644 index 0fde587..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/LocationService.qml +++ /dev/null @@ -1,55 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - readonly property bool locationAvailable: DMSService.isConnected && (DMSService.capabilities.length === 0 || DMSService.capabilities.includes("location")) - readonly property bool valid: latitude !== 0 || longitude !== 0 - - property var latitude: 0.0 - property var longitude: 0.0 - - signal locationChanged(var data) - - onLocationAvailableChanged: { - if (locationAvailable && !valid) - getState(); - } - - Connections { - target: DMSService - - function onLocationStateUpdate(data) { - if (!locationAvailable) - return; - handleStateUpdate(data); - } - } - - function handleStateUpdate(data) { - const lat = data.latitude; - const lon = data.longitude; - if (lat === 0 && lon === 0) - return; - - root.latitude = lat; - root.longitude = lon; - root.locationChanged(data); - } - - function getState() { - if (!locationAvailable) - return; - - DMSService.sendRequest("location.getState", null, response => { - if (response.result) - handleStateUpdate(response.result); - }); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/MprisController.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/MprisController.qml deleted file mode 100644 index 44ad135..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/MprisController.qml +++ /dev/null @@ -1,78 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Services.Mpris -import qs.Common - -Singleton { - id: root - - readonly property list<MprisPlayer> availablePlayers: Mpris.players.values - property MprisPlayer activePlayer: null - - onAvailablePlayersChanged: _resolveActivePlayer() - Component.onCompleted: _resolveActivePlayer() - - Instantiator { - model: root.availablePlayers - delegate: Connections { - required property MprisPlayer modelData - target: modelData - function onIsPlayingChanged() { - if (modelData.isPlaying) - root._resolveActivePlayer(); - } - } - } - - function _resolveActivePlayer(): void { - const playing = availablePlayers.find(p => p.isPlaying); - if (playing) { - activePlayer = playing; - _persistIdentity(playing.identity); - return; - } - if (activePlayer && availablePlayers.indexOf(activePlayer) >= 0) - return; - const savedId = SessionData.lastPlayerIdentity; - if (savedId) { - const match = availablePlayers.find(p => p.identity === savedId); - if (match) { - activePlayer = match; - return; - } - } - activePlayer = availablePlayers.find(p => p.canControl && p.canPlay) ?? null; - if (activePlayer) - _persistIdentity(activePlayer.identity); - } - - function setActivePlayer(player: MprisPlayer): void { - activePlayer = player; - if (player) - _persistIdentity(player.identity); - } - - function _persistIdentity(identity: string): void { - if (identity && SessionData.lastPlayerIdentity !== identity) - SessionData.set("lastPlayerIdentity", identity); - } - - Timer { - interval: 1000 - running: root.activePlayer?.playbackState === MprisPlaybackState.Playing - repeat: true - onTriggered: root.activePlayer?.positionChanged() - } - - function previousOrRewind(): void { - if (!activePlayer) - return; - if (activePlayer.position > 8 && activePlayer.canSeek) - activePlayer.position = 0; - else if (activePlayer.canGoPrevious) - activePlayer.previous(); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/MultimediaService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/MultimediaService.qml deleted file mode 100644 index f570c72..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/MultimediaService.qml +++ /dev/null @@ -1,35 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell - -Singleton { - id: root - - property bool available: false - - function detectAvailability() { - try { - const testObj = Qt.createQmlObject(` - import QtQuick - import QtMultimedia - Item {} - `, root, "MultimediaService.TestComponent"); - if (testObj) { - testObj.destroy(); - } - available = true; - return true; - } catch (e) { - available = false; - return false; - } - } - - Component.onCompleted: { - if (!detectAvailability()) { - console.warn("MultimediaService: QtMultimedia not available"); - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/MuxService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/MuxService.qml deleted file mode 100644 index c2f941c..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/MuxService.qml +++ /dev/null @@ -1,230 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property var sessions: [] - property bool loading: false - - property bool tmuxAvailable: false - property bool zellijAvailable: false - readonly property bool currentMuxAvailable: muxType === "zellij" ? zellijAvailable : tmuxAvailable - - readonly property string muxType: SettingsData.muxType - readonly property string displayName: muxType === "zellij" ? "Zellij" : "Tmux" - - readonly property var terminalFlags: ({ - "ghostty": ["-e"], - "kitty": ["-e"], - "alacritty": ["-e"], - "foot": [], - "wezterm": ["start", "--"], - "gnome-terminal": ["--"], - "xterm": ["-e"], - "konsole": ["-e"], - "st": ["-e"], - "terminator": ["-e"], - "xfce4-terminal": ["-e"] - }) - - function getTerminalFlag(terminal) { - return terminalFlags[terminal] ?? ["-e"] - } - - readonly property string terminal: Quickshell.env("TERMINAL") || "ghostty" - - function _terminalPrefix() { - return [terminal].concat(getTerminalFlag(terminal)) - } - - Process { - id: tmuxCheckProcess - command: ["which", "tmux"] - running: false - onExited: (code) => { root.tmuxAvailable = (code === 0) } - } - - Process { - id: zellijCheckProcess - command: ["which", "zellij"] - running: false - onExited: (code) => { root.zellijAvailable = (code === 0) } - } - - function checkAvailability() { - tmuxCheckProcess.running = true - zellijCheckProcess.running = true - } - - Component.onCompleted: checkAvailability() - - Process { - id: listProcess - running: false - - stdout: StdioCollector { - onStreamFinished: { - try { - if (root.muxType === "zellij") - root._parseZellijSessions(text) - else - root._parseTmuxSessions(text) - } catch (e) { - console.error("[MuxService] Error parsing sessions:", e) - root.sessions = [] - } - root.loading = false - } - } - - stderr: SplitParser { - onRead: (line) => { - if (line.trim()) - console.error("[MuxService] stderr:", line) - } - } - - onExited: (code) => { - if (code !== 0 && code !== 1) { - console.warn("[MuxService] Process exited with code:", code) - root.sessions = [] - } - root.loading = false - } - } - - function refreshSessions() { - if (!root.currentMuxAvailable) { - root.sessions = [] - return - } - - root.loading = true - - if (listProcess.running) - listProcess.running = false - - if (root.muxType === "zellij") - listProcess.command = ["zellij", "list-sessions", "--no-formatting"] - else - listProcess.command = ["tmux", "list-sessions", "-F", "#{session_name}|#{session_windows}|#{session_attached}"] - - Qt.callLater(function () { - listProcess.running = true - }) - } - - function _isSessionExcluded(name) { - var filter = SettingsData.muxSessionFilter.trim() - if (filter.length === 0) - return false - var parts = filter.split(",") - for (var i = 0; i < parts.length; i++) { - var pattern = parts[i].trim() - if (pattern.length === 0) - continue - if (pattern.startsWith("/") && pattern.endsWith("/") && pattern.length > 2) { - try { - var re = new RegExp(pattern.slice(1, -1)) - if (re.test(name)) - return true - } catch (e) {} - } else { - if (name.toLowerCase() === pattern.toLowerCase()) - return true - } - } - return false - } - - function _parseTmuxSessions(output) { - var sessionList = [] - var lines = output.trim().split('\n') - - for (var i = 0; i < lines.length; i++) { - var line = lines[i].trim() - if (line.length === 0) - continue - - var parts = line.split('|') - if (parts.length >= 3 && !_isSessionExcluded(parts[0])) { - sessionList.push({ - name: parts[0], - windows: parts[1], - attached: parts[2] === "1" - }) - } - } - - root.sessions = sessionList - } - - function _parseZellijSessions(output) { - var sessionList = [] - var lines = output.trim().split('\n') - - for (var i = 0; i < lines.length; i++) { - var line = lines[i].trim() - if (line.length === 0) - continue - - var exited = line.includes("(EXITED") - var bracketIdx = line.indexOf(" [") - var name = (bracketIdx > 0 ? line.substring(0, bracketIdx) : line).trim() - - if (!_isSessionExcluded(name)) { - sessionList.push({ - name: name, - windows: "N/A", - attached: !exited - }) - } - } - - root.sessions = sessionList - } - - function attachToSession(name) { - if (SettingsData.muxUseCustomCommand && SettingsData.muxCustomCommand) { - Quickshell.execDetached([SettingsData.muxCustomCommand, name]) - } else if (root.muxType === "zellij") { - Quickshell.execDetached(_terminalPrefix().concat(["zellij", "attach", name])) - } else { - Quickshell.execDetached(_terminalPrefix().concat(["tmux", "attach", "-t", name])) - } - } - - function createSession(name) { - if (SettingsData.muxUseCustomCommand && SettingsData.muxCustomCommand) { - Quickshell.execDetached([SettingsData.muxCustomCommand, name]) - } else if (root.muxType === "zellij") { - Quickshell.execDetached(_terminalPrefix().concat(["zellij", "-s", name])) - } else { - Quickshell.execDetached(_terminalPrefix().concat(["tmux", "new-session", "-s", name])) - } - } - - readonly property bool supportsRename: muxType !== "zellij" - - function renameSession(oldName, newName) { - if (root.muxType === "zellij") - return - Quickshell.execDetached(["tmux", "rename-session", "-t", oldName, newName]) - Qt.callLater(refreshSessions) - } - - function killSession(name) { - if (root.muxType === "zellij") { - Quickshell.execDetached(["zellij", "kill-session", name]) - } else { - Quickshell.execDetached(["tmux", "kill-session", "-t", name]) - } - Qt.callLater(refreshSessions) - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NetworkService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NetworkService.qml deleted file mode 100644 index 38383d2..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NetworkService.qml +++ /dev/null @@ -1,313 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell - -Singleton { - id: root - - property bool networkAvailable: activeService !== null - property string backend: activeService?.backend ?? "" - property string networkStatus: activeService?.networkStatus ?? "disconnected" - property string primaryConnection: activeService?.primaryConnection ?? "" - - property string ethernetIP: activeService?.ethernetIP ?? "" - property string ethernetInterface: activeService?.ethernetInterface ?? "" - property bool ethernetConnected: activeService?.ethernetConnected ?? false - property string ethernetConnectionUuid: activeService?.ethernetConnectionUuid ?? "" - property var ethernetDevices: activeService?.ethernetDevices ?? [] - - property var wiredConnections: activeService?.wiredConnections ?? [] - - property string wifiIP: activeService?.wifiIP ?? "" - property string wifiInterface: activeService?.wifiInterface ?? "" - property bool wifiConnected: activeService?.wifiConnected ?? false - property bool wifiEnabled: activeService?.wifiEnabled ?? true - property string wifiConnectionUuid: activeService?.wifiConnectionUuid ?? "" - property string wifiDevicePath: activeService?.wifiDevicePath ?? "" - property string activeAccessPointPath: activeService?.activeAccessPointPath ?? "" - property var wifiDevices: activeService?.wifiDevices ?? [] - property string wifiDeviceOverride: activeService?.wifiDeviceOverride ?? "" - property string connectingDevice: activeService?.connectingDevice ?? "" - - property string currentWifiSSID: activeService?.currentWifiSSID ?? "" - property int wifiSignalStrength: activeService?.wifiSignalStrength ?? 0 - property var wifiNetworks: activeService?.wifiNetworks ?? [] - property var savedConnections: activeService?.savedConnections ?? [] - property var ssidToConnectionName: activeService?.ssidToConnectionName ?? ({}) - property var wifiSignalIcon: activeService?.wifiSignalIcon ?? "wifi_off" - - property string userPreference: activeService?.userPreference ?? "auto" - property bool isConnecting: activeService?.isConnecting ?? false - property string connectingSSID: activeService?.connectingSSID ?? "" - property string connectionError: activeService?.connectionError ?? "" - - property bool isScanning: activeService?.isScanning ?? false - property bool autoScan: activeService?.autoScan ?? false - - property bool wifiAvailable: activeService?.wifiAvailable ?? true - property bool wifiToggling: activeService?.wifiToggling ?? false - property bool changingPreference: activeService?.changingPreference ?? false - property string targetPreference: activeService?.targetPreference ?? "" - property var savedWifiNetworks: activeService?.savedWifiNetworks ?? [] - property string connectionStatus: activeService?.connectionStatus ?? "" - property string lastConnectionError: activeService?.lastConnectionError ?? "" - property bool passwordDialogShouldReopen: activeService?.passwordDialogShouldReopen ?? false - property bool autoRefreshEnabled: activeService?.autoRefreshEnabled ?? false - property string wifiPassword: activeService?.wifiPassword ?? "" - property string forgetSSID: activeService?.forgetSSID ?? "" - - property string networkInfoSSID: activeService?.networkInfoSSID ?? "" - property string networkInfoDetails: activeService?.networkInfoDetails ?? "" - property bool networkInfoLoading: activeService?.networkInfoLoading ?? false - - property string networkWiredInfoUUID: activeService?.networkWiredInfoUUID ?? "" - property string networkWiredInfoDetails: activeService?.networkWiredInfoDetails ?? "" - property bool networkWiredInfoLoading: activeService?.networkWiredInfoLoading ?? false - - property int refCount: activeService?.refCount ?? 0 - property bool stateInitialized: activeService?.stateInitialized ?? false - - property bool subscriptionConnected: activeService?.subscriptionConnected ?? false - - property var vpnProfiles: activeService?.vpnProfiles ?? [] - property var vpnActive: activeService?.vpnActive ?? [] - property bool vpnAvailable: activeService?.vpnAvailable ?? false - property bool vpnIsBusy: activeService?.vpnIsBusy ?? false - property bool vpnConnected: activeService?.vpnConnected ?? false - property string vpnActiveUuid: activeService?.activeUuid ?? "" - property string vpnActiveName: activeService?.activeName ?? "" - - property string credentialsToken: activeService?.credentialsToken ?? "" - property string credentialsSSID: activeService?.credentialsSSID ?? "" - property string credentialsSetting: activeService?.credentialsSetting ?? "" - property var credentialsFields: activeService?.credentialsFields ?? [] - property var credentialsHints: activeService?.credentialsHints ?? [] - property string credentialsReason: activeService?.credentialsReason ?? "" - property bool credentialsRequested: activeService?.credentialsRequested ?? false - - signal networksUpdated - signal connectionChanged - signal credentialsNeeded(string token, string ssid, string setting, var fields, var hints, string reason, string connType, string connName, string vpnService, var fieldsInfo) - - property bool usingLegacy: false - property var activeService: null - - readonly property string socketPath: Quickshell.env("DMS_SOCKET") - - Component.onCompleted: { - console.info("NetworkService: Initializing..."); - if (!socketPath || socketPath.length === 0) { - console.info("NetworkService: DMS_SOCKET not set, using LegacyNetworkService"); - useLegacyService(); - } else { - console.log("NetworkService: DMS_SOCKET found, waiting for capabilities..."); - } - } - - Connections { - target: DMSNetworkService - - function onNetworkAvailableChanged() { - if (!activeService && DMSNetworkService.networkAvailable) { - console.info("NetworkService: Network capability detected, using DMSNetworkService"); - activeService = DMSNetworkService; - usingLegacy = false; - console.info("NetworkService: Switched to DMSNetworkService, networkAvailable:", networkAvailable); - connectSignals(); - } else if (!activeService && !DMSNetworkService.networkAvailable && socketPath && socketPath.length > 0) { - console.info("NetworkService: Network capability not available in DMS, using LegacyNetworkService"); - useLegacyService(); - } - } - } - - function useLegacyService() { - activeService = LegacyNetworkService; - usingLegacy = true; - console.info("NetworkService: Switched to LegacyNetworkService, networkAvailable:", networkAvailable); - if (LegacyNetworkService.activate) { - LegacyNetworkService.activate(); - } - connectSignals(); - } - - function connectSignals() { - if (activeService) { - if (activeService.networksUpdated) { - activeService.networksUpdated.connect(root.networksUpdated); - } - if (activeService.connectionChanged) { - activeService.connectionChanged.connect(root.connectionChanged); - } - if (activeService.credentialsNeeded) { - activeService.credentialsNeeded.connect(root.credentialsNeeded); - } - } - } - - function addRef() { - if (activeService && activeService.addRef) { - activeService.addRef(); - } - } - - function removeRef() { - if (activeService && activeService.removeRef) { - activeService.removeRef(); - } - } - - function getState() { - if (activeService && activeService.getState) { - activeService.getState(); - } - } - - function scanWifi() { - if (activeService && activeService.scanWifi) { - activeService.scanWifi(); - } - } - - function scanWifiNetworks() { - if (activeService && activeService.scanWifiNetworks) { - activeService.scanWifiNetworks(); - } - } - - function connectToWifi(ssid, password = "", username = "", anonymousIdentity = "", domainSuffixMatch = "") { - if (activeService && activeService.connectToWifi) { - activeService.connectToWifi(ssid, password, username, anonymousIdentity, domainSuffixMatch); - } - } - - function disconnectWifi() { - if (activeService && activeService.disconnectWifi) { - activeService.disconnectWifi(); - } - } - - function forgetWifiNetwork(ssid) { - if (activeService && activeService.forgetWifiNetwork) { - activeService.forgetWifiNetwork(ssid); - } - } - - function toggleWifiRadio() { - if (activeService && activeService.toggleWifiRadio) { - activeService.toggleWifiRadio(); - } - } - - function enableWifiDevice() { - if (activeService && activeService.enableWifiDevice) { - activeService.enableWifiDevice(); - } - } - - function setNetworkPreference(preference) { - if (activeService && activeService.setNetworkPreference) { - activeService.setNetworkPreference(preference); - } - } - - function setConnectionPriority(type) { - if (activeService && activeService.setConnectionPriority) { - activeService.setConnectionPriority(type); - } - } - - function connectToWifiAndSetPreference(ssid, password, username = "", anonymousIdentity = "", domainSuffixMatch = "") { - if (activeService && activeService.connectToWifiAndSetPreference) { - activeService.connectToWifiAndSetPreference(ssid, password, username, anonymousIdentity, domainSuffixMatch); - } - } - - function toggleNetworkConnection(type) { - if (activeService && activeService.toggleNetworkConnection) { - activeService.toggleNetworkConnection(type); - } - } - - function disconnectEthernetDevice(deviceName) { - if (activeService && activeService.disconnectEthernetDevice) { - activeService.disconnectEthernetDevice(deviceName); - } - } - - function startAutoScan() { - if (activeService && activeService.startAutoScan) { - activeService.startAutoScan(); - } - } - - function stopAutoScan() { - if (activeService && activeService.stopAutoScan) { - activeService.stopAutoScan(); - } - } - - function fetchNetworkInfo(ssid) { - if (activeService && activeService.fetchNetworkInfo) { - activeService.fetchNetworkInfo(ssid); - } - } - - function fetchWiredNetworkInfo(uuid) { - if (activeService && activeService.fetchWiredNetworkInfo) { - activeService.fetchWiredNetworkInfo(uuid); - } - } - - function getNetworkInfo(ssid) { - if (activeService && activeService.getNetworkInfo) { - return activeService.getNetworkInfo(ssid); - } - return null; - } - - function getWiredNetworkInfo(uuid) { - if (activeService && activeService.getWiredNetworkInfo) { - return activeService.getWiredNetworkInfo(uuid); - } - return null; - } - - function refreshNetworkState() { - if (activeService && activeService.refreshNetworkState) { - activeService.refreshNetworkState(); - } - } - - function connectToSpecificWiredConfig(uuid) { - if (activeService && activeService.connectToSpecificWiredConfig) { - activeService.connectToSpecificWiredConfig(uuid); - } - } - - function submitCredentials(token, secrets, save) { - if (activeService && activeService.submitCredentials) { - activeService.submitCredentials(token, secrets, save); - } - } - - function cancelCredentials(token) { - if (activeService && activeService.cancelCredentials) { - activeService.cancelCredentials(token); - } - } - - function setWifiAutoconnect(ssid, autoconnect) { - if (activeService && activeService.setWifiAutoconnect) { - activeService.setWifiAutoconnect(ssid, autoconnect); - } - } - - function setWifiDeviceOverride(deviceName) { - if (activeService && activeService.setWifiDeviceOverride) { - activeService.setWifiDeviceOverride(deviceName); - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NiriService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NiriService.qml deleted file mode 100644 index 49748eb..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NiriService.qml +++ /dev/null @@ -1,1494 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtCore -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - readonly property string socketPath: Quickshell.env("NIRI_SOCKET") - - property var workspaces: ({}) - property var allWorkspaces: [] - property int focusedWorkspaceIndex: 0 - property string focusedWorkspaceId: "" - property var currentOutputWorkspaces: [] - property string currentOutput: "" - - property var outputs: ({}) - property var windows: [] - property var displayScales: ({}) - - property var _realOutputs: ({}) - - property bool inOverview: false - - property var casts: [] - property bool hasCasts: casts.length > 0 - property bool hasActiveCast: casts.some(c => c.is_active) - - property int currentKeyboardLayoutIndex: 0 - property var keyboardLayoutNames: [] - - property string configValidationOutput: "" - property bool hasInitialConnection: false - property bool suppressConfigToast: true - property bool suppressNextConfigToast: false - property bool matugenSuppression: false - property bool configGenerationPending: false - - readonly property string screenshotsDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.PicturesLocation)) + "/Screenshots" - property string pendingScreenshotPath: "" - - signal windowUrgentChanged - signal configReloaded - - function setWorkspaces(newMap) { - root.workspaces = newMap; - root.allWorkspaces = Object.values(newMap).sort((a, b) => a.idx - b.idx); - } - - function validate() { - validateProcess.running = true; - } - - Component.onCompleted: fetchOutputs() - - Timer { - id: suppressToastTimer - interval: 3000 - onTriggered: root.suppressConfigToast = false - } - - Timer { - id: suppressResetTimer - interval: 5000 - onTriggered: root.matugenSuppression = false - } - - Timer { - id: configGenerationDebounce - interval: 100 - onTriggered: root.doGenerateNiriLayoutConfig() - } - - property int _lastGapValue: -1 - - Connections { - target: SettingsData - function onBarConfigsChanged() { - const newGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)); - if (newGaps === root._lastGapValue) - return; - root._lastGapValue = newGaps; - generateNiriLayoutConfig(); - } - } - - Process { - id: validateProcess - command: ["niri", "validate"] - running: false - - stderr: StdioCollector { - onStreamFinished: { - const lines = text.split('\n'); - const trimmedLines = lines.map(line => line.replace(/\s+$/, '')).filter(line => line.length > 0); - configValidationOutput = trimmedLines.join('\n').trim(); - } - } - - onExited: exitCode => { - if (exitCode === 0) { - configValidationOutput = ""; - } else if (hasInitialConnection && configValidationOutput.length > 0) { - ToastService.showError("niri: failed to load config", configValidationOutput, "", "niri-config"); - } - } - } - - Process { - id: writeConfigProcess - property string configContent: "" - property string configPath: "" - - onExited: exitCode => { - if (exitCode === 0) { - console.info("NiriService: Generated layout config at", configPath); - return; - } - console.warn("NiriService: Failed to write layout config, exit code:", exitCode); - } - } - - Process { - id: writeAlttabProcess - property string alttabContent: "" - property string alttabPath: "" - - onExited: exitCode => { - if (exitCode === 0) { - console.info("NiriService: Generated alttab config at", alttabPath); - return; - } - console.warn("NiriService: Failed to write alttab config, exit code:", exitCode); - } - } - - Process { - id: writeBlurruleProcess - property string blurrulePath: "" - - onExited: exitCode => { - if (exitCode === 0) { - console.info("NiriService: Generated wpblur config at", blurrulePath); - return; - } - console.warn("NiriService: Failed to write wpblur config, exit code:", exitCode); - } - } - - Process { - id: writeCursorProcess - property string cursorContent: "" - property string cursorPath: "" - - onExited: exitCode => { - if (exitCode === 0) { - console.info("NiriService: Generated cursor config at", cursorPath); - return; - } - console.warn("NiriService: Failed to write cursor config, exit code:", exitCode); - } - } - - DankSocket { - id: eventStreamSocket - path: root.socketPath - connected: CompositorService.isNiri - - onConnectionStateChanged: { - if (connected) { - send('"EventStream"'); - fetchOutputs(); - } - } - - parser: SplitParser { - onRead: line => { - try { - const event = JSON.parse(line); - handleNiriEvent(event); - } catch (e) { - console.warn("NiriService: Failed to parse event:", line, e); - } - } - } - } - - DankSocket { - id: requestSocket - path: root.socketPath - connected: CompositorService.isNiri - } - - function fetchOutputs() { - if (!CompositorService.isNiri) - return; - Proc.runCommand("niri-fetch-outputs", ["niri", "msg", "-j", "outputs"], (output, exitCode) => { - if (exitCode !== 0) { - console.warn("NiriService: Failed to fetch outputs, exit code:", exitCode); - return; - } - try { - const outputsData = JSON.parse(output); - outputs = outputsData; - console.info("NiriService: Loaded", Object.keys(outputsData).length, "outputs"); - updateDisplayScales(); - if (windows.length > 0) { - windows = sortWindowsByLayout(windows); - } - } catch (e) { - console.warn("NiriService: Failed to parse outputs:", e); - } - }); - } - - function updateDisplayScales() { - if (!outputs || Object.keys(outputs).length === 0) - return; - const scales = {}; - for (const outputName in outputs) { - const output = outputs[outputName]; - if (output.logical && output.logical.scale !== undefined) { - scales[outputName] = output.logical.scale; - } - } - - displayScales = scales; - } - - function sortWindowsByLayout(windowList) { - const enriched = windowList.map(w => { - const ws = workspaces[w.workspace_id]; - if (!ws) { - return { - "window": w, - "outputX": 999999, - "outputY": 999999, - "wsIdx": 999999, - "col": 999999, - "row": 999999 - }; - } - - const outputInfo = outputs[ws.output]; - const outputX = (outputInfo && outputInfo.logical) ? outputInfo.logical.x : 999999; - const outputY = (outputInfo && outputInfo.logical) ? outputInfo.logical.y : 999999; - - const pos = w.layout?.pos_in_scrolling_layout; - const col = (pos && pos.length >= 2) ? pos[0] : 999999; - const row = (pos && pos.length >= 2) ? pos[1] : 999999; - - return { - "window": w, - "outputX": outputX, - "outputY": outputY, - "wsIdx": ws.idx, - "col": col, - "row": row - }; - }); - - enriched.sort((a, b) => { - if (a.outputX !== b.outputX) - return a.outputX - b.outputX; - if (a.outputY !== b.outputY) - return a.outputY - b.outputY; - if (a.wsIdx !== b.wsIdx) - return a.wsIdx - b.wsIdx; - if (a.col !== b.col) - return a.col - b.col; - if (a.row !== b.row) - return a.row - b.row; - return a.window.id - b.window.id; - }); - - return enriched.map(e => e.window); - } - - function handleNiriEvent(event) { - const eventType = Object.keys(event)[0]; - - switch (eventType) { - case 'WorkspacesChanged': - handleWorkspacesChanged(event.WorkspacesChanged); - break; - case 'WorkspaceActivated': - handleWorkspaceActivated(event.WorkspaceActivated); - break; - case 'WorkspaceActiveWindowChanged': - handleWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged); - break; - case 'WindowFocusChanged': - handleWindowFocusChanged(event.WindowFocusChanged); - break; - case 'WindowsChanged': - handleWindowsChanged(event.WindowsChanged); - break; - case 'WindowClosed': - handleWindowClosed(event.WindowClosed); - break; - case 'WindowOpenedOrChanged': - handleWindowOpenedOrChanged(event.WindowOpenedOrChanged); - break; - case 'WindowLayoutsChanged': - handleWindowLayoutsChanged(event.WindowLayoutsChanged); - break; - case 'OutputsChanged': - handleOutputsChanged(event.OutputsChanged); - break; - case 'OverviewOpenedOrClosed': - handleOverviewChanged(event.OverviewOpenedOrClosed); - break; - case 'ConfigLoaded': - handleConfigLoaded(event.ConfigLoaded); - break; - case 'KeyboardLayoutsChanged': - handleKeyboardLayoutsChanged(event.KeyboardLayoutsChanged); - break; - case 'KeyboardLayoutSwitched': - handleKeyboardLayoutSwitched(event.KeyboardLayoutSwitched); - break; - case 'WorkspaceUrgencyChanged': - handleWorkspaceUrgencyChanged(event.WorkspaceUrgencyChanged); - break; - case 'WindowUrgencyChanged': - handleWindowUrgencyChanged(event.WindowUrgencyChanged); - break; - case 'ScreenshotCaptured': - handleScreenshotCaptured(event.ScreenshotCaptured); - break; - case 'CastsChanged': - handleCastsChanged(event.CastsChanged); - break; - case 'CastStartedOrChanged': - handleCastStartedOrChanged(event.CastStartedOrChanged); - break; - case 'CastStopped': - handleCastStopped(event.CastStopped); - break; - } - } - - function handleWorkspacesChanged(data) { - const newWorkspaces = {}; - - for (const ws of data.workspaces) { - const oldWs = root.workspaces[ws.id]; - newWorkspaces[ws.id] = ws; - if (oldWs && oldWs.active_window_id !== undefined) { - newWorkspaces[ws.id].active_window_id = oldWs.active_window_id; - } - } - - setWorkspaces(newWorkspaces); - - focusedWorkspaceIndex = allWorkspaces.findIndex(w => w.is_focused); - if (focusedWorkspaceIndex >= 0) { - const focusedWs = allWorkspaces[focusedWorkspaceIndex]; - focusedWorkspaceId = focusedWs.id; - currentOutput = focusedWs.output || ""; - } else { - focusedWorkspaceIndex = 0; - focusedWorkspaceId = ""; - } - - updateCurrentOutputWorkspaces(); - } - - function handleWorkspaceActivated(data) { - const ws = root.workspaces[data.id]; - if (!ws) { - return; - } - const output = ws.output; - - const updatedWorkspaces = {}; - - for (const id in root.workspaces) { - const workspace = root.workspaces[id]; - const got_activated = workspace.id === data.id; - - const updatedWs = {}; - for (let prop in workspace) { - updatedWs[prop] = workspace[prop]; - } - - if (workspace.output === output) { - updatedWs.is_active = got_activated; - } - - if (data.focused) { - updatedWs.is_focused = got_activated; - } - - updatedWorkspaces[id] = updatedWs; - } - - setWorkspaces(updatedWorkspaces); - - focusedWorkspaceId = data.id; - focusedWorkspaceIndex = allWorkspaces.findIndex(w => w.id === data.id); - - if (focusedWorkspaceIndex >= 0) { - currentOutput = allWorkspaces[focusedWorkspaceIndex].output || ""; - } - - updateCurrentOutputWorkspaces(); - } - - function handleWindowFocusChanged(data) { - const focusedWindowId = data.id; - - let focusedWindow = null; - const updatedWindows = []; - - for (var i = 0; i < windows.length; i++) { - const w = windows[i]; - const updatedWindow = {}; - - for (let prop in w) { - updatedWindow[prop] = w[prop]; - } - - updatedWindow.is_focused = (w.id === focusedWindowId); - if (updatedWindow.is_focused) { - focusedWindow = updatedWindow; - } - - updatedWindows.push(updatedWindow); - } - - windows = updatedWindows; - - if (focusedWindow) { - const ws = root.workspaces[focusedWindow.workspace_id]; - if (ws && ws.active_window_id !== focusedWindowId) { - const updatedWs = {}; - for (let prop in ws) { - updatedWs[prop] = ws[prop]; - } - updatedWs.active_window_id = focusedWindowId; - - const updatedWorkspaces = {}; - for (const id in root.workspaces) { - updatedWorkspaces[id] = id === focusedWindow.workspace_id ? updatedWs : root.workspaces[id]; - } - setWorkspaces(updatedWorkspaces); - } - } - } - - function handleWorkspaceActiveWindowChanged(data) { - const ws = root.workspaces[data.workspace_id]; - if (ws) { - const updatedWs = {}; - for (let prop in ws) { - updatedWs[prop] = ws[prop]; - } - updatedWs.active_window_id = data.active_window_id; - - const updatedWorkspaces = {}; - for (const id in root.workspaces) { - updatedWorkspaces[id] = id === data.workspace_id ? updatedWs : root.workspaces[id]; - } - setWorkspaces(updatedWorkspaces); - } - - const updatedWindows = []; - - for (var i = 0; i < windows.length; i++) { - const w = windows[i]; - const updatedWindow = {}; - - for (let prop in w) { - updatedWindow[prop] = w[prop]; - } - - if (data.active_window_id !== null && data.active_window_id !== undefined) { - updatedWindow.is_focused = (w.id == data.active_window_id); - } else { - updatedWindow.is_focused = w.workspace_id == data.workspace_id ? false : w.is_focused; - } - - updatedWindows.push(updatedWindow); - } - - windows = updatedWindows; - } - - function handleWindowsChanged(data) { - windows = sortWindowsByLayout(data.windows); - } - - function handleWindowClosed(data) { - windows = windows.filter(w => w.id !== data.id); - } - - function handleWindowOpenedOrChanged(data) { - if (!data.window) - return; - const window = data.window; - const existingIndex = windows.findIndex(w => w.id === window.id); - - if (existingIndex >= 0) { - const updatedWindows = [...windows]; - updatedWindows[existingIndex] = window; - windows = sortWindowsByLayout(updatedWindows); - } else { - windows = sortWindowsByLayout([...windows, window]); - } - } - - function handleWindowLayoutsChanged(data) { - if (!data.changes) - return; - const updatedWindows = [...windows]; - let hasChanges = false; - - for (const change of data.changes) { - const windowId = change[0]; - const layoutData = change[1]; - - const windowIndex = updatedWindows.findIndex(w => w.id === windowId); - if (windowIndex < 0) - continue; - const updatedWindow = {}; - for (var prop in updatedWindows[windowIndex]) { - updatedWindow[prop] = updatedWindows[windowIndex][prop]; - } - updatedWindow.layout = layoutData; - updatedWindows[windowIndex] = updatedWindow; - hasChanges = true; - } - - if (!hasChanges) - return; - windows = sortWindowsByLayout(updatedWindows); - } - - function handleOutputsChanged(data) { - if (!data.outputs) - return; - outputs = data.outputs; - updateDisplayScales(); - windows = sortWindowsByLayout(windows); - } - - function handleOverviewChanged(data) { - inOverview = data.is_open; - } - - function handleConfigLoaded(data) { - if (data.failed) { - validateProcess.running = true; - return; - } - - configValidationOutput = ""; - ToastService.dismissCategory("niri-config"); - fetchOutputs(); - configReloaded(); - - if (hasInitialConnection && !suppressConfigToast && !suppressNextConfigToast && !matugenSuppression) { - ToastService.showInfo("niri: config reloaded", "", "", "niri-config"); - } else if (suppressNextConfigToast) { - suppressNextConfigToast = false; - suppressResetTimer.stop(); - } - - if (!hasInitialConnection) { - hasInitialConnection = true; - suppressToastTimer.start(); - } - } - - function handleKeyboardLayoutsChanged(data) { - keyboardLayoutNames = data.keyboard_layouts.names; - currentKeyboardLayoutIndex = data.keyboard_layouts.current_idx; - } - - function handleKeyboardLayoutSwitched(data) { - currentKeyboardLayoutIndex = data.idx; - } - - function handleWorkspaceUrgencyChanged(data) { - const ws = root.workspaces[data.id]; - if (!ws) - return; - const updatedWs = {}; - for (let prop in ws) { - updatedWs[prop] = ws[prop]; - } - updatedWs.is_urgent = data.urgent; - - const updatedWorkspaces = {}; - for (const id in root.workspaces) { - updatedWorkspaces[id] = id === data.id ? updatedWs : root.workspaces[id]; - } - setWorkspaces(updatedWorkspaces); - - windowUrgentChanged(); - } - - function handleWindowUrgencyChanged(data) { - const windowIndex = windows.findIndex(w => w.id === data.id); - if (windowIndex < 0) - return; - const updatedWindows = [...windows]; - const updatedWindow = {}; - for (let prop in updatedWindows[windowIndex]) { - updatedWindow[prop] = updatedWindows[windowIndex][prop]; - } - updatedWindow.is_urgent = data.urgent; - updatedWindows[windowIndex] = updatedWindow; - windows = updatedWindows; - - windowUrgentChanged(); - } - - function handleScreenshotCaptured(data) { - if (!data.path) - return; - if (pendingScreenshotPath && data.path === pendingScreenshotPath) { - const editor = Quickshell.env("DMS_SCREENSHOT_EDITOR"); - let command; - if (editor === "satty" || !editor) { - command = ["satty", "-f", data.path]; - } else if (editor === "swappy") { - command = ["swappy", "-f", data.path]; - } else { - // Custom command with %path% placeholder - command = editor.split(" ").map(arg => arg === "%path%" ? data.path : arg); - } - Quickshell.execDetached({ - "command": command - }); - pendingScreenshotPath = ""; - } - } - - function handleCastsChanged(data) { - casts = data.casts || []; - } - - function handleCastStartedOrChanged(data) { - if (!data.cast) - return; - const cast = data.cast; - const existingIndex = casts.findIndex(c => c.stream_id === cast.stream_id); - if (existingIndex >= 0) { - const updatedCasts = [...casts]; - updatedCasts[existingIndex] = cast; - casts = updatedCasts; - } else { - casts = [...casts, cast]; - } - } - - function handleCastStopped(data) { - casts = casts.filter(c => c.stream_id !== data.stream_id); - } - - function updateCurrentOutputWorkspaces() { - if (!currentOutput) { - currentOutputWorkspaces = allWorkspaces; - return; - } - - const outputWs = allWorkspaces.filter(w => w.output === currentOutput); - currentOutputWorkspaces = outputWs; - } - - function send(request) { - if (!CompositorService.isNiri || !requestSocket.connected) - return false; - requestSocket.send(request); - return true; - } - - function doScreenTransition() { - return send({ - "Action": { - "DoScreenTransition": { - "delay_ms": 0 - } - } - }); - } - - function toggleOverview() { - return send({ - "Action": { - "ToggleOverview": {} - } - }); - } - - function moveColumnLeft() { - return send({ - "Action": { - "FocusColumnLeft": {} - } - }); - } - - function moveColumnRight() { - return send({ - "Action": { - "FocusColumnRight": {} - } - }); - } - - function moveWorkspaceDown() { - return send({ - "Action": { - "FocusWorkspaceDown": {} - } - }); - } - - function moveWorkspaceUp() { - return send({ - "Action": { - "FocusWorkspaceUp": {} - } - }); - } - - function switchToWorkspace(workspaceIndex) { - return send({ - "Action": { - "FocusWorkspace": { - "reference": { - "Index": workspaceIndex - } - } - } - }); - } - - function focusWindow(windowId) { - return send({ - "Action": { - "FocusWindow": { - "id": windowId - } - } - }); - } - - function powerOffMonitors() { - return send({ - "Action": { - "PowerOffMonitors": {} - } - }); - } - - function powerOnMonitors() { - return send({ - "Action": { - "PowerOnMonitors": {} - } - }); - } - - function cycleKeyboardLayout() { - return send({ - "Action": { - "SwitchLayout": { - "layout": "Next" - } - } - }); - } - - function quit() { - return send({ - "Action": { - "Quit": { - "skip_confirmation": true - } - } - }); - } - - function screenshot() { - pendingScreenshotPath = ""; - const timestamp = Date.now(); - const path = `${screenshotsDir}/dms-screenshot-${timestamp}.png`; - pendingScreenshotPath = path; - - return send({ - "Action": { - "Screenshot": { - "show_pointer": true, - "path": path - } - } - }); - } - - function screenshotScreen() { - pendingScreenshotPath = ""; - const timestamp = Date.now(); - const path = `${screenshotsDir}/dms-screenshot-${timestamp}.png`; - pendingScreenshotPath = path; - - return send({ - "Action": { - "ScreenshotScreen": { - "write_to_disk": true, - "show_pointer": true, - "path": path - } - } - }); - } - - function screenshotWindow() { - pendingScreenshotPath = ""; - const timestamp = Date.now(); - const path = `${screenshotsDir}/dms-screenshot-${timestamp}.png`; - pendingScreenshotPath = path; - - return send({ - "Action": { - "ScreenshotWindow": { - "write_to_disk": true, - "show_pointer": true, - "path": path - } - } - }); - } - - function getCurrentOutputWorkspaceNumbers() { - return currentOutputWorkspaces.map(w => w.idx + 1); - } - - function getCurrentOutputWorkspaces() { - return currentOutputWorkspaces.slice(); - } - - function getCurrentWorkspaceNumber() { - if (focusedWorkspaceIndex >= 0 && focusedWorkspaceIndex < allWorkspaces.length) { - return allWorkspaces[focusedWorkspaceIndex].idx; - } - return 1; - } - - function getCurrentKeyboardLayoutName() { - if (currentKeyboardLayoutIndex >= 0 && currentKeyboardLayoutIndex < keyboardLayoutNames.length) { - return keyboardLayoutNames[currentKeyboardLayoutIndex]; - } - return ""; - } - - function suppressNextToast() { - matugenSuppression = true; - suppressResetTimer.restart(); - } - - function findNiriWindow(toplevel) { - if (!toplevel.appId) - return null; - - for (var j = 0; j < windows.length; j++) { - const niriWindow = windows[j]; - if (niriWindow.app_id === toplevel.appId) { - if (!niriWindow.title || niriWindow.title === toplevel.title) { - return { - "niriIndex": j, - "niriWindow": niriWindow - }; - } - } - } - return null; - } - - function sortToplevels(toplevels) { - if (!toplevels || toplevels.length === 0 || !CompositorService.isNiri || windows.length === 0) { - return [...toplevels]; - } - - const usedToplevels = new Set(); - const enrichedToplevels = []; - - for (const niriWindow of sortWindowsByLayout(windows)) { - let bestMatch = null; - let bestScore = -1; - - for (const toplevel of toplevels) { - if (usedToplevels.has(toplevel)) - continue; - if (toplevel.appId === niriWindow.app_id) { - let score = 1; - - if (niriWindow.title && toplevel.title) { - if (toplevel.title === niriWindow.title) { - score = 3; - } else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) { - score = 2; - } - } - - if (score > bestScore) { - bestScore = score; - bestMatch = toplevel; - if (score === 3) - break; - } - } - } - - if (!bestMatch) - continue; - usedToplevels.add(bestMatch); - - const workspace = workspaces[niriWindow.workspace_id]; - const isFocused = niriWindow.is_focused ?? (workspace && workspace.active_window_id === niriWindow.id) ?? false; - - const enrichedToplevel = { - "appId": bestMatch.appId, - "title": bestMatch.title, - "activated": isFocused, - "niriWindowId": niriWindow.id, - "niriWorkspaceId": niriWindow.workspace_id, - "activate": function () { - return NiriService.focusWindow(niriWindow.id); - }, - "close": function () { - if (bestMatch.close) { - return bestMatch.close(); - } - return false; - } - }; - - for (let prop in bestMatch) { - if (!(prop in enrichedToplevel)) { - enrichedToplevel[prop] = bestMatch[prop]; - } - } - - enrichedToplevels.push(enrichedToplevel); - } - - for (const toplevel of toplevels) { - if (!usedToplevels.has(toplevel)) { - enrichedToplevels.push(toplevel); - } - } - - return enrichedToplevels; - } - - function _matchAndEnrichToplevels(toplevels, niriWindows) { - const usedToplevels = new Set(); - const result = []; - - for (const niriWindow of niriWindows) { - let bestMatch = null; - let bestScore = -1; - - for (const toplevel of toplevels) { - if (usedToplevels.has(toplevel)) - continue; - if (toplevel.appId !== niriWindow.app_id) - continue; - - let score = 1; - if (niriWindow.title && toplevel.title) { - if (toplevel.title === niriWindow.title) { - score = 3; - } else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) { - score = 2; - } - } - - if (score > bestScore) { - bestScore = score; - bestMatch = toplevel; - if (score === 3) - break; - } - } - - if (!bestMatch) - continue; - usedToplevels.add(bestMatch); - - const workspace = workspaces[niriWindow.workspace_id]; - const isFocused = niriWindow.is_focused ?? (workspace && workspace.active_window_id === niriWindow.id) ?? false; - - const enrichedToplevel = { - "appId": bestMatch.appId, - "title": bestMatch.title, - "activated": isFocused, - "niriWindowId": niriWindow.id, - "niriWorkspaceId": niriWindow.workspace_id, - "activate": function () { - return NiriService.focusWindow(niriWindow.id); - }, - "close": function () { - if (bestMatch.close) - return bestMatch.close(); - return false; - } - }; - - for (let prop in bestMatch) { - if (!(prop in enrichedToplevel)) - enrichedToplevel[prop] = bestMatch[prop]; - } - - result.push(enrichedToplevel); - } - - return result; - } - - function filterCurrentWorkspace(toplevels, screenName) { - let currentWorkspaceId = null; - - for (var i = 0; i < allWorkspaces.length; i++) { - const ws = allWorkspaces[i]; - if (ws.output === screenName && ws.is_active) { - currentWorkspaceId = ws.id; - break; - } - } - - if (currentWorkspaceId === null) - return toplevels; - - if (toplevels.length > 0 && toplevels[0].niriWorkspaceId !== undefined) - return toplevels.filter(t => t.niriWorkspaceId === currentWorkspaceId); - - return _matchAndEnrichToplevels(toplevels, windows.filter(nw => nw.workspace_id === currentWorkspaceId)); - } - - function filterCurrentDisplay(toplevels, screenName) { - if (!toplevels || toplevels.length === 0 || !screenName) - return toplevels; - - const outputWorkspaceIds = new Set(); - for (var i = 0; i < allWorkspaces.length; i++) { - const ws = allWorkspaces[i]; - if (ws.output === screenName) - outputWorkspaceIds.add(ws.id); - } - - if (outputWorkspaceIds.size === 0) - return toplevels; - - if (toplevels.length > 0 && toplevels[0].niriWorkspaceId !== undefined) - return toplevels.filter(t => outputWorkspaceIds.has(t.niriWorkspaceId)); - - return _matchAndEnrichToplevels(toplevels, windows.filter(nw => outputWorkspaceIds.has(nw.workspace_id))); - } - - function generateNiriLayoutConfig() { - if (!CompositorService.isNiri || configGenerationPending) - return; - suppressNextToast(); - configGenerationPending = true; - configGenerationDebounce.restart(); - } - - function doGenerateNiriLayoutConfig() { - console.log("NiriService: Generating layout config..."); - - const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12; - const defaultGaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4; - const defaultBorderSize = 2; - - const cornerRadius = (typeof SettingsData !== "undefined" && SettingsData.niriLayoutRadiusOverride >= 0) ? SettingsData.niriLayoutRadiusOverride : defaultRadius; - const gaps = (typeof SettingsData !== "undefined" && SettingsData.niriLayoutGapsOverride >= 0) ? SettingsData.niriLayoutGapsOverride : defaultGaps; - const borderSize = (typeof SettingsData !== "undefined" && SettingsData.niriLayoutBorderSize >= 0) ? SettingsData.niriLayoutBorderSize : defaultBorderSize; - - const dmsWarning = `// ! DO NOT EDIT ! - // ! AUTO-GENERATED BY DMS ! - // ! CHANGES WILL BE OVERWRITTEN ! - // ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE ! - - `; - - const configContent = dmsWarning + `layout { - gaps ${gaps} - - border { - width ${borderSize} - } - - focus-ring { - width ${borderSize} - } - } - window-rule { - geometry-corner-radius ${cornerRadius} - clip-to-geometry true - tiled-state true - draw-border-with-background false - }`; - - const alttabContent = dmsWarning + `recent-windows { - highlight { - corner-radius ${cornerRadius} - } - }`; - - const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)); - const niriDmsDir = configDir + "/niri/dms"; - const configPath = niriDmsDir + "/layout.kdl"; - const alttabPath = niriDmsDir + "/alttab.kdl"; - - writeConfigProcess.configContent = configContent; - writeConfigProcess.configPath = configPath; - writeConfigProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${configPath}" << 'EOF'\n${configContent}\nEOF`]; - writeConfigProcess.running = true; - - writeAlttabProcess.alttabContent = alttabContent; - writeAlttabProcess.alttabPath = alttabPath; - writeAlttabProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${alttabPath}" << 'EOF'\n${alttabContent}\nEOF`]; - writeAlttabProcess.running = true; - - for (const name of ["outputs", "binds", "cursor", "windowrules", "colors", "alttab", "layout"]) { - const path = niriDmsDir + "/" + name + ".kdl"; - Proc.runCommand("niri-ensure-" + name, ["sh", "-c", `mkdir -p "${niriDmsDir}" && [ ! -f "${path}" ] && touch "${path}" || true`], (output, exitCode) => { - if (exitCode !== 0) - console.warn("NiriService: Failed to ensure " + name + ".kdl, exit code:", exitCode); - }); - } - - configGenerationPending = false; - } - - function generateNiriBlurrule() { - console.log("NiriService: Generating wpblur config..."); - - const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)); - const niriDmsDir = configDir + "/niri/dms"; - const blurrulePath = niriDmsDir + "/wpblur.kdl"; - const sourceBlurrulePath = Paths.strip(Qt.resolvedUrl("niri-wpblur.kdl")); - - writeBlurruleProcess.blurrulePath = blurrulePath; - writeBlurruleProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cp --no-preserve=mode "${sourceBlurrulePath}" "${blurrulePath}"`]; - writeBlurruleProcess.running = true; - } - - function generateNiriCursorConfig() { - if (!CompositorService.isNiri) - return; - - console.log("NiriService: Generating cursor config..."); - - const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)); - const niriDmsDir = configDir + "/niri/dms"; - const cursorPath = niriDmsDir + "/cursor.kdl"; - - const settings = typeof SettingsData !== "undefined" ? SettingsData.cursorSettings : null; - if (!settings) { - writeCursorProcess.cursorContent = ""; - writeCursorProcess.cursorPath = cursorPath; - writeCursorProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && : > "${cursorPath}"`]; - writeCursorProcess.running = true; - return; - } - - const themeName = settings.theme === "System Default" ? (SettingsData.systemDefaultCursorTheme || "") : settings.theme; - const size = settings.size || 24; - const hideWhenTyping = settings.niri?.hideWhenTyping || false; - const hideAfterMs = settings.niri?.hideAfterInactiveMs || 0; - - const isDefaultConfig = !themeName && size === 24 && !hideWhenTyping && hideAfterMs === 0; - if (isDefaultConfig) { - writeCursorProcess.cursorContent = ""; - writeCursorProcess.cursorPath = cursorPath; - writeCursorProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && : > "${cursorPath}"`]; - writeCursorProcess.running = true; - return; - } - - const dmsWarning = `// ! DO NOT EDIT ! -// ! AUTO-GENERATED BY DMS ! -// ! CHANGES WILL BE OVERWRITTEN ! -// ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE ! - -`; - - let cursorContent = dmsWarning + `cursor {\n`; - - if (themeName) - cursorContent += ` xcursor-theme "${themeName}"\n`; - - cursorContent += ` xcursor-size ${size}\n`; - - if (hideWhenTyping) - cursorContent += ` hide-when-typing\n`; - - if (hideAfterMs > 0) - cursorContent += ` hide-after-inactive-ms ${hideAfterMs}\n`; - - cursorContent += `}`; - - writeCursorProcess.cursorContent = cursorContent; - writeCursorProcess.cursorPath = cursorPath; - - const escapedCursorContent = cursorContent.replace(/'/g, "'\\''"); - - writeCursorProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && printf '%s' '${escapedCursorContent}' > "${cursorPath}"`]; - writeCursorProcess.running = true; - } - - function updateOutputPosition(outputName, x, y) { - if (!outputs || !outputs[outputName]) - return; - const updatedOutputs = {}; - for (const name in outputs) { - const output = outputs[name]; - if (name === outputName && output.logical) { - updatedOutputs[name] = JSON.parse(JSON.stringify(output)); - updatedOutputs[name].logical.x = x; - updatedOutputs[name].logical.y = y; - } else { - updatedOutputs[name] = output; - } - } - outputs = updatedOutputs; - } - - function applyOutputConfig(outputName, config, callback) { - if (!CompositorService.isNiri || !outputName) { - if (callback) - callback(false, "Invalid config"); - return; - } - - const commands = []; - - if (config.position !== undefined) { - commands.push(`niri msg output "${outputName}" position ${config.position.x} ${config.position.y}`); - } - - if (config.mode !== undefined) { - commands.push(`niri msg output "${outputName}" mode ${config.mode}`); - } - - if (config.vrr !== undefined) { - commands.push(`niri msg output "${outputName}" vrr ${config.vrr ? "on" : "off"}`); - } - - if (config.scale !== undefined) { - commands.push(`niri msg output "${outputName}" scale ${config.scale}`); - } - - if (config.transform !== undefined) { - commands.push(`niri msg output "${outputName}" transform "${config.transform}"`); - } - - if (commands.length === 0) { - if (callback) - callback(true, "No changes"); - return; - } - - const fullCommand = commands.join(" && "); - Proc.runCommand("niri-output-config", ["sh", "-c", fullCommand], (output, exitCode) => { - if (exitCode !== 0) { - console.warn("NiriService: Failed to apply output config:", output); - if (callback) - callback(false, output); - return; - } - console.info("NiriService: Applied output config for", outputName); - fetchOutputs(); - if (callback) - callback(true, "Success"); - }); - } - - function getOutputIdentifier(output, outputName) { - if (SettingsData.displayNameMode === "model" && output.make && output.model) { - const serial = output.serial || "Unknown"; - return output.make + " " + output.model + " " + serial; - } - return outputName; - } - - function generateOutputsConfig(outputsData) { - const data = outputsData || outputs; - if (!data || Object.keys(data).length === 0) - return; - let kdlContent = `// Auto-generated by DMS - do not edit manually\n\n`; - - const sortedNames = Object.keys(data).sort((a, b) => { - const la = data[a].logical || {}; - const lb = data[b].logical || {}; - return (la.x ?? 0) - (lb.x ?? 0) || (la.y ?? 0) - (lb.y ?? 0); - }); - for (const outputName of sortedNames) { - const output = data[outputName]; - const identifier = getOutputIdentifier(output, outputName); - const niriSettings = SettingsData.getNiriOutputSettings(identifier); - - kdlContent += `output "${identifier}" {\n`; - - if (niriSettings.disabled) { - kdlContent += ` off\n`; - } - - if (output.current_mode !== undefined && output.modes && output.modes[output.current_mode]) { - const mode = output.modes[output.current_mode]; - kdlContent += ` mode "${mode.width}x${mode.height}@${(mode.refresh_rate / 1000).toFixed(3)}"\n`; - } - - if (output.logical) { - kdlContent += ` scale ${output.logical.scale || 1.0}\n`; - - if (output.logical.transform && output.logical.transform !== "Normal") { - const transformMap = { - "Normal": "normal", - "90": "90", - "180": "180", - "270": "270", - "Flipped": "flipped", - "Flipped90": "flipped-90", - "Flipped180": "flipped-180", - "Flipped270": "flipped-270" - }; - kdlContent += ` transform "${transformMap[output.logical.transform] || "normal"}"\n`; - } - - if (output.logical.x !== undefined && output.logical.y !== undefined) { - kdlContent += ` position x=${output.logical.x} y=${output.logical.y}\n`; - } - } - - if (output.vrr_enabled || niriSettings.vrrOnDemand) { - const vrrOnDemand = niriSettings.vrrOnDemand ?? false; - kdlContent += vrrOnDemand ? ` variable-refresh-rate on-demand=true\n` : ` variable-refresh-rate\n`; - } - - if (niriSettings.focusAtStartup) { - kdlContent += ` focus-at-startup\n`; - } - - if (niriSettings.backdropColor) { - kdlContent += ` backdrop-color "${niriSettings.backdropColor}"\n`; - } - - kdlContent += generateHotCornersBlock(niriSettings); - kdlContent += generateLayoutBlock(niriSettings); - - kdlContent += `}\n\n`; - } - - const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)); - const niriDmsDir = configDir + "/niri/dms"; - const outputsPath = niriDmsDir + "/outputs.kdl"; - - Proc.runCommand("niri-write-outputs", ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${kdlContent}EOF`], (output, exitCode) => { - if (exitCode !== 0) { - console.warn("NiriService: Failed to write outputs config:", output); - return; - } - console.info("NiriService: Generated outputs config at", outputsPath); - }); - } - - function generateHotCornersBlock(niriSettings) { - if (!niriSettings.hotCorners) - return ""; - const hc = niriSettings.hotCorners; - if (hc.off) - return ` hot-corners {\n off\n }\n`; - const corners = hc.corners || []; - if (corners.length === 0) - return ""; - let block = ` hot-corners {\n`; - for (const corner of corners) { - block += ` ${corner}\n`; - } - block += ` }\n`; - return block; - } - - function generateLayoutBlock(niriSettings) { - if (!niriSettings.layout) - return ""; - const layout = niriSettings.layout; - const hasSettings = layout.gaps !== undefined || layout.defaultColumnWidth || layout.presetColumnWidths || layout.alwaysCenterSingleColumn !== undefined; - if (!hasSettings) - return ""; - let block = ` layout {\n`; - if (layout.gaps !== undefined) - block += ` gaps ${layout.gaps}\n`; - if (layout.defaultColumnWidth?.type === "proportion") { - const val = layout.defaultColumnWidth.value; - const formatted = Number.isInteger(val) ? val.toFixed(1) : val.toString(); - block += ` default-column-width { proportion ${formatted}; }\n`; - } - if (layout.presetColumnWidths && layout.presetColumnWidths.length > 0) { - block += ` preset-column-widths {\n`; - for (const preset of layout.presetColumnWidths) { - if (preset.type === "proportion") { - const val = preset.value; - const formatted = Number.isInteger(val) ? val.toFixed(1) : val.toString(); - block += ` proportion ${formatted}\n`; - } - } - block += ` }\n`; - } - if (layout.alwaysCenterSingleColumn !== undefined) - block += layout.alwaysCenterSingleColumn ? ` always-center-single-column\n` : ` always-center-single-column false\n`; - block += ` }\n`; - return block; - } - - function renameWorkspace(name) { - if (!name || name.trim() === "") { - return send({ - "Action": { - "UnsetWorkspaceName": { - "workspace": null - } - } - }); - } - return send({ - "Action": { - "SetWorkspaceName": { - "name": name, - "workspace": null - } - } - }); - } - - function moveWorkspaceToIndex(workspaceIdx, targetIndex) { - return send({ - "Action": { - "MoveWorkspaceToIndex": { - "index": targetIndex, - "reference": { - "Index": workspaceIdx - } - } - } - }); - } - - IpcHandler { - function screenshot(): string { - if (!CompositorService.isNiri) { - return "NIRI_NOT_AVAILABLE"; - } - if (NiriService.screenshot()) { - return "SCREENSHOT_SUCCESS"; - } - return "SCREENSHOT_FAILED"; - } - - function screenshotScreen(): string { - if (!CompositorService.isNiri) { - return "NIRI_NOT_AVAILABLE"; - } - if (NiriService.screenshotScreen()) { - return "SCREENSHOT_SCREEN_SUCCESS"; - } - return "SCREENSHOT_SCREEN_FAILED"; - } - - function screenshotWindow(): string { - if (!CompositorService.isNiri) { - return "NIRI_NOT_AVAILABLE"; - } - if (NiriService.screenshotWindow()) { - return "SCREENSHOT_WINDOW_SUCCESS"; - } - return "SCREENSHOT_WINDOW_FAILED"; - } - - target: "niri" - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NotepadStorageService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NotepadStorageService.qml deleted file mode 100644 index 3a6257a..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NotepadStorageService.qml +++ /dev/null @@ -1,473 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import QtCore -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property int refCount: 0 - - readonly property string baseDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericStateLocation) + "/DankMaterialShell") - readonly property string filesDir: baseDir + "/notepad-files" - readonly property string metadataPath: baseDir + "/notepad-session.json" - - property var tabs: [] - property int currentTabIndex: 0 - property var tabsBeingCreated: ({}) - property bool metadataLoaded: false - - Component.onCompleted: { - ensureDirectories() - } - - FileView { - id: metadataFile - path: root.refCount > 0 ? root.metadataPath : "" - blockWrites: true - atomicWrites: true - - onLoaded: { - try { - var data = JSON.parse(text()) - root.tabs = data.tabs || [] - root.currentTabIndex = data.currentTabIndex || 0 - root.metadataLoaded = true - root.validateTabs() - } catch(e) { - console.warn("Failed to parse notepad metadata:", e) - root.createDefaultTab() - } - } - - onLoadFailed: { - root.createDefaultTab() - } - } - - onRefCountChanged: { - if (refCount === 1 && !metadataLoaded) { - metadataFile.path = "" - metadataFile.path = root.metadataPath - } - } - - function ensureDirectories() { - mkdirProcess.running = true - } - - function loadMetadata() { - metadataFile.path = "" - metadataFile.path = root.metadataPath - } - - function createDefaultTab() { - var id = Date.now() - var filePath = "notepad-files/untitled-" + id + ".txt" - var fullPath = baseDir + "/" + filePath - - var newTabsBeingCreated = Object.assign({}, tabsBeingCreated) - newTabsBeingCreated[id] = true - tabsBeingCreated = newTabsBeingCreated - - root.createEmptyFile(fullPath, function() { - root.tabs = [{ - id: id, - title: I18n.tr("Untitled"), - filePath: filePath, - isTemporary: true, - lastModified: new Date().toISOString(), - cursorPosition: 0, - scrollPosition: 0 - }] - root.currentTabIndex = 0 - - var updatedTabsBeingCreated = Object.assign({}, tabsBeingCreated) - delete updatedTabsBeingCreated[id] - tabsBeingCreated = updatedTabsBeingCreated - root.saveMetadata() - }) - } - - function saveMetadata() { - var metadata = { - version: 1, - currentTabIndex: currentTabIndex, - tabs: tabs - } - metadataFile.setText(JSON.stringify(metadata, null, 2)) - } - - function getTabById(tabId) { - for (var i = 0; i < tabs.length; i++) { - if (tabs[i].id === tabId) - return tabs[i] - } - return null - } - - function loadTabContent(tabIndex, callback) { - if (tabIndex < 0 || tabIndex >= tabs.length) { - callback("") - return - } - - var tab = tabs[tabIndex] - var requestTabId = tab.id - var fullPath = tab.isTemporary - ? baseDir + "/" + tab.filePath - : tab.filePath - - if (tabsBeingCreated[tab.id]) { - Qt.callLater(() => { - loadTabContent(tabIndex, callback) - }) - return - } - - var fileChecker = fileExistsComponent.createObject(root, { - path: fullPath, - callback: (exists) => { - var currentTab = root.getTabById(requestTabId) - var currentPath = currentTab - ? (currentTab.isTemporary ? baseDir + "/" + currentTab.filePath : currentTab.filePath) - : "" - - if (!currentTab || currentPath !== fullPath) { - callback("") - return - } - - if (exists) { - var loader = tabFileLoaderComponent.createObject(root, { - path: fullPath, - callback: callback - }) - } else { - console.warn("Tab file does not exist:", fullPath) - callback("") - } - } - }) - } - - function saveTabContent(tabIndex, content) { - if (tabIndex < 0 || tabIndex >= tabs.length) return - - var tab = tabs[tabIndex] - var fullPath = tab.isTemporary - ? baseDir + "/" + tab.filePath - : tab.filePath - - var saver = tabFileSaverComponent.createObject(root, { - path: fullPath, - content: content, - tabIndex: tabIndex - }) - } - - function createNewTab() { - var id = Date.now() - var filePath = "notepad-files/untitled-" + id + ".txt" - var fullPath = baseDir + "/" + filePath - - var newTab = { - id: id, - title: I18n.tr("Untitled"), - filePath: filePath, - isTemporary: true, - lastModified: new Date().toISOString(), - cursorPosition: 0, - scrollPosition: 0 - } - - var newTabsBeingCreated = Object.assign({}, tabsBeingCreated) - newTabsBeingCreated[id] = true - tabsBeingCreated = newTabsBeingCreated - createEmptyFile(fullPath, function() { - var newTabs = tabs.slice() - newTabs.push(newTab) - tabs = newTabs - currentTabIndex = tabs.length - 1 - - var updatedTabsBeingCreated = Object.assign({}, tabsBeingCreated) - delete updatedTabsBeingCreated[id] - tabsBeingCreated = updatedTabsBeingCreated - saveMetadata() - }) - - return newTab - } - - function closeTab(tabIndex) { - if (tabIndex < 0 || tabIndex >= tabs.length) return - - var newTabs = tabs.slice() - - if (newTabs.length <= 1) { - var id = Date.now() - var filePath = "notepad-files/untitled-" + id + ".txt" - - var newTabsBeingCreated = Object.assign({}, tabsBeingCreated) - newTabsBeingCreated[id] = true - tabsBeingCreated = newTabsBeingCreated - createEmptyFile(baseDir + "/" + filePath, function() { - newTabs[0] = { - id: id, - title: I18n.tr("Untitled"), - filePath: filePath, - isTemporary: true, - lastModified: new Date().toISOString(), - cursorPosition: 0, - scrollPosition: 0 - } - currentTabIndex = 0 - tabs = newTabs - - var updatedTabsBeingCreated = Object.assign({}, tabsBeingCreated) - delete updatedTabsBeingCreated[id] - tabsBeingCreated = updatedTabsBeingCreated - saveMetadata() - }) - return - } else { - var tabToDelete = newTabs[tabIndex] - if (tabToDelete && tabToDelete.isTemporary) { - deleteFile(baseDir + "/" + tabToDelete.filePath) - } - - newTabs.splice(tabIndex, 1) - if (currentTabIndex >= newTabs.length) { - currentTabIndex = newTabs.length - 1 - } else if (currentTabIndex > tabIndex) { - currentTabIndex -= 1 - } - } - - tabs = newTabs - saveMetadata() - - } - - function switchToTab(tabIndex) { - if (tabIndex < 0 || tabIndex >= tabs.length) return - - currentTabIndex = tabIndex - saveMetadata() - } - - function reorderTab(fromIndex, toIndex) { - if (fromIndex < 0 || fromIndex >= tabs.length || toIndex < 0 || toIndex >= tabs.length) - return - if (fromIndex === toIndex) - return - - var newTabs = tabs.slice() - var moved = newTabs.splice(fromIndex, 1)[0] - newTabs.splice(toIndex, 0, moved) - tabs = newTabs - - if (currentTabIndex === fromIndex) { - currentTabIndex = toIndex - } else if (fromIndex < currentTabIndex && toIndex >= currentTabIndex) { - currentTabIndex-- - } else if (fromIndex > currentTabIndex && toIndex <= currentTabIndex) { - currentTabIndex++ - } - - saveMetadata() - } - - function saveTabAs(tabIndex, userPath) { - if (tabIndex < 0 || tabIndex >= tabs.length) return - - var tab = tabs[tabIndex] - var fileName = userPath.split('/').pop() - - if (tab.isTemporary) { - var tempPath = baseDir + "/" + tab.filePath - copyFile(tempPath, userPath) - deleteFile(tempPath) - } - - var newTabs = tabs.slice() - newTabs[tabIndex] = Object.assign({}, tab, { - title: fileName, - filePath: userPath, - isTemporary: false, - lastModified: new Date().toISOString() - }) - tabs = newTabs - saveMetadata() - - } - - function updateTabMetadata(tabIndex, properties) { - if (tabIndex < 0 || tabIndex >= tabs.length) return - - var newTabs = tabs.slice() - var updatedTab = Object.assign({}, newTabs[tabIndex], properties) - updatedTab.lastModified = new Date().toISOString() - newTabs[tabIndex] = updatedTab - tabs = newTabs - saveMetadata() - - } - - function validateTabs() { - var validTabs = [] - for (var i = 0; i < tabs.length; i++) { - var tab = tabs[i] - validTabs.push(tab) - } - tabs = validTabs - - if (tabs.length === 0) { - root.createDefaultTab() - } - } - - Component { - id: tabFileLoaderComponent - FileView { - property var callback - blockLoading: true - preload: true - - onLoaded: { - callback(text()) - destroy() - } - - onLoadFailed: { - callback("") - destroy() - } - } - } - - Component { - id: fileExistsComponent - Process { - property string path - property var callback - command: ["test", "-f", path] - - Component.onCompleted: running = true - - onExited: (exitCode) => { - callback(exitCode === 0) - destroy() - } - } - } - - Component { - id: tabFileSaverComponent - FileView { - property string content - property int tabIndex - property var creationCallback - - blockWrites: false - atomicWrites: true - - Component.onCompleted: setText(content) - - onSaved: { - if (tabIndex >= 0) { - root.updateTabMetadata(tabIndex, {}) - } - if (creationCallback) { - creationCallback() - } - destroy() - } - - onSaveFailed: { - console.error("Failed to save tab content") - if (creationCallback) { - creationCallback() - } - destroy() - } - } - } - - function createEmptyFile(path, callback) { - var cleanPath = decodeURI(path.toString()) - - if (!cleanPath.startsWith("/")) { - cleanPath = baseDir + "/" + cleanPath - } - - var creator = fileCreatorComponent.createObject(root, { - filePath: cleanPath, - creationCallback: callback - }) - } - - function copyFile(source, destination) { - copyProcess.source = source - copyProcess.destination = destination - copyProcess.running = true - } - - function deleteFile(path) { - deleteProcess.filePath = path - deleteProcess.running = true - } - - Component { - id: fileCreatorComponent - QtObject { - property string filePath - property var creationCallback - - Component.onCompleted: { - var touchProcess = touchProcessComponent.createObject(this, { - filePath: filePath, - callback: creationCallback - }) - } - } - } - - Component { - id: touchProcessComponent - Process { - property string filePath - property var callback - command: ["touch", filePath] - - Component.onCompleted: running = true - - onExited: (exitCode) => { - if (callback) callback() - destroy() - } - } - } - - Process { - id: copyProcess - property string source - property string destination - command: ["cp", source, destination] - } - - Process { - id: deleteProcess - property string filePath - command: ["rm", "-f", filePath] - } - - Process { - id: mkdirProcess - command: ["mkdir", "-p", root.baseDir, root.filesDir] - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NotificationService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NotificationService.qml deleted file mode 100644 index 0615b58..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/NotificationService.qml +++ /dev/null @@ -1,1237 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import Quickshell.Services.Notifications -import qs.Common -import "../Common/markdown2html.js" as Markdown2Html - -Singleton { - id: root - - readonly property list<NotifWrapper> notifications: [] - readonly property list<NotifWrapper> allWrappers: [] - readonly property list<NotifWrapper> popups: allWrappers.filter(n => n && n.popup) - - property var historyList: [] - readonly property string historyFile: Paths.strip(Paths.cache) + "/notification_history.json" - readonly property string imageCacheDir: Paths.strip(Paths.cache) + "/notification_images" - property bool historyLoaded: false - property int historyEntryCounter: 0 - - property list<NotifWrapper> notificationQueue: [] - property list<NotifWrapper> visibleNotifications: [] - property int maxVisibleNotifications: 4 - property bool addGateBusy: false - property int enterAnimMs: 400 - property int seqCounter: 0 - property bool bulkDismissing: false - - property int maxQueueSize: 32 - property int maxIngressPerSecond: 20 - property double _lastIngressSec: 0 - property int _ingressCountThisSec: 0 - - property var _dismissQueue: [] - property int _dismissBatchSize: 8 - property int _dismissTickMs: 8 - property bool _suspendGrouping: false - property var _groupCache: ({ - "notifications": [], - "popups": [] - }) - property bool _groupsDirty: false - - Component.onCompleted: { - _recomputeGroups(); - Quickshell.execDetached(["mkdir", "-p", Paths.strip(Paths.cache)]); - Quickshell.execDetached(["mkdir", "-p", imageCacheDir]); - } - - FileView { - id: historyFileView - path: root.historyFile - printErrors: false - onLoaded: root.loadHistory() - onLoadFailed: error => { - if (error === 2) { - root.historyLoaded = true; - historyFileView.writeAdapter(); - } - } - - JsonAdapter { - id: historyAdapter - property var notifications: [] - } - } - - Timer { - id: historySaveTimer - interval: 200 - onTriggered: root.performSaveHistory() - } - - function _makeHistoryEntryId(sourceId, timestamp) { - historyEntryCounter += 1; - const safeSource = sourceId && sourceId !== "" ? sourceId : "notification"; - return safeSource + "_" + (timestamp || Date.now()) + "_" + historyEntryCounter; - } - - function getImageCachePath(wrapper) { - const ts = wrapper.time ? wrapper.time.getTime() : Date.now(); - const id = wrapper.notification?.id?.toString() || "0"; - return imageCacheDir + "/notif_" + ts + "_" + id + ".png"; - } - - function updateHistoryImage(wrapperId, imagePath) { - const idx = historyList.findIndex(n => n.sourceNotificationId === wrapperId || n.id === wrapperId); - if (idx < 0) - return; - const item = historyList[idx]; - const updated = { - id: item.id, - sourceNotificationId: item.sourceNotificationId || item.id, - summary: item.summary, - body: item.body, - htmlBody: item.htmlBody, - appName: item.appName, - appIcon: item.appIcon, - image: "file://" + imagePath, - urgency: item.urgency, - timestamp: item.timestamp, - desktopEntry: item.desktopEntry - }; - const newList = historyList.slice(); - newList[idx] = updated; - historyList = newList; - saveHistory(); - } - - function addToHistory(wrapper) { - if (!wrapper) - return; - const urg = typeof wrapper.urgency === "number" ? wrapper.urgency : 1; - const imageUrl = wrapper.image || ""; - let persistableImage = ""; - if (wrapper.persistedImagePath) { - persistableImage = "file://" + wrapper.persistedImagePath; - } else if (imageUrl && !imageUrl.startsWith("image://qsimage/")) { - persistableImage = imageUrl; - } - const sourceNotificationId = wrapper.notification?.id?.toString() || ""; - const timestamp = wrapper.time.getTime(); - const data = { - id: _makeHistoryEntryId(sourceNotificationId, timestamp), - sourceNotificationId: sourceNotificationId, - summary: wrapper.summary || "", - body: wrapper.body || "", - htmlBody: wrapper.htmlBody || wrapper.body || "", - appName: wrapper.appName || "", - appIcon: wrapper.appIcon || "", - image: persistableImage, - urgency: urg, - timestamp: timestamp, - desktopEntry: wrapper.desktopEntry || "" - }; - let newList = [data, ...historyList]; - if (newList.length > SettingsData.notificationHistoryMaxCount) { - newList = newList.slice(0, SettingsData.notificationHistoryMaxCount); - } - historyList = newList; - saveHistory(); - } - - function saveHistory() { - historySaveTimer.restart(); - } - - function performSaveHistory() { - try { - historyAdapter.notifications = historyList; - historyFileView.writeAdapter(); - } catch (e) { - console.warn("NotificationService: save history failed:", e); - } - } - - function loadHistory() { - try { - const maxAgeDays = SettingsData.notificationHistoryMaxAgeDays; - const now = Date.now(); - const maxAgeMs = maxAgeDays > 0 ? maxAgeDays * 24 * 60 * 60 * 1000 : 0; - const loaded = []; - const seenIds = {}; - let needsRewrite = false; - - for (const item of historyAdapter.notifications || []) { - if (maxAgeMs > 0 && (now - item.timestamp) > maxAgeMs) - continue; - const urg = typeof item.urgency === "number" ? item.urgency : 1; - const body = item.body || ""; - let htmlBody = item.htmlBody || _resolveHtmlBody(body); - if (htmlBody) { - htmlBody = htmlBody.replace(/<img\b[^>]*>/gi, ""); - } - const sourceNotificationId = (item.sourceNotificationId || item.id || "").toString(); - let historyId = (item.id || "").toString(); - if (!historyId || seenIds[historyId]) { - historyId = _makeHistoryEntryId(sourceNotificationId, item.timestamp || now); - needsRewrite = true; - } - if (!item.sourceNotificationId) - needsRewrite = true; - seenIds[historyId] = true; - loaded.push({ - id: historyId, - sourceNotificationId: sourceNotificationId, - summary: item.summary || "", - body: body, - htmlBody: htmlBody, - appName: item.appName || "", - appIcon: item.appIcon || "", - image: item.image || "", - urgency: urg, - timestamp: item.timestamp || 0, - desktopEntry: item.desktopEntry || "" - }); - } - historyList = loaded; - historyLoaded = true; - if ((maxAgeMs > 0 && loaded.length !== (historyAdapter.notifications || []).length) || needsRewrite) - saveHistory(); - } catch (e) { - console.warn("NotificationService: load history failed:", e); - historyLoaded = true; - } - } - - function _deleteCachedImage(imagePath) { - if (!imagePath || !imagePath.startsWith("file://")) - return; - const filePath = imagePath.replace("file://", ""); - if (filePath.startsWith(imageCacheDir)) { - Quickshell.execDetached(["rm", "-f", filePath]); - } - } - - function removeFromHistory(notificationId) { - const idx = historyList.findIndex(n => n.id === notificationId); - if (idx >= 0) { - _deleteCachedImage(historyList[idx].image); - historyList = historyList.filter((_, i) => i !== idx); - saveHistory(); - return true; - } - return false; - } - - function clearHistory() { - for (const item of historyList) { - _deleteCachedImage(item.image); - } - historyList = []; - saveHistory(); - } - - function getHistoryTimeRange(timestamp) { - const now = new Date(); - const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); - const itemDate = new Date(timestamp); - const itemDay = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); - const diffDays = Math.floor((today - itemDay) / (1000 * 60 * 60 * 24)); - if (diffDays === 0) - return 0; - if (diffDays === 1) - return 1; - return 2; - } - - function getHistoryCountForRange(range) { - if (range === -1) - return historyList.length; - return historyList.filter(n => getHistoryTimeRange(n.timestamp) === range).length; - } - - function formatHistoryTime(timestamp) { - root.timeUpdateTick; - root.clockFormatChanged; - const now = new Date(); - const date = new Date(timestamp); - const diff = now.getTime() - timestamp; - const minutes = Math.floor(diff / 60000); - const hours = Math.floor(minutes / 60); - if (hours < 1) { - if (minutes < 1) - return I18n.tr("now"); - return I18n.tr("%1m ago").arg(minutes); - } - const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()); - const itemDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); - const daysDiff = Math.floor((nowDate - itemDate) / (1000 * 60 * 60 * 24)); - const timeStr = SettingsData.use24HourClock ? date.toLocaleTimeString(Qt.locale(), "HH:mm") : date.toLocaleTimeString(Qt.locale(), "h:mm AP"); - if (daysDiff === 0) - return timeStr; - try { - const localeName = (typeof I18n !== "undefined" && I18n.locale) ? I18n.locale().name : "en-US"; - const weekday = date.toLocaleDateString(localeName, { - weekday: "long" - }); - return weekday + ", " + timeStr; - } catch (e) { - return timeStr; - } - } - - function _nowSec() { - return Date.now() / 1000.0; - } - - function _ingressAllowed(urgency) { - const t = _nowSec(); - if (t - _lastIngressSec >= 1.0) { - _lastIngressSec = t; - _ingressCountThisSec = 0; - } - _ingressCountThisSec += 1; - if (urgency === NotificationUrgency.Critical) { - return true; - } - return _ingressCountThisSec <= maxIngressPerSecond; - } - - function _enqueuePopup(wrapper) { - if (notificationQueue.length >= maxQueueSize) { - const gk = getGroupKey(wrapper); - let idx = notificationQueue.findIndex(w => w && getGroupKey(w) === gk && w.urgency !== NotificationUrgency.Critical); - if (idx === -1) { - idx = notificationQueue.findIndex(w => w && w.urgency !== NotificationUrgency.Critical); - } - if (idx === -1) { - idx = 0; - } - const victim = notificationQueue[idx]; - if (victim) { - victim.popup = false; - } - notificationQueue.splice(idx, 1); - } - notificationQueue = [...notificationQueue, wrapper]; - } - - function _initWrapperPersistence(wrapper) { - const timeoutMs = wrapper.timer ? wrapper.timer.interval : 5000; - const isCritical = wrapper && wrapper.urgency === NotificationUrgency.Critical; - wrapper.isPersistent = isCritical || (timeoutMs === 0); - } - - function _shouldSaveToHistory(urgency, forceDisable) { - if (forceDisable === true) - return false; - if (!SettingsData.notificationHistoryEnabled) - return false; - switch (urgency) { - case NotificationUrgency.Low: - return SettingsData.notificationHistorySaveLow; - case NotificationUrgency.Critical: - return SettingsData.notificationHistorySaveCritical; - default: - return SettingsData.notificationHistorySaveNormal; - } - } - - function _resolveAppNameForRule(notif) { - if (!notif) - return ""; - if (notif.appName && notif.appName !== "") - return notif.appName; - const entry = DesktopEntries.heuristicLookup(notif.desktopEntry); - if (entry && entry.name) - return entry.name; - return ""; - } - - function _ruleFieldValue(field, info) { - switch ((field || "").toString()) { - case "desktopEntry": - return info.desktopEntry; - case "summary": - return info.summary; - case "body": - return info.body; - case "appName": - default: - return info.appName; - } - } - - function _coerceRuleUrgency(value, fallbackUrgency) { - if (typeof value === "number" && value >= NotificationUrgency.Low && value <= NotificationUrgency.Critical) - return value; - - const mapped = (value || "default").toString().toLowerCase(); - switch (mapped) { - case "low": - return NotificationUrgency.Low; - case "normal": - return NotificationUrgency.Normal; - case "critical": - return NotificationUrgency.Critical; - default: - return fallbackUrgency; - } - } - - function _matchesNotificationRule(rule, info) { - if (!rule) - return false; - if (rule.enabled === false) - return false; - - const pattern = (rule.pattern || "").toString(); - if (!pattern.trim()) - return false; - - const value = (_ruleFieldValue(rule.field, info) || "").toString(); - const matchType = (rule.matchType || "contains").toString().toLowerCase(); - - if (matchType === "exact") - return value.toLowerCase() === pattern.toLowerCase(); - if (matchType === "regex") { - try { - return new RegExp(pattern, "i").test(value); - } catch (e) { - console.warn("NotificationService: invalid notification rule regex:", pattern); - return false; - } - } - - return value.toLowerCase().includes(pattern.toLowerCase()); - } - - function _evaluateNotificationPolicy(notif) { - const baseUrgency = typeof notif.urgency === "number" ? notif.urgency : NotificationUrgency.Normal; - const policy = { - "drop": false, - "disablePopup": false, - "hideFromCenter": false, - "disableHistory": false, - "urgency": baseUrgency - }; - - const rules = SettingsData.notificationRules || []; - if (!rules.length) - return policy; - - const info = { - "appName": _resolveAppNameForRule(notif), - "desktopEntry": notif.desktopEntry || "", - "summary": notif.summary || "", - "body": notif.body || "" - }; - - for (const rule of rules) { - if (!_matchesNotificationRule(rule, info)) - continue; - - const action = (rule.action || "default").toString().toLowerCase(); - switch (action) { - case "ignore": - policy.drop = true; - break; - case "mute": - policy.disablePopup = true; - break; - case "popup_only": - policy.hideFromCenter = true; - policy.disableHistory = true; - break; - case "no_history": - policy.disableHistory = true; - break; - default: - break; - } - - policy.urgency = _coerceRuleUrgency(rule.urgency, policy.urgency); - return policy; - } - - return policy; - } - - function pruneHistory() { - const maxAgeDays = SettingsData.notificationHistoryMaxAgeDays; - if (maxAgeDays <= 0) - return; - - const now = Date.now(); - const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1000; - const toRemove = historyList.filter(item => (now - item.timestamp) > maxAgeMs); - const pruned = historyList.filter(item => (now - item.timestamp) <= maxAgeMs); - - if (pruned.length !== historyList.length) { - for (const item of toRemove) { - _deleteCachedImage(item.image); - } - historyList = pruned; - saveHistory(); - } - } - - function deleteHistory() { - for (const item of historyList) { - _deleteCachedImage(item.image); - } - historyList = []; - historyAdapter.notifications = []; - historyFileView.writeAdapter(); - } - - function onOverlayOpen() { - popupsDisabled = true; - addGate.stop(); - addGateBusy = false; - - notificationQueue = []; - for (const w of visibleNotifications) { - if (w) { - w.popup = false; - } - } - visibleNotifications = []; - _recomputeGroupsLater(); - pruneHistory(); - } - - function onOverlayClose() { - popupsDisabled = false; - processQueue(); - } - - Timer { - id: addGate - interval: 80 - running: false - repeat: false - onTriggered: { - addGateBusy = false; - processQueue(); - } - } - - Timer { - id: timeUpdateTimer - interval: 30000 - repeat: true - running: root.allWrappers.length > 0 || visibleNotifications.length > 0 - triggeredOnStart: false - onTriggered: { - root.timeUpdateTick = !root.timeUpdateTick; - } - } - - Timer { - id: dismissPump - interval: _dismissTickMs - repeat: true - running: false - onTriggered: { - let n = Math.min(_dismissBatchSize, _dismissQueue.length); - for (var i = 0; i < n; ++i) { - const w = _dismissQueue.pop(); - try { - if (w && w.notification) { - w.notification.dismiss(); - } - } catch (e) {} - } - if (_dismissQueue.length === 0) { - dismissPump.stop(); - _suspendGrouping = false; - bulkDismissing = false; - popupsDisabled = false; - _recomputeGroupsLater(); - } - } - } - - Timer { - id: groupsDebounce - interval: 16 - repeat: false - onTriggered: _recomputeGroups() - } - - property bool timeUpdateTick: false - property bool clockFormatChanged: false - - readonly property var groupedNotifications: _groupCache.notifications - readonly property var groupedPopups: _groupCache.popups - - property var expandedGroups: ({}) - property var expandedMessages: ({}) - property bool popupsDisabled: false - - NotificationServer { - id: server - - keepOnReload: false - actionsSupported: true - actionIconsSupported: true - bodyHyperlinksSupported: true - bodyImagesSupported: true - bodyMarkupSupported: true - imageSupported: true - inlineReplySupported: true - persistenceSupported: true - - onNotification: notif => { - notif.tracked = true; - - const policy = _evaluateNotificationPolicy(notif); - if (policy.drop) { - try { - notif.dismiss(); - } catch (e) {} - return; - } - - if (!_ingressAllowed(policy.urgency)) { - if (policy.urgency !== NotificationUrgency.Critical) { - try { - notif.dismiss(); - } catch (e) {} - return; - } - } - - if (SettingsData.soundsEnabled && SettingsData.soundNewNotification) { - if (policy.urgency === NotificationUrgency.Critical) { - AudioService.playCriticalNotificationSound(); - } else { - AudioService.playNormalNotificationSound(); - } - } - - const shouldShowPopup = !root.popupsDisabled && !SessionData.doNotDisturb && !policy.disablePopup; - const isTransient = notif.transient; - const shouldKeepInCenter = !isTransient && !policy.hideFromCenter; - - if (!shouldShowPopup && !shouldKeepInCenter) { - try { - notif.dismiss(); - } catch (e) {} - return; - } - - const wrapper = notifComponent.createObject(root, { - "popup": shouldShowPopup, - "notification": notif, - "urgencyOverride": policy.urgency - }); - - if (wrapper) { - root.allWrappers.push(wrapper); - if (shouldKeepInCenter) { - root.notifications.push(wrapper); - if (_shouldSaveToHistory(wrapper.urgency, policy.disableHistory)) { - root.addToHistory(wrapper); - } - } - Qt.callLater(() => { - _initWrapperPersistence(wrapper); - }); - - if (shouldShowPopup) { - _enqueuePopup(wrapper); - processQueue(); - } - } - - _recomputeGroupsLater(); - } - } - - component NotifWrapper: QtObject { - id: wrapper - - property bool popup: false - property bool removedByLimit: false - property bool isPersistent: true - property int seq: 0 - property string persistedImagePath: "" - - onPopupChanged: { - if (!popup) { - removeFromVisibleNotifications(wrapper); - } - } - - readonly property Timer timer: Timer { - interval: { - if (!wrapper.notification) - return 5000; - switch (wrapper.urgency) { - case NotificationUrgency.Low: - return SettingsData.notificationTimeoutLow; - case NotificationUrgency.Critical: - return SettingsData.notificationTimeoutCritical; - default: - return SettingsData.notificationTimeoutNormal; - } - } - repeat: false - running: false - onTriggered: { - if (interval > 0) { - wrapper.popup = false; - } - } - } - - readonly property date time: new Date() - readonly property string timeStr: { - root.timeUpdateTick; - root.clockFormatChanged; - - const now = new Date(); - const diff = now.getTime() - time.getTime(); - const minutes = Math.floor(diff / 60000); - const hours = Math.floor(minutes / 60); - - if (hours < 1) { - if (minutes < 1) { - return "now"; - } - return `${minutes}m ago`; - } - - const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()); - const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate()); - const daysDiff = Math.floor((nowDate - timeDate) / (1000 * 60 * 60 * 24)); - - if (daysDiff === 0) { - return formatTime(time); - } - - try { - const localeName = (typeof I18n !== "undefined" && I18n.locale) ? I18n.locale().name : "en-US"; - const weekday = time.toLocaleDateString(localeName, { - weekday: "long" - }); - return `${weekday}, ${formatTime(time)}`; - } catch (e) { - return formatTime(time); - } - } - - function formatTime(date) { - let use24Hour = true; - try { - if (typeof SettingsData !== "undefined" && SettingsData.use24HourClock !== undefined) { - use24Hour = SettingsData.use24HourClock; - } - } catch (e) { - use24Hour = true; - } - - if (use24Hour) { - return date.toLocaleTimeString(Qt.locale(), "HH:mm"); - } else { - return date.toLocaleTimeString(Qt.locale(), "h:mm AP"); - } - } - - required property Notification notification - readonly property string summary: (notification?.summary ?? "").replace(/<img\b[^>]*>/gi, "") - readonly property string body: (notification?.body ?? "").replace(/<img\b[^>]*>/gi, "") - readonly property string htmlBody: root._resolveHtmlBody(body) - readonly property string appIcon: notification?.appIcon ?? "" - readonly property string appName: { - if (!notification) - return "app"; - if (notification.appName == "") { - const entry = DesktopEntries.heuristicLookup(notification.desktopEntry); - if (entry && entry.name) - return entry.name.toLowerCase(); - } - return notification.appName || "app"; - } - readonly property string desktopEntry: notification?.desktopEntry ?? "" - readonly property string image: notification?.image ?? "" - readonly property string cleanImage: { - if (!image) - return ""; - return Paths.strip(image); - } - property int urgencyOverride: notification?.urgency ?? NotificationUrgency.Normal - readonly property int urgency: urgencyOverride - readonly property list<NotificationAction> actions: notification?.actions ?? [] - - readonly property Connections conn: Connections { - target: wrapper.notification?.Retainable ?? null - - function onDropped(): void { - root.allWrappers = root.allWrappers.filter(w => w !== wrapper); - root.notifications = root.notifications.filter(w => w !== wrapper); - - if (root.bulkDismissing) { - return; - } - - const groupKey = getGroupKey(wrapper); - const remainingInGroup = root.notifications.filter(n => getGroupKey(n) === groupKey); - - if (remainingInGroup.length <= 1) { - clearGroupExpansionState(groupKey); - } - - cleanupExpansionStates(); - root._recomputeGroupsLater(); - } - - function onAboutToDestroy(): void { - wrapper.destroy(); - } - } - } - - Component { - id: notifComponent - NotifWrapper {} - } - - function dismissAllPopups() { - for (const w of visibleNotifications) { - if (w) { - w.popup = false; - } - } - visibleNotifications = []; - notificationQueue = []; - } - - function clearAllNotifications() { - if (!notifications.length) { - return; - } - bulkDismissing = true; - popupsDisabled = true; - addGate.stop(); - addGateBusy = false; - notificationQueue = []; - - for (const w of allWrappers) { - if (w) { - w.popup = false; - } - } - visibleNotifications = []; - - _dismissQueue = notifications.slice(); - if (notifications.length) { - notifications = []; - } - expandedGroups = {}; - expandedMessages = {}; - - _suspendGrouping = true; - - if (!dismissPump.running && _dismissQueue.length) { - dismissPump.start(); - } - } - - function dismissNotification(wrapper) { - if (!wrapper || !wrapper.notification) { - return; - } - wrapper.popup = false; - wrapper.notification.dismiss(); - } - - function disablePopups(disable) { - popupsDisabled = disable; - if (disable) { - notificationQueue = []; - for (const notif of visibleNotifications) { - notif.popup = false; - } - visibleNotifications = []; - } - } - - property bool _processingQueue: false - - function processQueue() { - if (addGateBusy || _processingQueue) - return; - if (popupsDisabled) - return; - if (SessionData.doNotDisturb) - return; - if (notificationQueue.length === 0) - return; - - _processingQueue = true; - - const next = notificationQueue.shift(); - if (!next) { - _processingQueue = false; - return; - } - - next.seq = ++seqCounter; - - const activePopups = visibleNotifications.filter(n => n && n.popup); - let evicted = null; - if (activePopups.length >= maxVisibleNotifications) { - const unhovered = activePopups.filter(n => n.timer?.running); - const pool = unhovered.length > 0 ? unhovered : activePopups; - evicted = pool.reduce((min, n) => (n.seq < min.seq) ? n : min, pool[0]); - if (evicted) - evicted.removedByLimit = true; - } - - if (evicted) { - visibleNotifications = [...visibleNotifications.filter(n => n !== evicted), next]; - } else { - visibleNotifications = [...visibleNotifications, next]; - } - - if (evicted) - evicted.popup = false; - next.popup = true; - - if (next.timer.interval > 0) - next.timer.start(); - - addGateBusy = true; - addGate.restart(); - _processingQueue = false; - } - - function removeFromVisibleNotifications(wrapper) { - visibleNotifications = visibleNotifications.filter(n => n !== wrapper); - processQueue(); - } - - function releaseWrapper(w) { - visibleNotifications = visibleNotifications.filter(n => n !== w); - notificationQueue = notificationQueue.filter(n => n !== w); - - if (w && w.destroy && !w.isPersistent && notifications.indexOf(w) === -1) { - Qt.callLater(() => { - try { - w.destroy(); - } catch (e) {} - }); - } - } - - function _decodeEntities(s) { - s = s.replace(/&#(\d+);/g, (_, n) => String.fromCodePoint(parseInt(n, 10))); - s = s.replace(/&#x([0-9a-fA-F]+);/g, (_, n) => String.fromCodePoint(parseInt(n, 16))); - return s.replace(/&([a-zA-Z][a-zA-Z0-9]*);/g, (match, name) => { - switch (name) { - case "amp": - return "&"; - case "lt": - return "<"; - case "gt": - return ">"; - case "quot": - return "\""; - case "apos": - return "'"; - case "nbsp": - return "\u00A0"; - case "ndash": - return "\u2013"; - case "mdash": - return "\u2014"; - case "lsquo": - return "\u2018"; - case "rsquo": - return "\u2019"; - case "ldquo": - return "\u201C"; - case "rdquo": - return "\u201D"; - case "bull": - return "\u2022"; - case "hellip": - return "\u2026"; - case "trade": - return "\u2122"; - case "copy": - return "\u00A9"; - case "reg": - return "\u00AE"; - case "deg": - return "\u00B0"; - case "plusmn": - return "\u00B1"; - case "times": - return "\u00D7"; - case "divide": - return "\u00F7"; - case "micro": - return "\u00B5"; - case "middot": - return "\u00B7"; - case "laquo": - return "\u00AB"; - case "raquo": - return "\u00BB"; - case "larr": - return "\u2190"; - case "rarr": - return "\u2192"; - case "uarr": - return "\u2191"; - case "darr": - return "\u2193"; - default: - return match; - } - }); - } - - function _resolveHtmlBody(body) { - if (!body) - return ""; - - let result = body; - - if (/<\/?[a-z][\s\S]*>/i.test(body)) { - result = body; - } else { - // Decode percent-encoded URLs (e.g. https%3A%2F%2F → https://) - let processed = body.replace(/\bhttps?%3A%2F%2F[^\s]+/gi, match => { - try { - return decodeURIComponent(match); - } catch (e) { - return match; - } - }); - - if (/&(#\d+|#x[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]+);/.test(processed)) { - const decoded = _decodeEntities(processed); - if (/<\/?[a-z][\s\S]*>/i.test(decoded)) - result = decoded; - else - result = Markdown2Html.markdownToHtml(decoded); - } else { - result = Markdown2Html.markdownToHtml(processed); - } - } - - // Strip out image tags to prevent IP tracking - return result.replace(/<img\b[^>]*>/gi, ""); - } - - function getGroupKey(wrapper) { - if (wrapper.desktopEntry && wrapper.desktopEntry !== "") { - return wrapper.desktopEntry.toLowerCase(); - } - - return wrapper.appName.toLowerCase(); - } - - function _recomputeGroups() { - if (_suspendGrouping) { - _groupsDirty = true; - return; - } - _groupCache = { - "notifications": _calcGroupedNotifications(), - "popups": _calcGroupedPopups() - }; - _groupsDirty = false; - } - - function _recomputeGroupsLater() { - _groupsDirty = true; - if (!groupsDebounce.running) { - groupsDebounce.start(); - } - } - - function _calcGroupedNotifications() { - const groups = {}; - - for (const notif of notifications) { - if (!notif || !notif.notification) - continue; - const groupKey = getGroupKey(notif); - if (!groups[groupKey]) { - groups[groupKey] = { - "key": groupKey, - "appName": notif.appName, - "notifications": [], - "latestNotification": null, - "count": 0, - "hasInlineReply": false - }; - } - - groups[groupKey].notifications.unshift(notif); - groups[groupKey].latestNotification = groups[groupKey].notifications[0]; - groups[groupKey].count = groups[groupKey].notifications.length; - - if (notif.notification?.hasInlineReply) - groups[groupKey].hasInlineReply = true; - } - - return Object.values(groups).sort((a, b) => { - if (!a.latestNotification || !b.latestNotification) - return 0; - const aUrgency = a.latestNotification.urgency ?? NotificationUrgency.Low; - const bUrgency = b.latestNotification.urgency ?? NotificationUrgency.Low; - if (aUrgency !== bUrgency) { - return bUrgency - aUrgency; - } - return b.latestNotification.time.getTime() - a.latestNotification.time.getTime(); - }); - } - - function _calcGroupedPopups() { - const groups = {}; - - for (const notif of popups) { - if (!notif || !notif.notification) - continue; - const groupKey = getGroupKey(notif); - if (!groups[groupKey]) { - groups[groupKey] = { - "key": groupKey, - "appName": notif.appName, - "notifications": [], - "latestNotification": null, - "count": 0, - "hasInlineReply": false - }; - } - - groups[groupKey].notifications.unshift(notif); - groups[groupKey].latestNotification = groups[groupKey].notifications[0]; - groups[groupKey].count = groups[groupKey].notifications.length; - - if (notif.notification?.hasInlineReply) - groups[groupKey].hasInlineReply = true; - } - - return Object.values(groups).sort((a, b) => { - if (!a.latestNotification || !b.latestNotification) - return 0; - return b.latestNotification.time.getTime() - a.latestNotification.time.getTime(); - }); - } - - function toggleGroupExpansion(groupKey) { - let newExpandedGroups = {}; - for (const key in expandedGroups) { - newExpandedGroups[key] = expandedGroups[key]; - } - newExpandedGroups[groupKey] = !newExpandedGroups[groupKey]; - expandedGroups = newExpandedGroups; - } - - function dismissGroup(groupKey) { - const group = groupedNotifications.find(g => g.key === groupKey); - if (group) { - for (const notif of group.notifications) { - if (notif && notif.notification) { - notif.notification.dismiss(); - } - } - } else { - for (const notif of allWrappers) { - if (notif && notif.notification && getGroupKey(notif) === groupKey) { - notif.notification.dismiss(); - } - } - } - } - - function clearGroupExpansionState(groupKey) { - let newExpandedGroups = {}; - for (const key in expandedGroups) { - if (key !== groupKey && expandedGroups[key]) { - newExpandedGroups[key] = true; - } - } - expandedGroups = newExpandedGroups; - } - - function cleanupExpansionStates() { - const currentGroupKeys = new Set(groupedNotifications.map(g => g.key)); - const currentMessageIds = new Set(); - for (const group of groupedNotifications) { - for (const notif of group.notifications) { - if (notif && notif.notification) { - currentMessageIds.add(notif.notification.id); - } - } - } - let newExpandedGroups = {}; - for (const key in expandedGroups) { - if (currentGroupKeys.has(key) && expandedGroups[key]) { - newExpandedGroups[key] = true; - } - } - expandedGroups = newExpandedGroups; - let newExpandedMessages = {}; - for (const messageId in expandedMessages) { - if (currentMessageIds.has(messageId) && expandedMessages[messageId]) { - newExpandedMessages[messageId] = true; - } - } - expandedMessages = newExpandedMessages; - } - - function toggleMessageExpansion(messageId) { - let newExpandedMessages = {}; - for (const key in expandedMessages) { - newExpandedMessages[key] = expandedMessages[key]; - } - newExpandedMessages[messageId] = !newExpandedMessages[messageId]; - expandedMessages = newExpandedMessages; - } - - Connections { - target: SessionData - function onDoNotDisturbChanged() { - if (SessionData.doNotDisturb) { - // Hide all current popups when DND is enabled - for (const notif of visibleNotifications) { - notif.popup = false; - } - visibleNotifications = []; - notificationQueue = []; - } else { - // Re-enable popup processing when DND is disabled - processQueue(); - } - } - } - - Connections { - target: typeof SettingsData !== "undefined" ? SettingsData : null - function onUse24HourClockChanged() { - root.clockFormatChanged = !root.clockFormatChanged; - } - function onNotificationHistoryMaxAgeDaysChanged() { - root.pruneHistory(); - } - function onNotificationHistoryEnabledChanged() { - if (!SettingsData.notificationHistoryEnabled) { - root.deleteHistory(); - } - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PluginService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PluginService.qml deleted file mode 100644 index 03f9be6..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PluginService.qml +++ /dev/null @@ -1,876 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtCore -import QtQuick -import Qt.labs.folderlistmodel -import Quickshell -import qs.Common -import qs.Services - -Singleton { - id: root - - property var availablePlugins: ({}) - property var loadedPlugins: ({}) - property var pluginWidgetComponents: ({}) - property var pluginDaemonComponents: ({}) - property var pluginLauncherComponents: ({}) - property var pluginDesktopComponents: ({}) - property var availablePluginsList: [] - property string pluginDirectory: { - var configDir = StandardPaths.writableLocation(StandardPaths.ConfigLocation); - var configDirStr = configDir.toString(); - if (configDirStr.startsWith("file://")) { - configDirStr = configDirStr.substring(7); - } - return configDirStr + "/DankMaterialShell/plugins"; - } - property string systemPluginDirectory: "/etc/xdg/quickshell/dms-plugins" - - property var knownManifests: ({}) - property var pathToPluginId: ({}) - property var pluginInstances: ({}) - property var globalVars: ({}) - - property var _stateCache: ({}) - property var _stateLoaded: ({}) - property var _stateWriters: ({}) - property var _stateDirtyPlugins: ({}) - property bool _stateDirCreated: false - - signal pluginLoaded(string pluginId) - signal pluginUnloaded(string pluginId) - signal pluginLoadFailed(string pluginId, string error) - signal pluginDataChanged(string pluginId) - signal pluginStateChanged(string pluginId) - signal pluginListUpdated - signal globalVarChanged(string pluginId, string varName) - signal requestLauncherUpdate(string pluginId) - - Timer { - id: resyncDebounce - interval: 120 - repeat: false - onTriggered: resyncAll() - } - - Timer { - id: _stateWriteTimer - interval: 150 - repeat: false - onTriggered: root._flushDirtyStates() - } - - Component.onCompleted: { - userWatcher.folder = Paths.toFileUrl(root.pluginDirectory); - systemWatcher.folder = Paths.toFileUrl(root.systemPluginDirectory); - Qt.callLater(resyncAll); - } - - FolderListModel { - id: userWatcher - showDirs: true - showFiles: false - showDotAndDotDot: false - - onCountChanged: resyncDebounce.restart() - onStatusChanged: { - if (status === FolderListModel.Ready) - resyncDebounce.restart(); - } - } - - FolderListModel { - id: systemWatcher - showDirs: true - showFiles: false - showDotAndDotDot: false - - onCountChanged: resyncDebounce.restart() - onStatusChanged: { - if (status === FolderListModel.Ready) - resyncDebounce.restart(); - } - } - - function snapshotModel(model, sourceTag) { - const out = []; - const n = model.count; - const baseDir = sourceTag === "user" ? pluginDirectory : systemPluginDirectory; - for (let i = 0; i < n; i++) { - let dirPath = model.get(i, "filePath"); - if (dirPath.startsWith("file://")) { - dirPath = dirPath.substring(7); - } - if (!dirPath.startsWith(baseDir)) { - continue; - } - const manifestPath = dirPath + "/plugin.json"; - out.push({ - path: manifestPath, - source: sourceTag - }); - } - return out; - } - - function resyncAll() { - const userList = snapshotModel(userWatcher, "user"); - const sysList = snapshotModel(systemWatcher, "system"); - const seenPaths = {}; - - function consider(entry) { - const key = entry.path; - seenPaths[key] = true; - const prev = knownManifests[key]; - if (!prev) { - loadPluginManifestFile(entry.path, entry.source, Date.now()); - } - } - for (let i = 0; i < userList.length; i++) - consider(userList[i]); - for (let i = 0; i < sysList.length; i++) - consider(sysList[i]); - - const removed = []; - for (const path in knownManifests) { - if (!seenPaths[path]) - removed.push(path); - } - if (removed.length) { - removed.forEach(function (path) { - const pid = pathToPluginId[path]; - if (pid) { - unregisterPluginByPath(path, pid); - } - delete knownManifests[path]; - delete pathToPluginId[path]; - }); - _updateAvailablePluginsList(); - pluginListUpdated(); - } - } - - function loadPluginManifestFile(manifestPathNoScheme, sourceTag, mtimeEpochMs) { - const manifestId = "m_" + Math.random().toString(36).slice(2); - const qml = ` - import QtQuick - import Quickshell.Io - FileView { - id: fv - property string absPath: "" - onLoaded: { - try { - let raw = text() - if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1) - const manifest = JSON.parse(raw) - root._onManifestParsed(absPath, manifest, "${sourceTag}", ${mtimeEpochMs}) - } catch (e) { - console.error("PluginService: bad manifest", absPath, e.message) - knownManifests[absPath] = { mtime: ${mtimeEpochMs}, source: "${sourceTag}", bad: true } - } - fv.destroy() - } - onLoadFailed: (err) => { - console.warn("PluginService: manifest load failed", absPath, err) - fv.destroy() - } - } - `; - - const loader = Qt.createQmlObject(qml, root, "mf_" + manifestId); - loader.absPath = manifestPathNoScheme; - loader.path = manifestPathNoScheme; - } - - function _onManifestParsed(absPath, manifest, sourceTag, mtimeEpochMs) { - if (!manifest || !manifest.id || !manifest.name || !manifest.component) { - console.error("PluginService: invalid manifest fields:", absPath); - knownManifests[absPath] = { - mtime: mtimeEpochMs, - source: sourceTag, - bad: true - }; - return; - } - - const dir = absPath.substring(0, absPath.lastIndexOf('/')); - let comp = manifest.component; - if (comp.startsWith("./")) - comp = comp.slice(2); - let settings = manifest.settings; - if (settings && settings.startsWith("./")) - settings = settings.slice(2); - - const info = {}; - for (const k in manifest) - info[k] = manifest[k]; - - let perms = manifest.permissions; - if (typeof perms === "string") { - perms = perms.split(/\s*,\s*/); - } - if (!Array.isArray(perms)) { - perms = []; - } - info.permissions = perms.map(p => String(p).trim()); - - info.manifestPath = absPath; - info.pluginDirectory = dir; - info.componentPath = dir + "/" + comp; - info.settingsPath = settings ? (dir + "/" + settings) : null; - info.loaded = isPluginLoaded(manifest.id); - info.type = manifest.type || "widget"; - info.source = sourceTag; - info.requires_dms = manifest.requires_dms || null; - - const existing = availablePlugins[manifest.id]; - const shouldReplace = (!existing) || (existing && existing.source === "system" && sourceTag === "user"); - - if (shouldReplace) { - if (existing && existing.loaded && existing.source !== sourceTag) { - unloadPlugin(manifest.id); - } - const newMap = Object.assign({}, availablePlugins); - newMap[manifest.id] = info; - availablePlugins = newMap; - pathToPluginId[absPath] = manifest.id; - knownManifests[absPath] = { - mtime: mtimeEpochMs, - source: sourceTag - }; - _updateAvailablePluginsList(); - pluginListUpdated(); - const enabled = info.type === "desktop" || SettingsData.getPluginSetting(manifest.id, "enabled", false); - if (enabled && !info.loaded) - loadPlugin(manifest.id); - } else { - knownManifests[absPath] = { - mtime: mtimeEpochMs, - source: sourceTag, - shadowedBy: existing.source - }; - pathToPluginId[absPath] = manifest.id; - } - } - - function unregisterPluginByPath(absPath, pluginId) { - const current = availablePlugins[pluginId]; - if (current && current.manifestPath === absPath) { - if (current.loaded) - unloadPlugin(pluginId); - const newMap = Object.assign({}, availablePlugins); - delete newMap[pluginId]; - availablePlugins = newMap; - } - } - - function loadPlugin(pluginId, bustCache) { - const plugin = availablePlugins[pluginId]; - if (!plugin) { - console.error("PluginService: Plugin not found:", pluginId); - pluginLoadFailed(pluginId, "Plugin not found"); - return false; - } - - if (plugin.loaded) { - return true; - } - - const isDaemon = plugin.type === "daemon"; - const isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher")); - const isDesktop = plugin.type === "desktop"; - - const prevInstance = pluginInstances[pluginId]; - if (prevInstance) { - prevInstance.destroy(); - const newInstances = Object.assign({}, pluginInstances); - delete newInstances[pluginId]; - pluginInstances = newInstances; - } - - try { - let url = "file://" + plugin.componentPath; - if (bustCache) - url += "?t=" + Date.now(); - const comp = Qt.createComponent(url, Component.PreferSynchronous); - if (comp.status === Component.Error) { - console.error("PluginService: component error", pluginId, comp.errorString()); - pluginLoadFailed(pluginId, comp.errorString()); - return false; - } - - if (isDaemon) { - const newDaemons = Object.assign({}, pluginDaemonComponents); - newDaemons[pluginId] = comp; - pluginDaemonComponents = newDaemons; - } else if (isLauncher) { - const instance = comp.createObject(root, { - "pluginService": root - }); - if (!instance) { - console.error("PluginService: failed to instantiate plugin:", pluginId, comp.errorString()); - pluginLoadFailed(pluginId, comp.errorString()); - return false; - } - const newInstances = Object.assign({}, pluginInstances); - newInstances[pluginId] = instance; - pluginInstances = newInstances; - - const newLaunchers = Object.assign({}, pluginLauncherComponents); - newLaunchers[pluginId] = comp; - pluginLauncherComponents = newLaunchers; - } else if (isDesktop) { - const newDesktop = Object.assign({}, pluginDesktopComponents); - newDesktop[pluginId] = comp; - pluginDesktopComponents = newDesktop; - } else { - const newComponents = Object.assign({}, pluginWidgetComponents); - newComponents[pluginId] = comp; - pluginWidgetComponents = newComponents; - } - - plugin.loaded = true; - const newLoaded = Object.assign({}, loadedPlugins); - newLoaded[pluginId] = plugin; - loadedPlugins = newLoaded; - - pluginLoaded(pluginId); - return true; - } catch (e) { - console.error("PluginService: Error loading plugin:", pluginId, e.message); - pluginLoadFailed(pluginId, e.message); - return false; - } - } - - function unloadPlugin(pluginId) { - const plugin = loadedPlugins[pluginId]; - if (!plugin) { - console.warn("PluginService: Plugin not loaded:", pluginId); - return false; - } - - try { - const isDaemon = plugin.type === "daemon"; - const isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher")); - const isDesktop = plugin.type === "desktop"; - - const instance = pluginInstances[pluginId]; - if (instance) { - instance.destroy(); - const newInstances = Object.assign({}, pluginInstances); - delete newInstances[pluginId]; - pluginInstances = newInstances; - } - - if (isDaemon && pluginDaemonComponents[pluginId]) { - const newDaemons = Object.assign({}, pluginDaemonComponents); - delete newDaemons[pluginId]; - pluginDaemonComponents = newDaemons; - } else if (isLauncher && pluginLauncherComponents[pluginId]) { - const newLaunchers = Object.assign({}, pluginLauncherComponents); - delete newLaunchers[pluginId]; - pluginLauncherComponents = newLaunchers; - } else if (isDesktop && pluginDesktopComponents[pluginId]) { - const newDesktop = Object.assign({}, pluginDesktopComponents); - delete newDesktop[pluginId]; - pluginDesktopComponents = newDesktop; - } else if (pluginWidgetComponents[pluginId]) { - const newComponents = Object.assign({}, pluginWidgetComponents); - delete newComponents[pluginId]; - pluginWidgetComponents = newComponents; - } - - plugin.loaded = false; - const newLoaded = Object.assign({}, loadedPlugins); - delete newLoaded[pluginId]; - loadedPlugins = newLoaded; - - _cleanupPluginStateWriter(pluginId); - pluginUnloaded(pluginId); - return true; - } catch (error) { - console.error("PluginService: Error unloading plugin:", pluginId, "Error:", error.message); - return false; - } - } - - function getWidgetComponents() { - return pluginWidgetComponents; - } - - function getDaemonComponents() { - return pluginDaemonComponents; - } - - function getDesktopComponents() { - return pluginDesktopComponents; - } - - function getAvailablePlugins() { - return availablePluginsList; - } - - function _updateAvailablePluginsList() { - const result = []; - for (const key in availablePlugins) { - result.push(availablePlugins[key]); - } - availablePluginsList = result; - } - - function getPluginVariants(pluginId) { - const plugin = availablePlugins[pluginId]; - if (!plugin) { - return []; - } - const variants = SettingsData.getPluginSetting(pluginId, "variants", []); - return variants; - } - - function getAllPluginVariants() { - const result = []; - for (const pluginId in availablePlugins) { - const plugin = availablePlugins[pluginId]; - if (plugin.type !== "widget") { - continue; - } - const variants = getPluginVariants(pluginId); - if (variants.length === 0) { - result.push({ - pluginId: pluginId, - variantId: null, - fullId: pluginId, - name: plugin.name, - icon: plugin.icon || "extension", - description: plugin.description || "Plugin widget", - loaded: plugin.loaded - }); - } else { - for (let i = 0; i < variants.length; i++) { - const variant = variants[i]; - result.push({ - pluginId: pluginId, - variantId: variant.id, - fullId: pluginId + ":" + variant.id, - name: plugin.name + " - " + variant.name, - icon: variant.icon || plugin.icon || "extension", - description: variant.description || plugin.description || "Plugin widget variant", - loaded: plugin.loaded - }); - } - } - } - return result; - } - - function createPluginVariant(pluginId, variantName, variantConfig) { - const variants = getPluginVariants(pluginId); - const variantId = "variant_" + Date.now(); - const newVariant = Object.assign({}, variantConfig, { - id: variantId, - name: variantName - }); - variants.push(newVariant); - SettingsData.setPluginSetting(pluginId, "variants", variants); - pluginDataChanged(pluginId); - return variantId; - } - - function removePluginVariant(pluginId, variantId) { - const variants = getPluginVariants(pluginId); - const newVariants = variants.filter(function (v) { - return v.id !== variantId; - }); - SettingsData.setPluginSetting(pluginId, "variants", newVariants); - - const fullId = pluginId + ":" + variantId; - removeWidgetFromDankBar(fullId); - - pluginDataChanged(pluginId); - } - - function removeWidgetFromDankBar(widgetId) { - function filterWidget(widget) { - const id = typeof widget === "string" ? widget : widget.id; - return id !== widgetId; - } - - const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default"); - if (!defaultBar) - return; - const leftWidgets = defaultBar.leftWidgets || []; - const centerWidgets = defaultBar.centerWidgets || []; - const rightWidgets = defaultBar.rightWidgets || []; - - const newLeft = leftWidgets.filter(filterWidget); - const newCenter = centerWidgets.filter(filterWidget); - const newRight = rightWidgets.filter(filterWidget); - - if (newLeft.length !== leftWidgets.length) { - SettingsData.setDankBarLeftWidgets(newLeft); - } - if (newCenter.length !== centerWidgets.length) { - SettingsData.setDankBarCenterWidgets(newCenter); - } - if (newRight.length !== rightWidgets.length) { - SettingsData.setDankBarRightWidgets(newRight); - } - } - - function updatePluginVariant(pluginId, variantId, variantConfig) { - const variants = getPluginVariants(pluginId); - for (let i = 0; i < variants.length; i++) { - if (variants[i].id === variantId) { - variants[i] = Object.assign({}, variants[i], variantConfig); - break; - } - } - SettingsData.setPluginSetting(pluginId, "variants", variants); - pluginDataChanged(pluginId); - } - - function getPluginVariantData(pluginId, variantId) { - const variants = getPluginVariants(pluginId); - for (let i = 0; i < variants.length; i++) { - if (variants[i].id === variantId) { - return variants[i]; - } - } - return null; - } - - function getLoadedPlugins() { - const result = []; - for (const key in loadedPlugins) { - result.push(loadedPlugins[key]); - } - return result; - } - - function isPluginLoaded(pluginId) { - return loadedPlugins[pluginId] !== undefined; - } - - function enablePlugin(pluginId) { - SettingsData.setPluginSetting(pluginId, "enabled", true); - return loadPlugin(pluginId); - } - - function disablePlugin(pluginId) { - SettingsData.setPluginSetting(pluginId, "enabled", false); - return unloadPlugin(pluginId); - } - - function reloadPlugin(pluginId) { - if (isPluginLoaded(pluginId)) - unloadPlugin(pluginId); - return loadPlugin(pluginId, true); - } - - function togglePlugin(pluginId) { - let instance = pluginInstances[pluginId]; - - // Lazy instantiate daemon plugins on first toggle - // This respects the daemon lifecycle (not instantiated on load) - // while supporting toggle functionality for slideout-capable daemons - if (!instance && pluginDaemonComponents[pluginId]) { - const comp = pluginDaemonComponents[pluginId]; - const newInstance = comp.createObject(root, { - "pluginId": pluginId, - "pluginService": root - }); - if (newInstance) { - const newInstances = Object.assign({}, pluginInstances); - newInstances[pluginId] = newInstance; - pluginInstances = newInstances; - instance = newInstance; - } - } - - if (instance && typeof instance.toggle === "function") { - instance.toggle(); - return true; - } - return false; - } - - function savePluginData(pluginId, key, value) { - SettingsData.setPluginSetting(pluginId, key, value); - pluginDataChanged(pluginId); - return true; - } - - function loadPluginData(pluginId, key, defaultValue) { - return SettingsData.getPluginSetting(pluginId, key, defaultValue); - } - - function getPluginPath(pluginId) { - const plugin = availablePlugins[pluginId]; - if (!plugin) - return ""; - return plugin.pluginDirectory || ""; - } - - function saveAllPluginSettings() { - SettingsData.savePluginSettings(); - } - - function getPluginStatePath(pluginId) { - return Paths.strip(Paths.state) + "/plugins/" + pluginId + "_state.json"; - } - - function loadPluginState(pluginId, key, defaultValue) { - if (!_stateLoaded[pluginId]) - _loadStateFromDisk(pluginId); - const state = _stateCache[pluginId]; - if (!state) - return defaultValue; - return state[key] !== undefined ? state[key] : defaultValue; - } - - function savePluginState(pluginId, key, value) { - if (!_stateLoaded[pluginId]) - _loadStateFromDisk(pluginId); - if (!_stateCache[pluginId]) - _stateCache[pluginId] = {}; - _stateCache[pluginId][key] = value; - _stateDirtyPlugins[pluginId] = true; - _stateWriteTimer.restart(); - pluginStateChanged(pluginId); - } - - function clearPluginState(pluginId) { - _stateCache[pluginId] = {}; - _stateLoaded[pluginId] = true; - _flushStateToDisk(pluginId); - pluginStateChanged(pluginId); - } - - function removePluginStateKey(pluginId, key) { - if (!_stateCache[pluginId]) - return; - delete _stateCache[pluginId][key]; - _stateDirtyPlugins[pluginId] = true; - _stateWriteTimer.restart(); - pluginStateChanged(pluginId); - } - - function _ensureStateDir() { - if (_stateDirCreated) - return; - _stateDirCreated = true; - Paths.mkdir(Paths.state + "/plugins"); - } - - function _loadStateFromDisk(pluginId) { - _stateLoaded[pluginId] = true; - _ensureStateDir(); - const path = getPluginStatePath(pluginId); - const escapedPath = path.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); - try { - const qml = 'import QtQuick; import Quickshell.Io; FileView { path: "' + escapedPath + '"; blockLoading: true; blockWrites: true; atomicWrites: true }'; - const fv = Qt.createQmlObject(qml, root, "sf_" + pluginId); - const raw = fv.text(); - if (raw && raw.trim()) { - _stateCache[pluginId] = JSON.parse(raw); - } else { - _stateCache[pluginId] = {}; - } - _stateWriters[pluginId] = fv; - } catch (e) { - _stateCache[pluginId] = {}; - } - } - - function _flushStateToDisk(pluginId) { - _ensureStateDir(); - const content = JSON.stringify(_stateCache[pluginId] || {}, null, 2); - if (_stateWriters[pluginId]) { - _stateWriters[pluginId].setText(content); - return; - } - const path = getPluginStatePath(pluginId); - const escapedPath = path.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); - try { - const qml = 'import QtQuick; import Quickshell.Io; FileView { path: "' + escapedPath + '"; blockWrites: true; atomicWrites: true }'; - const fv = Qt.createQmlObject(qml, root, "sw_" + pluginId); - _stateWriters[pluginId] = fv; - fv.loaded.connect(function () { - fv.setText(content); - }); - fv.loadFailed.connect(function () { - fv.setText(content); - }); - } catch (e) { - console.warn("PluginService: Failed to write state for", pluginId, e.message); - } - } - - function _flushDirtyStates() { - const dirty = _stateDirtyPlugins; - _stateDirtyPlugins = {}; - for (const pluginId in dirty) - _flushStateToDisk(pluginId); - } - - function _cleanupPluginStateWriter(pluginId) { - if (!_stateWriters[pluginId]) - return; - _stateWriters[pluginId].destroy(); - delete _stateWriters[pluginId]; - } - - function scanPlugins() { - const userUrl = Paths.toFileUrl(root.pluginDirectory); - const systemUrl = Paths.toFileUrl(root.systemPluginDirectory); - userWatcher.folder = ""; - userWatcher.folder = userUrl; - systemWatcher.folder = ""; - systemWatcher.folder = systemUrl; - resyncDebounce.restart(); - } - - function forceRescanPlugin(pluginId) { - const plugin = availablePlugins[pluginId]; - if (plugin && plugin.manifestPath) { - const manifestPath = plugin.manifestPath; - const source = plugin.source || "user"; - delete knownManifests[manifestPath]; - const newMap = Object.assign({}, availablePlugins); - delete newMap[pluginId]; - availablePlugins = newMap; - loadPluginManifestFile(manifestPath, source, Date.now()); - } - } - - function createPluginDirectory() { - const mkdirProcess = Qt.createComponent("data:text/plain,import Quickshell.Io; Process { }"); - if (mkdirProcess.status === Component.Ready) { - const process = mkdirProcess.createObject(root); - process.command = ["mkdir", "-p", pluginDirectory]; - process.exited.connect(function (exitCode) { - if (exitCode !== 0) { - console.error("PluginService: Failed to create plugin directory, exit code:", exitCode); - } - process.destroy(); - }); - process.running = true; - return true; - } else { - console.error("PluginService: Failed to create mkdir process"); - return false; - } - } - - // Launcher plugin helper functions - function getLauncherPlugins() { - const launchers = {}; - - // Check plugins that have launcher components - for (const pluginId in pluginLauncherComponents) { - const plugin = availablePlugins[pluginId]; - if (plugin && plugin.loaded) { - launchers[pluginId] = plugin; - } - } - return launchers; - } - - function getLauncherPlugin(pluginId) { - const plugin = availablePlugins[pluginId]; - if (plugin && plugin.loaded && pluginLauncherComponents[pluginId]) { - return plugin; - } - return null; - } - - function getPluginTrigger(pluginId) { - const plugin = getLauncherPlugin(pluginId); - if (plugin) { - // Check if noTrigger is set (always active mode) - const noTrigger = SettingsData.getPluginSetting(pluginId, "noTrigger", false); - if (noTrigger) { - return ""; - } - // Otherwise load the custom trigger, defaulting to plugin manifest trigger - const customTrigger = SettingsData.getPluginSetting(pluginId, "trigger", plugin.trigger || "!"); - return customTrigger; - } - return null; - } - - function getAllPluginTriggers() { - const triggers = {}; - const launchers = getLauncherPlugins(); - - for (const pluginId in launchers) { - const trigger = getPluginTrigger(pluginId); - if (trigger && trigger.trim() !== "") { - triggers[trigger] = pluginId; - } - } - return triggers; - } - - function getPluginsWithEmptyTrigger() { - const plugins = []; - const launchers = getLauncherPlugins(); - - for (const pluginId in launchers) { - const trigger = getPluginTrigger(pluginId); - if (!trigger || trigger.trim() === "") { - plugins.push(pluginId); - } - } - return plugins; - } - - function getPluginViewPreference(pluginId) { - const plugin = availablePlugins[pluginId]; - if (!plugin) - return null; - - return { - mode: plugin.viewMode || null, - enforced: plugin.viewModeEnforced === true - }; - } - - function getGlobalVar(pluginId, varName, defaultValue) { - if (globalVars[pluginId] && varName in globalVars[pluginId]) { - return globalVars[pluginId][varName]; - } - return defaultValue; - } - - function setGlobalVar(pluginId, varName, value) { - const newGlobals = Object.assign({}, globalVars); - if (!newGlobals[pluginId]) { - newGlobals[pluginId] = {}; - } - newGlobals[pluginId] = Object.assign({}, newGlobals[pluginId]); - newGlobals[pluginId][varName] = value; - globalVars = newGlobals; - globalVarChanged(pluginId, varName); - } - - function checkPluginCompatibility(requiresDms) { - if (!requiresDms) - return true; - return SystemUpdateService.checkVersionRequirement(requiresDms, SystemUpdateService.getParsedShellVersion()); - } - - function getIncompatiblePlugins() { - const result = []; - for (const pluginId in availablePlugins) { - const plugin = availablePlugins[pluginId]; - if (plugin.loaded && plugin.requires_dms && !checkPluginCompatibility(plugin.requires_dms)) { - result.push(plugin); - } - } - return result; - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PolkitService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PolkitService.qml deleted file mode 100644 index 5238d04..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PolkitService.qml +++ /dev/null @@ -1,40 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell - -Singleton { - id: root - - readonly property bool disablePolkitIntegration: Quickshell.env("DMS_DISABLE_POLKIT") === "1" - - property bool polkitAvailable: false - property var agent: null - - function createPolkitAgent() { - try { - const qmlString = ` - import QtQuick - import Quickshell.Services.Polkit - - PolkitAgent { - } - ` - - agent = Qt.createQmlObject(qmlString, root, "PolkitService.Agent") - polkitAvailable = true - console.info("PolkitService: Initialized successfully") - } catch (e) { - polkitAvailable = false - console.warn("PolkitService: Polkit not available - authentication prompts disabled. This requires a newer version of Quickshell.") - } - } - - Component.onCompleted: { - if (disablePolkitIntegration) { - return - } - createPolkitAgent() - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PopoutService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PopoutService.qml deleted file mode 100644 index c6e8a09..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PopoutService.qml +++ /dev/null @@ -1,709 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound -import QtQuick -import Quickshell -import Quickshell.Wayland -import qs.Common - -Singleton { - id: root - - property var controlCenterPopout: null - property var controlCenterLoader: null - property var notificationCenterPopout: null - property var notificationCenterLoader: null - property var appDrawerPopout: null - property var appDrawerLoader: null - property var processListPopout: null - property var processListPopoutLoader: null - property var dankDashPopout: null - property var dankDashPopoutLoader: null - property var batteryPopout: null - property var batteryPopoutLoader: null - property var vpnPopout: null - property var vpnPopoutLoader: null - property var systemUpdatePopout: null - property var systemUpdateLoader: null - property var layoutPopout: null - property var layoutPopoutLoader: null - property var clipboardHistoryPopout: null - property var clipboardHistoryPopoutLoader: null - - property var settingsModal: null - property var settingsModalLoader: null - property var clipboardHistoryModal: null - property var dankLauncherV2Modal: null - property var dankLauncherV2ModalLoader: null - property var powerMenuModal: null - property var processListModal: null - property var processListModalLoader: null - property var colorPickerModal: null - property var notificationModal: null - property var wifiPasswordModal: null - property var wifiPasswordModalLoader: null - property var wifiQRCodeModal: null - property var wifiQRCodeModalLoader: null - property var polkitAuthModal: null - property var polkitAuthModalLoader: null - property var bluetoothPairingModal: null - property var networkInfoModal: null - property var windowRuleModalLoader: null - - property var notepadSlideouts: [] - - property string pendingThemeInstall: "" - property string pendingPluginInstall: "" - - function setPosition(popout, x, y, width, section, screen) { - if (popout && popout.setTriggerPosition && arguments.length >= 6) { - popout.setTriggerPosition(x, y, width, section, screen); - } - } - - function openControlCenter(x, y, width, section, screen) { - if (controlCenterPopout) { - setPosition(controlCenterPopout, x, y, width, section, screen); - controlCenterPopout.open(); - } - } - - function closeControlCenter() { - controlCenterPopout?.close(); - } - - function unloadControlCenter() { - if (!controlCenterLoader) - return; - controlCenterPopout = null; - controlCenterLoader.active = false; - } - - function toggleControlCenter(x, y, width, section, screen) { - if (controlCenterPopout) { - setPosition(controlCenterPopout, x, y, width, section, screen); - controlCenterPopout.toggle(); - } - } - - function openNotificationCenter(x, y, width, section, screen) { - if (notificationCenterPopout) { - setPosition(notificationCenterPopout, x, y, width, section, screen); - notificationCenterPopout.open(); - } - } - - function closeNotificationCenter() { - notificationCenterPopout?.close(); - } - - function unloadNotificationCenter() { - if (!notificationCenterLoader) - return; - notificationCenterPopout = null; - notificationCenterLoader.active = false; - } - - function toggleNotificationCenter(x, y, width, section, screen) { - if (notificationCenterPopout) { - setPosition(notificationCenterPopout, x, y, width, section, screen); - notificationCenterPopout.toggle(); - } - } - - function openAppDrawer(x, y, width, section, screen) { - if (appDrawerPopout) { - setPosition(appDrawerPopout, x, y, width, section, screen); - appDrawerPopout.open(); - } - } - - function closeAppDrawer() { - appDrawerPopout?.close(); - } - - function unloadAppDrawer() { - if (!appDrawerLoader) - return; - appDrawerPopout = null; - appDrawerLoader.active = false; - } - - function toggleAppDrawer(x, y, width, section, screen) { - if (appDrawerPopout) { - setPosition(appDrawerPopout, x, y, width, section, screen); - appDrawerPopout.toggle(); - } - } - - function openProcessList(x, y, width, section, screen) { - if (processListPopout) { - setPosition(processListPopout, x, y, width, section, screen); - processListPopout.open(); - } - } - - function closeProcessList() { - processListPopout?.close(); - } - - function unloadProcessListPopout() { - if (!processListPopoutLoader) - return; - processListPopout = null; - processListPopoutLoader.active = false; - } - - function toggleProcessList(x, y, width, section, screen) { - if (processListPopout) { - setPosition(processListPopout, x, y, width, section, screen); - processListPopout.toggle(); - } - } - - property bool _dankDashWantsOpen: false - property bool _dankDashWantsToggle: false - property int _dankDashPendingTab: 0 - property real _dankDashPendingX: 0 - property real _dankDashPendingY: 0 - property real _dankDashPendingWidth: 0 - property string _dankDashPendingSection: "" - property var _dankDashPendingScreen: null - property bool _dankDashHasPosition: false - - function _storeDankDashPosition(x, y, width, section, screen, hasPos) { - _dankDashPendingX = x; - _dankDashPendingY = y; - _dankDashPendingWidth = width; - _dankDashPendingSection = section; - _dankDashPendingScreen = screen; - _dankDashHasPosition = hasPos; - } - - function openDankDash(tabIndex, x, y, width, section, screen) { - _dankDashPendingTab = tabIndex || 0; - if (dankDashPopout) { - if (arguments.length >= 6) - setPosition(dankDashPopout, x, y, width, section, screen); - dankDashPopout.currentTabIndex = _dankDashPendingTab; - dankDashPopout.dashVisible = true; - return; - } - if (!dankDashPopoutLoader) - return; - _storeDankDashPosition(x, y, width, section, screen, arguments.length >= 6); - _dankDashWantsOpen = true; - _dankDashWantsToggle = false; - dankDashPopoutLoader.active = true; - } - - function closeDankDash() { - if (dankDashPopout) - dankDashPopout.dashVisible = false; - } - - function unloadDankDash() { - if (!dankDashPopoutLoader) - return; - dankDashPopout = null; - dankDashPopoutLoader.active = false; - } - - function toggleDankDash(tabIndex, x, y, width, section, screen) { - _dankDashPendingTab = tabIndex || 0; - if (dankDashPopout) { - if (arguments.length >= 6) - setPosition(dankDashPopout, x, y, width, section, screen); - if (dankDashPopout.dashVisible) { - dankDashPopout.dashVisible = false; - } else { - dankDashPopout.currentTabIndex = _dankDashPendingTab; - dankDashPopout.dashVisible = true; - } - return; - } - if (!dankDashPopoutLoader) - return; - _storeDankDashPosition(x, y, width, section, screen, arguments.length >= 6); - _dankDashWantsToggle = true; - _dankDashWantsOpen = false; - dankDashPopoutLoader.active = true; - } - - function _onDankDashPopoutLoaded() { - if (!dankDashPopout) - return; - - if (_dankDashHasPosition) - setPosition(dankDashPopout, _dankDashPendingX, _dankDashPendingY, _dankDashPendingWidth, _dankDashPendingSection, _dankDashPendingScreen); - - if (_dankDashWantsOpen) { - _dankDashWantsOpen = false; - dankDashPopout.currentTabIndex = _dankDashPendingTab; - dankDashPopout.dashVisible = true; - return; - } - if (_dankDashWantsToggle) { - _dankDashWantsToggle = false; - if (dankDashPopout.dashVisible) { - dankDashPopout.dashVisible = false; - } else { - dankDashPopout.currentTabIndex = _dankDashPendingTab; - dankDashPopout.dashVisible = true; - } - } - } - - function openBattery(x, y, width, section, screen) { - if (batteryPopout) { - setPosition(batteryPopout, x, y, width, section, screen); - batteryPopout.open(); - } - } - - function closeBattery() { - batteryPopout?.close(); - } - - function unloadBattery() { - if (!batteryPopoutLoader) - return; - batteryPopout = null; - batteryPopoutLoader.active = false; - } - - function toggleBattery(x, y, width, section, screen) { - if (batteryPopout) { - setPosition(batteryPopout, x, y, width, section, screen); - batteryPopout.toggle(); - } - } - - function openVpn(x, y, width, section, screen) { - if (vpnPopout) { - setPosition(vpnPopout, x, y, width, section, screen); - vpnPopout.open(); - } - } - - function closeVpn() { - vpnPopout?.close(); - } - - function unloadVpn() { - if (!vpnPopoutLoader) - return; - vpnPopout = null; - vpnPopoutLoader.active = false; - } - - function toggleVpn(x, y, width, section, screen) { - if (vpnPopout) { - setPosition(vpnPopout, x, y, width, section, screen); - vpnPopout.toggle(); - } - } - - function openSystemUpdate(x, y, width, section, screen) { - if (systemUpdatePopout) { - setPosition(systemUpdatePopout, x, y, width, section, screen); - systemUpdatePopout.open(); - } - } - - function closeSystemUpdate() { - systemUpdatePopout?.close(); - } - - function unloadSystemUpdate() { - if (!systemUpdateLoader) - return; - systemUpdatePopout = null; - systemUpdateLoader.active = false; - } - - function toggleSystemUpdate(x, y, width, section, screen) { - if (systemUpdatePopout) { - setPosition(systemUpdatePopout, x, y, width, section, screen); - systemUpdatePopout.toggle(); - } - } - - property bool _settingsWantsOpen: false - property bool _settingsWantsToggle: false - - property string _settingsPendingTab: "" - property int _settingsPendingTabIndex: -1 - - function openSettings() { - if (settingsModal) { - settingsModal.show(); - } else if (settingsModalLoader) { - _settingsWantsOpen = true; - _settingsWantsToggle = false; - settingsModalLoader.activeAsync = true; - } - } - - function openSettingsWithTab(tabName: string) { - if (settingsModal) { - settingsModal.showWithTabName(tabName); - return; - } - if (settingsModalLoader) { - _settingsPendingTab = tabName; - _settingsWantsOpen = true; - _settingsWantsToggle = false; - settingsModalLoader.activeAsync = true; - } - } - - function openSettingsWithTabIndex(tabIndex: int) { - if (settingsModal) { - settingsModal.showWithTab(tabIndex); - return; - } - if (settingsModalLoader) { - _settingsPendingTabIndex = tabIndex; - _settingsWantsOpen = true; - _settingsWantsToggle = false; - settingsModalLoader.activeAsync = true; - } - } - - function closeSettings() { - settingsModal?.close(); - } - - function toggleSettings() { - if (settingsModal) { - settingsModal.toggle(); - } else if (settingsModalLoader) { - _settingsWantsToggle = true; - _settingsWantsOpen = false; - settingsModalLoader.activeAsync = true; - } - } - - function toggleSettingsWithTab(tabName: string) { - if (settingsModal) { - var idx = settingsModal.resolveTabIndex(tabName); - if (idx >= 0) - settingsModal.currentTabIndex = idx; - settingsModal.toggle(); - return; - } - if (settingsModalLoader) { - _settingsPendingTab = tabName; - _settingsWantsToggle = true; - _settingsWantsOpen = false; - settingsModalLoader.activeAsync = true; - } - } - - function focusOrToggleSettings() { - if (settingsModal?.visible) { - const settingsTitle = I18n.tr("Settings", "settings window title"); - for (const toplevel of ToplevelManager.toplevels.values) { - if (toplevel.title !== "Settings" && toplevel.title !== settingsTitle) - continue; - if (toplevel.activated) { - settingsModal.hide(); - return; - } - toplevel.activate(); - return; - } - } - openSettings(); - } - - function focusOrToggleSettingsWithTab(tabName: string) { - if (settingsModal?.visible) { - const settingsTitle = I18n.tr("Settings", "settings window title"); - for (const toplevel of ToplevelManager.toplevels.values) { - if (toplevel.title !== "Settings" && toplevel.title !== settingsTitle) - continue; - if (toplevel.activated) { - settingsModal.hide(); - return; - } - var idx = settingsModal.resolveTabIndex(tabName); - if (idx >= 0) - settingsModal.currentTabIndex = idx; - toplevel.activate(); - return; - } - } - openSettingsWithTab(tabName); - } - - function unloadSettings() { - if (settingsModalLoader) { - settingsModal = null; - settingsModalLoader.active = false; - } - } - - function _onSettingsModalLoaded() { - if (_settingsWantsOpen) { - _settingsWantsOpen = false; - if (_settingsPendingTabIndex >= 0) { - settingsModal?.showWithTab(_settingsPendingTabIndex); - _settingsPendingTabIndex = -1; - } else if (_settingsPendingTab) { - settingsModal?.showWithTabName(_settingsPendingTab); - _settingsPendingTab = ""; - } else { - settingsModal?.show(); - } - return; - } - if (_settingsWantsToggle) { - _settingsWantsToggle = false; - if (_settingsPendingTabIndex >= 0) { - settingsModal.currentTabIndex = _settingsPendingTabIndex; - _settingsPendingTabIndex = -1; - } else if (_settingsPendingTab) { - var idx = settingsModal?.resolveTabIndex(_settingsPendingTab) ?? -1; - if (idx >= 0) - settingsModal.currentTabIndex = idx; - _settingsPendingTab = ""; - } - settingsModal?.toggle(); - } - } - - function openClipboardHistory() { - clipboardHistoryModal?.show(); - } - - function closeClipboardHistory() { - clipboardHistoryModal?.close(); - } - - function unloadClipboardHistoryPopout() { - if (!clipboardHistoryPopoutLoader) - return; - clipboardHistoryPopout = null; - clipboardHistoryPopoutLoader.active = false; - } - - function unloadLayoutPopout() { - if (!layoutPopoutLoader) - return; - layoutPopout = null; - layoutPopoutLoader.active = false; - } - - property bool _dankLauncherV2WantsOpen: false - property bool _dankLauncherV2WantsToggle: false - property string _dankLauncherV2PendingQuery: "" - property string _dankLauncherV2PendingMode: "" - - function openDankLauncherV2() { - if (dankLauncherV2Modal) { - dankLauncherV2Modal.show(); - } else if (dankLauncherV2ModalLoader) { - _dankLauncherV2WantsOpen = true; - _dankLauncherV2WantsToggle = false; - dankLauncherV2ModalLoader.active = true; - } - } - - function openDankLauncherV2WithQuery(query: string) { - if (dankLauncherV2Modal) { - dankLauncherV2Modal.showWithQuery(query); - } else if (dankLauncherV2ModalLoader) { - _dankLauncherV2PendingQuery = query; - _dankLauncherV2WantsOpen = true; - _dankLauncherV2WantsToggle = false; - dankLauncherV2ModalLoader.active = true; - } - } - - function openDankLauncherV2WithMode(mode: string) { - if (dankLauncherV2Modal) { - dankLauncherV2Modal.showWithMode(mode); - } else if (dankLauncherV2ModalLoader) { - _dankLauncherV2PendingMode = mode; - _dankLauncherV2WantsOpen = true; - _dankLauncherV2WantsToggle = false; - dankLauncherV2ModalLoader.active = true; - } - } - - function closeDankLauncherV2() { - dankLauncherV2Modal?.hide(); - } - - function unloadDankLauncherV2() { - if (dankLauncherV2ModalLoader) { - dankLauncherV2Modal = null; - dankLauncherV2ModalLoader.active = false; - } - } - - function toggleDankLauncherV2() { - if (dankLauncherV2Modal) { - dankLauncherV2Modal.toggle(); - } else if (dankLauncherV2ModalLoader) { - _dankLauncherV2WantsToggle = true; - _dankLauncherV2WantsOpen = false; - dankLauncherV2ModalLoader.active = true; - } - } - - function toggleDankLauncherV2WithMode(mode: string) { - if (dankLauncherV2Modal) { - dankLauncherV2Modal.toggleWithMode(mode); - } else if (dankLauncherV2ModalLoader) { - _dankLauncherV2PendingMode = mode; - _dankLauncherV2WantsToggle = true; - _dankLauncherV2WantsOpen = false; - dankLauncherV2ModalLoader.active = true; - } - } - - function toggleDankLauncherV2WithQuery(query: string) { - if (dankLauncherV2Modal) { - dankLauncherV2Modal.toggleWithQuery(query); - } else if (dankLauncherV2ModalLoader) { - _dankLauncherV2PendingQuery = query; - _dankLauncherV2WantsOpen = true; - _dankLauncherV2WantsToggle = false; - dankLauncherV2ModalLoader.active = true; - } - } - - function _onDankLauncherV2ModalLoaded() { - if (_dankLauncherV2WantsOpen) { - _dankLauncherV2WantsOpen = false; - if (_dankLauncherV2PendingQuery) { - dankLauncherV2Modal?.showWithQuery(_dankLauncherV2PendingQuery); - _dankLauncherV2PendingQuery = ""; - } else if (_dankLauncherV2PendingMode) { - dankLauncherV2Modal?.showWithMode(_dankLauncherV2PendingMode); - _dankLauncherV2PendingMode = ""; - } else { - dankLauncherV2Modal?.show(); - } - return; - } - if (_dankLauncherV2WantsToggle) { - _dankLauncherV2WantsToggle = false; - if (_dankLauncherV2PendingMode) { - dankLauncherV2Modal?.toggleWithMode(_dankLauncherV2PendingMode); - _dankLauncherV2PendingMode = ""; - } else { - dankLauncherV2Modal?.toggle(); - } - } - } - - function openPowerMenu() { - powerMenuModal?.openCentered(); - } - - function closePowerMenu() { - powerMenuModal?.close(); - } - - function togglePowerMenu() { - if (powerMenuModal) { - if (powerMenuModal.shouldBeVisible) { - powerMenuModal.close(); - } else { - powerMenuModal.openCentered(); - } - } - } - - function showProcessListModal() { - if (processListModal) { - processListModal.show(); - } else if (processListModalLoader) { - processListModalLoader.active = true; - Qt.callLater(() => processListModal?.show()); - } - } - - function hideProcessListModal() { - processListModal?.hide(); - } - - function toggleProcessListModal() { - if (processListModal) { - processListModal.toggle(); - } else if (processListModalLoader) { - processListModalLoader.active = true; - Qt.callLater(() => processListModal?.show()); - } - } - - function showColorPicker() { - colorPickerModal?.show(); - } - - function hideColorPicker() { - colorPickerModal?.close(); - } - - function showNotificationModal() { - notificationModal?.show(); - } - - function hideNotificationModal() { - notificationModal?.close(); - } - - function showWifiPasswordModal(ssid) { - if (wifiPasswordModalLoader) - wifiPasswordModalLoader.active = true; - if (wifiPasswordModal) - wifiPasswordModal.show(ssid); - } - - function showWifiQRCodeModal(ssid) { - if (wifiQRCodeModalLoader) - wifiQRCodeModalLoader.active = true; - if (wifiQRCodeModal) - wifiQRCodeModal.show(ssid); - } - - function showHiddenNetworkModal() { - if (wifiPasswordModalLoader) - wifiPasswordModalLoader.active = true; - if (wifiPasswordModal) - wifiPasswordModal.showHidden(); - } - - function hideWifiPasswordModal() { - wifiPasswordModal?.hide(); - } - - function showNetworkInfoModal() { - networkInfoModal?.show(); - } - - function hideNetworkInfoModal() { - networkInfoModal?.close(); - } - - function openNotepad() { - if (notepadSlideouts.length > 0) { - notepadSlideouts[0]?.show(); - } - } - - function closeNotepad() { - if (notepadSlideouts.length > 0) { - notepadSlideouts[0]?.hide(); - } - } - - function toggleNotepad() { - if (notepadSlideouts.length > 0) { - notepadSlideouts[0]?.toggle(); - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PortalService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PortalService.qml deleted file mode 100644 index 73c0a9f..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PortalService.qml +++ /dev/null @@ -1,316 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property bool accountsServiceAvailable: false - property string systemProfileImage: "" - property string profileImage: "" - property bool settingsPortalAvailable: false - property int systemColorScheme: 0 - - property bool freedeskAvailable: false - property string colorSchemeCommand: "" - property string pendingProfileImage: "" - - readonly property string socketPath: Quickshell.env("DMS_SOCKET") - - function init() { - } - - function getSystemProfileImage() { - if (!freedeskAvailable) - return; - const username = Quickshell.env("USER"); - if (!username) - return; - DMSService.sendRequest("freedesktop.accounts.getUserIconFile", { - "username": username - }, response => { - if (response.result && response.result.success) { - const iconFile = response.result.value || ""; - if (iconFile && iconFile !== "" && iconFile !== "/var/lib/AccountsService/icons/") { - systemProfileImage = iconFile; - if (!profileImage || profileImage === "") { - profileImage = iconFile; - } - } - } - }); - } - - function getUserProfileImage(username) { - if (!username) { - profileImage = ""; - return; - } - if (Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true") { - profileImage = ""; - return; - } - - if (!freedeskAvailable) { - profileImage = ""; - return; - } - - DMSService.sendRequest("freedesktop.accounts.getUserIconFile", { - "username": username - }, response => { - if (response.result && response.result.success) { - const icon = response.result.value || ""; - if (icon && icon !== "" && icon !== "/var/lib/AccountsService/icons/") { - profileImage = icon; - } else { - profileImage = ""; - } - } else { - profileImage = ""; - } - }); - } - - function setProfileImage(imagePath) { - if (accountsServiceAvailable) { - pendingProfileImage = imagePath; - setSystemProfileImage(imagePath || ""); - } else { - profileImage = imagePath; - } - } - - function getSystemColorScheme() { - if (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal === false) { - return; - } - if (!freedeskAvailable) - return; - DMSService.sendRequest("freedesktop.settings.getColorScheme", null, response => { - if (response.result) { - systemColorScheme = response.result.value || 0; - } - }); - } - - function setLightMode(isLightMode) { - if (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal === false) { - return; - } - setSystemColorScheme(isLightMode); - } - - function setSystemColorScheme(isLightMode) { - if (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal === false) { - return; - } - - const targetScheme = isLightMode ? "default" : "prefer-dark"; - - if (colorSchemeCommand === "gsettings") { - Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", targetScheme]); - } - if (colorSchemeCommand === "dconf") { - Quickshell.execDetached(["dconf", "write", "/org/gnome/desktop/interface/color-scheme", `'${targetScheme}'`]); - } - } - - function setSystemIconTheme(themeName) { - if (!settingsPortalAvailable || !freedeskAvailable) - return; - DMSService.sendRequest("freedesktop.settings.setIconTheme", { - "iconTheme": themeName - }, response => { - if (response.error) { - console.warn("PortalService: Failed to set icon theme:", response.error); - } - }); - } - - function setSystemProfileImage(imagePath) { - if (!accountsServiceAvailable || !freedeskAvailable) - return; - DMSService.sendRequest("freedesktop.accounts.setIconFile", { - "path": imagePath || "" - }, response => { - if (response.error) { - console.warn("PortalService: Failed to set icon file:", response.error); - - const errorMsg = response.error.toString(); - let userMessage = I18n.tr("Failed to set profile image"); - - if (errorMsg.includes("too large")) { - userMessage = I18n.tr("Profile image is too large. Please use a smaller image."); - } else if (errorMsg.includes("permission")) { - userMessage = I18n.tr("Permission denied to set profile image."); - } else if (errorMsg.includes("not found") || errorMsg.includes("does not exist")) { - userMessage = I18n.tr("Selected image file not found."); - } else { - userMessage = I18n.tr("Failed to set profile image: %1").arg(errorMsg.split(":").pop().trim()); - } - - Quickshell.execDetached(["notify-send", "-u", "normal", "-a", "DMS", "-i", "error", I18n.tr("Profile Image Error"), userMessage]); - - pendingProfileImage = ""; - } else { - profileImage = pendingProfileImage; - pendingProfileImage = ""; - Qt.callLater(() => getSystemProfileImage()); - } - }); - } - - Component.onCompleted: { - if (socketPath && socketPath.length > 0) { - checkDMSCapabilities(); - } else { - console.info("PortalService: DMS_SOCKET not set"); - } - colorSchemeDetector.running = true; - } - - Connections { - target: DMSService - - function onConnectionStateChanged() { - if (DMSService.isConnected) { - checkDMSCapabilities(); - } - } - } - - Connections { - target: DMSService - enabled: DMSService.isConnected - - function onCapabilitiesChanged() { - checkDMSCapabilities(); - } - } - - function checkDMSCapabilities() { - if (!DMSService.isConnected) { - return; - } - - if (DMSService.capabilities.length === 0) { - return; - } - - freedeskAvailable = DMSService.capabilities.includes("freedesktop"); - if (freedeskAvailable) { - checkAccountsService(); - checkSettingsPortal(); - } else { - console.info("PortalService: freedesktop capability not available in DMS"); - } - } - - function checkAccountsService() { - if (!freedeskAvailable) - return; - DMSService.sendRequest("freedesktop.getState", null, response => { - if (response.result && response.result.accounts) { - accountsServiceAvailable = response.result.accounts.available || false; - if (accountsServiceAvailable) { - getSystemProfileImage(); - } - } - }); - } - - function checkSettingsPortal() { - if (!freedeskAvailable) - return; - DMSService.sendRequest("freedesktop.getState", null, response => { - if (response.result && response.result.settings) { - settingsPortalAvailable = response.result.settings.available || false; - if (settingsPortalAvailable && SettingsData.syncModeWithPortal) { - getSystemColorScheme(); - } - } - }); - } - - function getGreeterUserProfileImage(username) { - if (!username) { - profileImage = ""; - return; - } - userProfileCheckProcess.command = ["bash", "-c", `uid=$(id -u ${username} 2>/dev/null) && [ -n "$uid" ] && dbus-send --system --print-reply --dest=org.freedesktop.Accounts /org/freedesktop/Accounts/User$uid org.freedesktop.DBus.Properties.Get string:org.freedesktop.Accounts.User string:IconFile 2>/dev/null | grep -oP 'string "\\K[^"]+' || echo ""`]; - userProfileCheckProcess.running = true; - } - - Process { - id: userProfileCheckProcess - command: [] - running: false - - stdout: StdioCollector { - onStreamFinished: { - const trimmed = text.trim(); - if (trimmed && trimmed !== "" && !trimmed.includes("Error") && trimmed !== "/var/lib/AccountsService/icons/") { - root.profileImage = trimmed; - } else { - root.profileImage = ""; - } - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - root.profileImage = ""; - } - } - } - - Process { - id: colorSchemeDetector - command: ["bash", "-c", "command -v gsettings || command -v dconf"] - running: false - - stdout: StdioCollector { - onStreamFinished: { - const cmd = text.trim(); - if (cmd.includes("gsettings")) { - root.colorSchemeCommand = "gsettings"; - } else if (cmd.includes("dconf")) { - root.colorSchemeCommand = "dconf"; - } - } - } - } - - IpcHandler { - target: "profile" - - function getImage(): string { - return root.profileImage; - } - - function setImage(path: string): string { - if (!path) { - return "ERROR: No path provided"; - } - - const absolutePath = path.startsWith("/") ? path : `${StandardPaths.writableLocation(StandardPaths.HomeLocation)}/${path}`; - - try { - root.setProfileImage(absolutePath); - return "SUCCESS: Profile image set to " + absolutePath; - } catch (e) { - return "ERROR: Failed to set profile image: " + e.toString(); - } - } - - function clearImage(): string { - root.setProfileImage(""); - return "SUCCESS: Profile image cleared"; - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PowerProfileWatcher.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PowerProfileWatcher.qml deleted file mode 100644 index bab3517..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PowerProfileWatcher.qml +++ /dev/null @@ -1,36 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Services.UPower - -Singleton { - id: root - - property int currentProfile: -1 - property int previousProfile: -1 - - signal profileChanged(int profile) - - Connections { - target: typeof PowerProfiles !== "undefined" ? PowerProfiles : null - - function onProfileChanged() { - if (typeof PowerProfiles !== "undefined") { - root.previousProfile = root.currentProfile; - root.currentProfile = PowerProfiles.profile; - if (root.previousProfile !== -1) { - root.profileChanged(root.currentProfile); - } - } - } - } - - Component.onCompleted: { - if (typeof PowerProfiles !== "undefined") { - root.currentProfile = PowerProfiles.profile; - root.previousProfile = PowerProfiles.profile; - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PrivacyService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PrivacyService.qml deleted file mode 100644 index 4908862..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PrivacyService.qml +++ /dev/null @@ -1,155 +0,0 @@ -pragma Singleton - -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import Quickshell.Services.Pipewire -import qs.Services - -Singleton { - id: root - - readonly property bool microphoneActive: { - if (!Pipewire.ready || !Pipewire.nodes?.values) { - return false - } - - for (let i = 0; i < Pipewire.nodes.values.length; i++) { - const node = Pipewire.nodes.values[i] - if (!node) { - continue - } - - if ((node.type & PwNodeType.AudioInStream) === PwNodeType.AudioInStream) { - if (!looksLikeSystemVirtualMic(node)) { - if (node.audio && node.audio.muted) { - return false - } - return true - } - } - } - return false - } - - PwObjectTracker { - objects: Pipewire.nodes.values.filter(node => !node.isStream) - } - - readonly property bool cameraActive: { - if (!Pipewire.ready || !Pipewire.nodes?.values) { - return false - } - - for (let i = 0; i < Pipewire.nodes.values.length; i++) { - const node = Pipewire.nodes.values[i] - if (!node || !node.ready) { - continue - } - - if (node.properties && node.properties["media.class"] === "Stream/Input/Video") { - if (node.properties["stream.is-live"] === "true") { - return true - } - } - } - return false - } - - readonly property bool screensharingActive: { - if (CompositorService.isNiri && NiriService.hasActiveCast) { - return true - } - - if (!Pipewire.ready || !Pipewire.nodes?.values) { - return false - } - - for (let i = 0; i < Pipewire.nodes.values.length; i++) { - const node = Pipewire.nodes.values[i] - if (!node || !node.ready) { - continue - } - - if ((node.type & PwNodeType.VideoSource) === PwNodeType.VideoSource) { - if (looksLikeScreencast(node)) { - return true - } - } - - if (node.properties && node.properties["media.class"] === "Stream/Output/Video") { - if (looksLikeScreencast(node)) { - return true - } - } - - if (node.properties && node.properties["media.class"] === "Stream/Input/Audio") { - const mediaName = (node.properties["media.name"] || "").toLowerCase() - const appName = (node.properties["application.name"] || "").toLowerCase() - - if (mediaName.includes("desktop") || appName.includes("screen") || appName === "obs") { - if (node.properties["stream.is-live"] === "true") { - if (node.audio && node.audio.muted) { - return false - } - return true - } - } - } - } - return false - } - - readonly property bool anyPrivacyActive: microphoneActive || cameraActive || screensharingActive - - function looksLikeSystemVirtualMic(node) { - if (!node) { - return false - } - const name = (node.name || "").toLowerCase() - const mediaName = (node.properties && node.properties["media.name"] || "").toLowerCase() - const appName = (node.properties && node.properties["application.name"] || "").toLowerCase() - const combined = name + " " + mediaName + " " + appName - return /cava|monitor|system/.test(combined) - } - - function looksLikeScreencast(node) { - if (!node) { - return false - } - const appName = (node.properties && node.properties["application.name"] || "").toLowerCase() - const nodeName = (node.name || "").toLowerCase() - const mediaName = (node.properties && node.properties["media.name"] || "").toLowerCase() - const combined = appName + " " + nodeName + " " + mediaName - return /xdg-desktop-portal|xdpw|screencast|screen-cast|screen|gnome shell|kwin|obs|niri/.test(combined) - } - - function getMicrophoneStatus() { - return microphoneActive ? "active" : "inactive" - } - - function getCameraStatus() { - return cameraActive ? "active" : "inactive" - } - - function getScreensharingStatus() { - return screensharingActive ? "active" : "inactive" - } - - function getPrivacySummary() { - const active = [] - if (microphoneActive) { - active.push("microphone") - } - if (cameraActive) { - active.push("camera") - } - if (screensharingActive) { - active.push("screensharing") - } - - return active.length > 0 ? `Privacy active: ${active.join(", ")}` : "No privacy concerns detected" - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SessionService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SessionService.qml deleted file mode 100644 index 7f5a1ed..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SessionService.qml +++ /dev/null @@ -1,619 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import Quickshell.Hyprland -import Quickshell.I3 -import Quickshell.Wayland -import qs.Common - -Singleton { - id: root - - property bool hasUwsm: false - property bool isElogind: false - property bool hibernateSupported: false - property bool inhibitorAvailable: true - property bool idleInhibited: false - property string inhibitReason: "Keep system awake" - property string nvidiaCommand: "" - - readonly property bool nativeInhibitorAvailable: { - try { - return typeof IdleInhibitor !== "undefined"; - } catch (e) { - return false; - } - } - - property bool loginctlAvailable: false - property bool wtypeAvailable: false - property string sessionId: "" - property string sessionPath: "" - property bool locked: false - property bool active: false - property bool idleHint: false - property bool lockedHint: false - property bool preparingForSleep: false - property string sessionType: "" - property string userName: "" - property string seat: "" - property string display: "" - - signal sessionLocked - signal sessionUnlocked - signal sessionResumed - signal loginctlStateChanged - - property bool stateInitialized: false - - readonly property string socketPath: Quickshell.env("DMS_SOCKET") - - Timer { - id: sessionInitTimer - interval: 200 - running: true - repeat: false - onTriggered: { - detectElogindProcess.running = true; - detectHibernateProcess.running = true; - detectPrimeRunProcess.running = true; - detectWtypeProcess.running = true; - console.info("SessionService: Native inhibitor available:", nativeInhibitorAvailable); - if (!SettingsData.loginctlLockIntegration) { - console.log("SessionService: loginctl lock integration disabled by user"); - return; - } - if (socketPath && socketPath.length > 0) { - checkDMSCapabilities(); - } else { - console.log("SessionService: DMS_SOCKET not set"); - } - } - } - - Process { - id: detectUwsmProcess - running: false - command: ["which", "uwsm"] - - onExited: function (exitCode) { - hasUwsm = (exitCode === 0); - } - } - - Process { - id: detectElogindProcess - running: false - command: ["sh", "-c", "ps -eo comm= | grep -E '^(elogind|elogind-daemon)$'"] - - onExited: function (exitCode) { - console.log("SessionService: Elogind detection exited with code", exitCode); - isElogind = (exitCode === 0); - } - } - - Process { - id: detectHibernateProcess - running: false - command: ["grep", "-q", "disk", "/sys/power/state"] - - onExited: function (exitCode) { - hibernateSupported = (exitCode === 0); - } - } - - Process { - id: hibernateProcess - running: false - - property string errorOutput: "" - - stderr: SplitParser { - splitMarker: "\n" - onRead: data => hibernateProcess.errorOutput += data.trim() - } - - onExited: function (exitCode) { - if (exitCode === 0) { - errorOutput = ""; - return; - } - ToastService.showError("Hibernate failed", errorOutput); - errorOutput = ""; - } - } - - Process { - id: detectWtypeProcess - running: false - command: ["which", "wtype"] - onExited: exitCode => { - wtypeAvailable = (exitCode === 0); - } - } - - Process { - id: detectPrimeRunProcess - running: false - command: ["which", "prime-run"] - - onExited: function (exitCode) { - if (exitCode === 0) { - nvidiaCommand = "prime-run"; - } else { - detectNvidiaOffloadProcess.running = true; - } - } - } - - Process { - id: detectNvidiaOffloadProcess - running: false - command: ["which", "nvidia-offload"] - - onExited: function (exitCode) { - if (exitCode === 0) { - nvidiaCommand = "nvidia-offload"; - } - } - } - - Process { - id: uwsmLogout - command: ["uwsm", "stop"] - running: false - - stdout: SplitParser { - splitMarker: "\n" - onRead: data => { - if (data.trim().toLowerCase().includes("not running")) { - _logout(); - } - } - } - - onExited: function (exitCode) { - if (exitCode === 0) { - return; - } - _logout(); - } - } - - function escapeShellArg(arg) { - return "'" + arg.replace(/'/g, "'\\''") + "'"; - } - - function needsShellExecution(prefix) { - if (!prefix || prefix.length === 0) - return false; - return /[;&|<>()$`\\"']/.test(prefix); - } - - function parseEnvVars(envVarsStr) { - if (!envVarsStr || envVarsStr.trim().length === 0) - return {}; - const envObj = {}; - const pairs = envVarsStr.trim().split(/\s+/); - for (const pair of pairs) { - const eqIndex = pair.indexOf("="); - if (eqIndex > 0) { - const key = pair.substring(0, eqIndex); - const value = pair.substring(eqIndex + 1); - envObj[key] = value; - } - } - return envObj; - } - - function launchDesktopEntry(desktopEntry, useNvidia) { - let cmd = desktopEntry.command; - - const appId = desktopEntry.id || desktopEntry.execString || desktopEntry.exec || ""; - const override = SessionData.getAppOverride(appId); - - const dgpu = useNvidia || (override?.launchOnDgpu && nvidiaCommand); - if (dgpu && nvidiaCommand) - cmd = [nvidiaCommand].concat(cmd); - - if (override?.extraFlags) { - const extraArgs = override.extraFlags.trim().split(/\s+/).filter(arg => arg.length > 0); - cmd = cmd.concat(extraArgs); - } - - const userPrefix = SettingsData.launchPrefix?.trim() || ""; - const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || ""; - const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix; - const workDir = desktopEntry.workingDirectory || Quickshell.env("HOME"); - const cursorEnv = typeof SettingsData.getCursorEnvironment === "function" ? SettingsData.getCursorEnvironment() : {}; - - const overrideEnv = override?.envVars ? parseEnvVars(override.envVars) : {}; - const finalEnv = Object.assign({}, cursorEnv, overrideEnv); - - if (desktopEntry.runInTerminal) { - const terminal = Quickshell.env("TERMINAL") || "xterm"; - const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" "); - const shellCmd = prefix.length > 0 ? `${prefix} ${escapedCmd}` : escapedCmd; - Quickshell.execDetached({ - command: [terminal, "-e", "sh", "-c", shellCmd], - workingDirectory: workDir, - environment: finalEnv - }); - return; - } - - if (prefix.length > 0 && needsShellExecution(prefix)) { - const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" "); - Quickshell.execDetached({ - command: ["sh", "-c", `${prefix} ${escapedCmd}`], - workingDirectory: workDir, - environment: finalEnv - }); - return; - } - - if (prefix.length > 0) - cmd = prefix.split(" ").concat(cmd); - - Quickshell.execDetached({ - command: cmd, - workingDirectory: workDir, - environment: finalEnv - }); - } - - function launchDesktopAction(desktopEntry, action, useNvidia) { - let cmd = action.command; - - const appId = desktopEntry.id || desktopEntry.execString || desktopEntry.exec || ""; - const override = SessionData.getAppOverride(appId); - const dgpu = useNvidia || (override?.launchOnDgpu && nvidiaCommand); - if (dgpu && nvidiaCommand) - cmd = [nvidiaCommand].concat(cmd); - - const userPrefix = SettingsData.launchPrefix?.trim() || ""; - const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || ""; - const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix; - const workDir = desktopEntry.workingDirectory || Quickshell.env("HOME"); - const cursorEnv = typeof SettingsData.getCursorEnvironment === "function" ? SettingsData.getCursorEnvironment() : {}; - - if (prefix.length > 0 && needsShellExecution(prefix)) { - const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" "); - Quickshell.execDetached({ - command: ["sh", "-c", `${prefix} ${escapedCmd}`], - workingDirectory: workDir, - environment: cursorEnv - }); - return; - } - - if (prefix.length > 0) - cmd = prefix.split(" ").concat(cmd); - - Quickshell.execDetached({ - command: cmd, - workingDirectory: workDir, - environment: cursorEnv - }); - } - - // * Session management - function logout() { - if (hasUwsm) { - uwsmLogout.running = true; - } - _logout(); - } - - function _logout() { - if (SettingsData.customPowerActionLogout.length === 0) { - if (CompositorService.isNiri) { - NiriService.quit(); - return; - } - - if (CompositorService.isDwl) { - DwlService.quit(); - return; - } - - if (CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) { - try { - I3.dispatch("exit"); - } catch (_) {} - return; - } - - Hyprland.dispatch("exit"); - } else { - Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout]); - } - } - - function suspend() { - if (SettingsData.customPowerActionSuspend.length === 0) { - Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend"]); - } else { - Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionSuspend]); - } - } - - function hibernate() { - hibernateProcess.errorOutput = ""; - if (SettingsData.customPowerActionHibernate.length > 0) { - hibernateProcess.command = ["sh", "-c", SettingsData.customPowerActionHibernate]; - } else { - hibernateProcess.command = [isElogind ? "loginctl" : "systemctl", "hibernate"]; - } - hibernateProcess.running = true; - } - - function suspendThenHibernate() { - Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend-then-hibernate"]); - } - - function suspendWithBehavior(behavior) { - if (behavior === SettingsData.SuspendBehavior.Hibernate) { - hibernate(); - } else if (behavior === SettingsData.SuspendBehavior.SuspendThenHibernate) { - suspendThenHibernate(); - } else { - suspend(); - } - } - - function reboot() { - if (SettingsData.customPowerActionReboot.length === 0) { - Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "reboot"]); - } else { - Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionReboot]); - } - } - - function poweroff() { - if (SettingsData.customPowerActionPowerOff.length === 0) { - Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "poweroff"]); - } else { - Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionPowerOff]); - } - } - - // * Idle Inhibitor - signal inhibitorChanged - - function enableIdleInhibit() { - if (idleInhibited) { - return; - } - console.log("SessionService: Enabling idle inhibit (native:", nativeInhibitorAvailable, ")"); - idleInhibited = true; - inhibitorChanged(); - } - - function disableIdleInhibit() { - if (!idleInhibited) { - return; - } - console.log("SessionService: Disabling idle inhibit (native:", nativeInhibitorAvailable, ")"); - idleInhibited = false; - inhibitorChanged(); - } - - function toggleIdleInhibit() { - if (idleInhibited) { - disableIdleInhibit(); - } else { - enableIdleInhibit(); - } - } - - function setInhibitReason(reason) { - inhibitReason = reason; - - if (idleInhibited && !nativeInhibitorAvailable) { - const wasActive = idleInhibited; - idleInhibited = false; - - Qt.callLater(() => { - if (wasActive) { - idleInhibited = true; - } - }); - } - } - - Process { - id: idleInhibitProcess - - command: { - if (!idleInhibited || nativeInhibitorAvailable) { - return ["true"]; - } - - console.log("SessionService: Starting systemd/elogind inhibit process"); - return [isElogind ? "elogind-inhibit" : "systemd-inhibit", "--what=idle", "--who=quickshell", `--why=${inhibitReason}`, "--mode=block", "sleep", "infinity"]; - } - - running: idleInhibited && !nativeInhibitorAvailable - - onRunningChanged: { - console.log("SessionService: Inhibit process running:", running, "(native:", nativeInhibitorAvailable, ")"); - } - - onExited: function (exitCode) { - if (idleInhibited && exitCode !== 0 && !nativeInhibitorAvailable) { - console.warn("SessionService: Inhibitor process crashed with exit code:", exitCode); - idleInhibited = false; - ToastService.showWarning("Idle inhibitor failed"); - } - } - } - - Connections { - target: DMSService - - function onConnectionStateChanged() { - if (DMSService.isConnected) { - checkDMSCapabilities(); - } - } - - function onCapabilitiesReceived() { - syncSleepInhibitor(); - } - } - - Connections { - target: DMSService - enabled: DMSService.isConnected - - function onCapabilitiesChanged() { - checkDMSCapabilities(); - } - } - - Connections { - target: SettingsData - - function onLoginctlLockIntegrationChanged() { - if (SettingsData.loginctlLockIntegration) { - if (socketPath && socketPath.length > 0 && loginctlAvailable) { - if (!stateInitialized) { - stateInitialized = true; - getLoginctlState(); - syncLockBeforeSuspend(); - } - } - } else { - stateInitialized = false; - } - syncSleepInhibitor(); - } - - function onLockBeforeSuspendChanged() { - if (SettingsData.loginctlLockIntegration) { - syncLockBeforeSuspend(); - } - syncSleepInhibitor(); - } - } - - Connections { - target: DMSService - enabled: SettingsData.loginctlLockIntegration - - function onLoginctlStateUpdate(data) { - updateLoginctlState(data); - } - - function onLoginctlEvent(event) { - handleLoginctlEvent(event); - } - } - - function checkDMSCapabilities() { - if (!DMSService.isConnected) { - return; - } - - if (DMSService.capabilities.length === 0) { - return; - } - - if (DMSService.capabilities.includes("loginctl")) { - loginctlAvailable = true; - if (SettingsData.loginctlLockIntegration && !stateInitialized) { - stateInitialized = true; - getLoginctlState(); - syncLockBeforeSuspend(); - } - } else { - loginctlAvailable = false; - console.log("SessionService: loginctl capability not available in DMS"); - } - } - - function getLoginctlState() { - if (!loginctlAvailable) - return; - DMSService.sendRequest("loginctl.getState", null, response => { - if (response.result) { - updateLoginctlState(response.result); - } - }); - } - - function syncLockBeforeSuspend() { - if (!loginctlAvailable) - return; - DMSService.sendRequest("loginctl.setLockBeforeSuspend", { - enabled: SettingsData.lockBeforeSuspend - }, response => { - if (response.error) { - console.warn("SessionService: Failed to sync lock before suspend:", response.error); - } else { - console.log("SessionService: Synced lock before suspend:", SettingsData.lockBeforeSuspend); - } - }); - } - - function syncSleepInhibitor() { - if (!loginctlAvailable) - return; - if (!DMSService.apiVersion || DMSService.apiVersion < 4) - return; - DMSService.sendRequest("loginctl.setSleepInhibitorEnabled", { - enabled: SettingsData.loginctlLockIntegration && SettingsData.lockBeforeSuspend - }, response => { - if (response.error) { - console.warn("SessionService: Failed to sync sleep inhibitor:", response.error); - } else { - console.log("SessionService: Synced sleep inhibitor:", SettingsData.loginctlLockIntegration); - } - }); - } - - function updateLoginctlState(state) { - const wasLocked = locked; - const wasSleeping = preparingForSleep; - - sessionId = state.sessionId || ""; - sessionPath = state.sessionPath || ""; - locked = state.locked || false; - active = state.active || false; - idleHint = state.idleHint || false; - lockedHint = state.lockedHint || false; - preparingForSleep = state.preparingForSleep || false; - sessionType = state.sessionType || ""; - userName = state.userName || ""; - seat = state.seat || ""; - display = state.display || ""; - - if (locked && !wasLocked) { - sessionLocked(); - } else if (!locked && wasLocked) { - sessionUnlocked(); - } - - if (wasSleeping && !preparingForSleep) { - sessionResumed(); - } - - loginctlStateChanged(); - } - - function handleLoginctlEvent(event) { - if (event.event === "Lock") { - locked = true; - lockedHint = true; - sessionLocked(); - } else if (event.event === "Unlock") { - locked = false; - lockedHint = false; - sessionUnlocked(); - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SettingsSearchService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SettingsSearchService.qml deleted file mode 100644 index 97e5d11..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SettingsSearchService.qml +++ /dev/null @@ -1,240 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property string query: "" - property var results: [] - property string targetSection: "" - property string highlightSection: "" - property var registeredCards: ({}) - property var settingsIndex: [] - property bool indexLoaded: false - property var _translatedCache: [] - - readonly property var conditionMap: ({ - "isNiri": () => CompositorService.isNiri, - "isHyprland": () => CompositorService.isHyprland, - "isDwl": () => CompositorService.isDwl, - "keybindsAvailable": () => KeybindsService.available, - "soundsAvailable": () => AudioService.soundsAvailable, - "cupsAvailable": () => CupsService.cupsAvailable, - "networkNotLegacy": () => !NetworkService.usingLegacy, - "dmsConnected": () => DMSService.isConnected && DMSService.apiVersion >= 23, - "matugenAvailable": () => Theme.matugenAvailable - }) - - Component.onCompleted: indexFile.reload() - - FileView { - id: indexFile - path: Qt.resolvedUrl("../translations/settings_search_index.json") - onLoaded: { - try { - root.settingsIndex = JSON.parse(text()); - root.indexLoaded = true; - root._rebuildTranslationCache(); - } catch (e) { - console.warn("SettingsSearchService: Failed to parse index:", e); - root.settingsIndex = []; - root._translatedCache = []; - } - } - onLoadFailed: error => console.warn("SettingsSearchService: Failed to load index:", error) - } - - function registerCard(settingKey, item, flickable) { - if (!settingKey) - return; - registeredCards[settingKey] = { - item: item, - flickable: flickable - }; - if (targetSection === settingKey) - scrollTimer.restart(); - } - - function unregisterCard(settingKey) { - if (!settingKey) - return; - let cards = registeredCards; - delete cards[settingKey]; - registeredCards = cards; - } - - function navigateToSection(section) { - targetSection = section; - if (registeredCards[section]) - scrollTimer.restart(); - } - - function scrollToTarget() { - if (!targetSection) - return; - const entry = registeredCards[targetSection]; - if (!entry || !entry.item || !entry.flickable) - return; - const flickable = entry.flickable; - const item = entry.item; - const contentItem = flickable.contentItem; - - if (!contentItem) - return; - const mapped = item.mapToItem(contentItem, 0, 0); - const maxY = Math.max(0, flickable.contentHeight - flickable.height); - const targetY = Math.min(maxY, Math.max(0, mapped.y - 16)); - flickable.contentY = targetY; - - highlightSection = targetSection; - targetSection = ""; - highlightTimer.restart(); - } - - function clearHighlight() { - highlightSection = ""; - } - - Timer { - id: scrollTimer - interval: 50 - onTriggered: root.scrollToTarget() - } - - Timer { - id: highlightTimer - interval: 2500 - onTriggered: root.highlightSection = "" - } - - function checkCondition(item) { - if (!item.conditionKey) - return true; - const condFn = conditionMap[item.conditionKey]; - if (!condFn) - return true; - return condFn(); - } - - function translateItem(item) { - return { - section: item.section, - label: I18n.tr(item.label), - tabIndex: item.tabIndex, - category: I18n.tr(item.category), - keywords: item.keywords || [], - icon: item.icon || "settings", - description: item.description ? I18n.tr(item.description) : "", - conditionKey: item.conditionKey - }; - } - - function _rebuildTranslationCache() { - var cache = []; - for (var i = 0; i < settingsIndex.length; i++) { - var item = settingsIndex[i]; - var t = translateItem(item); - cache.push({ - section: t.section, - label: t.label, - tabIndex: t.tabIndex, - category: t.category, - keywords: t.keywords, - icon: t.icon, - description: t.description, - conditionKey: t.conditionKey, - labelLower: t.label.toLowerCase(), - categoryLower: t.category.toLowerCase() - }); - } - _translatedCache = cache; - } - - function search(text) { - query = text; - if (!text) { - results = []; - return; - } - - var queryLower = text.toLowerCase().trim(); - var queryWords = queryLower.split(/\s+/).filter(w => w.length > 0); - var scored = []; - var cache = _translatedCache; - - for (var i = 0; i < cache.length; i++) { - var entry = cache[i]; - if (!checkCondition(entry)) - continue; - - var labelLower = entry.labelLower; - var categoryLower = entry.categoryLower; - var score = 0; - - if (labelLower === queryLower) { - score = 10000; - } else if (labelLower.startsWith(queryLower)) { - score = 5000; - } else if (labelLower.includes(queryLower)) { - score = 1000; - } else if (categoryLower.includes(queryLower)) { - score = 500; - } - - if (score === 0) { - var keywords = entry.keywords; - for (var k = 0; k < keywords.length; k++) { - if (keywords[k].startsWith(queryLower)) { - score = 800; - break; - } - if (keywords[k].includes(queryLower) && score < 400) { - score = 400; - } - } - } - - if (score === 0 && queryWords.length > 1) { - var allMatch = true; - for (var w = 0; w < queryWords.length; w++) { - var word = queryWords[w]; - if (labelLower.includes(word)) - continue; - var inKeywords = false; - for (var k = 0; k < entry.keywords.length; k++) { - if (entry.keywords[k].includes(word)) { - inKeywords = true; - break; - } - } - if (!inKeywords && !categoryLower.includes(word)) { - allMatch = false; - break; - } - } - if (allMatch) - score = 300; - } - - if (score > 0) { - scored.push({ - item: entry, - score: score - }); - } - } - - scored.sort((a, b) => b.score - a.score); - results = scored.slice(0, 15).map(s => s.item); - } - - function clear() { - query = ""; - results = []; - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SystemUpdateService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SystemUpdateService.qml deleted file mode 100644 index 53ca7ad..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SystemUpdateService.qml +++ /dev/null @@ -1,396 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common - -Singleton { - id: root - - property int refCount: 0 - property var availableUpdates: [] - property bool isChecking: false - property bool hasError: false - property string errorMessage: "" - property string updChecker: "" - property string pkgManager: "" - property string distribution: "" - property bool distributionSupported: false - property string shellVersion: "" - property string shellCodename: "" - property string semverVersion: "" - - function getParsedShellVersion() { - return parseVersion(semverVersion); - } - - readonly property var archBasedUCSettings: { - "listUpdatesSettings": { - "params": [], - "correctExitCodes": [0, 2] // Exit code 0 = updates available, 2 = no updates - }, - "parserSettings": { - "lineRegex": /^(\S+)\s+([^\s]+)\s+->\s+([^\s]+)$/, - "entryProducer": function (match) { - return { - "name": match[1], - "currentVersion": match[2], - "newVersion": match[3], - "description": `${match[1]} ${match[2]} → ${match[3]}` - }; - } - } - } - - readonly property var archBasedPMSettings: function(requiresSudo) { - return { - "listUpdatesSettings": { - "params": ["-Qu"], - "correctExitCodes": [0, 1] // Exit code 0 = updates available, 1 = no updates - }, - "upgradeSettings": { - "params": ["-Syu"], - "requiresSudo": requiresSudo - }, - "parserSettings": { - "lineRegex": /^(\S+)\s+([^\s]+)\s+->\s+([^\s]+)$/, - "entryProducer": function (match) { - return { - "name": match[1], - "currentVersion": match[2], - "newVersion": match[3], - "description": `${match[1]} ${match[2]} → ${match[3]}` - }; - } - } - } - } - - readonly property var fedoraBasedPMSettings: { - "listUpdatesSettings": { - "params": ["list", "--upgrades", "--quiet", "--color=never"], - "correctExitCodes": [0, 1] // Exit code 0 = updates available, 1 = no updates - }, - "upgradeSettings": { - "params": ["upgrade"], - "requiresSudo": true - }, - "parserSettings": { - "lineRegex": /^([^\s]+)\s+([^\s]+)\s+.*$/, - "entryProducer": function (match) { - return { - "name": match[1], - "currentVersion": "", - "newVersion": match[2], - "description": `${match[1]} → ${match[2]}` - }; - } - } - } - - readonly property var updateCheckerParams: { - "checkupdates": archBasedUCSettings - } - readonly property var packageManagerParams: { - "yay": archBasedPMSettings(false), - "paru": archBasedPMSettings(false), - "pacman": archBasedPMSettings(true), - "dnf": fedoraBasedPMSettings - } - readonly property list<string> supportedDistributions: ["arch", "artix", "cachyos", "manjaro", "endeavouros", "fedora"] - readonly property int updateCount: availableUpdates.length - readonly property bool helperAvailable: pkgManager !== "" && distributionSupported - - Process { - id: distributionDetection - command: ["sh", "-c", "cat /etc/os-release | grep '^ID=' | cut -d'=' -f2 | tr -d '\"'"] - running: true - - onExited: exitCode => { - if (exitCode === 0) { - distribution = stdout.text.trim().toLowerCase(); - distributionSupported = supportedDistributions.includes(distribution); - - if (distributionSupported) { - updateFinderDetection.running = true; - pkgManagerDetection.running = true; - checkForUpdates(); - } else { - console.warn("SystemUpdate: Unsupported distribution:", distribution); - } - } else { - console.warn("SystemUpdate: Failed to detect distribution"); - } - } - - stdout: StdioCollector {} - - Component.onCompleted: { - versionDetection.running = true; - } - } - - Process { - id: versionDetection - command: ["sh", "-c", `cd "${Quickshell.shellDir}" && if [ -d .git ]; then echo "(git) $(git rev-parse --short HEAD)"; elif [ -f VERSION ]; then cat VERSION; fi`] - - stdout: StdioCollector { - onStreamFinished: { - shellVersion = text.trim(); - } - } - } - - Process { - id: semverDetection - command: ["sh", "-c", `cd "${Quickshell.shellDir}" && if [ -f VERSION ]; then cat VERSION; fi`] - running: true - - stdout: StdioCollector { - onStreamFinished: { - semverVersion = text.trim(); - } - } - } - - Process { - id: codenameDetection - command: ["sh", "-c", `cd "${Quickshell.shellDir}" && if [ -f CODENAME ]; then cat CODENAME; fi`] - running: true - - stdout: StdioCollector { - onStreamFinished: { - shellCodename = text.trim(); - } - } - } - - Process { - id: updateFinderDetection - command: ["sh", "-c", "which checkupdates"] - - onExited: exitCode => { - if (exitCode === 0) { - const exeFound = stdout.text.trim(); - updChecker = exeFound.split('/').pop(); - } else { - console.warn("SystemUpdate: No update checker found. Will use package manager."); - } - } - - stdout: StdioCollector {} - } - - Process { - id: pkgManagerDetection - command: ["sh", "-c", "which paru || which yay || which pacman || which dnf"] - - onExited: exitCode => { - if (exitCode === 0) { - const exeFound = stdout.text.trim(); - pkgManager = exeFound.split('/').pop(); - } else { - console.warn("SystemUpdate: No package manager found"); - } - } - - stdout: StdioCollector {} - } - - Process { - id: updateChecker - - onExited: exitCode => { - isChecking = false; - const correctExitCodes = updChecker.length > 0 ? [updChecker].concat(updateCheckerParams[updChecker].listUpdatesSettings.correctExitCodes) : [pkgManager].concat(packageManagerParams[pkgManager].listUpdatesSettings.correctExitCodes); - if (correctExitCodes.includes(exitCode)) { - parseUpdates(stdout.text); - hasError = false; - errorMessage = ""; - } else { - hasError = true; - errorMessage = "Failed to check for updates"; - console.warn("SystemUpdate: Update check failed with code:", exitCode); - } - } - - stdout: StdioCollector {} - } - - Process { - id: updater - onExited: exitCode => { - checkForUpdates(); - } - } - - function checkForUpdates() { - if (!distributionSupported || (!pkgManager && !updChecker) || isChecking) - return; - isChecking = true; - hasError = false; - if (pkgManager === "paru" || pkgManager === "yay") { - const repoCmd = updChecker.length > 0 ? updChecker : `${pkgManager} -Qu`; - updateChecker.command = ["sh", "-c", `(${repoCmd} 2>/dev/null; ${pkgManager} -Qua 2>/dev/null) || true`]; - } else if (updChecker.length > 0) { - updateChecker.command = [updChecker].concat(updateCheckerParams[updChecker].listUpdatesSettings.params); - } else { - updateChecker.command = [pkgManager].concat(packageManagerParams[pkgManager].listUpdatesSettings.params); - } - updateChecker.running = true; - } - - function parseUpdates(output) { - const lines = output.trim().split('\n').filter(line => line.trim()); - const updates = []; - - const regex = packageManagerParams[pkgManager].parserSettings.lineRegex; - const entryProducer = packageManagerParams[pkgManager].parserSettings.entryProducer; - - for (const line of lines) { - const match = line.match(regex); - if (match) { - updates.push(entryProducer(match)); - } - } - - availableUpdates = updates; - } - - function runUpdates() { - if (!distributionSupported || !pkgManager || updateCount === 0) - return; - const terminal = Quickshell.env("TERMINAL") || "xterm"; - - if (SettingsData.updaterUseCustomCommand && SettingsData.updaterCustomCommand.length > 0) { - const updateCommand = `${SettingsData.updaterCustomCommand} && echo -n "Updates complete! " ; echo "Press Enter to close..." && read`; - const termClass = SettingsData.updaterTerminalAdditionalParams; - - var finalCommand = [terminal]; - if (termClass.length > 0) { - finalCommand = finalCommand.concat(termClass.split(" ")); - } - finalCommand.push("-e"); - finalCommand.push("sh"); - finalCommand.push("-c"); - finalCommand.push(updateCommand); - updater.command = finalCommand; - } else { - const params = packageManagerParams[pkgManager].upgradeSettings.params.join(" "); - const sudo = packageManagerParams[pkgManager].upgradeSettings.requiresSudo ? "sudo" : ""; - const updateCommand = `${sudo} ${pkgManager} ${params} && echo -n "Updates complete! " ; echo "Press Enter to close..." && read`; - - updater.command = [terminal, "-e", "sh", "-c", updateCommand]; - } - updater.running = true; - } - - Timer { - interval: 30 * 60 * 1000 - repeat: true - running: refCount > 0 && distributionSupported && (pkgManager || updChecker) - onTriggered: checkForUpdates() - } - - IpcHandler { - target: "systemupdater" - - function updatestatus(): string { - if (root.isChecking) { - return "ERROR: already checking"; - } - if (!distributionSupported) { - return "ERROR: distribution not supported"; - } - if (!pkgManager && !updChecker) { - return "ERROR: update checker not available"; - } - root.checkForUpdates(); - return "SUCCESS: Now checking..."; - } - } - - function parseVersion(versionStr) { - if (!versionStr || typeof versionStr !== "string") - return { - major: 0, - minor: 0, - patch: 0 - }; - - let v = versionStr.trim(); - if (v.startsWith("v")) - v = v.substring(1); - - const dashIdx = v.indexOf("-"); - if (dashIdx !== -1) - v = v.substring(0, dashIdx); - - const plusIdx = v.indexOf("+"); - if (plusIdx !== -1) - v = v.substring(0, plusIdx); - - const parts = v.split("."); - return { - major: parseInt(parts[0], 10) || 0, - minor: parseInt(parts[1], 10) || 0, - patch: parseInt(parts[2], 10) || 0 - }; - } - - function compareVersions(v1, v2) { - if (v1.major !== v2.major) - return v1.major - v2.major; - if (v1.minor !== v2.minor) - return v1.minor - v2.minor; - return v1.patch - v2.patch; - } - - function checkVersionRequirement(requirementStr, currentVersion) { - if (!requirementStr || typeof requirementStr !== "string") - return true; - - const req = requirementStr.trim(); - let operator = ""; - let versionPart = req; - - if (req.startsWith(">=")) { - operator = ">="; - versionPart = req.substring(2); - } else if (req.startsWith("<=")) { - operator = "<="; - versionPart = req.substring(2); - } else if (req.startsWith(">")) { - operator = ">"; - versionPart = req.substring(1); - } else if (req.startsWith("<")) { - operator = "<"; - versionPart = req.substring(1); - } else if (req.startsWith("=")) { - operator = "="; - versionPart = req.substring(1); - } else { - operator = ">="; - } - - const reqVersion = parseVersion(versionPart); - const cmp = compareVersions(currentVersion, reqVersion); - - switch (operator) { - case ">=": - return cmp >= 0; - case ">": - return cmp > 0; - case "<=": - return cmp <= 0; - case "<": - return cmp < 0; - case "=": - return cmp === 0; - default: - return cmp >= 0; - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ToastService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ToastService.qml deleted file mode 100644 index 7684f90..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/ToastService.qml +++ /dev/null @@ -1,174 +0,0 @@ -pragma Singleton - -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell - -Singleton { - id: root - - readonly property int levelInfo: 0 - readonly property int levelWarn: 1 - readonly property int levelError: 2 - property string currentMessage: "" - property int currentLevel: levelInfo - property bool toastVisible: false - property var toastQueue: [] - property string currentDetails: "" - property string currentCommand: "" - property bool hasDetails: false - property string wallpaperErrorStatus: "" - property int maxQueueSize: 3 - property var lastErrorTime: ({}) - property int errorThrottleMs: 1000 - property string currentCategory: "" - - function showToast(message, level = levelInfo, details = "", command = "", category = "") { - const now = Date.now() - const messageKey = message + level - - if (level === levelError) { - const lastTime = lastErrorTime[messageKey] || 0 - if (now - lastTime < errorThrottleMs) { - return - } - lastErrorTime[messageKey] = now - } - - if (category && level === levelError) { - if (currentCategory === category && toastVisible && currentLevel === levelError) { - currentMessage = message - currentDetails = details || "" - currentCommand = command || "" - hasDetails = currentDetails.length > 0 || currentCommand.length > 0 - resetToastState() - if (hasDetails) { - toastTimer.interval = 8000 - } else { - toastTimer.interval = 5000 - } - toastTimer.restart() - return - } - - toastQueue = toastQueue.filter(t => t.category !== category) - } - - const isDuplicate = toastQueue.some(toast => - toast.message === message && toast.level === level - ) - if (isDuplicate) { - return - } - - if (toastQueue.length >= maxQueueSize) { - if (level === levelError) { - toastQueue = toastQueue.filter(t => t.level !== levelError).slice(0, maxQueueSize - 1) - } else { - return - } - } - - toastQueue.push({ - "message": message, - "level": level, - "details": details, - "command": command, - "category": category - }) - if (!toastVisible) { - processQueue() - } - } - - function showInfo(message, details = "", command = "", category = "") { - showToast(message, levelInfo, details, command, category) - } - - function showWarning(message, details = "", command = "", category = "") { - showToast(message, levelWarn, details, command, category) - } - - function showError(message, details = "", command = "", category = "") { - showToast(message, levelError, details, command, category) - } - - function dismissCategory(category) { - if (!category) { - return - } - - if (currentCategory === category && toastVisible) { - hideToast() - return - } - - toastQueue = toastQueue.filter(t => t.category !== category) - } - - function hideToast() { - toastVisible = false - currentMessage = "" - currentDetails = "" - currentCommand = "" - currentCategory = "" - hasDetails = false - currentLevel = levelInfo - toastTimer.stop() - resetToastState() - if (toastQueue.length > 0) { - processQueue() - } - } - - function processQueue() { - if (toastQueue.length === 0) { - return - } - - const toast = toastQueue.shift() - currentMessage = toast.message - currentLevel = toast.level - currentDetails = toast.details || "" - currentCommand = toast.command || "" - currentCategory = toast.category || "" - hasDetails = currentDetails.length > 0 || currentCommand.length > 0 - toastVisible = true - resetToastState() - - if (toast.level === levelError && hasDetails) { - toastTimer.interval = 8000 - toastTimer.start() - } else { - toastTimer.interval = toast.level === levelError ? 5000 : toast.level === levelWarn ? 3000 : 1500 - toastTimer.start() - } - } - - signal resetToastState - - function stopTimer() { - toastTimer.stop() - } - - function restartTimer() { - if (hasDetails && currentLevel === levelError) { - toastTimer.interval = 8000 - toastTimer.restart() - } - } - - function clearWallpaperError() { - wallpaperErrorStatus = "" - } - - Timer { - id: toastTimer - - interval: 5000 - running: false - repeat: false - onTriggered: hideToast() - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/TrackArtService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/TrackArtService.qml deleted file mode 100644 index e2c6ea5..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/TrackArtService.qml +++ /dev/null @@ -1,49 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import Quickshell -import QtQuick -import Quickshell.Services.Mpris -import qs.Common - -Singleton { - id: root - - property string _lastArtUrl: "" - property string _bgArtSource: "" - property bool loading: false - - function loadArtwork(url) { - if (!url || url === "") { - _bgArtSource = ""; - _lastArtUrl = ""; - loading = false; - return; - } - if (url === _lastArtUrl) - return; - _lastArtUrl = url; - - if (url.startsWith("http://") || url.startsWith("https://")) { - _bgArtSource = url; - loading = false; - return; - } - - loading = true; - const localUrl = url; - const filePath = url.startsWith("file://") ? url.substring(7) : url; - Proc.runCommand("trackart", ["test", "-f", filePath], (output, exitCode) => { - if (_lastArtUrl !== localUrl) - return; - _bgArtSource = exitCode === 0 ? localUrl : ""; - loading = false; - }, 200); - } - - property MprisPlayer activePlayer: MprisController.activePlayer - - onActivePlayerChanged: { - loadArtwork(activePlayer?.trackArtUrl ?? ""); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/UserInfoService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/UserInfoService.qml deleted file mode 100644 index 8378e01..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/UserInfoService.qml +++ /dev/null @@ -1,35 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import qs.Common - -Singleton { - id: root - - property string username: "" - property string fullName: "" - property string profilePicture: "" - property string hostname: "" - property bool profileAvailable: false - - function getUserInfo() { - Proc.runCommand("userInfo", ["sh", "-c", "echo \"$USER|$(getent passwd $USER | cut -d: -f5 | cut -d, -f1)|$(hostname)\""], (output, exitCode) => { - if (exitCode !== 0) { - root.username = "User"; - root.fullName = "User"; - root.hostname = "System"; - return; - } - const parts = output.trim().split("|"); - if (parts.length >= 3) { - root.username = parts[0] || ""; - root.fullName = parts[1] || parts[0] || ""; - root.hostname = parts[2] || ""; - } - }, 0); - } - - Component.onCompleted: getUserInfo() -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/VPNService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/VPNService.qml deleted file mode 100644 index effc54e..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/VPNService.qml +++ /dev/null @@ -1,221 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import qs.Common - -Singleton { - id: root - - readonly property bool available: DMSNetworkService.vpnAvailable - - property var plugins: [] - property var allExtensions: [] - property bool importing: false - property string importError: "" - - property var editConfig: null - property bool configLoading: false - - property bool pluginsLoading: false - - signal importComplete(string uuid, string name) - signal configLoaded(var config) - signal configUpdated - signal vpnDeleted(string uuid) - - Component.onCompleted: { - if (available) { - fetchPlugins(); - } - } - - Connections { - target: DMSNetworkService - function onVpnAvailableChanged() { - if (DMSNetworkService.vpnAvailable && plugins.length === 0) { - fetchPlugins(); - } - } - } - - function fetchPlugins() { - if (!available || pluginsLoading) - return; - pluginsLoading = true; - - DMSService.sendRequest("network.vpn.plugins", null, response => { - pluginsLoading = false; - if (response.error) { - console.warn("VPNService: Failed to fetch plugins:", response.error); - return; - } - if (!response.result) - return; - plugins = response.result; - const extSet = new Set(); - for (const plugin of response.result) { - for (const ext of plugin.fileExtensions || []) { - extSet.add(ext); - } - } - allExtensions = Array.from(extSet); - }); - } - - function importVpn(filePath, name = "") { - if (!available || importing) - return; - importing = true; - importError = ""; - - const params = { - file: filePath - }; - if (name) - params.name = name; - - DMSService.sendRequest("network.vpn.import", params, response => { - importing = false; - - if (response.error) { - importError = response.error; - ToastService.showError(I18n.tr("Failed to import VPN"), response.error); - return; - } - - if (!response.result) - return; - if (response.result.success) { - ToastService.showInfo(I18n.tr("VPN imported: %1").arg(response.result.name || "")); - DMSNetworkService.refreshVpnProfiles(); - importComplete(response.result.uuid || "", response.result.name || ""); - return; - } - - importError = response.result.error || "Import failed"; - ToastService.showError(I18n.tr("Failed to import VPN"), importError); - }); - } - - function getConfig(uuidOrName) { - if (!available) - return; - configLoading = true; - editConfig = null; - - DMSService.sendRequest("network.vpn.getConfig", { - uuid: uuidOrName - }, response => { - configLoading = false; - - if (response.error) { - ToastService.showError(I18n.tr("Failed to load VPN config"), response.error); - return; - } - - if (response.result) { - editConfig = response.result; - configLoaded(response.result); - } - }); - } - - function updateConfig(uuid, updates) { - if (!available) - return; - const params = { - uuid: uuid - }; - if (updates.name !== undefined) - params.name = updates.name; - if (updates.autoconnect !== undefined) - params.autoconnect = updates.autoconnect; - if (updates.data !== undefined) - params.data = updates.data; - - DMSService.sendRequest("network.vpn.updateConfig", params, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to update VPN"), response.error); - return; - } - ToastService.showInfo(I18n.tr("VPN configuration updated")); - DMSNetworkService.refreshVpnProfiles(); - getConfig(uuid); - configUpdated(); - }); - } - - function deleteVpn(uuidOrName) { - if (!available) - return; - DMSService.sendRequest("network.vpn.delete", { - uuid: uuidOrName - }, response => { - if (response.error) { - ToastService.showError(I18n.tr("Failed to delete VPN"), response.error); - return; - } - ToastService.showInfo(I18n.tr("VPN deleted")); - DMSNetworkService.refreshVpnProfiles(); - vpnDeleted(uuidOrName); - }); - } - - function getFileFilter() { - if (allExtensions.length === 0) { - return ["*.ovpn", "*.conf"]; - } - return allExtensions.map(e => "*" + e); - } - - function getExtensionsForPlugin(serviceType) { - const plugin = plugins.find(p => p.serviceType === serviceType); - if (!plugin) - return ["*.conf"]; - return (plugin.fileExtensions || [".conf"]).map(e => "*" + e); - } - - function getPluginName(serviceType) { - if (!serviceType) - return "VPN"; - - const plugin = plugins.find(p => p.serviceType === serviceType); - if (plugin) - return plugin.name; - - const svc = serviceType.toLowerCase(); - if (svc.includes("openvpn")) - return "OpenVPN"; - if (svc.includes("wireguard")) - return "WireGuard"; - if (svc.includes("openconnect")) - return "OpenConnect"; - if (svc.includes("fortissl") || svc.includes("forti")) - return "Fortinet"; - if (svc.includes("strongswan")) - return "IPsec (strongSwan)"; - if (svc.includes("libreswan")) - return "IPsec (Libreswan)"; - if (svc.includes("l2tp")) - return "L2TP/IPsec"; - if (svc.includes("pptp")) - return "PPTP"; - if (svc.includes("vpnc")) - return "Cisco (vpnc)"; - if (svc.includes("sstp")) - return "SSTP"; - - const parts = serviceType.split('.'); - return parts[parts.length - 1] || "VPN"; - } - - function getVpnTypeFromProfile(profile) { - if (!profile) - return "VPN"; - if (profile.type === "wireguard") - return "WireGuard"; - return getPluginName(profile.serviceType); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/WallpaperCyclingService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/WallpaperCyclingService.qml deleted file mode 100644 index a07c563..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/WallpaperCyclingService.qml +++ /dev/null @@ -1,495 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import Quickshell.Wayland -import qs.Common -import qs.Services - -Singleton { - id: root - - property bool cyclingActive: false - readonly property bool fullscreenShowing: { - if (!ToplevelManager.toplevels?.values) - return false; - for (const toplevel of ToplevelManager.toplevels.values) { - if (toplevel.fullscreen && toplevel.activated) - return true; - } - return false; - } - readonly property bool shouldPauseCycling: fullscreenShowing || SessionService.locked - property string cachedCyclingTime: SessionData.wallpaperCyclingTime - property int cachedCyclingInterval: SessionData.wallpaperCyclingInterval - property string lastTimeCheck: "" - property var monitorTimers: ({}) - property var monitorLastTimeChecks: ({}) - property var monitorProcesses: ({}) - - Component { - id: monitorTimerComponent - Timer { - property string targetScreen: "" - running: false - repeat: true - onTriggered: { - if (typeof WallpaperCyclingService !== "undefined" && targetScreen !== "" && !WallpaperCyclingService.shouldPauseCycling) { - WallpaperCyclingService.cycleNextForMonitor(targetScreen); - } - } - } - } - - Component { - id: monitorProcessComponent - Process { - property string targetScreenName: "" - property string currentWallpaper: "" - property bool goToPrevious: false - running: false - stdout: StdioCollector { - onStreamFinished: { - if (text && text.trim()) { - const files = text.trim().split('\n').filter(file => file.length > 0); - if (files.length <= 1) - return; - const wallpaperList = files.sort(); - const currentPath = currentWallpaper; - let currentIndex = wallpaperList.findIndex(path => path === currentPath); - if (currentIndex === -1) - currentIndex = 0; - let targetIndex; - if (goToPrevious) { - targetIndex = currentIndex === 0 ? wallpaperList.length - 1 : currentIndex - 1; - } else { - targetIndex = (currentIndex + 1) % wallpaperList.length; - } - const targetWallpaper = wallpaperList[targetIndex]; - if (targetWallpaper && targetWallpaper !== currentPath) { - if (targetScreenName) { - SessionData.setMonitorWallpaper(targetScreenName, targetWallpaper); - } else { - SessionData.setWallpaper(targetWallpaper); - } - } - } - } - } - } - } - - Connections { - target: SessionData - - function onWallpaperCyclingEnabledChanged() { - updateCyclingState(); - } - - function onWallpaperCyclingModeChanged() { - updateCyclingState(); - } - - function onWallpaperCyclingIntervalChanged() { - cachedCyclingInterval = SessionData.wallpaperCyclingInterval; - if (SessionData.wallpaperCyclingMode === "interval") { - updateCyclingState(); - } - } - - function onWallpaperCyclingTimeChanged() { - cachedCyclingTime = SessionData.wallpaperCyclingTime; - if (SessionData.wallpaperCyclingMode === "time") { - updateCyclingState(); - } - } - - function onPerMonitorWallpaperChanged() { - updateCyclingState(); - } - - function onMonitorCyclingSettingsChanged() { - updateCyclingState(); - } - } - - Connections { - target: SessionService - - function onSessionUnlocked() { - if (SessionData.wallpaperCyclingEnabled || SessionData.perMonitorWallpaper) { - updateCyclingState(); - } - } - } - - function updateCyclingState() { - if (SessionData.perMonitorWallpaper) { - stopCycling(); - updatePerMonitorCycling(); - } else if (SessionData.wallpaperCyclingEnabled && SessionData.wallpaperPath) { - startCycling(); - stopAllMonitorCycling(); - } else { - stopCycling(); - stopAllMonitorCycling(); - } - } - - function updatePerMonitorCycling() { - if (typeof Quickshell === "undefined") - return; - var screens = Quickshell.screens; - for (var i = 0; i < screens.length; i++) { - var screenName = screens[i].name; - var settings = SessionData.getMonitorCyclingSettings(screenName); - var wallpaper = SessionData.getMonitorWallpaper(screenName); - - if (settings.enabled && wallpaper && !wallpaper.startsWith("#")) { - startMonitorCycling(screenName, settings); - } else { - stopMonitorCycling(screenName); - } - } - } - - function stopAllMonitorCycling() { - var screenNames = Object.keys(monitorTimers); - for (var i = 0; i < screenNames.length; i++) { - stopMonitorCycling(screenNames[i]); - } - } - - function startCycling() { - switch (SessionData.wallpaperCyclingMode) { - case "interval": - lastTimeCheck = ""; - intervalTimer.interval = cachedCyclingInterval * 1000; - intervalTimer.start(); - cyclingActive = true; - break; - case "time": - intervalTimer.stop(); - cyclingActive = true; - checkTimeBasedCycling(); - break; - } - } - - function stopCycling() { - intervalTimer.stop(); - cyclingActive = false; - } - - function startMonitorCycling(screenName, settings) { - switch (settings.mode) { - case "interval": - { - var newChecks = Object.assign({}, monitorLastTimeChecks); - delete newChecks[screenName]; - monitorLastTimeChecks = newChecks; - - var timer = monitorTimers[screenName]; - if (!timer && monitorTimerComponent && monitorTimerComponent.status === Component.Ready) { - var newTimers = Object.assign({}, monitorTimers); - var newTimer = monitorTimerComponent.createObject(root); - newTimer.targetScreen = screenName; - newTimers[screenName] = newTimer; - monitorTimers = newTimers; - timer = newTimer; - } - if (timer) { - timer.interval = settings.interval * 1000; - timer.start(); - } - break; - } - case "time": - { - var existingTimer = monitorTimers[screenName]; - if (existingTimer) { - existingTimer.stop(); - existingTimer.destroy(); - var newTimers = Object.assign({}, monitorTimers); - delete newTimers[screenName]; - monitorTimers = newTimers; - } - - var newChecks = Object.assign({}, monitorLastTimeChecks); - newChecks[screenName] = ""; - monitorLastTimeChecks = newChecks; - break; - } - } - } - - function stopMonitorCycling(screenName) { - var timer = monitorTimers[screenName]; - if (timer) { - timer.stop(); - timer.destroy(); - var newTimers = Object.assign({}, monitorTimers); - delete newTimers[screenName]; - monitorTimers = newTimers; - } - - var process = monitorProcesses[screenName]; - if (process) { - process.destroy(); - var newProcesses = Object.assign({}, monitorProcesses); - delete newProcesses[screenName]; - monitorProcesses = newProcesses; - } - - var newChecks = Object.assign({}, monitorLastTimeChecks); - delete newChecks[screenName]; - monitorLastTimeChecks = newChecks; - } - - function cycleToNextWallpaper(screenName, wallpaperPath) { - const currentWallpaper = wallpaperPath || SessionData.wallpaperPath; - if (!currentWallpaper) - return; - const wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/')); - - if (screenName && monitorProcessComponent && monitorProcessComponent.status === Component.Ready) { - // Use per-monitor process - var process = monitorProcesses[screenName]; - if (!process) { - var newProcesses = Object.assign({}, monitorProcesses); - var newProcess = monitorProcessComponent.createObject(root); - newProcesses[screenName] = newProcess; - monitorProcesses = newProcesses; - process = newProcess; - } - - if (process) { - process.command = ["sh", "-c", `find -L "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`]; - process.targetScreenName = screenName; - process.currentWallpaper = currentWallpaper; - process.goToPrevious = false; - process.running = true; - } - } else { - // Use global process for fallback - cyclingProcess.command = ["sh", "-c", `find -L "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`]; - cyclingProcess.targetScreenName = screenName || ""; - cyclingProcess.currentWallpaper = currentWallpaper; - cyclingProcess.running = true; - } - } - - function cycleToPrevWallpaper(screenName, wallpaperPath) { - const currentWallpaper = wallpaperPath || SessionData.wallpaperPath; - if (!currentWallpaper) - return; - const wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/')); - - if (screenName && monitorProcessComponent && monitorProcessComponent.status === Component.Ready) { - // Use per-monitor process (same as next, but with prev flag) - var process = monitorProcesses[screenName]; - if (!process) { - var newProcesses = Object.assign({}, monitorProcesses); - var newProcess = monitorProcessComponent.createObject(root); - newProcesses[screenName] = newProcess; - monitorProcesses = newProcesses; - process = newProcess; - } - - if (process) { - process.command = ["sh", "-c", `find -L "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`]; - process.targetScreenName = screenName; - process.currentWallpaper = currentWallpaper; - process.goToPrevious = true; - process.running = true; - } - } else { - // Use global process for fallback - prevCyclingProcess.command = ["sh", "-c", `find -L "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`]; - prevCyclingProcess.targetScreenName = screenName || ""; - prevCyclingProcess.currentWallpaper = currentWallpaper; - prevCyclingProcess.running = true; - } - } - - function cycleNextManually() { - if (SessionData.wallpaperPath) { - cycleToNextWallpaper(); - // Restart timers if cycling is active - if (cyclingActive && SessionData.wallpaperCyclingEnabled) { - if (SessionData.wallpaperCyclingMode === "interval") { - intervalTimer.interval = cachedCyclingInterval * 1000; - intervalTimer.restart(); - } - } - } - } - - function cyclePrevManually() { - if (SessionData.wallpaperPath) { - cycleToPrevWallpaper(); - // Restart timers if cycling is active - if (cyclingActive && SessionData.wallpaperCyclingEnabled) { - if (SessionData.wallpaperCyclingMode === "interval") { - intervalTimer.interval = cachedCyclingInterval * 1000; - intervalTimer.restart(); - } - } - } - } - - function cycleNextForMonitor(screenName) { - if (!screenName) - return; - var currentWallpaper = SessionData.getMonitorWallpaper(screenName); - if (currentWallpaper) { - cycleToNextWallpaper(screenName, currentWallpaper); - } - } - - function cyclePrevForMonitor(screenName) { - if (!screenName) - return; - var currentWallpaper = SessionData.getMonitorWallpaper(screenName); - if (currentWallpaper) { - cycleToPrevWallpaper(screenName, currentWallpaper); - } - } - - function checkTimeBasedCycling() { - if (shouldPauseCycling) - return; - const currentTime = Qt.formatTime(systemClock.date, "hh:mm"); - - if (!SessionData.perMonitorWallpaper) { - if (currentTime === cachedCyclingTime && currentTime !== lastTimeCheck) { - lastTimeCheck = currentTime; - cycleToNextWallpaper(); - } else if (currentTime !== cachedCyclingTime) { - lastTimeCheck = ""; - } - } else { - checkPerMonitorTimeBasedCycling(currentTime); - } - } - - function checkPerMonitorTimeBasedCycling(currentTime) { - if (typeof Quickshell === "undefined") - return; - var screens = Quickshell.screens; - for (var i = 0; i < screens.length; i++) { - var screenName = screens[i].name; - var settings = SessionData.getMonitorCyclingSettings(screenName); - var wallpaper = SessionData.getMonitorWallpaper(screenName); - - if (settings.enabled && settings.mode === "time" && wallpaper && !wallpaper.startsWith("#")) { - var lastCheck = monitorLastTimeChecks[screenName] || ""; - - if (currentTime === settings.time && currentTime !== lastCheck) { - var newChecks = Object.assign({}, monitorLastTimeChecks); - newChecks[screenName] = currentTime; - monitorLastTimeChecks = newChecks; - cycleNextForMonitor(screenName); - } else if (currentTime !== settings.time) { - var newChecks = Object.assign({}, monitorLastTimeChecks); - newChecks[screenName] = ""; - monitorLastTimeChecks = newChecks; - } - } - } - } - - Timer { - id: intervalTimer - interval: cachedCyclingInterval * 1000 - running: false - repeat: true - onTriggered: { - if (shouldPauseCycling) - return; - cycleToNextWallpaper(); - } - } - - SystemClock { - id: systemClock - precision: SystemClock.Minutes - onDateChanged: { - if ((SessionData.wallpaperCyclingMode === "time" && cyclingActive) || SessionData.perMonitorWallpaper) { - checkTimeBasedCycling(); - } - } - } - - Process { - id: cyclingProcess - - property string targetScreenName: "" - property string currentWallpaper: "" - - running: false - - stdout: StdioCollector { - onStreamFinished: { - if (text && text.trim()) { - const files = text.trim().split('\n').filter(file => file.length > 0); - if (files.length <= 1) - return; - const wallpaperList = files.sort(); - const currentPath = cyclingProcess.currentWallpaper; - let currentIndex = wallpaperList.findIndex(path => path === currentPath); - if (currentIndex === -1) - currentIndex = 0; - - const nextIndex = (currentIndex + 1) % wallpaperList.length; - const nextWallpaper = wallpaperList[nextIndex]; - - if (nextWallpaper && nextWallpaper !== currentPath) { - if (cyclingProcess.targetScreenName) { - SessionData.setMonitorWallpaper(cyclingProcess.targetScreenName, nextWallpaper); - } else { - SessionData.setWallpaper(nextWallpaper); - } - } - } - } - } - } - - Process { - id: prevCyclingProcess - - property string targetScreenName: "" - property string currentWallpaper: "" - - running: false - - stdout: StdioCollector { - onStreamFinished: { - if (text && text.trim()) { - const files = text.trim().split('\n').filter(file => file.length > 0); - if (files.length <= 1) - return; - const wallpaperList = files.sort(); - const currentPath = prevCyclingProcess.currentWallpaper; - let currentIndex = wallpaperList.findIndex(path => path === currentPath); - if (currentIndex === -1) - currentIndex = 0; - - const prevIndex = currentIndex === 0 ? wallpaperList.length - 1 : currentIndex - 1; - const prevWallpaper = wallpaperList[prevIndex]; - - if (prevWallpaper && prevWallpaper !== currentPath) { - if (prevCyclingProcess.targetScreenName) { - SessionData.setMonitorWallpaper(prevCyclingProcess.targetScreenName, prevWallpaper); - } else { - SessionData.setWallpaper(prevWallpaper); - } - } - } - } - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/WeatherService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/WeatherService.qml deleted file mode 100644 index 134789e..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/WeatherService.qml +++ /dev/null @@ -1,914 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Common -import qs.Modules.Greetd -import "../Common/suncalc.js" as SunCalc - -Singleton { - id: root - - property int refCount: 0 - - property var selectedDate: new Date() - property var weather: ({ - "available": false, - "loading": true, - "temp": 0, - "tempF": 0, - "feelsLike": 0, - "feelsLikeF": 0, - "city": "", - "country": "", - "wCode": 0, - "humidity": 0, - "wind": "", - "sunrise": "06:00", - "sunset": "18:00", - "uv": 0, - "pressure": 0, - "precipitationProbability": 0, - "isDay": true, - "forecast": [] - }) - - property var location: null - property int updateInterval: 900000 // 15 minutes - property int retryAttempts: 0 - property int maxRetryAttempts: 3 - property int retryDelay: 30000 - property int lastFetchTime: 0 - property int minFetchInterval: 30000 - property int persistentRetryCount: 0 - - readonly property var lowPriorityCmd: ["nice", "-n", "19", "ionice", "-c3"] - readonly property var curlBaseCmd: ["curl", "-sS", "--fail", "--connect-timeout", "3", "--max-time", "6", "--limit-rate", "100k", "--compressed"] - - property var weatherIcons: ({ - "0": "clear_day", - "1": "clear_day", - "2": "partly_cloudy_day", - "3": "cloud", - "45": "foggy", - "48": "foggy", - "51": "rainy", - "53": "rainy", - "55": "rainy", - "56": "rainy", - "57": "rainy", - "61": "rainy", - "63": "rainy", - "65": "rainy", - "66": "rainy", - "67": "rainy", - "71": "cloudy_snowing", - "73": "cloudy_snowing", - "75": "snowing_heavy", - "77": "cloudy_snowing", - "80": "rainy", - "81": "rainy", - "82": "rainy", - "85": "cloudy_snowing", - "86": "snowing_heavy", - "95": "thunderstorm", - "96": "thunderstorm", - "99": "thunderstorm" - }) - - property var nightWeatherIcons: ({ - "0": "clear_night", - "1": "clear_night", - "2": "partly_cloudy_night", - "3": "cloud", - "45": "foggy", - "48": "foggy", - "51": "rainy", - "53": "rainy", - "55": "rainy", - "56": "rainy", - "57": "rainy", - "61": "rainy", - "63": "rainy", - "65": "rainy", - "66": "rainy", - "67": "rainy", - "71": "cloudy_snowing", - "73": "cloudy_snowing", - "75": "snowing_heavy", - "77": "cloudy_snowing", - "80": "rainy", - "81": "rainy", - "82": "rainy", - "85": "cloudy_snowing", - "86": "snowing_heavy", - "95": "thunderstorm", - "96": "thunderstorm", - "99": "thunderstorm" - }) - - function getWeatherIcon(code, isDay) { - if (typeof isDay === "undefined") { - isDay = weather.isDay; - } - const iconMap = isDay ? weatherIcons : nightWeatherIcons; - return iconMap[String(code)] || "cloud"; - } - - function getWeatherCondition(code) { - const conditions = { - "0": I18n.tr("Clear Sky"), - "1": I18n.tr("Clear Sky"), - "2": I18n.tr("Partly Cloudy"), - "3": I18n.tr("Overcast"), - "45": I18n.tr("Fog"), - "48": I18n.tr("Fog"), - "51": I18n.tr("Drizzle"), - "53": I18n.tr("Drizzle"), - "55": I18n.tr("Drizzle"), - "56": I18n.tr("Freezing Drizzle"), - "57": I18n.tr("Freezing Drizzle"), - "61": I18n.tr("Light Rain"), - "63": I18n.tr("Rain"), - "65": I18n.tr("Heavy Rain"), - "66": I18n.tr("Light Rain"), - "67": I18n.tr("Heavy Rain"), - "71": I18n.tr("Light Snow"), - "73": I18n.tr("Snow"), - "75": I18n.tr("Heavy Snow"), - "77": I18n.tr("Snow"), - "80": I18n.tr("Light Rain"), - "81": I18n.tr("Rain"), - "82": I18n.tr("Heavy Rain"), - "85": I18n.tr("Light Snow Showers"), - "86": I18n.tr("Heavy Snow Showers"), - "95": I18n.tr("Thunderstorm"), - "96": I18n.tr("Thunderstorm with Hail"), - "99": I18n.tr("Thunderstorm with Hail") - }; - return conditions[String(code)] || I18n.tr("Unknown"); - } - - property var moonPhaseNames: ["moon_new", "moon_waxing_crescent", "moon_first_quarter", "moon_waxing_gibbous", "moon_full", "moon_waning_gibbous", "moon_last_quarter", "moon_waning_crescent"] - - function getMoonPhase(date) { - const phases = moonPhaseNames; - const iconCount = phases.length; - const moon = SunCalc.getMoonIllumination(date); - const index = ((Math.floor(moon.phase * iconCount + 0.5) % iconCount) + iconCount) % iconCount; - - return phases[index]; - } - - function getMoonAngle(date) { - if (!location) { - return; - } - const pos = SunCalc.getMoonPosition(date, location.latitude, location.longitude); - return pos.parralacticAngle; - } - - function getLocation() { - return location; - } - - function getSunDeclination(date) { - return SunCalc.sunCoords(SunCalc.toDays(date)).dec * 180 / Math.PI; - } - - function getSunTimes(date) { - if (!location) { - return null; - } - return SunCalc.getTimes(date, location.latitude, location.longitude, location.elevation); - } - - function getEcliptic(date, points = 60) { - if (!location) { - return null; - } - const lat = location.latitude; - const lon = location.longitude; - const times = SunCalc.getTimes(date, lat, lon); - const solarNoon = times.solarNoon; - - const eclipticPoints = []; - - const sunIsNorth = getSunDeclination(date) > lat; - const transitAzimuth = sunIsNorth ? 0 : Math.PI; - - for (let i = 0; i <= points; i++) { - const t = new Date(solarNoon.getTime() + (i / points) * 24 * 60 * 60 * 1000); - const pos = SunCalc.getPosition(t, lat, lon); - - let h = (((pos.azimuth - transitAzimuth) / (2 * Math.PI)) + 1) % 1; - h = Math.max(0, Math.min(1, h)); - let v = Math.sin(pos.altitude); - v = Math.max(-1, Math.min(1, v)); - - eclipticPoints.push({ - h, - v - }); - } - - const sortedEntries = eclipticPoints.sort((a, b) => a.h - b.h); - return sortedEntries; - } - - function getCurrentSunTime(date) { - const times = getSunTimes(date); - if (!times) { - return; - } - const dateObj = new Date(date); - - const periods = [ - { - name: I18n.tr("Dawn (Astronomical Twilight)"), - start: new Date(times.nightEnd), - end: new Date(times.nauticalDawn) - }, - { - name: I18n.tr("Dawn (Nautical Twilight)"), - start: new Date(times.nauticalDawn), - end: new Date(times.dawn) - }, - { - name: I18n.tr("Dawn (Civil Twilight)"), - start: new Date(times.dawn), - end: new Date(times.sunrise) - }, - { - name: I18n.tr("Sunrise"), - start: new Date(times.sunrise), - end: new Date(times.sunriseEnd) - }, - { - name: I18n.tr("Golden Hour"), - start: new Date(times.sunriseEnd), - end: new Date(times.goldenHourEnd) - }, - { - name: I18n.tr("Morning"), - start: new Date(times.goldenHourEnd), - end: new Date(times.solarNoon) - }, - { - name: I18n.tr("Afternoon"), - start: new Date(times.solarNoon), - end: new Date(times.goldenHour) - }, - { - name: I18n.tr("Golden Hour"), - start: new Date(times.goldenHour), - end: new Date(times.sunsetStart) - }, - { - name: I18n.tr("Sunset"), - start: new Date(times.sunsetStart), - end: new Date(times.sunset) - }, - { - name: I18n.tr("Dusk (Civil Twighlight)"), - start: new Date(times.sunset), - end: new Date(times.dusk) - }, - { - name: I18n.tr("Dusk (Nautical Twilight)"), - start: new Date(times.dusk), - end: new Date(times.nauticalDusk) - }, - { - name: I18n.tr("Dusk (Astronomical Twilight)"), - start: new Date(times.nauticalDusk), - end: new Date(times.night) - }, - ]; - - const sunrise = new Date(times.nightEnd); - const sunset = new Date(times.night); - const dayPercent = dateObj > sunrise && dateObj < sunset ? (dateObj - sunrise) / (sunset - sunrise) : 0; - - for (let i = 0; i < periods.length; i++) { - const { - name, - start, - end - } = periods[i]; - if (dateObj >= start && dateObj < end) { - const percent = (dateObj - start) / (end - start); - return { - period: name, - periodIndex: i, - periodPercent: Math.min(Math.max(percent, 0), 1), - dayPercent: dayPercent - }; - } - } - - return { - period: I18n.tr("Night"), - periodIndex: 0, - periodPercent: 0, - dayPercent: dayPercent - }; - } - - function getSkyArcPosition(date, isSun) { - if (!location) { - return null; - } - const lat = location.latitude; - const lon = location.longitude; - - const pos = isSun ? SunCalc.getPosition(date, lat, lon) : SunCalc.getMoonPosition(date, lat, lon); - - const sunIsNorth = getSunDeclination(date) > lat; - const transitAzimuth = sunIsNorth ? 0 : Math.PI; - - let h = (((pos.azimuth - transitAzimuth) / (2 * Math.PI)) + 1) % 1; - h = Math.max(0, Math.min(1, h)); - // let v = pos.altitude / (Math.PI/2) - let v = Math.sin(pos.altitude); - v = Math.max(-1, Math.min(1, v)); - - return { - h, - v - }; - } - - function formatTemp(celsius, includeUnits = true, unitsShort = true) { - if (celsius == null) { - return null; - } - const value = SettingsData.useFahrenheit ? Math.round(celsius * (9 / 5) + 32) : celsius; - const unit = unitsShort ? "°" : (SettingsData.useFahrenheit ? "°F" : "°C"); - return includeUnits ? value + unit : value; - } - - function formatSpeed(kmh, includeUnits = true) { - if (kmh == null) { - return null; - } - if (SettingsData.useFahrenheit) { - const value = Math.round(kmh * 0.621371); - return includeUnits ? value + " mph" : value; - } - if (SettingsData.windSpeedUnit === "ms") { - const value = (kmh / 3.6).toFixed(1); - return includeUnits ? value + " m/s" : value; - } - return includeUnits ? kmh + " km/h" : kmh; - } - - function formatPressure(hpa, includeUnits = true) { - if (hpa == null) { - return null; - } - const value = SettingsData.useFahrenheit ? (hpa * 0.02953).toFixed(2) : hpa; - const unit = SettingsData.useFahrenheit ? "inHg" : "hPa"; - return includeUnits ? value + " " + unit : value; - } - - function formatPercent(percent, includeUnits = true) { - if (percent == null) { - return null; - } - const value = percent; - const unit = "%"; - return includeUnits ? value + unit : value; - } - - function formatVisibility(distance) { - if (distance == null) { - return null; - } - var value; - var unit; - if (SettingsData.useFahrenheit) { - value = (distance / 1609.344).toFixed(1); - unit = "mi"; - if (value < 1) { - value = Math.round(value * 5280 / 50) * 50; - unit = "ft"; - } - } else { - value = distance; - unit = "m"; - if (value > 1000) { - value = (value / 1000).toFixed(1); - unit = "km"; - } - } - - return value + " " + unit; - } - - function formatTime(isoString) { - if (!isoString) - return "--"; - - try { - const date = new Date(isoString); - const format = SettingsData.use24HourClock ? "HH:mm" : "h:mm AP"; - return date.toLocaleTimeString(Qt.locale(), format); - } catch (e) { - return "--"; - } - } - - function calendarDayDifference(date1, date2) { - const d1 = Date.UTC(date1.getFullYear(), date1.getMonth(), date1.getDate()); - const d2 = Date.UTC(date2.getFullYear(), date2.getMonth(), date2.getDate()); - return Math.floor((d2 - d1) / (1000 * 60 * 60 * 24)); - } - - function calendarHourDifference(date1, date2) { - const d1 = Date.UTC(date1.getFullYear(), date1.getMonth(), date1.getDate(), date1.getHours()); - const d2 = Date.UTC(date2.getFullYear(), date2.getMonth(), date2.getDate(), date2.getHours()); - return Math.floor((d2 - d1) / (1000 * 60 * 60)); - } - - function formatForecastDay(isoString, index) { - if (!isoString) - return "--"; - - if (index === 0) - return I18n.tr("Today"); - if (index === 1) - return I18n.tr("Tomorrow"); - - const date = new Date(); - date.setDate(date.getDate() + index); - const locale = I18n.locale(); - return locale.dayName(date.getDay(), Locale.ShortFormat); - } - - function getWeatherApiUrl() { - if (!location) { - return null; - } - - const params = ["latitude=" + location.latitude, "longitude=" + location.longitude, "current=temperature_2m,relative_humidity_2m,apparent_temperature,is_day,precipitation,weather_code,surface_pressure,wind_speed_10m", "daily=sunrise,sunset,temperature_2m_max,temperature_2m_min,weather_code,precipitation_probability_max", "hourly=temperature_2m,weather_code,precipitation_probability,wind_speed_10m,apparent_temperature,relative_humidity_2m,surface_pressure,visibility,cloud_cover", "timezone=auto", "forecast_days=7"]; - - return "https://api.open-meteo.com/v1/forecast?" + params.join('&'); - } - - function getGeocodingUrl(query) { - return "https://geocoding-api.open-meteo.com/v1/search?name=" + encodeURIComponent(query) + "&count=1&language=en&format=json"; - } - - function addRef() { - refCount++; - - if (refCount === 1 && !weather.available && SettingsData.weatherEnabled) { - fetchWeather(); - } - } - - function removeRef() { - refCount = Math.max(0, refCount - 1); - } - - function updateLocation() { - const useAuto = SessionData.isGreeterMode ? GreetdSettings.useAutoLocation : SettingsData.useAutoLocation; - const coords = SessionData.isGreeterMode ? GreetdSettings.weatherCoordinates : SettingsData.weatherCoordinates; - const cityName = SessionData.isGreeterMode ? GreetdSettings.weatherLocation : SettingsData.weatherLocation; - - if (useAuto) { - getLocationFromService(); - return; - } - - if (coords) { - const parts = coords.split(","); - if (parts.length === 2) { - const lat = parseFloat(parts[0]); - const lon = parseFloat(parts[1]); - if (!isNaN(lat) && !isNaN(lon)) { - getLocationFromCoords(lat, lon); - return; - } - } - } - - if (cityName) - getLocationFromCity(cityName); - } - - function getLocationFromCoords(lat, lon) { - const url = "https://nominatim.openstreetmap.org/reverse?lat=" + lat + "&lon=" + lon + "&format=json&addressdetails=1&accept-language=en"; - reverseGeocodeFetcher.command = lowPriorityCmd.concat(curlBaseCmd).concat(["-H", "User-Agent: DankMaterialShell Weather Widget", url]); - reverseGeocodeFetcher.running = true; - } - - function getLocationFromCity(city) { - cityGeocodeFetcher.command = lowPriorityCmd.concat(curlBaseCmd).concat([getGeocodingUrl(city)]); - cityGeocodeFetcher.running = true; - } - - function getLocationFromService() { - if (!LocationService.valid) - return; - getLocationFromCoords(LocationService.latitude, LocationService.longitude); - } - - function fetchWeather() { - if (root.refCount === 0 || !SettingsData.weatherEnabled) { - return; - } - - if (!location) { - updateLocation(); - return; - } - - if (weatherFetcher.running) { - return; - } - - const now = Date.now(); - if (now - root.lastFetchTime < root.minFetchInterval) { - return; - } - - const apiUrl = getWeatherApiUrl(); - if (!apiUrl) { - return; - } - - root.lastFetchTime = now; - root.weather.loading = true; - const weatherCmd = lowPriorityCmd.concat(["curl", "-sS", "--fail", "--connect-timeout", "3", "--max-time", "6", "--limit-rate", "150k", "--compressed"]); - weatherFetcher.command = weatherCmd.concat([apiUrl]); - weatherFetcher.running = true; - } - - function forceRefresh() { - root.lastFetchTime = 0; // Reset throttle - fetchWeather(); - } - - function nextInterval() { - const jitter = Math.floor(Math.random() * 15000) - 7500; - return Math.max(60000, root.updateInterval + jitter); - } - - function handleWeatherSuccess() { - root.retryAttempts = 0; - root.persistentRetryCount = 0; - if (persistentRetryTimer.running) { - persistentRetryTimer.stop(); - } - if (updateTimer.interval !== root.updateInterval) { - updateTimer.interval = root.updateInterval; - } - } - - function handleWeatherFailure() { - root.retryAttempts++; - if (root.retryAttempts < root.maxRetryAttempts) { - retryTimer.start(); - } else { - root.retryAttempts = 0; - if (!root.weather.available) { - root.weather.loading = false; - } - const backoffDelay = Math.min(60000 * Math.pow(2, persistentRetryCount), 300000); - persistentRetryCount++; - persistentRetryTimer.interval = backoffDelay; - persistentRetryTimer.start(); - } - } - - Process { - id: reverseGeocodeFetcher - running: false - - stdout: StdioCollector { - onStreamFinished: { - const raw = text.trim(); - if (!raw || raw[0] !== "{") { - root.handleWeatherFailure(); - return; - } - - try { - const data = JSON.parse(raw); - const address = data.address || {}; - - root.location = { - city: address.hamlet || address.city || address.town || address.village || I18n.tr("Unknown"), - country: address.country || I18n.tr("Unknown"), - latitude: parseFloat(data.lat), - longitude: parseFloat(data.lon) - }; - - fetchWeather(); - } catch (e) { - root.handleWeatherFailure(); - } - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - root.handleWeatherFailure(); - } - } - } - - Process { - id: cityGeocodeFetcher - running: false - - stdout: StdioCollector { - onStreamFinished: { - const raw = text.trim(); - if (!raw || raw[0] !== "{") { - root.handleWeatherFailure(); - return; - } - - try { - const data = JSON.parse(raw); - const results = data.results; - - if (!results || results.length === 0) { - throw new Error("No results found"); - } - - const result = results[0]; - - root.location = { - city: result.name, - country: result.country, - latitude: result.latitude, - longitude: result.longitude, - elevation: data.elevation - }; - - fetchWeather(); - } catch (e) { - root.handleWeatherFailure(); - } - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - root.handleWeatherFailure(); - } - } - } - - Process { - id: weatherFetcher - running: false - - stdout: StdioCollector { - onStreamFinished: { - const raw = text.trim(); - if (!raw || raw[0] !== "{") { - root.handleWeatherFailure(); - return; - } - - try { - const data = JSON.parse(raw); - - if (!data.current || !data.daily || !data.hourly) { - throw new Error("Required weather data fields missing"); - } - - const hourly = data.hourly; - const hourly_forecast = []; - if (hourly.time && hourly.time.length > 0) { - for (let i = 0; i < hourly.time.length; i++) { - const tempMinC = hourly.temperature_2m_min?.[i] || 0; - const tempMaxC = hourly.temperature_2m_max?.[i] || 0; - const tempMinF = tempMinC * 9 / 5 + 32; - const tempMaxF = tempMaxC * 9 / 5 + 32; - - const tempC = hourly.temperature_2m?.[i] || 0; - const tempF = tempC * 9 / 5 + 32; - const feelsLikeC = hourly.apparent_temperature?.[i] || tempC; - const feelsLikeF = feelsLikeC * 9 / 5 + 32; - - const sunrise = new Date(data.daily.sunrise?.[Math.floor(i / 24)]); - const sunset = new Date(data.daily.sunset?.[Math.floor(i / 24)]); - const time = new Date(hourly.time[i]); - const isDay = sunrise < time && time < sunset; - - hourly_forecast.push({ - "time": formatTime(hourly.time[i]), - "rawTime": hourly.time[i], - "temp": Math.round(tempC), - "tempF": Math.round(tempF), - "feelsLike": Math.round(feelsLikeC), - "feelsLikeF": Math.round(feelsLikeF), - "wCode": hourly.weather_code?.[i] || 0, - "humidity": Math.round(hourly.relative_humidity_2m?.[i] || 0), - "wind": Math.round(hourly.wind_speed_10m?.[i] || 0), - "pressure": Math.round(hourly.surface_pressure?.[i] || 0), - "precipitationProbability": Math.round(hourly.precipitation_probability?.[i] || 0), - "visibility": Math.round(hourly.visibility?.[i] || 0), - "isDay": isDay - }); - } - } - - const daily = data.daily; - const forecast = []; - if (daily.time && daily.time.length > 0) { - for (let i = 0; i < daily.time.length; i++) { - const tempMinC = daily.temperature_2m_min?.[i] || 0; - const tempMaxC = daily.temperature_2m_max?.[i] || 0; - const tempMinF = (tempMinC * 9 / 5 + 32); - const tempMaxF = (tempMaxC * 9 / 5 + 32); - - forecast.push({ - "day": formatForecastDay(daily.time[i], i), - "wCode": daily.weather_code?.[i] || 0, - "tempMin": Math.round(tempMinC), - "tempMax": Math.round(tempMaxC), - "tempMinF": Math.round(tempMinF), - "tempMaxF": Math.round(tempMaxF), - "precipitationProbability": Math.round(daily.precipitation_probability_max?.[i] || 0), - "sunrise": daily.sunrise?.[i] ? formatTime(daily.sunrise[i]) : "", - "sunset": daily.sunset?.[i] ? formatTime(daily.sunset[i]) : "", - "rawSunrise": daily.sunrise?.[i] || "", - "rawSunset": daily.sunset?.[i] || "" - }); - } - } - - const current = data.current; - const currentUnits = data.current_units || {}; - - const tempC = current.temperature_2m || 0; - const tempF = tempC * 9 / 5 + 32; - const feelsLikeC = current.apparent_temperature || tempC; - const feelsLikeF = feelsLikeC * 9 / 5 + 32; - - root.weather = { - "available": true, - "loading": false, - "temp": Math.round(tempC), - "tempF": Math.round(tempF), - "feelsLike": Math.round(feelsLikeC), - "feelsLikeF": Math.round(feelsLikeF), - "city": root.location?.city || I18n.tr("Unknown"), - "country": root.location?.country || I18n.tr("Unknown"), - "wCode": current.weather_code || 0, - "humidity": Math.round(current.relative_humidity_2m || 0), - "wind": Math.round(current.wind_speed_10m || 0), - "sunrise": formatTime(daily.sunrise?.[0]) || "06:00", - "sunset": formatTime(daily.sunset?.[0]) || "18:00", - "rawSunrise": daily.sunrise?.[0] || "", - "rawSunset": daily.sunset?.[0] || "", - "uv": 0, - "pressure": Math.round(current.surface_pressure || 0), - "precipitationProbability": Math.round(daily.precipitation_probability_max?.[0] || 0), - "isDay": Boolean(current.is_day), - "forecast": forecast, - "hourlyForecast": hourly_forecast - }; - root.handleWeatherSuccess(); - } catch (e) { - root.handleWeatherFailure(); - } - } - } - - onExited: exitCode => { - if (exitCode !== 0) { - root.handleWeatherFailure(); - } - } - } - - Timer { - id: updateTimer - interval: nextInterval() - running: root.refCount > 0 && SettingsData.weatherEnabled && !SessionData.isGreeterMode - repeat: true - triggeredOnStart: true - onTriggered: { - root.fetchWeather(); - interval = nextInterval(); - } - } - - Timer { - id: retryTimer - interval: root.retryDelay - running: false - repeat: false - onTriggered: { - root.fetchWeather(); - } - } - - Timer { - id: persistentRetryTimer - interval: 60000 - running: false - repeat: false - onTriggered: { - if (!root.weather.available) { - root.weather.loading = true; - } - root.fetchWeather(); - } - } - - Connections { - target: LocationService - - function onLocationChanged(data) { - if (!SettingsData.useAutoLocation) - return; - if (data.latitude === 0 && data.longitude === 0) - return; - root.getLocationFromCoords(data.latitude, data.longitude); - } - } - - Component.onCompleted: { - SettingsData.weatherCoordinatesChanged.connect(() => { - root.location = null; - root.weather = { - "available": false, - "loading": true, - "temp": 0, - "tempF": 0, - "feelsLike": 0, - "feelsLikeF": 0, - "city": "", - "country": "", - "wCode": 0, - "humidity": 0, - "wind": "", - "sunrise": "06:00", - "sunset": "18:00", - "uv": 0, - "pressure": 0, - "precipitationProbability": 0, - "isDay": true, - "forecast": [] - }; - root.lastFetchTime = 0; - root.forceRefresh(); - }); - - SettingsData.weatherLocationChanged.connect(() => { - root.location = null; - root.lastFetchTime = 0; - root.forceRefresh(); - }); - - SettingsData.useAutoLocationChanged.connect(() => { - root.location = null; - root.weather = { - "available": false, - "loading": true, - "temp": 0, - "tempF": 0, - "feelsLike": 0, - "feelsLikeF": 0, - "city": "", - "country": "", - "wCode": 0, - "humidity": 0, - "wind": "", - "sunrise": "06:00", - "sunset": "18:00", - "uv": 0, - "pressure": 0, - "precipitationProbability": 0, - "isDay": true, - "forecast": [] - }; - root.lastFetchTime = 0; - root.forceRefresh(); - }); - - SettingsData.weatherEnabledChanged.connect(() => { - if (SettingsData.weatherEnabled && root.refCount > 0 && !root.weather.available) { - root.forceRefresh(); - } else if (!SettingsData.weatherEnabled) { - updateTimer.stop(); - retryTimer.stop(); - persistentRetryTimer.stop(); - if (weatherFetcher.running) { - weatherFetcher.running = false; - } - } - }); - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/WlrOutputService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/WlrOutputService.qml deleted file mode 100644 index 655a485..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/WlrOutputService.qml +++ /dev/null @@ -1,346 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell - -Singleton { - id: root - - property bool wlrOutputAvailable: false - property var outputs: [] - property int serial: 0 - - signal stateChanged - signal configurationApplied(bool success, string message) - - Connections { - target: DMSService - - function onCapabilitiesReceived() { - checkCapabilities(); - } - - function onConnectionStateChanged() { - if (DMSService.isConnected) { - checkCapabilities(); - return; - } - wlrOutputAvailable = false; - } - - function onWlrOutputStateUpdate(data) { - if (!wlrOutputAvailable) { - return; - } - handleStateUpdate(data); - } - } - - Component.onCompleted: { - if (!DMSService.dmsAvailable) { - return; - } - checkCapabilities(); - } - - function checkCapabilities() { - if (!DMSService.capabilities || !Array.isArray(DMSService.capabilities)) { - wlrOutputAvailable = false; - return; - } - - const hasWlrOutput = DMSService.capabilities.includes("wlroutput"); - if (hasWlrOutput && !wlrOutputAvailable) { - wlrOutputAvailable = true; - console.info("WlrOutputService: wlr-output-management capability detected"); - requestState(); - return; - } - - if (!hasWlrOutput) { - wlrOutputAvailable = false; - } - } - - function requestState() { - if (!DMSService.isConnected || !wlrOutputAvailable) { - return; - } - - DMSService.sendRequest("wlroutput.getState", null, response => { - if (!response.result) { - return; - } - handleStateUpdate(response.result); - }); - } - - function handleStateUpdate(state) { - outputs = state.outputs || []; - serial = state.serial || 0; - - if (outputs.length === 0) { - console.warn("WlrOutputService: Received empty outputs list"); - } else { - console.log("WlrOutputService: Updated with", outputs.length, "outputs, serial:", serial); - outputs.forEach((output, index) => { - console.log("WlrOutputService: Output", index, "-", output.name, "enabled:", output.enabled, "mode:", output.currentMode ? output.currentMode.width + "x" + output.currentMode.height + "@" + (output.currentMode.refresh / 1000) + "Hz" : "none"); - }); - } - stateChanged(); - } - - function getOutput(name) { - for (const output of outputs) { - if (output.name === name) { - return output; - } - } - return null; - } - - function getEnabledOutputs() { - return outputs.filter(output => output.enabled); - } - - function applyConfiguration(heads, callback) { - if (!DMSService.isConnected || !wlrOutputAvailable) { - if (callback) { - callback(false, "Not connected"); - } - return; - } - - console.log("WlrOutputService: Applying configuration for", heads.length, "outputs"); - heads.forEach((head, index) => { - console.log("WlrOutputService: Head", index, "- name:", head.name, "enabled:", head.enabled, "modeId:", head.modeId, "customMode:", JSON.stringify(head.customMode), "position:", JSON.stringify(head.position), "scale:", head.scale, "transform:", head.transform, "adaptiveSync:", head.adaptiveSync); - }); - - DMSService.sendRequest("wlroutput.applyConfiguration", { - "heads": heads - }, response => { - const success = !response.error; - const message = response.error || response.result?.message || ""; - - if (response.error) { - console.warn("WlrOutputService: applyConfiguration error:", response.error); - } else { - console.log("WlrOutputService: Configuration applied successfully"); - } - - configurationApplied(success, message); - if (callback) { - callback(success, message); - } - }); - } - - function testConfiguration(heads, callback) { - if (!DMSService.isConnected || !wlrOutputAvailable) { - if (callback) { - callback(false, "Not connected"); - } - return; - } - - console.log("WlrOutputService: Testing configuration for", heads.length, "outputs"); - - DMSService.sendRequest("wlroutput.testConfiguration", { - "heads": heads - }, response => { - const success = !response.error; - const message = response.error || response.result?.message || ""; - - if (response.error) { - console.warn("WlrOutputService: testConfiguration error:", response.error); - } else { - console.log("WlrOutputService: Configuration test passed"); - } - - if (callback) { - callback(success, message); - } - }); - } - - function setOutputEnabled(outputName, enabled, callback) { - const output = getOutput(outputName); - if (!output) { - console.warn("WlrOutputService: Output not found:", outputName); - if (callback) { - callback(false, "Output not found"); - } - return; - } - - const heads = [ - { - "name": outputName, - "enabled": enabled - } - ]; - - if (enabled && output.currentMode) { - heads[0].modeId = output.currentMode.id; - } - - applyConfiguration(heads, callback); - } - - function setOutputMode(outputName, modeId, callback) { - const heads = [ - { - "name": outputName, - "enabled": true, - "modeId": modeId - } - ]; - - applyConfiguration(heads, callback); - } - - function setOutputCustomMode(outputName, width, height, refresh, callback) { - const heads = [ - { - "name": outputName, - "enabled": true, - "customMode": { - "width": width, - "height": height, - "refresh": refresh - } - } - ]; - - applyConfiguration(heads, callback); - } - - function setOutputPosition(outputName, x, y, callback) { - const heads = [ - { - "name": outputName, - "enabled": true, - "position": { - "x": x, - "y": y - } - } - ]; - - applyConfiguration(heads, callback); - } - - function setOutputScale(outputName, scale, callback) { - const heads = [ - { - "name": outputName, - "enabled": true, - "scale": scale - } - ]; - - applyConfiguration(heads, callback); - } - - function setOutputTransform(outputName, transform, callback) { - const heads = [ - { - "name": outputName, - "enabled": true, - "transform": transform - } - ]; - - applyConfiguration(heads, callback); - } - - function setOutputAdaptiveSync(outputName, state, callback) { - const heads = [ - { - "name": outputName, - "enabled": true, - "adaptiveSync": state - } - ]; - - applyConfiguration(heads, callback); - } - - function configureOutput(config, callback) { - const heads = [config]; - applyConfiguration(heads, callback); - } - - function configureMultipleOutputs(configs, callback) { - applyConfiguration(configs, callback); - } - - // High-level apply matching the generateOutputsConfig() pattern used by - // NiriService, HyprlandService and DwlService. Instead of writing a - // config file, the changes are applied directly via the - // wlr-output-management protocol. - function applyOutputsConfig(outputsData, connectedOutputs) { - if (!wlrOutputAvailable) - return; - const heads = []; - for (const name in outputsData) { - if (!connectedOutputs[name]) - continue; - const output = outputsData[name]; - const mode = (output.modes && output.current_mode >= 0) ? output.modes[output.current_mode] : null; - const enabled = !!mode; - const head = { - "name": name, - "enabled": enabled - }; - - if (enabled) { - if (mode.id !== undefined) - head.modeId = mode.id; - else - head.customMode = { - "width": mode.width, - "height": mode.height, - "refresh": mode.refresh_rate - }; - - if (output.logical) { - head.position = { - "x": output.logical.x ?? 0, - "y": output.logical.y ?? 0 - }; - head.scale = output.logical.scale ?? 1.0; - head.transform = transformFromName(output.logical.transform); - } - } - heads.push(head); - } - - if (heads.length > 0) - applyConfiguration(heads); - } - - function transformFromName(name) { - switch (name) { - case "Normal": - return 0; - case "90": - return 1; - case "180": - return 2; - case "270": - return 3; - case "Flipped": - return 4; - case "Flipped90": - return 5; - case "Flipped180": - return 6; - case "Flipped270": - return 7; - default: - return 0; - } - } -} diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/niri-wpblur.kdl b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/niri-wpblur.kdl deleted file mode 100644 index 3d58802..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/niri-wpblur.kdl +++ /dev/null @@ -1,9 +0,0 @@ -// ! DO NOT EDIT ! -// ! AUTO-GENERATED BY DMS ! -// ! CHANGES WILL BE OVERWRITTEN ! -// ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE ! - -layer-rule { - match namespace="dms:blurwallpaper" - place-within-backdrop true -} |