diff --git a/.github/workflows/go-ci.yml b/.github/workflows/go-ci.yml
index ad212163b..02fc28264 100644
--- a/.github/workflows/go-ci.yml
+++ b/.github/workflows/go-ci.yml
@@ -50,8 +50,11 @@ jobs:
- name: Build dms
run: go build -v ./cmd/dms
+ - name: Build dms (embedded shell)
+ run: make build
+
- name: Build dms (distropkg)
- run: go build -v -tags distro_binary ./cmd/dms
+ run: go build -v -tags 'distro_binary withshell' ./cmd/dms
- name: Build dankinstall
run: go build -v ./cmd/dankinstall
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0f57d0b1c..ec39ed261 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -74,6 +74,9 @@ jobs:
gzip -9 -k dankinstall-${{ matrix.arch }}
sha256sum dankinstall-${{ matrix.arch }}.gz > dankinstall-${{ matrix.arch }}.gz.sha256
+ - name: Sync embedded shell
+ run: make sync-shell
+
- name: Build dms (${{ matrix.arch }})
env:
GOOS: linux
@@ -82,7 +85,7 @@ jobs:
run: |
set -eux
cd cmd/dms
- go build -trimpath -ldflags "-s -w -X main.Version=${TAG}" \
+ go build -trimpath -tags withshell -ldflags "-s -w -X main.Version=${TAG}" \
-o ../../dms-${{ matrix.arch }}
cd ../..
gzip -9 -k dms-${{ matrix.arch }}
@@ -105,7 +108,7 @@ jobs:
run: |
set -eux
cd cmd/dms
- go build -trimpath -tags distro_binary -ldflags "-s -w -X main.Version=${TAG}" \
+ go build -trimpath -tags 'distro_binary withshell' -ldflags "-s -w -X main.Version=${TAG}" \
-o ../../dms-distropkg-${{ matrix.arch }}
cd ../..
gzip -9 -k dms-distropkg-${{ matrix.arch }}
@@ -379,18 +382,16 @@ jobs:
## Installation Steps
- 1. **Install quickshell assets:**
- ```bash
- mkdir -p ~/.config/quickshell
- cp -r dms ~/.config/quickshell/
- ```
+ The Quickshell UI is embedded in the `dms` binary. The bundled `dms/`
+ tree is optional — pass it with `-c` or `DMS_SHELL_DIR` to run a
+ modified copy instead of the embedded UI.
- 2. **Install the DMS CLI binaries:**
+ 1. **Install the DMS CLI binaries:**
```bash
sudo install -m 755 bin/dms /usr/local/bin/dms
```
- 3. **Install shell completions (optional):**
+ 2. **Install shell completions (optional):**
```bash
# Bash
sudo install -m 644 completions/completion.bash /usr/share/bash-completion/completions/dms
@@ -402,7 +403,7 @@ jobs:
sudo install -m 644 completions/completion.zsh /usr/share/zsh/site-functions/_dms
```
- 4. **Start the shell:**
+ 3. **Start the shell:**
```bash
dms run
```
diff --git a/.gitignore b/.gitignore
index f7a36aa87..4a25a19bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -129,3 +129,5 @@ distro/void/masterdir*/
# Often gets built
core/dms
+
+core/internal/shellembed/dist
diff --git a/CHANGELOG.MD b/CHANGELOG.MD
index 4494ec202..581e03792 100644
--- a/CHANGELOG.MD
+++ b/CHANGELOG.MD
@@ -1,5 +1,13 @@
This file is more of a quick reference so I know what to account for before next releases.
+# Next
+- Go core migrated to dankgo (shared log/paths/errdefs/ipc/shellapp)
+- Quickshell UI embedded in the dms binary, -c / DMS_SHELL_DIR override - breaking for setups relying on ~/.config/quickshell/dms auto-discovery (XDG search removed)
+- release binaries + all -git packages build with withshell; sockets/pidfiles/env names unchanged, IPC APIVersion 28 wire-compatible
+- -git packages (AUR dms-shell-git, fedora, opensuse, void, debian, ubuntu) no longer install /usr/share/quickshell/dms; dms greeter install falls back to the embedded UI when the greeter package is present
+- ping over the socket returns {"pong":true} instead of "pong"
+- startup: embedded UI resolution is keyed by build-time .dankrev (no per-start content hashing)
+
# 1.5.0
- Overhauled shadows
- App ID changed to com.danklinux.dms - breaking for window rules
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a51f22f24..78565aee3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -36,6 +36,29 @@ This will provide:
The dev shell automatically creates the `.qmlls.ini` file in the `quickshell/` directory.
+## Building and running
+
+The Quickshell UI is embedded into the `dms` binary at build time. `make build` copies `quickshell/` into `core/internal/shellembed/dist/` (generated, never committed) and compiles with the `withshell` tag. `make dev` builds without the tag — that binary carries no UI and requires an explicit config dir.
+
+```bash
+make build # embedded binary at core/bin/dms
+make dev # untagged development build
+make run # dev build, then launch against the live quickshell/ tree
+```
+
+The UI config dir resolves in order: `-c
`, `DMS_SHELL_DIR`, the dir a running instance is using, then the embedded UI. Each candidate must contain `shell.qml`. `make run` uses `-c $(pwd)/quickshell`, so QML edits hot-reload from the working tree.
+
+The Go core depends on [dankgo](https://github.com/AvengeMedia/dankgo) for logging, XDG paths, the IPC transport, and the quickshell process lifecycle. To develop against a local dankgo checkout, create a gitignored `go.work` at the repo root:
+
+```
+go 1.26.1
+
+use (
+ ./core
+ ../dankgo
+)
+```
+
## Shared widgets (dank-qml-common)
Everything under `quickshell/DankCommon/` (core widgets, the file browser, scroll physics, bundled fonts) is shared across the DMS suite and lives in the `dank-qml-common` submodule. It is a normal git worktree:
diff --git a/Makefile b/Makefile
index 2a5ba6b32..e68a6a543 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ SHELL_INSTALL_DIR=$(DATA_DIR)/quickshell/dms
ASSETS_DIR=assets
APPLICATIONS_DIR=$(DATA_DIR)/applications
-.PHONY: all build clean lint-qml install install-bin install-shell install-completions install-systemd install-icon install-desktop uninstall uninstall-bin uninstall-shell uninstall-completions uninstall-systemd uninstall-icon uninstall-desktop help
+.PHONY: all build dev run clean lint-qml install install-bin install-shell install-completions install-systemd install-icon install-desktop uninstall uninstall-bin uninstall-shell uninstall-completions uninstall-systemd uninstall-icon uninstall-desktop help
all: build
@@ -27,6 +27,12 @@ build:
@$(MAKE) -C $(CORE_DIR) build
@echo "Build complete"
+dev:
+ @$(MAKE) -C $(CORE_DIR) dev
+
+run: dev
+ @$(BUILD_DIR)/$(BINARY_NAME) run -c $(CURDIR)/$(SHELL_DIR)
+
clean:
@echo "Cleaning build artifacts..."
@$(MAKE) -C $(CORE_DIR) clean
diff --git a/core/Makefile b/core/Makefile
index cad9b95ec..305ba35ba 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -3,6 +3,8 @@ BINARY_NAME_INSTALL=dankinstall
SOURCE_DIR=cmd/dms
SOURCE_DIR_INSTALL=cmd/dankinstall
BUILD_DIR=bin
+SHELL_SRC=../quickshell
+EMBED_DIR=internal/shellembed/dist
PREFIX ?= /usr/local
INSTALL_DIR=$(PREFIX)/bin
@@ -22,16 +24,28 @@ BUILD_LDFLAGS=-ldflags='-s -w -X main.Version=$(VERSION) -X main.buildTime=$(BUI
# Architecture to build for dist target (amd64, arm64, or all)
ARCH ?= all
-.PHONY: all build dankinstall dist clean install install-all install-dankinstall uninstall uninstall-all uninstall-dankinstall install-config uninstall-config test fmt vet deps print-version help
+.PHONY: all build sync-shell dankinstall dist clean install install-all install-dankinstall uninstall uninstall-all uninstall-dankinstall install-config uninstall-config test fmt vet deps print-version help
# Default target
all: build
+# Copy the quickshell UI into the embed dir (gitignored) so tagged builds
+# can bake it into the binary. Dev-only files are stripped. -L dereferences
+# the DankCommon submodule symlink; go:embed rejects symlinks.
+sync-shell:
+ @test -e $(SHELL_SRC)/DankCommon/Widgets/DankIcon.qml || { echo "DankCommon missing: run git submodule update --init"; exit 1; }
+ @rm -rf $(EMBED_DIR)
+ @cp -rL $(SHELL_SRC) $(EMBED_DIR)
+ @rm -rf $(EMBED_DIR)/scripts $(EMBED_DIR)/.claude $(EMBED_DIR)/.git* $(EMBED_DIR)/.github
+ @rm -f $(EMBED_DIR)/.qmlls.ini $(EMBED_DIR)/AGENTS.md $(EMBED_DIR)/qmlformat-all.sh
+ @rm -f $(EMBED_DIR)/translations/*.py $(EMBED_DIR)/translations/WORKFLOW.md
+ @cd $(EMBED_DIR) && find . -type f -print0 | LC_ALL=C sort -z | xargs -0 sha256sum | sha256sum | cut -c1-16 > .dankrev
+
# Build the main binary (dms)
-build:
+build: sync-shell
@echo "Building $(BINARY_NAME)..."
@mkdir -p $(BUILD_DIR)
- CGO_ENABLED=0 $(GO) build $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) ./$(SOURCE_DIR)
+ CGO_ENABLED=0 $(GO) build -tags withshell $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) ./$(SOURCE_DIR)
@echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME)"
dankinstall:
@@ -41,14 +55,14 @@ dankinstall:
@echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME_INSTALL)"
# Build distro binaries for amd64 and arm64 (Linux only, no update/greeter support)
-dist:
+dist: sync-shell
ifeq ($(ARCH),all)
@echo "Building $(BINARY_NAME) for distribution (amd64 and arm64)..."
@mkdir -p $(BUILD_DIR)
@echo "Building for linux/amd64..."
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 ./$(SOURCE_DIR)
+ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -tags 'distro_binary withshell' $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 ./$(SOURCE_DIR)
@echo "Building for linux/arm64..."
- CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64 ./$(SOURCE_DIR)
+ CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $(GO) build -tags 'distro_binary withshell' $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64 ./$(SOURCE_DIR)
@echo "Distribution builds complete:"
@echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64"
@echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64"
@@ -56,7 +70,7 @@ else
@echo "Building $(BINARY_NAME) for distribution ($(ARCH))..."
@mkdir -p $(BUILD_DIR)
@echo "Building for linux/$(ARCH)..."
- CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-$(ARCH) ./$(SOURCE_DIR)
+ CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) $(GO) build -tags 'distro_binary withshell' $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-$(ARCH) ./$(SOURCE_DIR)
@echo "Distribution build complete:"
@echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-$(ARCH)"
endif
@@ -99,7 +113,7 @@ uninstall-dankinstall:
clean:
@echo "Cleaning build artifacts..."
- @rm -rf $(BUILD_DIR)
+ @rm -rf $(BUILD_DIR) $(EMBED_DIR)
@echo "Clean complete"
test:
@@ -141,7 +155,8 @@ print-version:
help:
@echo "Available targets:"
@echo " all - Build the main binary (dms) (default)"
- @echo " build - Build the main binary (dms)"
+ @echo " build - Build the main binary (dms) with the embedded UI"
+ @echo " sync-shell - Copy quickshell/ into the embed dir (runs before tagged builds)"
@echo " dankinstall - Build dankinstall binary"
@echo " dist - Build dms for linux amd64/arm64 (no update/greeter)"
@echo " Use ARCH=amd64 or ARCH=arm64 to build only one"
diff --git a/core/cmd/dms/commands_common.go b/core/cmd/dms/commands_common.go
index 48dec44d3..f64f5bef9 100644
--- a/core/cmd/dms/commands_common.go
+++ b/core/cmd/dms/commands_common.go
@@ -6,7 +6,6 @@ import (
"regexp"
"strings"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/config"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server"
@@ -19,63 +18,6 @@ var versionCmd = &cobra.Command{
Run: runVersion,
}
-var runCmd = &cobra.Command{
- Use: "run",
- Short: "Launch quickshell with DMS configuration",
- Long: "Launch quickshell with DMS configuration (qs -c dms)",
- PreRunE: findConfig,
- Run: func(cmd *cobra.Command, args []string) {
- daemon, _ := cmd.Flags().GetBool("daemon")
- session, _ := cmd.Flags().GetBool("session")
- if v, _ := cmd.Flags().GetString("log-level"); v != "" {
- if err := os.Setenv("DMS_LOG_LEVEL", v); err != nil {
- log.Fatalf("Failed to set DMS_LOG_LEVEL: %v", err)
- }
- }
- if v, _ := cmd.Flags().GetString("log-file"); v != "" {
- if err := os.Setenv("DMS_LOG_FILE", v); err != nil {
- log.Fatalf("Failed to set DMS_LOG_FILE: %v", err)
- }
- }
- log.ApplyEnvOverrides()
- config.CleanupStrayHyprlandConfFile(log.Infof)
- if daemon {
- runShellDaemon(session)
- } else {
- runShellInteractive(session)
- }
- },
-}
-
-var restartCmd = &cobra.Command{
- Use: "restart",
- Short: "Restart quickshell with DMS configuration",
- Long: "Kill existing DMS shell processes and restart quickshell with DMS configuration",
- PreRunE: findConfig,
- Run: func(cmd *cobra.Command, args []string) {
- restartShell()
- },
-}
-
-var restartDetachedCmd = &cobra.Command{
- Use: "restart-detached ",
- Hidden: true,
- Args: cobra.ExactArgs(1),
- PreRunE: findConfig,
- Run: func(cmd *cobra.Command, args []string) {
- runDetachedRestart(args[0])
- },
-}
-
-var killCmd = &cobra.Command{
- Use: "kill",
- Short: "Kill running DMS shell processes",
- Long: "Kill all running quickshell processes with DMS configuration",
- Run: func(cmd *cobra.Command, args []string) {
- killShell()
- },
-}
-
var ipcCmd = &cobra.Command{
Use: "ipc",
Short: "Send IPC commands to running DMS shell",
@@ -743,12 +685,9 @@ func checkAllPluginsCLI() error {
}
func getCommonCommands() []*cobra.Command {
- return []*cobra.Command{
+ commands := shellApp.Commands()
+ return append(commands, []*cobra.Command{
versionCmd,
- runCmd,
- restartCmd,
- restartDetachedCmd,
- killCmd,
ipcCmd,
debugSrvCmd,
pluginsCmd,
@@ -775,5 +714,5 @@ func getCommonCommands() []*cobra.Command {
trashCmd,
systemCmd,
switchUserCmd,
- }
+ }...)
}
diff --git a/core/cmd/dms/commands_doctor.go b/core/cmd/dms/commands_doctor.go
index 936657811..654e0c2d4 100644
--- a/core/cmd/dms/commands_doctor.go
+++ b/core/cmd/dms/commands_doctor.go
@@ -401,12 +401,12 @@ func checkVersions(qsMissingFeatures bool) []checkResult {
}
func getDMSShellVersion() (version, path string) {
- if err := findConfig(nil, nil); err == nil && configPath != "" {
- versionFile := filepath.Join(configPath, "VERSION")
+ if err := shellApp.ResolveConfig(nil, nil); err == nil && shellApp.ConfigPath() != "" {
+ versionFile := filepath.Join(shellApp.ConfigPath(), "VERSION")
if data, err := os.ReadFile(versionFile); err == nil {
- return strings.TrimSpace(string(data)), configPath
+ return strings.TrimSpace(string(data)), shellApp.ConfigPath()
}
- return "installed", configPath
+ return "installed", shellApp.ConfigPath()
}
if dmsPath, err := config.LocateDMSConfig(); err == nil {
@@ -450,8 +450,8 @@ func checkDMSInstallation() []checkResult {
var results []checkResult
dmsPath := ""
- if err := findConfig(nil, nil); err == nil && configPath != "" {
- dmsPath = configPath
+ if err := shellApp.ResolveConfig(nil, nil); err == nil && shellApp.ConfigPath() != "" {
+ dmsPath = shellApp.ConfigPath()
} else if path, err := config.LocateDMSConfig(); err == nil {
dmsPath = path
}
diff --git a/core/cmd/dms/commands_features.go b/core/cmd/dms/commands_features.go
index b3da0abdf..4cacf50b5 100644
--- a/core/cmd/dms/commands_features.go
+++ b/core/cmd/dms/commands_features.go
@@ -26,7 +26,7 @@ var updateCmd = &cobra.Command{
Use: "update",
Short: "Update DankMaterialShell to the latest version",
Long: "Update DankMaterialShell to the latest version using the appropriate package manager for your distribution",
- PreRunE: findConfig,
+ PreRunE: shellApp.ResolveConfig,
Run: func(cmd *cobra.Command, args []string) {
runUpdate()
},
@@ -98,7 +98,7 @@ func runUpdate() {
}
log.Info("Update complete! Restarting DMS...")
- restartShell()
+ shellApp.Restart()
}
func updateArchLinux() error {
diff --git a/core/cmd/dms/commands_greeter.go b/core/cmd/dms/commands_greeter.go
index 86ffdb16d..0d6746dc5 100644
--- a/core/cmd/dms/commands_greeter.go
+++ b/core/cmd/dms/commands_greeter.go
@@ -278,7 +278,7 @@ func installGreeter(nonInteractive bool) error {
}
fmt.Println("\nDetecting DMS installation...")
- dmsPath, err := greeter.DetectDMSPath()
+ dmsPath, err := detectDMSPath()
if err != nil {
return err
}
@@ -708,7 +708,7 @@ func syncGreeter(nonInteractive bool, forceAuth bool, local bool, profileOnly bo
fmt.Printf("✓ Using local DMS path: %s\n", dmsPath)
}
} else {
- dmsPath, err = greeter.DetectDMSPath()
+ dmsPath, err = detectDMSPath()
if err != nil {
return err
}
@@ -944,6 +944,24 @@ func resolveDMSLocalCandidate(path string) (string, bool) {
return "", false
}
+// detectDMSPath finds the QML tree the greeter copies from. On-disk installs
+// win; with a packaged greeter the embedded UI is a valid copy source, but an
+// extraction path must never be persisted into greetd config, so unpackaged
+// setups still require a real install.
+func detectDMSPath() (string, error) {
+ path, err := greeter.DetectDMSPath()
+ if err == nil {
+ return path, nil
+ }
+ if !greeter.IsGreeterPackaged() {
+ return "", err
+ }
+ if resolveErr := shellApp.ResolveConfig(nil, nil); resolveErr != nil {
+ return "", err
+ }
+ return shellApp.ConfigPath(), nil
+}
+
func resolveLocalDMSPath() (string, error) {
if override := strings.TrimSpace(os.Getenv("DMS_LOCAL_PATH")); override != "" {
if resolved, ok := resolveDMSLocalCandidate(override); ok {
diff --git a/core/cmd/dms/commands_root.go b/core/cmd/dms/commands_root.go
index b263ae6b7..9d6bb757b 100644
--- a/core/cmd/dms/commands_root.go
+++ b/core/cmd/dms/commands_root.go
@@ -1,19 +1,9 @@
package main
import (
- "fmt"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/config"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/spf13/cobra"
)
-var customConfigPath string
-var configPath string
-
var rootCmd = &cobra.Command{
Use: "dms",
Short: "dms CLI",
@@ -21,54 +11,5 @@ var rootCmd = &cobra.Command{
}
func init() {
- rootCmd.PersistentFlags().StringVarP(&customConfigPath, "config", "c", "", "Specify a custom path to the DMS config directory")
-}
-
-func findConfig(cmd *cobra.Command, args []string) error {
- if customConfigPath != "" {
- log.Debug("Custom config path provided via -c flag: %s", customConfigPath)
- shellPath := filepath.Join(customConfigPath, "shell.qml")
-
- info, statErr := os.Stat(shellPath)
-
- if statErr == nil && !info.IsDir() {
- configPath = customConfigPath
- log.Debug("Using config from: %s", configPath)
- return nil
- }
-
- if statErr != nil {
- return fmt.Errorf("custom config path error: %w", statErr)
- }
-
- return fmt.Errorf("path is a directory, not a file: %s", shellPath)
- }
-
- configStateFile := filepath.Join(getRuntimeDir(), "danklinux.path")
- if data, readErr := os.ReadFile(configStateFile); readErr == nil {
- if len(getAllDMSPIDs()) == 0 {
- os.Remove(configStateFile)
- } else {
- statePath := strings.TrimSpace(string(data))
- shellPath := filepath.Join(statePath, "shell.qml")
-
- if info, statErr := os.Stat(shellPath); statErr == nil && !info.IsDir() {
- log.Debug("Using config from active session state file: %s", statePath)
- configPath = statePath
- log.Debug("Using config from: %s", configPath)
- return nil
- }
- os.Remove(configStateFile)
- }
- }
-
- log.Debug("No custom path or active session, searching default XDG locations...")
- var err error
- configPath, err = config.LocateDMSConfig()
- if err != nil {
- return err
- }
-
- log.Debug("Using config from: %s", configPath)
- return nil
+ rootCmd.PersistentFlags().StringVarP(shellApp.CustomConfigVar(), "config", "c", "", "Path to a UI config dir (containing shell.qml) to use instead of the embedded UI (env: DMS_SHELL_DIR)")
}
diff --git a/core/cmd/dms/commands_screenshot.go b/core/cmd/dms/commands_screenshot.go
index 5c626f72a..4fa6b7a80 100644
--- a/core/cmd/dms/commands_screenshot.go
+++ b/core/cmd/dms/commands_screenshot.go
@@ -231,16 +231,16 @@ func setPopoutScreenshotMode(begin bool) {
fn = "begin"
}
cmdArgs := []string{"ipc"}
- if pid, ok := getFirstDMSPID(); ok {
+ if pid, ok := shellApp.SessionPID(); ok {
cmdArgs = append(cmdArgs, "--pid", strconv.Itoa(pid))
} else {
- if err := findConfig(nil, nil); err != nil {
+ if err := shellApp.ResolveConfig(nil, nil); err != nil {
return
}
if qsHasAnyDisplay() {
cmdArgs = append(cmdArgs, "--any-display")
}
- cmdArgs = append(cmdArgs, "-p", configPath)
+ cmdArgs = append(cmdArgs, "-p", shellApp.ConfigPath())
}
cmdArgs = append(cmdArgs, "call", "screenshot", fn)
_ = exec.Command("qs", cmdArgs...).Run()
diff --git a/core/cmd/dms/main.go b/core/cmd/dms/main.go
index cf8ba99eb..f1e5914be 100644
--- a/core/cmd/dms/main.go
+++ b/core/cmd/dms/main.go
@@ -12,13 +12,6 @@ import (
var Version = "dev"
func init() {
- runCmd.Flags().BoolP("daemon", "d", false, "Run in daemon mode")
- runCmd.Flags().Bool("daemon-child", false, "Internal flag for daemon child process")
- runCmd.Flags().Bool("session", false, "Session managed (like as a systemd unit)")
- runCmd.Flags().String("log-level", "", "Log level: debug, info, warn, error, fatal (overrides DMS_LOG_LEVEL)")
- runCmd.Flags().String("log-file", "", "Append logs to this file in addition to stderr (overrides DMS_LOG_FILE)")
- runCmd.Flags().MarkHidden("daemon-child")
-
greeterCmd.AddCommand(greeterInstallCmd, greeterSyncCmd, greeterEnableCmd, greeterStatusCmd, greeterUninstallCmd, greeterLaunchSessionCmd)
authCmd.AddCommand(authSyncCmd, authResolveLockCmd, authListServicesCmd, authValidateCmd)
setupCmd.AddCommand(setupBindsCmd, setupLayoutCmd, setupColorsCmd, setupAlttabCmd, setupOutputsCmd, setupCursorCmd, setupWindowrulesCmd)
diff --git a/core/cmd/dms/main_distro.go b/core/cmd/dms/main_distro.go
index f5a7a2209..2897085ce 100644
--- a/core/cmd/dms/main_distro.go
+++ b/core/cmd/dms/main_distro.go
@@ -12,13 +12,6 @@ import (
var Version = "dev"
func init() {
- runCmd.Flags().BoolP("daemon", "d", false, "Run in daemon mode")
- runCmd.Flags().Bool("daemon-child", false, "Internal flag for daemon child process")
- runCmd.Flags().Bool("session", false, "Session managed (like as a systemd unit)")
- runCmd.Flags().String("log-level", "", "Log level: debug, info, warn, error, fatal (overrides DMS_LOG_LEVEL)")
- runCmd.Flags().String("log-file", "", "Append logs to this file in addition to stderr (overrides DMS_LOG_FILE)")
- runCmd.Flags().MarkHidden("daemon-child")
-
greeterCmd.AddCommand(greeterInstallCmd, greeterSyncCmd, greeterEnableCmd, greeterStatusCmd, greeterUninstallCmd, greeterLaunchSessionCmd)
authCmd.AddCommand(authSyncCmd, authResolveLockCmd, authListServicesCmd, authValidateCmd)
setupCmd.AddCommand(setupBindsCmd, setupLayoutCmd, setupColorsCmd, setupAlttabCmd, setupOutputsCmd, setupCursorCmd, setupWindowrulesCmd)
diff --git a/core/cmd/dms/server_client.go b/core/cmd/dms/server_client.go
index fdd0abaac..27fb16857 100644
--- a/core/cmd/dms/server_client.go
+++ b/core/cmd/dms/server_client.go
@@ -101,11 +101,8 @@ func getServerSocketPath() string {
runtimeDir = os.TempDir()
}
- if parentPID, ok := sessionParentPID(os.Getenv("WAYLAND_DISPLAY")); ok {
- sessionSock := filepath.Join(runtimeDir, fmt.Sprintf("danklinux-%d.sock", parentPID))
- if _, err := os.Stat(sessionSock); err == nil {
- return sessionSock
- }
+ if sessionSock, ok := shellApp.SessionSocketPath(); ok {
+ return sessionSock
}
entries, err := os.ReadDir(runtimeDir)
diff --git a/core/cmd/dms/shell.go b/core/cmd/dms/shell.go
index 41df6ea11..cdbe39e2d 100644
--- a/core/cmd/dms/shell.go
+++ b/core/cmd/dms/shell.go
@@ -4,591 +4,20 @@ import (
"context"
"encoding/json"
"fmt"
- "io"
"os"
"os/exec"
- "os/signal"
"path/filepath"
"slices"
"strconv"
"strings"
"sync"
- "syscall"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server"
)
type ipcTargets map[string]map[string][]string
-// getProcessExitCode returns the exit code from a ProcessState.
-// For normal exits, returns the exit code directly.
-// For signal termination, returns 128 + signal number (Unix convention).
-func getProcessExitCode(state *os.ProcessState) int {
- if state == nil {
- return 1
- }
- if code := state.ExitCode(); code != -1 {
- return code
- }
- // Process was killed by signal - extract signal number
- if status, ok := state.Sys().(syscall.WaitStatus); ok {
- if status.Signaled() {
- return 128 + int(status.Signal())
- }
- }
- return 1
-}
-
-var isSessionManaged bool
-
-func execDetachedRestart(targetPID int) {
- selfPath, err := os.Executable()
- if err != nil {
- return
- }
-
- cmd := exec.Command(selfPath, "restart-detached", strconv.Itoa(targetPID))
- cmd.SysProcAttr = &syscall.SysProcAttr{
- Setsid: true,
- }
- cmd.Start()
-}
-
-func runDetachedRestart(targetPIDStr string) {
- targetPID, err := strconv.Atoi(targetPIDStr)
- if err != nil {
- return
- }
-
- time.Sleep(200 * time.Millisecond)
-
- proc, err := os.FindProcess(targetPID)
- if err == nil {
- proc.Signal(syscall.SIGTERM)
- }
-
- time.Sleep(500 * time.Millisecond)
-
- killShell()
- runShellDaemon(false)
-}
-
-func getRuntimeDir() string {
- if runtime := os.Getenv("XDG_RUNTIME_DIR"); runtime != "" {
- return runtime
- }
- return os.TempDir()
-}
-
-func appendLogEnv(env []string) []string {
- if v := os.Getenv("DMS_LOG_LEVEL"); v != "" {
- env = append(env, "DMS_LOG_LEVEL="+v)
- }
- if v := os.Getenv("DMS_LOG_FILE"); v != "" {
- env = append(env, "DMS_LOG_FILE="+v)
- }
- return env
-}
-
-func withDMSExecutable(env []string) []string {
- selfPath, err := os.Executable()
- if err != nil {
- return env
- }
- return append(env, "DMS_EXECUTABLE="+selfPath)
-}
-
-func hasSystemdRun() bool {
- _, err := exec.LookPath("systemd-run")
- return err == nil
-}
-
-func getPIDFilePath() string {
- return filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.pid", os.Getpid()))
-}
-
-func getSessionFilePath() string {
- return filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.session", os.Getpid()))
-}
-
-func writePIDFile(childPID int) error {
- pidFile := getPIDFilePath()
- if display := os.Getenv("WAYLAND_DISPLAY"); display != "" {
- if err := os.WriteFile(getSessionFilePath(), []byte(display), 0o644); err != nil {
- log.Warnf("Failed to write session file: %v", err)
- }
- }
- return os.WriteFile(pidFile, []byte(strconv.Itoa(childPID)), 0o644)
-}
-
-func removePIDFile() {
- os.Remove(getPIDFilePath())
- os.Remove(getSessionFilePath())
-}
-
-func getAllDMSPIDs() []int {
- dir := getRuntimeDir()
- entries, err := os.ReadDir(dir)
- if err != nil {
- return nil
- }
-
- var pids []int
-
- for _, entry := range entries {
- if !strings.HasPrefix(entry.Name(), "danklinux-") || !strings.HasSuffix(entry.Name(), ".pid") {
- continue
- }
-
- pidFile := filepath.Join(dir, entry.Name())
- data, err := os.ReadFile(pidFile)
- if err != nil {
- continue
- }
-
- childPID, err := strconv.Atoi(strings.TrimSpace(string(data)))
- if err != nil {
- os.Remove(pidFile)
- continue
- }
-
- proc, err := os.FindProcess(childPID)
- if err != nil {
- os.Remove(pidFile)
- continue
- }
-
- if err := proc.Signal(syscall.Signal(0)); err != nil {
- os.Remove(pidFile)
- continue
- }
-
- pids = append(pids, childPID)
-
- parentPIDStr := strings.TrimPrefix(entry.Name(), "danklinux-")
- parentPIDStr = strings.TrimSuffix(parentPIDStr, ".pid")
- if parentPID, err := strconv.Atoi(parentPIDStr); err == nil {
- if parentProc, err := os.FindProcess(parentPID); err == nil {
- if err := parentProc.Signal(syscall.Signal(0)); err == nil {
- pids = append(pids, parentPID)
- }
- }
- }
- }
-
- return pids
-}
-
-func runShellInteractive(session bool) {
- isSessionManaged = session
- go printASCII()
- fmt.Fprintf(os.Stderr, "dms %s\n", Version)
-
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
- socketPath := server.GetSocketPath()
-
- configStateFile := filepath.Join(getRuntimeDir(), "danklinux.path")
- if err := os.WriteFile(configStateFile, []byte(configPath), 0o644); err != nil {
- log.Warnf("Failed to write config state file: %v", err)
- }
- defer os.Remove(configStateFile)
-
- errChan := make(chan error, 2)
-
- go func() {
- defer func() {
- if r := recover(); r != nil {
- errChan <- fmt.Errorf("server panic: %v", r)
- }
- }()
- server.CLIVersion = Version
- if err := server.Start(false); err != nil {
- errChan <- fmt.Errorf("server error: %w", err)
- }
- }()
-
- ensureFontCache()
- log.Infof("Spawning quickshell with -p %s", configPath)
-
- cmd := exec.CommandContext(ctx, "qs", "-p", configPath)
- cmd.Env = withDMSExecutable(append(os.Environ(), "DMS_SOCKET="+socketPath))
- if os.Getenv("QT_LOGGING_RULES") == "" {
- if qtRules := log.GetQtLoggingRules(); qtRules != "" {
- cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules)
- }
- }
-
- if isSessionManaged && hasSystemdRun() {
- cmd.Env = append(cmd.Env, "DMS_DEFAULT_LAUNCH_PREFIX=systemd-run --user --scope")
- }
-
- homeDir, err := os.UserHomeDir()
- if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" {
- if !strings.HasPrefix(configPath, homeDir) {
- cmd.Env = append(cmd.Env, "DMS_DISABLE_HOT_RELOAD=1")
- }
- }
-
- if os.Getenv("QT_QPA_PLATFORMTHEME") == "" {
- cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME=gtk3")
- }
- if os.Getenv("QT_QPA_PLATFORMTHEME_QT6") == "" {
- cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME_QT6=gtk3")
- }
- if os.Getenv("QT_QPA_PLATFORM") == "" {
- cmd.Env = append(cmd.Env, "QT_QPA_PLATFORM=wayland;xcb")
- }
- if os.Getenv("QSG_USE_SIMPLE_ANIMATION_DRIVER") == "" {
- cmd.Env = append(cmd.Env, "QSG_USE_SIMPLE_ANIMATION_DRIVER=1")
- }
-
- cmd.Env = appendLogEnv(cmd.Env)
-
- cmd.Stdin = os.Stdin
- cmd.Stdout = os.Stdout
- tracker := &stderrTracker{parent: os.Stderr}
- cmd.Stderr = tracker
-
- startTime := time.Now()
- if err := cmd.Start(); err != nil {
- log.Fatalf("Error starting quickshell: %v", err)
- }
-
- // Write PID file for the quickshell child process
- if err := writePIDFile(cmd.Process.Pid); err != nil {
- log.Warnf("Failed to write PID file: %v", err)
- }
- defer removePIDFile()
-
- defer func() {
- if cmd.Process != nil {
- cmd.Process.Signal(syscall.SIGTERM)
- }
- }()
-
- sigChan := make(chan os.Signal, 1)
- signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1)
-
- go func() {
- if err := cmd.Wait(); err != nil {
- errChan <- fmt.Errorf("quickshell exited: %w", err)
- } else {
- errChan <- fmt.Errorf("quickshell exited")
- }
- }()
-
- for {
- select {
- case sig := <-sigChan:
- if sig == syscall.SIGUSR1 {
- if isSessionManaged {
- log.Infof("Received SIGUSR1, exiting for systemd restart...")
- cancel()
- cmd.Process.Signal(syscall.SIGTERM)
- os.Remove(socketPath)
- os.Exit(1)
- }
- log.Infof("Received SIGUSR1, spawning detached restart process...")
- execDetachedRestart(os.Getpid())
- return
- }
-
- // Check if qs already crashed before we got SIGTERM (systemd sends SIGTERM when D-Bus name is released)
- select {
- case <-errChan:
- cancel()
- os.Remove(socketPath)
- exitCode := getProcessExitCode(cmd.ProcessState)
- logStartupFailure(startTime, exitCode, tracker)
- os.Exit(exitCode)
- case <-time.After(500 * time.Millisecond):
- }
-
- log.Infof("\nReceived signal %v, shutting down...", sig)
- cancel()
- cmd.Process.Signal(syscall.SIGTERM)
- os.Remove(socketPath)
- return
-
- case err := <-errChan:
- log.Error(err)
- cancel()
- if cmd.Process != nil {
- cmd.Process.Signal(syscall.SIGTERM)
- }
- os.Remove(socketPath)
- exitCode := getProcessExitCode(cmd.ProcessState)
- logStartupFailure(startTime, exitCode, tracker)
- os.Exit(exitCode)
- }
- }
-}
-
-func restartShell() {
- pids := getAllDMSPIDs()
-
- if len(pids) == 0 {
- log.Info("No running DMS shell instances found. Starting daemon...")
- runShellDaemon(false)
- return
- }
-
- currentPid := os.Getpid()
- uniquePids := make(map[int]bool)
-
- for _, pid := range pids {
- if pid != currentPid {
- uniquePids[pid] = true
- }
- }
-
- for pid := range uniquePids {
- proc, err := os.FindProcess(pid)
- if err != nil {
- log.Errorf("Error finding process %d: %v", pid, err)
- continue
- }
-
- if err := proc.Signal(syscall.Signal(0)); err != nil {
- continue
- }
-
- if err := proc.Signal(syscall.SIGUSR1); err != nil {
- log.Errorf("Error sending SIGUSR1 to process %d: %v", pid, err)
- } else {
- log.Infof("Sent SIGUSR1 to DMS process with PID %d", pid)
- }
- }
-}
-
-func killShell() {
- pids := getAllDMSPIDs()
-
- if len(pids) == 0 {
- log.Info("No running DMS shell instances found.")
- return
- }
-
- currentPid := os.Getpid()
- uniquePids := make(map[int]bool)
-
- for _, pid := range pids {
- if pid != currentPid {
- uniquePids[pid] = true
- }
- }
-
- for pid := range uniquePids {
- proc, err := os.FindProcess(pid)
- if err != nil {
- log.Errorf("Error finding process %d: %v", pid, err)
- continue
- }
-
- if err := proc.Signal(syscall.Signal(0)); err != nil {
- continue
- }
-
- if err := proc.Kill(); err != nil {
- log.Errorf("Error killing process %d: %v", pid, err)
- } else {
- log.Infof("Killed DMS process with PID %d", pid)
- }
- }
-
- dir := getRuntimeDir()
- entries, err := os.ReadDir(dir)
- if err != nil {
- return
- }
-
- for _, entry := range entries {
- if !strings.HasPrefix(entry.Name(), "danklinux-") {
- continue
- }
- if strings.HasSuffix(entry.Name(), ".pid") || strings.HasSuffix(entry.Name(), ".session") {
- os.Remove(filepath.Join(dir, entry.Name()))
- }
- }
-}
-
-func runShellDaemon(session bool) {
- isSessionManaged = session
- isDaemonChild := slices.Contains(os.Args, "--daemon-child")
-
- if !isDaemonChild {
- fmt.Fprintf(os.Stderr, "dms %s\n", Version)
-
- cmd := exec.Command(os.Args[0], "run", "-d", "--daemon-child")
- cmd.Env = os.Environ()
-
- cmd.SysProcAttr = &syscall.SysProcAttr{
- Setsid: true,
- }
-
- if err := cmd.Start(); err != nil {
- log.Fatalf("Error starting daemon: %v", err)
- }
-
- log.Infof("DMS shell daemon started (PID: %d)", cmd.Process.Pid)
- return
- }
-
- fmt.Fprintf(os.Stderr, "dms %s\n", Version)
-
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
- socketPath := server.GetSocketPath()
-
- configStateFile := filepath.Join(getRuntimeDir(), "danklinux.path")
- if err := os.WriteFile(configStateFile, []byte(configPath), 0o644); err != nil {
- log.Warnf("Failed to write config state file: %v", err)
- }
- defer os.Remove(configStateFile)
-
- errChan := make(chan error, 2)
-
- go func() {
- defer func() {
- if r := recover(); r != nil {
- errChan <- fmt.Errorf("server panic: %v", r)
- }
- }()
- server.CLIVersion = Version
- if err := server.Start(false); err != nil {
- errChan <- fmt.Errorf("server error: %w", err)
- }
- }()
-
- ensureFontCache()
- log.Infof("Spawning quickshell with -p %s", configPath)
-
- cmd := exec.CommandContext(ctx, "qs", "-p", configPath)
- cmd.Env = withDMSExecutable(append(os.Environ(), "DMS_SOCKET="+socketPath))
- if os.Getenv("QT_LOGGING_RULES") == "" {
- if qtRules := log.GetQtLoggingRules(); qtRules != "" {
- cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules)
- }
- }
-
- // ! TODO - remove when QS 0.3 is up and we can use the pragma
- cmd.Env = append(cmd.Env, "QS_APP_ID=com.danklinux.dms")
-
- if isSessionManaged && hasSystemdRun() {
- cmd.Env = append(cmd.Env, "DMS_DEFAULT_LAUNCH_PREFIX=systemd-run --user --scope")
- }
-
- homeDir, err := os.UserHomeDir()
- if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" {
- if !strings.HasPrefix(configPath, homeDir) {
- cmd.Env = append(cmd.Env, "DMS_DISABLE_HOT_RELOAD=1")
- }
- }
-
- if os.Getenv("QT_QPA_PLATFORMTHEME") == "" {
- cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME=gtk3")
- }
- if os.Getenv("QT_QPA_PLATFORMTHEME_QT6") == "" {
- cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME_QT6=gtk3")
- }
- if os.Getenv("QT_QPA_PLATFORM") == "" {
- cmd.Env = append(cmd.Env, "QT_QPA_PLATFORM=wayland;xcb")
- }
- if os.Getenv("QSG_USE_SIMPLE_ANIMATION_DRIVER") == "" {
- cmd.Env = append(cmd.Env, "QSG_USE_SIMPLE_ANIMATION_DRIVER=1")
- }
-
- cmd.Env = appendLogEnv(cmd.Env)
-
- devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
- if err != nil {
- log.Fatalf("Error opening /dev/null: %v", err)
- }
- defer devNull.Close()
-
- cmd.Stdin = devNull
- cmd.Stdout = devNull
- tracker := &stderrTracker{parent: devNull}
- cmd.Stderr = tracker
-
- startTime := time.Now()
- if err := cmd.Start(); err != nil {
- log.Fatalf("Error starting daemon: %v", err)
- }
-
- // Write PID file for the quickshell child process
- if err := writePIDFile(cmd.Process.Pid); err != nil {
- log.Warnf("Failed to write PID file: %v", err)
- }
- defer removePIDFile()
-
- defer func() {
- if cmd.Process != nil {
- cmd.Process.Signal(syscall.SIGTERM)
- }
- }()
-
- sigChan := make(chan os.Signal, 1)
- signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1)
-
- go func() {
- if err := cmd.Wait(); err != nil {
- errChan <- fmt.Errorf("quickshell exited: %w", err)
- } else {
- errChan <- fmt.Errorf("quickshell exited")
- }
- }()
-
- for {
- select {
- case sig := <-sigChan:
- if sig == syscall.SIGUSR1 {
- if isSessionManaged {
- log.Infof("Received SIGUSR1, exiting for systemd restart...")
- cancel()
- cmd.Process.Signal(syscall.SIGTERM)
- os.Remove(socketPath)
- os.Exit(1)
- }
- log.Infof("Received SIGUSR1, spawning detached restart process...")
- execDetachedRestart(os.Getpid())
- return
- }
-
- // Check if qs already crashed before we got SIGTERM (systemd sends SIGTERM when D-Bus name is released)
- select {
- case <-errChan:
- cancel()
- os.Remove(socketPath)
- exitCode := getProcessExitCode(cmd.ProcessState)
- logStartupFailure(startTime, exitCode, tracker)
- os.Exit(exitCode)
- case <-time.After(500 * time.Millisecond):
- }
-
- cancel()
- cmd.Process.Signal(syscall.SIGTERM)
- os.Remove(socketPath)
- return
-
- case <-errChan:
- cancel()
- if cmd.Process != nil {
- cmd.Process.Signal(syscall.SIGTERM)
- }
- os.Remove(socketPath)
- exitCode := getProcessExitCode(cmd.ProcessState)
- logStartupFailure(startTime, exitCode, tracker)
- os.Exit(exitCode)
- }
- }
-}
-
var qsHasAnyDisplay = sync.OnceValue(func() bool {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
@@ -632,17 +61,17 @@ func parseTargetsFromIPCShowOutput(output string) ipcTargets {
func buildQsIPCBaseArgs() ([]string, error) {
cmdArgs := []string{"ipc"}
- switch pid, ok := getSessionDMSPID(); {
+ switch pid, ok := shellApp.SessionPID(); {
case ok:
cmdArgs = append(cmdArgs, "--pid", strconv.Itoa(pid))
default:
- if err := findConfig(nil, nil); err != nil {
+ if err := shellApp.ResolveConfig(nil, nil); err != nil {
return nil, err
}
if qsHasAnyDisplay() {
cmdArgs = append(cmdArgs, "--any-display")
}
- cmdArgs = append(cmdArgs, "-p", configPath)
+ cmdArgs = append(cmdArgs, "-p", shellApp.ConfigPath())
}
return cmdArgs, nil
}
@@ -699,101 +128,6 @@ func getShellIPCCompletions(args []string, _ string) []string {
return nil
}
-func getFirstDMSPID() (int, bool) {
- dir := getRuntimeDir()
- entries, err := os.ReadDir(dir)
- if err != nil {
- return 0, false
- }
-
- for _, entry := range entries {
- if !strings.HasPrefix(entry.Name(), "danklinux-") || !strings.HasSuffix(entry.Name(), ".pid") {
- continue
- }
-
- data, err := os.ReadFile(filepath.Join(dir, entry.Name()))
- if err != nil {
- continue
- }
-
- pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
- if err != nil {
- continue
- }
-
- proc, err := os.FindProcess(pid)
- if err != nil {
- continue
- }
-
- if proc.Signal(syscall.Signal(0)) != nil {
- continue
- }
-
- return pid, true
- }
-
- return 0, false
-}
-
-func sessionParentPID(display string) (int, bool) {
- if display == "" {
- return 0, false
- }
-
- dir := getRuntimeDir()
- entries, err := os.ReadDir(dir)
- if err != nil {
- return 0, false
- }
-
- for _, entry := range entries {
- name := entry.Name()
- if !strings.HasPrefix(name, "danklinux-") || !strings.HasSuffix(name, ".session") {
- continue
- }
-
- data, err := os.ReadFile(filepath.Join(dir, name))
- if err != nil || strings.TrimSpace(string(data)) != display {
- continue
- }
-
- parentStr := strings.TrimSuffix(strings.TrimPrefix(name, "danklinux-"), ".session")
- parentPID, err := strconv.Atoi(parentStr)
- if err != nil {
- continue
- }
-
- return parentPID, true
- }
-
- return 0, false
-}
-
-func getSessionDMSPID() (int, bool) {
- parentPID, ok := sessionParentPID(os.Getenv("WAYLAND_DISPLAY"))
- if !ok {
- return getFirstDMSPID()
- }
-
- data, err := os.ReadFile(filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.pid", parentPID)))
- if err != nil {
- return getFirstDMSPID()
- }
-
- pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
- if err != nil {
- return getFirstDMSPID()
- }
-
- proc, err := os.FindProcess(pid)
- if err != nil || proc.Signal(syscall.Signal(0)) != nil {
- return getFirstDMSPID()
- }
-
- return pid, true
-}
-
func runShellIPCCommand(args []string) {
if len(args) == 0 {
printIPCHelp()
@@ -946,36 +280,12 @@ func rebuildFontCache() {
}
}
-type stderrTracker struct {
- mu sync.Mutex
- buf strings.Builder
- parent io.Writer
-}
-
-func (s *stderrTracker) Write(p []byte) (n int, err error) {
- s.mu.Lock()
- defer s.mu.Unlock()
- if s.buf.Len() < 8192 {
- s.buf.Write(p)
- }
- if s.parent != nil {
- return s.parent.Write(p)
- }
- return len(p), nil
-}
-
-func (s *stderrTracker) String() string {
- s.mu.Lock()
- defer s.mu.Unlock()
- return s.buf.String()
-}
-
// logStartupFailure logs diagnostic advice if qs crashes within 5s of launch.
-func logStartupFailure(startTime time.Time, exitCode int, tracker *stderrTracker) {
- if time.Since(startTime) >= 5*time.Second || exitCode == 0 || exitCode > 128 {
+func logStartupFailure(exitCode int, uptime time.Duration, stderrTail string) {
+ if uptime >= 5*time.Second || exitCode == 0 || exitCode > 128 {
return
}
- if containsFontCrashSignature(tracker.String()) {
+ if containsFontCrashSignature(stderrTail) {
log.Errorf("DMS startup failed due to a potential font/rendering crash. Try running 'fc-cache -fv' and restarting DMS.")
} else {
log.Errorf("DMS startup failed (exit code %d). Run 'dms doctor' for more diagnostics.", exitCode)
diff --git a/core/cmd/dms/shellapp.go b/core/cmd/dms/shellapp.go
new file mode 100644
index 000000000..473b65441
--- /dev/null
+++ b/core/cmd/dms/shellapp.go
@@ -0,0 +1,82 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/config"
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/server"
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/shellembed"
+ "github.com/AvengeMedia/dankgo/shellapp"
+)
+
+var shellApp = shellapp.New(shellapp.Config{
+ ID: "danklinux",
+ EnvPrefix: "DMS",
+ QSAppID: "com.danklinux.dms",
+ Version: Version,
+ Embedded: embeddedShell{},
+ Boot: bootBackend,
+ PreLaunch: preLaunch,
+ ExtraEnv: dmsExtraEnv,
+ OnUIExit: logStartupFailure,
+})
+
+type embeddedShell struct{}
+
+func (embeddedShell) Available() bool { return shellembed.Available() }
+
+func (embeddedShell) Extract(baseDir string) (string, error) { return shellembed.Extract(baseDir) }
+
+func (embeddedShell) Prune(baseDir, keep string) { shellembed.Prune(baseDir, keep) }
+
+type dmsBackend struct {
+ srv *server.Server
+ done chan error
+}
+
+func (b *dmsBackend) SocketPath() string { return b.srv.SocketPath() }
+
+func (b *dmsBackend) Close() { b.srv.Close() }
+
+func (b *dmsBackend) Done() <-chan error { return b.done }
+
+func bootBackend(ctx context.Context) (shellapp.Backend, error) {
+ config.CleanupStrayHyprlandConfFile(log.Infof)
+ server.CLIVersion = Version
+
+ srv := server.New()
+ if err := srv.Listen(); err != nil {
+ return nil, err
+ }
+
+ backend := &dmsBackend{srv: srv, done: make(chan error, 1)}
+ go func() {
+ defer func() {
+ if r := recover(); r != nil {
+ backend.done <- fmt.Errorf("server panic: %v", r)
+ }
+ }()
+ backend.done <- srv.Serve(false)
+ }()
+
+ return backend, nil
+}
+
+func preLaunch() {
+ go printASCII()
+ ensureFontCache()
+}
+
+func dmsExtraEnv(string) []string {
+ var env []string
+ if selfPath, err := os.Executable(); err == nil {
+ env = append(env, "DMS_EXECUTABLE="+selfPath)
+ }
+ if os.Getenv("QSG_USE_SIMPLE_ANIMATION_DRIVER") == "" {
+ env = append(env, "QSG_USE_SIMPLE_ANIMATION_DRIVER=1")
+ }
+ return env
+}
diff --git a/core/go.mod b/core/go.mod
index 8195a8e38..92c7410be 100644
--- a/core/go.mod
+++ b/core/go.mod
@@ -1,14 +1,13 @@
module github.com/AvengeMedia/DankMaterialShell/core
-go 1.26.1
+go 1.26.4
require (
github.com/Wifx/gonetworkmanager/v2 v2.2.0
- github.com/alecthomas/chroma/v2 v2.24.1
+ github.com/alecthomas/chroma/v2 v2.27.0
github.com/charmbracelet/bubbles v1.0.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
- github.com/charmbracelet/log v1.0.0
github.com/fsnotify/fsnotify v1.10.1
github.com/godbus/dbus/v5 v5.2.2
github.com/holoplot/go-evdev v0.0.0-20260504100651-66d1748fe847
@@ -18,12 +17,12 @@ require (
github.com/stretchr/testify v1.11.1
github.com/yeqown/go-qrcode/v2 v2.2.5
github.com/yeqown/go-qrcode/writer/standard v1.3.0
- github.com/yuin/goldmark v1.8.2
+ github.com/yuin/goldmark v1.8.4
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
- go.etcd.io/bbolt v1.4.3
+ go.etcd.io/bbolt v1.5.0
go4.org/mem v0.0.0-20240501181205-ae6ca9944745
- golang.org/x/image v0.39.0
- tailscale.com v1.96.5
+ golang.org/x/image v0.44.0
+ tailscale.com v1.100.0
)
require (
@@ -31,28 +30,28 @@ require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.4.1 // indirect
github.com/akutz/memconn v0.1.0 // indirect
+ github.com/charmbracelet/log v1.0.0 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
- github.com/cloudflare/circl v1.6.3 // indirect
- github.com/coder/websocket v1.8.14 // indirect
- github.com/cyphar/filepath-securejoin v0.6.1 // indirect
- github.com/dblohm7/wingoes v0.0.0-20250822163801-6d8e6105c62d // indirect
- github.com/dlclark/regexp2 v1.12.0 // indirect
+ github.com/cloudflare/circl v1.6.4 // indirect
+ github.com/coder/websocket v1.8.15 // indirect
+ github.com/dblohm7/wingoes v0.0.0-20260526185140-fb298caac7ca // indirect
+ github.com/dlclark/regexp2/v2 v2.5.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fogleman/gg v1.3.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.2 // indirect
github.com/go-git/gcfg/v2 v2.0.2 // indirect
- github.com/go-git/go-billy/v6 v6.0.0-20260504142752-cb8e9d337266 // indirect
- github.com/go-json-experiment/json v0.0.0-20260430182902-b6187a392ed4 // indirect
+ github.com/go-git/go-billy/v6 v6.0.0-alpha.1 // indirect
+ github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
github.com/jsimonetti/rtnetlink v1.4.2 // indirect
github.com/kevinburke/ssh_config v1.6.0 // indirect
- github.com/klauspost/cpuid/v2 v2.3.0 // indirect
- github.com/mdlayher/netlink v1.11.1 // indirect
- github.com/mdlayher/socket v0.6.0 // indirect
+ github.com/klauspost/cpuid/v2 v2.4.0 // indirect
+ github.com/mdlayher/netlink v1.11.2 // indirect
+ github.com/mdlayher/socket v0.6.1 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/pjbgf/sha1cd v0.6.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
@@ -62,14 +61,15 @@ require (
github.com/x448/float16 v0.8.4 // indirect
github.com/yeqown/reedsolomon v1.0.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
- golang.org/x/crypto v0.50.0 // indirect
- golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
- golang.org/x/net v0.53.0 // indirect
- golang.org/x/sync v0.20.0 // indirect
+ golang.org/x/crypto v0.54.0 // indirect
+ golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597 // indirect
+ golang.org/x/net v0.57.0 // indirect
+ golang.org/x/sync v0.22.0 // indirect
golang.zx2c4.com/wireguard/windows v1.0.1 // indirect
)
require (
+ github.com/AvengeMedia/dankgo v0.0.0-20260718184203-ed59ffc6e599
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.4.3 // indirect
@@ -79,22 +79,22 @@ require (
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
- github.com/go-git/go-git/v6 v6.0.0-alpha.2
+ github.com/go-git/go-git/v6 v6.0.0-alpha.4
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.4.0
- github.com/mattn/go-isatty v0.0.22
+ github.com/mattn/go-isatty v0.0.23
github.com/mattn/go-localereader v0.0.1 // indirect
- github.com/mattn/go-runewidth v0.0.23 // indirect
+ github.com/mattn/go-runewidth v0.0.24 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
- github.com/muesli/termenv v0.16.0
+ github.com/muesli/termenv v0.16.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/afero v1.15.0
github.com/spf13/pflag v1.0.10 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
- golang.org/x/sys v0.43.0
- golang.org/x/text v0.36.0
+ golang.org/x/sys v0.47.0
+ golang.org/x/text v0.40.0
gopkg.in/yaml.v3 v3.0.1
)
diff --git a/core/go.sum b/core/go.sum
index e1bb4ebd9..836ab74eb 100644
--- a/core/go.sum
+++ b/core/go.sum
@@ -1,5 +1,7 @@
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
+github.com/AvengeMedia/dankgo v0.0.0-20260718184203-ed59ffc6e599 h1:hxy5SRGREsQ6v4xUOxWnWVPWD6ZU+H0jA+LHcTwXbCg=
+github.com/AvengeMedia/dankgo v0.0.0-20260718184203-ed59ffc6e599/go.mod h1:7p7cfydr4WM1G6eOPFlANXF3IV5du3FoA4CbDPprHAo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
@@ -11,8 +13,8 @@ github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukD
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-github.com/alecthomas/chroma/v2 v2.24.1 h1:m5ffpfZbIb++k8AqFEKy9uVgY12xIQtBsQlc6DfZJQM=
-github.com/alecthomas/chroma/v2 v2.24.1/go.mod h1:l+ohZ9xRXIbGe7cIW+YZgOGbvuVLjMps/FYN/CwuabI=
+github.com/alecthomas/chroma/v2 v2.27.0 h1:FodwmyOBgJULFYmDqibcp9pvfDLWdtPRh9v/r5BXYZs=
+github.com/alecthomas/chroma/v2 v2.27.0/go.mod h1:NjJ3ciIgrqBNeIkWZ4e46nseoLDslxU1LmfCoL+wcY8=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
@@ -48,25 +50,23 @@ github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSE
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
-github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
-github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
-github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
-github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
+github.com/cloudflare/circl v1.6.4 h1:pOXuDTCEYyzydgUpQ0CQz3LsinKjiSk6nNP5Lt5K64U=
+github.com/cloudflare/circl v1.6.4/go.mod h1:YxarevkLlbaHuWsxG6vmYNWBEsSp4pnp7j+4VljMavY=
+github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA=
+github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creachadair/taskgroup v0.13.2 h1:3KyqakBuFsm3KkXi/9XIb0QcA8tEzLHLgaoidf0MdVc=
github.com/creachadair/taskgroup v0.13.2/go.mod h1:i3V1Zx7H8RjwljUEeUWYT30Lmb9poewSb2XI1yTwD0g=
-github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
-github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dblohm7/wingoes v0.0.0-20250822163801-6d8e6105c62d h1:QRKpU+9ZBDs62LyBfwhZkJdB5DJX2Sm3p4kUh7l1aA0=
-github.com/dblohm7/wingoes v0.0.0-20250822163801-6d8e6105c62d/go.mod h1:SUxUaAK/0UG5lYyZR1L1nC4AaYYvSSYTWQSH3FPcxKU=
+github.com/dblohm7/wingoes v0.0.0-20260526185140-fb298caac7ca h1:h1Awca4lQOspNR/2eeo04Ricn5NixDX9mb17WSAgLhQ=
+github.com/dblohm7/wingoes v0.0.0-20260526185140-fb298caac7ca/go.mod h1:2TGl1jRJrRpbzykmg7asHm3h08TqutUgQqY5v9k/g3c=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8=
-github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/dlclark/regexp2/v2 v2.5.1 h1:E5Ug7Dh264W1ymdySmiHNcDG7fmsR307APCE5R07a20=
+github.com/dlclark/regexp2/v2 v2.5.1/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
@@ -85,14 +85,14 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=
github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs=
-github.com/go-git/go-billy/v6 v6.0.0-20260504142752-cb8e9d337266 h1:wH21vHuv323v9x78JNFNJ6P7HEAsdwr9yq2k9/o4zEE=
-github.com/go-git/go-billy/v6 v6.0.0-20260504142752-cb8e9d337266/go.mod h1:CdBVp7CXl9l3sOyNEog46cP1Pvx/hjCe9AD0mtaIUYU=
-github.com/go-git/go-git-fixtures/v6 v6.0.0-20260405195209-b16dd39735e0 h1:XoTsdvaghuVfIr7HpNTmFDLu2nz3I2iGqyn6Uk6MkJc=
-github.com/go-git/go-git-fixtures/v6 v6.0.0-20260405195209-b16dd39735e0/go.mod h1:1Lr7/vYEYyl6Ir9Ku0tKrCIRreM5zovv0Jdx2MPSM4s=
-github.com/go-git/go-git/v6 v6.0.0-alpha.2 h1:T3loNtDuAixNzXtlQxZhnYiYpaQ3CA4vn9RssAniEeI=
-github.com/go-git/go-git/v6 v6.0.0-alpha.2/go.mod h1:oCD3i19CTz7gBpeb11ZZqL91WzqbMq9avn5KpUYy/Ak=
-github.com/go-json-experiment/json v0.0.0-20260430182902-b6187a392ed4 h1:2WmHkJINIjgXXYDGik8d3oJvFA3DAwPy00csDJ3vo+o=
-github.com/go-json-experiment/json v0.0.0-20260430182902-b6187a392ed4/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
+github.com/go-git/go-billy/v6 v6.0.0-alpha.1 h1:xVjAR4oUvrKy7/Xuw/lLlV3gkxR3KO2H8W+MamuVVsQ=
+github.com/go-git/go-billy/v6 v6.0.0-alpha.1/go.mod h1:eaCUpHbedW7//EwcYmUDfJe2N6sJC9O12AT0OTqJR1E=
+github.com/go-git/go-git-fixtures/v6 v6.0.0-alpha.1 h1:gmqi2jvsreu0s8JMLylYDFq4sbjHwwlhktMw0DUg3mA=
+github.com/go-git/go-git-fixtures/v6 v6.0.0-alpha.1/go.mod h1:ECf1MqJlBdYpKggBrOXjo/0EnvRZx6D++I86UYjPgAQ=
+github.com/go-git/go-git/v6 v6.0.0-alpha.4 h1:aDTc2UGanmaE7FkGLSlBEB9nohMnQ+RKXcfq/D+esDQ=
+github.com/go-git/go-git/v6 v6.0.0-alpha.4/go.mod h1:4ODa/G7hPWrh4Y+7lmt59Ij3zW38IEfvRoAZxLYYBhc=
+github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 h1:KZaTBSyshWX3MP5jukJcNSuXDQTO+rNpt0J564dX/eg=
+github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -117,8 +117,8 @@ github.com/jsimonetti/rtnetlink v1.4.2 h1:Df9w9TZ3npHTyDn0Ev9e1uzmN2odmXd0QX+J5G
github.com/jsimonetti/rtnetlink v1.4.2/go.mod h1:92s6LJdE+1iOrw+F2/RO7LYI2Qd8pPpFNNUYW06gcoM=
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
-github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
-github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
+github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -128,16 +128,16 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
-github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
+github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ=
+github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw=
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
-github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
-github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
-github.com/mdlayher/netlink v1.11.1 h1:T136gDS6Gkt+hLncaBwKdW5GpEC8Z0ykqimOebVoal0=
-github.com/mdlayher/netlink v1.11.1/go.mod h1:ao4LjamyK4Uq9L8+fQzqFYpAncbeCdwbvd9Edv/pYnc=
-github.com/mdlayher/socket v0.6.0 h1:ScZPaAGyO1icQnbFrhPM8mnXyMu9qukC1K4ZoM2IQKU=
-github.com/mdlayher/socket v0.6.0/go.mod h1:q7vozUAnxSqnjHc12Fik5yUKIzfZ8ITCfMkhOtE9z18=
+github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
+github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
+github.com/mdlayher/netlink v1.11.2 h1:HKh2jqe+omdSWcQ88nrT7INE61B0NXfiSPFdgL4YbNI=
+github.com/mdlayher/netlink v1.11.2/go.mod h1:uT2Yc/QLaZubzDpZIBi9d4GoeLwtp3x1AMeqSRrK2sA=
+github.com/mdlayher/socket v0.6.1 h1:M7uj2NtuujUY4mYr1C57NmfNiRHbkKpnBxO856lsc3A=
+github.com/mdlayher/socket v0.6.1/go.mod h1:+/SGtqc9V+5dAuRgQsU0fGBI+oRDiW7O2Obx10OIWfg=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
@@ -180,8 +180,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
-github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da h1:jVRUZPRs9sqyKlYHHzHjAqKN+6e/Vog6NpHYeNPJqOw=
-github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
+github.com/tailscale/wireguard-go v0.0.0-20260527010701-b48af7099cad h1:Ky26FR5yZ5IKEB0xtm5A8xSTb06ImY7kxBFrvgOmJSg=
+github.com/tailscale/wireguard-go v0.0.0-20260527010701-b48af7099cad/go.mod h1:6SerzcvHWQchKO2BfNdmquA77CHSECZuFl+D9fp4RnI=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
@@ -193,36 +193,36 @@ github.com/yeqown/go-qrcode/writer/standard v1.3.0/go.mod h1:O4MbzsotGCvy8upYPCR
github.com/yeqown/reedsolomon v1.0.0 h1:x1h/Ej/uJnNu8jaX7GLHBWmZKCAWjEJTetkqaabr4B0=
github.com/yeqown/reedsolomon v1.0.0/go.mod h1:P76zpcn2TCuL0ul1Fso373qHRc69LKwAw/Iy6g1WiiM=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE=
-github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
+github.com/yuin/goldmark v1.8.4 h1:oat/nd3U6NeQqFEL3xpEJq7d7c86NI+DbSNGAs4xnjA=
+github.com/yuin/goldmark v1.8.4/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
-go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
-go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
+go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU=
+go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 h1:Tl++JLUCe4sxGu8cTpDzRLd3tN7US4hOxG5YpKCzkek=
go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
-golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
-golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
-golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
-golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
-golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
-golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
-golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
-golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
-golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
-golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
-golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
-golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
+golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
+golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
+golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597 h1:qLvzZeaANDgyVOA8pyHCOStGlXn0rseXma+GQjeuv2g=
+golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597/go.mod h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q=
+golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I=
+golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY=
+golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
+golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
+golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
+golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
+golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
+golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
-golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
-golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
-golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
-golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
-golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
+golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
+golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
+golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
+golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
+golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard/windows v1.0.1 h1:eOxiDVbywPC+ZQqvdCK7x+ZwWXKbYv50TtH8ysFIbw8=
@@ -235,5 +235,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-tailscale.com v1.96.5 h1:gNkfA/KSZAl6jCH9cj8urq00HRWItDDTtGsyATI89jA=
-tailscale.com v1.96.5/go.mod h1:/3lnZBYb2UEwnN0MNu2SDXUtT06AGd5k0s+OWx3WmcY=
+tailscale.com v1.100.0 h1:nm/M/dEaW9RaRsGUjW2HsSDpsZ60Jwd9k4gNW9tTFiE=
+tailscale.com v1.100.0/go.mod h1:DQ9YBy85DpNlSyeU2XRIWzbAu3RsGp/frv+Khg57meE=
diff --git a/core/internal/errdefs/errdefs.go b/core/internal/errdefs/errdefs.go
index 3b5f15984..7cf3faf81 100644
--- a/core/internal/errdefs/errdefs.go
+++ b/core/internal/errdefs/errdefs.go
@@ -1,9 +1,15 @@
package errdefs
-type ErrorType int
+import (
+ dankerrdefs "github.com/AvengeMedia/dankgo/errdefs"
+)
+
+type ErrorType = dankerrdefs.ErrorType
+
+type CustomError = dankerrdefs.CustomError
const (
- ErrTypeNotLinux ErrorType = iota
+ ErrTypeNotLinux ErrorType = dankerrdefs.AppErrorBase + iota
ErrTypeInvalidArchitecture
ErrTypeUnsupportedDistribution
ErrTypeUnsupportedVersion
@@ -22,20 +28,8 @@ const (
ErrTypeGeneric
)
-type CustomError struct {
- Type ErrorType
- Message string
-}
-
-func (e *CustomError) Error() string {
- return e.Message
-}
-
func NewCustomError(errType ErrorType, message string) error {
- return &CustomError{
- Type: errType,
- Message: message,
- }
+ return dankerrdefs.NewCustomError(errType, message)
}
const (
diff --git a/core/internal/geolocation/client_geoclue.go b/core/internal/geolocation/client_geoclue.go
index 90d68388d..b34c547c2 100644
--- a/core/internal/geolocation/client_geoclue.go
+++ b/core/internal/geolocation/client_geoclue.go
@@ -5,8 +5,8 @@ import (
"sync"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/dbusutil"
+ "github.com/AvengeMedia/dankgo/syncmap"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/log/log.go b/core/internal/log/log.go
index d116cb403..fdf465f97 100644
--- a/core/internal/log/log.go
+++ b/core/internal/log/log.go
@@ -1,220 +1,32 @@
package log
import (
- "io"
- "os"
- "regexp"
- "strings"
- "sync"
-
- "github.com/charmbracelet/lipgloss"
- cblog "github.com/charmbracelet/log"
- "github.com/mattn/go-isatty"
- "github.com/muesli/termenv"
+ danklog "github.com/AvengeMedia/dankgo/log"
)
-// Logger embeds the Charm Logger and adds Printf/Fatalf
-type Logger struct{ *cblog.Logger }
+type Logger = danklog.Logger
-// Printf routes goose/info-style logs through Infof.
-func (l *Logger) Printf(format string, v ...any) { l.Infof(format, v...) }
-
-// Fatalf keeps goose’s contract of exiting the program.
-func (l *Logger) Fatalf(format string, v ...any) { l.Logger.Fatalf(format, v...) }
-
-var (
- logger *Logger
- initLogger sync.Once
-
- logMu sync.Mutex
- logFile *os.File
- logStderr io.Writer = os.Stderr
-
- ansiRe = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
-)
-
-// ansiStripWriter strips ANSI escape sequences before forwarding to w. Used
-// for the file sink so colored stderr stays colored while the file stays plain.
-type ansiStripWriter struct{ w io.Writer }
-
-func (a *ansiStripWriter) Write(p []byte) (int, error) {
- stripped := ansiRe.ReplaceAll(p, nil)
- if _, err := a.w.Write(stripped); err != nil {
- return 0, err
- }
- return len(p), nil
+func init() {
+ danklog.SetEnvPrefix("DMS")
}
-func parseLogLevel(level string) cblog.Level {
- switch strings.ToLower(level) {
- case "debug":
- return cblog.DebugLevel
- case "info":
- return cblog.InfoLevel
- case "warn", "warning":
- return cblog.WarnLevel
- case "error":
- return cblog.ErrorLevel
- case "fatal":
- return cblog.FatalLevel
- default:
- return cblog.InfoLevel
- }
-}
+func GetLogger() *Logger { return danklog.GetLogger() }
-func GetQtLoggingRules() string {
- level := os.Getenv("DMS_LOG_LEVEL")
- if level == "" {
- level = "info"
- }
+func GetQtLoggingRules() string { return danklog.GetQtLoggingRules() }
- // scene carries QML engine warnings (e.g. QQuickImage "Cannot open" cache
- // probes); suppressed except at debug level
- var rules []string
- switch strings.ToLower(level) {
- case "fatal":
- rules = []string{"*.debug=false", "*.info=false", "*.warning=false", "*.critical=false"}
- case "error":
- rules = []string{"*.debug=false", "*.info=false", "*.warning=false"}
- case "warn", "warning":
- rules = []string{"*.debug=false", "*.info=false", "scene.warning=false"}
- case "info":
- rules = []string{"*.debug=false", "scene.warning=false"}
- case "debug":
- return ""
- default:
- rules = []string{"*.debug=false", "scene.warning=false"}
- }
+func SetLevel(level string) { danklog.SetLevel(level) }
- return strings.Join(rules, ";")
-}
+func SetLogFile(path string) error { return danklog.SetLogFile(path) }
-// GetLogger returns a logger instance
-func GetLogger() *Logger {
- initLogger.Do(func() {
- styles := cblog.DefaultStyles()
- // Attempt to match the colors used by qml/quickshell logs
- styles.Levels[cblog.FatalLevel] = lipgloss.NewStyle().
- SetString(" FATAL").
- Foreground(lipgloss.Color("1"))
- styles.Levels[cblog.ErrorLevel] = lipgloss.NewStyle().
- SetString(" ERROR").
- Foreground(lipgloss.Color("9"))
- styles.Levels[cblog.WarnLevel] = lipgloss.NewStyle().
- SetString(" WARN").
- Foreground(lipgloss.Color("3"))
- styles.Levels[cblog.InfoLevel] = lipgloss.NewStyle().
- SetString(" INFO").
- Foreground(lipgloss.Color("2"))
- styles.Levels[cblog.DebugLevel] = lipgloss.NewStyle().
- SetString(" DEBUG").
- Foreground(lipgloss.Color("4"))
+func ApplyEnvOverrides() { danklog.ApplyEnvOverrides() }
- base := cblog.New(logStderr)
- base.SetStyles(styles)
- base.SetReportTimestamp(false)
-
- level := cblog.InfoLevel
- if envLevel := os.Getenv("DMS_LOG_LEVEL"); envLevel != "" {
- level = parseLogLevel(envLevel)
- }
- base.SetLevel(level)
- base.SetPrefix(" go")
-
- logger = &Logger{base}
-
- if path := os.Getenv("DMS_LOG_FILE"); path != "" {
- logMu.Lock()
- _ = setLogFile(logger, path)
- logMu.Unlock()
- }
- })
- return logger
-}
-
-// SetLevel updates the active log level. Accepts the same strings as
-// DMS_LOG_LEVEL. Unknown values default to info.
-func SetLevel(level string) {
- GetLogger().SetLevel(parseLogLevel(level))
-}
-
-// SetLogFile makes the logger append to path in addition to stderr. Passing an
-// empty string detaches the file sink. Atomic per-line writes (≤PIPE_BUF) on
-// O_APPEND keep concurrent Go and QML writers from corrupting each other.
-//
-// Color handling: charmbracelet/log auto-detects color support from its
-// io.Writer, and io.MultiWriter doesn't pass that through, so we force the ANSI
-// profile when stderr is a TTY and route the file through ansiStripWriter so
-// the file stays plain while stderr keeps its colors.
-func SetLogFile(path string) error {
- l := GetLogger()
- logMu.Lock()
- defer logMu.Unlock()
- return setLogFile(l, path)
-}
-
-func setLogFile(l *Logger, path string) error {
- if logFile != nil {
- logFile.Close()
- logFile = nil
- }
-
- if path == "" {
- l.SetOutput(logStderr)
- applyColorProfile(l, logStderr)
- return nil
- }
-
- f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644)
- if err != nil {
- return err
- }
- logFile = f
- out := io.MultiWriter(logStderr, &ansiStripWriter{w: f})
- l.SetOutput(out)
- applyColorProfile(l, logStderr)
- return nil
-}
-
-// applyColorProfile forces the renderer's color profile to match what stderr
-// would produce on its own, undoing the auto-downgrade triggered by wrapping
-// stderr in a non-TTY writer (e.g. io.MultiWriter).
-func applyColorProfile(l *Logger, stderr io.Writer) {
- f, ok := stderr.(*os.File)
- if !ok {
- l.SetColorProfile(termenv.Ascii)
- return
- }
- if isatty.IsTerminal(f.Fd()) {
- l.SetColorProfile(termenv.ANSI)
- return
- }
- l.SetColorProfile(termenv.Ascii)
-}
-
-// ApplyEnvOverrides re-reads DMS_LOG_LEVEL and DMS_LOG_FILE and reconfigures
-// the singleton. Safe to call after CLI flags have rewritten the environment.
-func ApplyEnvOverrides() {
- GetLogger()
- if level := os.Getenv("DMS_LOG_LEVEL"); level != "" {
- SetLevel(level)
- }
- if path := os.Getenv("DMS_LOG_FILE"); path != "" {
- if err := SetLogFile(path); err != nil {
- Warnf("Failed to open log file %q: %v", path, err)
- }
- }
-}
-
-// * Convenience wrappers
-
-func Debug(msg any, keyvals ...any) { GetLogger().Debug(msg, keyvals...) }
-func Debugf(format string, v ...any) { GetLogger().Debugf(format, v...) }
-func Info(msg any, keyvals ...any) { GetLogger().Info(msg, keyvals...) }
-func Infof(format string, v ...any) { GetLogger().Infof(format, v...) }
-func Warn(msg any, keyvals ...any) { GetLogger().Warn(msg, keyvals...) }
-func Warnf(format string, v ...any) { GetLogger().Warnf(format, v...) }
-func Error(msg any, keyvals ...any) { GetLogger().Error(msg, keyvals...) }
-func Errorf(format string, v ...any) { GetLogger().Errorf(format, v...) }
-func Fatal(msg any, keyvals ...any) { GetLogger().Fatal(msg, keyvals...) }
-func Fatalf(format string, v ...any) { GetLogger().Fatalf(format, v...) }
+func Debug(msg any, keyvals ...any) { danklog.Debug(msg, keyvals...) }
+func Debugf(format string, v ...any) { danklog.Debugf(format, v...) }
+func Info(msg any, keyvals ...any) { danklog.Info(msg, keyvals...) }
+func Infof(format string, v ...any) { danklog.Infof(format, v...) }
+func Warn(msg any, keyvals ...any) { danklog.Warn(msg, keyvals...) }
+func Warnf(format string, v ...any) { danklog.Warnf(format, v...) }
+func Error(msg any, keyvals ...any) { danklog.Error(msg, keyvals...) }
+func Errorf(format string, v ...any) { danklog.Errorf(format, v...) }
+func Fatal(msg any, keyvals ...any) { danklog.Fatal(msg, keyvals...) }
+func Fatalf(format string, v ...any) { danklog.Fatalf(format, v...) }
diff --git a/core/internal/proto/wlr_output_management/output_management.go b/core/internal/proto/wlr_output_management/output_management.go
index 23f934fc2..001c91da9 100644
--- a/core/internal/proto/wlr_output_management/output_management.go
+++ b/core/internal/proto/wlr_output_management/output_management.go
@@ -34,7 +34,7 @@ import (
"unsafe"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
func registerServerProxy(ctx *client.Context, proxy client.Proxy, serverID uint32) {
diff --git a/core/internal/server/apppicker/handlers.go b/core/internal/server/apppicker/handlers.go
index 6fa416329..f824f80ab 100644
--- a/core/internal/server/apppicker/handlers.go
+++ b/core/internal/server/apppicker/handlers.go
@@ -1,14 +1,12 @@
package apppicker
import (
- "net"
-
"github.com/AvengeMedia/DankMaterialShell/core/internal/desktop"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
switch req.Method {
case "apppicker.open", "browser.open":
handleOpen(conn, req, manager)
@@ -17,7 +15,7 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleOpen(conn net.Conn, req models.Request, manager *Manager) {
+func handleOpen(conn *models.Conn, req models.Request, manager *Manager) {
log.Infof("AppPicker: Received %s request with params: %+v", req.Method, req.Params)
target, ok := models.Get[string](req, "target")
diff --git a/core/internal/server/apppicker/manager.go b/core/internal/server/apppicker/manager.go
index bde0846a0..c3a158afe 100644
--- a/core/internal/server/apppicker/manager.go
+++ b/core/internal/server/apppicker/manager.go
@@ -3,7 +3,7 @@ package apppicker
import (
"sync"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
type Manager struct {
diff --git a/core/internal/server/bluez/handlers.go b/core/internal/server/bluez/handlers.go
index be5a1c431..96f71b17d 100644
--- a/core/internal/server/bluez/handlers.go
+++ b/core/internal/server/bluez/handlers.go
@@ -1,12 +1,10 @@
package bluez
import (
- "encoding/json"
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
type BluetoothEvent struct {
@@ -14,7 +12,7 @@ type BluetoothEvent struct {
Data BluetoothState `json:"data"`
}
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
switch req.Method {
case "bluetooth.getState":
handleGetState(conn, req, manager)
@@ -47,11 +45,11 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
-func handleStartDiscovery(conn net.Conn, req models.Request, manager *Manager) {
+func handleStartDiscovery(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.StartDiscovery(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -59,7 +57,7 @@ func handleStartDiscovery(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "discovery started"})
}
-func handleStopDiscovery(conn net.Conn, req models.Request, manager *Manager) {
+func handleStopDiscovery(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.StopDiscovery(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -67,7 +65,7 @@ func handleStopDiscovery(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "discovery stopped"})
}
-func handleSetPowered(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetPowered(conn *models.Conn, req models.Request, manager *Manager) {
powered, err := params.Bool(req.Params, "powered")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -82,7 +80,7 @@ func handleSetPowered(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "powered state updated"})
}
-func handlePairDevice(conn net.Conn, req models.Request, manager *Manager) {
+func handlePairDevice(conn *models.Conn, req models.Request, manager *Manager) {
devicePath, err := params.String(req.Params, "device")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -97,7 +95,7 @@ func handlePairDevice(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "pairing initiated"})
}
-func handleConnectDevice(conn net.Conn, req models.Request, manager *Manager) {
+func handleConnectDevice(conn *models.Conn, req models.Request, manager *Manager) {
devicePath, err := params.String(req.Params, "device")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -112,7 +110,7 @@ func handleConnectDevice(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "connecting"})
}
-func handleDisconnectDevice(conn net.Conn, req models.Request, manager *Manager) {
+func handleDisconnectDevice(conn *models.Conn, req models.Request, manager *Manager) {
devicePath, err := params.String(req.Params, "device")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -127,7 +125,7 @@ func handleDisconnectDevice(conn net.Conn, req models.Request, manager *Manager)
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "disconnected"})
}
-func handleRemoveDevice(conn net.Conn, req models.Request, manager *Manager) {
+func handleRemoveDevice(conn *models.Conn, req models.Request, manager *Manager) {
devicePath, err := params.String(req.Params, "device")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -142,7 +140,7 @@ func handleRemoveDevice(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "device removed"})
}
-func handleTrustDevice(conn net.Conn, req models.Request, manager *Manager) {
+func handleTrustDevice(conn *models.Conn, req models.Request, manager *Manager) {
devicePath, err := params.String(req.Params, "device")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -157,7 +155,7 @@ func handleTrustDevice(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "device trusted"})
}
-func handleUntrustDevice(conn net.Conn, req models.Request, manager *Manager) {
+func handleUntrustDevice(conn *models.Conn, req models.Request, manager *Manager) {
devicePath, err := params.String(req.Params, "device")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -172,7 +170,7 @@ func handleUntrustDevice(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "device untrusted"})
}
-func handlePairingSubmit(conn net.Conn, req models.Request, manager *Manager) {
+func handlePairingSubmit(conn *models.Conn, req models.Request, manager *Manager) {
token, err := params.String(req.Params, "token")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -190,7 +188,7 @@ func handlePairingSubmit(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "pairing response submitted"})
}
-func handlePairingCancel(conn net.Conn, req models.Request, manager *Manager) {
+func handlePairingCancel(conn *models.Conn, req models.Request, manager *Manager) {
token, err := params.String(req.Params, "token")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -205,7 +203,7 @@ func handlePairingCancel(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "pairing cancelled"})
}
-func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, manager *Manager) {
clientID := fmt.Sprintf("client-%p", conn)
stateChan := manager.Subscribe(clientID)
defer manager.Unsubscribe(clientID)
@@ -216,7 +214,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
Data: initialState,
}
- if err := json.NewEncoder(conn).Encode(models.Response[BluetoothEvent]{
+ if err := conn.WriteResponse(models.Response[BluetoothEvent]{
ID: req.ID,
Result: &event,
}); err != nil {
@@ -228,7 +226,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
Type: "state_changed",
Data: state,
}
- if err := json.NewEncoder(conn).Encode(models.Response[BluetoothEvent]{
+ if err := conn.WriteResponse(models.Response[BluetoothEvent]{
Result: &event,
}); err != nil {
return
diff --git a/core/internal/server/bluez/manager.go b/core/internal/server/bluez/manager.go
index 4408d2541..542895c5b 100644
--- a/core/internal/server/bluez/manager.go
+++ b/core/internal/server/bluez/manager.go
@@ -7,7 +7,7 @@ import (
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
+ "github.com/AvengeMedia/dankgo/dbusutil"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/bluez/subscription_broker.go b/core/internal/server/bluez/subscription_broker.go
index ea04a190d..e0d5873d9 100644
--- a/core/internal/server/bluez/subscription_broker.go
+++ b/core/internal/server/bluez/subscription_broker.go
@@ -5,7 +5,7 @@ import (
"fmt"
"github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
type SubscriptionBroker struct {
diff --git a/core/internal/server/bluez/types.go b/core/internal/server/bluez/types.go
index 663accaee..54d402fb4 100644
--- a/core/internal/server/bluez/types.go
+++ b/core/internal/server/bluez/types.go
@@ -3,7 +3,7 @@ package bluez
import (
"sync"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/brightness/handlers.go b/core/internal/server/brightness/handlers.go
index c71163ee8..23fbcc6a1 100644
--- a/core/internal/server/brightness/handlers.go
+++ b/core/internal/server/brightness/handlers.go
@@ -1,15 +1,13 @@
package brightness
import (
- "encoding/json"
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
-func HandleRequest(conn net.Conn, req models.Request, m *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, m *Manager) {
switch req.Method {
case "brightness.getState":
handleGetState(conn, req, m)
@@ -28,11 +26,11 @@ func HandleRequest(conn net.Conn, req models.Request, m *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, m *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, m.GetState())
}
-func handleSetBrightness(conn net.Conn, req models.Request, m *Manager) {
+func handleSetBrightness(conn *models.Conn, req models.Request, m *Manager) {
device, err := params.String(req.Params, "device")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -56,7 +54,7 @@ func handleSetBrightness(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, m.GetState())
}
-func handleIncrement(conn net.Conn, req models.Request, m *Manager) {
+func handleIncrement(conn *models.Conn, req models.Request, m *Manager) {
device, err := params.String(req.Params, "device")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -75,7 +73,7 @@ func handleIncrement(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, m.GetState())
}
-func handleDecrement(conn net.Conn, req models.Request, m *Manager) {
+func handleDecrement(conn *models.Conn, req models.Request, m *Manager) {
device, err := params.String(req.Params, "device")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -94,19 +92,19 @@ func handleDecrement(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, m.GetState())
}
-func handleRescan(conn net.Conn, req models.Request, m *Manager) {
+func handleRescan(conn *models.Conn, req models.Request, m *Manager) {
m.Rescan()
models.Respond(conn, req.ID, m.GetState())
}
-func handleSubscribe(conn net.Conn, req models.Request, m *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, m *Manager) {
clientID := fmt.Sprintf("brightness-%d", req.ID)
ch := m.Subscribe(clientID)
defer m.Unsubscribe(clientID)
initialState := m.GetState()
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
ID: req.ID,
Result: &initialState,
}); err != nil {
@@ -114,7 +112,7 @@ func handleSubscribe(conn net.Conn, req models.Request, m *Manager) {
}
for state := range ch {
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
ID: req.ID,
Result: &state,
}); err != nil {
diff --git a/core/internal/server/brightness/types.go b/core/internal/server/brightness/types.go
index 81e00cfe1..0eb2c8307 100644
--- a/core/internal/server/brightness/types.go
+++ b/core/internal/server/brightness/types.go
@@ -4,7 +4,7 @@ import (
"sync"
"time"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
type DeviceClass string
diff --git a/core/internal/server/browser/handlers.go b/core/internal/server/browser/handlers.go
index 0b4159ae3..9eab4181e 100644
--- a/core/internal/server/browser/handlers.go
+++ b/core/internal/server/browser/handlers.go
@@ -1,12 +1,10 @@
package browser
import (
- "net"
-
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
switch req.Method {
case "browser.open":
url, ok := models.Get[string](req, "url")
diff --git a/core/internal/server/browser/manager.go b/core/internal/server/browser/manager.go
index 5306759b2..5aecfbade 100644
--- a/core/internal/server/browser/manager.go
+++ b/core/internal/server/browser/manager.go
@@ -3,7 +3,7 @@ package browser
import (
"sync"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
type Manager struct {
diff --git a/core/internal/server/clipboard/handlers.go b/core/internal/server/clipboard/handlers.go
index ac993094e..cac17aa16 100644
--- a/core/internal/server/clipboard/handlers.go
+++ b/core/internal/server/clipboard/handlers.go
@@ -1,17 +1,15 @@
package clipboard
import (
- "encoding/json"
"errors"
"fmt"
- "net"
clipboardstore "github.com/AvengeMedia/DankMaterialShell/core/internal/clipboard"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
-func HandleRequest(conn net.Conn, req models.Request, m *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, m *Manager) {
switch req.Method {
case "clipboard.getState":
handleGetState(conn, req, m)
@@ -58,11 +56,11 @@ func HandleRequest(conn net.Conn, req models.Request, m *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, m *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, m.GetState())
}
-func handleGetHistory(conn net.Conn, req models.Request, m *Manager) {
+func handleGetHistory(conn *models.Conn, req models.Request, m *Manager) {
history := m.GetHistory()
for i := range history {
history[i].Data = nil
@@ -70,7 +68,7 @@ func handleGetHistory(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, history)
}
-func handleGetEntry(conn net.Conn, req models.Request, m *Manager) {
+func handleGetEntry(conn *models.Conn, req models.Request, m *Manager) {
id, err := params.Int(req.Params, "id")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -90,7 +88,7 @@ func handleGetEntry(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, entry)
}
-func handleDeleteEntry(conn net.Conn, req models.Request, m *Manager) {
+func handleDeleteEntry(conn *models.Conn, req models.Request, m *Manager) {
id, err := params.Int(req.Params, "id")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -105,12 +103,12 @@ func handleDeleteEntry(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "entry deleted"})
}
-func handleClearHistory(conn net.Conn, req models.Request, m *Manager) {
+func handleClearHistory(conn *models.Conn, req models.Request, m *Manager) {
m.ClearHistory()
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "history cleared"})
}
-func handleCopy(conn net.Conn, req models.Request, m *Manager) {
+func handleCopy(conn *models.Conn, req models.Request, m *Manager) {
text, err := params.String(req.Params, "text")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -125,7 +123,7 @@ func handleCopy(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "copied to clipboard"})
}
-func handleCopyEntry(conn net.Conn, req models.Request, m *Manager) {
+func handleCopyEntry(conn *models.Conn, req models.Request, m *Manager) {
id, err := params.Int(req.Params, "id")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -173,7 +171,7 @@ func handleCopyEntry(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "copied to clipboard"})
}
-func handlePaste(conn net.Conn, req models.Request, m *Manager) {
+func handlePaste(conn *models.Conn, req models.Request, m *Manager) {
text, err := m.PasteText()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -183,7 +181,7 @@ func handlePaste(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, map[string]string{"text": text})
}
-func handleSendPaste(conn net.Conn, req models.Request) {
+func handleSendPaste(conn *models.Conn, req models.Request) {
shift, _ := models.Get[bool](req, "shift")
if err := clipboardstore.SendPasteKeystroke(shift); err != nil {
@@ -194,14 +192,14 @@ func handleSendPaste(conn net.Conn, req models.Request) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "paste sent"})
}
-func handleSubscribe(conn net.Conn, req models.Request, m *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, m *Manager) {
clientID := fmt.Sprintf("clipboard-%d", req.ID)
ch := m.Subscribe(clientID)
defer m.Unsubscribe(clientID)
initialState := m.GetState()
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
ID: req.ID,
Result: &initialState,
}); err != nil {
@@ -209,7 +207,7 @@ func handleSubscribe(conn net.Conn, req models.Request, m *Manager) {
}
for state := range ch {
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
ID: req.ID,
Result: &state,
}); err != nil {
@@ -218,7 +216,7 @@ func handleSubscribe(conn net.Conn, req models.Request, m *Manager) {
}
}
-func handleSearch(conn net.Conn, req models.Request, m *Manager) {
+func handleSearch(conn *models.Conn, req models.Request, m *Manager) {
p := SearchParams{
Query: params.StringOpt(req.Params, "query", ""),
MimeType: params.StringOpt(req.Params, "mimeType", ""),
@@ -241,11 +239,11 @@ func handleSearch(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, m.Search(p))
}
-func handleGetConfig(conn net.Conn, req models.Request, m *Manager) {
+func handleGetConfig(conn *models.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, m.GetConfig())
}
-func handleSetConfig(conn net.Conn, req models.Request, m *Manager) {
+func handleSetConfig(conn *models.Conn, req models.Request, m *Manager) {
cfg := m.GetConfig()
if v, ok := models.Get[float64](req, "maxHistory"); ok {
@@ -275,7 +273,7 @@ func handleSetConfig(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "config updated"})
}
-func handleStore(conn net.Conn, req models.Request, m *Manager) {
+func handleStore(conn *models.Conn, req models.Request, m *Manager) {
data, err := params.String(req.Params, "data")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -292,7 +290,7 @@ func handleStore(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "stored"})
}
-func handlePinEntry(conn net.Conn, req models.Request, m *Manager) {
+func handlePinEntry(conn *models.Conn, req models.Request, m *Manager) {
id, err := params.Int(req.Params, "id")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -307,7 +305,7 @@ func handlePinEntry(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "entry pinned"})
}
-func handleUnpinEntry(conn net.Conn, req models.Request, m *Manager) {
+func handleUnpinEntry(conn *models.Conn, req models.Request, m *Manager) {
id, err := params.Int(req.Params, "id")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -322,17 +320,17 @@ func handleUnpinEntry(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "entry unpinned"})
}
-func handleGetPinnedEntries(conn net.Conn, req models.Request, m *Manager) {
+func handleGetPinnedEntries(conn *models.Conn, req models.Request, m *Manager) {
pinned := m.GetPinnedEntries()
models.Respond(conn, req.ID, pinned)
}
-func handleGetPinnedCount(conn net.Conn, req models.Request, m *Manager) {
+func handleGetPinnedCount(conn *models.Conn, req models.Request, m *Manager) {
count := m.GetPinnedCount()
models.Respond(conn, req.ID, map[string]int{"count": count})
}
-func handleCopyFile(conn net.Conn, req models.Request, m *Manager) {
+func handleCopyFile(conn *models.Conn, req models.Request, m *Manager) {
filePath, err := params.String(req.Params, "filePath")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
diff --git a/core/internal/server/clipboard/manager_test.go b/core/internal/server/clipboard/manager_test.go
index a0a3c703f..9a9f64843 100644
--- a/core/internal/server/clipboard/manager_test.go
+++ b/core/internal/server/clipboard/manager_test.go
@@ -311,14 +311,15 @@ func TestHandleGetEntry_ReturnsExistingEntry(t *testing.T) {
history := m.GetHistory()
require.Len(t, history, 1)
- conn := newClipboardTestConn()
+ mc := newClipboardTestConn()
+ conn := models.NewConn(mc)
handleGetEntry(conn, models.Request{
ID: 1,
Params: map[string]any{"id": float64(history[0].ID)},
}, m)
var resp models.Response[Entry]
- require.NoError(t, json.NewDecoder(conn.writeBuf).Decode(&resp))
+ require.NoError(t, json.NewDecoder(mc.writeBuf).Decode(&resp))
assert.Empty(t, resp.Error)
require.NotNil(t, resp.Result)
assert.Equal(t, history[0].ID, resp.Result.ID)
@@ -327,7 +328,8 @@ func TestHandleGetEntry_ReturnsExistingEntry(t *testing.T) {
func TestHandleGetEntry_MissingIDReturnsNullResult(t *testing.T) {
m := newTestManagerWithDB(t)
- conn := newClipboardTestConn()
+ mc := newClipboardTestConn()
+ conn := models.NewConn(mc)
handleGetEntry(conn, models.Request{
ID: 1,
@@ -335,7 +337,7 @@ func TestHandleGetEntry_MissingIDReturnsNullResult(t *testing.T) {
}, m)
var resp models.Response[any]
- require.NoError(t, json.NewDecoder(conn.writeBuf).Decode(&resp))
+ require.NoError(t, json.NewDecoder(mc.writeBuf).Decode(&resp))
assert.Empty(t, resp.Error)
assert.Nil(t, resp.Result)
}
diff --git a/core/internal/server/cups/handlers.go b/core/internal/server/cups/handlers.go
index 57b45d55f..46e446307 100644
--- a/core/internal/server/cups/handlers.go
+++ b/core/internal/server/cups/handlers.go
@@ -1,12 +1,10 @@
package cups
import (
- "encoding/json"
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
type CUPSEvent struct {
@@ -20,7 +18,7 @@ type TestPageResult struct {
Message string `json:"message"`
}
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
switch req.Method {
case "cups.subscribe":
handleSubscribe(conn, req, manager)
@@ -77,7 +75,7 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetPrinters(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetPrinters(conn *models.Conn, req models.Request, manager *Manager) {
printers, err := manager.GetPrinters()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -86,7 +84,7 @@ func handleGetPrinters(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, printers)
}
-func handleGetJobs(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetJobs(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.String(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -101,7 +99,7 @@ func handleGetJobs(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, jobs)
}
-func handlePausePrinter(conn net.Conn, req models.Request, manager *Manager) {
+func handlePausePrinter(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.String(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -115,7 +113,7 @@ func handlePausePrinter(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "paused"})
}
-func handleResumePrinter(conn net.Conn, req models.Request, manager *Manager) {
+func handleResumePrinter(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.String(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -129,7 +127,7 @@ func handleResumePrinter(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "resumed"})
}
-func handleCancelJob(conn net.Conn, req models.Request, manager *Manager) {
+func handleCancelJob(conn *models.Conn, req models.Request, manager *Manager) {
jobID, err := params.Int(req.Params, "jobID")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -143,7 +141,7 @@ func handleCancelJob(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "job canceled"})
}
-func handlePurgeJobs(conn net.Conn, req models.Request, manager *Manager) {
+func handlePurgeJobs(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.String(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -157,7 +155,7 @@ func handlePurgeJobs(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "jobs canceled"})
}
-func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, manager *Manager) {
clientID := fmt.Sprintf("client-%p", conn)
stateChan := manager.Subscribe(clientID)
defer manager.Unsubscribe(clientID)
@@ -168,7 +166,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
Data: initialState,
}
- if err := json.NewEncoder(conn).Encode(models.Response[CUPSEvent]{
+ if err := conn.WriteResponse(models.Response[CUPSEvent]{
ID: req.ID,
Result: &event,
}); err != nil {
@@ -180,7 +178,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
Type: "state_changed",
Data: state,
}
- if err := json.NewEncoder(conn).Encode(models.Response[CUPSEvent]{
+ if err := conn.WriteResponse(models.Response[CUPSEvent]{
Result: &event,
}); err != nil {
return
@@ -188,7 +186,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetDevices(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetDevices(conn *models.Conn, req models.Request, manager *Manager) {
devices, err := manager.GetDevices()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -197,7 +195,7 @@ func handleGetDevices(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, devices)
}
-func handleGetPPDs(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetPPDs(conn *models.Conn, req models.Request, manager *Manager) {
ppds, err := manager.GetPPDs()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -206,7 +204,7 @@ func handleGetPPDs(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, ppds)
}
-func handleGetClasses(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetClasses(conn *models.Conn, req models.Request, manager *Manager) {
classes, err := manager.GetClasses()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -215,7 +213,7 @@ func handleGetClasses(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, classes)
}
-func handleCreatePrinter(conn net.Conn, req models.Request, manager *Manager) {
+func handleCreatePrinter(conn *models.Conn, req models.Request, manager *Manager) {
name, err := params.StringNonEmpty(req.Params, "name")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -246,7 +244,7 @@ func handleCreatePrinter(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "printer created"})
}
-func handleDeletePrinter(conn net.Conn, req models.Request, manager *Manager) {
+func handleDeletePrinter(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.StringNonEmpty(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -260,7 +258,7 @@ func handleDeletePrinter(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "printer deleted"})
}
-func handleAcceptJobs(conn net.Conn, req models.Request, manager *Manager) {
+func handleAcceptJobs(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.StringNonEmpty(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -274,7 +272,7 @@ func handleAcceptJobs(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "accepting jobs"})
}
-func handleRejectJobs(conn net.Conn, req models.Request, manager *Manager) {
+func handleRejectJobs(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.StringNonEmpty(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -288,7 +286,7 @@ func handleRejectJobs(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "rejecting jobs"})
}
-func handleSetPrinterShared(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetPrinterShared(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.StringNonEmpty(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -308,7 +306,7 @@ func handleSetPrinterShared(conn net.Conn, req models.Request, manager *Manager)
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "sharing updated"})
}
-func handleSetPrinterLocation(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetPrinterLocation(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.StringNonEmpty(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -328,7 +326,7 @@ func handleSetPrinterLocation(conn net.Conn, req models.Request, manager *Manage
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "location updated"})
}
-func handleSetPrinterInfo(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetPrinterInfo(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.StringNonEmpty(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -348,7 +346,7 @@ func handleSetPrinterInfo(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "info updated"})
}
-func handleMoveJob(conn net.Conn, req models.Request, manager *Manager) {
+func handleMoveJob(conn *models.Conn, req models.Request, manager *Manager) {
jobID, err := params.Int(req.Params, "jobID")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -368,7 +366,7 @@ func handleMoveJob(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "job moved"})
}
-func handlePrintTestPage(conn net.Conn, req models.Request, manager *Manager) {
+func handlePrintTestPage(conn *models.Conn, req models.Request, manager *Manager) {
printerName, err := params.StringNonEmpty(req.Params, "printerName")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -383,7 +381,7 @@ func handlePrintTestPage(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, TestPageResult{Success: true, JobID: jobID, Message: "test page queued"})
}
-func handleAddPrinterToClass(conn net.Conn, req models.Request, manager *Manager) {
+func handleAddPrinterToClass(conn *models.Conn, req models.Request, manager *Manager) {
className, err := params.StringNonEmpty(req.Params, "className")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -403,7 +401,7 @@ func handleAddPrinterToClass(conn net.Conn, req models.Request, manager *Manager
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "printer added to class"})
}
-func handleRemovePrinterFromClass(conn net.Conn, req models.Request, manager *Manager) {
+func handleRemovePrinterFromClass(conn *models.Conn, req models.Request, manager *Manager) {
className, err := params.StringNonEmpty(req.Params, "className")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -423,7 +421,7 @@ func handleRemovePrinterFromClass(conn net.Conn, req models.Request, manager *Ma
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "printer removed from class"})
}
-func handleDeleteClass(conn net.Conn, req models.Request, manager *Manager) {
+func handleDeleteClass(conn *models.Conn, req models.Request, manager *Manager) {
className, err := params.StringNonEmpty(req.Params, "className")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -437,7 +435,7 @@ func handleDeleteClass(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "class deleted"})
}
-func handleRestartJob(conn net.Conn, req models.Request, manager *Manager) {
+func handleRestartJob(conn *models.Conn, req models.Request, manager *Manager) {
jobID, err := params.Int(req.Params, "jobID")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -451,7 +449,7 @@ func handleRestartJob(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "job restarted"})
}
-func handleHoldJob(conn net.Conn, req models.Request, manager *Manager) {
+func handleHoldJob(conn *models.Conn, req models.Request, manager *Manager) {
jobID, err := params.Int(req.Params, "jobID")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -467,7 +465,7 @@ func handleHoldJob(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "job held"})
}
-func handleTestConnection(conn net.Conn, req models.Request, manager *Manager) {
+func handleTestConnection(conn *models.Conn, req models.Request, manager *Manager) {
host, err := params.StringNonEmpty(req.Params, "host")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
diff --git a/core/internal/server/cups/handlers_test.go b/core/internal/server/cups/handlers_test.go
index b73931e3e..790d78476 100644
--- a/core/internal/server/cups/handlers_test.go
+++ b/core/internal/server/cups/handlers_test.go
@@ -41,7 +41,7 @@ func TestHandleGetPrinters(t *testing.T) {
}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -66,7 +66,7 @@ func TestHandleGetPrinters_Error(t *testing.T) {
}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -98,7 +98,7 @@ func TestHandleGetJobs(t *testing.T) {
}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -125,7 +125,7 @@ func TestHandleGetJobs_MissingParam(t *testing.T) {
}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -150,7 +150,7 @@ func TestHandlePausePrinter(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -177,7 +177,7 @@ func TestHandleResumePrinter(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -204,7 +204,7 @@ func TestHandleCancelJob(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -231,7 +231,7 @@ func TestHandlePurgeJobs(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -258,7 +258,7 @@ func TestHandleRequest_UnknownMethod(t *testing.T) {
}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -285,7 +285,7 @@ func TestHandleGetDevices(t *testing.T) {
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{ID: 1, Method: "cups.getDevices"}
handleGetDevices(conn, req, m)
@@ -307,7 +307,7 @@ func TestHandleGetPPDs(t *testing.T) {
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{ID: 1, Method: "cups.getPPDs"}
handleGetPPDs(conn, req, m)
@@ -330,7 +330,7 @@ func TestHandleGetClasses(t *testing.T) {
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{ID: 1, Method: "cups.getClasses"}
handleGetClasses(conn, req, m)
@@ -351,7 +351,7 @@ func TestHandleCreatePrinter(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -375,7 +375,7 @@ func TestHandleCreatePrinter_MissingParams(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{ID: 1, Method: "cups.createPrinter", Params: map[string]any{}}
handleCreatePrinter(conn, req, m)
@@ -394,7 +394,7 @@ func TestHandleDeletePrinter(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -417,7 +417,7 @@ func TestHandleAcceptJobs(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -440,7 +440,7 @@ func TestHandleRejectJobs(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -463,7 +463,7 @@ func TestHandleSetPrinterShared(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -486,7 +486,7 @@ func TestHandleSetPrinterLocation(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -509,7 +509,7 @@ func TestHandleSetPrinterInfo(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -532,7 +532,7 @@ func TestHandleMoveJob(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -555,7 +555,7 @@ func TestHandlePrintTestPage(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -579,7 +579,7 @@ func TestHandleAddPrinterToClass(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -602,7 +602,7 @@ func TestHandleRemovePrinterFromClass(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -625,7 +625,7 @@ func TestHandleDeleteClass(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -648,7 +648,7 @@ func TestHandleRestartJob(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -671,7 +671,7 @@ func TestHandleHoldJob(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -694,7 +694,7 @@ func TestHandleHoldJob_WithHoldUntil(t *testing.T) {
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
diff --git a/core/internal/server/cups/test_connection_test.go b/core/internal/server/cups/test_connection_test.go
index b8350281b..4e1408931 100644
--- a/core/internal/server/cups/test_connection_test.go
+++ b/core/internal/server/cups/test_connection_test.go
@@ -302,7 +302,7 @@ func TestHandleTestConnection_Success(t *testing.T) {
}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -325,7 +325,7 @@ func TestHandleTestConnection_Success(t *testing.T) {
func TestHandleTestConnection_MissingHost(t *testing.T) {
m := NewTestManager(nil, nil)
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -351,7 +351,7 @@ func TestHandleTestConnection_CustomPortAndProtocol(t *testing.T) {
}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
@@ -379,7 +379,7 @@ func TestHandleRequest_TestConnection(t *testing.T) {
}
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{
ID: 1,
diff --git a/core/internal/server/cups/types.go b/core/internal/server/cups/types.go
index 52e9d2cda..52f0f5b9e 100644
--- a/core/internal/server/cups/types.go
+++ b/core/internal/server/cups/types.go
@@ -6,7 +6,7 @@ import (
"time"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
type CUPSState struct {
diff --git a/core/internal/server/dbus/handlers.go b/core/internal/server/dbus/handlers.go
index cab6ade12..68df1377b 100644
--- a/core/internal/server/dbus/handlers.go
+++ b/core/internal/server/dbus/handlers.go
@@ -2,10 +2,9 @@ package dbus
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
type objectParams struct {
@@ -43,7 +42,7 @@ func extractObjectParams(p map[string]any, requirePath bool) (objectParams, erro
return objectParams{bus: bus, dest: dest, path: path, iface: iface}, nil
}
-func HandleRequest(conn net.Conn, req models.Request, m *Manager, clientID string) {
+func HandleRequest(conn *models.Conn, req models.Request, m *Manager, clientID string) {
switch req.Method {
case "dbus.call":
handleCall(conn, req, m)
@@ -66,7 +65,7 @@ func HandleRequest(conn net.Conn, req models.Request, m *Manager, clientID strin
}
}
-func handleCall(conn net.Conn, req models.Request, m *Manager) {
+func handleCall(conn *models.Conn, req models.Request, m *Manager) {
op, err := extractObjectParams(req.Params, true)
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -95,7 +94,7 @@ func handleCall(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, result)
}
-func handleGetProperty(conn net.Conn, req models.Request, m *Manager) {
+func handleGetProperty(conn *models.Conn, req models.Request, m *Manager) {
op, err := extractObjectParams(req.Params, true)
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -117,7 +116,7 @@ func handleGetProperty(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, result)
}
-func handleSetProperty(conn net.Conn, req models.Request, m *Manager) {
+func handleSetProperty(conn *models.Conn, req models.Request, m *Manager) {
op, err := extractObjectParams(req.Params, true)
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -144,7 +143,7 @@ func handleSetProperty(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true})
}
-func handleGetAllProperties(conn net.Conn, req models.Request, m *Manager) {
+func handleGetAllProperties(conn *models.Conn, req models.Request, m *Manager) {
op, err := extractObjectParams(req.Params, true)
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -160,7 +159,7 @@ func handleGetAllProperties(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, result)
}
-func handleIntrospect(conn net.Conn, req models.Request, m *Manager) {
+func handleIntrospect(conn *models.Conn, req models.Request, m *Manager) {
bus, err := params.String(req.Params, "bus")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -184,7 +183,7 @@ func handleIntrospect(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, result)
}
-func handleListNames(conn net.Conn, req models.Request, m *Manager) {
+func handleListNames(conn *models.Conn, req models.Request, m *Manager) {
bus, err := params.String(req.Params, "bus")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -200,7 +199,7 @@ func handleListNames(conn net.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, result)
}
-func handleSubscribe(conn net.Conn, req models.Request, m *Manager, clientID string) {
+func handleSubscribe(conn *models.Conn, req models.Request, m *Manager, clientID string) {
if id := params.StringOpt(req.Params, "clientId", ""); id != "" {
clientID = id
}
@@ -225,7 +224,7 @@ func handleSubscribe(conn net.Conn, req models.Request, m *Manager, clientID str
models.Respond(conn, req.ID, result)
}
-func handleUnsubscribe(conn net.Conn, req models.Request, m *Manager) {
+func handleUnsubscribe(conn *models.Conn, req models.Request, m *Manager) {
subID, err := params.String(req.Params, "subscriptionId")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
diff --git a/core/internal/server/dbus/manager.go b/core/internal/server/dbus/manager.go
index 86da6bda1..994789d9d 100644
--- a/core/internal/server/dbus/manager.go
+++ b/core/internal/server/dbus/manager.go
@@ -7,7 +7,7 @@ import (
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
+ "github.com/AvengeMedia/dankgo/dbusutil"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/dbus/types.go b/core/internal/server/dbus/types.go
index 55c45bf4b..dcac8a239 100644
--- a/core/internal/server/dbus/types.go
+++ b/core/internal/server/dbus/types.go
@@ -1,7 +1,7 @@
package dbus
import (
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/evdev/handlers.go b/core/internal/server/evdev/handlers.go
index 54a7bca23..0281704fa 100644
--- a/core/internal/server/evdev/handlers.go
+++ b/core/internal/server/evdev/handlers.go
@@ -1,12 +1,10 @@
package evdev
import (
- "net"
-
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleRequest(conn net.Conn, req models.Request, m *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, m *Manager) {
switch req.Method {
case "evdev.getState":
handleGetState(conn, req, m)
@@ -15,6 +13,6 @@ func HandleRequest(conn net.Conn, req models.Request, m *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, m *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, m *Manager) {
models.Respond(conn, req.ID, m.GetState())
}
diff --git a/core/internal/server/evdev/handlers_test.go b/core/internal/server/evdev/handlers_test.go
index f9bd21db9..612777dd5 100644
--- a/core/internal/server/evdev/handlers_test.go
+++ b/core/internal/server/evdev/handlers_test.go
@@ -52,7 +52,8 @@ func TestHandleRequest(t *testing.T) {
closeChan: make(chan struct{}),
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "evdev.getState",
@@ -62,7 +63,7 @@ func TestHandleRequest(t *testing.T) {
HandleRequest(conn, req, m)
var resp models.Response[State]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -81,7 +82,8 @@ func TestHandleRequest(t *testing.T) {
closeChan: make(chan struct{}),
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 456,
Method: "evdev.unknownMethod",
@@ -91,7 +93,7 @@ func TestHandleRequest(t *testing.T) {
HandleRequest(conn, req, m)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 456, resp.ID)
@@ -110,7 +112,8 @@ func TestHandleGetState(t *testing.T) {
closeChan: make(chan struct{}),
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 789,
Method: "evdev.getState",
@@ -120,7 +123,7 @@ func TestHandleGetState(t *testing.T) {
handleGetState(conn, req, m)
var resp models.Response[State]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 789, resp.ID)
diff --git a/core/internal/server/evdev/manager.go b/core/internal/server/evdev/manager.go
index 324907237..af447bd22 100644
--- a/core/internal/server/evdev/manager.go
+++ b/core/internal/server/evdev/manager.go
@@ -9,7 +9,7 @@ import (
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
"github.com/fsnotify/fsnotify"
evdev "github.com/holoplot/go-evdev"
)
diff --git a/core/internal/server/freedesktop/handlers.go b/core/internal/server/freedesktop/handlers.go
index c3090fa22..cf322abed 100644
--- a/core/internal/server/freedesktop/handlers.go
+++ b/core/internal/server/freedesktop/handlers.go
@@ -2,13 +2,12 @@ package freedesktop
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
switch req.Method {
case "freedesktop.getState":
handleGetState(conn, req, manager)
@@ -33,11 +32,11 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
-func handleSetIconFile(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetIconFile(conn *models.Conn, req models.Request, manager *Manager) {
iconPath, err := params.String(req.Params, "path")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -52,7 +51,7 @@ func handleSetIconFile(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "icon file set"})
}
-func handleSetRealName(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetRealName(conn *models.Conn, req models.Request, manager *Manager) {
name, err := params.String(req.Params, "name")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -67,7 +66,7 @@ func handleSetRealName(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "real name set"})
}
-func handleSetEmail(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetEmail(conn *models.Conn, req models.Request, manager *Manager) {
email, err := params.String(req.Params, "email")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -82,7 +81,7 @@ func handleSetEmail(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "email set"})
}
-func handleSetLanguage(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetLanguage(conn *models.Conn, req models.Request, manager *Manager) {
language, err := params.String(req.Params, "language")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -97,7 +96,7 @@ func handleSetLanguage(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "language set"})
}
-func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetLocation(conn *models.Conn, req models.Request, manager *Manager) {
location, err := params.String(req.Params, "location")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -112,7 +111,7 @@ func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "location set"})
}
-func handleGetUserIconFile(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetUserIconFile(conn *models.Conn, req models.Request, manager *Manager) {
username, err := params.String(req.Params, "username")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -128,7 +127,7 @@ func handleGetUserIconFile(conn net.Conn, req models.Request, manager *Manager)
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Value: iconFile})
}
-func handleGetColorScheme(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetColorScheme(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.updateSettingsState(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -138,7 +137,7 @@ func handleGetColorScheme(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, map[string]uint32{"colorScheme": state.Settings.ColorScheme})
}
-func handleSetIconTheme(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetIconTheme(conn *models.Conn, req models.Request, manager *Manager) {
iconTheme, err := params.String(req.Params, "iconTheme")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
diff --git a/core/internal/server/freedesktop/handlers_test.go b/core/internal/server/freedesktop/handlers_test.go
index f2cb5a2ed..aa5bbd768 100644
--- a/core/internal/server/freedesktop/handlers_test.go
+++ b/core/internal/server/freedesktop/handlers_test.go
@@ -60,11 +60,12 @@ func mockGetAllAccountsProperties() *dbus.Call {
}
func TestRespondError_Freedesktop(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
models.RespondError(conn, 123, "test error")
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -73,12 +74,13 @@ func TestRespondError_Freedesktop(t *testing.T) {
}
func TestRespond_Freedesktop(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
result := models.SuccessResult{Success: true, Message: "test"}
models.Respond(conn, 123, result)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -105,13 +107,14 @@ func TestHandleGetState(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "freedesktop.getState"}
handleGetState(conn, req, manager)
var resp models.Response[FreedeskState]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -130,7 +133,8 @@ func TestHandleSetIconFile(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.setIconFile",
@@ -140,7 +144,7 @@ func TestHandleSetIconFile(t *testing.T) {
handleSetIconFile(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -163,7 +167,8 @@ func TestHandleSetIconFile(t *testing.T) {
accountsObj: mockAccountsObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.setIconFile",
@@ -175,7 +180,7 @@ func TestHandleSetIconFile(t *testing.T) {
handleSetIconFile(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -195,7 +200,8 @@ func TestHandleSetIconFile(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.setIconFile",
@@ -207,7 +213,7 @@ func TestHandleSetIconFile(t *testing.T) {
handleSetIconFile(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -222,7 +228,8 @@ func TestHandleSetRealName(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.setRealName",
@@ -232,7 +239,7 @@ func TestHandleSetRealName(t *testing.T) {
handleSetRealName(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -255,7 +262,8 @@ func TestHandleSetRealName(t *testing.T) {
accountsObj: mockAccountsObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.setRealName",
@@ -267,7 +275,7 @@ func TestHandleSetRealName(t *testing.T) {
handleSetRealName(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -285,7 +293,8 @@ func TestHandleSetEmail(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.setEmail",
@@ -295,7 +304,7 @@ func TestHandleSetEmail(t *testing.T) {
handleSetEmail(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -318,7 +327,8 @@ func TestHandleSetEmail(t *testing.T) {
accountsObj: mockAccountsObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.setEmail",
@@ -330,7 +340,7 @@ func TestHandleSetEmail(t *testing.T) {
handleSetEmail(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -348,7 +358,8 @@ func TestHandleSetLanguage(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.setLanguage",
@@ -358,7 +369,7 @@ func TestHandleSetLanguage(t *testing.T) {
handleSetLanguage(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -373,7 +384,8 @@ func TestHandleSetLocation(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.setLocation",
@@ -383,7 +395,7 @@ func TestHandleSetLocation(t *testing.T) {
handleSetLocation(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -398,7 +410,8 @@ func TestHandleGetUserIconFile(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.getUserIconFile",
@@ -408,7 +421,7 @@ func TestHandleGetUserIconFile(t *testing.T) {
handleGetUserIconFile(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -425,7 +438,8 @@ func TestHandleGetUserIconFile(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.accounts.getUserIconFile",
@@ -437,7 +451,7 @@ func TestHandleGetUserIconFile(t *testing.T) {
handleGetUserIconFile(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -456,13 +470,14 @@ func TestHandleGetColorScheme(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "freedesktop.settings.getColorScheme"}
handleGetColorScheme(conn, req, manager)
var resp models.Response[map[string]uint32]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -487,13 +502,14 @@ func TestHandleGetColorScheme(t *testing.T) {
settingsObj: mockSettingsObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "freedesktop.settings.getColorScheme"}
handleGetColorScheme(conn, req, manager)
var resp models.Response[map[string]uint32]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -515,7 +531,8 @@ func TestHandleRequest(t *testing.T) {
}
t.Run("unknown method", func(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.unknown",
@@ -524,7 +541,7 @@ func TestHandleRequest(t *testing.T) {
HandleRequest(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -532,7 +549,8 @@ func TestHandleRequest(t *testing.T) {
})
t.Run("valid method - getState", func(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "freedesktop.getState",
@@ -541,7 +559,7 @@ func TestHandleRequest(t *testing.T) {
HandleRequest(conn, req, manager)
var resp models.Response[FreedeskState]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -560,7 +578,8 @@ func TestHandleRequest(t *testing.T) {
}
for _, method := range tests {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: method,
@@ -570,7 +589,7 @@ func TestHandleRequest(t *testing.T) {
HandleRequest(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
diff --git a/core/internal/server/freedesktop/manager.go b/core/internal/server/freedesktop/manager.go
index 510c78cb2..e8e281505 100644
--- a/core/internal/server/freedesktop/manager.go
+++ b/core/internal/server/freedesktop/manager.go
@@ -8,7 +8,7 @@ import (
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
+ "github.com/AvengeMedia/dankgo/dbusutil"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/freedesktop/types.go b/core/internal/server/freedesktop/types.go
index c68ff92a5..47a350424 100644
--- a/core/internal/server/freedesktop/types.go
+++ b/core/internal/server/freedesktop/types.go
@@ -4,7 +4,7 @@ import (
"sync"
"time"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/location/handlers.go b/core/internal/server/location/handlers.go
index c9d556c42..984d6d41f 100644
--- a/core/internal/server/location/handlers.go
+++ b/core/internal/server/location/handlers.go
@@ -1,9 +1,7 @@
package location
import (
- "encoding/json"
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
@@ -13,7 +11,7 @@ type LocationEvent struct {
Data State `json:"data"`
}
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
switch req.Method {
case "location.getState":
handleGetState(conn, req, manager)
@@ -25,11 +23,11 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
-func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, manager *Manager) {
clientID := fmt.Sprintf("client-%p", conn)
stateChan := manager.Subscribe(clientID)
defer manager.Unsubscribe(clientID)
@@ -40,7 +38,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
Data: initialState,
}
- if err := json.NewEncoder(conn).Encode(models.Response[LocationEvent]{
+ if err := conn.WriteResponse(models.Response[LocationEvent]{
ID: req.ID,
Result: &event,
}); err != nil {
@@ -52,7 +50,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
Type: "state_changed",
Data: state,
}
- if err := json.NewEncoder(conn).Encode(models.Response[LocationEvent]{
+ if err := conn.WriteResponse(models.Response[LocationEvent]{
Result: &event,
}); err != nil {
return
diff --git a/core/internal/server/location/types.go b/core/internal/server/location/types.go
index 1f3d5180b..f18198738 100644
--- a/core/internal/server/location/types.go
+++ b/core/internal/server/location/types.go
@@ -4,7 +4,7 @@ import (
"sync"
"github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
type State struct {
diff --git a/core/internal/server/loginctl/handlers.go b/core/internal/server/loginctl/handlers.go
index ded5ed311..3d8db154c 100644
--- a/core/internal/server/loginctl/handlers.go
+++ b/core/internal/server/loginctl/handlers.go
@@ -1,15 +1,13 @@
package loginctl
import (
- "encoding/json"
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
switch req.Method {
case "loginctl.getState":
handleGetState(conn, req, manager)
@@ -38,11 +36,11 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
-func handleLock(conn net.Conn, req models.Request, manager *Manager) {
+func handleLock(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.Lock(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -50,7 +48,7 @@ func handleLock(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "locked"})
}
-func handleUnlock(conn net.Conn, req models.Request, manager *Manager) {
+func handleUnlock(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.Unlock(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -58,7 +56,7 @@ func handleUnlock(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "unlocked"})
}
-func handleActivate(conn net.Conn, req models.Request, manager *Manager) {
+func handleActivate(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.Activate(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -66,7 +64,7 @@ func handleActivate(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "activated"})
}
-func handleSetIdleHint(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetIdleHint(conn *models.Conn, req models.Request, manager *Manager) {
idle, err := params.Bool(req.Params, "idle")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -80,7 +78,7 @@ func handleSetIdleHint(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "idle hint set"})
}
-func handleSetLockedHint(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetLockedHint(conn *models.Conn, req models.Request, manager *Manager) {
locked, err := params.Bool(req.Params, "locked")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -94,7 +92,7 @@ func handleSetLockedHint(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "locked hint set"})
}
-func handleSetLockBeforeSuspend(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetLockBeforeSuspend(conn *models.Conn, req models.Request, manager *Manager) {
enabled, err := params.Bool(req.Params, "enabled")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -105,7 +103,7 @@ func handleSetLockBeforeSuspend(conn net.Conn, req models.Request, manager *Mana
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "lock before suspend set"})
}
-func handleSetSleepInhibitorEnabled(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetSleepInhibitorEnabled(conn *models.Conn, req models.Request, manager *Manager) {
enabled, err := params.Bool(req.Params, "enabled")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -116,7 +114,7 @@ func handleSetSleepInhibitorEnabled(conn net.Conn, req models.Request, manager *
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "sleep inhibitor setting updated"})
}
-func handleLockerReady(conn net.Conn, req models.Request, manager *Manager) {
+func handleLockerReady(conn *models.Conn, req models.Request, manager *Manager) {
manager.lockTimerMu.Lock()
if manager.lockTimer != nil {
manager.lockTimer.Stop()
@@ -133,7 +131,7 @@ func handleLockerReady(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "ok"})
}
-func handleTerminate(conn net.Conn, req models.Request, manager *Manager) {
+func handleTerminate(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.Terminate(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -141,7 +139,7 @@ func handleTerminate(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "terminated"})
}
-func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, manager *Manager) {
clientID := fmt.Sprintf("client-%p", conn)
stateChan := manager.Subscribe(clientID)
defer manager.Unsubscribe(clientID)
@@ -151,7 +149,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
Type: EventStateChanged,
Data: initialState,
}
- if err := json.NewEncoder(conn).Encode(models.Response[SessionEvent]{
+ if err := conn.WriteResponse(models.Response[SessionEvent]{
ID: req.ID,
Result: &event,
}); err != nil {
@@ -163,7 +161,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
Type: EventStateChanged,
Data: state,
}
- if err := json.NewEncoder(conn).Encode(models.Response[SessionEvent]{
+ if err := conn.WriteResponse(models.Response[SessionEvent]{
Result: &event,
}); err != nil {
return
diff --git a/core/internal/server/loginctl/handlers_test.go b/core/internal/server/loginctl/handlers_test.go
index d3169e48b..dfcb69329 100644
--- a/core/internal/server/loginctl/handlers_test.go
+++ b/core/internal/server/loginctl/handlers_test.go
@@ -44,11 +44,12 @@ func (m *mockNetConn) Close() error {
}
func TestRespondError_Loginctl(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
models.RespondError(conn, 123, "test error")
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -57,12 +58,13 @@ func TestRespondError_Loginctl(t *testing.T) {
}
func TestRespond_Loginctl(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
result := models.SuccessResult{Success: true, Message: "test"}
models.Respond(conn, 123, result)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -85,13 +87,14 @@ func TestHandleGetState(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "loginctl.getState"}
handleGetState(conn, req, manager)
var resp models.Response[SessionState]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -114,12 +117,13 @@ func TestHandleLock(t *testing.T) {
sessionObj: mockSessionObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "loginctl.lock"}
handleLock(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -140,12 +144,13 @@ func TestHandleLock(t *testing.T) {
sessionObj: mockSessionObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "loginctl.lock"}
handleLock(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -165,12 +170,13 @@ func TestHandleUnlock(t *testing.T) {
sessionObj: mockSessionObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "loginctl.unlock"}
handleUnlock(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -191,12 +197,13 @@ func TestHandleUnlock(t *testing.T) {
sessionObj: mockSessionObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "loginctl.unlock"}
handleUnlock(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -216,12 +223,13 @@ func TestHandleActivate(t *testing.T) {
sessionObj: mockSessionObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "loginctl.activate"}
handleActivate(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -242,12 +250,13 @@ func TestHandleActivate(t *testing.T) {
sessionObj: mockSessionObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "loginctl.activate"}
handleActivate(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -262,7 +271,8 @@ func TestHandleSetIdleHint(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "loginctl.setIdleHint",
@@ -272,7 +282,7 @@ func TestHandleSetIdleHint(t *testing.T) {
handleSetIdleHint(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -290,7 +300,8 @@ func TestHandleSetIdleHint(t *testing.T) {
sessionObj: mockSessionObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "loginctl.setIdleHint",
@@ -302,7 +313,7 @@ func TestHandleSetIdleHint(t *testing.T) {
handleSetIdleHint(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -323,7 +334,8 @@ func TestHandleSetIdleHint(t *testing.T) {
sessionObj: mockSessionObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "loginctl.setIdleHint",
@@ -335,7 +347,7 @@ func TestHandleSetIdleHint(t *testing.T) {
handleSetIdleHint(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -355,12 +367,13 @@ func TestHandleTerminate(t *testing.T) {
sessionObj: mockSessionObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "loginctl.terminate"}
handleTerminate(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -381,12 +394,13 @@ func TestHandleTerminate(t *testing.T) {
sessionObj: mockSessionObj,
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "loginctl.terminate"}
handleTerminate(conn, req, manager)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -404,7 +418,8 @@ func TestHandleRequest(t *testing.T) {
}
t.Run("unknown method", func(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "loginctl.unknown",
@@ -413,7 +428,7 @@ func TestHandleRequest(t *testing.T) {
HandleRequest(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -421,7 +436,8 @@ func TestHandleRequest(t *testing.T) {
})
t.Run("valid method - getState", func(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "loginctl.getState",
@@ -430,7 +446,7 @@ func TestHandleRequest(t *testing.T) {
HandleRequest(conn, req, manager)
var resp models.Response[SessionState]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -444,7 +460,8 @@ func TestHandleRequest(t *testing.T) {
manager.sessionObj = mockSessionObj
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "loginctl.lock",
@@ -453,7 +470,7 @@ func TestHandleRequest(t *testing.T) {
HandleRequest(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -469,7 +486,8 @@ func TestHandleSubscribe(t *testing.T) {
stateMutex: sync.RWMutex{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "loginctl.subscribe"}
done := make(chan bool)
@@ -480,11 +498,11 @@ func TestHandleSubscribe(t *testing.T) {
time.Sleep(50 * time.Millisecond)
- conn.Close()
+ mc.Close()
- if conn.writeBuf.Len() > 0 {
+ if mc.writeBuf.Len() > 0 {
var resp models.Response[SessionEvent]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
if err == nil {
assert.Equal(t, 123, resp.ID)
require.NotNil(t, resp.Result)
diff --git a/core/internal/server/loginctl/manager.go b/core/internal/server/loginctl/manager.go
index 0c51e6457..3ccd4ac40 100644
--- a/core/internal/server/loginctl/manager.go
+++ b/core/internal/server/loginctl/manager.go
@@ -7,7 +7,7 @@ import (
"sync"
"time"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
+ "github.com/AvengeMedia/dankgo/dbusutil"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/loginctl/monitor.go b/core/internal/server/loginctl/monitor.go
index 4c0605a0b..e10263903 100644
--- a/core/internal/server/loginctl/monitor.go
+++ b/core/internal/server/loginctl/monitor.go
@@ -3,7 +3,7 @@ package loginctl
import (
"time"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
+ "github.com/AvengeMedia/dankgo/dbusutil"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/loginctl/types.go b/core/internal/server/loginctl/types.go
index c3df1920b..eb5e36fba 100644
--- a/core/internal/server/loginctl/types.go
+++ b/core/internal/server/loginctl/types.go
@@ -6,7 +6,7 @@ import (
"sync/atomic"
"time"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/matugen_handler.go b/core/internal/server/matugen_handler.go
index 73c6ad610..0c84cd491 100644
--- a/core/internal/server/matugen_handler.go
+++ b/core/internal/server/matugen_handler.go
@@ -2,7 +2,6 @@ package server
import (
"context"
- "net"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/matugen"
@@ -14,7 +13,7 @@ type MatugenQueueResult struct {
Message string `json:"message,omitempty"`
}
-func handleMatugenQueue(conn net.Conn, req models.Request) {
+func handleMatugenQueue(conn *models.Conn, req models.Request) {
opts := matugen.Options{
StateDir: models.GetOr(req, "stateDir", ""),
ShellDir: models.GetOr(req, "shellDir", ""),
@@ -70,7 +69,7 @@ func handleMatugenQueue(conn net.Conn, req models.Request) {
}
}
-func handleMatugenStatus(conn net.Conn, req models.Request) {
+func handleMatugenStatus(conn *models.Conn, req models.Request) {
queue := matugen.GetQueue()
models.Respond(conn, req.ID, map[string]bool{
"running": queue.IsRunning(),
diff --git a/core/internal/server/mime/handlers.go b/core/internal/server/mime/handlers.go
index de4d6296c..4c58c51ed 100644
--- a/core/internal/server/mime/handlers.go
+++ b/core/internal/server/mime/handlers.go
@@ -2,11 +2,10 @@ package mime
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/desktop"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
type defaultResult struct {
@@ -23,7 +22,7 @@ type queryResult struct {
Defaults map[string]string `json:"defaults"`
}
-func HandleRequest(conn net.Conn, req models.Request) {
+func HandleRequest(conn *models.Conn, req models.Request) {
switch req.Method {
case "mime.getDefault":
handleGetDefault(conn, req)
@@ -43,7 +42,7 @@ func HandleRequest(conn net.Conn, req models.Request) {
}
}
-func handleGetDefault(conn net.Conn, req models.Request) {
+func handleGetDefault(conn *models.Conn, req models.Request) {
mimeType, err := mimeParam(req.Params, "mimeType")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -55,7 +54,7 @@ func handleGetDefault(conn net.Conn, req models.Request) {
})
}
-func handleSetDefault(conn net.Conn, req models.Request) {
+func handleSetDefault(conn *models.Conn, req models.Request) {
mimeType, err := mimeParam(req.Params, "mimeType")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -73,7 +72,7 @@ func handleSetDefault(conn net.Conn, req models.Request) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true})
}
-func handleSetDefaults(conn net.Conn, req models.Request) {
+func handleSetDefaults(conn *models.Conn, req models.Request) {
desktopID, err := params.StringNonEmpty(req.Params, "desktopId")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -91,7 +90,7 @@ func handleSetDefaults(conn net.Conn, req models.Request) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true})
}
-func handleAppsForMime(conn net.Conn, req models.Request) {
+func handleAppsForMime(conn *models.Conn, req models.Request) {
mimeType, err := mimeParam(req.Params, "mimeType")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -107,7 +106,7 @@ func handleAppsForMime(conn net.Conn, req models.Request) {
})
}
-func handleQueryDefaults(conn net.Conn, req models.Request) {
+func handleQueryDefaults(conn *models.Conn, req models.Request) {
mimeTypes, err := mimeListParam(req, "mimeTypes")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
diff --git a/core/internal/server/models/types.go b/core/internal/server/models/types.go
index 2933fb517..8449f4715 100644
--- a/core/internal/server/models/types.go
+++ b/core/internal/server/models/types.go
@@ -1,13 +1,17 @@
package models
import (
- "encoding/json"
"net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
+type Conn = ipc.ConnWriter
+
+func NewConn(c net.Conn) *Conn { return ipc.NewConnWriter(c) }
+
type Request struct {
ID int `json:"id,omitempty"`
Method string `json:"method"`
@@ -29,15 +33,13 @@ type Response[T any] struct {
Error string `json:"error,omitempty"`
}
-func RespondError(conn net.Conn, id int, errMsg string) {
+func RespondError(conn *Conn, id int, errMsg string) {
log.Errorf("DMS API Error: id=%d error=%s", id, errMsg)
- resp := Response[any]{ID: id, Error: errMsg}
- json.NewEncoder(conn).Encode(resp)
+ _ = conn.WriteResponse(Response[any]{ID: id, Error: errMsg})
}
-func Respond[T any](conn net.Conn, id int, result T) {
- resp := Response[T]{ID: id, Result: &result}
- json.NewEncoder(conn).Encode(resp)
+func Respond[T any](conn *Conn, id int, result T) {
+ _ = conn.WriteResponse(Response[T]{ID: id, Result: &result})
}
type SuccessResult struct {
diff --git a/core/internal/server/network/handlers.go b/core/internal/server/network/handlers.go
index 2c023d63e..d0b132061 100644
--- a/core/internal/server/network/handlers.go
+++ b/core/internal/server/network/handlers.go
@@ -1,17 +1,15 @@
package network
import (
- "encoding/json"
"fmt"
- "net"
"os"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
switch req.Method {
case "network.getState":
handleGetState(conn, req, manager)
@@ -86,7 +84,7 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleCredentialsSubmit(conn net.Conn, req models.Request, manager *Manager) {
+func handleCredentialsSubmit(conn *models.Conn, req models.Request, manager *Manager) {
token, err := params.String(req.Params, "token")
if err != nil {
log.Warnf("handleCredentialsSubmit: missing or invalid token parameter")
@@ -113,7 +111,7 @@ func handleCredentialsSubmit(conn net.Conn, req models.Request, manager *Manager
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "credentials submitted"})
}
-func handleCredentialsCancel(conn net.Conn, req models.Request, manager *Manager) {
+func handleCredentialsCancel(conn *models.Conn, req models.Request, manager *Manager) {
token, err := params.String(req.Params, "token")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -128,11 +126,11 @@ func handleCredentialsCancel(conn net.Conn, req models.Request, manager *Manager
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "credentials cancelled"})
}
-func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
-func handleScanWiFi(conn net.Conn, req models.Request, manager *Manager) {
+func handleScanWiFi(conn *models.Conn, req models.Request, manager *Manager) {
device := params.StringOpt(req.Params, "device", "")
var err error
if device != "" {
@@ -147,11 +145,11 @@ func handleScanWiFi(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "scanning"})
}
-func handleGetWiFiNetworks(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetWiFiNetworks(conn *models.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetWiFiNetworks())
}
-func handleConnectWiFi(conn net.Conn, req models.Request, manager *Manager) {
+func handleConnectWiFi(conn *models.Conn, req models.Request, manager *Manager) {
ssid, err := params.String(req.Params, "ssid")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -204,7 +202,7 @@ func handleConnectWiFi(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "connecting"})
}
-func handleDisconnectWiFi(conn net.Conn, req models.Request, manager *Manager) {
+func handleDisconnectWiFi(conn *models.Conn, req models.Request, manager *Manager) {
device := params.StringOpt(req.Params, "device", "")
var err error
if device != "" {
@@ -219,7 +217,7 @@ func handleDisconnectWiFi(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "disconnected"})
}
-func handleForgetWiFi(conn net.Conn, req models.Request, manager *Manager) {
+func handleForgetWiFi(conn *models.Conn, req models.Request, manager *Manager) {
ssid, err := params.String(req.Params, "ssid")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -234,7 +232,7 @@ func handleForgetWiFi(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "forgotten"})
}
-func handleToggleWiFi(conn net.Conn, req models.Request, manager *Manager) {
+func handleToggleWiFi(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.ToggleWiFi(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -244,7 +242,7 @@ func handleToggleWiFi(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, map[string]bool{"enabled": state.WiFiEnabled})
}
-func handleEnableWiFi(conn net.Conn, req models.Request, manager *Manager) {
+func handleEnableWiFi(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.EnableWiFi(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -252,7 +250,7 @@ func handleEnableWiFi(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, map[string]bool{"enabled": true})
}
-func handleDisableWiFi(conn net.Conn, req models.Request, manager *Manager) {
+func handleDisableWiFi(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.DisableWiFi(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -260,7 +258,7 @@ func handleDisableWiFi(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, map[string]bool{"enabled": false})
}
-func handleConnectEthernetSpecificConfig(conn net.Conn, req models.Request, manager *Manager) {
+func handleConnectEthernetSpecificConfig(conn *models.Conn, req models.Request, manager *Manager) {
uuid, err := params.String(req.Params, "uuid")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -273,7 +271,7 @@ func handleConnectEthernetSpecificConfig(conn net.Conn, req models.Request, mana
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "connecting"})
}
-func handleConnectEthernet(conn net.Conn, req models.Request, manager *Manager) {
+func handleConnectEthernet(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.ConnectEthernet(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -281,7 +279,7 @@ func handleConnectEthernet(conn net.Conn, req models.Request, manager *Manager)
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "connecting"})
}
-func handleDisconnectEthernet(conn net.Conn, req models.Request, manager *Manager) {
+func handleDisconnectEthernet(conn *models.Conn, req models.Request, manager *Manager) {
device := params.StringOpt(req.Params, "device", "")
var err error
if device != "" {
@@ -296,7 +294,7 @@ func handleDisconnectEthernet(conn net.Conn, req models.Request, manager *Manage
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "disconnected"})
}
-func handleSetPreference(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetPreference(conn *models.Conn, req models.Request, manager *Manager) {
preference, err := params.String(req.Params, "preference")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -311,7 +309,7 @@ func handleSetPreference(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, map[string]string{"preference": preference})
}
-func handleGetNetworkInfo(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetNetworkInfo(conn *models.Conn, req models.Request, manager *Manager) {
ssid, err := params.String(req.Params, "ssid")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -327,7 +325,7 @@ func handleGetNetworkInfo(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, network)
}
-func handleGetNetworkQRCode(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetNetworkQRCode(conn *models.Conn, req models.Request, manager *Manager) {
ssid, err := params.String(req.Params, "ssid")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -343,7 +341,7 @@ func handleGetNetworkQRCode(conn net.Conn, req models.Request, manager *Manager)
models.Respond(conn, req.ID, content)
}
-func handleGetNetworkQRCodeContent(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetNetworkQRCodeContent(conn *models.Conn, req models.Request, manager *Manager) {
ssid, err := params.String(req.Params, "ssid")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -359,7 +357,7 @@ func handleGetNetworkQRCodeContent(conn net.Conn, req models.Request, manager *M
models.Respond(conn, req.ID, content)
}
-func handleDeleteQRCode(conn net.Conn, req models.Request, _ *Manager) {
+func handleDeleteQRCode(conn *models.Conn, req models.Request, _ *Manager) {
path, err := params.String(req.Params, "path")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -379,7 +377,7 @@ func handleDeleteQRCode(conn net.Conn, req models.Request, _ *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "QR code file deleted"})
}
-func handleGetWiredNetworkInfo(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetWiredNetworkInfo(conn *models.Conn, req models.Request, manager *Manager) {
uuid, err := params.String(req.Params, "uuid")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -395,7 +393,7 @@ func handleGetWiredNetworkInfo(conn net.Conn, req models.Request, manager *Manag
models.Respond(conn, req.ID, network)
}
-func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, manager *Manager) {
clientID := fmt.Sprintf("client-%p", conn)
stateChan := manager.Subscribe(clientID)
defer manager.Unsubscribe(clientID)
@@ -405,7 +403,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
Type: EventStateChanged,
Data: initialState,
}
- if err := json.NewEncoder(conn).Encode(models.Response[NetworkEvent]{
+ if err := conn.WriteResponse(models.Response[NetworkEvent]{
ID: req.ID,
Result: &event,
}); err != nil {
@@ -417,7 +415,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
Type: EventStateChanged,
Data: state,
}
- if err := json.NewEncoder(conn).Encode(models.Response[NetworkEvent]{
+ if err := conn.WriteResponse(models.Response[NetworkEvent]{
Result: &event,
}); err != nil {
return
@@ -425,7 +423,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleListVPNProfiles(conn net.Conn, req models.Request, manager *Manager) {
+func handleListVPNProfiles(conn *models.Conn, req models.Request, manager *Manager) {
profiles, err := manager.ListVPNProfiles()
if err != nil {
log.Warnf("handleListVPNProfiles: failed to list profiles: %v", err)
@@ -436,7 +434,7 @@ func handleListVPNProfiles(conn net.Conn, req models.Request, manager *Manager)
models.Respond(conn, req.ID, profiles)
}
-func handleListActiveVPN(conn net.Conn, req models.Request, manager *Manager) {
+func handleListActiveVPN(conn *models.Conn, req models.Request, manager *Manager) {
active, err := manager.ListActiveVPN()
if err != nil {
log.Warnf("handleListActiveVPN: failed to list active VPNs: %v", err)
@@ -447,7 +445,7 @@ func handleListActiveVPN(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, active)
}
-func handleConnectVPN(conn net.Conn, req models.Request, manager *Manager) {
+func handleConnectVPN(conn *models.Conn, req models.Request, manager *Manager) {
uuidOrName, ok := params.StringAlt(req.Params, "uuidOrName", "name", "uuid")
if !ok {
log.Warnf("handleConnectVPN: missing uuidOrName/name/uuid parameter")
@@ -466,7 +464,7 @@ func handleConnectVPN(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN connection initiated"})
}
-func handleDisconnectVPN(conn net.Conn, req models.Request, manager *Manager) {
+func handleDisconnectVPN(conn *models.Conn, req models.Request, manager *Manager) {
uuidOrName, ok := params.StringAlt(req.Params, "uuidOrName", "name", "uuid")
if !ok {
log.Warnf("handleDisconnectVPN: missing uuidOrName/name/uuid parameter")
@@ -483,7 +481,7 @@ func handleDisconnectVPN(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN disconnected"})
}
-func handleDisconnectAllVPN(conn net.Conn, req models.Request, manager *Manager) {
+func handleDisconnectAllVPN(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.DisconnectAllVPN(); err != nil {
log.Warnf("handleDisconnectAllVPN: failed: %v", err)
models.RespondError(conn, req.ID, fmt.Sprintf("failed to disconnect all VPNs: %v", err))
@@ -493,7 +491,7 @@ func handleDisconnectAllVPN(conn net.Conn, req models.Request, manager *Manager)
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "All VPNs disconnected"})
}
-func handleClearVPNCredentials(conn net.Conn, req models.Request, manager *Manager) {
+func handleClearVPNCredentials(conn *models.Conn, req models.Request, manager *Manager) {
uuidOrName, ok := params.StringAlt(req.Params, "uuid", "name", "uuidOrName")
if !ok {
log.Warnf("handleClearVPNCredentials: missing uuidOrName/name/uuid parameter")
@@ -510,7 +508,7 @@ func handleClearVPNCredentials(conn net.Conn, req models.Request, manager *Manag
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN credentials cleared"})
}
-func handleSetWiFiAutoconnect(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetWiFiAutoconnect(conn *models.Conn, req models.Request, manager *Manager) {
ssid, err := params.String(req.Params, "ssid")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -531,7 +529,7 @@ func handleSetWiFiAutoconnect(conn net.Conn, req models.Request, manager *Manage
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "autoconnect updated"})
}
-func handleListVPNPlugins(conn net.Conn, req models.Request, manager *Manager) {
+func handleListVPNPlugins(conn *models.Conn, req models.Request, manager *Manager) {
plugins, err := manager.ListVPNPlugins()
if err != nil {
log.Warnf("handleListVPNPlugins: failed to list plugins: %v", err)
@@ -542,7 +540,7 @@ func handleListVPNPlugins(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, plugins)
}
-func handleImportVPN(conn net.Conn, req models.Request, manager *Manager) {
+func handleImportVPN(conn *models.Conn, req models.Request, manager *Manager) {
filePath, ok := params.StringAlt(req.Params, "file", "path")
if !ok {
models.RespondError(conn, req.ID, "missing 'file' or 'path' parameter")
@@ -561,7 +559,7 @@ func handleImportVPN(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, result)
}
-func handleGetVPNConfig(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetVPNConfig(conn *models.Conn, req models.Request, manager *Manager) {
uuidOrName, ok := params.StringAlt(req.Params, "uuid", "name", "uuidOrName")
if !ok {
models.RespondError(conn, req.ID, "missing 'uuid', 'name', or 'uuidOrName' parameter")
@@ -578,7 +576,7 @@ func handleGetVPNConfig(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, config)
}
-func handleUpdateVPNConfig(conn net.Conn, req models.Request, manager *Manager) {
+func handleUpdateVPNConfig(conn *models.Conn, req models.Request, manager *Manager) {
connUUID, err := params.String(req.Params, "uuid")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -611,7 +609,7 @@ func handleUpdateVPNConfig(conn net.Conn, req models.Request, manager *Manager)
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN config updated"})
}
-func handleDeleteVPN(conn net.Conn, req models.Request, manager *Manager) {
+func handleDeleteVPN(conn *models.Conn, req models.Request, manager *Manager) {
uuidOrName, ok := params.StringAlt(req.Params, "uuid", "name", "uuidOrName")
if !ok {
models.RespondError(conn, req.ID, "missing 'uuid', 'name', or 'uuidOrName' parameter")
@@ -627,7 +625,7 @@ func handleDeleteVPN(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN deleted"})
}
-func handleSetVPNCredentials(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetVPNCredentials(conn *models.Conn, req models.Request, manager *Manager) {
connUUID, err := params.String(req.Params, "uuid")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
diff --git a/core/internal/server/network/handlers_test.go b/core/internal/server/network/handlers_test.go
index ea8ae3335..092ccf9cf 100644
--- a/core/internal/server/network/handlers_test.go
+++ b/core/internal/server/network/handlers_test.go
@@ -39,11 +39,12 @@ func (m *mockNetConn) Close() error {
}
func TestRespondError_Network(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
models.RespondError(conn, 123, "test error")
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -52,12 +53,13 @@ func TestRespondError_Network(t *testing.T) {
}
func TestRespond_Network(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
result := models.SuccessResult{Success: true, Message: "test"}
models.Respond(conn, 123, result)
var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -76,13 +78,14 @@ func TestHandleGetState(t *testing.T) {
},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "network.getState"}
handleGetState(conn, req, manager)
var resp models.Response[NetworkState]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -102,13 +105,14 @@ func TestHandleGetWiFiNetworks(t *testing.T) {
},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{ID: 123, Method: "network.wifi.networks"}
handleGetWiFiNetworks(conn, req, manager)
var resp models.Response[[]WiFiNetwork]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -124,7 +128,8 @@ func TestHandleConnectWiFi(t *testing.T) {
state: &NetworkState{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "network.wifi.connect",
@@ -134,7 +139,7 @@ func TestHandleConnectWiFi(t *testing.T) {
handleConnectWiFi(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -148,7 +153,8 @@ func TestHandleSetPreference(t *testing.T) {
state: &NetworkState{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "network.preference.set",
@@ -158,7 +164,7 @@ func TestHandleSetPreference(t *testing.T) {
handleSetPreference(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -172,7 +178,8 @@ func TestHandleGetNetworkInfo(t *testing.T) {
state: &NetworkState{},
}
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "network.info",
@@ -182,7 +189,7 @@ func TestHandleGetNetworkInfo(t *testing.T) {
handleGetNetworkInfo(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -198,7 +205,8 @@ func TestHandleRequest(t *testing.T) {
}
t.Run("unknown method", func(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "network.unknown",
@@ -207,7 +215,7 @@ func TestHandleRequest(t *testing.T) {
HandleRequest(conn, req, manager)
var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -215,7 +223,8 @@ func TestHandleRequest(t *testing.T) {
})
t.Run("valid method - getState", func(t *testing.T) {
- conn := newMockNetConn()
+ mc := newMockNetConn()
+ conn := models.NewConn(mc)
req := models.Request{
ID: 123,
Method: "network.getState",
@@ -224,7 +233,7 @@ func TestHandleRequest(t *testing.T) {
HandleRequest(conn, req, manager)
var resp models.Response[NetworkState]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
+ err := json.NewDecoder(mc.writeBuf).Decode(&resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
diff --git a/core/internal/server/network/subscription_broker.go b/core/internal/server/network/subscription_broker.go
index 1b150a260..5985a815d 100644
--- a/core/internal/server/network/subscription_broker.go
+++ b/core/internal/server/network/subscription_broker.go
@@ -6,7 +6,7 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
type SubscriptionBroker struct {
diff --git a/core/internal/server/network/types.go b/core/internal/server/network/types.go
index 6f7191c4c..ba982bcf1 100644
--- a/core/internal/server/network/types.go
+++ b/core/internal/server/network/types.go
@@ -3,7 +3,7 @@ package network
import (
"sync"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/params/params.go b/core/internal/server/params/params.go
deleted file mode 100644
index 1f592c61f..000000000
--- a/core/internal/server/params/params.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package params
-
-import "fmt"
-
-func Get[T any](params map[string]any, key string) (T, error) {
- val, ok := params[key].(T)
- if !ok {
- var zero T
- return zero, fmt.Errorf("missing or invalid '%s' parameter", key)
- }
- return val, nil
-}
-
-func GetOpt[T any](params map[string]any, key string, def T) T {
- if val, ok := params[key].(T); ok {
- return val
- }
- return def
-}
-
-func String(params map[string]any, key string) (string, error) {
- return Get[string](params, key)
-}
-
-func StringNonEmpty(params map[string]any, key string) (string, error) {
- val, err := Get[string](params, key)
- if err != nil || val == "" {
- return "", fmt.Errorf("missing or invalid '%s' parameter", key)
- }
- return val, nil
-}
-
-func StringOpt(params map[string]any, key string, def string) string {
- return GetOpt(params, key, def)
-}
-
-func Int(params map[string]any, key string) (int, error) {
- val, err := Get[float64](params, key)
- if err != nil {
- return 0, err
- }
- return int(val), nil
-}
-
-func IntOpt(params map[string]any, key string, def int) int {
- if val, ok := params[key].(float64); ok {
- return int(val)
- }
- return def
-}
-
-func Float(params map[string]any, key string) (float64, error) {
- return Get[float64](params, key)
-}
-
-func FloatOpt(params map[string]any, key string, def float64) float64 {
- return GetOpt(params, key, def)
-}
-
-func Bool(params map[string]any, key string) (bool, error) {
- return Get[bool](params, key)
-}
-
-func BoolOpt(params map[string]any, key string, def bool) bool {
- return GetOpt(params, key, def)
-}
-
-func StringMap(params map[string]any, key string) (map[string]string, error) {
- rawMap, err := Get[map[string]any](params, key)
- if err != nil {
- return nil, err
- }
- result := make(map[string]string)
- for k, v := range rawMap {
- if str, ok := v.(string); ok {
- result[k] = str
- }
- }
- return result, nil
-}
-
-func StringMapOpt(params map[string]any, key string) map[string]string {
- rawMap, ok := params[key].(map[string]any)
- if !ok {
- return nil
- }
- result := make(map[string]string)
- for k, v := range rawMap {
- if str, ok := v.(string); ok {
- result[k] = str
- }
- }
- return result
-}
-
-func Any(params map[string]any, key string) (any, bool) {
- val, ok := params[key]
- return val, ok
-}
-
-func AnyMap(params map[string]any, key string) (map[string]any, bool) {
- val, ok := params[key].(map[string]any)
- return val, ok
-}
-
-func StringAlt(params map[string]any, keys ...string) (string, bool) {
- for _, key := range keys {
- if val, ok := params[key].(string); ok {
- return val, true
- }
- }
- return "", false
-}
diff --git a/core/internal/server/params/params_test.go b/core/internal/server/params/params_test.go
deleted file mode 100644
index 99847ff1c..000000000
--- a/core/internal/server/params/params_test.go
+++ /dev/null
@@ -1,154 +0,0 @@
-package params
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestGet(t *testing.T) {
- p := map[string]any{"key": "value"}
- val, err := Get[string](p, "key")
- assert.NoError(t, err)
- assert.Equal(t, "value", val)
-
- _, err = Get[string](p, "missing")
- assert.Error(t, err)
-
- _, err = Get[int](p, "key")
- assert.Error(t, err)
-}
-
-func TestGetOpt(t *testing.T) {
- p := map[string]any{"key": "value"}
- assert.Equal(t, "value", GetOpt(p, "key", "default"))
- assert.Equal(t, "default", GetOpt(p, "missing", "default"))
-}
-
-func TestString(t *testing.T) {
- p := map[string]any{"s": "hello", "n": 123}
- val, err := String(p, "s")
- assert.NoError(t, err)
- assert.Equal(t, "hello", val)
-
- _, err = String(p, "n")
- assert.Error(t, err)
-}
-
-func TestStringNonEmpty(t *testing.T) {
- p := map[string]any{"s": "hello", "empty": ""}
- val, err := StringNonEmpty(p, "s")
- assert.NoError(t, err)
- assert.Equal(t, "hello", val)
-
- _, err = StringNonEmpty(p, "empty")
- assert.Error(t, err)
-
- _, err = StringNonEmpty(p, "missing")
- assert.Error(t, err)
-}
-
-func TestStringOpt(t *testing.T) {
- p := map[string]any{"s": "hello"}
- assert.Equal(t, "hello", StringOpt(p, "s", "default"))
- assert.Equal(t, "default", StringOpt(p, "missing", "default"))
-}
-
-func TestInt(t *testing.T) {
- p := map[string]any{"n": float64(42), "s": "str"}
- val, err := Int(p, "n")
- assert.NoError(t, err)
- assert.Equal(t, 42, val)
-
- _, err = Int(p, "s")
- assert.Error(t, err)
-}
-
-func TestIntOpt(t *testing.T) {
- p := map[string]any{"n": float64(42)}
- assert.Equal(t, 42, IntOpt(p, "n", 0))
- assert.Equal(t, 99, IntOpt(p, "missing", 99))
-}
-
-func TestFloat(t *testing.T) {
- p := map[string]any{"f": 3.14, "s": "str"}
- val, err := Float(p, "f")
- assert.NoError(t, err)
- assert.Equal(t, 3.14, val)
-
- _, err = Float(p, "s")
- assert.Error(t, err)
-}
-
-func TestFloatOpt(t *testing.T) {
- p := map[string]any{"f": 3.14}
- assert.Equal(t, 3.14, FloatOpt(p, "f", 0))
- assert.Equal(t, 1.0, FloatOpt(p, "missing", 1.0))
-}
-
-func TestBool(t *testing.T) {
- p := map[string]any{"b": true, "s": "str"}
- val, err := Bool(p, "b")
- assert.NoError(t, err)
- assert.True(t, val)
-
- _, err = Bool(p, "s")
- assert.Error(t, err)
-}
-
-func TestBoolOpt(t *testing.T) {
- p := map[string]any{"b": true}
- assert.True(t, BoolOpt(p, "b", false))
- assert.True(t, BoolOpt(p, "missing", true))
-}
-
-func TestStringMap(t *testing.T) {
- p := map[string]any{
- "m": map[string]any{"a": "1", "b": "2", "c": 3},
- }
- val, err := StringMap(p, "m")
- assert.NoError(t, err)
- assert.Equal(t, map[string]string{"a": "1", "b": "2"}, val)
-
- _, err = StringMap(p, "missing")
- assert.Error(t, err)
-}
-
-func TestStringMapOpt(t *testing.T) {
- p := map[string]any{
- "m": map[string]any{"a": "1"},
- }
- assert.Equal(t, map[string]string{"a": "1"}, StringMapOpt(p, "m"))
- assert.Nil(t, StringMapOpt(p, "missing"))
-}
-
-func TestAny(t *testing.T) {
- p := map[string]any{"k": 123}
- val, ok := Any(p, "k")
- assert.True(t, ok)
- assert.Equal(t, 123, val)
-
- _, ok = Any(p, "missing")
- assert.False(t, ok)
-}
-
-func TestAnyMap(t *testing.T) {
- inner := map[string]any{"nested": true}
- p := map[string]any{"m": inner}
- val, ok := AnyMap(p, "m")
- assert.True(t, ok)
- assert.Equal(t, inner, val)
-
- _, ok = AnyMap(p, "missing")
- assert.False(t, ok)
-}
-
-func TestStringAlt(t *testing.T) {
- p := map[string]any{"b": "found"}
- val, ok := StringAlt(p, "a", "b", "c")
- assert.True(t, ok)
- assert.Equal(t, "found", val)
-
- _, ok = StringAlt(p, "x", "y")
- assert.False(t, ok)
-}
diff --git a/core/internal/server/plugins/handlers.go b/core/internal/server/plugins/handlers.go
index 33f656483..761f1f2fe 100644
--- a/core/internal/server/plugins/handlers.go
+++ b/core/internal/server/plugins/handlers.go
@@ -2,12 +2,11 @@ package plugins
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleRequest(conn net.Conn, req models.Request) {
+func HandleRequest(conn *models.Conn, req models.Request) {
switch req.Method {
case "plugins.list":
HandleList(conn, req)
diff --git a/core/internal/server/plugins/handlers_test.go b/core/internal/server/plugins/handlers_test.go
index 28978abf0..76cfbfac0 100644
--- a/core/internal/server/plugins/handlers_test.go
+++ b/core/internal/server/plugins/handlers_test.go
@@ -12,8 +12,9 @@ import (
)
func TestHandleList(t *testing.T) {
- conn := net.NewMockConn(t)
- conn.EXPECT().Write(mock.Anything).Return(0, nil).Maybe()
+ mc := net.NewMockConn(t)
+ conn := models.NewConn(mc)
+ mc.EXPECT().Write(mock.Anything).Return(0, nil).Maybe()
req := models.Request{
ID: 123,
@@ -25,8 +26,9 @@ func TestHandleList(t *testing.T) {
}
func TestHandleListInstalled(t *testing.T) {
- conn := net.NewMockConn(t)
- conn.EXPECT().Write(mock.Anything).Return(0, nil).Maybe()
+ mc := net.NewMockConn(t)
+ conn := models.NewConn(mc)
+ mc.EXPECT().Write(mock.Anything).Return(0, nil).Maybe()
req := models.Request{
ID: 123,
@@ -38,9 +40,10 @@ func TestHandleListInstalled(t *testing.T) {
}
func TestHandleInstallMissingName(t *testing.T) {
- conn := net.NewMockConn(t)
+ mc := net.NewMockConn(t)
+ conn := models.NewConn(mc)
var written []byte
- conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) {
+ mc.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) {
written = b
return len(b), nil
}).Maybe()
@@ -61,9 +64,10 @@ func TestHandleInstallMissingName(t *testing.T) {
}
func TestHandleInstallInvalidName(t *testing.T) {
- conn := net.NewMockConn(t)
+ mc := net.NewMockConn(t)
+ conn := models.NewConn(mc)
var written []byte
- conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) {
+ mc.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) {
written = b
return len(b), nil
}).Maybe()
@@ -85,9 +89,10 @@ func TestHandleInstallInvalidName(t *testing.T) {
}
func TestHandleUninstallMissingName(t *testing.T) {
- conn := net.NewMockConn(t)
+ mc := net.NewMockConn(t)
+ conn := models.NewConn(mc)
var written []byte
- conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) {
+ mc.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) {
written = b
return len(b), nil
}).Maybe()
@@ -107,9 +112,10 @@ func TestHandleUninstallMissingName(t *testing.T) {
}
func TestHandleUpdateMissingName(t *testing.T) {
- conn := net.NewMockConn(t)
+ mc := net.NewMockConn(t)
+ conn := models.NewConn(mc)
var written []byte
- conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) {
+ mc.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) {
written = b
return len(b), nil
}).Maybe()
@@ -129,9 +135,10 @@ func TestHandleUpdateMissingName(t *testing.T) {
}
func TestHandleSearchMissingQuery(t *testing.T) {
- conn := net.NewMockConn(t)
+ mc := net.NewMockConn(t)
+ conn := models.NewConn(mc)
var written []byte
- conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) {
+ mc.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) {
written = b
return len(b), nil
}).Maybe()
diff --git a/core/internal/server/plugins/install.go b/core/internal/server/plugins/install.go
index 2c13a0e17..388495839 100644
--- a/core/internal/server/plugins/install.go
+++ b/core/internal/server/plugins/install.go
@@ -2,13 +2,12 @@ package plugins
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleInstall(conn net.Conn, req models.Request) {
+func HandleInstall(conn *models.Conn, req models.Request) {
idOrName, ok := models.Get[string](req, "name")
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
diff --git a/core/internal/server/plugins/list.go b/core/internal/server/plugins/list.go
index 476589ac1..d6b22a4dc 100644
--- a/core/internal/server/plugins/list.go
+++ b/core/internal/server/plugins/list.go
@@ -2,13 +2,12 @@ package plugins
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleList(conn net.Conn, req models.Request) {
+func HandleList(conn *models.Conn, req models.Request) {
registry, err := plugins.NewRegistry()
if err != nil {
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err))
diff --git a/core/internal/server/plugins/list_installed.go b/core/internal/server/plugins/list_installed.go
index 7eeadeaa7..72485b12e 100644
--- a/core/internal/server/plugins/list_installed.go
+++ b/core/internal/server/plugins/list_installed.go
@@ -2,13 +2,12 @@ package plugins
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleListInstalled(conn net.Conn, req models.Request) {
+func HandleListInstalled(conn *models.Conn, req models.Request) {
manager, err := plugins.NewManager()
if err != nil {
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err))
diff --git a/core/internal/server/plugins/search.go b/core/internal/server/plugins/search.go
index 11ec973f1..1221c4e5c 100644
--- a/core/internal/server/plugins/search.go
+++ b/core/internal/server/plugins/search.go
@@ -2,13 +2,12 @@ package plugins
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleSearch(conn net.Conn, req models.Request) {
+func HandleSearch(conn *models.Conn, req models.Request) {
query, ok := models.Get[string](req, "query")
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'query' parameter")
diff --git a/core/internal/server/plugins/uninstall.go b/core/internal/server/plugins/uninstall.go
index 7a3d69848..8aadec788 100644
--- a/core/internal/server/plugins/uninstall.go
+++ b/core/internal/server/plugins/uninstall.go
@@ -2,13 +2,12 @@ package plugins
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleUninstall(conn net.Conn, req models.Request) {
+func HandleUninstall(conn *models.Conn, req models.Request) {
name, ok := models.Get[string](req, "name")
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
diff --git a/core/internal/server/plugins/update.go b/core/internal/server/plugins/update.go
index 4ea762a1e..98725a69b 100644
--- a/core/internal/server/plugins/update.go
+++ b/core/internal/server/plugins/update.go
@@ -2,13 +2,12 @@ package plugins
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleUpdate(conn net.Conn, req models.Request) {
+func HandleUpdate(conn *models.Conn, req models.Request) {
name, ok := models.Get[string](req, "name")
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
diff --git a/core/internal/server/router.go b/core/internal/server/router.go
index d07485678..acd08716d 100644
--- a/core/internal/server/router.go
+++ b/core/internal/server/router.go
@@ -2,7 +2,6 @@ package server
import (
"fmt"
- "net"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/apppicker"
@@ -28,7 +27,7 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput"
)
-func RouteRequest(conn net.Conn, req models.Request) {
+func RouteRequest(conn *models.Conn, req models.Request) {
if strings.HasPrefix(req.Method, "network.") {
if networkManager == nil {
models.RespondError(conn, req.ID, "network manager not initialized")
@@ -223,7 +222,7 @@ func RouteRequest(conn net.Conn, req models.Request) {
}
}
-func handleClipboardSetConfig(conn net.Conn, req models.Request) {
+func handleClipboardSetConfig(conn *models.Conn, req models.Request) {
cfg := clipboard.LoadConfig()
if v, ok := models.Get[float64](req, "maxHistory"); ok {
diff --git a/core/internal/server/server.go b/core/internal/server/server.go
index 02df7d073..b88e120d8 100644
--- a/core/internal/server/server.go
+++ b/core/internal/server/server.go
@@ -1,19 +1,12 @@
package server
import (
- "bufio"
- "encoding/json"
+ "context"
"errors"
"fmt"
- "net"
- "os"
- "path/filepath"
"runtime/debug"
- "strconv"
- "strings"
"sync"
"sync/atomic"
- "syscall"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation"
@@ -39,7 +32,9 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlcontext"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/ipc"
+ "github.com/AvengeMedia/dankgo/paths"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
const APIVersion = 28
@@ -88,74 +83,10 @@ var capabilitySubscribers syncmap.Map[string, chan ServerInfo]
var cupsSubscribers syncmap.Map[string, bool]
var cupsSubscriberCount atomic.Int32
-func getSocketDir() string {
- if runtime := os.Getenv("XDG_RUNTIME_DIR"); runtime != "" {
- return runtime
- }
-
- if os.Getuid() == 0 {
- if _, err := os.Stat("/run"); err == nil {
- return "/run/dankdots"
- }
- return "/var/run/dankdots"
- }
-
- return os.TempDir()
-}
+var appPaths = paths.New("danklinux")
func GetSocketPath() string {
- return filepath.Join(getSocketDir(), fmt.Sprintf("danklinux-%d.sock", os.Getpid()))
-}
-
-func FindSocket() (string, error) {
- dir := getSocketDir()
- entries, err := os.ReadDir(dir)
- if err != nil {
- return "", err
- }
-
- for _, entry := range entries {
- if strings.HasPrefix(entry.Name(), "danklinux-") && strings.HasSuffix(entry.Name(), ".sock") {
- return filepath.Join(dir, entry.Name()), nil
- }
- }
- return "", fmt.Errorf("no dms socket found")
-}
-
-func cleanupStaleSockets() {
- dir := getSocketDir()
- entries, err := os.ReadDir(dir)
- if err != nil {
- return
- }
-
- for _, entry := range entries {
- if !strings.HasPrefix(entry.Name(), "danklinux-") || !strings.HasSuffix(entry.Name(), ".sock") {
- continue
- }
-
- pidStr := strings.TrimPrefix(entry.Name(), "danklinux-")
- pidStr = strings.TrimSuffix(pidStr, ".sock")
- pid, err := strconv.Atoi(pidStr)
- if err != nil {
- continue
- }
-
- process, err := os.FindProcess(pid)
- if err != nil {
- socketPath := filepath.Join(dir, entry.Name())
- os.Remove(socketPath)
- log.Debugf("Removed stale socket: %s", socketPath)
- continue
- }
-
- err = process.Signal(syscall.Signal(0))
- if err != nil {
- socketPath := filepath.Join(dir, entry.Name())
- os.Remove(socketPath)
- log.Debugf("Removed stale socket: %s", socketPath)
- }
- }
+ return appPaths.SocketPath()
}
func InitializeNetworkManager() error {
@@ -397,36 +328,21 @@ func InitializeSysUpdateManager() error {
return nil
}
-func handleConnection(conn net.Conn) {
- defer conn.Close()
- defer func() {
- if r := recover(); r != nil {
- log.Errorf("handleConnection panic recovered: panic=%v\n%s", r, debug.Stack())
- }
- }()
+func routeHandler(_ context.Context, conn *models.Conn, req ipc.Request, _ *ipc.Subscriber) {
+ routeRequestRecovered(conn, models.Request(req))
+}
- caps := getCapabilities()
- capsData, _ := json.Marshal(caps)
- conn.Write(capsData)
- conn.Write([]byte("\n"))
- scanner := bufio.NewScanner(conn)
- scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), 64*1024*1024) // grow up to 64 MB for large clipboard payloads
- for scanner.Scan() {
- line := scanner.Bytes()
-
- var req models.Request
- if err := json.Unmarshal(line, &req); err != nil {
- log.Warnf("handleConnection: Failed to unmarshal JSON: %v, line: %s", err, string(line))
- models.RespondError(conn, 0, "invalid json")
- continue
- }
-
- go routeRequestRecovered(conn, req)
+func subscribeHandler(_ context.Context, conn *models.Conn, req ipc.Request, _ *ipc.Subscriber) {
+ switch req.Method {
+ case "subscribe":
+ routeRequestRecovered(conn, models.Request(req))
+ default:
+ models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
}
}
// routeRequestRecovered keeps a panicking handler from taking down the whole daemon
-func routeRequestRecovered(conn net.Conn, req models.Request) {
+func routeRequestRecovered(conn *models.Conn, req models.Request) {
defer func() {
if r := recover(); r != nil {
log.Errorf("RouteRequest panic recovered: method=%s panic=%v\n%s", req.Method, r, debug.Stack())
@@ -595,7 +511,7 @@ func notifyCapabilityChange() {
})
}
-func handleSubscribe(conn net.Conn, req models.Request) {
+func handleSubscribe(conn *models.Conn, req models.Request) {
clientID := fmt.Sprintf("meta-client-%p", conn)
dbusClient := dbusClientID
@@ -1300,7 +1216,7 @@ func handleSubscribe(conn net.Conn, req models.Request) {
}()
info := getServerInfo()
- if err := json.NewEncoder(conn).Encode(models.Response[ServiceEvent]{
+ if err := conn.WriteResponse(models.Response[ServiceEvent]{
ID: req.ID,
Result: &ServiceEvent{Service: "server", Data: info},
}); err != nil {
@@ -1309,7 +1225,7 @@ func handleSubscribe(conn net.Conn, req models.Request) {
}
for event := range eventChan {
- if err := json.NewEncoder(conn).Encode(models.Response[ServiceEvent]{
+ if err := conn.WriteResponse(models.Response[ServiceEvent]{
ID: req.ID,
Result: &event,
}); err != nil {
@@ -1382,8 +1298,39 @@ func cleanupManagers() {
}
}
+type Server struct {
+ ipc *ipc.Server
+}
+
+func New() *Server {
+ return &Server{ipc: ipc.NewServer(ipc.Config{
+ AppName: appPaths.Name,
+ APIVersion: APIVersion,
+ CapabilitiesFunc: func() []string { return getCapabilities().Capabilities },
+ MaxLineSize: 64 * 1024 * 1024, // large clipboard payloads
+ SubscribeHandler: subscribeHandler,
+ }, routeHandler)}
+}
+
+func (s *Server) Listen() error { return s.ipc.Listen() }
+
+func (s *Server) SocketPath() string { return s.ipc.SocketPath() }
+
+func (s *Server) Close() {
+ s.ipc.Close()
+}
+
func Start(printDocs bool) error {
- cleanupStaleSockets()
+ s := New()
+ if err := s.Listen(); err != nil {
+ return err
+ }
+ return s.Serve(printDocs)
+}
+
+func (s *Server) Serve(printDocs bool) error {
+ defer s.ipc.Close()
+ defer cleanupManagers()
// Tailscale manager always starts — reconnects internally via WatchIPNBus.
// The capability is only advertised once tailscaled is reachable; the
@@ -1393,17 +1340,7 @@ func Start(printDocs bool) error {
notifyCapabilityChange()
})
- socketPath := GetSocketPath()
- os.Remove(socketPath)
-
- listener, err := net.Listen("unix", socketPath)
- if err != nil {
- return err
- }
- defer listener.Close()
- defer cleanupManagers()
-
- log.Infof("DMS API Server listening on: %s", socketPath)
+ log.Infof("DMS API Server listening on: %s", s.ipc.SocketPath())
log.Infof("API Version: %d", APIVersion)
log.Info("Protocol: JSON over Unix socket")
log.Info("Request format: {\"id\": , \"method\": \"...\", \"params\": {...}}")
@@ -1768,21 +1705,16 @@ func Start(printDocs bool) error {
log.Info("")
log.Infof("Ready! Capabilities: %v", getCapabilities().Capabilities)
- listenerErrChan := make(chan error, 1)
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ serveErrChan := make(chan error, 1)
go func() {
- for {
- conn, err := listener.Accept()
- if err != nil {
- listenerErrChan <- err
- return
- }
- go handleConnection(conn)
- }
+ serveErrChan <- s.ipc.Serve(ctx)
}()
select {
- case err := <-listenerErrChan:
+ case err := <-serveErrChan:
return err
case err := <-fatalErrChan:
return err
diff --git a/core/internal/server/server_test.go b/core/internal/server/server_test.go
index 404f62438..e92ad5cf8 100644
--- a/core/internal/server/server_test.go
+++ b/core/internal/server/server_test.go
@@ -5,7 +5,6 @@ import (
"fmt"
"net"
"os"
- "path/filepath"
"testing"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
@@ -14,32 +13,6 @@ import (
"github.com/stretchr/testify/require"
)
-func TestGetSocketDir(t *testing.T) {
- tests := []struct {
- name string
- xdgRuntimeDir string
- uid int
- expectedSubstr string
- }{
- {
- name: "uses XDG_RUNTIME_DIR when set",
- xdgRuntimeDir: "/run/user/1000",
- expectedSubstr: "/run/user/1000",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if tt.xdgRuntimeDir != "" {
- t.Setenv("XDG_RUNTIME_DIR", tt.xdgRuntimeDir)
- }
-
- result := getSocketDir()
- assert.Contains(t, result, tt.expectedSubstr)
- })
- }
-}
-
func TestGetSocketPath(t *testing.T) {
path := GetSocketPath()
assert.Contains(t, path, "danklinux-")
@@ -81,11 +54,11 @@ func (m *mockConn) Close() error {
}
func TestRespondError(t *testing.T) {
- conn := &mockConn{}
- models.RespondError(conn, 123, "test error")
+ mc := &mockConn{}
+ models.RespondError(models.NewConn(mc), 123, "test error")
var resp models.Response[any]
- err := json.Unmarshal(conn.written, &resp)
+ err := json.Unmarshal(mc.written, &resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -94,12 +67,12 @@ func TestRespondError(t *testing.T) {
}
func TestRespond(t *testing.T) {
- conn := &mockConn{}
+ mc := &mockConn{}
result := map[string]string{"foo": "bar"}
- models.Respond(conn, 123, result)
+ models.Respond(models.NewConn(mc), 123, result)
var resp models.Response[map[string]string]
- err := json.Unmarshal(conn.written, &resp)
+ err := json.Unmarshal(mc.written, &resp)
require.NoError(t, err)
assert.Equal(t, 123, resp.ID)
@@ -157,24 +130,3 @@ func TestResponse_JSON(t *testing.T) {
assert.Nil(t, decoded.Result)
})
}
-
-func TestCleanupStaleSockets(t *testing.T) {
- tempDir := t.TempDir()
- t.Setenv("XDG_RUNTIME_DIR", tempDir)
-
- staleSocket := filepath.Join(tempDir, "danklinux-4194305.sock")
- err := os.WriteFile(staleSocket, []byte{}, 0o600)
- require.NoError(t, err)
-
- activeSocket := filepath.Join(tempDir, fmt.Sprintf("danklinux-%d.sock", os.Getpid()))
- err = os.WriteFile(activeSocket, []byte{}, 0o600)
- require.NoError(t, err)
-
- cleanupStaleSockets()
-
- _, err = os.Stat(staleSocket)
- assert.True(t, os.IsNotExist(err))
-
- _, err = os.Stat(activeSocket)
- assert.NoError(t, err)
-}
diff --git a/core/internal/server/sysupdate/handlers.go b/core/internal/server/sysupdate/handlers.go
index 42dd0db05..af140c73f 100644
--- a/core/internal/server/sysupdate/handlers.go
+++ b/core/internal/server/sysupdate/handlers.go
@@ -1,13 +1,11 @@
package sysupdate
import (
- "net"
-
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
-func HandleRequest(conn net.Conn, req models.Request, m *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, m *Manager) {
switch req.Method {
case "sysupdate.getState":
models.Respond(conn, req.ID, m.GetState())
@@ -39,7 +37,7 @@ func HandleRequest(conn net.Conn, req models.Request, m *Manager) {
}
}
-func handleUpgrade(conn net.Conn, req models.Request, m *Manager) {
+func handleUpgrade(conn *models.Conn, req models.Request, m *Manager) {
opts := UpgradeOptions{
IncludeFlatpak: params.BoolOpt(req.Params, "includeFlatpak", true),
IncludeAUR: params.BoolOpt(req.Params, "includeAUR", true),
diff --git a/core/internal/server/sysupdate/manager.go b/core/internal/server/sysupdate/manager.go
index 811168ebf..f5c157ff7 100644
--- a/core/internal/server/sysupdate/manager.go
+++ b/core/internal/server/sysupdate/manager.go
@@ -12,7 +12,7 @@ import (
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
const (
diff --git a/core/internal/server/tailscale/handlers.go b/core/internal/server/tailscale/handlers.go
index d87a20c7f..533b14d02 100644
--- a/core/internal/server/tailscale/handlers.go
+++ b/core/internal/server/tailscale/handlers.go
@@ -2,13 +2,12 @@ package tailscale
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
// HandleRequest routes an IPC request to the appropriate handler.
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
switch req.Method {
case "tailscale.getStatus":
handleGetStatus(conn, req, manager)
@@ -27,17 +26,17 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetStatus(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetStatus(conn *models.Conn, req models.Request, manager *Manager) {
state := manager.GetState()
models.Respond(conn, req.ID, state)
}
-func handleRefresh(conn net.Conn, req models.Request, manager *Manager) {
+func handleRefresh(conn *models.Conn, req models.Request, manager *Manager) {
manager.RefreshState()
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "refreshed"})
}
-func handleConnect(conn net.Conn, req models.Request, manager *Manager) {
+func handleConnect(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.Connect(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -45,7 +44,7 @@ func handleConnect(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "connected"})
}
-func handleDisconnect(conn net.Conn, req models.Request, manager *Manager) {
+func handleDisconnect(conn *models.Conn, req models.Request, manager *Manager) {
if err := manager.Disconnect(); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
@@ -53,7 +52,7 @@ func handleDisconnect(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "disconnected"})
}
-func handleSetExitNode(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetExitNode(conn *models.Conn, req models.Request, manager *Manager) {
id := models.GetOr(req, "id", "")
if err := manager.SetExitNode(id); err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -62,7 +61,7 @@ func handleSetExitNode(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "exit node updated"})
}
-func handleSetAllowLanAccess(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetAllowLanAccess(conn *models.Conn, req models.Request, manager *Manager) {
enabled := models.GetOr(req, "enabled", false)
if err := manager.SetAllowLANAccess(enabled); err != nil {
models.RespondError(conn, req.ID, err.Error())
diff --git a/core/internal/server/tailscale/handlers_test.go b/core/internal/server/tailscale/handlers_test.go
index ed1a32658..3a42e4f18 100644
--- a/core/internal/server/tailscale/handlers_test.go
+++ b/core/internal/server/tailscale/handlers_test.go
@@ -47,7 +47,7 @@ func TestHandleGetStatus(t *testing.T) {
defer m.Close()
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{ID: 1, Method: "tailscale.getStatus"}
handleGetStatus(conn, req, m)
@@ -66,7 +66,7 @@ func TestHandleRefresh(t *testing.T) {
defer m.Close()
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{ID: 1, Method: "tailscale.refresh"}
handleRefresh(conn, req, m)
@@ -98,7 +98,7 @@ func TestHandleActions(t *testing.T) {
defer m.Close()
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{ID: 1, Method: tc.method, Params: tc.params}
HandleRequest(conn, req, m)
@@ -125,7 +125,7 @@ func TestHandleAction_BackendError(t *testing.T) {
defer m.Close()
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{ID: 1, Method: "tailscale.connect"}
HandleRequest(conn, req, m)
@@ -141,7 +141,7 @@ func TestHandleRequest_UnknownMethod(t *testing.T) {
defer m.Close()
buf := &bytes.Buffer{}
- conn := &mockConn{Buffer: buf}
+ conn := models.NewConn(&mockConn{Buffer: buf})
req := models.Request{ID: 1, Method: "tailscale.unknownMethod"}
HandleRequest(conn, req, m)
diff --git a/core/internal/server/tailscale/manager.go b/core/internal/server/tailscale/manager.go
index e622df9ab..d6e97a888 100644
--- a/core/internal/server/tailscale/manager.go
+++ b/core/internal/server/tailscale/manager.go
@@ -7,7 +7,7 @@ import (
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
"tailscale.com/client/local"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
@@ -133,7 +133,7 @@ func (m *Manager) watchLoop(ctx context.Context) {
break
}
- if notify.State == nil && notify.NetMap == nil {
+ if notify.State == nil && notify.NetMap == nil { //nolint:staticcheck // NetMap is deprecated upstream but still the only activity signal on some platforms
continue
}
select {
diff --git a/core/internal/server/thememode/handlers.go b/core/internal/server/thememode/handlers.go
index 689187565..eb8ee3c4c 100644
--- a/core/internal/server/thememode/handlers.go
+++ b/core/internal/server/thememode/handlers.go
@@ -1,15 +1,13 @@
package thememode
import (
- "encoding/json"
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
if manager == nil {
models.RespondError(conn, req.ID, "theme mode manager not initialized")
return
@@ -37,11 +35,11 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
-func handleSetEnabled(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetEnabled(conn *models.Conn, req models.Request, manager *Manager) {
enabled, err := params.Bool(req.Params, "enabled")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -52,7 +50,7 @@ func handleSetEnabled(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto enabled set"})
}
-func handleSetMode(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetMode(conn *models.Conn, req models.Request, manager *Manager) {
mode, err := params.String(req.Params, "mode")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -68,7 +66,7 @@ func handleSetMode(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto mode set"})
}
-func handleSetSchedule(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetSchedule(conn *models.Conn, req models.Request, manager *Manager) {
startHour, err := params.Int(req.Params, "startHour")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -99,7 +97,7 @@ func handleSetSchedule(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
-func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetLocation(conn *models.Conn, req models.Request, manager *Manager) {
lat, err := params.Float(req.Params, "latitude")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -115,7 +113,7 @@ func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto location set"})
}
-func handleSetUseIPLocation(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetUseIPLocation(conn *models.Conn, req models.Request, manager *Manager) {
use, err := params.Bool(req.Params, "use")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -126,18 +124,18 @@ func handleSetUseIPLocation(conn net.Conn, req models.Request, manager *Manager)
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto IP location set"})
}
-func handleTrigger(conn net.Conn, req models.Request, manager *Manager) {
+func handleTrigger(conn *models.Conn, req models.Request, manager *Manager) {
manager.TriggerUpdate()
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto update triggered"})
}
-func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, manager *Manager) {
clientID := fmt.Sprintf("client-%p", conn)
stateChan := manager.Subscribe(clientID)
defer manager.Unsubscribe(clientID)
initialState := manager.GetState()
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
ID: req.ID,
Result: &initialState,
}); err != nil {
@@ -145,7 +143,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
}
for state := range stateChan {
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
Result: &state,
}); err != nil {
return
diff --git a/core/internal/server/thememode/manager.go b/core/internal/server/thememode/manager.go
index f7644ad26..855c87e38 100644
--- a/core/internal/server/thememode/manager.go
+++ b/core/internal/server/thememode/manager.go
@@ -8,7 +8,7 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
const (
diff --git a/core/internal/server/themes/handlers.go b/core/internal/server/themes/handlers.go
index b3b308a8e..6f3993844 100644
--- a/core/internal/server/themes/handlers.go
+++ b/core/internal/server/themes/handlers.go
@@ -2,12 +2,11 @@ package themes
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
-func HandleRequest(conn net.Conn, req models.Request) {
+func HandleRequest(conn *models.Conn, req models.Request) {
switch req.Method {
case "themes.list":
HandleList(conn, req)
diff --git a/core/internal/server/themes/install.go b/core/internal/server/themes/install.go
index 26d22bab2..861541d0d 100644
--- a/core/internal/server/themes/install.go
+++ b/core/internal/server/themes/install.go
@@ -2,13 +2,12 @@ package themes
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
)
-func HandleInstall(conn net.Conn, req models.Request) {
+func HandleInstall(conn *models.Conn, req models.Request) {
idOrName, ok := models.Get[string](req, "name")
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
diff --git a/core/internal/server/themes/list.go b/core/internal/server/themes/list.go
index 9564be1eb..73f3c1174 100644
--- a/core/internal/server/themes/list.go
+++ b/core/internal/server/themes/list.go
@@ -2,14 +2,13 @@ package themes
import (
"fmt"
- "net"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
)
-func HandleList(conn net.Conn, req models.Request) {
+func HandleList(conn *models.Conn, req models.Request) {
registry, err := themes.NewRegistry()
if err != nil {
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err))
diff --git a/core/internal/server/themes/list_installed.go b/core/internal/server/themes/list_installed.go
index 49be81b47..0ad6092f5 100644
--- a/core/internal/server/themes/list_installed.go
+++ b/core/internal/server/themes/list_installed.go
@@ -2,7 +2,6 @@ package themes
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
@@ -79,7 +78,7 @@ func addVariantsInfo(info *ThemeInfo, variants *themes.ThemeVariants) {
}
}
-func HandleListInstalled(conn net.Conn, req models.Request) {
+func HandleListInstalled(conn *models.Conn, req models.Request) {
manager, err := themes.NewManager()
if err != nil {
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err))
diff --git a/core/internal/server/themes/search.go b/core/internal/server/themes/search.go
index 2823fe0dc..f46618f59 100644
--- a/core/internal/server/themes/search.go
+++ b/core/internal/server/themes/search.go
@@ -2,13 +2,12 @@ package themes
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
)
-func HandleSearch(conn net.Conn, req models.Request) {
+func HandleSearch(conn *models.Conn, req models.Request) {
query, ok := models.Get[string](req, "query")
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'query' parameter")
diff --git a/core/internal/server/themes/uninstall.go b/core/internal/server/themes/uninstall.go
index 9b453e494..72be014ee 100644
--- a/core/internal/server/themes/uninstall.go
+++ b/core/internal/server/themes/uninstall.go
@@ -2,13 +2,12 @@ package themes
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
)
-func HandleUninstall(conn net.Conn, req models.Request) {
+func HandleUninstall(conn *models.Conn, req models.Request) {
idOrName, ok := models.Get[string](req, "name")
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
diff --git a/core/internal/server/themes/update.go b/core/internal/server/themes/update.go
index fb9f81f65..335cab3dd 100644
--- a/core/internal/server/themes/update.go
+++ b/core/internal/server/themes/update.go
@@ -2,13 +2,12 @@ package themes
import (
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
)
-func HandleUpdate(conn net.Conn, req models.Request) {
+func HandleUpdate(conn *models.Conn, req models.Request) {
idOrName, ok := models.Get[string](req, "name")
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
diff --git a/core/internal/server/wallpaper/handlers.go b/core/internal/server/wallpaper/handlers.go
index f7f4dffaa..226349c5d 100644
--- a/core/internal/server/wallpaper/handlers.go
+++ b/core/internal/server/wallpaper/handlers.go
@@ -3,13 +3,12 @@ package wallpaper
import (
"encoding/json"
"fmt"
- "net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
if manager == nil {
models.RespondError(conn, req.ID, "wallpaper manager not initialized")
return
@@ -29,11 +28,11 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
-func handleSetConfig(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetConfig(conn *models.Conn, req models.Request, manager *Manager) {
raw, ok := params.Any(req.Params, "config")
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'config' parameter")
@@ -56,18 +55,18 @@ func handleSetConfig(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "wallpaper schedule set"})
}
-func handleTrigger(conn net.Conn, req models.Request, manager *Manager) {
+func handleTrigger(conn *models.Conn, req models.Request, manager *Manager) {
manager.ResetSchedule(params.StringOpt(req.Params, "target", ""))
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "wallpaper schedule reset"})
}
-func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, manager *Manager) {
clientID := fmt.Sprintf("client-%p", conn)
stateChan := manager.Subscribe(clientID)
defer manager.Unsubscribe(clientID)
initialState := manager.GetState()
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
ID: req.ID,
Result: &initialState,
}); err != nil {
@@ -75,7 +74,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
}
for state := range stateChan {
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
Result: &state,
}); err != nil {
return
diff --git a/core/internal/server/wallpaper/manager.go b/core/internal/server/wallpaper/manager.go
index d3c2edbe3..e6e17407c 100644
--- a/core/internal/server/wallpaper/manager.go
+++ b/core/internal/server/wallpaper/manager.go
@@ -8,7 +8,7 @@ import (
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
type activeSchedule struct {
diff --git a/core/internal/server/wayland/handlers.go b/core/internal/server/wayland/handlers.go
index 76b08b5b4..4c7deb12b 100644
--- a/core/internal/server/wayland/handlers.go
+++ b/core/internal/server/wayland/handlers.go
@@ -1,16 +1,14 @@
package wayland
import (
- "encoding/json"
"fmt"
- "net"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+ "github.com/AvengeMedia/dankgo/ipc/params"
)
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
if manager == nil {
models.RespondError(conn, req.ID, "wayland manager not initialized")
return
@@ -38,11 +36,11 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
-func handleSetTemperature(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetTemperature(conn *models.Conn, req models.Request, manager *Manager) {
var lowTemp, highTemp int
if temp, ok := models.Get[float64](req, "temp"); ok {
@@ -71,7 +69,7 @@ func handleSetTemperature(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "temperature set"})
}
-func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetLocation(conn *models.Conn, req models.Request, manager *Manager) {
lat, err := params.Float(req.Params, "latitude")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -92,7 +90,7 @@ func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "location set"})
}
-func handleSetManualTimes(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetManualTimes(conn *models.Conn, req models.Request, manager *Manager) {
sunriseStr, sunriseOK := models.Get[string](req, "sunrise")
sunsetStr, sunsetOK := models.Get[string](req, "sunset")
@@ -122,7 +120,7 @@ func handleSetManualTimes(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "manual times set"})
}
-func handleSetUseIPLocation(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetUseIPLocation(conn *models.Conn, req models.Request, manager *Manager) {
use, err := params.Bool(req.Params, "use")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -133,7 +131,7 @@ func handleSetUseIPLocation(conn net.Conn, req models.Request, manager *Manager)
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "IP location preference set"})
}
-func handleSetGamma(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetGamma(conn *models.Conn, req models.Request, manager *Manager) {
gamma, err := params.Float(req.Params, "gamma")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -148,7 +146,7 @@ func handleSetGamma(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "gamma set"})
}
-func handleSetEnabled(conn net.Conn, req models.Request, manager *Manager) {
+func handleSetEnabled(conn *models.Conn, req models.Request, manager *Manager) {
enabled, err := params.Bool(req.Params, "enabled")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
@@ -159,13 +157,13 @@ func handleSetEnabled(conn net.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "enabled state set"})
}
-func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, manager *Manager) {
clientID := fmt.Sprintf("client-%p", conn)
stateChan := manager.Subscribe(clientID)
defer manager.Unsubscribe(clientID)
initialState := manager.GetState()
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
ID: req.ID,
Result: &initialState,
}); err != nil {
@@ -173,7 +171,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
}
for state := range stateChan {
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
Result: &state,
}); err != nil {
return
diff --git a/core/internal/server/wayland/types.go b/core/internal/server/wayland/types.go
index 863eeb8c3..5bae66b9f 100644
--- a/core/internal/server/wayland/types.go
+++ b/core/internal/server/wayland/types.go
@@ -9,7 +9,7 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
"github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation"
wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
"github.com/godbus/dbus/v5"
)
diff --git a/core/internal/server/wlroutput/handlers.go b/core/internal/server/wlroutput/handlers.go
index c885e1375..1b5bd2b72 100644
--- a/core/internal/server/wlroutput/handlers.go
+++ b/core/internal/server/wlroutput/handlers.go
@@ -3,7 +3,6 @@ package wlroutput
import (
"encoding/json"
"fmt"
- "net"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
@@ -31,7 +30,7 @@ type ConfigurationRequest struct {
Test bool `json:"test"`
}
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
if manager == nil {
models.RespondError(conn, req.ID, "wlroutput manager not initialized")
return
@@ -51,11 +50,11 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
}
}
-func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
+func handleGetState(conn *models.Conn, req models.Request, manager *Manager) {
models.Respond(conn, req.ID, manager.GetState())
}
-func handleApplyConfiguration(conn net.Conn, req models.Request, manager *Manager, test bool) {
+func handleApplyConfiguration(conn *models.Conn, req models.Request, manager *Manager, test bool) {
headsParam, ok := models.Get[any](req, "heads")
if !ok {
models.RespondError(conn, req.ID, "missing 'heads' parameter")
@@ -86,13 +85,13 @@ func handleApplyConfiguration(conn net.Conn, req models.Request, manager *Manage
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: msg})
}
-func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
+func handleSubscribe(conn *models.Conn, req models.Request, manager *Manager) {
clientID := fmt.Sprintf("client-%p", conn)
stateChan := manager.Subscribe(clientID)
defer manager.Unsubscribe(clientID)
initialState := manager.GetState()
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
ID: req.ID,
Result: &initialState,
}); err != nil {
@@ -100,7 +99,7 @@ func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
}
for state := range stateChan {
- if err := json.NewEncoder(conn).Encode(models.Response[State]{
+ if err := conn.WriteResponse(models.Response[State]{
Result: &state,
}); err != nil {
return
diff --git a/core/internal/server/wlroutput/types.go b/core/internal/server/wlroutput/types.go
index 9c79b1a8e..0ee030547 100644
--- a/core/internal/server/wlroutput/types.go
+++ b/core/internal/server/wlroutput/types.go
@@ -5,7 +5,7 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_management"
wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
type OutputMode struct {
diff --git a/core/internal/shellembed/embed_stub.go b/core/internal/shellembed/embed_stub.go
new file mode 100644
index 000000000..f8dd0402c
--- /dev/null
+++ b/core/internal/shellembed/embed_stub.go
@@ -0,0 +1,9 @@
+//go:build !withshell
+
+package shellembed
+
+import "embed"
+
+// Untagged builds (tests, vet, plain `go build`) carry no embedded UI;
+// config resolution then requires an explicit shell dir.
+var distFS embed.FS
diff --git a/core/internal/shellembed/embed_withshell.go b/core/internal/shellembed/embed_withshell.go
new file mode 100644
index 000000000..17c8381dc
--- /dev/null
+++ b/core/internal/shellembed/embed_withshell.go
@@ -0,0 +1,12 @@
+//go:build withshell
+
+package shellembed
+
+import "embed"
+
+// dist is populated from the repo's quickshell/ tree by `make sync-shell`
+// before any tagged build; it is never committed. all: keeps the .dankrev
+// revision key, which go:embed would otherwise skip as a dotfile.
+//
+//go:embed all:dist
+var distFS embed.FS
diff --git a/core/internal/shellembed/shellembed.go b/core/internal/shellembed/shellembed.go
new file mode 100644
index 000000000..f4a10796b
--- /dev/null
+++ b/core/internal/shellembed/shellembed.go
@@ -0,0 +1,36 @@
+// Package shellembed carries the quickshell UI inside the dms binary and
+// materializes it at runtime via dankgo/shellapp/shellfs, since quickshell
+// needs a real filesystem path. Customization goes through -c /
+// DMS_SHELL_DIR instead of editing the extraction.
+package shellembed
+
+import (
+ "io/fs"
+ "path"
+
+ "github.com/AvengeMedia/dankgo/shellapp/shellfs"
+)
+
+const (
+ distRoot = "dist"
+ shellEntry = "shell.qml"
+)
+
+// Available reports whether this binary was built with the embedded UI
+// (the withshell build tag).
+func Available() bool {
+ info, err := fs.Stat(distFS, path.Join(distRoot, shellEntry))
+ return err == nil && !info.IsDir()
+}
+
+func Extract(baseDir string) (string, error) {
+ sub, err := fs.Sub(distFS, distRoot)
+ if err != nil {
+ return "", err
+ }
+ return shellfs.Extract(sub, baseDir)
+}
+
+func Prune(baseDir, keep string) {
+ shellfs.Prune(baseDir, keep)
+}
diff --git a/core/internal/shellembed/shellembed_bench_test.go b/core/internal/shellembed/shellembed_bench_test.go
new file mode 100644
index 000000000..39e8d2f04
--- /dev/null
+++ b/core/internal/shellembed/shellembed_bench_test.go
@@ -0,0 +1,52 @@
+package shellembed
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+func benchDir(b *testing.B) string {
+ b.Helper()
+ dir, err := os.MkdirTemp("", "shellembed-bench-")
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.Cleanup(func() {
+ filepath.WalkDir(dir, func(p string, d os.DirEntry, err error) error {
+ if err == nil && d.IsDir() {
+ os.Chmod(p, 0o755)
+ }
+ return nil
+ })
+ os.RemoveAll(dir)
+ })
+ return dir
+}
+
+func BenchmarkExtractWarm(b *testing.B) {
+ if !Available() {
+ b.Skip("no embedded UI in this build")
+ }
+ base := benchDir(b)
+ if _, err := Extract(base); err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ for b.Loop() {
+ if _, err := Extract(base); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkExtractCold(b *testing.B) {
+ if !Available() {
+ b.Skip("no embedded UI in this build")
+ }
+ for b.Loop() {
+ if _, err := Extract(benchDir(b)); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/core/internal/shellembed/shellembed_test.go b/core/internal/shellembed/shellembed_test.go
new file mode 100644
index 000000000..f0d78dfe8
--- /dev/null
+++ b/core/internal/shellembed/shellembed_test.go
@@ -0,0 +1,9 @@
+package shellembed
+
+import "testing"
+
+func TestAvailableFalseWithoutTag(t *testing.T) {
+ if Available() {
+ t.Fatal("Available() should be false in untagged test builds")
+ }
+}
diff --git a/core/internal/utils/paths.go b/core/internal/utils/paths.go
index 31662d33d..05cf0fa0d 100644
--- a/core/internal/utils/paths.go
+++ b/core/internal/utils/paths.go
@@ -4,39 +4,19 @@ import (
"os"
"path/filepath"
"strings"
+
+ "github.com/AvengeMedia/dankgo/paths"
)
-func XDGStateHome() string {
- if dir := os.Getenv("XDG_STATE_HOME"); dir != "" {
- return dir
- }
- home, _ := os.UserHomeDir()
- return filepath.Join(home, ".local", "state")
-}
+func XDGStateHome() string { return paths.XDGStateHome() }
-func XDGDataHome() string {
- if dir := os.Getenv("XDG_DATA_HOME"); dir != "" {
- return dir
- }
- home, _ := os.UserHomeDir()
- return filepath.Join(home, ".local", "share")
-}
+func XDGDataHome() string { return paths.XDGDataHome() }
-func XDGCacheHome() string {
- if dir, err := os.UserCacheDir(); err == nil {
- return dir
- }
- home, _ := os.UserHomeDir()
- return filepath.Join(home, ".cache")
-}
+func XDGCacheHome() string { return paths.XDGCacheHome() }
-func XDGConfigHome() string {
- if dir, err := os.UserConfigDir(); err == nil {
- return dir
- }
- home, _ := os.UserHomeDir()
- return filepath.Join(home, ".config")
-}
+func XDGConfigHome() string { return paths.XDGConfigHome() }
+
+func ExpandPath(path string) (string, error) { return paths.ExpandPath(path) }
func XDGPicturesDir() string {
if dir := os.Getenv("XDG_PICTURES_DIR"); dir != "" {
@@ -83,18 +63,3 @@ func EmacsConfigDir() string {
return ""
}
-
-func ExpandPath(path string) (string, error) {
- expanded := os.ExpandEnv(path)
- expanded = filepath.Clean(expanded)
-
- if strings.HasPrefix(expanded, "~") {
- home, err := os.UserHomeDir()
- if err != nil {
- return "", err
- }
- expanded = filepath.Join(home, expanded[1:])
- }
-
- return expanded, nil
-}
diff --git a/core/pkg/dbusutil/variant.go b/core/pkg/dbusutil/variant.go
deleted file mode 100644
index 4080b6641..000000000
--- a/core/pkg/dbusutil/variant.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package dbusutil
-
-import "github.com/godbus/dbus/v5"
-
-func As[T any](v dbus.Variant) (T, bool) {
- val, ok := v.Value().(T)
- return val, ok
-}
-
-func AsOr[T any](v dbus.Variant, def T) T {
- if val, ok := v.Value().(T); ok {
- return val
- }
- return def
-}
-
-func Get[T any](m map[string]dbus.Variant, key string) (T, bool) {
- v, ok := m[key]
- if !ok {
- var zero T
- return zero, false
- }
- return As[T](v)
-}
-
-func GetOr[T any](m map[string]dbus.Variant, key string, def T) T {
- v, ok := m[key]
- if !ok {
- return def
- }
- return AsOr(v, def)
-}
-
-func Normalize(v any) any {
- switch val := v.(type) {
- case dbus.Variant:
- return Normalize(val.Value())
- case dbus.ObjectPath:
- return string(val)
- case []dbus.ObjectPath:
- result := make([]string, len(val))
- for i, p := range val {
- result[i] = string(p)
- }
- return result
- case map[string]dbus.Variant:
- result := make(map[string]any)
- for k, vv := range val {
- result[k] = Normalize(vv.Value())
- }
- return result
- case map[string]any:
- result := make(map[string]any)
- for k, vv := range val {
- result[k] = Normalize(vv)
- }
- return result
- case map[dbus.ObjectPath]map[string]map[string]dbus.Variant:
- result := make(map[string]any)
- for path, ifaces := range val {
- ifaceMap := make(map[string]any)
- for ifaceName, props := range ifaces {
- propMap := make(map[string]any)
- for propName, propVal := range props {
- propMap[propName] = Normalize(propVal.Value())
- }
- ifaceMap[ifaceName] = propMap
- }
- result[string(path)] = ifaceMap
- }
- return result
- case []any:
- result := make([]any, len(val))
- for i, item := range val {
- result[i] = Normalize(item)
- }
- return result
- case []dbus.Variant:
- result := make([]any, len(val))
- for i, item := range val {
- result[i] = Normalize(item.Value())
- }
- return result
- default:
- return v
- }
-}
-
-func NormalizeSlice(values []any) []any {
- result := make([]any, len(values))
- for i, v := range values {
- result[i] = Normalize(v)
- }
- return result
-}
diff --git a/core/pkg/dbusutil/variant_test.go b/core/pkg/dbusutil/variant_test.go
deleted file mode 100644
index a927e54be..000000000
--- a/core/pkg/dbusutil/variant_test.go
+++ /dev/null
@@ -1,155 +0,0 @@
-package dbusutil
-
-import (
- "testing"
-
- "github.com/godbus/dbus/v5"
- "github.com/stretchr/testify/assert"
-)
-
-func TestAs(t *testing.T) {
- t.Run("string", func(t *testing.T) {
- v := dbus.MakeVariant("hello")
- val, ok := As[string](v)
- assert.True(t, ok)
- assert.Equal(t, "hello", val)
- })
-
- t.Run("bool", func(t *testing.T) {
- v := dbus.MakeVariant(true)
- val, ok := As[bool](v)
- assert.True(t, ok)
- assert.True(t, val)
- })
-
- t.Run("int32", func(t *testing.T) {
- v := dbus.MakeVariant(int32(42))
- val, ok := As[int32](v)
- assert.True(t, ok)
- assert.Equal(t, int32(42), val)
- })
-
- t.Run("wrong type", func(t *testing.T) {
- v := dbus.MakeVariant("hello")
- _, ok := As[int](v)
- assert.False(t, ok)
- })
-}
-
-func TestAsOr(t *testing.T) {
- t.Run("exists", func(t *testing.T) {
- v := dbus.MakeVariant("hello")
- val := AsOr(v, "default")
- assert.Equal(t, "hello", val)
- })
-
- t.Run("wrong type uses default", func(t *testing.T) {
- v := dbus.MakeVariant(123)
- val := AsOr(v, "default")
- assert.Equal(t, "default", val)
- })
-}
-
-func TestGet(t *testing.T) {
- m := map[string]dbus.Variant{
- "name": dbus.MakeVariant("test"),
- "enabled": dbus.MakeVariant(true),
- "count": dbus.MakeVariant(int32(5)),
- }
-
- t.Run("exists", func(t *testing.T) {
- val, ok := Get[string](m, "name")
- assert.True(t, ok)
- assert.Equal(t, "test", val)
- })
-
- t.Run("missing key", func(t *testing.T) {
- _, ok := Get[string](m, "missing")
- assert.False(t, ok)
- })
-
- t.Run("wrong type", func(t *testing.T) {
- _, ok := Get[int](m, "name")
- assert.False(t, ok)
- })
-}
-
-func TestGetOr(t *testing.T) {
- m := map[string]dbus.Variant{
- "name": dbus.MakeVariant("test"),
- }
-
- t.Run("exists", func(t *testing.T) {
- val := GetOr(m, "name", "default")
- assert.Equal(t, "test", val)
- })
-
- t.Run("missing uses default", func(t *testing.T) {
- val := GetOr(m, "missing", "default")
- assert.Equal(t, "default", val)
- })
-
- t.Run("wrong type uses default", func(t *testing.T) {
- val := GetOr(m, "name", 42)
- assert.Equal(t, 42, val)
- })
-}
-
-func TestNormalize(t *testing.T) {
- t.Run("variant unwrap", func(t *testing.T) {
- v := dbus.MakeVariant("hello")
- result := Normalize(v)
- assert.Equal(t, "hello", result)
- })
-
- t.Run("nested variant", func(t *testing.T) {
- v := dbus.MakeVariant(dbus.MakeVariant("nested"))
- result := Normalize(v)
- assert.Equal(t, "nested", result)
- })
-
- t.Run("object path", func(t *testing.T) {
- v := dbus.ObjectPath("/org/test")
- result := Normalize(v)
- assert.Equal(t, "/org/test", result)
- })
-
- t.Run("object path slice", func(t *testing.T) {
- v := []dbus.ObjectPath{"/org/a", "/org/b"}
- result := Normalize(v)
- assert.Equal(t, []string{"/org/a", "/org/b"}, result)
- })
-
- t.Run("variant map", func(t *testing.T) {
- v := map[string]dbus.Variant{
- "key": dbus.MakeVariant("value"),
- }
- result := Normalize(v)
- expected := map[string]any{"key": "value"}
- assert.Equal(t, expected, result)
- })
-
- t.Run("any slice", func(t *testing.T) {
- v := []any{dbus.MakeVariant("a"), dbus.ObjectPath("/b")}
- result := Normalize(v)
- expected := []any{"a", "/b"}
- assert.Equal(t, expected, result)
- })
-
- t.Run("passthrough primitives", func(t *testing.T) {
- assert.Equal(t, "hello", Normalize("hello"))
- assert.Equal(t, 42, Normalize(42))
- assert.Equal(t, true, Normalize(true))
- })
-}
-
-func TestNormalizeSlice(t *testing.T) {
- input := []any{
- dbus.MakeVariant("a"),
- dbus.ObjectPath("/b"),
- "c",
- }
- result := NormalizeSlice(input)
- expected := []any{"a", "/b", "c"}
- assert.Equal(t, expected, result)
-}
diff --git a/core/pkg/go-wayland/wayland/client/context.go b/core/pkg/go-wayland/wayland/client/context.go
index 7e48f6aaa..220f5aebf 100644
--- a/core/pkg/go-wayland/wayland/client/context.go
+++ b/core/pkg/go-wayland/wayland/client/context.go
@@ -8,7 +8,7 @@ import (
"sync"
"time"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/AvengeMedia/dankgo/syncmap"
)
type Context struct {
diff --git a/core/pkg/syncmap/LICENSE b/core/pkg/syncmap/LICENSE
deleted file mode 100644
index edba27558..000000000
--- a/core/pkg/syncmap/LICENSE
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright 2009 The Go Authors.
-Copyright 2024 Zachary Olstein.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google LLC nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/core/pkg/syncmap/syncmap.go b/core/pkg/syncmap/syncmap.go
deleted file mode 100644
index 27d9458ad..000000000
--- a/core/pkg/syncmap/syncmap.go
+++ /dev/null
@@ -1,537 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package syncmap
-
-import (
- "sync"
- "sync/atomic"
- "unsafe"
-)
-
-// Map is like a Go map[K]V but is safe for concurrent use
-// by multiple goroutines without additional locking or coordination.
-// Loads, stores, and deletes run in amortized constant time.
-//
-// The Map type is specialized. Most code should use a plain Go map instead,
-// with separate locking or coordination, for better type safety and to make it
-// easier to maintain other invariants along with the map content.
-//
-// The Map type is optimized for two common use cases: (1) when the entry for a given
-// key is only ever written once but read many times, as in caches that only grow,
-// or (2) when multiple goroutines read, write, and overwrite entries for disjoint
-// sets of keys. In these two cases, use of a Map may significantly reduce lock
-// contention compared to a Go map paired with a separate [Mutex] or [RWMutex].
-//
-// The zero Map is empty and ready for use. A Map must not be copied after first use.
-//
-// In the terminology of [the Go memory model], Map arranges that a write operation
-// “synchronizes before” any read operation that observes the effect of the write, where
-// read and write operations are defined as follows.
-// [Map.Load], [Map.LoadAndDelete], [Map.LoadOrStore], and [Map.Swap] are read operations;
-// [Map.Delete], [Map.LoadAndDelete], [Map.Store], and [Map.Swap] are write operations;
-// [Map.LoadOrStore] is a write operation when it returns loaded set to false.
-//
-// [the Go memory model]: https://go.dev/ref/mem
-type Map[K comparable, V any] struct {
- mu sync.Mutex
-
- // read contains the portion of the map's contents that are safe for
- // concurrent access (with or without mu held).
- //
- // The read field itself is always safe to load, but must only be stored with
- // mu held.
- //
- // Entries stored in read may be updated concurrently without mu, but updating
- // a previously-expunged entry requires that the entry be copied to the dirty
- // map and unexpunged with mu held.
- read atomic.Pointer[readOnly[K, V]]
-
- // dirty contains the portion of the map's contents that require mu to be
- // held. To ensure that the dirty map can be promoted to the read map quickly,
- // it also includes all of the non-expunged entries in the read map.
- //
- // Expunged entries are not stored in the dirty map. An expunged entry in the
- // clean map must be unexpunged and added to the dirty map before a new value
- // can be stored to it.
- //
- // If the dirty map is nil, the next write to the map will initialize it by
- // making a shallow copy of the clean map, omitting stale entries.
- dirty map[K]*entry[V]
-
- // misses counts the number of loads since the read map was last updated that
- // needed to lock mu to determine whether the key was present.
- //
- // Once enough misses have occurred to cover the cost of copying the dirty
- // map, the dirty map will be promoted to the read map (in the unamended
- // state) and the next store to the map will make a new dirty copy.
- misses int
-}
-
-// readOnly is an immutable struct stored atomically in the Map.read field.
-type readOnly[K comparable, V any] struct {
- m map[K]*entry[V]
- amended bool // true if the dirty map contains some key not in m.
-}
-
-// expunged is an arbitrary pointer that marks entries which have been deleted
-// from the dirty map.
-// Because the same expunged pointer is used regardless of the Map's value type,
-// value pointers read from the map must be compared against expunged BEFORE
-// casting the pointer to *V.
-var expunged = unsafe.Pointer(new(int))
-
-// An entry is a slot in the map corresponding to a particular key.
-type entry[V any] struct {
- // p points to the value stored for the entry.
- //
- // If p == nil, the entry has been deleted, and either m.dirty == nil or
- // m.dirty[key] is e.
- //
- // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry
- // is missing from m.dirty.
- //
- // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty
- // != nil, in m.dirty[key].
- //
- // If p != expunged, it is always safe to cast it to (*V).
- //
- // An entry can be deleted by atomic replacement with nil: when m.dirty is
- // next created, it will atomically replace nil with expunged and leave
- // m.dirty[key] unset.
- //
- // An entry's associated value can be updated by atomic replacement, provided
- // p != expunged. If p == expunged, an entry's associated value can be updated
- // only after first setting m.dirty[key] = e so that lookups using the dirty
- // map find the entry.
- p unsafe.Pointer
-}
-
-func newEntry[V any](i V) *entry[V] {
- e := &entry[V]{}
- atomic.StorePointer(&e.p, unsafe.Pointer(&i))
- return e
-}
-
-func (m *Map[K, V]) loadReadOnly() readOnly[K, V] {
- if p := m.read.Load(); p != nil {
- return *p
- }
- return readOnly[K, V]{}
-}
-
-// Load returns the value stored in the map for a key, or nil if no
-// value is present.
-// The ok result indicates whether value was found in the map.
-func (m *Map[K, V]) Load(key K) (value V, ok bool) {
- read := m.loadReadOnly()
- e, ok := read.m[key]
- if !ok && read.amended {
- m.mu.Lock()
- // Avoid reporting a spurious miss if m.dirty got promoted while we were
- // blocked on m.mu. (If further loads of the same key will not miss, it's
- // not worth copying the dirty map for this key.)
- read = m.loadReadOnly()
- e, ok = read.m[key]
- if !ok && read.amended {
- e, ok = m.dirty[key]
- // Regardless of whether the entry was present, record a miss: this key
- // will take the slow path until the dirty map is promoted to the read
- // map.
- m.missLocked()
- }
- m.mu.Unlock()
- }
- if !ok {
- return value, false
- }
- return e.load()
-}
-
-func (e *entry[V]) load() (value V, ok bool) {
- p := atomic.LoadPointer(&e.p)
- if p == nil || p == expunged {
- return value, false
- }
- return *(*V)(p), true
-}
-
-// Store sets the value for a key.
-func (m *Map[K, V]) Store(key K, value V) {
- _, _ = m.Swap(key, value)
-}
-
-// unexpungeLocked ensures that the entry is not marked as expunged.
-//
-// If the entry was previously expunged, it must be added to the dirty map
-// before m.mu is unlocked.
-func (e *entry[V]) unexpungeLocked() (wasExpunged bool) {
- return atomic.CompareAndSwapPointer(&e.p, expunged, nil)
-}
-
-// swapLocked unconditionally swaps a value into the entry.
-//
-// The entry must be known not to be expunged.
-func (e *entry[V]) swapLocked(i *V) *V {
- return (*V)(atomic.SwapPointer(&e.p, unsafe.Pointer(i)))
-}
-
-// LoadOrStore returns the existing value for the key if present.
-// Otherwise, it stores and returns the given value.
-// The loaded result is true if the value was loaded, false if stored.
-func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
- // Avoid locking if it's a clean hit.
- read := m.loadReadOnly()
- if e, ok := read.m[key]; ok {
- actual, loaded, ok := e.tryLoadOrStore(value)
- if ok {
- return actual, loaded
- }
- }
-
- m.mu.Lock()
- read = m.loadReadOnly()
- if e, ok := read.m[key]; ok {
- if e.unexpungeLocked() {
- m.dirty[key] = e
- }
- actual, loaded, _ = e.tryLoadOrStore(value)
- } else if e, ok := m.dirty[key]; ok {
- actual, loaded, _ = e.tryLoadOrStore(value)
- m.missLocked()
- } else {
- if !read.amended {
- // We're adding the first new key to the dirty map.
- // Make sure it is allocated and mark the read-only map as incomplete.
- m.dirtyLocked()
- m.read.Store(&readOnly[K, V]{m: read.m, amended: true})
- }
- m.dirty[key] = newEntry(value)
- actual, loaded = value, false
- }
- m.mu.Unlock()
-
- return actual, loaded
-}
-
-// tryLoadOrStore atomically loads or stores a value if the entry is not
-// expunged.
-//
-// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and
-// returns with ok==false.
-func (e *entry[V]) tryLoadOrStore(i V) (actual V, loaded, ok bool) {
- ptr := atomic.LoadPointer(&e.p)
- if ptr == expunged {
- return actual, false, false
- }
- p := (*V)(ptr)
- if p != nil {
- return *p, true, true
- }
-
- // Copy the interface after the first load to make this method more amenable
- // to escape analysis: if we hit the "load" path or the entry is expunged, we
- // shouldn't bother heap-allocating.
- ic := i
- for {
- if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) {
- return i, false, true
- }
- ptr = atomic.LoadPointer(&e.p)
- if ptr == expunged {
- return actual, false, false
- }
- p = (*V)(ptr)
- if p != nil {
- return *p, true, true
- }
- }
-}
-
-// LoadAndDelete deletes the value for a key, returning the previous value if any.
-// The loaded result reports whether the key was present.
-func (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
- read := m.loadReadOnly()
- e, ok := read.m[key]
- if !ok && read.amended {
- m.mu.Lock()
- read = m.loadReadOnly()
- e, ok = read.m[key]
- if !ok && read.amended {
- e, ok = m.dirty[key]
- delete(m.dirty, key)
- // Regardless of whether the entry was present, record a miss: this key
- // will take the slow path until the dirty map is promoted to the read
- // map.
- m.missLocked()
- }
- m.mu.Unlock()
- }
- if ok {
- return e.delete()
- }
- return value, false
-}
-
-// Delete deletes the value for a key.
-func (m *Map[K, V]) Delete(key K) {
- m.LoadAndDelete(key)
-}
-
-func (e *entry[V]) delete() (value V, ok bool) {
- for {
- p := atomic.LoadPointer(&e.p)
- if p == nil || p == expunged {
- return value, false
- }
- if atomic.CompareAndSwapPointer(&e.p, p, nil) {
- return *(*V)(p), true
- }
- }
-}
-
-// trySwap swaps a value if the entry has not been expunged.
-//
-// If the entry is expunged, trySwap returns false and leaves the entry
-// unchanged.
-func (e *entry[V]) trySwap(i *V) (*V, bool) {
- for {
- p := atomic.LoadPointer(&e.p)
- if p == expunged {
- return nil, false
- }
- if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) {
- return (*V)(p), true
- }
- }
-}
-
-// Swap swaps the value for a key and returns the previous value if any.
-// The loaded result reports whether the key was present.
-func (m *Map[K, V]) Swap(key K, value V) (previous V, loaded bool) {
- read := m.loadReadOnly()
- if e, ok := read.m[key]; ok {
- if v, ok := e.trySwap(&value); ok {
- if v == nil {
- return previous, false
- }
- return *v, true
- }
- }
-
- m.mu.Lock()
- read = m.loadReadOnly()
- if e, ok := read.m[key]; ok {
- if e.unexpungeLocked() {
- // The entry was previously expunged, which implies that there is a
- // non-nil dirty map and this entry is not in it.
- m.dirty[key] = e
- }
- if v := e.swapLocked(&value); v != nil {
- loaded = true
- previous = *v
- }
- } else if e, ok := m.dirty[key]; ok {
- if v := e.swapLocked(&value); v != nil {
- loaded = true
- previous = *v
- }
- } else {
- if !read.amended {
- // We're adding the first new key to the dirty map.
- // Make sure it is allocated and mark the read-only map as incomplete.
- m.dirtyLocked()
- m.read.Store(&readOnly[K, V]{m: read.m, amended: true})
- }
- m.dirty[key] = newEntry(value)
- }
- m.mu.Unlock()
- return previous, loaded
-}
-
-// Range calls f sequentially for each key and value present in the map.
-// If f returns false, range stops the iteration.
-//
-// Range does not necessarily correspond to any consistent snapshot of the Map's
-// contents: no key will be visited more than once, but if the value for any key
-// is stored or deleted concurrently (including by f), Range may reflect any
-// mapping for that key from any point during the Range call. Range does not
-// block other methods on the receiver; even f itself may call any method on m.
-//
-// Range may be O(N) with the number of elements in the map even if f returns
-// false after a constant number of calls.
-func (m *Map[K, V]) Range(f func(key K, value V) bool) {
- // We need to be able to iterate over all of the keys that were already
- // present at the start of the call to Range.
- // If read.amended is false, then read.m satisfies that property without
- // requiring us to hold m.mu for a long time.
- read := m.loadReadOnly()
- if read.amended {
- // m.dirty contains keys not in read.m. Fortunately, Range is already O(N)
- // (assuming the caller does not break out early), so a call to Range
- // amortizes an entire copy of the map: we can promote the dirty copy
- // immediately!
- m.mu.Lock()
- read = m.loadReadOnly()
- if read.amended {
- read = readOnly[K, V]{m: m.dirty}
- copyRead := read
- m.read.Store(©Read)
- m.dirty = nil
- m.misses = 0
- }
- m.mu.Unlock()
- }
-
- for k, e := range read.m {
- v, ok := e.load()
- if !ok {
- continue
- }
- if !f(k, v) {
- break
- }
- }
-}
-
-// CompareAndSwap swaps the old and new values for key
-// if the value stored in the map is equal to old.
-// The old value must be of a comparable type.
-func CompareAndSwap[K comparable, V comparable](m *Map[K, V], key K, old, new V) (swapped bool) {
- read := m.loadReadOnly()
- if e, ok := read.m[key]; ok {
- return tryCompareAndSwap(e, old, new)
- } else if !read.amended {
- return false // No existing value for key.
- }
-
- m.mu.Lock()
- defer m.mu.Unlock()
- read = m.loadReadOnly()
- swapped = false
- if e, ok := read.m[key]; ok {
- swapped = tryCompareAndSwap(e, old, new)
- } else if e, ok := m.dirty[key]; ok {
- swapped = tryCompareAndSwap(e, old, new)
- // We needed to lock mu in order to load the entry for key,
- // and the operation didn't change the set of keys in the map
- // (so it would be made more efficient by promoting the dirty
- // map to read-only).
- // Count it as a miss so that we will eventually switch to the
- // more efficient steady state.
- m.missLocked()
- }
- return swapped
-}
-
-// CompareAndDelete deletes the entry for key if its value is equal to old.
-// The old value must be of a comparable type.
-//
-// If there is no current value for key in the map, CompareAndDelete
-// returns false (even if the old value is the zero value of V).
-func CompareAndDelete[K comparable, V comparable](m *Map[K, V], key K, old V) (deleted bool) {
- read := m.loadReadOnly()
- e, ok := read.m[key]
- if !ok && read.amended {
- m.mu.Lock()
- read = m.loadReadOnly()
- e, ok = read.m[key]
- if !ok && read.amended {
- e, ok = m.dirty[key]
- // Don't delete key from m.dirty: we still need to do the “compare” part
- // of the operation. The entry will eventually be expunged when the
- // dirty map is promoted to the read map.
- //
- // Regardless of whether the entry was present, record a miss: this key
- // will take the slow path until the dirty map is promoted to the read
- // map.
- m.missLocked()
- }
- m.mu.Unlock()
- }
- for ok {
- ptr := atomic.LoadPointer(&e.p)
- if ptr == nil || ptr == expunged {
- return false
- }
- p := (*V)(ptr)
- if *p != old {
- return false
- }
- if atomic.CompareAndSwapPointer(&e.p, ptr, nil) {
- return true
- }
- }
- return false
-}
-
-// tryCompareAndSwap compare the entry with the given old value and swaps
-// it with a new value if the entry is equal to the old value, and the entry
-// has not been expunged.
-//
-// If the entry is expunged, tryCompareAndSwap returns false and leaves
-// the entry unchanged.
-func tryCompareAndSwap[V comparable](e *entry[V], old, new V) bool {
- ptr := atomic.LoadPointer(&e.p)
- if ptr == nil || ptr == expunged {
- return false
- }
- p := (*V)(ptr)
- if *p != old {
- return false
- }
-
- // Copy the interface after the first load to make this method more amenable
- // to escape analysis: if the comparison fails from the start, we shouldn't
- // bother heap-allocating an interface value to store.
- nc := new
- for {
- if atomic.CompareAndSwapPointer(&e.p, ptr, unsafe.Pointer(&nc)) {
- return true
- }
- ptr = atomic.LoadPointer(&e.p)
- if ptr == nil || ptr == expunged {
- return false
- }
- p = (*V)(ptr)
- if *p != old {
- return false
- }
- }
-}
-
-func (m *Map[K, V]) missLocked() {
- m.misses++
- if m.misses < len(m.dirty) {
- return
- }
- m.read.Store(&readOnly[K, V]{m: m.dirty})
- m.dirty = nil
- m.misses = 0
-}
-
-func (m *Map[K, V]) dirtyLocked() {
- if m.dirty != nil {
- return
- }
-
- read := m.loadReadOnly()
- m.dirty = make(map[K]*entry[V], len(read.m))
- for k, e := range read.m {
- if !e.tryExpungeLocked() {
- m.dirty[k] = e
- }
- }
-}
-
-func (e *entry[V]) tryExpungeLocked() (isExpunged bool) {
- p := atomic.LoadPointer(&e.p)
- for p == nil {
- if atomic.CompareAndSwapPointer(&e.p, nil, expunged) {
- return true
- }
- p = atomic.LoadPointer(&e.p)
- }
- return p == expunged
-}
diff --git a/distro/debian/dms-git/debian/rules b/distro/debian/dms-git/debian/rules
index 6a2b12c51..b3f6f5f94 100755
--- a/distro/debian/dms-git/debian/rules
+++ b/distro/debian/dms-git/debian/rules
@@ -68,24 +68,11 @@ override_dh_auto_build:
override_dh_auto_install:
install -Dm755 dms debian/dms-git/usr/bin/dms
- mkdir -p debian/dms-git/usr/share/quickshell/dms debian/dms-git/usr/lib/systemd/user
- if [ -d quickshell ]; then \
- cp -rL quickshell/* debian/dms-git/usr/share/quickshell/dms/; \
- install -Dm644 assets/systemd/dms.service debian/dms-git/usr/lib/systemd/user/dms.service; \
- install -Dm644 assets/dms-open.desktop debian/dms-git/usr/share/applications/dms-open.desktop; \
- install -Dm644 assets/com.danklinux.dms.desktop debian/dms-git/usr/share/applications/com.danklinux.dms.desktop; \
- install -Dm644 assets/com.danklinux.dms.notepad.desktop debian/dms-git/usr/share/applications/com.danklinux.dms.notepad.desktop; \
- install -Dm644 assets/danklogo.svg debian/dms-git/usr/share/icons/hicolor/scalable/apps/danklogo.svg; \
- else \
- echo "ERROR: quickshell directory not found!" && \
- echo "Contents of current directory:" && ls -la && \
- exit 1; \
- fi
-
- rm -rf debian/dms-git/usr/share/quickshell/dms/core \
- debian/dms-git/usr/share/quickshell/dms/distro
-
- echo "$(UPSTREAM_VERSION)" > debian/dms-git/usr/share/quickshell/dms/VERSION
+ install -Dm644 assets/systemd/dms.service debian/dms-git/usr/lib/systemd/user/dms.service
+ install -Dm644 assets/dms-open.desktop debian/dms-git/usr/share/applications/dms-open.desktop
+ install -Dm644 assets/com.danklinux.dms.desktop debian/dms-git/usr/share/applications/com.danklinux.dms.desktop
+ install -Dm644 assets/com.danklinux.dms.notepad.desktop debian/dms-git/usr/share/applications/com.danklinux.dms.notepad.desktop
+ install -Dm644 assets/danklogo.svg debian/dms-git/usr/share/icons/hicolor/scalable/apps/danklogo.svg
override_dh_auto_clean:
# Clean up build artifacts
diff --git a/distro/fedora/dms-git.spec b/distro/fedora/dms-git.spec
index 32a514cf6..837a16189 100644
--- a/distro/fedora/dms-git.spec
+++ b/distro/fedora/dms-git.spec
@@ -3,7 +3,7 @@
%global debug_package %{nil}
%global version {{{ git_repo_version }}}
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
-%global go_toolchain_version 1.26.1
+%global go_toolchain_version 1.26.4
Name: dms
Epoch: 2
@@ -130,16 +130,6 @@ install -Dm644 assets/com.danklinux.dms.desktop %{buildroot}%{_datadir}/applicat
install -Dm644 assets/com.danklinux.dms.notepad.desktop %{buildroot}%{_datadir}/applications/com.danklinux.dms.notepad.desktop
install -Dm644 assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
-# Install shell files to shared data location
-install -dm755 %{buildroot}%{_datadir}/quickshell/dms
-cp -rL quickshell/* %{buildroot}%{_datadir}/quickshell/dms/
-
-# Remove build files
-rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
-rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
-rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
-rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
-
%posttrans
# Signal running DMS instances to reload
pkill -USR1 -x dms >/dev/null 2>&1 || :
@@ -148,7 +138,6 @@ pkill -USR1 -x dms >/dev/null 2>&1 || :
%license LICENSE
%doc CONTRIBUTING.md
%doc quickshell/README.md
-%{_datadir}/quickshell/dms/
%{_userunitdir}/dms.service
%{_datadir}/applications/dms-open.desktop
%{_datadir}/applications/com.danklinux.dms.desktop
diff --git a/distro/opensuse/dms-git.spec b/distro/opensuse/dms-git.spec
index fd5766800..4907784c8 100644
--- a/distro/opensuse/dms-git.spec
+++ b/distro/opensuse/dms-git.spec
@@ -1,5 +1,5 @@
%global debug_package %{nil}
-%global go_toolchain_version 1.26.1
+%global go_toolchain_version 1.26.4
Name: dms-git
Version: 1.4.0+git2528.d336866f
@@ -38,7 +38,7 @@ DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
and optimized for niri, Hyprland, Sway, and other wlroots compositors.
This git version tracks the master branch and includes the latest features
-and fixes. Includes pre-built dms CLI binary and QML shell files.
+and fixes. The Quickshell UI is embedded in the dms binary.
%prep
%setup -q -n dms-git-source
@@ -120,15 +120,6 @@ install -Dm644 assets/com.danklinux.dms.desktop %{buildroot}%{_datadir}/applicat
install -Dm644 assets/com.danklinux.dms.notepad.desktop %{buildroot}%{_datadir}/applications/com.danklinux.dms.notepad.desktop
install -Dm644 assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
-install -dm755 %{buildroot}%{_datadir}/quickshell/dms
-cp -r quickshell/* %{buildroot}%{_datadir}/quickshell/dms/
-
-rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
-rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
-rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
-rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
-rm -rf %{buildroot}%{_datadir}/quickshell/dms/core
-
%posttrans
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
@@ -149,8 +140,6 @@ pkill -USR1 -x dms >/dev/null 2>&1 || :
%dir %{_datadir}/zsh/site-functions
%{_datadir}/zsh/site-functions/_dms
%{_datadir}/bash-completion/completions/dms
-%dir %{_datadir}/quickshell
-%{_datadir}/quickshell/dms/
%{_userunitdir}/dms.service
%{_datadir}/applications/dms-open.desktop
%{_datadir}/applications/com.danklinux.dms.desktop
diff --git a/distro/ubuntu/dms-git/debian/rules b/distro/ubuntu/dms-git/debian/rules
index 737a0beb5..8f858a3e9 100755
--- a/distro/ubuntu/dms-git/debian/rules
+++ b/distro/ubuntu/dms-git/debian/rules
@@ -58,14 +58,6 @@ override_dh_auto_install:
# Install binary
install -Dm755 dms debian/dms-git/usr/bin/dms
- # Install QML files from git clone
- mkdir -p debian/dms-git/usr/share/quickshell/dms
- cp -rL dms-git-repo/* debian/dms-git/usr/share/quickshell/dms/
-
- # Remove unnecessary directories
- rm -rf debian/dms-git/usr/share/quickshell/dms/core
- rm -rf debian/dms-git/usr/share/quickshell/dms/distro
-
# Install systemd user service
install -Dm644 dms-git-repo/assets/systemd/dms.service \
debian/dms-git/usr/lib/systemd/user/dms.service
@@ -80,14 +72,6 @@ override_dh_auto_install:
install -Dm644 dms-git-repo/assets/danklogo.svg \
debian/dms-git/usr/share/icons/hicolor/scalable/apps/danklogo.svg
- # Create DMS Version file
- @if [ -f dms-git-repo/.dms-version ]; then \
- . dms-git-repo/.dms-version; \
- echo "$$VERSION" > debian/dms-git/usr/share/quickshell/dms/VERSION; \
- else \
- echo "dev" > debian/dms-git/usr/share/quickshell/dms/VERSION; \
- fi
-
override_dh_auto_clean:
# Don't delete dms-git-repo directory - it's part of the source package (native format)
# Clean up build artifacts
diff --git a/distro/void/srcpkgs/dms-git/template b/distro/void/srcpkgs/dms-git/template
index f2a82362b..dc5656604 100644
--- a/distro/void/srcpkgs/dms-git/template
+++ b/distro/void/srcpkgs/dms-git/template
@@ -8,16 +8,18 @@
# without first providing the source archive.
#
# The binary is built with the `distro_binary` build tag (drops the in-app
-# self-update command).
+# self-update command) and `withshell` (embeds the Quickshell UI; the shell
+# no longer discovers an installed tree, so an unembedded binary has no UI).
pkgname=dms-git
version=1.4.6.git
revision=1
create_wrksrc=yes
build_style=go
build_wrksrc="core"
+hostmakedepends="make"
go_import_path="github.com/AvengeMedia/DankMaterialShell/core"
go_package="${go_import_path}/cmd/dms"
-go_build_tags="distro_binary"
+go_build_tags="distro_binary withshell"
go_ldflags="-X main.Version=${version}"
short_desc="DankMaterialShell — Material 3 desktop shell for Wayland (git variant)"
maintainer="AvengeMedia "
@@ -34,11 +36,11 @@ provides="dms-${version}_${revision}"
# Optional feature deps are listed in distro/void/README.md.
depends="quickshell accountsservice dgop matugen dbus elogind"
-post_install() {
- # QML shell tree (build_style=go already installed the dms binary)
- vmkdir usr/share/quickshell/dms
- vcopy "${wrksrc}/quickshell/*" usr/share/quickshell/dms
+pre_build() {
+ make sync-shell
+}
+post_install() {
# Desktop entry + icon
vinstall "${wrksrc}/assets/dms-open.desktop" 644 usr/share/applications
vinstall "${wrksrc}/assets/com.danklinux.dms.desktop" 644 usr/share/applications
diff --git a/flake.nix b/flake.nix
index ef26355b3..5ef1fe6b1 100644
--- a/flake.nix
+++ b/flake.nix
@@ -111,7 +111,7 @@
inherit version;
pname = "dms-shell";
src = ./core;
- vendorHash = "sha256-nvxFHQhOfBGl3h51fgYDb39K0NCj+H8mAEyKr1qOwJQ=";
+ vendorHash = "sha256-//ya3Yb25/kEBeT6M4bO8Egi+Tb7iiUwevXlzTyIgqA=";
subPackages = [ "cmd/dms" ];