├── vendor ├── github.com │ └── BurntSushi │ │ ├── xgb │ │ ├── .gitignore │ │ ├── AUTHORS │ │ ├── sync.go │ │ ├── STYLE │ │ ├── CONTRIBUTORS │ │ ├── README │ │ ├── LICENSE │ │ ├── auth.go │ │ ├── help.go │ │ ├── Makefile │ │ ├── doc.go │ │ ├── conn.go │ │ ├── cookie.go │ │ └── xgb.go │ │ └── xgbutil │ │ ├── session.vim │ │ ├── .gitignore │ │ ├── COPYING │ │ ├── Makefile │ │ ├── STYLE │ │ ├── xprop │ │ ├── doc.go │ │ ├── atom.go │ │ └── xprop.go │ │ ├── README │ │ ├── doc.go │ │ ├── types.go │ │ └── xgbutil.go ├── gopkg.in │ └── yaml.v3 │ │ ├── go.mod │ │ ├── .travis.yml │ │ ├── NOTICE │ │ ├── writerc.go │ │ ├── LICENSE │ │ ├── sorter.go │ │ ├── README.md │ │ ├── yamlprivateh.go │ │ ├── resolve.go │ │ ├── readerc.go │ │ └── encode.go ├── go.i3wm.org │ └── i3 │ │ └── v4 │ │ ├── go.mod │ │ ├── marks.go │ │ ├── bindingmodes.go │ │ ├── validpid.go │ │ ├── tick.go │ │ ├── config.go │ │ ├── outputs.go │ │ ├── sync.go │ │ ├── getpid.go │ │ ├── go.sum │ │ ├── workspaces.go │ │ ├── doc.go │ │ ├── LICENSE │ │ ├── .travis.yml │ │ ├── command.go │ │ ├── version.go │ │ ├── byteorder.go │ │ ├── README.md │ │ ├── barconfig.go │ │ ├── socket.go │ │ ├── tree.go │ │ └── subscribe.go └── modules.txt ├── cmd └── i3-autodisplay │ └── i3-autodisplay.go ├── .gitignore ├── sample_config.yml ├── go.mod ├── i3 └── i3.go ├── config └── config.go ├── README.md ├── go.sum └── display └── randr.go /vendor/github.com/BurntSushi/xgb/.gitignore: -------------------------------------------------------------------------------- 1 | xgbgen/xgbgen 2 | .*.swp 3 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/session.vim: -------------------------------------------------------------------------------- 1 | au BufWritePost *.go silent!make tags > /dev/null 2>&1 2 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.png 3 | tst_first 4 | tst_graphics 5 | TAGS 6 | 7 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/go.mod: -------------------------------------------------------------------------------- 1 | module "gopkg.in/yaml.v3" 2 | 3 | require ( 4 | "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 5 | ) 6 | -------------------------------------------------------------------------------- /cmd/i3-autodisplay/i3-autodisplay.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/lpicanco/i3-autodisplay/display" 5 | ) 6 | 7 | func main() { 8 | display.Refresh() 9 | display.ListenEvents() 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /sample_config.yml: -------------------------------------------------------------------------------- 1 | displays: 2 | - name: eDP1 3 | workspaces: [1,2,3,4,5,6,7,8,9,0] 4 | - name: HDMI1 5 | workspaces: [2,4,6,8] 6 | randr_extra_options: "--left-of eDP1" 7 | - name: DP1 8 | workspaces: [1,3,5,7,9] 9 | randr_extra_options: "--left-of HDMI1" -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lpicanco/i3-autodisplay 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/BurntSushi/xgb v0.0.0-20200324125942-20f126ea2843 7 | github.com/jezek/xgb v1.1.1 // indirect 8 | go.i3wm.org/i3/v4 v4.18.0 9 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 10 | ) 11 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - "1.4.x" 5 | - "1.5.x" 6 | - "1.6.x" 7 | - "1.7.x" 8 | - "1.8.x" 9 | - "1.9.x" 10 | - "1.10.x" 11 | - "1.11.x" 12 | - "1.12.x" 13 | - "1.13.x" 14 | - "1.14.x" 15 | - "tip" 16 | 17 | go_import_path: gopkg.in/yaml.v3 18 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/go.mod: -------------------------------------------------------------------------------- 1 | module go.i3wm.org/i3/v4 2 | 3 | require ( 4 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 5 | github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e 6 | github.com/google/go-cmp v0.2.0 7 | golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc // indirect 8 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f 9 | ) 10 | 11 | go 1.13 12 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/marks.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import "encoding/json" 4 | 5 | // GetMarks returns the names of all currently set marks. 6 | // 7 | // GetMarks is supported in i3 ≥ v4.1 (2011-11-11). 8 | func GetMarks() ([]string, error) { 9 | reply, err := roundTrip(messageTypeGetMarks, nil) 10 | if err != nil { 11 | return nil, err 12 | } 13 | 14 | var marks []string 15 | err = json.Unmarshal(reply.Payload, &marks) 16 | return marks, err 17 | } 18 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/bindingmodes.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import "encoding/json" 4 | 5 | // GetBindingModes returns the names of all currently configured binding modes. 6 | // 7 | // GetBindingModes is supported in i3 ≥ v4.13 (2016-11-08). 8 | func GetBindingModes() ([]string, error) { 9 | reply, err := roundTrip(messageTypeGetBindingModes, nil) 10 | if err != nil { 11 | return nil, err 12 | } 13 | 14 | var bm []string 15 | err = json.Unmarshal(reply.Payload, &bm) 16 | return bm, err 17 | } 18 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/validpid.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import "syscall" 4 | 5 | func pidValid(pid int) bool { 6 | // As per kill(2) from POSIX.1-2008, sending signal 0 validates a pid. 7 | if err := syscall.Kill(pid, 0); err != nil { 8 | if err == syscall.EPERM { 9 | // Process still alive (but no permission to signal): 10 | return true 11 | } 12 | // errno is likely ESRCH (process not found). 13 | return false // Process not alive. 14 | } 15 | return true // Process still alive. 16 | } 17 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/BurntSushi/xgb v0.0.0-20200324125942-20f126ea2843 2 | github.com/BurntSushi/xgb 3 | github.com/BurntSushi/xgb/randr 4 | github.com/BurntSushi/xgb/render 5 | github.com/BurntSushi/xgb/xinerama 6 | github.com/BurntSushi/xgb/xproto 7 | # github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e 8 | github.com/BurntSushi/xgbutil 9 | github.com/BurntSushi/xgbutil/xprop 10 | # go.i3wm.org/i3/v4 v4.18.0 11 | go.i3wm.org/i3/v4 12 | # gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 13 | gopkg.in/yaml.v3 14 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/COPYING: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/tick.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import "encoding/json" 4 | 5 | // TickResult attests the tick command was successful. 6 | type TickResult struct { 7 | Success bool `json:"success"` 8 | } 9 | 10 | // SendTick sends a tick event with the provided payload. 11 | // 12 | // SendTick is supported in i3 ≥ v4.15 (2018-03-10). 13 | func SendTick(command string) (TickResult, error) { 14 | reply, err := roundTrip(messageTypeSendTick, []byte(command)) 15 | if err != nil { 16 | return TickResult{}, err 17 | } 18 | 19 | var tr TickResult 20 | err = json.Unmarshal(reply.Payload, &tr) 21 | return tr, err 22 | } 23 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2011-2016 Canonical Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/config.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import "encoding/json" 4 | 5 | // Config contains details about the configuration file. 6 | // 7 | // See https://i3wm.org/docs/ipc.html#_config_reply for more details. 8 | type Config struct { 9 | Config string `json:"config"` 10 | } 11 | 12 | // GetConfig returns i3’s in-memory copy of the configuration file contents. 13 | // 14 | // GetConfig is supported in i3 ≥ v4.14 (2017-09-04). 15 | func GetConfig() (Config, error) { 16 | reply, err := roundTrip(messageTypeGetConfig, nil) 17 | if err != nil { 18 | return Config{}, err 19 | } 20 | 21 | var cfg Config 22 | err = json.Unmarshal(reply.Payload, &cfg) 23 | return cfg, err 24 | } 25 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/AUTHORS: -------------------------------------------------------------------------------- 1 | Andrew Gallant is the maintainer of this fork. What follows is the original 2 | list of authors for the x-go-binding. 3 | 4 | # This is the official list of XGB authors for copyright purposes. 5 | # This file is distinct from the CONTRIBUTORS files. 6 | # See the latter for an explanation. 7 | 8 | # Names should be added to this file as 9 | # Name or Organization 10 | # The email address is not required for organizations. 11 | 12 | # Please keep the list sorted. 13 | 14 | Anthony Martin 15 | Firmansyah Adiputra 16 | Google Inc. 17 | Scott Lawrence 18 | Tor Andersson 19 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/outputs.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import "encoding/json" 4 | 5 | // Output describes an i3 output. 6 | // 7 | // See https://i3wm.org/docs/ipc.html#_outputs_reply for more details. 8 | type Output struct { 9 | Name string `json:"name"` 10 | Active bool `json:"active"` 11 | Primary bool `json:"primary"` 12 | CurrentWorkspace string `json:"current_workspace"` 13 | Rect Rect `json:"rect"` 14 | } 15 | 16 | // GetOutputs returns i3’s current outputs. 17 | // 18 | // GetOutputs is supported in i3 ≥ v4.0 (2011-07-31). 19 | func GetOutputs() ([]Output, error) { 20 | reply, err := roundTrip(messageTypeGetOutputs, nil) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | var outputs []Output 26 | err = json.Unmarshal(reply.Payload, &outputs) 27 | return outputs, err 28 | } 29 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/sync.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import "encoding/json" 4 | 5 | // SyncRequest represents the payload of a Sync request. 6 | type SyncRequest struct { 7 | Window uint32 `json:"window"` // X11 window id 8 | Rnd uint32 `json:"rnd"` // Random value for distinguishing requests 9 | } 10 | 11 | // SyncResult attests the sync command was successful. 12 | type SyncResult struct { 13 | Success bool `json:"success"` 14 | } 15 | 16 | // Sync sends a tick event with the provided payload. 17 | // 18 | // Sync is supported in i3 ≥ v4.16 (2018-11-04). 19 | func Sync(req SyncRequest) (SyncResult, error) { 20 | b, err := json.Marshal(req) 21 | if err != nil { 22 | return SyncResult{}, err 23 | } 24 | reply, err := roundTrip(messageTypeSync, b) 25 | if err != nil { 26 | return SyncResult{}, err 27 | } 28 | 29 | var tr SyncResult 30 | err = json.Unmarshal(reply.Payload, &tr) 31 | return tr, err 32 | } 33 | -------------------------------------------------------------------------------- /i3/i3.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/lpicanco/i3-autodisplay/config" 8 | "go.i3wm.org/i3/v4" 9 | ) 10 | 11 | func GetCurrentWorkspaceNumber() (int64, error) { 12 | ws, err := i3.GetWorkspaces() 13 | if err != nil { 14 | return -1, err 15 | } 16 | 17 | for _, w := range ws { 18 | if w.Focused { 19 | return w.Num, nil 20 | } 21 | } 22 | 23 | return -1, errors.New("Can't find current workspace") 24 | } 25 | 26 | func SetCurrentWorkspace(workspaceNum int64) error { 27 | command := fmt.Sprintf("workspace %d", workspaceNum) 28 | _, err := i3.RunCommand(command) 29 | return err 30 | } 31 | 32 | func UpdateWorkspaces(display config.Display) error { 33 | for _, workspace := range display.Workspaces { 34 | 35 | command := fmt.Sprintf("workspace number %d; move workspace to output %s", workspace, display.Name) 36 | _, err := i3.RunCommand(command) 37 | 38 | if err != nil { 39 | return err 40 | } 41 | } 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/sync.go: -------------------------------------------------------------------------------- 1 | package xgb 2 | 3 | // Sync sends a round trip request and waits for the response. 4 | // This forces all pending cookies to be dealt with. 5 | // You actually shouldn't need to use this like you might with Xlib. Namely, 6 | // buffers are automatically flushed using Go's channels and round trip requests 7 | // are forced where appropriate automatically. 8 | func (c *Conn) Sync() { 9 | cookie := c.NewCookie(true, true) 10 | c.NewRequest(c.getInputFocusRequest(), cookie) 11 | cookie.Reply() // wait for the buffer to clear 12 | } 13 | 14 | // getInputFocusRequest writes the raw bytes to a buffer. 15 | // It is duplicated from xproto/xproto.go. 16 | func (c *Conn) getInputFocusRequest() []byte { 17 | size := 4 18 | b := 0 19 | buf := make([]byte, size) 20 | 21 | buf[b] = 43 // request opcode 22 | b += 1 23 | 24 | b += 1 // padding 25 | Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units 26 | b += 2 27 | 28 | return buf 29 | } 30 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/getpid.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/BurntSushi/xgbutil" 7 | "github.com/BurntSushi/xgbutil/xprop" 8 | ) 9 | 10 | func i3Pid() int { 11 | xu, err := xgbutil.NewConn() 12 | if err != nil { 13 | return -1 // X session terminated 14 | } 15 | defer xu.Conn().Close() 16 | reply, err := xprop.GetProperty(xu, xu.RootWin(), "I3_PID") 17 | if err != nil { 18 | return -1 // I3_PID no longer present (X session replaced?) 19 | } 20 | num, err := xprop.PropValNum(reply, err) 21 | if err != nil { 22 | return -1 23 | } 24 | return int(num) 25 | } 26 | 27 | var lastPid struct { 28 | sync.Mutex 29 | pid int 30 | } 31 | 32 | // IsRunningHook provides a method to override the method which detects if i3 is running or not 33 | var IsRunningHook = func() bool { 34 | lastPid.Lock() 35 | defer lastPid.Unlock() 36 | if !wasRestart || lastPid.pid == 0 { 37 | lastPid.pid = i3Pid() 38 | } 39 | return pidValid(lastPid.pid) 40 | } 41 | 42 | func i3Running() bool { 43 | return IsRunningHook() 44 | } 45 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "path" 8 | 9 | "gopkg.in/yaml.v3" 10 | ) 11 | 12 | type Display struct { 13 | Name string 14 | RandrExtraOptions string `yaml:"randr_extra_options"` 15 | Workspaces []int 16 | } 17 | 18 | var Config = struct { 19 | Displays []Display 20 | }{} 21 | 22 | func init() { 23 | configFile := getConfirFilePath() 24 | data, err := os.ReadFile(configFile) 25 | 26 | if err != nil { 27 | log.Fatalf("error reading configuration file: %s", err) 28 | } 29 | 30 | if err := yaml.Unmarshal(data, &Config); err != nil { 31 | log.Fatalf("error processing configuration file %s: \n %s", configFile, err) 32 | } 33 | } 34 | 35 | func getConfirFilePath() (configFile string) { 36 | configDir := os.Getenv("XDG_HOME") 37 | if configDir == "" { 38 | configDir = path.Join(os.Getenv("HOME"), ".config") 39 | } 40 | 41 | flag.StringVar(&configFile, "config", path.Join(configDir, "i3-autodisplay", "config.yml"), "Path to configuration file.") 42 | flag.Parse() 43 | 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= 2 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 3 | github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e h1:4ZrkT/RzpnROylmoQL57iVUL57wGKTR5O6KpVnbm2tA= 4 | github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k= 5 | github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= 6 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 7 | golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc h1:ZMCWScCvS2fUVFw8LOpxyUUW5qiviqr4Dg5NdjLeiLU= 8 | golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 9 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= 10 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 11 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/workspaces.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import "encoding/json" 4 | 5 | // WorkspaceID is an i3-internal ID for the node, which can be used to identify 6 | // workspaces within the IPC interface. 7 | type WorkspaceID int64 8 | 9 | // Workspace describes an i3 workspace. 10 | // 11 | // See https://i3wm.org/docs/ipc.html#_workspaces_reply for more details. 12 | type Workspace struct { 13 | ID WorkspaceID `json:"id"` 14 | Num int64 `json:"num"` 15 | Name string `json:"name"` 16 | Visible bool `json:"visible"` 17 | Focused bool `json:"focused"` 18 | Urgent bool `json:"urgent"` 19 | Rect Rect `json:"rect"` 20 | Output string `json:"output"` 21 | } 22 | 23 | // GetWorkspaces returns i3’s current workspaces. 24 | // 25 | // GetWorkspaces is supported in i3 ≥ v4.0 (2011-07-31). 26 | func GetWorkspaces() ([]Workspace, error) { 27 | reply, err := roundTrip(messageTypeGetWorkspaces, nil) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | var ws []Workspace 33 | err = json.Unmarshal(reply.Payload, &ws) 34 | return ws, err 35 | } 36 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/Makefile: -------------------------------------------------------------------------------- 1 | all: callback.go types_auto.go gofmt 2 | 3 | install: 4 | go install -p 6 . ./ewmh ./gopher ./icccm ./keybind ./motif ./mousebind \ 5 | ./xcursor ./xevent ./xgraphics ./xinerama ./xprop ./xrect ./xwindow 6 | 7 | push: 8 | git push origin master 9 | git push github master 10 | 11 | build-ex: 12 | find ./_examples/ -type d -wholename './_examples/[a-z]*' -print0 \ 13 | | xargs -0 go build -p 6 14 | 15 | gofmt: 16 | gofmt -w *.go */*.go _examples/*/*.go 17 | colcheck *.go */*.go _examples/*/*.go 18 | 19 | callback.go: 20 | scripts/write-events callbacks > xevent/callback.go 21 | 22 | types_auto.go: 23 | scripts/write-events evtypes > xevent/types_auto.go 24 | 25 | tags: 26 | find ./ \( -name '*.go' -and -not -wholename './tests/*' -and -not -wholename './_examples/*' \) -print0 | xargs -0 gotags > TAGS 27 | 28 | loc: 29 | find ./ -name '*.go' -and -not -wholename './tests*' -and -not -name '*keysymdef.go' -and -not -name '*gopher.go' -print | sort | xargs wc -l 30 | 31 | ex-%: 32 | go run _examples/$*/main.go 33 | 34 | gopherimg: 35 | go-bindata -f GopherPng -p gopher -i gopher/gophercolor-small.png -o gopher/gopher.go 36 | 37 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/doc.go: -------------------------------------------------------------------------------- 1 | // Package i3 provides a convenient interface to the i3 window manager. 2 | // 3 | // Its function and type names don’t stutter, and all functions and methods are 4 | // safe for concurrent use (except where otherwise noted). The package does not 5 | // import "unsafe" and hence should be widely applicable. 6 | // 7 | // UNIX socket connections to i3 are transparently managed by the package. Upon 8 | // any read/write errors on a UNIX socket, the package transparently retries for 9 | // up to 10 seconds, but only as long as the i3 process keeps running. 10 | // 11 | // The package is published in versioned releases, where the major and minor 12 | // version are identical to the i3 release the package is compatible with 13 | // (e.g. 4.14 implements the entire documented IPC interface of i3 4.14). 14 | // 15 | // This package will only ever receive additions, so versioning should only be 16 | // relevant to you if you are interested in a recently-introduced IPC feature. 17 | // 18 | // Message type functions and event types are annotated with the i3 version in 19 | // which they were introduced. Under the covers, they use AtLeast, so they 20 | // return a helpful error message at runtime if the running i3 version is too 21 | // old. 22 | package i3 23 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/STYLE: -------------------------------------------------------------------------------- 1 | I like to keep all my code to 80 columns or less. I have plenty of screen real 2 | estate, but enjoy 80 columns so that I can have multiple code windows open side 3 | to side and not be plagued by the ugly auto-wrapping of a text editor. 4 | 5 | If you don't oblige me, I will fix any patch you submit to abide 80 columns. 6 | 7 | Note that this style restriction does not preclude gofmt, but introduces a few 8 | peculiarities. The first is that gofmt will occasionally add spacing (typically 9 | to comments) that ends up going over 80 columns. Either shorten the comment or 10 | put it on its own line. 11 | 12 | The second and more common hiccup is when a function definition extends beyond 13 | 80 columns. If one adds line breaks to keep it below 80 columns, gofmt will 14 | indent all subsequent lines in a function definition to the same indentation 15 | level of the function body. This results in a less-than-ideal separation 16 | between function definition and function body. To remedy this, simply add a 17 | line break like so: 18 | 19 | func RestackWindowExtra(xu *xgbutil.XUtil, win xproto.Window, stackMode int, 20 | sibling xproto.Window, source int) error { 21 | 22 | return ClientEvent(xu, win, "_NET_RESTACK_WINDOW", source, int(sibling), 23 | stackMode) 24 | } 25 | 26 | Something similar should also be applied to long 'if' or 'for' conditionals, 27 | although it would probably be preferrable to break up the conditional to 28 | smaller chunks with a few helper variables. 29 | 30 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/STYLE: -------------------------------------------------------------------------------- 1 | I like to keep all my code to 80 columns or less. I have plenty of screen real 2 | estate, but enjoy 80 columns so that I can have multiple code windows open side 3 | to side and not be plagued by the ugly auto-wrapping of a text editor. 4 | 5 | If you don't oblige me, I will fix any patch you submit to abide 80 columns. 6 | 7 | Note that this style restriction does not preclude gofmt, but introduces a few 8 | peculiarities. The first is that gofmt will occasionally add spacing (typically 9 | to comments) that ends up going over 80 columns. Either shorten the comment or 10 | put it on its own line. 11 | 12 | The second and more common hiccup is when a function definition extends beyond 13 | 80 columns. If one adds line breaks to keep it below 80 columns, gofmt will 14 | indent all subsequent lines in a function definition to the same indentation 15 | level of the function body. This results in a less-than-ideal separation 16 | between function definition and function body. To remedy this, simply add a 17 | line break like so: 18 | 19 | func RestackWindowExtra(xu *xgbutil.XUtil, win xproto.Window, stackMode int, 20 | sibling xproto.Window, source int) error { 21 | 22 | return ClientEvent(xu, win, "_NET_RESTACK_WINDOW", source, int(sibling), 23 | stackMode) 24 | } 25 | 26 | Something similar should also be applied to long 'if' or 'for' conditionals, 27 | although it would probably be preferrable to break up the conditional to 28 | smaller chunks with a few helper variables. 29 | 30 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2017, Michael Stapelberg and contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Michael Stapelberg nor the 15 | names of contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY Michael Stapelberg ''AS IS'' AND ANY 19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL Michael Stapelberg BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/.travis.yml: -------------------------------------------------------------------------------- 1 | # Use the (faster) container-based infrastructure, see also 2 | # http://docs.travis-ci.com/user/workers/container-based-infrastructure/ 3 | sudo: false 4 | dist: trusty 5 | services: 6 | - docker 7 | 8 | language: go 9 | go: 10 | - "1.12" 11 | go_import_path: go.i3wm.org/i3 12 | 13 | env: 14 | global: 15 | - secure: "c9Q/SwO3gQ0rCJDCk9V9NgchFVCUj5+dIJbKqUcbXpdtaG8WLNWEamrv4nqbrLWw/I9Zw9St4VErqbqxU4Fee9ftt+PBbtMOi/eZ6LbnnDjJCYnz912Wf7v05L5Ghl1WNf8ajNCjvBpsJMe6UixBmZHeBqSin5OUT55gN+wMi68VaxTbo0DchXjuU7hXfDvV4iVvmtQmP9C4yQDz9HiEdaDz9SP0HNCzngONikld8rGSRX+V2XyaJsd13hJQM0ZfCXuRS0J0OBLz3msiSbobuWUzApI/s81AHUdkj3JbXfrxaA0kj75fZquqFyTaiNQRjKVqwXGGaMWcVNFOeDv3l5n6HMuImjxEypDwFcjbDo/9ivGeG31f7oh+lB/JkhRoZmW6tiIlRSy4rdVKmzn0ENIvxPcEFzqoLdail5ffM/b7KS2eGetTG/86PaLbk4XoQDzVcOWgOjeNn/meOAgUBEEMauZQZhF57Bs7ev62BReu8PbTXTyCVhpR77oAnt9dfW1tEQTrUBH9vG32V+/aC6YmrD/NM4t4VpvyJr/npXGyTW9JJ9bPLfHZEXjdoyOtBPNuousa7rM6hD2hbQ+/lVuEZp2c2RIIpuP5cG4MzHmA4W1GsDRbm/zG87VKt2zaKA2tuXPULDvlux49zI19OZB7sSYm3RgVN/+6YhYPwSs=" # PERSIST_URL 16 | 17 | script: 18 | # Check whether files are syntactically correct. 19 | - "gofmt -l $(find . -name '*.go' | tr '\\n' ' ') >/dev/null" 20 | # Check whether files were not gofmt'ed. 21 | - "gosrc=$(find . -name '*.go' | tr '\\n' ' '); [ $(gofmt -l $gosrc 2>&- | wc -l) -eq 0 ] || (echo 'gofmt was not run on these files:'; gofmt -l $gosrc 2>&-; false)" 22 | - go vet . 23 | - go test -c 24 | - docker build --pull --no-cache --rm -t=goi3 -f travis/Dockerfile . 25 | - docker run -v $PWD:/usr/src goi3 /bin/sh -c 'strace -f -o st -s 2048 -v -y ./i3.test -test.v' || curl --data-binary @st -X PUT $PERSIST_URL 26 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/xprop/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package xprop provides a cache for interning atoms and helper functions for 3 | dealing with GetProperty and ChangeProperty X requests. 4 | 5 | Atoms 6 | 7 | Atoms in X are interned, meaning that strings are assigned unique integer 8 | identifiers. This minimizes the amount of data transmitted over an X connection. 9 | 10 | Once atoms have been interned, they are never changed while the X server is 11 | running. xgbutil takes advantage of this invariant and will only issue an 12 | intern atom request once and cache the result. 13 | 14 | To use the xprop package to intern an atom, use Atom: 15 | 16 | atom, err := xprop.Atom(XUtilValue, "THE_ATOM_NAME", false) 17 | if err == nil { 18 | println("The atom number: ", atom.Atom) 19 | } 20 | 21 | The 'false' parameter corresponds to the 'only_if_exists' parameter of the 22 | X InternAtom request. When it's false, the atom being interned always returns 23 | a non-zero atom number---even if the string being interned hasn't been interned 24 | before. If 'only_if_exists' is true, the atom being interned will return a 0 25 | atom number if it hasn't already been interned. 26 | 27 | The typical case is to set 'only_if_exists' to false. To this end, xprop.Atm is 28 | an alias that always sets this value to false. 29 | 30 | The reverse can also be done: getting an atom string if you have an atom 31 | number. This can be done with the xprop.AtomName function. 32 | 33 | Properties 34 | 35 | The other facility of xprop is to help with the use of GetProperty and 36 | ChangeProperty. Please see the source code of the ewmh package for plenty of 37 | examples. 38 | 39 | */ 40 | package xprop 41 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Andrew Gallant is the maintainer of this fork. What follows is the original 2 | list of contributors for the x-go-binding. 3 | 4 | # This is the official list of people who can contribute 5 | # (and typically have contributed) code to the XGB repository. 6 | # The AUTHORS file lists the copyright holders; this file 7 | # lists people. For example, Google employees are listed here 8 | # but not in AUTHORS, because Google holds the copyright. 9 | # 10 | # The submission process automatically checks to make sure 11 | # that people submitting code are listed in this file (by email address). 12 | # 13 | # Names should be added to this file only after verifying that 14 | # the individual or the individual's organization has agreed to 15 | # the appropriate Contributor License Agreement, found here: 16 | # 17 | # http://code.google.com/legal/individual-cla-v1.0.html 18 | # http://code.google.com/legal/corporate-cla-v1.0.html 19 | # 20 | # The agreement for individuals can be filled out on the web. 21 | # 22 | # When adding J Random Contributor's name to this file, 23 | # either J's name or J's organization's name should be 24 | # added to the AUTHORS file, depending on whether the 25 | # individual or corporate CLA was used. 26 | 27 | # Names should be added to this file like so: 28 | # Name 29 | 30 | # Please keep the list sorted. 31 | 32 | Anthony Martin 33 | Firmansyah Adiputra 34 | Ian Lance Taylor 35 | Nigel Tao 36 | Robert Griesemer 37 | Russ Cox 38 | Scott Lawrence 39 | Tor Andersson 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # i3-autodisplay 2 | i3-autodisplay is a i3wm display auto-configuration for multiple monitors 3 | 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/lpicanco/i3-autodisplay)](https://goreportcard.com/report/github.com/lpicanco/i3-autodisplay) 5 | ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/lpicanco/i3-autodisplay) 6 | 7 | ## Installation 8 | 9 | ### Pre requisites 10 | [xrandr](https://www.x.org/archive/current/doc/man/man1/xrandr.1.xhtml) program 11 | 12 | ### Pre built binary 13 | Fetch the [latest release](https://github.com/lpicanco/i3-autodisplay/releases). 14 | 15 | ### Archlinux AUR 16 | - [i3-autodisplay](https://aur.archlinux.org/packages/i3-autodisplay) 17 | - [i3-autodisplay-bin](https://aur.archlinux.org/packages/i3-autodisplay-bin) 18 | 19 | ### From sources 20 | 21 | ```bash 22 | git clone https://github.com/lpicanco/i3-autodisplay.git 23 | cd i3-autodisplay 24 | go build cmd/i3-autodisplay/i3-autodisplay.go 25 | ``` 26 | 27 | ## Usage 28 | `i3-autodisplay` requires a configuration file to work. The configuration file can be loaded from these locations: 29 | 30 | * `$XDG_HOME/i3-autodisplay/config.yml` 31 | * `$HOME/.config/i3-autodisplay/config.yml` 32 | * Specified via `-config` parameter 33 | 34 | In your i3wm configuration add the following line: 35 | 36 | ``` 37 | exec --no-startup-id 38 | ``` 39 | 40 | Usage via command line: 41 | ```bash 42 | ./i3-autodisplay -config sample_config.yml 43 | ``` 44 | 45 | Sample configuration file: 46 | ```yaml 47 | displays: 48 | - name: eDP1 49 | workspaces: [1,2,3,4,5,6,7,8,9,0] 50 | - name: HDMI1 51 | workspaces: [2,4,6,8] 52 | randr_extra_options: "--left-of eDP1" 53 | - name: DP1 54 | workspaces: [1,3,5,7,9] 55 | randr_extra_options: "--left-of HDMI1" 56 | ``` 57 | 58 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/command.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // CommandResult always contains Success, and command-specific fields where 9 | // appropriate. 10 | type CommandResult struct { 11 | // Success indicates whether the command was run without any errors. 12 | Success bool `json:"success"` 13 | 14 | // Error is a human-readable error message, non-empty for unsuccessful 15 | // commands. 16 | Error string `json:"error"` 17 | } 18 | 19 | // IsUnsuccessful is a convenience function which can be used to check if an 20 | // error is a CommandUnsuccessfulError. 21 | func IsUnsuccessful(err error) bool { 22 | _, ok := err.(*CommandUnsuccessfulError) 23 | return ok 24 | } 25 | 26 | // CommandUnsuccessfulError is returned by RunCommand for unsuccessful 27 | // commands. This type is exported so that you can ignore this error if you 28 | // expect your command(s) to fail. 29 | type CommandUnsuccessfulError struct { 30 | command string 31 | cr CommandResult 32 | } 33 | 34 | // Error implements error. 35 | func (e *CommandUnsuccessfulError) Error() string { 36 | return fmt.Sprintf("command %q unsuccessful: %v", e.command, e.cr.Error) 37 | } 38 | 39 | // RunCommand makes i3 run the specified command. 40 | // 41 | // Error is non-nil if any CommandResult.Success is not true. See IsUnsuccessful 42 | // if you send commands which are expected to fail. 43 | // 44 | // RunCommand is supported in i3 ≥ v4.0 (2011-07-31). 45 | func RunCommand(command string) ([]CommandResult, error) { 46 | reply, err := roundTrip(messageTypeRunCommand, []byte(command)) 47 | if err != nil { 48 | return []CommandResult{}, err 49 | } 50 | 51 | var crs []CommandResult 52 | err = json.Unmarshal(reply.Payload, &crs) 53 | if err == nil { 54 | for _, cr := range crs { 55 | if !cr.Success { 56 | return crs, &CommandUnsuccessfulError{ 57 | command: command, 58 | cr: cr, 59 | } 60 | } 61 | } 62 | } 63 | return crs, err 64 | } 65 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= 2 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 3 | github.com/BurntSushi/xgb v0.0.0-20200324125942-20f126ea2843 h1:3iF31c7rp7nGZVDv7YQ+VxOgpipVfPKotLXykjZmwM8= 4 | github.com/BurntSushi/xgb v0.0.0-20200324125942-20f126ea2843/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 5 | github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e h1:4ZrkT/RzpnROylmoQL57iVUL57wGKTR5O6KpVnbm2tA= 6 | github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k= 7 | github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= 8 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 9 | github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= 10 | github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= 11 | go.i3wm.org/i3/v4 v4.18.0 h1:yV9SCpYeyTqEuele2bw7rLmEfrGCRlid9enTqLjPuG0= 12 | go.i3wm.org/i3/v4 v4.18.0/go.mod h1:FN6trxp2TXmfzQVa6JSKq5IJx/XUJsnPFKAz15cBWtw= 13 | golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc h1:ZMCWScCvS2fUVFw8LOpxyUUW5qiviqr4Dg5NdjLeiLU= 14 | golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 15 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= 16 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 17 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 19 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= 20 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 21 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/version.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | ) 8 | 9 | // Version describes an i3 version. 10 | // 11 | // See https://i3wm.org/docs/ipc.html#_version_reply for more details. 12 | type Version struct { 13 | Major int64 `json:"major"` 14 | Minor int64 `json:"minor"` 15 | Patch int64 `json:"patch"` 16 | Variant string `json:"variant,omitempty"` 17 | HumanReadable string `json:"human_readable"` 18 | LoadedConfigFileName string `json:"loaded_config_file_name"` 19 | } 20 | 21 | // GetVersion returns i3’s version. 22 | // 23 | // GetVersion is supported in i3 ≥ v4.3 (2012-09-19). 24 | func GetVersion() (Version, error) { 25 | reply, err := roundTrip(messageTypeGetVersion, nil) 26 | if err != nil { 27 | return Version{}, err 28 | } 29 | 30 | var v Version 31 | err = json.Unmarshal(reply.Payload, &v) 32 | return v, err 33 | } 34 | 35 | // version is a lazily-initialized, possibly stale copy of i3’s GET_VERSION 36 | // reply. Access only values which don’t change, e.g. Major, Minor. 37 | var version Version 38 | 39 | // AtLeast returns nil if i3’s major version matches major and i3’s minor 40 | // version is at least minor or newer. Otherwise, it returns an error message 41 | // stating i3 is too old. 42 | func AtLeast(major int64, minor int64) error { 43 | if major == 0 { 44 | return fmt.Errorf("BUG: major == 0 is non-sensical. Is a lookup table entry missing?") 45 | } 46 | if version.Major == 0 { 47 | var err error 48 | version, err = GetVersion() 49 | if err != nil { 50 | return err 51 | } 52 | } 53 | if version.Variant != "" { 54 | log.Printf("non standard i3 payload variant '%s' detected. Ignoring version check. This is fully unsupported.", version.Variant) 55 | return nil 56 | } 57 | if version.Major == major && version.Minor >= minor { 58 | return nil 59 | } 60 | 61 | return fmt.Errorf("i3 version too old: got %d.%d, want ≥ %d.%d", version.Major, version.Minor, major, minor) 62 | } 63 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/writerc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | // Set the writer error and return false. 26 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { 27 | emitter.error = yaml_WRITER_ERROR 28 | emitter.problem = problem 29 | return false 30 | } 31 | 32 | // Flush the output buffer. 33 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool { 34 | if emitter.write_handler == nil { 35 | panic("write handler not set") 36 | } 37 | 38 | // Check if the buffer is empty. 39 | if emitter.buffer_pos == 0 { 40 | return true 41 | } 42 | 43 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 44 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 45 | } 46 | emitter.buffer_pos = 0 47 | return true 48 | } 49 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/README: -------------------------------------------------------------------------------- 1 | XGB is the X Go Binding, which is a low-level API to communicate with the 2 | core X protocol and many of the X extensions. It is closely modeled after 3 | XCB and xpyb. 4 | 5 | It is thread safe and gets immediate improvement from parallelism when 6 | GOMAXPROCS > 1. (See the benchmarks in xproto/xproto_test.go for evidence.) 7 | 8 | Please see doc.go for more info. 9 | 10 | Note that unless you know you need XGB, you can probably make your life 11 | easier by using a slightly higher level library: xgbutil. 12 | 13 | Quick Usage 14 | =========== 15 | go get github.com/BurntSushi/xgb 16 | go run go/path/src/github.com/BurntSushi/xgb/examples/create-window/main.go 17 | 18 | BurntSushi's Fork 19 | ================= 20 | I've forked the XGB repository from Google Code due to inactivty upstream. 21 | 22 | Godoc documentation can be found here: 23 | https://godoc.org/github.com/BurntSushi/xgb 24 | 25 | Much of the code has been rewritten in an effort to support thread safety 26 | and multiple extensions. Namely, go_client.py has been thrown away in favor 27 | of an xgbgen package. 28 | 29 | The biggest parts that *haven't* been rewritten by me are the connection and 30 | authentication handshakes. They're inherently messy, and there's really no 31 | reason to re-work them. The rest of XGB has been completely rewritten. 32 | 33 | I like to release my code under the WTFPL, but since I'm starting with someone 34 | else's work, I'm leaving the original license/contributor/author information 35 | in tact. 36 | 37 | I suppose I can legitimately release xgbgen under the WTFPL. To be fair, it is 38 | at least as complex as XGB itself. *sigh* 39 | 40 | What follows is the original README: 41 | 42 | XGB README 43 | ========== 44 | XGB is the X protocol Go language Binding. 45 | 46 | It is the Go equivalent of XCB, the X protocol C-language Binding 47 | (http://xcb.freedesktop.org/). 48 | 49 | Unless otherwise noted, the XGB source files are distributed 50 | under the BSD-style license found in the LICENSE file. 51 | 52 | Contributions should follow the same procedure as for the Go project: 53 | http://golang.org/doc/contribute.html 54 | 55 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | This project is covered by two different licenses: MIT and Apache. 3 | 4 | #### MIT License #### 5 | 6 | The following files were ported to Go from C files of libyaml, and thus 7 | are still covered by their original MIT license, with the additional 8 | copyright staring in 2011 when the project was ported over: 9 | 10 | apic.go emitterc.go parserc.go readerc.go scannerc.go 11 | writerc.go yamlh.go yamlprivateh.go 12 | 13 | Copyright (c) 2006-2010 Kirill Simonov 14 | Copyright (c) 2006-2011 Kirill Simonov 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy of 17 | this software and associated documentation files (the "Software"), to deal in 18 | the Software without restriction, including without limitation the rights to 19 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 20 | of the Software, and to permit persons to whom the Software is furnished to do 21 | so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in all 24 | copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | SOFTWARE. 33 | 34 | ### Apache License ### 35 | 36 | All the remaining project files are covered by the Apache license: 37 | 38 | Copyright (c) 2011-2019 Canonical Ltd 39 | 40 | Licensed under the Apache License, Version 2.0 (the "License"); 41 | you may not use this file except in compliance with the License. 42 | You may obtain a copy of the License at 43 | 44 | http://www.apache.org/licenses/LICENSE-2.0 45 | 46 | Unless required by applicable law or agreed to in writing, software 47 | distributed under the License is distributed on an "AS IS" BASIS, 48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49 | See the License for the specific language governing permissions and 50 | limitations under the License. 51 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/byteorder.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | // detectByteOrder sends messages to i3 to determine the byte order it uses. 10 | // For details on this technique, see: 11 | // https://build.i3wm.org/docs/ipc.html#_appendix_a_detecting_byte_order_in_memory_safe_languages 12 | func detectByteOrder(conn io.ReadWriter) (binary.ByteOrder, error) { 13 | const ( 14 | // targetLen is 0x00 01 01 00 in both, big and little endian 15 | targetLen = 65536 + 256 16 | 17 | // SUBSCRIBE was introduced in 3.e (2010-03-30) 18 | prefixSubscribe = "[]" 19 | 20 | // RUN_COMMAND was always present 21 | prefixCmd = "nop byte-order detection. padding: " 22 | ) 23 | 24 | // 2. Send a big endian encoded message of type SUBSCRIBE: 25 | payload := []byte(prefixSubscribe + strings.Repeat(" ", targetLen-len(prefixSubscribe))) 26 | if err := binary.Write(conn, binary.BigEndian, &header{magic, uint32(len(payload)), messageTypeSubscribe}); err != nil { 27 | return nil, err 28 | } 29 | if _, err := conn.Write(payload); err != nil { 30 | return nil, err 31 | } 32 | 33 | // 3. Send a byte order independent RUN_COMMAND message: 34 | payload = []byte(prefixCmd + strings.Repeat("a", targetLen-len(prefixCmd))) 35 | if err := binary.Write(conn, binary.BigEndian, &header{magic, uint32(len(payload)), messageTypeRunCommand}); err != nil { 36 | return nil, err 37 | } 38 | if _, err := conn.Write(payload); err != nil { 39 | return nil, err 40 | } 41 | 42 | // 4. Receive a message header, decode the message type as big endian: 43 | var header [14]byte 44 | if _, err := io.ReadFull(conn, header[:]); err != nil { 45 | return nil, err 46 | } 47 | if messageType(binary.BigEndian.Uint32(header[10:14])) == messageReplyTypeCommand { 48 | order := binary.LittleEndian // our big endian message was not answered 49 | // Read remaining payload 50 | _, err := io.ReadFull(conn, make([]byte, order.Uint32(header[6:10]))) 51 | return order, err 52 | } 53 | order := binary.BigEndian // our big endian message was answered 54 | // Read remaining payload 55 | if _, err := io.ReadFull(conn, make([]byte, order.Uint32(header[6:10]))); err != nil { 56 | return order, err 57 | } 58 | 59 | // Slurp the pending RUN_COMMAND reply. 60 | sock := &socket{conn: conn, order: order} 61 | _, err := sock.recvMsg() 62 | return binary.BigEndian, err 63 | } 64 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/README: -------------------------------------------------------------------------------- 1 | xgbutil is a utility library designed to work with the X Go Binding. This 2 | project's main goal is to make various X related tasks easier. For example, 3 | binding keys, using the EWMH or ICCCM specs with the window manager, 4 | moving/resizing windows, assigning function callbacks to particular events, 5 | drawing images to a window, etc. 6 | 7 | xgbutil attempts to be thread safe, but it has not been completely tested in 8 | this regard. In general, the X event loop implemented in the xevent package is 9 | sequential. The idea is to be sequential by default, and let the user spawn 10 | concurrent code at their discretion. (i.e., the complexity of making the main 11 | event loop generally concurrent is vast.) 12 | 13 | You may sleep safely at night by assuming that XGB is thread safe, though. 14 | 15 | To start using xgbutil, you should have at least a passing familiarity with X. 16 | Your first stop should be the examples directory. 17 | 18 | Installation 19 | ============ 20 | go get github.com/BurntSushi/xgbutil 21 | 22 | Dependencies 23 | ============ 24 | XGB is the main dependency. Use of the xgraphics packages requires graphics-go 25 | and freetype-go. 26 | 27 | XGB project URL: https://github.com/BurntSushi/xgb 28 | 29 | Quick Example 30 | ============= 31 | go get github.com/BurntSushi/xgbutil/_examples/window-name-sizes 32 | "$GOPATH"/bin/window-name-sizes 33 | 34 | The output will be a list of names of all top-level windows and their geometry 35 | including window manager decorations. (Assuming your window manager supports 36 | some basic EWMH properties.) 37 | 38 | Documentation 39 | ============= 40 | gopkgdoc is well-suited to provide documentation for xgbutil. Its cross-package 41 | hyperlinking on types is also extremely useful, since much of xgbutil relies on 42 | XGB. 43 | 44 | With that said, documentation is also hosted here: 45 | http://godoc.burntsushi.net/pkg/github.com/BurntSushi/xgbutil/ 46 | 47 | Examples 48 | ======== 49 | There are several examples in the examples directory covering common use cases. 50 | They are heavily documented and should run out of the box. 51 | 52 | Python 53 | ====== 54 | An older project of mine, xpybutil, served as inspiration for xgbutil. If you 55 | want to use Python, xpybutil should help quite a bit. Please note though, that 56 | at this point, xgbutil provides a lot more functionality and is much better 57 | documented. 58 | 59 | xpybutil project URL: https://github.com/BurntSushi/xpybutil 60 | 61 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package xgbutil is a utility library designed to make common tasks with the X 3 | server easier. The central design choice that has driven development is to hide 4 | the complexity of X wherever possible but expose it when necessary. 5 | 6 | For example, the xevent package provides an implementation of an X event loop 7 | that acts as a dispatcher to event handlers set up with the xevent, keybind and 8 | mousebind packages. At the same time, the event queue is exposed and can be 9 | modified using xevent.Peek and xevent.DequeueAt. 10 | 11 | Sub-packages 12 | 13 | The xgbutil package is considerably small, and only contains some type 14 | definitions and the initial setup for an X connection. Much of the 15 | functionality of xgbutil comes from its sub-packages. Each sub-package is 16 | appropriately documented. 17 | 18 | Installation 19 | 20 | xgbutil is go-gettable: 21 | 22 | go get github.com/BurntSushi/xgbutil 23 | 24 | Dependencies 25 | 26 | XGB is the main dependency, and is required for all packages inside xgbutil. 27 | 28 | graphics-go and freetype-go are also required if using the xgraphics package. 29 | 30 | Quick Example 31 | 32 | A quick example to demonstrate that xgbutil is working correctly: 33 | 34 | go get github.com/BurntSushi/xgbutil/examples/window-name-sizes 35 | GO/PATH/bin/window-name-sizes 36 | 37 | The output will be a list of names of all top-level windows and their geometry 38 | including window manager decorations. (Assuming your window manager supports 39 | some basic EWMH properties.) 40 | 41 | Examples 42 | 43 | The examples directory contains a sizable number of examples demonstrating 44 | common tasks with X. They are intended to demonstrate a single thing each, 45 | although a few that require setup are necessarily long. Each example is 46 | heavily documented. 47 | 48 | The examples directory should be your first stop when learning how to use 49 | xgbutil. 50 | 51 | xgbutil is also used heavily throughout my window manager, Wingo. It may be 52 | useful reference material. 53 | 54 | Wingo project page: https://github.com/BurntSushi/wingo 55 | 56 | Thread Safety 57 | 58 | While I am fairly confident that XGB is thread safe, I am only somewhat 59 | confident that xgbutil is thread safe. It simply has not been tested enough for 60 | my confidence to be higher. 61 | 62 | Note that the xevent package's X event loop is not concurrent. Namely, 63 | designing a generally concurrent X event loop is extremely complex. Instead, 64 | the onus is on you, the user, to design concurrent callback functions if 65 | concurrency is desired. 66 | */ 67 | package xgbutil 68 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2009 The XGB Authors. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google Inc. nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | // 29 | // Subject to the terms and conditions of this License, Google hereby 30 | // grants to You a perpetual, worldwide, non-exclusive, no-charge, 31 | // royalty-free, irrevocable (except as stated in this section) patent 32 | // license to make, have made, use, offer to sell, sell, import, and 33 | // otherwise transfer this implementation of XGB, where such license 34 | // applies only to those patent claims licensable by Google that are 35 | // necessarily infringed by use of this implementation of XGB. If You 36 | // institute patent litigation against any entity (including a 37 | // cross-claim or counterclaim in a lawsuit) alleging that this 38 | // implementation of XGB or a Contribution incorporated within this 39 | // implementation of XGB constitutes direct or contributory patent 40 | // infringement, then any patent licenses granted to You under this 41 | // License for this implementation of XGB shall terminate as of the date 42 | // such litigation is filed. 43 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/i3/go-i3.svg?branch=master)](https://travis-ci.org/i3/go-i3) 2 | [![Go Report Card](https://goreportcard.com/badge/go.i3wm.org/i3)](https://goreportcard.com/report/go.i3wm.org/i3) 3 | [![GoDoc](https://godoc.org/go.i3wm.org/i3?status.svg)](https://godoc.org/go.i3wm.org/i3) 4 | 5 | Package i3 provides a convenient interface to the i3 window manager via [its IPC 6 | interface](https://i3wm.org/docs/ipc.html). 7 | 8 | See [its documentation](https://godoc.org/go.i3wm.org/i3) for more details. 9 | 10 | ## Start using it 11 | 12 | In [module mode](https://github.com/golang/go/wiki/Modules), use import path 13 | `go.i3wm.org/i3/v4`. 14 | 15 | In non-module mode, use import path `go.i3wm.org/i3`. 16 | 17 | ## Advantages over other i3 IPC packages 18 | 19 | Here comes a grab bag of features to which we paid attention. At the time of 20 | writing, most other i3 IPC packages lack at least a good number of these 21 | features: 22 | 23 | * Retries are transparently handled: programs using this package will recover 24 | automatically from in-place i3 restarts. Additionally, programs can be started 25 | from xsession or user sessions before i3 is even running. 26 | 27 | * Version checks are transparently handled: if your program uses features which 28 | are not supported by the running i3 version, helpful error messages will be 29 | returned at run time. 30 | 31 | * Comprehensive: the entire documented IPC interface of the latest stable i3 32 | version is covered by this package. Tagged releases match i3’s major and minor 33 | version. 34 | 35 | * Consistent and familiar: once familiar with the i3 IPC protocol’s features, 36 | you should have no trouble matching the documentation to API and vice-versa. 37 | 38 | * Good test coverage (hard to display in a badge, as our multi-process setup 39 | breaks `go test`’s `-coverprofile` flag). 40 | 41 | * Implemented in pure Go, without resorting to the `unsafe` package. 42 | 43 | * Works on little and big endian architectures. 44 | 45 | ## Scope 46 | 47 | i3’s entire documented IPC interface is available in this package. 48 | 49 | In addition, helper functions which are useful for a broad range of programs 50 | (and only those!) are provided, e.g. Node’s FindChild and FindFocused. 51 | 52 | Packages which introduce higher-level abstractions should feel free to use this 53 | package as a building block. 54 | 55 | ## Assumptions 56 | 57 | * The `i3(1)` binary must be in `$PATH` so that the IPC socket path can be retrieved. 58 | * For transparent version checks to work, the running i3 version must be ≥ 4.3 (released 2012-09-19). 59 | 60 | ## Testing 61 | 62 | Be sure to include the target i3 version (the most recent stable release) in 63 | `$PATH` and use `go test` as usual: 64 | 65 | ```shell 66 | PATH=~/i3/build/i3:$PATH go test -v go.i3wm.org/i3 67 | ``` 68 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/auth.go: -------------------------------------------------------------------------------- 1 | package xgb 2 | 3 | /* 4 | auth.go contains functions to facilitate the parsing of .Xauthority files. 5 | 6 | It is largely unmodified from the original XGB package that I forked. 7 | */ 8 | 9 | import ( 10 | "encoding/binary" 11 | "errors" 12 | "io" 13 | "os" 14 | ) 15 | 16 | // readAuthority reads the X authority file for the DISPLAY. 17 | // If hostname == "" or hostname == "localhost", 18 | // then use the system's hostname (as returned by os.Hostname) instead. 19 | func readAuthority(hostname, display string) ( 20 | name string, data []byte, err error) { 21 | 22 | // b is a scratch buffer to use and should be at least 256 bytes long 23 | // (i.e. it should be able to hold a hostname). 24 | b := make([]byte, 256) 25 | 26 | // As per /usr/include/X11/Xauth.h. 27 | const familyLocal = 256 28 | const familyWild = 65535 29 | 30 | if len(hostname) == 0 || hostname == "localhost" { 31 | hostname, err = os.Hostname() 32 | if err != nil { 33 | return "", nil, err 34 | } 35 | } 36 | 37 | fname := os.Getenv("XAUTHORITY") 38 | if len(fname) == 0 { 39 | home := os.Getenv("HOME") 40 | if len(home) == 0 { 41 | err = errors.New("Xauthority not found: $XAUTHORITY, $HOME not set") 42 | return "", nil, err 43 | } 44 | fname = home + "/.Xauthority" 45 | } 46 | 47 | r, err := os.Open(fname) 48 | if err != nil { 49 | return "", nil, err 50 | } 51 | defer r.Close() 52 | 53 | for { 54 | var family uint16 55 | if err := binary.Read(r, binary.BigEndian, &family); err != nil { 56 | return "", nil, err 57 | } 58 | 59 | addr, err := getString(r, b) 60 | if err != nil { 61 | return "", nil, err 62 | } 63 | 64 | disp, err := getString(r, b) 65 | if err != nil { 66 | return "", nil, err 67 | } 68 | 69 | name0, err := getString(r, b) 70 | if err != nil { 71 | return "", nil, err 72 | } 73 | 74 | data0, err := getBytes(r, b) 75 | if err != nil { 76 | return "", nil, err 77 | } 78 | 79 | addrmatch := (family == familyWild) || 80 | (family == familyLocal && addr == hostname) 81 | dispmatch := (disp == "") || (disp == display) 82 | 83 | if addrmatch && dispmatch { 84 | return name0, data0, nil 85 | } 86 | } 87 | panic("unreachable") 88 | } 89 | 90 | func getBytes(r io.Reader, b []byte) ([]byte, error) { 91 | var n uint16 92 | if err := binary.Read(r, binary.BigEndian, &n); err != nil { 93 | return nil, err 94 | } else if n > uint16(len(b)) { 95 | return nil, errors.New("bytes too long for buffer") 96 | } 97 | 98 | if _, err := io.ReadFull(r, b[0:n]); err != nil { 99 | return nil, err 100 | } 101 | return b[0:n], nil 102 | } 103 | 104 | func getString(r io.Reader, b []byte) (string, error) { 105 | b, err := getBytes(r, b) 106 | if err != nil { 107 | return "", err 108 | } 109 | return string(b), nil 110 | } 111 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/help.go: -------------------------------------------------------------------------------- 1 | package xgb 2 | 3 | /* 4 | help.go is meant to contain a rough hodge podge of functions that are mainly 5 | used in the auto generated code. Indeed, several functions here are simple 6 | wrappers so that the sub-packages don't need to be smart about which stdlib 7 | packages to import. 8 | 9 | Also, the 'Get..' and 'Put..' functions are used through the core xgb package 10 | too. (xgbutil uses them too.) 11 | */ 12 | 13 | import ( 14 | "fmt" 15 | "strings" 16 | ) 17 | 18 | // StringsJoin is an alias to strings.Join. It allows us to avoid having to 19 | // import 'strings' in each of the generated Go files. 20 | func StringsJoin(ss []string, sep string) string { 21 | return strings.Join(ss, sep) 22 | } 23 | 24 | // Sprintf is so we don't need to import 'fmt' in the generated Go files. 25 | func Sprintf(format string, v ...interface{}) string { 26 | return fmt.Sprintf(format, v...) 27 | } 28 | 29 | // Errorf is just a wrapper for fmt.Errorf. Exists for the same reason 30 | // that 'stringsJoin' and 'sprintf' exists. 31 | func Errorf(format string, v ...interface{}) error { 32 | return fmt.Errorf(format, v...) 33 | } 34 | 35 | // Pad a length to align on 4 bytes. 36 | func Pad(n int) int { 37 | return (n + 3) & ^3 38 | } 39 | 40 | // PopCount counts the number of bits set in a value list mask. 41 | func PopCount(mask0 int) int { 42 | mask := uint32(mask0) 43 | n := 0 44 | for i := uint32(0); i < 32; i++ { 45 | if mask&(1<> 8) 56 | } 57 | 58 | // Put32 takes a 32 bit integer and copies it into a byte slice. 59 | func Put32(buf []byte, v uint32) { 60 | buf[0] = byte(v) 61 | buf[1] = byte(v >> 8) 62 | buf[2] = byte(v >> 16) 63 | buf[3] = byte(v >> 24) 64 | } 65 | 66 | // Put64 takes a 64 bit integer and copies it into a byte slice. 67 | func Put64(buf []byte, v uint64) { 68 | buf[0] = byte(v) 69 | buf[1] = byte(v >> 8) 70 | buf[2] = byte(v >> 16) 71 | buf[3] = byte(v >> 24) 72 | buf[4] = byte(v >> 32) 73 | buf[5] = byte(v >> 40) 74 | buf[6] = byte(v >> 48) 75 | buf[7] = byte(v >> 56) 76 | } 77 | 78 | // Get16 constructs a 16 bit integer from the beginning of a byte slice. 79 | func Get16(buf []byte) uint16 { 80 | v := uint16(buf[0]) 81 | v |= uint16(buf[1]) << 8 82 | return v 83 | } 84 | 85 | // Get32 constructs a 32 bit integer from the beginning of a byte slice. 86 | func Get32(buf []byte) uint32 { 87 | v := uint32(buf[0]) 88 | v |= uint32(buf[1]) << 8 89 | v |= uint32(buf[2]) << 16 90 | v |= uint32(buf[3]) << 24 91 | return v 92 | } 93 | 94 | // Get64 constructs a 64 bit integer from the beginning of a byte slice. 95 | func Get64(buf []byte) uint64 { 96 | v := uint64(buf[0]) 97 | v |= uint64(buf[1]) << 8 98 | v |= uint64(buf[2]) << 16 99 | v |= uint64(buf[3]) << 24 100 | v |= uint64(buf[4]) << 32 101 | v |= uint64(buf[5]) << 40 102 | v |= uint64(buf[6]) << 48 103 | v |= uint64(buf[7]) << 56 104 | return v 105 | } 106 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/Makefile: -------------------------------------------------------------------------------- 1 | # This Makefile is used by the developer. It is not needed in any way to build 2 | # a checkout of the XGB repository. 3 | # It will be useful, however, if you are hacking at the code generator. 4 | # i.e., after making a change to the code generator, run 'make' in the 5 | # xgb directory. This will build xgbgen and regenerate each sub-package. 6 | # 'make test' will then run any appropriate tests (just tests xproto right now). 7 | # 'make bench' will test a couple of benchmarks. 8 | # 'make build-all' will then try to build each extension. This isn't strictly 9 | # necessary, but it's a good idea to make sure each sub-package is a valid 10 | # Go package. 11 | 12 | # My path to the X protocol XML descriptions. 13 | XPROTO=/usr/share/xcb 14 | 15 | # All of the XML files in my /usr/share/xcb directory EXCEPT XKB. -_- 16 | # This is intended to build xgbgen and generate Go code for each supported 17 | # extension. 18 | all: build-xgbgen \ 19 | bigreq.xml composite.xml damage.xml dpms.xml dri2.xml \ 20 | ge.xml glx.xml randr.xml record.xml render.xml res.xml \ 21 | screensaver.xml shape.xml shm.xml xc_misc.xml \ 22 | xevie.xml xf86dri.xml xf86vidmode.xml xfixes.xml xinerama.xml \ 23 | xprint.xml xproto.xml xselinux.xml xtest.xml \ 24 | xvmc.xml xv.xml 25 | 26 | build-xgbgen: 27 | (cd xgbgen && go build) 28 | 29 | # Builds each individual sub-package to make sure its valid Go code. 30 | build-all: bigreq.b composite.b damage.b dpms.b dri2.b ge.b glx.b randr.b \ 31 | record.b render.b res.b screensaver.b shape.b shm.b xcmisc.b \ 32 | xevie.b xf86dri.b xf86vidmode.b xfixes.b xinerama.b \ 33 | xprint.b xproto.b xselinux.b xtest.b xv.b xvmc.b 34 | 35 | %.b: 36 | (cd $* ; go build) 37 | 38 | # Installs each individual sub-package. 39 | install: bigreq.i composite.i damage.i dpms.i dri2.i ge.i glx.i randr.i \ 40 | record.i render.i res.i screensaver.i shape.i shm.i xcmisc.i \ 41 | xevie.i xf86dri.i xf86vidmode.i xfixes.i xinerama.i \ 42 | xprint.i xproto.i xselinux.i xtest.i xv.i xvmc.i 43 | go install 44 | 45 | %.i: 46 | (cd $* ; go install) 47 | 48 | # xc_misc is special because it has an underscore. 49 | # There's probably a way to do this better, but Makefiles aren't my strong suit. 50 | xc_misc.xml: build-xgbgen 51 | mkdir -p xcmisc 52 | xgbgen/xgbgen --proto-path $(XPROTO) $(XPROTO)/xc_misc.xml > xcmisc/xcmisc.go 53 | 54 | %.xml: build-xgbgen 55 | mkdir -p $* 56 | xgbgen/xgbgen --proto-path $(XPROTO) $(XPROTO)/$*.xml > $*/$*.go 57 | 58 | # Just test the xproto core protocol for now. 59 | test: 60 | (cd xproto ; go test) 61 | 62 | # Force all xproto benchmarks to run and no tests. 63 | bench: 64 | (cd xproto ; go test -run 'nomatch' -bench '.*' -cpu 1,2,3,6) 65 | 66 | # gofmt all non-auto-generated code. 67 | # (auto-generated code is already gofmt'd.) 68 | # Also do a column check (80 cols) after a gofmt. 69 | # But don't check columns on auto-generated code, since I don't care if they 70 | # break 80 cols. 71 | gofmt: 72 | gofmt -w *.go xgbgen/*.go examples/*.go examples/*/*.go xproto/xproto_test.go 73 | colcheck *.go xgbgen/*.go examples/*.go examples/*/*.go xproto/xproto_test.go 74 | 75 | push: 76 | git push origin master 77 | git push github master 78 | 79 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/xprop/atom.go: -------------------------------------------------------------------------------- 1 | package xprop 2 | 3 | /* 4 | xprop/atom.go contains functions related to interning atoms and retrieving 5 | atom names from an atom identifier. 6 | 7 | It also manages an atom cache so that once an atom is interned from the X 8 | server, all future atom interns use that value. (So that one and only one 9 | request is sent for interning each atom.) 10 | */ 11 | 12 | import ( 13 | "fmt" 14 | 15 | "github.com/BurntSushi/xgb/xproto" 16 | 17 | "github.com/BurntSushi/xgbutil" 18 | ) 19 | 20 | // Atm is a short alias for Atom in the common case of interning an atom. 21 | // Namely, interning the atom always succeeds. (If the atom does not already 22 | // exist, a new one is created.) 23 | func Atm(xu *xgbutil.XUtil, name string) (xproto.Atom, error) { 24 | aid, err := Atom(xu, name, false) 25 | if err != nil { 26 | return 0, err 27 | } 28 | if aid == 0 { 29 | return 0, fmt.Errorf("Atm: '%s' returned an identifier of 0.", name) 30 | } 31 | 32 | return aid, err 33 | } 34 | 35 | // Atom interns an atom and panics if there is any error. 36 | func Atom(xu *xgbutil.XUtil, name string, 37 | onlyIfExists bool) (xproto.Atom, error) { 38 | 39 | // Check the cache first 40 | if aid, ok := atomGet(xu, name); ok { 41 | return aid, nil 42 | } 43 | 44 | reply, err := xproto.InternAtom(xu.Conn(), onlyIfExists, 45 | uint16(len(name)), name).Reply() 46 | if err != nil { 47 | return 0, fmt.Errorf("Atom: Error interning atom '%s': %s", name, err) 48 | } 49 | 50 | // If we're here, it means we didn't have this atom cached. So cache it! 51 | cacheAtom(xu, name, reply.Atom) 52 | 53 | return reply.Atom, nil 54 | } 55 | 56 | // AtomName fetches a string representation of an ATOM given its integer id. 57 | func AtomName(xu *xgbutil.XUtil, aid xproto.Atom) (string, error) { 58 | // Check the cache first 59 | if atomName, ok := atomNameGet(xu, aid); ok { 60 | return string(atomName), nil 61 | } 62 | 63 | reply, err := xproto.GetAtomName(xu.Conn(), aid).Reply() 64 | if err != nil { 65 | return "", fmt.Errorf("AtomName: Error fetching name for ATOM "+ 66 | "id '%d': %s", aid, err) 67 | } 68 | 69 | // If we're here, it means we didn't have ths ATOM id cached. So cache it. 70 | atomName := string(reply.Name) 71 | cacheAtom(xu, atomName, aid) 72 | 73 | return atomName, nil 74 | } 75 | 76 | // atomGet retrieves an atom identifier from a cache if it exists. 77 | func atomGet(xu *xgbutil.XUtil, name string) (xproto.Atom, bool) { 78 | xu.AtomsLck.RLock() 79 | defer xu.AtomsLck.RUnlock() 80 | 81 | aid, ok := xu.Atoms[name] 82 | return aid, ok 83 | } 84 | 85 | // atomNameGet retrieves an atom name from a cache if it exists. 86 | func atomNameGet(xu *xgbutil.XUtil, aid xproto.Atom) (string, bool) { 87 | xu.AtomNamesLck.RLock() 88 | defer xu.AtomNamesLck.RUnlock() 89 | 90 | name, ok := xu.AtomNames[aid] 91 | return name, ok 92 | } 93 | 94 | // cacheAtom puts an atom into the cache. 95 | func cacheAtom(xu *xgbutil.XUtil, name string, aid xproto.Atom) { 96 | xu.AtomsLck.Lock() 97 | xu.AtomNamesLck.Lock() 98 | defer xu.AtomsLck.Unlock() 99 | defer xu.AtomNamesLck.Unlock() 100 | 101 | xu.Atoms[name] = aid 102 | xu.AtomNames[aid] = name 103 | } 104 | -------------------------------------------------------------------------------- /display/randr.go: -------------------------------------------------------------------------------- 1 | package display 2 | 3 | import ( 4 | "log" 5 | "os/exec" 6 | "reflect" 7 | "strings" 8 | 9 | "github.com/jezek/xgb" 10 | 11 | "github.com/jezek/xgb/randr" 12 | "github.com/jezek/xgb/xproto" 13 | "github.com/lpicanco/i3-autodisplay/config" 14 | "github.com/lpicanco/i3-autodisplay/i3" 15 | ) 16 | 17 | var ( 18 | xgbConn *xgb.Conn 19 | lastOutputConfiguration map[string]bool 20 | ) 21 | 22 | func init() { 23 | var err error 24 | 25 | xgbConn, err = xgb.NewConn() 26 | if err != nil { 27 | log.Fatalf("error initializing xgb: %v", err) 28 | } 29 | 30 | err = randr.Init(xgbConn) 31 | if err != nil { 32 | log.Fatalf("error initializing randr: %v", err) 33 | } 34 | } 35 | 36 | func Refresh() { 37 | currentOutputConfiguration := getOutputConfiguration() 38 | 39 | if reflect.DeepEqual(currentOutputConfiguration, lastOutputConfiguration) { 40 | return 41 | } 42 | 43 | currentWorkspace, err := i3.GetCurrentWorkspaceNumber() 44 | if err != nil { 45 | log.Fatalf("error getting i3 current workspace: %v", err) 46 | } 47 | 48 | args := []string{} 49 | for _, display := range config.Config.Displays { 50 | active := currentOutputConfiguration[display.Name] 51 | args = append(args, getDisplayOptions(display, active)...) 52 | } 53 | 54 | log.Println("xrandr", args) 55 | cmd := exec.Command("xrandr", args...) 56 | out, err := cmd.CombinedOutput() 57 | 58 | if err != nil { 59 | log.Fatalf("Error executing xrandr: %s\n%s", err, out) 60 | } 61 | 62 | for _, display := range config.Config.Displays { 63 | if currentOutputConfiguration[display.Name] { 64 | refreshDisplay(display) 65 | } 66 | } 67 | 68 | err = i3.SetCurrentWorkspace(currentWorkspace) 69 | if err != nil { 70 | log.Fatalf("error setting i3 current workspace: %v", err) 71 | } 72 | 73 | lastOutputConfiguration = currentOutputConfiguration 74 | } 75 | 76 | func ListenEvents() { 77 | defer xgbConn.Close() 78 | 79 | root := xproto.Setup(xgbConn).DefaultScreen(xgbConn).Root 80 | err := randr.SelectInputChecked(xgbConn, root, 81 | randr.NotifyMaskScreenChange|randr.NotifyMaskCrtcChange|randr.NotifyMaskOutputChange).Check() 82 | 83 | if err != nil { 84 | log.Fatalf("error subscribing to randr events: %v", err) 85 | } 86 | 87 | for { 88 | ev, err := xgbConn.WaitForEvent() 89 | if err != nil { 90 | log.Fatalf("error processing randr event: %v", err) 91 | } 92 | 93 | switch ev.(type) { 94 | case randr.ScreenChangeNotifyEvent: 95 | Refresh() 96 | } 97 | } 98 | } 99 | 100 | func getDisplayOptions(display config.Display, active bool) []string { 101 | if active { 102 | args := []string{"--output", display.Name, "--auto"} 103 | if display.RandrExtraOptions != "" { 104 | args = append(args, strings.Split(display.RandrExtraOptions, " ")...) 105 | } 106 | return args 107 | } else { 108 | args := []string{"--output", display.Name, "--off"} 109 | return args 110 | } 111 | } 112 | 113 | func refreshDisplay(display config.Display) { 114 | err := i3.UpdateWorkspaces(display) 115 | if err != nil { 116 | log.Fatalf("Error updating i3 workspaces: %s\n", err) 117 | } 118 | } 119 | 120 | func getOutputConfiguration() map[string]bool { 121 | config := make(map[string]bool) 122 | 123 | root := xproto.Setup(xgbConn).DefaultScreen(xgbConn).Root 124 | resources, err := randr.GetScreenResources(xgbConn, root).Reply() 125 | 126 | if err != nil { 127 | log.Fatalf("error getting randr screen resources: %v", err) 128 | } 129 | 130 | for _, output := range resources.Outputs { 131 | info, err := randr.GetOutputInfo(xgbConn, output, 0).Reply() 132 | if err != nil { 133 | log.Fatalf("error getting randr output info: %v", err) 134 | } 135 | 136 | config[string(info.Name)] = info.Connection == randr.ConnectionConnected 137 | } 138 | 139 | return config 140 | } 141 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/sorter.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "reflect" 20 | "unicode" 21 | ) 22 | 23 | type keyList []reflect.Value 24 | 25 | func (l keyList) Len() int { return len(l) } 26 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 27 | func (l keyList) Less(i, j int) bool { 28 | a := l[i] 29 | b := l[j] 30 | ak := a.Kind() 31 | bk := b.Kind() 32 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { 33 | a = a.Elem() 34 | ak = a.Kind() 35 | } 36 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { 37 | b = b.Elem() 38 | bk = b.Kind() 39 | } 40 | af, aok := keyFloat(a) 41 | bf, bok := keyFloat(b) 42 | if aok && bok { 43 | if af != bf { 44 | return af < bf 45 | } 46 | if ak != bk { 47 | return ak < bk 48 | } 49 | return numLess(a, b) 50 | } 51 | if ak != reflect.String || bk != reflect.String { 52 | return ak < bk 53 | } 54 | ar, br := []rune(a.String()), []rune(b.String()) 55 | digits := false 56 | for i := 0; i < len(ar) && i < len(br); i++ { 57 | if ar[i] == br[i] { 58 | digits = unicode.IsDigit(ar[i]) 59 | continue 60 | } 61 | al := unicode.IsLetter(ar[i]) 62 | bl := unicode.IsLetter(br[i]) 63 | if al && bl { 64 | return ar[i] < br[i] 65 | } 66 | if al || bl { 67 | if digits { 68 | return al 69 | } else { 70 | return bl 71 | } 72 | } 73 | var ai, bi int 74 | var an, bn int64 75 | if ar[i] == '0' || br[i] == '0' { 76 | for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { 77 | if ar[j] != '0' { 78 | an = 1 79 | bn = 1 80 | break 81 | } 82 | } 83 | } 84 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 85 | an = an*10 + int64(ar[ai]-'0') 86 | } 87 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 88 | bn = bn*10 + int64(br[bi]-'0') 89 | } 90 | if an != bn { 91 | return an < bn 92 | } 93 | if ai != bi { 94 | return ai < bi 95 | } 96 | return ar[i] < br[i] 97 | } 98 | return len(ar) < len(br) 99 | } 100 | 101 | // keyFloat returns a float value for v if it is a number/bool 102 | // and whether it is a number/bool or not. 103 | func keyFloat(v reflect.Value) (f float64, ok bool) { 104 | switch v.Kind() { 105 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 106 | return float64(v.Int()), true 107 | case reflect.Float32, reflect.Float64: 108 | return v.Float(), true 109 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 110 | return float64(v.Uint()), true 111 | case reflect.Bool: 112 | if v.Bool() { 113 | return 1, true 114 | } 115 | return 0, true 116 | } 117 | return 0, false 118 | } 119 | 120 | // numLess returns whether a < b. 121 | // a and b must necessarily have the same kind. 122 | func numLess(a, b reflect.Value) bool { 123 | switch a.Kind() { 124 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 125 | return a.Int() < b.Int() 126 | case reflect.Float32, reflect.Float64: 127 | return a.Float() < b.Float() 128 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 129 | return a.Uint() < b.Uint() 130 | case reflect.Bool: 131 | return !a.Bool() && b.Bool() 132 | } 133 | panic("not a number") 134 | } 135 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/README.md: -------------------------------------------------------------------------------- 1 | # YAML support for the Go language 2 | 3 | Introduction 4 | ------------ 5 | 6 | The yaml package enables Go programs to comfortably encode and decode YAML 7 | values. It was developed within [Canonical](https://www.canonical.com) as 8 | part of the [juju](https://juju.ubuntu.com) project, and is based on a 9 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) 10 | C library to parse and generate YAML data quickly and reliably. 11 | 12 | Compatibility 13 | ------------- 14 | 15 | The yaml package supports most of YAML 1.2, but preserves some behavior 16 | from 1.1 for backwards compatibility. 17 | 18 | Specifically, as of v3 of the yaml package: 19 | 20 | - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being 21 | decoded into a typed bool value. Otherwise they behave as a string. Booleans 22 | in YAML 1.2 are _true/false_ only. 23 | - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ 24 | as specified in YAML 1.2, because most parsers still use the old format. 25 | Octals in the _0o777_ format are supported though, so new files work. 26 | - Does not support base-60 floats. These are gone from YAML 1.2, and were 27 | actually never supported by this package as it's clearly a poor choice. 28 | 29 | and offers backwards 30 | compatibility with YAML 1.1 in some cases. 31 | 1.2, including support for 32 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet 33 | implemented, and base-60 floats from YAML 1.1 are purposefully not 34 | supported since they're a poor design and are gone in YAML 1.2. 35 | 36 | Installation and usage 37 | ---------------------- 38 | 39 | The import path for the package is *gopkg.in/yaml.v3*. 40 | 41 | To install it, run: 42 | 43 | go get gopkg.in/yaml.v3 44 | 45 | API documentation 46 | ----------------- 47 | 48 | If opened in a browser, the import path itself leads to the API documentation: 49 | 50 | - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) 51 | 52 | API stability 53 | ------------- 54 | 55 | The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). 56 | 57 | 58 | License 59 | ------- 60 | 61 | The yaml package is licensed under the MIT and Apache License 2.0 licenses. 62 | Please see the LICENSE file for details. 63 | 64 | 65 | Example 66 | ------- 67 | 68 | ```Go 69 | package main 70 | 71 | import ( 72 | "fmt" 73 | "log" 74 | 75 | "gopkg.in/yaml.v3" 76 | ) 77 | 78 | var data = ` 79 | a: Easy! 80 | b: 81 | c: 2 82 | d: [3, 4] 83 | ` 84 | 85 | // Note: struct fields must be public in order for unmarshal to 86 | // correctly populate the data. 87 | type T struct { 88 | A string 89 | B struct { 90 | RenamedC int `yaml:"c"` 91 | D []int `yaml:",flow"` 92 | } 93 | } 94 | 95 | func main() { 96 | t := T{} 97 | 98 | err := yaml.Unmarshal([]byte(data), &t) 99 | if err != nil { 100 | log.Fatalf("error: %v", err) 101 | } 102 | fmt.Printf("--- t:\n%v\n\n", t) 103 | 104 | d, err := yaml.Marshal(&t) 105 | if err != nil { 106 | log.Fatalf("error: %v", err) 107 | } 108 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 109 | 110 | m := make(map[interface{}]interface{}) 111 | 112 | err = yaml.Unmarshal([]byte(data), &m) 113 | if err != nil { 114 | log.Fatalf("error: %v", err) 115 | } 116 | fmt.Printf("--- m:\n%v\n\n", m) 117 | 118 | d, err = yaml.Marshal(&m) 119 | if err != nil { 120 | log.Fatalf("error: %v", err) 121 | } 122 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 123 | } 124 | ``` 125 | 126 | This example will generate the following output: 127 | 128 | ``` 129 | --- t: 130 | {Easy! {2 [3 4]}} 131 | 132 | --- t dump: 133 | a: Easy! 134 | b: 135 | c: 2 136 | d: [3, 4] 137 | 138 | 139 | --- m: 140 | map[a:Easy! b:map[c:2 d:[3 4]]] 141 | 142 | --- m dump: 143 | a: Easy! 144 | b: 145 | c: 2 146 | d: 147 | - 3 148 | - 4 149 | ``` 150 | 151 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/barconfig.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import "encoding/json" 4 | 5 | // BarConfigColors describes a serialized bar colors configuration block. 6 | // 7 | // See https://i3wm.org/docs/ipc.html#_bar_config_reply for more details. 8 | type BarConfigColors struct { 9 | Background string `json:"background"` 10 | Statusline string `json:"statusline"` 11 | Separator string `json:"separator"` 12 | 13 | FocusedBackground string `json:"focused_background"` 14 | FocusedStatusline string `json:"focused_statusline"` 15 | FocusedSeparator string `json:"focused_separator"` 16 | 17 | FocusedWorkspaceText string `json:"focused_workspace_text"` 18 | FocusedWorkspaceBackground string `json:"focused_workspace_bg"` 19 | FocusedWorkspaceBorder string `json:"focused_workspace_border"` 20 | 21 | ActiveWorkspaceText string `json:"active_workspace_text"` 22 | ActiveWorkspaceBackground string `json:"active_workspace_bg"` 23 | ActiveWorkspaceBorder string `json:"active_workspace_border"` 24 | 25 | InactiveWorkspaceText string `json:"inactive_workspace_text"` 26 | InactiveWorkspaceBackground string `json:"inactive_workspace_bg"` 27 | InactiveWorkspaceBorder string `json:"inactive_workspace_border"` 28 | 29 | UrgentWorkspaceText string `json:"urgent_workspace_text"` 30 | UrgentWorkspaceBackground string `json:"urgent_workspace_bg"` 31 | UrgentWorkspaceBorder string `json:"urgent_workspace_border"` 32 | 33 | BindingModeText string `json:"binding_mode_text"` 34 | BindingModeBackground string `json:"binding_mode_bg"` 35 | BindingModeBorder string `json:"binding_mode_border"` 36 | } 37 | 38 | // BarConfig describes a serialized bar configuration block. 39 | // 40 | // See https://i3wm.org/docs/ipc.html#_bar_config_reply for more details. 41 | type BarConfig struct { 42 | ID string `json:"id"` 43 | Mode string `json:"mode"` 44 | Position string `json:"position"` 45 | StatusCommand string `json:"status_command"` 46 | Font string `json:"font"` 47 | WorkspaceButtons bool `json:"workspace_buttons"` 48 | BindingModeIndicator bool `json:"binding_mode_indicator"` 49 | Verbose bool `json:"verbose"` 50 | Colors BarConfigColors `json:"colors"` 51 | } 52 | 53 | // GetBarIDs returns an array of configured bar IDs. 54 | // 55 | // GetBarIDs is supported in i3 ≥ v4.1 (2011-11-11). 56 | func GetBarIDs() ([]string, error) { 57 | reply, err := roundTrip(messageTypeGetBarConfig, nil) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | var ids []string 63 | err = json.Unmarshal(reply.Payload, &ids) 64 | return ids, err 65 | } 66 | 67 | // GetBarConfig returns the configuration for the bar with the specified barID. 68 | // 69 | // Obtain the barID from GetBarIDs. 70 | // 71 | // GetBarConfig is supported in i3 ≥ v4.1 (2011-11-11). 72 | func GetBarConfig(barID string) (BarConfig, error) { 73 | reply, err := roundTrip(messageTypeGetBarConfig, []byte(barID)) 74 | if err != nil { 75 | return BarConfig{}, err 76 | } 77 | 78 | cfg := BarConfig{ 79 | Colors: BarConfigColors{ 80 | Background: "#000000", 81 | Statusline: "#ffffff", 82 | Separator: "#666666", 83 | 84 | FocusedBackground: "#000000", 85 | FocusedStatusline: "#ffffff", 86 | FocusedSeparator: "#666666", 87 | 88 | FocusedWorkspaceText: "#4c7899", 89 | FocusedWorkspaceBackground: "#285577", 90 | FocusedWorkspaceBorder: "#ffffff", 91 | 92 | ActiveWorkspaceText: "#333333", 93 | ActiveWorkspaceBackground: "#5f676a", 94 | ActiveWorkspaceBorder: "#ffffff", 95 | 96 | InactiveWorkspaceText: "#333333", 97 | InactiveWorkspaceBackground: "#222222", 98 | InactiveWorkspaceBorder: "#888888", 99 | 100 | UrgentWorkspaceText: "#2f343a", 101 | UrgentWorkspaceBackground: "#900000", 102 | UrgentWorkspaceBorder: "#ffffff", 103 | 104 | BindingModeText: "#2f343a", 105 | BindingModeBackground: "#900000", 106 | BindingModeBorder: "#ffffff", 107 | }, 108 | } 109 | err = json.Unmarshal(reply.Payload, &cfg) 110 | return cfg, err 111 | } 112 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package XGB provides the X Go Binding, which is a low-level API to communicate 3 | with the core X protocol and many of the X extensions. 4 | 5 | It is *very* closely modeled on XCB, so that experience with XCB (or xpyb) is 6 | easily translatable to XGB. That is, it uses the same cookie/reply model 7 | and is thread safe. There are otherwise no major differences (in the API). 8 | 9 | Most uses of XGB typically fall under the realm of window manager and GUI kit 10 | development, but other applications (like pagers, panels, tilers, etc.) may 11 | also require XGB. Moreover, it is a near certainty that if you need to work 12 | with X, xgbutil will be of great use to you as well: 13 | https://github.com/BurntSushi/xgbutil 14 | 15 | Example 16 | 17 | This is an extremely terse example that demonstrates how to connect to X, 18 | create a window, listen to StructureNotify events and Key{Press,Release} 19 | events, map the window, and print out all events received. An example with 20 | accompanying documentation can be found in examples/create-window. 21 | 22 | package main 23 | 24 | import ( 25 | "fmt" 26 | "github.com/BurntSushi/xgb" 27 | "github.com/BurntSushi/xgb/xproto" 28 | ) 29 | 30 | func main() { 31 | X, err := xgb.NewConn() 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | 37 | wid, _ := xproto.NewWindowId(X) 38 | screen := xproto.Setup(X).DefaultScreen(X) 39 | xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, 40 | 0, 0, 500, 500, 0, 41 | xproto.WindowClassInputOutput, screen.RootVisual, 42 | xproto.CwBackPixel | xproto.CwEventMask, 43 | []uint32{ // values must be in the order defined by the protocol 44 | 0xffffffff, 45 | xproto.EventMaskStructureNotify | 46 | xproto.EventMaskKeyPress | 47 | xproto.EventMaskKeyRelease}) 48 | 49 | xproto.MapWindow(X, wid) 50 | for { 51 | ev, xerr := X.WaitForEvent() 52 | if ev == nil && xerr == nil { 53 | fmt.Println("Both event and error are nil. Exiting...") 54 | return 55 | } 56 | 57 | if ev != nil { 58 | fmt.Printf("Event: %s\n", ev) 59 | } 60 | if xerr != nil { 61 | fmt.Printf("Error: %s\n", xerr) 62 | } 63 | } 64 | } 65 | 66 | Xinerama Example 67 | 68 | This is another small example that shows how to query Xinerama for geometry 69 | information of each active head. Accompanying documentation for this example 70 | can be found in examples/xinerama. 71 | 72 | package main 73 | 74 | import ( 75 | "fmt" 76 | "log" 77 | "github.com/BurntSushi/xgb" 78 | "github.com/BurntSushi/xgb/xinerama" 79 | ) 80 | 81 | func main() { 82 | X, err := xgb.NewConn() 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | 87 | // Initialize the Xinerama extension. 88 | // The appropriate 'Init' function must be run for *every* 89 | // extension before any of its requests can be used. 90 | err = xinerama.Init(X) 91 | if err != nil { 92 | log.Fatal(err) 93 | } 94 | 95 | reply, err := xinerama.QueryScreens(X).Reply() 96 | if err != nil { 97 | log.Fatal(err) 98 | } 99 | 100 | fmt.Printf("Number of heads: %d\n", reply.Number) 101 | for i, screen := range reply.ScreenInfo { 102 | fmt.Printf("%d :: X: %d, Y: %d, Width: %d, Height: %d\n", 103 | i, screen.XOrg, screen.YOrg, screen.Width, screen.Height) 104 | } 105 | } 106 | 107 | Parallelism 108 | 109 | XGB can benefit greatly from parallelism due to its concurrent design. For 110 | evidence of this claim, please see the benchmarks in xproto/xproto_test.go. 111 | 112 | Tests 113 | 114 | xproto/xproto_test.go contains a number of contrived tests that stress 115 | particular corners of XGB that I presume could be problem areas. Namely: 116 | requests with no replies, requests with replies, checked errors, unchecked 117 | errors, sequence number wrapping, cookie buffer flushing (i.e., forcing a round 118 | trip every N requests made that don't have a reply), getting/setting properties 119 | and creating a window and listening to StructureNotify events. 120 | 121 | Code Generator 122 | 123 | Both XCB and xpyb use the same Python module (xcbgen) for a code generator. XGB 124 | (before this fork) used the same code generator as well, but in my attempt to 125 | add support for more extensions, I found the code generator extremely difficult 126 | to work with. Therefore, I re-wrote the code generator in Go. It can be found 127 | in its own sub-package, xgbgen, of xgb. My design of xgbgen includes a rough 128 | consideration that it could be used for other languages. 129 | 130 | What works 131 | 132 | I am reasonably confident that the core X protocol is in full working form. I've 133 | also tested the Xinerama and RandR extensions sparingly. Many of the other 134 | existing extensions have Go source generated (and are compilable) and are 135 | included in this package, but I am currently unsure of their status. They 136 | *should* work. 137 | 138 | What does not work 139 | 140 | XKB is the only extension that intentionally does not work, although I suspect 141 | that GLX also does not work (however, there is Go source code for GLX that 142 | compiles, unlike XKB). I don't currently have any intention of getting XKB 143 | working, due to its complexity and my current mental incapacity to test it. 144 | 145 | */ 146 | package xgb 147 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/conn.go: -------------------------------------------------------------------------------- 1 | package xgb 2 | 3 | /* 4 | conn.go contains a couple of functions that do some real dirty work related 5 | to the initial connection handshake with X. 6 | 7 | This code is largely unmodified from the original XGB package that I forked. 8 | */ 9 | 10 | import ( 11 | "errors" 12 | "fmt" 13 | "io" 14 | "net" 15 | "os" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | // connect connects to the X server given in the 'display' string, 21 | // and does all the necessary setup handshaking. 22 | // If 'display' is empty it will be taken from os.Getenv("DISPLAY"). 23 | // Note that you should read and understand the "Connection Setup" of the 24 | // X Protocol Reference Manual before changing this function: 25 | // http://goo.gl/4zGQg 26 | func (c *Conn) connect(display string) error { 27 | err := c.dial(display) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | return c.postConnect() 33 | } 34 | 35 | // connect init from to the net.Conn, 36 | func (c *Conn) connectNet(netConn net.Conn) error { 37 | c.conn = netConn 38 | return c.postConnect() 39 | } 40 | 41 | // do the postConnect action after Conn get it's underly net.Conn 42 | func (c *Conn) postConnect() error { 43 | // Get authentication data 44 | authName, authData, err := readAuthority(c.host, c.display) 45 | noauth := false 46 | if err != nil { 47 | Logger.Printf("Could not get authority info: %v", err) 48 | Logger.Println("Trying connection without authority info...") 49 | authName = "" 50 | authData = []byte{} 51 | noauth = true 52 | } 53 | 54 | // Assume that the authentication protocol is "MIT-MAGIC-COOKIE-1". 55 | if !noauth && (authName != "MIT-MAGIC-COOKIE-1" || len(authData) != 16) { 56 | return errors.New("unsupported auth protocol " + authName) 57 | } 58 | 59 | buf := make([]byte, 12+Pad(len(authName))+Pad(len(authData))) 60 | buf[0] = 0x6c 61 | buf[1] = 0 62 | Put16(buf[2:], 11) 63 | Put16(buf[4:], 0) 64 | Put16(buf[6:], uint16(len(authName))) 65 | Put16(buf[8:], uint16(len(authData))) 66 | Put16(buf[10:], 0) 67 | copy(buf[12:], []byte(authName)) 68 | copy(buf[12+Pad(len(authName)):], authData) 69 | if _, err = c.conn.Write(buf); err != nil { 70 | return err 71 | } 72 | 73 | head := make([]byte, 8) 74 | if _, err = io.ReadFull(c.conn, head[0:8]); err != nil { 75 | return err 76 | } 77 | code := head[0] 78 | reasonLen := head[1] 79 | major := Get16(head[2:]) 80 | minor := Get16(head[4:]) 81 | dataLen := Get16(head[6:]) 82 | 83 | if major != 11 || minor != 0 { 84 | return fmt.Errorf("x protocol version mismatch: %d.%d", major, minor) 85 | } 86 | 87 | buf = make([]byte, int(dataLen)*4+8, int(dataLen)*4+8) 88 | copy(buf, head) 89 | if _, err = io.ReadFull(c.conn, buf[8:]); err != nil { 90 | return err 91 | } 92 | 93 | if code == 0 { 94 | reason := buf[8 : 8+reasonLen] 95 | return fmt.Errorf("x protocol authentication refused: %s", 96 | string(reason)) 97 | } 98 | 99 | // Unfortunately, it isn't really feasible to read the setup bytes here, 100 | // since the code to do so is in a different package. 101 | // Users must call 'xproto.Setup(X)' to get the setup info. 102 | c.SetupBytes = buf 103 | 104 | // But also read stuff that we *need* to get started. 105 | c.setupResourceIdBase = Get32(buf[12:]) 106 | c.setupResourceIdMask = Get32(buf[16:]) 107 | 108 | return nil 109 | } 110 | 111 | // dial initializes the actual net connection with X. 112 | func (c *Conn) dial(display string) error { 113 | if len(display) == 0 { 114 | display = os.Getenv("DISPLAY") 115 | } 116 | 117 | display0 := display 118 | if len(display) == 0 { 119 | return errors.New("empty display string") 120 | } 121 | 122 | colonIdx := strings.LastIndex(display, ":") 123 | if colonIdx < 0 { 124 | return errors.New("bad display string: " + display0) 125 | } 126 | 127 | var protocol, socket string 128 | 129 | if display[0] == '/' { 130 | socket = display[0:colonIdx] 131 | } else { 132 | slashIdx := strings.LastIndex(display, "/") 133 | if slashIdx >= 0 { 134 | protocol = display[0:slashIdx] 135 | c.host = display[slashIdx+1 : colonIdx] 136 | } else { 137 | c.host = display[0:colonIdx] 138 | } 139 | } 140 | 141 | display = display[colonIdx+1 : len(display)] 142 | if len(display) == 0 { 143 | return errors.New("bad display string: " + display0) 144 | } 145 | 146 | var scr string 147 | dotIdx := strings.LastIndex(display, ".") 148 | if dotIdx < 0 { 149 | c.display = display[0:] 150 | } else { 151 | c.display = display[0:dotIdx] 152 | scr = display[dotIdx+1:] 153 | } 154 | 155 | var err error 156 | c.DisplayNumber, err = strconv.Atoi(c.display) 157 | if err != nil || c.DisplayNumber < 0 { 158 | return errors.New("bad display string: " + display0) 159 | } 160 | 161 | if len(scr) != 0 { 162 | c.DefaultScreen, err = strconv.Atoi(scr) 163 | if err != nil { 164 | return errors.New("bad display string: " + display0) 165 | } 166 | } 167 | 168 | // Connect to server 169 | if len(socket) != 0 { 170 | c.conn, err = net.Dial("unix", socket+":"+c.display) 171 | } else if len(c.host) != 0 { 172 | if protocol == "" { 173 | protocol = "tcp" 174 | } 175 | c.conn, err = net.Dial(protocol, 176 | c.host+":"+strconv.Itoa(6000+c.DisplayNumber)) 177 | } else { 178 | c.conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+c.display) 179 | } 180 | 181 | if err != nil { 182 | return errors.New("cannot connect to " + display0 + ": " + err.Error()) 183 | } 184 | return nil 185 | } 186 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/socket.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "math/rand" 8 | "net" 9 | "os/exec" 10 | "strings" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | // If your computer takes more than 10s to restart i3, it must be seriously 16 | // overloaded, in which case we are probably doing you a favor by erroring out. 17 | const reconnectTimeout = 10 * time.Second 18 | 19 | // remote is a singleton containing the socket path and auto-detected byte order 20 | // which i3 is using. It is lazily initialized by getIPCSocket. 21 | var remote struct { 22 | path string 23 | order binary.ByteOrder 24 | mu sync.Mutex 25 | } 26 | 27 | // SocketPathHook Provides a way to override the default socket path lookup mechanism. Overriding this is unsupported. 28 | var SocketPathHook = func() (string, error) { 29 | out, err := exec.Command("i3", "--get-socketpath").CombinedOutput() 30 | if err != nil { 31 | return "", fmt.Errorf("getting i3 socketpath: %v (output: %s)", err, out) 32 | } 33 | return string(out), nil 34 | } 35 | 36 | func getIPCSocket(updateSocketPath bool) (*socket, net.Conn, error) { 37 | remote.mu.Lock() 38 | defer remote.mu.Unlock() 39 | path := remote.path 40 | if (!wasRestart && updateSocketPath) || remote.path == "" { 41 | out, err := SocketPathHook() 42 | if err != nil { 43 | return nil, nil, err 44 | } 45 | path = strings.TrimSpace(string(out)) 46 | } 47 | conn, err := net.Dial("unix", path) 48 | if err != nil { 49 | return nil, nil, err 50 | } 51 | remote.path = path 52 | if remote.order == nil { 53 | remote.order, err = detectByteOrder(conn) 54 | if err != nil { 55 | conn.Close() 56 | return nil, nil, err 57 | } 58 | } 59 | 60 | return &socket{conn: conn, order: remote.order}, conn, err 61 | } 62 | 63 | type messageType uint32 64 | 65 | const ( 66 | messageTypeRunCommand messageType = iota 67 | messageTypeGetWorkspaces 68 | messageTypeSubscribe 69 | messageTypeGetOutputs 70 | messageTypeGetTree 71 | messageTypeGetMarks 72 | messageTypeGetBarConfig 73 | messageTypeGetVersion 74 | messageTypeGetBindingModes 75 | messageTypeGetConfig 76 | messageTypeSendTick 77 | messageTypeSync 78 | ) 79 | 80 | var messageAtLeast = map[messageType]majorMinor{ 81 | messageTypeRunCommand: {4, 0}, 82 | messageTypeGetWorkspaces: {4, 0}, 83 | messageTypeSubscribe: {4, 0}, 84 | messageTypeGetOutputs: {4, 0}, 85 | messageTypeGetTree: {4, 0}, 86 | messageTypeGetMarks: {4, 1}, 87 | messageTypeGetBarConfig: {4, 1}, 88 | messageTypeGetVersion: {4, 3}, 89 | messageTypeGetBindingModes: {4, 13}, 90 | messageTypeGetConfig: {4, 14}, 91 | messageTypeSendTick: {4, 15}, 92 | messageTypeSync: {4, 16}, 93 | } 94 | 95 | const ( 96 | messageReplyTypeCommand messageType = iota 97 | messageReplyTypeWorkspaces 98 | messageReplyTypeSubscribe 99 | ) 100 | 101 | var magic = [6]byte{'i', '3', '-', 'i', 'p', 'c'} 102 | 103 | type header struct { 104 | Magic [6]byte 105 | Length uint32 106 | Type messageType 107 | } 108 | 109 | type message struct { 110 | Type messageType 111 | Payload []byte 112 | } 113 | 114 | type socket struct { 115 | conn io.ReadWriter 116 | order binary.ByteOrder 117 | } 118 | 119 | func (s *socket) recvMsg() (message, error) { 120 | if s == nil { 121 | return message{}, fmt.Errorf("not connected") 122 | } 123 | var h header 124 | if err := binary.Read(s.conn, s.order, &h); err != nil { 125 | return message{}, err 126 | } 127 | msg := message{ 128 | Type: h.Type, 129 | Payload: make([]byte, h.Length), 130 | } 131 | _, err := io.ReadFull(s.conn, msg.Payload) 132 | return msg, err 133 | } 134 | 135 | func (s *socket) roundTrip(t messageType, payload []byte) (message, error) { 136 | if s == nil { 137 | return message{}, fmt.Errorf("not connected") 138 | } 139 | 140 | if err := binary.Write(s.conn, s.order, &header{magic, uint32(len(payload)), t}); err != nil { 141 | return message{}, err 142 | } 143 | if len(payload) > 0 { // skip empty Write()s for net.Pipe 144 | _, err := s.conn.Write(payload) 145 | if err != nil { 146 | return message{}, err 147 | } 148 | } 149 | return s.recvMsg() 150 | } 151 | 152 | // defaultSock is a singleton, lazily initialized by roundTrip. All 153 | // request/response messages are sent to i3 via this socket, whereas 154 | // subscriptions use their own connection. 155 | var defaultSock struct { 156 | sock *socket 157 | conn net.Conn 158 | mu sync.Mutex 159 | } 160 | 161 | // roundTrip sends a message to i3 and returns the received result in a 162 | // concurrency-safe fashion. 163 | func roundTrip(t messageType, payload []byte) (message, error) { 164 | // Error out early in case the message type is not yet supported by the 165 | // running i3 version. 166 | if t != messageTypeGetVersion { 167 | if err := AtLeast(messageAtLeast[t].major, messageAtLeast[t].minor); err != nil { 168 | return message{}, err 169 | } 170 | } 171 | 172 | defaultSock.mu.Lock() 173 | defer defaultSock.mu.Unlock() 174 | 175 | Outer: 176 | for { 177 | msg, err := defaultSock.sock.roundTrip(t, payload) 178 | if err == nil { 179 | return msg, nil // happy path: success 180 | } 181 | 182 | // reconnect 183 | start := time.Now() 184 | for time.Since(start) < reconnectTimeout && (defaultSock.sock == nil || i3Running()) { 185 | if defaultSock.sock != nil { 186 | defaultSock.conn.Close() 187 | } 188 | defaultSock.sock, defaultSock.conn, err = getIPCSocket(defaultSock.sock != nil) 189 | if err == nil { 190 | continue Outer 191 | } 192 | 193 | // Reconnect within [10, 20) ms to prevent CPU-starving i3. 194 | time.Sleep(time.Duration(10+rand.Int63n(10)) * time.Millisecond) 195 | } 196 | return msg, err 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/tree.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // NodeType indicates the specific kind of Node. 8 | type NodeType string 9 | 10 | // i3 currently implements the following node types: 11 | const ( 12 | Root NodeType = "root" 13 | OutputNode NodeType = "output" 14 | Con NodeType = "con" 15 | FloatingCon NodeType = "floating_con" 16 | WorkspaceNode NodeType = "workspace" 17 | DockareaNode NodeType = "dockarea" 18 | ) 19 | 20 | // Layout indicates the layout of a Node. 21 | type Layout string 22 | 23 | // i3 currently implements the following layouts: 24 | const ( 25 | SplitH Layout = "splith" 26 | SplitV Layout = "splitv" 27 | Stacked Layout = "stacked" 28 | Tabbed Layout = "tabbed" 29 | DockareaLayout Layout = "dockarea" 30 | OutputLayout Layout = "output" 31 | ) 32 | 33 | // BorderStyle indicates the border style of a node. 34 | type BorderStyle string 35 | 36 | // i3 currently implements the following border styles: 37 | const ( 38 | NormalBorder BorderStyle = "normal" 39 | NoBorder BorderStyle = "none" 40 | PixelBorder BorderStyle = "pixel" 41 | ) 42 | 43 | // Rect is a rectangle, used for various dimensions in Node, for example. 44 | type Rect struct { 45 | X int64 `json:"x"` 46 | Y int64 `json:"y"` 47 | Width int64 `json:"width"` 48 | Height int64 `json:"height"` 49 | } 50 | 51 | // WindowProperties correspond to X11 window properties 52 | // 53 | // See https://build.i3wm.org/docs/ipc.html#_tree_reply 54 | type WindowProperties struct { 55 | Title string `json:"title"` 56 | Instance string `json:"instance"` 57 | Class string `json:"class"` 58 | Role string `json:"window_role"` 59 | Transient NodeID `json:"transient_for"` 60 | } 61 | 62 | // NodeID is an i3-internal ID for the node, which can be used to identify 63 | // containers within the IPC interface. 64 | type NodeID int64 65 | 66 | // FullscreenMode indicates whether the container is fullscreened, and relative 67 | // to where (its output, or globally). Note that all workspaces are considered 68 | // fullscreened on their respective output. 69 | type FullscreenMode int64 70 | 71 | const ( 72 | FullscreenNone FullscreenMode = 0 73 | FullscreenOutput FullscreenMode = 1 74 | FullscreenGlobal FullscreenMode = 2 75 | ) 76 | 77 | // Node is a node in a Tree. 78 | // 79 | // See https://i3wm.org/docs/ipc.html#_tree_reply for more details. 80 | type Node struct { 81 | ID NodeID `json:"id"` 82 | Name string `json:"name"` // window: title, container: internal name 83 | Type NodeType `json:"type"` 84 | Border BorderStyle `json:"border"` 85 | CurrentBorderWidth int64 `json:"current_border_width"` 86 | Layout Layout `json:"layout"` 87 | Percent float64 `json:"percent"` 88 | Rect Rect `json:"rect"` // absolute (= relative to X11 display) 89 | WindowRect Rect `json:"window_rect"` // window, relative to Rect 90 | DecoRect Rect `json:"deco_rect"` // decoration, relative to Rect 91 | Geometry Rect `json:"geometry"` // original window geometry, absolute 92 | Window int64 `json:"window"` // X11 window ID of the client window 93 | WindowProperties WindowProperties `json:"window_properties"` 94 | Urgent bool `json:"urgent"` // urgency hint set 95 | Marks []string `json:"marks"` 96 | Focused bool `json:"focused"` 97 | WindowType string `json:"window_type"` 98 | FullscreenMode FullscreenMode `json:"fullscreen_mode"` 99 | Focus []NodeID `json:"focus"` 100 | Nodes []*Node `json:"nodes"` 101 | FloatingNodes []*Node `json:"floating_nodes"` 102 | } 103 | 104 | // FindChild returns the first Node matching predicate, using pre-order 105 | // depth-first search. 106 | func (n *Node) FindChild(predicate func(*Node) bool) *Node { 107 | if predicate(n) { 108 | return n 109 | } 110 | for _, c := range n.Nodes { 111 | if con := c.FindChild(predicate); con != nil { 112 | return con 113 | } 114 | } 115 | for _, c := range n.FloatingNodes { 116 | if con := c.FindChild(predicate); con != nil { 117 | return con 118 | } 119 | } 120 | return nil 121 | } 122 | 123 | // FindFocused returns the first Node matching predicate from the sub-tree of 124 | // directly and indirectly focused containers. 125 | // 126 | // As an example, consider this layout tree (simplified): 127 | // 128 | // root 129 | // │ 130 | // HDMI2 131 | // ╱ ╲ 132 | // … workspace 1 133 | // ╱ ╲ 134 | // XTerm Firefox 135 | // 136 | // In this example, if Firefox is focused, FindFocused will return the first 137 | // container matching predicate of root, HDMI2, workspace 1, Firefox (in this 138 | // order). 139 | func (n *Node) FindFocused(predicate func(*Node) bool) *Node { 140 | if predicate(n) { 141 | return n 142 | } 143 | if len(n.Focus) == 0 { 144 | return nil 145 | } 146 | first := n.Focus[0] 147 | for _, c := range n.Nodes { 148 | if c.ID == first { 149 | return c.FindFocused(predicate) 150 | } 151 | } 152 | for _, c := range n.FloatingNodes { 153 | if c.ID == first { 154 | return c.FindFocused(predicate) 155 | } 156 | } 157 | return nil 158 | } 159 | 160 | // Tree is an i3 layout tree, starting with Root. 161 | type Tree struct { 162 | // Root is the root node of the layout tree. 163 | Root *Node 164 | } 165 | 166 | // GetTree returns i3’s layout tree. 167 | // 168 | // GetTree is supported in i3 ≥ v4.0 (2011-07-31). 169 | func GetTree() (Tree, error) { 170 | reply, err := roundTrip(messageTypeGetTree, nil) 171 | if err != nil { 172 | return Tree{}, err 173 | } 174 | 175 | var root Node 176 | err = json.Unmarshal(reply.Payload, &root) 177 | return Tree{Root: &root}, err 178 | } 179 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/cookie.go: -------------------------------------------------------------------------------- 1 | package xgb 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // Cookie is the internal representation of a cookie, where one is generated 8 | // for *every* request sent by XGB. 9 | // 'cookie' is most frequently used by embedding it into a more specific 10 | // kind of cookie, i.e., 'GetInputFocusCookie'. 11 | type Cookie struct { 12 | conn *Conn 13 | Sequence uint16 14 | replyChan chan []byte 15 | errorChan chan error 16 | pingChan chan bool 17 | } 18 | 19 | // NewCookie creates a new cookie with the correct channels initialized 20 | // depending upon the values of 'checked' and 'reply'. Together, there are 21 | // four different kinds of cookies. (See more detailed comments in the 22 | // function for more info on those.) 23 | // Note that a sequence number is not set until just before the request 24 | // corresponding to this cookie is sent over the wire. 25 | // 26 | // Unless you're building requests from bytes by hand, this method should 27 | // not be used. 28 | func (c *Conn) NewCookie(checked, reply bool) *Cookie { 29 | cookie := &Cookie{ 30 | conn: c, 31 | Sequence: 0, // we add the sequence id just before sending a request 32 | replyChan: nil, 33 | errorChan: nil, 34 | pingChan: nil, 35 | } 36 | 37 | // There are four different kinds of cookies: 38 | // Checked requests with replies get a reply channel and an error channel. 39 | // Unchecked requests with replies get a reply channel and a ping channel. 40 | // Checked requests w/o replies get a ping channel and an error channel. 41 | // Unchecked requests w/o replies get no channels. 42 | // The reply channel is used to send reply data. 43 | // The error channel is used to send error data. 44 | // The ping channel is used when one of the 'reply' or 'error' channels 45 | // is missing but the other is present. The ping channel is way to force 46 | // the blocking to stop and basically say "the error has been received 47 | // in the main event loop" (when the ping channel is coupled with a reply 48 | // channel) or "the request you made that has no reply was successful" 49 | // (when the ping channel is coupled with an error channel). 50 | if checked { 51 | cookie.errorChan = make(chan error, 1) 52 | if !reply { 53 | cookie.pingChan = make(chan bool, 1) 54 | } 55 | } 56 | if reply { 57 | cookie.replyChan = make(chan []byte, 1) 58 | if !checked { 59 | cookie.pingChan = make(chan bool, 1) 60 | } 61 | } 62 | 63 | return cookie 64 | } 65 | 66 | // Reply detects whether this is a checked or unchecked cookie, and calls 67 | // 'replyChecked' or 'replyUnchecked' appropriately. 68 | // 69 | // Unless you're building requests from bytes by hand, this method should 70 | // not be used. 71 | func (c Cookie) Reply() ([]byte, error) { 72 | // checked 73 | if c.errorChan != nil { 74 | return c.replyChecked() 75 | } 76 | return c.replyUnchecked() 77 | } 78 | 79 | // replyChecked waits for a response on either the replyChan or errorChan 80 | // channels. If the former arrives, the bytes are returned with a nil error. 81 | // If the latter arrives, no bytes are returned (nil) and the error received 82 | // is returned. 83 | // 84 | // Unless you're building requests from bytes by hand, this method should 85 | // not be used. 86 | func (c Cookie) replyChecked() ([]byte, error) { 87 | if c.replyChan == nil { 88 | return nil, errors.New("Cannot call 'replyChecked' on a cookie that " + 89 | "is not expecting a *reply* or an error.") 90 | } 91 | if c.errorChan == nil { 92 | return nil, errors.New("Cannot call 'replyChecked' on a cookie that " + 93 | "is not expecting a reply or an *error*.") 94 | } 95 | 96 | select { 97 | case reply := <-c.replyChan: 98 | return reply, nil 99 | case err := <-c.errorChan: 100 | return nil, err 101 | } 102 | } 103 | 104 | // replyUnchecked waits for a response on either the replyChan or pingChan 105 | // channels. If the former arrives, the bytes are returned with a nil error. 106 | // If the latter arrives, no bytes are returned (nil) and a nil error 107 | // is returned. (In the latter case, the corresponding error can be retrieved 108 | // from (Wait|Poll)ForEvent asynchronously.) 109 | // In all honesty, you *probably* don't want to use this method. 110 | // 111 | // Unless you're building requests from bytes by hand, this method should 112 | // not be used. 113 | func (c Cookie) replyUnchecked() ([]byte, error) { 114 | if c.replyChan == nil { 115 | return nil, errors.New("Cannot call 'replyUnchecked' on a cookie " + 116 | "that is not expecting a *reply*.") 117 | } 118 | 119 | select { 120 | case reply := <-c.replyChan: 121 | return reply, nil 122 | case <-c.pingChan: 123 | return nil, nil 124 | } 125 | } 126 | 127 | // Check is used for checked requests that have no replies. It is a mechanism 128 | // by which to report "success" or "error" in a synchronous fashion. (Therefore, 129 | // unchecked requests without replies cannot use this method.) 130 | // If the request causes an error, it is sent to this cookie's errorChan. 131 | // If the request was successful, there is no response from the server. 132 | // Thus, pingChan is sent a value when the *next* reply is read. 133 | // If no more replies are being processed, we force a round trip request with 134 | // GetInputFocus. 135 | // 136 | // Unless you're building requests from bytes by hand, this method should 137 | // not be used. 138 | func (c Cookie) Check() error { 139 | if c.replyChan != nil { 140 | return errors.New("Cannot call 'Check' on a cookie that is " + 141 | "expecting a *reply*. Use 'Reply' instead.") 142 | } 143 | if c.errorChan == nil { 144 | return errors.New("Cannot call 'Check' on a cookie that is " + 145 | "not expecting a possible *error*.") 146 | } 147 | 148 | // First do a quick non-blocking check to see if we've been pinged. 149 | select { 150 | case err := <-c.errorChan: 151 | return err 152 | case <-c.pingChan: 153 | return nil 154 | default: 155 | } 156 | 157 | // Now force a round trip and try again, but block this time. 158 | c.conn.Sync() 159 | select { 160 | case err := <-c.errorChan: 161 | return err 162 | case <-c.pingChan: 163 | return nil 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/types.go: -------------------------------------------------------------------------------- 1 | package xgbutil 2 | 3 | /* 4 | types.go contains several types used in the XUtil structure. In an ideal world, 5 | they would be defined in their appropriate packages, but must be defined here 6 | (and exported) for use in some sub-packages. (Namely, xevent, keybind and 7 | mousebind.) 8 | */ 9 | 10 | import ( 11 | "github.com/BurntSushi/xgb" 12 | "github.com/BurntSushi/xgb/xproto" 13 | ) 14 | 15 | // Callback is an interface that should be implemented by event callback 16 | // functions. Namely, to assign a function to a particular event/window 17 | // combination, simply define a function with type 'SomeEventFun' (pre-defined 18 | // in xevent/callback.go), and call the 'Connect' method. 19 | // The 'Run' method is used inside the Main event loop, and shouldn't be used 20 | // by the user. 21 | // Also, it is perfectly legitimate to connect to events that don't specify 22 | // a window (like MappingNotify and KeymapNotify). In this case, simply 23 | // use 'xgbutil.NoWindow' as the window id. 24 | // 25 | // Example to respond to ConfigureNotify events on window 0x1 26 | // 27 | // xevent.ConfigureNotifyFun( 28 | // func(X *xgbutil.XUtil, e xevent.ConfigureNotifyEvent) { 29 | // fmt.Printf("(%d, %d) %dx%d\n", e.X, e.Y, e.Width, e.Height) 30 | // }).Connect(X, 0x1) 31 | type Callback interface { 32 | // Connect modifies XUtil's state to attach an event handler to a 33 | // particular event. 34 | Connect(xu *XUtil, win xproto.Window) 35 | 36 | // Run is exported for use in the xevent package but should not be 37 | // used by the user. (It is used to run the callback function in the 38 | // main event loop.) 39 | Run(xu *XUtil, ev interface{}) 40 | } 41 | 42 | // CallbackHook works similarly to the more general Callback, but it is 43 | // for hooks into the main xevent loop. As such it does not get attached 44 | // to a window. 45 | type CallbackHook interface { 46 | // Connect connects this hook to the main loop of the passed XUtil 47 | // instance. 48 | Connect(xu *XUtil) 49 | 50 | // Run is exported for use in the xevent package, but should not be 51 | // used by the user. It should return true if it's ok to process 52 | // the event as usual, or false if it should be suppressed. 53 | Run(xu *XUtil, ev interface{}) bool 54 | } 55 | 56 | // CallbackKey works similarly to the more general Callback, but it adds 57 | // parameters specific to key bindings. 58 | type CallbackKey interface { 59 | // Connect modifies XUtil's state to attach an event handler to a 60 | // particular key press. If grab is true, connect will request a passive 61 | // grab. 62 | Connect(xu *XUtil, win xproto.Window, keyStr string, grab bool) error 63 | 64 | // Run is exported for use in the keybind package but should not be 65 | // used by the user. (It is used to run the callback function in the 66 | // main event loop. 67 | Run(xu *XUtil, ev interface{}) 68 | } 69 | 70 | // CallbackMouse works similarly to the more general Callback, but it adds 71 | // parameters specific to mouse bindings. 72 | type CallbackMouse interface { 73 | // Connect modifies XUtil's state to attach an event handler to a 74 | // particular button press. 75 | // If sync is true, the grab will be synchronous. (This will require a 76 | // call to xproto.AllowEvents in response, otherwise no further events 77 | // will be processed and your program will lock.) 78 | // If grab is true, connect will request a passive grab. 79 | Connect(xu *XUtil, win xproto.Window, buttonStr string, 80 | sync bool, grab bool) error 81 | 82 | // Run is exported for use in the mousebind package but should not be 83 | // used by the user. (It is used to run the callback function in the 84 | // main event loop.) 85 | Run(xu *XUtil, ev interface{}) 86 | } 87 | 88 | // KeyKey is the type of the key in the map of keybindings. 89 | // It essentially represents the tuple 90 | // (event type, window id, modifier, keycode). 91 | // It is exported for use in the keybind package. It should not be used. 92 | type KeyKey struct { 93 | Evtype int 94 | Win xproto.Window 95 | Mod uint16 96 | Code xproto.Keycode 97 | } 98 | 99 | // KeyString is the type of a key binding string used to connect to particular 100 | // key combinations. A list of all such key strings is maintained in order to 101 | // rebind keys when the keyboard mapping has been changed. 102 | type KeyString struct { 103 | Str string 104 | Callback CallbackKey 105 | Evtype int 106 | Win xproto.Window 107 | Grab bool 108 | } 109 | 110 | // MouseKey is the type of the key in the map of mouse bindings. 111 | // It essentially represents the tuple 112 | // (event type, window id, modifier, button). 113 | // It is exported for use in the mousebind package. It should not be used. 114 | type MouseKey struct { 115 | Evtype int 116 | Win xproto.Window 117 | Mod uint16 118 | Button xproto.Button 119 | } 120 | 121 | // KeyboardMapping embeds a keyboard mapping reply from XGB. 122 | // It should be retrieved using keybind.KeyMapGet, if necessary. 123 | // xgbutil tries quite hard to absolve you from ever having to use this. 124 | // A keyboard mapping is a table that maps keycodes to one or more keysyms. 125 | type KeyboardMapping struct { 126 | *xproto.GetKeyboardMappingReply 127 | } 128 | 129 | // ModifierMapping embeds a modifier mapping reply from XGB. 130 | // It should be retrieved using keybind.ModMapGet, if necessary. 131 | // xgbutil tries quite hard to absolve you from ever having to use this. 132 | // A modifier mapping is a table that maps modifiers to one or more keycodes. 133 | type ModifierMapping struct { 134 | *xproto.GetModifierMappingReply 135 | } 136 | 137 | // ErrorHandlerFun is the type of function required to handle errors that 138 | // come in through the main event loop. 139 | // For example, to set a new error handler, use: 140 | // 141 | // xevent.ErrorHandlerSet(xgbutil.ErrorHandlerFun( 142 | // func(err xgb.Error) { 143 | // // do something with err 144 | // })) 145 | type ErrorHandlerFun func(err xgb.Error) 146 | 147 | // EventOrError is a struct that contains either an event value or an error 148 | // value. It is an error to contain both. Containing neither indicates an 149 | // error too. 150 | // This is exported for use in the xevent package. You shouldn't have any 151 | // direct contact with values of this type, unless you need to inspect the 152 | // queue directly with xevent.Peek. 153 | type EventOrError struct { 154 | Event xgb.Event 155 | Err xgb.Error 156 | } 157 | 158 | // MouseDragFun is the kind of function used on each dragging step 159 | // and at the end of a drag. 160 | type MouseDragFun func(xu *XUtil, rootX, rootY, eventX, eventY int) 161 | 162 | // MouseDragBeginFun is the kind of function used to initialize a drag. 163 | // The difference between this and MouseDragFun is that the begin function 164 | // returns a bool (of whether or not to cancel the drag) and an X resource 165 | // identifier corresponding to a cursor. 166 | type MouseDragBeginFun func(xu *XUtil, rootX, rootY, 167 | eventX, eventY int) (bool, xproto.Cursor) 168 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/yamlprivateh.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | const ( 26 | // The size of the input raw buffer. 27 | input_raw_buffer_size = 512 28 | 29 | // The size of the input buffer. 30 | // It should be possible to decode the whole raw buffer. 31 | input_buffer_size = input_raw_buffer_size * 3 32 | 33 | // The size of the output buffer. 34 | output_buffer_size = 128 35 | 36 | // The size of the output raw buffer. 37 | // It should be possible to encode the whole output buffer. 38 | output_raw_buffer_size = (output_buffer_size*2 + 2) 39 | 40 | // The size of other stacks and queues. 41 | initial_stack_size = 16 42 | initial_queue_size = 16 43 | initial_string_size = 16 44 | ) 45 | 46 | // Check if the character at the specified position is an alphabetical 47 | // character, a digit, '_', or '-'. 48 | func is_alpha(b []byte, i int) bool { 49 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' 50 | } 51 | 52 | // Check if the character at the specified position is a digit. 53 | func is_digit(b []byte, i int) bool { 54 | return b[i] >= '0' && b[i] <= '9' 55 | } 56 | 57 | // Get the value of a digit. 58 | func as_digit(b []byte, i int) int { 59 | return int(b[i]) - '0' 60 | } 61 | 62 | // Check if the character at the specified position is a hex-digit. 63 | func is_hex(b []byte, i int) bool { 64 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' 65 | } 66 | 67 | // Get the value of a hex-digit. 68 | func as_hex(b []byte, i int) int { 69 | bi := b[i] 70 | if bi >= 'A' && bi <= 'F' { 71 | return int(bi) - 'A' + 10 72 | } 73 | if bi >= 'a' && bi <= 'f' { 74 | return int(bi) - 'a' + 10 75 | } 76 | return int(bi) - '0' 77 | } 78 | 79 | // Check if the character is ASCII. 80 | func is_ascii(b []byte, i int) bool { 81 | return b[i] <= 0x7F 82 | } 83 | 84 | // Check if the character at the start of the buffer can be printed unescaped. 85 | func is_printable(b []byte, i int) bool { 86 | return ((b[i] == 0x0A) || // . == #x0A 87 | (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E 88 | (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF 89 | (b[i] > 0xC2 && b[i] < 0xED) || 90 | (b[i] == 0xED && b[i+1] < 0xA0) || 91 | (b[i] == 0xEE) || 92 | (b[i] == 0xEF && // #xE000 <= . <= #xFFFD 93 | !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF 94 | !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) 95 | } 96 | 97 | // Check if the character at the specified position is NUL. 98 | func is_z(b []byte, i int) bool { 99 | return b[i] == 0x00 100 | } 101 | 102 | // Check if the beginning of the buffer is a BOM. 103 | func is_bom(b []byte, i int) bool { 104 | return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF 105 | } 106 | 107 | // Check if the character at the specified position is space. 108 | func is_space(b []byte, i int) bool { 109 | return b[i] == ' ' 110 | } 111 | 112 | // Check if the character at the specified position is tab. 113 | func is_tab(b []byte, i int) bool { 114 | return b[i] == '\t' 115 | } 116 | 117 | // Check if the character at the specified position is blank (space or tab). 118 | func is_blank(b []byte, i int) bool { 119 | //return is_space(b, i) || is_tab(b, i) 120 | return b[i] == ' ' || b[i] == '\t' 121 | } 122 | 123 | // Check if the character at the specified position is a line break. 124 | func is_break(b []byte, i int) bool { 125 | return (b[i] == '\r' || // CR (#xD) 126 | b[i] == '\n' || // LF (#xA) 127 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 128 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 129 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) 130 | } 131 | 132 | func is_crlf(b []byte, i int) bool { 133 | return b[i] == '\r' && b[i+1] == '\n' 134 | } 135 | 136 | // Check if the character is a line break or NUL. 137 | func is_breakz(b []byte, i int) bool { 138 | //return is_break(b, i) || is_z(b, i) 139 | return ( 140 | // is_break: 141 | b[i] == '\r' || // CR (#xD) 142 | b[i] == '\n' || // LF (#xA) 143 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 144 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 145 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 146 | // is_z: 147 | b[i] == 0) 148 | } 149 | 150 | // Check if the character is a line break, space, or NUL. 151 | func is_spacez(b []byte, i int) bool { 152 | //return is_space(b, i) || is_breakz(b, i) 153 | return ( 154 | // is_space: 155 | b[i] == ' ' || 156 | // is_breakz: 157 | b[i] == '\r' || // CR (#xD) 158 | b[i] == '\n' || // LF (#xA) 159 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 160 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 161 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 162 | b[i] == 0) 163 | } 164 | 165 | // Check if the character is a line break, space, tab, or NUL. 166 | func is_blankz(b []byte, i int) bool { 167 | //return is_blank(b, i) || is_breakz(b, i) 168 | return ( 169 | // is_blank: 170 | b[i] == ' ' || b[i] == '\t' || 171 | // is_breakz: 172 | b[i] == '\r' || // CR (#xD) 173 | b[i] == '\n' || // LF (#xA) 174 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 175 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 176 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 177 | b[i] == 0) 178 | } 179 | 180 | // Determine the width of the character. 181 | func width(b byte) int { 182 | // Don't replace these by a switch without first 183 | // confirming that it is being inlined. 184 | if b&0x80 == 0x00 { 185 | return 1 186 | } 187 | if b&0xE0 == 0xC0 { 188 | return 2 189 | } 190 | if b&0xF0 == 0xE0 { 191 | return 3 192 | } 193 | if b&0xF8 == 0xF0 { 194 | return 4 195 | } 196 | return 0 197 | 198 | } 199 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/xprop/xprop.go: -------------------------------------------------------------------------------- 1 | package xprop 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/BurntSushi/xgb" 7 | "github.com/BurntSushi/xgb/xproto" 8 | 9 | "github.com/BurntSushi/xgbutil" 10 | ) 11 | 12 | // GetProperty abstracts the messiness of calling xgb.GetProperty. 13 | func GetProperty(xu *xgbutil.XUtil, win xproto.Window, atom string) ( 14 | *xproto.GetPropertyReply, error) { 15 | 16 | atomId, err := Atm(xu, atom) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | reply, err := xproto.GetProperty(xu.Conn(), false, win, atomId, 22 | xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply() 23 | 24 | if err != nil { 25 | return nil, fmt.Errorf("GetProperty: Error retrieving property '%s' "+ 26 | "on window %x: %s", atom, win, err) 27 | } 28 | 29 | if reply.Format == 0 { 30 | return nil, fmt.Errorf("GetProperty: No such property '%s' on "+ 31 | "window %x.", atom, win) 32 | } 33 | 34 | return reply, nil 35 | } 36 | 37 | // ChangeProperty abstracts the semi-nastiness of xgb.ChangeProperty. 38 | func ChangeProp(xu *xgbutil.XUtil, win xproto.Window, format byte, prop string, 39 | typ string, data []byte) error { 40 | 41 | propAtom, err := Atm(xu, prop) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | typAtom, err := Atm(xu, typ) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | return xproto.ChangePropertyChecked(xu.Conn(), xproto.PropModeReplace, win, 52 | propAtom, typAtom, format, 53 | uint32(len(data)/(int(format)/8)), data).Check() 54 | } 55 | 56 | // ChangeProperty32 makes changing 32 bit formatted properties easier 57 | // by constructing the raw X data for you. 58 | func ChangeProp32(xu *xgbutil.XUtil, win xproto.Window, prop string, typ string, 59 | data ...uint) error { 60 | 61 | buf := make([]byte, len(data)*4) 62 | for i, datum := range data { 63 | xgb.Put32(buf[(i*4):], uint32(datum)) 64 | } 65 | 66 | return ChangeProp(xu, win, 32, prop, typ, buf) 67 | } 68 | 69 | // WindowToUint is a covenience function for converting []xproto.Window 70 | // to []uint. 71 | func WindowToInt(ids []xproto.Window) []uint { 72 | ids32 := make([]uint, len(ids)) 73 | for i, v := range ids { 74 | ids32[i] = uint(v) 75 | } 76 | return ids32 77 | } 78 | 79 | // AtomToInt is a covenience function for converting []xproto.Atom 80 | // to []uint. 81 | func AtomToUint(ids []xproto.Atom) []uint { 82 | ids32 := make([]uint, len(ids)) 83 | for i, v := range ids { 84 | ids32[i] = uint(v) 85 | } 86 | return ids32 87 | } 88 | 89 | // StrToAtoms is a convenience function for converting 90 | // []string to []uint32 atoms. 91 | // NOTE: If an atom name in the list doesn't exist, it will be created. 92 | func StrToAtoms(xu *xgbutil.XUtil, atomNames []string) ([]uint, error) { 93 | var err error 94 | atoms := make([]uint, len(atomNames)) 95 | for i, atomName := range atomNames { 96 | a, err := Atom(xu, atomName, false) 97 | if err != nil { 98 | return nil, err 99 | } 100 | atoms[i] = uint(a) 101 | } 102 | return atoms, err 103 | } 104 | 105 | // PropValAtom transforms a GetPropertyReply struct into an ATOM name. 106 | // The property reply must be in 32 bit format. 107 | func PropValAtom(xu *xgbutil.XUtil, reply *xproto.GetPropertyReply, 108 | err error) (string, error) { 109 | 110 | if err != nil { 111 | return "", err 112 | } 113 | if reply.Format != 32 { 114 | return "", fmt.Errorf("PropValAtom: Expected format 32 but got %d", 115 | reply.Format) 116 | } 117 | 118 | return AtomName(xu, xproto.Atom(xgb.Get32(reply.Value))) 119 | } 120 | 121 | // PropValAtoms is the same as PropValAtom, except that it returns a slice 122 | // of atom names. Also must be 32 bit format. 123 | // This is a method of an XUtil struct, unlike the other 'PropVal...' functions. 124 | func PropValAtoms(xu *xgbutil.XUtil, reply *xproto.GetPropertyReply, 125 | err error) ([]string, error) { 126 | 127 | if err != nil { 128 | return nil, err 129 | } 130 | if reply.Format != 32 { 131 | return nil, fmt.Errorf("PropValAtoms: Expected format 32 but got %d", 132 | reply.Format) 133 | } 134 | 135 | ids := make([]string, reply.ValueLen) 136 | vals := reply.Value 137 | for i := 0; len(vals) >= 4; i++ { 138 | ids[i], err = AtomName(xu, xproto.Atom(xgb.Get32(vals))) 139 | if err != nil { 140 | return nil, err 141 | } 142 | 143 | vals = vals[4:] 144 | } 145 | return ids, nil 146 | } 147 | 148 | // PropValWindow transforms a GetPropertyReply struct into an X resource 149 | // window identifier. 150 | // The property reply must be in 32 bit format. 151 | func PropValWindow(reply *xproto.GetPropertyReply, 152 | err error) (xproto.Window, error) { 153 | 154 | if err != nil { 155 | return 0, err 156 | } 157 | if reply.Format != 32 { 158 | return 0, fmt.Errorf("PropValId: Expected format 32 but got %d", 159 | reply.Format) 160 | } 161 | return xproto.Window(xgb.Get32(reply.Value)), nil 162 | } 163 | 164 | // PropValWindows is the same as PropValWindow, except that it returns a slice 165 | // of identifiers. Also must be 32 bit format. 166 | func PropValWindows(reply *xproto.GetPropertyReply, 167 | err error) ([]xproto.Window, error) { 168 | 169 | if err != nil { 170 | return nil, err 171 | } 172 | if reply.Format != 32 { 173 | return nil, fmt.Errorf("PropValIds: Expected format 32 but got %d", 174 | reply.Format) 175 | } 176 | 177 | ids := make([]xproto.Window, reply.ValueLen) 178 | vals := reply.Value 179 | for i := 0; len(vals) >= 4; i++ { 180 | ids[i] = xproto.Window(xgb.Get32(vals)) 181 | vals = vals[4:] 182 | } 183 | return ids, nil 184 | } 185 | 186 | // PropValNum transforms a GetPropertyReply struct into an unsigned 187 | // integer. Useful when the property value is a single integer. 188 | func PropValNum(reply *xproto.GetPropertyReply, err error) (uint, error) { 189 | if err != nil { 190 | return 0, err 191 | } 192 | if reply.Format != 32 { 193 | return 0, fmt.Errorf("PropValNum: Expected format 32 but got %d", 194 | reply.Format) 195 | } 196 | return uint(xgb.Get32(reply.Value)), nil 197 | } 198 | 199 | // PropValNums is the same as PropValNum, except that it returns a slice 200 | // of integers. Also must be 32 bit format. 201 | func PropValNums(reply *xproto.GetPropertyReply, err error) ([]uint, error) { 202 | if err != nil { 203 | return nil, err 204 | } 205 | if reply.Format != 32 { 206 | return nil, fmt.Errorf("PropValIds: Expected format 32 but got %d", 207 | reply.Format) 208 | } 209 | 210 | nums := make([]uint, reply.ValueLen) 211 | vals := reply.Value 212 | for i := 0; len(vals) >= 4; i++ { 213 | nums[i] = uint(xgb.Get32(vals)) 214 | vals = vals[4:] 215 | } 216 | return nums, nil 217 | } 218 | 219 | // PropValNum64 transforms a GetPropertyReply struct into a 64 bit 220 | // integer. Useful when the property value is a single integer. 221 | func PropValNum64(reply *xproto.GetPropertyReply, err error) (int64, error) { 222 | if err != nil { 223 | return 0, err 224 | } 225 | if reply.Format != 32 { 226 | return 0, fmt.Errorf("PropValNum: Expected format 32 but got %d", 227 | reply.Format) 228 | } 229 | return int64(xgb.Get32(reply.Value)), nil 230 | } 231 | 232 | // PropValStr transforms a GetPropertyReply struct into a string. 233 | // Useful when the property value is a null terminated string represented 234 | // by integers. Also must be 8 bit format. 235 | func PropValStr(reply *xproto.GetPropertyReply, err error) (string, error) { 236 | if err != nil { 237 | return "", err 238 | } 239 | if reply.Format != 8 { 240 | return "", fmt.Errorf("PropValStr: Expected format 8 but got %d", 241 | reply.Format) 242 | } 243 | return string(reply.Value), nil 244 | } 245 | 246 | // PropValStrs is the same as PropValStr, except that it returns a slice 247 | // of strings. The raw byte string is a sequence of null terminated strings, 248 | // which is translated into a slice of strings. 249 | func PropValStrs(reply *xproto.GetPropertyReply, err error) ([]string, error) { 250 | if err != nil { 251 | return nil, err 252 | } 253 | if reply.Format != 8 { 254 | return nil, fmt.Errorf("PropValStrs: Expected format 8 but got %d", 255 | reply.Format) 256 | } 257 | 258 | var strs []string 259 | sstart := 0 260 | for i, c := range reply.Value { 261 | if c == 0 { 262 | strs = append(strs, string(reply.Value[sstart:i])) 263 | sstart = i + 1 264 | } 265 | } 266 | if sstart < int(reply.ValueLen) { 267 | strs = append(strs, string(reply.Value[sstart:])) 268 | } 269 | return strs, nil 270 | } 271 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/resolve.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "encoding/base64" 20 | "math" 21 | "regexp" 22 | "strconv" 23 | "strings" 24 | "time" 25 | ) 26 | 27 | type resolveMapItem struct { 28 | value interface{} 29 | tag string 30 | } 31 | 32 | var resolveTable = make([]byte, 256) 33 | var resolveMap = make(map[string]resolveMapItem) 34 | 35 | func init() { 36 | t := resolveTable 37 | t[int('+')] = 'S' // Sign 38 | t[int('-')] = 'S' 39 | for _, c := range "0123456789" { 40 | t[int(c)] = 'D' // Digit 41 | } 42 | for _, c := range "yYnNtTfFoO~" { 43 | t[int(c)] = 'M' // In map 44 | } 45 | t[int('.')] = '.' // Float (potentially in map) 46 | 47 | var resolveMapList = []struct { 48 | v interface{} 49 | tag string 50 | l []string 51 | }{ 52 | {true, boolTag, []string{"true", "True", "TRUE"}}, 53 | {false, boolTag, []string{"false", "False", "FALSE"}}, 54 | {nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, 55 | {math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, 56 | {math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, 57 | {math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, 58 | {math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, 59 | {"<<", mergeTag, []string{"<<"}}, 60 | } 61 | 62 | m := resolveMap 63 | for _, item := range resolveMapList { 64 | for _, s := range item.l { 65 | m[s] = resolveMapItem{item.v, item.tag} 66 | } 67 | } 68 | } 69 | 70 | const ( 71 | nullTag = "!!null" 72 | boolTag = "!!bool" 73 | strTag = "!!str" 74 | intTag = "!!int" 75 | floatTag = "!!float" 76 | timestampTag = "!!timestamp" 77 | seqTag = "!!seq" 78 | mapTag = "!!map" 79 | binaryTag = "!!binary" 80 | mergeTag = "!!merge" 81 | ) 82 | 83 | var longTags = make(map[string]string) 84 | var shortTags = make(map[string]string) 85 | 86 | func init() { 87 | for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { 88 | ltag := longTag(stag) 89 | longTags[stag] = ltag 90 | shortTags[ltag] = stag 91 | } 92 | } 93 | 94 | const longTagPrefix = "tag:yaml.org,2002:" 95 | 96 | func shortTag(tag string) string { 97 | if strings.HasPrefix(tag, longTagPrefix) { 98 | if stag, ok := shortTags[tag]; ok { 99 | return stag 100 | } 101 | return "!!" + tag[len(longTagPrefix):] 102 | } 103 | return tag 104 | } 105 | 106 | func longTag(tag string) string { 107 | if strings.HasPrefix(tag, "!!") { 108 | if ltag, ok := longTags[tag]; ok { 109 | return ltag 110 | } 111 | return longTagPrefix + tag[2:] 112 | } 113 | return tag 114 | } 115 | 116 | func resolvableTag(tag string) bool { 117 | switch tag { 118 | case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: 119 | return true 120 | } 121 | return false 122 | } 123 | 124 | var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) 125 | 126 | func resolve(tag string, in string) (rtag string, out interface{}) { 127 | tag = shortTag(tag) 128 | if !resolvableTag(tag) { 129 | return tag, in 130 | } 131 | 132 | defer func() { 133 | switch tag { 134 | case "", rtag, strTag, binaryTag: 135 | return 136 | case floatTag: 137 | if rtag == intTag { 138 | switch v := out.(type) { 139 | case int64: 140 | rtag = floatTag 141 | out = float64(v) 142 | return 143 | case int: 144 | rtag = floatTag 145 | out = float64(v) 146 | return 147 | } 148 | } 149 | } 150 | failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) 151 | }() 152 | 153 | // Any data is accepted as a !!str or !!binary. 154 | // Otherwise, the prefix is enough of a hint about what it might be. 155 | hint := byte('N') 156 | if in != "" { 157 | hint = resolveTable[in[0]] 158 | } 159 | if hint != 0 && tag != strTag && tag != binaryTag { 160 | // Handle things we can lookup in a map. 161 | if item, ok := resolveMap[in]; ok { 162 | return item.tag, item.value 163 | } 164 | 165 | // Base 60 floats are a bad idea, were dropped in YAML 1.2, and 166 | // are purposefully unsupported here. They're still quoted on 167 | // the way out for compatibility with other parser, though. 168 | 169 | switch hint { 170 | case 'M': 171 | // We've already checked the map above. 172 | 173 | case '.': 174 | // Not in the map, so maybe a normal float. 175 | floatv, err := strconv.ParseFloat(in, 64) 176 | if err == nil { 177 | return floatTag, floatv 178 | } 179 | 180 | case 'D', 'S': 181 | // Int, float, or timestamp. 182 | // Only try values as a timestamp if the value is unquoted or there's an explicit 183 | // !!timestamp tag. 184 | if tag == "" || tag == timestampTag { 185 | t, ok := parseTimestamp(in) 186 | if ok { 187 | return timestampTag, t 188 | } 189 | } 190 | 191 | plain := strings.Replace(in, "_", "", -1) 192 | intv, err := strconv.ParseInt(plain, 0, 64) 193 | if err == nil { 194 | if intv == int64(int(intv)) { 195 | return intTag, int(intv) 196 | } else { 197 | return intTag, intv 198 | } 199 | } 200 | uintv, err := strconv.ParseUint(plain, 0, 64) 201 | if err == nil { 202 | return intTag, uintv 203 | } 204 | if yamlStyleFloat.MatchString(plain) { 205 | floatv, err := strconv.ParseFloat(plain, 64) 206 | if err == nil { 207 | return floatTag, floatv 208 | } 209 | } 210 | if strings.HasPrefix(plain, "0b") { 211 | intv, err := strconv.ParseInt(plain[2:], 2, 64) 212 | if err == nil { 213 | if intv == int64(int(intv)) { 214 | return intTag, int(intv) 215 | } else { 216 | return intTag, intv 217 | } 218 | } 219 | uintv, err := strconv.ParseUint(plain[2:], 2, 64) 220 | if err == nil { 221 | return intTag, uintv 222 | } 223 | } else if strings.HasPrefix(plain, "-0b") { 224 | intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) 225 | if err == nil { 226 | if true || intv == int64(int(intv)) { 227 | return intTag, int(intv) 228 | } else { 229 | return intTag, intv 230 | } 231 | } 232 | } 233 | // Octals as introduced in version 1.2 of the spec. 234 | // Octals from the 1.1 spec, spelled as 0777, are still 235 | // decoded by default in v3 as well for compatibility. 236 | // May be dropped in v4 depending on how usage evolves. 237 | if strings.HasPrefix(plain, "0o") { 238 | intv, err := strconv.ParseInt(plain[2:], 8, 64) 239 | if err == nil { 240 | if intv == int64(int(intv)) { 241 | return intTag, int(intv) 242 | } else { 243 | return intTag, intv 244 | } 245 | } 246 | uintv, err := strconv.ParseUint(plain[2:], 8, 64) 247 | if err == nil { 248 | return intTag, uintv 249 | } 250 | } else if strings.HasPrefix(plain, "-0o") { 251 | intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) 252 | if err == nil { 253 | if true || intv == int64(int(intv)) { 254 | return intTag, int(intv) 255 | } else { 256 | return intTag, intv 257 | } 258 | } 259 | } 260 | default: 261 | panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") 262 | } 263 | } 264 | return strTag, in 265 | } 266 | 267 | // encodeBase64 encodes s as base64 that is broken up into multiple lines 268 | // as appropriate for the resulting length. 269 | func encodeBase64(s string) string { 270 | const lineLen = 70 271 | encLen := base64.StdEncoding.EncodedLen(len(s)) 272 | lines := encLen/lineLen + 1 273 | buf := make([]byte, encLen*2+lines) 274 | in := buf[0:encLen] 275 | out := buf[encLen:] 276 | base64.StdEncoding.Encode(in, []byte(s)) 277 | k := 0 278 | for i := 0; i < len(in); i += lineLen { 279 | j := i + lineLen 280 | if j > len(in) { 281 | j = len(in) 282 | } 283 | k += copy(out[k:], in[i:j]) 284 | if lines > 1 { 285 | out[k] = '\n' 286 | k++ 287 | } 288 | } 289 | return string(out[:k]) 290 | } 291 | 292 | // This is a subset of the formats allowed by the regular expression 293 | // defined at http://yaml.org/type/timestamp.html. 294 | var allowedTimestampFormats = []string{ 295 | "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. 296 | "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". 297 | "2006-1-2 15:4:5.999999999", // space separated with no time zone 298 | "2006-1-2", // date only 299 | // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" 300 | // from the set of examples. 301 | } 302 | 303 | // parseTimestamp parses s as a timestamp string and 304 | // returns the timestamp and reports whether it succeeded. 305 | // Timestamp formats are defined at http://yaml.org/type/timestamp.html 306 | func parseTimestamp(s string) (time.Time, bool) { 307 | // TODO write code to check all the formats supported by 308 | // http://yaml.org/type/timestamp.html instead of using time.Parse. 309 | 310 | // Quick check: all date formats start with YYYY-. 311 | i := 0 312 | for ; i < len(s); i++ { 313 | if c := s[i]; c < '0' || c > '9' { 314 | break 315 | } 316 | } 317 | if i != 4 || i == len(s) || s[i] != '-' { 318 | return time.Time{}, false 319 | } 320 | for _, format := range allowedTimestampFormats { 321 | if t, err := time.Parse(format, s); err == nil { 322 | return t, true 323 | } 324 | } 325 | return time.Time{}, false 326 | } 327 | -------------------------------------------------------------------------------- /vendor/go.i3wm.org/i3/v4/subscribe.go: -------------------------------------------------------------------------------- 1 | package i3 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "net" 9 | "time" 10 | ) 11 | 12 | // Event is an event received from i3. 13 | // 14 | // Type-assert or type-switch on Event to obtain a more specific type. 15 | type Event interface{} 16 | 17 | // WorkspaceEvent contains details about various workspace-related changes. 18 | // 19 | // See https://i3wm.org/docs/ipc.html#_workspace_event for more details. 20 | type WorkspaceEvent struct { 21 | Change string `json:"change"` 22 | Current Node `json:"current"` 23 | Old Node `json:"old"` 24 | } 25 | 26 | // OutputEvent contains details about various output-related changes. 27 | // 28 | // See https://i3wm.org/docs/ipc.html#_output_event for more details. 29 | type OutputEvent struct { 30 | Change string `json:"change"` 31 | } 32 | 33 | // ModeEvent contains details about various mode-related changes. 34 | // 35 | // See https://i3wm.org/docs/ipc.html#_mode_event for more details. 36 | type ModeEvent struct { 37 | Change string `json:"change"` 38 | PangoMarkup bool `json:"pango_markup"` 39 | } 40 | 41 | // WindowEvent contains details about various window-related changes. 42 | // 43 | // See https://i3wm.org/docs/ipc.html#_window_event for more details. 44 | type WindowEvent struct { 45 | Change string `json:"change"` 46 | Container Node `json:"container"` 47 | } 48 | 49 | // BarconfigUpdateEvent contains details about various bar config-related changes. 50 | // 51 | // See https://i3wm.org/docs/ipc.html#_barconfig_update_event for more details. 52 | type BarconfigUpdateEvent BarConfig 53 | 54 | // BindingEvent contains details about various binding-related changes. 55 | // 56 | // See https://i3wm.org/docs/ipc.html#_binding_event for more details. 57 | type BindingEvent struct { 58 | Change string `json:"change"` 59 | Binding struct { 60 | Command string `json:"command"` 61 | EventStateMask []string `json:"event_state_mask"` 62 | InputCode int64 `json:"input_code"` 63 | Symbol string `json:"symbol"` 64 | InputType string `json:"input_type"` 65 | } `json:"binding"` 66 | } 67 | 68 | // ShutdownEvent contains the reason for which the IPC connection is about to be 69 | // shut down. 70 | // 71 | // See https://i3wm.org/docs/ipc.html#_shutdown_event for more details. 72 | type ShutdownEvent struct { 73 | Change string `json:"change"` 74 | } 75 | 76 | // TickEvent contains the payload of the last tick command. 77 | // 78 | // See https://i3wm.org/docs/ipc.html#_tick_event for more details. 79 | type TickEvent struct { 80 | First bool `json:"first"` 81 | Payload string `json:"payload"` 82 | } 83 | 84 | type eventReplyType int 85 | 86 | const ( 87 | eventReplyTypeWorkspace eventReplyType = iota 88 | eventReplyTypeOutput 89 | eventReplyTypeMode 90 | eventReplyTypeWindow 91 | eventReplyTypeBarconfigUpdate 92 | eventReplyTypeBinding 93 | eventReplyTypeShutdown 94 | eventReplyTypeTick 95 | ) 96 | 97 | const ( 98 | eventFlagMask = uint32(0x80000000) 99 | eventTypeMask = ^eventFlagMask 100 | ) 101 | 102 | // EventReceiver is not safe for concurrent use. 103 | type EventReceiver struct { 104 | types []EventType // for re-subscribing on io.EOF 105 | sock *socket 106 | conn net.Conn 107 | ev Event 108 | err error 109 | reconnect bool 110 | closed bool 111 | } 112 | 113 | // Event returns the most recent event received from i3 by a call to Next. 114 | func (r *EventReceiver) Event() Event { 115 | return r.ev 116 | } 117 | 118 | func (r *EventReceiver) subscribe() error { 119 | var err error 120 | if r.conn != nil { 121 | r.conn.Close() 122 | } 123 | if wasRestart { 124 | r.reconnect = false 125 | } 126 | r.sock, r.conn, err = getIPCSocket(r.reconnect) 127 | r.reconnect = true 128 | if err != nil { 129 | return err 130 | } 131 | payload, err := json.Marshal(r.types) 132 | if err != nil { 133 | return err 134 | } 135 | b, err := r.sock.roundTrip(messageTypeSubscribe, payload) 136 | if err != nil { 137 | return err 138 | } 139 | var reply struct { 140 | Success bool `json:"success"` 141 | } 142 | if err := json.Unmarshal(b.Payload, &reply); err != nil { 143 | return err 144 | } 145 | if !reply.Success { 146 | return fmt.Errorf("could not subscribe, check the i3 log") 147 | } 148 | r.err = nil 149 | return nil 150 | } 151 | 152 | func (r *EventReceiver) next() (Event, error) { 153 | reply, err := r.sock.recvMsg() 154 | if err != nil { 155 | return nil, err 156 | } 157 | if (uint32(reply.Type) & eventFlagMask) == 0 { 158 | return nil, fmt.Errorf("unexpectedly did not receive an event") 159 | } 160 | t := uint32(reply.Type) & eventTypeMask 161 | switch eventReplyType(t) { 162 | case eventReplyTypeWorkspace: 163 | var e WorkspaceEvent 164 | return &e, json.Unmarshal(reply.Payload, &e) 165 | 166 | case eventReplyTypeOutput: 167 | var e OutputEvent 168 | return &e, json.Unmarshal(reply.Payload, &e) 169 | 170 | case eventReplyTypeMode: 171 | var e ModeEvent 172 | return &e, json.Unmarshal(reply.Payload, &e) 173 | 174 | case eventReplyTypeWindow: 175 | var e WindowEvent 176 | return &e, json.Unmarshal(reply.Payload, &e) 177 | 178 | case eventReplyTypeBarconfigUpdate: 179 | var e BarconfigUpdateEvent 180 | return &e, json.Unmarshal(reply.Payload, &e) 181 | 182 | case eventReplyTypeBinding: 183 | var e BindingEvent 184 | return &e, json.Unmarshal(reply.Payload, &e) 185 | 186 | case eventReplyTypeShutdown: 187 | var e ShutdownEvent 188 | return &e, json.Unmarshal(reply.Payload, &e) 189 | 190 | case eventReplyTypeTick: 191 | var e TickEvent 192 | return &e, json.Unmarshal(reply.Payload, &e) 193 | } 194 | return nil, fmt.Errorf("BUG: event reply type %d not implemented yet", t) 195 | } 196 | 197 | // Next advances the EventReceiver to the next event, which will then be 198 | // available through the Event method. It returns false when reaching an 199 | // error. After Next returns false, the Close method will return the first 200 | // error. 201 | // 202 | // Until you call Close, you must call Next in a loop for every EventReceiver 203 | // (usually in a separate goroutine), otherwise i3 will deadlock as soon as the 204 | // UNIX socket buffer is full of unprocessed events. 205 | func (r *EventReceiver) Next() bool { 206 | Outer: 207 | for r.err == nil { 208 | r.ev, r.err = r.next() 209 | if r.err == nil { 210 | return true // happy path 211 | } 212 | 213 | if r.closed { 214 | return false 215 | } 216 | 217 | // reconnect 218 | start := time.Now() 219 | for time.Since(start) < reconnectTimeout && (r.sock == nil || i3Running()) { 220 | if err := r.subscribe(); err == nil { 221 | continue Outer 222 | } else { 223 | r.err = err 224 | } 225 | 226 | // Reconnect within [10, 20) ms to prevent CPU-starving i3. 227 | time.Sleep(time.Duration(10+rand.Int63n(10)) * time.Millisecond) 228 | } 229 | } 230 | return r.err == nil 231 | } 232 | 233 | // Close closes the connection to i3. If you don’t ever call Close, you must 234 | // consume events via Next to prevent i3 from deadlocking. 235 | func (r *EventReceiver) Close() error { 236 | r.closed = true 237 | if r.conn != nil { 238 | if r.err == nil { 239 | r.err = r.conn.Close() 240 | } else { 241 | // Retain the original error. 242 | r.conn.Close() 243 | } 244 | r.conn = nil 245 | r.sock = nil 246 | } 247 | return r.err 248 | } 249 | 250 | // EventType indicates the specific kind of event to subscribe to. 251 | type EventType string 252 | 253 | // i3 currently implements the following event types: 254 | const ( 255 | WorkspaceEventType EventType = "workspace" // since 4.0 256 | OutputEventType EventType = "output" // since 4.0 257 | ModeEventType EventType = "mode" // since 4.4 258 | WindowEventType EventType = "window" // since 4.5 259 | BarconfigUpdateEventType EventType = "barconfig_update" // since 4.6 260 | BindingEventType EventType = "binding" // since 4.9 261 | ShutdownEventType EventType = "shutdown" // since 4.14 262 | TickEventType EventType = "tick" // since 4.15 263 | ) 264 | 265 | type majorMinor struct { 266 | major int64 267 | minor int64 268 | } 269 | 270 | var eventAtLeast = map[EventType]majorMinor{ 271 | WorkspaceEventType: {4, 0}, 272 | OutputEventType: {4, 0}, 273 | ModeEventType: {4, 4}, 274 | WindowEventType: {4, 5}, 275 | BarconfigUpdateEventType: {4, 6}, 276 | BindingEventType: {4, 9}, 277 | ShutdownEventType: {4, 14}, 278 | TickEventType: {4, 15}, 279 | } 280 | 281 | // Subscribe returns an EventReceiver for receiving events of the specified 282 | // types from i3. 283 | // 284 | // Unless the ordering of events matters to your use-case, you are encouraged to 285 | // call Subscribe once per event type, so that you can use type assertions 286 | // instead of type switches. 287 | // 288 | // Subscribe is supported in i3 ≥ v4.0 (2011-07-31). 289 | func Subscribe(eventTypes ...EventType) *EventReceiver { 290 | // Error out early in case any requested event type is not yet supported by 291 | // the running i3 version. 292 | for _, t := range eventTypes { 293 | if err := AtLeast(eventAtLeast[t].major, eventAtLeast[t].minor); err != nil { 294 | return &EventReceiver{err: err} 295 | } 296 | } 297 | return &EventReceiver{types: eventTypes} 298 | } 299 | 300 | // restart runs the restart i3 command without entering an infinite loop: as 301 | // RUN_COMMAND with payload "restart" does not result in a reply, we subscribe 302 | // to the shutdown event beforehand (on a dedicated connection), which we can 303 | // receive instead of a reply. 304 | func restart(firstAttempt bool) error { 305 | sock, conn, err := getIPCSocket(!firstAttempt) 306 | if err != nil { 307 | return err 308 | } 309 | defer conn.Close() 310 | payload, err := json.Marshal([]EventType{ShutdownEventType}) 311 | if err != nil { 312 | return err 313 | } 314 | b, err := sock.roundTrip(messageTypeSubscribe, payload) 315 | if err != nil { 316 | return err 317 | } 318 | var sreply struct { 319 | Success bool `json:"success"` 320 | } 321 | if err := json.Unmarshal(b.Payload, &sreply); err != nil { 322 | return err 323 | } 324 | if !sreply.Success { 325 | return fmt.Errorf("could not subscribe, check the i3 log") 326 | } 327 | rreply, err := sock.roundTrip(messageTypeRunCommand, []byte("restart")) 328 | if err != nil { 329 | return err 330 | } 331 | if (uint32(rreply.Type) & eventFlagMask) == 0 { 332 | var crs []CommandResult 333 | err = json.Unmarshal(rreply.Payload, &crs) 334 | if err == nil { 335 | for _, cr := range crs { 336 | if !cr.Success { 337 | return &CommandUnsuccessfulError{ 338 | command: "restart", 339 | cr: cr, 340 | } 341 | } 342 | } 343 | } 344 | return nil // restart command successful 345 | } 346 | t := uint32(rreply.Type) & eventTypeMask 347 | if got, want := eventReplyType(t), eventReplyTypeShutdown; got != want { 348 | return fmt.Errorf("unexpected reply type: got %d, want %d", got, want) 349 | } 350 | return nil // shutdown event received 351 | } 352 | 353 | var wasRestart = false 354 | 355 | // Restart sends the restart command to i3. Sending restart via RunCommand will 356 | // result in a deadlock: since i3 restarts before it sends the reply to the 357 | // restart command, RunCommand will retry the command indefinitely. 358 | // 359 | // Restart is supported in i3 ≥ v4.14 (2017-09-04). 360 | func Restart() error { 361 | if err := AtLeast(eventAtLeast[ShutdownEventType].major, eventAtLeast[ShutdownEventType].minor); err != nil { 362 | return err 363 | } 364 | 365 | if AtLeast(4, 17) == nil { 366 | _, err := roundTrip(messageTypeRunCommand, []byte("restart")) 367 | return err 368 | } 369 | 370 | log.Println("preventing any further X11 connections to work around issue #3") 371 | wasRestart = true 372 | 373 | var ( 374 | firstAttempt = true 375 | start = time.Now() 376 | lastErr error 377 | ) 378 | for time.Since(start) < reconnectTimeout && (firstAttempt || i3Running()) { 379 | lastErr = restart(firstAttempt) 380 | if lastErr == nil { 381 | return nil // success 382 | } 383 | firstAttempt = false 384 | } 385 | return lastErr 386 | } 387 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgbutil/xgbutil.go: -------------------------------------------------------------------------------- 1 | package xgbutil 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "sync" 7 | 8 | "github.com/BurntSushi/xgb" 9 | "github.com/BurntSushi/xgb/xinerama" 10 | "github.com/BurntSushi/xgb/xproto" 11 | ) 12 | 13 | // Logger is used through xgbutil when messages need to be emitted to stderr. 14 | var Logger = log.New(os.Stderr, "[xgbutil] ", log.Lshortfile) 15 | 16 | // The current maximum request size. I think we can expand this with 17 | // BigReq, but it probably isn't worth it at the moment. 18 | const MaxReqSize = (1 << 16) * 4 19 | 20 | // An XUtil represents the state of xgbutil. It keeps track of the current 21 | // X connection, the root window, event callbacks, key/mouse bindings, etc. 22 | // Regrettably, many of the members are exported, even though they should not 23 | // be used directly by the user. They are exported for use in sub-packages. 24 | // (Namely, xevent, keybind and mousebind.) In fact, there should *never* 25 | // be a reason to access any members of an XUtil value directly. Any 26 | // interaction with an XUtil value should be through its methods. 27 | type XUtil struct { 28 | // conn is the XGB connection object used to issue protocol requests. 29 | conn *xgb.Conn 30 | 31 | // Quit can be set to true, and the main event loop will finish processing 32 | // the current event, and gracefully quit afterwards. 33 | // This is exported for use in the xevent package. Please us xevent.Quit 34 | // to set this value. 35 | Quit bool // when true, the main event loop will stop gracefully 36 | 37 | // setup contains all the setup information retrieved at connection time. 38 | setup *xproto.SetupInfo 39 | 40 | // screen is a simple alias to the default screen info. 41 | screen *xproto.ScreenInfo 42 | 43 | // root is an alias to the default root window. 44 | root xproto.Window 45 | 46 | // Atoms is a cache of atom names to resource identifiers. This minimizes 47 | // round trips to the X server, since atom identifiers never change. 48 | // It is exported for use in the xprop package. It should not be used. 49 | Atoms map[string]xproto.Atom 50 | AtomsLck *sync.RWMutex 51 | 52 | // AtomNames is a cache just like 'atoms', but in the reverse direction. 53 | // It is exported for use in the xprop package. It should not be used. 54 | AtomNames map[xproto.Atom]string 55 | AtomNamesLck *sync.RWMutex 56 | 57 | // Evqueue is the queue that stores the results of xgb.WaitForEvent. 58 | // Namely, each value is either an Event *or* an Error. 59 | // It is exported for use in the xevent package. Do not use it. 60 | // If you need to interact with the event queue, please use the functions 61 | // available in the xevent package: Dequeue, DequeueAt, QueueEmpty 62 | // and QueuePeek. 63 | Evqueue []EventOrError 64 | EvqueueLck *sync.RWMutex 65 | 66 | // Callbacks is a map of event numbers to a map of window identifiers 67 | // to callback functions. 68 | // This is the data structure that stores all callback functions, where 69 | // a callback function is always attached to a (event, window) tuple. 70 | // It is exported for use in the xevent package. Do not use it. 71 | Callbacks map[int]map[xproto.Window][]Callback 72 | CallbacksLck *sync.RWMutex 73 | 74 | // Hooks are called by the XEvent main loop before processing the event 75 | // itself. These are meant for instances when it's not possible / easy 76 | // to use the normal Hook system. You should not modify this yourself. 77 | Hooks []CallbackHook 78 | HooksLck *sync.RWMutex 79 | 80 | // eventTime is the last time recorded by an event. It is automatically 81 | // updated if xgbutil's main event loop is used. 82 | eventTime xproto.Timestamp 83 | 84 | // Keymap corresponds to xgbutil's current conception of the keyboard 85 | // mapping. It is automatically kept up-to-date if xgbutil's event loop 86 | // is used. 87 | // It is exported for use in the keybind package. It should not be 88 | // accessed directly. Instead, use keybind.KeyMapGet. 89 | Keymap *KeyboardMapping 90 | 91 | // Modmap corresponds to xgbutil's current conception of the modifier key 92 | // mapping. It is automatically kept up-to-date if xgbutil's event loop 93 | // is used. 94 | // It is exported for use in the keybind package. It should not be 95 | // accessed directly. Instead, use keybind.ModMapGet. 96 | Modmap *ModifierMapping 97 | 98 | // KeyRedirect corresponds to a window identifier that, when set, 99 | // automatically receives *all* keyboard events. This is a sort-of 100 | // synthetic grab and is helpful in avoiding race conditions. 101 | // It is exported for use in the xevent and keybind packages. Do not use 102 | // it directly. To redirect key events, please use xevent.RedirectKeyEvents. 103 | KeyRedirect xproto.Window 104 | 105 | // Keybinds is the data structure storing all callbacks for key bindings. 106 | // This is extremely similar to the general notion of event callbacks, 107 | // but adds extra support to make handling key bindings easier. (Like 108 | // specifying human readable key sequences to bind to.) 109 | // KeyBindKey is a struct representing the 4-tuple 110 | // (event-type, window-id, modifiers, keycode). 111 | // It is exported for use in the keybind package. Do not access it directly. 112 | Keybinds map[KeyKey][]CallbackKey 113 | KeybindsLck *sync.RWMutex 114 | 115 | // Keygrabs is a frequency count of the number of callbacks associated 116 | // with a particular KeyBindKey. This is necessary because we can only 117 | // grab a particular key *once*, but we may want to attach several callbacks 118 | // to a single keypress. 119 | // It is exported for use in the keybind package. Do not access it directly. 120 | Keygrabs map[KeyKey]int 121 | 122 | // Keystrings is a list of all key strings used to connect keybindings. 123 | // They are used to rebuild key grabs when the keyboard mapping is updated. 124 | // It is exported for use in the keybind package. Do not access it directly. 125 | Keystrings []KeyString 126 | 127 | // Mousebinds is the data structure storing all callbacks for mouse 128 | // bindings.This is extremely similar to the general notion of event 129 | // callbacks,but adds extra support to make handling mouse bindings easier. 130 | // (Like specifying human readable mouse sequences to bind to.) 131 | // MouseBindKey is a struct representing the 4-tuple 132 | // (event-type, window-id, modifiers, button). 133 | // It is exported for use in the mousebind package. Do not use it. 134 | Mousebinds map[MouseKey][]CallbackMouse 135 | MousebindsLck *sync.RWMutex 136 | 137 | // Mousegrabs is a frequency count of the number of callbacks associated 138 | // with a particular MouseBindKey. This is necessary because we can only 139 | // grab a particular mouse button *once*, but we may want to attach 140 | // several callbacks to a single button press. 141 | // It is exported for use in the mousebind package. Do not use it. 142 | Mousegrabs map[MouseKey]int 143 | 144 | // InMouseDrag is true if a drag is currently in progress. 145 | // It is exported for use in the mousebind package. Do not use it. 146 | InMouseDrag bool 147 | 148 | // MouseDragStep is the function executed for each step (i.e., pointer 149 | // movement) in the current mouse drag. Note that this is nil when a drag 150 | // is not in progress. 151 | // It is exported for use in the mousebind package. Do not use it. 152 | MouseDragStepFun MouseDragFun 153 | 154 | // MouseDragEnd is the function executed at the end of the current 155 | // mouse drag. This is nil when a drag is not in progress. 156 | // It is exported for use in the mousebind package. Do not use it. 157 | MouseDragEndFun MouseDragFun 158 | 159 | // gc is a general purpose graphics context; used to paint images. 160 | // Since we don't do any real X drawing, we don't really care about the 161 | // particulars of our graphics context. 162 | gc xproto.Gcontext 163 | 164 | // dummy is a dummy window used for mouse/key GRABs. 165 | // Basically, whenever a grab is instituted, mouse and key events are 166 | // redirected to the dummy the window. 167 | dummy xproto.Window 168 | 169 | // ErrorHandler is the function that handles errors *in the event loop*. 170 | // By default, it simply emits them to stderr. 171 | // It is exported for use in the xevent package. To set the default error 172 | // handler, please use xevent.ErrorHandlerSet. 173 | ErrorHandler ErrorHandlerFun 174 | } 175 | 176 | // NewConn connects to the X server using the DISPLAY environment variable 177 | // and creates a new XUtil. Most environments have the DISPLAY environment 178 | // variable set, so this is probably what you want to use to connect to X. 179 | func NewConn() (*XUtil, error) { 180 | return NewConnDisplay("") 181 | } 182 | 183 | // NewConnDisplay connects to the X server and creates a new XUtil. 184 | // If 'display' is empty, the DISPLAY environment variable is used. Otherwise 185 | // there are several different display formats supported: 186 | // 187 | // NewConn(":1") -> net.Dial("unix", "", "/tmp/.X11-unix/X1") 188 | // NewConn("/tmp/launch-12/:0") -> net.Dial("unix", "", "/tmp/launch-12/:0") 189 | // NewConn("hostname:2.1") -> net.Dial("tcp", "", "hostname:6002") 190 | // NewConn("tcp/hostname:1.0") -> net.Dial("tcp", "", "hostname:6001") 191 | func NewConnDisplay(display string) (*XUtil, error) { 192 | c, err := xgb.NewConnDisplay(display) 193 | 194 | if err != nil { 195 | return nil, err 196 | } 197 | 198 | return NewConnXgb(c) 199 | 200 | } 201 | 202 | // NewConnXgb use the specific xgb.Conn to create a new XUtil. 203 | // 204 | // NewConn, NewConnDisplay are wrapper of this function. 205 | func NewConnXgb(c *xgb.Conn) (*XUtil, error) { 206 | setup := xproto.Setup(c) 207 | screen := setup.DefaultScreen(c) 208 | 209 | // Initialize our central struct that stores everything. 210 | xu := &XUtil{ 211 | conn: c, 212 | Quit: false, 213 | Evqueue: make([]EventOrError, 0, 1000), 214 | EvqueueLck: &sync.RWMutex{}, 215 | setup: setup, 216 | screen: screen, 217 | root: screen.Root, 218 | eventTime: xproto.Timestamp(0), // last event time 219 | Atoms: make(map[string]xproto.Atom, 50), 220 | AtomsLck: &sync.RWMutex{}, 221 | AtomNames: make(map[xproto.Atom]string, 50), 222 | AtomNamesLck: &sync.RWMutex{}, 223 | Callbacks: make(map[int]map[xproto.Window][]Callback, 33), 224 | CallbacksLck: &sync.RWMutex{}, 225 | Hooks: make([]CallbackHook, 0), 226 | HooksLck: &sync.RWMutex{}, 227 | Keymap: nil, // we don't have anything yet 228 | Modmap: nil, 229 | KeyRedirect: 0, 230 | Keybinds: make(map[KeyKey][]CallbackKey, 10), 231 | KeybindsLck: &sync.RWMutex{}, 232 | Keygrabs: make(map[KeyKey]int, 10), 233 | Keystrings: make([]KeyString, 0, 10), 234 | Mousebinds: make(map[MouseKey][]CallbackMouse, 10), 235 | MousebindsLck: &sync.RWMutex{}, 236 | Mousegrabs: make(map[MouseKey]int, 10), 237 | InMouseDrag: false, 238 | MouseDragStepFun: nil, 239 | MouseDragEndFun: nil, 240 | ErrorHandler: func(err xgb.Error) { Logger.Println(err) }, 241 | } 242 | 243 | var err error = nil 244 | // Create a general purpose graphics context 245 | xu.gc, err = xproto.NewGcontextId(xu.conn) 246 | if err != nil { 247 | return nil, err 248 | } 249 | xproto.CreateGC(xu.conn, xu.gc, xproto.Drawable(xu.root), 250 | xproto.GcForeground, []uint32{xu.screen.WhitePixel}) 251 | 252 | // Create a dummy window 253 | xu.dummy, err = xproto.NewWindowId(xu.conn) 254 | if err != nil { 255 | return nil, err 256 | } 257 | xproto.CreateWindow(xu.conn, xu.Screen().RootDepth, xu.dummy, xu.RootWin(), 258 | -1000, -1000, 1, 1, 0, 259 | xproto.WindowClassInputOutput, xu.Screen().RootVisual, 260 | xproto.CwEventMask|xproto.CwOverrideRedirect, 261 | []uint32{1, xproto.EventMaskPropertyChange}) 262 | xproto.MapWindow(xu.conn, xu.dummy) 263 | 264 | // Register the Xinerama extension... because it doesn't cost much. 265 | err = xinerama.Init(xu.conn) 266 | 267 | // If we can't register Xinerama, that's okay. Output something 268 | // and move on. 269 | if err != nil { 270 | Logger.Printf("WARNING: %s\n", err) 271 | Logger.Printf("MESSAGE: The 'xinerama' package cannot be used " + 272 | "because the XINERAMA extension could not be loaded.") 273 | } 274 | 275 | return xu, nil 276 | } 277 | 278 | // Conn returns the xgb connection object. 279 | func (xu *XUtil) Conn() *xgb.Conn { 280 | return xu.conn 281 | } 282 | 283 | // ExtInitialized returns true if an extension has been initialized. 284 | // This is useful for determining whether an extension is available or not. 285 | func (xu *XUtil) ExtInitialized(extName string) bool { 286 | _, ok := xu.Conn().Extensions[extName] 287 | return ok 288 | } 289 | 290 | // Sync forces XGB to catch up with all events/requests and synchronize. 291 | // This is done by issuing a benign round trip request to X. 292 | func (xu *XUtil) Sync() { 293 | xproto.GetInputFocus(xu.Conn()).Reply() 294 | } 295 | 296 | // Setup returns the setup information retrieved during connection time. 297 | func (xu *XUtil) Setup() *xproto.SetupInfo { 298 | return xu.setup 299 | } 300 | 301 | // Screen returns the default screen 302 | func (xu *XUtil) Screen() *xproto.ScreenInfo { 303 | return xu.screen 304 | } 305 | 306 | // RootWin returns the current root window. 307 | func (xu *XUtil) RootWin() xproto.Window { 308 | return xu.root 309 | } 310 | 311 | // RootWinSet will change the current root window to the one provided. 312 | // N.B. This probably shouldn't be used unless you're desperately trying 313 | // to support multiple X screens. (This is *not* the same as Xinerama/RandR or 314 | // TwinView. All of those have a single root window.) 315 | func (xu *XUtil) RootWinSet(root xproto.Window) { 316 | xu.root = root 317 | } 318 | 319 | // TimeGet gets the most recent time seen by an event. 320 | func (xu *XUtil) TimeGet() xproto.Timestamp { 321 | return xu.eventTime 322 | } 323 | 324 | // TimeSet sets the most recent time seen by an event. 325 | func (xu *XUtil) TimeSet(t xproto.Timestamp) { 326 | xu.eventTime = t 327 | } 328 | 329 | // GC gets a general purpose graphics context that is typically used to simply 330 | // paint images. 331 | func (xu *XUtil) GC() xproto.Gcontext { 332 | return xu.gc 333 | } 334 | 335 | // Dummy gets the id of the dummy window. 336 | func (xu *XUtil) Dummy() xproto.Window { 337 | return xu.dummy 338 | } 339 | 340 | // Grabs the server. Everything becomes synchronous. 341 | func (xu *XUtil) Grab() { 342 | xproto.GrabServer(xu.Conn()) 343 | } 344 | 345 | // Ungrabs the server. 346 | func (xu *XUtil) Ungrab() { 347 | xproto.UngrabServer(xu.Conn()) 348 | } 349 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/readerc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | import ( 26 | "io" 27 | ) 28 | 29 | // Set the reader error and return 0. 30 | func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { 31 | parser.error = yaml_READER_ERROR 32 | parser.problem = problem 33 | parser.problem_offset = offset 34 | parser.problem_value = value 35 | return false 36 | } 37 | 38 | // Byte order marks. 39 | const ( 40 | bom_UTF8 = "\xef\xbb\xbf" 41 | bom_UTF16LE = "\xff\xfe" 42 | bom_UTF16BE = "\xfe\xff" 43 | ) 44 | 45 | // Determine the input stream encoding by checking the BOM symbol. If no BOM is 46 | // found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. 47 | func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { 48 | // Ensure that we had enough bytes in the raw buffer. 49 | for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { 50 | if !yaml_parser_update_raw_buffer(parser) { 51 | return false 52 | } 53 | } 54 | 55 | // Determine the encoding. 56 | buf := parser.raw_buffer 57 | pos := parser.raw_buffer_pos 58 | avail := len(buf) - pos 59 | if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { 60 | parser.encoding = yaml_UTF16LE_ENCODING 61 | parser.raw_buffer_pos += 2 62 | parser.offset += 2 63 | } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { 64 | parser.encoding = yaml_UTF16BE_ENCODING 65 | parser.raw_buffer_pos += 2 66 | parser.offset += 2 67 | } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { 68 | parser.encoding = yaml_UTF8_ENCODING 69 | parser.raw_buffer_pos += 3 70 | parser.offset += 3 71 | } else { 72 | parser.encoding = yaml_UTF8_ENCODING 73 | } 74 | return true 75 | } 76 | 77 | // Update the raw buffer. 78 | func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { 79 | size_read := 0 80 | 81 | // Return if the raw buffer is full. 82 | if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { 83 | return true 84 | } 85 | 86 | // Return on EOF. 87 | if parser.eof { 88 | return true 89 | } 90 | 91 | // Move the remaining bytes in the raw buffer to the beginning. 92 | if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { 93 | copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) 94 | } 95 | parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] 96 | parser.raw_buffer_pos = 0 97 | 98 | // Call the read handler to fill the buffer. 99 | size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) 100 | parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] 101 | if err == io.EOF { 102 | parser.eof = true 103 | } else if err != nil { 104 | return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) 105 | } 106 | return true 107 | } 108 | 109 | // Ensure that the buffer contains at least `length` characters. 110 | // Return true on success, false on failure. 111 | // 112 | // The length is supposed to be significantly less that the buffer size. 113 | func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { 114 | if parser.read_handler == nil { 115 | panic("read handler must be set") 116 | } 117 | 118 | // [Go] This function was changed to guarantee the requested length size at EOF. 119 | // The fact we need to do this is pretty awful, but the description above implies 120 | // for that to be the case, and there are tests 121 | 122 | // If the EOF flag is set and the raw buffer is empty, do nothing. 123 | if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { 124 | // [Go] ACTUALLY! Read the documentation of this function above. 125 | // This is just broken. To return true, we need to have the 126 | // given length in the buffer. Not doing that means every single 127 | // check that calls this function to make sure the buffer has a 128 | // given length is Go) panicking; or C) accessing invalid memory. 129 | //return true 130 | } 131 | 132 | // Return if the buffer contains enough characters. 133 | if parser.unread >= length { 134 | return true 135 | } 136 | 137 | // Determine the input encoding if it is not known yet. 138 | if parser.encoding == yaml_ANY_ENCODING { 139 | if !yaml_parser_determine_encoding(parser) { 140 | return false 141 | } 142 | } 143 | 144 | // Move the unread characters to the beginning of the buffer. 145 | buffer_len := len(parser.buffer) 146 | if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { 147 | copy(parser.buffer, parser.buffer[parser.buffer_pos:]) 148 | buffer_len -= parser.buffer_pos 149 | parser.buffer_pos = 0 150 | } else if parser.buffer_pos == buffer_len { 151 | buffer_len = 0 152 | parser.buffer_pos = 0 153 | } 154 | 155 | // Open the whole buffer for writing, and cut it before returning. 156 | parser.buffer = parser.buffer[:cap(parser.buffer)] 157 | 158 | // Fill the buffer until it has enough characters. 159 | first := true 160 | for parser.unread < length { 161 | 162 | // Fill the raw buffer if necessary. 163 | if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { 164 | if !yaml_parser_update_raw_buffer(parser) { 165 | parser.buffer = parser.buffer[:buffer_len] 166 | return false 167 | } 168 | } 169 | first = false 170 | 171 | // Decode the raw buffer. 172 | inner: 173 | for parser.raw_buffer_pos != len(parser.raw_buffer) { 174 | var value rune 175 | var width int 176 | 177 | raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos 178 | 179 | // Decode the next character. 180 | switch parser.encoding { 181 | case yaml_UTF8_ENCODING: 182 | // Decode a UTF-8 character. Check RFC 3629 183 | // (http://www.ietf.org/rfc/rfc3629.txt) for more details. 184 | // 185 | // The following table (taken from the RFC) is used for 186 | // decoding. 187 | // 188 | // Char. number range | UTF-8 octet sequence 189 | // (hexadecimal) | (binary) 190 | // --------------------+------------------------------------ 191 | // 0000 0000-0000 007F | 0xxxxxxx 192 | // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 193 | // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 194 | // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 195 | // 196 | // Additionally, the characters in the range 0xD800-0xDFFF 197 | // are prohibited as they are reserved for use with UTF-16 198 | // surrogate pairs. 199 | 200 | // Determine the length of the UTF-8 sequence. 201 | octet := parser.raw_buffer[parser.raw_buffer_pos] 202 | switch { 203 | case octet&0x80 == 0x00: 204 | width = 1 205 | case octet&0xE0 == 0xC0: 206 | width = 2 207 | case octet&0xF0 == 0xE0: 208 | width = 3 209 | case octet&0xF8 == 0xF0: 210 | width = 4 211 | default: 212 | // The leading octet is invalid. 213 | return yaml_parser_set_reader_error(parser, 214 | "invalid leading UTF-8 octet", 215 | parser.offset, int(octet)) 216 | } 217 | 218 | // Check if the raw buffer contains an incomplete character. 219 | if width > raw_unread { 220 | if parser.eof { 221 | return yaml_parser_set_reader_error(parser, 222 | "incomplete UTF-8 octet sequence", 223 | parser.offset, -1) 224 | } 225 | break inner 226 | } 227 | 228 | // Decode the leading octet. 229 | switch { 230 | case octet&0x80 == 0x00: 231 | value = rune(octet & 0x7F) 232 | case octet&0xE0 == 0xC0: 233 | value = rune(octet & 0x1F) 234 | case octet&0xF0 == 0xE0: 235 | value = rune(octet & 0x0F) 236 | case octet&0xF8 == 0xF0: 237 | value = rune(octet & 0x07) 238 | default: 239 | value = 0 240 | } 241 | 242 | // Check and decode the trailing octets. 243 | for k := 1; k < width; k++ { 244 | octet = parser.raw_buffer[parser.raw_buffer_pos+k] 245 | 246 | // Check if the octet is valid. 247 | if (octet & 0xC0) != 0x80 { 248 | return yaml_parser_set_reader_error(parser, 249 | "invalid trailing UTF-8 octet", 250 | parser.offset+k, int(octet)) 251 | } 252 | 253 | // Decode the octet. 254 | value = (value << 6) + rune(octet&0x3F) 255 | } 256 | 257 | // Check the length of the sequence against the value. 258 | switch { 259 | case width == 1: 260 | case width == 2 && value >= 0x80: 261 | case width == 3 && value >= 0x800: 262 | case width == 4 && value >= 0x10000: 263 | default: 264 | return yaml_parser_set_reader_error(parser, 265 | "invalid length of a UTF-8 sequence", 266 | parser.offset, -1) 267 | } 268 | 269 | // Check the range of the value. 270 | if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { 271 | return yaml_parser_set_reader_error(parser, 272 | "invalid Unicode character", 273 | parser.offset, int(value)) 274 | } 275 | 276 | case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: 277 | var low, high int 278 | if parser.encoding == yaml_UTF16LE_ENCODING { 279 | low, high = 0, 1 280 | } else { 281 | low, high = 1, 0 282 | } 283 | 284 | // The UTF-16 encoding is not as simple as one might 285 | // naively think. Check RFC 2781 286 | // (http://www.ietf.org/rfc/rfc2781.txt). 287 | // 288 | // Normally, two subsequent bytes describe a Unicode 289 | // character. However a special technique (called a 290 | // surrogate pair) is used for specifying character 291 | // values larger than 0xFFFF. 292 | // 293 | // A surrogate pair consists of two pseudo-characters: 294 | // high surrogate area (0xD800-0xDBFF) 295 | // low surrogate area (0xDC00-0xDFFF) 296 | // 297 | // The following formulas are used for decoding 298 | // and encoding characters using surrogate pairs: 299 | // 300 | // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) 301 | // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) 302 | // W1 = 110110yyyyyyyyyy 303 | // W2 = 110111xxxxxxxxxx 304 | // 305 | // where U is the character value, W1 is the high surrogate 306 | // area, W2 is the low surrogate area. 307 | 308 | // Check for incomplete UTF-16 character. 309 | if raw_unread < 2 { 310 | if parser.eof { 311 | return yaml_parser_set_reader_error(parser, 312 | "incomplete UTF-16 character", 313 | parser.offset, -1) 314 | } 315 | break inner 316 | } 317 | 318 | // Get the character. 319 | value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + 320 | (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) 321 | 322 | // Check for unexpected low surrogate area. 323 | if value&0xFC00 == 0xDC00 { 324 | return yaml_parser_set_reader_error(parser, 325 | "unexpected low surrogate area", 326 | parser.offset, int(value)) 327 | } 328 | 329 | // Check for a high surrogate area. 330 | if value&0xFC00 == 0xD800 { 331 | width = 4 332 | 333 | // Check for incomplete surrogate pair. 334 | if raw_unread < 4 { 335 | if parser.eof { 336 | return yaml_parser_set_reader_error(parser, 337 | "incomplete UTF-16 surrogate pair", 338 | parser.offset, -1) 339 | } 340 | break inner 341 | } 342 | 343 | // Get the next character. 344 | value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + 345 | (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) 346 | 347 | // Check for a low surrogate area. 348 | if value2&0xFC00 != 0xDC00 { 349 | return yaml_parser_set_reader_error(parser, 350 | "expected low surrogate area", 351 | parser.offset+2, int(value2)) 352 | } 353 | 354 | // Generate the value of the surrogate pair. 355 | value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) 356 | } else { 357 | width = 2 358 | } 359 | 360 | default: 361 | panic("impossible") 362 | } 363 | 364 | // Check if the character is in the allowed range: 365 | // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) 366 | // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) 367 | // | [#x10000-#x10FFFF] (32 bit) 368 | switch { 369 | case value == 0x09: 370 | case value == 0x0A: 371 | case value == 0x0D: 372 | case value >= 0x20 && value <= 0x7E: 373 | case value == 0x85: 374 | case value >= 0xA0 && value <= 0xD7FF: 375 | case value >= 0xE000 && value <= 0xFFFD: 376 | case value >= 0x10000 && value <= 0x10FFFF: 377 | default: 378 | return yaml_parser_set_reader_error(parser, 379 | "control characters are not allowed", 380 | parser.offset, int(value)) 381 | } 382 | 383 | // Move the raw pointers. 384 | parser.raw_buffer_pos += width 385 | parser.offset += width 386 | 387 | // Finally put the character into the buffer. 388 | if value <= 0x7F { 389 | // 0000 0000-0000 007F . 0xxxxxxx 390 | parser.buffer[buffer_len+0] = byte(value) 391 | buffer_len += 1 392 | } else if value <= 0x7FF { 393 | // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx 394 | parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) 395 | parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) 396 | buffer_len += 2 397 | } else if value <= 0xFFFF { 398 | // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx 399 | parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) 400 | parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) 401 | parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) 402 | buffer_len += 3 403 | } else { 404 | // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 405 | parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) 406 | parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) 407 | parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) 408 | parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) 409 | buffer_len += 4 410 | } 411 | 412 | parser.unread++ 413 | } 414 | 415 | // On EOF, put NUL into the buffer and return. 416 | if parser.eof { 417 | parser.buffer[buffer_len] = 0 418 | buffer_len++ 419 | parser.unread++ 420 | break 421 | } 422 | } 423 | // [Go] Read the documentation of this function above. To return true, 424 | // we need to have the given length in the buffer. Not doing that means 425 | // every single check that calls this function to make sure the buffer 426 | // has a given length is Go) panicking; or C) accessing invalid memory. 427 | // This happens here due to the EOF above breaking early. 428 | for buffer_len < length { 429 | parser.buffer[buffer_len] = 0 430 | buffer_len++ 431 | } 432 | parser.buffer = parser.buffer[:buffer_len] 433 | return true 434 | } 435 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/encode.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "encoding" 20 | "fmt" 21 | "io" 22 | "reflect" 23 | "regexp" 24 | "sort" 25 | "strconv" 26 | "strings" 27 | "time" 28 | "unicode/utf8" 29 | ) 30 | 31 | type encoder struct { 32 | emitter yaml_emitter_t 33 | event yaml_event_t 34 | out []byte 35 | flow bool 36 | indent int 37 | doneInit bool 38 | } 39 | 40 | func newEncoder() *encoder { 41 | e := &encoder{} 42 | yaml_emitter_initialize(&e.emitter) 43 | yaml_emitter_set_output_string(&e.emitter, &e.out) 44 | yaml_emitter_set_unicode(&e.emitter, true) 45 | return e 46 | } 47 | 48 | func newEncoderWithWriter(w io.Writer) *encoder { 49 | e := &encoder{} 50 | yaml_emitter_initialize(&e.emitter) 51 | yaml_emitter_set_output_writer(&e.emitter, w) 52 | yaml_emitter_set_unicode(&e.emitter, true) 53 | return e 54 | } 55 | 56 | func (e *encoder) init() { 57 | if e.doneInit { 58 | return 59 | } 60 | if e.indent == 0 { 61 | e.indent = 4 62 | } 63 | e.emitter.best_indent = e.indent 64 | yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) 65 | e.emit() 66 | e.doneInit = true 67 | } 68 | 69 | func (e *encoder) finish() { 70 | e.emitter.open_ended = false 71 | yaml_stream_end_event_initialize(&e.event) 72 | e.emit() 73 | } 74 | 75 | func (e *encoder) destroy() { 76 | yaml_emitter_delete(&e.emitter) 77 | } 78 | 79 | func (e *encoder) emit() { 80 | // This will internally delete the e.event value. 81 | e.must(yaml_emitter_emit(&e.emitter, &e.event)) 82 | } 83 | 84 | func (e *encoder) must(ok bool) { 85 | if !ok { 86 | msg := e.emitter.problem 87 | if msg == "" { 88 | msg = "unknown problem generating YAML content" 89 | } 90 | failf("%s", msg) 91 | } 92 | } 93 | 94 | func (e *encoder) marshalDoc(tag string, in reflect.Value) { 95 | e.init() 96 | var node *Node 97 | if in.IsValid() { 98 | node, _ = in.Interface().(*Node) 99 | } 100 | if node != nil && node.Kind == DocumentNode { 101 | e.nodev(in) 102 | } else { 103 | yaml_document_start_event_initialize(&e.event, nil, nil, true) 104 | e.emit() 105 | e.marshal(tag, in) 106 | yaml_document_end_event_initialize(&e.event, true) 107 | e.emit() 108 | } 109 | } 110 | 111 | func (e *encoder) marshal(tag string, in reflect.Value) { 112 | tag = shortTag(tag) 113 | if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { 114 | e.nilv() 115 | return 116 | } 117 | iface := in.Interface() 118 | switch value := iface.(type) { 119 | case *Node: 120 | e.nodev(in) 121 | return 122 | case Node: 123 | e.nodev(in.Addr()) 124 | return 125 | case time.Time: 126 | e.timev(tag, in) 127 | return 128 | case *time.Time: 129 | e.timev(tag, in.Elem()) 130 | return 131 | case time.Duration: 132 | e.stringv(tag, reflect.ValueOf(value.String())) 133 | return 134 | case Marshaler: 135 | v, err := value.MarshalYAML() 136 | if err != nil { 137 | fail(err) 138 | } 139 | if v == nil { 140 | e.nilv() 141 | return 142 | } 143 | e.marshal(tag, reflect.ValueOf(v)) 144 | return 145 | case encoding.TextMarshaler: 146 | text, err := value.MarshalText() 147 | if err != nil { 148 | fail(err) 149 | } 150 | in = reflect.ValueOf(string(text)) 151 | case nil: 152 | e.nilv() 153 | return 154 | } 155 | switch in.Kind() { 156 | case reflect.Interface: 157 | e.marshal(tag, in.Elem()) 158 | case reflect.Map: 159 | e.mapv(tag, in) 160 | case reflect.Ptr: 161 | e.marshal(tag, in.Elem()) 162 | case reflect.Struct: 163 | e.structv(tag, in) 164 | case reflect.Slice, reflect.Array: 165 | e.slicev(tag, in) 166 | case reflect.String: 167 | e.stringv(tag, in) 168 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 169 | e.intv(tag, in) 170 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 171 | e.uintv(tag, in) 172 | case reflect.Float32, reflect.Float64: 173 | e.floatv(tag, in) 174 | case reflect.Bool: 175 | e.boolv(tag, in) 176 | default: 177 | panic("cannot marshal type: " + in.Type().String()) 178 | } 179 | } 180 | 181 | func (e *encoder) mapv(tag string, in reflect.Value) { 182 | e.mappingv(tag, func() { 183 | keys := keyList(in.MapKeys()) 184 | sort.Sort(keys) 185 | for _, k := range keys { 186 | e.marshal("", k) 187 | e.marshal("", in.MapIndex(k)) 188 | } 189 | }) 190 | } 191 | 192 | func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { 193 | for _, num := range index { 194 | for { 195 | if v.Kind() == reflect.Ptr { 196 | if v.IsNil() { 197 | return reflect.Value{} 198 | } 199 | v = v.Elem() 200 | continue 201 | } 202 | break 203 | } 204 | v = v.Field(num) 205 | } 206 | return v 207 | } 208 | 209 | func (e *encoder) structv(tag string, in reflect.Value) { 210 | sinfo, err := getStructInfo(in.Type()) 211 | if err != nil { 212 | panic(err) 213 | } 214 | e.mappingv(tag, func() { 215 | for _, info := range sinfo.FieldsList { 216 | var value reflect.Value 217 | if info.Inline == nil { 218 | value = in.Field(info.Num) 219 | } else { 220 | value = e.fieldByIndex(in, info.Inline) 221 | if !value.IsValid() { 222 | continue 223 | } 224 | } 225 | if info.OmitEmpty && isZero(value) { 226 | continue 227 | } 228 | e.marshal("", reflect.ValueOf(info.Key)) 229 | e.flow = info.Flow 230 | e.marshal("", value) 231 | } 232 | if sinfo.InlineMap >= 0 { 233 | m := in.Field(sinfo.InlineMap) 234 | if m.Len() > 0 { 235 | e.flow = false 236 | keys := keyList(m.MapKeys()) 237 | sort.Sort(keys) 238 | for _, k := range keys { 239 | if _, found := sinfo.FieldsMap[k.String()]; found { 240 | panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) 241 | } 242 | e.marshal("", k) 243 | e.flow = false 244 | e.marshal("", m.MapIndex(k)) 245 | } 246 | } 247 | } 248 | }) 249 | } 250 | 251 | func (e *encoder) mappingv(tag string, f func()) { 252 | implicit := tag == "" 253 | style := yaml_BLOCK_MAPPING_STYLE 254 | if e.flow { 255 | e.flow = false 256 | style = yaml_FLOW_MAPPING_STYLE 257 | } 258 | yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) 259 | e.emit() 260 | f() 261 | yaml_mapping_end_event_initialize(&e.event) 262 | e.emit() 263 | } 264 | 265 | func (e *encoder) slicev(tag string, in reflect.Value) { 266 | implicit := tag == "" 267 | style := yaml_BLOCK_SEQUENCE_STYLE 268 | if e.flow { 269 | e.flow = false 270 | style = yaml_FLOW_SEQUENCE_STYLE 271 | } 272 | e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 273 | e.emit() 274 | n := in.Len() 275 | for i := 0; i < n; i++ { 276 | e.marshal("", in.Index(i)) 277 | } 278 | e.must(yaml_sequence_end_event_initialize(&e.event)) 279 | e.emit() 280 | } 281 | 282 | // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. 283 | // 284 | // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported 285 | // in YAML 1.2 and by this package, but these should be marshalled quoted for 286 | // the time being for compatibility with other parsers. 287 | func isBase60Float(s string) (result bool) { 288 | // Fast path. 289 | if s == "" { 290 | return false 291 | } 292 | c := s[0] 293 | if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { 294 | return false 295 | } 296 | // Do the full match. 297 | return base60float.MatchString(s) 298 | } 299 | 300 | // From http://yaml.org/type/float.html, except the regular expression there 301 | // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. 302 | var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) 303 | 304 | // isOldBool returns whether s is bool notation as defined in YAML 1.1. 305 | // 306 | // We continue to force strings that YAML 1.1 would interpret as booleans to be 307 | // rendered as quotes strings so that the marshalled output valid for YAML 1.1 308 | // parsing. 309 | func isOldBool(s string) (result bool) { 310 | switch s { 311 | case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", 312 | "n", "N", "no", "No", "NO", "off", "Off", "OFF": 313 | return true 314 | default: 315 | return false 316 | } 317 | } 318 | 319 | func (e *encoder) stringv(tag string, in reflect.Value) { 320 | var style yaml_scalar_style_t 321 | s := in.String() 322 | canUsePlain := true 323 | switch { 324 | case !utf8.ValidString(s): 325 | if tag == binaryTag { 326 | failf("explicitly tagged !!binary data must be base64-encoded") 327 | } 328 | if tag != "" { 329 | failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) 330 | } 331 | // It can't be encoded directly as YAML so use a binary tag 332 | // and encode it as base64. 333 | tag = binaryTag 334 | s = encodeBase64(s) 335 | case tag == "": 336 | // Check to see if it would resolve to a specific 337 | // tag when encoded unquoted. If it doesn't, 338 | // there's no need to quote it. 339 | rtag, _ := resolve("", s) 340 | canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) 341 | } 342 | // Note: it's possible for user code to emit invalid YAML 343 | // if they explicitly specify a tag and a string containing 344 | // text that's incompatible with that tag. 345 | switch { 346 | case strings.Contains(s, "\n"): 347 | if e.flow { 348 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 349 | } else { 350 | style = yaml_LITERAL_SCALAR_STYLE 351 | } 352 | case canUsePlain: 353 | style = yaml_PLAIN_SCALAR_STYLE 354 | default: 355 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 356 | } 357 | e.emitScalar(s, "", tag, style, nil, nil, nil, nil) 358 | } 359 | 360 | func (e *encoder) boolv(tag string, in reflect.Value) { 361 | var s string 362 | if in.Bool() { 363 | s = "true" 364 | } else { 365 | s = "false" 366 | } 367 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 368 | } 369 | 370 | func (e *encoder) intv(tag string, in reflect.Value) { 371 | s := strconv.FormatInt(in.Int(), 10) 372 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 373 | } 374 | 375 | func (e *encoder) uintv(tag string, in reflect.Value) { 376 | s := strconv.FormatUint(in.Uint(), 10) 377 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 378 | } 379 | 380 | func (e *encoder) timev(tag string, in reflect.Value) { 381 | t := in.Interface().(time.Time) 382 | s := t.Format(time.RFC3339Nano) 383 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 384 | } 385 | 386 | func (e *encoder) floatv(tag string, in reflect.Value) { 387 | // Issue #352: When formatting, use the precision of the underlying value 388 | precision := 64 389 | if in.Kind() == reflect.Float32 { 390 | precision = 32 391 | } 392 | 393 | s := strconv.FormatFloat(in.Float(), 'g', -1, precision) 394 | switch s { 395 | case "+Inf": 396 | s = ".inf" 397 | case "-Inf": 398 | s = "-.inf" 399 | case "NaN": 400 | s = ".nan" 401 | } 402 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 403 | } 404 | 405 | func (e *encoder) nilv() { 406 | e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 407 | } 408 | 409 | func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { 410 | // TODO Kill this function. Replace all initialize calls by their underlining Go literals. 411 | implicit := tag == "" 412 | if !implicit { 413 | tag = longTag(tag) 414 | } 415 | e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) 416 | e.event.head_comment = head 417 | e.event.line_comment = line 418 | e.event.foot_comment = foot 419 | e.event.tail_comment = tail 420 | e.emit() 421 | } 422 | 423 | func (e *encoder) nodev(in reflect.Value) { 424 | e.node(in.Interface().(*Node), "") 425 | } 426 | 427 | func (e *encoder) node(node *Node, tail string) { 428 | // Zero nodes behave as nil. 429 | if node.Kind == 0 && node.IsZero() { 430 | e.nilv() 431 | return 432 | } 433 | 434 | // If the tag was not explicitly requested, and dropping it won't change the 435 | // implicit tag of the value, don't include it in the presentation. 436 | var tag = node.Tag 437 | var stag = shortTag(tag) 438 | var forceQuoting bool 439 | if tag != "" && node.Style&TaggedStyle == 0 { 440 | if node.Kind == ScalarNode { 441 | if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { 442 | tag = "" 443 | } else { 444 | rtag, _ := resolve("", node.Value) 445 | if rtag == stag { 446 | tag = "" 447 | } else if stag == strTag { 448 | tag = "" 449 | forceQuoting = true 450 | } 451 | } 452 | } else { 453 | var rtag string 454 | switch node.Kind { 455 | case MappingNode: 456 | rtag = mapTag 457 | case SequenceNode: 458 | rtag = seqTag 459 | } 460 | if rtag == stag { 461 | tag = "" 462 | } 463 | } 464 | } 465 | 466 | switch node.Kind { 467 | case DocumentNode: 468 | yaml_document_start_event_initialize(&e.event, nil, nil, true) 469 | e.event.head_comment = []byte(node.HeadComment) 470 | e.emit() 471 | for _, node := range node.Content { 472 | e.node(node, "") 473 | } 474 | yaml_document_end_event_initialize(&e.event, true) 475 | e.event.foot_comment = []byte(node.FootComment) 476 | e.emit() 477 | 478 | case SequenceNode: 479 | style := yaml_BLOCK_SEQUENCE_STYLE 480 | if node.Style&FlowStyle != 0 { 481 | style = yaml_FLOW_SEQUENCE_STYLE 482 | } 483 | e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)) 484 | e.event.head_comment = []byte(node.HeadComment) 485 | e.emit() 486 | for _, node := range node.Content { 487 | e.node(node, "") 488 | } 489 | e.must(yaml_sequence_end_event_initialize(&e.event)) 490 | e.event.line_comment = []byte(node.LineComment) 491 | e.event.foot_comment = []byte(node.FootComment) 492 | e.emit() 493 | 494 | case MappingNode: 495 | style := yaml_BLOCK_MAPPING_STYLE 496 | if node.Style&FlowStyle != 0 { 497 | style = yaml_FLOW_MAPPING_STYLE 498 | } 499 | yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style) 500 | e.event.tail_comment = []byte(tail) 501 | e.event.head_comment = []byte(node.HeadComment) 502 | e.emit() 503 | 504 | // The tail logic below moves the foot comment of prior keys to the following key, 505 | // since the value for each key may be a nested structure and the foot needs to be 506 | // processed only the entirety of the value is streamed. The last tail is processed 507 | // with the mapping end event. 508 | var tail string 509 | for i := 0; i+1 < len(node.Content); i += 2 { 510 | k := node.Content[i] 511 | foot := k.FootComment 512 | if foot != "" { 513 | kopy := *k 514 | kopy.FootComment = "" 515 | k = &kopy 516 | } 517 | e.node(k, tail) 518 | tail = foot 519 | 520 | v := node.Content[i+1] 521 | e.node(v, "") 522 | } 523 | 524 | yaml_mapping_end_event_initialize(&e.event) 525 | e.event.tail_comment = []byte(tail) 526 | e.event.line_comment = []byte(node.LineComment) 527 | e.event.foot_comment = []byte(node.FootComment) 528 | e.emit() 529 | 530 | case AliasNode: 531 | yaml_alias_event_initialize(&e.event, []byte(node.Value)) 532 | e.event.head_comment = []byte(node.HeadComment) 533 | e.event.line_comment = []byte(node.LineComment) 534 | e.event.foot_comment = []byte(node.FootComment) 535 | e.emit() 536 | 537 | case ScalarNode: 538 | value := node.Value 539 | if !utf8.ValidString(value) { 540 | if stag == binaryTag { 541 | failf("explicitly tagged !!binary data must be base64-encoded") 542 | } 543 | if stag != "" { 544 | failf("cannot marshal invalid UTF-8 data as %s", stag) 545 | } 546 | // It can't be encoded directly as YAML so use a binary tag 547 | // and encode it as base64. 548 | tag = binaryTag 549 | value = encodeBase64(value) 550 | } 551 | 552 | style := yaml_PLAIN_SCALAR_STYLE 553 | switch { 554 | case node.Style&DoubleQuotedStyle != 0: 555 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 556 | case node.Style&SingleQuotedStyle != 0: 557 | style = yaml_SINGLE_QUOTED_SCALAR_STYLE 558 | case node.Style&LiteralStyle != 0: 559 | style = yaml_LITERAL_SCALAR_STYLE 560 | case node.Style&FoldedStyle != 0: 561 | style = yaml_FOLDED_SCALAR_STYLE 562 | case strings.Contains(value, "\n"): 563 | style = yaml_LITERAL_SCALAR_STYLE 564 | case forceQuoting: 565 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 566 | } 567 | 568 | e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) 569 | default: 570 | failf("cannot encode node with unknown kind %d", node.Kind) 571 | } 572 | } 573 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/xgb.go: -------------------------------------------------------------------------------- 1 | package xgb 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "log" 7 | "net" 8 | "os" 9 | "sync" 10 | ) 11 | 12 | var ( 13 | // Where to log error-messages. Defaults to stderr. 14 | // To disable logging, just set this to log.New(ioutil.Discard, "", 0) 15 | Logger = log.New(os.Stderr, "XGB: ", log.Lshortfile) 16 | ) 17 | 18 | const ( 19 | // cookieBuffer represents the queue size of cookies existing at any 20 | // point in time. The size of the buffer is really only important when 21 | // there are many requests without replies made in sequence. Once the 22 | // buffer fills, a round trip request is made to clear the buffer. 23 | cookieBuffer = 1000 24 | 25 | // xidBuffer represents the queue size of the xid channel. 26 | // I don't think this value matters much, since xid generation is not 27 | // that expensive. 28 | xidBuffer = 5 29 | 30 | // seqBuffer represents the queue size of the sequence number channel. 31 | // I don't think this value matters much, since sequence number generation 32 | // is not that expensive. 33 | seqBuffer = 5 34 | 35 | // reqBuffer represents the queue size of the number of requests that 36 | // can be made until new ones block. This value seems OK. 37 | reqBuffer = 100 38 | 39 | // eventBuffer represents the queue size of the number of events or errors 40 | // that can be loaded off the wire and not grabbed with WaitForEvent 41 | // until reading an event blocks. This value should be big enough to handle 42 | // bursts of events. 43 | eventBuffer = 5000 44 | ) 45 | 46 | // A Conn represents a connection to an X server. 47 | type Conn struct { 48 | host string 49 | conn net.Conn 50 | display string 51 | DisplayNumber int 52 | DefaultScreen int 53 | SetupBytes []byte 54 | 55 | setupResourceIdBase uint32 56 | setupResourceIdMask uint32 57 | 58 | eventChan chan eventOrError 59 | cookieChan chan *Cookie 60 | xidChan chan xid 61 | seqChan chan uint16 62 | reqChan chan *request 63 | closing chan chan struct{} 64 | 65 | // ExtLock is a lock used whenever new extensions are initialized. 66 | // It should not be used. It is exported for use in the extension 67 | // sub-packages. 68 | ExtLock sync.RWMutex 69 | 70 | // Extensions is a map from extension name to major opcode. It should 71 | // not be used. It is exported for use in the extension sub-packages. 72 | Extensions map[string]byte 73 | } 74 | 75 | // NewConn creates a new connection instance. It initializes locks, data 76 | // structures, and performs the initial handshake. (The code for the handshake 77 | // has been relegated to conn.go.) 78 | func NewConn() (*Conn, error) { 79 | return NewConnDisplay("") 80 | } 81 | 82 | // NewConnDisplay is just like NewConn, but allows a specific DISPLAY 83 | // string to be used. 84 | // If 'display' is empty it will be taken from os.Getenv("DISPLAY"). 85 | // 86 | // Examples: 87 | // NewConn(":1") -> net.Dial("unix", "", "/tmp/.X11-unix/X1") 88 | // NewConn("/tmp/launch-12/:0") -> net.Dial("unix", "", "/tmp/launch-12/:0") 89 | // NewConn("hostname:2.1") -> net.Dial("tcp", "", "hostname:6002") 90 | // NewConn("tcp/hostname:1.0") -> net.Dial("tcp", "", "hostname:6001") 91 | func NewConnDisplay(display string) (*Conn, error) { 92 | conn := &Conn{} 93 | 94 | // First connect. This reads authority, checks DISPLAY environment 95 | // variable, and loads the initial Setup info. 96 | err := conn.connect(display) 97 | if err != nil { 98 | return nil, err 99 | } 100 | 101 | return postNewConn(conn) 102 | } 103 | 104 | // NewConnDisplay is just like NewConn, but allows a specific net.Conn 105 | // to be used. 106 | func NewConnNet(netConn net.Conn) (*Conn, error) { 107 | conn := &Conn{} 108 | 109 | // First connect. This reads authority, checks DISPLAY environment 110 | // variable, and loads the initial Setup info. 111 | err := conn.connectNet(netConn) 112 | 113 | if err != nil { 114 | return nil, err 115 | } 116 | 117 | return postNewConn(conn) 118 | } 119 | 120 | func postNewConn(conn *Conn) (*Conn, error) { 121 | conn.Extensions = make(map[string]byte) 122 | 123 | conn.cookieChan = make(chan *Cookie, cookieBuffer) 124 | conn.xidChan = make(chan xid, xidBuffer) 125 | conn.seqChan = make(chan uint16, seqBuffer) 126 | conn.reqChan = make(chan *request, reqBuffer) 127 | conn.eventChan = make(chan eventOrError, eventBuffer) 128 | conn.closing = make(chan chan struct{}, 1) 129 | 130 | go conn.generateXIds() 131 | go conn.generateSeqIds() 132 | go conn.sendRequests() 133 | go conn.readResponses() 134 | 135 | return conn, nil 136 | } 137 | 138 | // Close gracefully closes the connection to the X server. 139 | func (c *Conn) Close() { 140 | close(c.reqChan) 141 | } 142 | 143 | // Event is an interface that can contain any of the events returned by the 144 | // server. Use a type assertion switch to extract the Event structs. 145 | type Event interface { 146 | Bytes() []byte 147 | String() string 148 | } 149 | 150 | // NewEventFun is the type of function use to construct events from raw bytes. 151 | // It should not be used. It is exported for use in the extension sub-packages. 152 | type NewEventFun func(buf []byte) Event 153 | 154 | // NewEventFuncs is a map from event numbers to functions that create 155 | // the corresponding event. It should not be used. It is exported for use 156 | // in the extension sub-packages. 157 | var NewEventFuncs = make(map[int]NewEventFun) 158 | 159 | // NewExtEventFuncs is a temporary map that stores event constructor functions 160 | // for each extension. When an extension is initialized, each event for that 161 | // extension is added to the 'NewEventFuncs' map. It should not be used. It is 162 | // exported for use in the extension sub-packages. 163 | var NewExtEventFuncs = make(map[string]map[int]NewEventFun) 164 | 165 | // Error is an interface that can contain any of the errors returned by 166 | // the server. Use a type assertion switch to extract the Error structs. 167 | type Error interface { 168 | SequenceId() uint16 169 | BadId() uint32 170 | Error() string 171 | } 172 | 173 | // NewErrorFun is the type of function use to construct errors from raw bytes. 174 | // It should not be used. It is exported for use in the extension sub-packages. 175 | type NewErrorFun func(buf []byte) Error 176 | 177 | // NewErrorFuncs is a map from error numbers to functions that create 178 | // the corresponding error. It should not be used. It is exported for use in 179 | // the extension sub-packages. 180 | var NewErrorFuncs = make(map[int]NewErrorFun) 181 | 182 | // NewExtErrorFuncs is a temporary map that stores error constructor functions 183 | // for each extension. When an extension is initialized, each error for that 184 | // extension is added to the 'NewErrorFuncs' map. It should not be used. It is 185 | // exported for use in the extension sub-packages. 186 | var NewExtErrorFuncs = make(map[string]map[int]NewErrorFun) 187 | 188 | // eventOrError corresponds to values that can be either an event or an 189 | // error. 190 | type eventOrError interface{} 191 | 192 | // NewId generates a new unused ID for use with requests like CreateWindow. 193 | // If no new ids can be generated, the id returned is 0 and error is non-nil. 194 | // This shouldn't be used directly, and is exported for use in the extension 195 | // sub-packages. 196 | // If you need identifiers, use the appropriate constructor. 197 | // e.g., For a window id, use xproto.NewWindowId. For 198 | // a new pixmap id, use xproto.NewPixmapId. And so on. 199 | func (c *Conn) NewId() (uint32, error) { 200 | xid := <-c.xidChan 201 | if xid.err != nil { 202 | return 0, xid.err 203 | } 204 | return xid.id, nil 205 | } 206 | 207 | // xid encapsulates a resource identifier being sent over the Conn.xidChan 208 | // channel. If no new resource id can be generated, id is set to 0 and a 209 | // non-nil error is set in xid.err. 210 | type xid struct { 211 | id uint32 212 | err error 213 | } 214 | 215 | // generateXids sends new Ids down the channel for NewId to use. 216 | // generateXids should be run in its own goroutine. 217 | // This needs to be updated to use the XC Misc extension once we run out of 218 | // new ids. 219 | // Thanks to libxcb/src/xcb_xid.c. This code is greatly inspired by it. 220 | func (conn *Conn) generateXIds() { 221 | defer close(conn.xidChan) 222 | 223 | // This requires some explanation. From the horse's mouth: 224 | // "The resource-id-mask contains a single contiguous set of bits (at least 225 | // 18). The client allocates resource IDs for types WINDOW, PIXMAP, 226 | // CURSOR, FONT, GCONTEXT, and COLORMAP by choosing a value with only some 227 | // subset of these bits set and ORing it with resource-id-base. Only values 228 | // constructed in this way can be used to name newly created resources over 229 | // this connection." 230 | // So for example (using 8 bit integers), the mask might look like: 231 | // 00111000 232 | // So that valid values would be 00101000, 00110000, 00001000, and so on. 233 | // Thus, the idea is to increment it by the place of the last least 234 | // significant '1'. In this case, that value would be 00001000. To get 235 | // that value, we can AND the original mask with its two's complement: 236 | // 00111000 & 11001000 = 00001000. 237 | // And we use that value to increment the last resource id to get a new one. 238 | // (And then, of course, we OR it with resource-id-base.) 239 | inc := conn.setupResourceIdMask & -conn.setupResourceIdMask 240 | max := conn.setupResourceIdMask 241 | last := uint32(0) 242 | for { 243 | // TODO: Use the XC Misc extension to look for released ids. 244 | if last > 0 && last >= max-inc+1 { 245 | conn.xidChan <- xid{ 246 | id: 0, 247 | err: errors.New("There are no more available resource" + 248 | "identifiers."), 249 | } 250 | } 251 | 252 | last += inc 253 | conn.xidChan <- xid{ 254 | id: last | conn.setupResourceIdBase, 255 | err: nil, 256 | } 257 | } 258 | } 259 | 260 | // newSeqId fetches the next sequence id from the Conn.seqChan channel. 261 | func (c *Conn) newSequenceId() uint16 { 262 | return <-c.seqChan 263 | } 264 | 265 | // generateSeqIds returns new sequence ids. It is meant to be run in its 266 | // own goroutine. 267 | // A sequence id is generated for *every* request. It's the identifier used 268 | // to match up replies with requests. 269 | // Since sequence ids can only be 16 bit integers we start over at zero when it 270 | // comes time to wrap. 271 | // N.B. As long as the cookie buffer is less than 2^16, there are no limitations 272 | // on the number (or kind) of requests made in sequence. 273 | func (c *Conn) generateSeqIds() { 274 | defer close(c.seqChan) 275 | 276 | seqid := uint16(1) 277 | for { 278 | c.seqChan <- seqid 279 | if seqid == uint16((1<<16)-1) { 280 | seqid = 0 281 | } else { 282 | seqid++ 283 | } 284 | } 285 | } 286 | 287 | // request encapsulates a buffer of raw bytes (containing the request data) 288 | // and a cookie, which when combined represents a single request. 289 | // The cookie is used to match up the reply/error. 290 | type request struct { 291 | buf []byte 292 | cookie *Cookie 293 | 294 | // seq is closed when the request (cookie) has been sequenced by the Conn. 295 | seq chan struct{} 296 | } 297 | 298 | // NewRequest takes the bytes and a cookie of a particular request, constructs 299 | // a request type, and sends it over the Conn.reqChan channel. 300 | // Note that the sequence number is added to the cookie after it is sent 301 | // over the request channel, but before it is sent to X. 302 | // 303 | // Note that you may safely use NewRequest to send arbitrary byte requests 304 | // to X. The resulting cookie can be used just like any normal cookie and 305 | // abides by the same rules, except that for replies, you'll get back the 306 | // raw byte data. This may be useful for performance critical sections where 307 | // every allocation counts, since all X requests in XGB allocate a new byte 308 | // slice. In contrast, NewRequest allocates one small request struct and 309 | // nothing else. (Except when the cookie buffer is full and has to be flushed.) 310 | // 311 | // If you're using NewRequest manually, you'll need to use NewCookie to create 312 | // a new cookie. 313 | // 314 | // In all likelihood, you should be able to copy and paste with some minor 315 | // edits the generated code for the request you want to issue. 316 | func (c *Conn) NewRequest(buf []byte, cookie *Cookie) { 317 | seq := make(chan struct{}) 318 | c.reqChan <- &request{buf: buf, cookie: cookie, seq: seq} 319 | <-seq 320 | } 321 | 322 | // sendRequests is run as a single goroutine that takes requests and writes 323 | // the bytes to the wire and adds the cookie to the cookie queue. 324 | // It is meant to be run as its own goroutine. 325 | func (c *Conn) sendRequests() { 326 | defer close(c.cookieChan) 327 | 328 | for req := range c.reqChan { 329 | // ho there! if the cookie channel is nearly full, force a round 330 | // trip to clear out the cookie buffer. 331 | // Note that we circumvent the request channel, because we're *in* 332 | // the request channel. 333 | if len(c.cookieChan) == cookieBuffer-1 { 334 | if err := c.noop(); err != nil { 335 | // Shut everything down. 336 | break 337 | } 338 | } 339 | req.cookie.Sequence = c.newSequenceId() 340 | c.cookieChan <- req.cookie 341 | c.writeBuffer(req.buf) 342 | close(req.seq) 343 | } 344 | response := make(chan struct{}) 345 | c.closing <- response 346 | c.noop() // Flush the response reading goroutine, ignore error. 347 | <-response 348 | c.conn.Close() 349 | } 350 | 351 | // noop circumvents the usual request sending goroutines and forces a round 352 | // trip request manually. 353 | func (c *Conn) noop() error { 354 | cookie := c.NewCookie(true, true) 355 | cookie.Sequence = c.newSequenceId() 356 | c.cookieChan <- cookie 357 | if err := c.writeBuffer(c.getInputFocusRequest()); err != nil { 358 | return err 359 | } 360 | cookie.Reply() // wait for the buffer to clear 361 | return nil 362 | } 363 | 364 | // writeBuffer is a convenience function for writing a byte slice to the wire. 365 | func (c *Conn) writeBuffer(buf []byte) error { 366 | if _, err := c.conn.Write(buf); err != nil { 367 | Logger.Printf("A write error is unrecoverable: %s", err) 368 | return err 369 | } else { 370 | return nil 371 | } 372 | } 373 | 374 | // readResponses is a goroutine that reads events, errors and 375 | // replies off the wire. 376 | // When an event is read, it is always added to the event channel. 377 | // When an error is read, if it corresponds to an existing checked cookie, 378 | // it is sent to that cookie's error channel. Otherwise it is added to the 379 | // event channel. 380 | // When a reply is read, it is added to the corresponding cookie's reply 381 | // channel. (It is an error if no such cookie exists in this case.) 382 | // Finally, cookies that came "before" this reply are always cleaned up. 383 | func (c *Conn) readResponses() { 384 | defer close(c.eventChan) 385 | 386 | var ( 387 | err Error 388 | seq uint16 389 | replyBytes []byte 390 | ) 391 | 392 | for { 393 | select { 394 | case respond := <-c.closing: 395 | respond <- struct{}{} 396 | return 397 | default: 398 | } 399 | 400 | buf := make([]byte, 32) 401 | err, seq = nil, 0 402 | if _, err := io.ReadFull(c.conn, buf); err != nil { 403 | Logger.Printf("A read error is unrecoverable: %s", err) 404 | c.eventChan <- err 405 | c.Close() 406 | continue 407 | } 408 | switch buf[0] { 409 | case 0: // This is an error 410 | // Use the constructor function for this error (that is auto 411 | // generated) by looking it up by the error number. 412 | newErrFun, ok := NewErrorFuncs[int(buf[1])] 413 | if !ok { 414 | Logger.Printf("BUG: Could not find error constructor function "+ 415 | "for error with number %d.", buf[1]) 416 | continue 417 | } 418 | err = newErrFun(buf) 419 | seq = err.SequenceId() 420 | 421 | // This error is either sent to the event channel or a specific 422 | // cookie's error channel below. 423 | case 1: // This is a reply 424 | seq = Get16(buf[2:]) 425 | 426 | // check to see if this reply has more bytes to be read 427 | size := Get32(buf[4:]) 428 | if size > 0 { 429 | byteCount := 32 + size*4 430 | biggerBuf := make([]byte, byteCount) 431 | copy(biggerBuf[:32], buf) 432 | if _, err := io.ReadFull(c.conn, biggerBuf[32:]); err != nil { 433 | Logger.Printf("A read error is unrecoverable: %s", err) 434 | c.eventChan <- err 435 | c.Close() 436 | continue 437 | } 438 | replyBytes = biggerBuf 439 | } else { 440 | replyBytes = buf 441 | } 442 | 443 | // This reply is sent to its corresponding cookie below. 444 | default: // This is an event 445 | // Use the constructor function for this event (like for errors, 446 | // and is also auto generated) by looking it up by the event number. 447 | // Note that we AND the event number with 127 so that we ignore 448 | // the most significant bit (which is set when it was sent from 449 | // a SendEvent request). 450 | evNum := int(buf[0] & 127) 451 | newEventFun, ok := NewEventFuncs[evNum] 452 | if !ok { 453 | Logger.Printf("BUG: Could not find event construct function "+ 454 | "for event with number %d.", evNum) 455 | continue 456 | } 457 | c.eventChan <- newEventFun(buf) 458 | continue 459 | } 460 | 461 | // At this point, we have a sequence number and we're either 462 | // processing an error or a reply, which are both responses to 463 | // requests. So all we have to do is find the cookie corresponding 464 | // to this error/reply, and send the appropriate data to it. 465 | // In doing so, we make sure that any cookies that came before it 466 | // are marked as successful if they are void and checked. 467 | // If there's a cookie that requires a reply that is before this 468 | // reply, then something is wrong. 469 | for cookie := range c.cookieChan { 470 | // This is the cookie we're looking for. Process and break. 471 | if cookie.Sequence == seq { 472 | if err != nil { // this is an error to a request 473 | // synchronous processing 474 | if cookie.errorChan != nil { 475 | cookie.errorChan <- err 476 | } else { // asynchronous processing 477 | c.eventChan <- err 478 | // if this is an unchecked reply, ping the cookie too 479 | if cookie.pingChan != nil { 480 | cookie.pingChan <- true 481 | } 482 | } 483 | } else { // this is a reply 484 | if cookie.replyChan == nil { 485 | Logger.Printf("Reply with sequence id %d does not "+ 486 | "have a cookie with a valid reply channel.", seq) 487 | continue 488 | } else { 489 | cookie.replyChan <- replyBytes 490 | } 491 | } 492 | break 493 | } 494 | 495 | switch { 496 | // Checked requests with replies 497 | case cookie.replyChan != nil && cookie.errorChan != nil: 498 | Logger.Printf("Found cookie with sequence id %d that is "+ 499 | "expecting a reply but will never get it. Currently "+ 500 | "on sequence number %d", cookie.Sequence, seq) 501 | // Unchecked requests with replies 502 | case cookie.replyChan != nil && cookie.pingChan != nil: 503 | Logger.Printf("Found cookie with sequence id %d that is "+ 504 | "expecting a reply (and not an error) but will never "+ 505 | "get it. Currently on sequence number %d", 506 | cookie.Sequence, seq) 507 | // Checked requests without replies 508 | case cookie.pingChan != nil && cookie.errorChan != nil: 509 | cookie.pingChan <- true 510 | // Unchecked requests without replies don't have any channels, 511 | // so we can't do anything with them except let them pass by. 512 | } 513 | } 514 | } 515 | } 516 | 517 | // processEventOrError takes an eventOrError, type switches on it, 518 | // and returns it in Go idiomatic style. 519 | func processEventOrError(everr eventOrError) (Event, Error) { 520 | switch ee := everr.(type) { 521 | case Event: 522 | return ee, nil 523 | case Error: 524 | return nil, ee 525 | default: 526 | Logger.Printf("Invalid event/error type: %T", everr) 527 | return nil, nil 528 | } 529 | } 530 | 531 | // WaitForEvent returns the next event from the server. 532 | // It will block until an event is available. 533 | // WaitForEvent returns either an Event or an Error. (Returning both 534 | // is a bug.) Note than an Error here is an X error and not an XGB error. That 535 | // is, X errors are sometimes completely expected (and you may want to ignore 536 | // them in some cases). 537 | // 538 | // If both the event and error are nil, then the connection has been closed. 539 | func (c *Conn) WaitForEvent() (Event, Error) { 540 | return processEventOrError(<-c.eventChan) 541 | } 542 | 543 | // PollForEvent returns the next event from the server if one is available in 544 | // the internal queue without blocking. Note that unlike WaitForEvent, both 545 | // Event and Error could be nil. Indeed, they are both nil when the event queue 546 | // is empty. 547 | func (c *Conn) PollForEvent() (Event, Error) { 548 | select { 549 | case everr := <-c.eventChan: 550 | return processEventOrError(everr) 551 | default: 552 | return nil, nil 553 | } 554 | } 555 | --------------------------------------------------------------------------------