mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-22 03:04:01 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a87e8227d | ||
|
|
c0b26caf2b | ||
|
|
c6c40dbced | ||
|
|
02ddd010a0 | ||
|
|
6eb9df87cf | ||
|
|
0adbdfdde1 | ||
|
|
fb002d1977 | ||
|
|
8b7c9085ca | ||
|
|
8b34bbf71a | ||
|
|
fd468a6b78 | ||
|
|
7c04b61bcc | ||
|
|
bc4916a132 | ||
|
|
58a356bb05 | ||
|
|
3061e1301d | ||
|
|
e333da2013 | ||
|
|
307d05d76c | ||
|
|
10dbe73335 | ||
|
|
b6fee3a98f | ||
|
|
6166d51c7c | ||
|
|
d06fcccff9 | ||
|
|
d9cfeded47 | ||
|
|
c4ced3ac9d | ||
|
|
ce2ec80d80 | ||
|
|
a340eba493 |
@@ -520,37 +520,37 @@ jobs:
|
||||
# Pin the satellite repos to this release's version. Each dispatch target
|
||||
# decides for itself whether to tag: X.Y.0 always tags, X.Y.Z skips when the
|
||||
# repo has no commits of its own since its last tag.
|
||||
fanout:
|
||||
name: Pin ${{ matrix.repo }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: [release]
|
||||
if: ${{ !contains(inputs.tag, '-') }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
repo:
|
||||
- dgop
|
||||
- dankgo
|
||||
# fanout:
|
||||
# name: Pin ${{ matrix.repo }}
|
||||
# runs-on: ubuntu-latest
|
||||
# needs: [release]
|
||||
# if: ${{ !contains(inputs.tag, '-') }}
|
||||
# strategy:
|
||||
# fail-fast: false
|
||||
# matrix:
|
||||
# repo:
|
||||
# - dgop
|
||||
# - dankgo
|
||||
# - dank-qml-common
|
||||
# - dankcalendar
|
||||
- danksearch
|
||||
- dank-greeter
|
||||
steps:
|
||||
- name: Create GitHub App token
|
||||
id: app_token
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: ${{ matrix.repo }}
|
||||
|
||||
- name: Dispatch pin-release
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app_token.outputs.token }}
|
||||
REPO: ${{ github.repository_owner }}/${{ matrix.repo }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh workflow run pin-release.yml --repo "$REPO" -f version="$TAG"
|
||||
echo "dispatched pin-release ${TAG} to ${REPO}" >> "$GITHUB_STEP_SUMMARY"
|
||||
# - danksearch
|
||||
# - dank-greeter
|
||||
# steps:
|
||||
# - name: Create GitHub App token
|
||||
# id: app_token
|
||||
# uses: actions/create-github-app-token@v3
|
||||
# with:
|
||||
# app-id: ${{ secrets.APP_ID }}
|
||||
# private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
# owner: ${{ github.repository_owner }}
|
||||
# repositories: ${{ matrix.repo }}
|
||||
#
|
||||
# - name: Dispatch pin-release
|
||||
# env:
|
||||
# GH_TOKEN: ${{ steps.app_token.outputs.token }}
|
||||
# REPO: ${{ github.repository_owner }}/${{ matrix.repo }}
|
||||
# TAG: ${{ inputs.tag }}
|
||||
# run: |
|
||||
# set -euo pipefail
|
||||
# gh workflow run pin-release.yml --repo "$REPO" -f version="$TAG"
|
||||
# echo "dispatched pin-release ${TAG} to ${REPO}" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
[submodule "dank-qml-common"]
|
||||
path = dank-qml-common
|
||||
url = https://github.com/AvengeMedia/dank-qml-common.git
|
||||
branch = master
|
||||
branch = stable-1.6
|
||||
|
||||
@@ -557,7 +557,7 @@ func TestKittyConfigStructure(t *testing.T) {
|
||||
func TestKittyThemeConfigStructure(t *testing.T) {
|
||||
assert.Contains(t, KittyThemeConfig, "foreground #e0e2e8")
|
||||
assert.Contains(t, KittyThemeConfig, "background #101418")
|
||||
assert.Contains(t, KittyThemeConfig, "cursor #e0e2e8")
|
||||
assert.Contains(t, KittyThemeConfig, "cursor #9dcbfb")
|
||||
assert.Contains(t, KittyThemeConfig, "color0 #101418")
|
||||
assert.Contains(t, KittyThemeConfig, "color15 #ffffff")
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
cursor #e0e2e8
|
||||
cursor_text_color #c2c7cf
|
||||
cursor #9dcbfb
|
||||
cursor_text_color #003354
|
||||
|
||||
foreground #e0e2e8
|
||||
background #101418
|
||||
|
||||
@@ -3,21 +3,32 @@ package config
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// Mirrors Hyprland's shipped target. Before=graphical-session.target would cycle with the add-wants implicit After=dms.service.
|
||||
const hyprlandSessionTargetUnit = `[Unit]
|
||||
Description=Hyprland Session Target
|
||||
BindsTo=graphical-session.target
|
||||
Wants=graphical-session-pre.target
|
||||
After=graphical-session-pre.target
|
||||
PropagatesStopTo=graphical-session.target
|
||||
`
|
||||
|
||||
var staleHyprlandSessionTargetUnits = []string{
|
||||
`[Unit]
|
||||
Description=Hyprland Session Target
|
||||
Requires=graphical-session.target
|
||||
After=graphical-session.target
|
||||
`,
|
||||
`[Unit]
|
||||
Description=Hyprland Session Target
|
||||
BindsTo=graphical-session.target
|
||||
Before=graphical-session.target
|
||||
Wants=graphical-session-pre.target
|
||||
After=graphical-session-pre.target
|
||||
`
|
||||
|
||||
const legacyHyprlandSessionTargetUnit = `[Unit]
|
||||
Description=Hyprland Session Target
|
||||
Requires=graphical-session.target
|
||||
After=graphical-session.target
|
||||
`
|
||||
`,
|
||||
}
|
||||
|
||||
// Only a missing or dankinstall-written unit is replaced; hand-written units are left alone.
|
||||
func EnsureHyprlandSessionTarget() (string, error) {
|
||||
@@ -30,10 +41,7 @@ func EnsureHyprlandSessionTarget() (string, error) {
|
||||
existing, err := os.ReadFile(targetPath)
|
||||
if err == nil {
|
||||
content := string(existing)
|
||||
if content == hyprlandSessionTargetUnit {
|
||||
return targetPath, nil
|
||||
}
|
||||
if content != legacyHyprlandSessionTargetUnit {
|
||||
if content == hyprlandSessionTargetUnit || !slices.Contains(staleHyprlandSessionTargetUnits, content) {
|
||||
return targetPath, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,23 +31,25 @@ func TestEnsureHyprlandSessionTargetWritesMissing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureHyprlandSessionTargetUpgradesLegacy(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
t.Setenv("HOME", home)
|
||||
path := targetPathFor(t, home)
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(path, []byte(legacyHyprlandSessionTargetUnit), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
func TestEnsureHyprlandSessionTargetUpgradesStale(t *testing.T) {
|
||||
for _, stale := range staleHyprlandSessionTargetUnits {
|
||||
home := t.TempDir()
|
||||
t.Setenv("HOME", home)
|
||||
path := targetPathFor(t, home)
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(path, []byte(stale), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := EnsureHyprlandSessionTarget(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, _ := os.ReadFile(path)
|
||||
if string(data) != hyprlandSessionTargetUnit {
|
||||
t.Fatalf("legacy unit not upgraded:\n%s", data)
|
||||
if _, err := EnsureHyprlandSessionTarget(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, _ := os.ReadFile(path)
|
||||
if string(data) != hyprlandSessionTargetUnit {
|
||||
t.Fatalf("stale unit not upgraded:\n%s", data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package qmlchecks
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -55,3 +56,51 @@ func TestLockScreenPamSupportsManagedAndSystemPolicies(t *testing.T) {
|
||||
t.Fatalf("DMS-managed policy must generate the sanitized user PAM stack on Nix-store installs")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockScreenFprintProfileMatchesPamQml(t *testing.T) {
|
||||
profile, err := os.ReadFile("../../../quickshell/assets/pam/fprint")
|
||||
if err != nil {
|
||||
t.Fatalf("read fprint PAM profile: %v", err)
|
||||
}
|
||||
|
||||
qml, err := os.ReadFile("../../../quickshell/Modules/Lock/Pam.qml")
|
||||
if err != nil {
|
||||
t.Fatalf("read lock screen PAM QML: %v", err)
|
||||
}
|
||||
|
||||
// Pam.qml tells a pam_fprintd timeout expiry apart from a device fault by
|
||||
// how long the session ran, so a profile change that is not mirrored in
|
||||
// sessionTimeoutMs makes real faults look like planned renewals.
|
||||
profileTimeout := regexp.MustCompile(`pam_fprintd\.so[^\n]*\btimeout=(\d+)`).FindSubmatch(profile)
|
||||
if profileTimeout == nil {
|
||||
t.Fatalf("fprint profile must set an explicit pam_fprintd timeout")
|
||||
}
|
||||
qmlTimeout := regexp.MustCompile(`sessionTimeoutMs:\s*(\d+)`).FindSubmatch(qml)
|
||||
if qmlTimeout == nil {
|
||||
t.Fatalf("Pam.qml must declare sessionTimeoutMs")
|
||||
}
|
||||
|
||||
seconds, err := strconv.Atoi(string(profileTimeout[1]))
|
||||
if err != nil {
|
||||
t.Fatalf("parse profile timeout: %v", err)
|
||||
}
|
||||
millis, err := strconv.Atoi(string(qmlTimeout[1]))
|
||||
if err != nil {
|
||||
t.Fatalf("parse sessionTimeoutMs: %v", err)
|
||||
}
|
||||
if seconds*1000 != millis {
|
||||
t.Fatalf("sessionTimeoutMs is %dms but the fprint profile sets timeout=%d", millis, seconds)
|
||||
}
|
||||
|
||||
// fprintd 1.94.0 and older ignore this option when it is longer than two
|
||||
// digits and silently fall back to 30 seconds.
|
||||
if len(profileTimeout[1]) > 2 {
|
||||
t.Fatalf("pam_fprintd timeout=%s is too long for fprintd <= 1.94.0, which would ignore it", profileTimeout[1])
|
||||
}
|
||||
|
||||
// One finished session must equal one wrong finger, otherwise
|
||||
// SettingsData.maxFprintTries silently means a multiple of the scans it names.
|
||||
if !regexp.MustCompile(`pam_fprintd\.so[^\n]*\bmax-tries=1\b`).Match(profile) {
|
||||
t.Fatalf("fprint profile must use max-tries=1 so the shell counts wrong scans one for one")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,12 @@ const (
|
||||
propertiesIface = "org.freedesktop.DBus.Properties"
|
||||
)
|
||||
|
||||
// Sender-scoped: the system bus is shared process-wide, so unscoped rules wake
|
||||
// every other subsystem's signal channel too.
|
||||
var bluezMatchRules = [][]dbus.MatchOption{
|
||||
{dbus.WithMatchInterface(propertiesIface), dbus.WithMatchMember("PropertiesChanged")},
|
||||
{dbus.WithMatchInterface(objectMgrIface), dbus.WithMatchMember("InterfacesAdded")},
|
||||
{dbus.WithMatchInterface(objectMgrIface), dbus.WithMatchMember("InterfacesRemoved")},
|
||||
{dbus.WithMatchSender(bluezService), dbus.WithMatchInterface(propertiesIface), dbus.WithMatchMember("PropertiesChanged")},
|
||||
{dbus.WithMatchSender(bluezService), dbus.WithMatchInterface(objectMgrIface), dbus.WithMatchMember("InterfacesAdded")},
|
||||
{dbus.WithMatchSender(bluezService), dbus.WithMatchInterface(objectMgrIface), dbus.WithMatchMember("InterfacesRemoved")},
|
||||
}
|
||||
|
||||
func NewManager() (*Manager, error) {
|
||||
|
||||
@@ -19,7 +19,7 @@ func WaitForAdapter() error {
|
||||
conn.Signal(signals)
|
||||
defer conn.RemoveSignal(signals)
|
||||
|
||||
adapterAdded := []dbus.MatchOption{dbus.WithMatchInterface(objectMgrIface), dbus.WithMatchMember("InterfacesAdded")}
|
||||
adapterAdded := []dbus.MatchOption{dbus.WithMatchSender(bluezService), dbus.WithMatchInterface(objectMgrIface), dbus.WithMatchMember("InterfacesAdded")}
|
||||
if err := conn.AddMatchSignal(adapterAdded...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package brightness
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
@@ -20,13 +21,40 @@ const (
|
||||
DDCCI_VCP_SET = 0x03
|
||||
VCP_BRIGHTNESS = 0x10
|
||||
DDC_SOURCE_ADDR = 0x51
|
||||
EDID_ADDR = 0x50
|
||||
)
|
||||
|
||||
// ddcutil's DEFAULT_FLOCK_POLL_MILLISEC and DEFAULT_FLOCK_MAX_WAIT_MILLISEC (src/base/parms.h).
|
||||
const (
|
||||
ddcBusLockPoll = 100 * time.Millisecond
|
||||
ddcBusLockWait = 3 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
errDDCDisabled = errors.New("disabled via DMS_NO_DDC")
|
||||
errInvalidEDID = errors.New("invalid edid")
|
||||
errDisplayAsleep = errors.New("display asleep")
|
||||
errNoMonitor = errors.New("no monitor on bus")
|
||||
errBusReused = errors.New("bus belongs to another adapter")
|
||||
)
|
||||
|
||||
var ddcRereadDelays = []time.Duration{5 * time.Second, 10 * time.Second, 20 * time.Second}
|
||||
|
||||
func NewDDCBackend() (*DDCBackend, error) {
|
||||
return newDDCBackend(nil)
|
||||
}
|
||||
|
||||
func newDDCBackend(onReread func()) (*DDCBackend, error) {
|
||||
if os.Getenv("DMS_NO_DDC") != "" {
|
||||
return nil, errDDCDisabled
|
||||
}
|
||||
|
||||
b := &DDCBackend{
|
||||
scanInterval: 30 * time.Second,
|
||||
debounceTimers: make(map[string]*time.Timer),
|
||||
debouncePending: make(map[string]ddcPendingSet),
|
||||
onReread: onReread,
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
|
||||
if err := b.scanI2CDevices(); err != nil {
|
||||
@@ -53,6 +81,7 @@ func (b *DDCBackend) scanI2CDevicesInternal(force bool) error {
|
||||
}
|
||||
|
||||
activeBuses := make(map[int]bool)
|
||||
connectors := drmConnectorsByBus()
|
||||
|
||||
for i := range 32 {
|
||||
busPath := fmt.Sprintf("/dev/i2c-%d", i)
|
||||
@@ -70,18 +99,36 @@ func (b *DDCBackend) scanI2CDevicesInternal(force bool) error {
|
||||
|
||||
// Don't re-probe identified monitors: DDC traffic during a wake
|
||||
// sequence can disturb some monitors' own brightness handling.
|
||||
if _, ok := b.devices.Load(id); ok {
|
||||
if known, ok := b.devices.Load(id); ok {
|
||||
if known.adapter == getI2CDeviceSysfsName(i) {
|
||||
continue
|
||||
}
|
||||
b.devices.Delete(id)
|
||||
log.Debugf("removed DDC device %s (bus number reused by another adapter)", id)
|
||||
}
|
||||
|
||||
verdict := ddcBusVerdictFor(i, connectors)
|
||||
if verdict == ddcBusSkip {
|
||||
log.Debugf("i2c-%d: no monitor per DRM sysfs, not probing", i)
|
||||
continue
|
||||
}
|
||||
if ddcDisplayAsleep(i, connectors) {
|
||||
log.Debugf("i2c-%d: display asleep, not probing", i)
|
||||
continue
|
||||
}
|
||||
|
||||
dev, err := b.probeDDCDevice(i)
|
||||
dev, err := b.probeDDCDevice(i, verdict == ddcBusNeedsEDIDRead)
|
||||
if err != nil || dev == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
dev.id = id
|
||||
unread := dev.max == 0
|
||||
b.devices.Store(id, dev)
|
||||
log.Debugf("found DDC device on i2c-%d", i)
|
||||
if unread && b.onReread != nil {
|
||||
go b.rereadUntilKnown(dev)
|
||||
}
|
||||
}
|
||||
|
||||
b.devices.Range(func(id string, dev *ddcDevice) bool {
|
||||
@@ -97,53 +144,119 @@ func (b *DDCBackend) scanI2CDevicesInternal(force bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *DDCBackend) probeDDCDevice(bus int) (*ddcDevice, error) {
|
||||
busPath := fmt.Sprintf("/dev/i2c-%d", bus)
|
||||
|
||||
fd, err := syscall.Open(busPath, syscall.O_RDWR, 0)
|
||||
func (b *DDCBackend) probeDDCDevice(bus int, readEDID bool) (*ddcDevice, error) {
|
||||
fd, err := openDDCBus(bus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), I2C_SLAVE, uintptr(DDCCI_ADDR)); errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
dummy := make([]byte, 32)
|
||||
syscall.Read(fd, dummy) //nolint:errcheck
|
||||
|
||||
writebuf := []byte{DDC_SOURCE_ADDR, 0x80}
|
||||
writebuf = append(writebuf, ddcciChecksum(writebuf))
|
||||
n, err := syscall.Write(fd, writebuf)
|
||||
if err == nil && n == len(writebuf) {
|
||||
name := b.getDDCName(bus)
|
||||
dev := &ddcDevice{
|
||||
bus: bus,
|
||||
addr: DDCCI_ADDR,
|
||||
name: name,
|
||||
if readEDID {
|
||||
edid, err := readBusEDID(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isLaptopEDID(edid) {
|
||||
return nil, errors.New("laptop panel")
|
||||
}
|
||||
b.readInitialBrightness(fd, dev)
|
||||
return dev, nil
|
||||
}
|
||||
|
||||
readbuf := make([]byte, 4)
|
||||
n, err = syscall.Read(fd, readbuf)
|
||||
if err != nil || n == 0 {
|
||||
if err := setI2CAddr(fd, DDCCI_ADDR); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !detectX37(fd) {
|
||||
return nil, fmt.Errorf("x37 unresponsive")
|
||||
}
|
||||
|
||||
name := b.getDDCName(bus)
|
||||
|
||||
dev := &ddcDevice{
|
||||
bus: bus,
|
||||
addr: DDCCI_ADDR,
|
||||
name: name,
|
||||
bus: bus,
|
||||
addr: DDCCI_ADDR,
|
||||
name: b.getDDCName(bus),
|
||||
adapter: getI2CDeviceSysfsName(bus),
|
||||
}
|
||||
b.readInitialBrightness(fd, dev)
|
||||
return dev, nil
|
||||
}
|
||||
|
||||
// Based on ddcutil's i2c_open_bus() cross-instance lock (src/base/flock.c): ddcutil, other dms
|
||||
// processes and this one take turns on a bus.
|
||||
func openDDCBus(bus int) (int, error) {
|
||||
// Without O_CLOEXEC a process spawned mid-probe inherits the fd and the flock with it.
|
||||
fd, err := syscall.Open(fmt.Sprintf("/dev/i2c-%d", bus), syscall.O_RDWR|syscall.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
deadline := time.Now().Add(ddcBusLockWait)
|
||||
for {
|
||||
err = unix.Flock(fd, unix.LOCK_EX|unix.LOCK_NB)
|
||||
if err == nil {
|
||||
return fd, nil
|
||||
}
|
||||
if !errors.Is(err, unix.EWOULDBLOCK) || time.Now().After(deadline) {
|
||||
syscall.Close(fd)
|
||||
return -1, fmt.Errorf("lock i2c-%d: %w", bus, err)
|
||||
}
|
||||
time.Sleep(ddcBusLockPoll)
|
||||
}
|
||||
}
|
||||
|
||||
func setI2CAddr(fd, addr int) error {
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), I2C_SLAVE, uintptr(addr)); errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Based on ddcutil's i2c_detect_x37() (src/i2c/i2c_x37.c).
|
||||
func detectX37(fd int) bool {
|
||||
if n, err := syscall.Read(fd, make([]byte, 1)); err == nil && n == 1 {
|
||||
return true
|
||||
}
|
||||
_, err := syscall.Write(fd, nil)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Based on ddcutil's i2c_get_raw_edid_by_fd() with the fileio reader (src/i2c/i2c_edid.c).
|
||||
func readBusEDID(fd int) ([]byte, error) {
|
||||
if err := setI2CAddr(fd, EDID_ADDR); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var err error
|
||||
consecutiveEIO := 0
|
||||
for _, size := range []int{128, 128, 256, 256} {
|
||||
edid := make([]byte, size)
|
||||
if err = readEDIDOnce(fd, edid); err == nil {
|
||||
return edid[:128], nil
|
||||
}
|
||||
switch {
|
||||
case errors.Is(err, syscall.ENXIO), errors.Is(err, syscall.EOPNOTSUPP), errors.Is(err, syscall.ETIMEDOUT):
|
||||
return nil, err
|
||||
case errors.Is(err, syscall.EIO):
|
||||
consecutiveEIO++
|
||||
if consecutiveEIO >= 2 {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
consecutiveEIO = 0
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func readEDIDOnce(fd int, edid []byte) error {
|
||||
if _, err := syscall.Write(fd, []byte{0x00}); err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := syscall.Read(fd, edid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n < 128 || !isValidEDID(edid[:128]) {
|
||||
return errInvalidEDID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *DDCBackend) getDDCName(bus int) string {
|
||||
sysfsPath := fmt.Sprintf("/sys/class/i2c-adapter/i2c-%d/name", bus)
|
||||
data, err := os.ReadFile(sysfsPath)
|
||||
@@ -176,6 +289,63 @@ func (b *DDCBackend) readInitialBrightness(fd int, dev *ddcDevice) {
|
||||
}
|
||||
}
|
||||
|
||||
// Bounded and spaced: extra DDC traffic can upset some monitors (#2049).
|
||||
func (b *DDCBackend) rereadUntilKnown(dev *ddcDevice) {
|
||||
for _, delay := range ddcRereadDelays {
|
||||
select {
|
||||
case <-b.stop:
|
||||
return
|
||||
case <-time.After(delay):
|
||||
}
|
||||
read, retry := b.rereadBrightness(dev)
|
||||
if read {
|
||||
b.onReread()
|
||||
return
|
||||
}
|
||||
if !retry {
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Debugf("giving up on reading brightness for %s", dev.id)
|
||||
}
|
||||
|
||||
func (b *DDCBackend) rereadBrightness(dev *ddcDevice) (read, retry bool) {
|
||||
b.ioMutex.Lock()
|
||||
defer b.ioMutex.Unlock()
|
||||
|
||||
if current, ok := b.devices.Load(dev.id); !ok || current != dev || dev.max > 0 {
|
||||
return false, false
|
||||
}
|
||||
switch err := b.checkBusAlive(dev); {
|
||||
case errors.Is(err, errBusReused):
|
||||
return false, false
|
||||
case err != nil:
|
||||
return false, true
|
||||
}
|
||||
|
||||
fd, err := openDDCBus(dev.bus)
|
||||
if err != nil {
|
||||
log.Debugf("brightness reread skipped for %s: %v", dev.id, err)
|
||||
return false, true
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
if err := setI2CAddr(fd, dev.addr); err != nil {
|
||||
return false, false
|
||||
}
|
||||
|
||||
cap, err := b.getVCPFeature(fd, VCP_BRIGHTNESS)
|
||||
if err != nil {
|
||||
log.Debugf("brightness reread failed for %s: %v", dev.id, err)
|
||||
return false, true
|
||||
}
|
||||
|
||||
dev.max = cap.max
|
||||
dev.lastBrightness = cap.current
|
||||
log.Debugf("read %s brightness %d/%d", dev.id, cap.current, cap.max)
|
||||
return true, false
|
||||
}
|
||||
|
||||
func (b *DDCBackend) GetDevices() ([]Device, error) {
|
||||
if err := b.scanI2CDevices(); err != nil {
|
||||
log.Debugf("DDC scan error: %v", err)
|
||||
@@ -183,6 +353,8 @@ func (b *DDCBackend) GetDevices() ([]Device, error) {
|
||||
|
||||
devices := make([]Device, 0)
|
||||
|
||||
b.ioMutex.Lock()
|
||||
defer b.ioMutex.Unlock()
|
||||
b.devices.Range(func(id string, dev *ddcDevice) bool {
|
||||
devices = append(devices, Device{
|
||||
Class: ClassDDC,
|
||||
@@ -190,7 +362,7 @@ func (b *DDCBackend) GetDevices() ([]Device, error) {
|
||||
Name: dev.name,
|
||||
Current: dev.lastBrightness,
|
||||
Max: dev.max,
|
||||
CurrentPercent: dev.lastBrightness,
|
||||
CurrentPercent: ddcPercentFromRaw(dev.lastBrightness, dev.max),
|
||||
Backend: "ddc",
|
||||
})
|
||||
return true
|
||||
@@ -274,6 +446,9 @@ func (b *DDCBackend) setBrightnessImmediateWithExponent(id string, value int) er
|
||||
return fmt.Errorf("device not found: %s", id)
|
||||
}
|
||||
|
||||
b.ioMutex.Lock()
|
||||
defer b.ioMutex.Unlock()
|
||||
|
||||
busPath := fmt.Sprintf("/dev/i2c-%d", dev.bus)
|
||||
|
||||
if _, err := os.Stat(busPath); os.IsNotExist(err) {
|
||||
@@ -282,52 +457,85 @@ func (b *DDCBackend) setBrightnessImmediateWithExponent(id string, value int) er
|
||||
return fmt.Errorf("device disconnected: %s", id)
|
||||
}
|
||||
|
||||
fd, err := syscall.Open(busPath, syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
switch err := b.checkBusAlive(dev); {
|
||||
case errors.Is(err, errBusReused):
|
||||
b.devices.Delete(id)
|
||||
log.Debugf("removed DDC device %s (bus number reused by another adapter)", id)
|
||||
return err
|
||||
case err != nil:
|
||||
return err
|
||||
}
|
||||
|
||||
fd, err := openDDCBus(dev.bus)
|
||||
switch {
|
||||
case errors.Is(err, unix.EWOULDBLOCK):
|
||||
return err
|
||||
case err != nil:
|
||||
b.devices.Delete(id)
|
||||
log.Debugf("removed DDC device %s (open failed: %v)", id, err)
|
||||
return fmt.Errorf("open i2c device: %w", err)
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), I2C_SLAVE, uintptr(dev.addr)); errno != 0 {
|
||||
return fmt.Errorf("set i2c slave addr: %w", errno)
|
||||
if err := setI2CAddr(fd, dev.addr); err != nil {
|
||||
return fmt.Errorf("set i2c slave addr: %w", err)
|
||||
}
|
||||
|
||||
max := dev.max
|
||||
if max == 0 {
|
||||
maxValue := dev.max
|
||||
if maxValue == 0 {
|
||||
cap, err := b.getVCPFeature(fd, VCP_BRIGHTNESS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current capability: %w", err)
|
||||
}
|
||||
max = cap.max
|
||||
dev.max = max
|
||||
b.devices.Store(id, dev)
|
||||
maxValue = cap.max
|
||||
}
|
||||
if maxValue <= 0 {
|
||||
return fmt.Errorf("%s reported max brightness %d", id, maxValue)
|
||||
}
|
||||
|
||||
if err := b.setVCPFeature(fd, VCP_BRIGHTNESS, value); err != nil {
|
||||
raw := ddcRawFromPercent(value, maxValue)
|
||||
if err := b.setVCPFeature(fd, VCP_BRIGHTNESS, raw); err != nil {
|
||||
return fmt.Errorf("set vcp feature: %w", err)
|
||||
}
|
||||
|
||||
log.Debugf("set %s to %d/%d", id, value, max)
|
||||
log.Debugf("set %s to %d/%d", id, raw, maxValue)
|
||||
|
||||
dev.max = max
|
||||
dev.lastBrightness = value
|
||||
dev.max = maxValue
|
||||
dev.lastBrightness = raw
|
||||
b.devices.Store(id, dev)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *DDCBackend) getVCPFeature(fd int, vcp byte) (*ddcCapability, error) {
|
||||
for range 3 {
|
||||
dummy := make([]byte, 32)
|
||||
n, _ := syscall.Read(fd, dummy)
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
// Based on ddcutil's i2c_check_open_bus_alive() (src/i2c/i2c_bus_core.c), sysfs side only. Known
|
||||
// devices are keyed by bus number, which the kernel hands to the next adapter once one goes away.
|
||||
func (b *DDCBackend) checkBusAlive(dev *ddcDevice) error {
|
||||
if isIgnorableI2CBus(dev.bus) || getI2CDeviceSysfsName(dev.bus) != dev.adapter {
|
||||
return errBusReused
|
||||
}
|
||||
connectors := drmConnectorsByBus()
|
||||
if ddcBusVerdictFor(dev.bus, connectors) == ddcBusSkip {
|
||||
return errNoMonitor
|
||||
}
|
||||
if ddcDisplayAsleep(dev.bus, connectors) {
|
||||
return errDisplayAsleep
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ddcRawFromPercent(percent, maxValue int) int {
|
||||
percent = min(max(percent, 0), 100)
|
||||
return int(math.Round(float64(percent*maxValue) / 100))
|
||||
}
|
||||
|
||||
func ddcPercentFromRaw(raw, maxValue int) int {
|
||||
if maxValue <= 0 {
|
||||
return 0
|
||||
}
|
||||
return min(int(math.Round(float64(raw*100)/float64(maxValue))), 100)
|
||||
}
|
||||
|
||||
func (b *DDCBackend) getVCPFeature(fd int, vcp byte) (*ddcCapability, error) {
|
||||
data := []byte{
|
||||
DDCCI_VCP_GET,
|
||||
vcp,
|
||||
@@ -347,25 +555,6 @@ func (b *DDCBackend) getVCPFeature(fd int, vcp byte) (*ddcCapability, error) {
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
pollFds := []unix.PollFd{
|
||||
{
|
||||
Fd: int32(fd),
|
||||
Events: unix.POLLIN,
|
||||
},
|
||||
}
|
||||
|
||||
pollTimeout := 200
|
||||
pollResult, err := unix.Poll(pollFds, pollTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("poll i2c: %w", err)
|
||||
}
|
||||
if pollResult == 0 {
|
||||
return nil, fmt.Errorf("poll timeout after %dms", pollTimeout)
|
||||
}
|
||||
if pollFds[0].Revents&unix.POLLIN == 0 {
|
||||
return nil, fmt.Errorf("poll returned but POLLIN not set")
|
||||
}
|
||||
|
||||
response := make([]byte, 12)
|
||||
n, err = syscall.Read(fd, response)
|
||||
if err != nil || n < 8 {
|
||||
@@ -520,4 +709,8 @@ func (b *DDCBackend) WaitPending() {
|
||||
}
|
||||
|
||||
func (b *DDCBackend) Close() {
|
||||
if b.stop == nil {
|
||||
return
|
||||
}
|
||||
b.closeOnce.Do(func() { close(b.stop) })
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package brightness
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -11,9 +14,12 @@ import (
|
||||
)
|
||||
|
||||
// isIgnorableI2CBus checks if an I2C bus should be skipped during DDC probing.
|
||||
// Based on ddcutil's sysfs_is_ignorable_i2c_device() (sysfs_base.c:1441)
|
||||
// Based on ddcutil's sysfs_is_ignorable_i2c_device() (src/sysfs/sysfs_simple.c)
|
||||
func isIgnorableI2CBus(busno int) bool {
|
||||
name := getI2CDeviceSysfsName(busno)
|
||||
if name == "DPMST" {
|
||||
return false
|
||||
}
|
||||
driver := getI2CSysfsDriver(busno)
|
||||
|
||||
if name != "" && isIgnorableI2CDeviceName(name, driver) {
|
||||
@@ -23,19 +29,20 @@ func isIgnorableI2CBus(busno int) bool {
|
||||
|
||||
// Only probe display adapters (0x03xxxx) and docking stations (0x0axxxx)
|
||||
class := getI2CDeviceSysfsClass(busno)
|
||||
if class != 0 {
|
||||
classHigh := class & 0xFFFF0000
|
||||
ignorable := (classHigh != 0x030000 && classHigh != 0x0A0000)
|
||||
if ignorable {
|
||||
log.Debugf("i2c-%d: ignoring class 0x%08x", busno, class)
|
||||
}
|
||||
return ignorable
|
||||
if class == 0 {
|
||||
// No PCI class says nothing about a platform adapter, but a real adapter always has a name.
|
||||
return name == ""
|
||||
}
|
||||
|
||||
return false
|
||||
classHigh := class & 0xFFFF0000
|
||||
ignorable := (classHigh != 0x030000 && classHigh != 0x0A0000)
|
||||
if ignorable {
|
||||
log.Debugf("i2c-%d: ignoring class 0x%08x", busno, class)
|
||||
}
|
||||
return ignorable
|
||||
}
|
||||
|
||||
// Based on ddcutil's ignorable_i2c_device_sysfs_name() (sysfs_base.c:1408)
|
||||
// Based on ddcutil's ignorable_i2c_device_sysfs_name() (src/sysfs/sysfs_simple.c)
|
||||
func isIgnorableI2CDeviceName(name, driver string) bool {
|
||||
ignorablePrefixes := []string{
|
||||
"SMBus",
|
||||
@@ -44,7 +51,8 @@ func isIgnorableI2CDeviceName(name, driver string) bool {
|
||||
"smu",
|
||||
"mac-io",
|
||||
"u4",
|
||||
"AMDGPU SMU", // AMD Navi2+ - probing hangs GPU
|
||||
"AMDGPU SMU", // AMD Navi2+ - probing hangs GPU
|
||||
"AMDGPU DM i2c OEM bus", // RGB controllers, not displays
|
||||
}
|
||||
|
||||
for _, prefix := range ignorablePrefixes {
|
||||
@@ -71,65 +79,194 @@ func getI2CDeviceSysfsName(busno int) string {
|
||||
return strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
// Based on ddcutil's get_i2c_device_sysfs_class() (sysfs_base.c:1380)
|
||||
// Based on ddcutil's get_i2c_device_sysfs_class() (src/sysfs/sysfs_simple.c)
|
||||
func getI2CDeviceSysfsClass(busno int) uint32 {
|
||||
classPath := fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d/device/class", busno)
|
||||
data, err := os.ReadFile(classPath)
|
||||
if err != nil {
|
||||
classPath = fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d/device/device/device/class", busno)
|
||||
data, err = os.ReadFile(classPath)
|
||||
paths := []string{
|
||||
fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d", busno),
|
||||
fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d/device", busno),
|
||||
fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d/i2c-dev/i2c-%d/device", busno, busno),
|
||||
}
|
||||
for _, path := range paths {
|
||||
adapter := findI2CAdapter(path)
|
||||
if adapter == "" {
|
||||
continue
|
||||
}
|
||||
data, err := os.ReadFile(filepath.Join(adapter, "class"))
|
||||
if err != nil {
|
||||
return 0
|
||||
continue
|
||||
}
|
||||
class, err := strconv.ParseUint(strings.TrimPrefix(strings.TrimSpace(string(data)), "0x"), 16, 32)
|
||||
if err == nil && class != 0 {
|
||||
return uint32(class)
|
||||
}
|
||||
}
|
||||
|
||||
classStr := strings.TrimSpace(string(data))
|
||||
classStr = strings.TrimPrefix(classStr, "0x")
|
||||
|
||||
class, err := strconv.ParseUint(classStr, 16, 32)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return uint32(class)
|
||||
return 0
|
||||
}
|
||||
|
||||
// Based on ddcutil's get_i2c_sysfs_driver_by_busno() (sysfs_base.c:1284)
|
||||
// Based on ddcutil's get_driver_for_busno() (src/sysfs/sysfs_simple.c)
|
||||
func getI2CSysfsDriver(busno int) string {
|
||||
devicePath := fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d", busno)
|
||||
adapterPath, err := findI2CAdapter(devicePath)
|
||||
adapter := findI2CAdapter(fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d", busno))
|
||||
if adapter == "" {
|
||||
return ""
|
||||
}
|
||||
module, err := filepath.EvalSymlinks(filepath.Join(adapter, "driver", "module"))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return filepath.Base(module)
|
||||
}
|
||||
|
||||
driverLink := filepath.Join(adapterPath, "driver")
|
||||
target, err := os.Readlink(driverLink)
|
||||
// Based on ddcutil's sysfs_find_adapter() (src/sysfs/sysfs_simple.c): the nearest
|
||||
// ancestor under /sys/devices carrying a class attribute.
|
||||
func findI2CAdapter(path string) string {
|
||||
resolved, err := filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return filepath.Base(target)
|
||||
}
|
||||
|
||||
func findI2CAdapter(devicePath string) (string, error) {
|
||||
currentPath := devicePath
|
||||
|
||||
for range 10 {
|
||||
if _, err := os.Stat(filepath.Join(currentPath, "name")); err == nil {
|
||||
return currentPath, nil
|
||||
for strings.HasPrefix(resolved, "/sys/devices") {
|
||||
if info, err := os.Stat(filepath.Join(resolved, "class")); err == nil && info.Mode().IsRegular() {
|
||||
return resolved
|
||||
}
|
||||
|
||||
deviceLink := filepath.Join(currentPath, "device")
|
||||
target, err := os.Readlink(deviceLink)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(target) {
|
||||
target = filepath.Join(filepath.Dir(currentPath), target)
|
||||
}
|
||||
currentPath = filepath.Clean(target)
|
||||
resolved = filepath.Dir(resolved)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not find adapter for %s", devicePath)
|
||||
// ddcutil's known_reliable_drivers (src/sysfs/sysfs_base.c): drivers that keep the DRM connector
|
||||
// edid, status and dpms attributes current.
|
||||
var sysfsReliableDrivers = []string{"i915", "xe", "amdgpu", "radeon", "nouveau"}
|
||||
|
||||
var (
|
||||
drmConnectorPattern = regexp.MustCompile(`^card[0-9]+-`)
|
||||
i2cBusPattern = regexp.MustCompile(`^i2c-([0-9]+)$`)
|
||||
)
|
||||
|
||||
type ddcBusVerdict int
|
||||
|
||||
const (
|
||||
ddcBusSkip ddcBusVerdict = iota
|
||||
ddcBusHasEDID
|
||||
ddcBusNeedsEDIDRead
|
||||
)
|
||||
|
||||
// Based on ddcutil's i2c_edid_exists() and its laptop panel exclusion (src/i2c/i2c_bus_core.c).
|
||||
func ddcBusVerdictFor(busno int, connectors map[int]string) ddcBusVerdict {
|
||||
name := getI2CDeviceSysfsName(busno)
|
||||
displayLink := name == "DisplayLink I2C Adapter"
|
||||
reliable := slices.Contains(sysfsReliableDrivers, getI2CSysfsDriver(busno))
|
||||
|
||||
connector, mapped := connectors[busno]
|
||||
if !mapped {
|
||||
if reliable && !displayLink && name != "DPMST" && len(connectors) > 0 {
|
||||
return ddcBusSkip
|
||||
}
|
||||
return ddcBusNeedsEDIDRead
|
||||
}
|
||||
if strings.Contains(connector, "-eDP-") || strings.Contains(connector, "-LVDS-") {
|
||||
return ddcBusSkip
|
||||
}
|
||||
if reliable || displayLink {
|
||||
if len(drmConnectorAttr(connector, "edid")) >= 128 {
|
||||
return ddcBusHasEDID
|
||||
}
|
||||
return ddcBusSkip
|
||||
}
|
||||
if strings.TrimSpace(drmConnectorAttr(connector, "status")) == "disconnected" {
|
||||
return ddcBusSkip
|
||||
}
|
||||
return ddcBusNeedsEDIDRead
|
||||
}
|
||||
|
||||
// Based on ddcutil's dpms_check_drm_asleep_by_businfo() (src/sysfs/sysfs_dpms.c).
|
||||
func ddcDisplayAsleep(busno int, connectors map[int]string) bool {
|
||||
connector, mapped := connectors[busno]
|
||||
if !mapped || !slices.Contains(sysfsReliableDrivers, getI2CSysfsDriver(busno)) {
|
||||
return false
|
||||
}
|
||||
return strings.TrimSpace(drmConnectorAttr(connector, "dpms")) != "On"
|
||||
}
|
||||
|
||||
// Based on ddcutil's get_connector_bus_numbers() (src/sysfs/sysfs_simple.c): a DP connector's own
|
||||
// i2c-N directory, any other connector's ddc/i2c-dev/i2c-N.
|
||||
func drmConnectorsByBus() map[int]string {
|
||||
connectors := map[int]string{}
|
||||
entries, err := os.ReadDir("/sys/class/drm")
|
||||
if err != nil {
|
||||
return connectors
|
||||
}
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
if !drmConnectorPattern.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
dir := filepath.Join("/sys/class/drm", name)
|
||||
if !strings.Contains(name, "-DP-") {
|
||||
dir = filepath.Join(dir, "ddc", "i2c-dev")
|
||||
}
|
||||
if busno, ok := i2cSubdirBus(dir); ok {
|
||||
connectors[busno] = name
|
||||
}
|
||||
}
|
||||
return connectors
|
||||
}
|
||||
|
||||
func i2cSubdirBus(dir string) (int, bool) {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
for _, entry := range entries {
|
||||
match := i2cBusPattern.FindStringSubmatch(entry.Name())
|
||||
if match == nil {
|
||||
continue
|
||||
}
|
||||
if busno, err := strconv.Atoi(match[1]); err == nil {
|
||||
return busno, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func drmConnectorAttr(connector, attr string) string {
|
||||
data, err := os.ReadFile(filepath.Join("/sys/class/drm", connector, attr))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
var edidHeader = []byte{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}
|
||||
|
||||
// Based on ddcutil's is_valid_raw_edid() (src/util/edid.c).
|
||||
func isValidEDID(edid []byte) bool {
|
||||
if len(edid) < 128 || !bytes.Equal(edid[:8], edidHeader) {
|
||||
return false
|
||||
}
|
||||
var sum byte
|
||||
for _, b := range edid[:128] {
|
||||
sum += b
|
||||
}
|
||||
return sum == 0
|
||||
}
|
||||
|
||||
// Based on ddcutil's is_laptop_parsed_edid() (src/util/edid.c).
|
||||
func isLaptopEDID(edid []byte) bool {
|
||||
return edidDescriptorText(edid, 0xfc) == "" && edidDescriptorText(edid, 0xff) == ""
|
||||
}
|
||||
|
||||
// Based on ddcutil's get_edid_descriptor_strings() (src/util/edid.c).
|
||||
func edidDescriptorText(edid []byte, tag byte) string {
|
||||
text := ""
|
||||
for i := range 4 {
|
||||
descriptor := edid[54+i*18 : 72+i*18]
|
||||
if descriptor[0] != 0 || descriptor[1] != 0 || descriptor[2] != 0 || descriptor[4] != 0 || descriptor[3] != tag {
|
||||
continue
|
||||
}
|
||||
raw := descriptor[5:18]
|
||||
if end := bytes.IndexByte(raw, 0x0a); end >= 0 {
|
||||
raw = raw[:end]
|
||||
}
|
||||
text = strings.TrimRight(string(raw), " \t\n\v\f\r")
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package brightness
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -42,12 +42,12 @@ func (m *Manager) initLogind() {
|
||||
}
|
||||
|
||||
func (m *Manager) initDDC() {
|
||||
if os.Getenv("DMS_NO_DDC") != "" {
|
||||
ddc, err := newDDCBackend(m.updateState)
|
||||
switch {
|
||||
case errors.Is(err, errDDCDisabled):
|
||||
log.Info("DDC backend disabled via DMS_NO_DDC")
|
||||
return
|
||||
}
|
||||
ddc, err := NewDDCBackend()
|
||||
if err != nil {
|
||||
case err != nil:
|
||||
log.Debugf("Failed to initialize DDC backend: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -96,6 +96,11 @@ type DDCBackend struct {
|
||||
debounceTimers map[string]*time.Timer
|
||||
debouncePending map[string]ddcPendingSet
|
||||
debounceWg sync.WaitGroup
|
||||
|
||||
ioMutex sync.Mutex
|
||||
onReread func()
|
||||
stop chan struct{}
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
type ddcPendingSet struct {
|
||||
@@ -108,6 +113,7 @@ type ddcDevice struct {
|
||||
addr int
|
||||
id string
|
||||
name string
|
||||
adapter string
|
||||
max int
|
||||
lastBrightness int
|
||||
}
|
||||
|
||||
@@ -154,23 +154,7 @@ func (b *IWDBackend) applyManagedObjects(objects map[dbus.ObjectPath]map[string]
|
||||
}
|
||||
if _, hasDevice := interfaces[iwdDeviceInterface]; hasDevice {
|
||||
b.devicePath = path
|
||||
|
||||
if devProps, ok := interfaces[iwdDeviceInterface]; ok {
|
||||
if nameVar, ok := devProps["Name"]; ok {
|
||||
if name, ok := nameVar.Value().(string); ok {
|
||||
b.stateMutex.Lock()
|
||||
b.state.WiFiDevice = name
|
||||
b.stateMutex.Unlock()
|
||||
}
|
||||
}
|
||||
if poweredVar, ok := devProps["Powered"]; ok {
|
||||
if powered, ok := poweredVar.Value().(bool); ok {
|
||||
b.stateMutex.Lock()
|
||||
b.state.WiFiEnabled = powered
|
||||
b.stateMutex.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
b.applyDeviceProps(interfaces[iwdDeviceInterface])
|
||||
}
|
||||
if _, hasAdapter := interfaces[iwdAdapterInterface]; hasAdapter {
|
||||
b.adapterPath = path
|
||||
@@ -193,6 +177,47 @@ func (b *IWDBackend) applyManagedObjects(objects map[dbus.ObjectPath]map[string]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *IWDBackend) applyDeviceProps(props map[string]dbus.Variant) {
|
||||
if nameVar, ok := props["Name"]; ok {
|
||||
if name, ok := nameVar.Value().(string); ok {
|
||||
b.stateMutex.Lock()
|
||||
b.state.WiFiDevice = name
|
||||
b.stateMutex.Unlock()
|
||||
}
|
||||
}
|
||||
if poweredVar, ok := props["Powered"]; ok {
|
||||
if powered, ok := poweredVar.Value().(bool); ok {
|
||||
b.stateMutex.Lock()
|
||||
b.state.WiFiEnabled = powered
|
||||
b.stateMutex.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setDevice records a device that appeared at a new path, which is what
|
||||
// happens when iwd restarts and recreates wlan0. It returns the path it
|
||||
// replaced so the caller can move the signal match over.
|
||||
func (b *IWDBackend) setDevice(path dbus.ObjectPath, props map[string]dbus.Variant) (prev dbus.ObjectPath, changed bool) {
|
||||
if path == "" || path == b.devicePath {
|
||||
return "", false
|
||||
}
|
||||
prev = b.devicePath
|
||||
b.devicePath = path
|
||||
b.applyDeviceProps(props)
|
||||
return prev, true
|
||||
}
|
||||
|
||||
func (b *IWDBackend) clearDevice(path dbus.ObjectPath) bool {
|
||||
if path == "" || path != b.devicePath {
|
||||
return false
|
||||
}
|
||||
b.devicePath = ""
|
||||
b.stateMutex.Lock()
|
||||
b.state.WiFiEnabled = false
|
||||
b.stateMutex.Unlock()
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *IWDBackend) GetCurrentState() (*BackendState, error) {
|
||||
b.stateMutex.RLock()
|
||||
defer b.stateMutex.RUnlock()
|
||||
|
||||
@@ -26,12 +26,7 @@ func (b *IWDBackend) StartMonitoring(onStateChange func()) error {
|
||||
b.conn.Signal(sigChan)
|
||||
|
||||
if b.devicePath != "" {
|
||||
err := b.conn.AddMatchSignal(
|
||||
dbus.WithMatchObjectPath(b.devicePath),
|
||||
dbus.WithMatchInterface(dbusPropertiesInterface),
|
||||
dbus.WithMatchMember("PropertiesChanged"),
|
||||
)
|
||||
if err != nil {
|
||||
if err := b.addDeviceSignalMatch(b.devicePath); err != nil {
|
||||
return fmt.Errorf("failed to add device signal match: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -86,6 +81,48 @@ func (b *IWDBackend) addStationSignalMatch(path dbus.ObjectPath) error {
|
||||
)
|
||||
}
|
||||
|
||||
func (b *IWDBackend) addDeviceSignalMatch(path dbus.ObjectPath) error {
|
||||
return b.conn.AddMatchSignal(
|
||||
dbus.WithMatchObjectPath(path),
|
||||
dbus.WithMatchInterface(dbusPropertiesInterface),
|
||||
dbus.WithMatchMember("PropertiesChanged"),
|
||||
)
|
||||
}
|
||||
|
||||
func (b *IWDBackend) removeDeviceSignalMatch(path dbus.ObjectPath) {
|
||||
err := b.conn.RemoveMatchSignal(
|
||||
dbus.WithMatchObjectPath(path),
|
||||
dbus.WithMatchInterface(dbusPropertiesInterface),
|
||||
dbus.WithMatchMember("PropertiesChanged"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to remove iwd device signal match for %s: %v", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *IWDBackend) handleDeviceAdded(path dbus.ObjectPath, props map[string]dbus.Variant) bool {
|
||||
prev, changed := b.setDevice(path, props)
|
||||
if !changed {
|
||||
return false
|
||||
}
|
||||
|
||||
if prev != "" {
|
||||
b.removeDeviceSignalMatch(prev)
|
||||
}
|
||||
if err := b.addDeviceSignalMatch(path); err != nil {
|
||||
log.Warnf("Failed to add iwd device signal match for %s: %v", path, err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *IWDBackend) handleDeviceRemoved(path dbus.ObjectPath) bool {
|
||||
if !b.clearDevice(path) {
|
||||
return false
|
||||
}
|
||||
b.removeDeviceSignalMatch(path)
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *IWDBackend) handleStationAdded(path dbus.ObjectPath) bool {
|
||||
if path == "" || path == b.stationPath {
|
||||
return false
|
||||
@@ -139,6 +176,12 @@ func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) {
|
||||
if len(sig.Body) >= 2 {
|
||||
path, _ := sig.Body[0].(dbus.ObjectPath)
|
||||
if interfaces, ok := sig.Body[1].(map[string]map[string]dbus.Variant); ok {
|
||||
// Device before Station: handleStationAdded reads Powered from devicePath.
|
||||
if props, ok := interfaces[iwdDeviceInterface]; ok {
|
||||
if b.handleDeviceAdded(path, props) && b.onStateChange != nil {
|
||||
b.onStateChange()
|
||||
}
|
||||
}
|
||||
if _, ok := interfaces[iwdStationInterface]; ok {
|
||||
if b.handleStationAdded(path) && b.onStateChange != nil {
|
||||
b.onStateChange()
|
||||
@@ -159,17 +202,19 @@ func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) {
|
||||
path, _ := sig.Body[0].(dbus.ObjectPath)
|
||||
if interfaces, ok := sig.Body[1].([]string); ok {
|
||||
for _, iface := range interfaces {
|
||||
if iface == iwdStationInterface {
|
||||
switch iface {
|
||||
case iwdDeviceInterface:
|
||||
if b.handleDeviceRemoved(path) && b.onStateChange != nil {
|
||||
b.onStateChange()
|
||||
}
|
||||
case iwdStationInterface:
|
||||
if b.handleStationRemoved(path) && b.onStateChange != nil {
|
||||
b.onStateChange()
|
||||
}
|
||||
break
|
||||
}
|
||||
if iface == iwdKnownNetworkInterface {
|
||||
case iwdKnownNetworkInterface:
|
||||
if b.refreshWiFiNetworkState() && b.onStateChange != nil {
|
||||
b.onStateChange()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,3 +460,71 @@ func TestConnectAttempt_DoubleFinalization(t *testing.T) {
|
||||
assert.Equal(t, "bad-credentials", backend.state.LastError)
|
||||
backend.stateMutex.RUnlock()
|
||||
}
|
||||
|
||||
func TestIWDBackend_SetDevice_FollowsRecreatedDevice(t *testing.T) {
|
||||
backend, _ := NewIWDBackend()
|
||||
err := backend.applyManagedObjects(map[dbus.ObjectPath]map[string]map[string]dbus.Variant{
|
||||
"/net/connman/iwd/0/3": {
|
||||
iwdDeviceInterface: {"Name": dbus.MakeVariant("wlan0"), "Powered": dbus.MakeVariant(true)},
|
||||
iwdStationInterface: {},
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// iwd restarts: the old objects go away and wlan0 comes back with a new ifindex.
|
||||
assert.True(t, backend.handleStationRemoved("/net/connman/iwd/0/3"))
|
||||
assert.True(t, backend.clearDevice("/net/connman/iwd/0/3"))
|
||||
|
||||
prev, changed := backend.setDevice("/net/connman/iwd/0/7", map[string]dbus.Variant{
|
||||
"Name": dbus.MakeVariant("wlan0"),
|
||||
"Powered": dbus.MakeVariant(true),
|
||||
})
|
||||
|
||||
assert.True(t, changed)
|
||||
assert.Empty(t, prev)
|
||||
assert.Equal(t, dbus.ObjectPath("/net/connman/iwd/0/7"), backend.devicePath)
|
||||
|
||||
enabled, _ := backend.GetWiFiEnabled()
|
||||
assert.True(t, enabled, "Wi-Fi must be enabled again once the recreated device reports Powered")
|
||||
}
|
||||
|
||||
func TestIWDBackend_SetDevice_ReplacesPathWithoutRemovalSignal(t *testing.T) {
|
||||
backend, _ := NewIWDBackend()
|
||||
backend.devicePath = "/net/connman/iwd/0/3"
|
||||
|
||||
prev, changed := backend.setDevice("/net/connman/iwd/0/7", map[string]dbus.Variant{
|
||||
"Powered": dbus.MakeVariant(true),
|
||||
})
|
||||
|
||||
assert.True(t, changed)
|
||||
assert.Equal(t, dbus.ObjectPath("/net/connman/iwd/0/3"), prev)
|
||||
assert.Equal(t, dbus.ObjectPath("/net/connman/iwd/0/7"), backend.devicePath)
|
||||
}
|
||||
|
||||
func TestIWDBackend_SetDevice_SamePathIsNoop(t *testing.T) {
|
||||
backend, _ := NewIWDBackend()
|
||||
backend.devicePath = "/net/connman/iwd/0/3"
|
||||
|
||||
_, changed := backend.setDevice("/net/connman/iwd/0/3", map[string]dbus.Variant{
|
||||
"Powered": dbus.MakeVariant(false),
|
||||
})
|
||||
assert.False(t, changed)
|
||||
|
||||
_, changed = backend.setDevice("", nil)
|
||||
assert.False(t, changed)
|
||||
}
|
||||
|
||||
func TestIWDBackend_ClearDevice(t *testing.T) {
|
||||
backend, _ := NewIWDBackend()
|
||||
backend.devicePath = "/net/connman/iwd/0/3"
|
||||
|
||||
assert.False(t, backend.clearDevice("/net/connman/iwd/0/9"), "unrelated path must be ignored")
|
||||
assert.Equal(t, dbus.ObjectPath("/net/connman/iwd/0/3"), backend.devicePath)
|
||||
|
||||
assert.True(t, backend.clearDevice("/net/connman/iwd/0/3"))
|
||||
assert.Empty(t, backend.devicePath)
|
||||
|
||||
enabled, _ := backend.GetWiFiEnabled()
|
||||
assert.False(t, enabled)
|
||||
assert.ErrorContains(t, backend.SetWiFiEnabled(true), "no WiFi device available")
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -30,12 +31,54 @@ func (l *linkInfo) isWired() bool {
|
||||
if looksVirtual(l.name) {
|
||||
return false
|
||||
}
|
||||
if l.linkType == "bridge" {
|
||||
return hasPhysicalMember(l.name)
|
||||
}
|
||||
if l.linkType != "" {
|
||||
return l.linkType == "ether"
|
||||
return isWiredLinkType(l.linkType)
|
||||
}
|
||||
return !strings.HasPrefix(l.name, "wlan") && !strings.HasPrefix(l.name, "wlp")
|
||||
}
|
||||
|
||||
// isWiredLinkType reports whether a networkd link Type can carry the wired
|
||||
// uplink. Link aggregates and bridges hold the IP configuration while the
|
||||
// enslaved member NIC stays in the "enslaved" state, same as #1581 for
|
||||
// NetworkManager. Bridges are further checked in isWired.
|
||||
func isWiredLinkType(t string) bool {
|
||||
switch t {
|
||||
case "ether", "bridge", "bond", "team", "vlan":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// bridgeMembers lists the interfaces enslaved to a bridge. It is a variable so
|
||||
// tests can stub the sysfs lookup.
|
||||
var bridgeMembers = func(bridge string) []string {
|
||||
entries, err := os.ReadDir("/sys/class/net/" + bridge + "/brif")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
names := make([]string, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
names = append(names, e.Name())
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// hasPhysicalMember reports whether a bridge has at least one member that is
|
||||
// not virtual (veth, tap, ...). This tells a host bridge carrying the uplink
|
||||
// apart from container/VM bridges (lxc, waydroid, ...) that only hold virtual
|
||||
// members.
|
||||
func hasPhysicalMember(bridge string) bool {
|
||||
for _, m := range bridgeMembers(bridge) {
|
||||
if !looksVirtual(m) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *linkInfo) isWireless() bool {
|
||||
if looksVirtual(l.name) {
|
||||
return false
|
||||
|
||||
@@ -166,6 +166,15 @@ func TestLinkInfo_Classify(t *testing.T) {
|
||||
{"veth ether excluded", "veth1234", "ether", false, false},
|
||||
{"podman bridge ether excluded", "podman3", "ether", false, false},
|
||||
{"docker bridge ether excluded", "docker0", "ether", false, false},
|
||||
// The IP lives on the aggregate while the member NIC is "enslaved" (#3463).
|
||||
{"bridge type", "br0", "bridge", true, false},
|
||||
{"bond type", "bond0", "bond", true, false},
|
||||
{"team type", "team0", "team", true, false},
|
||||
{"vlan type", "vlan10", "vlan", true, false},
|
||||
{"docker user bridge excluded", "br-1a2b3c", "bridge", false, false},
|
||||
{"libvirt bridge excluded", "virbr0", "bridge", false, false},
|
||||
{"bridge with only virtual members excluded", "lxcbr0", "bridge", false, false},
|
||||
{"bridge without members excluded", "waydroid0", "bridge", false, false},
|
||||
// Fallback path: linkType unavailable, name-prefix heuristic applies.
|
||||
{"fallback enp wired", "enp141s0", "", true, false},
|
||||
{"fallback wlan wireless", "wlan0", "", false, true},
|
||||
@@ -174,6 +183,15 @@ func TestLinkInfo_Classify(t *testing.T) {
|
||||
{"fallback docker skipped", "docker0", "", false, false},
|
||||
{"fallback tun skipped", "tun0", "", false, false},
|
||||
}
|
||||
members := map[string][]string{
|
||||
"br0": {"enp42s0", "vnet0"},
|
||||
"lxcbr0": {"vethAbCd12"},
|
||||
"waydroid0": nil,
|
||||
}
|
||||
orig := bridgeMembers
|
||||
bridgeMembers = func(b string) []string { return members[b] }
|
||||
t.Cleanup(func() { bridgeMembers = orig })
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
l := &linkInfo{name: tc.ifname, linkType: tc.linkType}
|
||||
@@ -254,7 +272,7 @@ func TestLooksVirtual(t *testing.T) {
|
||||
for _, n := range virtual {
|
||||
assert.True(t, looksVirtual(n), "%s should look virtual", n)
|
||||
}
|
||||
real := []string{"enp141s0", "eno1", "wlan0", "wlp3s0", "wifi", "dock", "nebula.homelab", "wg0"}
|
||||
real := []string{"enp141s0", "eno1", "wlan0", "wlp3s0", "wifi", "dock", "nebula.homelab", "wg0", "br0", "bond0", "team0", "vlan10"}
|
||||
for _, n := range real {
|
||||
assert.False(t, looksVirtual(n), "%s should not look virtual", n)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ const (
|
||||
|
||||
metricPreferred = int64(100)
|
||||
metricNonPreferred = int64(300)
|
||||
metricDefault = int64(100)
|
||||
// -1 lets NetworkManager pick the metric by device type (nm-settings ipv4.route-metric)
|
||||
metricDefault = int64(-1)
|
||||
)
|
||||
|
||||
func (m *Manager) SetConnectionPreference(pref ConnectionPreference) error {
|
||||
@@ -46,7 +47,7 @@ func (m *Manager) SetConnectionPreference(pref ConnectionPreference) error {
|
||||
case PreferenceCellular:
|
||||
return m.prioritizeCellular()
|
||||
case PreferenceAuto:
|
||||
return m.balancePriorities()
|
||||
return m.restoreDefaultPriorities()
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -112,7 +113,7 @@ func (m *Manager) prioritizeCellular() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) balancePriorities() error {
|
||||
func (m *Manager) restoreDefaultPriorities() error {
|
||||
if err := m.setConnectionPriority("802-3-ethernet", priorityDefault, metricDefault); err != nil {
|
||||
log.Warnf("Failed to reset Ethernet priority: %v", err)
|
||||
}
|
||||
@@ -242,16 +243,20 @@ func (m *Manager) setConnectionPriority(connType string, autoconnectPriority int
|
||||
}
|
||||
|
||||
func priorityMatches(variant dbus.Variant, expected int64) bool {
|
||||
value, ok := variantInt64(variant)
|
||||
return ok && value == expected
|
||||
return settingInt64(variant, int64(priorityDefault)) == expected
|
||||
}
|
||||
|
||||
func routeMetricMatches(section map[string]dbus.Variant, expected int64) bool {
|
||||
if section == nil {
|
||||
return false
|
||||
return settingInt64(section["route-metric"], metricDefault) == expected
|
||||
}
|
||||
|
||||
// GetSettings omits properties at their default, so a missing key is the default value
|
||||
func settingInt64(variant dbus.Variant, def int64) int64 {
|
||||
value, ok := variantInt64(variant)
|
||||
if !ok {
|
||||
return def
|
||||
}
|
||||
value, ok := variantInt64(section["route-metric"])
|
||||
return ok && value == expected
|
||||
return value
|
||||
}
|
||||
|
||||
func variantInt64(variant dbus.Variant) (int64, bool) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package network
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -45,7 +46,10 @@ func TestManager_GetConnectionPreference(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Full testing of priority operations would require mocking NetworkManager
|
||||
// D-Bus interfaces. The tests above cover the basic logic and error handling.
|
||||
// Integration tests would be needed for complete coverage of network connection
|
||||
// priority updates and reactivation.
|
||||
func TestProfileAtDefaultsMatchesAutoTargets(t *testing.T) {
|
||||
assert.True(t, priorityMatches(dbus.Variant{}, int64(priorityDefault)))
|
||||
assert.True(t, routeMetricMatches(nil, metricDefault))
|
||||
assert.True(t, routeMetricMatches(map[string]dbus.Variant{"method": dbus.MakeVariant("auto")}, metricDefault))
|
||||
assert.False(t, routeMetricMatches(map[string]dbus.Variant{"route-metric": dbus.MakeVariant(int64(100))}, metricDefault))
|
||||
assert.False(t, priorityMatches(dbus.MakeVariant(int32(100)), int64(priorityDefault)))
|
||||
}
|
||||
|
||||
+1
-1
Submodule dank-qml-common updated: 26396ce432...1ae722232d
Generated
+4
-3
@@ -3,15 +3,16 @@
|
||||
"dank-qml-common": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1788376015,
|
||||
"narHash": "sha256-/tT8rTznLADndTwLBzdgzzgfJH1ZhYQibYo5v/F8U+U=",
|
||||
"lastModified": 1791392812,
|
||||
"narHash": "sha256-id6Y5XNO5K3KfXSOsEYVUwiq1ygYGNX5LeKSgHM3GR8=",
|
||||
"owner": "AvengeMedia",
|
||||
"repo": "dank-qml-common",
|
||||
"rev": "26396ce432d6c71c3f5367438f96f4a8d667e160",
|
||||
"rev": "1ae722232da7110d26cd2c355b6615e078e94197",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "AvengeMedia",
|
||||
"ref": "stable-1.6",
|
||||
"repo": "dank-qml-common",
|
||||
"type": "github"
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
flake = false;
|
||||
};
|
||||
dank-qml-common = {
|
||||
url = "github:AvengeMedia/dank-qml-common";
|
||||
url = "github:AvengeMedia/dank-qml-common/stable-1.6";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ Singleton {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("SessionData")
|
||||
|
||||
readonly property int sessionConfigVersion: 4
|
||||
readonly property int sessionConfigVersion: 5
|
||||
|
||||
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
||||
|
||||
|
||||
@@ -1988,8 +1988,11 @@ Singleton {
|
||||
function saveSettings() {
|
||||
if (_loading || _parseError || !_hasLoaded)
|
||||
return;
|
||||
const json = JSON.stringify(Store.toJson(root), null, 2);
|
||||
if (json === settingsFile.text())
|
||||
return;
|
||||
_selfWrite = true;
|
||||
settingsFile.setText(JSON.stringify(Store.toJson(root), null, 2));
|
||||
settingsFile.setText(json);
|
||||
if (_isReadOnly)
|
||||
_checkSettingsWritable();
|
||||
}
|
||||
@@ -3225,10 +3228,11 @@ Singleton {
|
||||
if (!app && !desktop)
|
||||
return -1;
|
||||
return rules.findIndex(rule => {
|
||||
if (!predicate(rule))
|
||||
if (!predicate(rule) || (rule.matchType || "contains").toString().toLowerCase() !== "exact")
|
||||
return false;
|
||||
const pattern = (rule.pattern || "").toString().toLowerCase();
|
||||
return pattern !== "" && (pattern === app || pattern === desktop);
|
||||
const value = !rule.field || rule.field === "appName" ? app : rule.field === "desktopEntry" ? desktop : "";
|
||||
return pattern !== "" && pattern === value;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3246,20 +3250,31 @@ Singleton {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function _removeAppRule(appName, desktopEntry, predicate) {
|
||||
function _hasNoAction(rule) {
|
||||
return (rule.action || "default").toString().toLowerCase() === "default";
|
||||
}
|
||||
|
||||
// Edits the first enabled matching rule and drops it once it no longer does anything.
|
||||
function _updateAppRule(appName, desktopEntry, predicate, changes) {
|
||||
var rules = JSON.parse(JSON.stringify(notificationRules || []));
|
||||
const index = _appRuleIndex(rules, appName, desktopEntry, predicate);
|
||||
const index = _appRuleIndex(rules, appName, desktopEntry, rule => rule.enabled !== false && predicate(rule));
|
||||
if (index === -1)
|
||||
return;
|
||||
rules.splice(index, 1);
|
||||
return false;
|
||||
const rule = Object.assign(rules[index], changes);
|
||||
if (_hasNoAction(rule) && (rule.urgency || "default").toString().toLowerCase() === "default" && !_isDndBypassRule(rule))
|
||||
rules.splice(index, 1);
|
||||
notificationRules = rules;
|
||||
saveSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
function addMuteRuleForApp(appName, desktopEntry) {
|
||||
_addAppRule(appName, desktopEntry, {
|
||||
action: "mute"
|
||||
});
|
||||
if (!_updateAppRule(appName, desktopEntry, _hasNoAction, {
|
||||
action: "mute"
|
||||
}))
|
||||
_addAppRule(appName, desktopEntry, {
|
||||
action: "mute"
|
||||
});
|
||||
}
|
||||
|
||||
function isAppMuted(appName, desktopEntry) {
|
||||
@@ -3267,7 +3282,9 @@ Singleton {
|
||||
}
|
||||
|
||||
function removeMuteRuleForApp(appName, desktopEntry) {
|
||||
_removeAppRule(appName, desktopEntry, _isMuteRule);
|
||||
_updateAppRule(appName, desktopEntry, _isMuteRule, {
|
||||
action: "default"
|
||||
});
|
||||
}
|
||||
|
||||
function isAppDndBypassed(appName, desktopEntry) {
|
||||
@@ -3276,14 +3293,19 @@ Singleton {
|
||||
|
||||
function setAppDndBypass(appName, desktopEntry, enabled) {
|
||||
if (!enabled) {
|
||||
_removeAppRule(appName, desktopEntry, _isDndBypassRule);
|
||||
_updateAppRule(appName, desktopEntry, _isDndBypassRule, {
|
||||
bypassDnd: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (isAppDndBypassed(appName, desktopEntry))
|
||||
return;
|
||||
_addAppRule(appName, desktopEntry, {
|
||||
bypassDnd: true
|
||||
});
|
||||
if (!_updateAppRule(appName, desktopEntry, () => true, {
|
||||
bypassDnd: true
|
||||
}))
|
||||
_addAppRule(appName, desktopEntry, {
|
||||
bypassDnd: true
|
||||
});
|
||||
}
|
||||
|
||||
function updateNotificationRule(index, ruleData) {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
.pragma library
|
||||
|
||||
// Qt Wayland sends scroll phases only for the finger axis source; pixelDelta also comes with wheels from Qt 6.12 (qtbase d19bd66b4)
|
||||
const Kind = Object.freeze({
|
||||
none: "none",
|
||||
touchpad: "touchpad",
|
||||
wheel: "wheel",
|
||||
highResWheel: "highResWheel"
|
||||
});
|
||||
|
||||
function isTouchpad(wheel) {
|
||||
if (wheel.phase === Qt.NoScrollPhase)
|
||||
return false;
|
||||
return wheel.pixelDelta.x !== 0 || wheel.pixelDelta.y !== 0;
|
||||
}
|
||||
|
||||
function verticalKind(wheel) {
|
||||
if (isTouchpad(wheel))
|
||||
return Kind.touchpad;
|
||||
const angle = wheel.angleDelta.y;
|
||||
if (angle === 0)
|
||||
return Kind.none;
|
||||
return angle % 120 === 0 ? Kind.wheel : Kind.highResWheel;
|
||||
}
|
||||
|
||||
function anyAxisDelta(wheel) {
|
||||
const point = isTouchpad(wheel) ? wheel.pixelDelta : wheel.angleDelta;
|
||||
return point.x || point.y;
|
||||
}
|
||||
@@ -83,5 +83,29 @@ function migrateToVersion(obj, targetVersion, settingsData) {
|
||||
session.configVersion = 4;
|
||||
}
|
||||
|
||||
if (currentVersion < 5) {
|
||||
console.info("SessionData: Migrating session to version 5");
|
||||
console.info("SessionData: Tray keys now use a stable id (or id::instance) instead of id::tooltipTitle");
|
||||
|
||||
function stripTrayKeys(list) {
|
||||
if (!list || list.constructor !== Array) return list;
|
||||
var out = [];
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var raw = list[i];
|
||||
if (typeof raw !== "string") continue;
|
||||
var key = raw.includes("::") ? raw.split("::")[0] : raw;
|
||||
var hasDup = false;
|
||||
for (var j = 0; j < out.length; j++) {
|
||||
if (out[j] === key) { hasDup = true; break; }
|
||||
}
|
||||
if (key && !hasDup) out.push(key);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
if (session.hiddenTrayIds !== undefined) session.hiddenTrayIds = stripTrayKeys(session.hiddenTrayIds);
|
||||
if (session.trayItemOrder !== undefined) session.trayItemOrder = stripTrayKeys(session.trayItemOrder);
|
||||
session.configVersion = 5;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ DankFloatingWindow {
|
||||
}
|
||||
|
||||
function submitAndClose() {
|
||||
if (!root.visible)
|
||||
return;
|
||||
if (aqueousWorkspace || CompositorService.isAqueous) {
|
||||
if (!aqueousWorkspace || renaming)
|
||||
return;
|
||||
|
||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import "../../Common/WheelInput.js" as WheelInput
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
@@ -93,16 +94,15 @@ MouseArea {
|
||||
|
||||
const deltaY = wheel.angleDelta.y;
|
||||
const deltaX = wheel.angleDelta.x;
|
||||
const isTouchpadY = wheel.pixelDelta && wheel.pixelDelta.y !== 0;
|
||||
const isTouchpadX = wheel.pixelDelta && wheel.pixelDelta.x !== 0;
|
||||
const isTouchpad = WheelInput.isTouchpad(wheel);
|
||||
|
||||
if (CompositorService.isNiri && xBehavior !== "none" && Math.abs(deltaX) > Math.abs(deltaY)) {
|
||||
accumulateX(isTouchpadX, deltaX, xBehavior);
|
||||
accumulateX(isTouchpad, deltaX, xBehavior);
|
||||
return;
|
||||
}
|
||||
if (yBehavior === "none")
|
||||
return;
|
||||
accumulateY(isTouchpadY, deltaY, yBehavior);
|
||||
accumulateY(isTouchpad, deltaY, yBehavior);
|
||||
}
|
||||
|
||||
onWheel: wheel => processWheel(wheel)
|
||||
|
||||
@@ -40,13 +40,33 @@ BasePill {
|
||||
return !hiddenTrayIds.includes(itemId.toLowerCase());
|
||||
});
|
||||
}
|
||||
property var _trayKeyByItem: new Map()
|
||||
function getTrayItemKey(item) {
|
||||
const id = item?.id || "";
|
||||
const tooltipTitle = item?.tooltipTitle || "";
|
||||
if (!tooltipTitle || tooltipTitle === id) {
|
||||
return id;
|
||||
if (!id)
|
||||
return "";
|
||||
if (root._trayKeyByItem.has(item))
|
||||
return root._trayKeyByItem.get(item);
|
||||
const taken = new Set();
|
||||
for (const live of root.allTrayItems) {
|
||||
if (live !== item && (live?.id || "") === id && root._trayKeyByItem.has(live))
|
||||
taken.add(root._trayKeyByItem.get(live));
|
||||
}
|
||||
return `${id}::${tooltipTitle}`;
|
||||
let key = id;
|
||||
for (let n = 1; taken.has(key); n++)
|
||||
key = `${id}::${n}`;
|
||||
root._trayKeyByItem.set(item, key);
|
||||
return key;
|
||||
}
|
||||
|
||||
function resolveOrderIndex(key, orderMap) {
|
||||
if (orderMap.has(key))
|
||||
return orderMap.get(key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
function isTrayIdHidden(key) {
|
||||
return SessionData.isHiddenTrayId(key);
|
||||
}
|
||||
|
||||
function trayIconSourceFor(trayItem) {
|
||||
@@ -150,6 +170,13 @@ BasePill {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SystemTray
|
||||
function onItemUnregistered(item) {
|
||||
root._trayKeyByItem.delete(item);
|
||||
}
|
||||
}
|
||||
|
||||
function sortByPreferredOrder(items, trigger) {
|
||||
void trigger;
|
||||
const savedOrder = SessionData.trayItemOrder || [];
|
||||
@@ -159,15 +186,17 @@ BasePill {
|
||||
return [...items].sort((a, b) => {
|
||||
const keyA = getTrayItemKey(a);
|
||||
const keyB = getTrayItemKey(b);
|
||||
const orderA = orderMap.has(keyA) ? orderMap.get(keyA) : 10000 + items.indexOf(a);
|
||||
const orderB = orderMap.has(keyB) ? orderMap.get(keyB) : 10000 + items.indexOf(b);
|
||||
return orderA - orderB;
|
||||
const orderA = resolveOrderIndex(keyA, orderMap);
|
||||
const orderB = resolveOrderIndex(keyB, orderMap);
|
||||
const posA = orderA >= 0 ? orderA : 10000 + items.indexOf(a);
|
||||
const posB = orderB >= 0 ? orderB : 10000 + items.indexOf(b);
|
||||
return posA - posB;
|
||||
});
|
||||
}
|
||||
|
||||
readonly property var allSortedTrayItems: sortByPreferredOrder(allTrayItems, _trayOrderTrigger)
|
||||
readonly property var allSortedTrayItemKeys: allSortedTrayItems.map(item => getTrayItemKey(item))
|
||||
readonly property var visibleSortedTrayItems: allSortedTrayItems.filter(item => !SessionData.isHiddenTrayId(root.getTrayItemKey(item)))
|
||||
readonly property var visibleSortedTrayItems: allSortedTrayItems.filter(item => !root.isTrayIdHidden(root.getTrayItemKey(item)))
|
||||
readonly property int automaticVisibleItemLimit: {
|
||||
if (!root.useAutomaticOverflow)
|
||||
return root.visibleSortedTrayItems.length;
|
||||
@@ -190,7 +219,7 @@ BasePill {
|
||||
item: item
|
||||
}))
|
||||
readonly property var autoOverflowBarItems: visibleSortedTrayItems.slice(automaticVisibleItemLimit)
|
||||
readonly property var manualHiddenBarItems: allSortedTrayItems.filter(item => SessionData.isHiddenTrayId(root.getTrayItemKey(item)))
|
||||
readonly property var manualHiddenBarItems: allSortedTrayItems.filter(item => root.isTrayIdHidden(root.getTrayItemKey(item)))
|
||||
readonly property var hiddenBarItemKeys: manualHiddenBarItems.concat(autoOverflowBarItems).map(item => root.getTrayItemKey(item))
|
||||
readonly property var hiddenBarItems: allSortedTrayItems.filter(item => hiddenBarItemKeys.indexOf(root.getTrayItemKey(item)) !== -1)
|
||||
readonly property string trayIconTintMode: {
|
||||
@@ -298,7 +327,7 @@ BasePill {
|
||||
}
|
||||
|
||||
function isManualHiddenTrayItem(item) {
|
||||
return SessionData.isHiddenTrayId(getTrayItemKey(item));
|
||||
return root.isTrayIdHidden(root.getTrayItemKey(item));
|
||||
}
|
||||
|
||||
function isAutoOverflowTrayItem(item) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import Quickshell.WindowManager
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import "../../../Common/WheelInput.js" as WheelInput
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -992,7 +993,7 @@ Item {
|
||||
return;
|
||||
|
||||
const delta = wheel.angleDelta.y;
|
||||
const isTouchpad = wheel.pixelDelta && wheel.pixelDelta.y !== 0;
|
||||
const isTouchpad = WheelInput.isTouchpad(wheel);
|
||||
const reverse = SettingsData.reverseScrolling ? -1 : 1;
|
||||
|
||||
if (isTouchpad) {
|
||||
|
||||
@@ -194,7 +194,6 @@ Item {
|
||||
id: mediaExpandedLoader
|
||||
|
||||
activity: "media"
|
||||
height: Math.max(target.height, root.height)
|
||||
active: root.mediaSurfaceActive && (root.expanded || root.expandedFade > 0)
|
||||
asynchronous: false
|
||||
sourceComponent: root.mediaExpandedComponent
|
||||
|
||||
@@ -272,7 +272,8 @@ Scope {
|
||||
property bool isActiveScreen: {
|
||||
if (Quickshell.screens.length <= 1)
|
||||
return true;
|
||||
return SettingsData.getFilteredScreens("lockScreen").includes(screen);
|
||||
const screens = SettingsData.getFilteredScreens("lockScreen");
|
||||
return screens.length === 0 || screens.includes(screen);
|
||||
}
|
||||
|
||||
color: isActiveScreen ? "transparent" : SettingsData.lockScreenInactiveColor
|
||||
|
||||
@@ -82,6 +82,8 @@ Item {
|
||||
return I18n.tr("Too many attempts - locked out");
|
||||
if (root.pamState === "fail")
|
||||
return I18n.tr("Incorrect password - try again");
|
||||
if (pam.fprint.status === "disabled")
|
||||
return "";
|
||||
if (pam.fprintState === "error")
|
||||
return I18n.tr("Fingerprint error");
|
||||
if (pam.fprintState === "max")
|
||||
@@ -861,10 +863,15 @@ Item {
|
||||
name: {
|
||||
if (pam.u2fPending)
|
||||
return "passkey";
|
||||
if (pam.fprint.tries >= SettingsData.maxFprintTries)
|
||||
switch (pam.fprint.status) {
|
||||
case "max":
|
||||
case "stopped":
|
||||
return "fingerprint_off";
|
||||
if (pam.fprint.active || pam.fprint.retrying)
|
||||
case "active":
|
||||
return "fingerprint";
|
||||
case "retrying":
|
||||
return "hourglass_empty";
|
||||
}
|
||||
if (pam.u2f.active)
|
||||
return "passkey";
|
||||
return "lock";
|
||||
@@ -1005,6 +1012,8 @@ Item {
|
||||
return;
|
||||
}
|
||||
|
||||
root.pam.retryFprintOnActivity();
|
||||
|
||||
if (root.unlocking) {
|
||||
event.accepted = true;
|
||||
return;
|
||||
|
||||
@@ -16,7 +16,20 @@ FocusScope {
|
||||
signal passwordChanged(string newPassword)
|
||||
signal unlockRequested
|
||||
|
||||
HoverHandler {
|
||||
enabled: root.pam.awaitingActivityRetry
|
||||
onPointChanged: root.pam.retryFprintOnActivity()
|
||||
}
|
||||
|
||||
PointHandler {
|
||||
onActiveChanged: {
|
||||
if (active)
|
||||
root.pam.retryFprintOnActivity();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
root.pam.retryFprintOnActivity();
|
||||
if (videoScreensaver.active && videoScreensaver.inputEnabled) {
|
||||
videoScreensaver.dismiss();
|
||||
event.accepted = true;
|
||||
|
||||
+154
-52
@@ -32,9 +32,8 @@ Scope {
|
||||
|
||||
function resetAuthFlows(): void {
|
||||
passwd.abort();
|
||||
fprint.abort();
|
||||
fprint.stop();
|
||||
u2f.abort();
|
||||
errorRetry.running = false;
|
||||
u2fErrorRetry.running = false;
|
||||
u2fPendingTimeout.running = false;
|
||||
passwdActiveTimeout.running = false;
|
||||
@@ -58,9 +57,8 @@ Scope {
|
||||
if (!root.unlockInProgress) {
|
||||
root.unlockInProgress = true;
|
||||
passwd.abort();
|
||||
fprint.abort();
|
||||
fprint.stop();
|
||||
u2f.abort();
|
||||
errorRetry.running = false;
|
||||
u2fErrorRetry.running = false;
|
||||
u2fPendingTimeout.running = false;
|
||||
root.u2fPending = false;
|
||||
@@ -217,7 +215,7 @@ Scope {
|
||||
|
||||
if (res === PamResult.Success) {
|
||||
if (!root.unlockInProgress) {
|
||||
fprint.abort();
|
||||
fprint.stop();
|
||||
root.proceedAfterPrimaryAuth();
|
||||
}
|
||||
return;
|
||||
@@ -260,69 +258,147 @@ Scope {
|
||||
PamContext {
|
||||
id: fprint
|
||||
|
||||
property bool available: SettingsData.lockFingerprintReady
|
||||
property int tries
|
||||
property int errorTries
|
||||
property bool retrying: false
|
||||
readonly property bool available: SettingsData.lockFingerprintReady
|
||||
property int tries: 0
|
||||
property int errorTries: 0
|
||||
property double attemptStartedAt: 0
|
||||
property bool completedDuringStart: false
|
||||
readonly property int maxErrorTries: 200
|
||||
readonly property int daemonIdleExitMs: 30000
|
||||
// sessionTimeoutMs mirrors `timeout=` in assets/pam/fprint
|
||||
readonly property int sessionTimeoutMs: 90000
|
||||
readonly property int verifyStartSlackMs: 10000
|
||||
readonly property bool retrying: errorRetry.running
|
||||
readonly property int retryInterval: errorRetry.interval
|
||||
readonly property bool allowed: available && SettingsData.enableFprint && root.lockSecured && !root.fprintSuppressedByPrimaryPam && !root.unlockInProgress && !root.u2fPending && !SessionService.preparingForSleep && !IdleService.monitorsOff && tries < SettingsData.maxFprintTries
|
||||
readonly property string status: {
|
||||
if (!available || !SettingsData.enableFprint || !root.lockSecured || root.fprintSuppressedByPrimaryPam)
|
||||
return "disabled";
|
||||
if (tries >= SettingsData.maxFprintTries)
|
||||
return "max";
|
||||
if (!allowed)
|
||||
return "paused";
|
||||
if (active)
|
||||
return "active";
|
||||
if (retrying)
|
||||
return "retrying";
|
||||
return errorTries >= maxErrorTries ? "stopped" : "idle";
|
||||
}
|
||||
|
||||
function stop(): void {
|
||||
errorRetry.stop();
|
||||
attemptStartedAt = 0;
|
||||
abort();
|
||||
}
|
||||
|
||||
// pam_fprintd reports an expired timeout as PAM_AUTHINFO_UNAVAIL, the same
|
||||
// result a device fault gives, and its "Verification timed out" message is
|
||||
// translated, so age is the signal we can rely on. Its timeout runs from the
|
||||
// verify rather than from the claim, which is why the age is measured from
|
||||
// the message announcing the verify: a slow claim then cannot make a fault
|
||||
// look old enough, and a session that never reached a verify never qualifies.
|
||||
// That message lands after pam_fprintd has already armed its deadline, so the
|
||||
// age read here runs short of the real timeout and the slack makes it up. The
|
||||
// bias is deliberate: a timeout misread as a fault parks the reader behind the
|
||||
// backoff, while a fault misread as a timeout costs one extra retry.
|
||||
function attemptSettled(): bool {
|
||||
return attemptStartedAt > 0 && Date.now() - attemptStartedAt >= sessionTimeoutMs - verifyStartSlackMs;
|
||||
}
|
||||
|
||||
function checkAvail(): void {
|
||||
if (!available || !SettingsData.enableFprint || !root.lockSecured || root.fprintSuppressedByPrimaryPam) {
|
||||
retrying = false;
|
||||
abort();
|
||||
if (!allowed) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
if (!active && !retrying)
|
||||
startAttempt();
|
||||
}
|
||||
|
||||
function startAttempt(): void {
|
||||
if (!allowed || errorTries >= maxErrorTries) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
if (active)
|
||||
return;
|
||||
errorRetry.stop();
|
||||
if (root.fprintState === "error")
|
||||
root.fprintState = "";
|
||||
attemptStartedAt = 0;
|
||||
completedDuringStart = false;
|
||||
if (start())
|
||||
return;
|
||||
if (!completedDuringStart)
|
||||
scheduleErrorRetry();
|
||||
}
|
||||
|
||||
tries = 0;
|
||||
function scheduleErrorRetry(): void {
|
||||
stop();
|
||||
if (!allowed)
|
||||
return;
|
||||
errorTries++;
|
||||
root.fprintState = "error";
|
||||
if (errorTries === 1)
|
||||
root.flashMsg();
|
||||
fprintStateReset.restart();
|
||||
if (errorTries < maxErrorTries)
|
||||
errorRetry.restart();
|
||||
}
|
||||
|
||||
function recover(): void {
|
||||
if (!allowed)
|
||||
return;
|
||||
errorTries = 0;
|
||||
retrying = false;
|
||||
start();
|
||||
if (!active)
|
||||
startAttempt();
|
||||
}
|
||||
|
||||
onAllowedChanged: {
|
||||
if (!allowed)
|
||||
stop();
|
||||
else
|
||||
Qt.callLater(checkAvail);
|
||||
}
|
||||
|
||||
config: "fprint"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam"
|
||||
|
||||
onPamMessage: {
|
||||
if (attemptStartedAt === 0)
|
||||
attemptStartedAt = Date.now();
|
||||
}
|
||||
|
||||
onCompleted: res => {
|
||||
if (!available)
|
||||
completedDuringStart = true;
|
||||
if (!allowed)
|
||||
return;
|
||||
|
||||
switch (res) {
|
||||
case PamResult.Success:
|
||||
retrying = false;
|
||||
if (!root.unlockInProgress) {
|
||||
passwd.abort();
|
||||
root.proceedAfterPrimaryAuth();
|
||||
}
|
||||
stop();
|
||||
passwd.abort();
|
||||
root.proceedAfterPrimaryAuth();
|
||||
return;
|
||||
case PamResult.Error:
|
||||
errorTries++;
|
||||
if (errorTries < 200) {
|
||||
retrying = true;
|
||||
abort();
|
||||
errorRetry.restart();
|
||||
if (attemptSettled()) {
|
||||
stop();
|
||||
errorTries = 0;
|
||||
startAttempt();
|
||||
return;
|
||||
}
|
||||
retrying = false;
|
||||
abort();
|
||||
scheduleErrorRetry();
|
||||
return;
|
||||
case PamResult.Failed:
|
||||
case PamResult.MaxTries:
|
||||
retrying = false;
|
||||
stop();
|
||||
tries++;
|
||||
if (tries < SettingsData.maxFprintTries) {
|
||||
root.fprintState = "fail";
|
||||
start();
|
||||
} else {
|
||||
root.fprintState = "max";
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errorTries = 0;
|
||||
root.fprintState = tries < SettingsData.maxFprintTries ? "fail" : "max";
|
||||
root.flashMsg();
|
||||
fprintStateReset.restart();
|
||||
startAttempt();
|
||||
return;
|
||||
}
|
||||
|
||||
root.flashMsg();
|
||||
fprintStateReset.start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,20 +502,35 @@ Scope {
|
||||
Timer {
|
||||
id: errorRetry
|
||||
|
||||
interval: Math.min(1500 * Math.pow(2, Math.max(0, fprint.errorTries - 1)), 30000)
|
||||
onTriggered: fprint.start()
|
||||
// A device wedged by a suspend mid-verify only comes back when fprintd
|
||||
// restarts, and fprintd only exits after its idle timeout with no client
|
||||
// attached, so the second retry outlasts that instead of climbing to it.
|
||||
interval: {
|
||||
if (fprint.errorTries <= 1)
|
||||
return 1500;
|
||||
if (fprint.errorTries === 2)
|
||||
return fprint.daemonIdleExitMs + 5000;
|
||||
return 60000;
|
||||
}
|
||||
onTriggered: fprint.startAttempt()
|
||||
}
|
||||
|
||||
readonly property bool awaitingActivityRetry: fprint.errorTries > 0 && !fprint.retrying && !fprint.active
|
||||
|
||||
function retryFprintOnActivity(): void {
|
||||
if (root.awaitingActivityRetry)
|
||||
fprint.recover();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionService
|
||||
|
||||
// timers run on monotonic time, so suspend leaves the backoff remainder to burn after wake (#3171)
|
||||
function onSessionResumed() {
|
||||
if (!fprint.available || !SettingsData.enableFprint || !root.lockSecured || root.fprintSuppressedByPrimaryPam)
|
||||
return;
|
||||
fprint.errorTries = 0;
|
||||
if (errorRetry.running)
|
||||
errorRetry.restart();
|
||||
fprint.recover();
|
||||
}
|
||||
|
||||
function onLidOpened() {
|
||||
fprint.recover();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +582,10 @@ Scope {
|
||||
id: fprintStateReset
|
||||
|
||||
interval: 4000
|
||||
onTriggered: root.fprintState = ""
|
||||
onTriggered: {
|
||||
if (root.fprintState !== "max")
|
||||
root.fprintState = "";
|
||||
}
|
||||
}
|
||||
|
||||
onLockSecuredChanged: {
|
||||
@@ -508,6 +602,8 @@ Scope {
|
||||
root.attemptInfoMessages = [];
|
||||
root.lockoutAnnouncedThisAttempt = false;
|
||||
root.resetAuthFlows();
|
||||
fprint.tries = 0;
|
||||
fprint.errorTries = 0;
|
||||
if (!SettingsData.lockPamExternallyManaged && !dankshellConfigWatcher.loaded && !userPamWatcher.loaded)
|
||||
ensureUserPamConfig();
|
||||
// FileView cannot watch a path that does not exist yet; re-read so a
|
||||
@@ -521,11 +617,17 @@ Scope {
|
||||
target: SettingsData
|
||||
|
||||
function onEnableFprintChanged(): void {
|
||||
fprint.checkAvail();
|
||||
if (SettingsData.enableFprint)
|
||||
fprint.recover();
|
||||
else
|
||||
fprint.checkAvail();
|
||||
}
|
||||
|
||||
function onLockFingerprintReadyChanged(): void {
|
||||
fprint.checkAvail();
|
||||
if (SettingsData.lockFingerprintReady)
|
||||
fprint.recover();
|
||||
else
|
||||
fprint.checkAvail();
|
||||
}
|
||||
|
||||
function onEnableU2fChanged(): void {
|
||||
|
||||
@@ -698,7 +698,7 @@ Column {
|
||||
color: Theme.surfaceTextSecondary
|
||||
font.family: textArea.font.family
|
||||
font.pixelSize: textArea.font.pixelSize
|
||||
visible: textArea.text.length === 0
|
||||
visible: textArea.text.length === 0 && !textArea.inputMethodComposing
|
||||
anchors.left: textArea.left
|
||||
anchors.top: textArea.top
|
||||
anchors.leftMargin: textArea.leftPadding
|
||||
|
||||
@@ -63,6 +63,26 @@ Singleton {
|
||||
onTriggered: root.requestSync("startup")
|
||||
}
|
||||
|
||||
property int powerSyncRecoveryAttempt: 0
|
||||
readonly property var powerSyncRecoveryIntervals: [1000, 5000, 15000, 30000]
|
||||
|
||||
Timer {
|
||||
id: powerSyncRecoveryTimer
|
||||
interval: root.powerSyncRecoveryIntervals[0]
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
root.requestSync("resume-reconcile");
|
||||
root.powerSyncRecoveryAttempt++;
|
||||
if (root.powerSyncRecoveryAttempt < root.powerSyncRecoveryIntervals.length) {
|
||||
interval = root.powerSyncRecoveryIntervals[root.powerSyncRecoveryAttempt];
|
||||
restart();
|
||||
return;
|
||||
}
|
||||
root.powerSyncRecoveryAttempt = 0;
|
||||
interval = root.powerSyncRecoveryIntervals[0];
|
||||
}
|
||||
}
|
||||
|
||||
signal brightnessChanged(bool showOsd)
|
||||
signal deviceSwitched
|
||||
|
||||
@@ -1541,6 +1561,9 @@ Singleton {
|
||||
resumeRecoveryAttempt = 0;
|
||||
resumeRecoveryTimer.interval = 400;
|
||||
resumeRecoveryTimer.restart();
|
||||
powerSyncRecoveryAttempt = 0;
|
||||
powerSyncRecoveryTimer.interval = powerSyncRecoveryIntervals[0];
|
||||
powerSyncRecoveryTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -148,14 +148,13 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Wakes monitors powered off by the "power off monitors on lock" path.
|
||||
// Lock.qml's own wake handlers sit outside the session-lock surface and
|
||||
// never receive input, so wake on input via seat-level idle-notify instead.
|
||||
IdleMonitor {
|
||||
id: lockWakeMonitor
|
||||
timeout: 1
|
||||
respectInhibitors: false
|
||||
enabled: root.enabled && root.isShellLocked && root.monitorsOff && (SettingsData.lockScreenPowerOffMonitorsOnLock || root.lockPowerOffRequested)
|
||||
enabled: root.enabled && root.isShellLocked && root.monitorsOff
|
||||
onIsIdleChanged: {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
@@ -504,6 +504,7 @@ Singleton {
|
||||
};
|
||||
|
||||
policy.bypassDnd = rules.some(rule => rule.bypassDnd === true && _matchesNotificationRule(rule, info));
|
||||
policy.disablePopup = rules.some(rule => (rule.action || "default").toString().toLowerCase() === "mute" && _matchesNotificationRule(rule, info));
|
||||
|
||||
for (const rule of rules) {
|
||||
if (!_matchesNotificationRule(rule, info))
|
||||
@@ -514,9 +515,6 @@ Singleton {
|
||||
case "ignore":
|
||||
policy.drop = true;
|
||||
break;
|
||||
case "mute":
|
||||
policy.disablePopup = true;
|
||||
break;
|
||||
case "popup_only":
|
||||
policy.hideFromCenter = true;
|
||||
policy.disableHistory = true;
|
||||
@@ -699,23 +697,7 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Honor the freedesktop "suppress-sound" hint: the sender
|
||||
// plays its own audio for this notification and asks the
|
||||
// server not to double up. "sound-name" is the opposite — an
|
||||
// explicit request for audio — so it plays even when the
|
||||
// global new-notification sound is off.
|
||||
const soundHints = notif.hints || {};
|
||||
const suppressSound = !!soundHints["suppress-sound"];
|
||||
const requestsSound = !!soundHints["sound-name"];
|
||||
const dndBlocked = SessionData.doNotDisturb && !_allowedInDnd(policy.urgency, policy.bypassDnd);
|
||||
if (!dndBlocked && SettingsData.soundsEnabled && (SettingsData.soundNewNotification || requestsSound) && !suppressSound) {
|
||||
if (policy.urgency === NotificationUrgency.Critical) {
|
||||
AudioService.playCriticalNotificationSound();
|
||||
} else {
|
||||
AudioService.playNormalNotificationSound();
|
||||
}
|
||||
}
|
||||
|
||||
const shouldShowPopup = !root.popupsDisabled && !dndBlocked && !policy.disablePopup;
|
||||
const isTransient = notif.transient;
|
||||
const shouldKeepInCenter = !isTransient && !policy.hideFromCenter;
|
||||
@@ -727,6 +709,22 @@ Singleton {
|
||||
return;
|
||||
}
|
||||
|
||||
// Honor the freedesktop "suppress-sound" hint: the sender
|
||||
// plays its own audio for this notification and asks the
|
||||
// server not to double up. "sound-name" is the opposite — an
|
||||
// explicit request for audio — so it plays even when the
|
||||
// global new-notification sound is off.
|
||||
const soundHints = notif.hints || {};
|
||||
const suppressSound = !!soundHints["suppress-sound"];
|
||||
const requestsSound = !!soundHints["sound-name"];
|
||||
if (!dndBlocked && !policy.disablePopup && SettingsData.soundsEnabled && (SettingsData.soundNewNotification || requestsSound) && !suppressSound) {
|
||||
if (policy.urgency === NotificationUrgency.Critical) {
|
||||
AudioService.playCriticalNotificationSound();
|
||||
} else {
|
||||
AudioService.playNormalNotificationSound();
|
||||
}
|
||||
}
|
||||
|
||||
const wrapper = notifComponent.createObject(root, {
|
||||
"popup": shouldShowPopup,
|
||||
"notification": notif,
|
||||
|
||||
@@ -42,11 +42,14 @@ Singleton {
|
||||
signal sessionLocked
|
||||
signal sessionUnlocked
|
||||
signal sessionResumed
|
||||
signal lidOpened
|
||||
signal loginctlStateChanged
|
||||
|
||||
property bool stateInitialized: false
|
||||
property string prepareForSleepSubscriptionId: ""
|
||||
property bool prepareForSleepSubscriptionPending: false
|
||||
property string lidSubscriptionId: ""
|
||||
property bool lidSubscriptionPending: false
|
||||
property double lastResumeSignalTimestamp: 0
|
||||
|
||||
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
|
||||
@@ -696,7 +699,7 @@ Singleton {
|
||||
if (DMSService.isConnected) {
|
||||
checkDMSCapabilities();
|
||||
} else {
|
||||
clearPrepareForSleepSubscriptionState();
|
||||
clearPowerSubscriptionState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,10 +717,11 @@ Singleton {
|
||||
}
|
||||
|
||||
function onDbusSignalReceived(subscriptionId, data) {
|
||||
if (subscriptionId !== prepareForSleepSubscriptionId) {
|
||||
return;
|
||||
if (subscriptionId === prepareForSleepSubscriptionId) {
|
||||
handlePrepareForSleepSignal(data);
|
||||
} else if (subscriptionId === lidSubscriptionId) {
|
||||
handleLidPropertiesChanged(data);
|
||||
}
|
||||
handlePrepareForSleepSignal(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,14 +783,41 @@ Singleton {
|
||||
|
||||
if (DMSService.capabilities.includes("dbus")) {
|
||||
ensurePrepareForSleepSubscription();
|
||||
ensureLidSubscription();
|
||||
} else {
|
||||
clearPrepareForSleepSubscriptionState();
|
||||
clearPowerSubscriptionState();
|
||||
}
|
||||
}
|
||||
|
||||
function clearPrepareForSleepSubscriptionState() {
|
||||
function clearPowerSubscriptionState() {
|
||||
prepareForSleepSubscriptionId = "";
|
||||
prepareForSleepSubscriptionPending = false;
|
||||
lidSubscriptionId = "";
|
||||
lidSubscriptionPending = false;
|
||||
}
|
||||
|
||||
function ensureLidSubscription() {
|
||||
if (!DMSService.isConnected || !DMSService.capabilities.includes("dbus"))
|
||||
return;
|
||||
if (lidSubscriptionId || lidSubscriptionPending)
|
||||
return;
|
||||
|
||||
lidSubscriptionPending = true;
|
||||
DMSService.dbusSubscribe("system", "org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.DBus.Properties", "PropertiesChanged", response => {
|
||||
lidSubscriptionPending = false;
|
||||
if (response.error) {
|
||||
log.warn("Failed to subscribe to lid changes:", response.error);
|
||||
return;
|
||||
}
|
||||
lidSubscriptionId = response.result?.subscriptionId || "";
|
||||
});
|
||||
}
|
||||
|
||||
function handleLidPropertiesChanged(data) {
|
||||
if (data?.path !== "/org/freedesktop/UPower" || data.body?.[0] !== "org.freedesktop.UPower")
|
||||
return;
|
||||
if (data.body?.[1]?.LidIsClosed === false)
|
||||
lidOpened();
|
||||
}
|
||||
|
||||
function ensurePrepareForSleepSubscription() {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
v1.6.2
|
||||
v1.6.3
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#%PAM-1.0
|
||||
|
||||
auth required pam_fprintd.so max-tries=5
|
||||
# timeout bounds how long a stuck fprintd session can hold the reader.
|
||||
# Modules/Lock/Pam.qml mirrors the value in sessionTimeoutMs to tell an expiry
|
||||
# apart from a device fault; change both together.
|
||||
auth required pam_fprintd.so max-tries=1 timeout=90
|
||||
account required pam_permit.so
|
||||
password required pam_deny.so
|
||||
session required pam_permit.so
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
contrast=4
|
||||
|
||||
[General]
|
||||
ColorScheme=DankMatugen
|
||||
Name=Dank Shell (matugen)
|
||||
ColorScheme=DankMatugenDark
|
||||
Name=Dank Shell (matugen) Dark
|
||||
|
||||
[ColorEffects:Disabled]
|
||||
Color={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
Color={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ColorAmount=0
|
||||
ColorEffect=0
|
||||
ContrastAmount=0.65
|
||||
@@ -16,7 +16,7 @@ IntensityEffect=2
|
||||
|
||||
[ColorEffects:Inactive]
|
||||
ChangeSelectionColor=true
|
||||
Color={{colors.outline.default.red}},{{colors.outline.default.green}},{{colors.outline.default.blue}}
|
||||
Color={{colors.outline.dark.red}},{{colors.outline.dark.green}},{{colors.outline.dark.blue}}
|
||||
ColorAmount=0.025
|
||||
ColorEffect=2
|
||||
ContrastAmount=0.1
|
||||
@@ -26,121 +26,121 @@ IntensityAmount=0
|
||||
IntensityEffect=0
|
||||
|
||||
[Colors:Button]
|
||||
BackgroundAlternate={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
BackgroundNormal={{colors.background.dark.red}},{{colors.background.dark.green}},{{colors.background.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Complementary]
|
||||
BackgroundAlternate={{colors.surface_container.default.red}},{{colors.surface_container.default.green}},{{colors.surface_container.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.surface_container.dark.red}},{{colors.surface_container.dark.green}},{{colors.surface_container.dark.blue}}
|
||||
BackgroundNormal={{colors.background.dark.red}},{{colors.background.dark.green}},{{colors.background.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Header]
|
||||
BackgroundAlternate={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
BackgroundNormal={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.background.dark.red}},{{colors.background.dark.green}},{{colors.background.dark.blue}}
|
||||
BackgroundNormal={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Header][Inactive]
|
||||
BackgroundAlternate={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
BackgroundNormal={{colors.background.dark.red}},{{colors.background.dark.green}},{{colors.background.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundAlternate={{colors.primary_container.default.red}},{{colors.primary_container.default.green}},{{colors.primary_container.default.blue}}
|
||||
BackgroundNormal={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.on_primary.default.red}},{{colors.on_primary.default.green}},{{colors.on_primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_primary.default.red}},{{colors.on_primary.default.green}},{{colors.on_primary.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.primary_container.dark.red}},{{colors.primary_container.dark.green}},{{colors.primary_container.dark.blue}}
|
||||
BackgroundNormal={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.on_primary.dark.red}},{{colors.on_primary.dark.green}},{{colors.on_primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_primary.dark.red}},{{colors.on_primary.dark.green}},{{colors.on_primary.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Tooltip]
|
||||
BackgroundAlternate={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
BackgroundNormal={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.background.dark.red}},{{colors.background.dark.green}},{{colors.background.dark.blue}}
|
||||
BackgroundNormal={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:View]
|
||||
BackgroundAlternate={{colors.surface_container_low.default.red}},{{colors.surface_container_low.default.green}},{{colors.surface_container_low.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.surface_container_low.dark.red}},{{colors.surface_container_low.dark.green}},{{colors.surface_container_low.dark.blue}}
|
||||
BackgroundNormal={{colors.background.dark.red}},{{colors.background.dark.green}},{{colors.background.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[Colors:Window]
|
||||
BackgroundAlternate={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
BackgroundNormal={{colors.background.dark.red}},{{colors.background.dark.green}},{{colors.background.dark.blue}}
|
||||
DecorationFocus={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
DecorationHover={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundActive={{colors.primary.dark.red}},{{colors.primary.dark.green}},{{colors.primary.dark.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
ForegroundLink={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundNegative={{colors.error.dark.red}},{{colors.error.dark.green}},{{colors.error.dark.blue}}
|
||||
ForegroundNeutral={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
ForegroundNormal={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
ForegroundPositive={{colors.tertiary.dark.red}},{{colors.tertiary.dark.green}},{{colors.tertiary.dark.blue}}
|
||||
ForegroundVisited={{colors.secondary.dark.red}},{{colors.secondary.dark.green}},{{colors.secondary.dark.blue}}
|
||||
|
||||
[WM]
|
||||
activeBackground={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
activeBlend={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
activeForeground={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
inactiveBackground={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
inactiveBlend={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
inactiveForeground={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
activeBackground={{colors.surface.dark.red}},{{colors.surface.dark.green}},{{colors.surface.dark.blue}}
|
||||
activeBlend={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
activeForeground={{colors.on_surface.dark.red}},{{colors.on_surface.dark.green}},{{colors.on_surface.dark.blue}}
|
||||
inactiveBackground={{colors.background.dark.red}},{{colors.background.dark.green}},{{colors.background.dark.blue}}
|
||||
inactiveBlend={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
inactiveForeground={{colors.on_surface_variant.dark.red}},{{colors.on_surface_variant.dark.green}},{{colors.on_surface_variant.dark.blue}}
|
||||
|
||||
@@ -28,9 +28,9 @@ IntensityEffect=0
|
||||
[Colors:Button]
|
||||
BackgroundAlternate={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
DecorationHover={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
ForegroundActive={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
@@ -42,9 +42,9 @@ ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.gr
|
||||
[Colors:Complementary]
|
||||
BackgroundAlternate={{colors.surface_container.default.red}},{{colors.surface_container.default.green}},{{colors.surface_container.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
DecorationHover={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
ForegroundActive={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
@@ -56,9 +56,9 @@ ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.gr
|
||||
[Colors:Header]
|
||||
BackgroundAlternate={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
BackgroundNormal={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
DecorationFocus={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
DecorationHover={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
ForegroundActive={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
@@ -70,9 +70,9 @@ ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.gr
|
||||
[Colors:Header][Inactive]
|
||||
BackgroundAlternate={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
DecorationHover={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
ForegroundActive={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
@@ -82,25 +82,25 @@ ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.gre
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundAlternate={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
BackgroundNormal={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
DecorationFocus={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
DecorationHover={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
ForegroundActive={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
BackgroundAlternate={{colors.primary_container.default.red}},{{colors.primary_container.default.green}},{{colors.primary_container.default.blue}}
|
||||
BackgroundNormal={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.on_primary.default.red}},{{colors.on_primary.default.green}},{{colors.on_primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundNormal={{colors.on_primary.default.red}},{{colors.on_primary.default.green}},{{colors.on_primary.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
|
||||
[Colors:Tooltip]
|
||||
BackgroundAlternate={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
BackgroundNormal={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
DecorationFocus={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
DecorationHover={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
ForegroundActive={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
@@ -112,9 +112,9 @@ ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.gr
|
||||
[Colors:View]
|
||||
BackgroundAlternate={{colors.surface_container_low.default.red}},{{colors.surface_container_low.default.green}},{{colors.surface_container_low.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
DecorationHover={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
ForegroundActive={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
@@ -126,9 +126,9 @@ ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.gr
|
||||
[Colors:Window]
|
||||
BackgroundAlternate={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
DecorationHover={{colors.inverse_primary.default.red}},{{colors.inverse_primary.default.green}},{{colors.inverse_primary.default.blue}}
|
||||
ForegroundActive={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
cursor {{colors.on_surface.default.hex}}
|
||||
cursor_text_color {{colors.on_surface_variant.default.hex}}
|
||||
cursor {{colors.primary.default.hex}}
|
||||
cursor_text_color {{colors.on_primary.default.hex}}
|
||||
|
||||
foreground {{colors.on_surface.default.hex}}
|
||||
background {{colors.background.default.hex}}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
contrast=4
|
||||
|
||||
[General]
|
||||
ColorScheme=DankMatugen
|
||||
Name=Dank Shell (matugen)
|
||||
ColorScheme=DankMatugenLight
|
||||
Name=Dank Shell (matugen) Light
|
||||
|
||||
[ColorEffects:Disabled]
|
||||
Color={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
Color={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ColorAmount=0
|
||||
ColorEffect=0
|
||||
ContrastAmount=0.65
|
||||
@@ -16,7 +16,7 @@ IntensityEffect=2
|
||||
|
||||
[ColorEffects:Inactive]
|
||||
ChangeSelectionColor=true
|
||||
Color={{colors.outline.default.red}},{{colors.outline.default.green}},{{colors.outline.default.blue}}
|
||||
Color={{colors.outline.light.red}},{{colors.outline.light.green}},{{colors.outline.light.blue}}
|
||||
ColorAmount=0.025
|
||||
ColorEffect=2
|
||||
ContrastAmount=0.1
|
||||
@@ -26,121 +26,121 @@ IntensityAmount=0
|
||||
IntensityEffect=0
|
||||
|
||||
[Colors:Button]
|
||||
BackgroundAlternate={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
BackgroundNormal={{colors.background.light.red}},{{colors.background.light.green}},{{colors.background.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Complementary]
|
||||
BackgroundAlternate={{colors.surface_container.default.red}},{{colors.surface_container.default.green}},{{colors.surface_container.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.surface_container.light.red}},{{colors.surface_container.light.green}},{{colors.surface_container.light.blue}}
|
||||
BackgroundNormal={{colors.background.light.red}},{{colors.background.light.green}},{{colors.background.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Header]
|
||||
BackgroundAlternate={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
BackgroundNormal={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.background.light.red}},{{colors.background.light.green}},{{colors.background.light.blue}}
|
||||
BackgroundNormal={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Header][Inactive]
|
||||
BackgroundAlternate={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
BackgroundNormal={{colors.background.light.red}},{{colors.background.light.green}},{{colors.background.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundAlternate={{colors.primary_container.default.red}},{{colors.primary_container.default.green}},{{colors.primary_container.default.blue}}
|
||||
BackgroundNormal={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.on_primary.default.red}},{{colors.on_primary.default.green}},{{colors.on_primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_primary.default.red}},{{colors.on_primary.default.green}},{{colors.on_primary.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.primary_container.light.red}},{{colors.primary_container.light.green}},{{colors.primary_container.light.blue}}
|
||||
BackgroundNormal={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.on_primary.light.red}},{{colors.on_primary.light.green}},{{colors.on_primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_primary.light.red}},{{colors.on_primary.light.green}},{{colors.on_primary.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Tooltip]
|
||||
BackgroundAlternate={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
BackgroundNormal={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.background.light.red}},{{colors.background.light.green}},{{colors.background.light.blue}}
|
||||
BackgroundNormal={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:View]
|
||||
BackgroundAlternate={{colors.surface_container_low.default.red}},{{colors.surface_container_low.default.green}},{{colors.surface_container_low.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.surface_container_low.light.red}},{{colors.surface_container_low.light.green}},{{colors.surface_container_low.light.blue}}
|
||||
BackgroundNormal={{colors.background.light.red}},{{colors.background.light.green}},{{colors.background.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[Colors:Window]
|
||||
BackgroundAlternate={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
BackgroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
ForegroundLink={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||
ForegroundVisited={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||
BackgroundAlternate={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
BackgroundNormal={{colors.background.light.red}},{{colors.background.light.green}},{{colors.background.light.blue}}
|
||||
DecorationFocus={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
DecorationHover={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundActive={{colors.primary.light.red}},{{colors.primary.light.green}},{{colors.primary.light.blue}}
|
||||
ForegroundInactive={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
ForegroundLink={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundNegative={{colors.error.light.red}},{{colors.error.light.green}},{{colors.error.light.blue}}
|
||||
ForegroundNeutral={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
ForegroundNormal={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
ForegroundPositive={{colors.tertiary.light.red}},{{colors.tertiary.light.green}},{{colors.tertiary.light.blue}}
|
||||
ForegroundVisited={{colors.secondary.light.red}},{{colors.secondary.light.green}},{{colors.secondary.light.blue}}
|
||||
|
||||
[WM]
|
||||
activeBackground={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||
activeBlend={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
activeForeground={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||
inactiveBackground={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||
inactiveBlend={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
inactiveForeground={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||
activeBackground={{colors.surface.light.red}},{{colors.surface.light.green}},{{colors.surface.light.blue}}
|
||||
activeBlend={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
activeForeground={{colors.on_surface.light.red}},{{colors.on_surface.light.green}},{{colors.on_surface.light.blue}}
|
||||
inactiveBackground={{colors.background.light.red}},{{colors.background.light.green}},{{colors.background.light.blue}}
|
||||
inactiveBlend={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
inactiveForeground={{colors.on_surface_variant.light.red}},{{colors.on_surface_variant.light.green}},{{colors.on_surface_variant.light.blue}}
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
"list.focusOutline": "{{colors.primary.dark.hex}}",
|
||||
"list.focusBackground": "{{colors.surface_container_high.dark.hex}}",
|
||||
"list.highlightForeground": "{{colors.primary.dark.hex}}",
|
||||
"list.focusHighlightForeground": "{{colors.primary.dark.hex}}",
|
||||
"list.focusHighlightForeground": "{{colors.on_primary.dark.hex}}",
|
||||
"list.errorForeground": "{{colors.error.dark.hex}}",
|
||||
"list.warningForeground": "{{colors.secondary.dark.hex}}",
|
||||
//
|
||||
@@ -82,13 +82,16 @@
|
||||
"dropdown.border": "{{colors.outline_variant.dark.hex}}",
|
||||
"quickInput.background": "{{colors.surface_container_low.dark.hex}}",
|
||||
"quickInput.foreground": "{{colors.on_surface.dark.hex}}",
|
||||
"quickInputList.focusBackground": "{{colors.primary.dark.hex}}",
|
||||
"quickInputList.focusForeground": "{{colors.on_primary.dark.hex}}",
|
||||
"quickInputList.focusIconForeground": "{{colors.on_primary.dark.hex}}",
|
||||
"widget.shadow": "{{colors.background.dark.hex}}80",
|
||||
//
|
||||
// Buttons
|
||||
//
|
||||
"button.background": "{{colors.primary.dark.hex}}",
|
||||
"button.foreground": "{{colors.on_primary.dark.hex}}",
|
||||
"button.hoverBackground": "{{colors.primary_container.dark.hex}}",
|
||||
"button.hoverBackground": "{{colors.primary_fixed.dark.hex}}",
|
||||
//
|
||||
// Status bar
|
||||
//
|
||||
@@ -158,9 +161,10 @@
|
||||
"editorHoverWidget.border": "{{colors.outline.dark.hex}}",
|
||||
"editorSuggestWidget.background": "{{colors.surface_container.dark.hex}}",
|
||||
"editorSuggestWidget.foreground": "{{colors.on_surface.dark.hex}}",
|
||||
"editorSuggestWidget.selectedBackground": "{{colors.surface_container_high.dark.hex}}",
|
||||
"editorSuggestWidget.selectedForeground": "{{colors.on_surface.dark.hex}}",
|
||||
"editorSuggestWidget.selectedBackground": "{{colors.primary.dark.hex}}",
|
||||
"editorSuggestWidget.selectedForeground": "{{colors.on_primary.dark.hex}}",
|
||||
"editorSuggestWidget.highlightForeground": "{{colors.primary.dark.hex}}",
|
||||
"editorSuggestWidget.focusHighlightForeground": "{{colors.on_primary.dark.hex}}",
|
||||
//
|
||||
// Scrollbar
|
||||
//
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"list.focusOutline": "{{colors.primary.default.hex}}",
|
||||
"list.focusBackground": "{{colors.surface_container_high.default.hex}}",
|
||||
"list.highlightForeground": "{{colors.primary.default.hex}}",
|
||||
"list.focusHighlightForeground": "{{colors.primary.default.hex}}",
|
||||
"list.focusHighlightForeground": "{{colors.on_primary.default.hex}}",
|
||||
"list.errorForeground": "{{colors.error.default.hex}}",
|
||||
"list.warningForeground": "{{colors.secondary.default.hex}}",
|
||||
"input.background": "{{colors.surface_container_low.default.hex}}",
|
||||
@@ -61,10 +61,13 @@
|
||||
"dropdown.border": "{{colors.outline_variant.default.hex}}",
|
||||
"quickInput.background": "{{colors.surface_container_low.default.hex}}",
|
||||
"quickInput.foreground": "{{colors.on_surface.default.hex}}",
|
||||
"quickInputList.focusBackground": "{{colors.primary.default.hex}}",
|
||||
"quickInputList.focusForeground": "{{colors.on_primary.default.hex}}",
|
||||
"quickInputList.focusIconForeground": "{{colors.on_primary.default.hex}}",
|
||||
"widget.shadow": "{{colors.background.default.hex}}80",
|
||||
"button.background": "{{colors.primary.default.hex}}",
|
||||
"button.foreground": "{{colors.on_primary.default.hex}}",
|
||||
"button.hoverBackground": "{{colors.primary_container.default.hex}}",
|
||||
"button.hoverBackground": "{{colors.primary_fixed.default.hex}}",
|
||||
"statusBar.background": "{{colors.background.default.hex}}",
|
||||
"statusBar.foreground": "{{colors.on_surface.default.hex}}",
|
||||
"statusBar.border": "{{colors.outline_variant.default.hex}}",
|
||||
@@ -113,9 +116,10 @@
|
||||
"editorHoverWidget.border": "{{colors.outline.default.hex}}",
|
||||
"editorSuggestWidget.background": "{{colors.surface_container.default.hex}}",
|
||||
"editorSuggestWidget.foreground": "{{colors.on_surface.default.hex}}",
|
||||
"editorSuggestWidget.selectedBackground": "{{colors.surface_container_high.default.hex}}",
|
||||
"editorSuggestWidget.selectedForeground": "{{colors.on_surface.default.hex}}",
|
||||
"editorSuggestWidget.selectedBackground": "{{colors.primary.default.hex}}",
|
||||
"editorSuggestWidget.selectedForeground": "{{colors.on_primary.default.hex}}",
|
||||
"editorSuggestWidget.highlightForeground": "{{colors.primary.default.hex}}",
|
||||
"editorSuggestWidget.focusHighlightForeground": "{{colors.on_primary.default.hex}}",
|
||||
"scrollbarSlider.background": "{{colors.on_surface_variant.default.hex}}50",
|
||||
"scrollbarSlider.hoverBackground": "{{colors.on_surface_variant.default.hex}}80",
|
||||
"scrollbarSlider.activeBackground": "{{colors.on_surface_variant.default.hex}}AA",
|
||||
|
||||
@@ -43,9 +43,10 @@
|
||||
"editorSuggestWidget.background": "{{colors.surface_container.light.hex}}",
|
||||
"editorSuggestWidget.foreground": "{{colors.on_surface.light.hex}}",
|
||||
"editorSuggestWidget.border": "{{colors.outline_variant.light.hex}}",
|
||||
"editorSuggestWidget.selectedBackground": "{{colors.surface_container_high.light.hex}}",
|
||||
"editorSuggestWidget.selectedForeground": "{{colors.on_surface.light.hex}}",
|
||||
"editorSuggestWidget.selectedBackground": "{{colors.primary.light.hex}}",
|
||||
"editorSuggestWidget.selectedForeground": "{{colors.on_primary.light.hex}}",
|
||||
"editorSuggestWidget.highlightForeground": "{{colors.primary.light.hex}}",
|
||||
"editorSuggestWidget.focusHighlightForeground": "{{colors.on_primary.light.hex}}",
|
||||
"editorGroup.border": "{{colors.outline_variant.light.hex}}",
|
||||
"editorGroup.dropBackground": "{{colors.primary_container.light.hex}}33",
|
||||
"editorGroupHeader.tabsBackground": "{{colors.background.light.hex}}",
|
||||
@@ -79,7 +80,7 @@
|
||||
"list.focusBackground": "{{colors.surface_container_high.light.hex}}",
|
||||
"list.focusForeground": "{{colors.on_surface.light.hex}}",
|
||||
"list.highlightForeground": "{{colors.primary.light.hex}}",
|
||||
"list.focusHighlightForeground": "{{colors.primary.light.hex}}",
|
||||
"list.focusHighlightForeground": "{{colors.on_primary.light.hex}}",
|
||||
"list.errorForeground": "{{colors.error.light.hex}}",
|
||||
"list.warningForeground": "{{colors.tertiary.light.hex}}",
|
||||
"statusBar.background": "{{colors.surface_container.light.hex}}",
|
||||
@@ -106,11 +107,12 @@
|
||||
"dropdown.border": "{{colors.outline_variant.light.hex}}",
|
||||
"quickInput.background": "{{colors.surface_container.light.hex}}",
|
||||
"quickInput.foreground": "{{colors.on_surface.light.hex}}",
|
||||
"quickInputList.focusBackground": "{{colors.surface_container_high.light.hex}}",
|
||||
"quickInputList.focusForeground": "{{colors.on_surface.light.hex}}",
|
||||
"quickInputList.focusBackground": "{{colors.primary.light.hex}}",
|
||||
"quickInputList.focusForeground": "{{colors.on_primary.light.hex}}",
|
||||
"quickInputList.focusIconForeground": "{{colors.on_primary.light.hex}}",
|
||||
"button.background": "{{colors.primary.light.hex}}",
|
||||
"button.foreground": "{{colors.on_primary.light.hex}}",
|
||||
"button.hoverBackground": "{{colors.primary_container.light.hex}}",
|
||||
"button.hoverBackground": "{{colors.primary_fixed.light.hex}}",
|
||||
"badge.background": "{{colors.secondary.light.hex}}",
|
||||
"badge.foreground": "{{colors.on_secondary.light.hex}}",
|
||||
"panel.background": "{{colors.surface_container.light.hex}}",
|
||||
|
||||
Reference in New Issue
Block a user