Initial commit

This commit is contained in:
eleksir
2024-01-06 18:33:44 +03:00
commit d85dc5d5bb
657 changed files with 262300 additions and 0 deletions

2
vendor/github.com/BurntSushi/xgb/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
xgbgen/xgbgen
.*.swp

18
vendor/github.com/BurntSushi/xgb/AUTHORS generated vendored Normal file
View File

@@ -0,0 +1,18 @@
Andrew Gallant is the maintainer of this fork. What follows is the original
list of authors for the x-go-binding.
# This is the official list of XGB authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as
# Name or Organization <email address>
# The email address is not required for organizations.
# Please keep the list sorted.
Anthony Martin <ality@pbrane.org>
Firmansyah Adiputra <frm.adiputra@gmail.com>
Google Inc.
Scott Lawrence <bytbox@gmail.com>
Tor Andersson <tor.andersson@gmail.com>

39
vendor/github.com/BurntSushi/xgb/CONTRIBUTORS generated vendored Normal file
View File

@@ -0,0 +1,39 @@
Andrew Gallant is the maintainer of this fork. What follows is the original
list of contributors for the x-go-binding.
# This is the official list of people who can contribute
# (and typically have contributed) code to the XGB repository.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# The submission process automatically checks to make sure
# that people submitting code are listed in this file (by email address).
#
# Names should be added to this file only after verifying that
# the individual or the individual's organization has agreed to
# the appropriate Contributor License Agreement, found here:
#
# http://code.google.com/legal/individual-cla-v1.0.html
# http://code.google.com/legal/corporate-cla-v1.0.html
#
# The agreement for individuals can be filled out on the web.
#
# When adding J Random Contributor's name to this file,
# either J's name or J's organization's name should be
# added to the AUTHORS file, depending on whether the
# individual or corporate CLA was used.
# Names should be added to this file like so:
# Name <email address>
# Please keep the list sorted.
Anthony Martin <ality@pbrane.org>
Firmansyah Adiputra <frm.adiputra@gmail.com>
Ian Lance Taylor <iant@golang.org>
Nigel Tao <nigeltao@golang.org>
Robert Griesemer <gri@golang.org>
Russ Cox <rsc@golang.org>
Scott Lawrence <bytbox@gmail.com>
Tor Andersson <tor.andersson@gmail.com>

42
vendor/github.com/BurntSushi/xgb/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,42 @@
// Copyright (c) 2009 The XGB Authors. 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 Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "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 THE COPYRIGHT
// OWNER OR CONTRIBUTORS 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.
//
// Subject to the terms and conditions of this License, Google hereby
// grants to You a perpetual, worldwide, non-exclusive, no-charge,
// royalty-free, irrevocable (except as stated in this section) patent
// license to make, have made, use, offer to sell, sell, import, and
// otherwise transfer this implementation of XGB, where such license
// applies only to those patent claims licensable by Google that are
// necessarily infringed by use of this implementation of XGB. If You
// institute patent litigation against any entity (including a
// cross-claim or counterclaim in a lawsuit) alleging that this
// implementation of XGB or a Contribution incorporated within this
// implementation of XGB constitutes direct or contributory patent
// infringement, then any patent licenses granted to You under this
// License for this implementation of XGB shall terminate as of the date
// such litigation is filed.

78
vendor/github.com/BurntSushi/xgb/Makefile generated vendored Normal file
View File

@@ -0,0 +1,78 @@
# This Makefile is used by the developer. It is not needed in any way to build
# a checkout of the XGB repository.
# It will be useful, however, if you are hacking at the code generator.
# i.e., after making a change to the code generator, run 'make' in the
# xgb directory. This will build xgbgen and regenerate each sub-package.
# 'make test' will then run any appropriate tests (just tests xproto right now).
# 'make bench' will test a couple of benchmarks.
# 'make build-all' will then try to build each extension. This isn't strictly
# necessary, but it's a good idea to make sure each sub-package is a valid
# Go package.
# My path to the X protocol XML descriptions.
XPROTO=/usr/share/xcb
# All of the XML files in my /usr/share/xcb directory EXCEPT XKB. -_-
# This is intended to build xgbgen and generate Go code for each supported
# extension.
all: build-xgbgen \
bigreq.xml composite.xml damage.xml dpms.xml dri2.xml \
ge.xml glx.xml randr.xml record.xml render.xml res.xml \
screensaver.xml shape.xml shm.xml xc_misc.xml \
xevie.xml xf86dri.xml xf86vidmode.xml xfixes.xml xinerama.xml \
xprint.xml xproto.xml xselinux.xml xtest.xml \
xvmc.xml xv.xml
build-xgbgen:
(cd xgbgen && go build)
# Builds each individual sub-package to make sure its valid Go code.
build-all: bigreq.b composite.b damage.b dpms.b dri2.b ge.b glx.b randr.b \
record.b render.b res.b screensaver.b shape.b shm.b xcmisc.b \
xevie.b xf86dri.b xf86vidmode.b xfixes.b xinerama.b \
xprint.b xproto.b xselinux.b xtest.b xv.b xvmc.b
%.b:
(cd $* ; go build)
# Installs each individual sub-package.
install: bigreq.i composite.i damage.i dpms.i dri2.i ge.i glx.i randr.i \
record.i render.i res.i screensaver.i shape.i shm.i xcmisc.i \
xevie.i xf86dri.i xf86vidmode.i xfixes.i xinerama.i \
xprint.i xproto.i xselinux.i xtest.i xv.i xvmc.i
go install
%.i:
(cd $* ; go install)
# xc_misc is special because it has an underscore.
# There's probably a way to do this better, but Makefiles aren't my strong suit.
xc_misc.xml: build-xgbgen
mkdir -p xcmisc
xgbgen/xgbgen --proto-path $(XPROTO) $(XPROTO)/xc_misc.xml > xcmisc/xcmisc.go
%.xml: build-xgbgen
mkdir -p $*
xgbgen/xgbgen --proto-path $(XPROTO) $(XPROTO)/$*.xml > $*/$*.go
# Just test the xproto core protocol for now.
test:
(cd xproto ; go test)
# Force all xproto benchmarks to run and no tests.
bench:
(cd xproto ; go test -run 'nomatch' -bench '.*' -cpu 1,2,3,6)
# gofmt all non-auto-generated code.
# (auto-generated code is already gofmt'd.)
# Also do a column check (80 cols) after a gofmt.
# But don't check columns on auto-generated code, since I don't care if they
# break 80 cols.
gofmt:
gofmt -w *.go xgbgen/*.go examples/*.go examples/*/*.go xproto/xproto_test.go
colcheck *.go xgbgen/*.go examples/*.go examples/*/*.go xproto/xproto_test.go
push:
git push origin master
git push github master

59
vendor/github.com/BurntSushi/xgb/README generated vendored Normal file
View File

@@ -0,0 +1,59 @@
Note that this project is largely unmaintained as I don't have the time to do
or support more development. Please consider using this fork instead:
https://github.com/jezek/xgb
XGB is the X Go Binding, which is a low-level API to communicate with the
core X protocol and many of the X extensions. It is closely modeled after
XCB and xpyb.
It is thread safe and gets immediate improvement from parallelism when
GOMAXPROCS > 1. (See the benchmarks in xproto/xproto_test.go for evidence.)
Please see doc.go for more info.
Note that unless you know you need XGB, you can probably make your life
easier by using a slightly higher level library: xgbutil.
Quick Usage
===========
go get github.com/BurntSushi/xgb
go run go/path/src/github.com/BurntSushi/xgb/examples/create-window/main.go
BurntSushi's Fork
=================
I've forked the XGB repository from Google Code due to inactivty upstream.
Godoc documentation can be found here:
https://godoc.org/github.com/BurntSushi/xgb
Much of the code has been rewritten in an effort to support thread safety
and multiple extensions. Namely, go_client.py has been thrown away in favor
of an xgbgen package.
The biggest parts that *haven't* been rewritten by me are the connection and
authentication handshakes. They're inherently messy, and there's really no
reason to re-work them. The rest of XGB has been completely rewritten.
I like to release my code under the WTFPL, but since I'm starting with someone
else's work, I'm leaving the original license/contributor/author information
in tact.
I suppose I can legitimately release xgbgen under the WTFPL. To be fair, it is
at least as complex as XGB itself. *sigh*
What follows is the original README:
XGB README
==========
XGB is the X protocol Go language Binding.
It is the Go equivalent of XCB, the X protocol C-language Binding
(http://xcb.freedesktop.org/).
Unless otherwise noted, the XGB source files are distributed
under the BSD-style license found in the LICENSE file.
Contributions should follow the same procedure as for the Go project:
http://golang.org/doc/contribute.html

29
vendor/github.com/BurntSushi/xgb/STYLE generated vendored Normal file
View File

@@ -0,0 +1,29 @@
I like to keep all my code to 80 columns or less. I have plenty of screen real
estate, but enjoy 80 columns so that I can have multiple code windows open side
to side and not be plagued by the ugly auto-wrapping of a text editor.
If you don't oblige me, I will fix any patch you submit to abide 80 columns.
Note that this style restriction does not preclude gofmt, but introduces a few
peculiarities. The first is that gofmt will occasionally add spacing (typically
to comments) that ends up going over 80 columns. Either shorten the comment or
put it on its own line.
The second and more common hiccup is when a function definition extends beyond
80 columns. If one adds line breaks to keep it below 80 columns, gofmt will
indent all subsequent lines in a function definition to the same indentation
level of the function body. This results in a less-than-ideal separation
between function definition and function body. To remedy this, simply add a
line break like so:
func RestackWindowExtra(xu *xgbutil.XUtil, win xproto.Window, stackMode int,
sibling xproto.Window, source int) error {
return ClientEvent(xu, win, "_NET_RESTACK_WINDOW", source, int(sibling),
stackMode)
}
Something similar should also be applied to long 'if' or 'for' conditionals,
although it would probably be preferrable to break up the conditional to
smaller chunks with a few helper variables.

110
vendor/github.com/BurntSushi/xgb/auth.go generated vendored Normal file
View File

@@ -0,0 +1,110 @@
package xgb
/*
auth.go contains functions to facilitate the parsing of .Xauthority files.
It is largely unmodified from the original XGB package that I forked.
*/
import (
"encoding/binary"
"errors"
"io"
"os"
)
// readAuthority reads the X authority file for the DISPLAY.
// If hostname == "" or hostname == "localhost",
// then use the system's hostname (as returned by os.Hostname) instead.
func readAuthority(hostname, display string) (
name string, data []byte, err error) {
// b is a scratch buffer to use and should be at least 256 bytes long
// (i.e. it should be able to hold a hostname).
b := make([]byte, 256)
// As per /usr/include/X11/Xauth.h.
const familyLocal = 256
const familyWild = 65535
if len(hostname) == 0 || hostname == "localhost" {
hostname, err = os.Hostname()
if err != nil {
return "", nil, err
}
}
fname := os.Getenv("XAUTHORITY")
if len(fname) == 0 {
home := os.Getenv("HOME")
if len(home) == 0 {
err = errors.New("Xauthority not found: $XAUTHORITY, $HOME not set")
return "", nil, err
}
fname = home + "/.Xauthority"
}
r, err := os.Open(fname)
if err != nil {
return "", nil, err
}
defer r.Close()
for {
var family uint16
if err := binary.Read(r, binary.BigEndian, &family); err != nil {
return "", nil, err
}
addr, err := getString(r, b)
if err != nil {
return "", nil, err
}
disp, err := getString(r, b)
if err != nil {
return "", nil, err
}
name0, err := getString(r, b)
if err != nil {
return "", nil, err
}
data0, err := getBytes(r, b)
if err != nil {
return "", nil, err
}
addrmatch := (family == familyWild) ||
(family == familyLocal && addr == hostname)
dispmatch := (disp == "") || (disp == display)
if addrmatch && dispmatch {
return name0, data0, nil
}
}
panic("unreachable")
}
func getBytes(r io.Reader, b []byte) ([]byte, error) {
var n uint16
if err := binary.Read(r, binary.BigEndian, &n); err != nil {
return nil, err
} else if n > uint16(len(b)) {
return nil, errors.New("bytes too long for buffer")
}
if _, err := io.ReadFull(r, b[0:n]); err != nil {
return nil, err
}
return b[0:n], nil
}
func getString(r io.Reader, b []byte) (string, error) {
b, err := getBytes(r, b)
if err != nil {
return "", err
}
return string(b), nil
}

186
vendor/github.com/BurntSushi/xgb/conn.go generated vendored Normal file
View File

@@ -0,0 +1,186 @@
package xgb
/*
conn.go contains a couple of functions that do some real dirty work related
to the initial connection handshake with X.
This code is largely unmodified from the original XGB package that I forked.
*/
import (
"errors"
"fmt"
"io"
"net"
"os"
"strconv"
"strings"
)
// connect connects to the X server given in the 'display' string,
// and does all the necessary setup handshaking.
// If 'display' is empty it will be taken from os.Getenv("DISPLAY").
// Note that you should read and understand the "Connection Setup" of the
// X Protocol Reference Manual before changing this function:
// http://goo.gl/4zGQg
func (c *Conn) connect(display string) error {
err := c.dial(display)
if err != nil {
return err
}
return c.postConnect()
}
// connect init from to the net.Conn,
func (c *Conn) connectNet(netConn net.Conn) error {
c.conn = netConn
return c.postConnect()
}
// do the postConnect action after Conn get it's underly net.Conn
func (c *Conn) postConnect() error {
// Get authentication data
authName, authData, err := readAuthority(c.host, c.display)
noauth := false
if err != nil {
Logger.Printf("Could not get authority info: %v", err)
Logger.Println("Trying connection without authority info...")
authName = ""
authData = []byte{}
noauth = true
}
// Assume that the authentication protocol is "MIT-MAGIC-COOKIE-1".
if !noauth && (authName != "MIT-MAGIC-COOKIE-1" || len(authData) != 16) {
return errors.New("unsupported auth protocol " + authName)
}
buf := make([]byte, 12+Pad(len(authName))+Pad(len(authData)))
buf[0] = 0x6c
buf[1] = 0
Put16(buf[2:], 11)
Put16(buf[4:], 0)
Put16(buf[6:], uint16(len(authName)))
Put16(buf[8:], uint16(len(authData)))
Put16(buf[10:], 0)
copy(buf[12:], []byte(authName))
copy(buf[12+Pad(len(authName)):], authData)
if _, err = c.conn.Write(buf); err != nil {
return err
}
head := make([]byte, 8)
if _, err = io.ReadFull(c.conn, head[0:8]); err != nil {
return err
}
code := head[0]
reasonLen := head[1]
major := Get16(head[2:])
minor := Get16(head[4:])
dataLen := Get16(head[6:])
if major != 11 || minor != 0 {
return fmt.Errorf("x protocol version mismatch: %d.%d", major, minor)
}
buf = make([]byte, int(dataLen)*4+8, int(dataLen)*4+8)
copy(buf, head)
if _, err = io.ReadFull(c.conn, buf[8:]); err != nil {
return err
}
if code == 0 {
reason := buf[8 : 8+reasonLen]
return fmt.Errorf("x protocol authentication refused: %s",
string(reason))
}
// Unfortunately, it isn't really feasible to read the setup bytes here,
// since the code to do so is in a different package.
// Users must call 'xproto.Setup(X)' to get the setup info.
c.SetupBytes = buf
// But also read stuff that we *need* to get started.
c.setupResourceIdBase = Get32(buf[12:])
c.setupResourceIdMask = Get32(buf[16:])
return nil
}
// dial initializes the actual net connection with X.
func (c *Conn) dial(display string) error {
if len(display) == 0 {
display = os.Getenv("DISPLAY")
}
display0 := display
if len(display) == 0 {
return errors.New("empty display string")
}
colonIdx := strings.LastIndex(display, ":")
if colonIdx < 0 {
return errors.New("bad display string: " + display0)
}
var protocol, socket string
if display[0] == '/' {
socket = display[0:colonIdx]
} else {
slashIdx := strings.LastIndex(display, "/")
if slashIdx >= 0 {
protocol = display[0:slashIdx]
c.host = display[slashIdx+1 : colonIdx]
} else {
c.host = display[0:colonIdx]
}
}
display = display[colonIdx+1 : len(display)]
if len(display) == 0 {
return errors.New("bad display string: " + display0)
}
var scr string
dotIdx := strings.LastIndex(display, ".")
if dotIdx < 0 {
c.display = display[0:]
} else {
c.display = display[0:dotIdx]
scr = display[dotIdx+1:]
}
var err error
c.DisplayNumber, err = strconv.Atoi(c.display)
if err != nil || c.DisplayNumber < 0 {
return errors.New("bad display string: " + display0)
}
if len(scr) != 0 {
c.DefaultScreen, err = strconv.Atoi(scr)
if err != nil {
return errors.New("bad display string: " + display0)
}
}
// Connect to server
if len(socket) != 0 {
c.conn, err = net.Dial("unix", socket+":"+c.display)
} else if len(c.host) != 0 && c.host != "unix" {
if protocol == "" {
protocol = "tcp"
}
c.conn, err = net.Dial(protocol,
c.host+":"+strconv.Itoa(6000+c.DisplayNumber))
} else {
c.host = ""
c.conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+c.display)
}
if err != nil {
return errors.New("cannot connect to " + display0 + ": " + err.Error())
}
return nil
}

165
vendor/github.com/BurntSushi/xgb/cookie.go generated vendored Normal file
View File

@@ -0,0 +1,165 @@
package xgb
import (
"errors"
)
// Cookie is the internal representation of a cookie, where one is generated
// for *every* request sent by XGB.
// 'cookie' is most frequently used by embedding it into a more specific
// kind of cookie, i.e., 'GetInputFocusCookie'.
type Cookie struct {
conn *Conn
Sequence uint16
replyChan chan []byte
errorChan chan error
pingChan chan bool
}
// NewCookie creates a new cookie with the correct channels initialized
// depending upon the values of 'checked' and 'reply'. Together, there are
// four different kinds of cookies. (See more detailed comments in the
// function for more info on those.)
// Note that a sequence number is not set until just before the request
// corresponding to this cookie is sent over the wire.
//
// Unless you're building requests from bytes by hand, this method should
// not be used.
func (c *Conn) NewCookie(checked, reply bool) *Cookie {
cookie := &Cookie{
conn: c,
Sequence: 0, // we add the sequence id just before sending a request
replyChan: nil,
errorChan: nil,
pingChan: nil,
}
// There are four different kinds of cookies:
// Checked requests with replies get a reply channel and an error channel.
// Unchecked requests with replies get a reply channel and a ping channel.
// Checked requests w/o replies get a ping channel and an error channel.
// Unchecked requests w/o replies get no channels.
// The reply channel is used to send reply data.
// The error channel is used to send error data.
// The ping channel is used when one of the 'reply' or 'error' channels
// is missing but the other is present. The ping channel is way to force
// the blocking to stop and basically say "the error has been received
// in the main event loop" (when the ping channel is coupled with a reply
// channel) or "the request you made that has no reply was successful"
// (when the ping channel is coupled with an error channel).
if checked {
cookie.errorChan = make(chan error, 1)
if !reply {
cookie.pingChan = make(chan bool, 1)
}
}
if reply {
cookie.replyChan = make(chan []byte, 1)
if !checked {
cookie.pingChan = make(chan bool, 1)
}
}
return cookie
}
// Reply detects whether this is a checked or unchecked cookie, and calls
// 'replyChecked' or 'replyUnchecked' appropriately.
//
// Unless you're building requests from bytes by hand, this method should
// not be used.
func (c Cookie) Reply() ([]byte, error) {
// checked
if c.errorChan != nil {
return c.replyChecked()
}
return c.replyUnchecked()
}
// replyChecked waits for a response on either the replyChan or errorChan
// channels. If the former arrives, the bytes are returned with a nil error.
// If the latter arrives, no bytes are returned (nil) and the error received
// is returned.
//
// Unless you're building requests from bytes by hand, this method should
// not be used.
func (c Cookie) replyChecked() ([]byte, error) {
if c.replyChan == nil {
return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
"is not expecting a *reply* or an error.")
}
if c.errorChan == nil {
return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
"is not expecting a reply or an *error*.")
}
select {
case reply := <-c.replyChan:
return reply, nil
case err := <-c.errorChan:
return nil, err
}
}
// replyUnchecked waits for a response on either the replyChan or pingChan
// channels. If the former arrives, the bytes are returned with a nil error.
// If the latter arrives, no bytes are returned (nil) and a nil error
// is returned. (In the latter case, the corresponding error can be retrieved
// from (Wait|Poll)ForEvent asynchronously.)
// In all honesty, you *probably* don't want to use this method.
//
// Unless you're building requests from bytes by hand, this method should
// not be used.
func (c Cookie) replyUnchecked() ([]byte, error) {
if c.replyChan == nil {
return nil, errors.New("Cannot call 'replyUnchecked' on a cookie " +
"that is not expecting a *reply*.")
}
select {
case reply := <-c.replyChan:
return reply, nil
case <-c.pingChan:
return nil, nil
}
}
// Check is used for checked requests that have no replies. It is a mechanism
// by which to report "success" or "error" in a synchronous fashion. (Therefore,
// unchecked requests without replies cannot use this method.)
// If the request causes an error, it is sent to this cookie's errorChan.
// If the request was successful, there is no response from the server.
// Thus, pingChan is sent a value when the *next* reply is read.
// If no more replies are being processed, we force a round trip request with
// GetInputFocus.
//
// Unless you're building requests from bytes by hand, this method should
// not be used.
func (c Cookie) Check() error {
if c.replyChan != nil {
return errors.New("Cannot call 'Check' on a cookie that is " +
"expecting a *reply*. Use 'Reply' instead.")
}
if c.errorChan == nil {
return errors.New("Cannot call 'Check' on a cookie that is " +
"not expecting a possible *error*.")
}
// First do a quick non-blocking check to see if we've been pinged.
select {
case err := <-c.errorChan:
return err
case <-c.pingChan:
return nil
default:
}
// Now force a round trip and try again, but block this time.
c.conn.Sync()
select {
case err := <-c.errorChan:
return err
case <-c.pingChan:
return nil
}
}

146
vendor/github.com/BurntSushi/xgb/doc.go generated vendored Normal file
View File

@@ -0,0 +1,146 @@
/*
Package XGB provides the X Go Binding, which is a low-level API to communicate
with the core X protocol and many of the X extensions.
It is *very* closely modeled on XCB, so that experience with XCB (or xpyb) is
easily translatable to XGB. That is, it uses the same cookie/reply model
and is thread safe. There are otherwise no major differences (in the API).
Most uses of XGB typically fall under the realm of window manager and GUI kit
development, but other applications (like pagers, panels, tilers, etc.) may
also require XGB. Moreover, it is a near certainty that if you need to work
with X, xgbutil will be of great use to you as well:
https://github.com/BurntSushi/xgbutil
Example
This is an extremely terse example that demonstrates how to connect to X,
create a window, listen to StructureNotify events and Key{Press,Release}
events, map the window, and print out all events received. An example with
accompanying documentation can be found in examples/create-window.
package main
import (
"fmt"
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xproto"
)
func main() {
X, err := xgb.NewConn()
if err != nil {
fmt.Println(err)
return
}
wid, _ := xproto.NewWindowId(X)
screen := xproto.Setup(X).DefaultScreen(X)
xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root,
0, 0, 500, 500, 0,
xproto.WindowClassInputOutput, screen.RootVisual,
xproto.CwBackPixel | xproto.CwEventMask,
[]uint32{ // values must be in the order defined by the protocol
0xffffffff,
xproto.EventMaskStructureNotify |
xproto.EventMaskKeyPress |
xproto.EventMaskKeyRelease})
xproto.MapWindow(X, wid)
for {
ev, xerr := X.WaitForEvent()
if ev == nil && xerr == nil {
fmt.Println("Both event and error are nil. Exiting...")
return
}
if ev != nil {
fmt.Printf("Event: %s\n", ev)
}
if xerr != nil {
fmt.Printf("Error: %s\n", xerr)
}
}
}
Xinerama Example
This is another small example that shows how to query Xinerama for geometry
information of each active head. Accompanying documentation for this example
can be found in examples/xinerama.
package main
import (
"fmt"
"log"
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xinerama"
)
func main() {
X, err := xgb.NewConn()
if err != nil {
log.Fatal(err)
}
// Initialize the Xinerama extension.
// The appropriate 'Init' function must be run for *every*
// extension before any of its requests can be used.
err = xinerama.Init(X)
if err != nil {
log.Fatal(err)
}
reply, err := xinerama.QueryScreens(X).Reply()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Number of heads: %d\n", reply.Number)
for i, screen := range reply.ScreenInfo {
fmt.Printf("%d :: X: %d, Y: %d, Width: %d, Height: %d\n",
i, screen.XOrg, screen.YOrg, screen.Width, screen.Height)
}
}
Parallelism
XGB can benefit greatly from parallelism due to its concurrent design. For
evidence of this claim, please see the benchmarks in xproto/xproto_test.go.
Tests
xproto/xproto_test.go contains a number of contrived tests that stress
particular corners of XGB that I presume could be problem areas. Namely:
requests with no replies, requests with replies, checked errors, unchecked
errors, sequence number wrapping, cookie buffer flushing (i.e., forcing a round
trip every N requests made that don't have a reply), getting/setting properties
and creating a window and listening to StructureNotify events.
Code Generator
Both XCB and xpyb use the same Python module (xcbgen) for a code generator. XGB
(before this fork) used the same code generator as well, but in my attempt to
add support for more extensions, I found the code generator extremely difficult
to work with. Therefore, I re-wrote the code generator in Go. It can be found
in its own sub-package, xgbgen, of xgb. My design of xgbgen includes a rough
consideration that it could be used for other languages.
What works
I am reasonably confident that the core X protocol is in full working form. I've
also tested the Xinerama and RandR extensions sparingly. Many of the other
existing extensions have Go source generated (and are compilable) and are
included in this package, but I am currently unsure of their status. They
*should* work.
What does not work
XKB is the only extension that intentionally does not work, although I suspect
that GLX also does not work (however, there is Go source code for GLX that
compiles, unlike XKB). I don't currently have any intention of getting XKB
working, due to its complexity and my current mental incapacity to test it.
*/
package xgb

105
vendor/github.com/BurntSushi/xgb/help.go generated vendored Normal file
View File

@@ -0,0 +1,105 @@
package xgb
/*
help.go is meant to contain a rough hodge podge of functions that are mainly
used in the auto generated code. Indeed, several functions here are simple
wrappers so that the sub-packages don't need to be smart about which stdlib
packages to import.
Also, the 'Get..' and 'Put..' functions are used through the core xgb package
too. (xgbutil uses them too.)
*/
import (
"fmt"
"strings"
)
// StringsJoin is an alias to strings.Join. It allows us to avoid having to
// import 'strings' in each of the generated Go files.
func StringsJoin(ss []string, sep string) string {
return strings.Join(ss, sep)
}
// Sprintf is so we don't need to import 'fmt' in the generated Go files.
func Sprintf(format string, v ...interface{}) string {
return fmt.Sprintf(format, v...)
}
// Errorf is just a wrapper for fmt.Errorf. Exists for the same reason
// that 'stringsJoin' and 'sprintf' exists.
func Errorf(format string, v ...interface{}) error {
return fmt.Errorf(format, v...)
}
// Pad a length to align on 4 bytes.
func Pad(n int) int {
return (n + 3) & ^3
}
// PopCount counts the number of bits set in a value list mask.
func PopCount(mask0 int) int {
mask := uint32(mask0)
n := 0
for i := uint32(0); i < 32; i++ {
if mask&(1<<i) != 0 {
n++
}
}
return n
}
// Put16 takes a 16 bit integer and copies it into a byte slice.
func Put16(buf []byte, v uint16) {
buf[0] = byte(v)
buf[1] = byte(v >> 8)
}
// Put32 takes a 32 bit integer and copies it into a byte slice.
func Put32(buf []byte, v uint32) {
buf[0] = byte(v)
buf[1] = byte(v >> 8)
buf[2] = byte(v >> 16)
buf[3] = byte(v >> 24)
}
// Put64 takes a 64 bit integer and copies it into a byte slice.
func Put64(buf []byte, v uint64) {
buf[0] = byte(v)
buf[1] = byte(v >> 8)
buf[2] = byte(v >> 16)
buf[3] = byte(v >> 24)
buf[4] = byte(v >> 32)
buf[5] = byte(v >> 40)
buf[6] = byte(v >> 48)
buf[7] = byte(v >> 56)
}
// Get16 constructs a 16 bit integer from the beginning of a byte slice.
func Get16(buf []byte) uint16 {
v := uint16(buf[0])
v |= uint16(buf[1]) << 8
return v
}
// Get32 constructs a 32 bit integer from the beginning of a byte slice.
func Get32(buf []byte) uint32 {
v := uint32(buf[0])
v |= uint32(buf[1]) << 8
v |= uint32(buf[2]) << 16
v |= uint32(buf[3]) << 24
return v
}
// Get64 constructs a 64 bit integer from the beginning of a byte slice.
func Get64(buf []byte) uint64 {
v := uint64(buf[0])
v |= uint64(buf[1]) << 8
v |= uint64(buf[2]) << 16
v |= uint64(buf[3]) << 24
v |= uint64(buf[4]) << 32
v |= uint64(buf[5]) << 40
v |= uint64(buf[6]) << 48
v |= uint64(buf[7]) << 56
return v
}

29
vendor/github.com/BurntSushi/xgb/sync.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
package xgb
// Sync sends a round trip request and waits for the response.
// This forces all pending cookies to be dealt with.
// You actually shouldn't need to use this like you might with Xlib. Namely,
// buffers are automatically flushed using Go's channels and round trip requests
// are forced where appropriate automatically.
func (c *Conn) Sync() {
cookie := c.NewCookie(true, true)
c.NewRequest(c.getInputFocusRequest(), cookie)
cookie.Reply() // wait for the buffer to clear
}
// getInputFocusRequest writes the raw bytes to a buffer.
// It is duplicated from xproto/xproto.go.
func (c *Conn) getInputFocusRequest() []byte {
size := 4
b := 0
buf := make([]byte, size)
buf[b] = 43 // request opcode
b += 1
b += 1 // padding
Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
return buf
}

554
vendor/github.com/BurntSushi/xgb/xgb.go generated vendored Normal file
View File

@@ -0,0 +1,554 @@
package xgb
import (
"errors"
"io"
"log"
"net"
"os"
"sync"
)
var (
// Where to log error-messages. Defaults to stderr.
// To disable logging, just set this to log.New(ioutil.Discard, "", 0)
Logger = log.New(os.Stderr, "XGB: ", log.Lshortfile)
)
const (
// cookieBuffer represents the queue size of cookies existing at any
// point in time. The size of the buffer is really only important when
// there are many requests without replies made in sequence. Once the
// buffer fills, a round trip request is made to clear the buffer.
cookieBuffer = 1000
// xidBuffer represents the queue size of the xid channel.
// I don't think this value matters much, since xid generation is not
// that expensive.
xidBuffer = 5
// seqBuffer represents the queue size of the sequence number channel.
// I don't think this value matters much, since sequence number generation
// is not that expensive.
seqBuffer = 5
// reqBuffer represents the queue size of the number of requests that
// can be made until new ones block. This value seems OK.
reqBuffer = 100
// eventBuffer represents the queue size of the number of events or errors
// that can be loaded off the wire and not grabbed with WaitForEvent
// until reading an event blocks. This value should be big enough to handle
// bursts of events.
eventBuffer = 5000
)
// A Conn represents a connection to an X server.
type Conn struct {
host string
conn net.Conn
display string
DisplayNumber int
DefaultScreen int
SetupBytes []byte
setupResourceIdBase uint32
setupResourceIdMask uint32
eventChan chan eventOrError
cookieChan chan *Cookie
xidChan chan xid
seqChan chan uint16
reqChan chan *request
closing chan chan struct{}
// ExtLock is a lock used whenever new extensions are initialized.
// It should not be used. It is exported for use in the extension
// sub-packages.
ExtLock sync.RWMutex
// Extensions is a map from extension name to major opcode. It should
// not be used. It is exported for use in the extension sub-packages.
Extensions map[string]byte
}
// NewConn creates a new connection instance. It initializes locks, data
// structures, and performs the initial handshake. (The code for the handshake
// has been relegated to conn.go.)
func NewConn() (*Conn, error) {
return NewConnDisplay("")
}
// NewConnDisplay is just like NewConn, but allows a specific DISPLAY
// string to be used.
// If 'display' is empty it will be taken from os.Getenv("DISPLAY").
//
// Examples:
// NewConn(":1") -> net.Dial("unix", "", "/tmp/.X11-unix/X1")
// NewConn("/tmp/launch-12/:0") -> net.Dial("unix", "", "/tmp/launch-12/:0")
// NewConn("hostname:2.1") -> net.Dial("tcp", "", "hostname:6002")
// NewConn("tcp/hostname:1.0") -> net.Dial("tcp", "", "hostname:6001")
func NewConnDisplay(display string) (*Conn, error) {
conn := &Conn{}
// First connect. This reads authority, checks DISPLAY environment
// variable, and loads the initial Setup info.
err := conn.connect(display)
if err != nil {
return nil, err
}
return postNewConn(conn)
}
// NewConnDisplay is just like NewConn, but allows a specific net.Conn
// to be used.
func NewConnNet(netConn net.Conn) (*Conn, error) {
conn := &Conn{}
// First connect. This reads authority, checks DISPLAY environment
// variable, and loads the initial Setup info.
err := conn.connectNet(netConn)
if err != nil {
return nil, err
}
return postNewConn(conn)
}
func postNewConn(conn *Conn) (*Conn, error) {
conn.Extensions = make(map[string]byte)
conn.cookieChan = make(chan *Cookie, cookieBuffer)
conn.xidChan = make(chan xid, xidBuffer)
conn.seqChan = make(chan uint16, seqBuffer)
conn.reqChan = make(chan *request, reqBuffer)
conn.eventChan = make(chan eventOrError, eventBuffer)
conn.closing = make(chan chan struct{}, 1)
go conn.generateXIds()
go conn.generateSeqIds()
go conn.sendRequests()
go conn.readResponses()
return conn, nil
}
// Close gracefully closes the connection to the X server.
func (c *Conn) Close() {
close(c.reqChan)
}
// Event is an interface that can contain any of the events returned by the
// server. Use a type assertion switch to extract the Event structs.
type Event interface {
Bytes() []byte
String() string
}
// NewEventFun is the type of function use to construct events from raw bytes.
// It should not be used. It is exported for use in the extension sub-packages.
type NewEventFun func(buf []byte) Event
// NewEventFuncs is a map from event numbers to functions that create
// the corresponding event. It should not be used. It is exported for use
// in the extension sub-packages.
var NewEventFuncs = make(map[int]NewEventFun)
// NewExtEventFuncs is a temporary map that stores event constructor functions
// for each extension. When an extension is initialized, each event for that
// extension is added to the 'NewEventFuncs' map. It should not be used. It is
// exported for use in the extension sub-packages.
var NewExtEventFuncs = make(map[string]map[int]NewEventFun)
// Error is an interface that can contain any of the errors returned by
// the server. Use a type assertion switch to extract the Error structs.
type Error interface {
SequenceId() uint16
BadId() uint32
Error() string
}
// NewErrorFun is the type of function use to construct errors from raw bytes.
// It should not be used. It is exported for use in the extension sub-packages.
type NewErrorFun func(buf []byte) Error
// NewErrorFuncs is a map from error numbers to functions that create
// the corresponding error. It should not be used. It is exported for use in
// the extension sub-packages.
var NewErrorFuncs = make(map[int]NewErrorFun)
// NewExtErrorFuncs is a temporary map that stores error constructor functions
// for each extension. When an extension is initialized, each error for that
// extension is added to the 'NewErrorFuncs' map. It should not be used. It is
// exported for use in the extension sub-packages.
var NewExtErrorFuncs = make(map[string]map[int]NewErrorFun)
// eventOrError corresponds to values that can be either an event or an
// error.
type eventOrError interface{}
// NewId generates a new unused ID for use with requests like CreateWindow.
// If no new ids can be generated, the id returned is 0 and error is non-nil.
// This shouldn't be used directly, and is exported for use in the extension
// sub-packages.
// If you need identifiers, use the appropriate constructor.
// e.g., For a window id, use xproto.NewWindowId. For
// a new pixmap id, use xproto.NewPixmapId. And so on.
func (c *Conn) NewId() (uint32, error) {
xid := <-c.xidChan
if xid.err != nil {
return 0, xid.err
}
return xid.id, nil
}
// xid encapsulates a resource identifier being sent over the Conn.xidChan
// channel. If no new resource id can be generated, id is set to 0 and a
// non-nil error is set in xid.err.
type xid struct {
id uint32
err error
}
// generateXids sends new Ids down the channel for NewId to use.
// generateXids should be run in its own goroutine.
// This needs to be updated to use the XC Misc extension once we run out of
// new ids.
// Thanks to libxcb/src/xcb_xid.c. This code is greatly inspired by it.
func (conn *Conn) generateXIds() {
defer close(conn.xidChan)
// This requires some explanation. From the horse's mouth:
// "The resource-id-mask contains a single contiguous set of bits (at least
// 18). The client allocates resource IDs for types WINDOW, PIXMAP,
// CURSOR, FONT, GCONTEXT, and COLORMAP by choosing a value with only some
// subset of these bits set and ORing it with resource-id-base. Only values
// constructed in this way can be used to name newly created resources over
// this connection."
// So for example (using 8 bit integers), the mask might look like:
// 00111000
// So that valid values would be 00101000, 00110000, 00001000, and so on.
// Thus, the idea is to increment it by the place of the last least
// significant '1'. In this case, that value would be 00001000. To get
// that value, we can AND the original mask with its two's complement:
// 00111000 & 11001000 = 00001000.
// And we use that value to increment the last resource id to get a new one.
// (And then, of course, we OR it with resource-id-base.)
inc := conn.setupResourceIdMask & -conn.setupResourceIdMask
max := conn.setupResourceIdMask
last := uint32(0)
for {
// TODO: Use the XC Misc extension to look for released ids.
if last > 0 && last >= max-inc+1 {
conn.xidChan <- xid{
id: 0,
err: errors.New("There are no more available resource" +
"identifiers."),
}
}
last += inc
conn.xidChan <- xid{
id: last | conn.setupResourceIdBase,
err: nil,
}
}
}
// newSeqId fetches the next sequence id from the Conn.seqChan channel.
func (c *Conn) newSequenceId() uint16 {
return <-c.seqChan
}
// generateSeqIds returns new sequence ids. It is meant to be run in its
// own goroutine.
// A sequence id is generated for *every* request. It's the identifier used
// to match up replies with requests.
// Since sequence ids can only be 16 bit integers we start over at zero when it
// comes time to wrap.
// N.B. As long as the cookie buffer is less than 2^16, there are no limitations
// on the number (or kind) of requests made in sequence.
func (c *Conn) generateSeqIds() {
defer close(c.seqChan)
seqid := uint16(1)
for {
c.seqChan <- seqid
if seqid == uint16((1<<16)-1) {
seqid = 0
} else {
seqid++
}
}
}
// request encapsulates a buffer of raw bytes (containing the request data)
// and a cookie, which when combined represents a single request.
// The cookie is used to match up the reply/error.
type request struct {
buf []byte
cookie *Cookie
// seq is closed when the request (cookie) has been sequenced by the Conn.
seq chan struct{}
}
// NewRequest takes the bytes and a cookie of a particular request, constructs
// a request type, and sends it over the Conn.reqChan channel.
// Note that the sequence number is added to the cookie after it is sent
// over the request channel, but before it is sent to X.
//
// Note that you may safely use NewRequest to send arbitrary byte requests
// to X. The resulting cookie can be used just like any normal cookie and
// abides by the same rules, except that for replies, you'll get back the
// raw byte data. This may be useful for performance critical sections where
// every allocation counts, since all X requests in XGB allocate a new byte
// slice. In contrast, NewRequest allocates one small request struct and
// nothing else. (Except when the cookie buffer is full and has to be flushed.)
//
// If you're using NewRequest manually, you'll need to use NewCookie to create
// a new cookie.
//
// In all likelihood, you should be able to copy and paste with some minor
// edits the generated code for the request you want to issue.
func (c *Conn) NewRequest(buf []byte, cookie *Cookie) {
seq := make(chan struct{})
c.reqChan <- &request{buf: buf, cookie: cookie, seq: seq}
<-seq
}
// sendRequests is run as a single goroutine that takes requests and writes
// the bytes to the wire and adds the cookie to the cookie queue.
// It is meant to be run as its own goroutine.
func (c *Conn) sendRequests() {
defer close(c.cookieChan)
for req := range c.reqChan {
// ho there! if the cookie channel is nearly full, force a round
// trip to clear out the cookie buffer.
// Note that we circumvent the request channel, because we're *in*
// the request channel.
if len(c.cookieChan) == cookieBuffer-1 {
if err := c.noop(); err != nil {
// Shut everything down.
break
}
}
req.cookie.Sequence = c.newSequenceId()
c.cookieChan <- req.cookie
c.writeBuffer(req.buf)
close(req.seq)
}
response := make(chan struct{})
c.closing <- response
c.noop() // Flush the response reading goroutine, ignore error.
<-response
c.conn.Close()
}
// noop circumvents the usual request sending goroutines and forces a round
// trip request manually.
func (c *Conn) noop() error {
cookie := c.NewCookie(true, true)
cookie.Sequence = c.newSequenceId()
c.cookieChan <- cookie
if err := c.writeBuffer(c.getInputFocusRequest()); err != nil {
return err
}
cookie.Reply() // wait for the buffer to clear
return nil
}
// writeBuffer is a convenience function for writing a byte slice to the wire.
func (c *Conn) writeBuffer(buf []byte) error {
if _, err := c.conn.Write(buf); err != nil {
Logger.Printf("A write error is unrecoverable: %s", err)
return err
} else {
return nil
}
}
// readResponses is a goroutine that reads events, errors and
// replies off the wire.
// When an event is read, it is always added to the event channel.
// When an error is read, if it corresponds to an existing checked cookie,
// it is sent to that cookie's error channel. Otherwise it is added to the
// event channel.
// When a reply is read, it is added to the corresponding cookie's reply
// channel. (It is an error if no such cookie exists in this case.)
// Finally, cookies that came "before" this reply are always cleaned up.
func (c *Conn) readResponses() {
defer close(c.eventChan)
var (
err Error
seq uint16
replyBytes []byte
)
for {
select {
case respond := <-c.closing:
respond <- struct{}{}
return
default:
}
buf := make([]byte, 32)
err, seq = nil, 0
if _, err := io.ReadFull(c.conn, buf); err != nil {
Logger.Printf("A read error is unrecoverable: %s", err)
c.eventChan <- err
c.Close()
continue
}
switch buf[0] {
case 0: // This is an error
// Use the constructor function for this error (that is auto
// generated) by looking it up by the error number.
newErrFun, ok := NewErrorFuncs[int(buf[1])]
if !ok {
Logger.Printf("BUG: Could not find error constructor function "+
"for error with number %d.", buf[1])
continue
}
err = newErrFun(buf)
seq = err.SequenceId()
// This error is either sent to the event channel or a specific
// cookie's error channel below.
case 1: // This is a reply
seq = Get16(buf[2:])
// check to see if this reply has more bytes to be read
size := Get32(buf[4:])
if size > 0 {
byteCount := 32 + size*4
biggerBuf := make([]byte, byteCount)
copy(biggerBuf[:32], buf)
if _, err := io.ReadFull(c.conn, biggerBuf[32:]); err != nil {
Logger.Printf("A read error is unrecoverable: %s", err)
c.eventChan <- err
c.Close()
continue
}
replyBytes = biggerBuf
} else {
replyBytes = buf
}
// This reply is sent to its corresponding cookie below.
default: // This is an event
// Use the constructor function for this event (like for errors,
// and is also auto generated) by looking it up by the event number.
// Note that we AND the event number with 127 so that we ignore
// the most significant bit (which is set when it was sent from
// a SendEvent request).
evNum := int(buf[0] & 127)
newEventFun, ok := NewEventFuncs[evNum]
if !ok {
Logger.Printf("BUG: Could not find event construct function "+
"for event with number %d.", evNum)
continue
}
c.eventChan <- newEventFun(buf)
continue
}
// At this point, we have a sequence number and we're either
// processing an error or a reply, which are both responses to
// requests. So all we have to do is find the cookie corresponding
// to this error/reply, and send the appropriate data to it.
// In doing so, we make sure that any cookies that came before it
// are marked as successful if they are void and checked.
// If there's a cookie that requires a reply that is before this
// reply, then something is wrong.
for cookie := range c.cookieChan {
// This is the cookie we're looking for. Process and break.
if cookie.Sequence == seq {
if err != nil { // this is an error to a request
// synchronous processing
if cookie.errorChan != nil {
cookie.errorChan <- err
} else { // asynchronous processing
c.eventChan <- err
// if this is an unchecked reply, ping the cookie too
if cookie.pingChan != nil {
cookie.pingChan <- true
}
}
} else { // this is a reply
if cookie.replyChan == nil {
Logger.Printf("Reply with sequence id %d does not "+
"have a cookie with a valid reply channel.", seq)
continue
} else {
cookie.replyChan <- replyBytes
}
}
break
}
switch {
// Checked requests with replies
case cookie.replyChan != nil && cookie.errorChan != nil:
Logger.Printf("Found cookie with sequence id %d that is "+
"expecting a reply but will never get it. Currently "+
"on sequence number %d", cookie.Sequence, seq)
// Unchecked requests with replies
case cookie.replyChan != nil && cookie.pingChan != nil:
Logger.Printf("Found cookie with sequence id %d that is "+
"expecting a reply (and not an error) but will never "+
"get it. Currently on sequence number %d",
cookie.Sequence, seq)
// Checked requests without replies
case cookie.pingChan != nil && cookie.errorChan != nil:
cookie.pingChan <- true
// Unchecked requests without replies don't have any channels,
// so we can't do anything with them except let them pass by.
}
}
}
}
// processEventOrError takes an eventOrError, type switches on it,
// and returns it in Go idiomatic style.
func processEventOrError(everr eventOrError) (Event, Error) {
switch ee := everr.(type) {
case Event:
return ee, nil
case Error:
return nil, ee
default:
Logger.Printf("Invalid event/error type: %T", everr)
return nil, nil
}
}
// WaitForEvent returns the next event from the server.
// It will block until an event is available.
// WaitForEvent returns either an Event or an Error. (Returning both
// is a bug.) Note than an Error here is an X error and not an XGB error. That
// is, X errors are sometimes completely expected (and you may want to ignore
// them in some cases).
//
// If both the event and error are nil, then the connection has been closed.
func (c *Conn) WaitForEvent() (Event, Error) {
return processEventOrError(<-c.eventChan)
}
// PollForEvent returns the next event from the server if one is available in
// the internal queue without blocking. Note that unlike WaitForEvent, both
// Event and Error could be nil. Indeed, they are both nil when the event queue
// is empty.
func (c *Conn) PollForEvent() (Event, Error) {
select {
case everr := <-c.eventChan:
return processEventOrError(everr)
default:
return nil, nil
}
}

718
vendor/github.com/BurntSushi/xgb/xinerama/xinerama.go generated vendored Normal file
View File

@@ -0,0 +1,718 @@
// Package xinerama is the X client API for the XINERAMA extension.
package xinerama
// This file is automatically generated from xinerama.xml. Edit at your peril!
import (
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xproto"
)
// Init must be called before using the XINERAMA extension.
func Init(c *xgb.Conn) error {
reply, err := xproto.QueryExtension(c, 8, "XINERAMA").Reply()
switch {
case err != nil:
return err
case !reply.Present:
return xgb.Errorf("No extension named XINERAMA could be found on on the server.")
}
c.ExtLock.Lock()
c.Extensions["XINERAMA"] = reply.MajorOpcode
c.ExtLock.Unlock()
for evNum, fun := range xgb.NewExtEventFuncs["XINERAMA"] {
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
}
for errNum, fun := range xgb.NewExtErrorFuncs["XINERAMA"] {
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
}
return nil
}
func init() {
xgb.NewExtEventFuncs["XINERAMA"] = make(map[int]xgb.NewEventFun)
xgb.NewExtErrorFuncs["XINERAMA"] = make(map[int]xgb.NewErrorFun)
}
type ScreenInfo struct {
XOrg int16
YOrg int16
Width uint16
Height uint16
}
// ScreenInfoRead reads a byte slice into a ScreenInfo value.
func ScreenInfoRead(buf []byte, v *ScreenInfo) int {
b := 0
v.XOrg = int16(xgb.Get16(buf[b:]))
b += 2
v.YOrg = int16(xgb.Get16(buf[b:]))
b += 2
v.Width = xgb.Get16(buf[b:])
b += 2
v.Height = xgb.Get16(buf[b:])
b += 2
return b
}
// ScreenInfoReadList reads a byte slice into a list of ScreenInfo values.
func ScreenInfoReadList(buf []byte, dest []ScreenInfo) int {
b := 0
for i := 0; i < len(dest); i++ {
dest[i] = ScreenInfo{}
b += ScreenInfoRead(buf[b:], &dest[i])
}
return xgb.Pad(b)
}
// Bytes writes a ScreenInfo value to a byte slice.
func (v ScreenInfo) Bytes() []byte {
buf := make([]byte, 8)
b := 0
xgb.Put16(buf[b:], uint16(v.XOrg))
b += 2
xgb.Put16(buf[b:], uint16(v.YOrg))
b += 2
xgb.Put16(buf[b:], v.Width)
b += 2
xgb.Put16(buf[b:], v.Height)
b += 2
return buf[:b]
}
// ScreenInfoListBytes writes a list of ScreenInfo values to a byte slice.
func ScreenInfoListBytes(buf []byte, list []ScreenInfo) int {
b := 0
var structBytes []byte
for _, item := range list {
structBytes = item.Bytes()
copy(buf[b:], structBytes)
b += len(structBytes)
}
return xgb.Pad(b)
}
// Skipping definition for base type 'Bool'
// Skipping definition for base type 'Byte'
// Skipping definition for base type 'Card8'
// Skipping definition for base type 'Char'
// Skipping definition for base type 'Void'
// Skipping definition for base type 'Double'
// Skipping definition for base type 'Float'
// Skipping definition for base type 'Int16'
// Skipping definition for base type 'Int32'
// Skipping definition for base type 'Int8'
// Skipping definition for base type 'Card16'
// Skipping definition for base type 'Card32'
// GetScreenCountCookie is a cookie used only for GetScreenCount requests.
type GetScreenCountCookie struct {
*xgb.Cookie
}
// GetScreenCount sends a checked request.
// If an error occurs, it will be returned with the reply by calling GetScreenCountCookie.Reply()
func GetScreenCount(c *xgb.Conn, Window xproto.Window) GetScreenCountCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'GetScreenCount' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, true)
c.NewRequest(getScreenCountRequest(c, Window), cookie)
return GetScreenCountCookie{cookie}
}
// GetScreenCountUnchecked sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func GetScreenCountUnchecked(c *xgb.Conn, Window xproto.Window) GetScreenCountCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'GetScreenCount' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, true)
c.NewRequest(getScreenCountRequest(c, Window), cookie)
return GetScreenCountCookie{cookie}
}
// GetScreenCountReply represents the data returned from a GetScreenCount request.
type GetScreenCountReply struct {
Sequence uint16 // sequence number of the request for this reply
Length uint32 // number of bytes in this reply
ScreenCount byte
Window xproto.Window
}
// Reply blocks and returns the reply data for a GetScreenCount request.
func (cook GetScreenCountCookie) Reply() (*GetScreenCountReply, error) {
buf, err := cook.Cookie.Reply()
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return getScreenCountReply(buf), nil
}
// getScreenCountReply reads a byte slice into a GetScreenCountReply value.
func getScreenCountReply(buf []byte) *GetScreenCountReply {
v := new(GetScreenCountReply)
b := 1 // skip reply determinant
v.ScreenCount = buf[b]
b += 1
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Length = xgb.Get32(buf[b:]) // 4-byte units
b += 4
v.Window = xproto.Window(xgb.Get32(buf[b:]))
b += 4
return v
}
// Write request to wire for GetScreenCount
// getScreenCountRequest writes a GetScreenCount request to a byte slice.
func getScreenCountRequest(c *xgb.Conn, Window xproto.Window) []byte {
size := 8
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["XINERAMA"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 2 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Window))
b += 4
return buf
}
// GetScreenSizeCookie is a cookie used only for GetScreenSize requests.
type GetScreenSizeCookie struct {
*xgb.Cookie
}
// GetScreenSize sends a checked request.
// If an error occurs, it will be returned with the reply by calling GetScreenSizeCookie.Reply()
func GetScreenSize(c *xgb.Conn, Window xproto.Window, Screen uint32) GetScreenSizeCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'GetScreenSize' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, true)
c.NewRequest(getScreenSizeRequest(c, Window, Screen), cookie)
return GetScreenSizeCookie{cookie}
}
// GetScreenSizeUnchecked sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func GetScreenSizeUnchecked(c *xgb.Conn, Window xproto.Window, Screen uint32) GetScreenSizeCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'GetScreenSize' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, true)
c.NewRequest(getScreenSizeRequest(c, Window, Screen), cookie)
return GetScreenSizeCookie{cookie}
}
// GetScreenSizeReply represents the data returned from a GetScreenSize request.
type GetScreenSizeReply struct {
Sequence uint16 // sequence number of the request for this reply
Length uint32 // number of bytes in this reply
// padding: 1 bytes
Width uint32
Height uint32
Window xproto.Window
Screen uint32
}
// Reply blocks and returns the reply data for a GetScreenSize request.
func (cook GetScreenSizeCookie) Reply() (*GetScreenSizeReply, error) {
buf, err := cook.Cookie.Reply()
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return getScreenSizeReply(buf), nil
}
// getScreenSizeReply reads a byte slice into a GetScreenSizeReply value.
func getScreenSizeReply(buf []byte) *GetScreenSizeReply {
v := new(GetScreenSizeReply)
b := 1 // skip reply determinant
b += 1 // padding
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Length = xgb.Get32(buf[b:]) // 4-byte units
b += 4
v.Width = xgb.Get32(buf[b:])
b += 4
v.Height = xgb.Get32(buf[b:])
b += 4
v.Window = xproto.Window(xgb.Get32(buf[b:]))
b += 4
v.Screen = xgb.Get32(buf[b:])
b += 4
return v
}
// Write request to wire for GetScreenSize
// getScreenSizeRequest writes a GetScreenSize request to a byte slice.
func getScreenSizeRequest(c *xgb.Conn, Window xproto.Window, Screen uint32) []byte {
size := 12
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["XINERAMA"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 3 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Window))
b += 4
xgb.Put32(buf[b:], Screen)
b += 4
return buf
}
// GetStateCookie is a cookie used only for GetState requests.
type GetStateCookie struct {
*xgb.Cookie
}
// GetState sends a checked request.
// If an error occurs, it will be returned with the reply by calling GetStateCookie.Reply()
func GetState(c *xgb.Conn, Window xproto.Window) GetStateCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'GetState' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, true)
c.NewRequest(getStateRequest(c, Window), cookie)
return GetStateCookie{cookie}
}
// GetStateUnchecked sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func GetStateUnchecked(c *xgb.Conn, Window xproto.Window) GetStateCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'GetState' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, true)
c.NewRequest(getStateRequest(c, Window), cookie)
return GetStateCookie{cookie}
}
// GetStateReply represents the data returned from a GetState request.
type GetStateReply struct {
Sequence uint16 // sequence number of the request for this reply
Length uint32 // number of bytes in this reply
State byte
Window xproto.Window
}
// Reply blocks and returns the reply data for a GetState request.
func (cook GetStateCookie) Reply() (*GetStateReply, error) {
buf, err := cook.Cookie.Reply()
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return getStateReply(buf), nil
}
// getStateReply reads a byte slice into a GetStateReply value.
func getStateReply(buf []byte) *GetStateReply {
v := new(GetStateReply)
b := 1 // skip reply determinant
v.State = buf[b]
b += 1
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Length = xgb.Get32(buf[b:]) // 4-byte units
b += 4
v.Window = xproto.Window(xgb.Get32(buf[b:]))
b += 4
return v
}
// Write request to wire for GetState
// getStateRequest writes a GetState request to a byte slice.
func getStateRequest(c *xgb.Conn, Window xproto.Window) []byte {
size := 8
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["XINERAMA"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 1 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Window))
b += 4
return buf
}
// IsActiveCookie is a cookie used only for IsActive requests.
type IsActiveCookie struct {
*xgb.Cookie
}
// IsActive sends a checked request.
// If an error occurs, it will be returned with the reply by calling IsActiveCookie.Reply()
func IsActive(c *xgb.Conn) IsActiveCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'IsActive' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, true)
c.NewRequest(isActiveRequest(c), cookie)
return IsActiveCookie{cookie}
}
// IsActiveUnchecked sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func IsActiveUnchecked(c *xgb.Conn) IsActiveCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'IsActive' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, true)
c.NewRequest(isActiveRequest(c), cookie)
return IsActiveCookie{cookie}
}
// IsActiveReply represents the data returned from a IsActive request.
type IsActiveReply struct {
Sequence uint16 // sequence number of the request for this reply
Length uint32 // number of bytes in this reply
// padding: 1 bytes
State uint32
}
// Reply blocks and returns the reply data for a IsActive request.
func (cook IsActiveCookie) Reply() (*IsActiveReply, error) {
buf, err := cook.Cookie.Reply()
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return isActiveReply(buf), nil
}
// isActiveReply reads a byte slice into a IsActiveReply value.
func isActiveReply(buf []byte) *IsActiveReply {
v := new(IsActiveReply)
b := 1 // skip reply determinant
b += 1 // padding
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Length = xgb.Get32(buf[b:]) // 4-byte units
b += 4
v.State = xgb.Get32(buf[b:])
b += 4
return v
}
// Write request to wire for IsActive
// isActiveRequest writes a IsActive request to a byte slice.
func isActiveRequest(c *xgb.Conn) []byte {
size := 4
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["XINERAMA"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 4 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
return buf
}
// QueryScreensCookie is a cookie used only for QueryScreens requests.
type QueryScreensCookie struct {
*xgb.Cookie
}
// QueryScreens sends a checked request.
// If an error occurs, it will be returned with the reply by calling QueryScreensCookie.Reply()
func QueryScreens(c *xgb.Conn) QueryScreensCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'QueryScreens' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, true)
c.NewRequest(queryScreensRequest(c), cookie)
return QueryScreensCookie{cookie}
}
// QueryScreensUnchecked sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func QueryScreensUnchecked(c *xgb.Conn) QueryScreensCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'QueryScreens' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, true)
c.NewRequest(queryScreensRequest(c), cookie)
return QueryScreensCookie{cookie}
}
// QueryScreensReply represents the data returned from a QueryScreens request.
type QueryScreensReply struct {
Sequence uint16 // sequence number of the request for this reply
Length uint32 // number of bytes in this reply
// padding: 1 bytes
Number uint32
// padding: 20 bytes
ScreenInfo []ScreenInfo // size: xgb.Pad((int(Number) * 8))
}
// Reply blocks and returns the reply data for a QueryScreens request.
func (cook QueryScreensCookie) Reply() (*QueryScreensReply, error) {
buf, err := cook.Cookie.Reply()
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return queryScreensReply(buf), nil
}
// queryScreensReply reads a byte slice into a QueryScreensReply value.
func queryScreensReply(buf []byte) *QueryScreensReply {
v := new(QueryScreensReply)
b := 1 // skip reply determinant
b += 1 // padding
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Length = xgb.Get32(buf[b:]) // 4-byte units
b += 4
v.Number = xgb.Get32(buf[b:])
b += 4
b += 20 // padding
v.ScreenInfo = make([]ScreenInfo, v.Number)
b += ScreenInfoReadList(buf[b:], v.ScreenInfo)
return v
}
// Write request to wire for QueryScreens
// queryScreensRequest writes a QueryScreens request to a byte slice.
func queryScreensRequest(c *xgb.Conn) []byte {
size := 4
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["XINERAMA"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 5 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
return buf
}
// QueryVersionCookie is a cookie used only for QueryVersion requests.
type QueryVersionCookie struct {
*xgb.Cookie
}
// QueryVersion sends a checked request.
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply()
func QueryVersion(c *xgb.Conn, Major byte, Minor byte) QueryVersionCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, true)
c.NewRequest(queryVersionRequest(c, Major, Minor), cookie)
return QueryVersionCookie{cookie}
}
// QueryVersionUnchecked sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func QueryVersionUnchecked(c *xgb.Conn, Major byte, Minor byte) QueryVersionCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["XINERAMA"]; !ok {
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, true)
c.NewRequest(queryVersionRequest(c, Major, Minor), cookie)
return QueryVersionCookie{cookie}
}
// QueryVersionReply represents the data returned from a QueryVersion request.
type QueryVersionReply struct {
Sequence uint16 // sequence number of the request for this reply
Length uint32 // number of bytes in this reply
// padding: 1 bytes
Major uint16
Minor uint16
}
// Reply blocks and returns the reply data for a QueryVersion request.
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
buf, err := cook.Cookie.Reply()
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return queryVersionReply(buf), nil
}
// queryVersionReply reads a byte slice into a QueryVersionReply value.
func queryVersionReply(buf []byte) *QueryVersionReply {
v := new(QueryVersionReply)
b := 1 // skip reply determinant
b += 1 // padding
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Length = xgb.Get32(buf[b:]) // 4-byte units
b += 4
v.Major = xgb.Get16(buf[b:])
b += 2
v.Minor = xgb.Get16(buf[b:])
b += 2
return v
}
// Write request to wire for QueryVersion
// queryVersionRequest writes a QueryVersion request to a byte slice.
func queryVersionRequest(c *xgb.Conn, Major byte, Minor byte) []byte {
size := 8
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["XINERAMA"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 0 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
buf[b] = Major
b += 1
buf[b] = Minor
b += 1
return buf
}

14910
vendor/github.com/BurntSushi/xgb/xproto/xproto.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

6
vendor/github.com/BurntSushi/xgbutil/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,6 @@
*.swp
*.png
tst_first
tst_graphics
TAGS

13
vendor/github.com/BurntSushi/xgbutil/COPYING generated vendored Normal file
View File

@@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

36
vendor/github.com/BurntSushi/xgbutil/Makefile generated vendored Normal file
View File

@@ -0,0 +1,36 @@
all: callback.go types_auto.go gofmt
install:
go install -p 6 . ./ewmh ./gopher ./icccm ./keybind ./motif ./mousebind \
./xcursor ./xevent ./xgraphics ./xinerama ./xprop ./xrect ./xwindow
push:
git push origin master
git push github master
build-ex:
find ./_examples/ -type d -wholename './_examples/[a-z]*' -print0 \
| xargs -0 go build -p 6
gofmt:
gofmt -w *.go */*.go _examples/*/*.go
colcheck *.go */*.go _examples/*/*.go
callback.go:
scripts/write-events callbacks > xevent/callback.go
types_auto.go:
scripts/write-events evtypes > xevent/types_auto.go
tags:
find ./ \( -name '*.go' -and -not -wholename './tests/*' -and -not -wholename './_examples/*' \) -print0 | xargs -0 gotags > TAGS
loc:
find ./ -name '*.go' -and -not -wholename './tests*' -and -not -name '*keysymdef.go' -and -not -name '*gopher.go' -print | sort | xargs wc -l
ex-%:
go run _examples/$*/main.go
gopherimg:
go-bindata -f GopherPng -p gopher -i gopher/gophercolor-small.png -o gopher/gopher.go

55
vendor/github.com/BurntSushi/xgbutil/README generated vendored Normal file
View File

@@ -0,0 +1,55 @@
xgbutil is a utility library designed to work with the X Go Binding. This
project's main goal is to make various X related tasks easier. For example,
binding keys, using the EWMH or ICCCM specs with the window manager,
moving/resizing windows, assigning function callbacks to particular events,
drawing images to a window, etc.
xgbutil attempts to be thread safe, but it has not been completely tested in
this regard. In general, the X event loop implemented in the xevent package is
sequential. The idea is to be sequential by default, and let the user spawn
concurrent code at their discretion. (i.e., the complexity of making the main
event loop generally concurrent is vast.)
You may sleep safely at night by assuming that XGB is thread safe, though.
To start using xgbutil, you should have at least a passing familiarity with X.
Your first stop should be the examples directory.
Installation
============
go get github.com/BurntSushi/xgbutil
Dependencies
============
XGB is the main dependency. Use of the xgraphics packages requires graphics-go
and freetype-go.
XGB project URL: https://github.com/BurntSushi/xgb
Quick Example
=============
go get github.com/BurntSushi/xgbutil/_examples/window-name-sizes
"$GOPATH"/bin/window-name-sizes
The output will be a list of names of all top-level windows and their geometry
including window manager decorations. (Assuming your window manager supports
some basic EWMH properties.)
Documentation
=============
https://godoc.org/github.com/BurntSushi/xgbutil
Examples
========
There are several examples in the examples directory covering common use cases.
They are heavily documented and should run out of the box.
Python
======
An older project of mine, xpybutil, served as inspiration for xgbutil. If you
want to use Python, xpybutil should help quite a bit. Please note though, that
at this point, xgbutil provides a lot more functionality and is much better
documented.
xpybutil project URL: https://github.com/BurntSushi/xpybutil

29
vendor/github.com/BurntSushi/xgbutil/STYLE generated vendored Normal file
View File

@@ -0,0 +1,29 @@
I like to keep all my code to 80 columns or less. I have plenty of screen real
estate, but enjoy 80 columns so that I can have multiple code windows open side
to side and not be plagued by the ugly auto-wrapping of a text editor.
If you don't oblige me, I will fix any patch you submit to abide 80 columns.
Note that this style restriction does not preclude gofmt, but introduces a few
peculiarities. The first is that gofmt will occasionally add spacing (typically
to comments) that ends up going over 80 columns. Either shorten the comment or
put it on its own line.
The second and more common hiccup is when a function definition extends beyond
80 columns. If one adds line breaks to keep it below 80 columns, gofmt will
indent all subsequent lines in a function definition to the same indentation
level of the function body. This results in a less-than-ideal separation
between function definition and function body. To remedy this, simply add a
line break like so:
func RestackWindowExtra(xu *xgbutil.XUtil, win xproto.Window, stackMode int,
sibling xproto.Window, source int) error {
return ClientEvent(xu, win, "_NET_RESTACK_WINDOW", source, int(sibling),
stackMode)
}
Something similar should also be applied to long 'if' or 'for' conditionals,
although it would probably be preferrable to break up the conditional to
smaller chunks with a few helper variables.

67
vendor/github.com/BurntSushi/xgbutil/doc.go generated vendored Normal file
View File

@@ -0,0 +1,67 @@
/*
Package xgbutil is a utility library designed to make common tasks with the X
server easier. The central design choice that has driven development is to hide
the complexity of X wherever possible but expose it when necessary.
For example, the xevent package provides an implementation of an X event loop
that acts as a dispatcher to event handlers set up with the xevent, keybind and
mousebind packages. At the same time, the event queue is exposed and can be
modified using xevent.Peek and xevent.DequeueAt.
Sub-packages
The xgbutil package is considerably small, and only contains some type
definitions and the initial setup for an X connection. Much of the
functionality of xgbutil comes from its sub-packages. Each sub-package is
appropriately documented.
Installation
xgbutil is go-gettable:
go get github.com/BurntSushi/xgbutil
Dependencies
XGB is the main dependency, and is required for all packages inside xgbutil.
graphics-go and freetype-go are also required if using the xgraphics package.
Quick Example
A quick example to demonstrate that xgbutil is working correctly:
go get github.com/BurntSushi/xgbutil/examples/window-name-sizes
GO/PATH/bin/window-name-sizes
The output will be a list of names of all top-level windows and their geometry
including window manager decorations. (Assuming your window manager supports
some basic EWMH properties.)
Examples
The examples directory contains a sizable number of examples demonstrating
common tasks with X. They are intended to demonstrate a single thing each,
although a few that require setup are necessarily long. Each example is
heavily documented.
The examples directory should be your first stop when learning how to use
xgbutil.
xgbutil is also used heavily throughout my window manager, Wingo. It may be
useful reference material.
Wingo project page: https://github.com/BurntSushi/wingo
Thread Safety
While I am fairly confident that XGB is thread safe, I am only somewhat
confident that xgbutil is thread safe. It simply has not been tested enough for
my confidence to be higher.
Note that the xevent package's X event loop is not concurrent. Namely,
designing a generally concurrent X event loop is extremely complex. Instead,
the onus is on you, the user, to design concurrent callback functions if
concurrency is desired.
*/
package xgbutil

1
vendor/github.com/BurntSushi/xgbutil/session.vim generated vendored Normal file
View File

@@ -0,0 +1 @@
au BufWritePost *.go silent!make tags > /dev/null 2>&1

167
vendor/github.com/BurntSushi/xgbutil/types.go generated vendored Normal file
View File

@@ -0,0 +1,167 @@
package xgbutil
/*
types.go contains several types used in the XUtil structure. In an ideal world,
they would be defined in their appropriate packages, but must be defined here
(and exported) for use in some sub-packages. (Namely, xevent, keybind and
mousebind.)
*/
import (
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xproto"
)
// Callback is an interface that should be implemented by event callback
// functions. Namely, to assign a function to a particular event/window
// combination, simply define a function with type 'SomeEventFun' (pre-defined
// in xevent/callback.go), and call the 'Connect' method.
// The 'Run' method is used inside the Main event loop, and shouldn't be used
// by the user.
// Also, it is perfectly legitimate to connect to events that don't specify
// a window (like MappingNotify and KeymapNotify). In this case, simply
// use 'xgbutil.NoWindow' as the window id.
//
// Example to respond to ConfigureNotify events on window 0x1
//
// xevent.ConfigureNotifyFun(
// func(X *xgbutil.XUtil, e xevent.ConfigureNotifyEvent) {
// fmt.Printf("(%d, %d) %dx%d\n", e.X, e.Y, e.Width, e.Height)
// }).Connect(X, 0x1)
type Callback interface {
// Connect modifies XUtil's state to attach an event handler to a
// particular event.
Connect(xu *XUtil, win xproto.Window)
// Run is exported for use in the xevent package but should not be
// used by the user. (It is used to run the callback function in the
// main event loop.)
Run(xu *XUtil, ev interface{})
}
// CallbackHook works similarly to the more general Callback, but it is
// for hooks into the main xevent loop. As such it does not get attached
// to a window.
type CallbackHook interface {
// Connect connects this hook to the main loop of the passed XUtil
// instance.
Connect(xu *XUtil)
// Run is exported for use in the xevent package, but should not be
// used by the user. It should return true if it's ok to process
// the event as usual, or false if it should be suppressed.
Run(xu *XUtil, ev interface{}) bool
}
// CallbackKey works similarly to the more general Callback, but it adds
// parameters specific to key bindings.
type CallbackKey interface {
// Connect modifies XUtil's state to attach an event handler to a
// particular key press. If grab is true, connect will request a passive
// grab.
Connect(xu *XUtil, win xproto.Window, keyStr string, grab bool) error
// Run is exported for use in the keybind package but should not be
// used by the user. (It is used to run the callback function in the
// main event loop.
Run(xu *XUtil, ev interface{})
}
// CallbackMouse works similarly to the more general Callback, but it adds
// parameters specific to mouse bindings.
type CallbackMouse interface {
// Connect modifies XUtil's state to attach an event handler to a
// particular button press.
// If sync is true, the grab will be synchronous. (This will require a
// call to xproto.AllowEvents in response, otherwise no further events
// will be processed and your program will lock.)
// If grab is true, connect will request a passive grab.
Connect(xu *XUtil, win xproto.Window, buttonStr string,
sync bool, grab bool) error
// Run is exported for use in the mousebind package but should not be
// used by the user. (It is used to run the callback function in the
// main event loop.)
Run(xu *XUtil, ev interface{})
}
// KeyKey is the type of the key in the map of keybindings.
// It essentially represents the tuple
// (event type, window id, modifier, keycode).
// It is exported for use in the keybind package. It should not be used.
type KeyKey struct {
Evtype int
Win xproto.Window
Mod uint16
Code xproto.Keycode
}
// KeyString is the type of a key binding string used to connect to particular
// key combinations. A list of all such key strings is maintained in order to
// rebind keys when the keyboard mapping has been changed.
type KeyString struct {
Str string
Callback CallbackKey
Evtype int
Win xproto.Window
Grab bool
}
// MouseKey is the type of the key in the map of mouse bindings.
// It essentially represents the tuple
// (event type, window id, modifier, button).
// It is exported for use in the mousebind package. It should not be used.
type MouseKey struct {
Evtype int
Win xproto.Window
Mod uint16
Button xproto.Button
}
// KeyboardMapping embeds a keyboard mapping reply from XGB.
// It should be retrieved using keybind.KeyMapGet, if necessary.
// xgbutil tries quite hard to absolve you from ever having to use this.
// A keyboard mapping is a table that maps keycodes to one or more keysyms.
type KeyboardMapping struct {
*xproto.GetKeyboardMappingReply
}
// ModifierMapping embeds a modifier mapping reply from XGB.
// It should be retrieved using keybind.ModMapGet, if necessary.
// xgbutil tries quite hard to absolve you from ever having to use this.
// A modifier mapping is a table that maps modifiers to one or more keycodes.
type ModifierMapping struct {
*xproto.GetModifierMappingReply
}
// ErrorHandlerFun is the type of function required to handle errors that
// come in through the main event loop.
// For example, to set a new error handler, use:
//
// xevent.ErrorHandlerSet(xgbutil.ErrorHandlerFun(
// func(err xgb.Error) {
// // do something with err
// }))
type ErrorHandlerFun func(err xgb.Error)
// EventOrError is a struct that contains either an event value or an error
// value. It is an error to contain both. Containing neither indicates an
// error too.
// This is exported for use in the xevent package. You shouldn't have any
// direct contact with values of this type, unless you need to inspect the
// queue directly with xevent.Peek.
type EventOrError struct {
Event xgb.Event
Err xgb.Error
}
// MouseDragFun is the kind of function used on each dragging step
// and at the end of a drag.
type MouseDragFun func(xu *XUtil, rootX, rootY, eventX, eventY int)
// MouseDragBeginFun is the kind of function used to initialize a drag.
// The difference between this and MouseDragFun is that the begin function
// returns a bool (of whether or not to cancel the drag) and an X resource
// identifier corresponding to a cursor.
type MouseDragBeginFun func(xu *XUtil, rootX, rootY,
eventX, eventY int) (bool, xproto.Cursor)

348
vendor/github.com/BurntSushi/xgbutil/xgbutil.go generated vendored Normal file
View File

@@ -0,0 +1,348 @@
package xgbutil
import (
"log"
"os"
"sync"
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xinerama"
"github.com/BurntSushi/xgb/xproto"
)
// Logger is used through xgbutil when messages need to be emitted to stderr.
var Logger = log.New(os.Stderr, "[xgbutil] ", log.Lshortfile)
// The current maximum request size. I think we can expand this with
// BigReq, but it probably isn't worth it at the moment.
const MaxReqSize = (1 << 16) * 4
// An XUtil represents the state of xgbutil. It keeps track of the current
// X connection, the root window, event callbacks, key/mouse bindings, etc.
// Regrettably, many of the members are exported, even though they should not
// be used directly by the user. They are exported for use in sub-packages.
// (Namely, xevent, keybind and mousebind.) In fact, there should *never*
// be a reason to access any members of an XUtil value directly. Any
// interaction with an XUtil value should be through its methods.
type XUtil struct {
// conn is the XGB connection object used to issue protocol requests.
conn *xgb.Conn
// Quit can be set to true, and the main event loop will finish processing
// the current event, and gracefully quit afterwards.
// This is exported for use in the xevent package. Please us xevent.Quit
// to set this value.
Quit bool // when true, the main event loop will stop gracefully
// setup contains all the setup information retrieved at connection time.
setup *xproto.SetupInfo
// screen is a simple alias to the default screen info.
screen *xproto.ScreenInfo
// root is an alias to the default root window.
root xproto.Window
// Atoms is a cache of atom names to resource identifiers. This minimizes
// round trips to the X server, since atom identifiers never change.
// It is exported for use in the xprop package. It should not be used.
Atoms map[string]xproto.Atom
AtomsLck *sync.RWMutex
// AtomNames is a cache just like 'atoms', but in the reverse direction.
// It is exported for use in the xprop package. It should not be used.
AtomNames map[xproto.Atom]string
AtomNamesLck *sync.RWMutex
// Evqueue is the queue that stores the results of xgb.WaitForEvent.
// Namely, each value is either an Event *or* an Error.
// It is exported for use in the xevent package. Do not use it.
// If you need to interact with the event queue, please use the functions
// available in the xevent package: Dequeue, DequeueAt, QueueEmpty
// and QueuePeek.
Evqueue []EventOrError
EvqueueLck *sync.RWMutex
// Callbacks is a map of event numbers to a map of window identifiers
// to callback functions.
// This is the data structure that stores all callback functions, where
// a callback function is always attached to a (event, window) tuple.
// It is exported for use in the xevent package. Do not use it.
Callbacks map[int]map[xproto.Window][]Callback
CallbacksLck *sync.RWMutex
// Hooks are called by the XEvent main loop before processing the event
// itself. These are meant for instances when it's not possible / easy
// to use the normal Hook system. You should not modify this yourself.
Hooks []CallbackHook
HooksLck *sync.RWMutex
// eventTime is the last time recorded by an event. It is automatically
// updated if xgbutil's main event loop is used.
eventTime xproto.Timestamp
// Keymap corresponds to xgbutil's current conception of the keyboard
// mapping. It is automatically kept up-to-date if xgbutil's event loop
// is used.
// It is exported for use in the keybind package. It should not be
// accessed directly. Instead, use keybind.KeyMapGet.
Keymap *KeyboardMapping
// Modmap corresponds to xgbutil's current conception of the modifier key
// mapping. It is automatically kept up-to-date if xgbutil's event loop
// is used.
// It is exported for use in the keybind package. It should not be
// accessed directly. Instead, use keybind.ModMapGet.
Modmap *ModifierMapping
// KeyRedirect corresponds to a window identifier that, when set,
// automatically receives *all* keyboard events. This is a sort-of
// synthetic grab and is helpful in avoiding race conditions.
// It is exported for use in the xevent and keybind packages. Do not use
// it directly. To redirect key events, please use xevent.RedirectKeyEvents.
KeyRedirect xproto.Window
// Keybinds is the data structure storing all callbacks for key bindings.
// This is extremely similar to the general notion of event callbacks,
// but adds extra support to make handling key bindings easier. (Like
// specifying human readable key sequences to bind to.)
// KeyBindKey is a struct representing the 4-tuple
// (event-type, window-id, modifiers, keycode).
// It is exported for use in the keybind package. Do not access it directly.
Keybinds map[KeyKey][]CallbackKey
KeybindsLck *sync.RWMutex
// Keygrabs is a frequency count of the number of callbacks associated
// with a particular KeyBindKey. This is necessary because we can only
// grab a particular key *once*, but we may want to attach several callbacks
// to a single keypress.
// It is exported for use in the keybind package. Do not access it directly.
Keygrabs map[KeyKey]int
// Keystrings is a list of all key strings used to connect keybindings.
// They are used to rebuild key grabs when the keyboard mapping is updated.
// It is exported for use in the keybind package. Do not access it directly.
Keystrings []KeyString
// Mousebinds is the data structure storing all callbacks for mouse
// bindings.This is extremely similar to the general notion of event
// callbacks,but adds extra support to make handling mouse bindings easier.
// (Like specifying human readable mouse sequences to bind to.)
// MouseBindKey is a struct representing the 4-tuple
// (event-type, window-id, modifiers, button).
// It is exported for use in the mousebind package. Do not use it.
Mousebinds map[MouseKey][]CallbackMouse
MousebindsLck *sync.RWMutex
// Mousegrabs is a frequency count of the number of callbacks associated
// with a particular MouseBindKey. This is necessary because we can only
// grab a particular mouse button *once*, but we may want to attach
// several callbacks to a single button press.
// It is exported for use in the mousebind package. Do not use it.
Mousegrabs map[MouseKey]int
// InMouseDrag is true if a drag is currently in progress.
// It is exported for use in the mousebind package. Do not use it.
InMouseDrag bool
// MouseDragStep is the function executed for each step (i.e., pointer
// movement) in the current mouse drag. Note that this is nil when a drag
// is not in progress.
// It is exported for use in the mousebind package. Do not use it.
MouseDragStepFun MouseDragFun
// MouseDragEnd is the function executed at the end of the current
// mouse drag. This is nil when a drag is not in progress.
// It is exported for use in the mousebind package. Do not use it.
MouseDragEndFun MouseDragFun
// gc is a general purpose graphics context; used to paint images.
// Since we don't do any real X drawing, we don't really care about the
// particulars of our graphics context.
gc xproto.Gcontext
// dummy is a dummy window used for mouse/key GRABs.
// Basically, whenever a grab is instituted, mouse and key events are
// redirected to the dummy the window.
dummy xproto.Window
// ErrorHandler is the function that handles errors *in the event loop*.
// By default, it simply emits them to stderr.
// It is exported for use in the xevent package. To set the default error
// handler, please use xevent.ErrorHandlerSet.
ErrorHandler ErrorHandlerFun
}
// NewConn connects to the X server using the DISPLAY environment variable
// and creates a new XUtil. Most environments have the DISPLAY environment
// variable set, so this is probably what you want to use to connect to X.
func NewConn() (*XUtil, error) {
return NewConnDisplay("")
}
// NewConnDisplay connects to the X server and creates a new XUtil.
// If 'display' is empty, the DISPLAY environment variable is used. Otherwise
// there are several different display formats supported:
//
// NewConn(":1") -> net.Dial("unix", "", "/tmp/.X11-unix/X1")
// NewConn("/tmp/launch-12/:0") -> net.Dial("unix", "", "/tmp/launch-12/:0")
// NewConn("hostname:2.1") -> net.Dial("tcp", "", "hostname:6002")
// NewConn("tcp/hostname:1.0") -> net.Dial("tcp", "", "hostname:6001")
func NewConnDisplay(display string) (*XUtil, error) {
c, err := xgb.NewConnDisplay(display)
if err != nil {
return nil, err
}
return NewConnXgb(c)
}
// NewConnXgb use the specific xgb.Conn to create a new XUtil.
//
// NewConn, NewConnDisplay are wrapper of this function.
func NewConnXgb(c *xgb.Conn) (*XUtil, error) {
setup := xproto.Setup(c)
screen := setup.DefaultScreen(c)
// Initialize our central struct that stores everything.
xu := &XUtil{
conn: c,
Quit: false,
Evqueue: make([]EventOrError, 0, 1000),
EvqueueLck: &sync.RWMutex{},
setup: setup,
screen: screen,
root: screen.Root,
eventTime: xproto.Timestamp(0), // last event time
Atoms: make(map[string]xproto.Atom, 50),
AtomsLck: &sync.RWMutex{},
AtomNames: make(map[xproto.Atom]string, 50),
AtomNamesLck: &sync.RWMutex{},
Callbacks: make(map[int]map[xproto.Window][]Callback, 33),
CallbacksLck: &sync.RWMutex{},
Hooks: make([]CallbackHook, 0),
HooksLck: &sync.RWMutex{},
Keymap: nil, // we don't have anything yet
Modmap: nil,
KeyRedirect: 0,
Keybinds: make(map[KeyKey][]CallbackKey, 10),
KeybindsLck: &sync.RWMutex{},
Keygrabs: make(map[KeyKey]int, 10),
Keystrings: make([]KeyString, 0, 10),
Mousebinds: make(map[MouseKey][]CallbackMouse, 10),
MousebindsLck: &sync.RWMutex{},
Mousegrabs: make(map[MouseKey]int, 10),
InMouseDrag: false,
MouseDragStepFun: nil,
MouseDragEndFun: nil,
ErrorHandler: func(err xgb.Error) { Logger.Println(err) },
}
var err error = nil
// Create a general purpose graphics context
xu.gc, err = xproto.NewGcontextId(xu.conn)
if err != nil {
return nil, err
}
xproto.CreateGC(xu.conn, xu.gc, xproto.Drawable(xu.root),
xproto.GcForeground, []uint32{xu.screen.WhitePixel})
// Create a dummy window
xu.dummy, err = xproto.NewWindowId(xu.conn)
if err != nil {
return nil, err
}
xproto.CreateWindow(xu.conn, xu.Screen().RootDepth, xu.dummy, xu.RootWin(),
-1000, -1000, 1, 1, 0,
xproto.WindowClassInputOutput, xu.Screen().RootVisual,
xproto.CwEventMask|xproto.CwOverrideRedirect,
[]uint32{1, xproto.EventMaskPropertyChange})
xproto.MapWindow(xu.conn, xu.dummy)
// Register the Xinerama extension... because it doesn't cost much.
err = xinerama.Init(xu.conn)
// If we can't register Xinerama, that's okay. Output something
// and move on.
if err != nil {
Logger.Printf("WARNING: %s\n", err)
Logger.Printf("MESSAGE: The 'xinerama' package cannot be used " +
"because the XINERAMA extension could not be loaded.")
}
return xu, nil
}
// Conn returns the xgb connection object.
func (xu *XUtil) Conn() *xgb.Conn {
return xu.conn
}
// ExtInitialized returns true if an extension has been initialized.
// This is useful for determining whether an extension is available or not.
func (xu *XUtil) ExtInitialized(extName string) bool {
_, ok := xu.Conn().Extensions[extName]
return ok
}
// Sync forces XGB to catch up with all events/requests and synchronize.
// This is done by issuing a benign round trip request to X.
func (xu *XUtil) Sync() {
xproto.GetInputFocus(xu.Conn()).Reply()
}
// Setup returns the setup information retrieved during connection time.
func (xu *XUtil) Setup() *xproto.SetupInfo {
return xu.setup
}
// Screen returns the default screen
func (xu *XUtil) Screen() *xproto.ScreenInfo {
return xu.screen
}
// RootWin returns the current root window.
func (xu *XUtil) RootWin() xproto.Window {
return xu.root
}
// RootWinSet will change the current root window to the one provided.
// N.B. This probably shouldn't be used unless you're desperately trying
// to support multiple X screens. (This is *not* the same as Xinerama/RandR or
// TwinView. All of those have a single root window.)
func (xu *XUtil) RootWinSet(root xproto.Window) {
xu.root = root
}
// TimeGet gets the most recent time seen by an event.
func (xu *XUtil) TimeGet() xproto.Timestamp {
return xu.eventTime
}
// TimeSet sets the most recent time seen by an event.
func (xu *XUtil) TimeSet(t xproto.Timestamp) {
xu.eventTime = t
}
// GC gets a general purpose graphics context that is typically used to simply
// paint images.
func (xu *XUtil) GC() xproto.Gcontext {
return xu.gc
}
// Dummy gets the id of the dummy window.
func (xu *XUtil) Dummy() xproto.Window {
return xu.dummy
}
// Grabs the server. Everything becomes synchronous.
func (xu *XUtil) Grab() {
xproto.GrabServer(xu.Conn())
}
// Ungrabs the server.
func (xu *XUtil) Ungrab() {
xproto.UngrabServer(xu.Conn())
}

103
vendor/github.com/BurntSushi/xgbutil/xprop/atom.go generated vendored Normal file
View File

@@ -0,0 +1,103 @@
package xprop
/*
xprop/atom.go contains functions related to interning atoms and retrieving
atom names from an atom identifier.
It also manages an atom cache so that once an atom is interned from the X
server, all future atom interns use that value. (So that one and only one
request is sent for interning each atom.)
*/
import (
"fmt"
"github.com/BurntSushi/xgb/xproto"
"github.com/BurntSushi/xgbutil"
)
// Atm is a short alias for Atom in the common case of interning an atom.
// Namely, interning the atom always succeeds. (If the atom does not already
// exist, a new one is created.)
func Atm(xu *xgbutil.XUtil, name string) (xproto.Atom, error) {
aid, err := Atom(xu, name, false)
if err != nil {
return 0, err
}
if aid == 0 {
return 0, fmt.Errorf("Atm: '%s' returned an identifier of 0.", name)
}
return aid, err
}
// Atom interns an atom and panics if there is any error.
func Atom(xu *xgbutil.XUtil, name string,
onlyIfExists bool) (xproto.Atom, error) {
// Check the cache first
if aid, ok := atomGet(xu, name); ok {
return aid, nil
}
reply, err := xproto.InternAtom(xu.Conn(), onlyIfExists,
uint16(len(name)), name).Reply()
if err != nil {
return 0, fmt.Errorf("Atom: Error interning atom '%s': %s", name, err)
}
// If we're here, it means we didn't have this atom cached. So cache it!
cacheAtom(xu, name, reply.Atom)
return reply.Atom, nil
}
// AtomName fetches a string representation of an ATOM given its integer id.
func AtomName(xu *xgbutil.XUtil, aid xproto.Atom) (string, error) {
// Check the cache first
if atomName, ok := atomNameGet(xu, aid); ok {
return string(atomName), nil
}
reply, err := xproto.GetAtomName(xu.Conn(), aid).Reply()
if err != nil {
return "", fmt.Errorf("AtomName: Error fetching name for ATOM "+
"id '%d': %s", aid, err)
}
// If we're here, it means we didn't have ths ATOM id cached. So cache it.
atomName := string(reply.Name)
cacheAtom(xu, atomName, aid)
return atomName, nil
}
// atomGet retrieves an atom identifier from a cache if it exists.
func atomGet(xu *xgbutil.XUtil, name string) (xproto.Atom, bool) {
xu.AtomsLck.RLock()
defer xu.AtomsLck.RUnlock()
aid, ok := xu.Atoms[name]
return aid, ok
}
// atomNameGet retrieves an atom name from a cache if it exists.
func atomNameGet(xu *xgbutil.XUtil, aid xproto.Atom) (string, bool) {
xu.AtomNamesLck.RLock()
defer xu.AtomNamesLck.RUnlock()
name, ok := xu.AtomNames[aid]
return name, ok
}
// cacheAtom puts an atom into the cache.
func cacheAtom(xu *xgbutil.XUtil, name string, aid xproto.Atom) {
xu.AtomsLck.Lock()
xu.AtomNamesLck.Lock()
defer xu.AtomsLck.Unlock()
defer xu.AtomNamesLck.Unlock()
xu.Atoms[name] = aid
xu.AtomNames[aid] = name
}

40
vendor/github.com/BurntSushi/xgbutil/xprop/doc.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
/*
Package xprop provides a cache for interning atoms and helper functions for
dealing with GetProperty and ChangeProperty X requests.
Atoms
Atoms in X are interned, meaning that strings are assigned unique integer
identifiers. This minimizes the amount of data transmitted over an X connection.
Once atoms have been interned, they are never changed while the X server is
running. xgbutil takes advantage of this invariant and will only issue an
intern atom request once and cache the result.
To use the xprop package to intern an atom, use Atom:
atom, err := xprop.Atom(XUtilValue, "THE_ATOM_NAME", false)
if err == nil {
println("The atom number: ", atom.Atom)
}
The 'false' parameter corresponds to the 'only_if_exists' parameter of the
X InternAtom request. When it's false, the atom being interned always returns
a non-zero atom number---even if the string being interned hasn't been interned
before. If 'only_if_exists' is true, the atom being interned will return a 0
atom number if it hasn't already been interned.
The typical case is to set 'only_if_exists' to false. To this end, xprop.Atm is
an alias that always sets this value to false.
The reverse can also be done: getting an atom string if you have an atom
number. This can be done with the xprop.AtomName function.
Properties
The other facility of xprop is to help with the use of GetProperty and
ChangeProperty. Please see the source code of the ewmh package for plenty of
examples.
*/
package xprop

270
vendor/github.com/BurntSushi/xgbutil/xprop/xprop.go generated vendored Normal file
View File

@@ -0,0 +1,270 @@
package xprop
import (
"fmt"
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xproto"
"github.com/BurntSushi/xgbutil"
)
// GetProperty abstracts the messiness of calling xgb.GetProperty.
func GetProperty(xu *xgbutil.XUtil, win xproto.Window, atom string) (
*xproto.GetPropertyReply, error) {
atomId, err := Atm(xu, atom)
if err != nil {
return nil, err
}
reply, err := xproto.GetProperty(xu.Conn(), false, win, atomId,
xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
if err != nil {
return nil, fmt.Errorf("GetProperty: Error retrieving property '%s' "+
"on window %x: %s", atom, win, err)
}
if reply.Format == 0 {
return nil, fmt.Errorf("GetProperty: No such property '%s' on "+
"window %x.", atom, win)
}
return reply, nil
}
// ChangeProperty abstracts the semi-nastiness of xgb.ChangeProperty.
func ChangeProp(xu *xgbutil.XUtil, win xproto.Window, format byte, prop string,
typ string, data []byte) error {
propAtom, err := Atm(xu, prop)
if err != nil {
return err
}
typAtom, err := Atm(xu, typ)
if err != nil {
return err
}
return xproto.ChangePropertyChecked(xu.Conn(), xproto.PropModeReplace, win,
propAtom, typAtom, format,
uint32(len(data)/(int(format)/8)), data).Check()
}
// ChangeProperty32 makes changing 32 bit formatted properties easier
// by constructing the raw X data for you.
func ChangeProp32(xu *xgbutil.XUtil, win xproto.Window, prop string, typ string,
data ...uint) error {
buf := make([]byte, len(data)*4)
for i, datum := range data {
xgb.Put32(buf[(i*4):], uint32(datum))
}
return ChangeProp(xu, win, 32, prop, typ, buf)
}
// WindowToUint is a covenience function for converting []xproto.Window
// to []uint.
func WindowToInt(ids []xproto.Window) []uint {
ids32 := make([]uint, len(ids))
for i, v := range ids {
ids32[i] = uint(v)
}
return ids32
}
// AtomToInt is a covenience function for converting []xproto.Atom
// to []uint.
func AtomToUint(ids []xproto.Atom) []uint {
ids32 := make([]uint, len(ids))
for i, v := range ids {
ids32[i] = uint(v)
}
return ids32
}
// StrToAtoms is a convenience function for converting
// []string to []uint32 atoms.
// NOTE: If an atom name in the list doesn't exist, it will be created.
func StrToAtoms(xu *xgbutil.XUtil, atomNames []string) ([]uint, error) {
var err error
atoms := make([]uint, len(atomNames))
for i, atomName := range atomNames {
a, err := Atom(xu, atomName, false)
if err != nil {
return nil, err
}
atoms[i] = uint(a)
}
return atoms, err
}
// PropValAtom transforms a GetPropertyReply struct into an ATOM name.
// The property reply must be in 32 bit format.
func PropValAtom(xu *xgbutil.XUtil, reply *xproto.GetPropertyReply,
err error) (string, error) {
if err != nil {
return "", err
}
if reply.Format != 32 {
return "", fmt.Errorf("PropValAtom: Expected format 32 but got %d",
reply.Format)
}
return AtomName(xu, xproto.Atom(xgb.Get32(reply.Value)))
}
// PropValAtoms is the same as PropValAtom, except that it returns a slice
// of atom names. Also must be 32 bit format.
// This is a method of an XUtil struct, unlike the other 'PropVal...' functions.
func PropValAtoms(xu *xgbutil.XUtil, reply *xproto.GetPropertyReply,
err error) ([]string, error) {
if err != nil {
return nil, err
}
if reply.Format != 32 {
return nil, fmt.Errorf("PropValAtoms: Expected format 32 but got %d",
reply.Format)
}
ids := make([]string, reply.ValueLen)
vals := reply.Value
for i := 0; len(vals) >= 4; i++ {
ids[i], err = AtomName(xu, xproto.Atom(xgb.Get32(vals)))
if err != nil {
return nil, err
}
vals = vals[4:]
}
return ids, nil
}
// PropValWindow transforms a GetPropertyReply struct into an X resource
// window identifier.
// The property reply must be in 32 bit format.
func PropValWindow(reply *xproto.GetPropertyReply,
err error) (xproto.Window, error) {
if err != nil {
return 0, err
}
if reply.Format != 32 {
return 0, fmt.Errorf("PropValId: Expected format 32 but got %d",
reply.Format)
}
return xproto.Window(xgb.Get32(reply.Value)), nil
}
// PropValWindows is the same as PropValWindow, except that it returns a slice
// of identifiers. Also must be 32 bit format.
func PropValWindows(reply *xproto.GetPropertyReply,
err error) ([]xproto.Window, error) {
if err != nil {
return nil, err
}
if reply.Format != 32 {
return nil, fmt.Errorf("PropValIds: Expected format 32 but got %d",
reply.Format)
}
ids := make([]xproto.Window, reply.ValueLen)
vals := reply.Value
for i := 0; len(vals) >= 4; i++ {
ids[i] = xproto.Window(xgb.Get32(vals))
vals = vals[4:]
}
return ids, nil
}
// PropValNum transforms a GetPropertyReply struct into an unsigned
// integer. Useful when the property value is a single integer.
func PropValNum(reply *xproto.GetPropertyReply, err error) (uint, error) {
if err != nil {
return 0, err
}
if reply.Format != 32 {
return 0, fmt.Errorf("PropValNum: Expected format 32 but got %d",
reply.Format)
}
return uint(xgb.Get32(reply.Value)), nil
}
// PropValNums is the same as PropValNum, except that it returns a slice
// of integers. Also must be 32 bit format.
func PropValNums(reply *xproto.GetPropertyReply, err error) ([]uint, error) {
if err != nil {
return nil, err
}
if reply.Format != 32 {
return nil, fmt.Errorf("PropValIds: Expected format 32 but got %d",
reply.Format)
}
nums := make([]uint, reply.ValueLen)
vals := reply.Value
for i := 0; len(vals) >= 4; i++ {
nums[i] = uint(xgb.Get32(vals))
vals = vals[4:]
}
return nums, nil
}
// PropValNum64 transforms a GetPropertyReply struct into a 64 bit
// integer. Useful when the property value is a single integer.
func PropValNum64(reply *xproto.GetPropertyReply, err error) (int64, error) {
if err != nil {
return 0, err
}
if reply.Format != 32 {
return 0, fmt.Errorf("PropValNum: Expected format 32 but got %d",
reply.Format)
}
return int64(xgb.Get32(reply.Value)), nil
}
// PropValStr transforms a GetPropertyReply struct into a string.
// Useful when the property value is a null terminated string represented
// by integers. Also must be 8 bit format.
func PropValStr(reply *xproto.GetPropertyReply, err error) (string, error) {
if err != nil {
return "", err
}
if reply.Format != 8 {
return "", fmt.Errorf("PropValStr: Expected format 8 but got %d",
reply.Format)
}
return string(reply.Value), nil
}
// PropValStrs is the same as PropValStr, except that it returns a slice
// of strings. The raw byte string is a sequence of null terminated strings,
// which is translated into a slice of strings.
func PropValStrs(reply *xproto.GetPropertyReply, err error) ([]string, error) {
if err != nil {
return nil, err
}
if reply.Format != 8 {
return nil, fmt.Errorf("PropValStrs: Expected format 8 but got %d",
reply.Format)
}
var strs []string
sstart := 0
for i, c := range reply.Value {
if c == 0 {
strs = append(strs, string(reply.Value[sstart:i]))
sstart = i + 1
}
}
if sstart < int(reply.ValueLen) {
strs = append(strs, string(reply.Value[sstart:]))
}
return strs, nil
}

77
vendor/github.com/adrg/xdg/CODE_OF_CONDUCT.md generated vendored Normal file
View File

@@ -0,0 +1,77 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age,
body size, disability, ethnicity, sex characteristics, gender identity and
expression, level of experience, education, socio-economic status, nationality,
personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behaviour that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behaviour by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behaviour and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behaviour.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviour that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behaviour may be
reported by contacting the project team at adrg@epistack.com. All complaints
will be reviewed and investigated and will result in a response that is deemed
necessary and appropriate to the circumstances. The project team is obligated to
maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.4, available at
https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

135
vendor/github.com/adrg/xdg/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,135 @@
# Contributing to this project
Contributions in the form of pull requests, issues or just general feedback,
are always welcome. Please take a moment to review this document in order to
make the contribution process easy and effective for everyone involved.
Following these guidelines helps to communicate that you respect the time of
the developers managing and developing this open source project. In return,
they should reciprocate that respect in addressing your issue or assessing
patches and features.
## Using the issue tracker
The issue tracker is the preferred channel for [bug reports](#bugs),
[features requests](#features) and [submitting pull
requests](#pull-requests), but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests (use
[Stack Overflow](http://stackoverflow.com) or IRC).
* Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others.
<a name="bugs"></a>
## Bug reports
A bug is a _demonstrable problem_ that is caused by the code in the repository.
Good bug reports are extremely helpful - thank you!
Guidelines for bug reports:
1. **Use the GitHub issue search** &mdash; check if the issue has already been
reported.
2. **Check if the issue has been fixed** &mdash; try to reproduce it using the
latest `master` or development branch in the repository.
3. **Isolate the problem** &mdash; create a reduced test case.
A good bug report shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report. What is
your environment? What steps will reproduce the issue? What browser(s) and OS
experience the problem? What would you expect to be the outcome? All these
details will help people to fix any potential bugs.
Example:
> Short and descriptive example bug report title
>
> A summary of the issue and the browser/OS environment in which it occurs. If
> suitable, include the steps required to reproduce the bug.
>
> 1. This is the first step
> 2. This is the second step
> 3. Further steps, etc.
>
> `<url>` - a link to the reduced test case
>
> Any other information you want to share that is relevant to the issue being
> reported. This might include the lines of code that you have identified as
> causing the bug, and potential solutions (and your opinions on their
> merits).
<a name="features"></a>
## Feature requests
Feature requests are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong
case to convince the project's developers of the merits of this feature. Please
provide as much detail and context as possible.
<a name="pull-requests"></a>
## Pull requests
Good pull requests - patches, improvements, new features - are a fantastic
help. They should remain focused in scope and avoid containing unrelated
commits.
**Please ask first** before embarking on any significant pull request (e.g.
implementing features, refactoring code, porting to a different language),
otherwise you risk spending a lot of time working on something that the
project's developers might not want to merge into the project.
Please adhere to the coding conventions used throughout a project (indentation,
accurate comments, etc.) and any other requirements (such as test coverage).
Follow this process if you'd like your work considered for inclusion in the
project:
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
and configure the remotes:
```bash
# Clone your fork of the repo into the current directory
git clone https://github.com/<your-username>/<repo-name>
# Navigate to the newly cloned directory
cd <repo-name>
# Assign the original repo to a remote called "upstream"
git remote add upstream https://github.com/<upstream-owner>/<repo-name>
```
2. If you cloned a while ago, get the latest changes from upstream:
```bash
git checkout <dev-branch>
git pull upstream <dev-branch>
```
3. Create a new topic branch (off the main project development branch) to
contain your feature, change, or fix:
```bash
git checkout -b <topic-branch-name>
```
4. Commit your changes in logical chunks and use descriptive commit messages.
Use [interactive rebase](https://help.github.com/articles/interactive-rebase)
to tidy up your commits before making them public.
5. Locally merge (or rebase) the upstream development branch into your topic branch:
```bash
git pull [--rebase] upstream <dev-branch>
```
6. Push your topic branch up to your fork:
```bash
git push origin <topic-branch-name>
```
7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
with a clear title and description.
**IMPORTANT**: By submitting a patch, you agree to allow the project owner to
license your work under the same license as that used by the project.

21
vendor/github.com/adrg/xdg/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Adrian-George Bostan <adrg@epistack.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

280
vendor/github.com/adrg/xdg/README.md generated vendored Normal file
View File

@@ -0,0 +1,280 @@
<h1 align="center">
<div>
<img src="https://raw.githubusercontent.com/adrg/adrg.github.io/master/assets/projects/xdg/logo.svg" alt="xdg logo"/>
</div>
</h1>
<h4 align="center">Go implementation of the XDG Base Directory Specification and XDG user directories.</h4>
<p align="center">
<a href="https://github.com/adrg/xdg/actions?query=workflow%3ACI">
<img alt="Build status" src="https://github.com/adrg/xdg/workflows/CI/badge.svg">
</a>
<a href="https://app.codecov.io/gh/adrg/xdg">
<img alt="Code coverage" src="https://codecov.io/gh/adrg/xdg/branch/master/graphs/badge.svg?branch=master">
</a>
<a href="https://pkg.go.dev/github.com/adrg/xdg">
<img alt="pkg.go.dev documentation" src="https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white">
</a>
<a href="https://opensource.org/licenses/MIT" rel="nofollow">
<img alt="MIT license" src="https://img.shields.io/github/license/adrg/xdg">
</a>
<br />
<a href="https://goreportcard.com/report/github.com/adrg/xdg">
<img alt="Go report card" src="https://goreportcard.com/badge/github.com/adrg/xdg">
</a>
<a href="https://github.com/avelino/awesome-go#configuration">
<img alt="Awesome Go" src="https://awesome.re/mentioned-badge.svg">
</a>
<a href="https://github.com/adrg/xdg/graphs/contributors">
<img alt="GitHub contributors" src="https://img.shields.io/github/contributors/adrg/xdg" />
</a>
<a href="https://github.com/adrg/xdg/issues">
<img alt="GitHub open issues" src="https://img.shields.io/github/issues-raw/adrg/xdg">
</a>
<a href="https://ko-fi.com/T6T72WATK">
<img alt="Buy me a coffee" src="https://img.shields.io/static/v1.svg?label=%20&message=Buy%20me%20a%20coffee&color=579fbf&logo=buy%20me%20a%20coffee&logoColor=white">
</a>
</p>
Provides an implementation of the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
The specification defines a set of standard paths for storing application files,
including data and configuration files. For portability and flexibility reasons,
applications should use the XDG defined locations instead of hardcoding paths.
The package also includes the locations of well known [user directories](https://wiki.archlinux.org/index.php/XDG_user_directories), as well as
other common directories such as fonts and applications.
The current implementation supports **most flavors of Unix**, **Windows**, **macOS** and **Plan 9**.
On Windows, where XDG environment variables are not usually set, the package uses [Known Folders](https://docs.microsoft.com/en-us/windows/win32/shell/known-folders)
as defaults. Therefore, appropriate locations are used for common [folders](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid) which may have been redirected.
See usage [examples](#usage) below. Full documentation can be found at https://pkg.go.dev/github.com/adrg/xdg.
## Installation
go get github.com/adrg/xdg
## Default locations
The package defines sensible defaults for XDG variables which are empty or not
present in the environment.
- On Unix-like operating systems, XDG environment variables are tipically defined.
Appropriate default locations are used for the environment variables which are not set.
- On Windows, XDG environment variables are usually not set. If that is the case,
the package relies on the appropriate [Known Folders](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid).
Sensible fallback locations are used for the folders which are not set.
### XDG Base Directory
<details open>
<summary><strong>Unix-like operating systems</strong></summary>
<br/>
| <a href="#xdg-base-directory"><img width="400" height="0"></a> | <a href="#xdg-base-directory"><img width="500" height="0"></a><p>Unix</p> | <a href="#xdg-base-directory"><img width="600" height="0"></a><p>macOS</p> | <a href="#xdg-base-directory"><img width="500" height="0"></a><p>Plan 9</p> |
| :------------------------------------------------------------: | :-----------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------: |
| <kbd><b>XDG_DATA_HOME</b></kbd> | <kbd>~/.local/share</kbd> | <kbd>~/Library/Application&nbsp;Support</kbd> | <kbd>$home/lib</kbd> |
| <kbd><b>XDG_DATA_DIRS</b></kbd> | <kbd>/usr/local/share</kbd><br/><kbd>/usr/share</kbd> | <kbd>/Library/Application&nbsp;Support</kbd> | <kbd>/lib</kbd> |
| <kbd><b>XDG_CONFIG_HOME</b></kbd> | <kbd>~/.config</kbd> | <kbd>~/Library/Application&nbsp;Support</kbd> | <kbd>$home/lib</kbd> |
| <kbd><b>XDG_CONFIG_DIRS</b></kbd> | <kbd>/etc/xdg</kbd> | <kbd>~/Library/Preferences</kbd><br/><kbd>/Library/Application&nbsp;Support</kbd><br/><kbd>/Library/Preferences</kbd> | <kbd>/lib</kbd> |
| <kbd><b>XDG_STATE_HOME</b></kbd> | <kbd>~/.local/state</kbd> | <kbd>~/Library/Application&nbsp;Support</kbd> | <kbd>$home/lib/state</kbd> |
| <kbd><b>XDG_CACHE_HOME</b></kbd> | <kbd>~/.cache</kbd> | <kbd>~/Library/Caches</kbd> | <kbd>$home/lib/cache</kbd> |
| <kbd><b>XDG_RUNTIME_DIR</b></kbd> | <kbd>/run/user/UID</kbd> | <kbd>~/Library/Application&nbsp;Support</kbd> | <kbd>/tmp</kbd> |
</details>
<details open>
<summary><strong>Microsoft Windows</strong></summary>
<br/>
| <a href="#xdg-base-directory"><img width="400" height="0"></a> | <a href="#xdg-base-directory"><img width="700" height="0"></a><p>Known&nbsp;Folder(s)</p> | <a href="#xdg-base-directory"><img width="900" height="0"></a><p>Fallback(s)</p> |
| :------------------------------------------------------------: | :---------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------: |
| <kbd><b>XDG_DATA_HOME</b></kbd> | <kbd>LocalAppData</kbd> | <kbd>%LOCALAPPDATA%</kbd> |
| <kbd><b>XDG_DATA_DIRS</b></kbd> | <kbd>RoamingAppData</kbd><br/><kbd>ProgramData</kbd> | <kbd>%APPADATA%</kbd><br/><kbd>%ProgramData%</kbd> |
| <kbd><b>XDG_CONFIG_HOME</b></kbd> | <kbd>LocalAppData</kbd> | <kbd>%LOCALAPPDATA%</kbd> |
| <kbd><b>XDG_CONFIG_DIRS</b></kbd> | <kbd>ProgramData</kbd><br/><kbd>RoamingAppData</kbd> | <kbd>%ProgramData%</kbd><br/><kbd>%APPDATA%</kbd> |
| <kbd><b>XDG_STATE_HOME</b></kbd> | <kbd>LocalAppData</kbd> | <kbd>%LOCALAPPDATA%</kbd> |
| <kbd><b>XDG_CACHE_HOME</b></kbd> | <kbd>LocalAppData\cache</kbd> | <kbd>%LOCALAPPDATA%\cache</kbd> |
| <kbd><b>XDG_RUNTIME_DIR</b></kbd> | <kbd>LocalAppData</kbd> | <kbd>%LOCALAPPDATA%</kbd> |
</details>
### XDG user directories
<details open>
<summary><strong>Unix-like operating systems</strong></summary>
<br/>
| <a href="#xdg-user-directories"><img width="500" height="0"></a> | <a href="#xdg-user-directories"><img width="500" height="0"></a><p>Unix</p> | <a href="#xdg-user-directories"><img width="500" height="0"></a><p>macOS</p> | <a href="#xdg-user-directories"><img width="500" height="0"></a><p>Plan 9</p> |
| :--------------------------------------------------------------: | :-------------------------------------------------------------------------: | :---------------------------------------------------------------------------: | :---------------------------------------------------------------------------: |
| <kbd><b>XDG_DESKTOP_DIR</b></kbd> | <kbd>~/Desktop</kbd> | <kbd>~/Desktop</kbd> | <kbd>$home/desktop</kbd> |
| <kbd><b>XDG_DOWNLOAD_DIR</b></kbd> | <kbd>~/Downloads</kbd> | <kbd>~/Downloads</kbd> | <kbd>$home/downloads</kbd> |
| <kbd><b>XDG_DOCUMENTS_DIR</b></kbd> | <kbd>~/Documents</kbd> | <kbd>~/Documents</kbd> | <kbd>$home/documents</kbd> |
| <kbd><b>XDG_MUSIC_DIR</b></kbd> | <kbd>~/Music</kbd> | <kbd>~/Music</kbd> | <kbd>$home/music</kbd> |
| <kbd><b>XDG_PICTURES_DIR</b></kbd> | <kbd>~/Pictures</kbd> | <kbd>~/Pictures</kbd> | <kbd>$home/pictures</kbd> |
| <kbd><b>XDG_VIDEOS_DIR</b></kbd> | <kbd>~/Videos</kbd> | <kbd>~/Movies</kbd> | <kbd>$home/videos</kbd> |
| <kbd><b>XDG_TEMPLATES_DIR</b></kbd> | <kbd>~/Templates</kbd> | <kbd>~/Templates</kbd> | <kbd>$home/templates</kbd> |
| <kbd><b>XDG_PUBLICSHARE_DIR</b></kbd> | <kbd>~/Public</kbd> | <kbd>~/Public</kbd> | <kbd>$home/public</kbd> |
</details>
<details open>
<summary><strong>Microsoft Windows</strong></summary>
<br/>
| <a href="#xdg-user-directories"><img width="500" height="0"></a> | <a href="#xdg-user-directories"><img width="600" height="0"></a><p>Known&nbsp;Folder(s)</p> | <a href="#xdg-user-directories"><img width="900" height="0"></a><p>Fallback(s)</p> |
| :--------------------------------------------------------------: | :-----------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------: |
| <kbd><b>XDG_DESKTOP_DIR</b></kbd> | <kbd>Desktop</kbd> | <kbd>%USERPROFILE%\Desktop</kbd> |
| <kbd><b>XDG_DOWNLOAD_DIR</b></kbd> | <kbd>Downloads</kbd> | <kbd>%USERPROFILE%\Downloads</kbd> |
| <kbd><b>XDG_DOCUMENTS_DIR</b></kbd> | <kbd>Documents</kbd> | <kbd>%USERPROFILE%\Documents</kbd> |
| <kbd><b>XDG_MUSIC_DIR</b></kbd> | <kbd>Music</kbd> | <kbd>%USERPROFILE%\Music</kbd> |
| <kbd><b>XDG_PICTURES_DIR</b></kbd> | <kbd>Pictures</kbd> | <kbd>%USERPROFILE%\Pictures</kbd> |
| <kbd><b>XDG_VIDEOS_DIR</b></kbd> | <kbd>Videos</kbd> | <kbd>%USERPROFILE%\Videos</kbd> |
| <kbd><b>XDG_TEMPLATES_DIR</b></kbd> | <kbd>Templates</kbd> | <kbd>%APPDATA%\Microsoft\Windows\Templates</kbd> |
| <kbd><b>XDG_PUBLICSHARE_DIR</b></kbd> | <kbd>Public</kbd> | <kbd>%PUBLIC%</kbd> |
</details>
### Other directories
<details open>
<summary><strong>Unix-like operating systems</strong></summary>
<br/>
| <a href="#other-directories"><img width="400" height="0"></a> | <a href="#other-directories"><img width="600" height="0"></a><p>Unix</p> | <a href="#other-directories"><img width="600" height="0"></a><p>macOS</p> | <a href="#other-directories"><img width="400" height="0"></a><p>Plan 9</p> |
| :-----------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------: |
| <kbd><b>Home</b></kbd> | <kbd>$HOME</kbd> | <kbd>$HOME</kbd> | <kbd>$home</kbd> |
| <kbd><b>Applications</b></kbd> | <kbd>$XDG_DATA_HOME/applications</kbd><br/><kbd>~/.local/share/applications</kbd><br/><kbd>/usr/local/share/applications</kbd><br/><kbd>/usr/share/applications</kbd><br/><kbd>$XDG_DATA_DIRS/applications</kbd> | <kbd>/Applications</kbd> | <kbd>$home/bin</kbd><br/><kbd>/bin</kbd> |
| <kbd><b>Fonts</b></kbd> | <kbd>$XDG_DATA_HOME/fonts</kbd><br/><kbd>&#126;/.fonts</kbd><br/><kbd>~/.local/share/fonts</kbd><br/><kbd>/usr/local/share/fonts</kbd><br/><kbd>/usr/share/fonts</kbd><br/><kbd>$XDG_DATA_DIRS/fonts</kbd> | <kbd>~/Library/Fonts</kbd><br/><kbd>/Library/Fonts</kbd><br/><kbd>/System/Library/Fonts</kbd><br/><kbd>/Network/Library/Fonts</kbd> | <kbd>$home/lib/font</kbd><br/><kbd>/lib/font</kbd> |
</details>
<details open>
<summary><strong>Microsoft Windows</strong></summary>
<br/>
| <a href="#other-directories"><img width="400" height="0"></a> | <a href="#other-directories"><img width="300" height="0"></a><p>Known&nbsp;Folder(s)</p> | <a href="#other-directories"><img width="1300" height="0"></a><p>Fallback(s)</p> |
| :-----------------------------------------------------------: | :--------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: |
| <kbd><b>Home</b></kbd> | <kbd>Profile</kbd> | <kbd>%USERPROFILE%</kbd> |
| <kbd><b>Applications</b></kbd> | <kbd>Programs</kbd><br/><kbd>CommonPrograms</kbd> | <kbd>%APPDATA%\Microsoft\Windows\Start&nbsp;Menu\Programs</kbd><br/><kbd>%ProgramData%\Microsoft\Windows\Start&nbsp;Menu\Programs</kbd> |
| <kbd><b>Fonts</b></kbd> | <kbd>Fonts</kbd><br/><kbd>-</kbd> | <kbd>%SystemRoot%\Fonts</kbd><br/><kbd>%LOCALAPPDATA%\Microsoft\Windows\Fonts</kbd> |
</details>
## Usage
#### XDG Base Directory
```go
package main
import (
"log"
"github.com/adrg/xdg"
)
func main() {
// XDG Base Directory paths.
log.Println("Home data directory:", xdg.DataHome)
log.Println("Data directories:", xdg.DataDirs)
log.Println("Home config directory:", xdg.ConfigHome)
log.Println("Config directories:", xdg.ConfigDirs)
log.Println("Home state directory:", xdg.StateHome)
log.Println("Cache directory:", xdg.CacheHome)
log.Println("Runtime directory:", xdg.RuntimeDir)
// Other common directories.
log.Println("Home directory:", xdg.Home)
log.Println("Application directories:", xdg.ApplicationDirs)
log.Println("Font directories:", xdg.FontDirs)
// Obtain a suitable location for application config files.
// ConfigFile takes one parameter which must contain the name of the file,
// but it can also contain a set of parent directories. If the directories
// don't exist, they will be created relative to the base config directory.
configFilePath, err := xdg.ConfigFile("appname/config.yaml")
if err != nil {
log.Fatal(err)
}
log.Println("Save the config file at:", configFilePath)
// For other types of application files use:
// xdg.DataFile()
// xdg.StateFile()
// xdg.CacheFile()
// xdg.RuntimeFile()
// Finding application config files.
// SearchConfigFile takes one parameter which must contain the name of
// the file, but it can also contain a set of parent directories relative
// to the config search paths (xdg.ConfigHome and xdg.ConfigDirs).
configFilePath, err = xdg.SearchConfigFile("appname/config.yaml")
if err != nil {
log.Fatal(err)
}
log.Println("Config file was found at:", configFilePath)
// For other types of application files use:
// xdg.SearchDataFile()
// xdg.SearchStateFile()
// xdg.SearchCacheFile()
// xdg.SearchRuntimeFile()
}
```
#### XDG user directories
```go
package main
import (
"log"
"github.com/adrg/xdg"
)
func main() {
// XDG user directories.
log.Println("Desktop directory:", xdg.UserDirs.Desktop)
log.Println("Download directory:", xdg.UserDirs.Download)
log.Println("Documents directory:", xdg.UserDirs.Documents)
log.Println("Music directory:", xdg.UserDirs.Music)
log.Println("Pictures directory:", xdg.UserDirs.Pictures)
log.Println("Videos directory:", xdg.UserDirs.Videos)
log.Println("Templates directory:", xdg.UserDirs.Templates)
log.Println("Public directory:", xdg.UserDirs.PublicShare)
}
```
## Stargazers over time
[![Stargazers over time](https://starchart.cc/adrg/xdg.svg)](https://starchart.cc/adrg/xdg)
## Contributing
Contributions in the form of pull requests, issues or just general feedback,
are always welcome.
See [CONTRIBUTING.MD](CONTRIBUTING.md).
**Contributors**:
[adrg](https://github.com/adrg),
[wichert](https://github.com/wichert),
[bouncepaw](https://github.com/bouncepaw),
[gabriel-vasile](https://github.com/gabriel-vasile),
[KalleDK](https://github.com/KalleDK),
[nvkv](https://github.com/nvkv),
[djdv](https://github.com/djdv).
## References
For more information see:
* [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
* [XDG user directories](https://wiki.archlinux.org/index.php/XDG_user_directories)
* [Windows Known Folders](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid)
## License
Copyright (c) 2014 Adrian-George Bostan.
This project is licensed under the [MIT license](https://opensource.org/licenses/MIT).
See [LICENSE](LICENSE) for more details.

68
vendor/github.com/adrg/xdg/base_dirs.go generated vendored Normal file
View File

@@ -0,0 +1,68 @@
package xdg
import "github.com/adrg/xdg/internal/pathutil"
// XDG Base Directory environment variables.
const (
envDataHome = "XDG_DATA_HOME"
envDataDirs = "XDG_DATA_DIRS"
envConfigHome = "XDG_CONFIG_HOME"
envConfigDirs = "XDG_CONFIG_DIRS"
envStateHome = "XDG_STATE_HOME"
envCacheHome = "XDG_CACHE_HOME"
envRuntimeDir = "XDG_RUNTIME_DIR"
)
type baseDirectories struct {
dataHome string
data []string
configHome string
config []string
stateHome string
cacheHome string
runtime string
// Non-standard directories.
fonts []string
applications []string
}
func (bd baseDirectories) dataFile(relPath string) (string, error) {
return pathutil.Create(relPath, append([]string{bd.dataHome}, bd.data...))
}
func (bd baseDirectories) configFile(relPath string) (string, error) {
return pathutil.Create(relPath, append([]string{bd.configHome}, bd.config...))
}
func (bd baseDirectories) stateFile(relPath string) (string, error) {
return pathutil.Create(relPath, []string{bd.stateHome})
}
func (bd baseDirectories) cacheFile(relPath string) (string, error) {
return pathutil.Create(relPath, []string{bd.cacheHome})
}
func (bd baseDirectories) runtimeFile(relPath string) (string, error) {
return pathutil.Create(relPath, []string{bd.runtime})
}
func (bd baseDirectories) searchDataFile(relPath string) (string, error) {
return pathutil.Search(relPath, append([]string{bd.dataHome}, bd.data...))
}
func (bd baseDirectories) searchConfigFile(relPath string) (string, error) {
return pathutil.Search(relPath, append([]string{bd.configHome}, bd.config...))
}
func (bd baseDirectories) searchStateFile(relPath string) (string, error) {
return pathutil.Search(relPath, []string{bd.stateHome})
}
func (bd baseDirectories) searchCacheFile(relPath string) (string, error) {
return pathutil.Search(relPath, []string{bd.cacheHome})
}
func (bd baseDirectories) searchRuntimeFile(relPath string) (string, error) {
return pathutil.Search(relPath, []string{bd.runtime})
}

11
vendor/github.com/adrg/xdg/codecov.yml generated vendored Normal file
View File

@@ -0,0 +1,11 @@
coverage:
status:
project:
default:
target: 90%
threshold: 1%
patch:
default:
target: 100%
ignore:
- "paths_plan9.go"

99
vendor/github.com/adrg/xdg/doc.go generated vendored Normal file
View File

@@ -0,0 +1,99 @@
/*
Package xdg provides an implementation of the XDG Base Directory Specification.
The specification defines a set of standard paths for storing application files
including data and configuration files. For portability and flexibility reasons,
applications should use the XDG defined locations instead of hardcoding paths.
The package also includes the locations of well known user directories.
The current implementation supports most flavors of Unix, Windows, Mac OS and Plan 9.
For more information regarding the XDG Base Directory Specification see:
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
For more information regarding the XDG user directories see:
https://wiki.archlinux.org/index.php/XDG_user_directories
For more information regarding the Windows Known Folders see:
https://docs.microsoft.com/en-us/windows/win32/shell/known-folders
Usage
XDG Base Directory
package main
import (
"log"
"github.com/adrg/xdg"
)
func main() {
// XDG Base Directory paths.
log.Println("Home data directory:", xdg.DataHome)
log.Println("Data directories:", xdg.DataDirs)
log.Println("Home config directory:", xdg.ConfigHome)
log.Println("Config directories:", xdg.ConfigDirs)
log.Println("Home state directory:", xdg.StateHome)
log.Println("Cache directory:", xdg.CacheHome)
log.Println("Runtime directory:", xdg.RuntimeDir)
// Other common directories.
log.Println("Home directory:", xdg.Home)
log.Println("Application directories:", xdg.ApplicationDirs)
log.Println("Font directories:", xdg.FontDirs)
// Obtain a suitable location for application config files.
// ConfigFile takes one parameter which must contain the name of the file,
// but it can also contain a set of parent directories. If the directories
// don't exist, they will be created relative to the base config directory.
configFilePath, err := xdg.ConfigFile("appname/config.yaml")
if err != nil {
log.Fatal(err)
}
log.Println("Save the config file at:", configFilePath)
// For other types of application files use:
// xdg.DataFile()
// xdg.StateFile()
// xdg.CacheFile()
// xdg.RuntimeFile()
// Finding application config files.
// SearchConfigFile takes one parameter which must contain the name of
// the file, but it can also contain a set of parent directories relative
// to the config search paths (xdg.ConfigHome and xdg.ConfigDirs).
configFilePath, err = xdg.SearchConfigFile("appname/config.yaml")
if err != nil {
log.Fatal(err)
}
log.Println("Config file was found at:", configFilePath)
// For other types of application files use:
// xdg.SearchDataFile()
// xdg.SearchStateFile()
// xdg.SearchCacheFile()
// xdg.SearchRuntimeFile()
}
XDG user directories
package main
import (
"log"
"github.com/adrg/xdg"
)
func main() {
// XDG user directories.
log.Println("Desktop directory:", xdg.UserDirs.Desktop)
log.Println("Download directory:", xdg.UserDirs.Download)
log.Println("Documents directory:", xdg.UserDirs.Documents)
log.Println("Music directory:", xdg.UserDirs.Music)
log.Println("Pictures directory:", xdg.UserDirs.Pictures)
log.Println("Videos directory:", xdg.UserDirs.Videos)
log.Println("Templates directory:", xdg.UserDirs.Templates)
log.Println("Public directory:", xdg.UserDirs.PublicShare)
}
*/
package xdg

View File

@@ -0,0 +1,78 @@
package pathutil
import (
"fmt"
"os"
"path/filepath"
"strings"
)
// Unique eliminates the duplicate paths from the provided slice and returns
// the result. The items in the output slice are in the order in which they
// occur in the input slice. If a `home` location is provided, the paths are
// expanded using the `ExpandHome` function.
func Unique(paths []string, home string) []string {
var (
uniq []string
registry = map[string]struct{}{}
)
for _, p := range paths {
p = ExpandHome(p, home)
if p != "" && filepath.IsAbs(p) {
if _, ok := registry[p]; ok {
continue
}
registry[p] = struct{}{}
uniq = append(uniq, p)
}
}
return uniq
}
// Create returns a suitable location relative to which the file with the
// specified `name` can be written. The first path from the provided `paths`
// slice which is successfully created (or already exists) is used as a base
// path for the file. The `name` parameter should contain the name of the file
// which is going to be written in the location returned by this function, but
// it can also contain a set of parent directories, which will be created
// relative to the selected parent path.
func Create(name string, paths []string) (string, error) {
var searchedPaths []string
for _, p := range paths {
p = filepath.Join(p, name)
dir := filepath.Dir(p)
if Exists(dir) {
return p, nil
}
if err := os.MkdirAll(dir, os.ModeDir|0700); err == nil {
return p, nil
}
searchedPaths = append(searchedPaths, dir)
}
return "", fmt.Errorf("could not create any of the following paths: %s",
strings.Join(searchedPaths, ", "))
}
// Search searches for the file with the specified `name` in the provided
// slice of `paths`. The `name` parameter must contain the name of the file,
// but it can also contain a set of parent directories.
func Search(name string, paths []string) (string, error) {
var searchedPaths []string
for _, p := range paths {
p = filepath.Join(p, name)
if Exists(p) {
return p, nil
}
searchedPaths = append(searchedPaths, filepath.Dir(p))
}
return "", fmt.Errorf("could not locate `%s` in any of the following paths: %s",
filepath.Base(name), strings.Join(searchedPaths, ", "))
}

View File

@@ -0,0 +1,29 @@
package pathutil
import (
"os"
"path/filepath"
"strings"
)
// Exists returns true if the specified path exists.
func Exists(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}
// ExpandHome substitutes `~` and `$home` at the start of the specified
// `path` using the provided `home` location.
func ExpandHome(path, home string) string {
if path == "" || home == "" {
return path
}
if path[0] == '~' {
return filepath.Join(home, path[1:])
}
if strings.HasPrefix(path, "$home") {
return filepath.Join(home, path[5:])
}
return path
}

View File

@@ -0,0 +1,32 @@
//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || nacl || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd js,wasm nacl linux netbsd openbsd solaris
package pathutil
import (
"os"
"path/filepath"
"strings"
)
// Exists returns true if the specified path exists.
func Exists(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}
// ExpandHome substitutes `~` and `$HOME` at the start of the specified
// `path` using the provided `home` location.
func ExpandHome(path, home string) string {
if path == "" || home == "" {
return path
}
if path[0] == '~' {
return filepath.Join(home, path[1:])
}
if strings.HasPrefix(path, "$HOME") {
return filepath.Join(home, path[5:])
}
return path
}

View File

@@ -0,0 +1,64 @@
package pathutil
import (
"os"
"path/filepath"
"strings"
"golang.org/x/sys/windows"
)
// Exists returns true if the specified path exists.
func Exists(path string) bool {
fi, err := os.Lstat(path)
if fi != nil && fi.Mode()&os.ModeSymlink != 0 {
_, err = filepath.EvalSymlinks(path)
}
return err == nil || os.IsExist(err)
}
// ExpandHome substitutes `%USERPROFILE%` at the start of the specified
// `path` using the provided `home` location.
func ExpandHome(path, home string) string {
if path == "" || home == "" {
return path
}
if strings.HasPrefix(path, `%USERPROFILE%`) {
return filepath.Join(home, path[13:])
}
return path
}
// KnownFolder returns the location of the folder with the specified ID.
// If that fails, the folder location is determined by reading the provided
// environment variables (the first non-empty read value is returned).
// If that fails as well, the first non-empty fallback is returned.
// If all of the above fails, the function returns an empty string.
func KnownFolder(id *windows.KNOWNFOLDERID, envVars []string, fallbacks []string) string {
if id != nil {
flags := []uint32{windows.KF_FLAG_DEFAULT, windows.KF_FLAG_DEFAULT_PATH}
for _, flag := range flags {
p, _ := windows.KnownFolderPath(id, flag|windows.KF_FLAG_DONT_VERIFY)
if p != "" {
return p
}
}
}
for _, envVar := range envVars {
p := os.Getenv(envVar)
if p != "" {
return p
}
}
for _, fallback := range fallbacks {
if fallback != "" {
return fallback
}
}
return ""
}

60
vendor/github.com/adrg/xdg/paths_darwin.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
package xdg
import (
"os"
"path/filepath"
)
func homeDir() string {
if home := os.Getenv("HOME"); home != "" {
return home
}
return "/"
}
func initDirs(home string) {
initBaseDirs(home)
initUserDirs(home)
}
func initBaseDirs(home string) {
homeAppSupport := filepath.Join(home, "Library", "Application Support")
rootAppSupport := "/Library/Application Support"
// Initialize standard directories.
baseDirs.dataHome = xdgPath(envDataHome, homeAppSupport)
baseDirs.data = xdgPaths(envDataDirs, rootAppSupport)
baseDirs.configHome = xdgPath(envConfigHome, homeAppSupport)
baseDirs.config = xdgPaths(envConfigDirs,
filepath.Join(home, "Library", "Preferences"),
rootAppSupport,
"/Library/Preferences",
)
baseDirs.stateHome = xdgPath(envStateHome, homeAppSupport)
baseDirs.cacheHome = xdgPath(envCacheHome, filepath.Join(home, "Library", "Caches"))
baseDirs.runtime = xdgPath(envRuntimeDir, homeAppSupport)
// Initialize non-standard directories.
baseDirs.applications = []string{
"/Applications",
}
baseDirs.fonts = []string{
filepath.Join(home, "Library/Fonts"),
"/Library/Fonts",
"/System/Library/Fonts",
"/Network/Library/Fonts",
}
}
func initUserDirs(home string) {
UserDirs.Desktop = xdgPath(envDesktopDir, filepath.Join(home, "Desktop"))
UserDirs.Download = xdgPath(envDownloadDir, filepath.Join(home, "Downloads"))
UserDirs.Documents = xdgPath(envDocumentsDir, filepath.Join(home, "Documents"))
UserDirs.Music = xdgPath(envMusicDir, filepath.Join(home, "Music"))
UserDirs.Pictures = xdgPath(envPicturesDir, filepath.Join(home, "Pictures"))
UserDirs.Videos = xdgPath(envVideosDir, filepath.Join(home, "Movies"))
UserDirs.Templates = xdgPath(envTemplatesDir, filepath.Join(home, "Templates"))
UserDirs.PublicShare = xdgPath(envPublicShareDir, filepath.Join(home, "Public"))
}

55
vendor/github.com/adrg/xdg/paths_plan9.go generated vendored Normal file
View File

@@ -0,0 +1,55 @@
package xdg
import (
"os"
"path/filepath"
)
func homeDir() string {
if home := os.Getenv("home"); home != "" {
return home
}
return "/"
}
func initDirs(home string) {
initBaseDirs(home)
initUserDirs(home)
}
func initBaseDirs(home string) {
homeLibDir := filepath.Join(home, "lib")
rootLibDir := "/lib"
// Initialize standard directories.
baseDirs.dataHome = xdgPath(envDataHome, homeLibDir)
baseDirs.data = xdgPaths(envDataDirs, rootLibDir)
baseDirs.configHome = xdgPath(envConfigHome, homeLibDir)
baseDirs.config = xdgPaths(envConfigDirs, rootLibDir)
baseDirs.stateHome = xdgPath(envStateHome, filepath.Join(homeLibDir, "state"))
baseDirs.cacheHome = xdgPath(envCacheHome, filepath.Join(homeLibDir, "cache"))
baseDirs.runtime = xdgPath(envRuntimeDir, "/tmp")
// Initialize non-standard directories.
baseDirs.applications = []string{
filepath.Join(home, "bin"),
"/bin",
}
baseDirs.fonts = []string{
filepath.Join(homeLibDir, "font"),
"/lib/font",
}
}
func initUserDirs(home string) {
UserDirs.Desktop = xdgPath(envDesktopDir, filepath.Join(home, "desktop"))
UserDirs.Download = xdgPath(envDownloadDir, filepath.Join(home, "downloads"))
UserDirs.Documents = xdgPath(envDocumentsDir, filepath.Join(home, "documents"))
UserDirs.Music = xdgPath(envMusicDir, filepath.Join(home, "music"))
UserDirs.Pictures = xdgPath(envPicturesDir, filepath.Join(home, "pictures"))
UserDirs.Videos = xdgPath(envVideosDir, filepath.Join(home, "videos"))
UserDirs.Templates = xdgPath(envTemplatesDir, filepath.Join(home, "templates"))
UserDirs.PublicShare = xdgPath(envPublicShareDir, filepath.Join(home, "public"))
}

71
vendor/github.com/adrg/xdg/paths_unix.go generated vendored Normal file
View File

@@ -0,0 +1,71 @@
//go:build aix || dragonfly || freebsd || (js && wasm) || nacl || linux || netbsd || openbsd || solaris
// +build aix dragonfly freebsd js,wasm nacl linux netbsd openbsd solaris
package xdg
import (
"os"
"path/filepath"
"strconv"
"github.com/adrg/xdg/internal/pathutil"
)
func homeDir() string {
if home := os.Getenv("HOME"); home != "" {
return home
}
return "/"
}
func initDirs(home string) {
initBaseDirs(home)
initUserDirs(home)
}
func initBaseDirs(home string) {
// Initialize standard directories.
baseDirs.dataHome = xdgPath(envDataHome, filepath.Join(home, ".local", "share"))
baseDirs.data = xdgPaths(envDataDirs, "/usr/local/share", "/usr/share")
baseDirs.configHome = xdgPath(envConfigHome, filepath.Join(home, ".config"))
baseDirs.config = xdgPaths(envConfigDirs, "/etc/xdg")
baseDirs.stateHome = xdgPath(envStateHome, filepath.Join(home, ".local", "state"))
baseDirs.cacheHome = xdgPath(envCacheHome, filepath.Join(home, ".cache"))
baseDirs.runtime = xdgPath(envRuntimeDir, filepath.Join("/run/user", strconv.Itoa(os.Getuid())))
// Initialize non-standard directories.
appDirs := []string{
filepath.Join(baseDirs.dataHome, "applications"),
filepath.Join(home, ".local/share/applications"),
"/usr/local/share/applications",
"/usr/share/applications",
}
fontDirs := []string{
filepath.Join(baseDirs.dataHome, "fonts"),
filepath.Join(home, ".fonts"),
filepath.Join(home, ".local/share/fonts"),
"/usr/local/share/fonts",
"/usr/share/fonts",
}
for _, dir := range baseDirs.data {
appDirs = append(appDirs, filepath.Join(dir, "applications"))
fontDirs = append(fontDirs, filepath.Join(dir, "fonts"))
}
baseDirs.applications = pathutil.Unique(appDirs, Home)
baseDirs.fonts = pathutil.Unique(fontDirs, Home)
}
func initUserDirs(home string) {
UserDirs.Desktop = xdgPath(envDesktopDir, filepath.Join(home, "Desktop"))
UserDirs.Download = xdgPath(envDownloadDir, filepath.Join(home, "Downloads"))
UserDirs.Documents = xdgPath(envDocumentsDir, filepath.Join(home, "Documents"))
UserDirs.Music = xdgPath(envMusicDir, filepath.Join(home, "Music"))
UserDirs.Pictures = xdgPath(envPicturesDir, filepath.Join(home, "Pictures"))
UserDirs.Videos = xdgPath(envVideosDir, filepath.Join(home, "Videos"))
UserDirs.Templates = xdgPath(envTemplatesDir, filepath.Join(home, "Templates"))
UserDirs.PublicShare = xdgPath(envPublicShareDir, filepath.Join(home, "Public"))
}

168
vendor/github.com/adrg/xdg/paths_windows.go generated vendored Normal file
View File

@@ -0,0 +1,168 @@
package xdg
import (
"path/filepath"
"github.com/adrg/xdg/internal/pathutil"
"golang.org/x/sys/windows"
)
func homeDir() string {
return pathutil.KnownFolder(
windows.FOLDERID_Profile,
[]string{"USERPROFILE"},
nil,
)
}
func initDirs(home string) {
kf := initKnownFolders(home)
initBaseDirs(home, kf)
initUserDirs(home, kf)
}
func initBaseDirs(home string, kf *knownFolders) {
// Initialize standard directories.
baseDirs.dataHome = xdgPath(envDataHome, kf.localAppData)
baseDirs.data = xdgPaths(envDataDirs, kf.roamingAppData, kf.programData)
baseDirs.configHome = xdgPath(envConfigHome, kf.localAppData)
baseDirs.config = xdgPaths(envConfigDirs, kf.programData, kf.roamingAppData)
baseDirs.stateHome = xdgPath(envStateHome, kf.localAppData)
baseDirs.cacheHome = xdgPath(envCacheHome, filepath.Join(kf.localAppData, "cache"))
baseDirs.runtime = xdgPath(envRuntimeDir, kf.localAppData)
// Initialize non-standard directories.
baseDirs.applications = []string{
kf.programs,
kf.commonPrograms,
}
baseDirs.fonts = []string{
kf.fonts,
filepath.Join(kf.localAppData, "Microsoft", "Windows", "Fonts"),
}
}
func initUserDirs(home string, kf *knownFolders) {
UserDirs.Desktop = xdgPath(envDesktopDir, kf.desktop)
UserDirs.Download = xdgPath(envDownloadDir, kf.downloads)
UserDirs.Documents = xdgPath(envDocumentsDir, kf.documents)
UserDirs.Music = xdgPath(envMusicDir, kf.music)
UserDirs.Pictures = xdgPath(envPicturesDir, kf.pictures)
UserDirs.Videos = xdgPath(envVideosDir, kf.videos)
UserDirs.Templates = xdgPath(envTemplatesDir, kf.templates)
UserDirs.PublicShare = xdgPath(envPublicShareDir, kf.public)
}
type knownFolders struct {
systemDrive string
systemRoot string
programData string
userProfile string
userProfiles string
roamingAppData string
localAppData string
desktop string
downloads string
documents string
music string
pictures string
videos string
templates string
public string
fonts string
programs string
commonPrograms string
}
func initKnownFolders(home string) *knownFolders {
kf := &knownFolders{
userProfile: home,
}
kf.systemDrive = filepath.VolumeName(pathutil.KnownFolder(
windows.FOLDERID_Windows,
[]string{"SystemDrive", "SystemRoot", "windir"},
[]string{home, `C:`},
)) + string(filepath.Separator)
kf.systemRoot = pathutil.KnownFolder(
windows.FOLDERID_Windows,
[]string{"SystemRoot", "windir"},
[]string{filepath.Join(kf.systemDrive, "Windows")},
)
kf.programData = pathutil.KnownFolder(
windows.FOLDERID_ProgramData,
[]string{"ProgramData", "ALLUSERSPROFILE"},
[]string{filepath.Join(kf.systemDrive, "ProgramData")},
)
kf.userProfiles = pathutil.KnownFolder(
windows.FOLDERID_UserProfiles,
nil,
[]string{filepath.Join(kf.systemDrive, "Users")},
)
kf.roamingAppData = pathutil.KnownFolder(
windows.FOLDERID_RoamingAppData,
[]string{"APPDATA"},
[]string{filepath.Join(home, "AppData", "Roaming")},
)
kf.localAppData = pathutil.KnownFolder(
windows.FOLDERID_LocalAppData,
[]string{"LOCALAPPDATA"},
[]string{filepath.Join(home, "AppData", "Local")},
)
kf.desktop = pathutil.KnownFolder(
windows.FOLDERID_Desktop,
nil,
[]string{filepath.Join(home, "Desktop")},
)
kf.downloads = pathutil.KnownFolder(
windows.FOLDERID_Downloads,
nil,
[]string{filepath.Join(home, "Downloads")},
)
kf.documents = pathutil.KnownFolder(
windows.FOLDERID_Documents,
nil,
[]string{filepath.Join(home, "Documents")},
)
kf.music = pathutil.KnownFolder(
windows.FOLDERID_Music,
nil,
[]string{filepath.Join(home, "Music")},
)
kf.pictures = pathutil.KnownFolder(
windows.FOLDERID_Pictures,
nil,
[]string{filepath.Join(home, "Pictures")},
)
kf.videos = pathutil.KnownFolder(
windows.FOLDERID_Videos,
nil,
[]string{filepath.Join(home, "Videos")},
)
kf.templates = pathutil.KnownFolder(
windows.FOLDERID_Templates,
nil,
[]string{filepath.Join(kf.roamingAppData, "Microsoft", "Windows", "Templates")},
)
kf.public = pathutil.KnownFolder(
windows.FOLDERID_Public,
[]string{"PUBLIC"},
[]string{filepath.Join(kf.userProfiles, "Public")},
)
kf.fonts = pathutil.KnownFolder(
windows.FOLDERID_Fonts,
nil,
[]string{filepath.Join(kf.systemRoot, "Fonts")},
)
kf.programs = pathutil.KnownFolder(
windows.FOLDERID_Programs,
nil,
[]string{filepath.Join(kf.roamingAppData, "Microsoft", "Windows", "Start Menu", "Programs")},
)
kf.commonPrograms = pathutil.KnownFolder(
windows.FOLDERID_CommonPrograms,
nil,
[]string{filepath.Join(kf.programData, "Microsoft", "Windows", "Start Menu", "Programs")},
)
return kf
}

40
vendor/github.com/adrg/xdg/user_dirs.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
package xdg
// XDG user directories environment variables.
const (
envDesktopDir = "XDG_DESKTOP_DIR"
envDownloadDir = "XDG_DOWNLOAD_DIR"
envDocumentsDir = "XDG_DOCUMENTS_DIR"
envMusicDir = "XDG_MUSIC_DIR"
envPicturesDir = "XDG_PICTURES_DIR"
envVideosDir = "XDG_VIDEOS_DIR"
envTemplatesDir = "XDG_TEMPLATES_DIR"
envPublicShareDir = "XDG_PUBLICSHARE_DIR"
)
// UserDirectories defines the locations of well known user directories.
type UserDirectories struct {
// Desktop defines the location of the user's desktop directory.
Desktop string
// Download defines a suitable location for user downloaded files.
Download string
// Documents defines a suitable location for user document files.
Documents string
// Music defines a suitable location for user audio files.
Music string
// Pictures defines a suitable location for user image files.
Pictures string
// VideosDir defines a suitable location for user video files.
Videos string
// Templates defines a suitable location for user template files.
Templates string
// PublicShare defines a suitable location for user shared files.
PublicShare string
}

218
vendor/github.com/adrg/xdg/xdg.go generated vendored Normal file
View File

@@ -0,0 +1,218 @@
package xdg
import (
"os"
"path/filepath"
"github.com/adrg/xdg/internal/pathutil"
)
var (
// Home contains the path of the user's home directory.
Home string
// DataHome defines the base directory relative to which user-specific
// data files should be stored. This directory is defined by the
// $XDG_DATA_HOME environment variable. If the variable is not set,
// a default equal to $HOME/.local/share should be used.
DataHome string
// DataDirs defines the preference-ordered set of base directories to
// search for data files in addition to the DataHome base directory.
// This set of directories is defined by the $XDG_DATA_DIRS environment
// variable. If the variable is not set, the default directories
// to be used are /usr/local/share and /usr/share, in that order. The
// DataHome directory is considered more important than any of the
// directories defined by DataDirs. Therefore, user data files should be
// written relative to the DataHome directory, if possible.
DataDirs []string
// ConfigHome defines the base directory relative to which user-specific
// configuration files should be written. This directory is defined by
// the $XDG_CONFIG_HOME environment variable. If the variable is not
// not set, a default equal to $HOME/.config should be used.
ConfigHome string
// ConfigDirs defines the preference-ordered set of base directories to
// search for configuration files in addition to the ConfigHome base
// directory. This set of directories is defined by the $XDG_CONFIG_DIRS
// environment variable. If the variable is not set, a default equal
// to /etc/xdg should be used. The ConfigHome directory is considered
// more important than any of the directories defined by ConfigDirs.
// Therefore, user config files should be written relative to the
// ConfigHome directory, if possible.
ConfigDirs []string
// StateHome defines the base directory relative to which user-specific
// state files should be stored. This directory is defined by the
// $XDG_STATE_HOME environment variable. If the variable is not set,
// a default equal to ~/.local/state should be used.
StateHome string
// CacheHome defines the base directory relative to which user-specific
// non-essential (cached) data should be written. This directory is
// defined by the $XDG_CACHE_HOME environment variable. If the variable
// is not set, a default equal to $HOME/.cache should be used.
CacheHome string
// RuntimeDir defines the base directory relative to which user-specific
// non-essential runtime files and other file objects (such as sockets,
// named pipes, etc.) should be stored. This directory is defined by the
// $XDG_RUNTIME_DIR environment variable. If the variable is not set,
// applications should fall back to a replacement directory with similar
// capabilities. Applications should use this directory for communication
// and synchronization purposes and should not place larger files in it,
// since it might reside in runtime memory and cannot necessarily be
// swapped out to disk.
RuntimeDir string
// UserDirs defines the locations of well known user directories.
UserDirs UserDirectories
// FontDirs defines the common locations where font files are stored.
FontDirs []string
// ApplicationDirs defines the common locations of applications.
ApplicationDirs []string
// baseDirs defines the locations of base directories.
baseDirs baseDirectories
)
func init() {
Reload()
}
// Reload refreshes base and user directories by reading the environment.
// Defaults are applied for XDG variables which are empty or not present
// in the environment.
func Reload() {
// Initialize home directory.
Home = homeDir()
// Initialize base and user directories.
initDirs(Home)
// Set standard directories.
DataHome = baseDirs.dataHome
DataDirs = baseDirs.data
ConfigHome = baseDirs.configHome
ConfigDirs = baseDirs.config
StateHome = baseDirs.stateHome
CacheHome = baseDirs.cacheHome
RuntimeDir = baseDirs.runtime
// Set non-standard directories.
FontDirs = baseDirs.fonts
ApplicationDirs = baseDirs.applications
}
// DataFile returns a suitable location for the specified data file.
// The relPath parameter must contain the name of the data file, and
// optionally, a set of parent directories (e.g. appname/app.data).
// If the specified directories do not exist, they will be created relative
// to the base data directory. On failure, an error containing the
// attempted paths is returned.
func DataFile(relPath string) (string, error) {
return baseDirs.dataFile(relPath)
}
// ConfigFile returns a suitable location for the specified config file.
// The relPath parameter must contain the name of the config file, and
// optionally, a set of parent directories (e.g. appname/app.yaml).
// If the specified directories do not exist, they will be created relative
// to the base config directory. On failure, an error containing the
// attempted paths is returned.
func ConfigFile(relPath string) (string, error) {
return baseDirs.configFile(relPath)
}
// StateFile returns a suitable location for the specified state file. State
// files are usually volatile data files, not suitable to be stored relative
// to the $XDG_DATA_HOME directory.
// The relPath parameter must contain the name of the state file, and
// optionally, a set of parent directories (e.g. appname/app.state).
// If the specified directories do not exist, they will be created relative
// to the base state directory. On failure, an error containing the
// attempted paths is returned.
func StateFile(relPath string) (string, error) {
return baseDirs.stateFile(relPath)
}
// CacheFile returns a suitable location for the specified cache file.
// The relPath parameter must contain the name of the cache file, and
// optionally, a set of parent directories (e.g. appname/app.cache).
// If the specified directories do not exist, they will be created relative
// to the base cache directory. On failure, an error containing the
// attempted paths is returned.
func CacheFile(relPath string) (string, error) {
return baseDirs.cacheFile(relPath)
}
// RuntimeFile returns a suitable location for the specified runtime file.
// The relPath parameter must contain the name of the runtime file, and
// optionally, a set of parent directories (e.g. appname/app.pid).
// If the specified directories do not exist, they will be created relative
// to the base runtime directory. On failure, an error containing the
// attempted paths is returned.
func RuntimeFile(relPath string) (string, error) {
return baseDirs.runtimeFile(relPath)
}
// SearchDataFile searches for specified file in the data search paths.
// The relPath parameter must contain the name of the data file, and
// optionally, a set of parent directories (e.g. appname/app.data). If the
// file cannot be found, an error specifying the searched paths is returned.
func SearchDataFile(relPath string) (string, error) {
return baseDirs.searchDataFile(relPath)
}
// SearchConfigFile searches for the specified file in config search paths.
// The relPath parameter must contain the name of the config file, and
// optionally, a set of parent directories (e.g. appname/app.yaml). If the
// file cannot be found, an error specifying the searched paths is returned.
func SearchConfigFile(relPath string) (string, error) {
return baseDirs.searchConfigFile(relPath)
}
// SearchStateFile searches for the specified file in the state search path.
// The relPath parameter must contain the name of the state file, and
// optionally, a set of parent directories (e.g. appname/app.state). If the
// file cannot be found, an error specifying the searched path is returned.
func SearchStateFile(relPath string) (string, error) {
return baseDirs.searchStateFile(relPath)
}
// SearchCacheFile searches for the specified file in the cache search path.
// The relPath parameter must contain the name of the cache file, and
// optionally, a set of parent directories (e.g. appname/app.cache). If the
// file cannot be found, an error specifying the searched path is returned.
func SearchCacheFile(relPath string) (string, error) {
return baseDirs.searchCacheFile(relPath)
}
// SearchRuntimeFile searches for the specified file in the runtime search path.
// The relPath parameter must contain the name of the runtime file, and
// optionally, a set of parent directories (e.g. appname/app.pid). If the
// file cannot be found, an error specifying the searched path is returned.
func SearchRuntimeFile(relPath string) (string, error) {
return baseDirs.searchRuntimeFile(relPath)
}
func xdgPath(name, defaultPath string) string {
dir := pathutil.ExpandHome(os.Getenv(name), Home)
if dir != "" && filepath.IsAbs(dir) {
return dir
}
return defaultPath
}
func xdgPaths(name string, defaultPaths ...string) []string {
dirs := pathutil.Unique(filepath.SplitList(os.Getenv(name)), Home)
if len(dirs) != 0 {
return dirs
}
return pathutil.Unique(defaultPaths, Home)
}

1
vendor/github.com/distatus/battery/.gitignore generated vendored Normal file
View File

@@ -0,0 +1 @@
cmd/battery/battery*

7
vendor/github.com/distatus/battery/BREAKING.md generated vendored Normal file
View File

@@ -0,0 +1,7 @@
# 0.11.0
The `State` field is no longer just an `int`, but a separate `struct`.
It still implements the `Stringer` and `GoStringer` interfaces, so if you were just displaying the value, it should continue working as before.
For comparison purposes, the `State.Raw` field should be used now. It has all the same values as before + 2 new (`Undefined` and `Idle`).

21
vendor/github.com/distatus/battery/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (C) 2016 Karol 'Kenji Takahashi' Woźniak
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

73
vendor/github.com/distatus/battery/README.md generated vendored Normal file
View File

@@ -0,0 +1,73 @@
battery [![Build Status](https://github.com/distatus/battery/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/distatus/battery/actions/workflows/tests.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/distatus/battery)](https://goreportcard.com/report/github.com/distatus/battery) [![GoDoc](https://pkg.go.dev/badge/github.com/distatus/battery)](https://pkg.go.dev/github.com/distatus/battery)
=======
Cross-platform, normalized battery information library.
Gives access to a system independent, typed battery state, capacity, charge and voltage values recalculated as necessary to be returned in mW, mWh or V units.
Currently supported systems:
* Linux 2.6.39+
* OS X 10.10+
* Windows XP+
* FreeBSD
* DragonFlyBSD
* NetBSD
* OpenBSD
* Solaris
Installation
------------
```bash
$ go get -u github.com/distatus/battery
```
Code Example
------------
```go
package main
import (
"fmt"
"github.com/distatus/battery"
)
func main() {
batteries, err := battery.GetAll()
if err != nil {
fmt.Println("Could not get battery info!")
return
}
for i, battery := range batteries {
fmt.Printf("Bat%d: ", i)
fmt.Printf("state: %s, ", battery.State.String())
fmt.Printf("current capacity: %f mWh, ", battery.Current)
fmt.Printf("last full capacity: %f mWh, ", battery.Full)
fmt.Printf("design capacity: %f mWh, ", battery.Design)
fmt.Printf("charge rate: %f mW, ", battery.ChargeRate)
fmt.Printf("voltage: %f V, ", battery.Voltage)
fmt.Printf("design voltage: %f V\n", battery.DesignVoltage)
}
}
```
CLI
---
There is also a little utility which - more or less - mimicks the GNU/Linux `acpi -b` command.
*Installation*
```bash
$ go install github.com/distatus/battery/cmd/battery@latest
```
*Usage*
```bash
$ battery
BAT0: Full, 95.61% [Voltage: 12.15V (design: 12.15V)]
```

174
vendor/github.com/distatus/battery/battery.go generated vendored Normal file
View File

@@ -0,0 +1,174 @@
// battery
// Copyright (C) 2016-2017,2023 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Package battery provides cross-platform, normalized battery information.
//
// Gives access to a system independent, typed battery state, capacity, charge and voltage values recalculated as necessary to be returned in mW, mWh or V units.
//
// Currently supported systems:
//
// Linux 2.6.39+
// OS X 10.10+
// Windows XP+
// FreeBSD
// DragonFlyBSD
// NetBSD
// OpenBSD
// Solaris
package battery
import (
"fmt"
)
// AgnosticState type enumerates possible battery states, using platform agnostic naming.
type AgnosticState int8
const (
// Undefined specifies a state that was returned by the controller, but there is no
// platform agnostic mapping for it.
// This generally shouldn't happen, if it does consider opening a report for the library
// (ideally with the contents of `State.Explain()` call as well).
Undefined AgnosticState = -1
// Unknown specifies a literal unknown state returned by the controller.
// This state is also considered the "default", therefore it will be set when an Error
// was returned from the Get/GetAll call.
Unknown AgnosticState = iota - 1
Empty
Full
Charging
Discharging
// Idle specifies a state where battery is in "capacity saving" mode.
// It usually means that it sits idle at around 80% charge while power source is plugged in.
Idle
)
var states = map[AgnosticState]string{
Undefined: "Undefined",
Unknown: "Unknown",
Empty: "Empty",
Full: "Full",
Charging: "Charging",
Discharging: "Discharging",
Idle: "Idle",
}
func (s AgnosticState) String() string {
return states[s]
}
type State struct {
Raw AgnosticState
specific string
}
func (s State) Explain() string {
return s.specific
}
func (s State) String() string {
return s.Raw.String()
}
func (s State) GoString() string {
return fmt.Sprintf("%s (%s)", s.Raw, s.specific)
}
// Battery type represents a single battery entry information.
type Battery struct {
// Current battery state.
State State
// Current (momentary) capacity (in mWh).
Current float64
// Last known full capacity (in mWh).
Full float64
// Reported design capacity (in mWh).
Design float64
// Current (momentary) charge rate (in mW).
// It is always non-negative, consult .State field to check
// whether it means charging or discharging.
ChargeRate float64
// Current voltage (in V).
Voltage float64
// Design voltage (in V).
// Some systems (e.g. macOS) do not provide a separate
// value for this. In such cases, or if getting this fails,
// but getting `Voltage` succeeds, this field will have
// the same value as `Voltage`, for convenience.
DesignVoltage float64
}
func (b *Battery) String() string {
return fmt.Sprintf("%+v", *b)
}
func get(sg func(idx int) (*Battery, error), idx int) (*Battery, error) {
b, err := sg(idx)
return b, wrapError(err)
}
// Get returns battery information for given index.
//
// Note that index taken here is normalized, such that GetAll()[idx] == Get(idx).
// It does not necessarily represent the "name" or "position" a battery was given
// by the underlying system.
//
// If error != nil, it will be either ErrFatal or ErrPartial.
func Get(idx int) (*Battery, error) {
return get(systemGet, idx)
}
func getAll(sg func() ([]*Battery, error)) ([]*Battery, error) {
bs, err := sg()
if errors, ok := err.(Errors); ok {
nils := 0
partials := 0
for i, err := range errors {
err = wrapError(err)
if err == nil {
nils++
}
if _, ok := err.(ErrPartial); ok {
partials++
}
errors[i] = err
}
if nils == len(errors) {
return bs, nil
}
if nils > 0 || partials > 0 {
return bs, errors
}
return nil, ErrFatal{ErrAllNotNil}
}
if err != nil {
return bs, ErrFatal{err}
}
return bs, nil
}
// GetAll returns information about all batteries in the system.
//
// If error != nil, it will be either ErrFatal or Errors.
// If error is of type Errors, it is guaranteed that length of both returned slices is the same and that i-th error coresponds with i-th battery structure.
func GetAll() ([]*Battery, error) {
return getAll(systemGetAll)
}

117
vendor/github.com/distatus/battery/battery_darwin.go generated vendored Normal file
View File

@@ -0,0 +1,117 @@
// battery
// Copyright (C) 2016-2017,2023 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package battery
import (
"fmt"
"math"
"os/exec"
plist "howett.net/plist"
)
type battery struct {
Voltage int
CurrentCapacity int `plist:"AppleRawCurrentCapacity"`
MaxCapacity int `plist:"AppleRawMaxCapacity"`
DesignCapacity int
Amperage int64
FullyCharged bool
IsCharging bool
ExternalConnected bool
}
func readBatteries() ([]*battery, error) {
out, err := exec.Command("ioreg", "-n", "AppleSmartBattery", "-r", "-a").Output()
if err != nil {
return nil, err
}
if len(out) == 0 {
// No batteries.
return nil, nil
}
var data []*battery
if _, err = plist.Unmarshal(out, &data); err != nil {
return nil, err
}
return data, nil
}
func convertBattery(battery *battery) *Battery {
volts := float64(battery.Voltage) / 1000
b := &Battery{
Current: float64(battery.CurrentCapacity) * volts,
Full: float64(battery.MaxCapacity) * volts,
Design: float64(battery.DesignCapacity) * volts,
ChargeRate: math.Abs(float64(battery.Amperage)) * volts,
Voltage: volts,
DesignVoltage: volts,
}
switch {
case !battery.ExternalConnected:
b.State.Raw = Discharging
b.State.specific = "not(ExternalConnected)"
case battery.IsCharging:
b.State.Raw = Charging
b.State.specific = "IsCharging"
case battery.CurrentCapacity == 0:
b.State.Raw = Empty
b.State.specific = "CurrentCapacity is 0"
case !battery.FullyCharged:
b.State.Raw = Idle
b.State.specific = "not(FullyCharged)"
case battery.FullyCharged:
b.State.Raw = Full
b.State.specific = "FullyCharged"
default:
b.State.Raw = Undefined
b.State.specific = fmt.Sprintf("%+v", *battery)
}
return b
}
func systemGet(idx int) (*Battery, error) {
batteries, err := readBatteries()
if err != nil {
return nil, err
}
if idx >= len(batteries) {
return nil, ErrNotFound
}
return convertBattery(batteries[idx]), nil
}
func systemGetAll() ([]*Battery, error) {
_batteries, err := readBatteries()
if err != nil {
return nil, err
}
batteries := make([]*Battery, len(_batteries))
for i, battery := range _batteries {
batteries[i] = convertBattery(battery)
}
return batteries, nil
}

View File

@@ -0,0 +1,153 @@
// battery
// Copyright (C) 2016-2017,2023 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//go:build freebsd || dragonfly
package battery
import (
"errors"
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
func readUint32(bytes []byte) uint32 {
var ret uint32
for i, b := range bytes {
ret |= uint32(b) << uint(i*8)
}
return ret
}
func uint32ToFloat64(num uint32) (float64, error) {
if num == 0xffffffff {
return 0, errors.New("Unknown value received")
}
return float64(num), nil
}
func ioctl_(fd int, nr int64, retptr *[164]byte) error {
return ioctl(fd, nr, 'B', unsafe.Sizeof(*retptr), unsafe.Pointer(retptr))
}
func systemGet(idx int) (*Battery, error) {
fd, err := unix.Open("/dev/acpi", unix.O_RDONLY, 0777)
if err != nil {
return nil, err
}
defer unix.Close(fd)
b := &Battery{}
e := ErrPartial{}
// No unions in Go, so lets "emulate" union with byte array ;-].
var retptr [164]byte
// XXX What about byte encoding idx and storing it in retptr?
// Then we wouldn't need this unsafe pointer manipulations
unit := (*int)(unsafe.Pointer(&retptr[0]))
*unit = idx
err = ioctl_(fd, 0x10, &retptr) // ACPIIO_BATT_GET_BIF
if err != nil {
return nil, err
}
mw := readUint32(retptr[0:4]) == 0 // acpi_bif.units
b.Design, e.Design = uint32ToFloat64(readUint32(retptr[4:8])) // acpi_bif.dcap
b.Full, e.Full = uint32ToFloat64(readUint32(retptr[8:12])) // acpi_bif.lfcap
b.DesignVoltage, e.DesignVoltage = uint32ToFloat64(readUint32(retptr[16:20])) // acpi_bif.dvol
b.DesignVoltage /= 1000
*unit = idx
err = ioctl_(fd, 0x11, &retptr) // APCIIO_BATT_GET_BST
if err == nil {
state := readUint32(retptr[0:4]) // acpi_bst.state
b.State.specific = fmt.Sprintf("%x", state)
switch state {
case 0x0000:
b.State.Raw = Full
case 0x0001:
b.State.Raw = Discharging
case 0x0002:
b.State.Raw = Charging
case 0x0004:
b.State.Raw = Empty
default:
b.State.Raw = Undefined
}
b.ChargeRate, e.ChargeRate = uint32ToFloat64(readUint32(retptr[4:8])) // acpi_bst.rate
b.Current, e.Current = uint32ToFloat64(readUint32(retptr[8:12])) // acpi_bst.cap
b.Voltage, e.Voltage = uint32ToFloat64(readUint32(retptr[12:16])) // acpi_bst.volt
b.Voltage /= 1000
} else {
e.State = err
e.ChargeRate = err
e.Current = err
e.Voltage = err
}
if e.DesignVoltage != nil && e.Voltage == nil {
b.DesignVoltage, e.DesignVoltage = b.Voltage, nil
}
if !mw {
if e.DesignVoltage == nil {
b.Design *= b.DesignVoltage
} else {
e.Design = e.DesignVoltage
}
if e.Voltage == nil {
b.Full *= b.Voltage
b.ChargeRate *= b.Voltage
b.Current *= b.Voltage
} else {
e.Full = e.Voltage
e.ChargeRate = e.Voltage
e.Current = e.Voltage
}
}
return b, e
}
// There is no way to iterate over available batteries.
// Therefore we assume here that if we were not able to retrieve
// anything, it means we're done.
func systemGetAll() ([]*Battery, error) {
var batteries []*Battery
var errors Errors
for i := 0; ; i++ {
b, err := systemGet(i)
if perr, ok := err.(ErrPartial); ok && perr.noNil() {
break
}
if errno, ok := err.(syscall.Errno); ok && errno == 6 {
break
}
batteries = append(batteries, b)
errors = append(errors, err)
}
return batteries, errors
}

165
vendor/github.com/distatus/battery/battery_linux.go generated vendored Normal file
View File

@@ -0,0 +1,165 @@
// battery
// Copyright (C) 2016-2017,2023 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package battery
import (
"io/ioutil"
"os"
"path/filepath"
"strconv"
)
const sysfs = "/sys/class/power_supply"
func readFloat(path, filename string) (float64, error) {
str, err := ioutil.ReadFile(filepath.Join(path, filename))
if err != nil {
return 0, err
}
num, err := strconv.ParseFloat(string(str[:len(str)-1]), 64)
if err != nil {
return 0, err
}
return num / 1000, nil // Convert micro->milli
}
func readAmp(path, filename string, volts float64) (float64, error) {
val, err := readFloat(path, filename)
if err != nil {
return 0, err
}
return val * volts, nil
}
func isBattery(path string) bool {
t, err := ioutil.ReadFile(filepath.Join(path, "type"))
return err == nil && string(t) == "Battery\n"
}
func getBatteryFiles() ([]string, error) {
files, err := ioutil.ReadDir(sysfs)
if err != nil {
return nil, err
}
var bFiles []string
for _, file := range files {
path := filepath.Join(sysfs, file.Name())
if isBattery(path) {
bFiles = append(bFiles, path)
}
}
return bFiles, nil
}
func getByPath(path string) (*Battery, error) {
b := &Battery{}
e := ErrPartial{}
b.Current, e.Current = readFloat(path, "energy_now")
b.Voltage, e.Voltage = readFloat(path, "voltage_now")
b.Voltage /= 1000
b.DesignVoltage, e.DesignVoltage = readFloat(path, "voltage_max_design")
if e.DesignVoltage != nil {
b.DesignVoltage, e.DesignVoltage = readFloat(path, "voltage_min_design")
}
if e.DesignVoltage != nil && e.Voltage == nil {
b.DesignVoltage, e.DesignVoltage = b.Voltage, nil
}
b.DesignVoltage /= 1000
if os.IsNotExist(e.Current) {
if e.DesignVoltage == nil {
b.Design, e.Design = readAmp(path, "charge_full_design", b.DesignVoltage)
} else {
e.Design = e.DesignVoltage
}
if e.Voltage == nil {
b.Current, e.Current = readAmp(path, "charge_now", b.Voltage)
b.Full, e.Full = readAmp(path, "charge_full", b.Voltage)
b.ChargeRate, e.ChargeRate = readAmp(path, "current_now", b.Voltage)
} else {
e.Current = e.Voltage
e.Full = e.Voltage
e.ChargeRate = e.Voltage
}
} else {
b.Full, e.Full = readFloat(path, "energy_full")
b.Design, e.Design = readFloat(path, "energy_full_design")
b.ChargeRate, e.ChargeRate = readFloat(path, "power_now")
}
status, err := ioutil.ReadFile(filepath.Join(path, "status"))
if err == nil {
status := string(status[:len(status)-1])
b.State.specific = status
switch status {
case "Unknown":
b.State.Raw = Unknown
case "Empty":
b.State.Raw = Empty
case "Full":
b.State.Raw = Full
case "Charging":
b.State.Raw = Charging
case "Discharging":
b.State.Raw = Discharging
case "Not charging":
b.State.Raw = Idle
default:
b.State.Raw = Undefined
}
} else {
e.State = err
}
return b, e
}
func systemGet(idx int) (*Battery, error) {
bFiles, err := getBatteryFiles()
if err != nil {
return nil, err
}
if idx >= len(bFiles) {
return nil, ErrNotFound
}
return getByPath(bFiles[idx])
}
func systemGetAll() ([]*Battery, error) {
bFiles, err := getBatteryFiles()
if err != nil {
return nil, err
}
batteries := make([]*Battery, len(bFiles))
errors := make(Errors, len(bFiles))
for i, bFile := range bFiles {
battery, err := getByPath(bFile)
batteries[i] = battery
errors[i] = err
}
return batteries, errors
}

221
vendor/github.com/distatus/battery/battery_netbsd.go generated vendored Normal file
View File

@@ -0,0 +1,221 @@
// battery
// Copyright (C) 2016-2017,2023 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package battery
import (
"errors"
"fmt"
"math"
"sort"
"strings"
"unsafe"
"golang.org/x/sys/unix"
plist "howett.net/plist"
)
type plistref struct {
pref_plist unsafe.Pointer
pref_len uint64
}
type values struct {
Description string `plist:"description"`
CurValue int `plist:"cur-value"`
MaxValue int `plist:"max-value"`
State string `plist:"state"`
Type string `plist:"type"`
}
type prop []values
type props map[string]prop
func readBytes(ptr unsafe.Pointer, length uint64) []byte {
buf := make([]byte, length-1)
var i uint64
for ; i < length-1; i++ {
buf[i] = *(*byte)(unsafe.Pointer(uintptr(ptr) + uintptr(i)))
}
return buf
}
func readProps() (props, error) {
fd, err := unix.Open("/dev/sysmon", unix.O_RDONLY, 0777)
if err != nil {
return nil, err
}
defer unix.Close(fd)
var retptr plistref
if err = ioctl(fd, 0, 'E', unsafe.Sizeof(retptr), unsafe.Pointer(&retptr)); err != nil {
return nil, err
}
bytes := readBytes(retptr.pref_plist, retptr.pref_len)
var props props
if _, err = plist.Unmarshal(bytes, &props); err != nil {
return nil, err
}
return props, nil
}
func handleValue(val values, div float64, res *float64, amps *[]string) error {
if val.State == "invalid" || val.State == "unknown" {
return errors.New("Unknown value received")
}
*res = float64(val.CurValue) / div
if amps != nil && strings.HasPrefix(val.Type, "Amp") {
*amps = append(*amps, val.Description)
}
return nil
}
func deriveState(cr1, cr2 error, current float64, max int) (AgnosticState, error) {
if cr1 == nil && cr2 != nil {
return Charging, nil
}
if cr1 != nil && cr2 == nil {
return Discharging, nil
}
if cr1 != nil && cr2 != nil && current == float64(max)/1000 {
return Full, nil
}
return Unknown, errors.New("Contradicting values received")
}
func handleVoltage(amps []string, b *Battery, e *ErrPartial) {
if e.DesignVoltage != nil && e.Voltage == nil {
b.DesignVoltage, e.DesignVoltage = b.Voltage, nil
}
for _, val := range amps {
switch val {
case "design cap":
if e.DesignVoltage == nil {
b.Design *= b.DesignVoltage
} else {
e.Design = e.DesignVoltage
}
case "last full cap":
if e.Voltage == nil {
b.Full *= b.Voltage
} else {
e.Full = e.Voltage
}
case "charge":
if e.Voltage == nil {
b.Current *= b.Voltage
} else {
e.Current = e.Voltage
}
case "charge rate", "discharge rate":
if e.Voltage == nil {
b.ChargeRate *= b.Voltage
} else {
e.ChargeRate = e.Voltage
}
}
}
}
func sortFilterProps(props props) []string {
var keys []string
for key := range props {
if key[:7] != "acpibat" {
continue
}
keys = append(keys, key)
}
sort.Strings(keys)
return keys
}
func convertBattery(prop prop) (*Battery, error) {
b := &Battery{}
e := ErrPartial{}
amps := []string{}
var cr1, cr2 error
var maxCharge int
for _, val := range prop {
switch val.Description {
case "voltage":
e.Voltage = handleValue(val, 1000000, &b.Voltage, nil)
case "design voltage":
e.DesignVoltage = handleValue(val, 1000000, &b.DesignVoltage, nil)
case "design cap":
e.Design = handleValue(val, 1000, &b.Design, &amps)
case "last full cap":
e.Full = handleValue(val, 1000, &b.Full, &amps)
case "charge":
e.Current = handleValue(val, 1000, &b.Current, &amps)
maxCharge = val.MaxValue
case "charge rate":
cr1 = handleValue(val, 1000, &b.ChargeRate, &amps)
case "discharge rate":
cr2 = handleValue(val, 1000, &b.ChargeRate, &amps)
b.ChargeRate = math.Abs(b.ChargeRate)
}
}
b.State.Raw, e.State = deriveState(cr1, cr2, b.Current, maxCharge)
b.State.specific = fmt.Sprintf("cr1: %v, cr2: %v")
handleVoltage(amps, b, &e)
return b, e
}
func systemGet(idx int) (*Battery, error) {
props, err := readProps()
if err != nil {
return nil, err
}
keys := sortFilterProps(props)
if idx >= len(keys) {
return nil, ErrNotFound
}
return convertBattery(props[keys[idx]])
}
func systemGetAll() ([]*Battery, error) {
props, err := readProps()
if err != nil {
return nil, err
}
keys := sortFilterProps(props)
batteries := make([]*Battery, len(keys))
errors := make(Errors, len(keys))
for i, key := range keys {
batteries[i], errors[i] = convertBattery(props[key])
}
return batteries, errors
}

282
vendor/github.com/distatus/battery/battery_openbsd.go generated vendored Normal file
View File

@@ -0,0 +1,282 @@
// battery
// Copyright (C) 2016-2017,2019,2023 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package battery
import (
"bytes"
"fmt"
"strings"
"unsafe"
"golang.org/x/sys/unix"
)
func sysctl(mib []int32, out unsafe.Pointer, n uintptr) unix.Errno {
_, _, e := unix.Syscall6(
unix.SYS___SYSCTL,
uintptr(unsafe.Pointer(&mib[0])),
uintptr(len(mib)),
uintptr(out),
uintptr(unsafe.Pointer(&n)),
uintptr(unsafe.Pointer(nil)),
0,
)
return e
}
var errValueNotFound = fmt.Errorf("Value not found")
func setErr(err *error, newErr error) {
if *err == errValueNotFound {
*err = newErr
}
}
var sensorW = [4]int32{
2, // SENSOR_VOLTS_DC (uV)
5, // SENSOR_WATTS (uW)
7, // SENSOR_WATTHOUR (uWh)
10, // SENSOR_INTEGER
}
const (
sensorA = 6 // SENSOR_AMPS (uA)
sensorAH = 8 // SENSOR_AMPHOUR (uAh)
)
type sensorStatus int32
const (
unspecified sensorStatus = iota
ok
warning
critical
unknown
)
type sensor struct {
desc [32]byte
tv [16]byte // struct timeval
value int64
typ [4]byte // enum sensor_type
status sensorStatus
numt int32
flags int32
}
func (s *sensor) readValue(div float64) (float64, error) {
if s.status == unknown {
return 0, fmt.Errorf("Unknown value received")
}
return float64(s.value) / div, nil
}
func (s *sensor) handleA(factor float64, field *float64, fieldErr *error) {
*field, *fieldErr = s.readValue(1000)
if *fieldErr == nil {
*field *= factor
}
}
func (s *sensor) handleAH(factor float64, factorErr error, field *float64, fieldErr *error) {
if factorErr != nil {
*fieldErr = factorErr
return
}
s.handleA(factor, field, fieldErr)
}
type sensordev struct {
num int32
xname [16]byte
maxnumt [23]int32
sensors_count int32
}
func (sd *sensordev) get() (*Battery, error) {
var battery Battery
err := ErrPartial{
Design: errValueNotFound,
Full: errValueNotFound,
Current: errValueNotFound,
ChargeRate: errValueNotFound,
State: errValueNotFound,
Voltage: errValueNotFound,
DesignVoltage: errValueNotFound,
}
mib := []int32{unix.CTL_HW, 11, sd.num, 0, 0}
var s sensor
iter := func(maxnumtIdx int32, cb func(string)) {
mib[3] = maxnumtIdx
for i := int32(0); i < sd.maxnumt[maxnumtIdx]; i++ {
mib[4] = i
if errno := sysctl(mib, unsafe.Pointer(&s), unsafe.Sizeof(s)); errno != 0 {
setErr(&err.Design, errno)
setErr(&err.Full, errno)
setErr(&err.Current, errno)
setErr(&err.ChargeRate, errno)
setErr(&err.Voltage, errno)
setErr(&err.DesignVoltage, errno)
setErr(&err.State, errno)
}
// Convert 0-terminated C-string to a Go string
cb(string(s.desc[:bytes.IndexByte(s.desc[:], 0)]))
}
}
for _, w := range sensorW {
mib[3] = w
iter(w, func(desc string) {
if strings.HasPrefix(desc, "battery ") {
battery.State.specific, err.State = desc, nil
switch desc[8:] {
case "unknown":
battery.State.Raw = Unknown
case "full":
battery.State.Raw = Full
case "charging":
battery.State.Raw = Charging
case "discharging":
battery.State.Raw = Discharging
case "idle":
battery.State.Raw = Idle
case "critical":
battery.State.Raw = Empty
default:
battery.State.Raw = Undefined
}
return
}
switch desc {
case "rate":
battery.ChargeRate, err.ChargeRate = s.readValue(1000)
case "design capacity":
battery.Design, err.Design = s.readValue(1000)
case "last full capacity":
battery.Full, err.Full = s.readValue(1000)
case "remaining capacity":
battery.Current, err.Current = s.readValue(1000)
case "current voltage":
battery.Voltage, err.Voltage = s.readValue(1000_000)
case "voltage":
battery.DesignVoltage, err.DesignVoltage = s.readValue(1000_000)
}
})
}
if err.DesignVoltage != nil && err.Voltage == nil {
battery.DesignVoltage, err.DesignVoltage = battery.Voltage, nil
}
if err.ChargeRate == errValueNotFound {
if err.Voltage == nil {
iter(sensorA, func(desc string) {
if desc == "rate" {
s.handleA(battery.Voltage, &battery.ChargeRate, &err.ChargeRate)
}
})
} else {
err.ChargeRate = err.Voltage
}
}
if err.Design == errValueNotFound || err.Full == errValueNotFound || err.Current == errValueNotFound {
iter(sensorAH, func(desc string) {
switch desc {
case "design capacity":
s.handleAH(battery.DesignVoltage, err.DesignVoltage, &battery.Design, &err.Design)
case "last full capacity":
s.handleAH(battery.Voltage, err.Voltage, &battery.Full, &err.Full)
case "remaining capacity":
s.handleAH(battery.Voltage, err.Voltage, &battery.Current, &err.Current)
}
})
}
return &battery, err
}
func getBatteryAtMIBIndex(i int32) (*Battery, int32, error) {
mib := []int32{
unix.CTL_HW,
11, // HW_SENSORS
0,
}
var sd sensordev
for ; ; i++ {
mib[2] = i
errno := sysctl(mib, unsafe.Pointer(&sd), unsafe.Sizeof(sd))
if errno == unix.ENOENT {
break
}
if errno != 0 {
continue
}
if bytes.HasPrefix(sd.xname[:], []byte("acpibat")) {
battery, err := sd.get()
return battery, i + 1, err
}
}
return nil, i, nil
}
func systemGet(idx int) (*Battery, error) {
var i int32
for idxCurr := 0; ; idxCurr++ {
battery, iNext, err := getBatteryAtMIBIndex(i)
// If this is the index we look for, grab it.
// Otherwise just move on, regardless of errors etc.
if idxCurr == idx {
return battery, err
}
i = iNext
}
return nil, ErrNotFound
}
func systemGetAll() ([]*Battery, error) {
var batteries []*Battery
var errors Errors
var i int32
for {
battery, iNext, err := getBatteryAtMIBIndex(i)
if battery == nil && err == nil { // no more batteries
break
}
batteries = append(batteries, battery)
errors = append(errors, err)
i = iNext
}
return batteries, errors
}

260
vendor/github.com/distatus/battery/battery_solaris.go generated vendored Normal file
View File

@@ -0,0 +1,260 @@
// battery
// Copyright (C) 2016-2017,2023 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package battery
import (
"bufio"
"bytes"
"fmt"
"io"
"math"
"os/exec"
"strconv"
)
var errValueNotFound = fmt.Errorf("Value not found")
func readFloat(val string) (float64, error) {
num, err := strconv.ParseFloat(val, 64)
if err != nil {
return 0, err
}
if num == math.MaxUint32 {
return 0, fmt.Errorf("Unknown value received")
}
return num, nil
}
func readVoltage(val string) (float64, error) {
voltage, err := readFloat(val)
if err != nil {
return 0, err
}
return voltage / 1000, nil
}
func readState(val string) (AgnosticState, error) {
state, err := strconv.Atoi(val)
if err != nil {
return Unknown, err
}
switch {
case state&1 != 0:
return Discharging, nil
case state&2 != 0:
return Charging, nil
case state&4 != 0:
return Empty, nil
default:
return Undefined, nil
}
}
type errParse int
func (p errParse) Error() string {
return fmt.Sprintf("Parse error: `%d`", p)
}
type batteryReader struct {
cmdout *bufio.Scanner
li int
lline []byte
e ErrPartial
}
func (r *batteryReader) setErrParse(n int) {
if r.e.Design == errValueNotFound {
r.e.Design = errParse(n)
}
if r.e.Full == errValueNotFound {
r.e.Full = errParse(n)
}
if r.e.Current == errValueNotFound {
r.e.Current = errParse(n)
}
if r.e.ChargeRate == errValueNotFound {
r.e.ChargeRate = errParse(n)
}
if r.e.State == errValueNotFound {
r.e.State = errParse(n)
}
if r.e.Voltage == errValueNotFound {
r.e.Voltage = errParse(n)
}
if r.e.DesignVoltage == errValueNotFound {
r.e.DesignVoltage = errParse(n)
}
}
func (r *batteryReader) readValue() (string, string, int) {
var piece []byte
if r.lline != nil {
piece = r.lline
r.lline = nil
} else {
pieces := bytes.Split(r.cmdout.Bytes(), []byte{':'})
if len(pieces) < 4 {
return "", "", 4
}
i, err := strconv.Atoi(string(pieces[1]))
if err != nil {
return "", "", 1
}
if i != r.li {
r.li = i
r.lline = pieces[3]
return "", "", 666
}
piece = pieces[3]
}
values := bytes.Split(piece, []byte{'\t'})
if len(values) < 2 {
return "", "", 2
}
return string(values[0]), string(values[1]), 0
}
func (r *batteryReader) readBattery() (*Battery, bool, bool) {
b := &Battery{}
var exists, amps bool
for r.cmdout.Scan() {
exists = true
name, value, errno := r.readValue()
if errno == 666 {
break
}
if errno != 0 {
r.setErrParse(errno)
continue
}
switch name {
case "bif_design_cap":
b.Design, r.e.Design = readFloat(value)
case "bif_last_cap":
b.Full, r.e.Full = readFloat(value)
case "bif_unit":
amps = value != "0"
case "bif_voltage":
b.DesignVoltage, r.e.DesignVoltage = readVoltage(value)
case "bst_voltage":
b.Voltage, r.e.Voltage = readVoltage(value)
case "bst_rem_cap":
b.Current, r.e.Current = readFloat(value)
case "bst_rate":
b.ChargeRate, r.e.ChargeRate = readFloat(value)
case "bst_state":
b.State.Raw, r.e.State = readState(value)
b.State.specific = value
}
}
return b, amps, exists
}
func (r *batteryReader) next() (*Battery, error) {
r.e = ErrPartial{
Design: errValueNotFound,
Full: errValueNotFound,
Current: errValueNotFound,
ChargeRate: errValueNotFound,
State: errValueNotFound,
Voltage: errValueNotFound,
DesignVoltage: errValueNotFound,
}
b, amps, exists := r.readBattery()
if !exists {
return nil, io.EOF
}
if r.e.DesignVoltage != nil && r.e.Voltage == nil {
b.DesignVoltage, r.e.DesignVoltage = b.Voltage, nil
}
if amps {
if r.e.DesignVoltage == nil {
b.Design *= b.DesignVoltage
} else {
r.e.Design = r.e.DesignVoltage
}
if r.e.Voltage == nil {
b.Full *= b.Voltage
b.Current *= b.Voltage
b.ChargeRate *= b.Voltage
} else {
r.e.Full = r.e.Voltage
r.e.Current = r.e.Voltage
r.e.ChargeRate = r.e.Voltage
}
}
if b.State.Raw == Unknown && r.e.Current == nil && r.e.Full == nil && b.Current >= b.Full {
b.State.Raw = Full
b.State.specific = fmt.Sprintf("current >= full")
}
return b, r.e
}
func newBatteryReader() (*batteryReader, error) {
out, err := exec.Command("kstat", "-p", "-m", "acpi_drv", "-n", "battery B*").Output()
if err != nil {
return nil, err
}
return &batteryReader{cmdout: bufio.NewScanner(bytes.NewReader(out))}, nil
}
func systemGet(idx int) (*Battery, error) {
br, err := newBatteryReader()
if err != nil {
return nil, err
}
return br.next()
}
func systemGetAll() ([]*Battery, error) {
br, err := newBatteryReader()
if err != nil {
return nil, err
}
var batteries []*Battery
var errors Errors
for b, e := br.next(); e != io.EOF; b, e = br.next() {
batteries = append(batteries, b)
errors = append(errors, e)
}
return batteries, errors
}

320
vendor/github.com/distatus/battery/battery_windows.go generated vendored Normal file
View File

@@ -0,0 +1,320 @@
// battery
// Copyright (C) 2016-2017,2023 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package battery
import (
"errors"
"fmt"
"math"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
type batteryQueryInformation struct {
BatteryTag uint32
InformationLevel int32
AtRate int32
}
type batteryInformation struct {
Capabilities uint32
Technology uint8
Reserved [3]uint8
Chemistry [4]uint8
DesignedCapacity uint32
FullChargedCapacity uint32
DefaultAlert1 uint32
DefaultAlert2 uint32
CriticalBias uint32
CycleCount uint32
}
type batteryWaitStatus struct {
BatteryTag uint32
Timeout uint32
PowerState uint32
LowCapacity uint32
HighCapacity uint32
}
type batteryStatus struct {
PowerState uint32
Capacity uint32
Voltage uint32
Rate int32
}
type guid struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}
type spDeviceInterfaceData struct {
cbSize uint32
InterfaceClassGuid guid
Flags uint32
Reserved uint
}
var guidDeviceBattery = guid{
0x72631e54,
0x78A4,
0x11d0,
[8]byte{0xbc, 0xf7, 0x00, 0xaa, 0x00, 0xb7, 0xb3, 0x2a},
}
func int32ToFloat64(num int32) (float64, error) {
// There is something wrong with this constant, but
// it appears to work so far...
if num == -0x80000000 { // BATTERY_UNKNOWN_RATE
return 0, errors.New("Unknown value received")
}
return math.Abs(float64(num)), nil
}
func uint32ToFloat64(num uint32) (float64, error) {
if num == 0xffffffff { // BATTERY_UNKNOWN_CAPACITY
return 0, errors.New("Unknown value received")
}
return float64(num), nil
}
func setupDiSetup(proc *windows.LazyProc, nargs, a1, a2, a3, a4, a5, a6 uintptr) (uintptr, error) {
r1, _, errno := syscall.Syscall6(proc.Addr(), nargs, a1, a2, a3, a4, a5, a6)
if windows.Handle(r1) == windows.InvalidHandle {
if errno != 0 {
return 0, error(errno)
}
return 0, syscall.EINVAL
}
return r1, nil
}
func setupDiCall(proc *windows.LazyProc, nargs, a1, a2, a3, a4, a5, a6 uintptr) syscall.Errno {
r1, _, errno := syscall.Syscall6(proc.Addr(), nargs, a1, a2, a3, a4, a5, a6)
if r1 == 0 {
if errno != 0 {
return errno
}
return syscall.EINVAL
}
return 0
}
var setupapi = &windows.LazyDLL{Name: "setupapi.dll", System: true}
var setupDiGetClassDevsW = setupapi.NewProc("SetupDiGetClassDevsW")
var setupDiEnumDeviceInterfaces = setupapi.NewProc("SetupDiEnumDeviceInterfaces")
var setupDiGetDeviceInterfaceDetailW = setupapi.NewProc("SetupDiGetDeviceInterfaceDetailW")
var setupDiDestroyDeviceInfoList = setupapi.NewProc("SetupDiDestroyDeviceInfoList")
func readState(powerState uint32) AgnosticState {
switch {
case powerState&0x00000004 != 0:
return Charging
case powerState&0x00000008 != 0:
return Empty
case powerState&0x00000002 != 0:
return Discharging
case powerState&0x00000001 != 0:
return Full
default:
return Undefined
}
}
func systemGet(idx int) (*Battery, error) {
hdev, err := setupDiSetup(
setupDiGetClassDevsW,
4,
uintptr(unsafe.Pointer(&guidDeviceBattery)),
0,
0,
2|16, // DIGCF_PRESENT|DIGCF_DEVICEINTERFACE
0, 0,
)
if err != nil {
return nil, err
}
defer syscall.Syscall(setupDiDestroyDeviceInfoList.Addr(), 1, hdev, 0, 0)
var did spDeviceInterfaceData
did.cbSize = uint32(unsafe.Sizeof(did))
errno := setupDiCall(
setupDiEnumDeviceInterfaces,
5,
hdev,
0,
uintptr(unsafe.Pointer(&guidDeviceBattery)),
uintptr(idx),
uintptr(unsafe.Pointer(&did)),
0,
)
if errno == 259 { // ERROR_NO_MORE_ITEMS
return nil, ErrNotFound
}
if errno != 0 {
return nil, errno
}
var cbRequired uint32
errno = setupDiCall(
setupDiGetDeviceInterfaceDetailW,
6,
hdev,
uintptr(unsafe.Pointer(&did)),
0,
0,
uintptr(unsafe.Pointer(&cbRequired)),
0,
)
if errno != 0 && errno != 122 { // ERROR_INSUFFICIENT_BUFFER
return nil, errno
}
// The god damn struct with ANYSIZE_ARRAY of utf16 in it is crazy.
// So... let's emulate it with array of uint16 ;-D.
// Keep in mind that the first two elements are actually cbSize.
didd := make([]uint16, cbRequired/2)
cbSize := (*uint32)(unsafe.Pointer(&didd[0]))
if unsafe.Sizeof(uint(0)) == 8 {
*cbSize = 8
} else {
*cbSize = 6
}
errno = setupDiCall(
setupDiGetDeviceInterfaceDetailW,
6,
hdev,
uintptr(unsafe.Pointer(&did)),
uintptr(unsafe.Pointer(&didd[0])),
uintptr(cbRequired),
uintptr(unsafe.Pointer(&cbRequired)),
0,
)
if errno != 0 {
return nil, errno
}
devicePath := &didd[2:][0]
handle, err := windows.CreateFile(
devicePath,
windows.GENERIC_READ|windows.GENERIC_WRITE,
windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL,
0,
)
if err != nil {
return nil, err
}
defer windows.CloseHandle(handle)
var dwOut uint32
var dwWait uint32
var bqi batteryQueryInformation
err = windows.DeviceIoControl(
handle,
2703424, // IOCTL_BATTERY_QUERY_TAG
(*byte)(unsafe.Pointer(&dwWait)),
uint32(unsafe.Sizeof(dwWait)),
(*byte)(unsafe.Pointer(&bqi.BatteryTag)),
uint32(unsafe.Sizeof(bqi.BatteryTag)),
&dwOut,
nil,
)
if err != nil {
return nil, err
}
if bqi.BatteryTag == 0 {
return nil, errors.New("BatteryTag not returned")
}
b := &Battery{}
e := ErrPartial{}
var bi batteryInformation
err = windows.DeviceIoControl(
handle,
2703428, // IOCTL_BATTERY_QUERY_INFORMATION
(*byte)(unsafe.Pointer(&bqi)),
uint32(unsafe.Sizeof(bqi)),
(*byte)(unsafe.Pointer(&bi)),
uint32(unsafe.Sizeof(bi)),
&dwOut,
nil,
)
if err == nil {
b.Full = float64(bi.FullChargedCapacity)
b.Design = float64(bi.DesignedCapacity)
} else {
e.Full = err
e.Design = err
}
bws := batteryWaitStatus{BatteryTag: bqi.BatteryTag}
var bs batteryStatus
err = windows.DeviceIoControl(
handle,
2703436, // IOCTL_BATTERY_QUERY_STATUS
(*byte)(unsafe.Pointer(&bws)),
uint32(unsafe.Sizeof(bws)),
(*byte)(unsafe.Pointer(&bs)),
uint32(unsafe.Sizeof(bs)),
&dwOut,
nil,
)
if err == nil {
b.Current, e.Current = uint32ToFloat64(bs.Capacity)
b.ChargeRate, e.ChargeRate = int32ToFloat64(bs.Rate)
b.Voltage, e.Voltage = uint32ToFloat64(bs.Voltage)
b.Voltage /= 1000
b.State.Raw = readState(bs.PowerState)
b.State.specific = fmt.Sprintf("%x", bs.PowerState)
} else {
e.Current = err
e.ChargeRate = err
e.Voltage = err
e.State = err
}
b.DesignVoltage, e.DesignVoltage = b.Voltage, e.Voltage
return b, e
}
func systemGetAll() ([]*Battery, error) {
var batteries []*Battery
var errors Errors
for i := 0; ; i++ {
b, err := systemGet(i)
if err == ErrNotFound {
break
}
batteries = append(batteries, b)
errors = append(errors, err)
}
return batteries, errors
}

147
vendor/github.com/distatus/battery/errors.go generated vendored Normal file
View File

@@ -0,0 +1,147 @@
// battery
// Copyright (C) 2016-2017 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package battery
import "fmt"
// ErrNotFound variable represents battery not found error.
//
// Only ever returned wrapped in ErrFatal.
var ErrNotFound = fmt.Errorf("Not found")
// ErrAllNotNil variable says that backend returned ErrPartial with
// all fields having not nil values, hence it was converted to ErrFatal.
//
// Only ever returned wrapped in ErrFatal.
var ErrAllNotNil = fmt.Errorf("All fields had not nil errors")
// ErrFatal type represents a fatal error.
//
// It indicates that either the library was not able to perform some kind
// of operation critical to retrieving any data, or all partials have failed at
// once (which would be equivalent to returning a ErrPartial with no nils).
//
// As such, the caller should assume that no meaningful data was
// returned alongside the error and act accordingly.
type ErrFatal struct {
Err error // The actual error that happened.
}
func (f ErrFatal) Error() string {
return fmt.Sprintf("Could not retrieve battery info: `%s`", f.Err)
}
// ErrPartial type represents a partial error.
//
// It indicates that there were problems retrieving some of the data,
// but some was also retrieved successfully.
// If there would be all nils, nil is returned instead.
// If there would be all not nils, ErrFatal is returned instead.
//
// The fields represent fields in the Battery type.
type ErrPartial struct {
State error
Current error
Full error
Design error
ChargeRate error
Voltage error
DesignVoltage error
}
func (p ErrPartial) Error() string {
if p.isNil() {
return "{}"
}
errors := map[string]error{
"State": p.State,
"Current": p.Current,
"Full": p.Full,
"Design": p.Design,
"ChargeRate": p.ChargeRate,
"Voltage": p.Voltage,
"DesignVoltage": p.DesignVoltage,
}
keys := []string{"State", "Current", "Full", "Design", "ChargeRate", "Voltage", "DesignVoltage"}
s := "{"
for _, name := range keys {
err := errors[name]
if err != nil {
s += fmt.Sprintf("%s:%s ", name, err.Error())
}
}
return s[:len(s)-1] + "}"
}
func (p ErrPartial) isNil() bool {
return p.State == nil &&
p.Current == nil &&
p.Full == nil &&
p.Design == nil &&
p.ChargeRate == nil &&
p.Voltage == nil &&
p.DesignVoltage == nil
}
func (p ErrPartial) noNil() bool {
return p.State != nil &&
p.Current != nil &&
p.Full != nil &&
p.Design != nil &&
p.ChargeRate != nil &&
p.Voltage != nil &&
p.DesignVoltage != nil
}
// Errors type represents an array of ErrFatal, ErrPartial or nil values.
//
// Can only possibly be returned by GetAll() call.
type Errors []error
func (e Errors) Error() string {
s := "["
for _, err := range e {
if err != nil {
s += err.Error() + " "
}
}
if len(s) > 1 {
s = s[:len(s)-1]
}
return s + "]"
}
func wrapError(err error) error {
if perr, ok := err.(ErrPartial); ok {
if perr.isNil() {
return nil
}
if perr.noNil() {
return ErrFatal{ErrAllNotNil}
}
return perr
}
if err != nil {
return ErrFatal{err}
}
return nil
}

48
vendor/github.com/distatus/battery/ioctl.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
// battery
// Copyright (C) 2016,2023 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//go:build freebsd || dragonfly || netbsd
package battery
import (
"unsafe"
"golang.org/x/sys/unix"
)
func ioctl(fd int, nr int64, typ byte, size uintptr, retptr unsafe.Pointer) error {
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(fd),
// Some magicks derived from sys/ioccom.h.
uintptr((0x40000000|0x80000000)|
((int64(size)&(1<<13-1))<<16)|
(int64(typ)<<8)|
nr,
),
uintptr(retptr),
)
if errno != 0 {
return errno
}
return nil
}

20
vendor/github.com/go-co-op/gocron/v2/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,20 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
local_testing
coverage.out
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
vendor/
# IDE project files
.idea

49
vendor/github.com/go-co-op/gocron/v2/.golangci.yaml generated vendored Normal file
View File

@@ -0,0 +1,49 @@
run:
timeout: 5m
issues-exit-code: 1
tests: true
skip-dirs:
- local
issues:
max-same-issues: 100
include:
- EXC0012
- EXC0014
linters:
enable:
- bodyclose
- exportloopref
- gofumpt
- goimports
- gosec
- gosimple
- govet
- ineffassign
- misspell
- revive
- staticcheck
- typecheck
- unused
- whitespace
output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
format: colored-line-number
# print lines of code with issue, default is true
print-issued-lines: true
# print linter name in the end of issue text, default is true
print-linter-name: true
# make issues output unique by line, default is true
uniq-by-line: true
# add a prefix to the output file references; default is no prefix
path-prefix: ""
# sorts results by: filepath, line and column
sort-results: true
linters-settings:
golint:
min-confidence: 0.8
fix: true

View File

@@ -0,0 +1,24 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-yaml
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/golangci/golangci-lint
rev: v1.55.2
hooks:
- id: golangci-lint
- repo: https://github.com/TekWizely/pre-commit-golang
rev: v1.0.0-rc.1
hooks:
- id: go-fumpt
args:
- -w
- id: go-mod-tidy

View File

@@ -0,0 +1,73 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone. And we mean everyone!
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and kind language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team initially on Slack to coordinate private communication. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

38
vendor/github.com/go-co-op/gocron/v2/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,38 @@
# Contributing to gocron
Thank you for coming to contribute to gocron! We welcome new ideas, PRs and general feedback.
## Reporting Bugs
If you find a bug then please let the project know by opening an issue after doing the following:
- Do a quick search of the existing issues to make sure the bug isn't already reported
- Try and make a minimal list of steps that can reliably reproduce the bug you are experiencing
- Collect as much information as you can to help identify what the issue is (project version, configuration files, etc)
## Suggesting Enhancements
If you have a use case that you don't see a way to support yet, we would welcome the feedback in an issue. Before opening the issue, please consider:
- Is this a common use case?
- Is it simple to understand?
You can help us out by doing the following before raising a new issue:
- Check that the feature hasn't been requested already by searching existing issues
- Try and reduce your enhancement into a single, concise and deliverable request, rather than a general idea
- Explain your own use cases as the basis of the request
## Adding Features
Pull requests are always welcome. However, before going through the trouble of implementing a change it's worth creating a bug or feature request issue.
This allows us to discuss the changes and make sure they are a good fit for the project.
Please always make sure a pull request has been:
- Unit tested with `make test`
- Linted with `make lint`
## Writing Tests
Tests should follow the [table driven test pattern](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go). See other tests in the code base for additional examples.

21
vendor/github.com/go-co-op/gocron/v2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2014, 辣椒面
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

19
vendor/github.com/go-co-op/gocron/v2/Makefile generated vendored Normal file
View File

@@ -0,0 +1,19 @@
.PHONY: fmt lint test mocks test_coverage
GO_PKGS := $(shell go list -f {{.Dir}} ./...)
fmt:
@go list -f {{.Dir}} ./... | xargs -I{} gofmt -w -s {}
lint:
@grep "^func " example_test.go | sort -c
@golangci-lint run
test:
@go test -race -v $(GO_FLAGS) -count=1 $(GO_PKGS)
test_coverage:
@go test -race -v $(GO_FLAGS) -count=1 -coverprofile=coverage.out -covermode=atomic $(GO_PKGS)
mocks:
@go generate ./...

156
vendor/github.com/go-co-op/gocron/v2/README.md generated vendored Normal file
View File

@@ -0,0 +1,156 @@
# gocron: A Golang Job Scheduling Package
[![CI State](https://github.com/go-co-op/gocron/actions/workflows/go_test.yml/badge.svg?branch=v2&event=push)](https://github.com/go-co-op/gocron/actions)
![Go Report Card](https://goreportcard.com/badge/github.com/go-co-op/gocron) [![Go Doc](https://godoc.org/github.com/go-co-op/gocron/v2?status.svg)](https://pkg.go.dev/github.com/go-co-op/gocron/v2)
gocron is a job scheduling package which lets you run Go functions at pre-determined intervals.
If you want to chat, you can find us on Slack at
[<img src="https://img.shields.io/badge/gophers-gocron-brightgreen?logo=slack">](https://gophers.slack.com/archives/CQ7T0T1FW)
## Quick Start
```
go get github.com/go-co-op/gocron/v2
```
```golang
package main
import (
"fmt"
"time"
"github.com/go-co-op/gocron/v2"
)
func main() {
// create a scheduler
s, err := gocron.NewScheduler()
if err != nil {
// handle error
}
// add a job to the scheduler
j, err := s.NewJob(
gocron.DurationJob(
10*time.Second,
),
gocron.NewTask(
func(a string, b int) {
// do things
},
"hello",
1,
),
)
if err != nil {
// handle error
}
// each job has a unique id
fmt.Println(j.ID())
// start the scheduler
s.Start()
// when you're done, shut it down
err = s.Shutdown()
if err != nil {
// handle error
}
}
```
## Concepts
- **Job**: The job encapsulates a "task", which is made up of a go function and any function parameters. The Job then
provides the scheduler with the time the job should next be scheduled to run.
- **Scheduler**: The scheduler keeps track of all the jobs and sends each job to the executor when
it is ready to be run.
- **Executor**: The executor calls the job's task and manages the complexities of different job
execution timing requirements (e.g. singletons that shouldn't overrun each other, limiting the max number of jobs running)
## Features
### Job types
Jobs can be run at various intervals.
- [**Duration**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#DurationJob):
Jobs can be run at a fixed `time.Duration`.
- [**Random duration**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#DurationRandomJob):
Jobs can be run at a random `time.Duration` between a min and max.
- [**Cron**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#CronJob):
Jobs can be run using a crontab.
- [**Daily**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#DailyJob):
Jobs can be run every x days at specific times.
- [**Weekly**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WeeklyJob):
Jobs can be run every x weeks on specific days of the week and at specific times.
- [**Monthly**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#MonthlyJob):
Jobs can be run every x months on specific days of the month and at specific times.
- [**One time**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#OneTimeJob):
Jobs can be run once at a specific time. These are non-recurring jobs.
### Concurrency Limits
Jobs can be limited individually or across the entire scheduler.
- [**Per job limiting with singleton mode**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithSingletonMode):
Jobs can be limited to a single concurrent execution that either reschedules (skips overlapping executions)
or queues (waits for the previous execution to finish).
- [**Per scheduler limiting with limit mode**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithLimitConcurrentJobs):
Jobs can be limited to a certain number of concurrent executions across the entire scheduler
using either reschedule (skip when the limit is met) or queue (jobs are added to a queue to
wait for the limit to be available).
- **Note:** A scheduler limit and a job limit can both be enabled.
### Distributed instances of gocron
Multiple instances of gocron can be run.
- [**Elector**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithDistributedElector):
An elector can be used to elect a single instance of gocron to run as the primary with the
other instances checking to see if a new leader needs to be elected.
- Implementations: [go-co-op electors](https://github.com/go-co-op?q=-elector&type=all&language=&sort=)
- [**Locker**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithDistributedLocker):
A locker can be used to lock each run of a job to a single instance of gocron.
- Implementations: [go-co-op lockers](https://github.com/go-co-op?q=-lock&type=all&language=&sort=)
### Events
Job events can trigger actions.
- [**Listeners**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithEventListeners):
Can be added to a job, with [event listeners](https://pkg.go.dev/github.com/go-co-op/gocron/v2#EventListener),
or all jobs across the
[scheduler](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithGlobalJobOptions)
to listen for job events and trigger actions.
### Options
Many job and scheduler options are available.
- [**Job options**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#JobOption):
Job options can be set when creating a job using `NewJob`.
- [**Global job options**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithGlobalJobOptions):
Global job options can be set when creating a scheduler using `NewScheduler`
and the `WithGlobalJobOptions` option.
- [**Scheduler options**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#SchedulerOption):
Scheduler options can be set when creating a scheduler using `NewScheduler`.
### Logging
Logs can be enabled.
- [Logger](https://pkg.go.dev/github.com/go-co-op/gocron/v2#Logger):
The Logger interface can be implemented with your desired logging library.
The provided NewLogger uses the standard library's log package.
### Testing
The gocron library is set up to enable testing.
- Mocks are provided in [the mock package](mocks) using [gomock](https://github.com/uber-go/mock).
- Time can be mocked by passing in a [FakeClock](https://pkg.go.dev/github.com/jonboulle/clockwork#FakeClock)
to [WithClock](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithClock) -
see the [example on WithClock](https://pkg.go.dev/github.com/go-co-op/gocron/v2#example-WithClock).
## Supporters
We appreciate the support for free and open source software!
This project is supported by:
- [Jetbrains](https://www.jetbrains.com/?from=gocron)
- [Sentry](https://sentry.io/welcome/)
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=go-co-op/gocron&type=Date)](https://star-history.com/#go-co-op/gocron&Date)

15
vendor/github.com/go-co-op/gocron/v2/SECURITY.md generated vendored Normal file
View File

@@ -0,0 +1,15 @@
# Security Policy
## Supported Versions
The current plan is to maintain version 1 as long as possible incorporating any necessary security patches.
| Version | Supported |
| ------- | ------------------ |
| 1.x.x | :white_check_mark: |
## Reporting a Vulnerability
Vulnerabilities can be reported by [opening an issue](https://github.com/go-co-op/gocron/issues/new/choose) or reaching out on Slack: [<img src="https://img.shields.io/badge/gophers-gocron-brightgreen?logo=slack">](https://gophers.slack.com/archives/CQ7T0T1FW)
We will do our best to addrerss any vulnerabilities in an expeditious manner.

5
vendor/github.com/go-co-op/gocron/v2/codecov.yml generated vendored Normal file
View File

@@ -0,0 +1,5 @@
coverage:
status:
project:
default:
if_ci_failed: success

30
vendor/github.com/go-co-op/gocron/v2/distributed.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
//go:generate mockgen -destination=mocks/distributed.go -package=gocronmocks . Elector,Locker,Lock
package gocron
import (
"context"
)
// Elector determines the leader from instances asking to be the leader. Only
// the leader runs jobs. If the leader goes down, a new leader will be elected.
type Elector interface {
// IsLeader should return nil if the job should be scheduled by the instance
// making the request and an error if the job should not be scheduled.
IsLeader(context.Context) error
}
// Locker represents the required interface to lock jobs when running multiple schedulers.
// The lock is held for the duration of the job's run, and it is expected that the
// locker implementation handles time splay between schedulers.
// The lock key passed is the job's name - which, if not set, defaults to the
// go function's name, e.g. "pkg.myJob" for func myJob() {} in pkg
type Locker interface {
// Lock if an error is returned by lock, the job will not be scheduled.
Lock(ctx context.Context, key string) (Lock, error)
}
// Lock represents an obtained lock. The lock is released after the execution of the job
// by the scheduler.
type Lock interface {
Unlock(ctx context.Context) error
}

52
vendor/github.com/go-co-op/gocron/v2/errors.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
package gocron
import "fmt"
// Public error definitions
var (
ErrCronJobParse = fmt.Errorf("gocron: CronJob: crontab parse failure")
ErrDailyJobAtTimeNil = fmt.Errorf("gocron: DailyJob: atTime within atTimes must not be nil")
ErrDailyJobAtTimesNil = fmt.Errorf("gocron: DailyJob: atTimes must not be nil")
ErrDailyJobHours = fmt.Errorf("gocron: DailyJob: atTimes hours must be between 0 and 23 inclusive")
ErrDailyJobMinutesSeconds = fmt.Errorf("gocron: DailyJob: atTimes minutes and seconds must be between 0 and 59 inclusive")
ErrDurationRandomJobMinMax = fmt.Errorf("gocron: DurationRandomJob: minimum duration must be less than maximum duration")
ErrEventListenerFuncNil = fmt.Errorf("gocron: eventListenerFunc must not be nil")
ErrJobNotFound = fmt.Errorf("gocron: job not found")
ErrJobRunNowFailed = fmt.Errorf("gocron: Job: RunNow: scheduler unreachable")
ErrMonthlyJobDays = fmt.Errorf("gocron: MonthlyJob: daysOfTheMonth must be between 31 and -31 inclusive, and not 0")
ErrMonthlyJobAtTimeNil = fmt.Errorf("gocron: MonthlyJob: atTime within atTimes must not be nil")
ErrMonthlyJobAtTimesNil = fmt.Errorf("gocron: MonthlyJob: atTimes must not be nil")
ErrMonthlyJobDaysNil = fmt.Errorf("gocron: MonthlyJob: daysOfTheMonth must not be nil")
ErrMonthlyJobHours = fmt.Errorf("gocron: MonthlyJob: atTimes hours must be between 0 and 23 inclusive")
ErrMonthlyJobMinutesSeconds = fmt.Errorf("gocron: MonthlyJob: atTimes minutes and seconds must be between 0 and 59 inclusive")
ErrNewJobTaskNil = fmt.Errorf("gocron: NewJob: Task must not be nil")
ErrNewJobTaskNotFunc = fmt.Errorf("gocron: NewJob: Task.Function must be of kind reflect.Func")
ErrNewJobWrongNumberOfParameters = fmt.Errorf("gocron: NewJob: Number of provided parameters does not match expected")
ErrNewJobWrongTypeOfParameters = fmt.Errorf("gocron: NewJob: Type of provided parameters does not match expected")
ErrOneTimeJobStartDateTimePast = fmt.Errorf("gocron: OneTimeJob: start must not be in the past")
ErrStopExecutorTimedOut = fmt.Errorf("gocron: timed out waiting for executor to stop")
ErrStopJobsTimedOut = fmt.Errorf("gocron: timed out waiting for jobs to finish")
ErrStopSchedulerTimedOut = fmt.Errorf("gocron: timed out waiting for scheduler to stop")
ErrWeeklyJobAtTimeNil = fmt.Errorf("gocron: WeeklyJob: atTime within atTimes must not be nil")
ErrWeeklyJobAtTimesNil = fmt.Errorf("gocron: WeeklyJob: atTimes must not be nil")
ErrWeeklyJobDaysOfTheWeekNil = fmt.Errorf("gocron: WeeklyJob: daysOfTheWeek must not be nil")
ErrWeeklyJobHours = fmt.Errorf("gocron: WeeklyJob: atTimes hours must be between 0 and 23 inclusive")
ErrWeeklyJobMinutesSeconds = fmt.Errorf("gocron: WeeklyJob: atTimes minutes and seconds must be between 0 and 59 inclusive")
ErrWithClockNil = fmt.Errorf("gocron: WithClock: clock must not be nil")
ErrWithDistributedElectorNil = fmt.Errorf("gocron: WithDistributedElector: elector must not be nil")
ErrWithDistributedLockerNil = fmt.Errorf("gocron: WithDistributedLocker: locker must not be nil")
ErrWithLimitConcurrentJobsZero = fmt.Errorf("gocron: WithLimitConcurrentJobs: limit must be greater than 0")
ErrWithLocationNil = fmt.Errorf("gocron: WithLocation: location must not be nil")
ErrWithLoggerNil = fmt.Errorf("gocron: WithLogger: logger must not be nil")
ErrWithNameEmpty = fmt.Errorf("gocron: WithName: name must not be empty")
ErrWithStartDateTimePast = fmt.Errorf("gocron: WithStartDateTime: start must not be in the past")
ErrWithStopTimeoutZeroOrNegative = fmt.Errorf("gocron: WithStopTimeout: timeout must be greater than 0")
)
// internal errors
var (
errAtTimeNil = fmt.Errorf("errAtTimeNil")
errAtTimesNil = fmt.Errorf("errAtTimesNil")
errAtTimeHours = fmt.Errorf("errAtTimeHours")
errAtTimeMinSec = fmt.Errorf("errAtTimeMinSec")
)

440
vendor/github.com/go-co-op/gocron/v2/executor.go generated vendored Normal file
View File

@@ -0,0 +1,440 @@
package gocron
import (
"context"
"strconv"
"sync"
"time"
"github.com/google/uuid"
)
type executor struct {
ctx context.Context
cancel context.CancelFunc
logger Logger
stopCh chan struct{}
jobsIn chan jobIn
jobIDsOut chan uuid.UUID
jobOutRequest chan jobOutRequest
stopTimeout time.Duration
done chan error
singletonRunners map[uuid.UUID]singletonRunner
limitMode *limitModeConfig
elector Elector
locker Locker
}
type jobIn struct {
id uuid.UUID
shouldSendOut bool
}
type singletonRunner struct {
in chan jobIn
rescheduleLimiter chan struct{}
}
type limitModeConfig struct {
started bool
mode LimitMode
limit uint
rescheduleLimiter chan struct{}
in chan jobIn
// singletonJobs is used to track singleton jobs that are running
// in the limit mode runner. This is used to prevent the same job
// from running multiple times across limit mode runners when both
// a limit mode and singleton mode are enabled.
singletonJobs map[uuid.UUID]struct{}
singletonJobsMu sync.Mutex
}
func (e *executor) start() {
e.logger.Debug("gocron: executor started")
// creating the executor's context here as the executor
// is the only goroutine that should access this context
// any other uses within the executor should create a context
// using the executor context as parent.
e.ctx, e.cancel = context.WithCancel(context.Background())
// the standardJobsWg tracks
standardJobsWg := &waitGroupWithMutex{}
singletonJobsWg := &waitGroupWithMutex{}
limitModeJobsWg := &waitGroupWithMutex{}
// create a fresh map for tracking singleton runners
e.singletonRunners = make(map[uuid.UUID]singletonRunner)
// start the for leap that is the executor
// selecting on channels for work to do
for {
select {
// job ids in are sent from 1 of 2 places:
// 1. the scheduler sends directly when jobs
// are run immediately.
// 2. sent from time.AfterFuncs in which job schedules
// are spun up by the scheduler
case jIn := <-e.jobsIn:
select {
case <-e.stopCh:
e.stop(standardJobsWg, singletonJobsWg, limitModeJobsWg)
return
default:
}
// this context is used to handle cancellation of the executor
// on requests for a job to the scheduler via requestJobCtx
ctx, cancel := context.WithCancel(e.ctx)
if e.limitMode != nil && !e.limitMode.started {
// check if we are already running the limit mode runners
// if not, spin up the required number i.e. limit!
e.limitMode.started = true
for i := e.limitMode.limit; i > 0; i-- {
limitModeJobsWg.Add(1)
go e.limitModeRunner("limitMode-"+strconv.Itoa(int(i)), e.limitMode.in, limitModeJobsWg, e.limitMode.mode, e.limitMode.rescheduleLimiter)
}
}
// spin off into a goroutine to unblock the executor and
// allow for processing for more work
go func() {
// make sure to cancel the above context per the docs
// // Canceling this context releases resources associated with it, so code should
// // call cancel as soon as the operations running in this Context complete.
defer cancel()
// check for limit mode - this spins up a separate runner which handles
// limiting the total number of concurrently running jobs
if e.limitMode != nil {
if e.limitMode.mode == LimitModeReschedule {
select {
// rescheduleLimiter is a channel the size of the limit
// this blocks publishing to the channel and keeps
// the executor from building up a waiting queue
// and forces rescheduling
case e.limitMode.rescheduleLimiter <- struct{}{}:
e.limitMode.in <- jIn
default:
// all runners are busy, reschedule the work for later
// which means we just skip it here and do nothing
// TODO when metrics are added, this should increment a rescheduled metric
if jIn.shouldSendOut {
select {
case e.jobIDsOut <- jIn.id:
default:
}
}
}
} else {
// since we're not using LimitModeReschedule, but instead using LimitModeWait
// we do want to queue up the work to the limit mode runners and allow them
// to work through the channel backlog. A hard limit of 1000 is in place
// at which point this call would block.
// TODO when metrics are added, this should increment a wait metric
e.limitMode.in <- jIn
}
} else {
// no limit mode, so we're either running a regular job or
// a job with a singleton mode
//
// get the job, so we can figure out what kind it is and how
// to execute it
j := requestJobCtx(ctx, jIn.id, e.jobOutRequest)
if j == nil {
// safety check as it'd be strange bug if this occurred
return
}
if j.singletonMode {
// for singleton mode, get the existing runner for the job
// or spin up a new one
runner, ok := e.singletonRunners[jIn.id]
if !ok {
runner.in = make(chan jobIn, 1000)
if j.singletonLimitMode == LimitModeReschedule {
runner.rescheduleLimiter = make(chan struct{}, 1)
}
e.singletonRunners[jIn.id] = runner
singletonJobsWg.Add(1)
go e.singletonModeRunner("singleton-"+jIn.id.String(), runner.in, singletonJobsWg, j.singletonLimitMode, runner.rescheduleLimiter)
}
if j.singletonLimitMode == LimitModeReschedule {
// reschedule mode uses the limiter channel to check
// for a running job and reschedules if the channel is full.
select {
case runner.rescheduleLimiter <- struct{}{}:
runner.in <- jIn
default:
// runner is busy, reschedule the work for later
// which means we just skip it here and do nothing
// TODO when metrics are added, this should increment a rescheduled metric
if jIn.shouldSendOut {
select {
case e.jobIDsOut <- jIn.id:
default:
}
}
}
} else {
// wait mode, fill up that queue (buffered channel, so it's ok)
runner.in <- jIn
}
} else {
select {
case <-e.stopCh:
e.stop(standardJobsWg, singletonJobsWg, limitModeJobsWg)
return
default:
}
// we've gotten to the basic / standard jobs --
// the ones without anything special that just want
// to be run. Add to the WaitGroup so that
// stopping or shutting down can wait for the jobs to
// complete.
standardJobsWg.Add(1)
go func(j internalJob) {
e.runJob(j, jIn.shouldSendOut)
standardJobsWg.Done()
}(*j)
}
}
}()
case <-e.stopCh:
e.stop(standardJobsWg, singletonJobsWg, limitModeJobsWg)
return
}
}
}
func (e *executor) limitModeRunner(name string, in chan jobIn, wg *waitGroupWithMutex, limitMode LimitMode, rescheduleLimiter chan struct{}) {
e.logger.Debug("gocron: limitModeRunner starting", "name", name)
for {
select {
case jIn := <-in:
select {
case <-e.ctx.Done():
e.logger.Debug("gocron: limitModeRunner shutting down", "name", name)
wg.Done()
return
default:
}
ctx, cancel := context.WithCancel(e.ctx)
j := requestJobCtx(ctx, jIn.id, e.jobOutRequest)
cancel()
if j != nil {
if j.singletonMode {
e.limitMode.singletonJobsMu.Lock()
_, ok := e.limitMode.singletonJobs[jIn.id]
if ok {
// this job is already running, so don't run it
// but instead reschedule it
e.limitMode.singletonJobsMu.Unlock()
if jIn.shouldSendOut {
select {
case <-e.ctx.Done():
return
case <-j.ctx.Done():
return
case e.jobIDsOut <- j.id:
}
}
// remove the limiter block, as this particular job
// was a singleton already running, and we want to
// allow another job to be scheduled
if limitMode == LimitModeReschedule {
select {
case <-rescheduleLimiter:
default:
}
}
continue
}
e.limitMode.singletonJobs[jIn.id] = struct{}{}
e.limitMode.singletonJobsMu.Unlock()
}
e.runJob(*j, jIn.shouldSendOut)
if j.singletonMode {
e.limitMode.singletonJobsMu.Lock()
delete(e.limitMode.singletonJobs, jIn.id)
e.limitMode.singletonJobsMu.Unlock()
}
}
// remove the limiter block to allow another job to be scheduled
if limitMode == LimitModeReschedule {
select {
case <-rescheduleLimiter:
default:
}
}
case <-e.ctx.Done():
e.logger.Debug("limitModeRunner shutting down", "name", name)
wg.Done()
return
}
}
}
func (e *executor) singletonModeRunner(name string, in chan jobIn, wg *waitGroupWithMutex, limitMode LimitMode, rescheduleLimiter chan struct{}) {
e.logger.Debug("gocron: singletonModeRunner starting", "name", name)
for {
select {
case jIn := <-in:
select {
case <-e.ctx.Done():
e.logger.Debug("gocron: singletonModeRunner shutting down", "name", name)
wg.Done()
return
default:
}
ctx, cancel := context.WithCancel(e.ctx)
j := requestJobCtx(ctx, jIn.id, e.jobOutRequest)
cancel()
if j != nil {
e.runJob(*j, jIn.shouldSendOut)
}
// remove the limiter block to allow another job to be scheduled
if limitMode == LimitModeReschedule {
select {
case <-rescheduleLimiter:
default:
}
}
case <-e.ctx.Done():
e.logger.Debug("singletonModeRunner shutting down", "name", name)
wg.Done()
return
}
}
}
func (e *executor) runJob(j internalJob, shouldSendOut bool) {
if j.ctx == nil {
return
}
select {
case <-e.ctx.Done():
return
case <-j.ctx.Done():
return
default:
}
if e.elector != nil {
if err := e.elector.IsLeader(j.ctx); err != nil {
return
}
} else if e.locker != nil {
lock, err := e.locker.Lock(j.ctx, j.name)
if err != nil {
return
}
defer func() { _ = lock.Unlock(j.ctx) }()
}
_ = callJobFuncWithParams(j.beforeJobRuns, j.id, j.name)
if shouldSendOut {
select {
case <-e.ctx.Done():
return
case <-j.ctx.Done():
return
case e.jobIDsOut <- j.id:
}
}
err := callJobFuncWithParams(j.function, j.parameters...)
if err != nil {
_ = callJobFuncWithParams(j.afterJobRunsWithError, j.id, j.name, err)
} else {
_ = callJobFuncWithParams(j.afterJobRuns, j.id, j.name)
}
}
func (e *executor) stop(standardJobsWg, singletonJobsWg, limitModeJobsWg *waitGroupWithMutex) {
e.logger.Debug("gocron: stopping executor")
// we've been asked to stop. This is either because the scheduler has been told
// to stop all jobs or the scheduler has been asked to completely shutdown.
//
// cancel tells all the functions to stop their work and send in a done response
e.cancel()
// the wait for job channels are used to report back whether we successfully waited
// for all jobs to complete or if we hit the configured timeout.
waitForJobs := make(chan struct{}, 1)
waitForSingletons := make(chan struct{}, 1)
waitForLimitMode := make(chan struct{}, 1)
// the waiter context is used to cancel the functions waiting on jobs.
// this is done to avoid goroutine leaks.
waiterCtx, waiterCancel := context.WithCancel(context.Background())
// wait for standard jobs to complete
go func() {
e.logger.Debug("gocron: waiting for standard jobs to complete")
go func() {
// this is done in a separate goroutine, so we aren't
// blocked by the WaitGroup's Wait call in the event
// that the waiter context is cancelled.
// This particular goroutine could leak in the event that
// some long-running standard job doesn't complete.
standardJobsWg.Wait()
e.logger.Debug("gocron: standard jobs completed")
waitForJobs <- struct{}{}
}()
<-waiterCtx.Done()
}()
// wait for per job singleton limit mode runner jobs to complete
go func() {
e.logger.Debug("gocron: waiting for singleton jobs to complete")
go func() {
singletonJobsWg.Wait()
e.logger.Debug("gocron: singleton jobs completed")
waitForSingletons <- struct{}{}
}()
<-waiterCtx.Done()
}()
// wait for limit mode runners to complete
go func() {
e.logger.Debug("gocron: waiting for limit mode jobs to complete")
go func() {
limitModeJobsWg.Wait()
e.logger.Debug("gocron: limitMode jobs completed")
waitForLimitMode <- struct{}{}
}()
<-waiterCtx.Done()
}()
// now either wait for all the jobs to complete,
// or hit the timeout.
var count int
timeout := time.Now().Add(e.stopTimeout)
for time.Now().Before(timeout) && count < 3 {
select {
case <-waitForJobs:
count++
case <-waitForSingletons:
count++
case <-waitForLimitMode:
count++
default:
}
}
if count < 3 {
e.done <- ErrStopJobsTimedOut
e.logger.Debug("gocron: executor stopped - timed out")
} else {
e.done <- nil
e.logger.Debug("gocron: executor stopped")
}
waiterCancel()
}

913
vendor/github.com/go-co-op/gocron/v2/job.go generated vendored Normal file
View File

@@ -0,0 +1,913 @@
//go:generate mockgen -destination=mocks/job.go -package=gocronmocks . Job
package gocron
import (
"context"
"errors"
"fmt"
"math/rand"
"strings"
"time"
"github.com/google/uuid"
"github.com/jonboulle/clockwork"
"github.com/robfig/cron/v3"
"golang.org/x/exp/slices"
)
// internalJob stores the information needed by the scheduler
// to manage scheduling, starting and stopping the job
type internalJob struct {
ctx context.Context
cancel context.CancelFunc
id uuid.UUID
name string
tags []string
jobSchedule
lastRun, nextRun time.Time
function any
parameters []any
timer clockwork.Timer
singletonMode bool
singletonLimitMode LimitMode
limitRunsTo *limitRunsTo
startTime time.Time
startImmediately bool
// event listeners
afterJobRuns func(jobID uuid.UUID, jobName string)
beforeJobRuns func(jobID uuid.UUID, jobName string)
afterJobRunsWithError func(jobID uuid.UUID, jobName string, err error)
}
// stop is used to stop the job's timer and cancel the context
// stopping the timer is critical for cleaning up jobs that are
// sleeping in a time.AfterFunc timer when the job is being stopped.
// cancelling the context keeps the executor from continuing to try
// and run the job.
func (j *internalJob) stop() {
if j.timer != nil {
j.timer.Stop()
}
j.cancel()
}
// task stores the function and parameters
// that are actually run when the job is executed.
type task struct {
function any
parameters []any
}
// Task defines a function that returns the task
// function and parameters.
type Task func() task
// NewTask provides the job's task function and parameters.
func NewTask(function any, parameters ...any) Task {
return func() task {
return task{
function: function,
parameters: parameters,
}
}
}
// limitRunsTo is used for managing the number of runs
// when the user only wants the job to run a certain
// number of times and then be removed from the scheduler.
type limitRunsTo struct {
limit uint
runCount uint
}
// -----------------------------------------------
// -----------------------------------------------
// --------------- Job Variants ------------------
// -----------------------------------------------
// -----------------------------------------------
// JobDefinition defines the interface that must be
// implemented to create a job from the definition.
type JobDefinition interface {
setup(*internalJob, *time.Location) error
}
var _ JobDefinition = (*cronJobDefinition)(nil)
type cronJobDefinition struct {
crontab string
withSeconds bool
}
func (c cronJobDefinition) setup(j *internalJob, location *time.Location) error {
var withLocation string
if strings.HasPrefix(c.crontab, "TZ=") || strings.HasPrefix(c.crontab, "CRON_TZ=") {
withLocation = c.crontab
} else {
// since the user didn't provide a timezone default to the location
// passed in by the scheduler. Default: time.Local
withLocation = fmt.Sprintf("CRON_TZ=%s %s", location.String(), c.crontab)
}
var (
cronSchedule cron.Schedule
err error
)
if c.withSeconds {
p := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
cronSchedule, err = p.Parse(withLocation)
} else {
cronSchedule, err = cron.ParseStandard(withLocation)
}
if err != nil {
return errors.Join(ErrCronJobParse, err)
}
j.jobSchedule = &cronJob{cronSchedule: cronSchedule}
return nil
}
// CronJob defines a new job using the crontab syntax: `* * * * *`.
// An optional 6th field can be used at the beginning if withSeconds
// is set to true: `* * * * * *`.
// The timezone can be set on the Scheduler using WithLocation, or in the
// crontab in the form `TZ=America/Chicago * * * * *` or
// `CRON_TZ=America/Chicago * * * * *`
func CronJob(crontab string, withSeconds bool) JobDefinition {
return cronJobDefinition{
crontab: crontab,
withSeconds: withSeconds,
}
}
var _ JobDefinition = (*durationJobDefinition)(nil)
type durationJobDefinition struct {
duration time.Duration
}
func (d durationJobDefinition) setup(j *internalJob, _ *time.Location) error {
j.jobSchedule = &durationJob{duration: d.duration}
return nil
}
// DurationJob defines a new job using time.Duration
// for the interval.
func DurationJob(duration time.Duration) JobDefinition {
return durationJobDefinition{
duration: duration,
}
}
var _ JobDefinition = (*durationRandomJobDefinition)(nil)
type durationRandomJobDefinition struct {
min, max time.Duration
}
func (d durationRandomJobDefinition) setup(j *internalJob, _ *time.Location) error {
if d.min >= d.max {
return ErrDurationRandomJobMinMax
}
j.jobSchedule = &durationRandomJob{
min: d.min,
max: d.max,
rand: rand.New(rand.NewSource(time.Now().UnixNano())), // nolint:gosec
}
return nil
}
// DurationRandomJob defines a new job that runs on a random interval
// between the min and max duration values provided.
//
// To achieve a similar behavior as tools that use a splay/jitter technique
// consider the median value as the baseline and the difference between the
// max-median or median-min as the splay/jitter.
//
// For example, if you want a job to run every 5 minutes, but want to add
// up to 1 min of jitter to the interval, you could use
// DurationRandomJob(4*time.Minute, 6*time.Minute)
func DurationRandomJob(minDuration, maxDuration time.Duration) JobDefinition {
return durationRandomJobDefinition{
min: minDuration,
max: maxDuration,
}
}
// DailyJob runs the job on the interval of days, and at the set times.
// By default, the job will start the next available day, considering the last run to be now,
// and the time and day based on the interval and times you input. This means, if you
// select an interval greater than 1, your job by default will run X (interval) days from now
// if there are no atTimes left in the current day. You can use WithStartAt to tell the
// scheduler to start the job sooner.
func DailyJob(interval uint, atTimes AtTimes) JobDefinition {
return dailyJobDefinition{
interval: interval,
atTimes: atTimes,
}
}
var _ JobDefinition = (*dailyJobDefinition)(nil)
type dailyJobDefinition struct {
interval uint
atTimes AtTimes
}
func (d dailyJobDefinition) setup(j *internalJob, location *time.Location) error {
atTimesDate, err := convertAtTimesToDateTime(d.atTimes, location)
switch {
case errors.Is(err, errAtTimesNil):
return ErrDailyJobAtTimesNil
case errors.Is(err, errAtTimeNil):
return ErrDailyJobAtTimeNil
case errors.Is(err, errAtTimeHours):
return ErrDailyJobHours
case errors.Is(err, errAtTimeMinSec):
return ErrDailyJobMinutesSeconds
}
ds := dailyJob{
interval: d.interval,
atTimes: atTimesDate,
}
j.jobSchedule = ds
return nil
}
var _ JobDefinition = (*weeklyJobDefinition)(nil)
type weeklyJobDefinition struct {
interval uint
daysOfTheWeek Weekdays
atTimes AtTimes
}
func (w weeklyJobDefinition) setup(j *internalJob, location *time.Location) error {
var ws weeklyJob
ws.interval = w.interval
if w.daysOfTheWeek == nil {
return ErrWeeklyJobDaysOfTheWeekNil
}
daysOfTheWeek := w.daysOfTheWeek()
slices.Sort(daysOfTheWeek)
ws.daysOfWeek = daysOfTheWeek
atTimesDate, err := convertAtTimesToDateTime(w.atTimes, location)
switch {
case errors.Is(err, errAtTimesNil):
return ErrWeeklyJobAtTimesNil
case errors.Is(err, errAtTimeNil):
return ErrWeeklyJobAtTimeNil
case errors.Is(err, errAtTimeHours):
return ErrWeeklyJobHours
case errors.Is(err, errAtTimeMinSec):
return ErrWeeklyJobMinutesSeconds
}
ws.atTimes = atTimesDate
j.jobSchedule = ws
return nil
}
// Weekdays defines a function that returns a list of week days.
type Weekdays func() []time.Weekday
// NewWeekdays provide the days of the week the job should run.
func NewWeekdays(weekday time.Weekday, weekdays ...time.Weekday) Weekdays {
return func() []time.Weekday {
return append(weekdays, weekday)
}
}
// WeeklyJob runs the job on the interval of weeks, on the specific days of the week
// specified, and at the set times.
//
// By default, the job will start the next available day, considering the last run to be now,
// and the time and day based on the interval, days and times you input. This means, if you
// select an interval greater than 1, your job by default will run X (interval) weeks from now
// if there are no daysOfTheWeek left in the current week. You can use WithStartAt to tell the
// scheduler to start the job sooner.
func WeeklyJob(interval uint, daysOfTheWeek Weekdays, atTimes AtTimes) JobDefinition {
return weeklyJobDefinition{
interval: interval,
daysOfTheWeek: daysOfTheWeek,
atTimes: atTimes,
}
}
var _ JobDefinition = (*monthlyJobDefinition)(nil)
type monthlyJobDefinition struct {
interval uint
daysOfTheMonth DaysOfTheMonth
atTimes AtTimes
}
func (m monthlyJobDefinition) setup(j *internalJob, location *time.Location) error {
var ms monthlyJob
ms.interval = m.interval
if m.daysOfTheMonth == nil {
return ErrMonthlyJobDaysNil
}
var daysStart, daysEnd []int
for _, day := range m.daysOfTheMonth() {
if day > 31 || day == 0 || day < -31 {
return ErrMonthlyJobDays
}
if day > 0 {
daysStart = append(daysStart, day)
} else {
daysEnd = append(daysEnd, day)
}
}
daysStart = removeSliceDuplicatesInt(daysStart)
slices.Sort(daysStart)
ms.days = daysStart
daysEnd = removeSliceDuplicatesInt(daysEnd)
slices.Sort(daysEnd)
ms.daysFromEnd = daysEnd
atTimesDate, err := convertAtTimesToDateTime(m.atTimes, location)
switch {
case errors.Is(err, errAtTimesNil):
return ErrMonthlyJobAtTimesNil
case errors.Is(err, errAtTimeNil):
return ErrMonthlyJobAtTimeNil
case errors.Is(err, errAtTimeHours):
return ErrMonthlyJobHours
case errors.Is(err, errAtTimeMinSec):
return ErrMonthlyJobMinutesSeconds
}
ms.atTimes = atTimesDate
j.jobSchedule = ms
return nil
}
type days []int
// DaysOfTheMonth defines a function that returns a list of days.
type DaysOfTheMonth func() days
// NewDaysOfTheMonth provide the days of the month the job should
// run. The days can be positive 1 to 31 and/or negative -31 to -1.
// Negative values count backwards from the end of the month.
// For example: -1 == the last day of the month.
//
// -5 == 5 days before the end of the month.
func NewDaysOfTheMonth(day int, moreDays ...int) DaysOfTheMonth {
return func() days {
moreDays = append(moreDays, day)
return moreDays
}
}
type atTime struct {
hours, minutes, seconds uint
}
func (a atTime) time(location *time.Location) time.Time {
return time.Date(0, 0, 0, int(a.hours), int(a.minutes), int(a.seconds), 0, location)
}
// AtTime defines a function that returns the internal atTime
type AtTime func() atTime
// NewAtTime provide the hours, minutes and seconds at which
// the job should be run
func NewAtTime(hours, minutes, seconds uint) AtTime {
return func() atTime {
return atTime{hours: hours, minutes: minutes, seconds: seconds}
}
}
// AtTimes define a list of AtTime
type AtTimes func() []AtTime
// NewAtTimes provide the hours, minutes and seconds at which
// the job should be run
func NewAtTimes(atTime AtTime, atTimes ...AtTime) AtTimes {
return func() []AtTime {
atTimes = append(atTimes, atTime)
return atTimes
}
}
// MonthlyJob runs the job on the interval of months, on the specific days of the month
// specified, and at the set times. Days of the month can be 1 to 31 or negative (-1 to -31), which
// count backwards from the end of the month. E.g. -1 is the last day of the month.
//
// If a day of the month is selected that does not exist in all months (e.g. 31st)
// any month that does not have that day will be skipped.
//
// By default, the job will start the next available day, considering the last run to be now,
// and the time and month based on the interval, days and times you input.
// This means, if you select an interval greater than 1, your job by default will run
// X (interval) months from now if there are no daysOfTheMonth left in the current month.
// You can use WithStartAt to tell the scheduler to start the job sooner.
//
// Carefully consider your configuration!
// - For example: an interval of 2 months on the 31st of each month, starting 12/31
// would skip Feb, April, June, and next run would be in August.
func MonthlyJob(interval uint, daysOfTheMonth DaysOfTheMonth, atTimes AtTimes) JobDefinition {
return monthlyJobDefinition{
interval: interval,
daysOfTheMonth: daysOfTheMonth,
atTimes: atTimes,
}
}
var _ JobDefinition = (*oneTimeJobDefinition)(nil)
type oneTimeJobDefinition struct {
startAt OneTimeJobStartAtOption
}
func (o oneTimeJobDefinition) setup(j *internalJob, _ *time.Location) error {
j.jobSchedule = oneTimeJob{}
return o.startAt(j)
}
// OneTimeJobStartAtOption defines when the one time job is run
type OneTimeJobStartAtOption func(*internalJob) error
// OneTimeJobStartImmediately tells the scheduler to run the one time job immediately.
func OneTimeJobStartImmediately() OneTimeJobStartAtOption {
return func(j *internalJob) error {
j.startImmediately = true
return nil
}
}
// OneTimeJobStartDateTime sets the date & time at which the job should run.
// This datetime must be in the future.
func OneTimeJobStartDateTime(start time.Time) OneTimeJobStartAtOption {
return func(j *internalJob) error {
if start.IsZero() || start.Before(time.Now()) {
return ErrOneTimeJobStartDateTimePast
}
j.startTime = start
return nil
}
}
// OneTimeJob is to run a job once at a specified time and not on
// any regular schedule.
func OneTimeJob(startAt OneTimeJobStartAtOption) JobDefinition {
return oneTimeJobDefinition{
startAt: startAt,
}
}
// -----------------------------------------------
// -----------------------------------------------
// ----------------- Job Options -----------------
// -----------------------------------------------
// -----------------------------------------------
// JobOption defines the constructor for job options.
type JobOption func(*internalJob) error
// WithEventListeners sets the event listeners that should be
// run for the job.
func WithEventListeners(eventListeners ...EventListener) JobOption {
return func(j *internalJob) error {
for _, eventListener := range eventListeners {
if err := eventListener(j); err != nil {
return err
}
}
return nil
}
}
// WithLimitedRuns limits the number of executions of this job to n.
// Upon reaching the limit, the job is removed from the scheduler.
func WithLimitedRuns(limit uint) JobOption {
return func(j *internalJob) error {
j.limitRunsTo = &limitRunsTo{
limit: limit,
runCount: 0,
}
return nil
}
}
// WithName sets the name of the job. Name provides
// a human-readable identifier for the job.
func WithName(name string) JobOption {
// TODO use the name for metrics and future logging option
return func(j *internalJob) error {
if name == "" {
return ErrWithNameEmpty
}
j.name = name
return nil
}
}
// WithSingletonMode keeps the job from running again if it is already running.
// This is useful for jobs that should not overlap, and that occasionally
// (but not consistently) run longer than the interval between job runs.
func WithSingletonMode(mode LimitMode) JobOption {
return func(j *internalJob) error {
j.singletonMode = true
j.singletonLimitMode = mode
return nil
}
}
// WithStartAt sets the option for starting the job at
// a specific datetime.
func WithStartAt(option StartAtOption) JobOption {
return func(j *internalJob) error {
return option(j)
}
}
// StartAtOption defines options for starting the job
type StartAtOption func(*internalJob) error
// WithStartImmediately tells the scheduler to run the job immediately
// regardless of the type or schedule of job. After this immediate run
// the job is scheduled from this time based on the job definition.
func WithStartImmediately() StartAtOption {
return func(j *internalJob) error {
j.startImmediately = true
return nil
}
}
// WithStartDateTime sets the first date & time at which the job should run.
// This datetime must be in the future.
func WithStartDateTime(start time.Time) StartAtOption {
return func(j *internalJob) error {
if start.IsZero() || start.Before(time.Now()) {
return ErrWithStartDateTimePast
}
j.startTime = start
return nil
}
}
// WithTags sets the tags for the job. Tags provide
// a way to identify jobs by a set of tags and remove
// multiple jobs by tag.
func WithTags(tags ...string) JobOption {
return func(j *internalJob) error {
j.tags = tags
return nil
}
}
// -----------------------------------------------
// -----------------------------------------------
// ------------- Job Event Listeners -------------
// -----------------------------------------------
// -----------------------------------------------
// EventListener defines the constructor for event
// listeners that can be used to listen for job events.
type EventListener func(*internalJob) error
// AfterJobRuns is used to listen for when a job has run regardless
// of any returned error value, and run the provided function.
func AfterJobRuns(eventListenerFunc func(jobID uuid.UUID, jobName string)) EventListener {
return func(j *internalJob) error {
if eventListenerFunc == nil {
return ErrEventListenerFuncNil
}
j.afterJobRuns = eventListenerFunc
return nil
}
}
// AfterJobRunsWithError is used to listen for when a job has run and
// returned an error, and then run the provided function.
func AfterJobRunsWithError(eventListenerFunc func(jobID uuid.UUID, jobName string, err error)) EventListener {
return func(j *internalJob) error {
if eventListenerFunc == nil {
return ErrEventListenerFuncNil
}
j.afterJobRunsWithError = eventListenerFunc
return nil
}
}
// BeforeJobRuns is used to listen for when a job is about to run and
// then run the provided function.
func BeforeJobRuns(eventListenerFunc func(jobID uuid.UUID, jobName string)) EventListener {
return func(j *internalJob) error {
if eventListenerFunc == nil {
return ErrEventListenerFuncNil
}
j.beforeJobRuns = eventListenerFunc
return nil
}
}
// -----------------------------------------------
// -----------------------------------------------
// ---------------- Job Schedules ----------------
// -----------------------------------------------
// -----------------------------------------------
type jobSchedule interface {
next(lastRun time.Time) time.Time
}
var _ jobSchedule = (*cronJob)(nil)
type cronJob struct {
cronSchedule cron.Schedule
}
func (j *cronJob) next(lastRun time.Time) time.Time {
return j.cronSchedule.Next(lastRun)
}
var _ jobSchedule = (*durationJob)(nil)
type durationJob struct {
duration time.Duration
}
func (j *durationJob) next(lastRun time.Time) time.Time {
return lastRun.Add(j.duration)
}
var _ jobSchedule = (*durationRandomJob)(nil)
type durationRandomJob struct {
min, max time.Duration
rand *rand.Rand
}
func (j *durationRandomJob) next(lastRun time.Time) time.Time {
r := j.rand.Int63n(int64(j.max - j.min))
return lastRun.Add(j.min + time.Duration(r))
}
var _ jobSchedule = (*dailyJob)(nil)
type dailyJob struct {
interval uint
atTimes []time.Time
}
func (d dailyJob) next(lastRun time.Time) time.Time {
firstPass := true
next := d.nextDay(lastRun, firstPass)
if !next.IsZero() {
return next
}
firstPass = false
startNextDay := time.Date(lastRun.Year(), lastRun.Month(), lastRun.Day()+int(d.interval), 0, 0, 0, lastRun.Nanosecond(), lastRun.Location())
return d.nextDay(startNextDay, firstPass)
}
func (d dailyJob) nextDay(lastRun time.Time, firstPass bool) time.Time {
for _, at := range d.atTimes {
// sub the at time hour/min/sec onto the lastRun's values
// to use in checks to see if we've got our next run time
atDate := time.Date(lastRun.Year(), lastRun.Month(), lastRun.Day(), at.Hour(), at.Minute(), at.Second(), lastRun.Nanosecond(), lastRun.Location())
if firstPass && atDate.After(lastRun) {
// checking to see if it is after i.e. greater than,
// and not greater or equal as our lastRun day/time
// will be in the loop, and we don't want to select it again
return atDate
} else if !firstPass && !atDate.Before(lastRun) {
// now that we're looking at the next day, it's ok to consider
// the same at time that was last run (as lastRun has been incremented)
return atDate
}
}
return time.Time{}
}
var _ jobSchedule = (*weeklyJob)(nil)
type weeklyJob struct {
interval uint
daysOfWeek []time.Weekday
atTimes []time.Time
}
func (w weeklyJob) next(lastRun time.Time) time.Time {
firstPass := true
next := w.nextWeekDayAtTime(lastRun, firstPass)
if !next.IsZero() {
return next
}
firstPass = false
startOfTheNextIntervalWeek := (lastRun.Day() - int(lastRun.Weekday())) + int(w.interval*7)
from := time.Date(lastRun.Year(), lastRun.Month(), startOfTheNextIntervalWeek, 0, 0, 0, 0, lastRun.Location())
return w.nextWeekDayAtTime(from, firstPass)
}
func (w weeklyJob) nextWeekDayAtTime(lastRun time.Time, firstPass bool) time.Time {
for _, wd := range w.daysOfWeek {
// checking if we're on the same day or later in the same week
if wd >= lastRun.Weekday() {
// weekDayDiff is used to add the correct amount to the atDate day below
weekDayDiff := wd - lastRun.Weekday()
for _, at := range w.atTimes {
// sub the at time hour/min/sec onto the lastRun's values
// to use in checks to see if we've got our next run time
atDate := time.Date(lastRun.Year(), lastRun.Month(), lastRun.Day()+int(weekDayDiff), at.Hour(), at.Minute(), at.Second(), lastRun.Nanosecond(), lastRun.Location())
if firstPass && atDate.After(lastRun) {
// checking to see if it is after i.e. greater than,
// and not greater or equal as our lastRun day/time
// will be in the loop, and we don't want to select it again
return atDate
} else if !firstPass && !atDate.Before(lastRun) {
// now that we're looking at the next week, it's ok to consider
// the same at time that was last run (as lastRun has been incremented)
return atDate
}
}
}
}
return time.Time{}
}
var _ jobSchedule = (*monthlyJob)(nil)
type monthlyJob struct {
interval uint
days []int
daysFromEnd []int
atTimes []time.Time
}
func (m monthlyJob) next(lastRun time.Time) time.Time {
daysList := make([]int, len(m.days))
copy(daysList, m.days)
firstDayNextMonth := time.Date(lastRun.Year(), lastRun.Month()+1, 1, 0, 0, 0, 0, lastRun.Location())
for _, daySub := range m.daysFromEnd {
// getting a combined list of all the daysList and the negative daysList
// which count backwards from the first day of the next month
// -1 == the last day of the month
day := firstDayNextMonth.AddDate(0, 0, daySub).Day()
daysList = append(daysList, day)
}
slices.Sort(daysList)
firstPass := true
next := m.nextMonthDayAtTime(lastRun, daysList, firstPass)
if !next.IsZero() {
return next
}
firstPass = false
from := time.Date(lastRun.Year(), lastRun.Month()+time.Month(m.interval), 1, 0, 0, 0, 0, lastRun.Location())
for next.IsZero() {
next = m.nextMonthDayAtTime(from, daysList, firstPass)
from = from.AddDate(0, int(m.interval), 0)
}
return next
}
func (m monthlyJob) nextMonthDayAtTime(lastRun time.Time, days []int, firstPass bool) time.Time {
// find the next day in the month that should run and then check for an at time
for _, day := range days {
if day >= lastRun.Day() {
for _, at := range m.atTimes {
// sub the day, and the at time hour/min/sec onto the lastRun's values
// to use in checks to see if we've got our next run time
atDate := time.Date(lastRun.Year(), lastRun.Month(), day, at.Hour(), at.Minute(), at.Second(), lastRun.Nanosecond(), lastRun.Location())
if atDate.Month() != lastRun.Month() {
// this check handles if we're setting a day not in the current month
// e.g. setting day 31 in Feb results in March 2nd
continue
}
if firstPass && atDate.After(lastRun) {
// checking to see if it is after i.e. greater than,
// and not greater or equal as our lastRun day/time
// will be in the loop, and we don't want to select it again
return atDate
} else if !firstPass && !atDate.Before(lastRun) {
// now that we're looking at the next month, it's ok to consider
// the same at time that was lastRun (as lastRun has been incremented)
return atDate
}
}
continue
}
}
return time.Time{}
}
var _ jobSchedule = (*oneTimeJob)(nil)
type oneTimeJob struct{}
func (o oneTimeJob) next(_ time.Time) time.Time {
return time.Time{}
}
// -----------------------------------------------
// -----------------------------------------------
// ---------------- Job Interface ----------------
// -----------------------------------------------
// -----------------------------------------------
// Job provides the available methods on the job
// available to the caller.
type Job interface {
// ID returns the job's unique identifier.
ID() uuid.UUID
// LastRun returns the time of the job's last run
LastRun() (time.Time, error)
// Name returns the name defined on the job.
Name() string
// NextRun returns the time of the job's next scheduled run.
NextRun() (time.Time, error)
// RunNow runs the job once, now. This does not alter
// the existing run schedule, and will respect all job
// and scheduler limits.
RunNow() error
// Tags returns the job's string tags.
Tags() []string
}
var _ Job = (*job)(nil)
// job is the internal struct that implements
// the public interface. This is used to avoid
// leaking information the caller never needs
// to have or tinker with.
type job struct {
id uuid.UUID
name string
tags []string
jobOutRequest chan jobOutRequest
runJobRequest chan runJobRequest
}
func (j job) ID() uuid.UUID {
return j.id
}
func (j job) LastRun() (time.Time, error) {
ij := requestJob(j.id, j.jobOutRequest)
if ij == nil || ij.id == uuid.Nil {
return time.Time{}, ErrJobNotFound
}
return ij.lastRun, nil
}
func (j job) Name() string {
return j.name
}
func (j job) NextRun() (time.Time, error) {
ij := requestJob(j.id, j.jobOutRequest)
if ij == nil || ij.id == uuid.Nil {
return time.Time{}, ErrJobNotFound
}
return ij.nextRun, nil
}
func (j job) Tags() []string {
return j.tags
}
func (j job) RunNow() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
resp := make(chan error, 1)
select {
case j.runJobRequest <- runJobRequest{
id: j.id,
outChan: resp,
}:
case <-time.After(100 * time.Millisecond):
return ErrJobRunNowFailed
}
var err error
select {
case <-ctx.Done():
return ErrJobRunNowFailed
case errReceived := <-resp:
err = errReceived
}
return err
}

101
vendor/github.com/go-co-op/gocron/v2/logger.go generated vendored Normal file
View File

@@ -0,0 +1,101 @@
//go:generate mockgen -destination=mocks/logger.go -package=gocronmocks . Logger
package gocron
import (
"fmt"
"log"
"os"
"strings"
)
// Logger is the interface that wraps the basic logging methods
// used by gocron. The methods are modeled after the standard
// library slog package. The default logger is a no-op logger.
// To enable logging, use one of the provided New*Logger functions
// or implement your own Logger. The actual level of Log that is logged
// is handled by the implementation.
type Logger interface {
Debug(msg string, args ...any)
Error(msg string, args ...any)
Info(msg string, args ...any)
Warn(msg string, args ...any)
}
var _ Logger = (*noOpLogger)(nil)
type noOpLogger struct{}
func (l noOpLogger) Debug(_ string, _ ...any) {}
func (l noOpLogger) Error(_ string, _ ...any) {}
func (l noOpLogger) Info(_ string, _ ...any) {}
func (l noOpLogger) Warn(_ string, _ ...any) {}
var _ Logger = (*logger)(nil)
// LogLevel is the level of logging that should be logged
// when using the basic NewLogger.
type LogLevel int
// The different log levels that can be used.
const (
LogLevelError LogLevel = iota
LogLevelWarn
LogLevelInfo
LogLevelDebug
)
type logger struct {
log *log.Logger
level LogLevel
}
// NewLogger returns a new Logger that logs at the given level.
func NewLogger(level LogLevel) Logger {
l := log.New(os.Stdout, "", log.LstdFlags)
return &logger{
log: l,
level: level,
}
}
func (l *logger) Debug(msg string, args ...any) {
if l.level < LogLevelDebug {
return
}
l.log.Printf("DEBUG: %s%s\n", msg, logFormatArgs(args...))
}
func (l *logger) Error(msg string, args ...any) {
if l.level < LogLevelError {
return
}
l.log.Printf("ERROR: %s%s\n", msg, logFormatArgs(args...))
}
func (l *logger) Info(msg string, args ...any) {
if l.level < LogLevelInfo {
return
}
l.log.Printf("INFO: %s%s\n", msg, logFormatArgs(args...))
}
func (l *logger) Warn(msg string, args ...any) {
if l.level < LogLevelWarn {
return
}
l.log.Printf("WARN: %s%s\n", msg, logFormatArgs(args...))
}
func logFormatArgs(args ...any) string {
if len(args) == 0 {
return ""
}
if len(args)%2 != 0 {
return ", " + fmt.Sprint(args...)
}
var pairs []string
for i := 0; i < len(args); i += 2 {
pairs = append(pairs, fmt.Sprintf("%s=%v", args[i], args[i+1]))
}
return ", " + strings.Join(pairs, ", ")
}

776
vendor/github.com/go-co-op/gocron/v2/scheduler.go generated vendored Normal file
View File

@@ -0,0 +1,776 @@
//go:generate mockgen -destination=mocks/scheduler.go -package=gocronmocks . Scheduler
package gocron
import (
"context"
"reflect"
"runtime"
"time"
"github.com/google/uuid"
"github.com/jonboulle/clockwork"
"golang.org/x/exp/slices"
)
var _ Scheduler = (*scheduler)(nil)
// Scheduler defines the interface for the Scheduler.
type Scheduler interface {
// Jobs returns all the jobs currently in the scheduler.
Jobs() []Job
// NewJob creates a new job in the Scheduler. The job is scheduled per the provided
// definition when the Scheduler is started. If the Scheduler is already running
// the job will be scheduled when the Scheduler is started.
NewJob(JobDefinition, Task, ...JobOption) (Job, error)
// RemoveByTags removes all jobs that have at least one of the provided tags.
RemoveByTags(...string)
// RemoveJob removes the job with the provided id.
RemoveJob(uuid.UUID) error
// Shutdown should be called when you no longer need
// the Scheduler or Job's as the Scheduler cannot
// be restarted after calling Shutdown. This is similar
// to a Close or Cleanup method and is often deferred after
// starting the scheduler.
Shutdown() error
// Start begins scheduling jobs for execution based
// on each job's definition. Job's added to an already
// running scheduler will be scheduled immediately based
// on definition. Start is non-blocking.
Start()
// StopJobs stops the execution of all jobs in the scheduler.
// This can be useful in situations where jobs need to be
// paused globally and then restarted with Start().
StopJobs() error
// Update replaces the existing Job's JobDefinition with the provided
// JobDefinition. The Job's Job.ID() remains the same.
Update(uuid.UUID, JobDefinition, Task, ...JobOption) (Job, error)
}
// -----------------------------------------------
// -----------------------------------------------
// ----------------- Scheduler -------------------
// -----------------------------------------------
// -----------------------------------------------
type scheduler struct {
shutdownCtx context.Context
shutdownCancel context.CancelFunc
exec executor
jobs map[uuid.UUID]internalJob
location *time.Location
clock clockwork.Clock
started bool
globalJobOptions []JobOption
logger Logger
startCh chan struct{}
startedCh chan struct{}
stopCh chan struct{}
stopErrCh chan error
allJobsOutRequest chan allJobsOutRequest
jobOutRequestCh chan jobOutRequest
runJobRequestCh chan runJobRequest
newJobCh chan internalJob
removeJobCh chan uuid.UUID
removeJobsByTagsCh chan []string
}
type jobOutRequest struct {
id uuid.UUID
outChan chan internalJob
}
type runJobRequest struct {
id uuid.UUID
outChan chan error
}
type allJobsOutRequest struct {
outChan chan []Job
}
// NewScheduler creates a new Scheduler instance.
// The Scheduler is not started until Start() is called.
//
// NewJob will add jobs to the Scheduler, but they will not
// be scheduled until Start() is called.
func NewScheduler(options ...SchedulerOption) (Scheduler, error) {
schCtx, cancel := context.WithCancel(context.Background())
exec := executor{
stopCh: make(chan struct{}),
stopTimeout: time.Second * 10,
singletonRunners: make(map[uuid.UUID]singletonRunner),
logger: &noOpLogger{},
jobsIn: make(chan jobIn),
jobIDsOut: make(chan uuid.UUID),
jobOutRequest: make(chan jobOutRequest, 1000),
done: make(chan error),
}
s := &scheduler{
shutdownCtx: schCtx,
shutdownCancel: cancel,
exec: exec,
jobs: make(map[uuid.UUID]internalJob),
location: time.Local,
clock: clockwork.NewRealClock(),
logger: &noOpLogger{},
newJobCh: make(chan internalJob),
removeJobCh: make(chan uuid.UUID),
removeJobsByTagsCh: make(chan []string),
startCh: make(chan struct{}),
startedCh: make(chan struct{}),
stopCh: make(chan struct{}),
stopErrCh: make(chan error, 1),
jobOutRequestCh: make(chan jobOutRequest),
runJobRequestCh: make(chan runJobRequest),
allJobsOutRequest: make(chan allJobsOutRequest),
}
for _, option := range options {
err := option(s)
if err != nil {
return nil, err
}
}
go func() {
s.logger.Info("gocron: new scheduler created")
for {
select {
case id := <-s.exec.jobIDsOut:
s.selectExecJobIDsOut(id)
case j := <-s.newJobCh:
s.selectNewJob(j)
case id := <-s.removeJobCh:
s.selectRemoveJob(id)
case tags := <-s.removeJobsByTagsCh:
s.selectRemoveJobsByTags(tags)
case out := <-s.exec.jobOutRequest:
s.selectJobOutRequest(out)
case out := <-s.jobOutRequestCh:
s.selectJobOutRequest(out)
case out := <-s.allJobsOutRequest:
s.selectAllJobsOutRequest(out)
case run := <-s.runJobRequestCh:
s.selectRunJobRequest(run)
case <-s.startCh:
s.selectStart()
case <-s.stopCh:
s.stopScheduler()
case <-s.shutdownCtx.Done():
s.stopScheduler()
return
}
}
}()
return s, nil
}
// -----------------------------------------------
// -----------------------------------------------
// --------- Scheduler Channel Methods -----------
// -----------------------------------------------
// -----------------------------------------------
// The scheduler's channel functions are broken out here
// to allow prioritizing within the select blocks. The idea
// being that we want to make sure that scheduling tasks
// are not blocked by requests from the caller for information
// about jobs.
func (s *scheduler) stopScheduler() {
s.logger.Debug("gocron: stopping scheduler")
if s.started {
s.exec.stopCh <- struct{}{}
}
for _, j := range s.jobs {
j.stop()
}
for id, j := range s.jobs {
<-j.ctx.Done()
j.ctx, j.cancel = context.WithCancel(s.shutdownCtx)
s.jobs[id] = j
}
var err error
if s.started {
select {
case err = <-s.exec.done:
case <-time.After(s.exec.stopTimeout + 1*time.Second):
err = ErrStopExecutorTimedOut
}
}
s.stopErrCh <- err
s.started = false
s.logger.Debug("gocron: scheduler stopped")
}
func (s *scheduler) selectAllJobsOutRequest(out allJobsOutRequest) {
outJobs := make([]Job, len(s.jobs))
var counter int
for _, j := range s.jobs {
outJobs[counter] = s.jobFromInternalJob(j)
counter++
}
slices.SortFunc(outJobs, func(a, b Job) int {
aID, bID := a.ID().String(), b.ID().String()
switch {
case aID < bID:
return -1
case aID > bID:
return 1
default:
return 0
}
})
select {
case <-s.shutdownCtx.Done():
case out.outChan <- outJobs:
}
}
func (s *scheduler) selectRunJobRequest(run runJobRequest) {
j, ok := s.jobs[run.id]
if !ok {
select {
case run.outChan <- ErrJobNotFound:
default:
}
}
select {
case <-s.shutdownCtx.Done():
select {
case run.outChan <- ErrJobRunNowFailed:
default:
}
case s.exec.jobsIn <- jobIn{
id: j.id,
shouldSendOut: false,
}:
select {
case run.outChan <- nil:
default:
}
}
}
func (s *scheduler) selectRemoveJob(id uuid.UUID) {
j, ok := s.jobs[id]
if !ok {
return
}
j.stop()
delete(s.jobs, id)
}
// Jobs coming back from the executor to the scheduler that
// need to evaluated for rescheduling.
func (s *scheduler) selectExecJobIDsOut(id uuid.UUID) {
j := s.jobs[id]
j.lastRun = j.nextRun
// if the job has a limited number of runs set, we need to
// check how many runs have occurred and stop running this
// job if it has reached the limit.
if j.limitRunsTo != nil {
j.limitRunsTo.runCount = j.limitRunsTo.runCount + 1
if j.limitRunsTo.runCount == j.limitRunsTo.limit {
go func() {
select {
case <-s.shutdownCtx.Done():
return
case s.removeJobCh <- id:
}
}()
return
}
}
next := j.next(j.lastRun)
if next.IsZero() {
// the job's next function will return zero for OneTime jobs.
// since they are one time only, they do not need rescheduling.
return
}
if next.Before(s.now()) {
// in some cases the next run time can be in the past, for example:
// - the time on the machine was incorrect and has been synced with ntp
// - the machine went to sleep, and woke up some time later
// in those cases, we want to increment to the next run in the future
// and schedule the job for that time.
for next.Before(s.now()) {
next = j.next(next)
}
}
j.nextRun = next
j.timer = s.clock.AfterFunc(next.Sub(s.now()), func() {
// set the actual timer on the job here and listen for
// shut down events so that the job doesn't attempt to
// run if the scheduler has been shutdown.
select {
case <-s.shutdownCtx.Done():
return
case s.exec.jobsIn <- jobIn{
id: j.id,
shouldSendOut: true,
}:
}
})
// update the job with its new next and last run times and timer.
s.jobs[id] = j
}
func (s *scheduler) selectJobOutRequest(out jobOutRequest) {
if j, ok := s.jobs[out.id]; ok {
select {
case out.outChan <- j:
case <-s.shutdownCtx.Done():
}
}
close(out.outChan)
}
func (s *scheduler) selectNewJob(j internalJob) {
if s.started {
next := j.startTime
if j.startImmediately {
next = s.now()
select {
case <-s.shutdownCtx.Done():
case s.exec.jobsIn <- jobIn{
id: j.id,
shouldSendOut: true,
}:
}
} else {
if next.IsZero() {
next = j.next(s.now())
}
id := j.id
j.timer = s.clock.AfterFunc(next.Sub(s.now()), func() {
select {
case <-s.shutdownCtx.Done():
case s.exec.jobsIn <- jobIn{
id: id,
shouldSendOut: true,
}:
}
})
}
j.nextRun = next
}
s.jobs[j.id] = j
}
func (s *scheduler) selectRemoveJobsByTags(tags []string) {
for _, j := range s.jobs {
for _, tag := range tags {
if slices.Contains(j.tags, tag) {
j.stop()
delete(s.jobs, j.id)
break
}
}
}
}
func (s *scheduler) selectStart() {
s.logger.Debug("gocron: scheduler starting")
go s.exec.start()
s.started = true
for id, j := range s.jobs {
next := j.startTime
if j.startImmediately {
next = s.now()
select {
case <-s.shutdownCtx.Done():
case s.exec.jobsIn <- jobIn{
id: id,
shouldSendOut: true,
}:
}
} else {
if next.IsZero() {
next = j.next(s.now())
}
jobID := id
j.timer = s.clock.AfterFunc(next.Sub(s.now()), func() {
select {
case <-s.shutdownCtx.Done():
case s.exec.jobsIn <- jobIn{
id: jobID,
shouldSendOut: true,
}:
}
})
}
j.nextRun = next
s.jobs[id] = j
}
select {
case <-s.shutdownCtx.Done():
case s.startedCh <- struct{}{}:
s.logger.Info("gocron: scheduler started")
}
}
// -----------------------------------------------
// -----------------------------------------------
// ------------- Scheduler Methods ---------------
// -----------------------------------------------
// -----------------------------------------------
func (s *scheduler) now() time.Time {
return s.clock.Now().In(s.location)
}
func (s *scheduler) jobFromInternalJob(in internalJob) job {
return job{
id: in.id,
name: in.name,
tags: slices.Clone(in.tags),
jobOutRequest: s.jobOutRequestCh,
}
}
func (s *scheduler) Jobs() []Job {
outChan := make(chan []Job)
select {
case <-s.shutdownCtx.Done():
case s.allJobsOutRequest <- allJobsOutRequest{outChan: outChan}:
}
var jobs []Job
select {
case <-s.shutdownCtx.Done():
case jobs = <-outChan:
}
return jobs
}
func (s *scheduler) NewJob(jobDefinition JobDefinition, task Task, options ...JobOption) (Job, error) {
return s.addOrUpdateJob(uuid.Nil, jobDefinition, task, options)
}
func (s *scheduler) addOrUpdateJob(id uuid.UUID, definition JobDefinition, taskWrapper Task, options []JobOption) (Job, error) {
j := internalJob{}
if id == uuid.Nil {
j.id = uuid.New()
} else {
currentJob := requestJobCtx(s.shutdownCtx, id, s.jobOutRequestCh)
if currentJob != nil && currentJob.id != uuid.Nil {
select {
case <-s.shutdownCtx.Done():
return nil, nil
case s.removeJobCh <- id:
<-currentJob.ctx.Done()
}
}
j.id = id
}
j.ctx, j.cancel = context.WithCancel(s.shutdownCtx)
if taskWrapper == nil {
return nil, ErrNewJobTaskNil
}
tsk := taskWrapper()
taskFunc := reflect.ValueOf(tsk.function)
for taskFunc.Kind() == reflect.Ptr {
taskFunc = taskFunc.Elem()
}
if taskFunc.Kind() != reflect.Func {
return nil, ErrNewJobTaskNotFunc
}
expectedParameterLength := taskFunc.Type().NumIn()
if len(tsk.parameters) != expectedParameterLength {
return nil, ErrNewJobWrongNumberOfParameters
}
for i := 0; i < expectedParameterLength; i++ {
t1 := reflect.TypeOf(tsk.parameters[i]).Kind()
if t1 == reflect.Interface || t1 == reflect.Pointer {
t1 = reflect.TypeOf(tsk.parameters[i]).Elem().Kind()
}
t2 := reflect.New(taskFunc.Type().In(i)).Elem().Kind()
if t2 == reflect.Interface || t2 == reflect.Pointer {
t2 = reflect.Indirect(reflect.ValueOf(taskFunc.Type().In(i))).Kind()
}
if t1 != t2 {
return nil, ErrNewJobWrongTypeOfParameters
}
}
j.name = runtime.FuncForPC(taskFunc.Pointer()).Name()
j.function = tsk.function
j.parameters = tsk.parameters
// apply global job options
for _, option := range s.globalJobOptions {
if err := option(&j); err != nil {
return nil, err
}
}
// apply job specific options, which take precedence
for _, option := range options {
if err := option(&j); err != nil {
return nil, err
}
}
if err := definition.setup(&j, s.location); err != nil {
return nil, err
}
select {
case <-s.shutdownCtx.Done():
case s.newJobCh <- j:
}
return &job{
id: j.id,
name: j.name,
tags: slices.Clone(j.tags),
jobOutRequest: s.jobOutRequestCh,
runJobRequest: s.runJobRequestCh,
}, nil
}
func (s *scheduler) RemoveByTags(tags ...string) {
select {
case <-s.shutdownCtx.Done():
case s.removeJobsByTagsCh <- tags:
}
}
func (s *scheduler) RemoveJob(id uuid.UUID) error {
j := requestJobCtx(s.shutdownCtx, id, s.jobOutRequestCh)
if j == nil || j.id == uuid.Nil {
return ErrJobNotFound
}
select {
case <-s.shutdownCtx.Done():
case s.removeJobCh <- id:
}
return nil
}
func (s *scheduler) Start() {
select {
case <-s.shutdownCtx.Done():
case s.startCh <- struct{}{}:
<-s.startedCh
}
}
func (s *scheduler) StopJobs() error {
select {
case <-s.shutdownCtx.Done():
return nil
case s.stopCh <- struct{}{}:
}
select {
case err := <-s.stopErrCh:
return err
case <-time.After(s.exec.stopTimeout + 2*time.Second):
return ErrStopSchedulerTimedOut
}
}
func (s *scheduler) Shutdown() error {
s.shutdownCancel()
select {
case err := <-s.stopErrCh:
return err
case <-time.After(s.exec.stopTimeout + 2*time.Second):
return ErrStopSchedulerTimedOut
}
}
func (s *scheduler) Update(id uuid.UUID, jobDefinition JobDefinition, task Task, options ...JobOption) (Job, error) {
return s.addOrUpdateJob(id, jobDefinition, task, options)
}
// -----------------------------------------------
// -----------------------------------------------
// ------------- Scheduler Options ---------------
// -----------------------------------------------
// -----------------------------------------------
// SchedulerOption defines the function for setting
// options on the Scheduler.
type SchedulerOption func(*scheduler) error
// WithClock sets the clock used by the Scheduler
// to the clock provided. See https://github.com/jonboulle/clockwork
func WithClock(clock clockwork.Clock) SchedulerOption {
return func(s *scheduler) error {
if clock == nil {
return ErrWithClockNil
}
s.clock = clock
return nil
}
}
// WithDistributedElector sets the elector to be used by multiple
// Scheduler instances to determine who should be the leader.
// Only the leader runs jobs, while non-leaders wait and continue
// to check if a new leader has been elected.
func WithDistributedElector(elector Elector) SchedulerOption {
return func(s *scheduler) error {
if elector == nil {
return ErrWithDistributedElectorNil
}
s.exec.elector = elector
return nil
}
}
// WithDistributedLocker sets the locker to be used by multiple
// Scheduler instances to ensure that only one instance of each
// job is run.
func WithDistributedLocker(locker Locker) SchedulerOption {
return func(s *scheduler) error {
if locker == nil {
return ErrWithDistributedLockerNil
}
s.exec.locker = locker
return nil
}
}
// WithGlobalJobOptions sets JobOption's that will be applied to
// all jobs added to the scheduler. JobOption's set on the job
// itself will override if the same JobOption is set globally.
func WithGlobalJobOptions(jobOptions ...JobOption) SchedulerOption {
return func(s *scheduler) error {
s.globalJobOptions = jobOptions
return nil
}
}
// LimitMode defines the modes used for handling jobs that reach
// the limit provided in WithLimitConcurrentJobs
type LimitMode int
const (
// LimitModeReschedule causes jobs reaching the limit set in
// WithLimitConcurrentJobs or WithSingletonMode to be skipped
// and rescheduled for the next run time rather than being
// queued up to wait.
LimitModeReschedule = 1
// LimitModeWait causes jobs reaching the limit set in
// WithLimitConcurrentJobs or WithSingletonMode to wait
// in a queue until a slot becomes available to run.
//
// Note: this mode can produce unpredictable results as
// job execution order isn't guaranteed. For example, a job that
// executes frequently may pile up in the wait queue and be executed
// many times back to back when the queue opens.
//
// Warning: do not use this mode if your jobs will continue to stack
// up beyond the ability of the limit workers to keep up. An example of
// what NOT to do:
//
// s, _ := gocron.NewScheduler(gocron.WithLimitConcurrentJobs)
// s.NewJob(
// gocron.DurationJob(
// time.Second,
// Task{
// Function: func() {
// time.Sleep(10 * time.Second)
// },
// },
// ),
// )
LimitModeWait = 2
)
// WithLimitConcurrentJobs sets the limit and mode to be used by the
// Scheduler for limiting the number of jobs that may be running at
// a given time.
func WithLimitConcurrentJobs(limit uint, mode LimitMode) SchedulerOption {
return func(s *scheduler) error {
if limit == 0 {
return ErrWithLimitConcurrentJobsZero
}
s.exec.limitMode = &limitModeConfig{
mode: mode,
limit: limit,
in: make(chan jobIn, 1000),
singletonJobs: make(map[uuid.UUID]struct{}),
}
if mode == LimitModeReschedule {
s.exec.limitMode.rescheduleLimiter = make(chan struct{}, limit)
}
return nil
}
}
// WithLocation sets the location (i.e. timezone) that the scheduler
// should operate within. In many systems time.Local is UTC.
// Default: time.Local
func WithLocation(location *time.Location) SchedulerOption {
return func(s *scheduler) error {
if location == nil {
return ErrWithLocationNil
}
s.location = location
return nil
}
}
// WithLogger sets the logger to be used by the Scheduler.
func WithLogger(logger Logger) SchedulerOption {
return func(s *scheduler) error {
if logger == nil {
return ErrWithLoggerNil
}
s.logger = logger
s.exec.logger = logger
return nil
}
}
// WithStopTimeout sets the amount of time the Scheduler should
// wait gracefully for jobs to complete before returning when
// StopJobs() or Shutdown() are called.
// Default: 10 * time.Second
func WithStopTimeout(timeout time.Duration) SchedulerOption {
return func(s *scheduler) error {
if timeout <= 0 {
return ErrWithStopTimeoutZeroOrNegative
}
s.exec.stopTimeout = timeout
return nil
}
}

122
vendor/github.com/go-co-op/gocron/v2/util.go generated vendored Normal file
View File

@@ -0,0 +1,122 @@
package gocron
import (
"context"
"reflect"
"sync"
"time"
"github.com/google/uuid"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
func callJobFuncWithParams(jobFunc any, params ...any) error {
if jobFunc == nil {
return nil
}
f := reflect.ValueOf(jobFunc)
if f.IsZero() {
return nil
}
if len(params) != f.Type().NumIn() {
return nil
}
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
returnValues := f.Call(in)
for _, val := range returnValues {
i := val.Interface()
if err, ok := i.(error); ok {
return err
}
}
return nil
}
func requestJob(id uuid.UUID, ch chan jobOutRequest) *internalJob {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
return requestJobCtx(ctx, id, ch)
}
func requestJobCtx(ctx context.Context, id uuid.UUID, ch chan jobOutRequest) *internalJob {
resp := make(chan internalJob, 1)
select {
case <-ctx.Done():
return nil
default:
}
select {
case ch <- jobOutRequest{
id: id,
outChan: resp,
}:
default:
return nil
}
var j internalJob
select {
case <-ctx.Done():
return nil
case jobReceived := <-resp:
j = jobReceived
}
return &j
}
func removeSliceDuplicatesInt(in []int) []int {
m := make(map[int]struct{})
for _, i := range in {
m[i] = struct{}{}
}
return maps.Keys(m)
}
func convertAtTimesToDateTime(atTimes AtTimes, location *time.Location) ([]time.Time, error) {
if atTimes == nil {
return nil, errAtTimesNil
}
var atTimesDate []time.Time
for _, a := range atTimes() {
if a == nil {
return nil, errAtTimeNil
}
at := a()
if at.hours > 23 {
return nil, errAtTimeHours
} else if at.minutes > 59 || at.seconds > 59 {
return nil, errAtTimeMinSec
}
atTimesDate = append(atTimesDate, at.time(location))
}
slices.SortStableFunc(atTimesDate, func(a, b time.Time) int {
return a.Compare(b)
})
return atTimesDate, nil
}
type waitGroupWithMutex struct {
wg sync.WaitGroup
mu sync.Mutex
}
func (w *waitGroupWithMutex) Add(delta int) {
w.mu.Lock()
defer w.mu.Unlock()
w.wg.Add(delta)
}
func (w *waitGroupWithMutex) Done() {
w.wg.Done()
}
func (w *waitGroupWithMutex) Wait() {
w.mu.Lock()
defer w.mu.Unlock()
w.wg.Wait()
}

8
vendor/github.com/go-ole/go-ole/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,8 @@
language: go
sudo: false
go:
- 1.9.x
- 1.10.x
- 1.11.x
- tip

49
vendor/github.com/go-ole/go-ole/ChangeLog.md generated vendored Normal file
View File

@@ -0,0 +1,49 @@
# Version 1.x.x
* **Add more test cases and reference new test COM server project.** (Placeholder for future additions)
# Version 1.2.0-alphaX
**Minimum supported version is now Go 1.4. Go 1.1 support is deprecated, but should still build.**
* Added CI configuration for Travis-CI and AppVeyor.
* Added test InterfaceID and ClassID for the COM Test Server project.
* Added more inline documentation (#83).
* Added IEnumVARIANT implementation (#88).
* Added IEnumVARIANT test cases (#99, #100, #101).
* Added support for retrieving `time.Time` from VARIANT (#92).
* Added test case for IUnknown (#64).
* Added test case for IDispatch (#64).
* Added test cases for scalar variants (#64, #76).
# Version 1.1.1
* Fixes for Linux build.
* Fixes for Windows build.
# Version 1.1.0
The change to provide building on all platforms is a new feature. The increase in minor version reflects that and allows those who wish to stay on 1.0.x to continue to do so. Support for 1.0.x will be limited to bug fixes.
* Move GUID out of variables.go into its own file to make new documentation available.
* Move OleError out of ole.go into its own file to make new documentation available.
* Add documentation to utility functions.
* Add documentation to variant receiver functions.
* Add documentation to ole structures.
* Make variant available to other systems outside of Windows.
* Make OLE structures available to other systems outside of Windows.
## New Features
* Library should now be built on all platforms supported by Go. Library will NOOP on any platform that is not Windows.
* More functions are now documented and available on godoc.org.
# Version 1.0.1
1. Fix package references from repository location change.
# Version 1.0.0
This version is stable enough for use. The COM API is still incomplete, but provides enough functionality for accessing COM servers using IDispatch interface.
There is no changelog for this version. Check commits for history.

21
vendor/github.com/go-ole/go-ole/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright © 2013-2017 Yasuhiro Matsumoto, <mattn.jp@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

46
vendor/github.com/go-ole/go-ole/README.md generated vendored Normal file
View File

@@ -0,0 +1,46 @@
# Go OLE
[![Build status](https://ci.appveyor.com/api/projects/status/qr0u2sf7q43us9fj?svg=true)](https://ci.appveyor.com/project/jacobsantos/go-ole-jgs28)
[![Build Status](https://travis-ci.org/go-ole/go-ole.svg?branch=master)](https://travis-ci.org/go-ole/go-ole)
[![GoDoc](https://godoc.org/github.com/go-ole/go-ole?status.svg)](https://godoc.org/github.com/go-ole/go-ole)
Go bindings for Windows COM using shared libraries instead of cgo.
By Yasuhiro Matsumoto.
## Install
To experiment with go-ole, you can just compile and run the example program:
```
go get github.com/go-ole/go-ole
cd /path/to/go-ole/
go test
cd /path/to/go-ole/example/excel
go run excel.go
```
## Continuous Integration
Continuous integration configuration has been added for both Travis-CI and AppVeyor. You will have to add these to your own account for your fork in order for it to run.
**Travis-CI**
Travis-CI was added to check builds on Linux to ensure that `go get` works when cross building. Currently, Travis-CI is not used to test cross-building, but this may be changed in the future. It is also not currently possible to test the library on Linux, since COM API is specific to Windows and it is not currently possible to run a COM server on Linux or even connect to a remote COM server.
**AppVeyor**
AppVeyor is used to build on Windows using the (in-development) test COM server. It is currently only used to test the build and ensure that the code works on Windows. It will be used to register a COM server and then run the test cases based on the test COM server.
The tests currently do run and do pass and this should be maintained with commits.
## Versioning
Go OLE uses [semantic versioning](http://semver.org) for version numbers, which is similar to the version contract of the Go language. Which means that the major version will always maintain backwards compatibility with minor versions. Minor versions will only add new additions and changes. Fixes will always be in patch.
This contract should allow you to upgrade to new minor and patch versions without breakage or modifications to your existing code. Leave a ticket, if there is breakage, so that it could be fixed.
## LICENSE
Under the MIT License: http://mattn.mit-license.org/2013

13
vendor/github.com/go-ole/go-ole/SECURITY.md generated vendored Normal file
View File

@@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
Security updates are applied only to the latest release.
## Reporting a Vulnerability
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
Please disclose it at [security advisory](https://github.com/go-ole/go-ole/security/advisories/new).
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.

68
vendor/github.com/go-ole/go-ole/appveyor.yml generated vendored Normal file
View File

@@ -0,0 +1,68 @@
# Notes:
# - Minimal appveyor.yml file is an empty file. All sections are optional.
# - Indent each level of configuration with 2 spaces. Do not use tabs!
# - All section names are case-sensitive.
# - Section names should be unique on each level.
version: "1.3.0.{build}-alpha-{branch}"
os: Visual Studio 2019
build: off
skip_tags: true
clone_folder: c:\gopath\src\github.com\go-ole\go-ole
environment:
GOPATH: c:\gopath
GOROOT: c:\go
DOWNLOADPLATFORM: "x64"
before_test:
# - Download COM Server
- ps: Start-FileDownload "https://github.com/go-ole/test-com-server/releases/download/v1.0.2/test-com-server-${env:DOWNLOADPLATFORM}.zip"
- 7z e test-com-server-%DOWNLOADPLATFORM%.zip -oc:\gopath\src\github.com\go-ole\go-ole > NUL
- c:\gopath\src\github.com\go-ole\go-ole\build\register-assembly.bat
test_script:
- go test -v -cover ./...
# go vet has false positives on unsafe.Pointer with windows/sys. Disabling since it is recommended to use go test instead.
# - go vet ./...
branches:
only:
- master
- v1.2
- v1.1
- v1.0
matrix:
allow_failures:
- environment:
GOROOT: C:\go-x86
DOWNLOADPLATFORM: "x86"
- environment:
GOROOT: C:\go118
DOWNLOADPLATFORM: "x64"
- environment:
GOROOT: C:\go118-x86
DOWNLOADPLATFORM: "x86"
install:
- go version
- go env
- go get -u golang.org/x/tools/cmd/cover
- go get -u golang.org/x/tools/cmd/godoc
- go get -u golang.org/x/tools/cmd/stringer
build_script:
- cd c:\gopath\src\github.com\go-ole\go-ole
- go get -v -t ./...
- go build
# disable automatic tests
test: on
# disable deployment
deploy: off

386
vendor/github.com/go-ole/go-ole/com.go generated vendored Normal file
View File

@@ -0,0 +1,386 @@
// +build windows
package ole
import (
"syscall"
"unicode/utf16"
"unsafe"
)
var (
procCoInitialize = modole32.NewProc("CoInitialize")
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
procCoInitializeSecurity = modole32.NewProc("CoInitializeSecurity")
procCoUninitialize = modole32.NewProc("CoUninitialize")
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
procCLSIDFromProgID = modole32.NewProc("CLSIDFromProgID")
procCLSIDFromString = modole32.NewProc("CLSIDFromString")
procStringFromCLSID = modole32.NewProc("StringFromCLSID")
procStringFromIID = modole32.NewProc("StringFromIID")
procIIDFromString = modole32.NewProc("IIDFromString")
procCoGetObject = modole32.NewProc("CoGetObject")
procGetUserDefaultLCID = modkernel32.NewProc("GetUserDefaultLCID")
procCopyMemory = modkernel32.NewProc("RtlMoveMemory")
procVariantInit = modoleaut32.NewProc("VariantInit")
procVariantClear = modoleaut32.NewProc("VariantClear")
procVariantTimeToSystemTime = modoleaut32.NewProc("VariantTimeToSystemTime")
procSysAllocString = modoleaut32.NewProc("SysAllocString")
procSysAllocStringLen = modoleaut32.NewProc("SysAllocStringLen")
procSysFreeString = modoleaut32.NewProc("SysFreeString")
procSysStringLen = modoleaut32.NewProc("SysStringLen")
procCreateDispTypeInfo = modoleaut32.NewProc("CreateDispTypeInfo")
procCreateStdDispatch = modoleaut32.NewProc("CreateStdDispatch")
procGetActiveObject = modoleaut32.NewProc("GetActiveObject")
procGetMessageW = moduser32.NewProc("GetMessageW")
procDispatchMessageW = moduser32.NewProc("DispatchMessageW")
)
// This is to enable calling COM Security initialization multiple times
var bSecurityInit bool = false
// coInitialize initializes COM library on current thread.
//
// MSDN documentation suggests that this function should not be called. Call
// CoInitializeEx() instead. The reason has to do with threading and this
// function is only for single-threaded apartments.
//
// That said, most users of the library have gotten away with just this
// function. If you are experiencing threading issues, then use
// CoInitializeEx().
func coInitialize() (err error) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx
// Suggests that no value should be passed to CoInitialized.
// Could just be Call() since the parameter is optional. <-- Needs testing to be sure.
hr, _, _ := procCoInitialize.Call(uintptr(0))
if hr != 0 {
err = NewError(hr)
}
return
}
// coInitializeEx initializes COM library with concurrency model.
func coInitializeEx(coinit uint32) (err error) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx
// Suggests that the first parameter is not only optional but should always be NULL.
hr, _, _ := procCoInitializeEx.Call(uintptr(0), uintptr(coinit))
if hr != 0 {
err = NewError(hr)
}
return
}
// coInitializeSecurity: Registers security and sets the default security values
// for the process.
func coInitializeSecurity(cAuthSvc int32,
dwAuthnLevel uint32,
dwImpLevel uint32,
dwCapabilities uint32) (err error) {
// Check COM Security initialization has done previously
if !bSecurityInit {
// https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializesecurity
hr, _, _ := procCoInitializeSecurity.Call(
uintptr(0), // Allow *all* VSS writers to communicate back!
uintptr(cAuthSvc), // Default COM authentication service
uintptr(0), // Default COM authorization service
uintptr(0), // Reserved parameter
uintptr(dwAuthnLevel), // Strongest COM authentication level
uintptr(dwImpLevel), // Minimal impersonation abilities
uintptr(0), // Default COM authentication settings
uintptr(dwCapabilities), // Cloaking
uintptr(0)) // eserved parameter
if hr != 0 {
err = NewError(hr)
} else {
// COM Security initialization done make global flag true.
bSecurityInit = true
}
}
return
}
// CoInitialize initializes COM library on current thread.
//
// MSDN documentation suggests that this function should not be called. Call
// CoInitializeEx() instead. The reason has to do with threading and this
// function is only for single-threaded apartments.
//
// That said, most users of the library have gotten away with just this
// function. If you are experiencing threading issues, then use
// CoInitializeEx().
func CoInitialize(p uintptr) (err error) {
// p is ignored and won't be used.
// Avoid any variable not used errors.
p = uintptr(0)
return coInitialize()
}
// CoInitializeEx initializes COM library with concurrency model.
func CoInitializeEx(p uintptr, coinit uint32) (err error) {
// Avoid any variable not used errors.
p = uintptr(0)
return coInitializeEx(coinit)
}
// CoUninitialize uninitializes COM Library.
func CoUninitialize() {
procCoUninitialize.Call()
}
// CoInitializeSecurity: Registers security and sets the default security values
// for the process.
func CoInitializeSecurity(cAuthSvc int32,
dwAuthnLevel uint32,
dwImpLevel uint32,
dwCapabilities uint32) (err error) {
return coInitializeSecurity(cAuthSvc, dwAuthnLevel, dwImpLevel, dwCapabilities)
}
// CoTaskMemFree frees memory pointer.
func CoTaskMemFree(memptr uintptr) {
procCoTaskMemFree.Call(memptr)
}
// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
//
// The Programmatic Identifier must be registered, because it will be looked up
// in the Windows Registry. The registry entry has the following keys: CLSID,
// Insertable, Protocol and Shell
// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
//
// programID identifies the class id with less precision and is not guaranteed
// to be unique. These are usually found in the registry under
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
// "Program.Component.Version" with version being optional.
//
// CLSIDFromProgID in Windows API.
func CLSIDFromProgID(progId string) (clsid *GUID, err error) {
var guid GUID
lpszProgID := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
hr, _, _ := procCLSIDFromProgID.Call(lpszProgID, uintptr(unsafe.Pointer(&guid)))
if hr != 0 {
err = NewError(hr)
}
clsid = &guid
return
}
// CLSIDFromString retrieves Class ID from string representation.
//
// This is technically the string version of the GUID and will convert the
// string to object.
//
// CLSIDFromString in Windows API.
func CLSIDFromString(str string) (clsid *GUID, err error) {
var guid GUID
lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))
hr, _, _ := procCLSIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
if hr != 0 {
err = NewError(hr)
}
clsid = &guid
return
}
// StringFromCLSID returns GUID formated string from GUID object.
func StringFromCLSID(clsid *GUID) (str string, err error) {
var p *uint16
hr, _, _ := procStringFromCLSID.Call(uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(&p)))
if hr != 0 {
err = NewError(hr)
}
str = LpOleStrToString(p)
return
}
// IIDFromString returns GUID from program ID.
func IIDFromString(progId string) (clsid *GUID, err error) {
var guid GUID
lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
hr, _, _ := procIIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
if hr != 0 {
err = NewError(hr)
}
clsid = &guid
return
}
// StringFromIID returns GUID formatted string from GUID object.
func StringFromIID(iid *GUID) (str string, err error) {
var p *uint16
hr, _, _ := procStringFromIID.Call(uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&p)))
if hr != 0 {
err = NewError(hr)
}
str = LpOleStrToString(p)
return
}
// CreateInstance of single uninitialized object with GUID.
func CreateInstance(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
if iid == nil {
iid = IID_IUnknown
}
hr, _, _ := procCoCreateInstance.Call(
uintptr(unsafe.Pointer(clsid)),
0,
CLSCTX_SERVER,
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(&unk)))
if hr != 0 {
err = NewError(hr)
}
return
}
// GetActiveObject retrieves pointer to active object.
func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
if iid == nil {
iid = IID_IUnknown
}
hr, _, _ := procGetActiveObject.Call(
uintptr(unsafe.Pointer(clsid)),
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(&unk)))
if hr != 0 {
err = NewError(hr)
}
return
}
type BindOpts struct {
CbStruct uint32
GrfFlags uint32
GrfMode uint32
TickCountDeadline uint32
}
// GetObject retrieves pointer to active object.
func GetObject(programID string, bindOpts *BindOpts, iid *GUID) (unk *IUnknown, err error) {
if bindOpts != nil {
bindOpts.CbStruct = uint32(unsafe.Sizeof(BindOpts{}))
}
if iid == nil {
iid = IID_IUnknown
}
hr, _, _ := procCoGetObject.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(programID))),
uintptr(unsafe.Pointer(bindOpts)),
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(&unk)))
if hr != 0 {
err = NewError(hr)
}
return
}
// VariantInit initializes variant.
func VariantInit(v *VARIANT) (err error) {
hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v)))
if hr != 0 {
err = NewError(hr)
}
return
}
// VariantClear clears value in Variant settings to VT_EMPTY.
func VariantClear(v *VARIANT) (err error) {
hr, _, _ := procVariantClear.Call(uintptr(unsafe.Pointer(v)))
if hr != 0 {
err = NewError(hr)
}
return
}
// SysAllocString allocates memory for string and copies string into memory.
func SysAllocString(v string) (ss *int16) {
pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))
ss = (*int16)(unsafe.Pointer(pss))
return
}
// SysAllocStringLen copies up to length of given string returning pointer.
func SysAllocStringLen(v string) (ss *int16) {
utf16 := utf16.Encode([]rune(v + "\x00"))
ptr := &utf16[0]
pss, _, _ := procSysAllocStringLen.Call(uintptr(unsafe.Pointer(ptr)), uintptr(len(utf16)-1))
ss = (*int16)(unsafe.Pointer(pss))
return
}
// SysFreeString frees string system memory. This must be called with SysAllocString.
func SysFreeString(v *int16) (err error) {
hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v)))
if hr != 0 {
err = NewError(hr)
}
return
}
// SysStringLen is the length of the system allocated string.
func SysStringLen(v *int16) uint32 {
l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v)))
return uint32(l)
}
// CreateStdDispatch provides default IDispatch implementation for IUnknown.
//
// This handles default IDispatch implementation for objects. It haves a few
// limitations with only supporting one language. It will also only return
// default exception codes.
func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (disp *IDispatch, err error) {
hr, _, _ := procCreateStdDispatch.Call(
uintptr(unsafe.Pointer(unk)),
v,
uintptr(unsafe.Pointer(ptinfo)),
uintptr(unsafe.Pointer(&disp)))
if hr != 0 {
err = NewError(hr)
}
return
}
// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
//
// This will not handle the full implementation of the interface.
func CreateDispTypeInfo(idata *INTERFACEDATA) (pptinfo *IUnknown, err error) {
hr, _, _ := procCreateDispTypeInfo.Call(
uintptr(unsafe.Pointer(idata)),
uintptr(GetUserDefaultLCID()),
uintptr(unsafe.Pointer(&pptinfo)))
if hr != 0 {
err = NewError(hr)
}
return
}
// copyMemory moves location of a block of memory.
func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {
procCopyMemory.Call(uintptr(dest), uintptr(src), uintptr(length))
}
// GetUserDefaultLCID retrieves current user default locale.
func GetUserDefaultLCID() (lcid uint32) {
ret, _, _ := procGetUserDefaultLCID.Call()
lcid = uint32(ret)
return
}
// GetMessage in message queue from runtime.
//
// This function appears to block. PeekMessage does not block.
func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) {
r0, _, err := procGetMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax))
ret = int32(r0)
return
}
// DispatchMessage to window procedure.
func DispatchMessage(msg *Msg) (ret int32) {
r0, _, _ := procDispatchMessageW.Call(uintptr(unsafe.Pointer(msg)))
ret = int32(r0)
return
}

174
vendor/github.com/go-ole/go-ole/com_func.go generated vendored Normal file
View File

@@ -0,0 +1,174 @@
// +build !windows
package ole
import (
"time"
"unsafe"
)
// coInitialize initializes COM library on current thread.
//
// MSDN documentation suggests that this function should not be called. Call
// CoInitializeEx() instead. The reason has to do with threading and this
// function is only for single-threaded apartments.
//
// That said, most users of the library have gotten away with just this
// function. If you are experiencing threading issues, then use
// CoInitializeEx().
func coInitialize() error {
return NewError(E_NOTIMPL)
}
// coInitializeEx initializes COM library with concurrency model.
func coInitializeEx(coinit uint32) error {
return NewError(E_NOTIMPL)
}
// CoInitialize initializes COM library on current thread.
//
// MSDN documentation suggests that this function should not be called. Call
// CoInitializeEx() instead. The reason has to do with threading and this
// function is only for single-threaded apartments.
//
// That said, most users of the library have gotten away with just this
// function. If you are experiencing threading issues, then use
// CoInitializeEx().
func CoInitialize(p uintptr) error {
return NewError(E_NOTIMPL)
}
// CoInitializeEx initializes COM library with concurrency model.
func CoInitializeEx(p uintptr, coinit uint32) error {
return NewError(E_NOTIMPL)
}
// CoUninitialize uninitializes COM Library.
func CoUninitialize() {}
// CoTaskMemFree frees memory pointer.
func CoTaskMemFree(memptr uintptr) {}
// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
//
// The Programmatic Identifier must be registered, because it will be looked up
// in the Windows Registry. The registry entry has the following keys: CLSID,
// Insertable, Protocol and Shell
// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
//
// programID identifies the class id with less precision and is not guaranteed
// to be unique. These are usually found in the registry under
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
// "Program.Component.Version" with version being optional.
//
// CLSIDFromProgID in Windows API.
func CLSIDFromProgID(progId string) (*GUID, error) {
return nil, NewError(E_NOTIMPL)
}
// CLSIDFromString retrieves Class ID from string representation.
//
// This is technically the string version of the GUID and will convert the
// string to object.
//
// CLSIDFromString in Windows API.
func CLSIDFromString(str string) (*GUID, error) {
return nil, NewError(E_NOTIMPL)
}
// StringFromCLSID returns GUID formated string from GUID object.
func StringFromCLSID(clsid *GUID) (string, error) {
return "", NewError(E_NOTIMPL)
}
// IIDFromString returns GUID from program ID.
func IIDFromString(progId string) (*GUID, error) {
return nil, NewError(E_NOTIMPL)
}
// StringFromIID returns GUID formatted string from GUID object.
func StringFromIID(iid *GUID) (string, error) {
return "", NewError(E_NOTIMPL)
}
// CreateInstance of single uninitialized object with GUID.
func CreateInstance(clsid *GUID, iid *GUID) (*IUnknown, error) {
return nil, NewError(E_NOTIMPL)
}
// GetActiveObject retrieves pointer to active object.
func GetActiveObject(clsid *GUID, iid *GUID) (*IUnknown, error) {
return nil, NewError(E_NOTIMPL)
}
// VariantInit initializes variant.
func VariantInit(v *VARIANT) error {
return NewError(E_NOTIMPL)
}
// VariantClear clears value in Variant settings to VT_EMPTY.
func VariantClear(v *VARIANT) error {
return NewError(E_NOTIMPL)
}
// SysAllocString allocates memory for string and copies string into memory.
func SysAllocString(v string) *int16 {
u := int16(0)
return &u
}
// SysAllocStringLen copies up to length of given string returning pointer.
func SysAllocStringLen(v string) *int16 {
u := int16(0)
return &u
}
// SysFreeString frees string system memory. This must be called with SysAllocString.
func SysFreeString(v *int16) error {
return NewError(E_NOTIMPL)
}
// SysStringLen is the length of the system allocated string.
func SysStringLen(v *int16) uint32 {
return uint32(0)
}
// CreateStdDispatch provides default IDispatch implementation for IUnknown.
//
// This handles default IDispatch implementation for objects. It haves a few
// limitations with only supporting one language. It will also only return
// default exception codes.
func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (*IDispatch, error) {
return nil, NewError(E_NOTIMPL)
}
// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
//
// This will not handle the full implementation of the interface.
func CreateDispTypeInfo(idata *INTERFACEDATA) (*IUnknown, error) {
return nil, NewError(E_NOTIMPL)
}
// copyMemory moves location of a block of memory.
func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {}
// GetUserDefaultLCID retrieves current user default locale.
func GetUserDefaultLCID() uint32 {
return uint32(0)
}
// GetMessage in message queue from runtime.
//
// This function appears to block. PeekMessage does not block.
func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (int32, error) {
return int32(0), NewError(E_NOTIMPL)
}
// DispatchMessage to window procedure.
func DispatchMessage(msg *Msg) int32 {
return int32(0)
}
func GetVariantDate(value uint64) (time.Time, error) {
return time.Now(), NewError(E_NOTIMPL)
}

192
vendor/github.com/go-ole/go-ole/connect.go generated vendored Normal file
View File

@@ -0,0 +1,192 @@
package ole
// Connection contains IUnknown for fluent interface interaction.
//
// Deprecated. Use oleutil package instead.
type Connection struct {
Object *IUnknown // Access COM
}
// Initialize COM.
func (*Connection) Initialize() (err error) {
return coInitialize()
}
// Uninitialize COM.
func (*Connection) Uninitialize() {
CoUninitialize()
}
// Create IUnknown object based first on ProgId and then from String.
func (c *Connection) Create(progId string) (err error) {
var clsid *GUID
clsid, err = CLSIDFromProgID(progId)
if err != nil {
clsid, err = CLSIDFromString(progId)
if err != nil {
return
}
}
unknown, err := CreateInstance(clsid, IID_IUnknown)
if err != nil {
return
}
c.Object = unknown
return
}
// Release IUnknown object.
func (c *Connection) Release() {
c.Object.Release()
}
// Load COM object from list of programIDs or strings.
func (c *Connection) Load(names ...string) (errors []error) {
var tempErrors []error = make([]error, len(names))
var numErrors int = 0
for _, name := range names {
err := c.Create(name)
if err != nil {
tempErrors = append(tempErrors, err)
numErrors += 1
continue
}
break
}
copy(errors, tempErrors[0:numErrors])
return
}
// Dispatch returns Dispatch object.
func (c *Connection) Dispatch() (object *Dispatch, err error) {
dispatch, err := c.Object.QueryInterface(IID_IDispatch)
if err != nil {
return
}
object = &Dispatch{dispatch}
return
}
// Dispatch stores IDispatch object.
type Dispatch struct {
Object *IDispatch // Dispatch object.
}
// Call method on IDispatch with parameters.
func (d *Dispatch) Call(method string, params ...interface{}) (result *VARIANT, err error) {
id, err := d.GetId(method)
if err != nil {
return
}
result, err = d.Invoke(id, DISPATCH_METHOD, params)
return
}
// MustCall method on IDispatch with parameters.
func (d *Dispatch) MustCall(method string, params ...interface{}) (result *VARIANT) {
id, err := d.GetId(method)
if err != nil {
panic(err)
}
result, err = d.Invoke(id, DISPATCH_METHOD, params)
if err != nil {
panic(err)
}
return
}
// Get property on IDispatch with parameters.
func (d *Dispatch) Get(name string, params ...interface{}) (result *VARIANT, err error) {
id, err := d.GetId(name)
if err != nil {
return
}
result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params)
return
}
// MustGet property on IDispatch with parameters.
func (d *Dispatch) MustGet(name string, params ...interface{}) (result *VARIANT) {
id, err := d.GetId(name)
if err != nil {
panic(err)
}
result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params)
if err != nil {
panic(err)
}
return
}
// Set property on IDispatch with parameters.
func (d *Dispatch) Set(name string, params ...interface{}) (result *VARIANT, err error) {
id, err := d.GetId(name)
if err != nil {
return
}
result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params)
return
}
// MustSet property on IDispatch with parameters.
func (d *Dispatch) MustSet(name string, params ...interface{}) (result *VARIANT) {
id, err := d.GetId(name)
if err != nil {
panic(err)
}
result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params)
if err != nil {
panic(err)
}
return
}
// GetId retrieves ID of name on IDispatch.
func (d *Dispatch) GetId(name string) (id int32, err error) {
var dispid []int32
dispid, err = d.Object.GetIDsOfName([]string{name})
if err != nil {
return
}
id = dispid[0]
return
}
// GetIds retrieves all IDs of names on IDispatch.
func (d *Dispatch) GetIds(names ...string) (dispid []int32, err error) {
dispid, err = d.Object.GetIDsOfName(names)
return
}
// Invoke IDispatch on DisplayID of dispatch type with parameters.
//
// There have been problems where if send cascading params..., it would error
// out because the parameters would be empty.
func (d *Dispatch) Invoke(id int32, dispatch int16, params []interface{}) (result *VARIANT, err error) {
if len(params) < 1 {
result, err = d.Object.Invoke(id, dispatch)
} else {
result, err = d.Object.Invoke(id, dispatch, params...)
}
return
}
// Release IDispatch object.
func (d *Dispatch) Release() {
d.Object.Release()
}
// Connect initializes COM and attempts to load IUnknown based on given names.
func Connect(names ...string) (connection *Connection) {
connection.Initialize()
connection.Load(names...)
return
}

153
vendor/github.com/go-ole/go-ole/constants.go generated vendored Normal file
View File

@@ -0,0 +1,153 @@
package ole
const (
CLSCTX_INPROC_SERVER = 1
CLSCTX_INPROC_HANDLER = 2
CLSCTX_LOCAL_SERVER = 4
CLSCTX_INPROC_SERVER16 = 8
CLSCTX_REMOTE_SERVER = 16
CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER
CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER
CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
)
const (
COINIT_APARTMENTTHREADED = 0x2
COINIT_MULTITHREADED = 0x0
COINIT_DISABLE_OLE1DDE = 0x4
COINIT_SPEED_OVER_MEMORY = 0x8
)
const (
DISPATCH_METHOD = 1
DISPATCH_PROPERTYGET = 2
DISPATCH_PROPERTYPUT = 4
DISPATCH_PROPERTYPUTREF = 8
)
const (
S_OK = 0x00000000
E_UNEXPECTED = 0x8000FFFF
E_NOTIMPL = 0x80004001
E_OUTOFMEMORY = 0x8007000E
E_INVALIDARG = 0x80070057
E_NOINTERFACE = 0x80004002
E_POINTER = 0x80004003
E_HANDLE = 0x80070006
E_ABORT = 0x80004004
E_FAIL = 0x80004005
E_ACCESSDENIED = 0x80070005
E_PENDING = 0x8000000A
CO_E_CLASSSTRING = 0x800401F3
)
const (
CC_FASTCALL = iota
CC_CDECL
CC_MSCPASCAL
CC_PASCAL = CC_MSCPASCAL
CC_MACPASCAL
CC_STDCALL
CC_FPFASTCALL
CC_SYSCALL
CC_MPWCDECL
CC_MPWPASCAL
CC_MAX = CC_MPWPASCAL
)
type VT uint16
const (
VT_EMPTY VT = 0x0
VT_NULL VT = 0x1
VT_I2 VT = 0x2
VT_I4 VT = 0x3
VT_R4 VT = 0x4
VT_R8 VT = 0x5
VT_CY VT = 0x6
VT_DATE VT = 0x7
VT_BSTR VT = 0x8
VT_DISPATCH VT = 0x9
VT_ERROR VT = 0xa
VT_BOOL VT = 0xb
VT_VARIANT VT = 0xc
VT_UNKNOWN VT = 0xd
VT_DECIMAL VT = 0xe
VT_I1 VT = 0x10
VT_UI1 VT = 0x11
VT_UI2 VT = 0x12
VT_UI4 VT = 0x13
VT_I8 VT = 0x14
VT_UI8 VT = 0x15
VT_INT VT = 0x16
VT_UINT VT = 0x17
VT_VOID VT = 0x18
VT_HRESULT VT = 0x19
VT_PTR VT = 0x1a
VT_SAFEARRAY VT = 0x1b
VT_CARRAY VT = 0x1c
VT_USERDEFINED VT = 0x1d
VT_LPSTR VT = 0x1e
VT_LPWSTR VT = 0x1f
VT_RECORD VT = 0x24
VT_INT_PTR VT = 0x25
VT_UINT_PTR VT = 0x26
VT_FILETIME VT = 0x40
VT_BLOB VT = 0x41
VT_STREAM VT = 0x42
VT_STORAGE VT = 0x43
VT_STREAMED_OBJECT VT = 0x44
VT_STORED_OBJECT VT = 0x45
VT_BLOB_OBJECT VT = 0x46
VT_CF VT = 0x47
VT_CLSID VT = 0x48
VT_BSTR_BLOB VT = 0xfff
VT_VECTOR VT = 0x1000
VT_ARRAY VT = 0x2000
VT_BYREF VT = 0x4000
VT_RESERVED VT = 0x8000
VT_ILLEGAL VT = 0xffff
VT_ILLEGALMASKED VT = 0xfff
VT_TYPEMASK VT = 0xfff
)
const (
DISPID_UNKNOWN = -1
DISPID_VALUE = 0
DISPID_PROPERTYPUT = -3
DISPID_NEWENUM = -4
DISPID_EVALUATE = -5
DISPID_CONSTRUCTOR = -6
DISPID_DESTRUCTOR = -7
DISPID_COLLECT = -8
)
const (
TKIND_ENUM = 1
TKIND_RECORD = 2
TKIND_MODULE = 3
TKIND_INTERFACE = 4
TKIND_DISPATCH = 5
TKIND_COCLASS = 6
TKIND_ALIAS = 7
TKIND_UNION = 8
TKIND_MAX = 9
)
// Safe Array Feature Flags
const (
FADF_AUTO = 0x0001
FADF_STATIC = 0x0002
FADF_EMBEDDED = 0x0004
FADF_FIXEDSIZE = 0x0010
FADF_RECORD = 0x0020
FADF_HAVEIID = 0x0040
FADF_HAVEVARTYPE = 0x0080
FADF_BSTR = 0x0100
FADF_UNKNOWN = 0x0200
FADF_DISPATCH = 0x0400
FADF_VARIANT = 0x0800
FADF_RESERVED = 0xF008
)

51
vendor/github.com/go-ole/go-ole/error.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
package ole
// OleError stores COM errors.
type OleError struct {
hr uintptr
description string
subError error
}
// NewError creates new error with HResult.
func NewError(hr uintptr) *OleError {
return &OleError{hr: hr}
}
// NewErrorWithDescription creates new COM error with HResult and description.
func NewErrorWithDescription(hr uintptr, description string) *OleError {
return &OleError{hr: hr, description: description}
}
// NewErrorWithSubError creates new COM error with parent error.
func NewErrorWithSubError(hr uintptr, description string, err error) *OleError {
return &OleError{hr: hr, description: description, subError: err}
}
// Code is the HResult.
func (v *OleError) Code() uintptr {
return uintptr(v.hr)
}
// String description, either manually set or format message with error code.
func (v *OleError) String() string {
if v.description != "" {
return errstr(int(v.hr)) + " (" + v.description + ")"
}
return errstr(int(v.hr))
}
// Error implements error interface.
func (v *OleError) Error() string {
return v.String()
}
// Description retrieves error summary, if there is one.
func (v *OleError) Description() string {
return v.description
}
// SubError returns parent error, if there is one.
func (v *OleError) SubError() error {
return v.subError
}

8
vendor/github.com/go-ole/go-ole/error_func.go generated vendored Normal file
View File

@@ -0,0 +1,8 @@
// +build !windows
package ole
// errstr converts error code to string.
func errstr(errno int) string {
return ""
}

24
vendor/github.com/go-ole/go-ole/error_windows.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
// +build windows
package ole
import (
"fmt"
"syscall"
"unicode/utf16"
)
// errstr converts error code to string.
func errstr(errno int) string {
// ask windows for the remaining errors
var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS
b := make([]uint16, 300)
n, err := syscall.FormatMessage(flags, 0, uint32(errno), 0, b, nil)
if err != nil {
return fmt.Sprintf("error %d (FormatMessage failed with: %v)", errno, err)
}
// trim terminating \r and \n
for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- {
}
return string(utf16.Decode(b[:n]))
}

284
vendor/github.com/go-ole/go-ole/guid.go generated vendored Normal file
View File

@@ -0,0 +1,284 @@
package ole
var (
// IID_NULL is null Interface ID, used when no other Interface ID is known.
IID_NULL = NewGUID("{00000000-0000-0000-0000-000000000000}")
// IID_IUnknown is for IUnknown interfaces.
IID_IUnknown = NewGUID("{00000000-0000-0000-C000-000000000046}")
// IID_IDispatch is for IDispatch interfaces.
IID_IDispatch = NewGUID("{00020400-0000-0000-C000-000000000046}")
// IID_IEnumVariant is for IEnumVariant interfaces
IID_IEnumVariant = NewGUID("{00020404-0000-0000-C000-000000000046}")
// IID_IConnectionPointContainer is for IConnectionPointContainer interfaces.
IID_IConnectionPointContainer = NewGUID("{B196B284-BAB4-101A-B69C-00AA00341D07}")
// IID_IConnectionPoint is for IConnectionPoint interfaces.
IID_IConnectionPoint = NewGUID("{B196B286-BAB4-101A-B69C-00AA00341D07}")
// IID_IInspectable is for IInspectable interfaces.
IID_IInspectable = NewGUID("{AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}")
// IID_IProvideClassInfo is for IProvideClassInfo interfaces.
IID_IProvideClassInfo = NewGUID("{B196B283-BAB4-101A-B69C-00AA00341D07}")
)
// These are for testing and not part of any library.
var (
// IID_ICOMTestString is for ICOMTestString interfaces.
//
// {E0133EB4-C36F-469A-9D3D-C66B84BE19ED}
IID_ICOMTestString = NewGUID("{E0133EB4-C36F-469A-9D3D-C66B84BE19ED}")
// IID_ICOMTestInt8 is for ICOMTestInt8 interfaces.
//
// {BEB06610-EB84-4155-AF58-E2BFF53680B4}
IID_ICOMTestInt8 = NewGUID("{BEB06610-EB84-4155-AF58-E2BFF53680B4}")
// IID_ICOMTestInt16 is for ICOMTestInt16 interfaces.
//
// {DAA3F9FA-761E-4976-A860-8364CE55F6FC}
IID_ICOMTestInt16 = NewGUID("{DAA3F9FA-761E-4976-A860-8364CE55F6FC}")
// IID_ICOMTestInt32 is for ICOMTestInt32 interfaces.
//
// {E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}
IID_ICOMTestInt32 = NewGUID("{E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}")
// IID_ICOMTestInt64 is for ICOMTestInt64 interfaces.
//
// {8D437CBC-B3ED-485C-BC32-C336432A1623}
IID_ICOMTestInt64 = NewGUID("{8D437CBC-B3ED-485C-BC32-C336432A1623}")
// IID_ICOMTestFloat is for ICOMTestFloat interfaces.
//
// {BF1ED004-EA02-456A-AA55-2AC8AC6B054C}
IID_ICOMTestFloat = NewGUID("{BF1ED004-EA02-456A-AA55-2AC8AC6B054C}")
// IID_ICOMTestDouble is for ICOMTestDouble interfaces.
//
// {BF908A81-8687-4E93-999F-D86FAB284BA0}
IID_ICOMTestDouble = NewGUID("{BF908A81-8687-4E93-999F-D86FAB284BA0}")
// IID_ICOMTestBoolean is for ICOMTestBoolean interfaces.
//
// {D530E7A6-4EE8-40D1-8931-3D63B8605010}
IID_ICOMTestBoolean = NewGUID("{D530E7A6-4EE8-40D1-8931-3D63B8605010}")
// IID_ICOMEchoTestObject is for ICOMEchoTestObject interfaces.
//
// {6485B1EF-D780-4834-A4FE-1EBB51746CA3}
IID_ICOMEchoTestObject = NewGUID("{6485B1EF-D780-4834-A4FE-1EBB51746CA3}")
// IID_ICOMTestTypes is for ICOMTestTypes interfaces.
//
// {CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}
IID_ICOMTestTypes = NewGUID("{CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}")
// CLSID_COMEchoTestObject is for COMEchoTestObject class.
//
// {3C24506A-AE9E-4D50-9157-EF317281F1B0}
CLSID_COMEchoTestObject = NewGUID("{3C24506A-AE9E-4D50-9157-EF317281F1B0}")
// CLSID_COMTestScalarClass is for COMTestScalarClass class.
//
// {865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}
CLSID_COMTestScalarClass = NewGUID("{865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}")
)
const hextable = "0123456789ABCDEF"
const emptyGUID = "{00000000-0000-0000-0000-000000000000}"
// GUID is Windows API specific GUID type.
//
// This exists to match Windows GUID type for direct passing for COM.
// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx.
type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}
// NewGUID converts the given string into a globally unique identifier that is
// compliant with the Windows API.
//
// The supplied string may be in any of these formats:
//
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
//
// The conversion of the supplied string is not case-sensitive.
func NewGUID(guid string) *GUID {
d := []byte(guid)
var d1, d2, d3, d4a, d4b []byte
switch len(d) {
case 38:
if d[0] != '{' || d[37] != '}' {
return nil
}
d = d[1:37]
fallthrough
case 36:
if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' {
return nil
}
d1 = d[0:8]
d2 = d[9:13]
d3 = d[14:18]
d4a = d[19:23]
d4b = d[24:36]
case 32:
d1 = d[0:8]
d2 = d[8:12]
d3 = d[12:16]
d4a = d[16:20]
d4b = d[20:32]
default:
return nil
}
var g GUID
var ok1, ok2, ok3, ok4 bool
g.Data1, ok1 = decodeHexUint32(d1)
g.Data2, ok2 = decodeHexUint16(d2)
g.Data3, ok3 = decodeHexUint16(d3)
g.Data4, ok4 = decodeHexByte64(d4a, d4b)
if ok1 && ok2 && ok3 && ok4 {
return &g
}
return nil
}
func decodeHexUint32(src []byte) (value uint32, ok bool) {
var b1, b2, b3, b4 byte
var ok1, ok2, ok3, ok4 bool
b1, ok1 = decodeHexByte(src[0], src[1])
b2, ok2 = decodeHexByte(src[2], src[3])
b3, ok3 = decodeHexByte(src[4], src[5])
b4, ok4 = decodeHexByte(src[6], src[7])
value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4)
ok = ok1 && ok2 && ok3 && ok4
return
}
func decodeHexUint16(src []byte) (value uint16, ok bool) {
var b1, b2 byte
var ok1, ok2 bool
b1, ok1 = decodeHexByte(src[0], src[1])
b2, ok2 = decodeHexByte(src[2], src[3])
value = (uint16(b1) << 8) | uint16(b2)
ok = ok1 && ok2
return
}
func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) {
var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool
value[0], ok1 = decodeHexByte(s1[0], s1[1])
value[1], ok2 = decodeHexByte(s1[2], s1[3])
value[2], ok3 = decodeHexByte(s2[0], s2[1])
value[3], ok4 = decodeHexByte(s2[2], s2[3])
value[4], ok5 = decodeHexByte(s2[4], s2[5])
value[5], ok6 = decodeHexByte(s2[6], s2[7])
value[6], ok7 = decodeHexByte(s2[8], s2[9])
value[7], ok8 = decodeHexByte(s2[10], s2[11])
ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8
return
}
func decodeHexByte(c1, c2 byte) (value byte, ok bool) {
var n1, n2 byte
var ok1, ok2 bool
n1, ok1 = decodeHexChar(c1)
n2, ok2 = decodeHexChar(c2)
value = (n1 << 4) | n2
ok = ok1 && ok2
return
}
func decodeHexChar(c byte) (byte, bool) {
switch {
case '0' <= c && c <= '9':
return c - '0', true
case 'a' <= c && c <= 'f':
return c - 'a' + 10, true
case 'A' <= c && c <= 'F':
return c - 'A' + 10, true
}
return 0, false
}
// String converts the GUID to string form. It will adhere to this pattern:
//
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
//
// If the GUID is nil, the string representation of an empty GUID is returned:
//
// {00000000-0000-0000-0000-000000000000}
func (guid *GUID) String() string {
if guid == nil {
return emptyGUID
}
var c [38]byte
c[0] = '{'
putUint32Hex(c[1:9], guid.Data1)
c[9] = '-'
putUint16Hex(c[10:14], guid.Data2)
c[14] = '-'
putUint16Hex(c[15:19], guid.Data3)
c[19] = '-'
putByteHex(c[20:24], guid.Data4[0:2])
c[24] = '-'
putByteHex(c[25:37], guid.Data4[2:8])
c[37] = '}'
return string(c[:])
}
func putUint32Hex(b []byte, v uint32) {
b[0] = hextable[byte(v>>24)>>4]
b[1] = hextable[byte(v>>24)&0x0f]
b[2] = hextable[byte(v>>16)>>4]
b[3] = hextable[byte(v>>16)&0x0f]
b[4] = hextable[byte(v>>8)>>4]
b[5] = hextable[byte(v>>8)&0x0f]
b[6] = hextable[byte(v)>>4]
b[7] = hextable[byte(v)&0x0f]
}
func putUint16Hex(b []byte, v uint16) {
b[0] = hextable[byte(v>>8)>>4]
b[1] = hextable[byte(v>>8)&0x0f]
b[2] = hextable[byte(v)>>4]
b[3] = hextable[byte(v)&0x0f]
}
func putByteHex(dst, src []byte) {
for i := 0; i < len(src); i++ {
dst[i*2] = hextable[src[i]>>4]
dst[i*2+1] = hextable[src[i]&0x0f]
}
}
// IsEqualGUID compares two GUID.
//
// Not constant time comparison.
func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool {
return guid1.Data1 == guid2.Data1 &&
guid1.Data2 == guid2.Data2 &&
guid1.Data3 == guid2.Data3 &&
guid1.Data4[0] == guid2.Data4[0] &&
guid1.Data4[1] == guid2.Data4[1] &&
guid1.Data4[2] == guid2.Data4[2] &&
guid1.Data4[3] == guid2.Data4[3] &&
guid1.Data4[4] == guid2.Data4[4] &&
guid1.Data4[5] == guid2.Data4[5] &&
guid1.Data4[6] == guid2.Data4[6] &&
guid1.Data4[7] == guid2.Data4[7]
}

20
vendor/github.com/go-ole/go-ole/iconnectionpoint.go generated vendored Normal file
View File

@@ -0,0 +1,20 @@
package ole
import "unsafe"
type IConnectionPoint struct {
IUnknown
}
type IConnectionPointVtbl struct {
IUnknownVtbl
GetConnectionInterface uintptr
GetConnectionPointContainer uintptr
Advise uintptr
Unadvise uintptr
EnumConnections uintptr
}
func (v *IConnectionPoint) VTable() *IConnectionPointVtbl {
return (*IConnectionPointVtbl)(unsafe.Pointer(v.RawVTable))
}

View File

@@ -0,0 +1,21 @@
// +build !windows
package ole
import "unsafe"
func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 {
return int32(0)
}
func (v *IConnectionPoint) Advise(unknown *IUnknown) (uint32, error) {
return uint32(0), NewError(E_NOTIMPL)
}
func (v *IConnectionPoint) Unadvise(cookie uint32) error {
return NewError(E_NOTIMPL)
}
func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) (err error) {
return NewError(E_NOTIMPL)
}

View File

@@ -0,0 +1,43 @@
// +build windows
package ole
import (
"syscall"
"unsafe"
)
func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 {
// XXX: This doesn't look like it does what it's supposed to
return release((*IUnknown)(unsafe.Pointer(v)))
}
func (v *IConnectionPoint) Advise(unknown *IUnknown) (cookie uint32, err error) {
hr, _, _ := syscall.Syscall(
v.VTable().Advise,
3,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(unknown)),
uintptr(unsafe.Pointer(&cookie)))
if hr != 0 {
err = NewError(hr)
}
return
}
func (v *IConnectionPoint) Unadvise(cookie uint32) (err error) {
hr, _, _ := syscall.Syscall(
v.VTable().Unadvise,
2,
uintptr(unsafe.Pointer(v)),
uintptr(cookie),
0)
if hr != 0 {
err = NewError(hr)
}
return
}
func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) error {
return NewError(E_NOTIMPL)
}

View File

@@ -0,0 +1,17 @@
package ole
import "unsafe"
type IConnectionPointContainer struct {
IUnknown
}
type IConnectionPointContainerVtbl struct {
IUnknownVtbl
EnumConnectionPoints uintptr
FindConnectionPoint uintptr
}
func (v *IConnectionPointContainer) VTable() *IConnectionPointContainerVtbl {
return (*IConnectionPointContainerVtbl)(unsafe.Pointer(v.RawVTable))
}

View File

@@ -0,0 +1,11 @@
// +build !windows
package ole
func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error {
return NewError(E_NOTIMPL)
}
func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) error {
return NewError(E_NOTIMPL)
}

View File

@@ -0,0 +1,25 @@
// +build windows
package ole
import (
"syscall"
"unsafe"
)
func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error {
return NewError(E_NOTIMPL)
}
func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) (err error) {
hr, _, _ := syscall.Syscall(
v.VTable().FindConnectionPoint,
3,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(point)))
if hr != 0 {
err = NewError(hr)
}
return
}

94
vendor/github.com/go-ole/go-ole/idispatch.go generated vendored Normal file
View File

@@ -0,0 +1,94 @@
package ole
import "unsafe"
type IDispatch struct {
IUnknown
}
type IDispatchVtbl struct {
IUnknownVtbl
GetTypeInfoCount uintptr
GetTypeInfo uintptr
GetIDsOfNames uintptr
Invoke uintptr
}
func (v *IDispatch) VTable() *IDispatchVtbl {
return (*IDispatchVtbl)(unsafe.Pointer(v.RawVTable))
}
func (v *IDispatch) GetIDsOfName(names []string) (dispid []int32, err error) {
dispid, err = getIDsOfName(v, names)
return
}
func (v *IDispatch) Invoke(dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) {
result, err = invoke(v, dispid, dispatch, params...)
return
}
func (v *IDispatch) GetTypeInfoCount() (c uint32, err error) {
c, err = getTypeInfoCount(v)
return
}
func (v *IDispatch) GetTypeInfo() (tinfo *ITypeInfo, err error) {
tinfo, err = getTypeInfo(v)
return
}
// GetSingleIDOfName is a helper that returns single display ID for IDispatch name.
//
// This replaces the common pattern of attempting to get a single name from the list of available
// IDs. It gives the first ID, if it is available.
func (v *IDispatch) GetSingleIDOfName(name string) (displayID int32, err error) {
var displayIDs []int32
displayIDs, err = v.GetIDsOfName([]string{name})
if err != nil {
return
}
displayID = displayIDs[0]
return
}
// InvokeWithOptionalArgs accepts arguments as an array, works like Invoke.
//
// Accepts name and will attempt to retrieve Display ID to pass to Invoke.
//
// Passing params as an array is a workaround that could be fixed in later versions of Go that
// prevent passing empty params. During testing it was discovered that this is an acceptable way of
// getting around not being able to pass params normally.
func (v *IDispatch) InvokeWithOptionalArgs(name string, dispatch int16, params []interface{}) (result *VARIANT, err error) {
displayID, err := v.GetSingleIDOfName(name)
if err != nil {
return
}
if len(params) < 1 {
result, err = v.Invoke(displayID, dispatch)
} else {
result, err = v.Invoke(displayID, dispatch, params...)
}
return
}
// CallMethod invokes named function with arguments on object.
func (v *IDispatch) CallMethod(name string, params ...interface{}) (*VARIANT, error) {
return v.InvokeWithOptionalArgs(name, DISPATCH_METHOD, params)
}
// GetProperty retrieves the property with the name with the ability to pass arguments.
//
// Most of the time you will not need to pass arguments as most objects do not allow for this
// feature. Or at least, should not allow for this feature. Some servers don't follow best practices
// and this is provided for those edge cases.
func (v *IDispatch) GetProperty(name string, params ...interface{}) (*VARIANT, error) {
return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYGET, params)
}
// PutProperty attempts to mutate a property in the object.
func (v *IDispatch) PutProperty(name string, params ...interface{}) (*VARIANT, error) {
return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYPUT, params)
}

19
vendor/github.com/go-ole/go-ole/idispatch_func.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
// +build !windows
package ole
func getIDsOfName(disp *IDispatch, names []string) ([]int32, error) {
return []int32{}, NewError(E_NOTIMPL)
}
func getTypeInfoCount(disp *IDispatch) (uint32, error) {
return uint32(0), NewError(E_NOTIMPL)
}
func getTypeInfo(disp *IDispatch) (*ITypeInfo, error) {
return nil, NewError(E_NOTIMPL)
}
func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (*VARIANT, error) {
return nil, NewError(E_NOTIMPL)
}

203
vendor/github.com/go-ole/go-ole/idispatch_windows.go generated vendored Normal file
View File

@@ -0,0 +1,203 @@
//go:build windows
// +build windows
package ole
import (
"math/big"
"syscall"
"time"
"unsafe"
)
func getIDsOfName(disp *IDispatch, names []string) (dispid []int32, err error) {
wnames := make([]*uint16, len(names))
for i := 0; i < len(names); i++ {
wnames[i] = syscall.StringToUTF16Ptr(names[i])
}
dispid = make([]int32, len(names))
namelen := uint32(len(names))
hr, _, _ := syscall.Syscall6(
disp.VTable().GetIDsOfNames,
6,
uintptr(unsafe.Pointer(disp)),
uintptr(unsafe.Pointer(IID_NULL)),
uintptr(unsafe.Pointer(&wnames[0])),
uintptr(namelen),
uintptr(GetUserDefaultLCID()),
uintptr(unsafe.Pointer(&dispid[0])))
if hr != 0 {
err = NewError(hr)
}
return
}
func getTypeInfoCount(disp *IDispatch) (c uint32, err error) {
hr, _, _ := syscall.Syscall(
disp.VTable().GetTypeInfoCount,
2,
uintptr(unsafe.Pointer(disp)),
uintptr(unsafe.Pointer(&c)),
0)
if hr != 0 {
err = NewError(hr)
}
return
}
func getTypeInfo(disp *IDispatch) (tinfo *ITypeInfo, err error) {
hr, _, _ := syscall.Syscall(
disp.VTable().GetTypeInfo,
3,
uintptr(unsafe.Pointer(disp)),
uintptr(GetUserDefaultLCID()),
uintptr(unsafe.Pointer(&tinfo)))
if hr != 0 {
err = NewError(hr)
}
return
}
func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) {
var dispparams DISPPARAMS
if dispatch&DISPATCH_PROPERTYPUT != 0 {
dispnames := [1]int32{DISPID_PROPERTYPUT}
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
dispparams.cNamedArgs = 1
} else if dispatch&DISPATCH_PROPERTYPUTREF != 0 {
dispnames := [1]int32{DISPID_PROPERTYPUT}
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
dispparams.cNamedArgs = 1
}
var vargs []VARIANT
if len(params) > 0 {
vargs = make([]VARIANT, len(params))
for i, v := range params {
//n := len(params)-i-1
n := len(params) - i - 1
VariantInit(&vargs[n])
switch vv := v.(type) {
case bool:
if vv {
vargs[n] = NewVariant(VT_BOOL, 0xffff)
} else {
vargs[n] = NewVariant(VT_BOOL, 0)
}
case *bool:
vargs[n] = NewVariant(VT_BOOL|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*bool)))))
case uint8:
vargs[n] = NewVariant(VT_I1, int64(v.(uint8)))
case *uint8:
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8)))))
case int8:
vargs[n] = NewVariant(VT_I1, int64(v.(int8)))
case *int8:
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int8)))))
case int16:
vargs[n] = NewVariant(VT_I2, int64(v.(int16)))
case *int16:
vargs[n] = NewVariant(VT_I2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int16)))))
case uint16:
vargs[n] = NewVariant(VT_UI2, int64(v.(uint16)))
case *uint16:
vargs[n] = NewVariant(VT_UI2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint16)))))
case int32:
vargs[n] = NewVariant(VT_I4, int64(v.(int32)))
case *int32:
vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int32)))))
case uint32:
vargs[n] = NewVariant(VT_UI4, int64(v.(uint32)))
case *uint32:
vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint32)))))
case int64:
vargs[n] = NewVariant(VT_I8, int64(v.(int64)))
case *int64:
vargs[n] = NewVariant(VT_I8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int64)))))
case uint64:
vargs[n] = NewVariant(VT_UI8, int64(uintptr(v.(uint64))))
case *uint64:
vargs[n] = NewVariant(VT_UI8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint64)))))
case int:
vargs[n] = NewVariant(VT_I4, int64(v.(int)))
case *int:
vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int)))))
case uint:
vargs[n] = NewVariant(VT_UI4, int64(v.(uint)))
case *uint:
vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint)))))
case float32:
vargs[n] = NewVariant(VT_R4, *(*int64)(unsafe.Pointer(&vv)))
case *float32:
vargs[n] = NewVariant(VT_R4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float32)))))
case float64:
vargs[n] = NewVariant(VT_R8, *(*int64)(unsafe.Pointer(&vv)))
case *float64:
vargs[n] = NewVariant(VT_R8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float64)))))
case *big.Int:
vargs[n] = NewVariant(VT_DECIMAL, v.(*big.Int).Int64())
case string:
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(v.(string))))))
case *string:
vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*string)))))
case time.Time:
s := vv.Format("2006-01-02 15:04:05")
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(s)))))
case *time.Time:
s := vv.Format("2006-01-02 15:04:05")
vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(&s))))
case *IDispatch:
vargs[n] = NewVariant(VT_DISPATCH, int64(uintptr(unsafe.Pointer(v.(*IDispatch)))))
case **IDispatch:
vargs[n] = NewVariant(VT_DISPATCH|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(**IDispatch)))))
case nil:
vargs[n] = NewVariant(VT_NULL, 0)
case *VARIANT:
vargs[n] = NewVariant(VT_VARIANT|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*VARIANT)))))
case []byte:
safeByteArray := safeArrayFromByteSlice(v.([]byte))
vargs[n] = NewVariant(VT_ARRAY|VT_UI1, int64(uintptr(unsafe.Pointer(safeByteArray))))
defer VariantClear(&vargs[n])
case []string:
safeByteArray := safeArrayFromStringSlice(v.([]string))
vargs[n] = NewVariant(VT_ARRAY|VT_BSTR, int64(uintptr(unsafe.Pointer(safeByteArray))))
defer VariantClear(&vargs[n])
default:
panic("unknown type")
}
}
dispparams.rgvarg = uintptr(unsafe.Pointer(&vargs[0]))
dispparams.cArgs = uint32(len(params))
}
result = new(VARIANT)
var excepInfo EXCEPINFO
VariantInit(result)
hr, _, _ := syscall.Syscall9(
disp.VTable().Invoke,
9,
uintptr(unsafe.Pointer(disp)),
uintptr(dispid),
uintptr(unsafe.Pointer(IID_NULL)),
uintptr(GetUserDefaultLCID()),
uintptr(dispatch),
uintptr(unsafe.Pointer(&dispparams)),
uintptr(unsafe.Pointer(result)),
uintptr(unsafe.Pointer(&excepInfo)),
0)
if hr != 0 {
excepInfo.renderStrings()
excepInfo.Clear()
err = NewErrorWithSubError(hr, excepInfo.description, excepInfo)
}
for i, varg := range vargs {
n := len(params) - i - 1
if varg.VT == VT_BSTR && varg.Val != 0 {
SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val)))))
}
if varg.VT == (VT_BSTR|VT_BYREF) && varg.Val != 0 {
*(params[n].(*string)) = LpOleStrToString(*(**uint16)(unsafe.Pointer(uintptr(varg.Val))))
}
}
return
}

19
vendor/github.com/go-ole/go-ole/ienumvariant.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
package ole
import "unsafe"
type IEnumVARIANT struct {
IUnknown
}
type IEnumVARIANTVtbl struct {
IUnknownVtbl
Next uintptr
Skip uintptr
Reset uintptr
Clone uintptr
}
func (v *IEnumVARIANT) VTable() *IEnumVARIANTVtbl {
return (*IEnumVARIANTVtbl)(unsafe.Pointer(v.RawVTable))
}

Some files were not shown because too many files have changed in this diff Show More