Initial commit
This commit is contained in:
2
vendor/github.com/BurntSushi/xgb/.gitignore
generated
vendored
Normal file
2
vendor/github.com/BurntSushi/xgb/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
xgbgen/xgbgen
|
||||
.*.swp
|
||||
18
vendor/github.com/BurntSushi/xgb/AUTHORS
generated
vendored
Normal file
18
vendor/github.com/BurntSushi/xgb/AUTHORS
generated
vendored
Normal 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
39
vendor/github.com/BurntSushi/xgb/CONTRIBUTORS
generated
vendored
Normal 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
42
vendor/github.com/BurntSushi/xgb/LICENSE
generated
vendored
Normal 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
78
vendor/github.com/BurntSushi/xgb/Makefile
generated
vendored
Normal 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
59
vendor/github.com/BurntSushi/xgb/README
generated
vendored
Normal 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
29
vendor/github.com/BurntSushi/xgb/STYLE
generated
vendored
Normal 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
110
vendor/github.com/BurntSushi/xgb/auth.go
generated
vendored
Normal 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
186
vendor/github.com/BurntSushi/xgb/conn.go
generated
vendored
Normal 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
165
vendor/github.com/BurntSushi/xgb/cookie.go
generated
vendored
Normal 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
146
vendor/github.com/BurntSushi/xgb/doc.go
generated
vendored
Normal 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
105
vendor/github.com/BurntSushi/xgb/help.go
generated
vendored
Normal 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
29
vendor/github.com/BurntSushi/xgb/sync.go
generated
vendored
Normal 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
554
vendor/github.com/BurntSushi/xgb/xgb.go
generated
vendored
Normal 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
718
vendor/github.com/BurntSushi/xgb/xinerama/xinerama.go
generated
vendored
Normal 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
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
6
vendor/github.com/BurntSushi/xgbutil/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
*.swp
|
||||
*.png
|
||||
tst_first
|
||||
tst_graphics
|
||||
TAGS
|
||||
|
||||
13
vendor/github.com/BurntSushi/xgbutil/COPYING
generated
vendored
Normal file
13
vendor/github.com/BurntSushi/xgbutil/COPYING
generated
vendored
Normal 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
36
vendor/github.com/BurntSushi/xgbutil/Makefile
generated
vendored
Normal 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
55
vendor/github.com/BurntSushi/xgbutil/README
generated
vendored
Normal 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
29
vendor/github.com/BurntSushi/xgbutil/STYLE
generated
vendored
Normal 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
67
vendor/github.com/BurntSushi/xgbutil/doc.go
generated
vendored
Normal 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
1
vendor/github.com/BurntSushi/xgbutil/session.vim
generated
vendored
Normal 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
167
vendor/github.com/BurntSushi/xgbutil/types.go
generated
vendored
Normal 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
348
vendor/github.com/BurntSushi/xgbutil/xgbutil.go
generated
vendored
Normal 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
103
vendor/github.com/BurntSushi/xgbutil/xprop/atom.go
generated
vendored
Normal 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
40
vendor/github.com/BurntSushi/xgbutil/xprop/doc.go
generated
vendored
Normal 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
270
vendor/github.com/BurntSushi/xgbutil/xprop/xprop.go
generated
vendored
Normal 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
77
vendor/github.com/adrg/xdg/CODE_OF_CONDUCT.md
generated
vendored
Normal 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
135
vendor/github.com/adrg/xdg/CONTRIBUTING.md
generated
vendored
Normal 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** — check if the issue has already been
|
||||
reported.
|
||||
2. **Check if the issue has been fixed** — try to reproduce it using the
|
||||
latest `master` or development branch in the repository.
|
||||
3. **Isolate the problem** — 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
21
vendor/github.com/adrg/xdg/LICENSE
generated
vendored
Normal 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
280
vendor/github.com/adrg/xdg/README.md
generated
vendored
Normal 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 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 Support</kbd> | <kbd>/lib</kbd> |
|
||||
| <kbd><b>XDG_CONFIG_HOME</b></kbd> | <kbd>~/.config</kbd> | <kbd>~/Library/Application 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 Support</kbd><br/><kbd>/Library/Preferences</kbd> | <kbd>/lib</kbd> |
|
||||
| <kbd><b>XDG_STATE_HOME</b></kbd> | <kbd>~/.local/state</kbd> | <kbd>~/Library/Application 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 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 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 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>~/.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 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 Menu\Programs</kbd><br/><kbd>%ProgramData%\Microsoft\Windows\Start 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
|
||||
|
||||
[](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
68
vendor/github.com/adrg/xdg/base_dirs.go
generated
vendored
Normal 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
11
vendor/github.com/adrg/xdg/codecov.yml
generated
vendored
Normal 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
99
vendor/github.com/adrg/xdg/doc.go
generated
vendored
Normal 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
|
||||
78
vendor/github.com/adrg/xdg/internal/pathutil/pathutil.go
generated
vendored
Normal file
78
vendor/github.com/adrg/xdg/internal/pathutil/pathutil.go
generated
vendored
Normal 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, ", "))
|
||||
}
|
||||
29
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_plan9.go
generated
vendored
Normal file
29
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_plan9.go
generated
vendored
Normal 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
|
||||
}
|
||||
32
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_unix.go
generated
vendored
Normal file
32
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_unix.go
generated
vendored
Normal 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
|
||||
}
|
||||
64
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_windows.go
generated
vendored
Normal file
64
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_windows.go
generated
vendored
Normal 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
60
vendor/github.com/adrg/xdg/paths_darwin.go
generated
vendored
Normal 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
55
vendor/github.com/adrg/xdg/paths_plan9.go
generated
vendored
Normal 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
71
vendor/github.com/adrg/xdg/paths_unix.go
generated
vendored
Normal 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
168
vendor/github.com/adrg/xdg/paths_windows.go
generated
vendored
Normal 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
40
vendor/github.com/adrg/xdg/user_dirs.go
generated
vendored
Normal 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
218
vendor/github.com/adrg/xdg/xdg.go
generated
vendored
Normal 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
1
vendor/github.com/distatus/battery/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
cmd/battery/battery*
|
||||
7
vendor/github.com/distatus/battery/BREAKING.md
generated
vendored
Normal file
7
vendor/github.com/distatus/battery/BREAKING.md
generated
vendored
Normal 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
21
vendor/github.com/distatus/battery/LICENSE.md
generated
vendored
Normal 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
73
vendor/github.com/distatus/battery/README.md
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
battery [](https://github.com/distatus/battery/actions/workflows/tests.yml) [](https://goreportcard.com/report/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
174
vendor/github.com/distatus/battery/battery.go
generated
vendored
Normal 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
117
vendor/github.com/distatus/battery/battery_darwin.go
generated
vendored
Normal 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
|
||||
}
|
||||
153
vendor/github.com/distatus/battery/battery_freebsd_dragonfly_.go
generated
vendored
Normal file
153
vendor/github.com/distatus/battery/battery_freebsd_dragonfly_.go
generated
vendored
Normal 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
165
vendor/github.com/distatus/battery/battery_linux.go
generated
vendored
Normal 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
221
vendor/github.com/distatus/battery/battery_netbsd.go
generated
vendored
Normal 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, &s)
|
||||
case "last full cap":
|
||||
e.Full = handleValue(val, 1000, &b.Full, &s)
|
||||
case "charge":
|
||||
e.Current = handleValue(val, 1000, &b.Current, &s)
|
||||
maxCharge = val.MaxValue
|
||||
case "charge rate":
|
||||
cr1 = handleValue(val, 1000, &b.ChargeRate, &s)
|
||||
case "discharge rate":
|
||||
cr2 = handleValue(val, 1000, &b.ChargeRate, &s)
|
||||
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
282
vendor/github.com/distatus/battery/battery_openbsd.go
generated
vendored
Normal 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
260
vendor/github.com/distatus/battery/battery_solaris.go
generated
vendored
Normal 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
320
vendor/github.com/distatus/battery/battery_windows.go
generated
vendored
Normal 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
147
vendor/github.com/distatus/battery/errors.go
generated
vendored
Normal 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
48
vendor/github.com/distatus/battery/ioctl.go
generated
vendored
Normal 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
20
vendor/github.com/go-co-op/gocron/v2/.gitignore
generated
vendored
Normal 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
49
vendor/github.com/go-co-op/gocron/v2/.golangci.yaml
generated
vendored
Normal 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
|
||||
24
vendor/github.com/go-co-op/gocron/v2/.pre-commit-config.yaml
generated
vendored
Normal file
24
vendor/github.com/go-co-op/gocron/v2/.pre-commit-config.yaml
generated
vendored
Normal 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
|
||||
73
vendor/github.com/go-co-op/gocron/v2/CODE_OF_CONDUCT.md
generated
vendored
Normal file
73
vendor/github.com/go-co-op/gocron/v2/CODE_OF_CONDUCT.md
generated
vendored
Normal 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
38
vendor/github.com/go-co-op/gocron/v2/CONTRIBUTING.md
generated
vendored
Normal 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
21
vendor/github.com/go-co-op/gocron/v2/LICENSE
generated
vendored
Normal 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
19
vendor/github.com/go-co-op/gocron/v2/Makefile
generated
vendored
Normal 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
156
vendor/github.com/go-co-op/gocron/v2/README.md
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
# gocron: A Golang Job Scheduling Package
|
||||
|
||||
[](https://github.com/go-co-op/gocron/actions)
|
||||
 [](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
|
||||
|
||||
[](https://star-history.com/#go-co-op/gocron&Date)
|
||||
15
vendor/github.com/go-co-op/gocron/v2/SECURITY.md
generated
vendored
Normal file
15
vendor/github.com/go-co-op/gocron/v2/SECURITY.md
generated
vendored
Normal 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
5
vendor/github.com/go-co-op/gocron/v2/codecov.yml
generated
vendored
Normal 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
30
vendor/github.com/go-co-op/gocron/v2/distributed.go
generated
vendored
Normal 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
52
vendor/github.com/go-co-op/gocron/v2/errors.go
generated
vendored
Normal 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
440
vendor/github.com/go-co-op/gocron/v2/executor.go
generated
vendored
Normal 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
913
vendor/github.com/go-co-op/gocron/v2/job.go
generated
vendored
Normal 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
101
vendor/github.com/go-co-op/gocron/v2/logger.go
generated
vendored
Normal 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
776
vendor/github.com/go-co-op/gocron/v2/scheduler.go
generated
vendored
Normal 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
122
vendor/github.com/go-co-op/gocron/v2/util.go
generated
vendored
Normal 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
8
vendor/github.com/go-ole/go-ole/.travis.yml
generated
vendored
Normal 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
49
vendor/github.com/go-ole/go-ole/ChangeLog.md
generated
vendored
Normal 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
21
vendor/github.com/go-ole/go-ole/LICENSE
generated
vendored
Normal 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
46
vendor/github.com/go-ole/go-ole/README.md
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# Go OLE
|
||||
|
||||
[](https://ci.appveyor.com/project/jacobsantos/go-ole-jgs28)
|
||||
[](https://travis-ci.org/go-ole/go-ole)
|
||||
[](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
13
vendor/github.com/go-ole/go-ole/SECURITY.md
generated
vendored
Normal 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
68
vendor/github.com/go-ole/go-ole/appveyor.yml
generated
vendored
Normal 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
386
vendor/github.com/go-ole/go-ole/com.go
generated
vendored
Normal 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
174
vendor/github.com/go-ole/go-ole/com_func.go
generated
vendored
Normal 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
192
vendor/github.com/go-ole/go-ole/connect.go
generated
vendored
Normal 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
153
vendor/github.com/go-ole/go-ole/constants.go
generated
vendored
Normal 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
51
vendor/github.com/go-ole/go-ole/error.go
generated
vendored
Normal 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
8
vendor/github.com/go-ole/go-ole/error_func.go
generated
vendored
Normal 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
24
vendor/github.com/go-ole/go-ole/error_windows.go
generated
vendored
Normal 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
284
vendor/github.com/go-ole/go-ole/guid.go
generated
vendored
Normal 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
20
vendor/github.com/go-ole/go-ole/iconnectionpoint.go
generated
vendored
Normal 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))
|
||||
}
|
||||
21
vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go
generated
vendored
Normal file
21
vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go
generated
vendored
Normal 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)
|
||||
}
|
||||
43
vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go
generated
vendored
Normal file
43
vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go
generated
vendored
Normal 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)
|
||||
}
|
||||
17
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go
generated
vendored
Normal file
17
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go
generated
vendored
Normal 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))
|
||||
}
|
||||
11
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go
generated
vendored
Normal file
11
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go
generated
vendored
Normal 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)
|
||||
}
|
||||
25
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go
generated
vendored
Normal file
25
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go
generated
vendored
Normal 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
94
vendor/github.com/go-ole/go-ole/idispatch.go
generated
vendored
Normal 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
19
vendor/github.com/go-ole/go-ole/idispatch_func.go
generated
vendored
Normal 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
203
vendor/github.com/go-ole/go-ole/idispatch_windows.go
generated
vendored
Normal 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
19
vendor/github.com/go-ole/go-ole/ienumvariant.go
generated
vendored
Normal 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
Reference in New Issue
Block a user