[
  {
    "path": ".travis.yml",
    "content": "sudo: required\n\nservices:\n  - docker\n\nlanguage: go\ngo: 1.10.x\n\nscript:\n  - docker build -t sct .\n  - docker run --rm -it sct ./test.sh\n\naddons:\n  apt:\n    packages:\n    - xorg-dev\n    - libglu1-mesa-dev\n\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM golang:1.10\n\nRUN apt-get update && apt-get install -y curl xvfb xorg-dev libglu1-mesa-dev\n\nWORKDIR /go/src/github.com/d4l3k/go-sct\nCOPY . .\n\nRUN go get -d -v ./...\nRUN go install -v ./...\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (C) 2015 Tristan Rice\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# go-sct [![GoDoc](https://godoc.org/github.com/d4l3k/go-sct?status.svg)](https://godoc.org/github.com/d4l3k/go-sct)\n\nA color temperature setting library and CLI that operates in a similar way to f.lux and Redshift.\n\nThe command line app automatically determines your location using GeoIP and adjusts the color temperature depending on time.\n\nFor wayland support replace all commands with `waysct` instead of `sct`.\n\n```sh\n# For X11 and Windows\n$ go install github.com/d4l3k/go-sct/cmd/sct@latest\n\n$ sct # Launch in background\n$ sct 3000 # One time temperature change. Temperature must be 1000-10000.\n\n# For Wayland\n$ go install github.com/d4l3k/go-sct/cmd/waysct@latest\n\n$ waysct # Launch in background\n$ waysct 3000 # One time temperature change. Wayland requires a persistent manager so this will immediately revert.\n```\n\n## Windows\nBy default, the lowest color temperature allowed is around 4500K. More\ninformation is available [here](http://jonls.dk/2010/09/windows-gamma-adjustments/)\n\nThere is a workaround to allow all possible adjustments by alterting the registry.\n\n```\nWindows Registry Editor Version 5.00\n\n[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ICM]\n\"GdiIcmGammaRange\"=dword:00000100\n```\nSave the above as a file with a \".reg\" extension and double click to apply.\n\n## Credit\nSetting the color temperature uses a port of [sct](http://www.tedunangst.com/flak/post/sct-set-color-temperature) in Go. Credit goes to him for figuring out how to do this.\n\ngo-sct also provides the `geoip` package which is a packaged version of\nhttp://devdungeon.com/content/ip-geolocation-go\n\n## License\nMade by [Tristan Rice](https://fn.lc).\n\ngo-sct is licensed under the MIT license. `geoip` and `sct` are copyrighted by their respective owners.\n\n`waysct` is using code from the redshift implementation and is licensed under\nGPLv3. See https://github.com/minus7/redshift/commit/7da875d34854a6a34612d5ce4bd8718c32bec804\n\n"
  },
  {
    "path": "cmd/sct/sct.go",
    "content": "package main\n\nimport (\n\t\"github.com/d4l3k/go-sct\"\n\t\"github.com/d4l3k/go-sct/sctcli\"\n)\n\nfunc main() {\n\tsctcli.Main(func(temp int) error {\n\t\tsct.SetColorTemp(temp)\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "cmd/waysct/waysct.go",
    "content": "// waysct is a set color temp utility for Wayland.\npackage main\n\nimport (\n\t\"log\"\n\n\t\"github.com/d4l3k/go-sct/sctcli\"\n\t\"github.com/d4l3k/go-sct/waysct\"\n)\n\nfunc main() {\n\tm, err := waysct.StartManager()\n\tif err != nil {\n\t\tlog.Fatalf(\"%+v\", err)\n\t}\n\tdefer m.Close()\n\tsctcli.Main(m.SetColorTemp)\n}\n"
  },
  {
    "path": "geoip/geoip.go",
    "content": "// geoip returns the lat/lng of the target IP address (or current machine)\npackage geoip\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\ntype GeoIP struct {\n\tCity      string  `json:\"city\"`\n\tLatitude  float64 `json:\"lat\"`\n\tLongitude float64 `json:\"lon\"`\n}\n\n// LookupIP looks up the geolocation information for the specified address (\"\" for current host).\nfunc LookupIP(address string) (*GeoIP, error) {\n\tresponse, err := http.Get(fmt.Sprintf(\"http://ip-api.com/json/%s?fields=lat,lon,city\", address))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer response.Body.Close()\n\n\tvar geo GeoIP\n\tif err := json.NewDecoder(response.Body).Decode(&geo); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &geo, nil\n}\n"
  },
  {
    "path": "geoip/geoip_test.go",
    "content": "package geoip\n\nimport \"testing\"\n\nfunc TestLookupIP(t *testing.T) {\n\tgeo, err := LookupIP(\"\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif geo.Latitude == 0 {\n\t\tt.Fatalf(\"expected non empty Latitude: %+v\", geo)\n\t}\n\tif geo.Longitude == 0 {\n\t\tt.Fatalf(\"expected non empty Longitude: %+v\", geo)\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/d4l3k/go-sct\n\ngo 1.16\n\nrequire (\n\tgithub.com/cpucycle/astrotime v0.0.0-20120927164819-9c7d514efdb5\n\tgithub.com/pkg/errors v0.9.1\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/cpucycle/astrotime v0.0.0-20120927164819-9c7d514efdb5 h1:Z6YGTs9TwkwIVCltpgUNFa+L5SAyGfIL9gH0RB7yDgw=\ngithub.com/cpucycle/astrotime v0.0.0-20120927164819-9c7d514efdb5/go.mod h1:O+WdStE4WLD+lG5MaDRl9dSJR7w4HZJrJbqVsOEx5Lw=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\n"
  },
  {
    "path": "sct.go",
    "content": "package sct\n\nimport \"runtime\"\n\ntype color struct {\n\tr, g, b float64\n}\n\n/* cribbed from redshift, but truncated with 500K steps */\nvar whitepoints = []color{\n\t{1.00000000, 0.18172716, 0.00000000}, /* 1000K */\n\t{1.00000000, 0.42322816, 0.00000000},\n\t{1.00000000, 0.54360078, 0.08679949},\n\t{1.00000000, 0.64373109, 0.28819679},\n\t{1.00000000, 0.71976951, 0.42860152},\n\t{1.00000000, 0.77987699, 0.54642268},\n\t{1.00000000, 0.82854786, 0.64816570},\n\t{1.00000000, 0.86860704, 0.73688797},\n\t{1.00000000, 0.90198230, 0.81465502},\n\t{1.00000000, 0.93853986, 0.88130458},\n\t{1.00000000, 0.97107439, 0.94305985},\n\t{1.00000000, 1.00000000, 1.00000000}, /* 6500K */\n\t{0.95160805, 0.96983355, 1.00000000},\n\t{0.91194747, 0.94470005, 1.00000000},\n\t{0.87906581, 0.92357340, 1.00000000},\n\t{0.85139976, 0.90559011, 1.00000000},\n\t{0.82782969, 0.89011714, 1.00000000},\n\t{0.80753191, 0.87667891, 1.00000000},\n\t{0.78988728, 0.86491137, 1.00000000}, /* 10000K */\n\t{0.77442176, 0.85453121, 1.00000000},\n}\n\n// SetColorTemp changes the monitor colors to reflect the specified color temperature.\nfunc SetColorTemp(temp int) {\n\t// An attempt to fix https://github.com/d4l3k/go-sct/issues/9\n\tif runtime.GOOS == \"windows\" {\n\t\tsetColorTemp(temp + 1)\n\t}\n\tsetColorTemp(temp)\n}\n\nfunc setColorTemp(temp int) {\n\tif temp < 1000 || temp > 10000 {\n\t\ttemp = 6500\n\t}\n\ttemp -= 1000\n\tratio := float64((temp-1000)%500) / 500.0\n\tpoint := whitepoints[temp/500]\n\tpoint1 := whitepoints[(temp/500)+1]\n\tgammar := point.r*(1-ratio) + point1.r*ratio\n\tgammag := point.g*(1-ratio) + point1.g*ratio\n\tgammab := point.b*(1-ratio) + point1.b*ratio\n\tsetColorGamma(gammar, gammag, gammab)\n}\n"
  },
  {
    "path": "sct_nix.go",
    "content": "// +build linux freebsd openbsd\n\npackage sct\n\n// #cgo CFLAGS: -I/usr/X11R6/include -I/usr/local/include\n// #cgo LDFLAGS: -L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXrandr\n// #include <stdio.h>\n// #include <strings.h>\n// #include <string.h>\n// #include <stdlib.h>\n// #include <stdint.h>\n// #include <inttypes.h>\n// #include <stdarg.h>\n// #include <math.h>\n// #include <X11/Xlib.h>\n// #include <X11/Xlibint.h>\n// #include <X11/Xproto.h>\n// #include <X11/Xatom.h>\n// #include <X11/extensions/Xrandr.h>\n// #include <X11/extensions/Xrender.h>\n// Window RootWindowMacro(Display * dpy, int scr) {\n//   return RootWindow(dpy, scr);\n// }\n// RRCrtc crtcxid(RRCrtc * crtcs, int i) {\n//\t return crtcs[i];\n// }\n// void ushortSet(ushort * s, int k, double v) {\n//\t s[k] = (ushort)v;\n// }\n// ushort* ushortCast(void* s) {\n// \treturn (ushort*)s;\n// }\n// int screenCount(Display * dpy) {\n//   return XScreenCount(dpy);\n// }\nimport \"C\"\nimport \"unsafe\"\n\n// setColorGamma changes the Xrandr colors to reflect the specified color temperature.\nfunc setColorGamma(gammar, gammag, gammab float64) {\n\tdpy := C.XOpenDisplay(nil)\n\tscreenCount := C.screenCount(dpy)\n\tfor screen := C.int(0); screen < screenCount; screen++ {\n\t\troot := C.RootWindowMacro(dpy, screen)\n\n\t\tres := C.XRRGetScreenResourcesCurrent(dpy, root)\n\n\t\tfor c := C.int(0); c < res.ncrtc; c++ {\n\t\t\tcrtcxid := C.crtcxid(res.crtcs, c)\n\n\t\t\tsize := C.XRRGetCrtcGammaSize(dpy, crtcxid)\n\t\t\tcrtc_gamma := C.XRRAllocGamma(size)\n\t\t\tfor i := C.int(0); i < size; i++ {\n\t\t\t\tg := 65535.0 * float64(i) / float64(size)\n\t\t\t\tC.ushortSet(C.ushortCast(unsafe.Pointer(crtc_gamma.red)), i, C.double(g*gammar))\n\t\t\t\tC.ushortSet(C.ushortCast(unsafe.Pointer(crtc_gamma.green)), i, C.double(g*gammag))\n\t\t\t\tC.ushortSet(C.ushortCast(unsafe.Pointer(crtc_gamma.blue)), i, C.double(g*gammab))\n\t\t\t}\n\t\t\tC.XRRSetCrtcGamma(dpy, crtcxid, crtc_gamma)\n\t\t\tC.XFree(unsafe.Pointer(crtc_gamma))\n\t\t}\n\n\t\tC.XFree(unsafe.Pointer(res))\n\t}\n\tC.XCloseDisplay(dpy)\n}\n"
  },
  {
    "path": "sct_windows.go",
    "content": "package sct\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n)\n\n// setColorGamma changes the device gamma curve colors to reflect the specified color temperature.\nfunc setColorGamma(gammar, gammag, gammab float64) {\n\tuser32 := syscall.NewLazyDLL(\"User32.dll\")\n\tgdi32 := syscall.NewLazyDLL(\"Gdi32.dll\")\n\tprocGetDC := user32.NewProc(\"GetDC\")\n\tprocSetDeviceGammaRamp := gdi32.NewProc(\"SetDeviceGammaRamp\")\n\n\tvar gamma [3][256]uint16\n\n\tfor i := 0; i < 256; i++ {\n\t\tg := 65535.0 * float64(i) / float64(256)\n\t\tgamma[0][i] = uint16(g * gammar)\n\t\tgamma[1][i] = uint16(g * gammag)\n\t\tgamma[2][i] = uint16(g * gammab)\n\t}\n\n\thdc, _, _ := procGetDC.Call(uintptr(0))\n\n\tprocSetDeviceGammaRamp.Call(hdc, uintptr(unsafe.Pointer(&gamma)))\n}\n"
  },
  {
    "path": "sctcli/sctcli.go",
    "content": "package sctcli\n\nimport (\n\t\"flag\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/cpucycle/astrotime\"\n\t\"github.com/d4l3k/go-sct/geoip\"\n)\n\nvar dayTemp = flag.Int(\"dayTemp\", 6500, \"The color temperature during the day.\")\nvar nightTemp = flag.Int(\"nightTemp\", 3000, \"The color temperature during the night.\")\nvar daemon = flag.Bool(\"d\", true, \"run app as a daemon\")\nvar mode = flag.String(\"mode\", \"geoip\", \"Mode of daemon (geoip or timed). Timed mode uses specified sunrise-time, midday-time, and sunset-time.\")\nvar sunriseTimeStr = flag.String(\"sunrise-time\", \"06:30\", \"Sunrise time (HH:MM)\")\nvar sunsetTimeStr = flag.String(\"sunset-time\", \"21:00\", \"Sunset time (HH:MM)\")\nvar middayTimeStr = flag.String(\"midday-time\", \"14:00\", \"Mid day (brightest) time (HH:MM)\")\nvar midnight, sunriseTime, sunsetTime, middayTime time.Time\n\nfunc Main(setColorTemp func(temp int) error) {\n\tc := SCTCLI{\n\t\tSetColorTemp: setColorTemp,\n\t}\n\tif err := c.Run(); err != nil {\n\t\tlog.Fatalf(\"%+v\", err)\n\t}\n}\n\ntype SCTCLI struct {\n\tSetColorTemp func(temp int) error\n}\n\nfunc (c SCTCLI) monitorGeo() error {\n\tlog.Printf(\"Fetching location...\")\n\tgeo, err := geoip.LookupIP(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Printf(\" - City: %s, Lat: %f, Lon: %f\", geo.City, geo.Latitude, geo.Longitude)\n\tlog.Printf(\"Monitoring daylight settings...\")\n\tvar lastState *bool\n\tfor {\n\t\trise := astrotime.NextSunrise(time.Now(), geo.Latitude, -geo.Longitude)\n\t\tset := astrotime.NextSunset(time.Now(), geo.Latitude, -geo.Longitude)\n\t\tstate := rise.Before(set)\n\t\tif lastState != nil && state == *lastState {\n\t\t\ttime.Sleep(1 * time.Minute)\n\t\t\tcontinue\n\t\t}\n\t\tlastState = &state\n\t\tif state {\n\t\t\tlog.Print(\"Good night!\")\n\t\t\tif err := c.interpolateColorTemp(*nightTemp); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tlog.Print(\"Good morning!\")\n\t\t\tif err := c.interpolateColorTemp(*dayTemp); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n}\n\nvar (\n\ttotalTime = 3 * time.Second\n\tstepEvery = 1 * time.Second / 60\n)\n\nfunc (c SCTCLI) interpolateColorTemp(new int) error {\n\told, err := getCurrentColorTemp()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsteps := int(totalTime / stepEvery)\n\tstepSize := (new - old) / steps\n\tfor i := 0; i < steps; i++ {\n\t\ttimer := time.After(stepEvery)\n\t\tif err := c.SetColorTemp(old + stepSize*i); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t<-timer\n\t}\n\tif err := c.SetColorTemp(new); err != nil {\n\t\treturn err\n\t}\n\n\treturn saveCurrentColorTemp(new)\n}\n\nfunc tempFile() string {\n\tdisplay := os.Getenv(\"DISPLAY\")\n\treturn filepath.Join(os.TempDir(), \"sct-temp-\"+display)\n}\n\nfunc saveCurrentColorTemp(temp int) error {\n\treturn ioutil.WriteFile(tempFile(), []byte(strconv.Itoa(temp)), 0644)\n}\n\nfunc getCurrentColorTemp() (int, error) {\n\tbody, err := ioutil.ReadFile(tempFile())\n\tif os.IsNotExist(err) {\n\t\treturn *dayTemp, nil\n\t} else if err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strconv.Atoi(string(body))\n}\n\nfunc (c SCTCLI) monitorTime() error {\n\tvar monitorTemperature int\n\tmonitorTemperature = 6500\n\n\tfor {\n\t\tcurTime := time.Now()\n\t\tmidnight = time.Date(curTime.Year(), curTime.Month(), curTime.Day(), 0, 0, 0, 0, time.Local)\n\n\t\t// Advance the day?\n\t\tif midnight.After(sunsetTime) {\n\t\t\tsunriseTime = sunriseTime.AddDate(0, 0, 1)\n\t\t\tmiddayTime = middayTime.AddDate(0, 0, 1)\n\t\t\tsunsetTime = sunsetTime.AddDate(0, 0, 1)\n\t\t}\n\n\t\tif curTime.After(sunriseTime) && curTime.Before(sunsetTime) {\n\t\t\tif curTime.Before(middayTime) {\n\t\t\t\telapsedDuration := curTime.Sub(sunriseTime)\n\t\t\t\tratio := float64(elapsedDuration) / float64(middayTime.Sub(sunriseTime))\n\t\t\t\tmonitorTemperature = int(float64(*nightTemp)*(1-ratio) + float64(*dayTemp)*ratio)\n\t\t\t} else {\n\t\t\t\telapsedDuration := curTime.Sub(middayTime)\n\t\t\t\tratio := float64(elapsedDuration) / float64(sunsetTime.Sub(middayTime))\n\t\t\t\tmonitorTemperature = int(float64(*dayTemp)*(1-ratio) + float64(*nightTemp)*ratio)\n\t\t\t}\n\t\t} else {\n\t\t\tmonitorTemperature = *nightTemp\n\t\t}\n\t\tif err := c.SetColorTemp(monitorTemperature); err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttime.Sleep(10 * time.Minute)\n\t}\n}\n\nfunc (c SCTCLI) Run() error {\n\tflag.Parse()\n\targs := flag.Args()\n\n\tif len(args) == 1 {\n\t\tif temp, err := strconv.Atoi(args[0]); err == nil {\n\t\t\tif err := c.interpolateColorTemp(temp); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t} else if len(args) == 0 {\n\t\t// Parse time arguments\n\t\tcurTime := time.Now()\n\t\tmidnight = time.Date(curTime.Year(), curTime.Month(), curTime.Day(), 0, 0, 0, 0, time.Local)\n\t\tvar perr error\n\t\tif sunriseTime, perr = time.ParseInLocation(\"2006-01-02 15:04\", midnight.Format(\"2006-01-02 \")+*sunriseTimeStr, time.Local); perr != nil {\n\t\t\treturn perr\n\t\t}\n\t\tif sunsetTime, perr = time.ParseInLocation(\"2006-01-02 15:04\", midnight.Format(\"2006-01-02 \")+*sunsetTimeStr, time.Local); perr != nil {\n\t\t\treturn perr\n\t\t}\n\t\tif middayTime, perr = time.ParseInLocation(\"2006-01-02 15:04\", midnight.Format(\"2006-01-02 \")+*middayTimeStr, time.Local); perr != nil {\n\t\t\treturn perr\n\t\t}\n\n\t\tif *daemon {\n\t\t\targs := os.Args[1:]\n\t\t\tfor i := 0; i < len(args); i++ {\n\t\t\t\tif strings.HasPrefix(args[i], \"-d\") {\n\t\t\t\t\targs[i] = \"-d=false\"\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\targs = append(args, \"-d=false\")\n\t\t\tcmd := exec.Command(os.Args[0], args...)\n\t\t\tcmd.Start()\n\t\t\tlog.Println(\"Launched background process... pid\", cmd.Process.Pid)\n\t\t\tos.Exit(0)\n\t\t} else {\n\t\t\tswitch *mode {\n\t\t\tcase \"timed\":\n\t\t\t\tif err := c.monitorTime(); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tif err := c.monitorGeo(); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "sctcli/sctcli_test.go",
    "content": "package sctcli\n\nimport (\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc setDisplay(val string) func() {\n\told := os.Getenv(\"DISPLAY\")\n\tos.Setenv(\"DISPLAY\", val)\n\treturn func() {\n\t\tos.Setenv(\"DISPLAY\", old)\n\t}\n}\n\nfunc TestGetCurrentColorTemp(t *testing.T) {\n\tdefer setDisplay(\"testdisplay\" + time.Now().String())()\n\n\ttemp, err := getCurrentColorTemp()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif temp != *dayTemp {\n\t\tt.Fatalf(\"expected default day temp\")\n\t}\n\n\tfor _, want := range []int{3000, 6500} {\n\t\tif err := saveCurrentColorTemp(want); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\ttemp, err := getCurrentColorTemp()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif temp != want {\n\t\t\tt.Fatalf(\"expected %d temp\", want)\n\t\t}\n\t}\n}\n\nfunc TestInterpolate(t *testing.T) {\n\ttotalTime = 100 * time.Millisecond\n\n\tc := SCTCLI{SetColorTemp: func(temp int) error { return nil }}\n\n\tfor _, want := range []int{3000, 6500} {\n\t\tif err := c.interpolateColorTemp(want); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\ttemp, err := getCurrentColorTemp()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif temp != want {\n\t\t\tt.Fatalf(\"expected %d temp\", want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "test.sh",
    "content": "#!/bin/bash\n\nxvfb-run go test -v ./...\n"
  },
  {
    "path": "waysct/gamma-control-client-protocol.h",
    "content": "/* Generated by wayland-scanner 1.19.0 */\n\n#ifndef WLR_GAMMA_CONTROL_UNSTABLE_V1_CLIENT_PROTOCOL_H\n#define WLR_GAMMA_CONTROL_UNSTABLE_V1_CLIENT_PROTOCOL_H\n\n#include <stdint.h>\n#include <stddef.h>\n#include \"wayland-client.h\"\n\n#ifdef  __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @page page_wlr_gamma_control_unstable_v1 The wlr_gamma_control_unstable_v1 protocol\n * manage gamma tables of outputs\n *\n * @section page_desc_wlr_gamma_control_unstable_v1 Description\n *\n * This protocol allows a privileged client to set the gamma tables for\n * outputs.\n *\n * Warning! The protocol described in this file is experimental and\n * backward incompatible changes may be made. Backward compatible changes\n * may be added together with the corresponding interface version bump.\n * Backward incompatible changes are done by bumping the version number in\n * the protocol and interface names and resetting the interface version.\n * Once the protocol is to be declared stable, the 'z' prefix and the\n * version number in the protocol and interface names are removed and the\n * interface version number is reset.\n *\n * @section page_ifaces_wlr_gamma_control_unstable_v1 Interfaces\n * - @subpage page_iface_zwlr_gamma_control_manager_v1 - manager to create per-output gamma controls\n * - @subpage page_iface_zwlr_gamma_control_v1 - adjust gamma tables for an output\n * @section page_copyright_wlr_gamma_control_unstable_v1 Copyright\n * <pre>\n *\n * Copyright © 2015 Giulio camuffo\n * Copyright © 2018 Simon Ser\n *\n * Permission to use, copy, modify, distribute, and sell this\n * software and its documentation for any purpose is hereby granted\n * without fee, provided that the above copyright notice appear in\n * all copies and that both that copyright notice and this permission\n * notice appear in supporting documentation, and that the name of\n * the copyright holders not be used in advertising or publicity\n * pertaining to distribution of the software without specific,\n * written prior permission.  The copyright holders make no\n * representations about the suitability of this software for any\n * purpose.  It is provided \"as is\" without express or implied\n * warranty.\n *\n * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS\n * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\n * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN\n * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\n * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\n * THIS SOFTWARE.\n * </pre>\n */\nstruct wl_output;\nstruct zwlr_gamma_control_manager_v1;\nstruct zwlr_gamma_control_v1;\n\n#ifndef ZWLR_GAMMA_CONTROL_MANAGER_V1_INTERFACE\n#define ZWLR_GAMMA_CONTROL_MANAGER_V1_INTERFACE\n/**\n * @page page_iface_zwlr_gamma_control_manager_v1 zwlr_gamma_control_manager_v1\n * @section page_iface_zwlr_gamma_control_manager_v1_desc Description\n *\n * This interface is a manager that allows creating per-output gamma\n * controls.\n * @section page_iface_zwlr_gamma_control_manager_v1_api API\n * See @ref iface_zwlr_gamma_control_manager_v1.\n */\n/**\n * @defgroup iface_zwlr_gamma_control_manager_v1 The zwlr_gamma_control_manager_v1 interface\n *\n * This interface is a manager that allows creating per-output gamma\n * controls.\n */\nextern const struct wl_interface zwlr_gamma_control_manager_v1_interface;\n#endif\n#ifndef ZWLR_GAMMA_CONTROL_V1_INTERFACE\n#define ZWLR_GAMMA_CONTROL_V1_INTERFACE\n/**\n * @page page_iface_zwlr_gamma_control_v1 zwlr_gamma_control_v1\n * @section page_iface_zwlr_gamma_control_v1_desc Description\n *\n * This interface allows a client to adjust gamma tables for a particular\n * output.\n *\n * The client will receive the gamma size, and will then be able to set gamma\n * tables. At any time the compositor can send a failed event indicating that\n * this object is no longer valid.\n *\n * There must always be at most one gamma control object per output, which\n * has exclusive access to this particular output. When the gamma control\n * object is destroyed, the gamma table is restored to its original value.\n * @section page_iface_zwlr_gamma_control_v1_api API\n * See @ref iface_zwlr_gamma_control_v1.\n */\n/**\n * @defgroup iface_zwlr_gamma_control_v1 The zwlr_gamma_control_v1 interface\n *\n * This interface allows a client to adjust gamma tables for a particular\n * output.\n *\n * The client will receive the gamma size, and will then be able to set gamma\n * tables. At any time the compositor can send a failed event indicating that\n * this object is no longer valid.\n *\n * There must always be at most one gamma control object per output, which\n * has exclusive access to this particular output. When the gamma control\n * object is destroyed, the gamma table is restored to its original value.\n */\nextern const struct wl_interface zwlr_gamma_control_v1_interface;\n#endif\n\n#define ZWLR_GAMMA_CONTROL_MANAGER_V1_GET_GAMMA_CONTROL 0\n#define ZWLR_GAMMA_CONTROL_MANAGER_V1_DESTROY 1\n\n\n/**\n * @ingroup iface_zwlr_gamma_control_manager_v1\n */\n#define ZWLR_GAMMA_CONTROL_MANAGER_V1_GET_GAMMA_CONTROL_SINCE_VERSION 1\n/**\n * @ingroup iface_zwlr_gamma_control_manager_v1\n */\n#define ZWLR_GAMMA_CONTROL_MANAGER_V1_DESTROY_SINCE_VERSION 1\n\n/** @ingroup iface_zwlr_gamma_control_manager_v1 */\nstatic inline void\nzwlr_gamma_control_manager_v1_set_user_data(struct zwlr_gamma_control_manager_v1 *zwlr_gamma_control_manager_v1, void *user_data)\n{\n\twl_proxy_set_user_data((struct wl_proxy *) zwlr_gamma_control_manager_v1, user_data);\n}\n\n/** @ingroup iface_zwlr_gamma_control_manager_v1 */\nstatic inline void *\nzwlr_gamma_control_manager_v1_get_user_data(struct zwlr_gamma_control_manager_v1 *zwlr_gamma_control_manager_v1)\n{\n\treturn wl_proxy_get_user_data((struct wl_proxy *) zwlr_gamma_control_manager_v1);\n}\n\nstatic inline uint32_t\nzwlr_gamma_control_manager_v1_get_version(struct zwlr_gamma_control_manager_v1 *zwlr_gamma_control_manager_v1)\n{\n\treturn wl_proxy_get_version((struct wl_proxy *) zwlr_gamma_control_manager_v1);\n}\n\n/**\n * @ingroup iface_zwlr_gamma_control_manager_v1\n *\n * Create a gamma control that can be used to adjust gamma tables for the\n * provided output.\n */\nstatic inline struct zwlr_gamma_control_v1 *\nzwlr_gamma_control_manager_v1_get_gamma_control(struct zwlr_gamma_control_manager_v1 *zwlr_gamma_control_manager_v1, struct wl_output *output)\n{\n\tstruct wl_proxy *id;\n\n\tid = wl_proxy_marshal_constructor((struct wl_proxy *) zwlr_gamma_control_manager_v1,\n\t\t\t ZWLR_GAMMA_CONTROL_MANAGER_V1_GET_GAMMA_CONTROL, &zwlr_gamma_control_v1_interface, NULL, output);\n\n\treturn (struct zwlr_gamma_control_v1 *) id;\n}\n\n/**\n * @ingroup iface_zwlr_gamma_control_manager_v1\n *\n * All objects created by the manager will still remain valid, until their\n * appropriate destroy request has been called.\n */\nstatic inline void\nzwlr_gamma_control_manager_v1_destroy(struct zwlr_gamma_control_manager_v1 *zwlr_gamma_control_manager_v1)\n{\n\twl_proxy_marshal((struct wl_proxy *) zwlr_gamma_control_manager_v1,\n\t\t\t ZWLR_GAMMA_CONTROL_MANAGER_V1_DESTROY);\n\n\twl_proxy_destroy((struct wl_proxy *) zwlr_gamma_control_manager_v1);\n}\n\n#ifndef ZWLR_GAMMA_CONTROL_V1_ERROR_ENUM\n#define ZWLR_GAMMA_CONTROL_V1_ERROR_ENUM\nenum zwlr_gamma_control_v1_error {\n\t/**\n\t * invalid gamma tables\n\t */\n\tZWLR_GAMMA_CONTROL_V1_ERROR_INVALID_GAMMA = 1,\n};\n#endif /* ZWLR_GAMMA_CONTROL_V1_ERROR_ENUM */\n\n/**\n * @ingroup iface_zwlr_gamma_control_v1\n * @struct zwlr_gamma_control_v1_listener\n */\nstruct zwlr_gamma_control_v1_listener {\n\t/**\n\t * size of gamma ramps\n\t *\n\t * Advertise the size of each gamma ramp.\n\t *\n\t * This event is sent immediately when the gamma control object is\n\t * created.\n\t */\n\tvoid (*gamma_size)(void *data,\n\t\t\t   struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1,\n\t\t\t   uint32_t size);\n\t/**\n\t * object no longer valid\n\t *\n\t * This event indicates that the gamma control is no longer\n\t * valid. This can happen for a number of reasons, including: - The\n\t * output doesn't support gamma tables - Setting the gamma tables\n\t * failed - Another client already has exclusive gamma control for\n\t * this output - The compositor has transfered gamma control to\n\t * another client\n\t *\n\t * Upon receiving this event, the client should destroy this\n\t * object.\n\t */\n\tvoid (*failed)(void *data,\n\t\t       struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1);\n};\n\n/**\n * @ingroup iface_zwlr_gamma_control_v1\n */\nstatic inline int\nzwlr_gamma_control_v1_add_listener(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1,\n\t\t\t\t   const struct zwlr_gamma_control_v1_listener *listener, void *data)\n{\n\treturn wl_proxy_add_listener((struct wl_proxy *) zwlr_gamma_control_v1,\n\t\t\t\t     (void (**)(void)) listener, data);\n}\n\n#define ZWLR_GAMMA_CONTROL_V1_SET_GAMMA 0\n#define ZWLR_GAMMA_CONTROL_V1_DESTROY 1\n\n/**\n * @ingroup iface_zwlr_gamma_control_v1\n */\n#define ZWLR_GAMMA_CONTROL_V1_GAMMA_SIZE_SINCE_VERSION 1\n/**\n * @ingroup iface_zwlr_gamma_control_v1\n */\n#define ZWLR_GAMMA_CONTROL_V1_FAILED_SINCE_VERSION 1\n\n/**\n * @ingroup iface_zwlr_gamma_control_v1\n */\n#define ZWLR_GAMMA_CONTROL_V1_SET_GAMMA_SINCE_VERSION 1\n/**\n * @ingroup iface_zwlr_gamma_control_v1\n */\n#define ZWLR_GAMMA_CONTROL_V1_DESTROY_SINCE_VERSION 1\n\n/** @ingroup iface_zwlr_gamma_control_v1 */\nstatic inline void\nzwlr_gamma_control_v1_set_user_data(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1, void *user_data)\n{\n\twl_proxy_set_user_data((struct wl_proxy *) zwlr_gamma_control_v1, user_data);\n}\n\n/** @ingroup iface_zwlr_gamma_control_v1 */\nstatic inline void *\nzwlr_gamma_control_v1_get_user_data(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1)\n{\n\treturn wl_proxy_get_user_data((struct wl_proxy *) zwlr_gamma_control_v1);\n}\n\nstatic inline uint32_t\nzwlr_gamma_control_v1_get_version(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1)\n{\n\treturn wl_proxy_get_version((struct wl_proxy *) zwlr_gamma_control_v1);\n}\n\n/**\n * @ingroup iface_zwlr_gamma_control_v1\n *\n * Set the gamma table. The file descriptor can be memory-mapped to provide\n * the raw gamma table, which contains successive gamma ramps for the red,\n * green and blue channels. Each gamma ramp is an array of 16-byte unsigned\n * integers which has the same length as the gamma size.\n *\n * The file descriptor data must have the same length as three times the\n * gamma size.\n */\nstatic inline void\nzwlr_gamma_control_v1_set_gamma(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1, int32_t fd)\n{\n\twl_proxy_marshal((struct wl_proxy *) zwlr_gamma_control_v1,\n\t\t\t ZWLR_GAMMA_CONTROL_V1_SET_GAMMA, fd);\n}\n\n/**\n * @ingroup iface_zwlr_gamma_control_v1\n *\n * Destroys the gamma control object. If the object is still valid, this\n * restores the original gamma tables.\n */\nstatic inline void\nzwlr_gamma_control_v1_destroy(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1)\n{\n\twl_proxy_marshal((struct wl_proxy *) zwlr_gamma_control_v1,\n\t\t\t ZWLR_GAMMA_CONTROL_V1_DESTROY);\n\n\twl_proxy_destroy((struct wl_proxy *) zwlr_gamma_control_v1);\n}\n\n#ifdef  __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "waysct/gamma-control-protocol.h",
    "content": "/* Generated by wayland-scanner 1.19.0 */\n\n/*\n * Copyright © 2015 Giulio camuffo\n * Copyright © 2018 Simon Ser\n *\n * Permission to use, copy, modify, distribute, and sell this\n * software and its documentation for any purpose is hereby granted\n * without fee, provided that the above copyright notice appear in\n * all copies and that both that copyright notice and this permission\n * notice appear in supporting documentation, and that the name of\n * the copyright holders not be used in advertising or publicity\n * pertaining to distribution of the software without specific,\n * written prior permission.  The copyright holders make no\n * representations about the suitability of this software for any\n * purpose.  It is provided \"as is\" without express or implied\n * warranty.\n *\n * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS\n * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\n * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN\n * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\n * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\n * THIS SOFTWARE.\n */\n\n#include <stdlib.h>\n#include <stdint.h>\n#include \"wayland-util.h\"\n\n#ifndef __has_attribute\n# define __has_attribute(x) 0  /* Compatibility with non-clang compilers. */\n#endif\n\n#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)\n#define WL_PRIVATE __attribute__ ((visibility(\"hidden\")))\n#else\n#define WL_PRIVATE\n#endif\n\nextern const struct wl_interface wl_output_interface;\nextern const struct wl_interface zwlr_gamma_control_v1_interface;\n\nstatic const struct wl_interface *wlr_gamma_control_unstable_v1_types[] = {\n\tNULL,\n\t&zwlr_gamma_control_v1_interface,\n\t&wl_output_interface,\n};\n\nstatic const struct wl_message zwlr_gamma_control_manager_v1_requests[] = {\n\t{ \"get_gamma_control\", \"no\", wlr_gamma_control_unstable_v1_types + 1 },\n\t{ \"destroy\", \"\", wlr_gamma_control_unstable_v1_types + 0 },\n};\n\nWL_PRIVATE const struct wl_interface zwlr_gamma_control_manager_v1_interface = {\n\t\"zwlr_gamma_control_manager_v1\", 1,\n\t2, zwlr_gamma_control_manager_v1_requests,\n\t0, NULL,\n};\n\nstatic const struct wl_message zwlr_gamma_control_v1_requests[] = {\n\t{ \"set_gamma\", \"h\", wlr_gamma_control_unstable_v1_types + 0 },\n\t{ \"destroy\", \"\", wlr_gamma_control_unstable_v1_types + 0 },\n};\n\nstatic const struct wl_message zwlr_gamma_control_v1_events[] = {\n\t{ \"gamma_size\", \"u\", wlr_gamma_control_unstable_v1_types + 0 },\n\t{ \"failed\", \"\", wlr_gamma_control_unstable_v1_types + 0 },\n};\n\nWL_PRIVATE const struct wl_interface zwlr_gamma_control_v1_interface = {\n\t\"zwlr_gamma_control_v1\", 1,\n\t2, zwlr_gamma_control_v1_requests,\n\t2, zwlr_gamma_control_v1_events,\n};\n\n"
  },
  {
    "path": "waysct/gamma-control.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<protocol name=\"wlr_gamma_control_unstable_v1\">\n  <copyright>\n    Copyright © 2015 Giulio camuffo\n    Copyright © 2018 Simon Ser\n\n    Permission to use, copy, modify, distribute, and sell this\n    software and its documentation for any purpose is hereby granted\n    without fee, provided that the above copyright notice appear in\n    all copies and that both that copyright notice and this permission\n    notice appear in supporting documentation, and that the name of\n    the copyright holders not be used in advertising or publicity\n    pertaining to distribution of the software without specific,\n    written prior permission.  The copyright holders make no\n    representations about the suitability of this software for any\n    purpose.  It is provided \"as is\" without express or implied\n    warranty.\n\n    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS\n    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\n    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY\n    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN\n    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\n    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\n    THIS SOFTWARE.\n  </copyright>\n\n  <description summary=\"manage gamma tables of outputs\">\n    This protocol allows a privileged client to set the gamma tables for\n    outputs.\n\n    Warning! The protocol described in this file is experimental and\n    backward incompatible changes may be made. Backward compatible changes\n    may be added together with the corresponding interface version bump.\n    Backward incompatible changes are done by bumping the version number in\n    the protocol and interface names and resetting the interface version.\n    Once the protocol is to be declared stable, the 'z' prefix and the\n    version number in the protocol and interface names are removed and the\n    interface version number is reset.\n  </description>\n\n  <interface name=\"zwlr_gamma_control_manager_v1\" version=\"1\">\n    <description summary=\"manager to create per-output gamma controls\">\n      This interface is a manager that allows creating per-output gamma\n      controls.\n    </description>\n\n    <request name=\"get_gamma_control\">\n      <description summary=\"get a gamma control for an output\">\n        Create a gamma control that can be used to adjust gamma tables for the\n        provided output.\n      </description>\n      <arg name=\"id\" type=\"new_id\" interface=\"zwlr_gamma_control_v1\"/>\n      <arg name=\"output\" type=\"object\" interface=\"wl_output\"/>\n    </request>\n\n    <request name=\"destroy\" type=\"destructor\">\n      <description summary=\"destroy the manager\">\n        All objects created by the manager will still remain valid, until their\n        appropriate destroy request has been called.\n      </description>\n    </request>\n  </interface>\n\n  <interface name=\"zwlr_gamma_control_v1\" version=\"1\">\n    <description summary=\"adjust gamma tables for an output\">\n      This interface allows a client to adjust gamma tables for a particular\n      output.\n\n      The client will receive the gamma size, and will then be able to set gamma\n      tables. At any time the compositor can send a failed event indicating that\n      this object is no longer valid.\n\n      There must always be at most one gamma control object per output, which\n      has exclusive access to this particular output. When the gamma control\n      object is destroyed, the gamma table is restored to its original value.\n    </description>\n\n    <event name=\"gamma_size\">\n      <description summary=\"size of gamma ramps\">\n        Advertise the size of each gamma ramp.\n\n        This event is sent immediately when the gamma control object is created.\n      </description>\n      <arg name=\"size\" type=\"uint\"/>\n    </event>\n\n    <enum name=\"error\">\n      <entry name=\"invalid_gamma\" value=\"1\" summary=\"invalid gamma tables\"/>\n    </enum>\n\n    <request name=\"set_gamma\">\n      <description summary=\"set the gamma table\">\n        Set the gamma table. The file descriptor can be memory-mapped to provide\n        the raw gamma table, which contains successive gamma ramps for the red,\n        green and blue channels. Each gamma ramp is an array of 16-byte unsigned\n        integers which has the same length as the gamma size.\n\n        The file descriptor data must have the same length as three times the\n        gamma size.\n      </description>\n      <arg name=\"fd\" type=\"fd\" summary=\"gamma table file descriptor\"/>\n    </request>\n\n    <event name=\"failed\">\n      <description summary=\"object no longer valid\">\n        This event indicates that the gamma control is no longer valid. This\n        can happen for a number of reasons, including:\n        - The output doesn't support gamma tables\n        - Setting the gamma tables failed\n        - Another client already has exclusive gamma control for this output\n        - The compositor has transfered gamma control to another client\n\n        Upon receiving this event, the client should destroy this object.\n      </description>\n    </event>\n\n    <request name=\"destroy\" type=\"destructor\">\n      <description summary=\"destroy this control\">\n        Destroys the gamma control object. If the object is still valid, this\n        restores the original gamma tables.\n      </description>\n    </request>\n  </interface>\n</protocol>\n"
  },
  {
    "path": "waysct/gamma-wl.h",
    "content": "/* gamma-wl.c -- Wayland gamma adjustment header\n   This file is part of Redshift.\n\n   Redshift is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by\n   the Free Software Foundation, either version 3 of the License, or\n   (at your option) any later version.\n\n   Redshift is distributed in the hope that it will be useful,\n   but WITHOUT ANY WARRANTY; without even the implied warranty of\n   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n   GNU General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with Redshift.  If not, see <http://www.gnu.org/licenses/>.\n\n   Copyright (c) 2015  Giulio Camuffo <giuliocamuffo@gmail.com>\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <alloca.h>\n#include <sys/mman.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <wayland-client.h>\n#include <math.h>\n\n#ifdef ENABLE_NLS\n# include <libintl.h>\n# define _(s) gettext(s)\n#else\n# define _(s) s\n#endif\n\n#include \"os-compatibility.h\"\n#include \"os-compatibility-impl.h\"\n//#include \"colorramp.h\"\n\n#include \"gamma-control-client-protocol.h\"\n#include \"gamma-control-protocol.h\"\n#include \"orbital-authorizer-client-protocol.h\"\n#include \"orbital-authorizer-protocol.h\"\n\n/* Color setting */\ntypedef struct {\n\tint temperature;\n\tfloat gamma[3];\n\tfloat brightness;\n} color_setting_t;\n\n/* Whitepoint values for temperatures at 100K intervals.\n   These will be interpolated for the actual temperature.\n   This table was provided by Ingo Thies, 2013. See\n   the file README-colorramp for more information. */\nstatic const float blackbody_color[] = {\n\t1.00000000,  0.18172716,  0.00000000, /* 1000K */\n\t1.00000000,  0.25503671,  0.00000000, /* 1100K */\n\t1.00000000,  0.30942099,  0.00000000, /* 1200K */\n\t1.00000000,  0.35357379,  0.00000000, /* ...   */\n\t1.00000000,  0.39091524,  0.00000000,\n\t1.00000000,  0.42322816,  0.00000000,\n\t1.00000000,  0.45159884,  0.00000000,\n\t1.00000000,  0.47675916,  0.00000000,\n\t1.00000000,  0.49923747,  0.00000000,\n\t1.00000000,  0.51943421,  0.00000000,\n\t1.00000000,  0.54360078,  0.08679949,\n\t1.00000000,  0.56618736,  0.14065513,\n\t1.00000000,  0.58734976,  0.18362641,\n\t1.00000000,  0.60724493,  0.22137978,\n\t1.00000000,  0.62600248,  0.25591950,\n\t1.00000000,  0.64373109,  0.28819679,\n\t1.00000000,  0.66052319,  0.31873863,\n\t1.00000000,  0.67645822,  0.34786758,\n\t1.00000000,  0.69160518,  0.37579588,\n\t1.00000000,  0.70602449,  0.40267128,\n\t1.00000000,  0.71976951,  0.42860152,\n\t1.00000000,  0.73288760,  0.45366838,\n\t1.00000000,  0.74542112,  0.47793608,\n\t1.00000000,  0.75740814,  0.50145662,\n\t1.00000000,  0.76888303,  0.52427322,\n\t1.00000000,  0.77987699,  0.54642268,\n\t1.00000000,  0.79041843,  0.56793692,\n\t1.00000000,  0.80053332,  0.58884417,\n\t1.00000000,  0.81024551,  0.60916971,\n\t1.00000000,  0.81957693,  0.62893653,\n\t1.00000000,  0.82854786,  0.64816570,\n\t1.00000000,  0.83717703,  0.66687674,\n\t1.00000000,  0.84548188,  0.68508786,\n\t1.00000000,  0.85347859,  0.70281616,\n\t1.00000000,  0.86118227,  0.72007777,\n\t1.00000000,  0.86860704,  0.73688797,\n\t1.00000000,  0.87576611,  0.75326132,\n\t1.00000000,  0.88267187,  0.76921169,\n\t1.00000000,  0.88933596,  0.78475236,\n\t1.00000000,  0.89576933,  0.79989606,\n\t1.00000000,  0.90198230,  0.81465502,\n\t1.00000000,  0.90963069,  0.82838210,\n\t1.00000000,  0.91710889,  0.84190889,\n\t1.00000000,  0.92441842,  0.85523742,\n\t1.00000000,  0.93156127,  0.86836903,\n\t1.00000000,  0.93853986,  0.88130458,\n\t1.00000000,  0.94535695,  0.89404470,\n\t1.00000000,  0.95201559,  0.90658983,\n\t1.00000000,  0.95851906,  0.91894041,\n\t1.00000000,  0.96487079,  0.93109690,\n\t1.00000000,  0.97107439,  0.94305985,\n\t1.00000000,  0.97713351,  0.95482993,\n\t1.00000000,  0.98305189,  0.96640795,\n\t1.00000000,  0.98883326,  0.97779486,\n\t1.00000000,  0.99448139,  0.98899179,\n\t1.00000000,  1.00000000,  1.00000000, /* 6500K */\n\t0.98947904,  0.99348723,  1.00000000,\n\t0.97940448,  0.98722715,  1.00000000,\n\t0.96975025,  0.98120637,  1.00000000,\n\t0.96049223,  0.97541240,  1.00000000,\n\t0.95160805,  0.96983355,  1.00000000,\n\t0.94303638,  0.96443333,  1.00000000,\n\t0.93480451,  0.95923080,  1.00000000,\n\t0.92689056,  0.95421394,  1.00000000,\n\t0.91927697,  0.94937330,  1.00000000,\n\t0.91194747,  0.94470005,  1.00000000,\n\t0.90488690,  0.94018594,  1.00000000,\n\t0.89808115,  0.93582323,  1.00000000,\n\t0.89151710,  0.93160469,  1.00000000,\n\t0.88518247,  0.92752354,  1.00000000,\n\t0.87906581,  0.92357340,  1.00000000,\n\t0.87315640,  0.91974827,  1.00000000,\n\t0.86744421,  0.91604254,  1.00000000,\n\t0.86191983,  0.91245088,  1.00000000,\n\t0.85657444,  0.90896831,  1.00000000,\n\t0.85139976,  0.90559011,  1.00000000,\n\t0.84638799,  0.90231183,  1.00000000,\n\t0.84153180,  0.89912926,  1.00000000,\n\t0.83682430,  0.89603843,  1.00000000,\n\t0.83225897,  0.89303558,  1.00000000,\n\t0.82782969,  0.89011714,  1.00000000,\n\t0.82353066,  0.88727974,  1.00000000,\n\t0.81935641,  0.88452017,  1.00000000,\n\t0.81530175,  0.88183541,  1.00000000,\n\t0.81136180,  0.87922257,  1.00000000,\n\t0.80753191,  0.87667891,  1.00000000,\n\t0.80380769,  0.87420182,  1.00000000,\n\t0.80018497,  0.87178882,  1.00000000,\n\t0.79665980,  0.86943756,  1.00000000,\n\t0.79322843,  0.86714579,  1.00000000,\n\t0.78988728,  0.86491137,  1.00000000, /* 10000K */\n\t0.78663296,  0.86273225,  1.00000000,\n\t0.78346225,  0.86060650,  1.00000000,\n\t0.78037207,  0.85853224,  1.00000000,\n\t0.77735950,  0.85650771,  1.00000000,\n\t0.77442176,  0.85453121,  1.00000000,\n\t0.77155617,  0.85260112,  1.00000000,\n\t0.76876022,  0.85071588,  1.00000000,\n\t0.76603147,  0.84887402,  1.00000000,\n\t0.76336762,  0.84707411,  1.00000000,\n\t0.76076645,  0.84531479,  1.00000000,\n\t0.75822586,  0.84359476,  1.00000000,\n\t0.75574383,  0.84191277,  1.00000000,\n\t0.75331843,  0.84026762,  1.00000000,\n\t0.75094780,  0.83865816,  1.00000000,\n\t0.74863017,  0.83708329,  1.00000000,\n\t0.74636386,  0.83554194,  1.00000000,\n\t0.74414722,  0.83403311,  1.00000000,\n\t0.74197871,  0.83255582,  1.00000000,\n\t0.73985682,  0.83110912,  1.00000000,\n\t0.73778012,  0.82969211,  1.00000000,\n\t0.73574723,  0.82830393,  1.00000000,\n\t0.73375683,  0.82694373,  1.00000000,\n\t0.73180765,  0.82561071,  1.00000000,\n\t0.72989845,  0.82430410,  1.00000000,\n\t0.72802807,  0.82302316,  1.00000000,\n\t0.72619537,  0.82176715,  1.00000000,\n\t0.72439927,  0.82053539,  1.00000000,\n\t0.72263872,  0.81932722,  1.00000000,\n\t0.72091270,  0.81814197,  1.00000000,\n\t0.71922025,  0.81697905,  1.00000000,\n\t0.71756043,  0.81583783,  1.00000000,\n\t0.71593234,  0.81471775,  1.00000000,\n\t0.71433510,  0.81361825,  1.00000000,\n\t0.71276788,  0.81253878,  1.00000000,\n\t0.71122987,  0.81147883,  1.00000000,\n\t0.70972029,  0.81043789,  1.00000000,\n\t0.70823838,  0.80941546,  1.00000000,\n\t0.70678342,  0.80841109,  1.00000000,\n\t0.70535469,  0.80742432,  1.00000000,\n\t0.70395153,  0.80645469,  1.00000000,\n\t0.70257327,  0.80550180,  1.00000000,\n\t0.70121928,  0.80456522,  1.00000000,\n\t0.69988894,  0.80364455,  1.00000000,\n\t0.69858167,  0.80273941,  1.00000000,\n\t0.69729688,  0.80184943,  1.00000000,\n\t0.69603402,  0.80097423,  1.00000000,\n\t0.69479255,  0.80011347,  1.00000000,\n\t0.69357196,  0.79926681,  1.00000000,\n\t0.69237173,  0.79843391,  1.00000000,\n\t0.69119138,  0.79761446,  1.00000000, /* 15000K */\n\t0.69003044,  0.79680814,  1.00000000,\n\t0.68888844,  0.79601466,  1.00000000,\n\t0.68776494,  0.79523371,  1.00000000,\n\t0.68665951,  0.79446502,  1.00000000,\n\t0.68557173,  0.79370830,  1.00000000,\n\t0.68450119,  0.79296330,  1.00000000,\n\t0.68344751,  0.79222975,  1.00000000,\n\t0.68241029,  0.79150740,  1.00000000,\n\t0.68138918,  0.79079600,  1.00000000,\n\t0.68038380,  0.79009531,  1.00000000,\n\t0.67939381,  0.78940511,  1.00000000,\n\t0.67841888,  0.78872517,  1.00000000,\n\t0.67745866,  0.78805526,  1.00000000,\n\t0.67651284,  0.78739518,  1.00000000,\n\t0.67558112,  0.78674472,  1.00000000,\n\t0.67466317,  0.78610368,  1.00000000,\n\t0.67375872,  0.78547186,  1.00000000,\n\t0.67286748,  0.78484907,  1.00000000,\n\t0.67198916,  0.78423512,  1.00000000,\n\t0.67112350,  0.78362984,  1.00000000,\n\t0.67027024,  0.78303305,  1.00000000,\n\t0.66942911,  0.78244457,  1.00000000,\n\t0.66859988,  0.78186425,  1.00000000,\n\t0.66778228,  0.78129191,  1.00000000,\n\t0.66697610,  0.78072740,  1.00000000,\n\t0.66618110,  0.78017057,  1.00000000,\n\t0.66539706,  0.77962127,  1.00000000,\n\t0.66462376,  0.77907934,  1.00000000,\n\t0.66386098,  0.77854465,  1.00000000,\n\t0.66310852,  0.77801705,  1.00000000,\n\t0.66236618,  0.77749642,  1.00000000,\n\t0.66163375,  0.77698261,  1.00000000,\n\t0.66091106,  0.77647551,  1.00000000,\n\t0.66019791,  0.77597498,  1.00000000,\n\t0.65949412,  0.77548090,  1.00000000,\n\t0.65879952,  0.77499315,  1.00000000,\n\t0.65811392,  0.77451161,  1.00000000,\n\t0.65743716,  0.77403618,  1.00000000,\n\t0.65676908,  0.77356673,  1.00000000,\n\t0.65610952,  0.77310316,  1.00000000,\n\t0.65545831,  0.77264537,  1.00000000,\n\t0.65481530,  0.77219324,  1.00000000,\n\t0.65418036,  0.77174669,  1.00000000,\n\t0.65355332,  0.77130560,  1.00000000,\n\t0.65293404,  0.77086988,  1.00000000,\n\t0.65232240,  0.77043944,  1.00000000,\n\t0.65171824,  0.77001419,  1.00000000,\n\t0.65112144,  0.76959404,  1.00000000,\n\t0.65053187,  0.76917889,  1.00000000,\n\t0.64994941,  0.76876866,  1.00000000, /* 20000K */\n\t0.64937392,  0.76836326,  1.00000000,\n\t0.64880528,  0.76796263,  1.00000000,\n\t0.64824339,  0.76756666,  1.00000000,\n\t0.64768812,  0.76717529,  1.00000000,\n\t0.64713935,  0.76678844,  1.00000000,\n\t0.64659699,  0.76640603,  1.00000000,\n\t0.64606092,  0.76602798,  1.00000000,\n\t0.64553103,  0.76565424,  1.00000000,\n\t0.64500722,  0.76528472,  1.00000000,\n\t0.64448939,  0.76491935,  1.00000000,\n\t0.64397745,  0.76455808,  1.00000000,\n\t0.64347129,  0.76420082,  1.00000000,\n\t0.64297081,  0.76384753,  1.00000000,\n\t0.64247594,  0.76349813,  1.00000000,\n\t0.64198657,  0.76315256,  1.00000000,\n\t0.64150261,  0.76281076,  1.00000000,\n\t0.64102399,  0.76247267,  1.00000000,\n\t0.64055061,  0.76213824,  1.00000000,\n\t0.64008239,  0.76180740,  1.00000000,\n\t0.63961926,  0.76148010,  1.00000000,\n\t0.63916112,  0.76115628,  1.00000000,\n\t0.63870790,  0.76083590,  1.00000000,\n\t0.63825953,  0.76051890,  1.00000000,\n\t0.63781592,  0.76020522,  1.00000000,\n\t0.63737701,  0.75989482,  1.00000000,\n\t0.63694273,  0.75958764,  1.00000000,\n\t0.63651299,  0.75928365,  1.00000000,\n\t0.63608774,  0.75898278,  1.00000000,\n\t0.63566691,  0.75868499,  1.00000000,\n\t0.63525042,  0.75839025,  1.00000000,\n\t0.63483822,  0.75809849,  1.00000000,\n\t0.63443023,  0.75780969,  1.00000000,\n\t0.63402641,  0.75752379,  1.00000000,\n\t0.63362667,  0.75724075,  1.00000000,\n\t0.63323097,  0.75696053,  1.00000000,\n\t0.63283925,  0.75668310,  1.00000000,\n\t0.63245144,  0.75640840,  1.00000000,\n\t0.63206749,  0.75613641,  1.00000000,\n\t0.63168735,  0.75586707,  1.00000000,\n\t0.63131096,  0.75560036,  1.00000000,\n\t0.63093826,  0.75533624,  1.00000000,\n\t0.63056920,  0.75507467,  1.00000000,\n\t0.63020374,  0.75481562,  1.00000000,\n\t0.62984181,  0.75455904,  1.00000000,\n\t0.62948337,  0.75430491,  1.00000000,\n\t0.62912838,  0.75405319,  1.00000000,\n\t0.62877678,  0.75380385,  1.00000000,\n\t0.62842852,  0.75355685,  1.00000000,\n\t0.62808356,  0.75331217,  1.00000000,\n\t0.62774186,  0.75306977,  1.00000000, /* 25000K */\n\t0.62740336,  0.75282962,  1.00000000  /* 25100K */\n};\n\n\nstatic void\ninterpolate_color(float a, const float *c1, const float *c2, float *c)\n{\n\tc[0] = (1.0-a)*c1[0] + a*c2[0];\n\tc[1] = (1.0-a)*c1[1] + a*c2[1];\n\tc[2] = (1.0-a)*c1[2] + a*c2[2];\n}\n\n/* Helper macro used in the fill functions */\n#define F(Y, C)  pow((Y) * setting->brightness * \\\n\t\t     white_point[C], 1.0/setting->gamma[C])\n\nvoid\ncolorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b,\n\t       int size, const color_setting_t *setting)\n{\n\t/* Approximate white point */\n\tfloat white_point[3];\n\tfloat alpha = (setting->temperature % 100) / 100.0;\n\tint temp_index = ((setting->temperature - 1000) / 100)*3;\n\tinterpolate_color(alpha, &blackbody_color[temp_index],\n\t\t\t  &blackbody_color[temp_index+3], white_point);\n\n\tfor (int i = 0; i < size; i++) {\n\t\tgamma_r[i] = F((double)gamma_r[i]/(UINT16_MAX+1), 0) *\n\t\t\t(UINT16_MAX+1);\n\t\tgamma_g[i] = F((double)gamma_g[i]/(UINT16_MAX+1), 1) *\n\t\t\t(UINT16_MAX+1);\n\t\tgamma_b[i] = F((double)gamma_b[i]/(UINT16_MAX+1), 2) *\n\t\t\t(UINT16_MAX+1);\n\t}\n}\n\ntypedef struct {\n\tstruct wl_display *display;\n\tstruct wl_registry *registry;\n\tstruct wl_callback *callback;\n\tuint32_t gamma_control_manager_id;\n\tstruct zwlr_gamma_control_manager_v1 *gamma_control_manager;\n\tint num_outputs;\n\tstruct output *outputs;\n\tint authorized;\n} wayland_state_t;\n\nstruct output {\n\tuint32_t global_id;\n\tstruct wl_output *output;\n\tstruct zwlr_gamma_control_v1 *gamma_control;\n\tuint32_t gamma_size;\n};\n\nstatic int\nwayland_init(wayland_state_t **state)\n{\n\t/* Initialize state. */\n\t*state = malloc(sizeof(**state));\n\tif (*state == NULL) return -1;\n\n\tmemset(*state, 0, sizeof **state);\n\treturn 0;\n}\n\nstatic void\nauthorizer_feedback_granted(void *data, struct orbital_authorizer_feedback *feedback)\n{\n\twayland_state_t *state = data;\n\tstate->authorized = 1;\n}\n\nstatic void\nauthorizer_feedback_denied(void *data, struct orbital_authorizer_feedback *feedback)\n{\n\tfprintf(stderr, _(\"Fatal: redshift was not authorized to bind the 'zwlr_gamma_control_manager_v1' interface.\\n\"));\n\texit(EXIT_FAILURE);\n}\n\nstatic const struct orbital_authorizer_feedback_listener authorizer_feedback_listener = {\n\tauthorizer_feedback_granted,\n\tauthorizer_feedback_denied\n};\n\nstatic void\nregistry_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version)\n{\n\twayland_state_t *state = data;\n\n\tif (strcmp(interface, \"zwlr_gamma_control_manager_v1\") == 0) {\n\t\tstate->gamma_control_manager_id = id;\n\t\tstate->gamma_control_manager = wl_registry_bind(registry, id, &zwlr_gamma_control_manager_v1_interface, 1);\n\t} else if (strcmp(interface, \"wl_output\") == 0) {\n\t\tstate->num_outputs++;\n\t\tif (!(state->outputs = realloc(state->outputs, state->num_outputs * sizeof(struct output)))) {\n\t\t\tfprintf(stderr, _(\"Failed to allcate memory\\n\"));\n\t\t\treturn;\n\t\t}\n\n\t\tstruct output *output = &state->outputs[state->num_outputs - 1];\n\t\toutput->global_id = id;\n\t\toutput->output = wl_registry_bind(registry, id, &wl_output_interface, 1);\n\t\toutput->gamma_control = NULL;\n\t} else if (strcmp(interface, \"orbital_authorizer\") == 0) {\n\t\tstruct wl_event_queue *queue = wl_display_create_queue(state->display);\n\n\t\tstruct orbital_authorizer *auth = wl_registry_bind(registry, id, &orbital_authorizer_interface, 1u);\n\t\twl_proxy_set_queue((struct wl_proxy *)auth, queue);\n\n\t\tstruct orbital_authorizer_feedback *feedback = orbital_authorizer_authorize(auth, \"zwlr_gamma_control_manager_v1\");\n\t\torbital_authorizer_feedback_add_listener(feedback, &authorizer_feedback_listener, state);\n\n\t\tint ret = 0;\n\t\twhile (!state->authorized && ret >= 0) {\n\t\t\tret = wl_display_dispatch_queue(state->display, queue);\n\t\t}\n\n\t\torbital_authorizer_feedback_destroy(feedback);\n\t\torbital_authorizer_destroy(auth);\n\t\twl_event_queue_destroy(queue);\n\t}\n}\n\nstatic void\nregistry_global_remove(void *data, struct wl_registry *registry, uint32_t id)\n{\n\twayland_state_t *state = data;\n\n\tif (state->gamma_control_manager_id == id) {\n\t\tfprintf(stderr, _(\"The zwlr_gamma_control_manager_v1 was removed\\n\"));\n\t\texit(EXIT_FAILURE);\n\t}\n\n\tfor (int i = 0; i < state->num_outputs; ++i) {\n\t\tstruct output *output = &state->outputs[i];\n\t\tif (output->global_id == id) {\n\t\t\tif (output->gamma_control) {\n\t\t\t\tzwlr_gamma_control_v1_destroy(output->gamma_control);\n\t\t\t\toutput->gamma_control = NULL;\n\t\t\t}\n\t\t\twl_output_destroy(output->output);\n\n\t\t\t/* If the removed output is not the last one in the array move the last one\n\t\t\t * in the now empty slot. Then shrink the array */\n\t\t\tif (i < --state->num_outputs) {\n\t\t\t\tmemcpy(output, &state->outputs[state->num_outputs], sizeof(struct output));\n\t\t\t}\n\t\t\tstate->outputs = realloc(state->outputs, state->num_outputs * sizeof(struct output));\n\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic const struct wl_registry_listener registry_listener = {\n\tregistry_global,\n\tregistry_global_remove\n};\n\nstatic void\ngamma_control_gamma_size(void *data, struct zwlr_gamma_control_v1 *control, uint32_t size)\n{\n\tstruct output *output = data;\n\toutput->gamma_size = size;\n}\n\nstatic void\ngamma_control_failed(void *data, struct zwlr_gamma_control_v1 *control)\n{\n}\n\n\nstatic const struct zwlr_gamma_control_v1_listener gamma_control_listener = {\n\tgamma_control_gamma_size,\n\tgamma_control_failed\n};\n\nstatic int\nwayland_start(wayland_state_t *state)\n{\n\tstate->display = wl_display_connect(NULL);\n\tif (!state->display) {\n\t\tfputs(_(\"Could not connect to wayland display, exiting.\\n\"), stderr);\n\t\treturn -1;\n\t}\n\tstate->registry = wl_display_get_registry(state->display);\n\n\twl_registry_add_listener(state->registry, &registry_listener, state);\n\n\twl_display_roundtrip(state->display);\n\tif (!state->gamma_control_manager) {\n\t\treturn -1;\n\t}\n\tif (state->num_outputs > 0 && !state->outputs) {\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\nstatic void\nwayland_restore(wayland_state_t *state)\n{\n\tfor (int i = 0; i < state->num_outputs; ++i) {\n\t\tstruct output *output = &state->outputs[i];\n\t\tif (output->gamma_control) {\n\t\t\tzwlr_gamma_control_v1_destroy(output->gamma_control);\n\t\t\toutput->gamma_control = NULL;\n\t\t}\n\t}\n\twl_display_flush(state->display);\n}\n\nstatic void\nwayland_free(wayland_state_t *state)\n{\n\tint ret = 0;\n\t/* Wait for the sync callback to destroy everything, otherwise\n\t * we could destroy the gamma control before gamma has been set */\n\twhile (state->callback && ret >= 0) {\n\t\tret = wl_display_dispatch(state->display);\n\t}\n\tif (state->callback) {\n\t\tfprintf(stderr, _(\"Ignoring error on wayland connection while waiting to disconnect: %d\\n\"), ret);\n\t\twl_callback_destroy(state->callback);\n\t}\n\n\tfor (int i = 0; i < state->num_outputs; ++i) {\n\t\tstruct output *output = &state->outputs[i];\n\t\tif (output->gamma_control) {\n\t\t\tzwlr_gamma_control_v1_destroy(output->gamma_control);\n\t\t\toutput->gamma_control = NULL;\n\t\t}\n\t\twl_output_destroy(output->output);\n\t}\n\n\tif (state->gamma_control_manager) {\n\t\tzwlr_gamma_control_manager_v1_destroy(state->gamma_control_manager);\n\t}\n\tif (state->registry) {\n\t\twl_registry_destroy(state->registry);\n\t}\n\tif (state->display) {\n\t\twl_display_disconnect(state->display);\n\t}\n\n\tfree(state);\n}\n\nstatic void\ncallback_done(void *data, struct wl_callback *cb, uint32_t t)\n{\n\twayland_state_t *state = data;\n\tstate->callback = NULL;\n\twl_callback_destroy(cb);\n}\n\nstatic const struct wl_callback_listener callback_listener = {\n\tcallback_done\n};\n\nstatic int\nwayland_set_temperature(wayland_state_t *state, const color_setting_t *setting)\n{\n\tint ret = 0, roundtrip = 0;\n\n\t/* We wait for the sync callback to throttle a bit and not send more\n\t * requests than the compositor can manage, otherwise we'd get disconnected.\n\t * This also allows us to dispatch other incoming events such as\n\t * wl_registry.global_remove. */\n\twhile (state->callback && ret >= 0) {\n\t\tret = wl_display_dispatch(state->display);\n\t}\n\tif (ret < 0) {\n\t\tfprintf(stderr, _(\"The Wayland connection experienced a fatal error: %d\\n\"), ret);\n\t\treturn ret;\n\t}\n\n\tfor (int i = 0; i < state->num_outputs; ++i) {\n\t\tstruct output *output = &state->outputs[i];\n\t\tif (!output->gamma_control) {\n\t\t\toutput->gamma_control = zwlr_gamma_control_manager_v1_get_gamma_control(state->gamma_control_manager, output->output);\n\t\t\tzwlr_gamma_control_v1_add_listener(output->gamma_control, &gamma_control_listener, output);\n\t\t\troundtrip = 1;\n\t\t}\n\t}\n\tif (roundtrip) {\n\t\twl_display_roundtrip(state->display);\n\t}\n\n\tfor (int i = 0; i < state->num_outputs; ++i) {\n\t\tstruct output *output = &state->outputs[i];\n\t\tint size = output->gamma_size;\n\t\tsize_t ramp_bytes = size * sizeof(uint16_t);\n\t\tsize_t total_bytes = ramp_bytes * 3;\n\n\t\tint fd = os_create_anonymous_file(total_bytes);\n\t\tif (fd < 0) {\n\t\t\tperror(\"os_create_anonymous_file\");\n\t\t\treturn -1;\n\t\t}\n\n\t\tvoid *ptr = mmap(NULL, total_bytes,\n\t\t\tPROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);\n\t\tif (ptr == MAP_FAILED) {\n\t\t\tperror(\"mmap\");\n\t\t\tclose(fd);\n\t\t\treturn -1;\n\t\t}\n\n\t\tuint16_t *r_gamma = ptr;\n\t\tuint16_t *g_gamma = ptr + ramp_bytes;\n\t\tuint16_t *b_gamma = ptr + 2 * ramp_bytes;\n\n\t\t/* Initialize gamma ramps to pure state */\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tuint16_t value = (double)i / size * (UINT16_MAX+1);\n\t\t\tr_gamma[i] = value;\n\t\t\tg_gamma[i] = value;\n\t\t\tb_gamma[i] = value;\n\t\t}\n\n\t\tcolorramp_fill(r_gamma, g_gamma, b_gamma, size, setting);\n\t\tif (munmap(ptr, total_bytes) == -1) {\n\t\t\tperror(\"munmap\");\n\t\t\tclose(fd);\n\t\t\treturn -1;\n\t\t}\n\n\t\tzwlr_gamma_control_v1_set_gamma(output->gamma_control, fd);\n\t\tclose(fd);\n\t}\n\n\tstate->callback = wl_display_sync(state->display);\n\twl_callback_add_listener(state->callback, &callback_listener, state);\n\twl_display_flush(state->display);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "waysct/orbital-authorizer-client-protocol.h",
    "content": "/* Generated by wayland-scanner 1.19.0 */\n\n#ifndef ORBITAL_AUTHORIZER_CLIENT_PROTOCOL_H\n#define ORBITAL_AUTHORIZER_CLIENT_PROTOCOL_H\n\n#include <stdint.h>\n#include <stddef.h>\n#include \"wayland-client.h\"\n\n#ifdef  __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @page page_orbital_authorizer The orbital_authorizer protocol\n * @section page_ifaces_orbital_authorizer Interfaces\n * - @subpage page_iface_orbital_authorizer - authorize clients to use certain interfaces\n * - @subpage page_iface_orbital_authorizer_feedback - feedback for an authorization request\n */\nstruct orbital_authorizer;\nstruct orbital_authorizer_feedback;\n\n#ifndef ORBITAL_AUTHORIZER_INTERFACE\n#define ORBITAL_AUTHORIZER_INTERFACE\n/**\n * @page page_iface_orbital_authorizer orbital_authorizer\n * @section page_iface_orbital_authorizer_desc Description\n *\n * The orbital_authorizer global interface allows clients to\n * ask the compositor the authorization to bind certain restricted\n * global interfaces.\n * Any client that aims to bind restricted interfaces should first\n * request the authorization by using this interface. Failing to do\n * so will result in the compositor sending a protocol error to the\n * client when it binds the restricted interface.\n *\n * The list of restricted interfaces is compositor dependant, but must\n * not include the core interfaces defined in wayland.xml.\n * @section page_iface_orbital_authorizer_api API\n * See @ref iface_orbital_authorizer.\n */\n/**\n * @defgroup iface_orbital_authorizer The orbital_authorizer interface\n *\n * The orbital_authorizer global interface allows clients to\n * ask the compositor the authorization to bind certain restricted\n * global interfaces.\n * Any client that aims to bind restricted interfaces should first\n * request the authorization by using this interface. Failing to do\n * so will result in the compositor sending a protocol error to the\n * client when it binds the restricted interface.\n *\n * The list of restricted interfaces is compositor dependant, but must\n * not include the core interfaces defined in wayland.xml.\n */\nextern const struct wl_interface orbital_authorizer_interface;\n#endif\n#ifndef ORBITAL_AUTHORIZER_FEEDBACK_INTERFACE\n#define ORBITAL_AUTHORIZER_FEEDBACK_INTERFACE\n/**\n * @page page_iface_orbital_authorizer_feedback orbital_authorizer_feedback\n * @section page_iface_orbital_authorizer_feedback_desc Description\n *\n * The orbital_authorizer_feedback interface is used by requesting\n * an authorization with the orbital_authorizer.authorize request.\n * The compositor will send either the granted or denied event based\n * on the system and user configuration. How the authorization process\n * works is compositor specific, but a compositor is allowed to ask\n * for user input, so the response for an authorization request may\n * come after some time.\n * @section page_iface_orbital_authorizer_feedback_api API\n * See @ref iface_orbital_authorizer_feedback.\n */\n/**\n * @defgroup iface_orbital_authorizer_feedback The orbital_authorizer_feedback interface\n *\n * The orbital_authorizer_feedback interface is used by requesting\n * an authorization with the orbital_authorizer.authorize request.\n * The compositor will send either the granted or denied event based\n * on the system and user configuration. How the authorization process\n * works is compositor specific, but a compositor is allowed to ask\n * for user input, so the response for an authorization request may\n * come after some time.\n */\nextern const struct wl_interface orbital_authorizer_feedback_interface;\n#endif\n\n#define ORBITAL_AUTHORIZER_DESTROY 0\n#define ORBITAL_AUTHORIZER_AUTHORIZE 1\n\n\n/**\n * @ingroup iface_orbital_authorizer\n */\n#define ORBITAL_AUTHORIZER_DESTROY_SINCE_VERSION 1\n/**\n * @ingroup iface_orbital_authorizer\n */\n#define ORBITAL_AUTHORIZER_AUTHORIZE_SINCE_VERSION 1\n\n/** @ingroup iface_orbital_authorizer */\nstatic inline void\norbital_authorizer_set_user_data(struct orbital_authorizer *orbital_authorizer, void *user_data)\n{\n\twl_proxy_set_user_data((struct wl_proxy *) orbital_authorizer, user_data);\n}\n\n/** @ingroup iface_orbital_authorizer */\nstatic inline void *\norbital_authorizer_get_user_data(struct orbital_authorizer *orbital_authorizer)\n{\n\treturn wl_proxy_get_user_data((struct wl_proxy *) orbital_authorizer);\n}\n\nstatic inline uint32_t\norbital_authorizer_get_version(struct orbital_authorizer *orbital_authorizer)\n{\n\treturn wl_proxy_get_version((struct wl_proxy *) orbital_authorizer);\n}\n\n/**\n * @ingroup iface_orbital_authorizer\n */\nstatic inline void\norbital_authorizer_destroy(struct orbital_authorizer *orbital_authorizer)\n{\n\twl_proxy_marshal((struct wl_proxy *) orbital_authorizer,\n\t\t\t ORBITAL_AUTHORIZER_DESTROY);\n\n\twl_proxy_destroy((struct wl_proxy *) orbital_authorizer);\n}\n\n/**\n * @ingroup iface_orbital_authorizer\n *\n * The authorize request allows the client to ask the compositor the\n * authorization to bind a restricted global interface. The newly\n * created orbital_authorizer_feedback will be invalid after the\n * compositor send either the granted or denied event so the client\n * must destroy it immediately after.\n */\nstatic inline struct orbital_authorizer_feedback *\norbital_authorizer_authorize(struct orbital_authorizer *orbital_authorizer, const char *global)\n{\n\tstruct wl_proxy *id;\n\n\tid = wl_proxy_marshal_constructor((struct wl_proxy *) orbital_authorizer,\n\t\t\t ORBITAL_AUTHORIZER_AUTHORIZE, &orbital_authorizer_feedback_interface, NULL, global);\n\n\treturn (struct orbital_authorizer_feedback *) id;\n}\n\n/**\n * @ingroup iface_orbital_authorizer_feedback\n * @struct orbital_authorizer_feedback_listener\n */\nstruct orbital_authorizer_feedback_listener {\n\t/**\n\t * the authorization was granted\n\t *\n\t * The authorization was granted. The client can now bind the\n\t * restricted interface.\n\t */\n\tvoid (*granted)(void *data,\n\t\t\tstruct orbital_authorizer_feedback *orbital_authorizer_feedback);\n\t/**\n\t * the authorization was denied\n\t *\n\t * The authorization was denied. The client is not allowed to\n\t * bind the restricted interface and trying to do so will trigger a\n\t * protocol error killing the client.\n\t */\n\tvoid (*denied)(void *data,\n\t\t       struct orbital_authorizer_feedback *orbital_authorizer_feedback);\n};\n\n/**\n * @ingroup iface_orbital_authorizer_feedback\n */\nstatic inline int\norbital_authorizer_feedback_add_listener(struct orbital_authorizer_feedback *orbital_authorizer_feedback,\n\t\t\t\t\t const struct orbital_authorizer_feedback_listener *listener, void *data)\n{\n\treturn wl_proxy_add_listener((struct wl_proxy *) orbital_authorizer_feedback,\n\t\t\t\t     (void (**)(void)) listener, data);\n}\n\n/**\n * @ingroup iface_orbital_authorizer_feedback\n */\n#define ORBITAL_AUTHORIZER_FEEDBACK_GRANTED_SINCE_VERSION 1\n/**\n * @ingroup iface_orbital_authorizer_feedback\n */\n#define ORBITAL_AUTHORIZER_FEEDBACK_DENIED_SINCE_VERSION 1\n\n\n/** @ingroup iface_orbital_authorizer_feedback */\nstatic inline void\norbital_authorizer_feedback_set_user_data(struct orbital_authorizer_feedback *orbital_authorizer_feedback, void *user_data)\n{\n\twl_proxy_set_user_data((struct wl_proxy *) orbital_authorizer_feedback, user_data);\n}\n\n/** @ingroup iface_orbital_authorizer_feedback */\nstatic inline void *\norbital_authorizer_feedback_get_user_data(struct orbital_authorizer_feedback *orbital_authorizer_feedback)\n{\n\treturn wl_proxy_get_user_data((struct wl_proxy *) orbital_authorizer_feedback);\n}\n\nstatic inline uint32_t\norbital_authorizer_feedback_get_version(struct orbital_authorizer_feedback *orbital_authorizer_feedback)\n{\n\treturn wl_proxy_get_version((struct wl_proxy *) orbital_authorizer_feedback);\n}\n\n/** @ingroup iface_orbital_authorizer_feedback */\nstatic inline void\norbital_authorizer_feedback_destroy(struct orbital_authorizer_feedback *orbital_authorizer_feedback)\n{\n\twl_proxy_destroy((struct wl_proxy *) orbital_authorizer_feedback);\n}\n\n#ifdef  __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "waysct/orbital-authorizer-protocol.h",
    "content": "/* Generated by wayland-scanner 1.19.0 */\n\n#include <stdlib.h>\n#include <stdint.h>\n#include \"wayland-util.h\"\n\n#ifndef __has_attribute\n# define __has_attribute(x) 0  /* Compatibility with non-clang compilers. */\n#endif\n\n#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)\n#define WL_PRIVATE __attribute__ ((visibility(\"hidden\")))\n#else\n#define WL_PRIVATE\n#endif\n\nextern const struct wl_interface orbital_authorizer_feedback_interface;\n\nstatic const struct wl_interface *orbital_authorizer_types[] = {\n\t&orbital_authorizer_feedback_interface,\n\tNULL,\n};\n\nstatic const struct wl_message orbital_authorizer_requests[] = {\n\t{ \"destroy\", \"\", orbital_authorizer_types + 0 },\n\t{ \"authorize\", \"ns\", orbital_authorizer_types + 0 },\n};\n\nWL_PRIVATE const struct wl_interface orbital_authorizer_interface = {\n\t\"orbital_authorizer\", 1,\n\t2, orbital_authorizer_requests,\n\t0, NULL,\n};\n\nstatic const struct wl_message orbital_authorizer_feedback_events[] = {\n\t{ \"granted\", \"\", orbital_authorizer_types + 0 },\n\t{ \"denied\", \"\", orbital_authorizer_types + 0 },\n};\n\nWL_PRIVATE const struct wl_interface orbital_authorizer_feedback_interface = {\n\t\"orbital_authorizer_feedback\", 1,\n\t0, NULL,\n\t2, orbital_authorizer_feedback_events,\n};\n\n"
  },
  {
    "path": "waysct/orbital-authorizer.xml",
    "content": "<protocol name=\"orbital_authorizer\">\n\n    <interface name=\"orbital_authorizer\" version=\"1\">\n        <description summary=\"authorize clients to use certain interfaces\">\n            The orbital_authorizer global interface allows clients to\n            ask the compositor the authorization to bind certain restricted\n            global interfaces.\n            Any client that aims to bind restricted interfaces should first\n            request the authorization by using this interface. Failing to do\n            so will result in the compositor sending a protocol error to the\n            client when it binds the restricted interface.\n\n            The list of restricted interfaces is compositor dependant, but must\n            not include the core interfaces defined in wayland.xml.\n        </description>\n\n        <request name=\"destroy\" type=\"destructor\">\n            <description summary=\"destroy this orbital_authorizer object\"/>\n        </request>\n\n        <request name=\"authorize\">\n            <description summary=\"authorize a global interface\">\n                The authorize request allows the client to ask the compositor the\n                authorization to bind a restricted global interface. The newly\n                created orbital_authorizer_feedback will be invalid after the\n                compositor send either the granted or denied event so the client\n                must destroy it immediately after.\n            </description>\n            <arg name=\"id\" type=\"new_id\" interface=\"orbital_authorizer_feedback\" summary=\"the new feedback object\"/>\n            <arg name=\"global\" type=\"string\" summary=\"the global interface the client wants to bind\"/>\n        </request>\n    </interface>\n\n    <interface name=\"orbital_authorizer_feedback\" version=\"1\">\n        <description summary=\"feedback for an authorization request\">\n            The orbital_authorizer_feedback interface is used by requesting\n            an authorization with the orbital_authorizer.authorize request.\n            The compositor will send either the granted or denied event based\n            on the system and user configuration. How the authorization process\n            works is compositor specific, but a compositor is allowed to ask\n            for user input, so the response for an authorization request may\n            come after some time.\n        </description>\n\n        <event name=\"granted\">\n            <description summary=\"the authorization was granted\">\n                The authorization was granted. The client can now bind the restricted\n                interface.\n            </description>\n        </event>\n\n        <event name=\"denied\">\n            <description summary=\"the authorization was denied\">\n                The authorization was denied. The client is not allowed to bind the\n                restricted interface and trying to do so will trigger a protocol\n                error killing the client.\n            </description>\n        </event>\n    </interface>\n\n</protocol>\n"
  },
  {
    "path": "waysct/os-compatibility-impl.h",
    "content": "/*\n * Copyright © 2012 Collabora, Ltd.\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice (including the\n * next paragraph) shall be included in all copies or substantial\n * portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include \"os-compatibility.h\"\n\nint os_fd_set_cloexec(int fd) {\n\tif (fd == -1) {\n\t\treturn -1;\n\t}\n\n\tlong flags = fcntl(fd, F_GETFD);\n\tif (flags == -1) {\n\t\treturn -1;\n\t}\n\n\tif (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\nint set_cloexec_or_close(int fd) {\n\tif (os_fd_set_cloexec(fd) != 0) {\n\t\tclose(fd);\n\t\treturn -1;\n\t}\n\treturn fd;\n}\n\nint create_tmpfile_cloexec(char *tmpname) {\n\tint fd;\n\tmode_t prev_umask = umask(0066);\n#ifdef HAVE_MKOSTEMP\n\tfd = mkostemp(tmpname, O_CLOEXEC);\n\tif (fd >= 0) {\n\t\tunlink(tmpname);\n\t}\n#else\n\tfd = mkstemp(tmpname);\n\tif (fd >= 0) {\n\t\tfd = set_cloexec_or_close(fd);\n\t\tunlink(tmpname);\n\t}\n#endif\n\tumask(prev_umask);\n\n\treturn fd;\n}\n\n/*\n * Create a new, unique, anonymous file of the given size, and\n * return the file descriptor for it. The file descriptor is set\n * CLOEXEC. The file is immediately suitable for mmap()'ing\n * the given size at offset zero.\n *\n * The file should not have a permanent backing store like a disk,\n * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.\n *\n * The file name is deleted from the file system.\n *\n * The file is suitable for buffer sharing between processes by\n * transmitting the file descriptor over Unix sockets using the\n * SCM_RIGHTS methods.\n *\n * If the C library implements posix_fallocate(), it is used to\n * guarantee that disk space is available for the file at the\n * given size. If disk space is insufficient, errno is set to ENOSPC.\n * If posix_fallocate() is not supported, program may receive\n * SIGBUS on accessing mmap()'ed file contents instead.\n */\nint os_create_anonymous_file(off_t size) {\n\tstatic const char template[] = \"/redshift-shared-XXXXXX\";\n\n\tconst char *path = getenv(\"XDG_RUNTIME_DIR\");\n\tif (!path) {\n\t\terrno = ENOENT;\n\t\treturn -1;\n\t}\n\n\tchar *name = malloc(strlen(path) + sizeof(template));\n\tif (!name) {\n\t\treturn -1;\n\t}\n\n\tstrcpy(name, path);\n\tstrcat(name, template);\n\n\tint fd = create_tmpfile_cloexec(name);\n\tfree(name);\n\tif (fd < 0) {\n\t\treturn -1;\n\t}\n\n#ifdef WLR_HAS_POSIX_FALLOCATE\n\tint ret;\n\tdo {\n\t\tret = posix_fallocate(fd, 0, size);\n\t} while (ret == EINTR);\n\tif (ret != 0) {\n\t\tclose(fd);\n\t\terrno = ret;\n\t\treturn -1;\n\t}\n#else\n\tint ret;\n\tdo {\n\t\tret = ftruncate(fd, size);\n\t} while (ret < 0 && errno == EINTR);\n\tif (ret < 0) {\n\t\tclose(fd);\n\t\treturn -1;\n\t}\n#endif\n\n\treturn fd;\n}\n"
  },
  {
    "path": "waysct/os-compatibility.h",
    "content": "#ifndef UTIL_OS_COMPATIBILITY_H\n#define UTIL_OS_COMPATIBILITY_H\n\nint os_fd_set_cloexec(int fd);\nint set_cloexec_or_close(int fd);\nint create_tmpfile_cloexec(char *tmpname);\nint os_create_anonymous_file(off_t size);\n\n#endif\n"
  },
  {
    "path": "waysct/waysct.go",
    "content": "// waysct is a set color temp implementation for Wayland.\n// Many of these files are taken from\n// https://github.com/minus7/redshift/commit/7da875d34854a6a34612d5ce4bd8718c32bec804\n// see Redshift for the GPL license.\npackage waysct\n\n//go:generate wayland-scanner private-code orbital-authorizer.xml orbital-authorizer-protocol.h\n//go:generate wayland-scanner client-header orbital-authorizer.xml orbital-authorizer-client-protocol.h\n//go:generate wayland-scanner private-code gamma-control.xml gamma-control-protocol.h\n//go:generate wayland-scanner client-header gamma-control.xml gamma-control-client-protocol.h\n\n// #cgo LDFLAGS: -lm -lwayland-client\n// #include \"gamma-wl.h\"\nimport \"C\"\nimport (\n\t\"github.com/pkg/errors\"\n)\n\ntype Manager struct {\n\tstate *C.wayland_state_t\n}\n\nfunc StartManager() (*Manager, error) {\n\tm := Manager{}\n\tif errno := C.wayland_init(&m.state); errno != 0 {\n\t\treturn nil, errors.Errorf(\"wayland_init: errno %d\", errno)\n\t}\n\tif errno := C.wayland_start(m.state); errno != 0 {\n\t\treturn nil, errors.Errorf(\"wayland_start: errno %d\", errno)\n\t}\n\treturn &m, nil\n}\n\nfunc (m *Manager) Close() {\n\tC.wayland_free(m.state)\n\tm.state = nil\n}\n\nfunc (m *Manager) SetColorTemp(temp int) error {\n\tvar setting C.color_setting_t\n\tsetting.brightness = 1.0\n\tsetting.gamma[0] = 1.0\n\tsetting.gamma[1] = 1.0\n\tsetting.gamma[2] = 1.0\n\tsetting.temperature = C.int(temp)\n\tif errno := C.wayland_set_temperature(m.state, &setting); errno != 0 {\n\t\treturn errors.Errorf(\"wayland_set_temperature: errno %d\", errno)\n\t}\n\treturn nil\n}\n"
  }
]