Initial commit
This commit is contained in:
25
vendor/go.i3wm.org/i3/.travis.yml
generated
vendored
Normal file
25
vendor/go.i3wm.org/i3/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# Use the (faster) container-based infrastructure, see also
|
||||
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||
sudo: false
|
||||
dist: trusty
|
||||
services:
|
||||
- docker
|
||||
|
||||
language: go
|
||||
go:
|
||||
- "1.12"
|
||||
go_import_path: go.i3wm.org/i3
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "c9Q/SwO3gQ0rCJDCk9V9NgchFVCUj5+dIJbKqUcbXpdtaG8WLNWEamrv4nqbrLWw/I9Zw9St4VErqbqxU4Fee9ftt+PBbtMOi/eZ6LbnnDjJCYnz912Wf7v05L5Ghl1WNf8ajNCjvBpsJMe6UixBmZHeBqSin5OUT55gN+wMi68VaxTbo0DchXjuU7hXfDvV4iVvmtQmP9C4yQDz9HiEdaDz9SP0HNCzngONikld8rGSRX+V2XyaJsd13hJQM0ZfCXuRS0J0OBLz3msiSbobuWUzApI/s81AHUdkj3JbXfrxaA0kj75fZquqFyTaiNQRjKVqwXGGaMWcVNFOeDv3l5n6HMuImjxEypDwFcjbDo/9ivGeG31f7oh+lB/JkhRoZmW6tiIlRSy4rdVKmzn0ENIvxPcEFzqoLdail5ffM/b7KS2eGetTG/86PaLbk4XoQDzVcOWgOjeNn/meOAgUBEEMauZQZhF57Bs7ev62BReu8PbTXTyCVhpR77oAnt9dfW1tEQTrUBH9vG32V+/aC6YmrD/NM4t4VpvyJr/npXGyTW9JJ9bPLfHZEXjdoyOtBPNuousa7rM6hD2hbQ+/lVuEZp2c2RIIpuP5cG4MzHmA4W1GsDRbm/zG87VKt2zaKA2tuXPULDvlux49zI19OZB7sSYm3RgVN/+6YhYPwSs=" # PERSIST_URL
|
||||
|
||||
script:
|
||||
# Check whether files are syntactically correct.
|
||||
- "gofmt -l $(find . -name '*.go' | tr '\\n' ' ') >/dev/null"
|
||||
# Check whether files were not gofmt'ed.
|
||||
- "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)"
|
||||
- go vet .
|
||||
- go test -c
|
||||
- docker build --pull --no-cache --rm -t=goi3 -f travis/Dockerfile .
|
||||
- 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
|
||||
27
vendor/go.i3wm.org/i3/LICENSE
generated
vendored
Normal file
27
vendor/go.i3wm.org/i3/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright © 2017, Michael Stapelberg and contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Michael Stapelberg nor the
|
||||
names of contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Michael Stapelberg ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL Michael Stapelberg BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
60
vendor/go.i3wm.org/i3/README.md
generated
vendored
Normal file
60
vendor/go.i3wm.org/i3/README.md
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
[](https://travis-ci.org/i3/go-i3)
|
||||
[](https://goreportcard.com/report/go.i3wm.org/i3)
|
||||
[](https://godoc.org/go.i3wm.org/i3)
|
||||
|
||||
Package i3 provides a convenient interface to the i3 window manager via [its IPC
|
||||
interface](https://i3wm.org/docs/ipc.html).
|
||||
|
||||
See [its documentation](https://godoc.org/go.i3wm.org/i3) for more details.
|
||||
|
||||
## Advantages over other i3 IPC packages
|
||||
|
||||
Here comes a grab bag of features to which we paid attention. At the time of
|
||||
writing, most other i3 IPC packages lack at least a good number of these
|
||||
features:
|
||||
|
||||
* Retries are transparently handled: programs using this package will recover
|
||||
automatically from in-place i3 restarts. Additionally, programs can be started
|
||||
from xsession or user sessions before i3 is even running.
|
||||
|
||||
* Version checks are transparently handled: if your program uses features which
|
||||
are not supported by the running i3 version, helpful error messages will be
|
||||
returned at run time.
|
||||
|
||||
* Comprehensive: the entire documented IPC interface of the latest stable i3
|
||||
version is covered by this package. Tagged releases match i3’s major and minor
|
||||
version.
|
||||
|
||||
* Consistent and familiar: once familiar with the i3 IPC protocol’s features,
|
||||
you should have no trouble matching the documentation to API and vice-versa.
|
||||
|
||||
* Good test coverage (hard to display in a badge, as our multi-process setup
|
||||
breaks `go test`’s `-coverprofile` flag).
|
||||
|
||||
* Implemented in pure Go, without resorting to the `unsafe` package.
|
||||
|
||||
* Works on little and big endian architectures.
|
||||
|
||||
## Scope
|
||||
|
||||
i3’s entire documented IPC interface is available in this package.
|
||||
|
||||
In addition, helper functions which are useful for a broad range of programs
|
||||
(and only those!) are provided, e.g. Node’s FindChild and FindFocused.
|
||||
|
||||
Packages which introduce higher-level abstractions should feel free to use this
|
||||
package as a building block.
|
||||
|
||||
## Assumptions
|
||||
|
||||
* The `i3(1)` binary must be in `$PATH` so that the IPC socket path can be retrieved.
|
||||
* For transparent version checks to work, the running i3 version must be ≥ 4.3 (released 2012-09-19).
|
||||
|
||||
## Testing
|
||||
|
||||
Be sure to include the target i3 version (the most recent stable release) in
|
||||
`$PATH` and use `go test` as usual:
|
||||
|
||||
```shell
|
||||
PATH=~/i3/build/i3:$PATH go test -v go.i3wm.org/i3
|
||||
```
|
||||
111
vendor/go.i3wm.org/i3/barconfig.go
generated
vendored
Normal file
111
vendor/go.i3wm.org/i3/barconfig.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
package i3
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// BarConfigColors describes a serialized bar colors configuration block.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_bar_config_reply for more details.
|
||||
type BarConfigColors struct {
|
||||
Background string `json:"background"`
|
||||
Statusline string `json:"statusline"`
|
||||
Separator string `json:"separator"`
|
||||
|
||||
FocusedBackground string `json:"focused_background"`
|
||||
FocusedStatusline string `json:"focused_statusline"`
|
||||
FocusedSeparator string `json:"focused_separator"`
|
||||
|
||||
FocusedWorkspaceText string `json:"focused_workspace_text"`
|
||||
FocusedWorkspaceBackground string `json:"focused_workspace_bg"`
|
||||
FocusedWorkspaceBorder string `json:"focused_workspace_border"`
|
||||
|
||||
ActiveWorkspaceText string `json:"active_workspace_text"`
|
||||
ActiveWorkspaceBackground string `json:"active_workspace_bg"`
|
||||
ActiveWorkspaceBorder string `json:"active_workspace_border"`
|
||||
|
||||
InactiveWorkspaceText string `json:"inactive_workspace_text"`
|
||||
InactiveWorkspaceBackground string `json:"inactive_workspace_bg"`
|
||||
InactiveWorkspaceBorder string `json:"inactive_workspace_border"`
|
||||
|
||||
UrgentWorkspaceText string `json:"urgent_workspace_text"`
|
||||
UrgentWorkspaceBackground string `json:"urgent_workspace_bg"`
|
||||
UrgentWorkspaceBorder string `json:"urgent_workspace_border"`
|
||||
|
||||
BindingModeText string `json:"binding_mode_text"`
|
||||
BindingModeBackground string `json:"binding_mode_bg"`
|
||||
BindingModeBorder string `json:"binding_mode_border"`
|
||||
}
|
||||
|
||||
// BarConfig describes a serialized bar configuration block.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_bar_config_reply for more details.
|
||||
type BarConfig struct {
|
||||
ID string `json:"id"`
|
||||
Mode string `json:"mode"`
|
||||
Position string `json:"position"`
|
||||
StatusCommand string `json:"status_command"`
|
||||
Font string `json:"font"`
|
||||
WorkspaceButtons bool `json:"workspace_buttons"`
|
||||
BindingModeIndicator bool `json:"binding_mode_indicator"`
|
||||
Verbose bool `json:"verbose"`
|
||||
Colors BarConfigColors `json:"colors"`
|
||||
}
|
||||
|
||||
// GetBarIDs returns an array of configured bar IDs.
|
||||
//
|
||||
// GetBarIDs is supported in i3 ≥ v4.1 (2011-11-11).
|
||||
func GetBarIDs() ([]string, error) {
|
||||
reply, err := roundTrip(messageTypeGetBarConfig, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ids []string
|
||||
err = json.Unmarshal(reply.Payload, &ids)
|
||||
return ids, err
|
||||
}
|
||||
|
||||
// GetBarConfig returns the configuration for the bar with the specified barID.
|
||||
//
|
||||
// Obtain the barID from GetBarIDs.
|
||||
//
|
||||
// GetBarConfig is supported in i3 ≥ v4.1 (2011-11-11).
|
||||
func GetBarConfig(barID string) (BarConfig, error) {
|
||||
reply, err := roundTrip(messageTypeGetBarConfig, []byte(barID))
|
||||
if err != nil {
|
||||
return BarConfig{}, err
|
||||
}
|
||||
|
||||
cfg := BarConfig{
|
||||
Colors: BarConfigColors{
|
||||
Background: "#000000",
|
||||
Statusline: "#ffffff",
|
||||
Separator: "#666666",
|
||||
|
||||
FocusedBackground: "#000000",
|
||||
FocusedStatusline: "#ffffff",
|
||||
FocusedSeparator: "#666666",
|
||||
|
||||
FocusedWorkspaceText: "#4c7899",
|
||||
FocusedWorkspaceBackground: "#285577",
|
||||
FocusedWorkspaceBorder: "#ffffff",
|
||||
|
||||
ActiveWorkspaceText: "#333333",
|
||||
ActiveWorkspaceBackground: "#5f676a",
|
||||
ActiveWorkspaceBorder: "#ffffff",
|
||||
|
||||
InactiveWorkspaceText: "#333333",
|
||||
InactiveWorkspaceBackground: "#222222",
|
||||
InactiveWorkspaceBorder: "#888888",
|
||||
|
||||
UrgentWorkspaceText: "#2f343a",
|
||||
UrgentWorkspaceBackground: "#900000",
|
||||
UrgentWorkspaceBorder: "#ffffff",
|
||||
|
||||
BindingModeText: "#2f343a",
|
||||
BindingModeBackground: "#900000",
|
||||
BindingModeBorder: "#ffffff",
|
||||
},
|
||||
}
|
||||
err = json.Unmarshal(reply.Payload, &cfg)
|
||||
return cfg, err
|
||||
}
|
||||
17
vendor/go.i3wm.org/i3/bindingmodes.go
generated
vendored
Normal file
17
vendor/go.i3wm.org/i3/bindingmodes.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package i3
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// GetBindingModes returns the names of all currently configured binding modes.
|
||||
//
|
||||
// GetBindingModes is supported in i3 ≥ v4.13 (2016-11-08).
|
||||
func GetBindingModes() ([]string, error) {
|
||||
reply, err := roundTrip(messageTypeGetBindingModes, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var bm []string
|
||||
err = json.Unmarshal(reply.Payload, &bm)
|
||||
return bm, err
|
||||
}
|
||||
63
vendor/go.i3wm.org/i3/byteorder.go
generated
vendored
Normal file
63
vendor/go.i3wm.org/i3/byteorder.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package i3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// detectByteOrder sends messages to i3 to determine the byte order it uses.
|
||||
// For details on this technique, see:
|
||||
// https://build.i3wm.org/docs/ipc.html#_appendix_a_detecting_byte_order_in_memory_safe_languages
|
||||
func detectByteOrder(conn io.ReadWriter) (binary.ByteOrder, error) {
|
||||
const (
|
||||
// targetLen is 0x00 01 01 00 in both, big and little endian
|
||||
targetLen = 65536 + 256
|
||||
|
||||
// SUBSCRIBE was introduced in 3.e (2010-03-30)
|
||||
prefixSubscribe = "[]"
|
||||
|
||||
// RUN_COMMAND was always present
|
||||
prefixCmd = "nop byte-order detection. padding: "
|
||||
)
|
||||
|
||||
// 2. Send a big endian encoded message of type SUBSCRIBE:
|
||||
payload := []byte(prefixSubscribe + strings.Repeat(" ", targetLen-len(prefixSubscribe)))
|
||||
if err := binary.Write(conn, binary.BigEndian, &header{magic, uint32(len(payload)), messageTypeSubscribe}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := conn.Write(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 3. Send a byte order independent RUN_COMMAND message:
|
||||
payload = []byte(prefixCmd + strings.Repeat("a", targetLen-len(prefixCmd)))
|
||||
if err := binary.Write(conn, binary.BigEndian, &header{magic, uint32(len(payload)), messageTypeRunCommand}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := conn.Write(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4. Receive a message header, decode the message type as big endian:
|
||||
var header [14]byte
|
||||
if _, err := io.ReadFull(conn, header[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if messageType(binary.BigEndian.Uint32(header[10:14])) == messageReplyTypeCommand {
|
||||
order := binary.LittleEndian // our big endian message was not answered
|
||||
// Read remaining payload
|
||||
_, err := io.ReadFull(conn, make([]byte, order.Uint32(header[6:10])))
|
||||
return order, err
|
||||
}
|
||||
order := binary.BigEndian // our big endian message was answered
|
||||
// Read remaining payload
|
||||
if _, err := io.ReadFull(conn, make([]byte, order.Uint32(header[6:10]))); err != nil {
|
||||
return order, err
|
||||
}
|
||||
|
||||
// Slurp the pending RUN_COMMAND reply.
|
||||
sock := &socket{conn: conn, order: order}
|
||||
_, err := sock.recvMsg()
|
||||
return binary.BigEndian, err
|
||||
}
|
||||
64
vendor/go.i3wm.org/i3/command.go
generated
vendored
Normal file
64
vendor/go.i3wm.org/i3/command.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package i3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CommandResult always contains Success, and command-specific fields where
|
||||
// appropriate.
|
||||
type CommandResult struct {
|
||||
// Success indicates whether the command was run without any errors.
|
||||
Success bool `json:"success"`
|
||||
|
||||
// Error is a human-readable error message, non-empty for unsuccessful
|
||||
// commands.
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// IsUnsuccessful is a convenience function which can be used to check if an
|
||||
// error is a CommandUnsuccessfulError.
|
||||
func IsUnsuccessful(err error) bool {
|
||||
_, ok := err.(*CommandUnsuccessfulError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// CommandUnsuccessfulError is returned by RunCommand for unsuccessful
|
||||
// commands. This type is exported so that you can ignore this error if you
|
||||
// expect your command(s) to fail.
|
||||
type CommandUnsuccessfulError struct {
|
||||
command string
|
||||
cr CommandResult
|
||||
}
|
||||
|
||||
// Error implements error.
|
||||
func (e *CommandUnsuccessfulError) Error() string {
|
||||
return fmt.Sprintf("command %q unsuccessful: %v", e.command, e.cr.Error)
|
||||
}
|
||||
|
||||
// RunCommand makes i3 run the specified command.
|
||||
//
|
||||
// Error is non-nil if any CommandResult.Success is not true. See IsUnsuccessful
|
||||
// if you send commands which are expected to fail.
|
||||
//
|
||||
// RunCommand is supported in i3 ≥ v4.0 (2011-07-31).
|
||||
func RunCommand(command string) ([]CommandResult, error) {
|
||||
reply, err := roundTrip(messageTypeRunCommand, []byte(command))
|
||||
if err != nil {
|
||||
return []CommandResult{}, err
|
||||
}
|
||||
|
||||
var crs []CommandResult
|
||||
err = json.Unmarshal(reply.Payload, &crs)
|
||||
if err == nil {
|
||||
for _, cr := range crs {
|
||||
if !cr.Success {
|
||||
return crs, &CommandUnsuccessfulError{
|
||||
command: command,
|
||||
cr: cr,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return crs, err
|
||||
}
|
||||
24
vendor/go.i3wm.org/i3/config.go
generated
vendored
Normal file
24
vendor/go.i3wm.org/i3/config.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package i3
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// Config contains details about the configuration file.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_config_reply for more details.
|
||||
type Config struct {
|
||||
Config string `json:"config"`
|
||||
}
|
||||
|
||||
// GetConfig returns i3’s in-memory copy of the configuration file contents.
|
||||
//
|
||||
// GetConfig is supported in i3 ≥ v4.14 (2017-09-04).
|
||||
func GetConfig() (Config, error) {
|
||||
reply, err := roundTrip(messageTypeGetConfig, nil)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
err = json.Unmarshal(reply.Payload, &cfg)
|
||||
return cfg, err
|
||||
}
|
||||
22
vendor/go.i3wm.org/i3/doc.go
generated
vendored
Normal file
22
vendor/go.i3wm.org/i3/doc.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Package i3 provides a convenient interface to the i3 window manager.
|
||||
//
|
||||
// Its function and type names don’t stutter, and all functions and methods are
|
||||
// safe for concurrent use (except where otherwise noted). The package does not
|
||||
// import "unsafe" and hence should be widely applicable.
|
||||
//
|
||||
// UNIX socket connections to i3 are transparently managed by the package. Upon
|
||||
// any read/write errors on a UNIX socket, the package transparently retries for
|
||||
// up to 10 seconds, but only as long as the i3 process keeps running.
|
||||
//
|
||||
// The package is published in versioned releases, where the major and minor
|
||||
// version are identical to the i3 release the package is compatible with
|
||||
// (e.g. 4.14 implements the entire documented IPC interface of i3 4.14).
|
||||
//
|
||||
// This package will only ever receive additions, so versioning should only be
|
||||
// relevant to you if you are interested in a recently-introduced IPC feature.
|
||||
//
|
||||
// Message type functions and event types are annotated with the i3 version in
|
||||
// which they were introduced. Under the covers, they use AtLeast, so they
|
||||
// return a helpful error message at runtime if the running i3 version is too
|
||||
// old.
|
||||
package i3 // import "go.i3wm.org/i3"
|
||||
44
vendor/go.i3wm.org/i3/getpid.go
generated
vendored
Normal file
44
vendor/go.i3wm.org/i3/getpid.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package i3
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
"github.com/BurntSushi/xgbutil/xprop"
|
||||
)
|
||||
|
||||
func i3Pid() int {
|
||||
xu, err := xgbutil.NewConn()
|
||||
if err != nil {
|
||||
return -1 // X session terminated
|
||||
}
|
||||
defer xu.Conn().Close()
|
||||
reply, err := xprop.GetProperty(xu, xu.RootWin(), "I3_PID")
|
||||
if err != nil {
|
||||
return -1 // I3_PID no longer present (X session replaced?)
|
||||
}
|
||||
num, err := xprop.PropValNum(reply, err)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return int(num)
|
||||
}
|
||||
|
||||
var lastPid struct {
|
||||
sync.Mutex
|
||||
pid int
|
||||
}
|
||||
|
||||
// IsRunningHook provides a method to override the method which detects if i3 is running or not
|
||||
var IsRunningHook = func() bool {
|
||||
lastPid.Lock()
|
||||
defer lastPid.Unlock()
|
||||
if !wasRestart || lastPid.pid == 0 {
|
||||
lastPid.pid = i3Pid()
|
||||
}
|
||||
return pidValid(lastPid.pid)
|
||||
}
|
||||
|
||||
func i3Running() bool {
|
||||
return IsRunningHook()
|
||||
}
|
||||
17
vendor/go.i3wm.org/i3/marks.go
generated
vendored
Normal file
17
vendor/go.i3wm.org/i3/marks.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package i3
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// GetMarks returns the names of all currently set marks.
|
||||
//
|
||||
// GetMarks is supported in i3 ≥ v4.1 (2011-11-11).
|
||||
func GetMarks() ([]string, error) {
|
||||
reply, err := roundTrip(messageTypeGetMarks, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var marks []string
|
||||
err = json.Unmarshal(reply.Payload, &marks)
|
||||
return marks, err
|
||||
}
|
||||
28
vendor/go.i3wm.org/i3/outputs.go
generated
vendored
Normal file
28
vendor/go.i3wm.org/i3/outputs.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package i3
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// Output describes an i3 output.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_outputs_reply for more details.
|
||||
type Output struct {
|
||||
Name string `json:"name"`
|
||||
Active bool `json:"active"`
|
||||
Primary bool `json:"primary"`
|
||||
CurrentWorkspace string `json:"current_workspace"`
|
||||
Rect Rect `json:"rect"`
|
||||
}
|
||||
|
||||
// GetOutputs returns i3’s current outputs.
|
||||
//
|
||||
// GetOutputs is supported in i3 ≥ v4.0 (2011-07-31).
|
||||
func GetOutputs() ([]Output, error) {
|
||||
reply, err := roundTrip(messageTypeGetOutputs, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var outputs []Output
|
||||
err = json.Unmarshal(reply.Payload, &outputs)
|
||||
return outputs, err
|
||||
}
|
||||
198
vendor/go.i3wm.org/i3/socket.go
generated
vendored
Normal file
198
vendor/go.i3wm.org/i3/socket.go
generated
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
package i3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// If your computer takes more than 10s to restart i3, it must be seriously
|
||||
// overloaded, in which case we are probably doing you a favor by erroring out.
|
||||
const reconnectTimeout = 10 * time.Second
|
||||
|
||||
// remote is a singleton containing the socket path and auto-detected byte order
|
||||
// which i3 is using. It is lazily initialized by getIPCSocket.
|
||||
var remote struct {
|
||||
path string
|
||||
order binary.ByteOrder
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// SocketPathHook Provides a way to override the default socket path lookup mechanism. Overriding this is unsupported.
|
||||
var SocketPathHook = func() (string, error) {
|
||||
out, err := exec.Command("i3", "--get-socketpath").CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getting i3 socketpath: %v (output: %s)", err, out)
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func getIPCSocket(updateSocketPath bool) (*socket, net.Conn, error) {
|
||||
remote.mu.Lock()
|
||||
defer remote.mu.Unlock()
|
||||
path := remote.path
|
||||
if (!wasRestart && updateSocketPath) || remote.path == "" {
|
||||
out, err := SocketPathHook()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
path = strings.TrimSpace(string(out))
|
||||
}
|
||||
conn, err := net.Dial("unix", path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
remote.path = path
|
||||
if remote.order == nil {
|
||||
remote.order, err = detectByteOrder(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &socket{conn: conn, order: remote.order}, conn, err
|
||||
}
|
||||
|
||||
type messageType uint32
|
||||
|
||||
const (
|
||||
messageTypeRunCommand messageType = iota
|
||||
messageTypeGetWorkspaces
|
||||
messageTypeSubscribe
|
||||
messageTypeGetOutputs
|
||||
messageTypeGetTree
|
||||
messageTypeGetMarks
|
||||
messageTypeGetBarConfig
|
||||
messageTypeGetVersion
|
||||
messageTypeGetBindingModes
|
||||
messageTypeGetConfig
|
||||
messageTypeSendTick
|
||||
messageTypeSync
|
||||
)
|
||||
|
||||
var messageAtLeast = map[messageType]majorMinor{
|
||||
messageTypeRunCommand: {4, 0},
|
||||
messageTypeGetWorkspaces: {4, 0},
|
||||
messageTypeSubscribe: {4, 0},
|
||||
messageTypeGetOutputs: {4, 0},
|
||||
messageTypeGetTree: {4, 0},
|
||||
messageTypeGetMarks: {4, 1},
|
||||
messageTypeGetBarConfig: {4, 1},
|
||||
messageTypeGetVersion: {4, 3},
|
||||
messageTypeGetBindingModes: {4, 13},
|
||||
messageTypeGetConfig: {4, 14},
|
||||
messageTypeSendTick: {4, 15},
|
||||
messageTypeSync: {4, 16},
|
||||
}
|
||||
|
||||
const (
|
||||
messageReplyTypeCommand messageType = iota
|
||||
messageReplyTypeWorkspaces
|
||||
messageReplyTypeSubscribe
|
||||
)
|
||||
|
||||
var magic = [6]byte{'i', '3', '-', 'i', 'p', 'c'}
|
||||
|
||||
type header struct {
|
||||
Magic [6]byte
|
||||
Length uint32
|
||||
Type messageType
|
||||
}
|
||||
|
||||
type message struct {
|
||||
Type messageType
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
type socket struct {
|
||||
conn io.ReadWriter
|
||||
order binary.ByteOrder
|
||||
}
|
||||
|
||||
func (s *socket) recvMsg() (message, error) {
|
||||
if s == nil {
|
||||
return message{}, fmt.Errorf("not connected")
|
||||
}
|
||||
var h header
|
||||
if err := binary.Read(s.conn, s.order, &h); err != nil {
|
||||
return message{}, err
|
||||
}
|
||||
msg := message{
|
||||
Type: h.Type,
|
||||
Payload: make([]byte, h.Length),
|
||||
}
|
||||
_, err := io.ReadFull(s.conn, msg.Payload)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func (s *socket) roundTrip(t messageType, payload []byte) (message, error) {
|
||||
if s == nil {
|
||||
return message{}, fmt.Errorf("not connected")
|
||||
}
|
||||
|
||||
if err := binary.Write(s.conn, s.order, &header{magic, uint32(len(payload)), t}); err != nil {
|
||||
return message{}, err
|
||||
}
|
||||
if len(payload) > 0 { // skip empty Write()s for net.Pipe
|
||||
_, err := s.conn.Write(payload)
|
||||
if err != nil {
|
||||
return message{}, err
|
||||
}
|
||||
}
|
||||
return s.recvMsg()
|
||||
}
|
||||
|
||||
// defaultSock is a singleton, lazily initialized by roundTrip. All
|
||||
// request/response messages are sent to i3 via this socket, whereas
|
||||
// subscriptions use their own connection.
|
||||
var defaultSock struct {
|
||||
sock *socket
|
||||
conn net.Conn
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// roundTrip sends a message to i3 and returns the received result in a
|
||||
// concurrency-safe fashion.
|
||||
func roundTrip(t messageType, payload []byte) (message, error) {
|
||||
// Error out early in case the message type is not yet supported by the
|
||||
// running i3 version.
|
||||
if t != messageTypeGetVersion {
|
||||
if err := AtLeast(messageAtLeast[t].major, messageAtLeast[t].minor); err != nil {
|
||||
return message{}, err
|
||||
}
|
||||
}
|
||||
|
||||
defaultSock.mu.Lock()
|
||||
defer defaultSock.mu.Unlock()
|
||||
|
||||
Outer:
|
||||
for {
|
||||
msg, err := defaultSock.sock.roundTrip(t, payload)
|
||||
if err == nil {
|
||||
return msg, nil // happy path: success
|
||||
}
|
||||
|
||||
// reconnect
|
||||
start := time.Now()
|
||||
for time.Since(start) < reconnectTimeout && (defaultSock.sock == nil || i3Running()) {
|
||||
if defaultSock.sock != nil {
|
||||
defaultSock.conn.Close()
|
||||
}
|
||||
defaultSock.sock, defaultSock.conn, err = getIPCSocket(defaultSock.sock != nil)
|
||||
if err == nil {
|
||||
continue Outer
|
||||
}
|
||||
|
||||
// Reconnect within [10, 20) ms to prevent CPU-starving i3.
|
||||
time.Sleep(time.Duration(10+rand.Int63n(10)) * time.Millisecond)
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
}
|
||||
382
vendor/go.i3wm.org/i3/subscribe.go
generated
vendored
Normal file
382
vendor/go.i3wm.org/i3/subscribe.go
generated
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
package i3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Event is an event received from i3.
|
||||
//
|
||||
// Type-assert or type-switch on Event to obtain a more specific type.
|
||||
type Event interface{}
|
||||
|
||||
// WorkspaceEvent contains details about various workspace-related changes.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_workspace_event for more details.
|
||||
type WorkspaceEvent struct {
|
||||
Change string `json:"change"`
|
||||
Current Node `json:"current"`
|
||||
Old Node `json:"old"`
|
||||
}
|
||||
|
||||
// OutputEvent contains details about various output-related changes.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_output_event for more details.
|
||||
type OutputEvent struct {
|
||||
Change string `json:"change"`
|
||||
}
|
||||
|
||||
// ModeEvent contains details about various mode-related changes.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_mode_event for more details.
|
||||
type ModeEvent struct {
|
||||
Change string `json:"change"`
|
||||
PangoMarkup bool `json:"pango_markup"`
|
||||
}
|
||||
|
||||
// WindowEvent contains details about various window-related changes.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_window_event for more details.
|
||||
type WindowEvent struct {
|
||||
Change string `json:"change"`
|
||||
Container Node `json:"container"`
|
||||
}
|
||||
|
||||
// BarconfigUpdateEvent contains details about various bar config-related changes.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_barconfig_update_event for more details.
|
||||
type BarconfigUpdateEvent BarConfig
|
||||
|
||||
// BindingEvent contains details about various binding-related changes.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_binding_event for more details.
|
||||
type BindingEvent struct {
|
||||
Change string `json:"change"`
|
||||
Binding struct {
|
||||
Command string `json:"command"`
|
||||
EventStateMask []string `json:"event_state_mask"`
|
||||
InputCode int64 `json:"input_code"`
|
||||
Symbol string `json:"symbol"`
|
||||
InputType string `json:"input_type"`
|
||||
} `json:"binding"`
|
||||
}
|
||||
|
||||
// ShutdownEvent contains the reason for which the IPC connection is about to be
|
||||
// shut down.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_shutdown_event for more details.
|
||||
type ShutdownEvent struct {
|
||||
Change string `json:"change"`
|
||||
}
|
||||
|
||||
// TickEvent contains the payload of the last tick command.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_tick_event for more details.
|
||||
type TickEvent struct {
|
||||
First bool `json:"first"`
|
||||
Payload string `json:"payload"`
|
||||
}
|
||||
|
||||
type eventReplyType int
|
||||
|
||||
const (
|
||||
eventReplyTypeWorkspace eventReplyType = iota
|
||||
eventReplyTypeOutput
|
||||
eventReplyTypeMode
|
||||
eventReplyTypeWindow
|
||||
eventReplyTypeBarconfigUpdate
|
||||
eventReplyTypeBinding
|
||||
eventReplyTypeShutdown
|
||||
eventReplyTypeTick
|
||||
)
|
||||
|
||||
const (
|
||||
eventFlagMask = uint32(0x80000000)
|
||||
eventTypeMask = ^eventFlagMask
|
||||
)
|
||||
|
||||
// EventReceiver is not safe for concurrent use.
|
||||
type EventReceiver struct {
|
||||
types []EventType // for re-subscribing on io.EOF
|
||||
sock *socket
|
||||
conn net.Conn
|
||||
ev Event
|
||||
err error
|
||||
reconnect bool
|
||||
}
|
||||
|
||||
// Event returns the most recent event received from i3 by a call to Next.
|
||||
func (r *EventReceiver) Event() Event {
|
||||
return r.ev
|
||||
}
|
||||
|
||||
func (r *EventReceiver) subscribe() error {
|
||||
var err error
|
||||
if r.conn != nil {
|
||||
r.conn.Close()
|
||||
}
|
||||
if wasRestart {
|
||||
r.reconnect = false
|
||||
}
|
||||
r.sock, r.conn, err = getIPCSocket(r.reconnect)
|
||||
r.reconnect = true
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload, err := json.Marshal(r.types)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := r.sock.roundTrip(messageTypeSubscribe, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var reply struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
if err := json.Unmarshal(b.Payload, &reply); err != nil {
|
||||
return err
|
||||
}
|
||||
if !reply.Success {
|
||||
return fmt.Errorf("could not subscribe, check the i3 log")
|
||||
}
|
||||
r.err = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *EventReceiver) next() (Event, error) {
|
||||
reply, err := r.sock.recvMsg()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if (uint32(reply.Type) & eventFlagMask) == 0 {
|
||||
return nil, fmt.Errorf("unexpectedly did not receive an event")
|
||||
}
|
||||
t := uint32(reply.Type) & eventTypeMask
|
||||
switch eventReplyType(t) {
|
||||
case eventReplyTypeWorkspace:
|
||||
var e WorkspaceEvent
|
||||
return &e, json.Unmarshal(reply.Payload, &e)
|
||||
|
||||
case eventReplyTypeOutput:
|
||||
var e OutputEvent
|
||||
return &e, json.Unmarshal(reply.Payload, &e)
|
||||
|
||||
case eventReplyTypeMode:
|
||||
var e ModeEvent
|
||||
return &e, json.Unmarshal(reply.Payload, &e)
|
||||
|
||||
case eventReplyTypeWindow:
|
||||
var e WindowEvent
|
||||
return &e, json.Unmarshal(reply.Payload, &e)
|
||||
|
||||
case eventReplyTypeBarconfigUpdate:
|
||||
var e BarconfigUpdateEvent
|
||||
return &e, json.Unmarshal(reply.Payload, &e)
|
||||
|
||||
case eventReplyTypeBinding:
|
||||
var e BindingEvent
|
||||
return &e, json.Unmarshal(reply.Payload, &e)
|
||||
|
||||
case eventReplyTypeShutdown:
|
||||
var e ShutdownEvent
|
||||
return &e, json.Unmarshal(reply.Payload, &e)
|
||||
|
||||
case eventReplyTypeTick:
|
||||
var e TickEvent
|
||||
return &e, json.Unmarshal(reply.Payload, &e)
|
||||
}
|
||||
return nil, fmt.Errorf("BUG: event reply type %d not implemented yet", t)
|
||||
}
|
||||
|
||||
// Next advances the EventReceiver to the next event, which will then be
|
||||
// available through the Event method. It returns false when reaching an
|
||||
// error. After Next returns false, the Close method will return the first
|
||||
// error.
|
||||
//
|
||||
// Until you call Close, you must call Next in a loop for every EventReceiver
|
||||
// (usually in a separate goroutine), otherwise i3 will deadlock as soon as the
|
||||
// UNIX socket buffer is full of unprocessed events.
|
||||
func (r *EventReceiver) Next() bool {
|
||||
Outer:
|
||||
for r.err == nil {
|
||||
r.ev, r.err = r.next()
|
||||
if r.err == nil {
|
||||
return true // happy path
|
||||
}
|
||||
|
||||
// reconnect
|
||||
start := time.Now()
|
||||
for time.Since(start) < reconnectTimeout && (r.sock == nil || i3Running()) {
|
||||
if err := r.subscribe(); err == nil {
|
||||
continue Outer
|
||||
} else {
|
||||
r.err = err
|
||||
}
|
||||
|
||||
// Reconnect within [10, 20) ms to prevent CPU-starving i3.
|
||||
time.Sleep(time.Duration(10+rand.Int63n(10)) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
return r.err == nil
|
||||
}
|
||||
|
||||
// Close closes the connection to i3. If you don’t ever call Close, you must
|
||||
// consume events via Next to prevent i3 from deadlocking.
|
||||
func (r *EventReceiver) Close() error {
|
||||
if r.conn != nil {
|
||||
if r.err == nil {
|
||||
r.err = r.conn.Close()
|
||||
} else {
|
||||
// Retain the original error.
|
||||
r.conn.Close()
|
||||
}
|
||||
r.conn = nil
|
||||
r.sock = nil
|
||||
}
|
||||
return r.err
|
||||
}
|
||||
|
||||
// EventType indicates the specific kind of event to subscribe to.
|
||||
type EventType string
|
||||
|
||||
// i3 currently implements the following event types:
|
||||
const (
|
||||
WorkspaceEventType EventType = "workspace" // since 4.0
|
||||
OutputEventType EventType = "output" // since 4.0
|
||||
ModeEventType EventType = "mode" // since 4.4
|
||||
WindowEventType EventType = "window" // since 4.5
|
||||
BarconfigUpdateEventType EventType = "barconfig_update" // since 4.6
|
||||
BindingEventType EventType = "binding" // since 4.9
|
||||
ShutdownEventType EventType = "shutdown" // since 4.14
|
||||
TickEventType EventType = "tick" // since 4.15
|
||||
)
|
||||
|
||||
type majorMinor struct {
|
||||
major int64
|
||||
minor int64
|
||||
}
|
||||
|
||||
var eventAtLeast = map[EventType]majorMinor{
|
||||
WorkspaceEventType: {4, 0},
|
||||
OutputEventType: {4, 0},
|
||||
ModeEventType: {4, 4},
|
||||
WindowEventType: {4, 5},
|
||||
BarconfigUpdateEventType: {4, 6},
|
||||
BindingEventType: {4, 9},
|
||||
ShutdownEventType: {4, 14},
|
||||
TickEventType: {4, 15},
|
||||
}
|
||||
|
||||
// Subscribe returns an EventReceiver for receiving events of the specified
|
||||
// types from i3.
|
||||
//
|
||||
// Unless the ordering of events matters to your use-case, you are encouraged to
|
||||
// call Subscribe once per event type, so that you can use type assertions
|
||||
// instead of type switches.
|
||||
//
|
||||
// Subscribe is supported in i3 ≥ v4.0 (2011-07-31).
|
||||
func Subscribe(eventTypes ...EventType) *EventReceiver {
|
||||
// Error out early in case any requested event type is not yet supported by
|
||||
// the running i3 version.
|
||||
for _, t := range eventTypes {
|
||||
if err := AtLeast(eventAtLeast[t].major, eventAtLeast[t].minor); err != nil {
|
||||
return &EventReceiver{err: err}
|
||||
}
|
||||
}
|
||||
return &EventReceiver{types: eventTypes}
|
||||
}
|
||||
|
||||
// restart runs the restart i3 command without entering an infinite loop: as
|
||||
// RUN_COMMAND with payload "restart" does not result in a reply, we subscribe
|
||||
// to the shutdown event beforehand (on a dedicated connection), which we can
|
||||
// receive instead of a reply.
|
||||
func restart(firstAttempt bool) error {
|
||||
sock, conn, err := getIPCSocket(!firstAttempt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
payload, err := json.Marshal([]EventType{ShutdownEventType})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := sock.roundTrip(messageTypeSubscribe, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sreply struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
if err := json.Unmarshal(b.Payload, &sreply); err != nil {
|
||||
return err
|
||||
}
|
||||
if !sreply.Success {
|
||||
return fmt.Errorf("could not subscribe, check the i3 log")
|
||||
}
|
||||
rreply, err := sock.roundTrip(messageTypeRunCommand, []byte("restart"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (uint32(rreply.Type) & eventFlagMask) == 0 {
|
||||
var crs []CommandResult
|
||||
err = json.Unmarshal(rreply.Payload, &crs)
|
||||
if err == nil {
|
||||
for _, cr := range crs {
|
||||
if !cr.Success {
|
||||
return &CommandUnsuccessfulError{
|
||||
command: "restart",
|
||||
cr: cr,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil // restart command successful
|
||||
}
|
||||
t := uint32(rreply.Type) & eventTypeMask
|
||||
if got, want := eventReplyType(t), eventReplyTypeShutdown; got != want {
|
||||
return fmt.Errorf("unexpected reply type: got %d, want %d", got, want)
|
||||
}
|
||||
return nil // shutdown event received
|
||||
}
|
||||
|
||||
var wasRestart = false
|
||||
|
||||
// Restart sends the restart command to i3. Sending restart via RunCommand will
|
||||
// result in a deadlock: since i3 restarts before it sends the reply to the
|
||||
// restart command, RunCommand will retry the command indefinitely.
|
||||
//
|
||||
// Restart is supported in i3 ≥ v4.14 (2017-09-04).
|
||||
func Restart() error {
|
||||
if err := AtLeast(eventAtLeast[ShutdownEventType].major, eventAtLeast[ShutdownEventType].minor); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(https://github.com/i3/i3/pull/3743): conditionally use this approach
|
||||
// once the restart reply PR made it into an i3 release:
|
||||
if false {
|
||||
_, err := roundTrip(messageTypeRunCommand, []byte("restart"))
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("preventing any further X11 connections to work around issue #3")
|
||||
wasRestart = true
|
||||
|
||||
var (
|
||||
firstAttempt = true
|
||||
start = time.Now()
|
||||
lastErr error
|
||||
)
|
||||
for time.Since(start) < reconnectTimeout && (firstAttempt || i3Running()) {
|
||||
lastErr = restart(firstAttempt)
|
||||
if lastErr == nil {
|
||||
return nil // success
|
||||
}
|
||||
firstAttempt = false
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
32
vendor/go.i3wm.org/i3/sync.go
generated
vendored
Normal file
32
vendor/go.i3wm.org/i3/sync.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package i3
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// SyncRequest represents the payload of a Sync request.
|
||||
type SyncRequest struct {
|
||||
Window uint32 `json:"window"` // X11 window id
|
||||
Rnd uint32 `json:"rnd"` // Random value for distinguishing requests
|
||||
}
|
||||
|
||||
// SyncResult attests the sync command was successful.
|
||||
type SyncResult struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// Sync sends a tick event with the provided payload.
|
||||
//
|
||||
// Sync is supported in i3 ≥ v4.16 (2018-11-04).
|
||||
func Sync(req SyncRequest) (SyncResult, error) {
|
||||
b, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return SyncResult{}, err
|
||||
}
|
||||
reply, err := roundTrip(messageTypeSync, b)
|
||||
if err != nil {
|
||||
return SyncResult{}, err
|
||||
}
|
||||
|
||||
var tr SyncResult
|
||||
err = json.Unmarshal(reply.Payload, &tr)
|
||||
return tr, err
|
||||
}
|
||||
22
vendor/go.i3wm.org/i3/tick.go
generated
vendored
Normal file
22
vendor/go.i3wm.org/i3/tick.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package i3
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// TickResult attests the tick command was successful.
|
||||
type TickResult struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// SendTick sends a tick event with the provided payload.
|
||||
//
|
||||
// SendTick is supported in i3 ≥ v4.15 (2018-03-10).
|
||||
func SendTick(command string) (TickResult, error) {
|
||||
reply, err := roundTrip(messageTypeSendTick, []byte(command))
|
||||
if err != nil {
|
||||
return TickResult{}, err
|
||||
}
|
||||
|
||||
var tr TickResult
|
||||
err = json.Unmarshal(reply.Payload, &tr)
|
||||
return tr, err
|
||||
}
|
||||
164
vendor/go.i3wm.org/i3/tree.go
generated
vendored
Normal file
164
vendor/go.i3wm.org/i3/tree.go
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
package i3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// NodeType indicates the specific kind of Node.
|
||||
type NodeType string
|
||||
|
||||
// i3 currently implements the following node types:
|
||||
const (
|
||||
Root NodeType = "root"
|
||||
OutputNode NodeType = "output"
|
||||
Con NodeType = "con"
|
||||
FloatingCon NodeType = "floating_con"
|
||||
WorkspaceNode NodeType = "workspace"
|
||||
DockareaNode NodeType = "dockarea"
|
||||
)
|
||||
|
||||
// Layout indicates the layout of a Node.
|
||||
type Layout string
|
||||
|
||||
// i3 currently implements the following layouts:
|
||||
const (
|
||||
SplitH Layout = "splith"
|
||||
SplitV Layout = "splitv"
|
||||
Stacked Layout = "stacked"
|
||||
Tabbed Layout = "tabbed"
|
||||
DockareaLayout Layout = "dockarea"
|
||||
OutputLayout Layout = "output"
|
||||
)
|
||||
|
||||
// BorderStyle indicates the border style of a node.
|
||||
type BorderStyle string
|
||||
|
||||
// i3 currently implements the following border styles:
|
||||
const (
|
||||
NormalBorder BorderStyle = "normal"
|
||||
NoBorder BorderStyle = "none"
|
||||
PixelBorder BorderStyle = "pixel"
|
||||
)
|
||||
|
||||
// Rect is a rectangle, used for various dimensions in Node, for example.
|
||||
type Rect struct {
|
||||
X int64 `json:"x"`
|
||||
Y int64 `json:"y"`
|
||||
Width int64 `json:"width"`
|
||||
Height int64 `json:"height"`
|
||||
}
|
||||
|
||||
// WindowProperties correspond to X11 window properties
|
||||
//
|
||||
// See https://build.i3wm.org/docs/ipc.html#_tree_reply
|
||||
type WindowProperties struct {
|
||||
Title string `json:"title"`
|
||||
Instance string `json:"instance"`
|
||||
Class string `json:"class"`
|
||||
Role string `json:"window_role"`
|
||||
Transient NodeID `json:"transient_for"`
|
||||
}
|
||||
|
||||
// NodeID is an i3-internal ID for the node, which can be used to identify
|
||||
// containers within the IPC interface.
|
||||
type NodeID int64
|
||||
|
||||
// Node is a node in a Tree.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_tree_reply for more details.
|
||||
type Node struct {
|
||||
ID NodeID `json:"id"`
|
||||
Name string `json:"name"` // window: title, container: internal name
|
||||
Type NodeType `json:"type"`
|
||||
Border BorderStyle `json:"border"`
|
||||
CurrentBorderWidth int64 `json:"current_border_width"`
|
||||
Layout Layout `json:"layout"`
|
||||
Percent float64 `json:"percent"`
|
||||
Rect Rect `json:"rect"` // absolute (= relative to X11 display)
|
||||
WindowRect Rect `json:"window_rect"` // window, relative to Rect
|
||||
DecoRect Rect `json:"deco_rect"` // decoration, relative to Rect
|
||||
Geometry Rect `json:"geometry"` // original window geometry, absolute
|
||||
Window int64 `json:"window"` // X11 window ID of the client window
|
||||
WindowProperties WindowProperties `json:"window_properties"`
|
||||
Urgent bool `json:"urgent"` // urgency hint set
|
||||
Focused bool `json:"focused"`
|
||||
Focus []NodeID `json:"focus"`
|
||||
Nodes []*Node `json:"nodes"`
|
||||
FloatingNodes []*Node `json:"floating_nodes"`
|
||||
}
|
||||
|
||||
// FindChild returns the first Node matching predicate, using pre-order
|
||||
// depth-first search.
|
||||
func (n *Node) FindChild(predicate func(*Node) bool) *Node {
|
||||
if predicate(n) {
|
||||
return n
|
||||
}
|
||||
for _, c := range n.Nodes {
|
||||
if con := c.FindChild(predicate); con != nil {
|
||||
return con
|
||||
}
|
||||
}
|
||||
for _, c := range n.FloatingNodes {
|
||||
if con := c.FindChild(predicate); con != nil {
|
||||
return con
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindFocused returns the first Node matching predicate from the sub-tree of
|
||||
// directly and indirectly focused containers.
|
||||
//
|
||||
// As an example, consider this layout tree (simplified):
|
||||
//
|
||||
// root
|
||||
// │
|
||||
// HDMI2
|
||||
// ╱ ╲
|
||||
// … workspace 1
|
||||
// ╱ ╲
|
||||
// XTerm Firefox
|
||||
//
|
||||
// In this example, if Firefox is focused, FindFocused will return the first
|
||||
// container matching predicate of root, HDMI2, workspace 1, Firefox (in this
|
||||
// order).
|
||||
func (n *Node) FindFocused(predicate func(*Node) bool) *Node {
|
||||
if predicate(n) {
|
||||
return n
|
||||
}
|
||||
if len(n.Focus) == 0 {
|
||||
return nil
|
||||
}
|
||||
first := n.Focus[0]
|
||||
for _, c := range n.Nodes {
|
||||
if c.ID == first {
|
||||
return c.FindFocused(predicate)
|
||||
}
|
||||
}
|
||||
for _, c := range n.FloatingNodes {
|
||||
if c.ID == first {
|
||||
return c.FindFocused(predicate)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tree is an i3 layout tree, starting with Root.
|
||||
type Tree struct {
|
||||
// Root is the root node of the layout tree.
|
||||
Root *Node
|
||||
}
|
||||
|
||||
// GetTree returns i3’s layout tree.
|
||||
//
|
||||
// GetTree is supported in i3 ≥ v4.0 (2011-07-31).
|
||||
func GetTree() (Tree, error) {
|
||||
reply, err := roundTrip(messageTypeGetTree, nil)
|
||||
if err != nil {
|
||||
return Tree{}, err
|
||||
}
|
||||
|
||||
var root Node
|
||||
err = json.Unmarshal(reply.Payload, &root)
|
||||
return Tree{Root: &root}, err
|
||||
}
|
||||
16
vendor/go.i3wm.org/i3/validpid.go
generated
vendored
Normal file
16
vendor/go.i3wm.org/i3/validpid.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package i3
|
||||
|
||||
import "syscall"
|
||||
|
||||
func pidValid(pid int) bool {
|
||||
// As per kill(2) from POSIX.1-2008, sending signal 0 validates a pid.
|
||||
if err := syscall.Kill(pid, 0); err != nil {
|
||||
if err == syscall.EPERM {
|
||||
// Process still alive (but no permission to signal):
|
||||
return true
|
||||
}
|
||||
// errno is likely ESRCH (process not found).
|
||||
return false // Process not alive.
|
||||
}
|
||||
return true // Process still alive.
|
||||
}
|
||||
62
vendor/go.i3wm.org/i3/version.go
generated
vendored
Normal file
62
vendor/go.i3wm.org/i3/version.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
package i3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Version describes an i3 version.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_version_reply for more details.
|
||||
type Version struct {
|
||||
Major int64 `json:"major"`
|
||||
Minor int64 `json:"minor"`
|
||||
Patch int64 `json:"patch"`
|
||||
Variant string `json:"variant,omitempty"`
|
||||
HumanReadable string `json:"human_readable"`
|
||||
LoadedConfigFileName string `json:"loaded_config_file_name"`
|
||||
}
|
||||
|
||||
// GetVersion returns i3’s version.
|
||||
//
|
||||
// GetVersion is supported in i3 ≥ v4.3 (2012-09-19).
|
||||
func GetVersion() (Version, error) {
|
||||
reply, err := roundTrip(messageTypeGetVersion, nil)
|
||||
if err != nil {
|
||||
return Version{}, err
|
||||
}
|
||||
|
||||
var v Version
|
||||
err = json.Unmarshal(reply.Payload, &v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// version is a lazily-initialized, possibly stale copy of i3’s GET_VERSION
|
||||
// reply. Access only values which don’t change, e.g. Major, Minor.
|
||||
var version Version
|
||||
|
||||
// AtLeast returns nil if i3’s major version matches major and i3’s minor
|
||||
// version is at least minor or newer. Otherwise, it returns an error message
|
||||
// stating i3 is too old.
|
||||
func AtLeast(major int64, minor int64) error {
|
||||
if major == 0 {
|
||||
return fmt.Errorf("BUG: major == 0 is non-sensical. Is a lookup table entry missing?")
|
||||
}
|
||||
if version.Major == 0 {
|
||||
var err error
|
||||
version, err = GetVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if version.Variant != "" {
|
||||
log.Printf("non standard i3 payload variant '%s' detected. Ignoring version check. This is fully unsupported.", version.Variant)
|
||||
return nil
|
||||
}
|
||||
if version.Major == major && version.Minor >= minor {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("i3 version too old: got %d.%d, want ≥ %d.%d", version.Major, version.Minor, major, minor)
|
||||
}
|
||||
30
vendor/go.i3wm.org/i3/workspaces.go
generated
vendored
Normal file
30
vendor/go.i3wm.org/i3/workspaces.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package i3
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// Workspace describes an i3 workspace.
|
||||
//
|
||||
// See https://i3wm.org/docs/ipc.html#_workspaces_reply for more details.
|
||||
type Workspace struct {
|
||||
Num int64 `json:"num"`
|
||||
Name string `json:"name"`
|
||||
Visible bool `json:"visible"`
|
||||
Focused bool `json:"focused"`
|
||||
Urgent bool `json:"urgent"`
|
||||
Rect Rect `json:"rect"`
|
||||
Output string `json:"output"`
|
||||
}
|
||||
|
||||
// GetWorkspaces returns i3’s current workspaces.
|
||||
//
|
||||
// GetWorkspaces is supported in i3 ≥ v4.0 (2011-07-31).
|
||||
func GetWorkspaces() ([]Workspace, error) {
|
||||
reply, err := roundTrip(messageTypeGetWorkspaces, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ws []Workspace
|
||||
err = json.Unmarshal(reply.Payload, &ws)
|
||||
return ws, err
|
||||
}
|
||||
Reference in New Issue
Block a user