1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

Compare commits

..

6 Commits

Author SHA1 Message Date
bbedward
4a68ce35a3 hyprland: remove focus grab from dankpopout 2025-11-16 11:44:59 -05:00
bbedward
eb4655fcbc rebase woes 2025-11-16 11:37:19 -05:00
bbedward
6eb349c9d4 barmask: fix multi-screen handling 2025-11-16 11:33:47 -05:00
bbedward
0a8a7895b3 hyprland: use FocusGrab for ondemand windows 2025-11-16 11:33:47 -05:00
bbedward
73c82a4dd9 dankbar/mask: extra polish for Hyprland maybe 2025-11-16 11:33:47 -05:00
bbedward
ccf28fc4e7 dankbar: add a mask while popouts are open
- Retains ability to click items on the bar, while another is open
2025-11-16 11:32:47 -05:00
471 changed files with 27789 additions and 61758 deletions

View File

@@ -1,4 +1,8 @@
#!/usr/bin/env bash
#!/bin/bash
# DISABLED for now
exit 0
set -euo pipefail
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -6,61 +10,18 @@ REPO_ROOT="$(cd "$HOOK_DIR/.." && pwd)"
cd "$REPO_ROOT"
# =============================================================================
# Go CI checks (when core/ files are staged)
# =============================================================================
STAGED_CORE_FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep '^core/' || true)
if [[ -n "$STAGED_CORE_FILES" ]]; then
echo "Go files staged in core/, running CI checks..."
cd "$REPO_ROOT/core"
# Format check
echo " Checking gofmt..."
UNFORMATTED=$(gofmt -s -l . 2>/dev/null || true)
if [[ -n "$UNFORMATTED" ]]; then
echo "The following files are not formatted:"
echo "$UNFORMATTED"
echo ""
echo "Run: cd core && gofmt -s -w ."
exit 1
fi
# golangci-lint
if command -v golangci-lint &>/dev/null; then
echo " Running golangci-lint..."
golangci-lint run ./...
else
echo " Warning: golangci-lint not installed, skipping lint"
echo " Install: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"
fi
# Tests
echo " Running tests..."
go test ./... >/dev/null
# Build checks
echo " Building..."
mkdir -p bin
go build -buildvcs=false -o bin/dms ./cmd/dms
go build -buildvcs=false -o bin/dms-distro -tags distro_binary ./cmd/dms
go build -buildvcs=false -o bin/dankinstall ./cmd/dankinstall
echo "All Go CI checks passed!"
cd "$REPO_ROOT"
if [[ -z "${POEDITOR_API_TOKEN:-}" ]] || [[ -z "${POEDITOR_PROJECT_ID:-}" ]]; then
exit 0
fi
# =============================================================================
# i18n sync check (DISABLED for now)
# =============================================================================
# if [[ -n "${POEDITOR_API_TOKEN:-}" ]] && [[ -n "${POEDITOR_PROJECT_ID:-}" ]]; then
# if command -v python3 &>/dev/null; then
# if ! python3 scripts/i18nsync.py check &>/dev/null; then
# echo "Translations out of sync"
# echo "Run: python3 scripts/i18nsync.py sync"
# exit 1
# fi
# fi
# fi
if ! command -v python3 &>/dev/null; then
exit 0
fi
if ! python3 scripts/i18nsync.py check &>/dev/null; then
echo "Translations out of sync"
echo "run python3 scripts/i18nsync.py sync"
exit 1
fi
exit 0

View File

@@ -1,4 +1,4 @@
name: DMS Copr Stable Release
name: DMS Copr Stable Release (Manual)
on:
workflow_dispatch:

View File

@@ -3,22 +3,13 @@ name: Go CI
on:
push:
branches:
- "**"
- '**'
paths:
- "core/**"
- ".github/workflows/go-ci.yml"
pull_request:
branches: [master, main]
paths:
- "core/**"
- ".github/workflows/go-ci.yml"
concurrency:
group: go-ci-${{ github.ref }}
cancel-in-progress: true
- 'core/**'
- '.github/workflows/go-ci.yml'
jobs:
lint-and-test:
test:
runs-on: ubuntu-latest
defaults:
run:
@@ -41,20 +32,11 @@ jobs:
exit 1
fi
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: v2.6
working-directory: core
- name: Test
run: go test -v ./...
- name: Build dms
run: go build -v ./cmd/dms
- name: Build dms (distropkg)
run: go build -v -tags distro_binary ./cmd/dms
- name: Build dankinstall
run: go build -v ./cmd/dankinstall

View File

@@ -132,40 +132,38 @@ jobs:
runs-on: ubuntu-latest
needs: build-core
steps:
- name: Create GitHub App token
id: app_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ steps.app_token.outputs.token }}
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Update VERSION
env:
GH_TOKEN: ${{ steps.app_token.outputs.token }}
run: |
set -euo pipefail
git config user.name "dms-ci[bot]"
git config user.email "dms-ci[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
version="${GITHUB_REF#refs/tags/}"
version_no_v="${version#v}"
echo "Updating to version: $version"
# Update VERSION file in quickshell/
echo "${version}" > quickshell/VERSION
git add quickshell/VERSION
if ! git diff --cached --quiet; then
git commit -m "chore: bump version to $version"
git pull --rebase origin master
git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git HEAD:master
git push origin HEAD:master || git push origin HEAD:main
echo "Pushed version updates to master"
else
echo "No version changes needed"
fi
# Force-push the tag to point to the commit with updated VERSION
git tag -f "${version}"
git push -f https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git "${version}"
git push -f origin "${version}"
release:
runs-on: ubuntu-24.04
@@ -388,68 +386,6 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
trigger-obs-update:
runs-on: ubuntu-latest
needs: release
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install OSC
run: |
sudo apt-get update
sudo apt-get install -y osc
mkdir -p ~/.config/osc
cat > ~/.config/osc/oscrc << EOF
[general]
apiurl = https://api.opensuse.org
[https://api.opensuse.org]
user = ${{ secrets.OBS_USERNAME }}
pass = ${{ secrets.OBS_PASSWORD }}
EOF
chmod 600 ~/.config/osc/oscrc
- name: Update OBS packages
run: |
VERSION="${{ github.ref_name }}"
cd distro
bash scripts/obs-upload.sh dms "Update to $VERSION"
trigger-ppa-update:
runs-on: ubuntu-latest
needs: release
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
debhelper \
devscripts \
dput \
lftp \
build-essential \
fakeroot \
dpkg-dev
- name: Configure GPG
env:
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
run: |
echo "$GPG_KEY" | gpg --import
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | awk '{print $2}' | cut -d'/' -f2)
echo "DEBSIGN_KEYID=$GPG_KEY_ID" >> $GITHUB_ENV
- name: Upload to PPA
run: |
VERSION="${{ github.ref_name }}"
cd distro/ubuntu/ppa
bash create-and-upload.sh ../dms dms questing
copr-build:
runs-on: ubuntu-latest
needs: release
@@ -597,10 +533,7 @@ jobs:
%{_builddir}/dms-cli completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
%{_builddir}/dms-cli completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
install -Dm644 assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
install -Dm644 assets/dms-open.desktop %{buildroot}%{_datadir}/applications/dms-open.desktop
install -Dm644 assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
install -Dm644 %{_builddir}/dms-qml/assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
@@ -626,8 +559,6 @@ jobs:
%doc README.md CONTRIBUTING.md
%{_datadir}/quickshell/dms/
%{_userunitdir}/dms.service
%{_datadir}/applications/dms-open.desktop
%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
%files -n dms-cli
%{_bindir}/dms

View File

@@ -1,288 +0,0 @@
name: Update OBS Packages
on:
workflow_dispatch:
inputs:
package:
description: 'Package to update (dms, dms-git, or all)'
required: false
default: 'all'
rebuild_release:
description: 'Release number for rebuilds (e.g., 2, 3, 4 to increment spec Release)'
required: false
default: ''
push:
tags:
- 'v*'
schedule:
- cron: '0 */3 * * *' # Every 3 hours for dms-git builds
jobs:
check-updates:
name: Check for updates
runs-on: ubuntu-latest
outputs:
has_updates: ${{ steps.check.outputs.has_updates }}
packages: ${{ steps.check.outputs.packages }}
version: ${{ steps.check.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install OSC
run: |
sudo apt-get update
sudo apt-get install -y osc
mkdir -p ~/.config/osc
cat > ~/.config/osc/oscrc << EOF
[general]
apiurl = https://api.opensuse.org
[https://api.opensuse.org]
user = ${{ secrets.OBS_USERNAME }}
pass = ${{ secrets.OBS_PASSWORD }}
EOF
chmod 600 ~/.config/osc/oscrc
- name: Check for updates
id: check
run: |
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
echo "packages=dms" >> $GITHUB_OUTPUT
VERSION="${GITHUB_REF#refs/tags/}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "Triggered by tag: $VERSION (always update)"
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
echo "packages=dms-git" >> $GITHUB_OUTPUT
echo "Checking if dms-git source has changed..."
# Get latest commit hash from master branch
LATEST_COMMIT=$(git rev-parse origin/master 2>/dev/null || git rev-parse master 2>/dev/null || echo "")
if [[ -z "$LATEST_COMMIT" ]]; then
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "Could not determine git commit, proceeding with update"
else
# Check OBS for last uploaded commit
OBS_BASE="$HOME/.cache/osc-checkouts"
mkdir -p "$OBS_BASE"
OBS_PROJECT="home:AvengeMedia:dms-git"
if [[ -d "$OBS_BASE/$OBS_PROJECT/dms-git" ]]; then
cd "$OBS_BASE/$OBS_PROJECT/dms-git"
osc up -q 2>/dev/null || true
# Check tarball age - if older than 3 hours, update needed
if [[ -f "dms-git-source.tar.gz" ]]; then
TARBALL_MTIME=$(stat -c%Y "dms-git-source.tar.gz" 2>/dev/null || echo "0")
CURRENT_TIME=$(date +%s)
AGE_SECONDS=$((CURRENT_TIME - TARBALL_MTIME))
AGE_HOURS=$((AGE_SECONDS / 3600))
# If tarball is older than 3 hours, check for new commits
if [[ $AGE_HOURS -ge 3 ]]; then
# Check if there are new commits in the last 3 hours
cd "${{ github.workspace }}"
NEW_COMMITS=$(git log --since="3 hours ago" --oneline origin/master 2>/dev/null | wc -l)
if [[ $NEW_COMMITS -gt 0 ]]; then
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 New commits detected in last 3 hours, update needed"
else
echo "has_updates=false" >> $GITHUB_OUTPUT
echo "📋 No new commits in last 3 hours, skipping update"
fi
else
echo "has_updates=false" >> $GITHUB_OUTPUT
echo "📋 Recent upload exists (< 3 hours), skipping update"
fi
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 No existing tarball in OBS, update needed"
fi
cd "${{ github.workspace }}"
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 First upload to OBS, update needed"
fi
fi
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "Manual trigger: ${{ github.event.inputs.package }}"
else
echo "packages=all" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
fi
update-obs:
name: Upload to OBS
needs: check-updates
runs-on: ubuntu-latest
if: |
github.event_name == 'workflow_dispatch' ||
needs.check-updates.outputs.has_updates == 'true'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine packages to update
id: packages
run: |
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
echo "packages=dms" >> $GITHUB_OUTPUT
VERSION="${GITHUB_REF#refs/tags/}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Triggered by tag: $VERSION"
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
echo "Triggered by schedule: updating git package"
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
echo "Manual trigger: ${{ github.event.inputs.package }}"
else
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
fi
- name: Update dms-git spec version
if: contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all'
run: |
# Get commit info for dms-git versioning
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
COMMIT_COUNT=$(git rev-list --count HEAD)
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.2")
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
echo "📦 Updating dms-git.spec to version: $NEW_VERSION"
# Update version in spec
sed -i "s/^Version:.*/Version: $NEW_VERSION/" distro/opensuse/dms-git.spec
# Add changelog entry
DATE_STR=$(date "+%a %b %d %Y")
CHANGELOG_ENTRY="* $DATE_STR Avenge Media <AvengeMedia.US@gmail.com> - ${NEW_VERSION}-1\n- Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)"
sed -i "/%changelog/a\\$CHANGELOG_ENTRY" distro/opensuse/dms-git.spec
- name: Update Debian dms-git changelog version
if: contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all'
run: |
# Get commit info for dms-git versioning
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
COMMIT_COUNT=$(git rev-list --count HEAD)
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.2")
# Debian version format: 0.6.2+git2256.9162e314
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
echo "📦 Updating Debian dms-git changelog to version: $NEW_VERSION"
CHANGELOG_DATE=$(date -R)
CHANGELOG_FILE="distro/debian/dms-git/debian/changelog"
# Get current version from changelog
CURRENT_VERSION=$(head -1 "$CHANGELOG_FILE" | sed 's/.*(\([^)]*\)).*/\1/')
echo "Current Debian version: $CURRENT_VERSION"
echo "New version: $NEW_VERSION"
# Only update if version changed
if [ "$CURRENT_VERSION" != "$NEW_VERSION" ]; then
# Create new changelog entry at top
TEMP_CHANGELOG=$(mktemp)
cat > "$TEMP_CHANGELOG" << EOF
dms-git ($NEW_VERSION) nightly; urgency=medium
* Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)
-- Avenge Media <AvengeMedia.US@gmail.com> $CHANGELOG_DATE
EOF
# Prepend to existing changelog
cat "$CHANGELOG_FILE" >> "$TEMP_CHANGELOG"
mv "$TEMP_CHANGELOG" "$CHANGELOG_FILE"
echo "✓ Updated Debian changelog: $CURRENT_VERSION → $NEW_VERSION"
else
echo "✓ Debian changelog already at version $NEW_VERSION"
fi
- name: Update dms stable version
if: steps.packages.outputs.version != ''
run: |
VERSION="${{ steps.packages.outputs.version }}"
VERSION_NO_V="${VERSION#v}"
echo "Updating packaging to version $VERSION_NO_V"
# Update openSUSE dms spec (stable only)
sed -i "s/^Version:.*/Version: $VERSION_NO_V/" distro/opensuse/dms.spec
# Update Debian _service files
for service in distro/debian/*/_service; do
if [[ -f "$service" ]]; then
sed -i "s|<param name=\"revision\">v[0-9.]*</param>|<param name=\"revision\">$VERSION</param>|" "$service"
fi
done
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
- name: Install OSC
run: |
sudo apt-get update
sudo apt-get install -y osc
mkdir -p ~/.config/osc
cat > ~/.config/osc/oscrc << EOF
[general]
apiurl = https://api.opensuse.org
[https://api.opensuse.org]
user = ${{ secrets.OBS_USERNAME }}
pass = ${{ secrets.OBS_PASSWORD }}
EOF
chmod 600 ~/.config/osc/oscrc
- name: Upload to OBS
env:
FORCE_REBUILD: ${{ github.event_name == 'workflow_dispatch' && 'true' || '' }}
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
run: |
PACKAGES="${{ steps.packages.outputs.packages }}"
MESSAGE="Automated update from GitHub Actions"
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
MESSAGE="Update to ${{ steps.packages.outputs.version }}"
fi
if [[ "$PACKAGES" == "all" ]]; then
bash distro/scripts/obs-upload.sh dms "$MESSAGE"
bash distro/scripts/obs-upload.sh dms-git "Automated git update"
else
bash distro/scripts/obs-upload.sh "$PACKAGES" "$MESSAGE"
fi
- name: Summary
run: |
echo "### OBS Package Update Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Packages**: ${{ steps.packages.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
echo "- **Version**: ${{ steps.packages.outputs.version }}" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.check-updates.outputs.has_updates }}" == "false" ]]; then
echo "- **Status**: Skipped (no changes detected)" >> $GITHUB_STEP_SUMMARY
fi
echo "- **Project**: https://build.opensuse.org/project/show/home:AvengeMedia" >> $GITHUB_STEP_SUMMARY

View File

@@ -1,123 +0,0 @@
name: Update PPA Packages
on:
workflow_dispatch:
inputs:
package:
description: 'Package to upload (dms, dms-git, dms-greeter, or all)'
required: false
default: 'dms-git'
rebuild_release:
description: 'Release number for rebuilds (e.g., 2, 3, 4 for ppa2, ppa3, ppa4)'
required: false
default: ''
schedule:
- cron: '0 */3 * * *' # Every 3 hours for dms-git builds
jobs:
upload-ppa:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
cache: false
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
debhelper \
devscripts \
dput \
lftp \
build-essential \
fakeroot \
dpkg-dev
- name: Configure GPG
env:
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
run: |
echo "$GPG_KEY" | gpg --import
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | awk '{print $2}' | cut -d'/' -f2)
echo "DEBSIGN_KEYID=$GPG_KEY_ID" >> $GITHUB_ENV
- name: Determine packages to upload
id: packages
run: |
if [[ "${{ github.event_name }}" == "schedule" ]]; then
echo "packages=dms-git" >> $GITHUB_OUTPUT
echo "Triggered by schedule: uploading git package"
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
echo "Manual trigger: ${{ github.event.inputs.package }}"
else
echo "packages=dms-git" >> $GITHUB_OUTPUT
fi
- name: Upload to PPA
env:
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
run: |
PACKAGES="${{ steps.packages.outputs.packages }}"
if [[ "$PACKAGES" == "all" ]]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Uploading dms to PPA..."
if [ -n "$REBUILD_RELEASE" ]; then
echo "🔄 Using rebuild release number: ppa$REBUILD_RELEASE"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
bash distro/scripts/ppa-upload.sh "distro/ubuntu/dms" dms questing
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Uploading dms-git to PPA..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
bash distro/scripts/ppa-upload.sh "distro/ubuntu/dms-git" dms-git questing
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Uploading dms-greeter to PPA..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
bash distro/scripts/ppa-upload.sh "distro/ubuntu/dms-greeter" danklinux questing
else
PPA_NAME="$PACKAGES"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Uploading $PACKAGES to PPA..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
bash distro/scripts/ppa-upload.sh "distro/ubuntu/$PACKAGES" "$PPA_NAME" questing
fi
- name: Summary
run: |
echo "### PPA Package Upload Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Packages**: ${{ steps.packages.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
PACKAGES="${{ steps.packages.outputs.packages }}"
if [[ "$PACKAGES" == "all" ]]; then
echo "- **PPA dms**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
echo "- **PPA dms-git**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
echo "- **PPA danklinux**: https://launchpad.net/~avengemedia/+archive/ubuntu/danklinux/+packages" >> $GITHUB_STEP_SUMMARY
elif [[ "$PACKAGES" == "dms" ]]; then
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
elif [[ "$PACKAGES" == "dms-git" ]]; then
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
elif [[ "$PACKAGES" == "dms-greeter" ]]; then
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/danklinux/+packages" >> $GITHUB_STEP_SUMMARY
fi
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
echo "- **Version**: ${{ steps.packages.outputs.version }}" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "Builds will appear once Launchpad processes the uploads." >> $GITHUB_STEP_SUMMARY

View File

@@ -1,7 +1,6 @@
name: Update Vendor Hash
on:
workflow_dispatch:
push:
paths:
- "core/go.mod"
@@ -9,25 +8,14 @@ on:
branches:
- master
permissions:
contents: write
jobs:
update-vendor-hash:
runs-on: ubuntu-latest
steps:
- name: Create GitHub App token
id: app_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.app_token.outputs.token }}
- name: Install Nix
uses: cachix/install-nix-action@v31
@@ -35,32 +23,68 @@ jobs:
- name: Update vendorHash in flake.nix
run: |
set -euo pipefail
# Try to build and capture the expected hash from error message
echo "Attempting nix build to get new vendorHash..."
if output=$(nix build .#dmsCli 2>&1); then
echo "Build succeeded, no hash update needed"
exit 0
fi
# Extract the expected hash from the error message
new_hash=$(echo "$output" | grep -oP "got:\s+\K\S+" | head -n1)
[ -n "$new_hash" ] || { echo "Could not extract new vendorHash"; echo "$output"; exit 1; }
if [ -z "$new_hash" ]; then
echo "Could not extract new vendorHash from build output"
echo "Build output:"
echo "$output"
exit 1
fi
echo "New vendorHash: $new_hash"
# Get current hash from flake.nix
current_hash=$(grep -oP 'vendorHash = "\K[^"]+' flake.nix)
[ "$current_hash" = "$new_hash" ] && { echo "vendorHash already up to date"; exit 0; }
echo "Current vendorHash: $current_hash"
if [ "$current_hash" = "$new_hash" ]; then
echo "vendorHash is already up to date"
exit 0
fi
# Update the hash in flake.nix
sed -i "s|vendorHash = \"$current_hash\"|vendorHash = \"$new_hash\"|" flake.nix
# Verify the build works with the new hash
echo "Verifying build with new vendorHash..."
nix build .#dmsCli
echo "vendorHash updated successfully!"
- name: Commit and push vendorHash update
env:
GH_TOKEN: ${{ steps.app_token.outputs.token }}
run: |
set -euo pipefail
if ! git diff --quiet flake.nix; then
git config user.name "dms-ci[bot]"
git config user.email "dms-ci[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add flake.nix
git commit -m "nix: update vendorHash for go.mod changes" || exit 0
git pull --rebase origin master
git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git HEAD:master
git commit -m "nix: update vendorHash for go.mod changes"
for attempt in 1 2 3; do
if git push; then
echo "Successfully pushed vendorHash update"
exit 0
fi
echo "Push attempt $attempt failed, pulling and retrying..."
git pull --rebase
sleep $((attempt*2))
done
echo "Failed to push after retries" >&2
exit 1
else
echo "No changes to flake.nix"
fi

6
.gitignore vendored
View File

@@ -136,9 +136,3 @@ go.work.sum
# .vscode/
bin/
# Extracted source trees in Ubuntu package directories
distro/ubuntu/*/dms-git-repo/
distro/ubuntu/*/DankMaterialShell-*/
distro/ubuntu/danklinux/*/dsearch-*/
distro/ubuntu/danklinux/*/dgop-*/

View File

@@ -2,50 +2,28 @@
Contributions are welcome and encouraged.
To contribute fork this repository, make your changes, and open a pull request.
## Formatting
## Setup
The preferred tool for formatting files is [qmlfmt](https://github.com/jesperhh/qmlfmt) (also available on aur as qmlfmt-git). It actually kinda sucks, but `qmlformat` doesn't work with null safe operators and ternarys and pragma statements and a bunch of other things that are supported.
Enable pre-commit hooks to catch CI failures before pushing:
We need some consistent style, so this at least gives the same formatter that Qt Creator uses.
```bash
git config core.hooksPath .githooks
```
## VSCode Setup
This is a monorepo, the easiest thing to do is to open an editor in either `quickshell`, `core`, or both depending on which part of the project you are working on.
### QML (`quickshell` directory)
1. Install the [QML Extension](https://doc.qt.io/vscodeext/)
2. Configure `ctrl+shift+p` -> user preferences (json) with qmlls path
You can configure it to format on save in vscode by configuring the "custom local formatters" extension then adding this to settings json.
```json
{
"qt-qml.doNotAskForQmllsDownload": true,
"qt-qml.qmlls.customExePath": "/usr/lib/qt6/bin/qmlls"
}
"customLocalFormatters.formatters": [
{
"command": "sh -c \"qmlfmt -t 4 -i 4 -b 250 | sed 's/pragma ComponentBehavior$/pragma ComponentBehavior: Bound/g'\"",
"languages": ["qml"]
}
],
"[qml]": {
"editor.defaultFormatter": "jkillian.custom-local-formatters",
"editor.formatOnSave": true
},
```
3. Create empty `.qmlls.ini` file in `quickshell/` directory
```bash
cd quickshell
touch .qmlls.ini
```
4. Restart dms to generate the `.qmlls.ini` file
5. Make your changes, test, and open a pull request.
### GO (`core` directory)
1. Install the [Go Extension](https://code.visualstudio.com/docs/languages/go)
2. Ensure code is formatted with `make fmt`
3. Add appropriate test coverage and ensure tests pass with `make test`
4. Run `go mod tidy`
5. Open pull request
Sometimes it just breaks code though. Like turning `"_\""` into `"_""`, so you may not want to do formatOnSave.
## Pull request

156
Makefile
View File

@@ -1,156 +0,0 @@
# Root Makefile for DankMaterialShell (DMS)
# Orchestrates building, installation, and systemd management
# Build configuration
BINARY_NAME=dms
CORE_DIR=core
BUILD_DIR=$(CORE_DIR)/bin
PREFIX ?= /usr/local
INSTALL_DIR=$(PREFIX)/bin
DATA_DIR=$(PREFIX)/share
ICON_DIR=$(DATA_DIR)/icons/hicolor/scalable/apps
USER_HOME := $(if $(SUDO_USER),$(shell getent passwd $(SUDO_USER) | cut -d: -f6),$(HOME))
SYSTEMD_USER_DIR=$(USER_HOME)/.config/systemd/user
SHELL_DIR=quickshell
SHELL_INSTALL_DIR=$(DATA_DIR)/quickshell/dms
ASSETS_DIR=assets
APPLICATIONS_DIR=$(DATA_DIR)/applications
.PHONY: all build clean 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
build:
@echo "Building $(BINARY_NAME)..."
@$(MAKE) -C $(CORE_DIR) build
@echo "Build complete"
clean:
@echo "Cleaning build artifacts..."
@$(MAKE) -C $(CORE_DIR) clean
@echo "Clean complete"
# Installation targets
install-bin:
@echo "Installing $(BINARY_NAME) to $(INSTALL_DIR)..."
@install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME) $(INSTALL_DIR)/$(BINARY_NAME)
@echo "Binary installed"
install-shell:
@echo "Installing shell files to $(SHELL_INSTALL_DIR)..."
@mkdir -p $(SHELL_INSTALL_DIR)
@cp -r $(SHELL_DIR)/* $(SHELL_INSTALL_DIR)/
@rm -rf $(SHELL_INSTALL_DIR)/.git* $(SHELL_INSTALL_DIR)/.github
@$(MAKE) --no-print-directory -C $(CORE_DIR) print-version > $(SHELL_INSTALL_DIR)/VERSION
@echo "Shell files installed"
install-completions:
@echo "Installing shell completions..."
@mkdir -p $(DATA_DIR)/bash-completion/completions
@mkdir -p $(DATA_DIR)/zsh/site-functions
@mkdir -p $(DATA_DIR)/fish/vendor_completions.d
@$(BUILD_DIR)/$(BINARY_NAME) completion bash > $(DATA_DIR)/bash-completion/completions/dms 2>/dev/null || true
@$(BUILD_DIR)/$(BINARY_NAME) completion zsh > $(DATA_DIR)/zsh/site-functions/_dms 2>/dev/null || true
@$(BUILD_DIR)/$(BINARY_NAME) completion fish > $(DATA_DIR)/fish/vendor_completions.d/dms.fish 2>/dev/null || true
@echo "Shell completions installed"
install-systemd:
@echo "Installing systemd user service..."
@mkdir -p $(SYSTEMD_USER_DIR)
@if [ -n "$(SUDO_USER)" ]; then chown -R $(SUDO_USER):$(SUDO_USER) $(SYSTEMD_USER_DIR); fi
@sed 's|/usr/bin/dms|$(INSTALL_DIR)/dms|g' $(ASSETS_DIR)/systemd/dms.service > $(SYSTEMD_USER_DIR)/dms.service
@chmod 644 $(SYSTEMD_USER_DIR)/dms.service
@if [ -n "$(SUDO_USER)" ]; then chown $(SUDO_USER):$(SUDO_USER) $(SYSTEMD_USER_DIR)/dms.service; fi
@echo "Systemd service installed to $(SYSTEMD_USER_DIR)/dms.service"
install-icon:
@echo "Installing icon..."
@install -D -m 644 $(ASSETS_DIR)/danklogo.svg $(ICON_DIR)/danklogo.svg
@gtk-update-icon-cache -q $(DATA_DIR)/icons/hicolor 2>/dev/null || true
@echo "Icon installed"
install-desktop:
@echo "Installing desktop entry..."
@install -D -m 644 $(ASSETS_DIR)/dms-open.desktop $(APPLICATIONS_DIR)/dms-open.desktop
@update-desktop-database -q $(APPLICATIONS_DIR) 2>/dev/null || true
@echo "Desktop entry installed"
install: build install-bin install-shell install-completions install-systemd install-icon install-desktop
@echo ""
@echo "Installation complete!"
@echo ""
@echo "=== The DMS Team! ==="
# Uninstallation targets
uninstall-bin:
@echo "Removing $(BINARY_NAME) from $(INSTALL_DIR)..."
@rm -f $(INSTALL_DIR)/$(BINARY_NAME)
@echo "Binary removed"
uninstall-shell:
@echo "Removing shell files from $(SHELL_INSTALL_DIR)..."
@rm -rf $(SHELL_INSTALL_DIR)
@echo "Shell files removed"
uninstall-completions:
@echo "Removing shell completions..."
@rm -f $(DATA_DIR)/bash-completion/completions/dms
@rm -f $(DATA_DIR)/zsh/site-functions/_dms
@rm -f $(DATA_DIR)/fish/vendor_completions.d/dms.fish
@echo "Shell completions removed"
uninstall-systemd:
@echo "Removing systemd user service..."
@rm -f $(SYSTEMD_USER_DIR)/dms.service
@echo "Systemd service removed"
@echo "Note: Stop/disable service manually if running: systemctl --user stop dms"
uninstall-icon:
@echo "Removing icon..."
@rm -f $(ICON_DIR)/danklogo.svg
@gtk-update-icon-cache -q $(DATA_DIR)/icons/hicolor 2>/dev/null || true
@echo "Icon removed"
uninstall-desktop:
@echo "Removing desktop entry..."
@rm -f $(APPLICATIONS_DIR)/dms-open.desktop
@update-desktop-database -q $(APPLICATIONS_DIR) 2>/dev/null || true
@echo "Desktop entry removed"
uninstall: uninstall-systemd uninstall-desktop uninstall-icon uninstall-completions uninstall-shell uninstall-bin
@echo ""
@echo "Uninstallation complete!"
# Target assist
help:
@echo "Available targets:"
@echo ""
@echo "Build:"
@echo " all (default) - Build the DMS binary"
@echo " build - Same as 'all'"
@echo " clean - Clean build artifacts"
@echo ""
@echo "Install:"
@echo " install - Build and install everything (requires sudo)"
@echo " install-bin - Install only the binary"
@echo " install-shell - Install only shell files"
@echo " install-completions - Install only shell completions"
@echo " install-systemd - Install only systemd service"
@echo " install-icon - Install only icon"
@echo " install-desktop - Install only desktop entry"
@echo ""
@echo "Uninstall:"
@echo " uninstall - Remove everything (requires sudo)"
@echo " uninstall-bin - Remove only the binary"
@echo " uninstall-shell - Remove only shell files"
@echo " uninstall-completions - Remove only shell completions"
@echo " uninstall-systemd - Remove only systemd service"
@echo " uninstall-icon - Remove only icon"
@echo " uninstall-desktop - Remove only desktop entry"
@echo ""
@echo "Usage:"
@echo " sudo make install - Build and install DMS"
@echo " sudo make uninstall - Remove DMS"
@echo " systemctl --user enable --now dms - Enable and start service"

View File

@@ -15,11 +15,11 @@
[![GitHub release](https://img.shields.io/github/v/release/AvengeMedia/DankMaterialShell?style=for-the-badge&labelColor=101418&color=9ccbfb)](https://github.com/AvengeMedia/DankMaterialShell/releases)
[![AUR version](https://img.shields.io/aur/version/dms-shell-bin?style=for-the-badge&labelColor=101418&color=9ccbfb)](https://aur.archlinux.org/packages/dms-shell-bin)
[![AUR version (git)](https://img.shields.io/aur/version/dms-shell-git?style=for-the-badge&labelColor=101418&color=9ccbfb&label=AUR%20(git))](https://aur.archlinux.org/packages/dms-shell-git)
[![Ko-Fi donate](https://img.shields.io/badge/donate-kofi?style=for-the-badge&logo=ko-fi&logoColor=ffffff&label=ko-fi&labelColor=101418&color=f16061&link=https%3A%2F%2Fko-fi.com%2Fdanklinux)](https://ko-fi.com/danklinux)
[![Ko-Fi donate](https://img.shields.io/badge/donate-kofi?style=for-the-badge&logo=ko-fi&logoColor=ffffff&label=ko-fi&labelColor=101418&color=f16061&link=https%3A%2F%2Fko-fi.com%2Favengemediallc)](https://ko-fi.com/avengemediallc)
</div>
DankMaterialShell is a complete desktop shell for [niri](https://github.com/YaLTeR/niri), [Hyprland](https://hyprland.org/), [MangoWC](https://github.com/DreamMaoMao/mangowc), [Sway](https://swaywm.org), [labwc](https://labwc.github.io/), and other Wayland compositors. It replaces waybar, swaylock, swayidle, mako, fuzzel, polkit, and everything else you'd normally stitch together to make a desktop.
DankMaterialShell is a complete desktop shell for [niri](https://github.com/YaLTeR/niri), [Hyprland](https://hyprland.org/), [MangoWC](https://github.com/DreamMaoMao/mangowc), [Sway](https://swaywm.org), and other Wayland compositors. It replaces waybar, swaylock, swayidle, mako, fuzzel, polkit, and everything else you'd normally stitch together to make a desktop.
## Repository Structure
@@ -105,7 +105,7 @@ Extend functionality with the [plugin registry](https://plugins.danklinux.com).
## Supported Compositors
Works best with [niri](https://github.com/YaLTeR/niri), [Hyprland](https://hyprland.org/), [Sway](https://swaywm.org/), [MangoWC](https://github.com/DreamMaoMao/mangowc), and [labwc](https://labwc.github.io/) with full workspace switching, overview integration, and monitor management. Other Wayland compositors work with reduced features.
Works best with [niri](https://github.com/YaLTeR/niri), [Hyprland](https://hyprland.org/), [Sway](https://swaywm.org/), and [MangoWC](https://github.com/DreamMaoMao/mangowc) with full workspace switching, overview integration, and monitor management. Other Wayland compositors work with reduced features.
[Compositor configuration guide](https://danklinux.com/docs/dankmaterialshell/compositors)
@@ -183,10 +183,6 @@ For documentation contributions, see [DankLinux-Docs](https://github.com/AvengeM
- [soramanew](https://github.com/soramanew) - [Caelestia](https://github.com/caelestia-dots/shell) inspiration
- [end-4](https://github.com/end-4) - [dots-hyprland](https://github.com/end-4/dots-hyprland) inspiration
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=AvengeMedia/DankMaterialShell&type=date&legend=top-left)](https://www.star-history.com/#AvengeMedia/DankMaterialShell&type=date&legend=top-left)
## License
MIT License - See [LICENSE](LICENSE) for details.

View File

@@ -1,10 +0,0 @@
[Desktop Entry]
Type=Application
Name=DMS Application Picker
Comment=Select an application to open links and files
Exec=dms open %u
Icon=danklogo
Terminal=false
NoDisplay=true
MimeType=x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/file;text/html;
Categories=Utility;

View File

@@ -1,105 +0,0 @@
version: "2"
linters:
enable:
- revive
settings:
revive:
rules:
- name: use-any
severity: error
errcheck:
check-type-assertions: false
check-blank: false
exclude-functions:
# Cleanup/destroy operations
- (io.Closer).Close
- (*os.File).Close
- (net.Conn).Close
- (*net.Conn).Close
# Signal handling
- (*os.Process).Signal
- (*os.Process).Kill
# DBus cleanup
- (*github.com/godbus/dbus/v5.Conn).RemoveMatchSignal
- (*github.com/godbus/dbus/v5.Conn).RemoveSignal
# Encoding to network connections (if conn is bad, nothing we can do)
- (*encoding/json.Encoder).Encode
- (net.Conn).Write
# Command execution where failure is expected/ignored
- (*os/exec.Cmd).Run
- (*os/exec.Cmd).Start
# Flush operations
- (*bufio.Writer).Flush
# Scanning user input
- fmt.Scanln
- fmt.Scanf
# Parse operations where default value is acceptable
- fmt.Sscanf
# Flag operations
- (*github.com/spf13/pflag.FlagSet).MarkHidden
# Binary encoding to buffer (can't fail for basic types)
- binary.Write
# File operations in cleanup paths
- os.Rename
- os.Remove
- os.RemoveAll
- (*os.File).WriteString
# Stdout/stderr writes (can't meaningfully handle failure)
- fmt.Fprintln
- fmt.Fprintf
- fmt.Fprint
# Writing to pipes (if pipe is bad, nothing we can do)
- (*io.PipeWriter).Write
- (*os.File).Write
exclusions:
rules:
# Exclude generated mocks from all linters
- path: internal/mocks/
linters:
- errcheck
- govet
- unused
- ineffassign
- staticcheck
- gosimple
- revive
- path: _test\.go
linters:
- errcheck
- govet
- unused
- ineffassign
- staticcheck
- gosimple
# Exclude cleanup/teardown method calls from errcheck
- linters:
- errcheck
text: "Error return value of `.+\\.(Destroy|Release|Stop|Close|Roundtrip|Store)` is not checked"
# Exclude internal state update methods that are best-effort
- linters:
- errcheck
text: "Error return value of `[mb]\\.\\w*(update|initialize|recreate|acquire|enumerate|list|List|Ensure|refresh|Lock)\\w*` is not checked"
# Exclude SetMode on wayland power controls (best-effort)
- linters:
- errcheck
text: "Error return value of `.+\\.SetMode` is not checked"
# Exclude AddMatchSignal which is best-effort monitoring setup
- linters:
- errcheck
text: "Error return value of `.+\\.AddMatchSignal` is not checked"
# Exclude wayland pkg from errcheck and ineffassign (generated code patterns)
- linters:
- errcheck
- ineffassign
path: pkg/go-wayland/
# Exclude proto pkg from ineffassign (generated protocol code)
- linters:
- ineffassign
path: internal/proto/
# binary.Write to bytes.Buffer can't fail
- linters:
- errcheck
text: "Error return value of `binary\\.Write` is not checked"

View File

@@ -28,31 +28,21 @@ packages:
outpkg: mocks_brightness
interfaces:
DBusConn:
github.com/AvengeMedia/DankMaterialShell/core/internal/server/network:
github.com/AvengeMedia/danklinux/internal/server/network:
config:
dir: "internal/mocks/network"
outpkg: mocks_network
interfaces:
Backend:
github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups:
github.com/AvengeMedia/danklinux/internal/server/cups:
config:
dir: "internal/mocks/cups"
outpkg: mocks_cups
interfaces:
CUPSClientInterface:
PkHelper:
config:
dir: "internal/mocks/cups_pkhelper"
outpkg: mocks_cups_pkhelper
github.com/AvengeMedia/DankMaterialShell/core/internal/server/evdev:
config:
dir: "internal/mocks/evdev"
outpkg: mocks_evdev
interfaces:
EvdevDevice:
github.com/AvengeMedia/DankMaterialShell/core/internal/version:
config:
dir: "internal/mocks/version"
outpkg: mocks_version
interfaces:
VersionFetcher:

View File

@@ -10,19 +10,16 @@ GO=go
GOFLAGS=-ldflags="-s -w"
# Version and build info
BASE_VERSION=$(shell git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.0.0")
COMMIT_COUNT=$(shell git rev-list --count HEAD 2>/dev/null || echo "0")
COMMIT_HASH=$(shell git rev-parse --short=8 HEAD 2>/dev/null || echo "unknown")
VERSION?=$(BASE_VERSION)+git$(COMMIT_COUNT).$(COMMIT_HASH)
BUILD_TIME?=$(shell date -u '+%Y-%m-%d_%H:%M:%S')
COMMIT?=$(COMMIT_HASH)
VERSION=$(shell git describe --tags --always 2>/dev/null || echo "dev")
BUILD_TIME=$(shell date -u '+%Y-%m-%d_%H:%M:%S')
COMMIT=$(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILD_LDFLAGS=-ldflags='-s -w -X main.Version=$(VERSION) -X main.buildTime=$(BUILD_TIME) -X main.commit=$(COMMIT)'
# 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 dankinstall dist clean install install-all install-dankinstall uninstall uninstall-all uninstall-dankinstall install-config uninstall-config test fmt vet deps help
# Default target
all: build
@@ -135,9 +132,6 @@ version: check-go
@echo "Build Time: $(BUILD_TIME)"
@echo "Commit: $(COMMIT)"
print-version:
@echo "$(VERSION)"
help:
@echo "Available targets:"
@echo " all - Build the main binary (dms) (default)"

View File

@@ -72,13 +72,6 @@ sudo make install # Install to /usr/local/bin/dms
## Development
**Setup pre-commit hooks:**
```bash
git config core.hooksPath .githooks
```
This runs gofmt, golangci-lint, tests, and builds before each commit when `core/` files are staged.
**Regenerating Wayland Protocol Bindings:**
```bash
go install github.com/rajveermalviya/go-wayland/cmd/go-wayland-scanner@latest

View File

@@ -28,14 +28,7 @@ var brightnessSetCmd = &cobra.Command{
Short: "Set brightness for a device",
Long: "Set brightness percentage (0-100) for a specific device",
Args: cobra.ExactArgs(2),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
includeDDC, _ := cmd.Flags().GetBool("ddc")
return getBrightnessDevices(includeDDC), cobra.ShellCompDirectiveNoFileComp
},
Run: runBrightnessSet,
Run: runBrightnessSet,
}
var brightnessGetCmd = &cobra.Command{
@@ -43,14 +36,7 @@ var brightnessGetCmd = &cobra.Command{
Short: "Get brightness for a device",
Long: "Get current brightness percentage for a specific device",
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
includeDDC, _ := cmd.Flags().GetBool("ddc")
return getBrightnessDevices(includeDDC), cobra.ShellCompDirectiveNoFileComp
},
Run: runBrightnessGet,
Run: runBrightnessGet,
}
func init() {
@@ -119,7 +105,9 @@ Global Flags:
brightnessCmd.AddCommand(brightnessListCmd, brightnessSetCmd, brightnessGetCmd)
}
func getAllBrightnessDevices(includeDDC bool) []brightness.Device {
func runBrightnessList(cmd *cobra.Command, args []string) {
includeDDC, _ := cmd.Flags().GetBool("ddc")
allDevices := []brightness.Device{}
sysfs, err := brightness.NewSysfsBackend()
@@ -150,13 +138,6 @@ func getAllBrightnessDevices(includeDDC bool) []brightness.Device {
}
}
return allDevices
}
func runBrightnessList(cmd *cobra.Command, args []string) {
includeDDC, _ := cmd.Flags().GetBool("ddc")
allDevices := getAllBrightnessDevices(includeDDC)
if len(allDevices) == 0 {
fmt.Println("No brightness devices found")
return
@@ -280,20 +261,31 @@ func runBrightnessSet(cmd *cobra.Command, args []string) {
log.Fatalf("Failed to set brightness for device: %s", deviceID)
}
func getBrightnessDevices(includeDDC bool) []string {
allDevices := getAllBrightnessDevices(includeDDC)
var deviceIDs []string
for _, device := range allDevices {
deviceIDs = append(deviceIDs, device.ID)
}
return deviceIDs
}
func runBrightnessGet(cmd *cobra.Command, args []string) {
deviceID := args[0]
includeDDC, _ := cmd.Flags().GetBool("ddc")
allDevices := getAllBrightnessDevices(includeDDC)
allDevices := []brightness.Device{}
sysfs, err := brightness.NewSysfsBackend()
if err == nil {
devices, err := sysfs.GetDevices()
if err == nil {
allDevices = append(allDevices, devices...)
}
}
if includeDDC {
ddc, err := brightness.NewDDCBackend()
if err == nil {
defer ddc.Close()
time.Sleep(100 * time.Millisecond)
devices, err := ddc.GetDevices()
if err == nil {
allDevices = append(allDevices, devices...)
}
}
}
for _, device := range allDevices {
if device.ID == deviceID {

View File

@@ -66,10 +66,6 @@ var ipcCmd = &cobra.Command{
Short: "Send IPC commands to running DMS shell",
Long: "Send IPC commands to running DMS shell (qs -c dms ipc <args>)",
PreRunE: findConfig,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
_ = findConfig(cmd, args)
return getShellIPCCompletions(args, toComplete), cobra.ShellCompDirectiveNoFileComp
},
Run: func(cmd *cobra.Command, args []string) {
runShellIPCCommand(args)
},
@@ -119,12 +115,6 @@ var pluginsInstallCmd = &cobra.Command{
Short: "Install a plugin by ID",
Long: "Install a DMS plugin from the registry using its ID (e.g., 'myPlugin'). Plugin names with spaces are also supported for backward compatibility.",
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getAvailablePluginIDs(), cobra.ShellCompDirectiveNoFileComp
},
Run: func(cmd *cobra.Command, args []string) {
if err := installPluginCLI(args[0]); err != nil {
log.Fatalf("Error installing plugin: %v", err)
@@ -137,12 +127,6 @@ var pluginsUninstallCmd = &cobra.Command{
Short: "Uninstall a plugin by ID",
Long: "Uninstall a DMS plugin using its ID (e.g., 'myPlugin'). Plugin names with spaces are also supported for backward compatibility.",
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getInstalledPluginIDs(), cobra.ShellCompDirectiveNoFileComp
},
Run: func(cmd *cobra.Command, args []string) {
if err := uninstallPluginCLI(args[0]); err != nil {
log.Fatalf("Error uninstalling plugin: %v", err)
@@ -156,7 +140,6 @@ func runVersion(cmd *cobra.Command, args []string) {
}
func startDebugServer() error {
server.CLIVersion = Version
return server.Start(true)
}
@@ -315,38 +298,6 @@ func installPluginCLI(idOrName string) error {
return nil
}
func getAvailablePluginIDs() []string {
registry, err := plugins.NewRegistry()
if err != nil {
return nil
}
pluginList, err := registry.List()
if err != nil {
return nil
}
var ids []string
for _, p := range pluginList {
ids = append(ids, p.ID)
}
return ids
}
func getInstalledPluginIDs() []string {
manager, err := plugins.NewManager()
if err != nil {
return nil
}
installed, err := manager.ListInstalled()
if err != nil {
return nil
}
return installed
}
func uninstallPluginCLI(idOrName string) error {
manager, err := plugins.NewManager()
if err != nil {
@@ -417,7 +368,6 @@ func getCommonCommands() []*cobra.Command {
pluginsCmd,
dank16Cmd,
brightnessCmd,
dpmsCmd,
keybindsCmd,
greeterCmd,
setupCmd,

View File

@@ -2,6 +2,7 @@ package main
import (
"fmt"
"os"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/dank16"
@@ -25,11 +26,9 @@ func init() {
dank16Cmd.Flags().Bool("alacritty", false, "Output in Alacritty terminal format")
dank16Cmd.Flags().Bool("ghostty", false, "Output in Ghostty terminal format")
dank16Cmd.Flags().Bool("wezterm", false, "Output in Wezterm terminal format")
dank16Cmd.Flags().String("vscode-enrich", "", "Enrich existing VSCode theme file with terminal colors")
dank16Cmd.Flags().String("background", "", "Custom background color")
dank16Cmd.Flags().String("contrast", "dps", "Contrast algorithm: dps (Delta Phi Star, default) or wcag")
_ = dank16Cmd.RegisterFlagCompletionFunc("contrast", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"dps", "wcag"}, cobra.ShellCompDirectiveNoFileComp
})
}
func runDank16(cmd *cobra.Command, args []string) {
@@ -45,6 +44,7 @@ func runDank16(cmd *cobra.Command, args []string) {
isAlacritty, _ := cmd.Flags().GetBool("alacritty")
isGhostty, _ := cmd.Flags().GetBool("ghostty")
isWezterm, _ := cmd.Flags().GetBool("wezterm")
vscodeEnrich, _ := cmd.Flags().GetString("vscode-enrich")
background, _ := cmd.Flags().GetString("background")
contrastAlgo, _ := cmd.Flags().GetString("contrast")
@@ -65,7 +65,18 @@ func runDank16(cmd *cobra.Command, args []string) {
colors := dank16.GeneratePalette(primaryColor, opts)
if isJson {
if vscodeEnrich != "" {
data, err := os.ReadFile(vscodeEnrich)
if err != nil {
log.Fatalf("Error reading file: %v", err)
}
enriched, err := dank16.EnrichVSCodeTheme(data, colors)
if err != nil {
log.Fatalf("Error enriching theme: %v", err)
}
fmt.Println(string(enriched))
} else if isJson {
fmt.Print(dank16.GenerateJSON(colors))
} else if isKitty {
fmt.Print(dank16.GenerateKittyTheme(colors))

View File

@@ -1,105 +0,0 @@
package main
import (
"fmt"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/spf13/cobra"
)
var dpmsCmd = &cobra.Command{
Use: "dpms",
Short: "Control display power management",
}
var dpmsOnCmd = &cobra.Command{
Use: "on [output]",
Short: "Turn display(s) on",
Args: cobra.MaximumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getDPMSOutputs(), cobra.ShellCompDirectiveNoFileComp
},
Run: runDPMSOn,
}
var dpmsOffCmd = &cobra.Command{
Use: "off [output]",
Short: "Turn display(s) off",
Args: cobra.MaximumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getDPMSOutputs(), cobra.ShellCompDirectiveNoFileComp
},
Run: runDPMSOff,
}
var dpmsListCmd = &cobra.Command{
Use: "list",
Short: "List outputs",
Args: cobra.NoArgs,
Run: runDPMSList,
}
func init() {
dpmsCmd.AddCommand(dpmsOnCmd, dpmsOffCmd, dpmsListCmd)
}
func runDPMSOn(cmd *cobra.Command, args []string) {
outputName := ""
if len(args) > 0 {
outputName = args[0]
}
client, err := newDPMSClient()
if err != nil {
log.Fatalf("%v", err)
}
defer client.Close()
if err := client.SetDPMS(outputName, true); err != nil {
log.Fatalf("%v", err)
}
}
func runDPMSOff(cmd *cobra.Command, args []string) {
outputName := ""
if len(args) > 0 {
outputName = args[0]
}
client, err := newDPMSClient()
if err != nil {
log.Fatalf("%v", err)
}
defer client.Close()
if err := client.SetDPMS(outputName, false); err != nil {
log.Fatalf("%v", err)
}
}
func getDPMSOutputs() []string {
client, err := newDPMSClient()
if err != nil {
return nil
}
defer client.Close()
return client.ListOutputs()
}
func runDPMSList(cmd *cobra.Command, args []string) {
client, err := newDPMSClient()
if err != nil {
log.Fatalf("%v", err)
}
defer client.Close()
for _, output := range client.ListOutputs() {
fmt.Println(output)
}
}

View File

@@ -77,6 +77,8 @@ func runUpdate() {
switch config.Family {
case distros.FamilyArch:
updateErr = updateArchLinux()
case distros.FamilyNix:
updateErr = updateNixOS()
case distros.FamilySUSE:
updateErr = updateOtherDistros()
default:
@@ -150,6 +152,27 @@ func updateArchLinux() error {
return nil
}
func updateNixOS() error {
fmt.Println("This will update DankMaterialShell using nix profile.")
if !confirmUpdate() {
return errdefs.ErrUpdateCancelled
}
fmt.Println("\nRunning: nix profile upgrade github:AvengeMedia/DankMaterialShell")
updateCmd := exec.Command("nix", "profile", "upgrade", "github:AvengeMedia/DankMaterialShell")
updateCmd.Stdout = os.Stdout
updateCmd.Stderr = os.Stderr
err := updateCmd.Run()
if err != nil {
fmt.Printf("Error: Failed to update using nix profile: %v\n", err)
fmt.Println("Falling back to git-based update method...")
return updateOtherDistros()
}
fmt.Println("dms successfully updated")
return nil
}
func updateOtherDistros() error {
homeDir, err := os.UserHomeDir()
if err != nil {

View File

@@ -11,8 +11,6 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/greeter"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
var greeterCmd = &cobra.Command{
@@ -219,191 +217,6 @@ func checkGroupExists(groupName string) bool {
return false
}
func disableDisplayManager(dmName string) (bool, error) {
state, err := getSystemdServiceState(dmName)
if err != nil {
return false, fmt.Errorf("failed to check %s state: %w", dmName, err)
}
if !state.Exists {
return false, nil
}
fmt.Printf("\nChecking %s...\n", dmName)
fmt.Printf(" Current state: enabled=%s\n", state.EnabledState)
actionTaken := false
if state.NeedsDisable {
var disableCmd *exec.Cmd
var actionVerb string
if state.EnabledState == "static" {
fmt.Printf(" Masking %s (static service cannot be disabled)...\n", dmName)
disableCmd = exec.Command("sudo", "systemctl", "mask", dmName)
actionVerb = "masked"
} else {
fmt.Printf(" Disabling %s...\n", dmName)
disableCmd = exec.Command("sudo", "systemctl", "disable", dmName)
actionVerb = "disabled"
}
disableCmd.Stdout = os.Stdout
disableCmd.Stderr = os.Stderr
if err := disableCmd.Run(); err != nil {
return actionTaken, fmt.Errorf("failed to disable/mask %s: %w", dmName, err)
}
enabledState, shouldDisable, verifyErr := checkSystemdServiceEnabled(dmName)
if verifyErr != nil {
fmt.Printf(" ⚠ Warning: Could not verify %s was %s: %v\n", dmName, actionVerb, verifyErr)
} else if shouldDisable {
return actionTaken, fmt.Errorf("%s is still in state '%s' after %s operation", dmName, enabledState, actionVerb)
} else {
fmt.Printf(" ✓ %s %s (now: %s)\n", cases.Title(language.English).String(actionVerb), dmName, enabledState)
}
actionTaken = true
} else {
if state.EnabledState == "masked" || state.EnabledState == "masked-runtime" {
fmt.Printf(" ✓ %s is already masked\n", dmName)
} else {
fmt.Printf(" ✓ %s is already disabled\n", dmName)
}
}
return actionTaken, nil
}
func ensureGreetdEnabled() error {
fmt.Println("\nChecking greetd service status...")
state, err := getSystemdServiceState("greetd")
if err != nil {
return fmt.Errorf("failed to check greetd state: %w", err)
}
if !state.Exists {
return fmt.Errorf("greetd service not found. Please install greetd first")
}
fmt.Printf(" Current state: %s\n", state.EnabledState)
if state.EnabledState == "masked" || state.EnabledState == "masked-runtime" {
fmt.Println(" Unmasking greetd...")
unmaskCmd := exec.Command("sudo", "systemctl", "unmask", "greetd")
unmaskCmd.Stdout = os.Stdout
unmaskCmd.Stderr = os.Stderr
if err := unmaskCmd.Run(); err != nil {
return fmt.Errorf("failed to unmask greetd: %w", err)
}
fmt.Println(" ✓ Unmasked greetd")
}
switch state.EnabledState {
case "disabled", "masked", "masked-runtime":
fmt.Println(" Enabling greetd service...")
enableCmd := exec.Command("sudo", "systemctl", "enable", "greetd")
enableCmd.Stdout = os.Stdout
enableCmd.Stderr = os.Stderr
if err := enableCmd.Run(); err != nil {
return fmt.Errorf("failed to enable greetd: %w", err)
}
fmt.Println(" ✓ Enabled greetd service")
case "enabled", "enabled-runtime":
fmt.Println(" ✓ greetd is already enabled")
default:
fmt.Printf(" greetd is in state '%s' (should work, no action needed)\n", state.EnabledState)
}
return nil
}
func ensureGraphicalTarget() error {
getDefaultCmd := exec.Command("systemctl", "get-default")
currentTarget, err := getDefaultCmd.Output()
if err != nil {
fmt.Println("⚠ Warning: Could not detect current default systemd target")
return nil
}
currentTargetStr := strings.TrimSpace(string(currentTarget))
if currentTargetStr != "graphical.target" {
fmt.Printf("\nSetting graphical.target as default (current: %s)...\n", currentTargetStr)
setDefaultCmd := exec.Command("sudo", "systemctl", "set-default", "graphical.target")
setDefaultCmd.Stdout = os.Stdout
setDefaultCmd.Stderr = os.Stderr
if err := setDefaultCmd.Run(); err != nil {
fmt.Println("⚠ Warning: Failed to set graphical.target as default")
fmt.Println(" Greeter may not start on boot. Run manually:")
fmt.Println(" sudo systemctl set-default graphical.target")
return nil
}
fmt.Println("✓ Set graphical.target as default")
} else {
fmt.Println("✓ Default target already set to graphical.target")
}
return nil
}
func handleConflictingDisplayManagers() error {
fmt.Println("\n=== Checking for Conflicting Display Managers ===")
conflictingDMs := []string{"gdm", "gdm3", "lightdm", "sddm", "lxdm", "xdm"}
disabledAny := false
var errors []string
for _, dm := range conflictingDMs {
actionTaken, err := disableDisplayManager(dm)
if err != nil {
errMsg := fmt.Sprintf("Failed to handle %s: %v", dm, err)
errors = append(errors, errMsg)
fmt.Printf(" ⚠⚠⚠ ERROR: %s\n", errMsg)
continue
}
if actionTaken {
disabledAny = true
}
}
if len(errors) > 0 {
fmt.Println("\n╔════════════════════════════════════════════════════════════╗")
fmt.Println("║ ⚠⚠⚠ ERRORS OCCURRED ⚠⚠⚠ ║")
fmt.Println("╚════════════════════════════════════════════════════════════╝")
fmt.Println("\nSome display managers could not be disabled:")
for _, err := range errors {
fmt.Printf(" ✗ %s\n", err)
}
fmt.Println("\nThis may prevent greetd from starting properly.")
fmt.Println("You may need to manually disable them before greetd will work.")
fmt.Println("\nManual commands to try:")
for _, dm := range conflictingDMs {
fmt.Printf(" sudo systemctl disable %s\n", dm)
fmt.Printf(" sudo systemctl mask %s\n", dm)
}
fmt.Print("\nContinue with greeter enablement anyway? (Y/n): ")
var response string
fmt.Scanln(&response)
response = strings.ToLower(strings.TrimSpace(response))
if response == "n" || response == "no" {
return fmt.Errorf("aborted due to display manager conflicts")
}
fmt.Println("\nContinuing despite errors...")
}
if !disabledAny && len(errors) == 0 {
fmt.Println("\n✓ No conflicting display managers found")
} else if disabledAny && len(errors) == 0 {
fmt.Println("\n✓ Successfully handled all conflicting display managers")
}
return nil
}
func enableGreeter() error {
fmt.Println("=== DMS Greeter Enable ===")
fmt.Println()
@@ -419,29 +232,8 @@ func enableGreeter() error {
}
configContent := string(data)
configAlreadyCorrect := strings.Contains(configContent, "dms-greeter")
if configAlreadyCorrect {
if strings.Contains(configContent, "dms-greeter") {
fmt.Println("✓ Greeter is already configured with dms-greeter")
if err := ensureGraphicalTarget(); err != nil {
return err
}
if err := handleConflictingDisplayManagers(); err != nil {
return err
}
if err := ensureGreetdEnabled(); err != nil {
return err
}
fmt.Println("\n=== Enable Complete ===")
fmt.Println("\nGreeter configuration verified and system state corrected.")
fmt.Println("To start the greeter now, run:")
fmt.Println(" sudo systemctl start greetd")
fmt.Println("\nOr reboot to see the greeter at boot time.")
return nil
}
@@ -530,23 +322,11 @@ func enableGreeter() error {
}
fmt.Printf("✓ Updated greetd configuration to use %s\n", selectedCompositor)
if err := ensureGraphicalTarget(); err != nil {
return err
}
if err := handleConflictingDisplayManagers(); err != nil {
return err
}
if err := ensureGreetdEnabled(); err != nil {
return err
}
fmt.Println("\n=== Enable Complete ===")
fmt.Println("\nTo start the greeter now, run:")
fmt.Println("\nTo start the greeter, run:")
fmt.Println(" sudo systemctl start greetd")
fmt.Println("\nOr reboot to see the greeter at boot time.")
fmt.Println("\nTo enable on boot, run:")
fmt.Println(" sudo systemctl enable --now greetd")
return nil
}

View File

@@ -30,14 +30,7 @@ var keybindsShowCmd = &cobra.Command{
Short: "Show keybinds for a provider",
Long: "Display keybinds/cheatsheet for the specified provider",
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
registry := keybinds.GetDefaultRegistry()
return registry.List(), cobra.ShellCompDirectiveNoFileComp
},
Run: runKeybindsShow,
Run: runKeybindsShow,
}
func init() {
@@ -71,11 +64,6 @@ func initializeProviders() {
log.Warnf("Failed to register Sway provider: %v", err)
}
niriProvider := providers.NewNiriProvider("")
if err := registry.Register(niriProvider); err != nil {
log.Warnf("Failed to register Niri provider: %v", err)
}
config := keybinds.DefaultDiscoveryConfig()
if err := keybinds.AutoDiscoverProviders(registry, config); err != nil {
log.Warnf("Failed to auto-discover providers: %v", err)
@@ -111,8 +99,6 @@ func runKeybindsShow(cmd *cobra.Command, args []string) {
provider = providers.NewMangoWCProvider(customPath)
case "sway":
provider = providers.NewSwayProvider(customPath)
case "niri":
provider = providers.NewNiriProvider(customPath)
default:
log.Fatalf("Provider %s does not support custom path", providerName)
}

View File

@@ -1,227 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"mime"
"net"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
"github.com/spf13/cobra"
)
var (
openMimeType string
openCategories []string
openRequestType string
)
var openCmd = &cobra.Command{
Use: "open [target]",
Short: "Open a file, URL, or resource with an application picker",
Long: `Open a target (URL, file, or other resource) using the DMS application picker.
By default, this opens URLs with the browser picker. You can customize the behavior
with flags to handle different MIME types or application categories.
Examples:
dms open https://example.com # Open URL with browser picker
dms open file.pdf --mime application/pdf # Open PDF with compatible apps
dms open document.odt --category Office # Open with office applications
dms open --mime image/png image.png # Open image with image viewers`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
runOpen(args[0])
},
}
func init() {
rootCmd.AddCommand(openCmd)
openCmd.Flags().StringVar(&openMimeType, "mime", "", "MIME type for filtering applications")
openCmd.Flags().StringSliceVar(&openCategories, "category", []string{}, "Application categories to filter (e.g., WebBrowser, Office, Graphics)")
openCmd.Flags().StringVar(&openRequestType, "type", "url", "Request type (url, file, or custom)")
_ = openCmd.RegisterFlagCompletionFunc("type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"url", "file", "custom"}, cobra.ShellCompDirectiveNoFileComp
})
}
// mimeTypeToCategories maps MIME types to desktop file categories
func mimeTypeToCategories(mimeType string) []string {
// Split MIME type to get the main type
parts := strings.Split(mimeType, "/")
if len(parts) < 1 {
return nil
}
mainType := parts[0]
switch mainType {
case "image":
return []string{"Graphics", "Viewer"}
case "video":
return []string{"Video", "AudioVideo"}
case "audio":
return []string{"Audio", "AudioVideo"}
case "text":
if strings.Contains(mimeType, "html") {
return []string{"WebBrowser"}
}
return []string{"TextEditor", "Office"}
case "application":
if strings.Contains(mimeType, "pdf") {
return []string{"Office", "Viewer"}
}
if strings.Contains(mimeType, "document") || strings.Contains(mimeType, "spreadsheet") ||
strings.Contains(mimeType, "presentation") || strings.Contains(mimeType, "msword") ||
strings.Contains(mimeType, "ms-excel") || strings.Contains(mimeType, "ms-powerpoint") ||
strings.Contains(mimeType, "opendocument") {
return []string{"Office"}
}
if strings.Contains(mimeType, "zip") || strings.Contains(mimeType, "tar") ||
strings.Contains(mimeType, "gzip") || strings.Contains(mimeType, "compress") {
return []string{"Archiving", "Utility"}
}
return []string{"Office", "Viewer"}
}
return nil
}
func runOpen(target string) {
socketPath, err := server.FindSocket()
if err != nil {
log.Warnf("DMS socket not found: %v", err)
fmt.Println("DMS is not running. Please start DMS first.")
os.Exit(1)
}
conn, err := net.Dial("unix", socketPath)
if err != nil {
log.Warnf("DMS socket connection failed: %v", err)
fmt.Println("DMS is not running. Please start DMS first.")
os.Exit(1)
}
defer conn.Close()
buf := make([]byte, 1)
for {
_, err := conn.Read(buf)
if err != nil {
return
}
if buf[0] == '\n' {
break
}
}
// Parse file:// URIs to extract the actual file path
actualTarget := target
detectedMimeType := openMimeType
detectedCategories := openCategories
detectedRequestType := openRequestType
log.Infof("Processing target: %s", target)
if parsedURL, err := url.Parse(target); err == nil && parsedURL.Scheme == "file" {
// Extract file path from file:// URI and convert to absolute path
actualTarget = parsedURL.Path
if absPath, err := filepath.Abs(actualTarget); err == nil {
actualTarget = absPath
}
if detectedRequestType == "url" || detectedRequestType == "" {
detectedRequestType = "file"
}
log.Infof("Detected file:// URI, extracted absolute path: %s", actualTarget)
// Auto-detect MIME type if not provided
if detectedMimeType == "" {
ext := filepath.Ext(actualTarget)
if ext != "" {
detectedMimeType = mime.TypeByExtension(ext)
log.Infof("Detected MIME type from extension %s: %s", ext, detectedMimeType)
}
}
// Auto-detect categories based on MIME type if not provided
if len(detectedCategories) == 0 && detectedMimeType != "" {
detectedCategories = mimeTypeToCategories(detectedMimeType)
log.Infof("Detected categories from MIME type: %v", detectedCategories)
}
} else if strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") {
// Handle HTTP(S) URLs
if detectedRequestType == "" {
detectedRequestType = "url"
}
log.Infof("Detected HTTP(S) URL")
} else if _, err := os.Stat(target); err == nil {
// Handle local file paths directly (not file:// URIs)
// Convert to absolute path
if absPath, err := filepath.Abs(target); err == nil {
actualTarget = absPath
}
if detectedRequestType == "url" || detectedRequestType == "" {
detectedRequestType = "file"
}
log.Infof("Detected local file path, converted to absolute: %s", actualTarget)
// Auto-detect MIME type if not provided
if detectedMimeType == "" {
ext := filepath.Ext(actualTarget)
if ext != "" {
detectedMimeType = mime.TypeByExtension(ext)
log.Infof("Detected MIME type from extension %s: %s", ext, detectedMimeType)
}
}
// Auto-detect categories based on MIME type if not provided
if len(detectedCategories) == 0 && detectedMimeType != "" {
detectedCategories = mimeTypeToCategories(detectedMimeType)
log.Infof("Detected categories from MIME type: %v", detectedCategories)
}
}
params := map[string]any{
"target": actualTarget,
}
if detectedMimeType != "" {
params["mimeType"] = detectedMimeType
}
if len(detectedCategories) > 0 {
params["categories"] = detectedCategories
}
if detectedRequestType != "" {
params["requestType"] = detectedRequestType
}
method := "apppicker.open"
if detectedMimeType == "" && len(detectedCategories) == 0 && (strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://")) {
method = "browser.open"
params["url"] = target
}
req := models.Request{
ID: 1,
Method: method,
Params: params,
}
log.Infof("Sending request - Method: %s, Params: %+v", method, params)
if err := json.NewEncoder(conn).Encode(req); err != nil {
log.Fatalf("Failed to send request: %v", err)
}
log.Infof("Request sent successfully")
}

View File

@@ -1,12 +1,14 @@
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
"github.com/AvengeMedia/DankMaterialShell/core/internal/distros"
"github.com/AvengeMedia/DankMaterialShell/core/internal/dms"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
tea "github.com/charmbracelet/bubbletea"
@@ -74,7 +76,14 @@ func findConfig(cmd *cobra.Command, args []string) error {
return nil
}
func runInteractiveMode(cmd *cobra.Command, args []string) {
detector, _ := dms.NewDetector()
detector, err := dms.NewDetector()
if err != nil && !errors.Is(err, &distros.UnsupportedDistributionError{}) {
log.Fatalf("Error initializing DMS detector: %v", err)
} else if errors.Is(err, &distros.UnsupportedDistributionError{}) {
log.Error("Interactive mode is not supported on this distribution.")
log.Info("Please run 'dms --help' for available commands.")
os.Exit(1)
}
if !detector.IsDMSInstalled() {
log.Error("DankMaterialShell (DMS) is not detected as installed on this system.")

View File

@@ -1,345 +0,0 @@
package main
import (
"fmt"
"sync"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_power"
wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
)
type cmd struct {
fn func()
done chan error
}
type dpmsClient struct {
display *wlclient.Display
ctx *wlclient.Context
powerMgr *wlr_output_power.ZwlrOutputPowerManagerV1
outputs map[string]*outputState
mu sync.Mutex
syncRound int
done bool
err error
cmdq chan cmd
stopChan chan struct{}
wg sync.WaitGroup
}
type outputState struct {
wlOutput *wlclient.Output
powerCtrl *wlr_output_power.ZwlrOutputPowerV1
name string
mode uint32
failed bool
waitCh chan struct{}
wantMode *uint32
}
func (c *dpmsClient) post(fn func()) {
done := make(chan error, 1)
select {
case c.cmdq <- cmd{fn: fn, done: done}:
<-done
case <-c.stopChan:
}
}
func (c *dpmsClient) waylandActor() {
defer c.wg.Done()
for {
select {
case <-c.stopChan:
return
case cmd := <-c.cmdq:
cmd.fn()
close(cmd.done)
}
}
}
func newDPMSClient() (*dpmsClient, error) {
display, err := wlclient.Connect("")
if err != nil {
return nil, fmt.Errorf("failed to connect to Wayland: %w", err)
}
c := &dpmsClient{
display: display,
ctx: display.Context(),
outputs: make(map[string]*outputState),
cmdq: make(chan cmd, 128),
stopChan: make(chan struct{}),
}
c.wg.Add(1)
go c.waylandActor()
registry, err := display.GetRegistry()
if err != nil {
display.Context().Close()
return nil, fmt.Errorf("failed to get registry: %w", err)
}
registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
switch e.Interface {
case wlr_output_power.ZwlrOutputPowerManagerV1InterfaceName:
powerMgr := wlr_output_power.NewZwlrOutputPowerManagerV1(c.ctx)
version := e.Version
if version > 1 {
version = 1
}
if err := registry.Bind(e.Name, e.Interface, version, powerMgr); err == nil {
c.powerMgr = powerMgr
}
case "wl_output":
output := wlclient.NewOutput(c.ctx)
version := e.Version
if version > 4 {
version = 4
}
if err := registry.Bind(e.Name, e.Interface, version, output); err == nil {
outputID := fmt.Sprintf("output-%d", output.ID())
state := &outputState{
wlOutput: output,
name: outputID,
}
c.mu.Lock()
c.outputs[outputID] = state
c.mu.Unlock()
output.SetNameHandler(func(ev wlclient.OutputNameEvent) {
c.mu.Lock()
delete(c.outputs, state.name)
state.name = ev.Name
c.outputs[ev.Name] = state
c.mu.Unlock()
})
}
}
})
syncCallback, err := display.Sync()
if err != nil {
c.Close()
return nil, fmt.Errorf("failed to sync display: %w", err)
}
syncCallback.SetDoneHandler(func(e wlclient.CallbackDoneEvent) {
c.handleSync()
})
for !c.done {
if err := c.ctx.Dispatch(); err != nil {
c.Close()
return nil, fmt.Errorf("dispatch error: %w", err)
}
}
if c.err != nil {
c.Close()
return nil, c.err
}
return c, nil
}
func (c *dpmsClient) handleSync() {
c.syncRound++
switch c.syncRound {
case 1:
if c.powerMgr == nil {
c.err = fmt.Errorf("wlr-output-power-management protocol not supported by compositor")
c.done = true
return
}
c.mu.Lock()
for _, state := range c.outputs {
powerCtrl, err := c.powerMgr.GetOutputPower(state.wlOutput)
if err != nil {
continue
}
state.powerCtrl = powerCtrl
powerCtrl.SetModeHandler(func(e wlr_output_power.ZwlrOutputPowerV1ModeEvent) {
c.mu.Lock()
defer c.mu.Unlock()
if state.powerCtrl == nil {
return
}
state.mode = e.Mode
if state.wantMode != nil && e.Mode == *state.wantMode && state.waitCh != nil {
close(state.waitCh)
state.wantMode = nil
}
})
powerCtrl.SetFailedHandler(func(e wlr_output_power.ZwlrOutputPowerV1FailedEvent) {
c.mu.Lock()
defer c.mu.Unlock()
if state.powerCtrl == nil {
return
}
state.failed = true
if state.waitCh != nil {
close(state.waitCh)
state.wantMode = nil
}
})
}
c.mu.Unlock()
syncCallback, err := c.display.Sync()
if err != nil {
c.err = fmt.Errorf("failed to sync display: %w", err)
c.done = true
return
}
syncCallback.SetDoneHandler(func(e wlclient.CallbackDoneEvent) {
c.handleSync()
})
default:
c.done = true
}
}
func (c *dpmsClient) ListOutputs() []string {
c.mu.Lock()
defer c.mu.Unlock()
names := make([]string, 0, len(c.outputs))
for name := range c.outputs {
names = append(names, name)
}
return names
}
func (c *dpmsClient) SetDPMS(outputName string, on bool) error {
var mode uint32
if on {
mode = uint32(wlr_output_power.ZwlrOutputPowerV1ModeOn)
} else {
mode = uint32(wlr_output_power.ZwlrOutputPowerV1ModeOff)
}
var setErr error
c.post(func() {
c.mu.Lock()
var waitStates []*outputState
if outputName == "" || outputName == "all" {
if len(c.outputs) == 0 {
c.mu.Unlock()
setErr = fmt.Errorf("no outputs found")
return
}
for _, state := range c.outputs {
if state.powerCtrl == nil {
continue
}
state.wantMode = &mode
state.waitCh = make(chan struct{})
state.failed = false
waitStates = append(waitStates, state)
state.powerCtrl.SetMode(mode)
}
} else {
state, ok := c.outputs[outputName]
if !ok {
c.mu.Unlock()
setErr = fmt.Errorf("output not found: %s", outputName)
return
}
if state.powerCtrl == nil {
c.mu.Unlock()
setErr = fmt.Errorf("output %s has nil powerCtrl", outputName)
return
}
state.wantMode = &mode
state.waitCh = make(chan struct{})
state.failed = false
waitStates = append(waitStates, state)
state.powerCtrl.SetMode(mode)
}
c.mu.Unlock()
deadline := time.Now().Add(10 * time.Second)
for _, state := range waitStates {
c.mu.Lock()
ch := state.waitCh
c.mu.Unlock()
done := false
for !done {
if err := c.ctx.Dispatch(); err != nil {
setErr = fmt.Errorf("dispatch error: %w", err)
return
}
select {
case <-ch:
c.mu.Lock()
if state.failed {
setErr = fmt.Errorf("compositor reported failed for %s", state.name)
c.mu.Unlock()
return
}
c.mu.Unlock()
done = true
default:
if time.Now().After(deadline) {
setErr = fmt.Errorf("timeout waiting for mode change on %s", state.name)
return
}
time.Sleep(10 * time.Millisecond)
}
}
}
c.mu.Lock()
for _, state := range waitStates {
if state.powerCtrl != nil {
state.powerCtrl.Destroy()
state.powerCtrl = nil
}
}
c.mu.Unlock()
c.display.Roundtrip()
})
return setErr
}
func (c *dpmsClient) Close() {
close(c.stopChan)
c.wg.Wait()
c.mu.Lock()
defer c.mu.Unlock()
for _, state := range c.outputs {
if state.powerCtrl != nil {
state.powerCtrl.Destroy()
}
}
c.outputs = nil
if c.powerMgr != nil {
c.powerMgr.Destroy()
c.powerMgr = nil
}
if c.display != nil {
c.ctx.Close()
c.display = nil
}
}

View File

@@ -16,8 +16,6 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/server"
)
type ipcTargets map[string][]string
var isSessionManaged bool
func execDetachedRestart(targetPID int) {
@@ -59,18 +57,13 @@ func getRuntimeDir() string {
return os.TempDir()
}
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 writePIDFile(childPID int) error {
pidFile := getPIDFilePath()
return os.WriteFile(pidFile, []byte(strconv.Itoa(childPID)), 0o644)
return os.WriteFile(pidFile, []byte(strconv.Itoa(childPID)), 0644)
}
func removePIDFile() {
@@ -146,7 +139,7 @@ func runShellInteractive(session bool) {
socketPath := server.GetSocketPath()
configStateFile := filepath.Join(getRuntimeDir(), "danklinux.path")
if err := os.WriteFile(configStateFile, []byte(configPath), 0o644); err != nil {
if err := os.WriteFile(configStateFile, []byte(configPath), 0644); err != nil {
log.Warnf("Failed to write config state file: %v", err)
}
defer os.Remove(configStateFile)
@@ -172,10 +165,6 @@ func runShellInteractive(session bool) {
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) {
@@ -372,7 +361,7 @@ func runShellDaemon(session bool) {
socketPath := server.GetSocketPath()
configStateFile := filepath.Join(getRuntimeDir(), "danklinux.path")
if err := os.WriteFile(configStateFile, []byte(configPath), 0o644); err != nil {
if err := os.WriteFile(configStateFile, []byte(configPath), 0644); err != nil {
log.Warnf("Failed to write config state file: %v", err)
}
defer os.Remove(configStateFile)
@@ -385,7 +374,6 @@ func runShellDaemon(session bool) {
errChan <- fmt.Errorf("server panic: %v", r)
}
}()
server.CLIVersion = Version
if err := server.Start(false); err != nil {
errChan <- fmt.Errorf("server error: %w", err)
}
@@ -399,10 +387,6 @@ func runShellDaemon(session bool) {
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) {
@@ -475,51 +459,6 @@ func runShellDaemon(session bool) {
}
}
func parseTargetsFromIPCShowOutput(output string) ipcTargets {
targets := map[string][]string{}
var currentTarget string
for _, line := range strings.Split(output, "\n") {
if strings.HasPrefix(line, "target ") {
currentTarget = strings.TrimSpace(strings.TrimPrefix(line, "target "))
}
if strings.HasPrefix(line, " function") && currentTarget != "" {
currentFunc := strings.TrimPrefix(line, " function ")
currentFunc = strings.SplitN(currentFunc, "(", 2)[0]
targets[currentTarget] = append(targets[currentTarget], currentFunc)
}
}
return targets
}
func getShellIPCCompletions(args []string, toComplete string) []string {
cmdArgs := []string{"-p", configPath, "ipc", "show"}
cmd := exec.Command("qs", cmdArgs...)
var targets ipcTargets
if output, err := cmd.Output(); err == nil {
log.Debugf("IPC show output: %s", string(output))
targets = parseTargetsFromIPCShowOutput(string(output))
} else {
log.Debugf("Error getting IPC show output for completions: %v", err)
return nil
}
if len(args) > 0 && args[0] == "call" {
args = args[1:]
}
if len(args) == 0 {
targetNames := make([]string, 0)
targetNames = append(targetNames, "call")
for k := range targets {
targetNames = append(targetNames, k)
}
return targetNames
}
return targets[args[0]]
}
func runShellIPCCommand(args []string) {
if len(args) == 0 {
log.Error("IPC command requires arguments")

View File

@@ -3,7 +3,6 @@ package main
import (
"fmt"
"os/exec"
"strings"
)
func commandExists(cmd string) bool {
@@ -25,68 +24,3 @@ func isArchPackageInstalled(packageName string) bool {
err := cmd.Run()
return err == nil
}
type systemdServiceState struct {
Name string
EnabledState string
NeedsDisable bool
Exists bool
}
// checkSystemdServiceEnabled returns (state, should_disable, error) for a systemd service
func checkSystemdServiceEnabled(serviceName string) (string, bool, error) {
cmd := exec.Command("systemctl", "is-enabled", serviceName)
output, err := cmd.Output()
stateStr := strings.TrimSpace(string(output))
if err != nil {
knownStates := []string{"disabled", "masked", "masked-runtime", "not-found", "enabled", "enabled-runtime", "static", "indirect", "alias"}
isKnownState := false
for _, known := range knownStates {
if stateStr == known {
isKnownState = true
break
}
}
if !isKnownState {
return stateStr, false, fmt.Errorf("systemctl is-enabled failed: %w (output: %s)", err, stateStr)
}
}
shouldDisable := false
switch stateStr {
case "enabled", "enabled-runtime", "static", "indirect", "alias":
shouldDisable = true
case "disabled", "masked", "masked-runtime", "not-found":
shouldDisable = false
default:
shouldDisable = true
}
return stateStr, shouldDisable, nil
}
func getSystemdServiceState(serviceName string) (*systemdServiceState, error) {
state := &systemdServiceState{
Name: serviceName,
Exists: false,
}
enabledState, needsDisable, err := checkSystemdServiceEnabled(serviceName)
if err != nil {
return nil, fmt.Errorf("failed to check enabled state: %w", err)
}
state.EnabledState = enabledState
state.NeedsDisable = needsDisable
if enabledState == "not-found" {
state.Exists = false
return state, nil
}
state.Exists = true
return state, nil
}

View File

@@ -9,26 +9,24 @@ require (
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/log v0.4.2
github.com/fsnotify/fsnotify v1.9.0
github.com/godbus/dbus/v5 v5.2.0
github.com/godbus/dbus/v5 v5.1.0
github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83
github.com/pilebones/go-udev v0.9.1
github.com/sblinch/kdl-go v0.0.0-20250930225324-bf4099d4614a
github.com/spf13/cobra v1.10.1
github.com/stretchr/testify v1.11.1
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6
)
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/clipperhouse/displaywidth v0.6.0 // indirect
github.com/clipperhouse/displaywidth v0.5.0 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/cyphar/filepath-securejoin v0.6.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg/v2 v2.0.2 // indirect
github.com/go-git/go-billy/v6 v6.0.0-20251126203821-7f9c95185ee0 // indirect
github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect
@@ -36,7 +34,7 @@ require (
github.com/pjbgf/sha1cd v0.5.0 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/stretchr/objx v0.5.3 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/crypto v0.44.0 // indirect
golang.org/x/net v0.47.0 // indirect
)
@@ -45,12 +43,12 @@ require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.3.3 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/charmbracelet/x/ansi v0.11.2 // indirect
github.com/charmbracelet/x/ansi v0.11.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/go-git/go-git/v6 v6.0.0-20251128074608-48f817f57805
github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0
github.com/mattn/go-isatty v0.0.20 // indirect
@@ -65,6 +63,6 @@ require (
github.com/spf13/pflag v1.0.10 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sys v0.38.0
golang.org/x/text v0.31.0
golang.org/x/text v0.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -24,14 +24,14 @@ github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoF
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
github.com/charmbracelet/x/ansi v0.11.2 h1:XAG3FSjiVtFvgEgGrNBkCNNYrsucAt8c6bfxHyROLLs=
github.com/charmbracelet/x/ansi v0.11.2/go.mod h1:9tY2bzX5SiJCU0iWyskjBeI2BRQfvPqI+J760Mjf+Rg=
github.com/charmbracelet/x/ansi v0.11.0 h1:uuIVK7GIplwX6UBIz8S2TF8nkr7xRlygSsBRjSJqIvA=
github.com/charmbracelet/x/ansi v0.11.0/go.mod h1:uQt8bOrq/xgXjlGcFMc8U2WYbnxyjrKhnvTQluvfCaE=
github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4=
github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/clipperhouse/displaywidth v0.6.0 h1:k32vueaksef9WIKCNcoqRNyKbyvkvkysNYnAWz2fN4s=
github.com/clipperhouse/displaywidth v0.6.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
github.com/clipperhouse/displaywidth v0.5.0 h1:AIG5vQaSL2EKqzt0M9JMnvNxOCRTKUc4vUnLWGgP89I=
github.com/clipperhouse/displaywidth v0.5.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
@@ -39,8 +39,8 @@ github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsV
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
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/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
github.com/cyphar/filepath-securejoin v0.6.0/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -56,17 +56,16 @@ 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-20251126203821-7f9c95185ee0 h1:eY5aB2GXiVdgTueBcqsBt53WuJTRZAuCdIS/86Pcq5c=
github.com/go-git/go-billy/v6 v6.0.0-20251126203821-7f9c95185ee0/go.mod h1:0NjwVNrwtVFZBReAp5OoGklGJIgJFEbVyHneAr4lc8k=
github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0 h1:EC9n6hr6yKDoVJ6g7Ko523LbbceJfR0ohbOp809Fyf4=
github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0/go.mod h1:E3VhlS+AKkrq6ZNn1axE2/nDRJ87l1FJk9r5HT2vPX0=
github.com/go-git/go-git-fixtures/v5 v5.1.1 h1:OH8i1ojV9bWfr0ZfasfpgtUXQHQyVS8HXik/V1C099w=
github.com/go-git/go-git-fixtures/v5 v5.1.1/go.mod h1:Altk43lx3b1ks+dVoAG2300o5WWUnktvfY3VI6bcaXU=
github.com/go-git/go-git/v6 v6.0.0-20251128074608-48f817f57805 h1:jxQ3BzYeErNRvlI/4+0mpwqMzvB4g97U+ksfgvrUEbY=
github.com/go-git/go-git/v6 v6.0.0-20251128074608-48f817f57805/go.mod h1:dIwT3uWK1ooHInyVnK2JS5VfQ3peVGYaw2QPqX7uFvs=
github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9 h1:SOFrnF9LCssC6q6Rb0084Bzg2aBYbe8QXv9xKGXmt/w=
github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9/go.mod h1:0wtvm/JfPC9RFVEAP3ks0ec5h64/qmZkTTUE3pjz7Hc=
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 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8=
github.com/godbus/dbus/v5 v5.2.0/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -84,9 +83,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -101,8 +99,6 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/pilebones/go-udev v0.9.1 h1:uN72M1C1fgzhsVmBGEM8w9RD1JY4iVsPZpr+Z6rb3O8=
github.com/pilebones/go-udev v0.9.1/go.mod h1:Bgcl07crebF3JSeS4+nuaRvhWFdCeFoBhXXeAp93XNo=
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -112,8 +108,6 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sblinch/kdl-go v0.0.0-20250930225324-bf4099d4614a h1:8ZZwZWIQKC0YVMyaCkbrdeI8faTjD1QBrRAAWc1TjMI=
github.com/sblinch/kdl-go v0.0.0-20250930225324-bf4099d4614a/go.mod h1:b3oNGuAKOQzhsCKmuLc/urEOPzgHj6fB8vl8bwTBh28=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
@@ -131,10 +125,10 @@ 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/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY=
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0=
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@@ -22,19 +22,8 @@ func LocateDMSConfig() (string, error) {
primaryPaths = append(primaryPaths, filepath.Join(configHome, "quickshell", "dms"))
}
// System data directories
dataDirs := os.Getenv("XDG_DATA_DIRS")
if dataDirs == "" {
dataDirs = "/usr/local/share:/usr/share"
}
primaryPaths = append(primaryPaths, "/usr/share/quickshell/dms")
for _, dir := range strings.Split(dataDirs, ":") {
if dir != "" {
primaryPaths = append(primaryPaths, filepath.Join(dir, "quickshell", "dms"))
}
}
// System config directories (fallback)
configDirs := os.Getenv("XDG_CONFIG_DIRS")
if configDirs == "" {
configDirs = "/etc/xdg"

View File

@@ -125,8 +125,6 @@ windowrulev2 = noborder, class:^(kitty)$
windowrulev2 = float, class:^(firefox)$, title:^(Picture-in-Picture)$
windowrulev2 = float, class:^(zoom)$
# DMS windows floating by default
windowrulev2 = float, class:^(org.quickshell)$
windowrulev2 = opacity 0.9 0.9, floating:0, focus:0
layerrule = noanim, ^(quickshell)$
@@ -140,8 +138,8 @@ $mod = SUPER
bind = $mod, T, exec, {{TERMINAL_COMMAND}}
bind = $mod, space, exec, dms ipc call spotlight toggle
bind = $mod, V, exec, dms ipc call clipboard toggle
bind = $mod, M, exec, dms ipc call processlist focusOrToggle
bind = $mod, comma, exec, dms ipc call settings focusOrToggle
bind = $mod, M, exec, dms ipc call processlist toggle
bind = $mod, comma, exec, dms ipc call settings toggle
bind = $mod, N, exec, dms ipc call notifications toggle
bind = $mod SHIFT, N, exec, dms ipc call notepad toggle
bind = $mod, Y, exec, dms ipc call dankdash wallpaper
@@ -153,7 +151,7 @@ bind = $mod SHIFT, Slash, exec, dms ipc call keybinds toggle hyprland
# === Security ===
bind = $mod ALT, L, exec, dms ipc call lock lock
bind = $mod SHIFT, E, exit
bind = CTRL ALT, Delete, exec, dms ipc call processlist focusOrToggle
bind = CTRL ALT, Delete, exec, dms ipc call processlist toggle
# === Audio Controls ===
bindel = , XF86AudioRaiseVolume, exec, dms ipc call audio increment 3

View File

@@ -218,11 +218,6 @@ window-rule {
geometry-corner-radius 12
clip-to-geometry true
}
// Open dms windows as floating by default
window-rule {
match app-id=r#"org.quickshell$"#
open-floating true
}
binds {
// === System & Overview ===
Mod+D { spawn "niri" "msg" "action" "toggle-overview"; }
@@ -238,10 +233,10 @@ binds {
spawn "dms" "ipc" "call" "clipboard" "toggle";
}
Mod+M hotkey-overlay-title="Task Manager" {
spawn "dms" "ipc" "call" "processlist" "focusOrToggle";
spawn "dms" "ipc" "call" "processlist" "toggle";
}
Mod+Comma hotkey-overlay-title="Settings" {
spawn "dms" "ipc" "call" "settings" "focusOrToggle";
spawn "dms" "ipc" "call" "settings" "toggle";
}
Mod+Y hotkey-overlay-title="Browse Wallpapers" {
spawn "dms" "ipc" "call" "dankdash" "wallpaper";
@@ -255,7 +250,7 @@ binds {
}
Mod+Shift+E { quit; }
Ctrl+Alt+Delete hotkey-overlay-title="Task Manager" {
spawn "dms" "ipc" "call" "processlist" "focusOrToggle";
spawn "dms" "ipc" "call" "processlist" "toggle";
}
// === Audio Controls ===

View File

@@ -1,6 +0,0 @@
package config
import _ "embed"
//go:embed embedded/testpage.pdf
var TestPage string

View File

@@ -15,48 +15,6 @@ type HSV struct {
H, S, V float64
}
type ColorInfo struct {
Hex string `json:"hex"`
HexStripped string `json:"hex_stripped"`
R int `json:"r"`
G int `json:"g"`
B int `json:"b"`
}
type Palette struct {
Color0 ColorInfo `json:"color0"`
Color1 ColorInfo `json:"color1"`
Color2 ColorInfo `json:"color2"`
Color3 ColorInfo `json:"color3"`
Color4 ColorInfo `json:"color4"`
Color5 ColorInfo `json:"color5"`
Color6 ColorInfo `json:"color6"`
Color7 ColorInfo `json:"color7"`
Color8 ColorInfo `json:"color8"`
Color9 ColorInfo `json:"color9"`
Color10 ColorInfo `json:"color10"`
Color11 ColorInfo `json:"color11"`
Color12 ColorInfo `json:"color12"`
Color13 ColorInfo `json:"color13"`
Color14 ColorInfo `json:"color14"`
Color15 ColorInfo `json:"color15"`
}
func NewColorInfo(hex string) ColorInfo {
rgb := HexToRGB(hex)
stripped := hex
if len(hex) > 0 && hex[0] == '#' {
stripped = hex[1:]
}
return ColorInfo{
Hex: hex,
HexStripped: stripped,
R: int(math.Round(rgb.R * 255)),
G: int(math.Round(rgb.G * 255)),
B: int(math.Round(rgb.B * 255)),
}
}
func HexToRGB(hex string) RGB {
if hex[0] == '#' {
hex = hex[1:]
@@ -352,13 +310,13 @@ func DeriveContainer(primary string, isLight bool) string {
return RGBToHex(HSVToRGB(HSV{H: hsv.H, S: containerS, V: containerV}))
}
func GeneratePalette(primaryColor string, opts PaletteOptions) Palette {
func GeneratePalette(primaryColor string, opts PaletteOptions) []string {
baseColor := DeriveContainer(primaryColor, opts.IsLight)
rgb := HexToRGB(baseColor)
hsv := RGBToHSV(rgb)
var palette Palette
palette := make([]string, 0, 16)
var normalTextTarget, secondaryTarget float64
if opts.UseDPS {
@@ -377,7 +335,7 @@ func GeneratePalette(primaryColor string, opts PaletteOptions) Palette {
} else {
bgColor = "#1a1a1a"
}
palette.Color0 = NewColorInfo(bgColor)
palette = append(palette, bgColor)
hueShift := (hsv.H - 0.6) * 0.12
satBoost := 1.15
@@ -386,39 +344,39 @@ func GeneratePalette(primaryColor string, opts PaletteOptions) Palette {
var redColor string
if opts.IsLight {
redColor = RGBToHex(HSVToRGB(HSV{H: redH, S: math.Min(0.80*satBoost, 1.0), V: 0.55}))
palette.Color1 = NewColorInfo(ensureContrastAuto(redColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(redColor, bgColor, normalTextTarget, opts))
} else {
redColor = RGBToHex(HSVToRGB(HSV{H: redH, S: math.Min(0.65*satBoost, 1.0), V: 0.80}))
palette.Color1 = NewColorInfo(ensureContrastAuto(redColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(redColor, bgColor, normalTextTarget, opts))
}
greenH := math.Mod(0.33+hueShift+1.0, 1.0)
var greenColor string
if opts.IsLight {
greenColor = RGBToHex(HSVToRGB(HSV{H: greenH, S: math.Min(math.Max(hsv.S*0.9, 0.80)*satBoost, 1.0), V: 0.45}))
palette.Color2 = NewColorInfo(ensureContrastAuto(greenColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(greenColor, bgColor, normalTextTarget, opts))
} else {
greenColor = RGBToHex(HSVToRGB(HSV{H: greenH, S: math.Min(0.42*satBoost, 1.0), V: 0.84}))
palette.Color2 = NewColorInfo(ensureContrastAuto(greenColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(greenColor, bgColor, normalTextTarget, opts))
}
yellowH := math.Mod(0.15+hueShift+1.0, 1.0)
var yellowColor string
if opts.IsLight {
yellowColor = RGBToHex(HSVToRGB(HSV{H: yellowH, S: math.Min(0.75*satBoost, 1.0), V: 0.50}))
palette.Color3 = NewColorInfo(ensureContrastAuto(yellowColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(yellowColor, bgColor, normalTextTarget, opts))
} else {
yellowColor = RGBToHex(HSVToRGB(HSV{H: yellowH, S: math.Min(0.38*satBoost, 1.0), V: 0.86}))
palette.Color3 = NewColorInfo(ensureContrastAuto(yellowColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(yellowColor, bgColor, normalTextTarget, opts))
}
var blueColor string
if opts.IsLight {
blueColor = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: math.Max(hsv.S*0.9, 0.7), V: hsv.V * 1.1}))
palette.Color4 = NewColorInfo(ensureContrastAuto(blueColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(blueColor, bgColor, normalTextTarget, opts))
} else {
blueColor = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: math.Max(hsv.S*0.8, 0.6), V: math.Min(hsv.V*1.6, 1.0)}))
palette.Color4 = NewColorInfo(ensureContrastAuto(blueColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(blueColor, bgColor, normalTextTarget, opts))
}
magH := hsv.H - 0.03
@@ -430,64 +388,65 @@ func GeneratePalette(primaryColor string, opts PaletteOptions) Palette {
hh := RGBToHSV(hr)
if opts.IsLight {
magColor = RGBToHex(HSVToRGB(HSV{H: hh.H, S: math.Max(hh.S*0.9, 0.7), V: hh.V * 0.85}))
palette.Color5 = NewColorInfo(ensureContrastAuto(magColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(magColor, bgColor, normalTextTarget, opts))
} else {
magColor = RGBToHex(HSVToRGB(HSV{H: hh.H, S: hh.S * 0.8, V: hh.V * 0.75}))
palette.Color5 = NewColorInfo(ensureContrastAuto(magColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(magColor, bgColor, normalTextTarget, opts))
}
cyanH := hsv.H + 0.08
if cyanH > 1.0 {
cyanH -= 1.0
}
palette.Color6 = NewColorInfo(ensureContrastAuto(primaryColor, bgColor, normalTextTarget, opts))
palette = append(palette, ensureContrastAuto(primaryColor, bgColor, normalTextTarget, opts))
if opts.IsLight {
palette.Color7 = NewColorInfo("#1a1a1a")
palette.Color8 = NewColorInfo("#2e2e2e")
palette = append(palette, "#1a1a1a")
palette = append(palette, "#2e2e2e")
} else {
palette.Color7 = NewColorInfo("#abb2bf")
palette.Color8 = NewColorInfo("#5c6370")
palette = append(palette, "#abb2bf")
palette = append(palette, "#5c6370")
}
if opts.IsLight {
brightRed := RGBToHex(HSVToRGB(HSV{H: redH, S: math.Min(0.70*satBoost, 1.0), V: 0.65}))
palette.Color9 = NewColorInfo(ensureContrastAuto(brightRed, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightRed, bgColor, secondaryTarget, opts))
brightGreen := RGBToHex(HSVToRGB(HSV{H: greenH, S: math.Min(math.Max(hsv.S*0.85, 0.75)*satBoost, 1.0), V: 0.55}))
palette.Color10 = NewColorInfo(ensureContrastAuto(brightGreen, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightGreen, bgColor, secondaryTarget, opts))
brightYellow := RGBToHex(HSVToRGB(HSV{H: yellowH, S: math.Min(0.68*satBoost, 1.0), V: 0.60}))
palette.Color11 = NewColorInfo(ensureContrastAuto(brightYellow, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightYellow, bgColor, secondaryTarget, opts))
hr := HexToRGB(primaryColor)
hh := RGBToHSV(hr)
brightBlue := RGBToHex(HSVToRGB(HSV{H: hh.H, S: math.Min(hh.S*1.1, 1.0), V: math.Min(hh.V*1.2, 1.0)}))
palette.Color12 = NewColorInfo(ensureContrastAuto(brightBlue, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightBlue, bgColor, secondaryTarget, opts))
brightMag := RGBToHex(HSVToRGB(HSV{H: magH, S: math.Max(hsv.S*0.9, 0.75), V: math.Min(hsv.V*1.25, 1.0)}))
palette.Color13 = NewColorInfo(ensureContrastAuto(brightMag, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightMag, bgColor, secondaryTarget, opts))
brightCyan := RGBToHex(HSVToRGB(HSV{H: cyanH, S: math.Max(hsv.S*0.75, 0.65), V: math.Min(hsv.V*1.25, 1.0)}))
palette.Color14 = NewColorInfo(ensureContrastAuto(brightCyan, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightCyan, bgColor, secondaryTarget, opts))
} else {
brightRed := RGBToHex(HSVToRGB(HSV{H: redH, S: math.Min(0.50*satBoost, 1.0), V: 0.88}))
palette.Color9 = NewColorInfo(ensureContrastAuto(brightRed, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightRed, bgColor, secondaryTarget, opts))
brightGreen := RGBToHex(HSVToRGB(HSV{H: greenH, S: math.Min(0.35*satBoost, 1.0), V: 0.88}))
palette.Color10 = NewColorInfo(ensureContrastAuto(brightGreen, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightGreen, bgColor, secondaryTarget, opts))
brightYellow := RGBToHex(HSVToRGB(HSV{H: yellowH, S: math.Min(0.30*satBoost, 1.0), V: 0.91}))
palette.Color11 = NewColorInfo(ensureContrastAuto(brightYellow, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightYellow, bgColor, secondaryTarget, opts))
// Make it way brighter for type names in dark mode
brightBlue := retoneToL(primaryColor, 85.0)
palette.Color12 = NewColorInfo(brightBlue)
palette = append(palette, brightBlue)
brightMag := RGBToHex(HSVToRGB(HSV{H: magH, S: math.Max(hsv.S*0.7, 0.6), V: math.Min(hsv.V*1.3, 0.9)}))
palette.Color13 = NewColorInfo(ensureContrastAuto(brightMag, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightMag, bgColor, secondaryTarget, opts))
brightCyanH := hsv.H + 0.02
if brightCyanH > 1.0 {
brightCyanH -= 1.0
}
brightCyan := RGBToHex(HSVToRGB(HSV{H: brightCyanH, S: math.Max(hsv.S*0.6, 0.5), V: math.Min(hsv.V*1.2, 0.85)}))
palette.Color14 = NewColorInfo(ensureContrastAuto(brightCyan, bgColor, secondaryTarget, opts))
palette = append(palette, ensureContrastAuto(brightCyan, bgColor, secondaryTarget, opts))
}
if opts.IsLight {
palette.Color15 = NewColorInfo("#1a1a1a")
palette = append(palette, "#1a1a1a")
} else {
palette.Color15 = NewColorInfo("#ffffff")
palette = append(palette, "#ffffff")
}
return palette

View File

@@ -1,6 +1,7 @@
package dank16
import (
"encoding/json"
"math"
"testing"
)
@@ -345,36 +346,106 @@ func TestGeneratePalette(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
result := GeneratePalette(tt.base, tt.opts)
colors := []ColorInfo{
result.Color0, result.Color1, result.Color2, result.Color3,
result.Color4, result.Color5, result.Color6, result.Color7,
result.Color8, result.Color9, result.Color10, result.Color11,
result.Color12, result.Color13, result.Color14, result.Color15,
if len(result) != 16 {
t.Errorf("GeneratePalette returned %d colors, expected 16", len(result))
}
for i, color := range colors {
if len(color.Hex) != 7 || color.Hex[0] != '#' {
t.Errorf("Color at index %d (%s) is not a valid hex color", i, color.Hex)
for i, color := range result {
if len(color) != 7 || color[0] != '#' {
t.Errorf("Color at index %d (%s) is not a valid hex color", i, color)
}
}
if tt.opts.Background != "" && result.Color0.Hex != tt.opts.Background {
t.Errorf("Background color = %s, expected %s", result.Color0.Hex, tt.opts.Background)
} else if !tt.opts.IsLight && tt.opts.Background == "" && result.Color0.Hex != "#1a1a1a" {
t.Errorf("Dark mode background = %s, expected #1a1a1a", result.Color0.Hex)
} else if tt.opts.IsLight && tt.opts.Background == "" && result.Color0.Hex != "#f8f8f8" {
t.Errorf("Light mode background = %s, expected #f8f8f8", result.Color0.Hex)
if tt.opts.Background != "" && result[0] != tt.opts.Background {
t.Errorf("Background color = %s, expected %s", result[0], tt.opts.Background)
} else if !tt.opts.IsLight && tt.opts.Background == "" && result[0] != "#1a1a1a" {
t.Errorf("Dark mode background = %s, expected #1a1a1a", result[0])
} else if tt.opts.IsLight && tt.opts.Background == "" && result[0] != "#f8f8f8" {
t.Errorf("Light mode background = %s, expected #f8f8f8", result[0])
}
if tt.opts.IsLight && result.Color15.Hex != "#1a1a1a" {
t.Errorf("Light mode foreground = %s, expected #1a1a1a", result.Color15.Hex)
} else if !tt.opts.IsLight && result.Color15.Hex != "#ffffff" {
t.Errorf("Dark mode foreground = %s, expected #ffffff", result.Color15.Hex)
if tt.opts.IsLight && result[15] != "#1a1a1a" {
t.Errorf("Light mode foreground = %s, expected #1a1a1a", result[15])
} else if !tt.opts.IsLight && result[15] != "#ffffff" {
t.Errorf("Dark mode foreground = %s, expected #ffffff", result[15])
}
})
}
}
func TestEnrichVSCodeTheme(t *testing.T) {
colors := GeneratePalette("#625690", PaletteOptions{IsLight: false})
baseTheme := map[string]interface{}{
"name": "Test Theme",
"type": "dark",
"colors": map[string]interface{}{
"editor.background": "#000000",
},
}
themeJSON, err := json.Marshal(baseTheme)
if err != nil {
t.Fatalf("Failed to marshal base theme: %v", err)
}
result, err := EnrichVSCodeTheme(themeJSON, colors)
if err != nil {
t.Fatalf("EnrichVSCodeTheme failed: %v", err)
}
var enriched map[string]interface{}
if err := json.Unmarshal(result, &enriched); err != nil {
t.Fatalf("Failed to unmarshal result: %v", err)
}
colorsMap, ok := enriched["colors"].(map[string]interface{})
if !ok {
t.Fatal("colors is not a map")
}
terminalColors := []string{
"terminal.ansiBlack",
"terminal.ansiRed",
"terminal.ansiGreen",
"terminal.ansiYellow",
"terminal.ansiBlue",
"terminal.ansiMagenta",
"terminal.ansiCyan",
"terminal.ansiWhite",
"terminal.ansiBrightBlack",
"terminal.ansiBrightRed",
"terminal.ansiBrightGreen",
"terminal.ansiBrightYellow",
"terminal.ansiBrightBlue",
"terminal.ansiBrightMagenta",
"terminal.ansiBrightCyan",
"terminal.ansiBrightWhite",
}
for i, key := range terminalColors {
if val, ok := colorsMap[key]; !ok {
t.Errorf("Missing terminal color: %s", key)
} else if val != colors[i] {
t.Errorf("%s = %s, expected %s", key, val, colors[i])
}
}
if colorsMap["editor.background"] != "#000000" {
t.Error("Original theme colors should be preserved")
}
}
func TestEnrichVSCodeThemeInvalidJSON(t *testing.T) {
colors := GeneratePalette("#625690", PaletteOptions{IsLight: false})
invalidJSON := []byte("{invalid json")
_, err := EnrichVSCodeTheme(invalidJSON, colors)
if err == nil {
t.Error("Expected error for invalid JSON, got nil")
}
}
func TestRoundTripConversion(t *testing.T) {
testColors := []string{"#000000", "#ffffff", "#ff0000", "#00ff00", "#0000ff", "#625690", "#808080"}
@@ -564,26 +635,23 @@ func TestGeneratePaletteWithDPS(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
result := GeneratePalette(tt.base, tt.opts)
colors := []ColorInfo{
result.Color0, result.Color1, result.Color2, result.Color3,
result.Color4, result.Color5, result.Color6, result.Color7,
result.Color8, result.Color9, result.Color10, result.Color11,
result.Color12, result.Color13, result.Color14, result.Color15,
if len(result) != 16 {
t.Errorf("GeneratePalette returned %d colors, expected 16", len(result))
}
for i, color := range colors {
if len(color.Hex) != 7 || color.Hex[0] != '#' {
t.Errorf("Color at index %d (%s) is not a valid hex color", i, color.Hex)
for i, color := range result {
if len(color) != 7 || color[0] != '#' {
t.Errorf("Color at index %d (%s) is not a valid hex color", i, color)
}
}
bgColor := result.Color0.Hex
bgColor := result[0]
for i := 1; i < 8; i++ {
lc := DeltaPhiStarContrast(colors[i].Hex, bgColor, tt.opts.IsLight)
lc := DeltaPhiStarContrast(result[i], bgColor, tt.opts.IsLight)
minLc := 30.0
if lc < minLc && lc > 0 {
t.Errorf("Color %d (%s) has insufficient DPS contrast %f with background %s (expected >= %f)",
i, colors[i].Hex, lc, bgColor, minLc)
i, result[i], lc, bgColor, minLc)
}
}
})
@@ -640,26 +708,17 @@ func TestContrastAlgorithmComparison(t *testing.T) {
paletteWCAG := GeneratePalette(base, optsWCAG)
paletteDPS := GeneratePalette(base, optsDPS)
wcagColors := []ColorInfo{
paletteWCAG.Color0, paletteWCAG.Color1, paletteWCAG.Color2, paletteWCAG.Color3,
paletteWCAG.Color4, paletteWCAG.Color5, paletteWCAG.Color6, paletteWCAG.Color7,
paletteWCAG.Color8, paletteWCAG.Color9, paletteWCAG.Color10, paletteWCAG.Color11,
paletteWCAG.Color12, paletteWCAG.Color13, paletteWCAG.Color14, paletteWCAG.Color15,
}
dpsColors := []ColorInfo{
paletteDPS.Color0, paletteDPS.Color1, paletteDPS.Color2, paletteDPS.Color3,
paletteDPS.Color4, paletteDPS.Color5, paletteDPS.Color6, paletteDPS.Color7,
paletteDPS.Color8, paletteDPS.Color9, paletteDPS.Color10, paletteDPS.Color11,
paletteDPS.Color12, paletteDPS.Color13, paletteDPS.Color14, paletteDPS.Color15,
if len(paletteWCAG) != 16 || len(paletteDPS) != 16 {
t.Fatal("Both palettes should have 16 colors")
}
if paletteWCAG.Color0.Hex != paletteDPS.Color0.Hex {
t.Errorf("Background colors differ: WCAG=%s, DPS=%s", paletteWCAG.Color0.Hex, paletteDPS.Color0.Hex)
if paletteWCAG[0] != paletteDPS[0] {
t.Errorf("Background colors differ: WCAG=%s, DPS=%s", paletteWCAG[0], paletteDPS[0])
}
differentCount := 0
for i := 0; i < 16; i++ {
if wcagColors[i].Hex != dpsColors[i].Hex {
if paletteWCAG[i] != paletteDPS[i] {
differentCount++
}
}

View File

@@ -6,104 +6,135 @@ import (
"strings"
)
func GenerateJSON(p Palette) string {
marshalled, _ := json.Marshal(p)
func GenerateJSON(colors []string) string {
colorMap := make(map[string]string)
for i, color := range colors {
colorMap[fmt.Sprintf("color%d", i)] = color
}
marshalled, _ := json.Marshal(colorMap)
return string(marshalled)
}
func GenerateKittyTheme(p Palette) string {
func GenerateKittyTheme(colors []string) string {
kittyColors := []struct {
name string
index int
}{
{"color0", 0},
{"color1", 1},
{"color2", 2},
{"color3", 3},
{"color4", 4},
{"color5", 5},
{"color6", 6},
{"color7", 7},
{"color8", 8},
{"color9", 9},
{"color10", 10},
{"color11", 11},
{"color12", 12},
{"color13", 13},
{"color14", 14},
{"color15", 15},
}
var result strings.Builder
fmt.Fprintf(&result, "color0 %s\n", p.Color0.Hex)
fmt.Fprintf(&result, "color1 %s\n", p.Color1.Hex)
fmt.Fprintf(&result, "color2 %s\n", p.Color2.Hex)
fmt.Fprintf(&result, "color3 %s\n", p.Color3.Hex)
fmt.Fprintf(&result, "color4 %s\n", p.Color4.Hex)
fmt.Fprintf(&result, "color5 %s\n", p.Color5.Hex)
fmt.Fprintf(&result, "color6 %s\n", p.Color6.Hex)
fmt.Fprintf(&result, "color7 %s\n", p.Color7.Hex)
fmt.Fprintf(&result, "color8 %s\n", p.Color8.Hex)
fmt.Fprintf(&result, "color9 %s\n", p.Color9.Hex)
fmt.Fprintf(&result, "color10 %s\n", p.Color10.Hex)
fmt.Fprintf(&result, "color11 %s\n", p.Color11.Hex)
fmt.Fprintf(&result, "color12 %s\n", p.Color12.Hex)
fmt.Fprintf(&result, "color13 %s\n", p.Color13.Hex)
fmt.Fprintf(&result, "color14 %s\n", p.Color14.Hex)
fmt.Fprintf(&result, "color15 %s\n", p.Color15.Hex)
for _, kc := range kittyColors {
fmt.Fprintf(&result, "%s %s\n", kc.name, colors[kc.index])
}
return result.String()
}
func GenerateFootTheme(p Palette) string {
func GenerateFootTheme(colors []string) string {
footColors := []struct {
name string
index int
}{
{"regular0", 0},
{"regular1", 1},
{"regular2", 2},
{"regular3", 3},
{"regular4", 4},
{"regular5", 5},
{"regular6", 6},
{"regular7", 7},
{"bright0", 8},
{"bright1", 9},
{"bright2", 10},
{"bright3", 11},
{"bright4", 12},
{"bright5", 13},
{"bright6", 14},
{"bright7", 15},
}
var result strings.Builder
fmt.Fprintf(&result, "regular0=%s\n", p.Color0.HexStripped)
fmt.Fprintf(&result, "regular1=%s\n", p.Color1.HexStripped)
fmt.Fprintf(&result, "regular2=%s\n", p.Color2.HexStripped)
fmt.Fprintf(&result, "regular3=%s\n", p.Color3.HexStripped)
fmt.Fprintf(&result, "regular4=%s\n", p.Color4.HexStripped)
fmt.Fprintf(&result, "regular5=%s\n", p.Color5.HexStripped)
fmt.Fprintf(&result, "regular6=%s\n", p.Color6.HexStripped)
fmt.Fprintf(&result, "regular7=%s\n", p.Color7.HexStripped)
fmt.Fprintf(&result, "bright0=%s\n", p.Color8.HexStripped)
fmt.Fprintf(&result, "bright1=%s\n", p.Color9.HexStripped)
fmt.Fprintf(&result, "bright2=%s\n", p.Color10.HexStripped)
fmt.Fprintf(&result, "bright3=%s\n", p.Color11.HexStripped)
fmt.Fprintf(&result, "bright4=%s\n", p.Color12.HexStripped)
fmt.Fprintf(&result, "bright5=%s\n", p.Color13.HexStripped)
fmt.Fprintf(&result, "bright6=%s\n", p.Color14.HexStripped)
fmt.Fprintf(&result, "bright7=%s\n", p.Color15.HexStripped)
for _, fc := range footColors {
fmt.Fprintf(&result, "%s=%s\n", fc.name, strings.TrimPrefix(colors[fc.index], "#"))
}
return result.String()
}
func GenerateAlacrittyTheme(p Palette) string {
func GenerateAlacrittyTheme(colors []string) string {
alacrittyColors := []struct {
section string
name string
index int
}{
{"normal", "black", 0},
{"normal", "red", 1},
{"normal", "green", 2},
{"normal", "yellow", 3},
{"normal", "blue", 4},
{"normal", "magenta", 5},
{"normal", "cyan", 6},
{"normal", "white", 7},
{"bright", "black", 8},
{"bright", "red", 9},
{"bright", "green", 10},
{"bright", "yellow", 11},
{"bright", "blue", 12},
{"bright", "magenta", 13},
{"bright", "cyan", 14},
{"bright", "white", 15},
}
var result strings.Builder
result.WriteString("[colors.normal]\n")
fmt.Fprintf(&result, "black = '%s'\n", p.Color0.Hex)
fmt.Fprintf(&result, "red = '%s'\n", p.Color1.Hex)
fmt.Fprintf(&result, "green = '%s'\n", p.Color2.Hex)
fmt.Fprintf(&result, "yellow = '%s'\n", p.Color3.Hex)
fmt.Fprintf(&result, "blue = '%s'\n", p.Color4.Hex)
fmt.Fprintf(&result, "magenta = '%s'\n", p.Color5.Hex)
fmt.Fprintf(&result, "cyan = '%s'\n", p.Color6.Hex)
fmt.Fprintf(&result, "white = '%s'\n", p.Color7.Hex)
result.WriteString("\n[colors.bright]\n")
fmt.Fprintf(&result, "black = '%s'\n", p.Color8.Hex)
fmt.Fprintf(&result, "red = '%s'\n", p.Color9.Hex)
fmt.Fprintf(&result, "green = '%s'\n", p.Color10.Hex)
fmt.Fprintf(&result, "yellow = '%s'\n", p.Color11.Hex)
fmt.Fprintf(&result, "blue = '%s'\n", p.Color12.Hex)
fmt.Fprintf(&result, "magenta = '%s'\n", p.Color13.Hex)
fmt.Fprintf(&result, "cyan = '%s'\n", p.Color14.Hex)
fmt.Fprintf(&result, "white = '%s'\n", p.Color15.Hex)
currentSection := ""
for _, ac := range alacrittyColors {
if ac.section != currentSection {
if currentSection != "" {
result.WriteString("\n")
}
fmt.Fprintf(&result, "[colors.%s]\n", ac.section)
currentSection = ac.section
}
fmt.Fprintf(&result, "%-7s = '%s'\n", ac.name, colors[ac.index])
}
return result.String()
}
func GenerateGhosttyTheme(p Palette) string {
func GenerateGhosttyTheme(colors []string) string {
var result strings.Builder
fmt.Fprintf(&result, "palette = 0=%s\n", p.Color0.Hex)
fmt.Fprintf(&result, "palette = 1=%s\n", p.Color1.Hex)
fmt.Fprintf(&result, "palette = 2=%s\n", p.Color2.Hex)
fmt.Fprintf(&result, "palette = 3=%s\n", p.Color3.Hex)
fmt.Fprintf(&result, "palette = 4=%s\n", p.Color4.Hex)
fmt.Fprintf(&result, "palette = 5=%s\n", p.Color5.Hex)
fmt.Fprintf(&result, "palette = 6=%s\n", p.Color6.Hex)
fmt.Fprintf(&result, "palette = 7=%s\n", p.Color7.Hex)
fmt.Fprintf(&result, "palette = 8=%s\n", p.Color8.Hex)
fmt.Fprintf(&result, "palette = 9=%s\n", p.Color9.Hex)
fmt.Fprintf(&result, "palette = 10=%s\n", p.Color10.Hex)
fmt.Fprintf(&result, "palette = 11=%s\n", p.Color11.Hex)
fmt.Fprintf(&result, "palette = 12=%s\n", p.Color12.Hex)
fmt.Fprintf(&result, "palette = 13=%s\n", p.Color13.Hex)
fmt.Fprintf(&result, "palette = 14=%s\n", p.Color14.Hex)
fmt.Fprintf(&result, "palette = 15=%s\n", p.Color15.Hex)
for i, color := range colors {
fmt.Fprintf(&result, "palette = %d=%s\n", i, color)
}
return result.String()
}
func GenerateWeztermTheme(p Palette) string {
func GenerateWeztermTheme(colors []string) string {
var result strings.Builder
fmt.Fprintf(&result, "ansi = ['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']\n",
p.Color0.Hex, p.Color1.Hex, p.Color2.Hex, p.Color3.Hex,
p.Color4.Hex, p.Color5.Hex, p.Color6.Hex, p.Color7.Hex)
fmt.Fprintf(&result, "brights = ['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']\n",
p.Color8.Hex, p.Color9.Hex, p.Color10.Hex, p.Color11.Hex,
p.Color12.Hex, p.Color13.Hex, p.Color14.Hex, p.Color15.Hex)
labels := []string{"ansi", "brights"}
for j, label := range labels {
start := j * 8
colorSlice := make([]string, 8)
for i, color := range colors[start : start+8] {
colorSlice[i] = fmt.Sprintf("'%s'", color)
}
fmt.Fprintf(&result, "%s = [%s]\n", label, strings.Join(colorSlice, ", "))
}
return result.String()
}

View File

@@ -0,0 +1,250 @@
package dank16
import (
"encoding/json"
"fmt"
)
type VSCodeTheme struct {
Schema string `json:"$schema"`
Name string `json:"name"`
Type string `json:"type"`
Colors map[string]string `json:"colors"`
TokenColors []VSCodeTokenColor `json:"tokenColors"`
SemanticHighlighting bool `json:"semanticHighlighting"`
SemanticTokenColors map[string]VSCodeTokenSetting `json:"semanticTokenColors"`
}
type VSCodeTokenColor struct {
Scope interface{} `json:"scope"`
Settings VSCodeTokenSetting `json:"settings"`
}
type VSCodeTokenSetting struct {
Foreground string `json:"foreground,omitempty"`
FontStyle string `json:"fontStyle,omitempty"`
}
func updateTokenColor(tc interface{}, scopeToColor map[string]string) {
tcMap, ok := tc.(map[string]interface{})
if !ok {
return
}
scopes, ok := tcMap["scope"].([]interface{})
if !ok {
return
}
settings, ok := tcMap["settings"].(map[string]interface{})
if !ok {
return
}
isYaml := hasScopeContaining(scopes, "yaml")
for _, scope := range scopes {
scopeStr, ok := scope.(string)
if !ok {
continue
}
if scopeStr == "string" && isYaml {
continue
}
if applyColorToScope(settings, scope, scopeToColor) {
break
}
}
}
func applyColorToScope(settings map[string]interface{}, scope interface{}, scopeToColor map[string]string) bool {
scopeStr, ok := scope.(string)
if !ok {
return false
}
newColor, exists := scopeToColor[scopeStr]
if !exists {
return false
}
settings["foreground"] = newColor
return true
}
func hasScopeContaining(scopes []interface{}, substring string) bool {
for _, scope := range scopes {
scopeStr, ok := scope.(string)
if !ok {
continue
}
for i := 0; i <= len(scopeStr)-len(substring); i++ {
if scopeStr[i:i+len(substring)] == substring {
return true
}
}
}
return false
}
func EnrichVSCodeTheme(themeData []byte, colors []string) ([]byte, error) {
var theme map[string]interface{}
if err := json.Unmarshal(themeData, &theme); err != nil {
return nil, err
}
colorsMap, ok := theme["colors"].(map[string]interface{})
if !ok {
colorsMap = make(map[string]interface{})
theme["colors"] = colorsMap
}
bg := colors[0]
isLight := false
if len(bg) == 7 && bg[0] == '#' {
r, g, b := 0, 0, 0
fmt.Sscanf(bg[1:], "%02x%02x%02x", &r, &g, &b)
luminance := (0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)) / 255.0
isLight = luminance > 0.5
}
if isLight {
theme["type"] = "light"
} else {
theme["type"] = "dark"
}
colorsMap["terminal.ansiBlack"] = colors[0]
colorsMap["terminal.ansiRed"] = colors[1]
colorsMap["terminal.ansiGreen"] = colors[2]
colorsMap["terminal.ansiYellow"] = colors[3]
colorsMap["terminal.ansiBlue"] = colors[4]
colorsMap["terminal.ansiMagenta"] = colors[5]
colorsMap["terminal.ansiCyan"] = colors[6]
colorsMap["terminal.ansiWhite"] = colors[7]
colorsMap["terminal.ansiBrightBlack"] = colors[8]
colorsMap["terminal.ansiBrightRed"] = colors[9]
colorsMap["terminal.ansiBrightGreen"] = colors[10]
colorsMap["terminal.ansiBrightYellow"] = colors[11]
colorsMap["terminal.ansiBrightBlue"] = colors[12]
colorsMap["terminal.ansiBrightMagenta"] = colors[13]
colorsMap["terminal.ansiBrightCyan"] = colors[14]
colorsMap["terminal.ansiBrightWhite"] = colors[15]
tokenColors, ok := theme["tokenColors"].([]interface{})
if ok {
scopeToColor := map[string]string{
"comment": colors[8],
"punctuation.definition.comment": colors[8],
"keyword": colors[5],
"storage.type": colors[13],
"storage.modifier": colors[5],
"variable": colors[15],
"variable.parameter": colors[7],
"meta.object-literal.key": colors[4],
"meta.property.object": colors[4],
"variable.other.property": colors[4],
"constant.other.symbol": colors[12],
"constant.numeric": colors[12],
"constant.language": colors[12],
"constant.character": colors[3],
"entity.name.type": colors[12],
"support.type": colors[13],
"entity.name.class": colors[12],
"entity.name.function": colors[2],
"support.function": colors[2],
"support.class": colors[15],
"support.variable": colors[15],
"variable.language": colors[12],
"entity.name.tag.yaml": colors[12],
"string.unquoted.plain.out.yaml": colors[15],
"string.unquoted.yaml": colors[15],
"string": colors[3],
}
for i, tc := range tokenColors {
updateTokenColor(tc, scopeToColor)
tokenColors[i] = tc
}
yamlRules := []VSCodeTokenColor{
{
Scope: "entity.name.tag.yaml",
Settings: VSCodeTokenSetting{Foreground: colors[12]},
},
{
Scope: []string{"string.unquoted.plain.out.yaml", "string.unquoted.yaml"},
Settings: VSCodeTokenSetting{Foreground: colors[15]},
},
}
for _, rule := range yamlRules {
tokenColors = append(tokenColors, rule)
}
theme["tokenColors"] = tokenColors
}
if semanticTokenColors, ok := theme["semanticTokenColors"].(map[string]interface{}); ok {
updates := map[string]string{
"variable": colors[15],
"variable.readonly": colors[12],
"property": colors[4],
"function": colors[2],
"method": colors[2],
"type": colors[12],
"class": colors[12],
"typeParameter": colors[13],
"enumMember": colors[12],
"string": colors[3],
"number": colors[12],
"comment": colors[8],
"keyword": colors[5],
"operator": colors[15],
"parameter": colors[7],
"namespace": colors[15],
}
for key, color := range updates {
if existing, ok := semanticTokenColors[key].(map[string]interface{}); ok {
existing["foreground"] = color
} else {
semanticTokenColors[key] = map[string]interface{}{
"foreground": color,
}
}
}
} else {
semanticTokenColors := make(map[string]interface{})
updates := map[string]string{
"variable": colors[7],
"variable.readonly": colors[12],
"property": colors[4],
"function": colors[2],
"method": colors[2],
"type": colors[12],
"class": colors[12],
"typeParameter": colors[13],
"enumMember": colors[12],
"string": colors[3],
"number": colors[12],
"comment": colors[8],
"keyword": colors[5],
"operator": colors[15],
"parameter": colors[7],
"namespace": colors[15],
}
for key, color := range updates {
semanticTokenColors[key] = map[string]interface{}{
"foreground": color,
}
}
theme["semanticTokenColors"] = semanticTokenColors
}
return json.MarshalIndent(theme, "", " ")
}

View File

@@ -37,9 +37,6 @@ func init() {
Register("garuda", "#cba6f7", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
return NewArchDistribution(config, logChan)
})
Register("artix", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
return NewArchDistribution(config, logChan)
})
}
type ArchDistribution struct {

View File

@@ -19,12 +19,10 @@ func init() {
Register("fedora-asahi-remix", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
return NewFedoraDistribution(config, logChan)
})
Register("bluefin", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
return NewFedoraDistribution(config, logChan)
})
Register("ultramarine", "#00078b", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
return NewFedoraDistribution(config, logChan)
})
}
type FedoraDistribution struct {
@@ -508,14 +506,6 @@ func (f *FedoraDistribution) installDNFPackages(ctx context.Context, packages []
f.log(fmt.Sprintf("Installing DNF packages: %s", strings.Join(packages, ", ")))
args := []string{"dnf", "install", "-y"}
for _, pkg := range packages {
if pkg == "niri" || pkg == "niri-git" {
args = append(args, "--setopt=install_weak_deps=False")
break
}
}
args = append(args, packages...)
progressChan <- InstallProgressMsg{

View File

@@ -0,0 +1,466 @@
package distros
import (
"context"
"fmt"
"os/exec"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
func init() {
Register("nixos", "#7EBAE4", FamilyNix, func(config DistroConfig, logChan chan<- string) Distribution {
return NewNixOSDistribution(config, logChan)
})
}
type NixOSDistribution struct {
*BaseDistribution
config DistroConfig
}
func NewNixOSDistribution(config DistroConfig, logChan chan<- string) *NixOSDistribution {
base := NewBaseDistribution(logChan)
return &NixOSDistribution{
BaseDistribution: base,
config: config,
}
}
func (n *NixOSDistribution) GetID() string {
return n.config.ID
}
func (n *NixOSDistribution) GetColorHex() string {
return n.config.ColorHex
}
func (n *NixOSDistribution) GetFamily() DistroFamily {
return n.config.Family
}
func (n *NixOSDistribution) GetPackageManager() PackageManagerType {
return PackageManagerNix
}
func (n *NixOSDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) {
return n.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty)
}
func (n *NixOSDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) {
var dependencies []deps.Dependency
// DMS at the top (shell is prominent)
dependencies = append(dependencies, n.detectDMS())
// Terminal with choice support
dependencies = append(dependencies, n.detectSpecificTerminal(terminal))
// Common detections using base methods
dependencies = append(dependencies, n.detectGit())
dependencies = append(dependencies, n.detectWindowManager(wm))
dependencies = append(dependencies, n.detectQuickshell())
dependencies = append(dependencies, n.detectXDGPortal())
dependencies = append(dependencies, n.detectPolkitAgent())
dependencies = append(dependencies, n.detectAccountsService())
// Hyprland-specific tools
if wm == deps.WindowManagerHyprland {
dependencies = append(dependencies, n.detectHyprlandTools()...)
}
// Niri-specific tools
if wm == deps.WindowManagerNiri {
dependencies = append(dependencies, n.detectXwaylandSatellite())
}
// Base detections (common across distros)
dependencies = append(dependencies, n.detectMatugen())
dependencies = append(dependencies, n.detectDgop())
dependencies = append(dependencies, n.detectHyprpicker())
dependencies = append(dependencies, n.detectClipboardTools()...)
return dependencies, nil
}
func (n *NixOSDistribution) detectDMS() deps.Dependency {
status := deps.StatusMissing
// For NixOS, check if quickshell can find the dms config
cmd := exec.Command("qs", "-c", "dms", "--list")
if err := cmd.Run(); err == nil {
status = deps.StatusInstalled
} else if n.packageInstalled("DankMaterialShell") {
// Fallback: check if flake is in profile
status = deps.StatusInstalled
}
return deps.Dependency{
Name: "dms (DankMaterialShell)",
Status: status,
Description: "Desktop Management System configuration (installed as flake)",
Required: true,
}
}
func (n *NixOSDistribution) detectXDGPortal() deps.Dependency {
status := deps.StatusMissing
if n.packageInstalled("xdg-desktop-portal-gtk") {
status = deps.StatusInstalled
}
return deps.Dependency{
Name: "xdg-desktop-portal-gtk",
Status: status,
Description: "Desktop integration portal for GTK",
Required: true,
}
}
func (n *NixOSDistribution) detectWindowManager(wm deps.WindowManager) deps.Dependency {
switch wm {
case deps.WindowManagerHyprland:
status := deps.StatusMissing
description := "Dynamic tiling Wayland compositor"
if n.commandExists("hyprland") || n.commandExists("Hyprland") {
status = deps.StatusInstalled
} else {
description = "Install system-wide: programs.hyprland.enable = true; in configuration.nix"
}
return deps.Dependency{
Name: "hyprland",
Status: status,
Description: description,
Required: true,
}
case deps.WindowManagerNiri:
status := deps.StatusMissing
description := "Scrollable-tiling Wayland compositor"
if n.commandExists("niri") {
status = deps.StatusInstalled
} else {
description = "Install system-wide: environment.systemPackages = [ pkgs.niri ]; in configuration.nix"
}
return deps.Dependency{
Name: "niri",
Status: status,
Description: description,
Required: true,
}
default:
return deps.Dependency{
Name: "unknown-wm",
Status: deps.StatusMissing,
Description: "Unknown window manager",
Required: true,
}
}
}
func (n *NixOSDistribution) detectHyprlandTools() []deps.Dependency {
var dependencies []deps.Dependency
tools := []struct {
name string
description string
}{
{"grim", "Screenshot utility for Wayland"},
{"slurp", "Region selection utility for Wayland"},
{"hyprctl", "Hyprland control utility (comes with system Hyprland)"},
{"hyprpicker", "Color picker for Hyprland"},
{"grimblast", "Screenshot script for Hyprland"},
{"jq", "JSON processor"},
}
for _, tool := range tools {
status := deps.StatusMissing
// Special handling for hyprctl - it comes with system hyprland
if tool.name == "hyprctl" {
if n.commandExists("hyprctl") {
status = deps.StatusInstalled
}
} else {
if n.commandExists(tool.name) {
status = deps.StatusInstalled
}
}
dependencies = append(dependencies, deps.Dependency{
Name: tool.name,
Status: status,
Description: tool.description,
Required: true,
})
}
return dependencies
}
func (n *NixOSDistribution) detectXwaylandSatellite() deps.Dependency {
status := deps.StatusMissing
if n.commandExists("xwayland-satellite") {
status = deps.StatusInstalled
}
return deps.Dependency{
Name: "xwayland-satellite",
Status: status,
Description: "Xwayland support",
Required: true,
}
}
func (n *NixOSDistribution) detectPolkitAgent() deps.Dependency {
status := deps.StatusMissing
if n.packageInstalled("mate-polkit") {
status = deps.StatusInstalled
}
return deps.Dependency{
Name: "mate-polkit",
Status: status,
Description: "PolicyKit authentication agent",
Required: true,
}
}
func (n *NixOSDistribution) detectAccountsService() deps.Dependency {
status := deps.StatusMissing
if n.packageInstalled("accountsservice") {
status = deps.StatusInstalled
}
return deps.Dependency{
Name: "accountsservice",
Status: status,
Description: "D-Bus interface for user account query and manipulation",
Required: true,
}
}
func (n *NixOSDistribution) packageInstalled(pkg string) bool {
cmd := exec.Command("nix", "profile", "list")
output, err := cmd.Output()
if err != nil {
return false
}
return strings.Contains(string(output), pkg)
}
func (n *NixOSDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping {
packages := map[string]PackageMapping{
"git": {Name: "nixpkgs#git", Repository: RepoTypeSystem},
"quickshell": {Name: "github:quickshell-mirror/quickshell", Repository: RepoTypeFlake},
"matugen": {Name: "github:InioX/matugen", Repository: RepoTypeFlake},
"dgop": {Name: "github:AvengeMedia/dgop", Repository: RepoTypeFlake},
"dms (DankMaterialShell)": {Name: "github:AvengeMedia/DankMaterialShell", Repository: RepoTypeFlake},
"ghostty": {Name: "nixpkgs#ghostty", Repository: RepoTypeSystem},
"alacritty": {Name: "nixpkgs#alacritty", Repository: RepoTypeSystem},
"cliphist": {Name: "nixpkgs#cliphist", Repository: RepoTypeSystem},
"wl-clipboard": {Name: "nixpkgs#wl-clipboard", Repository: RepoTypeSystem},
"xdg-desktop-portal-gtk": {Name: "nixpkgs#xdg-desktop-portal-gtk", Repository: RepoTypeSystem},
"mate-polkit": {Name: "nixpkgs#mate.mate-polkit", Repository: RepoTypeSystem},
"accountsservice": {Name: "nixpkgs#accountsservice", Repository: RepoTypeSystem},
"hyprpicker": {Name: "nixpkgs#hyprpicker", Repository: RepoTypeSystem},
}
// Note: Window managers (hyprland/niri) should be installed system-wide on NixOS
// We only install the tools here
switch wm {
case deps.WindowManagerHyprland:
// Skip hyprland itself - should be installed system-wide
packages["grim"] = PackageMapping{Name: "nixpkgs#grim", Repository: RepoTypeSystem}
packages["slurp"] = PackageMapping{Name: "nixpkgs#slurp", Repository: RepoTypeSystem}
packages["grimblast"] = PackageMapping{Name: "github:hyprwm/contrib#grimblast", Repository: RepoTypeFlake}
packages["jq"] = PackageMapping{Name: "nixpkgs#jq", Repository: RepoTypeSystem}
case deps.WindowManagerNiri:
// Skip niri itself - should be installed system-wide
packages["xwayland-satellite"] = PackageMapping{Name: "nixpkgs#xwayland-satellite", Repository: RepoTypeFlake}
}
return packages
}
func (n *NixOSDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
progressChan <- InstallProgressMsg{
Phase: PhasePrerequisites,
Progress: 0.10,
Step: "NixOS prerequisites ready",
IsComplete: false,
LogOutput: "NixOS package manager is ready to use",
}
return nil
}
func (n *NixOSDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error {
// Phase 1: Check Prerequisites
progressChan <- InstallProgressMsg{
Phase: PhasePrerequisites,
Progress: 0.05,
Step: "Checking system prerequisites...",
IsComplete: false,
LogOutput: "Starting prerequisite check...",
}
if err := n.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install prerequisites: %w", err)
}
nixpkgsPkgs, flakePkgs, _ := n.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
// Phase 2: Nixpkgs Packages
if len(nixpkgsPkgs) > 0 {
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.35,
Step: fmt.Sprintf("Installing %d packages from nixpkgs...", len(nixpkgsPkgs)),
IsComplete: false,
LogOutput: fmt.Sprintf("Installing nixpkgs packages: %s", strings.Join(nixpkgsPkgs, ", ")),
}
if err := n.installNixpkgsPackages(ctx, nixpkgsPkgs, progressChan); err != nil {
return fmt.Errorf("failed to install nixpkgs packages: %w", err)
}
}
// Phase 3: Flake Packages
if len(flakePkgs) > 0 {
progressChan <- InstallProgressMsg{
Phase: PhaseAURPackages,
Progress: 0.65,
Step: fmt.Sprintf("Installing %d packages from flakes...", len(flakePkgs)),
IsComplete: false,
LogOutput: fmt.Sprintf("Installing flake packages: %s", strings.Join(flakePkgs, ", ")),
}
if err := n.installFlakePackages(ctx, flakePkgs, progressChan); err != nil {
return fmt.Errorf("failed to install flake packages: %w", err)
}
}
// Phase 4: Configuration
progressChan <- InstallProgressMsg{
Phase: PhaseConfiguration,
Progress: 0.90,
Step: "Configuring system...",
IsComplete: false,
LogOutput: "Starting post-installation configuration...",
}
if err := n.postInstallConfig(progressChan); err != nil {
return fmt.Errorf("failed to configure system: %w", err)
}
// Phase 5: Complete
progressChan <- InstallProgressMsg{
Phase: PhaseComplete,
Progress: 1.0,
Step: "Installation complete!",
IsComplete: true,
LogOutput: "All packages installed and configured successfully",
}
return nil
}
func (n *NixOSDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string, map[string]deps.PackageVariant) {
nixpkgsPkgs := []string{}
flakePkgs := []string{}
variantMap := make(map[string]deps.PackageVariant)
for _, dep := range dependencies {
variantMap[dep.Name] = dep.Variant
}
packageMap := n.GetPackageMapping(wm)
for _, dep := range dependencies {
if disabledFlags[dep.Name] {
continue
}
if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] {
continue
}
pkgInfo, exists := packageMap[dep.Name]
if !exists {
n.log(fmt.Sprintf("Warning: No package mapping found for %s", dep.Name))
continue
}
switch pkgInfo.Repository {
case RepoTypeSystem:
nixpkgsPkgs = append(nixpkgsPkgs, pkgInfo.Name)
case RepoTypeFlake:
flakePkgs = append(flakePkgs, pkgInfo.Name)
}
}
return nixpkgsPkgs, flakePkgs, variantMap
}
func (n *NixOSDistribution) installNixpkgsPackages(ctx context.Context, packages []string, progressChan chan<- InstallProgressMsg) error {
if len(packages) == 0 {
return nil
}
n.log(fmt.Sprintf("Installing nixpkgs packages: %s", strings.Join(packages, ", ")))
args := []string{"profile", "install"}
args = append(args, packages...)
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.40,
Step: "Installing nixpkgs packages...",
IsComplete: false,
CommandInfo: fmt.Sprintf("nix %s", strings.Join(args, " ")),
}
cmd := exec.CommandContext(ctx, "nix", args...)
return n.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60)
}
func (n *NixOSDistribution) installFlakePackages(ctx context.Context, packages []string, progressChan chan<- InstallProgressMsg) error {
if len(packages) == 0 {
return nil
}
n.log(fmt.Sprintf("Installing flake packages: %s", strings.Join(packages, ", ")))
baseProgress := 0.65
progressStep := 0.20 / float64(len(packages))
for i, pkg := range packages {
currentProgress := baseProgress + (float64(i) * progressStep)
progressChan <- InstallProgressMsg{
Phase: PhaseAURPackages,
Progress: currentProgress,
Step: fmt.Sprintf("Installing flake package %s (%d/%d)...", pkg, i+1, len(packages)),
IsComplete: false,
CommandInfo: fmt.Sprintf("nix profile install %s", pkg),
}
cmd := exec.CommandContext(ctx, "nix", "profile", "install", pkg)
if err := n.runWithProgress(cmd, progressChan, PhaseAURPackages, currentProgress, currentProgress+progressStep); err != nil {
return fmt.Errorf("failed to install flake package %s: %w", pkg, err)
}
}
return nil
}
func (n *NixOSDistribution) postInstallConfig(progressChan chan<- InstallProgressMsg) error {
// For NixOS, DMS is installed as a flake package, so we skip both the binary installation and git clone
// The flake installation handles both the binary and config files correctly
progressChan <- InstallProgressMsg{
Phase: PhaseConfiguration,
Progress: 0.95,
Step: "NixOS configuration complete",
IsComplete: false,
LogOutput: "DMS installed via flake - binary and config handled by Nix",
}
return nil
}

View File

@@ -105,19 +105,14 @@ type MenuItem struct {
func NewModel(version string) Model {
detector, _ := NewDetector()
var dependencies []DependencyInfo
var hyprlandInstalled, niriInstalled bool
var err error
if detector != nil {
dependencies = detector.GetInstalledComponents()
dependencies := detector.GetInstalledComponents()
// Use the proper detection method for both window managers
hyprlandInstalled, niriInstalled, err = detector.GetWindowManagerStatus()
if err != nil {
// Fallback to false if detection fails
hyprlandInstalled = false
niriInstalled = false
}
// Use the proper detection method for both window managers
hyprlandInstalled, niriInstalled, err := detector.GetWindowManagerStatus()
if err != nil {
// Fallback to false if detection fails
hyprlandInstalled = false
niriInstalled = false
}
updateToggles := make(map[string]bool)

View File

@@ -122,8 +122,15 @@ func (d *Detector) GetInstalledComponents() []DependencyInfo {
return []DependencyInfo{}
}
isNixOS := d.isNixOS()
var components []DependencyInfo
for _, dep := range dependencies {
// On NixOS, filter out the window managers themselves but keep their components
if isNixOS && (dep.Name == "hyprland" || dep.Name == "niri") {
continue
}
components = append(components, DependencyInfo{
Name: dep.Name,
Status: dep.Status,
@@ -135,6 +142,23 @@ func (d *Detector) GetInstalledComponents() []DependencyInfo {
return components
}
func (d *Detector) isNixOS() bool {
_, err := os.Stat("/etc/nixos")
if err == nil {
return true
}
// Alternative check
if _, err := os.Stat("/nix/store"); err == nil {
// Also check for nixos-version command
if d.commandExists("nixos-version") {
return true
}
}
return false
}
type DependencyInfo struct {
Name string
Status deps.DependencyStatus

View File

@@ -109,9 +109,6 @@ func (m Model) renderAboutView() string {
b.WriteString(normalStyle.Render("Components:"))
b.WriteString("\n")
if len(m.dependencies) == 0 {
b.WriteString(normalStyle.Render("\n Component detection not supported on this platform."))
}
for _, dep := range m.dependencies {
status := "✗"
if dep.Status == 1 {

View File

@@ -367,7 +367,7 @@ func SyncDMSConfigs(dmsPath string, logFunc func(string), sudoPassword string) e
}
}
runSudoCmd(sudoPassword, "rm", "-f", link.target) //nolint:errcheck
runSudoCmd(sudoPassword, "rm", "-f", link.target)
if err := runSudoCmd(sudoPassword, "ln", "-sf", link.source, link.target); err != nil {
logFunc(fmt.Sprintf("⚠ Warning: Failed to create symlink for %s: %v", link.desc, err))

View File

@@ -77,7 +77,7 @@ func (d *DiscoveryConfig) FindJSONFiles() ([]string, error) {
func expandPath(path string) (string, error) {
expandedPath := os.ExpandEnv(path)
if strings.HasPrefix(expandedPath, "~") {
if filepath.HasPrefix(expandedPath, "~") {
home, err := os.UserHomeDir()
if err != nil {
return "", err

View File

@@ -87,22 +87,20 @@ func (h *HyprlandProvider) categorizeByDispatcher(dispatcher string) string {
func (h *HyprlandProvider) convertKeybind(kb *HyprlandKeyBinding, subcategory string) keybinds.Keybind {
key := h.formatKey(kb)
rawAction := h.formatRawAction(kb.Dispatcher, kb.Params)
desc := kb.Comment
if desc == "" {
desc = rawAction
desc = h.generateDescription(kb.Dispatcher, kb.Params)
}
return keybinds.Keybind{
Key: key,
Description: desc,
Action: rawAction,
Subcategory: subcategory,
}
}
func (h *HyprlandProvider) formatRawAction(dispatcher, params string) string {
func (h *HyprlandProvider) generateDescription(dispatcher, params string) string {
if params != "" {
return dispatcher + " " + params
}

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds"
)
@@ -44,7 +43,7 @@ func (j *JSONFileProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
return nil, fmt.Errorf("failed to read file: %w", err)
}
var rawData map[string]any
var rawData map[string]interface{}
if err := json.Unmarshal(data, &rawData); err != nil {
return nil, fmt.Errorf("failed to parse JSON: %w", err)
}
@@ -63,9 +62,9 @@ func (j *JSONFileProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
}
switch binds := bindsRaw.(type) {
case map[string]any:
case map[string]interface{}:
for category, categoryBindsRaw := range binds {
categoryBindsList, ok := categoryBindsRaw.([]any)
categoryBindsList, ok := categoryBindsRaw.([]interface{})
if !ok {
continue
}
@@ -79,12 +78,11 @@ func (j *JSONFileProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
categorizedBinds[category] = keybindsList
}
case []any:
case []interface{}:
flatBindsJSON, _ := json.Marshal(binds)
var flatBinds []struct {
Key string `json:"key"`
Description string `json:"desc"`
Action string `json:"action,omitempty"`
Category string `json:"cat,omitempty"`
Subcategory string `json:"subcat,omitempty"`
}
@@ -101,7 +99,6 @@ func (j *JSONFileProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
kb := keybinds.Keybind{
Key: bind.Key,
Description: bind.Description,
Action: bind.Action,
Subcategory: bind.Subcategory,
}
categorizedBinds[category] = append(categorizedBinds[category], kb)
@@ -121,7 +118,7 @@ func (j *JSONFileProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
func expandPath(path string) (string, error) {
expandedPath := os.ExpandEnv(path)
if strings.HasPrefix(expandedPath, "~") {
if filepath.HasPrefix(expandedPath, "~") {
home, err := os.UserHomeDir()
if err != nil {
return "", err

View File

@@ -84,21 +84,19 @@ func (m *MangoWCProvider) categorizeByCommand(command string) string {
func (m *MangoWCProvider) convertKeybind(kb *MangoWCKeyBinding) keybinds.Keybind {
key := m.formatKey(kb)
rawAction := m.formatRawAction(kb.Command, kb.Params)
desc := kb.Comment
if desc == "" {
desc = rawAction
desc = m.generateDescription(kb.Command, kb.Params)
}
return keybinds.Keybind{
Key: key,
Description: desc,
Action: rawAction,
}
}
func (m *MangoWCProvider) formatRawAction(command, params string) string {
func (m *MangoWCProvider) generateDescription(command, params string) string {
if params != "" {
return command + " " + params
}

View File

@@ -1,137 +0,0 @@
package providers
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds"
)
type NiriProvider struct {
configDir string
}
func NewNiriProvider(configDir string) *NiriProvider {
if configDir == "" {
configDir = defaultNiriConfigDir()
}
return &NiriProvider{
configDir: configDir,
}
}
func defaultNiriConfigDir() string {
configHome := os.Getenv("XDG_CONFIG_HOME")
if configHome != "" {
return filepath.Join(configHome, "niri")
}
home, err := os.UserHomeDir()
if err != nil {
return ""
}
return filepath.Join(home, ".config", "niri")
}
func (n *NiriProvider) Name() string {
return "niri"
}
func (n *NiriProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
section, err := ParseNiriKeys(n.configDir)
if err != nil {
return nil, fmt.Errorf("failed to parse niri config: %w", err)
}
categorizedBinds := make(map[string][]keybinds.Keybind)
n.convertSection(section, "", categorizedBinds)
return &keybinds.CheatSheet{
Title: "Niri Keybinds",
Provider: n.Name(),
Binds: categorizedBinds,
}, nil
}
func (n *NiriProvider) convertSection(section *NiriSection, subcategory string, categorizedBinds map[string][]keybinds.Keybind) {
currentSubcat := subcategory
if section.Name != "" {
currentSubcat = section.Name
}
for _, kb := range section.Keybinds {
category := n.categorizeByAction(kb.Action)
bind := n.convertKeybind(&kb, currentSubcat)
categorizedBinds[category] = append(categorizedBinds[category], bind)
}
for _, child := range section.Children {
n.convertSection(&child, currentSubcat, categorizedBinds)
}
}
func (n *NiriProvider) categorizeByAction(action string) string {
switch {
case action == "next-window" || action == "previous-window":
return "Alt-Tab"
case strings.Contains(action, "screenshot"):
return "Screenshot"
case action == "show-hotkey-overlay" || action == "toggle-overview":
return "Overview"
case action == "quit" ||
action == "power-off-monitors" ||
action == "toggle-keyboard-shortcuts-inhibit" ||
strings.Contains(action, "dpms"):
return "System"
case action == "spawn":
return "Execute"
case strings.Contains(action, "workspace"):
return "Workspace"
case strings.HasPrefix(action, "focus-monitor") ||
strings.HasPrefix(action, "move-column-to-monitor") ||
strings.HasPrefix(action, "move-window-to-monitor"):
return "Monitor"
case strings.Contains(action, "window") ||
strings.Contains(action, "focus") ||
strings.Contains(action, "move") ||
strings.Contains(action, "swap") ||
strings.Contains(action, "resize") ||
strings.Contains(action, "column"):
return "Window"
default:
return "Other"
}
}
func (n *NiriProvider) convertKeybind(kb *NiriKeyBinding, subcategory string) keybinds.Keybind {
key := n.formatKey(kb)
desc := kb.Description
rawAction := n.formatRawAction(kb.Action, kb.Args)
if desc == "" {
desc = rawAction
}
return keybinds.Keybind{
Key: key,
Description: desc,
Action: rawAction,
Subcategory: subcategory,
}
}
func (n *NiriProvider) formatRawAction(action string, args []string) string {
if len(args) == 0 {
return action
}
return action + " " + strings.Join(args, " ")
}
func (n *NiriProvider) formatKey(kb *NiriKeyBinding) string {
parts := make([]string, 0, len(kb.Mods)+1)
parts = append(parts, kb.Mods...)
parts = append(parts, kb.Key)
return strings.Join(parts, "+")
}

View File

@@ -1,229 +0,0 @@
package providers
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/sblinch/kdl-go"
"github.com/sblinch/kdl-go/document"
)
type NiriKeyBinding struct {
Mods []string
Key string
Action string
Args []string
Description string
}
type NiriSection struct {
Name string
Keybinds []NiriKeyBinding
Children []NiriSection
}
type NiriParser struct {
configDir string
processedFiles map[string]bool
bindMap map[string]*NiriKeyBinding
bindOrder []string
}
func NewNiriParser(configDir string) *NiriParser {
return &NiriParser{
configDir: configDir,
processedFiles: make(map[string]bool),
bindMap: make(map[string]*NiriKeyBinding),
bindOrder: []string{},
}
}
func (p *NiriParser) Parse() (*NiriSection, error) {
configPath := filepath.Join(p.configDir, "config.kdl")
section, err := p.parseFile(configPath, "")
if err != nil {
return nil, err
}
section.Keybinds = p.finalizeBinds()
return section, nil
}
func (p *NiriParser) finalizeBinds() []NiriKeyBinding {
binds := make([]NiriKeyBinding, 0, len(p.bindOrder))
for _, key := range p.bindOrder {
if kb, ok := p.bindMap[key]; ok {
binds = append(binds, *kb)
}
}
return binds
}
func (p *NiriParser) addBind(kb *NiriKeyBinding) {
key := p.formatBindKey(kb)
if _, exists := p.bindMap[key]; !exists {
p.bindOrder = append(p.bindOrder, key)
}
p.bindMap[key] = kb
}
func (p *NiriParser) formatBindKey(kb *NiriKeyBinding) string {
parts := make([]string, 0, len(kb.Mods)+1)
parts = append(parts, kb.Mods...)
parts = append(parts, kb.Key)
return strings.Join(parts, "+")
}
func (p *NiriParser) parseFile(filePath, sectionName string) (*NiriSection, error) {
absPath, err := filepath.Abs(filePath)
if err != nil {
return nil, fmt.Errorf("failed to resolve path %s: %w", filePath, err)
}
if p.processedFiles[absPath] {
return &NiriSection{Name: sectionName}, nil
}
p.processedFiles[absPath] = true
data, err := os.ReadFile(absPath)
if err != nil {
return nil, fmt.Errorf("failed to read %s: %w", absPath, err)
}
doc, err := kdl.Parse(strings.NewReader(string(data)))
if err != nil {
return nil, fmt.Errorf("failed to parse KDL in %s: %w", absPath, err)
}
section := &NiriSection{
Name: sectionName,
}
baseDir := filepath.Dir(absPath)
p.processNodes(doc.Nodes, section, baseDir)
return section, nil
}
func (p *NiriParser) processNodes(nodes []*document.Node, section *NiriSection, baseDir string) {
for _, node := range nodes {
name := node.Name.String()
switch name {
case "include":
p.handleInclude(node, section, baseDir)
case "binds":
p.extractBinds(node, section, "")
case "recent-windows":
p.handleRecentWindows(node, section)
}
}
}
func (p *NiriParser) handleInclude(node *document.Node, section *NiriSection, baseDir string) {
if len(node.Arguments) == 0 {
return
}
includePath := node.Arguments[0].String()
includePath = strings.Trim(includePath, "\"")
var fullPath string
if filepath.IsAbs(includePath) {
fullPath = includePath
} else {
fullPath = filepath.Join(baseDir, includePath)
}
includedSection, err := p.parseFile(fullPath, "")
if err != nil {
return
}
section.Children = append(section.Children, includedSection.Children...)
}
func (p *NiriParser) handleRecentWindows(node *document.Node, section *NiriSection) {
if node.Children == nil {
return
}
for _, child := range node.Children {
if child.Name.String() != "binds" {
continue
}
p.extractBinds(child, section, "Alt-Tab")
}
}
func (p *NiriParser) extractBinds(node *document.Node, section *NiriSection, subcategory string) {
if node.Children == nil {
return
}
for _, child := range node.Children {
kb := p.parseKeybindNode(child, subcategory)
if kb == nil {
continue
}
p.addBind(kb)
}
}
func (p *NiriParser) parseKeybindNode(node *document.Node, subcategory string) *NiriKeyBinding {
keyCombo := node.Name.String()
if keyCombo == "" {
return nil
}
mods, key := p.parseKeyCombo(keyCombo)
var action string
var args []string
if len(node.Children) > 0 {
actionNode := node.Children[0]
action = actionNode.Name.String()
for _, arg := range actionNode.Arguments {
args = append(args, strings.Trim(arg.String(), "\""))
}
}
description := ""
if node.Properties != nil {
if val, ok := node.Properties.Get("hotkey-overlay-title"); ok {
description = strings.Trim(val.String(), "\"")
}
}
return &NiriKeyBinding{
Mods: mods,
Key: key,
Action: action,
Args: args,
Description: description,
}
}
func (p *NiriParser) parseKeyCombo(combo string) ([]string, string) {
parts := strings.Split(combo, "+")
if len(parts) == 0 {
return nil, combo
}
if len(parts) == 1 {
return nil, parts[0]
}
mods := parts[:len(parts)-1]
key := parts[len(parts)-1]
return mods, key
}
func ParseNiriKeys(configDir string) (*NiriSection, error) {
parser := NewNiriParser(configDir)
return parser.Parse()
}

View File

@@ -1,498 +0,0 @@
package providers
import (
"os"
"path/filepath"
"testing"
)
func TestNiriParseKeyCombo(t *testing.T) {
tests := []struct {
combo string
expectedMods []string
expectedKey string
}{
{"Mod+Q", []string{"Mod"}, "Q"},
{"Mod+Shift+F", []string{"Mod", "Shift"}, "F"},
{"Ctrl+Alt+Delete", []string{"Ctrl", "Alt"}, "Delete"},
{"Print", nil, "Print"},
{"XF86AudioMute", nil, "XF86AudioMute"},
{"Super+Tab", []string{"Super"}, "Tab"},
{"Mod+Shift+Ctrl+H", []string{"Mod", "Shift", "Ctrl"}, "H"},
}
parser := NewNiriParser("")
for _, tt := range tests {
t.Run(tt.combo, func(t *testing.T) {
mods, key := parser.parseKeyCombo(tt.combo)
if len(mods) != len(tt.expectedMods) {
t.Errorf("Mods length = %d, want %d", len(mods), len(tt.expectedMods))
} else {
for i := range mods {
if mods[i] != tt.expectedMods[i] {
t.Errorf("Mods[%d] = %q, want %q", i, mods[i], tt.expectedMods[i])
}
}
}
if key != tt.expectedKey {
t.Errorf("Key = %q, want %q", key, tt.expectedKey)
}
})
}
}
func TestNiriParseBasicBinds(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.kdl")
content := `binds {
Mod+Q { close-window; }
Mod+F { fullscreen-window; }
Mod+T hotkey-overlay-title="Open Terminal" { spawn "kitty"; }
}
`
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseNiriKeys(tmpDir)
if err != nil {
t.Fatalf("ParseNiriKeys failed: %v", err)
}
if len(section.Keybinds) != 3 {
t.Errorf("Expected 3 keybinds, got %d", len(section.Keybinds))
}
foundClose := false
foundFullscreen := false
foundTerminal := false
for _, kb := range section.Keybinds {
switch kb.Action {
case "close-window":
foundClose = true
if kb.Key != "Q" || len(kb.Mods) != 1 || kb.Mods[0] != "Mod" {
t.Errorf("close-window keybind mismatch: %+v", kb)
}
case "fullscreen-window":
foundFullscreen = true
case "spawn":
foundTerminal = true
if kb.Description != "Open Terminal" {
t.Errorf("spawn description = %q, want %q", kb.Description, "Open Terminal")
}
if len(kb.Args) != 1 || kb.Args[0] != "kitty" {
t.Errorf("spawn args = %v, want [kitty]", kb.Args)
}
}
}
if !foundClose {
t.Error("close-window keybind not found")
}
if !foundFullscreen {
t.Error("fullscreen-window keybind not found")
}
if !foundTerminal {
t.Error("spawn keybind not found")
}
}
func TestNiriParseRecentWindows(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.kdl")
content := `recent-windows {
binds {
Alt+Tab { next-window scope="output"; }
Alt+Shift+Tab { previous-window scope="output"; }
}
}
`
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseNiriKeys(tmpDir)
if err != nil {
t.Fatalf("ParseNiriKeys failed: %v", err)
}
if len(section.Keybinds) != 2 {
t.Errorf("Expected 2 keybinds from recent-windows, got %d", len(section.Keybinds))
}
foundNext := false
foundPrev := false
for _, kb := range section.Keybinds {
switch kb.Action {
case "next-window":
foundNext = true
case "previous-window":
foundPrev = true
}
}
if !foundNext {
t.Error("next-window keybind not found")
}
if !foundPrev {
t.Error("previous-window keybind not found")
}
}
func TestNiriParseInclude(t *testing.T) {
tmpDir := t.TempDir()
subDir := filepath.Join(tmpDir, "dms")
if err := os.MkdirAll(subDir, 0755); err != nil {
t.Fatalf("Failed to create subdir: %v", err)
}
mainConfig := filepath.Join(tmpDir, "config.kdl")
includeConfig := filepath.Join(subDir, "binds.kdl")
mainContent := `binds {
Mod+Q { close-window; }
}
include "dms/binds.kdl"
`
includeContent := `binds {
Mod+T hotkey-overlay-title="Terminal" { spawn "kitty"; }
}
`
if err := os.WriteFile(mainConfig, []byte(mainContent), 0644); err != nil {
t.Fatalf("Failed to write main config: %v", err)
}
if err := os.WriteFile(includeConfig, []byte(includeContent), 0644); err != nil {
t.Fatalf("Failed to write include config: %v", err)
}
section, err := ParseNiriKeys(tmpDir)
if err != nil {
t.Fatalf("ParseNiriKeys failed: %v", err)
}
if len(section.Keybinds) != 2 {
t.Errorf("Expected 2 keybinds (1 main + 1 include), got %d", len(section.Keybinds))
}
}
func TestNiriParseIncludeOverride(t *testing.T) {
tmpDir := t.TempDir()
subDir := filepath.Join(tmpDir, "dms")
if err := os.MkdirAll(subDir, 0755); err != nil {
t.Fatalf("Failed to create subdir: %v", err)
}
mainConfig := filepath.Join(tmpDir, "config.kdl")
includeConfig := filepath.Join(subDir, "binds.kdl")
mainContent := `binds {
Mod+T hotkey-overlay-title="Main Terminal" { spawn "alacritty"; }
}
include "dms/binds.kdl"
`
includeContent := `binds {
Mod+T hotkey-overlay-title="Override Terminal" { spawn "kitty"; }
}
`
if err := os.WriteFile(mainConfig, []byte(mainContent), 0644); err != nil {
t.Fatalf("Failed to write main config: %v", err)
}
if err := os.WriteFile(includeConfig, []byte(includeContent), 0644); err != nil {
t.Fatalf("Failed to write include config: %v", err)
}
section, err := ParseNiriKeys(tmpDir)
if err != nil {
t.Fatalf("ParseNiriKeys failed: %v", err)
}
if len(section.Keybinds) != 1 {
t.Errorf("Expected 1 keybind (later overrides earlier), got %d", len(section.Keybinds))
}
if len(section.Keybinds) > 0 {
kb := section.Keybinds[0]
if kb.Description != "Override Terminal" {
t.Errorf("Expected description 'Override Terminal' (from include), got %q", kb.Description)
}
if len(kb.Args) != 1 || kb.Args[0] != "kitty" {
t.Errorf("Expected args [kitty] (from include), got %v", kb.Args)
}
}
}
func TestNiriParseCircularInclude(t *testing.T) {
tmpDir := t.TempDir()
mainConfig := filepath.Join(tmpDir, "config.kdl")
otherConfig := filepath.Join(tmpDir, "other.kdl")
mainContent := `binds {
Mod+Q { close-window; }
}
include "other.kdl"
`
otherContent := `binds {
Mod+T { spawn "kitty"; }
}
include "config.kdl"
`
if err := os.WriteFile(mainConfig, []byte(mainContent), 0644); err != nil {
t.Fatalf("Failed to write main config: %v", err)
}
if err := os.WriteFile(otherConfig, []byte(otherContent), 0644); err != nil {
t.Fatalf("Failed to write other config: %v", err)
}
section, err := ParseNiriKeys(tmpDir)
if err != nil {
t.Fatalf("ParseNiriKeys failed (should handle circular includes): %v", err)
}
if len(section.Keybinds) != 2 {
t.Errorf("Expected 2 keybinds (circular include handled), got %d", len(section.Keybinds))
}
}
func TestNiriParseMissingInclude(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.kdl")
content := `binds {
Mod+Q { close-window; }
}
include "nonexistent/file.kdl"
`
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseNiriKeys(tmpDir)
if err != nil {
t.Fatalf("ParseNiriKeys failed (should skip missing include): %v", err)
}
if len(section.Keybinds) != 1 {
t.Errorf("Expected 1 keybind (missing include skipped), got %d", len(section.Keybinds))
}
}
func TestNiriParseNoBinds(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.kdl")
content := `cursor {
xcursor-theme "Bibata"
xcursor-size 24
}
input {
keyboard {
numlock
}
}
`
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseNiriKeys(tmpDir)
if err != nil {
t.Fatalf("ParseNiriKeys failed: %v", err)
}
if len(section.Keybinds) != 0 {
t.Errorf("Expected 0 keybinds, got %d", len(section.Keybinds))
}
}
func TestNiriParseErrors(t *testing.T) {
tests := []struct {
name string
path string
}{
{
name: "nonexistent_directory",
path: "/nonexistent/path/that/does/not/exist",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := ParseNiriKeys(tt.path)
if err == nil {
t.Error("Expected error, got nil")
}
})
}
}
func TestNiriBindOverrideBehavior(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.kdl")
content := `binds {
Mod+T hotkey-overlay-title="First" { spawn "first"; }
Mod+Q { close-window; }
Mod+T hotkey-overlay-title="Second" { spawn "second"; }
Mod+F { fullscreen-window; }
Mod+T hotkey-overlay-title="Third" { spawn "third"; }
}
`
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseNiriKeys(tmpDir)
if err != nil {
t.Fatalf("ParseNiriKeys failed: %v", err)
}
if len(section.Keybinds) != 3 {
t.Fatalf("Expected 3 unique keybinds, got %d", len(section.Keybinds))
}
var modT *NiriKeyBinding
for i := range section.Keybinds {
kb := &section.Keybinds[i]
if len(kb.Mods) == 1 && kb.Mods[0] == "Mod" && kb.Key == "T" {
modT = kb
break
}
}
if modT == nil {
t.Fatal("Mod+T keybind not found")
}
if modT.Description != "Third" {
t.Errorf("Mod+T description = %q, want 'Third' (last definition wins)", modT.Description)
}
if len(modT.Args) != 1 || modT.Args[0] != "third" {
t.Errorf("Mod+T args = %v, want [third] (last definition wins)", modT.Args)
}
}
func TestNiriBindOverrideWithIncludes(t *testing.T) {
tmpDir := t.TempDir()
subDir := filepath.Join(tmpDir, "custom")
if err := os.MkdirAll(subDir, 0755); err != nil {
t.Fatalf("Failed to create subdir: %v", err)
}
mainConfig := filepath.Join(tmpDir, "config.kdl")
includeConfig := filepath.Join(subDir, "overrides.kdl")
mainContent := `binds {
Mod+1 { focus-workspace 1; }
Mod+2 { focus-workspace 2; }
Mod+T hotkey-overlay-title="Default Terminal" { spawn "xterm"; }
}
include "custom/overrides.kdl"
binds {
Mod+3 { focus-workspace 3; }
}
`
includeContent := `binds {
Mod+T hotkey-overlay-title="Custom Terminal" { spawn "kitty"; }
Mod+2 { focus-workspace 22; }
}
`
if err := os.WriteFile(mainConfig, []byte(mainContent), 0644); err != nil {
t.Fatalf("Failed to write main config: %v", err)
}
if err := os.WriteFile(includeConfig, []byte(includeContent), 0644); err != nil {
t.Fatalf("Failed to write include config: %v", err)
}
section, err := ParseNiriKeys(tmpDir)
if err != nil {
t.Fatalf("ParseNiriKeys failed: %v", err)
}
if len(section.Keybinds) != 4 {
t.Errorf("Expected 4 unique keybinds, got %d", len(section.Keybinds))
}
bindMap := make(map[string]*NiriKeyBinding)
for i := range section.Keybinds {
kb := &section.Keybinds[i]
key := ""
for _, m := range kb.Mods {
key += m + "+"
}
key += kb.Key
bindMap[key] = kb
}
if kb, ok := bindMap["Mod+T"]; ok {
if kb.Description != "Custom Terminal" {
t.Errorf("Mod+T should be overridden by include, got description %q", kb.Description)
}
} else {
t.Error("Mod+T not found")
}
if kb, ok := bindMap["Mod+2"]; ok {
if len(kb.Args) != 1 || kb.Args[0] != "22" {
t.Errorf("Mod+2 should be overridden by include with workspace 22, got args %v", kb.Args)
}
} else {
t.Error("Mod+2 not found")
}
if _, ok := bindMap["Mod+1"]; !ok {
t.Error("Mod+1 should exist (not overridden)")
}
if _, ok := bindMap["Mod+3"]; !ok {
t.Error("Mod+3 should exist (added after include)")
}
}
func TestNiriParseMultipleArgs(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.kdl")
content := `binds {
Mod+Space hotkey-overlay-title="Application Launcher" {
spawn "dms" "ipc" "call" "spotlight" "toggle";
}
}
`
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseNiriKeys(tmpDir)
if err != nil {
t.Fatalf("ParseNiriKeys failed: %v", err)
}
if len(section.Keybinds) != 1 {
t.Fatalf("Expected 1 keybind, got %d", len(section.Keybinds))
}
kb := section.Keybinds[0]
if len(kb.Args) != 5 {
t.Errorf("Expected 5 args, got %d: %v", len(kb.Args), kb.Args)
}
expectedArgs := []string{"dms", "ipc", "call", "spotlight", "toggle"}
for i, arg := range expectedArgs {
if i < len(kb.Args) && kb.Args[i] != arg {
t.Errorf("Args[%d] = %q, want %q", i, kb.Args[i], arg)
}
}
}

View File

@@ -1,261 +0,0 @@
package providers
import (
"os"
"path/filepath"
"testing"
)
func TestNiriProviderName(t *testing.T) {
provider := NewNiriProvider("")
if provider.Name() != "niri" {
t.Errorf("Name() = %q, want %q", provider.Name(), "niri")
}
}
func TestNiriProviderGetCheatSheet(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.kdl")
content := `binds {
Mod+Q { close-window; }
Mod+F { fullscreen-window; }
Mod+T hotkey-overlay-title="Open Terminal" { spawn "kitty"; }
Mod+1 { focus-workspace 1; }
Mod+Shift+1 { move-column-to-workspace 1; }
Print { screenshot; }
Mod+Shift+E { quit; }
}
`
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write test config: %v", err)
}
provider := NewNiriProvider(tmpDir)
cheatSheet, err := provider.GetCheatSheet()
if err != nil {
t.Fatalf("GetCheatSheet failed: %v", err)
}
if cheatSheet.Title != "Niri Keybinds" {
t.Errorf("Title = %q, want %q", cheatSheet.Title, "Niri Keybinds")
}
if cheatSheet.Provider != "niri" {
t.Errorf("Provider = %q, want %q", cheatSheet.Provider, "niri")
}
windowBinds := cheatSheet.Binds["Window"]
if len(windowBinds) < 2 {
t.Errorf("Expected at least 2 Window binds, got %d", len(windowBinds))
}
execBinds := cheatSheet.Binds["Execute"]
if len(execBinds) < 1 {
t.Errorf("Expected at least 1 Execute bind, got %d", len(execBinds))
}
workspaceBinds := cheatSheet.Binds["Workspace"]
if len(workspaceBinds) < 2 {
t.Errorf("Expected at least 2 Workspace binds, got %d", len(workspaceBinds))
}
screenshotBinds := cheatSheet.Binds["Screenshot"]
if len(screenshotBinds) < 1 {
t.Errorf("Expected at least 1 Screenshot bind, got %d", len(screenshotBinds))
}
systemBinds := cheatSheet.Binds["System"]
if len(systemBinds) < 1 {
t.Errorf("Expected at least 1 System bind, got %d", len(systemBinds))
}
}
func TestNiriCategorizeByAction(t *testing.T) {
provider := NewNiriProvider("")
tests := []struct {
action string
expected string
}{
{"focus-workspace", "Workspace"},
{"focus-workspace-up", "Workspace"},
{"move-column-to-workspace", "Workspace"},
{"focus-monitor-left", "Monitor"},
{"move-column-to-monitor-right", "Monitor"},
{"close-window", "Window"},
{"fullscreen-window", "Window"},
{"maximize-column", "Window"},
{"toggle-window-floating", "Window"},
{"focus-column-left", "Window"},
{"move-column-right", "Window"},
{"spawn", "Execute"},
{"quit", "System"},
{"power-off-monitors", "System"},
{"screenshot", "Screenshot"},
{"screenshot-window", "Screenshot"},
{"toggle-overview", "Overview"},
{"show-hotkey-overlay", "Overview"},
{"next-window", "Alt-Tab"},
{"previous-window", "Alt-Tab"},
{"unknown-action", "Other"},
}
for _, tt := range tests {
t.Run(tt.action, func(t *testing.T) {
result := provider.categorizeByAction(tt.action)
if result != tt.expected {
t.Errorf("categorizeByAction(%q) = %q, want %q", tt.action, result, tt.expected)
}
})
}
}
func TestNiriFormatRawAction(t *testing.T) {
provider := NewNiriProvider("")
tests := []struct {
action string
args []string
expected string
}{
{"spawn", []string{"kitty"}, "spawn kitty"},
{"spawn", []string{"dms", "ipc", "call"}, "spawn dms ipc call"},
{"close-window", nil, "close-window"},
{"fullscreen-window", nil, "fullscreen-window"},
{"focus-workspace", []string{"1"}, "focus-workspace 1"},
{"move-column-to-workspace", []string{"5"}, "move-column-to-workspace 5"},
{"set-column-width", []string{"+10%"}, "set-column-width +10%"},
}
for _, tt := range tests {
t.Run(tt.action, func(t *testing.T) {
result := provider.formatRawAction(tt.action, tt.args)
if result != tt.expected {
t.Errorf("formatRawAction(%q, %v) = %q, want %q", tt.action, tt.args, result, tt.expected)
}
})
}
}
func TestNiriFormatKey(t *testing.T) {
provider := NewNiriProvider("")
tests := []struct {
mods []string
key string
expected string
}{
{[]string{"Mod"}, "Q", "Mod+Q"},
{[]string{"Mod", "Shift"}, "F", "Mod+Shift+F"},
{[]string{"Ctrl", "Alt"}, "Delete", "Ctrl+Alt+Delete"},
{nil, "Print", "Print"},
{[]string{}, "XF86AudioMute", "XF86AudioMute"},
}
for _, tt := range tests {
t.Run(tt.expected, func(t *testing.T) {
kb := &NiriKeyBinding{
Mods: tt.mods,
Key: tt.key,
}
result := provider.formatKey(kb)
if result != tt.expected {
t.Errorf("formatKey(%v) = %q, want %q", kb, result, tt.expected)
}
})
}
}
func TestNiriDefaultConfigDir(t *testing.T) {
originalXDG := os.Getenv("XDG_CONFIG_HOME")
defer os.Setenv("XDG_CONFIG_HOME", originalXDG)
os.Setenv("XDG_CONFIG_HOME", "/custom/config")
dir := defaultNiriConfigDir()
if dir != "/custom/config/niri" {
t.Errorf("With XDG_CONFIG_HOME set, got %q, want %q", dir, "/custom/config/niri")
}
os.Unsetenv("XDG_CONFIG_HOME")
dir = defaultNiriConfigDir()
home, _ := os.UserHomeDir()
expected := filepath.Join(home, ".config", "niri")
if dir != expected {
t.Errorf("Without XDG_CONFIG_HOME, got %q, want %q", dir, expected)
}
}
func TestNiriProviderWithRealWorldConfig(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.kdl")
content := `binds {
Mod+Shift+Ctrl+D { debug-toggle-damage; }
Super+D { spawn "niri" "msg" "action" "toggle-overview"; }
Super+Tab repeat=false { toggle-overview; }
Mod+Shift+Slash { show-hotkey-overlay; }
Mod+T hotkey-overlay-title="Open Terminal" { spawn "kitty"; }
Mod+Space hotkey-overlay-title="Application Launcher" {
spawn "dms" "ipc" "call" "spotlight" "toggle";
}
XF86AudioRaiseVolume allow-when-locked=true {
spawn "dms" "ipc" "call" "audio" "increment" "3";
}
XF86AudioLowerVolume allow-when-locked=true {
spawn "dms" "ipc" "call" "audio" "decrement" "3";
}
Mod+Q repeat=false { close-window; }
Mod+F { maximize-column; }
Mod+Shift+F { fullscreen-window; }
Mod+Left { focus-column-left; }
Mod+Down { focus-window-down; }
Mod+Up { focus-window-up; }
Mod+Right { focus-column-right; }
Mod+1 { focus-workspace 1; }
Mod+2 { focus-workspace 2; }
Mod+Shift+1 { move-column-to-workspace 1; }
Mod+Shift+2 { move-column-to-workspace 2; }
Print { screenshot; }
Ctrl+Print { screenshot-screen; }
Alt+Print { screenshot-window; }
Mod+Shift+E { quit; }
}
recent-windows {
binds {
Alt+Tab { next-window scope="output"; }
Alt+Shift+Tab { previous-window scope="output"; }
}
}
`
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write test config: %v", err)
}
provider := NewNiriProvider(tmpDir)
cheatSheet, err := provider.GetCheatSheet()
if err != nil {
t.Fatalf("GetCheatSheet failed: %v", err)
}
totalBinds := 0
for _, binds := range cheatSheet.Binds {
totalBinds += len(binds)
}
if totalBinds < 20 {
t.Errorf("Expected at least 20 keybinds, got %d", totalBinds)
}
if len(cheatSheet.Binds["Alt-Tab"]) < 2 {
t.Errorf("Expected at least 2 Alt-Tab binds, got %d", len(cheatSheet.Binds["Alt-Tab"]))
}
}

View File

@@ -99,7 +99,6 @@ func (s *SwayProvider) convertKeybind(kb *SwayKeyBinding, subcategory string) ke
return keybinds.Keybind{
Key: key,
Description: desc,
Action: kb.Command,
Subcategory: subcategory,
}
}

View File

@@ -3,7 +3,6 @@ package keybinds
type Keybind struct {
Key string `json:"key"`
Description string `json:"desc"`
Action string `json:"action,omitempty"`
Subcategory string `json:"subcat,omitempty"`
}

View File

@@ -64,7 +64,7 @@ func (l *FileLogger) writeToFile(message string) {
redacted := l.redactPassword(message)
timestamp := time.Now().Format("15:04:05.000")
fmt.Fprintf(l.writer, "[%s] %s\n", timestamp, redacted)
l.writer.WriteString(fmt.Sprintf("[%s] %s\n", timestamp, redacted))
l.writer.Flush()
}
@@ -93,7 +93,7 @@ func (l *FileLogger) Close() error {
defer l.mu.Unlock()
footer := fmt.Sprintf("\n=== DankInstall Log End ===\nCompleted: %s\n", time.Now().Format(time.RFC3339))
l.writer.WriteString(footer) //nolint:errcheck
l.writer.WriteString(footer)
l.writer.Flush()
if err := l.file.Sync(); err != nil {

View File

@@ -13,10 +13,10 @@ import (
type Logger struct{ *cblog.Logger }
// Printf routes goose/info-style logs through Infof.
func (l *Logger) Printf(format string, v ...any) { l.Infof(format, v...) }
func (l *Logger) Printf(format string, v ...interface{}) { l.Infof(format, v...) }
// Fatalf keeps gooses contract of exiting the program.
func (l *Logger) Fatalf(format string, v ...any) { l.Logger.Fatalf(format, v...) }
func (l *Logger) Fatalf(format string, v ...interface{}) { l.Logger.Fatalf(format, v...) }
var (
logger *Logger
@@ -104,13 +104,13 @@ func GetLogger() *Logger {
// * 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 interface{}, keyvals ...interface{}) { GetLogger().Logger.Debug(msg, keyvals...) }
func Debugf(format string, v ...interface{}) { GetLogger().Logger.Debugf(format, v...) }
func Info(msg interface{}, keyvals ...interface{}) { GetLogger().Logger.Info(msg, keyvals...) }
func Infof(format string, v ...interface{}) { GetLogger().Logger.Infof(format, v...) }
func Warn(msg interface{}, keyvals ...interface{}) { GetLogger().Logger.Warn(msg, keyvals...) }
func Warnf(format string, v ...interface{}) { GetLogger().Logger.Warnf(format, v...) }
func Error(msg interface{}, keyvals ...interface{}) { GetLogger().Logger.Error(msg, keyvals...) }
func Errorf(format string, v ...interface{}) { GetLogger().Logger.Errorf(format, v...) }
func Fatal(msg interface{}, keyvals ...interface{}) { GetLogger().Logger.Fatal(msg, keyvals...) }
func Fatalf(format string, v ...interface{}) { GetLogger().Logger.Fatalf(format, v...) }

View File

@@ -93,7 +93,7 @@ type MockDBusConn_Object_Call struct {
// Object is a helper method to define mock.On call
// - dest string
// - path dbus.ObjectPath
func (_e *MockDBusConn_Expecter) Object(dest any, path any) *MockDBusConn_Object_Call {
func (_e *MockDBusConn_Expecter) Object(dest interface{}, path interface{}) *MockDBusConn_Object_Call {
return &MockDBusConn_Object_Call{Call: _e.mock.On("Object", dest, path)}
}
@@ -119,8 +119,7 @@ func (_c *MockDBusConn_Object_Call) RunAndReturn(run func(string, dbus.ObjectPat
func NewMockDBusConn(t interface {
mock.TestingT
Cleanup(func())
},
) *MockDBusConn {
}) *MockDBusConn {
mock := &MockDBusConn{}
mock.Mock.Test(t)

View File

@@ -22,99 +22,6 @@ func (_m *MockCUPSClientInterface) EXPECT() *MockCUPSClientInterface_Expecter {
return &MockCUPSClientInterface_Expecter{mock: &_m.Mock}
}
// AcceptJobs provides a mock function with given fields: printer
func (_m *MockCUPSClientInterface) AcceptJobs(printer string) error {
ret := _m.Called(printer)
if len(ret) == 0 {
panic("no return value specified for AcceptJobs")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(printer)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_AcceptJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AcceptJobs'
type MockCUPSClientInterface_AcceptJobs_Call struct {
*mock.Call
}
// AcceptJobs is a helper method to define mock.On call
// - printer string
func (_e *MockCUPSClientInterface_Expecter) AcceptJobs(printer interface{}) *MockCUPSClientInterface_AcceptJobs_Call {
return &MockCUPSClientInterface_AcceptJobs_Call{Call: _e.mock.On("AcceptJobs", printer)}
}
func (_c *MockCUPSClientInterface_AcceptJobs_Call) Run(run func(printer string)) *MockCUPSClientInterface_AcceptJobs_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_AcceptJobs_Call) Return(_a0 error) *MockCUPSClientInterface_AcceptJobs_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_AcceptJobs_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_AcceptJobs_Call {
_c.Call.Return(run)
return _c
}
// AddPrinterToClass provides a mock function with given fields: class, printer
func (_m *MockCUPSClientInterface) AddPrinterToClass(class string, printer string) error {
ret := _m.Called(class, printer)
if len(ret) == 0 {
panic("no return value specified for AddPrinterToClass")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(class, printer)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_AddPrinterToClass_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddPrinterToClass'
type MockCUPSClientInterface_AddPrinterToClass_Call struct {
*mock.Call
}
// AddPrinterToClass is a helper method to define mock.On call
// - class string
// - printer string
func (_e *MockCUPSClientInterface_Expecter) AddPrinterToClass(class interface{}, printer interface{}) *MockCUPSClientInterface_AddPrinterToClass_Call {
return &MockCUPSClientInterface_AddPrinterToClass_Call{Call: _e.mock.On("AddPrinterToClass", class, printer)}
}
func (_c *MockCUPSClientInterface_AddPrinterToClass_Call) Run(run func(class string, printer string)) *MockCUPSClientInterface_AddPrinterToClass_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_AddPrinterToClass_Call) Return(_a0 error) *MockCUPSClientInterface_AddPrinterToClass_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_AddPrinterToClass_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_AddPrinterToClass_Call {
_c.Call.Return(run)
return _c
}
// CancelAllJob provides a mock function with given fields: printer, purge
func (_m *MockCUPSClientInterface) CancelAllJob(printer string, purge bool) error {
ret := _m.Called(printer, purge)
@@ -209,312 +116,6 @@ func (_c *MockCUPSClientInterface_CancelJob_Call) RunAndReturn(run func(int, boo
return _c
}
// CreatePrinter provides a mock function with given fields: name, deviceURI, ppd, shared, errorPolicy, information, location
func (_m *MockCUPSClientInterface) CreatePrinter(name string, deviceURI string, ppd string, shared bool, errorPolicy string, information string, location string) error {
ret := _m.Called(name, deviceURI, ppd, shared, errorPolicy, information, location)
if len(ret) == 0 {
panic("no return value specified for CreatePrinter")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string, string, bool, string, string, string) error); ok {
r0 = rf(name, deviceURI, ppd, shared, errorPolicy, information, location)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_CreatePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePrinter'
type MockCUPSClientInterface_CreatePrinter_Call struct {
*mock.Call
}
// CreatePrinter is a helper method to define mock.On call
// - name string
// - deviceURI string
// - ppd string
// - shared bool
// - errorPolicy string
// - information string
// - location string
func (_e *MockCUPSClientInterface_Expecter) CreatePrinter(name interface{}, deviceURI interface{}, ppd interface{}, shared interface{}, errorPolicy interface{}, information interface{}, location interface{}) *MockCUPSClientInterface_CreatePrinter_Call {
return &MockCUPSClientInterface_CreatePrinter_Call{Call: _e.mock.On("CreatePrinter", name, deviceURI, ppd, shared, errorPolicy, information, location)}
}
func (_c *MockCUPSClientInterface_CreatePrinter_Call) Run(run func(name string, deviceURI string, ppd string, shared bool, errorPolicy string, information string, location string)) *MockCUPSClientInterface_CreatePrinter_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string), args[2].(string), args[3].(bool), args[4].(string), args[5].(string), args[6].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_CreatePrinter_Call) Return(_a0 error) *MockCUPSClientInterface_CreatePrinter_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_CreatePrinter_Call) RunAndReturn(run func(string, string, string, bool, string, string, string) error) *MockCUPSClientInterface_CreatePrinter_Call {
_c.Call.Return(run)
return _c
}
// DeleteClass provides a mock function with given fields: class
func (_m *MockCUPSClientInterface) DeleteClass(class string) error {
ret := _m.Called(class)
if len(ret) == 0 {
panic("no return value specified for DeleteClass")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(class)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_DeleteClass_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteClass'
type MockCUPSClientInterface_DeleteClass_Call struct {
*mock.Call
}
// DeleteClass is a helper method to define mock.On call
// - class string
func (_e *MockCUPSClientInterface_Expecter) DeleteClass(class interface{}) *MockCUPSClientInterface_DeleteClass_Call {
return &MockCUPSClientInterface_DeleteClass_Call{Call: _e.mock.On("DeleteClass", class)}
}
func (_c *MockCUPSClientInterface_DeleteClass_Call) Run(run func(class string)) *MockCUPSClientInterface_DeleteClass_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_DeleteClass_Call) Return(_a0 error) *MockCUPSClientInterface_DeleteClass_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_DeleteClass_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_DeleteClass_Call {
_c.Call.Return(run)
return _c
}
// DeletePrinter provides a mock function with given fields: printer
func (_m *MockCUPSClientInterface) DeletePrinter(printer string) error {
ret := _m.Called(printer)
if len(ret) == 0 {
panic("no return value specified for DeletePrinter")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(printer)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_DeletePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeletePrinter'
type MockCUPSClientInterface_DeletePrinter_Call struct {
*mock.Call
}
// DeletePrinter is a helper method to define mock.On call
// - printer string
func (_e *MockCUPSClientInterface_Expecter) DeletePrinter(printer interface{}) *MockCUPSClientInterface_DeletePrinter_Call {
return &MockCUPSClientInterface_DeletePrinter_Call{Call: _e.mock.On("DeletePrinter", printer)}
}
func (_c *MockCUPSClientInterface_DeletePrinter_Call) Run(run func(printer string)) *MockCUPSClientInterface_DeletePrinter_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_DeletePrinter_Call) Return(_a0 error) *MockCUPSClientInterface_DeletePrinter_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_DeletePrinter_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_DeletePrinter_Call {
_c.Call.Return(run)
return _c
}
// DeletePrinterFromClass provides a mock function with given fields: class, printer
func (_m *MockCUPSClientInterface) DeletePrinterFromClass(class string, printer string) error {
ret := _m.Called(class, printer)
if len(ret) == 0 {
panic("no return value specified for DeletePrinterFromClass")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(class, printer)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_DeletePrinterFromClass_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeletePrinterFromClass'
type MockCUPSClientInterface_DeletePrinterFromClass_Call struct {
*mock.Call
}
// DeletePrinterFromClass is a helper method to define mock.On call
// - class string
// - printer string
func (_e *MockCUPSClientInterface_Expecter) DeletePrinterFromClass(class interface{}, printer interface{}) *MockCUPSClientInterface_DeletePrinterFromClass_Call {
return &MockCUPSClientInterface_DeletePrinterFromClass_Call{Call: _e.mock.On("DeletePrinterFromClass", class, printer)}
}
func (_c *MockCUPSClientInterface_DeletePrinterFromClass_Call) Run(run func(class string, printer string)) *MockCUPSClientInterface_DeletePrinterFromClass_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_DeletePrinterFromClass_Call) Return(_a0 error) *MockCUPSClientInterface_DeletePrinterFromClass_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_DeletePrinterFromClass_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_DeletePrinterFromClass_Call {
_c.Call.Return(run)
return _c
}
// GetClasses provides a mock function with given fields: attributes
func (_m *MockCUPSClientInterface) GetClasses(attributes []string) (map[string]ipp.Attributes, error) {
ret := _m.Called(attributes)
if len(ret) == 0 {
panic("no return value specified for GetClasses")
}
var r0 map[string]ipp.Attributes
var r1 error
if rf, ok := ret.Get(0).(func([]string) (map[string]ipp.Attributes, error)); ok {
return rf(attributes)
}
if rf, ok := ret.Get(0).(func([]string) map[string]ipp.Attributes); ok {
r0 = rf(attributes)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]ipp.Attributes)
}
}
if rf, ok := ret.Get(1).(func([]string) error); ok {
r1 = rf(attributes)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockCUPSClientInterface_GetClasses_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetClasses'
type MockCUPSClientInterface_GetClasses_Call struct {
*mock.Call
}
// GetClasses is a helper method to define mock.On call
// - attributes []string
func (_e *MockCUPSClientInterface_Expecter) GetClasses(attributes interface{}) *MockCUPSClientInterface_GetClasses_Call {
return &MockCUPSClientInterface_GetClasses_Call{Call: _e.mock.On("GetClasses", attributes)}
}
func (_c *MockCUPSClientInterface_GetClasses_Call) Run(run func(attributes []string)) *MockCUPSClientInterface_GetClasses_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].([]string))
})
return _c
}
func (_c *MockCUPSClientInterface_GetClasses_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetClasses_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockCUPSClientInterface_GetClasses_Call) RunAndReturn(run func([]string) (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetClasses_Call {
_c.Call.Return(run)
return _c
}
// GetDevices provides a mock function with no fields
func (_m *MockCUPSClientInterface) GetDevices() (map[string]ipp.Attributes, error) {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetDevices")
}
var r0 map[string]ipp.Attributes
var r1 error
if rf, ok := ret.Get(0).(func() (map[string]ipp.Attributes, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() map[string]ipp.Attributes); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]ipp.Attributes)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockCUPSClientInterface_GetDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDevices'
type MockCUPSClientInterface_GetDevices_Call struct {
*mock.Call
}
// GetDevices is a helper method to define mock.On call
func (_e *MockCUPSClientInterface_Expecter) GetDevices() *MockCUPSClientInterface_GetDevices_Call {
return &MockCUPSClientInterface_GetDevices_Call{Call: _e.mock.On("GetDevices")}
}
func (_c *MockCUPSClientInterface_GetDevices_Call) Run(run func()) *MockCUPSClientInterface_GetDevices_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockCUPSClientInterface_GetDevices_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetDevices_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockCUPSClientInterface_GetDevices_Call) RunAndReturn(run func() (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetDevices_Call {
_c.Call.Return(run)
return _c
}
// GetJobs provides a mock function with given fields: printer, class, whichJobs, myJobs, firstJobId, limit, attributes
func (_m *MockCUPSClientInterface) GetJobs(printer string, class string, whichJobs string, myJobs bool, firstJobId int, limit int, attributes []string) (map[int]ipp.Attributes, error) {
ret := _m.Called(printer, class, whichJobs, myJobs, firstJobId, limit, attributes)
@@ -579,63 +180,6 @@ func (_c *MockCUPSClientInterface_GetJobs_Call) RunAndReturn(run func(string, st
return _c
}
// GetPPDs provides a mock function with no fields
func (_m *MockCUPSClientInterface) GetPPDs() (map[string]ipp.Attributes, error) {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetPPDs")
}
var r0 map[string]ipp.Attributes
var r1 error
if rf, ok := ret.Get(0).(func() (map[string]ipp.Attributes, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() map[string]ipp.Attributes); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]ipp.Attributes)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockCUPSClientInterface_GetPPDs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPPDs'
type MockCUPSClientInterface_GetPPDs_Call struct {
*mock.Call
}
// GetPPDs is a helper method to define mock.On call
func (_e *MockCUPSClientInterface_Expecter) GetPPDs() *MockCUPSClientInterface_GetPPDs_Call {
return &MockCUPSClientInterface_GetPPDs_Call{Call: _e.mock.On("GetPPDs")}
}
func (_c *MockCUPSClientInterface_GetPPDs_Call) Run(run func()) *MockCUPSClientInterface_GetPPDs_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockCUPSClientInterface_GetPPDs_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetPPDs_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockCUPSClientInterface_GetPPDs_Call) RunAndReturn(run func() (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetPPDs_Call {
_c.Call.Return(run)
return _c
}
// GetPrinters provides a mock function with given fields: attributes
func (_m *MockCUPSClientInterface) GetPrinters(attributes []string) (map[string]ipp.Attributes, error) {
ret := _m.Called(attributes)
@@ -694,100 +238,6 @@ func (_c *MockCUPSClientInterface_GetPrinters_Call) RunAndReturn(run func([]stri
return _c
}
// HoldJobUntil provides a mock function with given fields: jobID, holdUntil
func (_m *MockCUPSClientInterface) HoldJobUntil(jobID int, holdUntil string) error {
ret := _m.Called(jobID, holdUntil)
if len(ret) == 0 {
panic("no return value specified for HoldJobUntil")
}
var r0 error
if rf, ok := ret.Get(0).(func(int, string) error); ok {
r0 = rf(jobID, holdUntil)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_HoldJobUntil_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HoldJobUntil'
type MockCUPSClientInterface_HoldJobUntil_Call struct {
*mock.Call
}
// HoldJobUntil is a helper method to define mock.On call
// - jobID int
// - holdUntil string
func (_e *MockCUPSClientInterface_Expecter) HoldJobUntil(jobID interface{}, holdUntil interface{}) *MockCUPSClientInterface_HoldJobUntil_Call {
return &MockCUPSClientInterface_HoldJobUntil_Call{Call: _e.mock.On("HoldJobUntil", jobID, holdUntil)}
}
func (_c *MockCUPSClientInterface_HoldJobUntil_Call) Run(run func(jobID int, holdUntil string)) *MockCUPSClientInterface_HoldJobUntil_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_HoldJobUntil_Call) Return(_a0 error) *MockCUPSClientInterface_HoldJobUntil_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_HoldJobUntil_Call) RunAndReturn(run func(int, string) error) *MockCUPSClientInterface_HoldJobUntil_Call {
_c.Call.Return(run)
return _c
}
// MoveJob provides a mock function with given fields: jobID, destPrinter
func (_m *MockCUPSClientInterface) MoveJob(jobID int, destPrinter string) error {
ret := _m.Called(jobID, destPrinter)
if len(ret) == 0 {
panic("no return value specified for MoveJob")
}
var r0 error
if rf, ok := ret.Get(0).(func(int, string) error); ok {
r0 = rf(jobID, destPrinter)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_MoveJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MoveJob'
type MockCUPSClientInterface_MoveJob_Call struct {
*mock.Call
}
// MoveJob is a helper method to define mock.On call
// - jobID int
// - destPrinter string
func (_e *MockCUPSClientInterface_Expecter) MoveJob(jobID interface{}, destPrinter interface{}) *MockCUPSClientInterface_MoveJob_Call {
return &MockCUPSClientInterface_MoveJob_Call{Call: _e.mock.On("MoveJob", jobID, destPrinter)}
}
func (_c *MockCUPSClientInterface_MoveJob_Call) Run(run func(jobID int, destPrinter string)) *MockCUPSClientInterface_MoveJob_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_MoveJob_Call) Return(_a0 error) *MockCUPSClientInterface_MoveJob_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_MoveJob_Call) RunAndReturn(run func(int, string) error) *MockCUPSClientInterface_MoveJob_Call {
_c.Call.Return(run)
return _c
}
// PausePrinter provides a mock function with given fields: printer
func (_m *MockCUPSClientInterface) PausePrinter(printer string) error {
ret := _m.Called(printer)
@@ -834,156 +284,6 @@ func (_c *MockCUPSClientInterface_PausePrinter_Call) RunAndReturn(run func(strin
return _c
}
// PrintTestPage provides a mock function with given fields: printer, testPageData, size
func (_m *MockCUPSClientInterface) PrintTestPage(printer string, testPageData io.Reader, size int) (int, error) {
ret := _m.Called(printer, testPageData, size)
if len(ret) == 0 {
panic("no return value specified for PrintTestPage")
}
var r0 int
var r1 error
if rf, ok := ret.Get(0).(func(string, io.Reader, int) (int, error)); ok {
return rf(printer, testPageData, size)
}
if rf, ok := ret.Get(0).(func(string, io.Reader, int) int); ok {
r0 = rf(printer, testPageData, size)
} else {
r0 = ret.Get(0).(int)
}
if rf, ok := ret.Get(1).(func(string, io.Reader, int) error); ok {
r1 = rf(printer, testPageData, size)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockCUPSClientInterface_PrintTestPage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrintTestPage'
type MockCUPSClientInterface_PrintTestPage_Call struct {
*mock.Call
}
// PrintTestPage is a helper method to define mock.On call
// - printer string
// - testPageData io.Reader
// - size int
func (_e *MockCUPSClientInterface_Expecter) PrintTestPage(printer interface{}, testPageData interface{}, size interface{}) *MockCUPSClientInterface_PrintTestPage_Call {
return &MockCUPSClientInterface_PrintTestPage_Call{Call: _e.mock.On("PrintTestPage", printer, testPageData, size)}
}
func (_c *MockCUPSClientInterface_PrintTestPage_Call) Run(run func(printer string, testPageData io.Reader, size int)) *MockCUPSClientInterface_PrintTestPage_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(io.Reader), args[2].(int))
})
return _c
}
func (_c *MockCUPSClientInterface_PrintTestPage_Call) Return(_a0 int, _a1 error) *MockCUPSClientInterface_PrintTestPage_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockCUPSClientInterface_PrintTestPage_Call) RunAndReturn(run func(string, io.Reader, int) (int, error)) *MockCUPSClientInterface_PrintTestPage_Call {
_c.Call.Return(run)
return _c
}
// RejectJobs provides a mock function with given fields: printer
func (_m *MockCUPSClientInterface) RejectJobs(printer string) error {
ret := _m.Called(printer)
if len(ret) == 0 {
panic("no return value specified for RejectJobs")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(printer)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_RejectJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RejectJobs'
type MockCUPSClientInterface_RejectJobs_Call struct {
*mock.Call
}
// RejectJobs is a helper method to define mock.On call
// - printer string
func (_e *MockCUPSClientInterface_Expecter) RejectJobs(printer interface{}) *MockCUPSClientInterface_RejectJobs_Call {
return &MockCUPSClientInterface_RejectJobs_Call{Call: _e.mock.On("RejectJobs", printer)}
}
func (_c *MockCUPSClientInterface_RejectJobs_Call) Run(run func(printer string)) *MockCUPSClientInterface_RejectJobs_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_RejectJobs_Call) Return(_a0 error) *MockCUPSClientInterface_RejectJobs_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_RejectJobs_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_RejectJobs_Call {
_c.Call.Return(run)
return _c
}
// RestartJob provides a mock function with given fields: jobID
func (_m *MockCUPSClientInterface) RestartJob(jobID int) error {
ret := _m.Called(jobID)
if len(ret) == 0 {
panic("no return value specified for RestartJob")
}
var r0 error
if rf, ok := ret.Get(0).(func(int) error); ok {
r0 = rf(jobID)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_RestartJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RestartJob'
type MockCUPSClientInterface_RestartJob_Call struct {
*mock.Call
}
// RestartJob is a helper method to define mock.On call
// - jobID int
func (_e *MockCUPSClientInterface_Expecter) RestartJob(jobID interface{}) *MockCUPSClientInterface_RestartJob_Call {
return &MockCUPSClientInterface_RestartJob_Call{Call: _e.mock.On("RestartJob", jobID)}
}
func (_c *MockCUPSClientInterface_RestartJob_Call) Run(run func(jobID int)) *MockCUPSClientInterface_RestartJob_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int))
})
return _c
}
func (_c *MockCUPSClientInterface_RestartJob_Call) Return(_a0 error) *MockCUPSClientInterface_RestartJob_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_RestartJob_Call) RunAndReturn(run func(int) error) *MockCUPSClientInterface_RestartJob_Call {
_c.Call.Return(run)
return _c
}
// ResumePrinter provides a mock function with given fields: printer
func (_m *MockCUPSClientInterface) ResumePrinter(printer string) error {
ret := _m.Called(printer)
@@ -1090,147 +390,6 @@ func (_c *MockCUPSClientInterface_SendRequest_Call) RunAndReturn(run func(string
return _c
}
// SetPrinterInformation provides a mock function with given fields: printer, information
func (_m *MockCUPSClientInterface) SetPrinterInformation(printer string, information string) error {
ret := _m.Called(printer, information)
if len(ret) == 0 {
panic("no return value specified for SetPrinterInformation")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(printer, information)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_SetPrinterInformation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPrinterInformation'
type MockCUPSClientInterface_SetPrinterInformation_Call struct {
*mock.Call
}
// SetPrinterInformation is a helper method to define mock.On call
// - printer string
// - information string
func (_e *MockCUPSClientInterface_Expecter) SetPrinterInformation(printer interface{}, information interface{}) *MockCUPSClientInterface_SetPrinterInformation_Call {
return &MockCUPSClientInterface_SetPrinterInformation_Call{Call: _e.mock.On("SetPrinterInformation", printer, information)}
}
func (_c *MockCUPSClientInterface_SetPrinterInformation_Call) Run(run func(printer string, information string)) *MockCUPSClientInterface_SetPrinterInformation_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterInformation_Call) Return(_a0 error) *MockCUPSClientInterface_SetPrinterInformation_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterInformation_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_SetPrinterInformation_Call {
_c.Call.Return(run)
return _c
}
// SetPrinterIsShared provides a mock function with given fields: printer, shared
func (_m *MockCUPSClientInterface) SetPrinterIsShared(printer string, shared bool) error {
ret := _m.Called(printer, shared)
if len(ret) == 0 {
panic("no return value specified for SetPrinterIsShared")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, bool) error); ok {
r0 = rf(printer, shared)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_SetPrinterIsShared_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPrinterIsShared'
type MockCUPSClientInterface_SetPrinterIsShared_Call struct {
*mock.Call
}
// SetPrinterIsShared is a helper method to define mock.On call
// - printer string
// - shared bool
func (_e *MockCUPSClientInterface_Expecter) SetPrinterIsShared(printer interface{}, shared interface{}) *MockCUPSClientInterface_SetPrinterIsShared_Call {
return &MockCUPSClientInterface_SetPrinterIsShared_Call{Call: _e.mock.On("SetPrinterIsShared", printer, shared)}
}
func (_c *MockCUPSClientInterface_SetPrinterIsShared_Call) Run(run func(printer string, shared bool)) *MockCUPSClientInterface_SetPrinterIsShared_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(bool))
})
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterIsShared_Call) Return(_a0 error) *MockCUPSClientInterface_SetPrinterIsShared_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterIsShared_Call) RunAndReturn(run func(string, bool) error) *MockCUPSClientInterface_SetPrinterIsShared_Call {
_c.Call.Return(run)
return _c
}
// SetPrinterLocation provides a mock function with given fields: printer, location
func (_m *MockCUPSClientInterface) SetPrinterLocation(printer string, location string) error {
ret := _m.Called(printer, location)
if len(ret) == 0 {
panic("no return value specified for SetPrinterLocation")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(printer, location)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockCUPSClientInterface_SetPrinterLocation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPrinterLocation'
type MockCUPSClientInterface_SetPrinterLocation_Call struct {
*mock.Call
}
// SetPrinterLocation is a helper method to define mock.On call
// - printer string
// - location string
func (_e *MockCUPSClientInterface_Expecter) SetPrinterLocation(printer interface{}, location interface{}) *MockCUPSClientInterface_SetPrinterLocation_Call {
return &MockCUPSClientInterface_SetPrinterLocation_Call{Call: _e.mock.On("SetPrinterLocation", printer, location)}
}
func (_c *MockCUPSClientInterface_SetPrinterLocation_Call) Run(run func(printer string, location string)) *MockCUPSClientInterface_SetPrinterLocation_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterLocation_Call) Return(_a0 error) *MockCUPSClientInterface_SetPrinterLocation_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockCUPSClientInterface_SetPrinterLocation_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_SetPrinterLocation_Call {
_c.Call.Return(run)
return _c
}
// NewMockCUPSClientInterface creates a new instance of MockCUPSClientInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockCUPSClientInterface(t interface {

View File

@@ -1,708 +0,0 @@
// Code generated by mockery v2.53.5. DO NOT EDIT.
package mocks_cups_pkhelper
import (
cups "github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups"
mock "github.com/stretchr/testify/mock"
)
// MockPkHelper is an autogenerated mock type for the PkHelper type
type MockPkHelper struct {
mock.Mock
}
type MockPkHelper_Expecter struct {
mock *mock.Mock
}
func (_m *MockPkHelper) EXPECT() *MockPkHelper_Expecter {
return &MockPkHelper_Expecter{mock: &_m.Mock}
}
// ClassAddPrinter provides a mock function with given fields: className, printerName
func (_m *MockPkHelper) ClassAddPrinter(className string, printerName string) error {
ret := _m.Called(className, printerName)
if len(ret) == 0 {
panic("no return value specified for ClassAddPrinter")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(className, printerName)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_ClassAddPrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClassAddPrinter'
type MockPkHelper_ClassAddPrinter_Call struct {
*mock.Call
}
// ClassAddPrinter is a helper method to define mock.On call
// - className string
// - printerName string
func (_e *MockPkHelper_Expecter) ClassAddPrinter(className interface{}, printerName interface{}) *MockPkHelper_ClassAddPrinter_Call {
return &MockPkHelper_ClassAddPrinter_Call{Call: _e.mock.On("ClassAddPrinter", className, printerName)}
}
func (_c *MockPkHelper_ClassAddPrinter_Call) Run(run func(className string, printerName string)) *MockPkHelper_ClassAddPrinter_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockPkHelper_ClassAddPrinter_Call) Return(_a0 error) *MockPkHelper_ClassAddPrinter_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_ClassAddPrinter_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_ClassAddPrinter_Call {
_c.Call.Return(run)
return _c
}
// ClassDelete provides a mock function with given fields: className
func (_m *MockPkHelper) ClassDelete(className string) error {
ret := _m.Called(className)
if len(ret) == 0 {
panic("no return value specified for ClassDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(className)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_ClassDelete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClassDelete'
type MockPkHelper_ClassDelete_Call struct {
*mock.Call
}
// ClassDelete is a helper method to define mock.On call
// - className string
func (_e *MockPkHelper_Expecter) ClassDelete(className interface{}) *MockPkHelper_ClassDelete_Call {
return &MockPkHelper_ClassDelete_Call{Call: _e.mock.On("ClassDelete", className)}
}
func (_c *MockPkHelper_ClassDelete_Call) Run(run func(className string)) *MockPkHelper_ClassDelete_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockPkHelper_ClassDelete_Call) Return(_a0 error) *MockPkHelper_ClassDelete_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_ClassDelete_Call) RunAndReturn(run func(string) error) *MockPkHelper_ClassDelete_Call {
_c.Call.Return(run)
return _c
}
// ClassDeletePrinter provides a mock function with given fields: className, printerName
func (_m *MockPkHelper) ClassDeletePrinter(className string, printerName string) error {
ret := _m.Called(className, printerName)
if len(ret) == 0 {
panic("no return value specified for ClassDeletePrinter")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(className, printerName)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_ClassDeletePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClassDeletePrinter'
type MockPkHelper_ClassDeletePrinter_Call struct {
*mock.Call
}
// ClassDeletePrinter is a helper method to define mock.On call
// - className string
// - printerName string
func (_e *MockPkHelper_Expecter) ClassDeletePrinter(className interface{}, printerName interface{}) *MockPkHelper_ClassDeletePrinter_Call {
return &MockPkHelper_ClassDeletePrinter_Call{Call: _e.mock.On("ClassDeletePrinter", className, printerName)}
}
func (_c *MockPkHelper_ClassDeletePrinter_Call) Run(run func(className string, printerName string)) *MockPkHelper_ClassDeletePrinter_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockPkHelper_ClassDeletePrinter_Call) Return(_a0 error) *MockPkHelper_ClassDeletePrinter_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_ClassDeletePrinter_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_ClassDeletePrinter_Call {
_c.Call.Return(run)
return _c
}
// DevicesGet provides a mock function with given fields: timeout, limit, includeSchemes, excludeSchemes
func (_m *MockPkHelper) DevicesGet(timeout int, limit int, includeSchemes []string, excludeSchemes []string) ([]cups.Device, error) {
ret := _m.Called(timeout, limit, includeSchemes, excludeSchemes)
if len(ret) == 0 {
panic("no return value specified for DevicesGet")
}
var r0 []cups.Device
var r1 error
if rf, ok := ret.Get(0).(func(int, int, []string, []string) ([]cups.Device, error)); ok {
return rf(timeout, limit, includeSchemes, excludeSchemes)
}
if rf, ok := ret.Get(0).(func(int, int, []string, []string) []cups.Device); ok {
r0 = rf(timeout, limit, includeSchemes, excludeSchemes)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]cups.Device)
}
}
if rf, ok := ret.Get(1).(func(int, int, []string, []string) error); ok {
r1 = rf(timeout, limit, includeSchemes, excludeSchemes)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockPkHelper_DevicesGet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DevicesGet'
type MockPkHelper_DevicesGet_Call struct {
*mock.Call
}
// DevicesGet is a helper method to define mock.On call
// - timeout int
// - limit int
// - includeSchemes []string
// - excludeSchemes []string
func (_e *MockPkHelper_Expecter) DevicesGet(timeout interface{}, limit interface{}, includeSchemes interface{}, excludeSchemes interface{}) *MockPkHelper_DevicesGet_Call {
return &MockPkHelper_DevicesGet_Call{Call: _e.mock.On("DevicesGet", timeout, limit, includeSchemes, excludeSchemes)}
}
func (_c *MockPkHelper_DevicesGet_Call) Run(run func(timeout int, limit int, includeSchemes []string, excludeSchemes []string)) *MockPkHelper_DevicesGet_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int), args[1].(int), args[2].([]string), args[3].([]string))
})
return _c
}
func (_c *MockPkHelper_DevicesGet_Call) Return(_a0 []cups.Device, _a1 error) *MockPkHelper_DevicesGet_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockPkHelper_DevicesGet_Call) RunAndReturn(run func(int, int, []string, []string) ([]cups.Device, error)) *MockPkHelper_DevicesGet_Call {
_c.Call.Return(run)
return _c
}
// JobCancelPurge provides a mock function with given fields: jobID, purge
func (_m *MockPkHelper) JobCancelPurge(jobID int, purge bool) error {
ret := _m.Called(jobID, purge)
if len(ret) == 0 {
panic("no return value specified for JobCancelPurge")
}
var r0 error
if rf, ok := ret.Get(0).(func(int, bool) error); ok {
r0 = rf(jobID, purge)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_JobCancelPurge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'JobCancelPurge'
type MockPkHelper_JobCancelPurge_Call struct {
*mock.Call
}
// JobCancelPurge is a helper method to define mock.On call
// - jobID int
// - purge bool
func (_e *MockPkHelper_Expecter) JobCancelPurge(jobID interface{}, purge interface{}) *MockPkHelper_JobCancelPurge_Call {
return &MockPkHelper_JobCancelPurge_Call{Call: _e.mock.On("JobCancelPurge", jobID, purge)}
}
func (_c *MockPkHelper_JobCancelPurge_Call) Run(run func(jobID int, purge bool)) *MockPkHelper_JobCancelPurge_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int), args[1].(bool))
})
return _c
}
func (_c *MockPkHelper_JobCancelPurge_Call) Return(_a0 error) *MockPkHelper_JobCancelPurge_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_JobCancelPurge_Call) RunAndReturn(run func(int, bool) error) *MockPkHelper_JobCancelPurge_Call {
_c.Call.Return(run)
return _c
}
// JobRestart provides a mock function with given fields: jobID
func (_m *MockPkHelper) JobRestart(jobID int) error {
ret := _m.Called(jobID)
if len(ret) == 0 {
panic("no return value specified for JobRestart")
}
var r0 error
if rf, ok := ret.Get(0).(func(int) error); ok {
r0 = rf(jobID)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_JobRestart_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'JobRestart'
type MockPkHelper_JobRestart_Call struct {
*mock.Call
}
// JobRestart is a helper method to define mock.On call
// - jobID int
func (_e *MockPkHelper_Expecter) JobRestart(jobID interface{}) *MockPkHelper_JobRestart_Call {
return &MockPkHelper_JobRestart_Call{Call: _e.mock.On("JobRestart", jobID)}
}
func (_c *MockPkHelper_JobRestart_Call) Run(run func(jobID int)) *MockPkHelper_JobRestart_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int))
})
return _c
}
func (_c *MockPkHelper_JobRestart_Call) Return(_a0 error) *MockPkHelper_JobRestart_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_JobRestart_Call) RunAndReturn(run func(int) error) *MockPkHelper_JobRestart_Call {
_c.Call.Return(run)
return _c
}
// JobSetHoldUntil provides a mock function with given fields: jobID, holdUntil
func (_m *MockPkHelper) JobSetHoldUntil(jobID int, holdUntil string) error {
ret := _m.Called(jobID, holdUntil)
if len(ret) == 0 {
panic("no return value specified for JobSetHoldUntil")
}
var r0 error
if rf, ok := ret.Get(0).(func(int, string) error); ok {
r0 = rf(jobID, holdUntil)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_JobSetHoldUntil_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'JobSetHoldUntil'
type MockPkHelper_JobSetHoldUntil_Call struct {
*mock.Call
}
// JobSetHoldUntil is a helper method to define mock.On call
// - jobID int
// - holdUntil string
func (_e *MockPkHelper_Expecter) JobSetHoldUntil(jobID interface{}, holdUntil interface{}) *MockPkHelper_JobSetHoldUntil_Call {
return &MockPkHelper_JobSetHoldUntil_Call{Call: _e.mock.On("JobSetHoldUntil", jobID, holdUntil)}
}
func (_c *MockPkHelper_JobSetHoldUntil_Call) Run(run func(jobID int, holdUntil string)) *MockPkHelper_JobSetHoldUntil_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(int), args[1].(string))
})
return _c
}
func (_c *MockPkHelper_JobSetHoldUntil_Call) Return(_a0 error) *MockPkHelper_JobSetHoldUntil_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_JobSetHoldUntil_Call) RunAndReturn(run func(int, string) error) *MockPkHelper_JobSetHoldUntil_Call {
_c.Call.Return(run)
return _c
}
// PrinterAdd provides a mock function with given fields: name, uri, ppd, info, location
func (_m *MockPkHelper) PrinterAdd(name string, uri string, ppd string, info string, location string) error {
ret := _m.Called(name, uri, ppd, info, location)
if len(ret) == 0 {
panic("no return value specified for PrinterAdd")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string, string, string, string) error); ok {
r0 = rf(name, uri, ppd, info, location)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterAdd_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterAdd'
type MockPkHelper_PrinterAdd_Call struct {
*mock.Call
}
// PrinterAdd is a helper method to define mock.On call
// - name string
// - uri string
// - ppd string
// - info string
// - location string
func (_e *MockPkHelper_Expecter) PrinterAdd(name interface{}, uri interface{}, ppd interface{}, info interface{}, location interface{}) *MockPkHelper_PrinterAdd_Call {
return &MockPkHelper_PrinterAdd_Call{Call: _e.mock.On("PrinterAdd", name, uri, ppd, info, location)}
}
func (_c *MockPkHelper_PrinterAdd_Call) Run(run func(name string, uri string, ppd string, info string, location string)) *MockPkHelper_PrinterAdd_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string), args[2].(string), args[3].(string), args[4].(string))
})
return _c
}
func (_c *MockPkHelper_PrinterAdd_Call) Return(_a0 error) *MockPkHelper_PrinterAdd_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterAdd_Call) RunAndReturn(run func(string, string, string, string, string) error) *MockPkHelper_PrinterAdd_Call {
_c.Call.Return(run)
return _c
}
// PrinterDelete provides a mock function with given fields: name
func (_m *MockPkHelper) PrinterDelete(name string) error {
ret := _m.Called(name)
if len(ret) == 0 {
panic("no return value specified for PrinterDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(name)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterDelete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterDelete'
type MockPkHelper_PrinterDelete_Call struct {
*mock.Call
}
// PrinterDelete is a helper method to define mock.On call
// - name string
func (_e *MockPkHelper_Expecter) PrinterDelete(name interface{}) *MockPkHelper_PrinterDelete_Call {
return &MockPkHelper_PrinterDelete_Call{Call: _e.mock.On("PrinterDelete", name)}
}
func (_c *MockPkHelper_PrinterDelete_Call) Run(run func(name string)) *MockPkHelper_PrinterDelete_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockPkHelper_PrinterDelete_Call) Return(_a0 error) *MockPkHelper_PrinterDelete_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterDelete_Call) RunAndReturn(run func(string) error) *MockPkHelper_PrinterDelete_Call {
_c.Call.Return(run)
return _c
}
// PrinterSetAcceptJobs provides a mock function with given fields: name, enabled, reason
func (_m *MockPkHelper) PrinterSetAcceptJobs(name string, enabled bool, reason string) error {
ret := _m.Called(name, enabled, reason)
if len(ret) == 0 {
panic("no return value specified for PrinterSetAcceptJobs")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, bool, string) error); ok {
r0 = rf(name, enabled, reason)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterSetAcceptJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetAcceptJobs'
type MockPkHelper_PrinterSetAcceptJobs_Call struct {
*mock.Call
}
// PrinterSetAcceptJobs is a helper method to define mock.On call
// - name string
// - enabled bool
// - reason string
func (_e *MockPkHelper_Expecter) PrinterSetAcceptJobs(name interface{}, enabled interface{}, reason interface{}) *MockPkHelper_PrinterSetAcceptJobs_Call {
return &MockPkHelper_PrinterSetAcceptJobs_Call{Call: _e.mock.On("PrinterSetAcceptJobs", name, enabled, reason)}
}
func (_c *MockPkHelper_PrinterSetAcceptJobs_Call) Run(run func(name string, enabled bool, reason string)) *MockPkHelper_PrinterSetAcceptJobs_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(bool), args[2].(string))
})
return _c
}
func (_c *MockPkHelper_PrinterSetAcceptJobs_Call) Return(_a0 error) *MockPkHelper_PrinterSetAcceptJobs_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterSetAcceptJobs_Call) RunAndReturn(run func(string, bool, string) error) *MockPkHelper_PrinterSetAcceptJobs_Call {
_c.Call.Return(run)
return _c
}
// PrinterSetEnabled provides a mock function with given fields: name, enabled
func (_m *MockPkHelper) PrinterSetEnabled(name string, enabled bool) error {
ret := _m.Called(name, enabled)
if len(ret) == 0 {
panic("no return value specified for PrinterSetEnabled")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, bool) error); ok {
r0 = rf(name, enabled)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterSetEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetEnabled'
type MockPkHelper_PrinterSetEnabled_Call struct {
*mock.Call
}
// PrinterSetEnabled is a helper method to define mock.On call
// - name string
// - enabled bool
func (_e *MockPkHelper_Expecter) PrinterSetEnabled(name interface{}, enabled interface{}) *MockPkHelper_PrinterSetEnabled_Call {
return &MockPkHelper_PrinterSetEnabled_Call{Call: _e.mock.On("PrinterSetEnabled", name, enabled)}
}
func (_c *MockPkHelper_PrinterSetEnabled_Call) Run(run func(name string, enabled bool)) *MockPkHelper_PrinterSetEnabled_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(bool))
})
return _c
}
func (_c *MockPkHelper_PrinterSetEnabled_Call) Return(_a0 error) *MockPkHelper_PrinterSetEnabled_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterSetEnabled_Call) RunAndReturn(run func(string, bool) error) *MockPkHelper_PrinterSetEnabled_Call {
_c.Call.Return(run)
return _c
}
// PrinterSetInfo provides a mock function with given fields: name, info
func (_m *MockPkHelper) PrinterSetInfo(name string, info string) error {
ret := _m.Called(name, info)
if len(ret) == 0 {
panic("no return value specified for PrinterSetInfo")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(name, info)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterSetInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetInfo'
type MockPkHelper_PrinterSetInfo_Call struct {
*mock.Call
}
// PrinterSetInfo is a helper method to define mock.On call
// - name string
// - info string
func (_e *MockPkHelper_Expecter) PrinterSetInfo(name interface{}, info interface{}) *MockPkHelper_PrinterSetInfo_Call {
return &MockPkHelper_PrinterSetInfo_Call{Call: _e.mock.On("PrinterSetInfo", name, info)}
}
func (_c *MockPkHelper_PrinterSetInfo_Call) Run(run func(name string, info string)) *MockPkHelper_PrinterSetInfo_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockPkHelper_PrinterSetInfo_Call) Return(_a0 error) *MockPkHelper_PrinterSetInfo_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterSetInfo_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_PrinterSetInfo_Call {
_c.Call.Return(run)
return _c
}
// PrinterSetLocation provides a mock function with given fields: name, location
func (_m *MockPkHelper) PrinterSetLocation(name string, location string) error {
ret := _m.Called(name, location)
if len(ret) == 0 {
panic("no return value specified for PrinterSetLocation")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string) error); ok {
r0 = rf(name, location)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterSetLocation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetLocation'
type MockPkHelper_PrinterSetLocation_Call struct {
*mock.Call
}
// PrinterSetLocation is a helper method to define mock.On call
// - name string
// - location string
func (_e *MockPkHelper_Expecter) PrinterSetLocation(name interface{}, location interface{}) *MockPkHelper_PrinterSetLocation_Call {
return &MockPkHelper_PrinterSetLocation_Call{Call: _e.mock.On("PrinterSetLocation", name, location)}
}
func (_c *MockPkHelper_PrinterSetLocation_Call) Run(run func(name string, location string)) *MockPkHelper_PrinterSetLocation_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockPkHelper_PrinterSetLocation_Call) Return(_a0 error) *MockPkHelper_PrinterSetLocation_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterSetLocation_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_PrinterSetLocation_Call {
_c.Call.Return(run)
return _c
}
// PrinterSetShared provides a mock function with given fields: name, shared
func (_m *MockPkHelper) PrinterSetShared(name string, shared bool) error {
ret := _m.Called(name, shared)
if len(ret) == 0 {
panic("no return value specified for PrinterSetShared")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, bool) error); ok {
r0 = rf(name, shared)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockPkHelper_PrinterSetShared_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetShared'
type MockPkHelper_PrinterSetShared_Call struct {
*mock.Call
}
// PrinterSetShared is a helper method to define mock.On call
// - name string
// - shared bool
func (_e *MockPkHelper_Expecter) PrinterSetShared(name interface{}, shared interface{}) *MockPkHelper_PrinterSetShared_Call {
return &MockPkHelper_PrinterSetShared_Call{Call: _e.mock.On("PrinterSetShared", name, shared)}
}
func (_c *MockPkHelper_PrinterSetShared_Call) Run(run func(name string, shared bool)) *MockPkHelper_PrinterSetShared_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(bool))
})
return _c
}
func (_c *MockPkHelper_PrinterSetShared_Call) Return(_a0 error) *MockPkHelper_PrinterSetShared_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockPkHelper_PrinterSetShared_Call) RunAndReturn(run func(string, bool) error) *MockPkHelper_PrinterSetShared_Call {
_c.Call.Return(run)
return _c
}
// NewMockPkHelper creates a new instance of MockPkHelper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockPkHelper(t interface {
mock.TestingT
Cleanup(func())
}) *MockPkHelper {
mock := &MockPkHelper{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -52,7 +52,7 @@ type MockGitClient_HasUpdates_Call struct {
// HasUpdates is a helper method to define mock.On call
// - path string
func (_e *MockGitClient_Expecter) HasUpdates(path any) *MockGitClient_HasUpdates_Call {
func (_e *MockGitClient_Expecter) HasUpdates(path interface{}) *MockGitClient_HasUpdates_Call {
return &MockGitClient_HasUpdates_Call{Call: _e.mock.On("HasUpdates", path)}
}
@@ -99,7 +99,7 @@ type MockGitClient_PlainClone_Call struct {
// PlainClone is a helper method to define mock.On call
// - path string
// - url string
func (_e *MockGitClient_Expecter) PlainClone(path any, url any) *MockGitClient_PlainClone_Call {
func (_e *MockGitClient_Expecter) PlainClone(path interface{}, url interface{}) *MockGitClient_PlainClone_Call {
return &MockGitClient_PlainClone_Call{Call: _e.mock.On("PlainClone", path, url)}
}
@@ -145,7 +145,7 @@ type MockGitClient_Pull_Call struct {
// Pull is a helper method to define mock.On call
// - path string
func (_e *MockGitClient_Expecter) Pull(path any) *MockGitClient_Pull_Call {
func (_e *MockGitClient_Expecter) Pull(path interface{}) *MockGitClient_Pull_Call {
return &MockGitClient_Pull_Call{Call: _e.mock.On("Pull", path)}
}

View File

@@ -328,52 +328,6 @@ func (_c *MockBackend_ConnectWiFi_Call) RunAndReturn(run func(network.Connection
return _c
}
// DeleteVPN provides a mock function with given fields: uuidOrName
func (_m *MockBackend) DeleteVPN(uuidOrName string) error {
ret := _m.Called(uuidOrName)
if len(ret) == 0 {
panic("no return value specified for DeleteVPN")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(uuidOrName)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockBackend_DeleteVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteVPN'
type MockBackend_DeleteVPN_Call struct {
*mock.Call
}
// DeleteVPN is a helper method to define mock.On call
// - uuidOrName string
func (_e *MockBackend_Expecter) DeleteVPN(uuidOrName interface{}) *MockBackend_DeleteVPN_Call {
return &MockBackend_DeleteVPN_Call{Call: _e.mock.On("DeleteVPN", uuidOrName)}
}
func (_c *MockBackend_DeleteVPN_Call) Run(run func(uuidOrName string)) *MockBackend_DeleteVPN_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockBackend_DeleteVPN_Call) Return(_a0 error) *MockBackend_DeleteVPN_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_DeleteVPN_Call) RunAndReturn(run func(string) error) *MockBackend_DeleteVPN_Call {
_c.Call.Return(run)
return _c
}
// DisconnectAllVPN provides a mock function with no fields
func (_m *MockBackend) DisconnectAllVPN() error {
ret := _m.Called()
@@ -464,52 +418,6 @@ func (_c *MockBackend_DisconnectEthernet_Call) RunAndReturn(run func() error) *M
return _c
}
// DisconnectEthernetDevice provides a mock function with given fields: device
func (_m *MockBackend) DisconnectEthernetDevice(device string) error {
ret := _m.Called(device)
if len(ret) == 0 {
panic("no return value specified for DisconnectEthernetDevice")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(device)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockBackend_DisconnectEthernetDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectEthernetDevice'
type MockBackend_DisconnectEthernetDevice_Call struct {
*mock.Call
}
// DisconnectEthernetDevice is a helper method to define mock.On call
// - device string
func (_e *MockBackend_Expecter) DisconnectEthernetDevice(device interface{}) *MockBackend_DisconnectEthernetDevice_Call {
return &MockBackend_DisconnectEthernetDevice_Call{Call: _e.mock.On("DisconnectEthernetDevice", device)}
}
func (_c *MockBackend_DisconnectEthernetDevice_Call) Run(run func(device string)) *MockBackend_DisconnectEthernetDevice_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockBackend_DisconnectEthernetDevice_Call) Return(_a0 error) *MockBackend_DisconnectEthernetDevice_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_DisconnectEthernetDevice_Call) RunAndReturn(run func(string) error) *MockBackend_DisconnectEthernetDevice_Call {
_c.Call.Return(run)
return _c
}
// DisconnectVPN provides a mock function with given fields: uuidOrName
func (_m *MockBackend) DisconnectVPN(uuidOrName string) error {
ret := _m.Called(uuidOrName)
@@ -601,52 +509,6 @@ func (_c *MockBackend_DisconnectWiFi_Call) RunAndReturn(run func() error) *MockB
return _c
}
// DisconnectWiFiDevice provides a mock function with given fields: device
func (_m *MockBackend) DisconnectWiFiDevice(device string) error {
ret := _m.Called(device)
if len(ret) == 0 {
panic("no return value specified for DisconnectWiFiDevice")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(device)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockBackend_DisconnectWiFiDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectWiFiDevice'
type MockBackend_DisconnectWiFiDevice_Call struct {
*mock.Call
}
// DisconnectWiFiDevice is a helper method to define mock.On call
// - device string
func (_e *MockBackend_Expecter) DisconnectWiFiDevice(device interface{}) *MockBackend_DisconnectWiFiDevice_Call {
return &MockBackend_DisconnectWiFiDevice_Call{Call: _e.mock.On("DisconnectWiFiDevice", device)}
}
func (_c *MockBackend_DisconnectWiFiDevice_Call) Run(run func(device string)) *MockBackend_DisconnectWiFiDevice_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockBackend_DisconnectWiFiDevice_Call) Return(_a0 error) *MockBackend_DisconnectWiFiDevice_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_DisconnectWiFiDevice_Call) RunAndReturn(run func(string) error) *MockBackend_DisconnectWiFiDevice_Call {
_c.Call.Return(run)
return _c
}
// ForgetWiFiNetwork provides a mock function with given fields: ssid
func (_m *MockBackend) ForgetWiFiNetwork(ssid string) error {
ret := _m.Called(ssid)
@@ -750,53 +612,6 @@ func (_c *MockBackend_GetCurrentState_Call) RunAndReturn(run func() (*network.Ba
return _c
}
// GetEthernetDevices provides a mock function with no fields
func (_m *MockBackend) GetEthernetDevices() []network.EthernetDevice {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetEthernetDevices")
}
var r0 []network.EthernetDevice
if rf, ok := ret.Get(0).(func() []network.EthernetDevice); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]network.EthernetDevice)
}
}
return r0
}
// MockBackend_GetEthernetDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEthernetDevices'
type MockBackend_GetEthernetDevices_Call struct {
*mock.Call
}
// GetEthernetDevices is a helper method to define mock.On call
func (_e *MockBackend_Expecter) GetEthernetDevices() *MockBackend_GetEthernetDevices_Call {
return &MockBackend_GetEthernetDevices_Call{Call: _e.mock.On("GetEthernetDevices")}
}
func (_c *MockBackend_GetEthernetDevices_Call) Run(run func()) *MockBackend_GetEthernetDevices_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockBackend_GetEthernetDevices_Call) Return(_a0 []network.EthernetDevice) *MockBackend_GetEthernetDevices_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_GetEthernetDevices_Call) RunAndReturn(run func() []network.EthernetDevice) *MockBackend_GetEthernetDevices_Call {
_c.Call.Return(run)
return _c
}
// GetPromptBroker provides a mock function with no fields
func (_m *MockBackend) GetPromptBroker() network.PromptBroker {
ret := _m.Called()
@@ -844,111 +659,6 @@ func (_c *MockBackend_GetPromptBroker_Call) RunAndReturn(run func() network.Prom
return _c
}
// GetVPNConfig provides a mock function with given fields: uuidOrName
func (_m *MockBackend) GetVPNConfig(uuidOrName string) (*network.VPNConfig, error) {
ret := _m.Called(uuidOrName)
if len(ret) == 0 {
panic("no return value specified for GetVPNConfig")
}
var r0 *network.VPNConfig
var r1 error
if rf, ok := ret.Get(0).(func(string) (*network.VPNConfig, error)); ok {
return rf(uuidOrName)
}
if rf, ok := ret.Get(0).(func(string) *network.VPNConfig); ok {
r0 = rf(uuidOrName)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*network.VPNConfig)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(uuidOrName)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockBackend_GetVPNConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetVPNConfig'
type MockBackend_GetVPNConfig_Call struct {
*mock.Call
}
// GetVPNConfig is a helper method to define mock.On call
// - uuidOrName string
func (_e *MockBackend_Expecter) GetVPNConfig(uuidOrName interface{}) *MockBackend_GetVPNConfig_Call {
return &MockBackend_GetVPNConfig_Call{Call: _e.mock.On("GetVPNConfig", uuidOrName)}
}
func (_c *MockBackend_GetVPNConfig_Call) Run(run func(uuidOrName string)) *MockBackend_GetVPNConfig_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockBackend_GetVPNConfig_Call) Return(_a0 *network.VPNConfig, _a1 error) *MockBackend_GetVPNConfig_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockBackend_GetVPNConfig_Call) RunAndReturn(run func(string) (*network.VPNConfig, error)) *MockBackend_GetVPNConfig_Call {
_c.Call.Return(run)
return _c
}
// GetWiFiDevices provides a mock function with no fields
func (_m *MockBackend) GetWiFiDevices() []network.WiFiDevice {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetWiFiDevices")
}
var r0 []network.WiFiDevice
if rf, ok := ret.Get(0).(func() []network.WiFiDevice); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]network.WiFiDevice)
}
}
return r0
}
// MockBackend_GetWiFiDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiFiDevices'
type MockBackend_GetWiFiDevices_Call struct {
*mock.Call
}
// GetWiFiDevices is a helper method to define mock.On call
func (_e *MockBackend_Expecter) GetWiFiDevices() *MockBackend_GetWiFiDevices_Call {
return &MockBackend_GetWiFiDevices_Call{Call: _e.mock.On("GetWiFiDevices")}
}
func (_c *MockBackend_GetWiFiDevices_Call) Run(run func()) *MockBackend_GetWiFiDevices_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockBackend_GetWiFiDevices_Call) Return(_a0 []network.WiFiDevice) *MockBackend_GetWiFiDevices_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_GetWiFiDevices_Call) RunAndReturn(run func() []network.WiFiDevice) *MockBackend_GetWiFiDevices_Call {
_c.Call.Return(run)
return _c
}
// GetWiFiEnabled provides a mock function with no fields
func (_m *MockBackend) GetWiFiEnabled() (bool, error) {
ret := _m.Called()
@@ -1177,65 +887,6 @@ func (_c *MockBackend_GetWiredNetworkDetails_Call) RunAndReturn(run func(string)
return _c
}
// ImportVPN provides a mock function with given fields: filePath, name
func (_m *MockBackend) ImportVPN(filePath string, name string) (*network.VPNImportResult, error) {
ret := _m.Called(filePath, name)
if len(ret) == 0 {
panic("no return value specified for ImportVPN")
}
var r0 *network.VPNImportResult
var r1 error
if rf, ok := ret.Get(0).(func(string, string) (*network.VPNImportResult, error)); ok {
return rf(filePath, name)
}
if rf, ok := ret.Get(0).(func(string, string) *network.VPNImportResult); ok {
r0 = rf(filePath, name)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*network.VPNImportResult)
}
}
if rf, ok := ret.Get(1).(func(string, string) error); ok {
r1 = rf(filePath, name)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockBackend_ImportVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ImportVPN'
type MockBackend_ImportVPN_Call struct {
*mock.Call
}
// ImportVPN is a helper method to define mock.On call
// - filePath string
// - name string
func (_e *MockBackend_Expecter) ImportVPN(filePath interface{}, name interface{}) *MockBackend_ImportVPN_Call {
return &MockBackend_ImportVPN_Call{Call: _e.mock.On("ImportVPN", filePath, name)}
}
func (_c *MockBackend_ImportVPN_Call) Run(run func(filePath string, name string)) *MockBackend_ImportVPN_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string))
})
return _c
}
func (_c *MockBackend_ImportVPN_Call) Return(_a0 *network.VPNImportResult, _a1 error) *MockBackend_ImportVPN_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockBackend_ImportVPN_Call) RunAndReturn(run func(string, string) (*network.VPNImportResult, error)) *MockBackend_ImportVPN_Call {
_c.Call.Return(run)
return _c
}
// Initialize provides a mock function with no fields
func (_m *MockBackend) Initialize() error {
ret := _m.Called()
@@ -1338,63 +989,6 @@ func (_c *MockBackend_ListActiveVPN_Call) RunAndReturn(run func() ([]network.VPN
return _c
}
// ListVPNPlugins provides a mock function with no fields
func (_m *MockBackend) ListVPNPlugins() ([]network.VPNPlugin, error) {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for ListVPNPlugins")
}
var r0 []network.VPNPlugin
var r1 error
if rf, ok := ret.Get(0).(func() ([]network.VPNPlugin, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() []network.VPNPlugin); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]network.VPNPlugin)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockBackend_ListVPNPlugins_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListVPNPlugins'
type MockBackend_ListVPNPlugins_Call struct {
*mock.Call
}
// ListVPNPlugins is a helper method to define mock.On call
func (_e *MockBackend_Expecter) ListVPNPlugins() *MockBackend_ListVPNPlugins_Call {
return &MockBackend_ListVPNPlugins_Call{Call: _e.mock.On("ListVPNPlugins")}
}
func (_c *MockBackend_ListVPNPlugins_Call) Run(run func()) *MockBackend_ListVPNPlugins_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockBackend_ListVPNPlugins_Call) Return(_a0 []network.VPNPlugin, _a1 error) *MockBackend_ListVPNPlugins_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockBackend_ListVPNPlugins_Call) RunAndReturn(run func() ([]network.VPNPlugin, error)) *MockBackend_ListVPNPlugins_Call {
_c.Call.Return(run)
return _c
}
// ListVPNProfiles provides a mock function with no fields
func (_m *MockBackend) ListVPNProfiles() ([]network.VPNProfile, error) {
ret := _m.Called()
@@ -1497,52 +1091,6 @@ func (_c *MockBackend_ScanWiFi_Call) RunAndReturn(run func() error) *MockBackend
return _c
}
// ScanWiFiDevice provides a mock function with given fields: device
func (_m *MockBackend) ScanWiFiDevice(device string) error {
ret := _m.Called(device)
if len(ret) == 0 {
panic("no return value specified for ScanWiFiDevice")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(device)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockBackend_ScanWiFiDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ScanWiFiDevice'
type MockBackend_ScanWiFiDevice_Call struct {
*mock.Call
}
// ScanWiFiDevice is a helper method to define mock.On call
// - device string
func (_e *MockBackend_Expecter) ScanWiFiDevice(device interface{}) *MockBackend_ScanWiFiDevice_Call {
return &MockBackend_ScanWiFiDevice_Call{Call: _e.mock.On("ScanWiFiDevice", device)}
}
func (_c *MockBackend_ScanWiFiDevice_Call) Run(run func(device string)) *MockBackend_ScanWiFiDevice_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockBackend_ScanWiFiDevice_Call) Return(_a0 error) *MockBackend_ScanWiFiDevice_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_ScanWiFiDevice_Call) RunAndReturn(run func(string) error) *MockBackend_ScanWiFiDevice_Call {
_c.Call.Return(run)
return _c
}
// SetPromptBroker provides a mock function with given fields: broker
func (_m *MockBackend) SetPromptBroker(broker network.PromptBroker) error {
ret := _m.Called(broker)
@@ -1589,55 +1137,6 @@ func (_c *MockBackend_SetPromptBroker_Call) RunAndReturn(run func(network.Prompt
return _c
}
// SetVPNCredentials provides a mock function with given fields: uuid, username, password, save
func (_m *MockBackend) SetVPNCredentials(uuid string, username string, password string, save bool) error {
ret := _m.Called(uuid, username, password, save)
if len(ret) == 0 {
panic("no return value specified for SetVPNCredentials")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string, string, bool) error); ok {
r0 = rf(uuid, username, password, save)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockBackend_SetVPNCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetVPNCredentials'
type MockBackend_SetVPNCredentials_Call struct {
*mock.Call
}
// SetVPNCredentials is a helper method to define mock.On call
// - uuid string
// - username string
// - password string
// - save bool
func (_e *MockBackend_Expecter) SetVPNCredentials(uuid interface{}, username interface{}, password interface{}, save interface{}) *MockBackend_SetVPNCredentials_Call {
return &MockBackend_SetVPNCredentials_Call{Call: _e.mock.On("SetVPNCredentials", uuid, username, password, save)}
}
func (_c *MockBackend_SetVPNCredentials_Call) Run(run func(uuid string, username string, password string, save bool)) *MockBackend_SetVPNCredentials_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(string), args[2].(string), args[3].(bool))
})
return _c
}
func (_c *MockBackend_SetVPNCredentials_Call) Return(_a0 error) *MockBackend_SetVPNCredentials_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_SetVPNCredentials_Call) RunAndReturn(run func(string, string, string, bool) error) *MockBackend_SetVPNCredentials_Call {
_c.Call.Return(run)
return _c
}
// SetWiFiAutoconnect provides a mock function with given fields: ssid, autoconnect
func (_m *MockBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error {
ret := _m.Called(ssid, autoconnect)
@@ -1857,53 +1356,6 @@ func (_c *MockBackend_SubmitCredentials_Call) RunAndReturn(run func(string, map[
return _c
}
// UpdateVPNConfig provides a mock function with given fields: uuid, updates
func (_m *MockBackend) UpdateVPNConfig(uuid string, updates map[string]interface{}) error {
ret := _m.Called(uuid, updates)
if len(ret) == 0 {
panic("no return value specified for UpdateVPNConfig")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, map[string]interface{}) error); ok {
r0 = rf(uuid, updates)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockBackend_UpdateVPNConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateVPNConfig'
type MockBackend_UpdateVPNConfig_Call struct {
*mock.Call
}
// UpdateVPNConfig is a helper method to define mock.On call
// - uuid string
// - updates map[string]interface{}
func (_e *MockBackend_Expecter) UpdateVPNConfig(uuid interface{}, updates interface{}) *MockBackend_UpdateVPNConfig_Call {
return &MockBackend_UpdateVPNConfig_Call{Call: _e.mock.On("UpdateVPNConfig", uuid, updates)}
}
func (_c *MockBackend_UpdateVPNConfig_Call) Run(run func(uuid string, updates map[string]interface{})) *MockBackend_UpdateVPNConfig_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].(map[string]interface{}))
})
return _c
}
func (_c *MockBackend_UpdateVPNConfig_Call) Return(_a0 error) *MockBackend_UpdateVPNConfig_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_UpdateVPNConfig_Call) RunAndReturn(run func(string, map[string]interface{}) error) *MockBackend_UpdateVPNConfig_Call {
_c.Call.Return(run)
return _c
}
// NewMockBackend creates a new instance of MockBackend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockBackend(t interface {

View File

@@ -1,144 +0,0 @@
// Code generated by mockery v2.53.5. DO NOT EDIT.
package mocks_version
import mock "github.com/stretchr/testify/mock"
// MockVersionFetcher is an autogenerated mock type for the VersionFetcher type
type MockVersionFetcher struct {
mock.Mock
}
type MockVersionFetcher_Expecter struct {
mock *mock.Mock
}
func (_m *MockVersionFetcher) EXPECT() *MockVersionFetcher_Expecter {
return &MockVersionFetcher_Expecter{mock: &_m.Mock}
}
// GetCurrentVersion provides a mock function with given fields: dmsPath
func (_m *MockVersionFetcher) GetCurrentVersion(dmsPath string) (string, error) {
ret := _m.Called(dmsPath)
if len(ret) == 0 {
panic("no return value specified for GetCurrentVersion")
}
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(string) (string, error)); ok {
return rf(dmsPath)
}
if rf, ok := ret.Get(0).(func(string) string); ok {
r0 = rf(dmsPath)
} else {
r0 = ret.Get(0).(string)
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(dmsPath)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockVersionFetcher_GetCurrentVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCurrentVersion'
type MockVersionFetcher_GetCurrentVersion_Call struct {
*mock.Call
}
// GetCurrentVersion is a helper method to define mock.On call
// - dmsPath string
func (_e *MockVersionFetcher_Expecter) GetCurrentVersion(dmsPath interface{}) *MockVersionFetcher_GetCurrentVersion_Call {
return &MockVersionFetcher_GetCurrentVersion_Call{Call: _e.mock.On("GetCurrentVersion", dmsPath)}
}
func (_c *MockVersionFetcher_GetCurrentVersion_Call) Run(run func(dmsPath string)) *MockVersionFetcher_GetCurrentVersion_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockVersionFetcher_GetCurrentVersion_Call) Return(_a0 string, _a1 error) *MockVersionFetcher_GetCurrentVersion_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockVersionFetcher_GetCurrentVersion_Call) RunAndReturn(run func(string) (string, error)) *MockVersionFetcher_GetCurrentVersion_Call {
_c.Call.Return(run)
return _c
}
// GetLatestVersion provides a mock function with given fields: dmsPath
func (_m *MockVersionFetcher) GetLatestVersion(dmsPath string) (string, error) {
ret := _m.Called(dmsPath)
if len(ret) == 0 {
panic("no return value specified for GetLatestVersion")
}
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(string) (string, error)); ok {
return rf(dmsPath)
}
if rf, ok := ret.Get(0).(func(string) string); ok {
r0 = rf(dmsPath)
} else {
r0 = ret.Get(0).(string)
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(dmsPath)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockVersionFetcher_GetLatestVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLatestVersion'
type MockVersionFetcher_GetLatestVersion_Call struct {
*mock.Call
}
// GetLatestVersion is a helper method to define mock.On call
// - dmsPath string
func (_e *MockVersionFetcher_Expecter) GetLatestVersion(dmsPath interface{}) *MockVersionFetcher_GetLatestVersion_Call {
return &MockVersionFetcher_GetLatestVersion_Call{Call: _e.mock.On("GetLatestVersion", dmsPath)}
}
func (_c *MockVersionFetcher_GetLatestVersion_Call) Run(run func(dmsPath string)) *MockVersionFetcher_GetLatestVersion_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockVersionFetcher_GetLatestVersion_Call) Return(_a0 string, _a1 error) *MockVersionFetcher_GetLatestVersion_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockVersionFetcher_GetLatestVersion_Call) RunAndReturn(run func(string) (string, error)) *MockVersionFetcher_GetLatestVersion_Call {
_c.Call.Return(run)
return _c
}
// NewMockVersionFetcher creates a new instance of MockVersionFetcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockVersionFetcher(t interface {
mock.TestingT
Cleanup(func())
}) *MockVersionFetcher {
mock := &MockVersionFetcher{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -93,7 +93,7 @@ func (m *Manager) Install(plugin Plugin) error {
if !repoExists {
if err := m.gitClient.PlainClone(repoPath, plugin.Repo); err != nil {
m.fs.RemoveAll(repoPath) //nolint:errcheck
m.fs.RemoveAll(repoPath)
return fmt.Errorf("failed to clone repository: %w", err)
}
} else {
@@ -130,7 +130,7 @@ func (m *Manager) Install(plugin Plugin) error {
}
} else {
if err := m.gitClient.PlainClone(pluginPath, plugin.Repo); err != nil {
m.fs.RemoveAll(pluginPath) //nolint:errcheck
m.fs.RemoveAll(pluginPath)
return fmt.Errorf("failed to clone plugin: %w", err)
}
}

View File

@@ -157,16 +157,6 @@ type ZdwlIpcOutputV2 struct {
appidHandler ZdwlIpcOutputV2AppidHandlerFunc
layoutSymbolHandler ZdwlIpcOutputV2LayoutSymbolHandlerFunc
frameHandler ZdwlIpcOutputV2FrameHandlerFunc
fullscreenHandler ZdwlIpcOutputV2FullscreenHandlerFunc
floatingHandler ZdwlIpcOutputV2FloatingHandlerFunc
xHandler ZdwlIpcOutputV2XHandlerFunc
yHandler ZdwlIpcOutputV2YHandlerFunc
widthHandler ZdwlIpcOutputV2WidthHandlerFunc
heightHandler ZdwlIpcOutputV2HeightHandlerFunc
lastLayerHandler ZdwlIpcOutputV2LastLayerHandlerFunc
kbLayoutHandler ZdwlIpcOutputV2KbLayoutHandlerFunc
keymodeHandler ZdwlIpcOutputV2KeymodeHandlerFunc
scalefactorHandler ZdwlIpcOutputV2ScalefactorHandlerFunc
}
// NewZdwlIpcOutputV2 : control dwl output
@@ -261,60 +251,6 @@ func (i *ZdwlIpcOutputV2) SetLayout(index uint32) error {
return err
}
// Quit : Quit mango
// This request allows clients to instruct the compositor to quit mango.
func (i *ZdwlIpcOutputV2) Quit() error {
const opcode = 4
const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte
l := 0
client.PutUint32(_reqBuf[l:4], i.ID())
l += 4
client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
l += 4
err := i.Context().WriteMsg(_reqBuf[:], nil)
return err
}
// SendDispatch : Set the active tags of this output
//
// dispatch: dispatch name.
// arg1: arg1.
// arg2: arg2.
// arg3: arg3.
// arg4: arg4.
// arg5: arg5.
func (i *ZdwlIpcOutputV2) SendDispatch(dispatch, arg1, arg2, arg3, arg4, arg5 string) error {
const opcode = 5
dispatchLen := client.PaddedLen(len(dispatch) + 1)
arg1Len := client.PaddedLen(len(arg1) + 1)
arg2Len := client.PaddedLen(len(arg2) + 1)
arg3Len := client.PaddedLen(len(arg3) + 1)
arg4Len := client.PaddedLen(len(arg4) + 1)
arg5Len := client.PaddedLen(len(arg5) + 1)
_reqBufLen := 8 + (4 + dispatchLen) + (4 + arg1Len) + (4 + arg2Len) + (4 + arg3Len) + (4 + arg4Len) + (4 + arg5Len)
_reqBuf := make([]byte, _reqBufLen)
l := 0
client.PutUint32(_reqBuf[l:4], i.ID())
l += 4
client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
l += 4
client.PutString(_reqBuf[l:l+(4+dispatchLen)], dispatch)
l += (4 + dispatchLen)
client.PutString(_reqBuf[l:l+(4+arg1Len)], arg1)
l += (4 + arg1Len)
client.PutString(_reqBuf[l:l+(4+arg2Len)], arg2)
l += (4 + arg2Len)
client.PutString(_reqBuf[l:l+(4+arg3Len)], arg3)
l += (4 + arg3Len)
client.PutString(_reqBuf[l:l+(4+arg4Len)], arg4)
l += (4 + arg4Len)
client.PutString(_reqBuf[l:l+(4+arg5Len)], arg5)
l += (4 + arg5Len)
err := i.Context().WriteMsg(_reqBuf, nil)
return err
}
type ZdwlIpcOutputV2TagState uint32
// ZdwlIpcOutputV2TagState :
@@ -463,136 +399,6 @@ func (i *ZdwlIpcOutputV2) SetFrameHandler(f ZdwlIpcOutputV2FrameHandlerFunc) {
i.frameHandler = f
}
// ZdwlIpcOutputV2FullscreenEvent : Update fullscreen status
//
// Indicates if the selected client on this output is fullscreen.
type ZdwlIpcOutputV2FullscreenEvent struct {
IsFullscreen uint32
}
type ZdwlIpcOutputV2FullscreenHandlerFunc func(ZdwlIpcOutputV2FullscreenEvent)
// SetFullscreenHandler : sets handler for ZdwlIpcOutputV2FullscreenEvent
func (i *ZdwlIpcOutputV2) SetFullscreenHandler(f ZdwlIpcOutputV2FullscreenHandlerFunc) {
i.fullscreenHandler = f
}
// ZdwlIpcOutputV2FloatingEvent : Update the floating status
//
// Indicates if the selected client on this output is floating.
type ZdwlIpcOutputV2FloatingEvent struct {
IsFloating uint32
}
type ZdwlIpcOutputV2FloatingHandlerFunc func(ZdwlIpcOutputV2FloatingEvent)
// SetFloatingHandler : sets handler for ZdwlIpcOutputV2FloatingEvent
func (i *ZdwlIpcOutputV2) SetFloatingHandler(f ZdwlIpcOutputV2FloatingHandlerFunc) {
i.floatingHandler = f
}
// ZdwlIpcOutputV2XEvent : Update the x coordinates
//
// Indicates if x coordinates of the selected client.
type ZdwlIpcOutputV2XEvent struct {
X int32
}
type ZdwlIpcOutputV2XHandlerFunc func(ZdwlIpcOutputV2XEvent)
// SetXHandler : sets handler for ZdwlIpcOutputV2XEvent
func (i *ZdwlIpcOutputV2) SetXHandler(f ZdwlIpcOutputV2XHandlerFunc) {
i.xHandler = f
}
// ZdwlIpcOutputV2YEvent : Update the y coordinates
//
// Indicates if y coordinates of the selected client.
type ZdwlIpcOutputV2YEvent struct {
Y int32
}
type ZdwlIpcOutputV2YHandlerFunc func(ZdwlIpcOutputV2YEvent)
// SetYHandler : sets handler for ZdwlIpcOutputV2YEvent
func (i *ZdwlIpcOutputV2) SetYHandler(f ZdwlIpcOutputV2YHandlerFunc) {
i.yHandler = f
}
// ZdwlIpcOutputV2WidthEvent : Update the width
//
// Indicates if width of the selected client.
type ZdwlIpcOutputV2WidthEvent struct {
Width int32
}
type ZdwlIpcOutputV2WidthHandlerFunc func(ZdwlIpcOutputV2WidthEvent)
// SetWidthHandler : sets handler for ZdwlIpcOutputV2WidthEvent
func (i *ZdwlIpcOutputV2) SetWidthHandler(f ZdwlIpcOutputV2WidthHandlerFunc) {
i.widthHandler = f
}
// ZdwlIpcOutputV2HeightEvent : Update the height
//
// Indicates if height of the selected client.
type ZdwlIpcOutputV2HeightEvent struct {
Height int32
}
type ZdwlIpcOutputV2HeightHandlerFunc func(ZdwlIpcOutputV2HeightEvent)
// SetHeightHandler : sets handler for ZdwlIpcOutputV2HeightEvent
func (i *ZdwlIpcOutputV2) SetHeightHandler(f ZdwlIpcOutputV2HeightHandlerFunc) {
i.heightHandler = f
}
// ZdwlIpcOutputV2LastLayerEvent : last map layer.
//
// last map layer.
type ZdwlIpcOutputV2LastLayerEvent struct {
LastLayer string
}
type ZdwlIpcOutputV2LastLayerHandlerFunc func(ZdwlIpcOutputV2LastLayerEvent)
// SetLastLayerHandler : sets handler for ZdwlIpcOutputV2LastLayerEvent
func (i *ZdwlIpcOutputV2) SetLastLayerHandler(f ZdwlIpcOutputV2LastLayerHandlerFunc) {
i.lastLayerHandler = f
}
// ZdwlIpcOutputV2KbLayoutEvent : current keyboard layout.
//
// current keyboard layout.
type ZdwlIpcOutputV2KbLayoutEvent struct {
KbLayout string
}
type ZdwlIpcOutputV2KbLayoutHandlerFunc func(ZdwlIpcOutputV2KbLayoutEvent)
// SetKbLayoutHandler : sets handler for ZdwlIpcOutputV2KbLayoutEvent
func (i *ZdwlIpcOutputV2) SetKbLayoutHandler(f ZdwlIpcOutputV2KbLayoutHandlerFunc) {
i.kbLayoutHandler = f
}
// ZdwlIpcOutputV2KeymodeEvent : current keybind mode.
//
// current keybind mode.
type ZdwlIpcOutputV2KeymodeEvent struct {
Keymode string
}
type ZdwlIpcOutputV2KeymodeHandlerFunc func(ZdwlIpcOutputV2KeymodeEvent)
// SetKeymodeHandler : sets handler for ZdwlIpcOutputV2KeymodeEvent
func (i *ZdwlIpcOutputV2) SetKeymodeHandler(f ZdwlIpcOutputV2KeymodeHandlerFunc) {
i.keymodeHandler = f
}
// ZdwlIpcOutputV2ScalefactorEvent : scale factor of monitor.
//
// scale factor of monitor.
type ZdwlIpcOutputV2ScalefactorEvent struct {
Scalefactor uint32
}
type ZdwlIpcOutputV2ScalefactorHandlerFunc func(ZdwlIpcOutputV2ScalefactorEvent)
// SetScalefactorHandler : sets handler for ZdwlIpcOutputV2ScalefactorEvent
func (i *ZdwlIpcOutputV2) SetScalefactorHandler(f ZdwlIpcOutputV2ScalefactorHandlerFunc) {
i.scalefactorHandler = f
}
func (i *ZdwlIpcOutputV2) Dispatch(opcode uint32, fd int, data []byte) {
switch opcode {
case 0:
@@ -681,111 +487,5 @@ func (i *ZdwlIpcOutputV2) Dispatch(opcode uint32, fd int, data []byte) {
var e ZdwlIpcOutputV2FrameEvent
i.frameHandler(e)
case 8:
if i.fullscreenHandler == nil {
return
}
var e ZdwlIpcOutputV2FullscreenEvent
l := 0
e.IsFullscreen = client.Uint32(data[l : l+4])
l += 4
i.fullscreenHandler(e)
case 9:
if i.floatingHandler == nil {
return
}
var e ZdwlIpcOutputV2FloatingEvent
l := 0
e.IsFloating = client.Uint32(data[l : l+4])
l += 4
i.floatingHandler(e)
case 10:
if i.xHandler == nil {
return
}
var e ZdwlIpcOutputV2XEvent
l := 0
e.X = int32(client.Uint32(data[l : l+4]))
l += 4
i.xHandler(e)
case 11:
if i.yHandler == nil {
return
}
var e ZdwlIpcOutputV2YEvent
l := 0
e.Y = int32(client.Uint32(data[l : l+4]))
l += 4
i.yHandler(e)
case 12:
if i.widthHandler == nil {
return
}
var e ZdwlIpcOutputV2WidthEvent
l := 0
e.Width = int32(client.Uint32(data[l : l+4]))
l += 4
i.widthHandler(e)
case 13:
if i.heightHandler == nil {
return
}
var e ZdwlIpcOutputV2HeightEvent
l := 0
e.Height = int32(client.Uint32(data[l : l+4]))
l += 4
i.heightHandler(e)
case 14:
if i.lastLayerHandler == nil {
return
}
var e ZdwlIpcOutputV2LastLayerEvent
l := 0
lastLayerLen := client.PaddedLen(int(client.Uint32(data[l : l+4])))
l += 4
e.LastLayer = client.String(data[l : l+lastLayerLen])
l += lastLayerLen
i.lastLayerHandler(e)
case 15:
if i.kbLayoutHandler == nil {
return
}
var e ZdwlIpcOutputV2KbLayoutEvent
l := 0
kbLayoutLen := client.PaddedLen(int(client.Uint32(data[l : l+4])))
l += 4
e.KbLayout = client.String(data[l : l+kbLayoutLen])
l += kbLayoutLen
i.kbLayoutHandler(e)
case 16:
if i.keymodeHandler == nil {
return
}
var e ZdwlIpcOutputV2KeymodeEvent
l := 0
keymodeLen := client.PaddedLen(int(client.Uint32(data[l : l+4])))
l += 4
e.Keymode = client.String(data[l : l+keymodeLen])
l += keymodeLen
i.keymodeHandler(e)
case 17:
if i.scalefactorHandler == nil {
return
}
var e ZdwlIpcOutputV2ScalefactorEvent
l := 0
e.Scalefactor = client.Uint32(data[l : l+4])
l += 4
i.scalefactorHandler(e)
}
}

View File

@@ -1,283 +0,0 @@
// Generated by go-wayland-scanner
// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner
// XML file : internal/proto/xml/wlr-output-power-management-unstable-v1.xml
//
// wlr_output_power_management_unstable_v1 Protocol Copyright:
//
// Copyright © 2019 Purism SPC
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice (including the next
// paragraph) shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
package wlr_output_power
import "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
// ZwlrOutputPowerManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry].
// It can be used to match the [client.RegistryGlobalEvent.Interface] in the
// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies.
const ZwlrOutputPowerManagerV1InterfaceName = "zwlr_output_power_manager_v1"
// ZwlrOutputPowerManagerV1 : manager to create per-output power management
//
// This interface is a manager that allows creating per-output power
// management mode controls.
type ZwlrOutputPowerManagerV1 struct {
client.BaseProxy
}
// NewZwlrOutputPowerManagerV1 : manager to create per-output power management
//
// This interface is a manager that allows creating per-output power
// management mode controls.
func NewZwlrOutputPowerManagerV1(ctx *client.Context) *ZwlrOutputPowerManagerV1 {
zwlrOutputPowerManagerV1 := &ZwlrOutputPowerManagerV1{}
ctx.Register(zwlrOutputPowerManagerV1)
return zwlrOutputPowerManagerV1
}
// GetOutputPower : get a power management for an output
//
// Create an output power management mode control that can be used to
// adjust the power management mode for a given output.
func (i *ZwlrOutputPowerManagerV1) GetOutputPower(output *client.Output) (*ZwlrOutputPowerV1, error) {
id := NewZwlrOutputPowerV1(i.Context())
const opcode = 0
const _reqBufLen = 8 + 4 + 4
var _reqBuf [_reqBufLen]byte
l := 0
client.PutUint32(_reqBuf[l:4], i.ID())
l += 4
client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
l += 4
client.PutUint32(_reqBuf[l:l+4], id.ID())
l += 4
client.PutUint32(_reqBuf[l:l+4], output.ID())
l += 4
err := i.Context().WriteMsg(_reqBuf[:], nil)
return id, err
}
// Destroy : destroy the manager
//
// All objects created by the manager will still remain valid, until their
// appropriate destroy request has been called.
func (i *ZwlrOutputPowerManagerV1) Destroy() error {
defer i.Context().Unregister(i)
const opcode = 1
const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte
l := 0
client.PutUint32(_reqBuf[l:4], i.ID())
l += 4
client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
l += 4
err := i.Context().WriteMsg(_reqBuf[:], nil)
return err
}
// ZwlrOutputPowerV1InterfaceName is the name of the interface as it appears in the [client.Registry].
// It can be used to match the [client.RegistryGlobalEvent.Interface] in the
// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies.
const ZwlrOutputPowerV1InterfaceName = "zwlr_output_power_v1"
// ZwlrOutputPowerV1 : adjust power management mode for an output
//
// This object offers requests to set the power management mode of
// an output.
type ZwlrOutputPowerV1 struct {
client.BaseProxy
modeHandler ZwlrOutputPowerV1ModeHandlerFunc
failedHandler ZwlrOutputPowerV1FailedHandlerFunc
}
// NewZwlrOutputPowerV1 : adjust power management mode for an output
//
// This object offers requests to set the power management mode of
// an output.
func NewZwlrOutputPowerV1(ctx *client.Context) *ZwlrOutputPowerV1 {
zwlrOutputPowerV1 := &ZwlrOutputPowerV1{}
ctx.Register(zwlrOutputPowerV1)
return zwlrOutputPowerV1
}
// SetMode : Set an outputs power save mode
//
// Set an output's power save mode to the given mode. The mode change
// is effective immediately. If the output does not support the given
// mode a failed event is sent.
//
// mode: the power save mode to set
func (i *ZwlrOutputPowerV1) SetMode(mode uint32) error {
const opcode = 0
const _reqBufLen = 8 + 4
var _reqBuf [_reqBufLen]byte
l := 0
client.PutUint32(_reqBuf[l:4], i.ID())
l += 4
client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
l += 4
client.PutUint32(_reqBuf[l:l+4], uint32(mode))
l += 4
err := i.Context().WriteMsg(_reqBuf[:], nil)
return err
}
// Destroy : destroy this power management
//
// Destroys the output power management mode control object.
func (i *ZwlrOutputPowerV1) Destroy() error {
defer i.Context().Unregister(i)
const opcode = 1
const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte
l := 0
client.PutUint32(_reqBuf[l:4], i.ID())
l += 4
client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
l += 4
err := i.Context().WriteMsg(_reqBuf[:], nil)
return err
}
type ZwlrOutputPowerV1Mode uint32
// ZwlrOutputPowerV1Mode :
const (
// ZwlrOutputPowerV1ModeOff : Output is turned off.
ZwlrOutputPowerV1ModeOff ZwlrOutputPowerV1Mode = 0
// ZwlrOutputPowerV1ModeOn : Output is turned on, no power saving
ZwlrOutputPowerV1ModeOn ZwlrOutputPowerV1Mode = 1
)
func (e ZwlrOutputPowerV1Mode) Name() string {
switch e {
case ZwlrOutputPowerV1ModeOff:
return "off"
case ZwlrOutputPowerV1ModeOn:
return "on"
default:
return ""
}
}
func (e ZwlrOutputPowerV1Mode) Value() string {
switch e {
case ZwlrOutputPowerV1ModeOff:
return "0"
case ZwlrOutputPowerV1ModeOn:
return "1"
default:
return ""
}
}
func (e ZwlrOutputPowerV1Mode) String() string {
return e.Name() + "=" + e.Value()
}
type ZwlrOutputPowerV1Error uint32
// ZwlrOutputPowerV1Error :
const (
// ZwlrOutputPowerV1ErrorInvalidMode : nonexistent power save mode
ZwlrOutputPowerV1ErrorInvalidMode ZwlrOutputPowerV1Error = 1
)
func (e ZwlrOutputPowerV1Error) Name() string {
switch e {
case ZwlrOutputPowerV1ErrorInvalidMode:
return "invalid_mode"
default:
return ""
}
}
func (e ZwlrOutputPowerV1Error) Value() string {
switch e {
case ZwlrOutputPowerV1ErrorInvalidMode:
return "1"
default:
return ""
}
}
func (e ZwlrOutputPowerV1Error) String() string {
return e.Name() + "=" + e.Value()
}
// ZwlrOutputPowerV1ModeEvent : Report a power management mode change
//
// Report the power management mode change of an output.
//
// The mode event is sent after an output changed its power
// management mode. The reason can be a client using set_mode or the
// compositor deciding to change an output's mode.
// This event is also sent immediately when the object is created
// so the client is informed about the current power management mode.
type ZwlrOutputPowerV1ModeEvent struct {
Mode uint32
}
type ZwlrOutputPowerV1ModeHandlerFunc func(ZwlrOutputPowerV1ModeEvent)
// SetModeHandler : sets handler for ZwlrOutputPowerV1ModeEvent
func (i *ZwlrOutputPowerV1) SetModeHandler(f ZwlrOutputPowerV1ModeHandlerFunc) {
i.modeHandler = f
}
// ZwlrOutputPowerV1FailedEvent : object no longer valid
//
// This event indicates that the output power management mode control
// is no longer valid. This can happen for a number of reasons,
// including:
// - The output doesn't support power management
// - Another client already has exclusive power management mode control
// for this output
// - The output disappeared
//
// Upon receiving this event, the client should destroy this object.
type ZwlrOutputPowerV1FailedEvent struct{}
type ZwlrOutputPowerV1FailedHandlerFunc func(ZwlrOutputPowerV1FailedEvent)
// SetFailedHandler : sets handler for ZwlrOutputPowerV1FailedEvent
func (i *ZwlrOutputPowerV1) SetFailedHandler(f ZwlrOutputPowerV1FailedHandlerFunc) {
i.failedHandler = f
}
func (i *ZwlrOutputPowerV1) Dispatch(opcode uint32, fd int, data []byte) {
switch opcode {
case 0:
if i.modeHandler == nil {
return
}
var e ZwlrOutputPowerV1ModeEvent
l := 0
e.Mode = client.Uint32(data[l : l+4])
l += 4
i.modeHandler(e)
case 1:
if i.failedHandler == nil {
return
}
var e ZwlrOutputPowerV1FailedEvent
i.failedHandler(e)
}
}

View File

@@ -19,7 +19,7 @@ I would probably just submit raphi's patchset but I don't think that would be po
reset.
</description>
<interface name="zdwl_ipc_manager_v2" version="2">
<interface name="zdwl_ipc_manager_v2" version="1">
<description summary="manage dwl state">
This interface is exposed as a global in wl_registry.
@@ -60,7 +60,7 @@ I would probably just submit raphi's patchset but I don't think that would be po
</event>
</interface>
<interface name="zdwl_ipc_output_v2" version="2">
<interface name="zdwl_ipc_output_v2" version="1">
<description summary="control dwl output">
Observe and control a dwl output.
@@ -162,91 +162,5 @@ I would probably just submit raphi's patchset but I don't think that would be po
<description summary="Set the layout of this output"/>
<arg name="index" type="uint" summary="index of a layout recieved by dwl_ipc_manager.layout"/>
</request>
<request name="quit" since="2">
<description summary="Quit mango">This request allows clients to instruct the compositor to quit mango.</description>
</request>
<request name="dispatch" since="2">
<description summary="Set the active tags of this output"/>
<arg name="dispatch" type="string" summary="dispatch name."/>
<arg name="arg1" type="string" summary="arg1."/>
<arg name="arg2" type="string" summary="arg2."/>
<arg name="arg3" type="string" summary="arg3."/>
<arg name="arg4" type="string" summary="arg4."/>
<arg name="arg5" type="string" summary="arg5."/>
</request>
<!-- Version 2 -->
<event name="fullscreen" since="2">
<description summary="Update fullscreen status">
Indicates if the selected client on this output is fullscreen.
</description>
<arg name="is_fullscreen" type="uint" summary="If the selected client is fullscreen. Nonzero is valid, zero invalid"/>
</event>
<event name="floating" since="2">
<description summary="Update the floating status">
Indicates if the selected client on this output is floating.
</description>
<arg name="is_floating" type="uint" summary="If the selected client is floating. Nonzero is valid, zero invalid"/>
</event>
<event name="x" since="2">
<description summary="Update the x coordinates">
Indicates if x coordinates of the selected client.
</description>
<arg name="x" type="int" summary="x coordinate of the selected client"/>
</event>
<event name="y" since="2">
<description summary="Update the y coordinates">
Indicates if y coordinates of the selected client.
</description>
<arg name="y" type="int" summary="y coordinate of the selected client"/>
</event>
<event name="width" since="2">
<description summary="Update the width">
Indicates if width of the selected client.
</description>
<arg name="width" type="int" summary="width of the selected client"/>
</event>
<event name="height" since="2">
<description summary="Update the height">
Indicates if height of the selected client.
</description>
<arg name="height" type="int" summary="height of the selected client"/>
</event>
<event name="last_layer" since="2">
<description summary="last map layer.">
last map layer.
</description>
<arg name="last_layer" type="string" summary="last map layer."/>
</event>
<event name="kb_layout" since="2">
<description summary="current keyboard layout.">
current keyboard layout.
</description>
<arg name="kb_layout" type="string" summary="current keyboard layout."/>
</event>
<event name="keymode" since="2">
<description summary="current keybind mode.">
current keybind mode.
</description>
<arg name="keymode" type="string" summary="current keybind mode."/>
</event>
<event name="scalefactor" since="2">
<description summary="scale factor of monitor.">
scale factor of monitor.
</description>
<arg name="scalefactor" type="uint" summary="scale factor of monitor."/>
</event>
</interface>
</protocol>

View File

@@ -1,128 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_output_power_management_unstable_v1">
<copyright>
Copyright © 2019 Purism SPC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="Control power management modes of outputs">
This protocol allows clients to control power management modes
of outputs that are currently part of the compositor space. The
intent is to allow special clients like desktop shells to power
down outputs when the system is idle.
To modify outputs not currently part of the compositor space see
wlr-output-management.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<interface name="zwlr_output_power_manager_v1" version="1">
<description summary="manager to create per-output power management">
This interface is a manager that allows creating per-output power
management mode controls.
</description>
<request name="get_output_power">
<description summary="get a power management for an output">
Create an output power management mode control that can be used to
adjust the power management mode for a given output.
</description>
<arg name="id" type="new_id" interface="zwlr_output_power_v1"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the manager">
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
</description>
</request>
</interface>
<interface name="zwlr_output_power_v1" version="1">
<description summary="adjust power management mode for an output">
This object offers requests to set the power management mode of
an output.
</description>
<enum name="mode">
<entry name="off" value="0"
summary="Output is turned off."/>
<entry name="on" value="1"
summary="Output is turned on, no power saving"/>
</enum>
<enum name="error">
<entry name="invalid_mode" value="1" summary="nonexistent power save mode"/>
</enum>
<request name="set_mode">
<description summary="Set an outputs power save mode">
Set an output's power save mode to the given mode. The mode change
is effective immediately. If the output does not support the given
mode a failed event is sent.
</description>
<arg name="mode" type="uint" enum="mode" summary="the power save mode to set"/>
</request>
<event name="mode">
<description summary="Report a power management mode change">
Report the power management mode change of an output.
The mode event is sent after an output changed its power
management mode. The reason can be a client using set_mode or the
compositor deciding to change an output's mode.
This event is also sent immediately when the object is created
so the client is informed about the current power management mode.
</description>
<arg name="mode" type="uint" enum="mode"
summary="the output's new power management mode"/>
</event>
<event name="failed">
<description summary="object no longer valid">
This event indicates that the output power management mode control
is no longer valid. This can happen for a number of reasons,
including:
- The output doesn't support power management
- Another client already has exclusive power management mode control
for this output
- The output disappeared
Upon receiving this event, the client should destroy this object.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy this power management">
Destroys the output power management mode control object.
</description>
</request>
</interface>
</protocol>

View File

@@ -1,64 +0,0 @@
package apppicker
import (
"net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
type Request struct {
ID int `json:"id"`
Method string `json:"method"`
Params map[string]any `json:"params"`
}
func HandleRequest(conn net.Conn, req Request, manager *Manager) {
switch req.Method {
case "apppicker.open", "browser.open":
handleOpen(conn, req, manager)
default:
models.RespondError(conn, req.ID, "unknown method")
}
}
func handleOpen(conn net.Conn, req Request, manager *Manager) {
log.Infof("AppPicker: Received %s request with params: %+v", req.Method, req.Params)
target, ok := req.Params["target"].(string)
if !ok {
target, ok = req.Params["url"].(string)
if !ok {
log.Warnf("AppPicker: Invalid target parameter in request")
models.RespondError(conn, req.ID, "invalid target parameter")
return
}
}
event := OpenEvent{
Target: target,
RequestType: "url",
}
if mimeType, ok := req.Params["mimeType"].(string); ok {
event.MimeType = mimeType
}
if categories, ok := req.Params["categories"].([]any); ok {
event.Categories = make([]string, 0, len(categories))
for _, cat := range categories {
if catStr, ok := cat.(string); ok {
event.Categories = append(event.Categories, catStr)
}
}
}
if requestType, ok := req.Params["requestType"].(string); ok {
event.RequestType = requestType
}
log.Infof("AppPicker: Broadcasting event: %+v", event)
manager.RequestOpen(event)
models.Respond(conn, req.ID, "ok")
log.Infof("AppPicker: Request handled successfully")
}

View File

@@ -1,48 +0,0 @@
package apppicker
import (
"sync"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
)
type Manager struct {
subscribers syncmap.Map[string, chan OpenEvent]
closeOnce sync.Once
}
func NewManager() *Manager {
return &Manager{}
}
func (m *Manager) Subscribe(id string) chan OpenEvent {
ch := make(chan OpenEvent, 16)
m.subscribers.Store(id, ch)
return ch
}
func (m *Manager) Unsubscribe(id string) {
if val, ok := m.subscribers.LoadAndDelete(id); ok {
close(val)
}
}
func (m *Manager) RequestOpen(event OpenEvent) {
m.subscribers.Range(func(key string, ch chan OpenEvent) bool {
select {
case ch <- event:
default:
}
return true
})
}
func (m *Manager) Close() {
m.closeOnce.Do(func() {
m.subscribers.Range(func(key string, ch chan OpenEvent) bool {
close(ch)
m.subscribers.Delete(key)
return true
})
})
}

View File

@@ -1,8 +0,0 @@
package apppicker
type OpenEvent struct {
Target string `json:"target"`
MimeType string `json:"mimeType,omitempty"`
Categories []string `json:"categories,omitempty"`
RequestType string `json:"requestType"`
}

View File

@@ -165,11 +165,12 @@ func (a *BluezAgent) DisplayPasskey(device dbus.ObjectPath, passkey uint32, ente
log.Infof("[BluezAgent] DisplayPasskey: device=%s, passkey=%06d, entered=%d", device, passkey, entered)
if entered == 0 {
passkeyStr := strconv.FormatUint(uint64(passkey), 10)
_, err := a.promptFor(device, "display-passkey", []string{}, &passkeyStr)
pk := passkey
_, err := a.promptFor(device, "display-passkey", []string{}, nil)
if err != nil {
log.Warnf("[BluezAgent] DisplayPasskey acknowledgment failed: %v", err)
}
_ = pk
}
return nil
@@ -178,8 +179,7 @@ func (a *BluezAgent) DisplayPasskey(device dbus.ObjectPath, passkey uint32, ente
func (a *BluezAgent) RequestConfirmation(device dbus.ObjectPath, passkey uint32) *dbus.Error {
log.Infof("[BluezAgent] RequestConfirmation: device=%s, passkey=%06d", device, passkey)
passkeyStr := strconv.FormatUint(uint64(passkey), 10)
secrets, err := a.promptFor(device, "confirm", []string{"decision"}, &passkeyStr)
secrets, err := a.promptFor(device, "confirm", []string{"decision"}, nil)
if err != nil {
log.Warnf("[BluezAgent] RequestConfirmation failed: %v", err)
return a.errorFrom(err)

View File

@@ -9,9 +9,9 @@ import (
)
type Request struct {
ID int `json:"id,omitempty"`
Method string `json:"method"`
Params map[string]any `json:"params,omitempty"`
ID int `json:"id,omitempty"`
Method string `json:"method"`
Params map[string]interface{} `json:"params,omitempty"`
}
type SuccessResult struct {
@@ -190,7 +190,7 @@ func handlePairingSubmit(conn net.Conn, req Request, manager *Manager) {
return
}
secretsRaw, ok := req.Params["secrets"].(map[string]any)
secretsRaw, ok := req.Params["secrets"].(map[string]interface{})
secrets := make(map[string]string)
if ok {
for k, v := range secretsRaw {

View File

@@ -354,25 +354,21 @@ func (m *Manager) handleDevicePropertiesChanged(path dbus.ObjectPath, changed ma
_, hasTrusted := changed["Trusted"]
if hasPaired {
devicePath := string(path)
if paired, ok := pairedVar.Value().(bool); ok {
if paired {
_, wasPending := m.pendingPairings.LoadAndDelete(devicePath)
if paired, ok := pairedVar.Value().(bool); ok && paired {
devicePath := string(path)
_, wasPending := m.pendingPairings.LoadAndDelete(devicePath)
if wasPending {
select {
case m.eventQueue <- func() {
time.Sleep(300 * time.Millisecond)
log.Infof("[Bluetooth] Auto-connecting newly paired device: %s", devicePath)
if err := m.ConnectDevice(devicePath); err != nil {
log.Warnf("[Bluetooth] Auto-connect failed: %v", err)
}
}:
default:
if wasPending {
select {
case m.eventQueue <- func() {
time.Sleep(300 * time.Millisecond)
log.Infof("[Bluetooth] Auto-connecting newly paired device: %s", devicePath)
if err := m.ConnectDevice(devicePath); err != nil {
log.Warnf("[Bluetooth] Auto-connect failed: %v", err)
}
}:
default:
}
} else {
m.pendingPairings.Delete(devicePath)
}
}
}

View File

@@ -37,18 +37,25 @@ func NewDDCBackend() (*DDCBackend, error) {
}
func (b *DDCBackend) scanI2CDevices() error {
return b.scanI2CDevicesInternal(false)
}
func (b *DDCBackend) scanI2CDevicesInternal(force bool) error {
b.scanMutex.Lock()
defer b.scanMutex.Unlock()
lastScan := b.lastScan
b.scanMutex.Unlock()
if !force && time.Since(b.lastScan) < b.scanInterval {
if time.Since(lastScan) < b.scanInterval {
return nil
}
activeBuses := make(map[int]bool)
b.scanMutex.Lock()
defer b.scanMutex.Unlock()
if time.Since(b.lastScan) < b.scanInterval {
return nil
}
b.devices.Range(func(key string, value *ddcDevice) bool {
b.devices.Delete(key)
return true
})
for i := 0; i < 32; i++ {
busPath := fmt.Sprintf("/dev/i2c-%d", i)
@@ -61,31 +68,17 @@ func (b *DDCBackend) scanI2CDevicesInternal(force bool) error {
continue
}
activeBuses[i] = true
id := fmt.Sprintf("ddc:i2c-%d", i)
if _, exists := b.devices.Load(id); exists {
continue
}
dev, err := b.probeDDCDevice(i)
if err != nil || dev == nil {
continue
}
id := fmt.Sprintf("ddc:i2c-%d", i)
dev.id = id
b.devices.Store(id, dev)
log.Debugf("found DDC device on i2c-%d", i)
}
b.devices.Range(func(id string, dev *ddcDevice) bool {
if !activeBuses[dev.bus] {
b.devices.Delete(id)
log.Debugf("removed DDC device %s (bus no longer exists)", id)
}
return true
})
b.lastScan = time.Now()
return nil
@@ -105,7 +98,7 @@ func (b *DDCBackend) probeDDCDevice(bus int) (*ddcDevice, error) {
}
dummy := make([]byte, 32)
syscall.Read(fd, dummy) //nolint:errcheck
syscall.Read(fd, dummy)
writebuf := []byte{0x00}
n, err := syscall.Write(fd, writebuf)
@@ -194,13 +187,6 @@ func (b *DDCBackend) SetBrightness(id string, value int, exponential bool, callb
func (b *DDCBackend) SetBrightnessWithExponent(id string, value int, exponential bool, exponent float64, callback func()) error {
_, ok := b.devices.Load(id)
if !ok {
if err := b.scanI2CDevicesInternal(true); err != nil {
log.Debugf("rescan failed for %s: %v", id, err)
}
_, ok = b.devices.Load(id)
}
if !ok {
return fmt.Errorf("device not found: %s", id)
}
@@ -248,13 +234,6 @@ func (b *DDCBackend) SetBrightnessWithExponent(id string, value int, exponential
func (b *DDCBackend) setBrightnessImmediateWithExponent(id string, value int) error {
dev, ok := b.devices.Load(id)
if !ok {
if err := b.scanI2CDevicesInternal(true); err != nil {
log.Debugf("rescan failed for %s: %v", id, err)
}
dev, ok = b.devices.Load(id)
}
if !ok {
return fmt.Errorf("device not found: %s", id)
}

View File

@@ -13,7 +13,8 @@ type DBusConn interface {
}
type LogindBackend struct {
conn DBusConn
conn DBusConn
connOnce bool
}
func NewLogindBackend() (*LogindBackend, error) {

View File

@@ -54,7 +54,6 @@ func (m *Manager) initSysfs() {
m.sysfsBackend = sysfs
m.sysfsReady = true
m.updateState()
m.initUdev()
return
}
@@ -66,11 +65,6 @@ func (m *Manager) initSysfs() {
m.sysfsBackend = sysfs
m.sysfsReady = true
m.updateState()
m.initUdev()
}
func (m *Manager) initUdev() {
m.udevMonitor = NewUdevMonitor(m)
}
func (m *Manager) initDDC() {

View File

@@ -34,16 +34,15 @@ type DeviceUpdate struct {
}
type Request struct {
ID any `json:"id"`
Method string `json:"method"`
Params map[string]any `json:"params"`
ID interface{} `json:"id"`
Method string `json:"method"`
Params map[string]interface{} `json:"params"`
}
type Manager struct {
logindBackend *LogindBackend
sysfsBackend *SysfsBackend
ddcBackend *DDCBackend
udevMonitor *UdevMonitor
logindReady bool
sysfsReady bool
@@ -182,10 +181,6 @@ func (m *Manager) Close() {
return true
})
if m.udevMonitor != nil {
m.udevMonitor.Close()
}
if m.logindBackend != nil {
m.logindBackend.Close()
}

View File

@@ -1,151 +0,0 @@
package brightness
import (
"os"
"path/filepath"
"strconv"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/pilebones/go-udev/netlink"
)
type UdevMonitor struct {
stop chan struct{}
}
func NewUdevMonitor(manager *Manager) *UdevMonitor {
m := &UdevMonitor{
stop: make(chan struct{}),
}
go m.run(manager)
return m
}
func (m *UdevMonitor) run(manager *Manager) {
conn := &netlink.UEventConn{}
if err := conn.Connect(netlink.UdevEvent); err != nil {
log.Errorf("Failed to connect to udev netlink: %v", err)
return
}
defer conn.Close()
matcher := &netlink.RuleDefinitions{
Rules: []netlink.RuleDefinition{
{Env: map[string]string{"SUBSYSTEM": "backlight"}},
// ! TODO: most drivers dont emit this for leds?
// ! inotify brightness_hw_changed works, but thn some devices dont do that...
// ! So for now the GUI just shows OSDs for leds, without reflecting actual HW value
// {Env: map[string]string{"SUBSYSTEM": "leds"}},
},
}
if err := matcher.Compile(); err != nil {
log.Errorf("Failed to compile udev matcher: %v", err)
return
}
events := make(chan netlink.UEvent)
errs := make(chan error)
conn.Monitor(events, errs, matcher)
log.Info("Udev monitor started for backlight/leds events")
for {
select {
case <-m.stop:
return
case err := <-errs:
log.Errorf("Udev monitor error: %v", err)
return
case event := <-events:
m.handleEvent(manager, event)
}
}
}
func (m *UdevMonitor) handleEvent(manager *Manager, event netlink.UEvent) {
subsystem := event.Env["SUBSYSTEM"]
devpath := event.Env["DEVPATH"]
if subsystem == "" || devpath == "" {
return
}
sysname := filepath.Base(devpath)
action := string(event.Action)
switch action {
case "change":
m.handleChange(manager, subsystem, sysname)
case "add", "remove":
log.Debugf("Udev %s event: %s:%s - triggering rescan", action, subsystem, sysname)
manager.Rescan()
}
}
func (m *UdevMonitor) handleChange(manager *Manager, subsystem, sysname string) {
deviceID := subsystem + ":" + sysname
if manager.sysfsBackend == nil {
return
}
brightnessPath := filepath.Join(manager.sysfsBackend.basePath, subsystem, sysname, "brightness")
data, err := os.ReadFile(brightnessPath)
if err != nil {
log.Debugf("Udev change event for %s but failed to read brightness: %v", deviceID, err)
return
}
brightness, err := strconv.Atoi(strings.TrimSpace(string(data)))
if err != nil {
log.Debugf("Failed to parse brightness for %s: %v", deviceID, err)
return
}
manager.handleUdevBrightnessChange(deviceID, brightness)
}
func (m *UdevMonitor) Close() {
close(m.stop)
}
func (m *Manager) handleUdevBrightnessChange(deviceID string, rawBrightness int) {
if m.sysfsBackend == nil {
return
}
dev, err := m.sysfsBackend.GetDevice(deviceID)
if err != nil {
log.Debugf("Udev event for unknown device %s: %v", deviceID, err)
return
}
percent := m.sysfsBackend.ValueToPercent(rawBrightness, dev, false)
m.stateMutex.Lock()
var found bool
for i, d := range m.state.Devices {
if d.ID != deviceID {
continue
}
found = true
if d.Current == rawBrightness {
m.stateMutex.Unlock()
return
}
m.state.Devices[i].Current = rawBrightness
m.state.Devices[i].CurrentPercent = percent
break
}
m.stateMutex.Unlock()
if !found {
log.Debugf("Udev event for device not in state: %s", deviceID)
return
}
log.Debugf("Udev brightness change: %s -> %d (%d%%)", deviceID, rawBrightness, percent)
m.broadcastDeviceUpdate(deviceID)
}

View File

@@ -1,260 +0,0 @@
package brightness
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/pilebones/go-udev/netlink"
)
func setupTestManager(t *testing.T) (*Manager, string) {
tmpDir := t.TempDir()
backlightDir := filepath.Join(tmpDir, "backlight", "intel_backlight")
if err := os.MkdirAll(backlightDir, 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(backlightDir, "max_brightness"), []byte("1000\n"), 0644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(backlightDir, "brightness"), []byte("500\n"), 0644); err != nil {
t.Fatal(err)
}
sysfs := &SysfsBackend{
basePath: tmpDir,
classes: []string{"backlight"},
}
if err := sysfs.scanDevices(); err != nil {
t.Fatal(err)
}
m := &Manager{
sysfsBackend: sysfs,
sysfsReady: true,
stopChan: make(chan struct{}),
}
m.state = State{
Devices: []Device{
{
Class: ClassBacklight,
ID: "backlight:intel_backlight",
Name: "intel_backlight",
Current: 500,
Max: 1000,
CurrentPercent: 50,
Backend: "sysfs",
},
},
}
return m, tmpDir
}
func TestHandleUdevBrightnessChange_UpdatesState(t *testing.T) {
m, _ := setupTestManager(t)
m.handleUdevBrightnessChange("backlight:intel_backlight", 750)
state := m.GetState()
if len(state.Devices) != 1 {
t.Fatalf("expected 1 device, got %d", len(state.Devices))
}
dev := state.Devices[0]
if dev.Current != 750 {
t.Errorf("expected Current=750, got %d", dev.Current)
}
if dev.CurrentPercent != 75 {
t.Errorf("expected CurrentPercent=75, got %d", dev.CurrentPercent)
}
}
func TestHandleUdevBrightnessChange_NoChangeWhenSameValue(t *testing.T) {
m, _ := setupTestManager(t)
updateCh := m.SubscribeUpdates("test")
defer m.UnsubscribeUpdates("test")
m.handleUdevBrightnessChange("backlight:intel_backlight", 500)
select {
case <-updateCh:
t.Error("should not broadcast when brightness unchanged")
case <-time.After(50 * time.Millisecond):
}
}
func TestHandleUdevBrightnessChange_BroadcastsOnChange(t *testing.T) {
m, _ := setupTestManager(t)
updateCh := m.SubscribeUpdates("test")
defer m.UnsubscribeUpdates("test")
m.handleUdevBrightnessChange("backlight:intel_backlight", 750)
select {
case update := <-updateCh:
if update.Device.Current != 750 {
t.Errorf("broadcast had wrong Current: got %d, want 750", update.Device.Current)
}
case <-time.After(100 * time.Millisecond):
t.Error("expected broadcast on brightness change")
}
}
func TestHandleUdevBrightnessChange_UnknownDevice(t *testing.T) {
m, _ := setupTestManager(t)
m.handleUdevBrightnessChange("backlight:unknown_device", 500)
state := m.GetState()
if len(state.Devices) != 1 {
t.Errorf("state should be unchanged, got %d devices", len(state.Devices))
}
}
func TestHandleUdevBrightnessChange_NilSysfsBackend(t *testing.T) {
m := &Manager{
sysfsBackend: nil,
stopChan: make(chan struct{}),
}
m.handleUdevBrightnessChange("backlight:test", 500)
}
func TestHandleUdevBrightnessChange_DeviceNotInState(t *testing.T) {
m, _ := setupTestManager(t)
m.sysfsBackend.deviceCache.Store("backlight:other_device", &sysfsDevice{
class: ClassBacklight,
id: "backlight:other_device",
name: "other_device",
maxBrightness: 100,
minValue: 1,
})
m.handleUdevBrightnessChange("backlight:other_device", 50)
state := m.GetState()
for _, d := range state.Devices {
if d.ID == "backlight:other_device" {
t.Error("device should not be added to state via udev change event")
}
}
}
func TestHandleEvent_ChangeAction(t *testing.T) {
m, tmpDir := setupTestManager(t)
um := &UdevMonitor{stop: make(chan struct{})}
brightnessPath := filepath.Join(tmpDir, "backlight", "intel_backlight", "brightness")
if err := os.WriteFile(brightnessPath, []byte("800\n"), 0644); err != nil {
t.Fatal(err)
}
event := netlink.UEvent{
Action: netlink.CHANGE,
Env: map[string]string{
"SUBSYSTEM": "backlight",
"DEVPATH": "/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/intel_backlight",
},
}
um.handleEvent(m, event)
state := m.GetState()
if state.Devices[0].Current != 800 {
t.Errorf("expected Current=800 after change event, got %d", state.Devices[0].Current)
}
}
func TestHandleEvent_MissingEnvVars(t *testing.T) {
m, _ := setupTestManager(t)
um := &UdevMonitor{stop: make(chan struct{})}
event := netlink.UEvent{
Action: netlink.CHANGE,
Env: map[string]string{},
}
um.handleEvent(m, event)
state := m.GetState()
if state.Devices[0].Current != 500 {
t.Error("state should be unchanged with missing env vars")
}
}
func TestHandleEvent_MissingSubsystem(t *testing.T) {
m, _ := setupTestManager(t)
um := &UdevMonitor{stop: make(chan struct{})}
event := netlink.UEvent{
Action: netlink.CHANGE,
Env: map[string]string{
"DEVPATH": "/devices/foo/bar",
},
}
um.handleEvent(m, event)
state := m.GetState()
if state.Devices[0].Current != 500 {
t.Error("state should be unchanged with missing SUBSYSTEM")
}
}
func TestHandleChange_BrightnessFileNotFound(t *testing.T) {
m, _ := setupTestManager(t)
um := &UdevMonitor{stop: make(chan struct{})}
um.handleChange(m, "backlight", "nonexistent_device")
state := m.GetState()
if state.Devices[0].Current != 500 {
t.Error("state should be unchanged when brightness file not found")
}
}
func TestHandleChange_InvalidBrightnessValue(t *testing.T) {
m, tmpDir := setupTestManager(t)
um := &UdevMonitor{stop: make(chan struct{})}
brightnessPath := filepath.Join(tmpDir, "backlight", "intel_backlight", "brightness")
if err := os.WriteFile(brightnessPath, []byte("not_a_number\n"), 0644); err != nil {
t.Fatal(err)
}
um.handleChange(m, "backlight", "intel_backlight")
state := m.GetState()
if state.Devices[0].Current != 500 {
t.Error("state should be unchanged with invalid brightness value")
}
}
func TestUdevMonitor_Close(t *testing.T) {
um := &UdevMonitor{stop: make(chan struct{})}
um.Close()
select {
case <-um.stop:
default:
t.Error("stop channel should be closed")
}
}
func TestHandleChange_NilSysfsBackend(t *testing.T) {
m := &Manager{
sysfsBackend: nil,
stopChan: make(chan struct{}),
}
um := &UdevMonitor{stop: make(chan struct{})}
um.handleChange(m, "backlight", "test_device")
}

View File

@@ -1,28 +0,0 @@
package browser
import (
"net"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
type Request struct {
ID int `json:"id"`
Method string `json:"method"`
Params map[string]any `json:"params"`
}
func HandleRequest(conn net.Conn, req Request, manager *Manager) {
switch req.Method {
case "browser.open":
url, ok := req.Params["url"].(string)
if !ok {
models.RespondError(conn, req.ID, "invalid url parameter")
return
}
manager.RequestOpen(url)
models.Respond(conn, req.ID, "ok")
default:
models.RespondError(conn, req.ID, "unknown method")
}
}

View File

@@ -1,49 +0,0 @@
package browser
import (
"sync"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
)
type Manager struct {
subscribers syncmap.Map[string, chan OpenEvent]
closeOnce sync.Once
}
func NewManager() *Manager {
return &Manager{}
}
func (m *Manager) Subscribe(id string) chan OpenEvent {
ch := make(chan OpenEvent, 16)
m.subscribers.Store(id, ch)
return ch
}
func (m *Manager) Unsubscribe(id string) {
if val, ok := m.subscribers.LoadAndDelete(id); ok {
close(val)
}
}
func (m *Manager) RequestOpen(url string) {
event := OpenEvent{URL: url}
m.subscribers.Range(func(key string, ch chan OpenEvent) bool {
select {
case ch <- event:
default:
}
return true
})
}
func (m *Manager) Close() {
m.closeOnce.Do(func() {
m.subscribers.Range(func(key string, ch chan OpenEvent) bool {
close(ch)
m.subscribers.Delete(key)
return true
})
})
}

View File

@@ -1,5 +0,0 @@
package browser
type OpenEvent struct {
URL string `json:"url"`
}

View File

@@ -1,34 +1,12 @@
package cups
import (
"errors"
"strings"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp"
)
func isAuthError(err error) bool {
if err == nil {
return false
}
var httpErr ipp.HTTPError
if errors.As(err, &httpErr) {
return httpErr.Code == 401 || httpErr.Code == 403
}
var ippErr ipp.IPPError
if errors.As(err, &ippErr) {
return ippErr.Status == ipp.StatusErrorForbidden ||
ippErr.Status == ipp.StatusErrorNotAuthenticated ||
ippErr.Status == ipp.StatusErrorNotAuthorized
}
return false
}
func (m *Manager) GetPrinters() ([]Printer, error) {
attributes := []string{
ipp.AttributePrinterName,
@@ -43,9 +21,6 @@ func (m *Manager) GetPrinters() ([]Printer, error) {
printerAttrs, err := m.client.GetPrinters(attributes)
if err != nil {
if isNoPrintersError(err) {
return []Printer{}, nil
}
return nil, err
}
@@ -116,289 +91,17 @@ func (m *Manager) GetJobs(printerName string, whichJobs string) ([]Job, error) {
}
func (m *Manager) CancelJob(jobID int) error {
err := m.client.CancelJob(jobID, false)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.JobCancelPurge(jobID, false)
}
if err == nil {
m.RefreshState()
}
return err
return m.client.CancelJob(jobID, false)
}
func (m *Manager) PausePrinter(printerName string) error {
err := m.client.PausePrinter(printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetEnabled(printerName, false)
}
if err == nil {
m.RefreshState()
}
return err
return m.client.PausePrinter(printerName)
}
func (m *Manager) ResumePrinter(printerName string) error {
err := m.client.ResumePrinter(printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetEnabled(printerName, true)
}
if err == nil {
m.RefreshState()
}
return err
return m.client.ResumePrinter(printerName)
}
func (m *Manager) PurgeJobs(printerName string) error {
err := m.client.CancelAllJob(printerName, true)
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) GetDevices() ([]Device, error) {
if m.pkHelper != nil {
return m.pkHelper.DevicesGet(10, 0, nil, nil)
}
deviceAttrs, err := m.client.GetDevices()
if err != nil {
return nil, err
}
devices := make([]Device, 0, len(deviceAttrs))
for uri, attrs := range deviceAttrs {
device := Device{
URI: uri,
Class: getStringAttr(attrs, "device-class"),
Info: getStringAttr(attrs, "device-info"),
MakeModel: getStringAttr(attrs, "device-make-and-model"),
ID: getStringAttr(attrs, "device-id"),
Location: getStringAttr(attrs, "device-location"),
}
devices = append(devices, device)
}
return devices, nil
}
func (m *Manager) GetPPDs() ([]PPD, error) {
ppdAttrs, err := m.client.GetPPDs()
if err != nil {
return nil, err
}
ppds := make([]PPD, 0, len(ppdAttrs))
for name, attrs := range ppdAttrs {
ppd := PPD{
Name: name,
NaturalLanguage: getStringAttr(attrs, "ppd-natural-language"),
MakeModel: getStringAttr(attrs, ipp.AttributePPDMakeAndModel),
DeviceID: getStringAttr(attrs, "ppd-device-id"),
Product: getStringAttr(attrs, "ppd-product"),
PSVersion: getStringAttr(attrs, "ppd-psversion"),
Type: getStringAttr(attrs, "ppd-type"),
}
ppds = append(ppds, ppd)
}
return ppds, nil
}
func (m *Manager) GetClasses() ([]PrinterClass, error) {
attributes := []string{
ipp.AttributePrinterName,
ipp.AttributePrinterUriSupported,
ipp.AttributePrinterState,
ipp.AttributeMemberURIs,
ipp.AttributeMemberNames,
ipp.AttributePrinterLocation,
ipp.AttributePrinterInfo,
}
classAttrs, err := m.client.GetClasses(attributes)
if err != nil {
return nil, err
}
classes := make([]PrinterClass, 0, len(classAttrs))
for _, attrs := range classAttrs {
class := PrinterClass{
Name: getStringAttr(attrs, ipp.AttributePrinterName),
URI: getStringAttr(attrs, ipp.AttributePrinterUriSupported),
State: parsePrinterState(attrs),
Location: getStringAttr(attrs, ipp.AttributePrinterLocation),
Info: getStringAttr(attrs, ipp.AttributePrinterInfo),
Members: getStringSliceAttr(attrs, ipp.AttributeMemberNames),
}
classes = append(classes, class)
}
return classes, nil
}
func (m *Manager) CreatePrinter(name, deviceURI, ppd string, shared bool, errorPolicy, information, location string) error {
usedPkHelper := false
err := m.client.CreatePrinter(name, deviceURI, ppd, shared, errorPolicy, information, location)
if isAuthError(err) && m.pkHelper != nil {
if err = m.pkHelper.PrinterAdd(name, deviceURI, ppd, information, location); err != nil {
return err
}
usedPkHelper = true
} else if err != nil {
return err
}
if usedPkHelper {
m.pkHelper.PrinterSetEnabled(name, true) //nolint:errcheck
m.pkHelper.PrinterSetAcceptJobs(name, true, "") //nolint:errcheck
} else {
if err := m.client.ResumePrinter(name); isAuthError(err) && m.pkHelper != nil {
m.pkHelper.PrinterSetEnabled(name, true) //nolint:errcheck
}
if err := m.client.AcceptJobs(name); isAuthError(err) && m.pkHelper != nil {
m.pkHelper.PrinterSetAcceptJobs(name, true, "") //nolint:errcheck
}
}
m.RefreshState()
return nil
}
func (m *Manager) DeletePrinter(printerName string) error {
err := m.client.DeletePrinter(printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterDelete(printerName)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) AcceptJobs(printerName string) error {
err := m.client.AcceptJobs(printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetAcceptJobs(printerName, true, "")
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) RejectJobs(printerName string) error {
err := m.client.RejectJobs(printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetAcceptJobs(printerName, false, "")
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) SetPrinterShared(printerName string, shared bool) error {
err := m.client.SetPrinterIsShared(printerName, shared)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetShared(printerName, shared)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) SetPrinterLocation(printerName, location string) error {
err := m.client.SetPrinterLocation(printerName, location)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetLocation(printerName, location)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) SetPrinterInfo(printerName, info string) error {
err := m.client.SetPrinterInformation(printerName, info)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.PrinterSetInfo(printerName, info)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) MoveJob(jobID int, destPrinter string) error {
err := m.client.MoveJob(jobID, destPrinter)
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) PrintTestPage(printerName string) (int, error) {
jobID, err := m.client.PrintTestPage(printerName, strings.NewReader(config.TestPage), len(config.TestPage))
if err == nil {
m.RefreshState()
}
return jobID, err
}
func (m *Manager) AddPrinterToClass(className, printerName string) error {
err := m.client.AddPrinterToClass(className, printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.ClassAddPrinter(className, printerName)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) RemovePrinterFromClass(className, printerName string) error {
err := m.client.DeletePrinterFromClass(className, printerName)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.ClassDeletePrinter(className, printerName)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) DeleteClass(className string) error {
err := m.client.DeleteClass(className)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.ClassDelete(className)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) RestartJob(jobID int) error {
err := m.client.RestartJob(jobID)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.JobRestart(jobID)
}
if err == nil {
m.RefreshState()
}
return err
}
func (m *Manager) HoldJob(jobID int, holdUntil string) error {
err := m.client.HoldJobUntil(jobID, holdUntil)
if isAuthError(err) && m.pkHelper != nil {
err = m.pkHelper.JobSetHoldUntil(jobID, holdUntil)
}
if err == nil {
m.RefreshState()
}
return err
return m.client.CancelAllJob(printerName, true)
}

View File

@@ -1,235 +0,0 @@
package cups_test
import (
"testing"
mocks_cups "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/cups"
mocks_pkhelper "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/cups_pkhelper"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func authErr() error {
return ipp.IPPError{Status: ipp.StatusErrorForbidden}
}
func TestManager_CancelJob_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelJob(1, false).Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().JobCancelPurge(1, false).Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.CancelJob(1))
}
func TestManager_CancelJob_PkHelperError(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelJob(1, false).Return(authErr())
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().JobCancelPurge(1, false).Return(assert.AnError)
m := cups.NewTestManager(mockClient, mockPk)
assert.Error(t, m.CancelJob(1))
}
func TestManager_PausePrinter_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PausePrinter("printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetEnabled("printer1", false).Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.PausePrinter("printer1"))
}
func TestManager_ResumePrinter_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().ResumePrinter("printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetEnabled("printer1", true).Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.ResumePrinter("printer1"))
}
func TestManager_GetDevices_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().DevicesGet(10, 0, []string(nil), []string(nil)).Return([]cups.Device{
{URI: "usb://HP/LaserJet", Class: "direct"},
}, nil)
m := cups.NewTestManager(mockClient, mockPk)
got, err := m.GetDevices()
assert.NoError(t, err)
assert.Len(t, got, 1)
assert.Equal(t, "usb://HP/LaserJet", got[0].URI)
}
func TestManager_GetDevices_PkHelperError(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().DevicesGet(10, 0, []string(nil), []string(nil)).Return(nil, assert.AnError)
m := cups.NewTestManager(mockClient, mockPk)
_, err := m.GetDevices()
assert.Error(t, err)
}
func TestManager_CreatePrinter_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterAdd("newprinter", "usb://HP", "generic.ppd", "info", "location").Return(nil)
mockPk.EXPECT().PrinterSetEnabled("newprinter", true).Return(nil)
mockPk.EXPECT().PrinterSetAcceptJobs("newprinter", true, "").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location"))
}
func TestManager_DeletePrinter_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinter("printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterDelete("printer1").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.DeletePrinter("printer1"))
}
func TestManager_AcceptJobs_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AcceptJobs("printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetAcceptJobs("printer1", true, "").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.AcceptJobs("printer1"))
}
func TestManager_RejectJobs_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RejectJobs("printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetAcceptJobs("printer1", false, "").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.RejectJobs("printer1"))
}
func TestManager_SetPrinterShared_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterIsShared("printer1", true).Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetShared("printer1", true).Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.SetPrinterShared("printer1", true))
}
func TestManager_SetPrinterLocation_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterLocation("printer1", "Office").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetLocation("printer1", "Office").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.SetPrinterLocation("printer1", "Office"))
}
func TestManager_SetPrinterInfo_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterInformation("printer1", "Main Printer").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().PrinterSetInfo("printer1", "Main Printer").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.SetPrinterInfo("printer1", "Main Printer"))
}
func TestManager_AddPrinterToClass_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AddPrinterToClass("office", "printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().ClassAddPrinter("office", "printer1").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.AddPrinterToClass("office", "printer1"))
}
func TestManager_RemovePrinterFromClass_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinterFromClass("office", "printer1").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().ClassDeletePrinter("office", "printer1").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.RemovePrinterFromClass("office", "printer1"))
}
func TestManager_DeleteClass_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeleteClass("office").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().ClassDelete("office").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.DeleteClass("office"))
}
func TestManager_RestartJob_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RestartJob(1).Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().JobRestart(1).Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.RestartJob(1))
}
func TestManager_HoldJob_WithPkHelper(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().HoldJobUntil(1, "indefinite").Return(authErr())
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
mockPk := mocks_pkhelper.NewMockPkHelper(t)
mockPk.EXPECT().JobSetHoldUntil(1, "indefinite").Return(nil)
m := cups.NewTestManager(mockClient, mockPk)
assert.NoError(t, m.HoldJob(1, "indefinite"))
}

View File

@@ -137,30 +137,114 @@ func TestManager_GetJobs(t *testing.T) {
}
func TestManager_CancelJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelJob(1, false).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
tests := []struct {
name string
mockErr error
wantErr bool
}{
{
name: "success",
mockErr: nil,
wantErr: false,
},
{
name: "error",
mockErr: errors.New("test error"),
wantErr: true,
},
}
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.CancelJob(1))
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelJob(1, false).Return(tt.mockErr)
m := &Manager{
client: mockClient,
}
err := m.CancelJob(1)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestManager_PausePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PausePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
tests := []struct {
name string
mockErr error
wantErr bool
}{
{
name: "success",
mockErr: nil,
wantErr: false,
},
{
name: "error",
mockErr: errors.New("test error"),
wantErr: true,
},
}
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.PausePrinter("printer1"))
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PausePrinter("printer1").Return(tt.mockErr)
m := &Manager{
client: mockClient,
}
err := m.PausePrinter("printer1")
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestManager_ResumePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().ResumePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
tests := []struct {
name string
mockErr error
wantErr bool
}{
{
name: "success",
mockErr: nil,
wantErr: false,
},
{
name: "error",
mockErr: errors.New("test error"),
wantErr: true,
},
}
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.ResumePrinter("printer1"))
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().ResumePrinter("printer1").Return(tt.mockErr)
m := &Manager{
client: mockClient,
}
err := m.ResumePrinter("printer1")
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestManager_PurgeJobs(t *testing.T) {
@@ -185,11 +269,10 @@ func TestManager_PurgeJobs(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelAllJob("printer1", true).Return(tt.mockErr)
if !tt.wantErr {
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
}
m := NewTestManager(mockClient, nil)
m := &Manager{
client: mockClient,
}
err := m.PurgeJobs("printer1")
if tt.wantErr {
@@ -200,251 +283,3 @@ func TestManager_PurgeJobs(t *testing.T) {
})
}
}
func TestManager_GetDevices(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetDevices().Return(map[string]ipp.Attributes{
"usb://HP/LaserJet": {
"device-class": []ipp.Attribute{{Value: "direct"}},
"device-info": []ipp.Attribute{{Value: "HP LaserJet"}},
"device-make-and-model": []ipp.Attribute{{Value: "HP LaserJet 1020"}},
},
}, nil)
m := &Manager{client: mockClient}
got, err := m.GetDevices()
assert.NoError(t, err)
assert.Len(t, got, 1)
assert.Equal(t, "usb://HP/LaserJet", got[0].URI)
assert.Equal(t, "direct", got[0].Class)
}
func TestManager_GetPPDs(t *testing.T) {
tests := []struct {
name string
mockRet map[string]ipp.Attributes
mockErr error
want int
wantErr bool
}{
{
name: "success",
mockRet: map[string]ipp.Attributes{
"drv:///sample.drv/generic.ppd": {
"ppd-make-and-model": []ipp.Attribute{{Value: "Generic PostScript"}},
"ppd-type": []ipp.Attribute{{Value: "ppd"}},
},
},
mockErr: nil,
want: 1,
wantErr: false,
},
{
name: "error",
mockRet: nil,
mockErr: errors.New("test error"),
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetPPDs().Return(tt.mockRet, tt.mockErr)
m := &Manager{client: mockClient}
got, err := m.GetPPDs()
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.want, len(got))
})
}
}
func TestManager_GetClasses(t *testing.T) {
tests := []struct {
name string
mockRet map[string]ipp.Attributes
mockErr error
want int
wantErr bool
}{
{
name: "success",
mockRet: map[string]ipp.Attributes{
"office": {
ipp.AttributePrinterName: []ipp.Attribute{{Value: "office"}},
ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}},
ipp.AttributeMemberNames: []ipp.Attribute{{Value: "printer1"}, {Value: "printer2"}},
},
},
mockErr: nil,
want: 1,
wantErr: false,
},
{
name: "error",
mockRet: nil,
mockErr: errors.New("test error"),
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetClasses(mock.Anything).Return(tt.mockRet, tt.mockErr)
m := &Manager{client: mockClient}
got, err := m.GetClasses()
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.want, len(got))
if len(got) > 0 {
assert.Equal(t, "office", got[0].Name)
assert.Equal(t, 2, len(got[0].Members))
}
})
}
}
func TestManager_CreatePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location").Return(nil)
mockClient.EXPECT().ResumePrinter("newprinter").Return(nil)
mockClient.EXPECT().AcceptJobs("newprinter").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location"))
}
func TestManager_DeletePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.DeletePrinter("printer1"))
}
func TestManager_AcceptJobs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AcceptJobs("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.AcceptJobs("printer1"))
}
func TestManager_RejectJobs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RejectJobs("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.RejectJobs("printer1"))
}
func TestManager_SetPrinterShared(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterIsShared("printer1", true).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.SetPrinterShared("printer1", true))
}
func TestManager_SetPrinterLocation(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterLocation("printer1", "Office").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.SetPrinterLocation("printer1", "Office"))
}
func TestManager_SetPrinterInfo(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterInformation("printer1", "Main Printer").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.SetPrinterInfo("printer1", "Main Printer"))
}
func TestManager_MoveJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().MoveJob(1, "printer2").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
err := m.MoveJob(1, "printer2")
assert.NoError(t, err)
}
func TestManager_PrintTestPage(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PrintTestPage("printer1", mock.Anything, mock.Anything).Return(42, nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
jobID, err := m.PrintTestPage("printer1")
assert.NoError(t, err)
assert.Equal(t, 42, jobID)
}
func TestManager_AddPrinterToClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AddPrinterToClass("office", "printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.AddPrinterToClass("office", "printer1"))
}
func TestManager_RemovePrinterFromClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinterFromClass("office", "printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.RemovePrinterFromClass("office", "printer1"))
}
func TestManager_DeleteClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeleteClass("office").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.DeleteClass("office"))
}
func TestManager_RestartJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RestartJob(1).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.RestartJob(1))
}
func TestManager_HoldJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().HoldJobUntil(1, "indefinite").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
assert.NoError(t, m.HoldJob(1, "indefinite"))
}

View File

@@ -9,9 +9,9 @@ import (
)
type Request struct {
ID int `json:"id,omitempty"`
Method string `json:"method"`
Params map[string]any `json:"params,omitempty"`
ID int `json:"id,omitempty"`
Method string `json:"method"`
Params map[string]interface{} `json:"params,omitempty"`
}
type SuccessResult struct {
@@ -40,40 +40,6 @@ func HandleRequest(conn net.Conn, req Request, manager *Manager) {
handleCancelJob(conn, req, manager)
case "cups.purgeJobs":
handlePurgeJobs(conn, req, manager)
case "cups.getDevices":
handleGetDevices(conn, req, manager)
case "cups.getPPDs":
handleGetPPDs(conn, req, manager)
case "cups.getClasses":
handleGetClasses(conn, req, manager)
case "cups.createPrinter":
handleCreatePrinter(conn, req, manager)
case "cups.deletePrinter":
handleDeletePrinter(conn, req, manager)
case "cups.acceptJobs":
handleAcceptJobs(conn, req, manager)
case "cups.rejectJobs":
handleRejectJobs(conn, req, manager)
case "cups.setPrinterShared":
handleSetPrinterShared(conn, req, manager)
case "cups.setPrinterLocation":
handleSetPrinterLocation(conn, req, manager)
case "cups.setPrinterInfo":
handleSetPrinterInfo(conn, req, manager)
case "cups.moveJob":
handleMoveJob(conn, req, manager)
case "cups.printTestPage":
handlePrintTestPage(conn, req, manager)
case "cups.addPrinterToClass":
handleAddPrinterToClass(conn, req, manager)
case "cups.removePrinterFromClass":
handleRemovePrinterFromClass(conn, req, manager)
case "cups.deleteClass":
handleDeleteClass(conn, req, manager)
case "cups.restartJob":
handleRestartJob(conn, req, manager)
case "cups.holdJob":
handleHoldJob(conn, req, manager)
default:
models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
}
@@ -192,291 +158,3 @@ func handleSubscribe(conn net.Conn, req Request, manager *Manager) {
}
}
}
func handleGetDevices(conn net.Conn, req Request, manager *Manager) {
devices, err := manager.GetDevices()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, devices)
}
func handleGetPPDs(conn net.Conn, req Request, manager *Manager) {
ppds, err := manager.GetPPDs()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, ppds)
}
func handleGetClasses(conn net.Conn, req Request, manager *Manager) {
classes, err := manager.GetClasses()
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, classes)
}
func handleCreatePrinter(conn net.Conn, req Request, manager *Manager) {
name, ok := req.Params["name"].(string)
if !ok || name == "" {
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
return
}
deviceURI, ok := req.Params["deviceURI"].(string)
if !ok || deviceURI == "" {
models.RespondError(conn, req.ID, "missing or invalid 'deviceURI' parameter")
return
}
ppd, ok := req.Params["ppd"].(string)
if !ok || ppd == "" {
models.RespondError(conn, req.ID, "missing or invalid 'ppd' parameter")
return
}
shared, _ := req.Params["shared"].(bool)
errorPolicy, _ := req.Params["errorPolicy"].(string)
information, _ := req.Params["information"].(string)
location, _ := req.Params["location"].(string)
if err := manager.CreatePrinter(name, deviceURI, ppd, shared, errorPolicy, information, location); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "printer created"})
}
func handleDeletePrinter(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
if err := manager.DeletePrinter(printerName); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "printer deleted"})
}
func handleAcceptJobs(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
if err := manager.AcceptJobs(printerName); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "accepting jobs"})
}
func handleRejectJobs(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
if err := manager.RejectJobs(printerName); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "rejecting jobs"})
}
func handleSetPrinterShared(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
shared, ok := req.Params["shared"].(bool)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'shared' parameter")
return
}
if err := manager.SetPrinterShared(printerName, shared); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "sharing updated"})
}
func handleSetPrinterLocation(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
location, ok := req.Params["location"].(string)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'location' parameter")
return
}
if err := manager.SetPrinterLocation(printerName, location); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "location updated"})
}
func handleSetPrinterInfo(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
info, ok := req.Params["info"].(string)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'info' parameter")
return
}
if err := manager.SetPrinterInfo(printerName, info); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "info updated"})
}
func handleMoveJob(conn net.Conn, req Request, manager *Manager) {
jobIDFloat, ok := req.Params["jobID"].(float64)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'jobID' parameter")
return
}
destPrinter, ok := req.Params["destPrinter"].(string)
if !ok || destPrinter == "" {
models.RespondError(conn, req.ID, "missing or invalid 'destPrinter' parameter")
return
}
if err := manager.MoveJob(int(jobIDFloat), destPrinter); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "job moved"})
}
type TestPageResult struct {
Success bool `json:"success"`
JobID int `json:"jobId"`
Message string `json:"message"`
}
func handlePrintTestPage(conn net.Conn, req Request, manager *Manager) {
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
jobID, err := manager.PrintTestPage(printerName)
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, TestPageResult{Success: true, JobID: jobID, Message: "test page queued"})
}
func handleAddPrinterToClass(conn net.Conn, req Request, manager *Manager) {
className, ok := req.Params["className"].(string)
if !ok || className == "" {
models.RespondError(conn, req.ID, "missing or invalid 'className' parameter")
return
}
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
if err := manager.AddPrinterToClass(className, printerName); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "printer added to class"})
}
func handleRemovePrinterFromClass(conn net.Conn, req Request, manager *Manager) {
className, ok := req.Params["className"].(string)
if !ok || className == "" {
models.RespondError(conn, req.ID, "missing or invalid 'className' parameter")
return
}
printerName, ok := req.Params["printerName"].(string)
if !ok || printerName == "" {
models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter")
return
}
if err := manager.RemovePrinterFromClass(className, printerName); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "printer removed from class"})
}
func handleDeleteClass(conn net.Conn, req Request, manager *Manager) {
className, ok := req.Params["className"].(string)
if !ok || className == "" {
models.RespondError(conn, req.ID, "missing or invalid 'className' parameter")
return
}
if err := manager.DeleteClass(className); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "class deleted"})
}
func handleRestartJob(conn net.Conn, req Request, manager *Manager) {
jobIDFloat, ok := req.Params["jobID"].(float64)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'jobID' parameter")
return
}
if err := manager.RestartJob(int(jobIDFloat)); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "job restarted"})
}
func handleHoldJob(conn net.Conn, req Request, manager *Manager) {
jobIDFloat, ok := req.Params["jobID"].(float64)
if !ok {
models.RespondError(conn, req.ID, "missing or invalid 'jobID' parameter")
return
}
holdUntil, _ := req.Params["holdUntil"].(string)
if holdUntil == "" {
holdUntil = "indefinite"
}
if err := manager.HoldJob(int(jobIDFloat), holdUntil); err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "job held"})
}

View File

@@ -75,7 +75,7 @@ func TestHandleGetPrinters_Error(t *testing.T) {
handleGetPrinters(conn, req, m)
var resp models.Response[any]
var resp models.Response[interface{}]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.Nil(t, resp.Result)
@@ -103,7 +103,7 @@ func TestHandleGetJobs(t *testing.T) {
req := Request{
ID: 1,
Method: "cups.getJobs",
Params: map[string]any{
Params: map[string]interface{}{
"printerName": "printer1",
},
}
@@ -130,12 +130,12 @@ func TestHandleGetJobs_MissingParam(t *testing.T) {
req := Request{
ID: 1,
Method: "cups.getJobs",
Params: map[string]any{},
Params: map[string]interface{}{},
}
handleGetJobs(conn, req, m)
var resp models.Response[any]
var resp models.Response[interface{}]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.Nil(t, resp.Result)
@@ -145,9 +145,10 @@ func TestHandleGetJobs_MissingParam(t *testing.T) {
func TestHandlePausePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PausePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
m := &Manager{
client: mockClient,
}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
@@ -155,7 +156,7 @@ func TestHandlePausePrinter(t *testing.T) {
req := Request{
ID: 1,
Method: "cups.pausePrinter",
Params: map[string]any{
Params: map[string]interface{}{
"printerName": "printer1",
},
}
@@ -172,9 +173,10 @@ func TestHandlePausePrinter(t *testing.T) {
func TestHandleResumePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().ResumePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
m := &Manager{
client: mockClient,
}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
@@ -182,7 +184,7 @@ func TestHandleResumePrinter(t *testing.T) {
req := Request{
ID: 1,
Method: "cups.resumePrinter",
Params: map[string]any{
Params: map[string]interface{}{
"printerName": "printer1",
},
}
@@ -199,9 +201,10 @@ func TestHandleResumePrinter(t *testing.T) {
func TestHandleCancelJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelJob(1, false).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
m := &Manager{
client: mockClient,
}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
@@ -209,7 +212,7 @@ func TestHandleCancelJob(t *testing.T) {
req := Request{
ID: 1,
Method: "cups.cancelJob",
Params: map[string]any{
Params: map[string]interface{}{
"jobID": float64(1),
},
}
@@ -226,9 +229,10 @@ func TestHandleCancelJob(t *testing.T) {
func TestHandlePurgeJobs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CancelAllJob("printer1", true).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
m := &Manager{
client: mockClient,
}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
@@ -236,7 +240,7 @@ func TestHandlePurgeJobs(t *testing.T) {
req := Request{
ID: 1,
Method: "cups.purgeJobs",
Params: map[string]any{
Params: map[string]interface{}{
"printerName": "printer1",
},
}
@@ -267,445 +271,9 @@ func TestHandleRequest_UnknownMethod(t *testing.T) {
HandleRequest(conn, req, m)
var resp models.Response[any]
var resp models.Response[interface{}]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.Nil(t, resp.Result)
assert.NotNil(t, resp.Error)
}
func TestHandleGetDevices(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetDevices().Return(map[string]ipp.Attributes{
"usb://HP/LaserJet": {
"device-class": []ipp.Attribute{{Value: "direct"}},
"device-info": []ipp.Attribute{{Value: "HP LaserJet"}},
},
}, nil)
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{ID: 1, Method: "cups.getDevices"}
handleGetDevices(conn, req, m)
var resp models.Response[[]Device]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.Equal(t, 1, len(*resp.Result))
}
func TestHandleGetPPDs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetPPDs().Return(map[string]ipp.Attributes{
"generic.ppd": {
"ppd-make-and-model": []ipp.Attribute{{Value: "Generic"}},
},
}, nil)
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{ID: 1, Method: "cups.getPPDs"}
handleGetPPDs(conn, req, m)
var resp models.Response[[]PPD]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.Equal(t, 1, len(*resp.Result))
}
func TestHandleGetClasses(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().GetClasses(mock.Anything).Return(map[string]ipp.Attributes{
"office": {
ipp.AttributePrinterName: []ipp.Attribute{{Value: "office"}},
ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}},
},
}, nil)
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{ID: 1, Method: "cups.getClasses"}
handleGetClasses(conn, req, m)
var resp models.Response[[]PrinterClass]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.Equal(t, 1, len(*resp.Result))
}
func TestHandleCreatePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().CreatePrinter("newprinter", "usb://HP", "generic.ppd", false, "", "", "").Return(nil)
mockClient.EXPECT().ResumePrinter("newprinter").Return(nil)
mockClient.EXPECT().AcceptJobs("newprinter").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.createPrinter",
Params: map[string]any{
"name": "newprinter",
"deviceURI": "usb://HP",
"ppd": "generic.ppd",
},
}
handleCreatePrinter(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleCreatePrinter_MissingParams(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
m := &Manager{client: mockClient}
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{ID: 1, Method: "cups.createPrinter", Params: map[string]any{}}
handleCreatePrinter(conn, req, m)
var resp models.Response[any]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.Nil(t, resp.Result)
assert.NotNil(t, resp.Error)
}
func TestHandleDeletePrinter(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinter("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.deletePrinter",
Params: map[string]any{"printerName": "printer1"},
}
handleDeletePrinter(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleAcceptJobs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AcceptJobs("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.acceptJobs",
Params: map[string]any{"printerName": "printer1"},
}
handleAcceptJobs(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleRejectJobs(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RejectJobs("printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.rejectJobs",
Params: map[string]any{"printerName": "printer1"},
}
handleRejectJobs(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleSetPrinterShared(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterIsShared("printer1", true).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.setPrinterShared",
Params: map[string]any{"printerName": "printer1", "shared": true},
}
handleSetPrinterShared(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleSetPrinterLocation(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterLocation("printer1", "Office").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.setPrinterLocation",
Params: map[string]any{"printerName": "printer1", "location": "Office"},
}
handleSetPrinterLocation(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleSetPrinterInfo(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().SetPrinterInformation("printer1", "Main Printer").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.setPrinterInfo",
Params: map[string]any{"printerName": "printer1", "info": "Main Printer"},
}
handleSetPrinterInfo(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleMoveJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().MoveJob(1, "printer2").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.moveJob",
Params: map[string]any{"jobID": float64(1), "destPrinter": "printer2"},
}
handleMoveJob(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandlePrintTestPage(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().PrintTestPage("printer1", mock.Anything, mock.Anything).Return(42, nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.printTestPage",
Params: map[string]any{"printerName": "printer1"},
}
handlePrintTestPage(conn, req, m)
var resp models.Response[TestPageResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
assert.Equal(t, 42, resp.Result.JobID)
}
func TestHandleAddPrinterToClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().AddPrinterToClass("office", "printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.addPrinterToClass",
Params: map[string]any{"className": "office", "printerName": "printer1"},
}
handleAddPrinterToClass(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleRemovePrinterFromClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeletePrinterFromClass("office", "printer1").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.removePrinterFromClass",
Params: map[string]any{"className": "office", "printerName": "printer1"},
}
handleRemovePrinterFromClass(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleDeleteClass(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().DeleteClass("office").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.deleteClass",
Params: map[string]any{"className": "office"},
}
handleDeleteClass(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleRestartJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().RestartJob(1).Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.restartJob",
Params: map[string]any{"jobID": float64(1)},
}
handleRestartJob(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleHoldJob(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().HoldJobUntil(1, "indefinite").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.holdJob",
Params: map[string]any{"jobID": float64(1)},
}
handleHoldJob(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}
func TestHandleHoldJob_WithHoldUntil(t *testing.T) {
mockClient := mocks_cups.NewMockCUPSClientInterface(t)
mockClient.EXPECT().HoldJobUntil(1, "no-hold").Return(nil)
mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil)
m := NewTestManager(mockClient, nil)
buf := &bytes.Buffer{}
conn := &mockConn{Buffer: buf}
req := Request{
ID: 1,
Method: "cups.holdJob",
Params: map[string]any{"jobID": float64(1), "holdUntil": "no-hold"},
}
handleHoldJob(conn, req, m)
var resp models.Response[SuccessResult]
err := json.NewDecoder(buf).Decode(&resp)
assert.NoError(t, err)
assert.NotNil(t, resp.Result)
assert.True(t, resp.Result.Success)
}

View File

@@ -1,7 +1,6 @@
package cups
import (
"errors"
"fmt"
"os"
"strconv"
@@ -32,21 +31,11 @@ func NewManager() (*Manager, error) {
client := ipp.NewCUPSClient(host, port, username, password, false)
baseURL := fmt.Sprintf("http://%s:%d", host, port)
var pkHelper PkHelper
if isLocalCUPS(host) {
var err error
pkHelper, err = NewPkHelper()
if err != nil {
log.Warnf("[CUPS] Failed to initialize pkhelper: %v", err)
}
}
m := &Manager{
state: &CUPSState{
Printers: make(map[string]*Printer),
},
client: client,
pkHelper: pkHelper,
baseURL: baseURL,
stateMutex: sync.RWMutex{},
stopChan: make(chan struct{}),
@@ -109,12 +98,6 @@ func (m *Manager) eventHandler() {
func (m *Manager) updateState() error {
printers, err := m.GetPrinters()
if err != nil {
if isNoPrintersError(err) {
m.stateMutex.Lock()
m.state.Printers = make(map[string]*Printer)
m.stateMutex.Unlock()
return nil
}
return err
}
@@ -136,19 +119,6 @@ func (m *Manager) updateState() error {
return nil
}
func isNoPrintersError(err error) bool {
if err == nil {
return false
}
var ippErr ipp.IPPError
if errors.As(err, &ippErr) {
return ippErr.Status == 1030
}
return false
}
func (m *Manager) notifier() {
defer m.notifierWg.Done()
const minGap = 100 * time.Millisecond
@@ -200,14 +170,6 @@ func (m *Manager) notifySubscribers() {
}
}
func (m *Manager) RefreshState() {
if err := m.updateState(); err != nil {
log.Warnf("[CUPS] Failed to refresh state: %v", err)
return
}
m.notifySubscribers()
}
func (m *Manager) GetState() CUPSState {
return m.snapshotState()
}
@@ -294,7 +256,6 @@ func stateChanged(old, new *CUPSState) bool {
}
if oldPrinter.State != newPrinter.State ||
oldPrinter.StateReason != newPrinter.StateReason ||
oldPrinter.Accepting != newPrinter.Accepting ||
len(oldPrinter.Jobs) != len(newPrinter.Jobs) {
return true
}
@@ -373,18 +334,3 @@ func getBoolAttr(attrs ipp.Attributes, key string) bool {
}
return false
}
func getStringSliceAttr(attrs ipp.Attributes, key string) []string {
attr, ok := attrs[key]
if !ok {
return nil
}
result := make([]string, 0, len(attr))
for _, a := range attr {
if val, ok := a.Value.(string); ok {
result = append(result, val)
}
}
return result
}

View File

@@ -1,184 +0,0 @@
package cups
import (
"fmt"
"strings"
"github.com/godbus/dbus/v5"
)
const (
pkHelperDest = "org.opensuse.CupsPkHelper.Mechanism"
pkHelperPath = "/"
pkHelperInterface = "org.opensuse.CupsPkHelper.Mechanism"
)
type PkHelper interface {
DevicesGet(timeout, limit int, includeSchemes, excludeSchemes []string) ([]Device, error)
PrinterAdd(name, uri, ppd, info, location string) error
PrinterDelete(name string) error
PrinterSetEnabled(name string, enabled bool) error
PrinterSetAcceptJobs(name string, enabled bool, reason string) error
PrinterSetInfo(name, info string) error
PrinterSetLocation(name, location string) error
PrinterSetShared(name string, shared bool) error
ClassAddPrinter(className, printerName string) error
ClassDeletePrinter(className, printerName string) error
ClassDelete(className string) error
JobCancelPurge(jobID int, purge bool) error
JobRestart(jobID int) error
JobSetHoldUntil(jobID int, holdUntil string) error
}
type DBusPkHelper struct {
conn *dbus.Conn
obj dbus.BusObject
}
func NewPkHelper() (*DBusPkHelper, error) {
conn, err := dbus.SystemBus()
if err != nil {
return nil, fmt.Errorf("failed to connect to system bus: %w", err)
}
return &DBusPkHelper{
conn: conn,
obj: conn.Object(pkHelperDest, pkHelperPath),
}, nil
}
func (p *DBusPkHelper) DevicesGet(timeout, limit int, includeSchemes, excludeSchemes []string) ([]Device, error) {
if includeSchemes == nil {
includeSchemes = []string{}
}
if excludeSchemes == nil {
excludeSchemes = []string{}
}
var errStr string
var devicesMap map[string]string
call := p.obj.Call(pkHelperInterface+".DevicesGet", 0, int32(timeout), int32(limit), includeSchemes, excludeSchemes)
if call.Err != nil {
return nil, call.Err
}
if err := call.Store(&errStr, &devicesMap); err != nil {
return nil, err
}
if errStr != "" {
return nil, fmt.Errorf("%s", errStr)
}
return parseDevicesMap(devicesMap), nil
}
func parseDevicesMap(devicesMap map[string]string) []Device {
devicesByIndex := make(map[string]*Device)
for key, value := range devicesMap {
idx := strings.LastIndex(key, ":")
if idx == -1 {
continue
}
attr := key[:idx]
index := key[idx+1:]
dev, ok := devicesByIndex[index]
if !ok {
dev = &Device{}
devicesByIndex[index] = dev
}
switch attr {
case "device-uri":
dev.URI = value
case "device-class":
dev.Class = value
case "device-info":
dev.Info = value
case "device-make-and-model":
dev.MakeModel = value
case "device-id":
dev.ID = value
case "device-location":
dev.Location = value
}
}
devices := make([]Device, 0, len(devicesByIndex))
for _, dev := range devicesByIndex {
if dev.URI != "" {
devices = append(devices, *dev)
}
}
return devices
}
func (p *DBusPkHelper) PrinterAdd(name, uri, ppd, info, location string) error {
return p.callSimple("PrinterAdd", name, uri, ppd, info, location)
}
func (p *DBusPkHelper) PrinterDelete(name string) error {
return p.callSimple("PrinterDelete", name)
}
func (p *DBusPkHelper) PrinterSetEnabled(name string, enabled bool) error {
return p.callSimple("PrinterSetEnabled", name, enabled)
}
func (p *DBusPkHelper) PrinterSetAcceptJobs(name string, enabled bool, reason string) error {
return p.callSimple("PrinterSetAcceptJobs", name, enabled, reason)
}
func (p *DBusPkHelper) PrinterSetInfo(name, info string) error {
return p.callSimple("PrinterSetInfo", name, info)
}
func (p *DBusPkHelper) PrinterSetLocation(name, location string) error {
return p.callSimple("PrinterSetLocation", name, location)
}
func (p *DBusPkHelper) PrinterSetShared(name string, shared bool) error {
return p.callSimple("PrinterSetShared", name, shared)
}
func (p *DBusPkHelper) ClassAddPrinter(className, printerName string) error {
return p.callSimple("ClassAddPrinter", className, printerName)
}
func (p *DBusPkHelper) ClassDeletePrinter(className, printerName string) error {
return p.callSimple("ClassDeletePrinter", className, printerName)
}
func (p *DBusPkHelper) ClassDelete(className string) error {
return p.callSimple("ClassDelete", className)
}
func (p *DBusPkHelper) JobCancelPurge(jobID int, purge bool) error {
return p.callSimple("JobCancelPurge", int32(jobID), purge)
}
func (p *DBusPkHelper) JobRestart(jobID int) error {
return p.callSimple("JobRestart", int32(jobID))
}
func (p *DBusPkHelper) JobSetHoldUntil(jobID int, holdUntil string) error {
return p.callSimple("JobSetHoldUntil", int32(jobID), holdUntil)
}
func (p *DBusPkHelper) callSimple(method string, args ...any) error {
var errStr string
call := p.obj.Call(pkHelperInterface+"."+method, 0, args...)
if call.Err != nil {
return call.Err
}
if err := call.Store(&errStr); err != nil {
return err
}
if errStr != "" {
return fmt.Errorf("%s", errStr)
}
return nil
}

View File

@@ -1,95 +0,0 @@
package cups
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseDevicesMap(t *testing.T) {
tests := []struct {
name string
input map[string]string
wantLen int
wantURIs []string
}{
{
name: "empty",
input: map[string]string{},
wantLen: 0,
wantURIs: nil,
},
{
name: "single_device",
input: map[string]string{
"device-uri:0": "usb://HP/LaserJet",
"device-class:0": "direct",
"device-info:0": "HP LaserJet",
"device-make-and-model:0": "HP LaserJet 1020",
"device-id:0": "MFG:HP;MDL:LaserJet",
},
wantLen: 1,
wantURIs: []string{"usb://HP/LaserJet"},
},
{
name: "multiple_devices",
input: map[string]string{
"device-uri:0": "usb://HP/LaserJet",
"device-class:0": "direct",
"device-info:0": "HP LaserJet",
"device-uri:1": "socket://192.168.1.100",
"device-class:1": "network",
"device-info:1": "Network Printer",
},
wantLen: 2,
wantURIs: []string{"usb://HP/LaserJet", "socket://192.168.1.100"},
},
{
name: "malformed_key",
input: map[string]string{
"no-colon-here": "value",
},
wantLen: 0,
wantURIs: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := parseDevicesMap(tt.input)
assert.Len(t, got, tt.wantLen)
if tt.wantURIs != nil {
gotURIs := make(map[string]bool)
for _, d := range got {
gotURIs[d.URI] = true
}
for _, uri := range tt.wantURIs {
assert.True(t, gotURIs[uri], "expected URI %s not found", uri)
}
}
})
}
}
func TestParseDevicesMap_Attributes(t *testing.T) {
input := map[string]string{
"device-uri:0": "usb://HP/LaserJet",
"device-class:0": "direct",
"device-info:0": "HP LaserJet",
"device-make-and-model:0": "HP LaserJet 1020",
"device-id:0": "MFG:HP;MDL:LaserJet",
"device-location:0": "USB Port",
}
got := parseDevicesMap(input)
assert.Len(t, got, 1)
dev := got[0]
assert.Equal(t, "usb://HP/LaserJet", dev.URI)
assert.Equal(t, "direct", dev.Class)
assert.Equal(t, "HP LaserJet", dev.Info)
assert.Equal(t, "HP LaserJet 1020", dev.MakeModel)
assert.Equal(t, "MFG:HP;MDL:LaserJet", dev.ID)
assert.Equal(t, "USB Port", dev.Location)
}

View File

@@ -62,7 +62,7 @@ func (sm *SubscriptionManager) createSubscription() (int, error) {
req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms"
// Subscription attributes go in SubscriptionAttributes (subscription-attributes-tag in IPP)
req.SubscriptionAttributes = map[string]any{
req.SubscriptionAttributes = map[string]interface{}{
"notify-events": []string{
"printer-state-changed",
"printer-added",

View File

@@ -83,7 +83,7 @@ func (sm *DBusSubscriptionManager) createDBusSubscription() (int, error) {
req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL)
req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms"
req.SubscriptionAttributes = map[string]any{
req.SubscriptionAttributes = map[string]interface{}{
"notify-events": []string{
"printer-state-changed",
"printer-added",

View File

@@ -1,13 +0,0 @@
package cups
func NewTestManager(client CUPSClientInterface, pkHelper PkHelper) *Manager {
return &Manager{
client: client,
pkHelper: pkHelper,
state: &CUPSState{
Printers: make(map[string]*Printer),
},
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
}
}

View File

@@ -35,38 +35,9 @@ type Job struct {
TimeCreated time.Time `json:"timeCreated"`
}
type Device struct {
URI string `json:"uri"`
Class string `json:"class"`
Info string `json:"info"`
MakeModel string `json:"makeModel"`
ID string `json:"id"`
Location string `json:"location"`
}
type PPD struct {
Name string `json:"name"`
NaturalLanguage string `json:"naturalLanguage"`
MakeModel string `json:"makeModel"`
DeviceID string `json:"deviceId"`
Product string `json:"product"`
PSVersion string `json:"psVersion"`
Type string `json:"type"`
}
type PrinterClass struct {
Name string `json:"name"`
URI string `json:"uri"`
State string `json:"state"`
Members []string `json:"members"`
Location string `json:"location"`
Info string `json:"info"`
}
type Manager struct {
state *CUPSState
client CUPSClientInterface
pkHelper PkHelper
subscription SubscriptionManagerInterface
stateMutex sync.RWMutex
subscribers syncmap.Map[string, chan CUPSState]
@@ -92,24 +63,6 @@ type CUPSClientInterface interface {
ResumePrinter(printer string) error
CancelAllJob(printer string, purge bool) error
SendRequest(url string, req *ipp.Request, additionalResponseData io.Writer) (*ipp.Response, error)
GetDevices() (map[string]ipp.Attributes, error)
GetPPDs() (map[string]ipp.Attributes, error)
GetClasses(attributes []string) (map[string]ipp.Attributes, error)
CreatePrinter(name, deviceURI, ppd string, shared bool, errorPolicy, information, location string) error
DeletePrinter(printer string) error
AcceptJobs(printer string) error
RejectJobs(printer string) error
SetPrinterIsShared(printer string, shared bool) error
SetPrinterLocation(printer, location string) error
SetPrinterInformation(printer, information string) error
MoveJob(jobID int, destPrinter string) error
PrintTestPage(printer string, testPageData io.Reader, size int) (int, error)
AddPrinterToClass(class, printer string) error
DeletePrinterFromClass(class, printer string) error
DeleteClass(class string) error
RestartJob(jobID int) error
HoldJobUntil(jobID int, holdUntil string) error
}
type SubscriptionEvent struct {

View File

@@ -9,9 +9,9 @@ import (
)
type Request struct {
ID int `json:"id,omitempty"`
Method string `json:"method"`
Params map[string]any `json:"params,omitempty"`
ID int `json:"id,omitempty"`
Method string `json:"method"`
Params map[string]interface{} `json:"params,omitempty"`
}
type SuccessResult struct {

View File

@@ -100,8 +100,8 @@ func (m *Manager) setupRegistry() error {
log.Infof("DWL: found %s", dwl_ipc.ZdwlIpcManagerV2InterfaceName)
manager := dwl_ipc.NewZdwlIpcManagerV2(m.ctx)
version := e.Version
if version > 2 {
version = 2
if version > 1 {
version = 1
}
if err := registry.Bind(e.Name, e.Interface, version, manager); err == nil {
dwlMgr = manager
@@ -282,14 +282,6 @@ func (m *Manager) setupOutput(manager *dwl_ipc.ZdwlIpcManagerV2, output *wlclien
outState.layoutSymbol = e.Layout
})
ipcOutput.SetKbLayoutHandler(func(e dwl_ipc.ZdwlIpcOutputV2KbLayoutEvent) {
outState.kbLayout = e.KbLayout
})
ipcOutput.SetKeymodeHandler(func(e dwl_ipc.ZdwlIpcOutputV2KeymodeEvent) {
outState.keymode = e.Keymode
})
ipcOutput.SetFrameHandler(func(e dwl_ipc.ZdwlIpcOutputV2FrameEvent) {
m.updateState()
})
@@ -318,8 +310,6 @@ func (m *Manager) updateState() {
LayoutSymbol: out.layoutSymbol,
Title: out.title,
AppID: out.appID,
KbLayout: out.kbLayout,
Keymode: out.keymode,
}
if out.active != 0 {

View File

@@ -22,8 +22,6 @@ type OutputState struct {
LayoutSymbol string `json:"layoutSymbol"`
Title string `json:"title"`
AppID string `json:"appId"`
KbLayout string `json:"kbLayout"`
Keymode string `json:"keymode"`
}
type State struct {
@@ -41,7 +39,7 @@ type Manager struct {
display *wlclient.Display
ctx *wlclient.Context
registry *wlclient.Registry
manager any
manager interface{}
outputs syncmap.Map[uint32, *outputState]
@@ -67,7 +65,7 @@ type outputState struct {
id uint32
registryName uint32
output *wlclient.Output
ipcOutput any
ipcOutput interface{}
name string
active uint32
tags []TagState
@@ -75,8 +73,6 @@ type outputState struct {
layoutSymbol string
title string
appID string
kbLayout string
keymode string
}
func (m *Manager) GetState() State {
@@ -151,12 +147,6 @@ func stateChanged(old, new *State) bool {
if oldOut.AppID != newOut.AppID {
return true
}
if oldOut.KbLayout != newOut.KbLayout {
return true
}
if oldOut.Keymode != newOut.Keymode {
return true
}
if len(oldOut.Tags) != len(newOut.Tags) {
return true
}

View File

@@ -7,9 +7,9 @@ import (
)
type Request struct {
ID any `json:"id"`
Method string `json:"method"`
Params map[string]any `json:"params"`
ID interface{} `json:"id"`
Method string `json:"method"`
Params map[string]interface{} `json:"params"`
}
func HandleRequest(conn net.Conn, req Request, m *Manager) {

Some files were not shown because too many files have changed in this diff Show More