1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-31 08:52:49 -05:00

gamma: switch to wlsunset-style transitions

This commit is contained in:
bbedward
2025-12-09 09:44:16 -05:00
parent bc27253cbf
commit 5647323449
12 changed files with 854 additions and 964 deletions

View File

@@ -22,6 +22,8 @@ linters:
- (*os.Process).Signal - (*os.Process).Signal
- (*os.Process).Kill - (*os.Process).Kill
- syscall.Kill - syscall.Kill
# Seek on memfd (reset position before passing fd)
- syscall.Seek
# DBus cleanup # DBus cleanup
- (*github.com/godbus/dbus/v5.Conn).RemoveMatchSignal - (*github.com/godbus/dbus/v5.Conn).RemoveMatchSignal
- (*github.com/godbus/dbus/v5.Conn).RemoveSignal - (*github.com/godbus/dbus/v5.Conn).RemoveSignal

View File

@@ -159,6 +159,7 @@ func (n *NiriProvider) convertKeybind(kb *NiriKeyBinding, subcategory string, co
Action: rawAction, Action: rawAction,
Subcategory: subcategory, Subcategory: subcategory,
Source: source, Source: source,
HideOnOverlay: kb.HideOnOverlay,
} }
if source == "dms" && conflicts != nil { if source == "dms" && conflicts != nil {

View File

@@ -16,6 +16,7 @@ type NiriKeyBinding struct {
Action string Action string
Args []string Args []string
Description string Description string
HideOnOverlay bool
Source string Source string
} }
@@ -273,11 +274,17 @@ func (p *NiriParser) parseKeybindNode(node *document.Node, _ string) *NiriKeyBin
} }
var description string var description string
var hideOnOverlay bool
if node.Properties != nil { if node.Properties != nil {
if val, ok := node.Properties.Get("hotkey-overlay-title"); ok { if val, ok := node.Properties.Get("hotkey-overlay-title"); ok {
switch val.ValueString() {
case "null", "":
hideOnOverlay = true
default:
description = val.ValueString() description = val.ValueString()
} }
} }
}
return &NiriKeyBinding{ return &NiriKeyBinding{
Mods: mods, Mods: mods,
@@ -285,6 +292,7 @@ func (p *NiriParser) parseKeybindNode(node *document.Node, _ string) *NiriKeyBin
Action: action, Action: action,
Args: args, Args: args,
Description: description, Description: description,
HideOnOverlay: hideOnOverlay,
Source: p.currentSource, Source: p.currentSource,
} }
} }

View File

@@ -6,6 +6,7 @@ type Keybind struct {
Action string `json:"action,omitempty"` Action string `json:"action,omitempty"`
Subcategory string `json:"subcat,omitempty"` Subcategory string `json:"subcat,omitempty"`
Source string `json:"source,omitempty"` Source string `json:"source,omitempty"`
HideOnOverlay bool `json:"hideOnOverlay,omitempty"`
Conflict *Keybind `json:"conflict,omitempty"` Conflict *Keybind `json:"conflict,omitempty"`
} }

View File

@@ -238,9 +238,17 @@ func (i *ZwlrOutputManagerV1) Dispatch(opcode uint32, fd int, data []byte) {
l := 0 l := 0
objectID := client.Uint32(data[l : l+4]) objectID := client.Uint32(data[l : l+4])
proxy := i.Context().GetProxy(objectID) proxy := i.Context().GetProxy(objectID)
if proxy != nil { if proxy == nil {
e.Head = proxy.(*ZwlrOutputHeadV1) head := &ZwlrOutputHeadV1{}
head.SetContext(i.Context())
head.SetID(objectID)
registerServerProxy(i.Context(), head, objectID)
e.Head = head
} else if head, ok := proxy.(*ZwlrOutputHeadV1); ok {
e.Head = head
} else { } else {
// Stale proxy of wrong type (can happen after suspend/resume)
// Replace it with the correct type
head := &ZwlrOutputHeadV1{} head := &ZwlrOutputHeadV1{}
head.SetContext(i.Context()) head.SetContext(i.Context())
head.SetID(objectID) head.SetID(objectID)
@@ -715,9 +723,17 @@ func (i *ZwlrOutputHeadV1) Dispatch(opcode uint32, fd int, data []byte) {
l := 0 l := 0
objectID := client.Uint32(data[l : l+4]) objectID := client.Uint32(data[l : l+4])
proxy := i.Context().GetProxy(objectID) proxy := i.Context().GetProxy(objectID)
if proxy != nil { if proxy == nil {
e.Mode = proxy.(*ZwlrOutputModeV1) mode := &ZwlrOutputModeV1{}
mode.SetContext(i.Context())
mode.SetID(objectID)
registerServerProxy(i.Context(), mode, objectID)
e.Mode = mode
} else if mode, ok := proxy.(*ZwlrOutputModeV1); ok {
e.Mode = mode
} else { } else {
// Stale proxy of wrong type (can happen after suspend/resume)
// Replace it with the correct type
mode := &ZwlrOutputModeV1{} mode := &ZwlrOutputModeV1{}
mode.SetContext(i.Context()) mode.SetContext(i.Context())
mode.SetID(objectID) mode.SetID(objectID)
@@ -743,7 +759,26 @@ func (i *ZwlrOutputHeadV1) Dispatch(opcode uint32, fd int, data []byte) {
} }
var e ZwlrOutputHeadV1CurrentModeEvent var e ZwlrOutputHeadV1CurrentModeEvent
l := 0 l := 0
e.Mode = i.Context().GetProxy(client.Uint32(data[l : l+4])).(*ZwlrOutputModeV1) objectID := client.Uint32(data[l : l+4])
proxy := i.Context().GetProxy(objectID)
if proxy == nil {
// Mode not yet registered, create it
mode := &ZwlrOutputModeV1{}
mode.SetContext(i.Context())
mode.SetID(objectID)
registerServerProxy(i.Context(), mode, objectID)
e.Mode = mode
} else if mode, ok := proxy.(*ZwlrOutputModeV1); ok {
e.Mode = mode
} else {
// Stale proxy of wrong type (can happen after suspend/resume)
// Replace it with the correct type
mode := &ZwlrOutputModeV1{}
mode.SetContext(i.Context())
mode.SetID(objectID)
registerServerProxy(i.Context(), mode, objectID)
e.Mode = mode
}
l += 4 l += 4
i.currentModeHandler(e) i.currentModeHandler(e)

View File

@@ -2,8 +2,6 @@ package wayland
import ( import (
"math" "math"
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
) )
type GammaRamp struct { type GammaRamp struct {
@@ -12,6 +10,126 @@ type GammaRamp struct {
Blue []uint16 Blue []uint16
} }
type rgb struct {
r, g, b float64
}
type xyz struct {
x, y, z float64
}
func illuminantD(temp int) (float64, float64, bool) {
var x float64
switch {
case temp >= 2500 && temp <= 7000:
t := float64(temp)
x = 0.244063 + 0.09911e3/t + 2.9678e6/(t*t) - 4.6070e9/(t*t*t)
case temp > 7000 && temp <= 25000:
t := float64(temp)
x = 0.237040 + 0.24748e3/t + 1.9018e6/(t*t) - 2.0064e9/(t*t*t)
default:
return 0, 0, false
}
y := -3*(x*x) + 2.870*x - 0.275
return x, y, true
}
func planckianLocus(temp int) (float64, float64, bool) {
var x, y float64
switch {
case temp >= 1667 && temp <= 4000:
t := float64(temp)
x = -0.2661239e9/(t*t*t) - 0.2343589e6/(t*t) + 0.8776956e3/t + 0.179910
if temp <= 2222 {
y = -1.1064814*(x*x*x) - 1.34811020*(x*x) + 2.18555832*x - 0.20219683
} else {
y = -0.9549476*(x*x*x) - 1.37418593*(x*x) + 2.09137015*x - 0.16748867
}
case temp > 4000 && temp < 25000:
t := float64(temp)
x = -3.0258469e9/(t*t*t) + 2.1070379e6/(t*t) + 0.2226347e3/t + 0.240390
y = 3.0817580*(x*x*x) - 5.87338670*(x*x) + 3.75112997*x - 0.37001483
default:
return 0, 0, false
}
return x, y, true
}
func srgbGamma(value, gamma float64) float64 {
if value <= 0.0031308 {
return 12.92 * value
}
return math.Pow(1.055*value, 1.0/gamma) - 0.055
}
func clamp01(v float64) float64 {
switch {
case v > 1.0:
return 1.0
case v < 0.0:
return 0.0
default:
return v
}
}
func xyzToSRGB(c xyz) rgb {
return rgb{
r: srgbGamma(clamp01(3.2404542*c.x-1.5371385*c.y-0.4985314*c.z), 2.2),
g: srgbGamma(clamp01(-0.9692660*c.x+1.8760108*c.y+0.0415560*c.z), 2.2),
b: srgbGamma(clamp01(0.0556434*c.x-0.2040259*c.y+1.0572252*c.z), 2.2),
}
}
func normalizeRGB(c *rgb) {
maxw := math.Max(c.r, math.Max(c.g, c.b))
if maxw > 0 {
c.r /= maxw
c.g /= maxw
c.b /= maxw
}
}
func calcWhitepoint(temp int) rgb {
if temp == 6500 {
return rgb{r: 1.0, g: 1.0, b: 1.0}
}
var wp xyz
switch {
case temp >= 25000:
x, y, _ := illuminantD(25000)
wp.x = x
wp.y = y
case temp >= 4000:
x, y, _ := illuminantD(temp)
wp.x = x
wp.y = y
case temp >= 2500:
x1, y1, _ := illuminantD(temp)
x2, y2, _ := planckianLocus(temp)
factor := float64(4000-temp) / 1500.0
sineFactor := (math.Cos(math.Pi*factor) + 1.0) / 2.0
wp.x = x1*sineFactor + x2*(1.0-sineFactor)
wp.y = y1*sineFactor + y2*(1.0-sineFactor)
default:
t := temp
if t < 1667 {
t = 1667
}
x, y, _ := planckianLocus(t)
wp.x = x
wp.y = y
}
wp.z = 1.0 - wp.x - wp.y
wpRGB := xyzToSRGB(wp)
normalizeRGB(&wpRGB)
return wpRGB
}
func GenerateGammaRamp(size uint32, temp int, gamma float64) GammaRamp { func GenerateGammaRamp(size uint32, temp int, gamma float64) GammaRamp {
ramp := GammaRamp{ ramp := GammaRamp{
Red: make([]uint16, size), Red: make([]uint16, size),
@@ -19,16 +137,13 @@ func GenerateGammaRamp(size uint32, temp int, gamma float64) GammaRamp {
Blue: make([]uint16, size), Blue: make([]uint16, size),
} }
wp := calcWhitepoint(temp)
for i := uint32(0); i < size; i++ { for i := uint32(0); i < size; i++ {
val := float64(i) / float64(size-1) val := float64(i) / float64(size-1)
ramp.Red[i] = uint16(clamp01(math.Pow(val*wp.r, 1.0/gamma)) * 65535.0)
valGamma := math.Pow(val, 1.0/gamma) ramp.Green[i] = uint16(clamp01(math.Pow(val*wp.g, 1.0/gamma)) * 65535.0)
ramp.Blue[i] = uint16(clamp01(math.Pow(val*wp.b, 1.0/gamma)) * 65535.0)
r, g, b := temperatureToRGB(temp)
ramp.Red[i] = uint16(utils.Clamp(valGamma*r*65535.0, 0, 65535))
ramp.Green[i] = uint16(utils.Clamp(valGamma*g*65535.0, 0, 65535))
ramp.Blue[i] = uint16(utils.Clamp(valGamma*b*65535.0, 0, 65535))
} }
return ramp return ramp
@@ -50,39 +165,3 @@ func GenerateIdentityRamp(size uint32) GammaRamp {
return ramp return ramp
} }
func temperatureToRGB(temp int) (float64, float64, float64) {
tempK := float64(temp) / 100.0
var r, g, b float64
if tempK <= 66 {
r = 1.0
} else {
r = tempK - 60
r = 329.698727446 * math.Pow(r, -0.1332047592)
r = utils.Clamp(r, 0, 255) / 255.0
}
if tempK <= 66 {
g = tempK
g = 99.4708025861*math.Log(g) - 161.1195681661
g = utils.Clamp(g, 0, 255) / 255.0
} else {
g = tempK - 60
g = 288.1221695283 * math.Pow(g, -0.0755148492)
g = utils.Clamp(g, 0, 255) / 255.0
}
if tempK >= 66 {
b = 1.0
} else if tempK <= 19 {
b = 0.0
} else {
b = tempK - 10
b = 138.5177312231*math.Log(b) - 305.0447927307
b = utils.Clamp(b, 0, 255) / 255.0
}
return r, g, b
}

View File

@@ -54,7 +54,7 @@ func TestGenerateGammaRamp(t *testing.T) {
} }
} }
func TestTemperatureToRGB(t *testing.T) { func TestCalcWhitepoint(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
temp int temp int
@@ -67,32 +67,32 @@ func TestTemperatureToRGB(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
r, g, b := temperatureToRGB(tt.temp) wp := calcWhitepoint(tt.temp)
if r < 0 || r > 1 { if wp.r < 0 || wp.r > 1 {
t.Errorf("red out of range: %f", r) t.Errorf("red out of range: %f", wp.r)
} }
if g < 0 || g > 1 { if wp.g < 0 || wp.g > 1 {
t.Errorf("green out of range: %f", g) t.Errorf("green out of range: %f", wp.g)
} }
if b < 0 || b > 1 { if wp.b < 0 || wp.b > 1 {
t.Errorf("blue out of range: %f", b) t.Errorf("blue out of range: %f", wp.b)
} }
}) })
} }
} }
func TestTemperatureProgression(t *testing.T) { func TestWhitepointProgression(t *testing.T) {
temps := []int{3000, 4000, 5000, 6000, 6500} temps := []int{3000, 4000, 5000, 6000, 6500}
var prevBlue float64 var prevBlue float64
for i, temp := range temps { for i, temp := range temps {
_, _, b := temperatureToRGB(temp) wp := calcWhitepoint(temp)
if i > 0 && b < prevBlue { if i > 0 && wp.b < prevBlue {
t.Errorf("blue should increase with temperature, %d->%d: %f->%f", t.Errorf("blue should increase with temperature, %d->%d: %f->%f",
temps[i-1], temp, prevBlue, b) temps[i-1], temp, prevBlue, wp.b)
} }
prevBlue = b prevBlue = wp.b
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -8,79 +8,115 @@ import (
const ( const (
degToRad = math.Pi / 180.0 degToRad = math.Pi / 180.0
radToDeg = 180.0 / math.Pi radToDeg = 180.0 / math.Pi
solarNoon = 12.0
sunriseAngle = -0.833
) )
func CalculateSunTimes(lat, lon float64, date time.Time) SunTimes { type SunCondition int
utcDate := date.UTC()
year, month, day := utcDate.Date()
loc := date.Location()
dayOfYear := utcDate.YearDay() const (
SunNormal SunCondition = iota
SunMidnightSun
SunPolarNight
)
gamma := 2 * math.Pi / 365 * float64(dayOfYear-1) type SunTimes struct {
Dawn time.Time
Sunrise time.Time
Sunset time.Time
Night time.Time
}
eqTime := 229.18 * (0.000075 + func daysInYear(year int) int {
0.001868*math.Cos(gamma) - if (year%4 == 0 && year%100 != 0) || year%400 == 0 {
0.032077*math.Sin(gamma) - return 366
0.014615*math.Cos(2*gamma) - }
0.040849*math.Sin(2*gamma)) return 365
}
decl := 0.006918 - func dateOrbitAngle(t time.Time) float64 {
0.399912*math.Cos(gamma) + return 2 * math.Pi / float64(daysInYear(t.Year())) * float64(t.YearDay()-1)
0.070257*math.Sin(gamma) - }
0.006758*math.Cos(2*gamma) +
0.000907*math.Sin(2*gamma) -
0.002697*math.Cos(3*gamma) +
0.00148*math.Sin(3*gamma)
func equationOfTime(orbitAngle float64) float64 {
return 4 * (0.000075 +
0.001868*math.Cos(orbitAngle) -
0.032077*math.Sin(orbitAngle) -
0.014615*math.Cos(2*orbitAngle) -
0.040849*math.Sin(2*orbitAngle))
}
func sunDeclination(orbitAngle float64) float64 {
return 0.006918 -
0.399912*math.Cos(orbitAngle) +
0.070257*math.Sin(orbitAngle) -
0.006758*math.Cos(2*orbitAngle) +
0.000907*math.Sin(2*orbitAngle) -
0.002697*math.Cos(3*orbitAngle) +
0.00148*math.Sin(3*orbitAngle)
}
func sunHourAngle(latRad, declination, targetSunRad float64) float64 {
return math.Acos(math.Cos(targetSunRad)/
math.Cos(latRad)*math.Cos(declination) -
math.Tan(latRad)*math.Tan(declination))
}
func hourAngleToSeconds(hourAngle, eqtime float64) float64 {
return radToDeg * (4.0*math.Pi - 4*hourAngle - eqtime) * 60
}
func sunCondition(latRad, declination float64) SunCondition {
signLat := latRad >= 0
signDecl := declination >= 0
if signLat == signDecl {
return SunMidnightSun
}
return SunPolarNight
}
func CalculateSunTimesWithTwilight(lat, lon float64, date time.Time, elevTwilight, elevDaylight float64) (SunTimes, SunCondition) {
latRad := lat * degToRad latRad := lat * degToRad
elevTwilightRad := (90.833 - elevTwilight) * degToRad
elevDaylightRad := (90.833 - elevDaylight) * degToRad
cosHourAngle := (math.Sin(sunriseAngle*degToRad) - utc := date.UTC()
math.Sin(latRad)*math.Sin(decl)) / orbitAngle := dateOrbitAngle(utc)
(math.Cos(latRad) * math.Cos(decl)) decl := sunDeclination(orbitAngle)
eqtime := equationOfTime(orbitAngle)
if cosHourAngle > 1 { haTwilight := sunHourAngle(latRad, decl, elevTwilightRad)
return SunTimes{ haDaylight := sunHourAngle(latRad, decl, elevDaylightRad)
Sunrise: time.Date(year, month, day, 0, 0, 0, 0, time.UTC).In(loc),
Sunset: time.Date(year, month, day, 0, 0, 0, 0, time.UTC).In(loc), if math.IsNaN(haTwilight) || math.IsNaN(haDaylight) {
} cond := sunCondition(latRad, decl)
} return SunTimes{}, cond
if cosHourAngle < -1 {
return SunTimes{
Sunrise: time.Date(year, month, day, 0, 0, 0, 0, time.UTC).In(loc),
Sunset: time.Date(year, month, day, 23, 59, 59, 0, time.UTC).In(loc),
}
} }
hourAngle := math.Acos(cosHourAngle) * radToDeg dayStart := time.Date(utc.Year(), utc.Month(), utc.Day(), 0, 0, 0, 0, time.UTC)
lonOffset := time.Duration(-lon*4) * time.Minute
sunriseTime := solarNoon - hourAngle/15.0 - lon/15.0 - eqTime/60.0 dawnSecs := hourAngleToSeconds(math.Abs(haTwilight), eqtime)
sunsetTime := solarNoon + hourAngle/15.0 - lon/15.0 - eqTime/60.0 sunriseSecs := hourAngleToSeconds(math.Abs(haDaylight), eqtime)
sunsetSecs := hourAngleToSeconds(-math.Abs(haDaylight), eqtime)
sunrise := timeOfDayToTime(sunriseTime, year, month, day, time.UTC).In(loc) nightSecs := hourAngleToSeconds(-math.Abs(haTwilight), eqtime)
sunset := timeOfDayToTime(sunsetTime, year, month, day, time.UTC).In(loc)
return SunTimes{ return SunTimes{
Sunrise: sunrise, Dawn: dayStart.Add(time.Duration(dawnSecs)*time.Second + lonOffset).In(date.Location()),
Sunset: sunset, Sunrise: dayStart.Add(time.Duration(sunriseSecs)*time.Second + lonOffset).In(date.Location()),
} Sunset: dayStart.Add(time.Duration(sunsetSecs)*time.Second + lonOffset).In(date.Location()),
Night: dayStart.Add(time.Duration(nightSecs)*time.Second + lonOffset).In(date.Location()),
}, SunNormal
} }
func timeOfDayToTime(hours float64, year int, month time.Month, day int, loc *time.Location) time.Time { func CalculateSunTimes(lat, lon float64, date time.Time) SunTimes {
h := int(hours) times, cond := CalculateSunTimesWithTwilight(lat, lon, date, -6.0, 3.0)
m := int((hours - float64(h)) * 60) switch cond {
s := int(((hours-float64(h))*60 - float64(m)) * 60) case SunMidnightSun:
dayStart := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
if h < 0 { dayEnd := dayStart.Add(24*time.Hour - time.Second)
h += 24 return SunTimes{Dawn: dayStart, Sunrise: dayStart, Sunset: dayEnd, Night: dayEnd}
day-- case SunPolarNight:
dayStart := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
return SunTimes{Dawn: dayStart, Sunrise: dayStart, Sunset: dayStart, Night: dayStart}
} }
if h >= 24 { return times
h -= 24
day++
}
return time.Date(year, month, day, h, m, s, 0, loc)
} }

View File

@@ -340,38 +340,47 @@ func TestCalculateNextTransition(t *testing.T) {
} }
} }
func TestTimeOfDayToTime(t *testing.T) { func TestSunTimesWithTwilight(t *testing.T) {
lat := 40.7128
lon := -74.0060
date := time.Date(2024, 6, 21, 12, 0, 0, 0, time.Local)
times, cond := CalculateSunTimesWithTwilight(lat, lon, date, -6.0, 3.0)
if cond != SunNormal {
t.Errorf("expected SunNormal, got %v", cond)
}
if !times.Dawn.Before(times.Sunrise) {
t.Error("dawn should be before sunrise")
}
if !times.Sunrise.Before(times.Sunset) {
t.Error("sunrise should be before sunset")
}
if !times.Sunset.Before(times.Night) {
t.Error("sunset should be before night")
}
}
func TestSunConditions(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
hours float64 lat float64
expected time.Time date time.Time
expected SunCondition
}{ }{
{ {
name: "noon", name: "normal_conditions",
hours: 12.0, lat: 40.0,
expected: time.Date(2024, 6, 21, 12, 0, 0, 0, time.Local), date: time.Date(2024, 6, 21, 12, 0, 0, 0, time.UTC),
}, expected: SunNormal,
{
name: "half_past",
hours: 12.5,
expected: time.Date(2024, 6, 21, 12, 30, 0, 0, time.Local),
},
{
name: "early_morning",
hours: 6.25,
expected: time.Date(2024, 6, 21, 6, 15, 0, 0, time.Local),
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
result := timeOfDayToTime(tt.hours, 2024, 6, 21, time.Local) _, cond := CalculateSunTimesWithTwilight(tt.lat, 0, tt.date, -6.0, 3.0)
if cond != tt.expected {
if result.Hour() != tt.expected.Hour() { t.Errorf("expected condition %v, got %v", tt.expected, cond)
t.Errorf("hour = %d, want %d", result.Hour(), tt.expected.Hour())
}
if result.Minute() != tt.expected.Minute() {
t.Errorf("minute = %d, want %d", result.Minute(), tt.expected.Minute())
} }
}) })
} }

View File

@@ -11,6 +11,14 @@ import (
"github.com/godbus/dbus/v5" "github.com/godbus/dbus/v5"
) )
type GammaState int
const (
StateNormal GammaState = iota
StateTransition
StateStatic
)
type Config struct { type Config struct {
Outputs []string Outputs []string
LowTemp int LowTemp int
@@ -23,6 +31,8 @@ type Config struct {
ManualDuration *time.Duration ManualDuration *time.Duration
Gamma float64 Gamma float64
Enabled bool Enabled bool
ElevationTwilight float64
ElevationDaylight float64
} }
type State struct { type State struct {
@@ -31,13 +41,24 @@ type State struct {
NextTransition time.Time `json:"nextTransition"` NextTransition time.Time `json:"nextTransition"`
SunriseTime time.Time `json:"sunriseTime"` SunriseTime time.Time `json:"sunriseTime"`
SunsetTime time.Time `json:"sunsetTime"` SunsetTime time.Time `json:"sunsetTime"`
DawnTime time.Time `json:"dawnTime"`
NightTime time.Time `json:"nightTime"`
IsDay bool `json:"isDay"` IsDay bool `json:"isDay"`
SunPosition float64 `json:"sunPosition"`
} }
type cmd struct { type cmd struct {
fn func() fn func()
} }
type sunSchedule struct {
times SunTimes
condition SunCondition
dawnStepTime time.Duration
nightStepTime time.Duration
calcDay time.Time
}
type Manager struct { type Manager struct {
config Config config Config
configMutex sync.RWMutex configMutex sync.RWMutex
@@ -60,10 +81,9 @@ type Manager struct {
updateTrigger chan struct{} updateTrigger chan struct{}
wg sync.WaitGroup wg sync.WaitGroup
currentTemp int schedule sunSchedule
targetTemp int scheduleMutex sync.RWMutex
transitionMutex sync.RWMutex gammaState GammaState
transitionChan chan int
cachedIPLat *float64 cachedIPLat *float64
cachedIPLon *float64 cachedIPLon *float64
@@ -80,7 +100,6 @@ type Manager struct {
type outputState struct { type outputState struct {
id uint32 id uint32
name string
registryName uint32 registryName uint32
output *wlclient.Output output *wlclient.Output
gammaControl any gammaControl any
@@ -91,11 +110,6 @@ type outputState struct {
lastFailTime time.Time lastFailTime time.Time
} }
type SunTimes struct {
Sunrise time.Time
Sunset time.Time
}
func DefaultConfig() Config { func DefaultConfig() Config {
return Config{ return Config{
Outputs: []string{}, Outputs: []string{},
@@ -103,6 +117,8 @@ func DefaultConfig() Config {
HighTemp: 6500, HighTemp: 6500,
Gamma: 1.0, Gamma: 1.0,
Enabled: false, Enabled: false,
ElevationTwilight: -6.0,
ElevationDaylight: 3.0,
} }
} }
@@ -140,8 +156,7 @@ func (m *Manager) GetState() State {
if m.state == nil { if m.state == nil {
return State{} return State{}
} }
stateCopy := *m.state return *m.state
return stateCopy
} }
func (m *Manager) Subscribe(id string) chan State { func (m *Manager) Subscribe(id string) chan State {
@@ -185,5 +200,8 @@ func stateChanged(old, new *State) bool {
if old.Config.Enabled != new.Config.Enabled { if old.Config.Enabled != new.Config.Enabled {
return true return true
} }
if old.SunPosition != new.SunPosition {
return true
}
return false return false
} }

View File

@@ -96,6 +96,8 @@ DankModal {
for (let i = 0; i < binds.length; i++) { for (let i = 0; i < binds.length; i++) {
const bind = binds[i]; const bind = binds[i];
if (bind.hideOnOverlay)
continue;
if (bind.subcat) { if (bind.subcat) {
hasSubcats = true; hasSubcats = true;
if (!subcats[bind.subcat]) if (!subcats[bind.subcat])
@@ -108,6 +110,9 @@ DankModal {
} }
} }
if (Object.keys(subcats).length === 0)
continue;
processed[cat] = { processed[cat] = {
hasSubcats: hasSubcats, hasSubcats: hasSubcats,
subcats: subcats, subcats: subcats,