Repository: d4l3k/go-sct Branch: master Commit: bb1dc072d627 Files: 26 Total size: 72.2 KB Directory structure: gitextract_ec4q16gx/ ├── .travis.yml ├── Dockerfile ├── LICENSE ├── README.md ├── cmd/ │ ├── sct/ │ │ └── sct.go │ └── waysct/ │ └── waysct.go ├── geoip/ │ ├── geoip.go │ └── geoip_test.go ├── go.mod ├── go.sum ├── sct.go ├── sct_nix.go ├── sct_windows.go ├── sctcli/ │ ├── sctcli.go │ └── sctcli_test.go ├── test.sh └── waysct/ ├── gamma-control-client-protocol.h ├── gamma-control-protocol.h ├── gamma-control.xml ├── gamma-wl.h ├── orbital-authorizer-client-protocol.h ├── orbital-authorizer-protocol.h ├── orbital-authorizer.xml ├── os-compatibility-impl.h ├── os-compatibility.h └── waysct.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .travis.yml ================================================ sudo: required services: - docker language: go go: 1.10.x script: - docker build -t sct . - docker run --rm -it sct ./test.sh addons: apt: packages: - xorg-dev - libglu1-mesa-dev ================================================ FILE: Dockerfile ================================================ FROM golang:1.10 RUN apt-get update && apt-get install -y curl xvfb xorg-dev libglu1-mesa-dev WORKDIR /go/src/github.com/d4l3k/go-sct COPY . . RUN go get -d -v ./... RUN go install -v ./... ================================================ FILE: LICENSE ================================================ Copyright (C) 2015 Tristan Rice Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # go-sct [![GoDoc](https://godoc.org/github.com/d4l3k/go-sct?status.svg)](https://godoc.org/github.com/d4l3k/go-sct) A color temperature setting library and CLI that operates in a similar way to f.lux and Redshift. The command line app automatically determines your location using GeoIP and adjusts the color temperature depending on time. For wayland support replace all commands with `waysct` instead of `sct`. ```sh # For X11 and Windows $ go install github.com/d4l3k/go-sct/cmd/sct@latest $ sct # Launch in background $ sct 3000 # One time temperature change. Temperature must be 1000-10000. # For Wayland $ go install github.com/d4l3k/go-sct/cmd/waysct@latest $ waysct # Launch in background $ waysct 3000 # One time temperature change. Wayland requires a persistent manager so this will immediately revert. ``` ## Windows By default, the lowest color temperature allowed is around 4500K. More information is available [here](http://jonls.dk/2010/09/windows-gamma-adjustments/) There is a workaround to allow all possible adjustments by alterting the registry. ``` Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ICM] "GdiIcmGammaRange"=dword:00000100 ``` Save the above as a file with a ".reg" extension and double click to apply. ## Credit Setting 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. go-sct also provides the `geoip` package which is a packaged version of http://devdungeon.com/content/ip-geolocation-go ## License Made by [Tristan Rice](https://fn.lc). go-sct is licensed under the MIT license. `geoip` and `sct` are copyrighted by their respective owners. `waysct` is using code from the redshift implementation and is licensed under GPLv3. See https://github.com/minus7/redshift/commit/7da875d34854a6a34612d5ce4bd8718c32bec804 ================================================ FILE: cmd/sct/sct.go ================================================ package main import ( "github.com/d4l3k/go-sct" "github.com/d4l3k/go-sct/sctcli" ) func main() { sctcli.Main(func(temp int) error { sct.SetColorTemp(temp) return nil }) } ================================================ FILE: cmd/waysct/waysct.go ================================================ // waysct is a set color temp utility for Wayland. package main import ( "log" "github.com/d4l3k/go-sct/sctcli" "github.com/d4l3k/go-sct/waysct" ) func main() { m, err := waysct.StartManager() if err != nil { log.Fatalf("%+v", err) } defer m.Close() sctcli.Main(m.SetColorTemp) } ================================================ FILE: geoip/geoip.go ================================================ // geoip returns the lat/lng of the target IP address (or current machine) package geoip import ( "encoding/json" "fmt" "net/http" ) type GeoIP struct { City string `json:"city"` Latitude float64 `json:"lat"` Longitude float64 `json:"lon"` } // LookupIP looks up the geolocation information for the specified address ("" for current host). func LookupIP(address string) (*GeoIP, error) { response, err := http.Get(fmt.Sprintf("http://ip-api.com/json/%s?fields=lat,lon,city", address)) if err != nil { return nil, err } defer response.Body.Close() var geo GeoIP if err := json.NewDecoder(response.Body).Decode(&geo); err != nil { return nil, err } return &geo, nil } ================================================ FILE: geoip/geoip_test.go ================================================ package geoip import "testing" func TestLookupIP(t *testing.T) { geo, err := LookupIP("") if err != nil { t.Fatal(err) } if geo.Latitude == 0 { t.Fatalf("expected non empty Latitude: %+v", geo) } if geo.Longitude == 0 { t.Fatalf("expected non empty Longitude: %+v", geo) } } ================================================ FILE: go.mod ================================================ module github.com/d4l3k/go-sct go 1.16 require ( github.com/cpucycle/astrotime v0.0.0-20120927164819-9c7d514efdb5 github.com/pkg/errors v0.9.1 ) ================================================ FILE: go.sum ================================================ github.com/cpucycle/astrotime v0.0.0-20120927164819-9c7d514efdb5 h1:Z6YGTs9TwkwIVCltpgUNFa+L5SAyGfIL9gH0RB7yDgw= github.com/cpucycle/astrotime v0.0.0-20120927164819-9c7d514efdb5/go.mod h1:O+WdStE4WLD+lG5MaDRl9dSJR7w4HZJrJbqVsOEx5Lw= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= ================================================ FILE: sct.go ================================================ package sct import "runtime" type color struct { r, g, b float64 } /* cribbed from redshift, but truncated with 500K steps */ var whitepoints = []color{ {1.00000000, 0.18172716, 0.00000000}, /* 1000K */ {1.00000000, 0.42322816, 0.00000000}, {1.00000000, 0.54360078, 0.08679949}, {1.00000000, 0.64373109, 0.28819679}, {1.00000000, 0.71976951, 0.42860152}, {1.00000000, 0.77987699, 0.54642268}, {1.00000000, 0.82854786, 0.64816570}, {1.00000000, 0.86860704, 0.73688797}, {1.00000000, 0.90198230, 0.81465502}, {1.00000000, 0.93853986, 0.88130458}, {1.00000000, 0.97107439, 0.94305985}, {1.00000000, 1.00000000, 1.00000000}, /* 6500K */ {0.95160805, 0.96983355, 1.00000000}, {0.91194747, 0.94470005, 1.00000000}, {0.87906581, 0.92357340, 1.00000000}, {0.85139976, 0.90559011, 1.00000000}, {0.82782969, 0.89011714, 1.00000000}, {0.80753191, 0.87667891, 1.00000000}, {0.78988728, 0.86491137, 1.00000000}, /* 10000K */ {0.77442176, 0.85453121, 1.00000000}, } // SetColorTemp changes the monitor colors to reflect the specified color temperature. func SetColorTemp(temp int) { // An attempt to fix https://github.com/d4l3k/go-sct/issues/9 if runtime.GOOS == "windows" { setColorTemp(temp + 1) } setColorTemp(temp) } func setColorTemp(temp int) { if temp < 1000 || temp > 10000 { temp = 6500 } temp -= 1000 ratio := float64((temp-1000)%500) / 500.0 point := whitepoints[temp/500] point1 := whitepoints[(temp/500)+1] gammar := point.r*(1-ratio) + point1.r*ratio gammag := point.g*(1-ratio) + point1.g*ratio gammab := point.b*(1-ratio) + point1.b*ratio setColorGamma(gammar, gammag, gammab) } ================================================ FILE: sct_nix.go ================================================ // +build linux freebsd openbsd package sct // #cgo CFLAGS: -I/usr/X11R6/include -I/usr/local/include // #cgo LDFLAGS: -L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXrandr // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // Window RootWindowMacro(Display * dpy, int scr) { // return RootWindow(dpy, scr); // } // RRCrtc crtcxid(RRCrtc * crtcs, int i) { // return crtcs[i]; // } // void ushortSet(ushort * s, int k, double v) { // s[k] = (ushort)v; // } // ushort* ushortCast(void* s) { // return (ushort*)s; // } // int screenCount(Display * dpy) { // return XScreenCount(dpy); // } import "C" import "unsafe" // setColorGamma changes the Xrandr colors to reflect the specified color temperature. func setColorGamma(gammar, gammag, gammab float64) { dpy := C.XOpenDisplay(nil) screenCount := C.screenCount(dpy) for screen := C.int(0); screen < screenCount; screen++ { root := C.RootWindowMacro(dpy, screen) res := C.XRRGetScreenResourcesCurrent(dpy, root) for c := C.int(0); c < res.ncrtc; c++ { crtcxid := C.crtcxid(res.crtcs, c) size := C.XRRGetCrtcGammaSize(dpy, crtcxid) crtc_gamma := C.XRRAllocGamma(size) for i := C.int(0); i < size; i++ { g := 65535.0 * float64(i) / float64(size) C.ushortSet(C.ushortCast(unsafe.Pointer(crtc_gamma.red)), i, C.double(g*gammar)) C.ushortSet(C.ushortCast(unsafe.Pointer(crtc_gamma.green)), i, C.double(g*gammag)) C.ushortSet(C.ushortCast(unsafe.Pointer(crtc_gamma.blue)), i, C.double(g*gammab)) } C.XRRSetCrtcGamma(dpy, crtcxid, crtc_gamma) C.XFree(unsafe.Pointer(crtc_gamma)) } C.XFree(unsafe.Pointer(res)) } C.XCloseDisplay(dpy) } ================================================ FILE: sct_windows.go ================================================ package sct import ( "syscall" "unsafe" ) // setColorGamma changes the device gamma curve colors to reflect the specified color temperature. func setColorGamma(gammar, gammag, gammab float64) { user32 := syscall.NewLazyDLL("User32.dll") gdi32 := syscall.NewLazyDLL("Gdi32.dll") procGetDC := user32.NewProc("GetDC") procSetDeviceGammaRamp := gdi32.NewProc("SetDeviceGammaRamp") var gamma [3][256]uint16 for i := 0; i < 256; i++ { g := 65535.0 * float64(i) / float64(256) gamma[0][i] = uint16(g * gammar) gamma[1][i] = uint16(g * gammag) gamma[2][i] = uint16(g * gammab) } hdc, _, _ := procGetDC.Call(uintptr(0)) procSetDeviceGammaRamp.Call(hdc, uintptr(unsafe.Pointer(&gamma))) } ================================================ FILE: sctcli/sctcli.go ================================================ package sctcli import ( "flag" "io/ioutil" "log" "os" "os/exec" "path/filepath" "strconv" "strings" "time" "github.com/cpucycle/astrotime" "github.com/d4l3k/go-sct/geoip" ) var dayTemp = flag.Int("dayTemp", 6500, "The color temperature during the day.") var nightTemp = flag.Int("nightTemp", 3000, "The color temperature during the night.") var daemon = flag.Bool("d", true, "run app as a daemon") var mode = flag.String("mode", "geoip", "Mode of daemon (geoip or timed). Timed mode uses specified sunrise-time, midday-time, and sunset-time.") var sunriseTimeStr = flag.String("sunrise-time", "06:30", "Sunrise time (HH:MM)") var sunsetTimeStr = flag.String("sunset-time", "21:00", "Sunset time (HH:MM)") var middayTimeStr = flag.String("midday-time", "14:00", "Mid day (brightest) time (HH:MM)") var midnight, sunriseTime, sunsetTime, middayTime time.Time func Main(setColorTemp func(temp int) error) { c := SCTCLI{ SetColorTemp: setColorTemp, } if err := c.Run(); err != nil { log.Fatalf("%+v", err) } } type SCTCLI struct { SetColorTemp func(temp int) error } func (c SCTCLI) monitorGeo() error { log.Printf("Fetching location...") geo, err := geoip.LookupIP("") if err != nil { return err } log.Printf(" - City: %s, Lat: %f, Lon: %f", geo.City, geo.Latitude, geo.Longitude) log.Printf("Monitoring daylight settings...") var lastState *bool for { rise := astrotime.NextSunrise(time.Now(), geo.Latitude, -geo.Longitude) set := astrotime.NextSunset(time.Now(), geo.Latitude, -geo.Longitude) state := rise.Before(set) if lastState != nil && state == *lastState { time.Sleep(1 * time.Minute) continue } lastState = &state if state { log.Print("Good night!") if err := c.interpolateColorTemp(*nightTemp); err != nil { return err } } else { log.Print("Good morning!") if err := c.interpolateColorTemp(*dayTemp); err != nil { return err } } } } var ( totalTime = 3 * time.Second stepEvery = 1 * time.Second / 60 ) func (c SCTCLI) interpolateColorTemp(new int) error { old, err := getCurrentColorTemp() if err != nil { return err } steps := int(totalTime / stepEvery) stepSize := (new - old) / steps for i := 0; i < steps; i++ { timer := time.After(stepEvery) if err := c.SetColorTemp(old + stepSize*i); err != nil { return err } <-timer } if err := c.SetColorTemp(new); err != nil { return err } return saveCurrentColorTemp(new) } func tempFile() string { display := os.Getenv("DISPLAY") return filepath.Join(os.TempDir(), "sct-temp-"+display) } func saveCurrentColorTemp(temp int) error { return ioutil.WriteFile(tempFile(), []byte(strconv.Itoa(temp)), 0644) } func getCurrentColorTemp() (int, error) { body, err := ioutil.ReadFile(tempFile()) if os.IsNotExist(err) { return *dayTemp, nil } else if err != nil { return 0, err } return strconv.Atoi(string(body)) } func (c SCTCLI) monitorTime() error { var monitorTemperature int monitorTemperature = 6500 for { curTime := time.Now() midnight = time.Date(curTime.Year(), curTime.Month(), curTime.Day(), 0, 0, 0, 0, time.Local) // Advance the day? if midnight.After(sunsetTime) { sunriseTime = sunriseTime.AddDate(0, 0, 1) middayTime = middayTime.AddDate(0, 0, 1) sunsetTime = sunsetTime.AddDate(0, 0, 1) } if curTime.After(sunriseTime) && curTime.Before(sunsetTime) { if curTime.Before(middayTime) { elapsedDuration := curTime.Sub(sunriseTime) ratio := float64(elapsedDuration) / float64(middayTime.Sub(sunriseTime)) monitorTemperature = int(float64(*nightTemp)*(1-ratio) + float64(*dayTemp)*ratio) } else { elapsedDuration := curTime.Sub(middayTime) ratio := float64(elapsedDuration) / float64(sunsetTime.Sub(middayTime)) monitorTemperature = int(float64(*dayTemp)*(1-ratio) + float64(*nightTemp)*ratio) } } else { monitorTemperature = *nightTemp } if err := c.SetColorTemp(monitorTemperature); err != nil { return err } time.Sleep(10 * time.Minute) } } func (c SCTCLI) Run() error { flag.Parse() args := flag.Args() if len(args) == 1 { if temp, err := strconv.Atoi(args[0]); err == nil { if err := c.interpolateColorTemp(temp); err != nil { return err } } } else if len(args) == 0 { // Parse time arguments curTime := time.Now() midnight = time.Date(curTime.Year(), curTime.Month(), curTime.Day(), 0, 0, 0, 0, time.Local) var perr error if sunriseTime, perr = time.ParseInLocation("2006-01-02 15:04", midnight.Format("2006-01-02 ")+*sunriseTimeStr, time.Local); perr != nil { return perr } if sunsetTime, perr = time.ParseInLocation("2006-01-02 15:04", midnight.Format("2006-01-02 ")+*sunsetTimeStr, time.Local); perr != nil { return perr } if middayTime, perr = time.ParseInLocation("2006-01-02 15:04", midnight.Format("2006-01-02 ")+*middayTimeStr, time.Local); perr != nil { return perr } if *daemon { args := os.Args[1:] for i := 0; i < len(args); i++ { if strings.HasPrefix(args[i], "-d") { args[i] = "-d=false" break } } args = append(args, "-d=false") cmd := exec.Command(os.Args[0], args...) cmd.Start() log.Println("Launched background process... pid", cmd.Process.Pid) os.Exit(0) } else { switch *mode { case "timed": if err := c.monitorTime(); err != nil { return err } default: if err := c.monitorGeo(); err != nil { return err } } } } return nil } ================================================ FILE: sctcli/sctcli_test.go ================================================ package sctcli import ( "os" "testing" "time" ) func setDisplay(val string) func() { old := os.Getenv("DISPLAY") os.Setenv("DISPLAY", val) return func() { os.Setenv("DISPLAY", old) } } func TestGetCurrentColorTemp(t *testing.T) { defer setDisplay("testdisplay" + time.Now().String())() temp, err := getCurrentColorTemp() if err != nil { t.Fatal(err) } if temp != *dayTemp { t.Fatalf("expected default day temp") } for _, want := range []int{3000, 6500} { if err := saveCurrentColorTemp(want); err != nil { t.Fatal(err) } temp, err := getCurrentColorTemp() if err != nil { t.Fatal(err) } if temp != want { t.Fatalf("expected %d temp", want) } } } func TestInterpolate(t *testing.T) { totalTime = 100 * time.Millisecond c := SCTCLI{SetColorTemp: func(temp int) error { return nil }} for _, want := range []int{3000, 6500} { if err := c.interpolateColorTemp(want); err != nil { t.Fatal(err) } temp, err := getCurrentColorTemp() if err != nil { t.Fatal(err) } if temp != want { t.Fatalf("expected %d temp", want) } } } ================================================ FILE: test.sh ================================================ #!/bin/bash xvfb-run go test -v ./... ================================================ FILE: waysct/gamma-control-client-protocol.h ================================================ /* Generated by wayland-scanner 1.19.0 */ #ifndef WLR_GAMMA_CONTROL_UNSTABLE_V1_CLIENT_PROTOCOL_H #define WLR_GAMMA_CONTROL_UNSTABLE_V1_CLIENT_PROTOCOL_H #include #include #include "wayland-client.h" #ifdef __cplusplus extern "C" { #endif /** * @page page_wlr_gamma_control_unstable_v1 The wlr_gamma_control_unstable_v1 protocol * manage gamma tables of outputs * * @section page_desc_wlr_gamma_control_unstable_v1 Description * * This protocol allows a privileged client to set the gamma tables for * outputs. * * Warning! The protocol described in this file is experimental and * backward incompatible changes may be made. Backward compatible changes * may be added together with the corresponding interface version bump. * Backward incompatible changes are done by bumping the version number in * the protocol and interface names and resetting the interface version. * Once the protocol is to be declared stable, the 'z' prefix and the * version number in the protocol and interface names are removed and the * interface version number is reset. * * @section page_ifaces_wlr_gamma_control_unstable_v1 Interfaces * - @subpage page_iface_zwlr_gamma_control_manager_v1 - manager to create per-output gamma controls * - @subpage page_iface_zwlr_gamma_control_v1 - adjust gamma tables for an output * @section page_copyright_wlr_gamma_control_unstable_v1 Copyright *
 *
 * Copyright © 2015 Giulio camuffo
 * Copyright © 2018 Simon Ser
 *
 * Permission to use, copy, modify, distribute, and sell this
 * software and its documentation for any purpose is hereby granted
 * without fee, provided that the above copyright notice appear in
 * all copies and that both that copyright notice and this permission
 * notice appear in supporting documentation, and that the name of
 * the copyright holders not be used in advertising or publicity
 * pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied
 * warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
 * THIS SOFTWARE.
 * 
*/ struct wl_output; struct zwlr_gamma_control_manager_v1; struct zwlr_gamma_control_v1; #ifndef ZWLR_GAMMA_CONTROL_MANAGER_V1_INTERFACE #define ZWLR_GAMMA_CONTROL_MANAGER_V1_INTERFACE /** * @page page_iface_zwlr_gamma_control_manager_v1 zwlr_gamma_control_manager_v1 * @section page_iface_zwlr_gamma_control_manager_v1_desc Description * * This interface is a manager that allows creating per-output gamma * controls. * @section page_iface_zwlr_gamma_control_manager_v1_api API * See @ref iface_zwlr_gamma_control_manager_v1. */ /** * @defgroup iface_zwlr_gamma_control_manager_v1 The zwlr_gamma_control_manager_v1 interface * * This interface is a manager that allows creating per-output gamma * controls. */ extern const struct wl_interface zwlr_gamma_control_manager_v1_interface; #endif #ifndef ZWLR_GAMMA_CONTROL_V1_INTERFACE #define ZWLR_GAMMA_CONTROL_V1_INTERFACE /** * @page page_iface_zwlr_gamma_control_v1 zwlr_gamma_control_v1 * @section page_iface_zwlr_gamma_control_v1_desc Description * * This interface allows a client to adjust gamma tables for a particular * output. * * The client will receive the gamma size, and will then be able to set gamma * tables. At any time the compositor can send a failed event indicating that * this object is no longer valid. * * There must always be at most one gamma control object per output, which * has exclusive access to this particular output. When the gamma control * object is destroyed, the gamma table is restored to its original value. * @section page_iface_zwlr_gamma_control_v1_api API * See @ref iface_zwlr_gamma_control_v1. */ /** * @defgroup iface_zwlr_gamma_control_v1 The zwlr_gamma_control_v1 interface * * This interface allows a client to adjust gamma tables for a particular * output. * * The client will receive the gamma size, and will then be able to set gamma * tables. At any time the compositor can send a failed event indicating that * this object is no longer valid. * * There must always be at most one gamma control object per output, which * has exclusive access to this particular output. When the gamma control * object is destroyed, the gamma table is restored to its original value. */ extern const struct wl_interface zwlr_gamma_control_v1_interface; #endif #define ZWLR_GAMMA_CONTROL_MANAGER_V1_GET_GAMMA_CONTROL 0 #define ZWLR_GAMMA_CONTROL_MANAGER_V1_DESTROY 1 /** * @ingroup iface_zwlr_gamma_control_manager_v1 */ #define ZWLR_GAMMA_CONTROL_MANAGER_V1_GET_GAMMA_CONTROL_SINCE_VERSION 1 /** * @ingroup iface_zwlr_gamma_control_manager_v1 */ #define ZWLR_GAMMA_CONTROL_MANAGER_V1_DESTROY_SINCE_VERSION 1 /** @ingroup iface_zwlr_gamma_control_manager_v1 */ static inline void zwlr_gamma_control_manager_v1_set_user_data(struct zwlr_gamma_control_manager_v1 *zwlr_gamma_control_manager_v1, void *user_data) { wl_proxy_set_user_data((struct wl_proxy *) zwlr_gamma_control_manager_v1, user_data); } /** @ingroup iface_zwlr_gamma_control_manager_v1 */ static inline void * zwlr_gamma_control_manager_v1_get_user_data(struct zwlr_gamma_control_manager_v1 *zwlr_gamma_control_manager_v1) { return wl_proxy_get_user_data((struct wl_proxy *) zwlr_gamma_control_manager_v1); } static inline uint32_t zwlr_gamma_control_manager_v1_get_version(struct zwlr_gamma_control_manager_v1 *zwlr_gamma_control_manager_v1) { return wl_proxy_get_version((struct wl_proxy *) zwlr_gamma_control_manager_v1); } /** * @ingroup iface_zwlr_gamma_control_manager_v1 * * Create a gamma control that can be used to adjust gamma tables for the * provided output. */ static inline struct zwlr_gamma_control_v1 * zwlr_gamma_control_manager_v1_get_gamma_control(struct zwlr_gamma_control_manager_v1 *zwlr_gamma_control_manager_v1, struct wl_output *output) { struct wl_proxy *id; id = wl_proxy_marshal_constructor((struct wl_proxy *) zwlr_gamma_control_manager_v1, ZWLR_GAMMA_CONTROL_MANAGER_V1_GET_GAMMA_CONTROL, &zwlr_gamma_control_v1_interface, NULL, output); return (struct zwlr_gamma_control_v1 *) id; } /** * @ingroup iface_zwlr_gamma_control_manager_v1 * * All objects created by the manager will still remain valid, until their * appropriate destroy request has been called. */ static inline void zwlr_gamma_control_manager_v1_destroy(struct zwlr_gamma_control_manager_v1 *zwlr_gamma_control_manager_v1) { wl_proxy_marshal((struct wl_proxy *) zwlr_gamma_control_manager_v1, ZWLR_GAMMA_CONTROL_MANAGER_V1_DESTROY); wl_proxy_destroy((struct wl_proxy *) zwlr_gamma_control_manager_v1); } #ifndef ZWLR_GAMMA_CONTROL_V1_ERROR_ENUM #define ZWLR_GAMMA_CONTROL_V1_ERROR_ENUM enum zwlr_gamma_control_v1_error { /** * invalid gamma tables */ ZWLR_GAMMA_CONTROL_V1_ERROR_INVALID_GAMMA = 1, }; #endif /* ZWLR_GAMMA_CONTROL_V1_ERROR_ENUM */ /** * @ingroup iface_zwlr_gamma_control_v1 * @struct zwlr_gamma_control_v1_listener */ struct zwlr_gamma_control_v1_listener { /** * size of gamma ramps * * Advertise the size of each gamma ramp. * * This event is sent immediately when the gamma control object is * created. */ void (*gamma_size)(void *data, struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1, uint32_t size); /** * object no longer valid * * This event indicates that the gamma control is no longer * valid. This can happen for a number of reasons, including: - The * output doesn't support gamma tables - Setting the gamma tables * failed - Another client already has exclusive gamma control for * this output - The compositor has transfered gamma control to * another client * * Upon receiving this event, the client should destroy this * object. */ void (*failed)(void *data, struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1); }; /** * @ingroup iface_zwlr_gamma_control_v1 */ static inline int zwlr_gamma_control_v1_add_listener(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1, const struct zwlr_gamma_control_v1_listener *listener, void *data) { return wl_proxy_add_listener((struct wl_proxy *) zwlr_gamma_control_v1, (void (**)(void)) listener, data); } #define ZWLR_GAMMA_CONTROL_V1_SET_GAMMA 0 #define ZWLR_GAMMA_CONTROL_V1_DESTROY 1 /** * @ingroup iface_zwlr_gamma_control_v1 */ #define ZWLR_GAMMA_CONTROL_V1_GAMMA_SIZE_SINCE_VERSION 1 /** * @ingroup iface_zwlr_gamma_control_v1 */ #define ZWLR_GAMMA_CONTROL_V1_FAILED_SINCE_VERSION 1 /** * @ingroup iface_zwlr_gamma_control_v1 */ #define ZWLR_GAMMA_CONTROL_V1_SET_GAMMA_SINCE_VERSION 1 /** * @ingroup iface_zwlr_gamma_control_v1 */ #define ZWLR_GAMMA_CONTROL_V1_DESTROY_SINCE_VERSION 1 /** @ingroup iface_zwlr_gamma_control_v1 */ static inline void zwlr_gamma_control_v1_set_user_data(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1, void *user_data) { wl_proxy_set_user_data((struct wl_proxy *) zwlr_gamma_control_v1, user_data); } /** @ingroup iface_zwlr_gamma_control_v1 */ static inline void * zwlr_gamma_control_v1_get_user_data(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1) { return wl_proxy_get_user_data((struct wl_proxy *) zwlr_gamma_control_v1); } static inline uint32_t zwlr_gamma_control_v1_get_version(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1) { return wl_proxy_get_version((struct wl_proxy *) zwlr_gamma_control_v1); } /** * @ingroup iface_zwlr_gamma_control_v1 * * Set the gamma table. The file descriptor can be memory-mapped to provide * the raw gamma table, which contains successive gamma ramps for the red, * green and blue channels. Each gamma ramp is an array of 16-byte unsigned * integers which has the same length as the gamma size. * * The file descriptor data must have the same length as three times the * gamma size. */ static inline void zwlr_gamma_control_v1_set_gamma(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1, int32_t fd) { wl_proxy_marshal((struct wl_proxy *) zwlr_gamma_control_v1, ZWLR_GAMMA_CONTROL_V1_SET_GAMMA, fd); } /** * @ingroup iface_zwlr_gamma_control_v1 * * Destroys the gamma control object. If the object is still valid, this * restores the original gamma tables. */ static inline void zwlr_gamma_control_v1_destroy(struct zwlr_gamma_control_v1 *zwlr_gamma_control_v1) { wl_proxy_marshal((struct wl_proxy *) zwlr_gamma_control_v1, ZWLR_GAMMA_CONTROL_V1_DESTROY); wl_proxy_destroy((struct wl_proxy *) zwlr_gamma_control_v1); } #ifdef __cplusplus } #endif #endif ================================================ FILE: waysct/gamma-control-protocol.h ================================================ /* Generated by wayland-scanner 1.19.0 */ /* * Copyright © 2015 Giulio camuffo * Copyright © 2018 Simon Ser * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this permission * notice appear in supporting documentation, and that the name of * the copyright holders not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF * THIS SOFTWARE. */ #include #include #include "wayland-util.h" #ifndef __has_attribute # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ #endif #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) #define WL_PRIVATE __attribute__ ((visibility("hidden"))) #else #define WL_PRIVATE #endif extern const struct wl_interface wl_output_interface; extern const struct wl_interface zwlr_gamma_control_v1_interface; static const struct wl_interface *wlr_gamma_control_unstable_v1_types[] = { NULL, &zwlr_gamma_control_v1_interface, &wl_output_interface, }; static const struct wl_message zwlr_gamma_control_manager_v1_requests[] = { { "get_gamma_control", "no", wlr_gamma_control_unstable_v1_types + 1 }, { "destroy", "", wlr_gamma_control_unstable_v1_types + 0 }, }; WL_PRIVATE const struct wl_interface zwlr_gamma_control_manager_v1_interface = { "zwlr_gamma_control_manager_v1", 1, 2, zwlr_gamma_control_manager_v1_requests, 0, NULL, }; static const struct wl_message zwlr_gamma_control_v1_requests[] = { { "set_gamma", "h", wlr_gamma_control_unstable_v1_types + 0 }, { "destroy", "", wlr_gamma_control_unstable_v1_types + 0 }, }; static const struct wl_message zwlr_gamma_control_v1_events[] = { { "gamma_size", "u", wlr_gamma_control_unstable_v1_types + 0 }, { "failed", "", wlr_gamma_control_unstable_v1_types + 0 }, }; WL_PRIVATE const struct wl_interface zwlr_gamma_control_v1_interface = { "zwlr_gamma_control_v1", 1, 2, zwlr_gamma_control_v1_requests, 2, zwlr_gamma_control_v1_events, }; ================================================ FILE: waysct/gamma-control.xml ================================================ Copyright © 2015 Giulio camuffo Copyright © 2018 Simon Ser Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. This protocol allows a privileged client to set the gamma tables for outputs. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. This interface is a manager that allows creating per-output gamma controls. Create a gamma control that can be used to adjust gamma tables for the provided output. All objects created by the manager will still remain valid, until their appropriate destroy request has been called. This interface allows a client to adjust gamma tables for a particular output. The client will receive the gamma size, and will then be able to set gamma tables. At any time the compositor can send a failed event indicating that this object is no longer valid. There must always be at most one gamma control object per output, which has exclusive access to this particular output. When the gamma control object is destroyed, the gamma table is restored to its original value. Advertise the size of each gamma ramp. This event is sent immediately when the gamma control object is created. Set the gamma table. The file descriptor can be memory-mapped to provide the raw gamma table, which contains successive gamma ramps for the red, green and blue channels. Each gamma ramp is an array of 16-byte unsigned integers which has the same length as the gamma size. The file descriptor data must have the same length as three times the gamma size. This event indicates that the gamma control is no longer valid. This can happen for a number of reasons, including: - The output doesn't support gamma tables - Setting the gamma tables failed - Another client already has exclusive gamma control for this output - The compositor has transfered gamma control to another client Upon receiving this event, the client should destroy this object. Destroys the gamma control object. If the object is still valid, this restores the original gamma tables. ================================================ FILE: waysct/gamma-wl.h ================================================ /* gamma-wl.c -- Wayland gamma adjustment header This file is part of Redshift. Redshift is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Redshift is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Redshift. If not, see . Copyright (c) 2015 Giulio Camuffo */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_NLS # include # define _(s) gettext(s) #else # define _(s) s #endif #include "os-compatibility.h" #include "os-compatibility-impl.h" //#include "colorramp.h" #include "gamma-control-client-protocol.h" #include "gamma-control-protocol.h" #include "orbital-authorizer-client-protocol.h" #include "orbital-authorizer-protocol.h" /* Color setting */ typedef struct { int temperature; float gamma[3]; float brightness; } color_setting_t; /* Whitepoint values for temperatures at 100K intervals. These will be interpolated for the actual temperature. This table was provided by Ingo Thies, 2013. See the file README-colorramp for more information. */ static const float blackbody_color[] = { 1.00000000, 0.18172716, 0.00000000, /* 1000K */ 1.00000000, 0.25503671, 0.00000000, /* 1100K */ 1.00000000, 0.30942099, 0.00000000, /* 1200K */ 1.00000000, 0.35357379, 0.00000000, /* ... */ 1.00000000, 0.39091524, 0.00000000, 1.00000000, 0.42322816, 0.00000000, 1.00000000, 0.45159884, 0.00000000, 1.00000000, 0.47675916, 0.00000000, 1.00000000, 0.49923747, 0.00000000, 1.00000000, 0.51943421, 0.00000000, 1.00000000, 0.54360078, 0.08679949, 1.00000000, 0.56618736, 0.14065513, 1.00000000, 0.58734976, 0.18362641, 1.00000000, 0.60724493, 0.22137978, 1.00000000, 0.62600248, 0.25591950, 1.00000000, 0.64373109, 0.28819679, 1.00000000, 0.66052319, 0.31873863, 1.00000000, 0.67645822, 0.34786758, 1.00000000, 0.69160518, 0.37579588, 1.00000000, 0.70602449, 0.40267128, 1.00000000, 0.71976951, 0.42860152, 1.00000000, 0.73288760, 0.45366838, 1.00000000, 0.74542112, 0.47793608, 1.00000000, 0.75740814, 0.50145662, 1.00000000, 0.76888303, 0.52427322, 1.00000000, 0.77987699, 0.54642268, 1.00000000, 0.79041843, 0.56793692, 1.00000000, 0.80053332, 0.58884417, 1.00000000, 0.81024551, 0.60916971, 1.00000000, 0.81957693, 0.62893653, 1.00000000, 0.82854786, 0.64816570, 1.00000000, 0.83717703, 0.66687674, 1.00000000, 0.84548188, 0.68508786, 1.00000000, 0.85347859, 0.70281616, 1.00000000, 0.86118227, 0.72007777, 1.00000000, 0.86860704, 0.73688797, 1.00000000, 0.87576611, 0.75326132, 1.00000000, 0.88267187, 0.76921169, 1.00000000, 0.88933596, 0.78475236, 1.00000000, 0.89576933, 0.79989606, 1.00000000, 0.90198230, 0.81465502, 1.00000000, 0.90963069, 0.82838210, 1.00000000, 0.91710889, 0.84190889, 1.00000000, 0.92441842, 0.85523742, 1.00000000, 0.93156127, 0.86836903, 1.00000000, 0.93853986, 0.88130458, 1.00000000, 0.94535695, 0.89404470, 1.00000000, 0.95201559, 0.90658983, 1.00000000, 0.95851906, 0.91894041, 1.00000000, 0.96487079, 0.93109690, 1.00000000, 0.97107439, 0.94305985, 1.00000000, 0.97713351, 0.95482993, 1.00000000, 0.98305189, 0.96640795, 1.00000000, 0.98883326, 0.97779486, 1.00000000, 0.99448139, 0.98899179, 1.00000000, 1.00000000, 1.00000000, /* 6500K */ 0.98947904, 0.99348723, 1.00000000, 0.97940448, 0.98722715, 1.00000000, 0.96975025, 0.98120637, 1.00000000, 0.96049223, 0.97541240, 1.00000000, 0.95160805, 0.96983355, 1.00000000, 0.94303638, 0.96443333, 1.00000000, 0.93480451, 0.95923080, 1.00000000, 0.92689056, 0.95421394, 1.00000000, 0.91927697, 0.94937330, 1.00000000, 0.91194747, 0.94470005, 1.00000000, 0.90488690, 0.94018594, 1.00000000, 0.89808115, 0.93582323, 1.00000000, 0.89151710, 0.93160469, 1.00000000, 0.88518247, 0.92752354, 1.00000000, 0.87906581, 0.92357340, 1.00000000, 0.87315640, 0.91974827, 1.00000000, 0.86744421, 0.91604254, 1.00000000, 0.86191983, 0.91245088, 1.00000000, 0.85657444, 0.90896831, 1.00000000, 0.85139976, 0.90559011, 1.00000000, 0.84638799, 0.90231183, 1.00000000, 0.84153180, 0.89912926, 1.00000000, 0.83682430, 0.89603843, 1.00000000, 0.83225897, 0.89303558, 1.00000000, 0.82782969, 0.89011714, 1.00000000, 0.82353066, 0.88727974, 1.00000000, 0.81935641, 0.88452017, 1.00000000, 0.81530175, 0.88183541, 1.00000000, 0.81136180, 0.87922257, 1.00000000, 0.80753191, 0.87667891, 1.00000000, 0.80380769, 0.87420182, 1.00000000, 0.80018497, 0.87178882, 1.00000000, 0.79665980, 0.86943756, 1.00000000, 0.79322843, 0.86714579, 1.00000000, 0.78988728, 0.86491137, 1.00000000, /* 10000K */ 0.78663296, 0.86273225, 1.00000000, 0.78346225, 0.86060650, 1.00000000, 0.78037207, 0.85853224, 1.00000000, 0.77735950, 0.85650771, 1.00000000, 0.77442176, 0.85453121, 1.00000000, 0.77155617, 0.85260112, 1.00000000, 0.76876022, 0.85071588, 1.00000000, 0.76603147, 0.84887402, 1.00000000, 0.76336762, 0.84707411, 1.00000000, 0.76076645, 0.84531479, 1.00000000, 0.75822586, 0.84359476, 1.00000000, 0.75574383, 0.84191277, 1.00000000, 0.75331843, 0.84026762, 1.00000000, 0.75094780, 0.83865816, 1.00000000, 0.74863017, 0.83708329, 1.00000000, 0.74636386, 0.83554194, 1.00000000, 0.74414722, 0.83403311, 1.00000000, 0.74197871, 0.83255582, 1.00000000, 0.73985682, 0.83110912, 1.00000000, 0.73778012, 0.82969211, 1.00000000, 0.73574723, 0.82830393, 1.00000000, 0.73375683, 0.82694373, 1.00000000, 0.73180765, 0.82561071, 1.00000000, 0.72989845, 0.82430410, 1.00000000, 0.72802807, 0.82302316, 1.00000000, 0.72619537, 0.82176715, 1.00000000, 0.72439927, 0.82053539, 1.00000000, 0.72263872, 0.81932722, 1.00000000, 0.72091270, 0.81814197, 1.00000000, 0.71922025, 0.81697905, 1.00000000, 0.71756043, 0.81583783, 1.00000000, 0.71593234, 0.81471775, 1.00000000, 0.71433510, 0.81361825, 1.00000000, 0.71276788, 0.81253878, 1.00000000, 0.71122987, 0.81147883, 1.00000000, 0.70972029, 0.81043789, 1.00000000, 0.70823838, 0.80941546, 1.00000000, 0.70678342, 0.80841109, 1.00000000, 0.70535469, 0.80742432, 1.00000000, 0.70395153, 0.80645469, 1.00000000, 0.70257327, 0.80550180, 1.00000000, 0.70121928, 0.80456522, 1.00000000, 0.69988894, 0.80364455, 1.00000000, 0.69858167, 0.80273941, 1.00000000, 0.69729688, 0.80184943, 1.00000000, 0.69603402, 0.80097423, 1.00000000, 0.69479255, 0.80011347, 1.00000000, 0.69357196, 0.79926681, 1.00000000, 0.69237173, 0.79843391, 1.00000000, 0.69119138, 0.79761446, 1.00000000, /* 15000K */ 0.69003044, 0.79680814, 1.00000000, 0.68888844, 0.79601466, 1.00000000, 0.68776494, 0.79523371, 1.00000000, 0.68665951, 0.79446502, 1.00000000, 0.68557173, 0.79370830, 1.00000000, 0.68450119, 0.79296330, 1.00000000, 0.68344751, 0.79222975, 1.00000000, 0.68241029, 0.79150740, 1.00000000, 0.68138918, 0.79079600, 1.00000000, 0.68038380, 0.79009531, 1.00000000, 0.67939381, 0.78940511, 1.00000000, 0.67841888, 0.78872517, 1.00000000, 0.67745866, 0.78805526, 1.00000000, 0.67651284, 0.78739518, 1.00000000, 0.67558112, 0.78674472, 1.00000000, 0.67466317, 0.78610368, 1.00000000, 0.67375872, 0.78547186, 1.00000000, 0.67286748, 0.78484907, 1.00000000, 0.67198916, 0.78423512, 1.00000000, 0.67112350, 0.78362984, 1.00000000, 0.67027024, 0.78303305, 1.00000000, 0.66942911, 0.78244457, 1.00000000, 0.66859988, 0.78186425, 1.00000000, 0.66778228, 0.78129191, 1.00000000, 0.66697610, 0.78072740, 1.00000000, 0.66618110, 0.78017057, 1.00000000, 0.66539706, 0.77962127, 1.00000000, 0.66462376, 0.77907934, 1.00000000, 0.66386098, 0.77854465, 1.00000000, 0.66310852, 0.77801705, 1.00000000, 0.66236618, 0.77749642, 1.00000000, 0.66163375, 0.77698261, 1.00000000, 0.66091106, 0.77647551, 1.00000000, 0.66019791, 0.77597498, 1.00000000, 0.65949412, 0.77548090, 1.00000000, 0.65879952, 0.77499315, 1.00000000, 0.65811392, 0.77451161, 1.00000000, 0.65743716, 0.77403618, 1.00000000, 0.65676908, 0.77356673, 1.00000000, 0.65610952, 0.77310316, 1.00000000, 0.65545831, 0.77264537, 1.00000000, 0.65481530, 0.77219324, 1.00000000, 0.65418036, 0.77174669, 1.00000000, 0.65355332, 0.77130560, 1.00000000, 0.65293404, 0.77086988, 1.00000000, 0.65232240, 0.77043944, 1.00000000, 0.65171824, 0.77001419, 1.00000000, 0.65112144, 0.76959404, 1.00000000, 0.65053187, 0.76917889, 1.00000000, 0.64994941, 0.76876866, 1.00000000, /* 20000K */ 0.64937392, 0.76836326, 1.00000000, 0.64880528, 0.76796263, 1.00000000, 0.64824339, 0.76756666, 1.00000000, 0.64768812, 0.76717529, 1.00000000, 0.64713935, 0.76678844, 1.00000000, 0.64659699, 0.76640603, 1.00000000, 0.64606092, 0.76602798, 1.00000000, 0.64553103, 0.76565424, 1.00000000, 0.64500722, 0.76528472, 1.00000000, 0.64448939, 0.76491935, 1.00000000, 0.64397745, 0.76455808, 1.00000000, 0.64347129, 0.76420082, 1.00000000, 0.64297081, 0.76384753, 1.00000000, 0.64247594, 0.76349813, 1.00000000, 0.64198657, 0.76315256, 1.00000000, 0.64150261, 0.76281076, 1.00000000, 0.64102399, 0.76247267, 1.00000000, 0.64055061, 0.76213824, 1.00000000, 0.64008239, 0.76180740, 1.00000000, 0.63961926, 0.76148010, 1.00000000, 0.63916112, 0.76115628, 1.00000000, 0.63870790, 0.76083590, 1.00000000, 0.63825953, 0.76051890, 1.00000000, 0.63781592, 0.76020522, 1.00000000, 0.63737701, 0.75989482, 1.00000000, 0.63694273, 0.75958764, 1.00000000, 0.63651299, 0.75928365, 1.00000000, 0.63608774, 0.75898278, 1.00000000, 0.63566691, 0.75868499, 1.00000000, 0.63525042, 0.75839025, 1.00000000, 0.63483822, 0.75809849, 1.00000000, 0.63443023, 0.75780969, 1.00000000, 0.63402641, 0.75752379, 1.00000000, 0.63362667, 0.75724075, 1.00000000, 0.63323097, 0.75696053, 1.00000000, 0.63283925, 0.75668310, 1.00000000, 0.63245144, 0.75640840, 1.00000000, 0.63206749, 0.75613641, 1.00000000, 0.63168735, 0.75586707, 1.00000000, 0.63131096, 0.75560036, 1.00000000, 0.63093826, 0.75533624, 1.00000000, 0.63056920, 0.75507467, 1.00000000, 0.63020374, 0.75481562, 1.00000000, 0.62984181, 0.75455904, 1.00000000, 0.62948337, 0.75430491, 1.00000000, 0.62912838, 0.75405319, 1.00000000, 0.62877678, 0.75380385, 1.00000000, 0.62842852, 0.75355685, 1.00000000, 0.62808356, 0.75331217, 1.00000000, 0.62774186, 0.75306977, 1.00000000, /* 25000K */ 0.62740336, 0.75282962, 1.00000000 /* 25100K */ }; static void interpolate_color(float a, const float *c1, const float *c2, float *c) { c[0] = (1.0-a)*c1[0] + a*c2[0]; c[1] = (1.0-a)*c1[1] + a*c2[1]; c[2] = (1.0-a)*c1[2] + a*c2[2]; } /* Helper macro used in the fill functions */ #define F(Y, C) pow((Y) * setting->brightness * \ white_point[C], 1.0/setting->gamma[C]) void colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, int size, const color_setting_t *setting) { /* Approximate white point */ float white_point[3]; float alpha = (setting->temperature % 100) / 100.0; int temp_index = ((setting->temperature - 1000) / 100)*3; interpolate_color(alpha, &blackbody_color[temp_index], &blackbody_color[temp_index+3], white_point); for (int i = 0; i < size; i++) { gamma_r[i] = F((double)gamma_r[i]/(UINT16_MAX+1), 0) * (UINT16_MAX+1); gamma_g[i] = F((double)gamma_g[i]/(UINT16_MAX+1), 1) * (UINT16_MAX+1); gamma_b[i] = F((double)gamma_b[i]/(UINT16_MAX+1), 2) * (UINT16_MAX+1); } } typedef struct { struct wl_display *display; struct wl_registry *registry; struct wl_callback *callback; uint32_t gamma_control_manager_id; struct zwlr_gamma_control_manager_v1 *gamma_control_manager; int num_outputs; struct output *outputs; int authorized; } wayland_state_t; struct output { uint32_t global_id; struct wl_output *output; struct zwlr_gamma_control_v1 *gamma_control; uint32_t gamma_size; }; static int wayland_init(wayland_state_t **state) { /* Initialize state. */ *state = malloc(sizeof(**state)); if (*state == NULL) return -1; memset(*state, 0, sizeof **state); return 0; } static void authorizer_feedback_granted(void *data, struct orbital_authorizer_feedback *feedback) { wayland_state_t *state = data; state->authorized = 1; } static void authorizer_feedback_denied(void *data, struct orbital_authorizer_feedback *feedback) { fprintf(stderr, _("Fatal: redshift was not authorized to bind the 'zwlr_gamma_control_manager_v1' interface.\n")); exit(EXIT_FAILURE); } static const struct orbital_authorizer_feedback_listener authorizer_feedback_listener = { authorizer_feedback_granted, authorizer_feedback_denied }; static void registry_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { wayland_state_t *state = data; if (strcmp(interface, "zwlr_gamma_control_manager_v1") == 0) { state->gamma_control_manager_id = id; state->gamma_control_manager = wl_registry_bind(registry, id, &zwlr_gamma_control_manager_v1_interface, 1); } else if (strcmp(interface, "wl_output") == 0) { state->num_outputs++; if (!(state->outputs = realloc(state->outputs, state->num_outputs * sizeof(struct output)))) { fprintf(stderr, _("Failed to allcate memory\n")); return; } struct output *output = &state->outputs[state->num_outputs - 1]; output->global_id = id; output->output = wl_registry_bind(registry, id, &wl_output_interface, 1); output->gamma_control = NULL; } else if (strcmp(interface, "orbital_authorizer") == 0) { struct wl_event_queue *queue = wl_display_create_queue(state->display); struct orbital_authorizer *auth = wl_registry_bind(registry, id, &orbital_authorizer_interface, 1u); wl_proxy_set_queue((struct wl_proxy *)auth, queue); struct orbital_authorizer_feedback *feedback = orbital_authorizer_authorize(auth, "zwlr_gamma_control_manager_v1"); orbital_authorizer_feedback_add_listener(feedback, &authorizer_feedback_listener, state); int ret = 0; while (!state->authorized && ret >= 0) { ret = wl_display_dispatch_queue(state->display, queue); } orbital_authorizer_feedback_destroy(feedback); orbital_authorizer_destroy(auth); wl_event_queue_destroy(queue); } } static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t id) { wayland_state_t *state = data; if (state->gamma_control_manager_id == id) { fprintf(stderr, _("The zwlr_gamma_control_manager_v1 was removed\n")); exit(EXIT_FAILURE); } for (int i = 0; i < state->num_outputs; ++i) { struct output *output = &state->outputs[i]; if (output->global_id == id) { if (output->gamma_control) { zwlr_gamma_control_v1_destroy(output->gamma_control); output->gamma_control = NULL; } wl_output_destroy(output->output); /* If the removed output is not the last one in the array move the last one * in the now empty slot. Then shrink the array */ if (i < --state->num_outputs) { memcpy(output, &state->outputs[state->num_outputs], sizeof(struct output)); } state->outputs = realloc(state->outputs, state->num_outputs * sizeof(struct output)); return; } } } static const struct wl_registry_listener registry_listener = { registry_global, registry_global_remove }; static void gamma_control_gamma_size(void *data, struct zwlr_gamma_control_v1 *control, uint32_t size) { struct output *output = data; output->gamma_size = size; } static void gamma_control_failed(void *data, struct zwlr_gamma_control_v1 *control) { } static const struct zwlr_gamma_control_v1_listener gamma_control_listener = { gamma_control_gamma_size, gamma_control_failed }; static int wayland_start(wayland_state_t *state) { state->display = wl_display_connect(NULL); if (!state->display) { fputs(_("Could not connect to wayland display, exiting.\n"), stderr); return -1; } state->registry = wl_display_get_registry(state->display); wl_registry_add_listener(state->registry, ®istry_listener, state); wl_display_roundtrip(state->display); if (!state->gamma_control_manager) { return -1; } if (state->num_outputs > 0 && !state->outputs) { return -1; } return 0; } static void wayland_restore(wayland_state_t *state) { for (int i = 0; i < state->num_outputs; ++i) { struct output *output = &state->outputs[i]; if (output->gamma_control) { zwlr_gamma_control_v1_destroy(output->gamma_control); output->gamma_control = NULL; } } wl_display_flush(state->display); } static void wayland_free(wayland_state_t *state) { int ret = 0; /* Wait for the sync callback to destroy everything, otherwise * we could destroy the gamma control before gamma has been set */ while (state->callback && ret >= 0) { ret = wl_display_dispatch(state->display); } if (state->callback) { fprintf(stderr, _("Ignoring error on wayland connection while waiting to disconnect: %d\n"), ret); wl_callback_destroy(state->callback); } for (int i = 0; i < state->num_outputs; ++i) { struct output *output = &state->outputs[i]; if (output->gamma_control) { zwlr_gamma_control_v1_destroy(output->gamma_control); output->gamma_control = NULL; } wl_output_destroy(output->output); } if (state->gamma_control_manager) { zwlr_gamma_control_manager_v1_destroy(state->gamma_control_manager); } if (state->registry) { wl_registry_destroy(state->registry); } if (state->display) { wl_display_disconnect(state->display); } free(state); } static void callback_done(void *data, struct wl_callback *cb, uint32_t t) { wayland_state_t *state = data; state->callback = NULL; wl_callback_destroy(cb); } static const struct wl_callback_listener callback_listener = { callback_done }; static int wayland_set_temperature(wayland_state_t *state, const color_setting_t *setting) { int ret = 0, roundtrip = 0; /* We wait for the sync callback to throttle a bit and not send more * requests than the compositor can manage, otherwise we'd get disconnected. * This also allows us to dispatch other incoming events such as * wl_registry.global_remove. */ while (state->callback && ret >= 0) { ret = wl_display_dispatch(state->display); } if (ret < 0) { fprintf(stderr, _("The Wayland connection experienced a fatal error: %d\n"), ret); return ret; } for (int i = 0; i < state->num_outputs; ++i) { struct output *output = &state->outputs[i]; if (!output->gamma_control) { output->gamma_control = zwlr_gamma_control_manager_v1_get_gamma_control(state->gamma_control_manager, output->output); zwlr_gamma_control_v1_add_listener(output->gamma_control, &gamma_control_listener, output); roundtrip = 1; } } if (roundtrip) { wl_display_roundtrip(state->display); } for (int i = 0; i < state->num_outputs; ++i) { struct output *output = &state->outputs[i]; int size = output->gamma_size; size_t ramp_bytes = size * sizeof(uint16_t); size_t total_bytes = ramp_bytes * 3; int fd = os_create_anonymous_file(total_bytes); if (fd < 0) { perror("os_create_anonymous_file"); return -1; } void *ptr = mmap(NULL, total_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap"); close(fd); return -1; } uint16_t *r_gamma = ptr; uint16_t *g_gamma = ptr + ramp_bytes; uint16_t *b_gamma = ptr + 2 * ramp_bytes; /* Initialize gamma ramps to pure state */ for (int i = 0; i < size; i++) { uint16_t value = (double)i / size * (UINT16_MAX+1); r_gamma[i] = value; g_gamma[i] = value; b_gamma[i] = value; } colorramp_fill(r_gamma, g_gamma, b_gamma, size, setting); if (munmap(ptr, total_bytes) == -1) { perror("munmap"); close(fd); return -1; } zwlr_gamma_control_v1_set_gamma(output->gamma_control, fd); close(fd); } state->callback = wl_display_sync(state->display); wl_callback_add_listener(state->callback, &callback_listener, state); wl_display_flush(state->display); return 0; } ================================================ FILE: waysct/orbital-authorizer-client-protocol.h ================================================ /* Generated by wayland-scanner 1.19.0 */ #ifndef ORBITAL_AUTHORIZER_CLIENT_PROTOCOL_H #define ORBITAL_AUTHORIZER_CLIENT_PROTOCOL_H #include #include #include "wayland-client.h" #ifdef __cplusplus extern "C" { #endif /** * @page page_orbital_authorizer The orbital_authorizer protocol * @section page_ifaces_orbital_authorizer Interfaces * - @subpage page_iface_orbital_authorizer - authorize clients to use certain interfaces * - @subpage page_iface_orbital_authorizer_feedback - feedback for an authorization request */ struct orbital_authorizer; struct orbital_authorizer_feedback; #ifndef ORBITAL_AUTHORIZER_INTERFACE #define ORBITAL_AUTHORIZER_INTERFACE /** * @page page_iface_orbital_authorizer orbital_authorizer * @section page_iface_orbital_authorizer_desc Description * * The orbital_authorizer global interface allows clients to * ask the compositor the authorization to bind certain restricted * global interfaces. * Any client that aims to bind restricted interfaces should first * request the authorization by using this interface. Failing to do * so will result in the compositor sending a protocol error to the * client when it binds the restricted interface. * * The list of restricted interfaces is compositor dependant, but must * not include the core interfaces defined in wayland.xml. * @section page_iface_orbital_authorizer_api API * See @ref iface_orbital_authorizer. */ /** * @defgroup iface_orbital_authorizer The orbital_authorizer interface * * The orbital_authorizer global interface allows clients to * ask the compositor the authorization to bind certain restricted * global interfaces. * Any client that aims to bind restricted interfaces should first * request the authorization by using this interface. Failing to do * so will result in the compositor sending a protocol error to the * client when it binds the restricted interface. * * The list of restricted interfaces is compositor dependant, but must * not include the core interfaces defined in wayland.xml. */ extern const struct wl_interface orbital_authorizer_interface; #endif #ifndef ORBITAL_AUTHORIZER_FEEDBACK_INTERFACE #define ORBITAL_AUTHORIZER_FEEDBACK_INTERFACE /** * @page page_iface_orbital_authorizer_feedback orbital_authorizer_feedback * @section page_iface_orbital_authorizer_feedback_desc Description * * The orbital_authorizer_feedback interface is used by requesting * an authorization with the orbital_authorizer.authorize request. * The compositor will send either the granted or denied event based * on the system and user configuration. How the authorization process * works is compositor specific, but a compositor is allowed to ask * for user input, so the response for an authorization request may * come after some time. * @section page_iface_orbital_authorizer_feedback_api API * See @ref iface_orbital_authorizer_feedback. */ /** * @defgroup iface_orbital_authorizer_feedback The orbital_authorizer_feedback interface * * The orbital_authorizer_feedback interface is used by requesting * an authorization with the orbital_authorizer.authorize request. * The compositor will send either the granted or denied event based * on the system and user configuration. How the authorization process * works is compositor specific, but a compositor is allowed to ask * for user input, so the response for an authorization request may * come after some time. */ extern const struct wl_interface orbital_authorizer_feedback_interface; #endif #define ORBITAL_AUTHORIZER_DESTROY 0 #define ORBITAL_AUTHORIZER_AUTHORIZE 1 /** * @ingroup iface_orbital_authorizer */ #define ORBITAL_AUTHORIZER_DESTROY_SINCE_VERSION 1 /** * @ingroup iface_orbital_authorizer */ #define ORBITAL_AUTHORIZER_AUTHORIZE_SINCE_VERSION 1 /** @ingroup iface_orbital_authorizer */ static inline void orbital_authorizer_set_user_data(struct orbital_authorizer *orbital_authorizer, void *user_data) { wl_proxy_set_user_data((struct wl_proxy *) orbital_authorizer, user_data); } /** @ingroup iface_orbital_authorizer */ static inline void * orbital_authorizer_get_user_data(struct orbital_authorizer *orbital_authorizer) { return wl_proxy_get_user_data((struct wl_proxy *) orbital_authorizer); } static inline uint32_t orbital_authorizer_get_version(struct orbital_authorizer *orbital_authorizer) { return wl_proxy_get_version((struct wl_proxy *) orbital_authorizer); } /** * @ingroup iface_orbital_authorizer */ static inline void orbital_authorizer_destroy(struct orbital_authorizer *orbital_authorizer) { wl_proxy_marshal((struct wl_proxy *) orbital_authorizer, ORBITAL_AUTHORIZER_DESTROY); wl_proxy_destroy((struct wl_proxy *) orbital_authorizer); } /** * @ingroup iface_orbital_authorizer * * The authorize request allows the client to ask the compositor the * authorization to bind a restricted global interface. The newly * created orbital_authorizer_feedback will be invalid after the * compositor send either the granted or denied event so the client * must destroy it immediately after. */ static inline struct orbital_authorizer_feedback * orbital_authorizer_authorize(struct orbital_authorizer *orbital_authorizer, const char *global) { struct wl_proxy *id; id = wl_proxy_marshal_constructor((struct wl_proxy *) orbital_authorizer, ORBITAL_AUTHORIZER_AUTHORIZE, &orbital_authorizer_feedback_interface, NULL, global); return (struct orbital_authorizer_feedback *) id; } /** * @ingroup iface_orbital_authorizer_feedback * @struct orbital_authorizer_feedback_listener */ struct orbital_authorizer_feedback_listener { /** * the authorization was granted * * The authorization was granted. The client can now bind the * restricted interface. */ void (*granted)(void *data, struct orbital_authorizer_feedback *orbital_authorizer_feedback); /** * the authorization was denied * * The authorization was denied. The client is not allowed to * bind the restricted interface and trying to do so will trigger a * protocol error killing the client. */ void (*denied)(void *data, struct orbital_authorizer_feedback *orbital_authorizer_feedback); }; /** * @ingroup iface_orbital_authorizer_feedback */ static inline int orbital_authorizer_feedback_add_listener(struct orbital_authorizer_feedback *orbital_authorizer_feedback, const struct orbital_authorizer_feedback_listener *listener, void *data) { return wl_proxy_add_listener((struct wl_proxy *) orbital_authorizer_feedback, (void (**)(void)) listener, data); } /** * @ingroup iface_orbital_authorizer_feedback */ #define ORBITAL_AUTHORIZER_FEEDBACK_GRANTED_SINCE_VERSION 1 /** * @ingroup iface_orbital_authorizer_feedback */ #define ORBITAL_AUTHORIZER_FEEDBACK_DENIED_SINCE_VERSION 1 /** @ingroup iface_orbital_authorizer_feedback */ static inline void orbital_authorizer_feedback_set_user_data(struct orbital_authorizer_feedback *orbital_authorizer_feedback, void *user_data) { wl_proxy_set_user_data((struct wl_proxy *) orbital_authorizer_feedback, user_data); } /** @ingroup iface_orbital_authorizer_feedback */ static inline void * orbital_authorizer_feedback_get_user_data(struct orbital_authorizer_feedback *orbital_authorizer_feedback) { return wl_proxy_get_user_data((struct wl_proxy *) orbital_authorizer_feedback); } static inline uint32_t orbital_authorizer_feedback_get_version(struct orbital_authorizer_feedback *orbital_authorizer_feedback) { return wl_proxy_get_version((struct wl_proxy *) orbital_authorizer_feedback); } /** @ingroup iface_orbital_authorizer_feedback */ static inline void orbital_authorizer_feedback_destroy(struct orbital_authorizer_feedback *orbital_authorizer_feedback) { wl_proxy_destroy((struct wl_proxy *) orbital_authorizer_feedback); } #ifdef __cplusplus } #endif #endif ================================================ FILE: waysct/orbital-authorizer-protocol.h ================================================ /* Generated by wayland-scanner 1.19.0 */ #include #include #include "wayland-util.h" #ifndef __has_attribute # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ #endif #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) #define WL_PRIVATE __attribute__ ((visibility("hidden"))) #else #define WL_PRIVATE #endif extern const struct wl_interface orbital_authorizer_feedback_interface; static const struct wl_interface *orbital_authorizer_types[] = { &orbital_authorizer_feedback_interface, NULL, }; static const struct wl_message orbital_authorizer_requests[] = { { "destroy", "", orbital_authorizer_types + 0 }, { "authorize", "ns", orbital_authorizer_types + 0 }, }; WL_PRIVATE const struct wl_interface orbital_authorizer_interface = { "orbital_authorizer", 1, 2, orbital_authorizer_requests, 0, NULL, }; static const struct wl_message orbital_authorizer_feedback_events[] = { { "granted", "", orbital_authorizer_types + 0 }, { "denied", "", orbital_authorizer_types + 0 }, }; WL_PRIVATE const struct wl_interface orbital_authorizer_feedback_interface = { "orbital_authorizer_feedback", 1, 0, NULL, 2, orbital_authorizer_feedback_events, }; ================================================ FILE: waysct/orbital-authorizer.xml ================================================ The orbital_authorizer global interface allows clients to ask the compositor the authorization to bind certain restricted global interfaces. Any client that aims to bind restricted interfaces should first request the authorization by using this interface. Failing to do so will result in the compositor sending a protocol error to the client when it binds the restricted interface. The list of restricted interfaces is compositor dependant, but must not include the core interfaces defined in wayland.xml. The authorize request allows the client to ask the compositor the authorization to bind a restricted global interface. The newly created orbital_authorizer_feedback will be invalid after the compositor send either the granted or denied event so the client must destroy it immediately after. The orbital_authorizer_feedback interface is used by requesting an authorization with the orbital_authorizer.authorize request. The compositor will send either the granted or denied event based on the system and user configuration. How the authorization process works is compositor specific, but a compositor is allowed to ask for user input, so the response for an authorization request may come after some time. The authorization was granted. The client can now bind the restricted interface. The authorization was denied. The client is not allowed to bind the restricted interface and trying to do so will trigger a protocol error killing the client. ================================================ FILE: waysct/os-compatibility-impl.h ================================================ /* * Copyright © 2012 Collabora, Ltd. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include "os-compatibility.h" int os_fd_set_cloexec(int fd) { if (fd == -1) { return -1; } long flags = fcntl(fd, F_GETFD); if (flags == -1) { return -1; } if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { return -1; } return 0; } int set_cloexec_or_close(int fd) { if (os_fd_set_cloexec(fd) != 0) { close(fd); return -1; } return fd; } int create_tmpfile_cloexec(char *tmpname) { int fd; mode_t prev_umask = umask(0066); #ifdef HAVE_MKOSTEMP fd = mkostemp(tmpname, O_CLOEXEC); if (fd >= 0) { unlink(tmpname); } #else fd = mkstemp(tmpname); if (fd >= 0) { fd = set_cloexec_or_close(fd); unlink(tmpname); } #endif umask(prev_umask); return fd; } /* * Create a new, unique, anonymous file of the given size, and * return the file descriptor for it. The file descriptor is set * CLOEXEC. The file is immediately suitable for mmap()'ing * the given size at offset zero. * * The file should not have a permanent backing store like a disk, * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. * * The file name is deleted from the file system. * * The file is suitable for buffer sharing between processes by * transmitting the file descriptor over Unix sockets using the * SCM_RIGHTS methods. * * If the C library implements posix_fallocate(), it is used to * guarantee that disk space is available for the file at the * given size. If disk space is insufficient, errno is set to ENOSPC. * If posix_fallocate() is not supported, program may receive * SIGBUS on accessing mmap()'ed file contents instead. */ int os_create_anonymous_file(off_t size) { static const char template[] = "/redshift-shared-XXXXXX"; const char *path = getenv("XDG_RUNTIME_DIR"); if (!path) { errno = ENOENT; return -1; } char *name = malloc(strlen(path) + sizeof(template)); if (!name) { return -1; } strcpy(name, path); strcat(name, template); int fd = create_tmpfile_cloexec(name); free(name); if (fd < 0) { return -1; } #ifdef WLR_HAS_POSIX_FALLOCATE int ret; do { ret = posix_fallocate(fd, 0, size); } while (ret == EINTR); if (ret != 0) { close(fd); errno = ret; return -1; } #else int ret; do { ret = ftruncate(fd, size); } while (ret < 0 && errno == EINTR); if (ret < 0) { close(fd); return -1; } #endif return fd; } ================================================ FILE: waysct/os-compatibility.h ================================================ #ifndef UTIL_OS_COMPATIBILITY_H #define UTIL_OS_COMPATIBILITY_H int os_fd_set_cloexec(int fd); int set_cloexec_or_close(int fd); int create_tmpfile_cloexec(char *tmpname); int os_create_anonymous_file(off_t size); #endif ================================================ FILE: waysct/waysct.go ================================================ // waysct is a set color temp implementation for Wayland. // Many of these files are taken from // https://github.com/minus7/redshift/commit/7da875d34854a6a34612d5ce4bd8718c32bec804 // see Redshift for the GPL license. package waysct //go:generate wayland-scanner private-code orbital-authorizer.xml orbital-authorizer-protocol.h //go:generate wayland-scanner client-header orbital-authorizer.xml orbital-authorizer-client-protocol.h //go:generate wayland-scanner private-code gamma-control.xml gamma-control-protocol.h //go:generate wayland-scanner client-header gamma-control.xml gamma-control-client-protocol.h // #cgo LDFLAGS: -lm -lwayland-client // #include "gamma-wl.h" import "C" import ( "github.com/pkg/errors" ) type Manager struct { state *C.wayland_state_t } func StartManager() (*Manager, error) { m := Manager{} if errno := C.wayland_init(&m.state); errno != 0 { return nil, errors.Errorf("wayland_init: errno %d", errno) } if errno := C.wayland_start(m.state); errno != 0 { return nil, errors.Errorf("wayland_start: errno %d", errno) } return &m, nil } func (m *Manager) Close() { C.wayland_free(m.state) m.state = nil } func (m *Manager) SetColorTemp(temp int) error { var setting C.color_setting_t setting.brightness = 1.0 setting.gamma[0] = 1.0 setting.gamma[1] = 1.0 setting.gamma[2] = 1.0 setting.temperature = C.int(temp) if errno := C.wayland_set_temperature(m.state, &setting); errno != 0 { return errors.Errorf("wayland_set_temperature: errno %d", errno) } return nil }