update dependencies
|
@ -1,6 +1,12 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/0xAX/notificator"
|
||||
packages = ["."]
|
||||
revision = "88d57ee9043ba88d6a62e437fa15dda1ca0d2b59"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fatih/color"
|
||||
packages = ["."]
|
||||
|
@ -70,6 +76,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "59a956a9d4768c1668abfcf5af67434d0f8251797d381c64b9dcf013a3dfcb34"
|
||||
inputs-digest = "5913a16a0927350ebbd5117158473bb0252181f6aa8777cb5a095a18dee8bd40"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
|
||||
.idea
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2014, 0xAX
|
||||
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 the {organization} 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 HOLDER 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.
|
|
@ -0,0 +1,49 @@
|
|||
notificator
|
||||
===========================
|
||||
|
||||
Desktop notification with Golang for:
|
||||
|
||||
* Windows with `growlnotify`;
|
||||
* Mac OS X with `terminal-notifier` (if installed) or `osascript` (native, 10.9 Mavericks or Up.);
|
||||
* Linux with `notify-send` for Gnome and `kdialog` for Kde.
|
||||
|
||||
Usage
|
||||
------
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/0xAX/notificator"
|
||||
)
|
||||
|
||||
var notify *notificator.Notificator
|
||||
|
||||
func main() {
|
||||
|
||||
notify = notificator.New(notificator.Options{
|
||||
DefaultIcon: "icon/default.png",
|
||||
AppName: "My test App",
|
||||
})
|
||||
|
||||
notify.Push("title", "text", "/home/user/icon.png", notificator.UR_CRITICAL)
|
||||
}
|
||||
```
|
||||
|
||||
TODO
|
||||
-----
|
||||
|
||||
* Add more options for different notificators.
|
||||
|
||||
Сontribution
|
||||
------------
|
||||
|
||||
* Fork;
|
||||
* Make changes;
|
||||
* Send pull request;
|
||||
* Thank you.
|
||||
|
||||
author
|
||||
----------
|
||||
|
||||
[@0xAX](https://twitter.com/0xAX)
|
|
@ -0,0 +1,24 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"../../notificator"
|
||||
"log"
|
||||
)
|
||||
|
||||
var notify *notificator.Notificator
|
||||
|
||||
func main() {
|
||||
notify = notificator.New(notificator.Options{
|
||||
DefaultIcon: "icon/default.png",
|
||||
AppName: "My test App",
|
||||
})
|
||||
|
||||
notify.Push("title", "text", "/home/user/icon.png", notificator.UR_NORMAL)
|
||||
|
||||
// Check errors
|
||||
err := notify.Push("error", "ops =(", "/home/user/icon.png", notificator.UR_CRITICAL)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 37 KiB |
|
@ -0,0 +1,166 @@
|
|||
package notificator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
DefaultIcon string
|
||||
AppName string
|
||||
}
|
||||
|
||||
const (
|
||||
UR_NORMAL = "normal"
|
||||
UR_CRITICAL = "critical"
|
||||
)
|
||||
|
||||
type notifier interface {
|
||||
push(title string, text string, iconPath string) *exec.Cmd
|
||||
pushCritical(title string, text string, iconPath string) *exec.Cmd
|
||||
}
|
||||
|
||||
type Notificator struct {
|
||||
notifier notifier
|
||||
defaultIcon string
|
||||
}
|
||||
|
||||
func (n Notificator) Push(title string, text string, iconPath string, urgency string) error {
|
||||
icon := n.defaultIcon
|
||||
|
||||
if iconPath != "" {
|
||||
icon = iconPath
|
||||
}
|
||||
|
||||
if urgency == UR_CRITICAL {
|
||||
return n.notifier.pushCritical(title, text, icon).Run()
|
||||
}
|
||||
|
||||
return n.notifier.push(title, text, icon).Run()
|
||||
|
||||
}
|
||||
|
||||
type osxNotificator struct {
|
||||
AppName string
|
||||
}
|
||||
|
||||
func (o osxNotificator) push(title string, text string, iconPath string) *exec.Cmd {
|
||||
|
||||
// Checks if terminal-notifier exists, and is accessible.
|
||||
|
||||
term_notif := CheckTermNotif()
|
||||
os_version_check := CheckMacOSVersion()
|
||||
|
||||
// if terminal-notifier exists, use it.
|
||||
// else, fall back to osascript. (Mavericks and later.)
|
||||
|
||||
if term_notif == true {
|
||||
return exec.Command("terminal-notifier", "-title", o.AppName, "-message", text, "-subtitle", title, "-appIcon", iconPath)
|
||||
} else if os_version_check == true {
|
||||
title = strings.Replace(title, `"`, `\"`, -1)
|
||||
text = strings.Replace(text, `"`, `\"`, -1)
|
||||
|
||||
notification := fmt.Sprintf("display notification \"%s\" with title \"%s\" subtitle \"%s\"", text, o.AppName, title)
|
||||
return exec.Command("osascript", "-e", notification)
|
||||
}
|
||||
|
||||
// finally falls back to growlnotify.
|
||||
|
||||
return exec.Command("growlnotify", "-n", o.AppName, "--image", iconPath, "-m", title)
|
||||
}
|
||||
|
||||
// Causes the notification to stick around until clicked.
|
||||
func (o osxNotificator) pushCritical(title string, text string, iconPath string) *exec.Cmd {
|
||||
|
||||
// same function as above...
|
||||
|
||||
term_notif := CheckTermNotif()
|
||||
os_version_check := CheckMacOSVersion()
|
||||
|
||||
if term_notif == true {
|
||||
// timeout set to 30 seconds, to show the importance of the notification
|
||||
return exec.Command("terminal-notifier", "-title", o.AppName, "-message", text, "-subtitle", title, "-timeout", "30")
|
||||
} else if os_version_check == true {
|
||||
notification := fmt.Sprintf("display notification \"%s\" with title \"%s\" subtitle \"%s\"", text, o.AppName, title)
|
||||
return exec.Command("osascript", "-e", notification)
|
||||
}
|
||||
|
||||
return exec.Command("growlnotify", "-n", o.AppName, "--image", iconPath, "-m", title)
|
||||
|
||||
}
|
||||
|
||||
type linuxNotificator struct{}
|
||||
|
||||
func (l linuxNotificator) push(title string, text string, iconPath string) *exec.Cmd {
|
||||
return exec.Command("notify-send", "-i", iconPath, title, text)
|
||||
}
|
||||
|
||||
// Causes the notification to stick around until clicked.
|
||||
func (l linuxNotificator) pushCritical(title string, text string, iconPath string) *exec.Cmd {
|
||||
return exec.Command("notify-send", "-i", iconPath, title, text, "-u", "critical")
|
||||
}
|
||||
|
||||
type windowsNotificator struct{}
|
||||
|
||||
func (w windowsNotificator) push(title string, text string, iconPath string) *exec.Cmd {
|
||||
return exec.Command("growlnotify", "/i:", iconPath, "/t:", title, text)
|
||||
}
|
||||
|
||||
// Causes the notification to stick around until clicked.
|
||||
func (w windowsNotificator) pushCritical(title string, text string, iconPath string) *exec.Cmd {
|
||||
return exec.Command("notify-send", "-i", iconPath, title, text, "/s", "true", "/p", "2")
|
||||
}
|
||||
|
||||
func New(o Options) *Notificator {
|
||||
|
||||
var Notifier notifier
|
||||
|
||||
switch runtime.GOOS {
|
||||
|
||||
case "darwin":
|
||||
Notifier = osxNotificator{AppName: o.AppName}
|
||||
case "linux":
|
||||
Notifier = linuxNotificator{}
|
||||
case "windows":
|
||||
Notifier = windowsNotificator{}
|
||||
|
||||
}
|
||||
|
||||
return &Notificator{notifier: Notifier, defaultIcon: o.DefaultIcon}
|
||||
}
|
||||
|
||||
// Helper function for macOS
|
||||
|
||||
func CheckTermNotif() bool {
|
||||
// Checks if terminal-notifier exists, and is accessible.
|
||||
if err := exec.Command("which", "terminal-notifier").Run(); err != nil {
|
||||
return false
|
||||
}
|
||||
// no error, so return true. (terminal-notifier exists)
|
||||
return true
|
||||
}
|
||||
|
||||
func CheckMacOSVersion() bool {
|
||||
// Checks if the version of macOS is 10.9 or Higher (osascript support for notifications.)
|
||||
|
||||
cmd := exec.Command("sw_vers", "-productVersion")
|
||||
check, _ := cmd.Output()
|
||||
|
||||
version := strings.Split(strings.TrimSpace(string(check)), ".")
|
||||
|
||||
// semantic versioning of macOS
|
||||
|
||||
major, _ := strconv.Atoi(version[0])
|
||||
minor, _ := strconv.Atoi(version[1])
|
||||
|
||||
if major < 10 {
|
||||
return false
|
||||
} else if major == 10 && minor < 9 {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
Overview
|
||||
---
|
||||
|
||||
Bufferer
|
||||
---
|
||||
|
||||
Block
|
||||
---
|
||||
|
||||
BarChart
|
||||
---
|
||||
|
||||
Canvas
|
||||
---
|
||||
|
||||
Gauge
|
||||
---
|
||||
|
||||
LineChart
|
||||
---
|
||||
|
||||
MBarChart
|
||||
---
|
||||
|
||||
Par
|
||||
---
|
||||
|
||||
Sparkline
|
||||
---
|
||||
|
||||
Sparklines
|
||||
---
|
|
@ -0,0 +1,14 @@
|
|||
Event System
|
||||
---
|
||||
|
||||
Keyboard Events
|
||||
---
|
||||
|
||||
Mouse Events
|
||||
---
|
||||
|
||||
Window Events
|
||||
---
|
||||
|
||||
Custom Events
|
||||
---
|
After Width: | Height: | Size: 152 KiB |
After Width: | Height: | Size: 125 KiB |
|
@ -0,0 +1,15 @@
|
|||
[termui]() is a cross-platform, easy-to-compile, and fully-customizable terminal dashboard. It aims to provide a terminal front end for your applications with less struggle:
|
||||
|
||||
> ![dashboard](img/dashboard.gif)
|
||||
>
|
||||
> _cast under osx 10.10; Terminal.app; Menlo Regular 12pt._
|
||||
|
||||
This guide describes the essential parts used to build a interface, which includes:
|
||||
|
||||
- Installation & Usage
|
||||
- Layout System
|
||||
- Event System
|
||||
- Theming
|
||||
- Components
|
||||
|
||||
[Quickstart](quickstart.md) is the way to go for starters and [Recipes](recipes.md) contains some practical resolutions you might need.
|
|
@ -0,0 +1,26 @@
|
|||
Overview
|
||||
---
|
||||
|
||||
termui offers two layout system: [Absolute]() and [Grid](). The two concept actually spawned from Web:
|
||||
|
||||
- The __Absolute layout__ is a plain coordination system, like [CSS position property](https://developer.mozilla.org/en/docs/Web/CSS/position) `position: absolute`. You will need manually assign `.X`, `.Y`, `.Width` and `.Height` to a component.
|
||||
- The __Grid system__ actually is a simplified version of [the 12 columns CSS grid system](http://www.w3schools.com/bootstrap/bootstrap_grid_system.asp) on terminal. You do not need to bother setting positions and width properties, these values will be synced up according to their containers.
|
||||
|
||||
!!! note
|
||||
`Align` property can help you set your component position based on terminal window. Find more at [Magic Variables](#magic-variables)
|
||||
|
||||
__Cons and pros:__
|
||||
|
||||
- Use of Absolute layout gives you maximum control over how to arrange your components, while you have
|
||||
to put a little more effort to set things up. Fortunately there are some "magic variables" may help you out.
|
||||
- Grid layout can save you some time, it adjusts components location and size based on it's container. But note that you do need to set `.Height` property to each components because termui can not decide it for you.
|
||||
|
||||
|
||||
Absolute Layout
|
||||
---
|
||||
|
||||
Grid Layout
|
||||
---
|
||||
|
||||
Magic Variables
|
||||
---
|
|
@ -0,0 +1,80 @@
|
|||
Installation
|
||||
---
|
||||
|
||||
Since [termui](https://github.com/gizak/termui) is a Go lib, we will need a working Go environment to begin with. If you have not set it up, there is a great intro you can follow up: [How to write Go code](https://golang.org/doc/code.html).
|
||||
|
||||
Once you have the environment set up, you can proceed to install termui by the following command:
|
||||
|
||||
`go get github.com/gizak/termui`
|
||||
|
||||
The current version of termui is v2. If you are working with the old version of termui or the new version does not seem right to you, you can always go back to v1 version by:
|
||||
|
||||
`go get gopkg.in/gizak/termui.v1`
|
||||
|
||||
!!! note
|
||||
v2 has many features implemented which you can not find in v1, such as new event system and asynchronous rendering. To find more about versions difference in section [Versions](versions.md).
|
||||
|
||||
|
||||
Usage
|
||||
---
|
||||
|
||||
Let's throw an simple example to get our feet wet:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import ui "github.com/gizak/termui" // use ui as an alias
|
||||
|
||||
func main() {
|
||||
err := ui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer ui.Close()
|
||||
|
||||
p := ui.NewPar(":PRESS q TO QUIT DEMO")
|
||||
p.Height = 3
|
||||
p.Width = 50
|
||||
p.TextFgColor = ui.ColorWhite
|
||||
p.BorderLabel = "Text Box"
|
||||
p.BorderFg = ui.ColorCyan
|
||||
|
||||
ui.Render(p) // feel free to call Render, it's async and non-block
|
||||
|
||||
ui.Handle("/sys/kbd/q",func(e ui.Event){
|
||||
ui.StopLoop()
|
||||
})
|
||||
|
||||
ui.Loop()
|
||||
}
|
||||
```
|
||||
There are only around 20 lines for the main function. Break this down into 4 parts:
|
||||
|
||||
1. __Init termui__:
|
||||
`ui.Init()` initializes the termui. From this point, termui will take over your terminal display.
|
||||
`ui.Close()` closes resources and cleans up your terminal content. Make sure it is called before exit or you will end up with a messed up looking terminal.
|
||||
|
||||
2. __Build your component__:
|
||||
`ui.NewPar(:PRESS q TO QUIT DEMO)` returns a structure representing a paragraph component. You can assign position, size, text colour, border and many other properties to a component.
|
||||
|
||||
3. __Draw your component on display__:
|
||||
`ui.Render(p)` renders p onto terminal display.
|
||||
|
||||
4. __Handle events__:
|
||||
`ui.Handle("/sys/kbd/q", func(e Event))` registers an event handler for event: key q is pressed.
|
||||
`ui.StopLoop()` exits the event listening loop invoked by `ui.Loop()`.
|
||||
`ui.Loop()` makes the program stops at here and start listening & handling events. Call
|
||||
`ui.StopLoop()` to leave the circle.
|
||||
|
||||
The example code gives us:
|
||||
|
||||
> ![example screenshot](img/demo1.png)
|
||||
|
||||
Now you can press q to quit the program.
|
||||
|
||||
After knowing of some basics, next we can discover more about:
|
||||
|
||||
1. how to set component location in [Layouts](layouts.md)
|
||||
2. how to capture and handle events in [Events](events.md)
|
||||
3. the different [components](components.md)
|
||||
4. check out some real world examples in [recipes](recipes.md)
|
|
@ -0,0 +1 @@
|
|||
_Sorry, it is still Work in Progress..._
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/gizak/termui"
|
||||
|
||||
func main() {
|
||||
if err := termui.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
bc := termui.NewBarChart()
|
||||
data := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6}
|
||||
bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
|
||||
bc.BorderLabel = "Bar Chart"
|
||||
bc.Data = data
|
||||
bc.Width = 26
|
||||
bc.Height = 10
|
||||
bc.DataLabels = bclabels
|
||||
bc.TextColor = termui.ColorGreen
|
||||
bc.BarColor = termui.ColorRed
|
||||
bc.NumColor = termui.ColorYellow
|
||||
|
||||
termui.Render(bc)
|
||||
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
termui.StopLoop()
|
||||
})
|
||||
termui.Loop()
|
||||
|
||||
}
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 152 KiB |
|
@ -0,0 +1,142 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import ui "github.com/gizak/termui"
|
||||
import "math"
|
||||
|
||||
func main() {
|
||||
if err := ui.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer ui.Close()
|
||||
|
||||
p := ui.NewPar(":PRESS q TO QUIT DEMO")
|
||||
p.Height = 3
|
||||
p.Width = 50
|
||||
p.TextFgColor = ui.ColorWhite
|
||||
p.BorderLabel = "Text Box"
|
||||
p.BorderFg = ui.ColorCyan
|
||||
p.Handle("/timer/1s", func(e ui.Event) {
|
||||
cnt := e.Data.(ui.EvtTimer)
|
||||
if cnt.Count%2 == 0 {
|
||||
p.TextFgColor = ui.ColorRed
|
||||
} else {
|
||||
p.TextFgColor = ui.ColorWhite
|
||||
}
|
||||
})
|
||||
|
||||
strs := []string{"[0] gizak/termui", "[1] editbox.go", "[2] iterrupt.go", "[3] keyboard.go", "[4] output.go", "[5] random_out.go", "[6] dashboard.go", "[7] nsf/termbox-go"}
|
||||
list := ui.NewList()
|
||||
list.Items = strs
|
||||
list.ItemFgColor = ui.ColorYellow
|
||||
list.BorderLabel = "List"
|
||||
list.Height = 7
|
||||
list.Width = 25
|
||||
list.Y = 4
|
||||
|
||||
g := ui.NewGauge()
|
||||
g.Percent = 50
|
||||
g.Width = 50
|
||||
g.Height = 3
|
||||
g.Y = 11
|
||||
g.BorderLabel = "Gauge"
|
||||
g.BarColor = ui.ColorRed
|
||||
g.BorderFg = ui.ColorWhite
|
||||
g.BorderLabelFg = ui.ColorCyan
|
||||
|
||||
spark := ui.Sparkline{}
|
||||
spark.Height = 1
|
||||
spark.Title = "srv 0:"
|
||||
spdata := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6, 4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6, 4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6, 4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6}
|
||||
spark.Data = spdata
|
||||
spark.LineColor = ui.ColorCyan
|
||||
spark.TitleColor = ui.ColorWhite
|
||||
|
||||
spark1 := ui.Sparkline{}
|
||||
spark1.Height = 1
|
||||
spark1.Title = "srv 1:"
|
||||
spark1.Data = spdata
|
||||
spark1.TitleColor = ui.ColorWhite
|
||||
spark1.LineColor = ui.ColorRed
|
||||
|
||||
sp := ui.NewSparklines(spark, spark1)
|
||||
sp.Width = 25
|
||||
sp.Height = 7
|
||||
sp.BorderLabel = "Sparkline"
|
||||
sp.Y = 4
|
||||
sp.X = 25
|
||||
|
||||
sinps := (func() []float64 {
|
||||
n := 220
|
||||
ps := make([]float64, n)
|
||||
for i := range ps {
|
||||
ps[i] = 1 + math.Sin(float64(i)/5)
|
||||
}
|
||||
return ps
|
||||
})()
|
||||
|
||||
lc := ui.NewLineChart()
|
||||
lc.BorderLabel = "dot-mode Line Chart"
|
||||
lc.Data = sinps
|
||||
lc.Width = 50
|
||||
lc.Height = 11
|
||||
lc.X = 0
|
||||
lc.Y = 14
|
||||
lc.AxesColor = ui.ColorWhite
|
||||
lc.LineColor = ui.ColorRed | ui.AttrBold
|
||||
lc.Mode = "dot"
|
||||
|
||||
bc := ui.NewBarChart()
|
||||
bcdata := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6}
|
||||
bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
|
||||
bc.BorderLabel = "Bar Chart"
|
||||
bc.Width = 26
|
||||
bc.Height = 10
|
||||
bc.X = 51
|
||||
bc.Y = 0
|
||||
bc.DataLabels = bclabels
|
||||
bc.BarColor = ui.ColorGreen
|
||||
bc.NumColor = ui.ColorBlack
|
||||
|
||||
lc1 := ui.NewLineChart()
|
||||
lc1.BorderLabel = "braille-mode Line Chart"
|
||||
lc1.Data = sinps
|
||||
lc1.Width = 26
|
||||
lc1.Height = 11
|
||||
lc1.X = 51
|
||||
lc1.Y = 14
|
||||
lc1.AxesColor = ui.ColorWhite
|
||||
lc1.LineColor = ui.ColorYellow | ui.AttrBold
|
||||
|
||||
p1 := ui.NewPar("Hey!\nI am a borderless block!")
|
||||
p1.Border = false
|
||||
p1.Width = 26
|
||||
p1.Height = 2
|
||||
p1.TextFgColor = ui.ColorMagenta
|
||||
p1.X = 52
|
||||
p1.Y = 11
|
||||
|
||||
draw := func(t int) {
|
||||
g.Percent = t % 101
|
||||
list.Items = strs[t%9:]
|
||||
sp.Lines[0].Data = spdata[:30+t%50]
|
||||
sp.Lines[1].Data = spdata[:35+t%50]
|
||||
lc.Data = sinps[t/2%220:]
|
||||
lc1.Data = sinps[2*t%220:]
|
||||
bc.Data = bcdata[t/2%10:]
|
||||
ui.Render(p, list, g, sp, lc, bc, lc1, p1)
|
||||
}
|
||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
||||
ui.StopLoop()
|
||||
})
|
||||
ui.Handle("/timer/1s", func(e ui.Event) {
|
||||
t := e.Data.(ui.EvtTimer)
|
||||
draw(int(t.Count))
|
||||
})
|
||||
ui.Loop()
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/gizak/termui"
|
||||
|
||||
func main() {
|
||||
err := termui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
//termui.UseTheme("helloworld")
|
||||
|
||||
g0 := termui.NewGauge()
|
||||
g0.Percent = 40
|
||||
g0.Width = 50
|
||||
g0.Height = 3
|
||||
g0.BorderLabel = "Slim Gauge"
|
||||
g0.BarColor = termui.ColorRed
|
||||
g0.BorderFg = termui.ColorWhite
|
||||
g0.BorderLabelFg = termui.ColorCyan
|
||||
|
||||
gg := termui.NewBlock()
|
||||
gg.Width = 50
|
||||
gg.Height = 5
|
||||
gg.Y = 12
|
||||
gg.BorderLabel = "TEST"
|
||||
gg.Align()
|
||||
|
||||
g2 := termui.NewGauge()
|
||||
g2.Percent = 60
|
||||
g2.Width = 50
|
||||
g2.Height = 3
|
||||
g2.PercentColor = termui.ColorBlue
|
||||
g2.Y = 3
|
||||
g2.BorderLabel = "Slim Gauge"
|
||||
g2.BarColor = termui.ColorYellow
|
||||
g2.BorderFg = termui.ColorWhite
|
||||
|
||||
g1 := termui.NewGauge()
|
||||
g1.Percent = 30
|
||||
g1.Width = 50
|
||||
g1.Height = 5
|
||||
g1.Y = 6
|
||||
g1.BorderLabel = "Big Gauge"
|
||||
g1.PercentColor = termui.ColorYellow
|
||||
g1.BarColor = termui.ColorGreen
|
||||
g1.BorderFg = termui.ColorWhite
|
||||
g1.BorderLabelFg = termui.ColorMagenta
|
||||
|
||||
g3 := termui.NewGauge()
|
||||
g3.Percent = 50
|
||||
g3.Width = 50
|
||||
g3.Height = 3
|
||||
g3.Y = 11
|
||||
g3.BorderLabel = "Gauge with custom label"
|
||||
g3.Label = "{{percent}}% (100MBs free)"
|
||||
g3.LabelAlign = termui.AlignRight
|
||||
|
||||
g4 := termui.NewGauge()
|
||||
g4.Percent = 50
|
||||
g4.Width = 50
|
||||
g4.Height = 3
|
||||
g4.Y = 14
|
||||
g4.BorderLabel = "Gauge"
|
||||
g4.Label = "Gauge with custom highlighted label"
|
||||
g4.PercentColor = termui.ColorYellow
|
||||
g4.BarColor = termui.ColorGreen
|
||||
g4.PercentColorHighlighted = termui.ColorBlack
|
||||
|
||||
termui.Render(g0, g1, g2, g3, g4)
|
||||
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
termui.StopLoop()
|
||||
})
|
||||
|
||||
termui.Loop()
|
||||
}
|
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 782 KiB |
|
@ -0,0 +1,122 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import ui "github.com/gizak/termui"
|
||||
|
||||
import "math"
|
||||
|
||||
func main() {
|
||||
if err := ui.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer ui.Close()
|
||||
|
||||
sinps := (func() []float64 {
|
||||
n := 400
|
||||
ps := make([]float64, n)
|
||||
for i := range ps {
|
||||
ps[i] = 1 + math.Sin(float64(i)/5)
|
||||
}
|
||||
return ps
|
||||
})()
|
||||
sinpsint := (func() []int {
|
||||
ps := make([]int, len(sinps))
|
||||
for i, v := range sinps {
|
||||
ps[i] = int(100*v + 10)
|
||||
}
|
||||
return ps
|
||||
})()
|
||||
|
||||
spark := ui.Sparkline{}
|
||||
spark.Height = 8
|
||||
spdata := sinpsint
|
||||
spark.Data = spdata[:100]
|
||||
spark.LineColor = ui.ColorCyan
|
||||
spark.TitleColor = ui.ColorWhite
|
||||
|
||||
sp := ui.NewSparklines(spark)
|
||||
sp.Height = 11
|
||||
sp.BorderLabel = "Sparkline"
|
||||
|
||||
lc := ui.NewLineChart()
|
||||
lc.BorderLabel = "braille-mode Line Chart"
|
||||
lc.Data = sinps
|
||||
lc.Height = 11
|
||||
lc.AxesColor = ui.ColorWhite
|
||||
lc.LineColor = ui.ColorYellow | ui.AttrBold
|
||||
|
||||
gs := make([]*ui.Gauge, 3)
|
||||
for i := range gs {
|
||||
gs[i] = ui.NewGauge()
|
||||
//gs[i].LabelAlign = ui.AlignCenter
|
||||
gs[i].Height = 2
|
||||
gs[i].Border = false
|
||||
gs[i].Percent = i * 10
|
||||
gs[i].PaddingBottom = 1
|
||||
gs[i].BarColor = ui.ColorRed
|
||||
}
|
||||
|
||||
ls := ui.NewList()
|
||||
ls.Border = false
|
||||
ls.Items = []string{
|
||||
"[1] Downloading File 1",
|
||||
"", // == \newline
|
||||
"[2] Downloading File 2",
|
||||
"",
|
||||
"[3] Uploading File 3",
|
||||
}
|
||||
ls.Height = 5
|
||||
|
||||
par := ui.NewPar("<> This row has 3 columns\n<- Widgets can be stacked up like left side\n<- Stacked widgets are treated as a single widget")
|
||||
par.Height = 5
|
||||
par.BorderLabel = "Demonstration"
|
||||
|
||||
// build layout
|
||||
ui.Body.AddRows(
|
||||
ui.NewRow(
|
||||
ui.NewCol(6, 0, sp),
|
||||
ui.NewCol(6, 0, lc)),
|
||||
ui.NewRow(
|
||||
ui.NewCol(3, 0, ls),
|
||||
ui.NewCol(3, 0, gs[0], gs[1], gs[2]),
|
||||
ui.NewCol(6, 0, par)))
|
||||
|
||||
// calculate layout
|
||||
ui.Body.Align()
|
||||
|
||||
ui.Render(ui.Body)
|
||||
|
||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
||||
ui.StopLoop()
|
||||
})
|
||||
ui.Handle("/timer/1s", func(e ui.Event) {
|
||||
t := e.Data.(ui.EvtTimer)
|
||||
i := t.Count
|
||||
if i > 103 {
|
||||
ui.StopLoop()
|
||||
return
|
||||
}
|
||||
|
||||
for _, g := range gs {
|
||||
g.Percent = (g.Percent + 3) % 100
|
||||
}
|
||||
|
||||
sp.Lines[0].Data = spdata[:100+i]
|
||||
lc.Data = sinps[2*i:]
|
||||
ui.Render(ui.Body)
|
||||
})
|
||||
|
||||
ui.Handle("/sys/wnd/resize", func(e ui.Event) {
|
||||
ui.Body.Width = ui.TermWidth()
|
||||
ui.Body.Align()
|
||||
ui.Clear()
|
||||
ui.Render(ui.Body)
|
||||
})
|
||||
|
||||
ui.Loop()
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/gizak/termui"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := termui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
//termui.UseTheme("helloworld")
|
||||
|
||||
sinps := (func() []float64 {
|
||||
n := 220
|
||||
ps := make([]float64, n)
|
||||
for i := range ps {
|
||||
ps[i] = 1 + math.Sin(float64(i)/5)
|
||||
}
|
||||
return ps
|
||||
})()
|
||||
|
||||
lc0 := termui.NewLineChart()
|
||||
lc0.BorderLabel = "braille-mode Line Chart"
|
||||
lc0.Data = sinps
|
||||
lc0.Width = 50
|
||||
lc0.Height = 12
|
||||
lc0.X = 0
|
||||
lc0.Y = 0
|
||||
lc0.AxesColor = termui.ColorWhite
|
||||
lc0.LineColor = termui.ColorGreen | termui.AttrBold
|
||||
|
||||
lc1 := termui.NewLineChart()
|
||||
lc1.BorderLabel = "dot-mode Line Chart"
|
||||
lc1.Mode = "dot"
|
||||
lc1.Data = sinps
|
||||
lc1.Width = 26
|
||||
lc1.Height = 12
|
||||
lc1.X = 51
|
||||
lc1.DotStyle = '+'
|
||||
lc1.AxesColor = termui.ColorWhite
|
||||
lc1.LineColor = termui.ColorYellow | termui.AttrBold
|
||||
|
||||
lc2 := termui.NewLineChart()
|
||||
lc2.BorderLabel = "dot-mode Line Chart"
|
||||
lc2.Mode = "dot"
|
||||
lc2.Data = sinps[4:]
|
||||
lc2.Width = 77
|
||||
lc2.Height = 16
|
||||
lc2.X = 0
|
||||
lc2.Y = 12
|
||||
lc2.AxesColor = termui.ColorWhite
|
||||
lc2.LineColor = termui.ColorCyan | termui.AttrBold
|
||||
|
||||
termui.Render(lc0, lc1, lc2)
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
termui.StopLoop()
|
||||
})
|
||||
termui.Loop()
|
||||
|
||||
}
|
After Width: | Height: | Size: 85 KiB |
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/gizak/termui"
|
||||
|
||||
func main() {
|
||||
err := termui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
//termui.UseTheme("helloworld")
|
||||
|
||||
strs := []string{
|
||||
"[0] github.com/gizak/termui",
|
||||
"[1] [你好,世界](fg-blue)",
|
||||
"[2] [こんにちは世界](fg-red)",
|
||||
"[3] [color output](fg-white,bg-green)",
|
||||
"[4] output.go",
|
||||
"[5] random_out.go",
|
||||
"[6] dashboard.go",
|
||||
"[7] nsf/termbox-go"}
|
||||
|
||||
ls := termui.NewList()
|
||||
ls.Items = strs
|
||||
ls.ItemFgColor = termui.ColorYellow
|
||||
ls.BorderLabel = "List"
|
||||
ls.Height = 7
|
||||
ls.Width = 25
|
||||
ls.Y = 0
|
||||
|
||||
termui.Render(ls)
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
termui.StopLoop()
|
||||
})
|
||||
termui.Loop()
|
||||
|
||||
}
|
After Width: | Height: | Size: 28 KiB |
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/gizak/termui"
|
||||
|
||||
func main() {
|
||||
err := termui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
//termui.UseTheme("helloworld")
|
||||
|
||||
bc := termui.NewMBarChart()
|
||||
math := []int{90, 85, 90, 80}
|
||||
english := []int{70, 85, 75, 60}
|
||||
science := []int{75, 60, 80, 85}
|
||||
compsci := []int{100, 100, 100, 100}
|
||||
bc.Data[0] = math
|
||||
bc.Data[1] = english
|
||||
bc.Data[2] = science
|
||||
bc.Data[3] = compsci
|
||||
studentsName := []string{"Ken", "Rob", "Dennis", "Linus"}
|
||||
bc.BorderLabel = "Student's Marks X-Axis=Name Y-Axis=Marks[Math,English,Science,ComputerScience] in %"
|
||||
bc.Width = 100
|
||||
bc.Height = 30
|
||||
bc.Y = 0
|
||||
bc.BarWidth = 10
|
||||
bc.DataLabels = studentsName
|
||||
bc.ShowScale = true //Show y_axis scale value (min and max)
|
||||
bc.SetMax(400)
|
||||
|
||||
bc.TextColor = termui.ColorGreen //this is color for label (x-axis)
|
||||
bc.BarColor[3] = termui.ColorGreen //BarColor for computerscience
|
||||
bc.BarColor[1] = termui.ColorYellow //Bar Color for english
|
||||
bc.NumColor[3] = termui.ColorRed // Num color for computerscience
|
||||
bc.NumColor[1] = termui.ColorRed // num color for english
|
||||
|
||||
//Other colors are automatically populated, btw All the students seems do well in computerscience. :p
|
||||
|
||||
termui.Render(bc)
|
||||
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
termui.StopLoop()
|
||||
})
|
||||
termui.Loop()
|
||||
|
||||
}
|
After Width: | Height: | Size: 71 KiB |
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/gizak/termui"
|
||||
|
||||
func main() {
|
||||
err := termui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
//termui.UseTheme("helloworld")
|
||||
|
||||
par0 := termui.NewPar("Borderless Text")
|
||||
par0.Height = 1
|
||||
par0.Width = 20
|
||||
par0.Y = 1
|
||||
par0.Border = false
|
||||
|
||||
par1 := termui.NewPar("你好,世界。")
|
||||
par1.Height = 3
|
||||
par1.Width = 17
|
||||
par1.X = 20
|
||||
par1.BorderLabel = "标签"
|
||||
|
||||
par2 := termui.NewPar("Simple colored text\nwith label. It [can be](fg-red) multilined with \\n or [break automatically](fg-red,fg-bold)")
|
||||
par2.Height = 5
|
||||
par2.Width = 37
|
||||
par2.Y = 4
|
||||
par2.BorderLabel = "Multiline"
|
||||
par2.BorderFg = termui.ColorYellow
|
||||
|
||||
par3 := termui.NewPar("Long text with label and it is auto trimmed.")
|
||||
par3.Height = 3
|
||||
par3.Width = 37
|
||||
par3.Y = 9
|
||||
par3.BorderLabel = "Auto Trim"
|
||||
|
||||
termui.Render(par0, par1, par2, par3)
|
||||
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
termui.StopLoop()
|
||||
})
|
||||
termui.Loop()
|
||||
|
||||
}
|
After Width: | Height: | Size: 45 KiB |
|
@ -0,0 +1,69 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/gizak/termui"
|
||||
|
||||
func main() {
|
||||
err := termui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
//termui.UseTheme("helloworld")
|
||||
|
||||
data := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6}
|
||||
spl0 := termui.NewSparkline()
|
||||
spl0.Data = data[3:]
|
||||
spl0.Title = "Sparkline 0"
|
||||
spl0.LineColor = termui.ColorGreen
|
||||
|
||||
// single
|
||||
spls0 := termui.NewSparklines(spl0)
|
||||
spls0.Height = 2
|
||||
spls0.Width = 20
|
||||
spls0.Border = false
|
||||
|
||||
spl1 := termui.NewSparkline()
|
||||
spl1.Data = data
|
||||
spl1.Title = "Sparkline 1"
|
||||
spl1.LineColor = termui.ColorRed
|
||||
|
||||
spl2 := termui.NewSparkline()
|
||||
spl2.Data = data[5:]
|
||||
spl2.Title = "Sparkline 2"
|
||||
spl2.LineColor = termui.ColorMagenta
|
||||
|
||||
// group
|
||||
spls1 := termui.NewSparklines(spl0, spl1, spl2)
|
||||
spls1.Height = 8
|
||||
spls1.Width = 20
|
||||
spls1.Y = 3
|
||||
spls1.BorderLabel = "Group Sparklines"
|
||||
|
||||
spl3 := termui.NewSparkline()
|
||||
spl3.Data = data
|
||||
spl3.Title = "Enlarged Sparkline"
|
||||
spl3.Height = 8
|
||||
spl3.LineColor = termui.ColorYellow
|
||||
|
||||
spls2 := termui.NewSparklines(spl3)
|
||||
spls2.Height = 11
|
||||
spls2.Width = 30
|
||||
spls2.BorderFg = termui.ColorCyan
|
||||
spls2.X = 21
|
||||
spls2.BorderLabel = "Tweeked Sparkline"
|
||||
|
||||
termui.Render(spls0, spls1, spls2)
|
||||
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
termui.StopLoop()
|
||||
})
|
||||
termui.Loop()
|
||||
|
||||
}
|
After Width: | Height: | Size: 48 KiB |
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/gizak/termui"
|
||||
|
||||
func main() {
|
||||
err := termui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
rows1 := [][]string{
|
||||
[]string{"header1", "header2", "header3"},
|
||||
[]string{"你好吗", "Go-lang is so cool", "Im working on Ruby"},
|
||||
[]string{"2016", "10", "11"},
|
||||
}
|
||||
|
||||
table1 := termui.NewTable()
|
||||
table1.Rows = rows1
|
||||
table1.FgColor = termui.ColorWhite
|
||||
table1.BgColor = termui.ColorDefault
|
||||
table1.Y = 0
|
||||
table1.X = 0
|
||||
table1.Width = 62
|
||||
table1.Height = 7
|
||||
|
||||
termui.Render(table1)
|
||||
|
||||
rows2 := [][]string{
|
||||
[]string{"header1", "header2", "header3"},
|
||||
[]string{"Foundations", "Go-lang is so cool", "Im working on Ruby"},
|
||||
[]string{"2016", "11", "11"},
|
||||
}
|
||||
|
||||
table2 := termui.NewTable()
|
||||
table2.Rows = rows2
|
||||
table2.FgColor = termui.ColorWhite
|
||||
table2.BgColor = termui.ColorDefault
|
||||
table2.TextAlign = termui.AlignCenter
|
||||
table2.Separator = false
|
||||
table2.Analysis()
|
||||
table2.SetSize()
|
||||
table2.BgColors[2] = termui.ColorRed
|
||||
table2.Y = 10
|
||||
table2.X = 0
|
||||
table2.Border = true
|
||||
|
||||
termui.Render(table2)
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
termui.StopLoop()
|
||||
})
|
||||
termui.Loop()
|
||||
}
|
After Width: | Height: | Size: 46 KiB |
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gizak/termui"
|
||||
"github.com/gizak/termui/extra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := termui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
//termui.UseTheme("helloworld")
|
||||
|
||||
header := termui.NewPar("Press q to quit, Press j or k to switch tabs")
|
||||
header.Height = 1
|
||||
header.Width = 50
|
||||
header.Border = false
|
||||
header.TextBgColor = termui.ColorBlue
|
||||
|
||||
tab1 := extra.NewTab("pierwszy")
|
||||
par2 := termui.NewPar("Press q to quit\nPress j or k to switch tabs\n")
|
||||
par2.Height = 5
|
||||
par2.Width = 37
|
||||
par2.Y = 0
|
||||
par2.BorderLabel = "Keys"
|
||||
par2.BorderFg = termui.ColorYellow
|
||||
tab1.AddBlocks(par2)
|
||||
|
||||
tab2 := extra.NewTab("drugi")
|
||||
bc := termui.NewBarChart()
|
||||
data := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6}
|
||||
bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
|
||||
bc.BorderLabel = "Bar Chart"
|
||||
bc.Data = data
|
||||
bc.Width = 26
|
||||
bc.Height = 10
|
||||
bc.DataLabels = bclabels
|
||||
bc.TextColor = termui.ColorGreen
|
||||
bc.BarColor = termui.ColorRed
|
||||
bc.NumColor = termui.ColorYellow
|
||||
tab2.AddBlocks(bc)
|
||||
|
||||
tab3 := extra.NewTab("trzeci")
|
||||
tab4 := extra.NewTab("żółw")
|
||||
tab5 := extra.NewTab("four")
|
||||
tab6 := extra.NewTab("five")
|
||||
|
||||
tabpane := extra.NewTabpane()
|
||||
tabpane.Y = 1
|
||||
tabpane.Width = 30
|
||||
tabpane.Border = true
|
||||
|
||||
tabpane.SetTabs(*tab1, *tab2, *tab3, *tab4, *tab5, *tab6)
|
||||
|
||||
termui.Render(header, tabpane)
|
||||
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
termui.StopLoop()
|
||||
})
|
||||
|
||||
termui.Handle("/sys/kbd/j", func(termui.Event) {
|
||||
tabpane.SetActiveLeft()
|
||||
termui.Clear()
|
||||
termui.Render(header, tabpane)
|
||||
})
|
||||
|
||||
termui.Handle("/sys/kbd/k", func(termui.Event) {
|
||||
tabpane.SetActiveRight()
|
||||
termui.Clear()
|
||||
termui.Render(header, tabpane)
|
||||
})
|
||||
|
||||
termui.Loop()
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import ui "github.com/gizak/termui"
|
||||
import "math"
|
||||
|
||||
import "time"
|
||||
|
||||
func main() {
|
||||
err := ui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer ui.Close()
|
||||
|
||||
ui.UseTheme("helloworld")
|
||||
|
||||
p := ui.NewPar(":PRESS q TO QUIT DEMO")
|
||||
p.Height = 3
|
||||
p.Width = 50
|
||||
p.BorderLabel = "Text Box"
|
||||
|
||||
strs := []string{"[0] gizak/termui", "[1] editbox.go", "[2] iterrupt.go", "[3] keyboard.go", "[4] output.go", "[5] random_out.go", "[6] dashboard.go", "[7] nsf/termbox-go"}
|
||||
list := ui.NewList()
|
||||
list.Items = strs
|
||||
list.BorderLabel = "List"
|
||||
list.Height = 7
|
||||
list.Width = 25
|
||||
list.Y = 4
|
||||
|
||||
g := ui.NewGauge()
|
||||
g.Percent = 50
|
||||
g.Width = 50
|
||||
g.Height = 3
|
||||
g.Y = 11
|
||||
g.BorderLabel = "Gauge"
|
||||
|
||||
spark := ui.NewSparkline()
|
||||
spark.Title = "srv 0:"
|
||||
spdata := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6}
|
||||
spark.Data = spdata
|
||||
|
||||
spark1 := ui.NewSparkline()
|
||||
spark1.Title = "srv 1:"
|
||||
spark1.Data = spdata
|
||||
|
||||
sp := ui.NewSparklines(spark, spark1)
|
||||
sp.Width = 25
|
||||
sp.Height = 7
|
||||
sp.BorderLabel = "Sparkline"
|
||||
sp.Y = 4
|
||||
sp.X = 25
|
||||
|
||||
lc := ui.NewLineChart()
|
||||
sinps := (func() []float64 {
|
||||
n := 100
|
||||
ps := make([]float64, n)
|
||||
for i := range ps {
|
||||
ps[i] = 1 + math.Sin(float64(i)/4)
|
||||
}
|
||||
return ps
|
||||
})()
|
||||
|
||||
lc.BorderLabel = "Line Chart"
|
||||
lc.Data = sinps
|
||||
lc.Width = 50
|
||||
lc.Height = 11
|
||||
lc.X = 0
|
||||
lc.Y = 14
|
||||
lc.Mode = "dot"
|
||||
|
||||
bc := ui.NewBarChart()
|
||||
bcdata := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6}
|
||||
bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
|
||||
bc.BorderLabel = "Bar Chart"
|
||||
bc.Width = 26
|
||||
bc.Height = 10
|
||||
bc.X = 51
|
||||
bc.Y = 0
|
||||
bc.DataLabels = bclabels
|
||||
|
||||
lc1 := ui.NewLineChart()
|
||||
lc1.BorderLabel = "Line Chart"
|
||||
rndwalk := (func() []float64 {
|
||||
n := 150
|
||||
d := make([]float64, n)
|
||||
for i := 1; i < n; i++ {
|
||||
if i < 20 {
|
||||
d[i] = d[i-1] + 0.01
|
||||
}
|
||||
if i > 20 {
|
||||
d[i] = d[i-1] - 0.05
|
||||
}
|
||||
}
|
||||
return d
|
||||
})()
|
||||
lc1.Data = rndwalk
|
||||
lc1.Width = 26
|
||||
lc1.Height = 11
|
||||
lc1.X = 51
|
||||
lc1.Y = 14
|
||||
|
||||
p1 := ui.NewPar("Hey!\nI am a borderless block!")
|
||||
p1.HasBorder = false
|
||||
p1.Width = 26
|
||||
p1.Height = 2
|
||||
p1.X = 52
|
||||
p1.Y = 11
|
||||
|
||||
draw := func(t int) {
|
||||
g.Percent = t % 101
|
||||
list.Items = strs[t%9:]
|
||||
sp.Lines[0].Data = spdata[t%10:]
|
||||
sp.Lines[1].Data = spdata[t/2%10:]
|
||||
lc.Data = sinps[t/2:]
|
||||
lc1.Data = rndwalk[t:]
|
||||
bc.Data = bcdata[t/2%10:]
|
||||
ui.Render(p, list, g, sp, lc, bc, lc1, p1)
|
||||
}
|
||||
|
||||
evt := ui.EventCh()
|
||||
i := 0
|
||||
for {
|
||||
select {
|
||||
case e := <-evt:
|
||||
if e.Type == ui.EventKey && e.Ch == 'q' {
|
||||
return
|
||||
}
|
||||
default:
|
||||
draw(i)
|
||||
i++
|
||||
if i == 102 {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Second / 2)
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 103 KiB |
After Width: | Height: | Size: 88 KiB |
|
@ -0,0 +1,369 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gizak/termui"
|
||||
"github.com/gizak/termui/extra"
|
||||
)
|
||||
|
||||
const statFilePath = "/proc/stat"
|
||||
const meminfoFilePath = "/proc/meminfo"
|
||||
|
||||
type CpuStat struct {
|
||||
user float32
|
||||
nice float32
|
||||
system float32
|
||||
idle float32
|
||||
}
|
||||
|
||||
type CpusStats struct {
|
||||
stat map[string]CpuStat
|
||||
proc map[string]CpuStat
|
||||
}
|
||||
|
||||
func NewCpusStats(s map[string]CpuStat) *CpusStats {
|
||||
return &CpusStats{stat: s, proc: make(map[string]CpuStat)}
|
||||
}
|
||||
|
||||
func (cs *CpusStats) String() (ret string) {
|
||||
for key, _ := range cs.proc {
|
||||
ret += fmt.Sprintf("%s: %.2f %.2f %.2f %.2f\n", key, cs.proc[key].user, cs.proc[key].nice, cs.proc[key].system, cs.proc[key].idle)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func subCpuStat(m CpuStat, s CpuStat) CpuStat {
|
||||
return CpuStat{user: m.user - s.user,
|
||||
nice: m.nice - s.nice,
|
||||
system: m.system - s.system,
|
||||
idle: m.idle - s.idle}
|
||||
}
|
||||
|
||||
func procCpuStat(c CpuStat) CpuStat {
|
||||
sum := c.user + c.nice + c.system + c.idle
|
||||
return CpuStat{user: c.user / sum * 100,
|
||||
nice: c.nice / sum * 100,
|
||||
system: c.system / sum * 100,
|
||||
idle: c.idle / sum * 100}
|
||||
}
|
||||
|
||||
func (cs *CpusStats) tick(ns map[string]CpuStat) {
|
||||
for key, _ := range cs.stat {
|
||||
proc := subCpuStat(ns[key], cs.stat[key])
|
||||
cs.proc[key] = procCpuStat(proc)
|
||||
cs.stat[key] = ns[key]
|
||||
}
|
||||
}
|
||||
|
||||
type errIntParser struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (eip *errIntParser) parse(s string) (ret int64) {
|
||||
if eip.err != nil {
|
||||
return 0
|
||||
}
|
||||
ret, eip.err = strconv.ParseInt(s, 10, 0)
|
||||
return
|
||||
}
|
||||
|
||||
type LineProcessor interface {
|
||||
process(string) error
|
||||
finalize() interface{}
|
||||
}
|
||||
|
||||
type CpuLineProcessor struct {
|
||||
m map[string]CpuStat
|
||||
}
|
||||
|
||||
func (clp *CpuLineProcessor) process(line string) (err error) {
|
||||
r := regexp.MustCompile("^cpu([0-9]*)")
|
||||
|
||||
if r.MatchString(line) {
|
||||
tab := strings.Fields(line)
|
||||
if len(tab) < 5 {
|
||||
err = errors.New("cpu info line has not enough fields")
|
||||
return
|
||||
}
|
||||
parser := errIntParser{}
|
||||
cs := CpuStat{user: float32(parser.parse(tab[1])),
|
||||
nice: float32(parser.parse(tab[2])),
|
||||
system: float32(parser.parse(tab[3])),
|
||||
idle: float32(parser.parse(tab[4]))}
|
||||
clp.m[tab[0]] = cs
|
||||
err = parser.err
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (clp *CpuLineProcessor) finalize() interface{} {
|
||||
return clp.m
|
||||
}
|
||||
|
||||
type MemStat struct {
|
||||
total int64
|
||||
free int64
|
||||
}
|
||||
|
||||
func (ms MemStat) String() (ret string) {
|
||||
ret = fmt.Sprintf("TotalMem: %d, FreeMem: %d\n", ms.total, ms.free)
|
||||
return
|
||||
}
|
||||
|
||||
func (ms *MemStat) process(line string) (err error) {
|
||||
rtotal := regexp.MustCompile("^MemTotal:")
|
||||
rfree := regexp.MustCompile("^MemFree:")
|
||||
var aux int64
|
||||
if rtotal.MatchString(line) || rfree.MatchString(line) {
|
||||
tab := strings.Fields(line)
|
||||
if len(tab) < 3 {
|
||||
err = errors.New("mem info line has not enough fields")
|
||||
return
|
||||
}
|
||||
aux, err = strconv.ParseInt(tab[1], 10, 0)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if rtotal.MatchString(line) {
|
||||
ms.total = aux
|
||||
}
|
||||
if rfree.MatchString(line) {
|
||||
ms.free = aux
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ms *MemStat) finalize() interface{} {
|
||||
return *ms
|
||||
}
|
||||
|
||||
func processFileLines(filePath string, lp LineProcessor) (ret interface{}, err error) {
|
||||
var statFile *os.File
|
||||
statFile, err = os.Open(filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("open: %v\n", err)
|
||||
}
|
||||
defer statFile.Close()
|
||||
|
||||
statFileReader := bufio.NewReader(statFile)
|
||||
|
||||
for {
|
||||
var line string
|
||||
line, err = statFileReader.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf("open: %v\n", err)
|
||||
break
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
err = lp.process(line)
|
||||
}
|
||||
|
||||
ret = lp.finalize()
|
||||
return
|
||||
}
|
||||
|
||||
func getCpusStatsMap() (m map[string]CpuStat, err error) {
|
||||
var aux interface{}
|
||||
aux, err = processFileLines(statFilePath, &CpuLineProcessor{m: make(map[string]CpuStat)})
|
||||
return aux.(map[string]CpuStat), err
|
||||
}
|
||||
|
||||
func getMemStats() (ms MemStat, err error) {
|
||||
var aux interface{}
|
||||
aux, err = processFileLines(meminfoFilePath, &MemStat{})
|
||||
return aux.(MemStat), err
|
||||
}
|
||||
|
||||
type CpuTabElems struct {
|
||||
GMap map[string]*termui.Gauge
|
||||
LChart *termui.LineChart
|
||||
}
|
||||
|
||||
func NewCpuTabElems(width int) *CpuTabElems {
|
||||
lc := termui.NewLineChart()
|
||||
lc.Width = width
|
||||
lc.Height = 12
|
||||
lc.X = 0
|
||||
lc.Mode = "dot"
|
||||
lc.BorderLabel = "CPU"
|
||||
return &CpuTabElems{GMap: make(map[string]*termui.Gauge),
|
||||
LChart: lc}
|
||||
}
|
||||
|
||||
func (cte *CpuTabElems) AddGauge(key string, Y int, width int) *termui.Gauge {
|
||||
cte.GMap[key] = termui.NewGauge()
|
||||
cte.GMap[key].Width = width
|
||||
cte.GMap[key].Height = 3
|
||||
cte.GMap[key].Y = Y
|
||||
cte.GMap[key].BorderLabel = key
|
||||
cte.GMap[key].Percent = 0 //int(val.user + val.nice + val.system)
|
||||
return cte.GMap[key]
|
||||
}
|
||||
|
||||
func (cte *CpuTabElems) Update(cs CpusStats) {
|
||||
for key, val := range cs.proc {
|
||||
p := int(val.user + val.nice + val.system)
|
||||
cte.GMap[key].Percent = p
|
||||
if key == "cpu" {
|
||||
cte.LChart.Data = append(cte.LChart.Data, 0)
|
||||
copy(cte.LChart.Data[1:], cte.LChart.Data[0:])
|
||||
cte.LChart.Data[0] = float64(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type MemTabElems struct {
|
||||
Gauge *termui.Gauge
|
||||
SLines *termui.Sparklines
|
||||
}
|
||||
|
||||
func NewMemTabElems(width int) *MemTabElems {
|
||||
g := termui.NewGauge()
|
||||
g.Width = width
|
||||
g.Height = 3
|
||||
g.Y = 0
|
||||
|
||||
sline := termui.NewSparkline()
|
||||
sline.Title = "MEM"
|
||||
sline.Height = 8
|
||||
|
||||
sls := termui.NewSparklines(sline)
|
||||
sls.Width = width
|
||||
sls.Height = 12
|
||||
sls.Y = 3
|
||||
return &MemTabElems{Gauge: g, SLines: sls}
|
||||
}
|
||||
|
||||
func (mte *MemTabElems) Update(ms MemStat) {
|
||||
used := int((ms.total - ms.free) * 100 / ms.total)
|
||||
mte.Gauge.Percent = used
|
||||
mte.SLines.Lines[0].Data = append(mte.SLines.Lines[0].Data, 0)
|
||||
copy(mte.SLines.Lines[0].Data[1:], mte.SLines.Lines[0].Data[0:])
|
||||
mte.SLines.Lines[0].Data[0] = used
|
||||
if len(mte.SLines.Lines[0].Data) > mte.SLines.Width-2 {
|
||||
mte.SLines.Lines[0].Data = mte.SLines.Lines[0].Data[0 : mte.SLines.Width-2]
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if runtime.GOOS != "linux" {
|
||||
panic("Currently works only on Linux")
|
||||
}
|
||||
err := termui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
termWidth := 70
|
||||
|
||||
//termui.UseTheme("helloworld")
|
||||
|
||||
header := termui.NewPar("Press q to quit, Press j or k to switch tabs")
|
||||
header.Height = 1
|
||||
header.Width = 50
|
||||
header.Border = false
|
||||
header.TextBgColor = termui.ColorBlue
|
||||
|
||||
tabCpu := extra.NewTab("CPU")
|
||||
tabMem := extra.NewTab("MEM")
|
||||
|
||||
tabpane := extra.NewTabpane()
|
||||
tabpane.Y = 1
|
||||
tabpane.Width = 30
|
||||
tabpane.Border = false
|
||||
|
||||
cs, errcs := getCpusStatsMap()
|
||||
cpusStats := NewCpusStats(cs)
|
||||
|
||||
if errcs != nil {
|
||||
panic("error")
|
||||
}
|
||||
|
||||
cpuTabElems := NewCpuTabElems(termWidth)
|
||||
|
||||
Y := 0
|
||||
cpuKeys := make([]string, 0, len(cs))
|
||||
for key := range cs {
|
||||
cpuKeys = append(cpuKeys, key)
|
||||
}
|
||||
sort.Strings(cpuKeys)
|
||||
for _, key := range cpuKeys {
|
||||
g := cpuTabElems.AddGauge(key, Y, termWidth)
|
||||
Y += 3
|
||||
tabCpu.AddBlocks(g)
|
||||
}
|
||||
cpuTabElems.LChart.Y = Y
|
||||
tabCpu.AddBlocks(cpuTabElems.LChart)
|
||||
|
||||
memTabElems := NewMemTabElems(termWidth)
|
||||
ms, errm := getMemStats()
|
||||
if errm != nil {
|
||||
panic(errm)
|
||||
}
|
||||
memTabElems.Update(ms)
|
||||
tabMem.AddBlocks(memTabElems.Gauge)
|
||||
tabMem.AddBlocks(memTabElems.SLines)
|
||||
|
||||
tabpane.SetTabs(*tabCpu, *tabMem)
|
||||
|
||||
termui.Render(header, tabpane)
|
||||
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
termui.StopLoop()
|
||||
})
|
||||
|
||||
termui.Handle("/sys/kbd/j", func(termui.Event) {
|
||||
tabpane.SetActiveLeft()
|
||||
termui.Render(header, tabpane)
|
||||
})
|
||||
|
||||
termui.Handle("/sys/kbd/k", func(termui.Event) {
|
||||
tabpane.SetActiveRight()
|
||||
termui.Render(header, tabpane)
|
||||
})
|
||||
|
||||
termui.Handle("/timer/1s", func(e termui.Event) {
|
||||
cs, errcs := getCpusStatsMap()
|
||||
if errcs != nil {
|
||||
panic(errcs)
|
||||
}
|
||||
cpusStats.tick(cs)
|
||||
cpuTabElems.Update(*cpusStats)
|
||||
|
||||
ms, errm := getMemStats()
|
||||
if errm != nil {
|
||||
panic(errm)
|
||||
}
|
||||
memTabElems.Update(ms)
|
||||
termui.Render(header, tabpane)
|
||||
})
|
||||
|
||||
termui.Loop()
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import ui "github.com/gizak/termui"
|
||||
|
||||
func main() {
|
||||
|
||||
err := ui.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer ui.Close()
|
||||
|
||||
p := ui.NewPar("Press q to QUIT THE DEMO. [There](fg-blue) are other things [that](fg-red) are going to fit in here I think. What do you think? Now is the time for all good [men to](bg-blue) come to the aid of their country. [This is going to be one really really really long line](fg-green) that is going to go together and stuffs and things. Let's see how this thing renders out.\n Here is a new paragraph and stuffs and things. There should be a tab indent at the beginning of the paragraph. Let's see if that worked as well.")
|
||||
p.WrapLength = 48 // this should be at least p.Width - 2
|
||||
p.Height = 20
|
||||
p.Width = 50
|
||||
p.Y = 2
|
||||
p.X = 20
|
||||
p.TextFgColor = ui.ColorWhite
|
||||
p.BorderLabel = "Text Box with Wrapping"
|
||||
p.BorderFg = ui.ColorCyan
|
||||
//p.Border = false
|
||||
|
||||
ui.Render(p)
|
||||
|
||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
||||
ui.StopLoop()
|
||||
})
|
||||
|
||||
ui.Loop()
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Port string
|
||||
Addr string
|
||||
Path string
|
||||
Msg chan string
|
||||
chs []chan string
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
Port string
|
||||
Addr string
|
||||
Path string
|
||||
ws *websocket.Conn
|
||||
}
|
||||
|
||||
var defaultPort = ":8080"
|
||||
|
||||
func NewServer() *Server {
|
||||
return &Server{
|
||||
Port: defaultPort,
|
||||
Addr: "localhost",
|
||||
Path: "/echo",
|
||||
Msg: make(chan string),
|
||||
chs: make([]chan string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func NewClient() Client {
|
||||
return Client{
|
||||
Port: defaultPort,
|
||||
Addr: "localhost",
|
||||
Path: "/echo",
|
||||
}
|
||||
}
|
||||
|
||||
func (c Client) ConnectAndListen() error {
|
||||
ws, err := websocket.Dial("ws://"+c.Addr+c.Port+c.Path, "", "http://"+c.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ws.Close()
|
||||
|
||||
var m string
|
||||
for {
|
||||
err := websocket.Message.Receive(ws, &m)
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
return err
|
||||
}
|
||||
fmt.Print(m)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) ListenAndServe() error {
|
||||
http.Handle(s.Path, websocket.Handler(func(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
|
||||
mc := make(chan string)
|
||||
s.chs = append(s.chs, mc)
|
||||
|
||||
for m := range mc {
|
||||
websocket.Message.Send(ws, m)
|
||||
}
|
||||
}))
|
||||
|
||||
go func() {
|
||||
for msg := range s.Msg {
|
||||
for _, c := range s.chs {
|
||||
go func(a chan string) {
|
||||
a <- msg
|
||||
}(c)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return http.ListenAndServe(s.Port, nil)
|
||||
}
|
||||
|
||||
func (s *Server) Log(msg string) {
|
||||
go func() { s.Msg <- msg }()
|
||||
}
|
||||
|
||||
func (s *Server) Logf(format string, a ...interface{}) {
|
||||
s.Log(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
var DefaultServer = NewServer()
|
||||
var DefaultClient = NewClient()
|
||||
|
||||
func ListenAndServe() error {
|
||||
return DefaultServer.ListenAndServe()
|
||||
}
|
||||
|
||||
func ConnectAndListen() error {
|
||||
return DefaultClient.ConnectAndListen()
|
||||
}
|
||||
|
||||
func Log(msg string) {
|
||||
DefaultServer.Log(msg)
|
||||
}
|
||||
|
||||
func Logf(format string, a ...interface{}) {
|
||||
DefaultServer.Logf(format, a...)
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
package extra
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
. "github.com/gizak/termui"
|
||||
)
|
||||
|
||||
type Tab struct {
|
||||
Label string
|
||||
RuneLen int
|
||||
Blocks []Bufferer
|
||||
}
|
||||
|
||||
func NewTab(label string) *Tab {
|
||||
return &Tab{
|
||||
Label: label,
|
||||
RuneLen: utf8.RuneCount([]byte(label))}
|
||||
}
|
||||
|
||||
func (tab *Tab) AddBlocks(rs ...Bufferer) {
|
||||
for _, r := range rs {
|
||||
tab.Blocks = append(tab.Blocks, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (tab *Tab) Buffer() Buffer {
|
||||
buf := NewBuffer()
|
||||
for blockNum := 0; blockNum < len(tab.Blocks); blockNum++ {
|
||||
b := tab.Blocks[blockNum]
|
||||
buf.Merge(b.Buffer())
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
type Tabpane struct {
|
||||
Block
|
||||
Tabs []Tab
|
||||
activeTabIndex int
|
||||
ActiveTabBg Attribute
|
||||
posTabText []int
|
||||
offTabText int
|
||||
}
|
||||
|
||||
func NewTabpane() *Tabpane {
|
||||
tp := Tabpane{
|
||||
Block: *NewBlock(),
|
||||
activeTabIndex: 0,
|
||||
offTabText: 0,
|
||||
ActiveTabBg: ThemeAttr("bg.tab.active")}
|
||||
return &tp
|
||||
}
|
||||
|
||||
func (tp *Tabpane) SetTabs(tabs ...Tab) {
|
||||
tp.Tabs = make([]Tab, len(tabs))
|
||||
tp.posTabText = make([]int, len(tabs)+1)
|
||||
off := 0
|
||||
for i := 0; i < len(tp.Tabs); i++ {
|
||||
tp.Tabs[i] = tabs[i]
|
||||
tp.posTabText[i] = off
|
||||
off += tp.Tabs[i].RuneLen + 1 //+1 for space between tabs
|
||||
}
|
||||
tp.posTabText[len(tabs)] = off - 1 //total length of Tab's text
|
||||
}
|
||||
|
||||
func (tp *Tabpane) SetActiveLeft() {
|
||||
if tp.activeTabIndex == 0 {
|
||||
return
|
||||
}
|
||||
tp.activeTabIndex -= 1
|
||||
if tp.posTabText[tp.activeTabIndex] < tp.offTabText {
|
||||
tp.offTabText = tp.posTabText[tp.activeTabIndex]
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *Tabpane) SetActiveRight() {
|
||||
if tp.activeTabIndex == len(tp.Tabs)-1 {
|
||||
return
|
||||
}
|
||||
tp.activeTabIndex += 1
|
||||
endOffset := tp.posTabText[tp.activeTabIndex] + tp.Tabs[tp.activeTabIndex].RuneLen
|
||||
if endOffset+tp.offTabText > tp.InnerWidth() {
|
||||
tp.offTabText = endOffset - tp.InnerWidth()
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if left and right tabs are fully visible
|
||||
// if only left tabs are not visible return -1
|
||||
// if only right tabs are not visible return 1
|
||||
// if both return 0
|
||||
// use only if fitsWidth() returns false
|
||||
func (tp *Tabpane) checkAlignment() int {
|
||||
ret := 0
|
||||
if tp.offTabText > 0 {
|
||||
ret = -1
|
||||
}
|
||||
if tp.offTabText+tp.InnerWidth() < tp.posTabText[len(tp.Tabs)] {
|
||||
ret += 1
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Checks if all tabs fits innerWidth of Tabpane
|
||||
func (tp *Tabpane) fitsWidth() bool {
|
||||
return tp.InnerWidth() >= tp.posTabText[len(tp.Tabs)]
|
||||
}
|
||||
|
||||
func (tp *Tabpane) align() {
|
||||
if !tp.fitsWidth() && !tp.Border {
|
||||
tp.PaddingLeft += 1
|
||||
tp.PaddingRight += 1
|
||||
tp.Block.Align()
|
||||
}
|
||||
}
|
||||
|
||||
// bridge the old Point stuct
|
||||
type point struct {
|
||||
X int
|
||||
Y int
|
||||
Ch rune
|
||||
Fg Attribute
|
||||
Bg Attribute
|
||||
}
|
||||
|
||||
func buf2pt(b Buffer) []point {
|
||||
ps := make([]point, 0, len(b.CellMap))
|
||||
for k, c := range b.CellMap {
|
||||
ps = append(ps, point{X: k.X, Y: k.Y, Ch: c.Ch, Fg: c.Fg, Bg: c.Bg})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
// Adds the point only if it is visible in Tabpane.
|
||||
// Point can be invisible if concatenation of Tab's texts is widther then
|
||||
// innerWidth of Tabpane
|
||||
func (tp *Tabpane) addPoint(ptab []point, charOffset *int, oftX *int, points ...point) []point {
|
||||
if *charOffset < tp.offTabText || tp.offTabText+tp.InnerWidth() < *charOffset {
|
||||
*charOffset++
|
||||
return ptab
|
||||
}
|
||||
for _, p := range points {
|
||||
p.X = *oftX
|
||||
ptab = append(ptab, p)
|
||||
}
|
||||
*oftX++
|
||||
*charOffset++
|
||||
return ptab
|
||||
}
|
||||
|
||||
// Draws the point and redraws upper and lower border points (if it has one)
|
||||
func (tp *Tabpane) drawPointWithBorder(p point, ch rune, chbord rune, chdown rune, chup rune) []point {
|
||||
var addp []point
|
||||
p.Ch = ch
|
||||
if tp.Border {
|
||||
p.Ch = chdown
|
||||
p.Y = tp.InnerY() - 1
|
||||
addp = append(addp, p)
|
||||
p.Ch = chup
|
||||
p.Y = tp.InnerY() + 1
|
||||
addp = append(addp, p)
|
||||
p.Ch = chbord
|
||||
}
|
||||
p.Y = tp.InnerY()
|
||||
return append(addp, p)
|
||||
}
|
||||
|
||||
func (tp *Tabpane) Buffer() Buffer {
|
||||
if tp.Border {
|
||||
tp.Height = 3
|
||||
} else {
|
||||
tp.Height = 1
|
||||
}
|
||||
if tp.Width > tp.posTabText[len(tp.Tabs)]+2 {
|
||||
tp.Width = tp.posTabText[len(tp.Tabs)] + 2
|
||||
}
|
||||
buf := tp.Block.Buffer()
|
||||
ps := []point{}
|
||||
|
||||
tp.align()
|
||||
if tp.InnerHeight() <= 0 || tp.InnerWidth() <= 0 {
|
||||
return NewBuffer()
|
||||
}
|
||||
oftX := tp.InnerX()
|
||||
charOffset := 0
|
||||
pt := point{Bg: tp.BorderBg, Fg: tp.BorderFg}
|
||||
for i, tab := range tp.Tabs {
|
||||
|
||||
if i != 0 {
|
||||
pt.X = oftX
|
||||
pt.Y = tp.InnerY()
|
||||
addp := tp.drawPointWithBorder(pt, ' ', VERTICAL_LINE, HORIZONTAL_DOWN, HORIZONTAL_UP)
|
||||
ps = tp.addPoint(ps, &charOffset, &oftX, addp...)
|
||||
}
|
||||
|
||||
if i == tp.activeTabIndex {
|
||||
pt.Bg = tp.ActiveTabBg
|
||||
}
|
||||
rs := []rune(tab.Label)
|
||||
for k := 0; k < len(rs); k++ {
|
||||
|
||||
addp := make([]point, 0, 2)
|
||||
if i == tp.activeTabIndex && tp.Border {
|
||||
pt.Ch = ' '
|
||||
pt.Y = tp.InnerY() + 1
|
||||
pt.Bg = tp.BorderBg
|
||||
addp = append(addp, pt)
|
||||
pt.Bg = tp.ActiveTabBg
|
||||
}
|
||||
|
||||
pt.Y = tp.InnerY()
|
||||
pt.Ch = rs[k]
|
||||
|
||||
addp = append(addp, pt)
|
||||
ps = tp.addPoint(ps, &charOffset, &oftX, addp...)
|
||||
}
|
||||
pt.Bg = tp.BorderBg
|
||||
|
||||
if !tp.fitsWidth() {
|
||||
all := tp.checkAlignment()
|
||||
pt.X = tp.InnerX() - 1
|
||||
|
||||
pt.Ch = '*'
|
||||
if tp.Border {
|
||||
pt.Ch = VERTICAL_LINE
|
||||
}
|
||||
ps = append(ps, pt)
|
||||
|
||||
if all <= 0 {
|
||||
addp := tp.drawPointWithBorder(pt, '<', '«', HORIZONTAL_LINE, HORIZONTAL_LINE)
|
||||
ps = append(ps, addp...)
|
||||
}
|
||||
|
||||
pt.X = tp.InnerX() + tp.InnerWidth()
|
||||
pt.Ch = '*'
|
||||
if tp.Border {
|
||||
pt.Ch = VERTICAL_LINE
|
||||
}
|
||||
ps = append(ps, pt)
|
||||
if all >= 0 {
|
||||
addp := tp.drawPointWithBorder(pt, '>', '»', HORIZONTAL_LINE, HORIZONTAL_LINE)
|
||||
ps = append(ps, addp...)
|
||||
}
|
||||
}
|
||||
|
||||
//draw tab content below the Tabpane
|
||||
if i == tp.activeTabIndex {
|
||||
blockPoints := buf2pt(tab.Buffer())
|
||||
for i := 0; i < len(blockPoints); i++ {
|
||||
blockPoints[i].Y += tp.Height + tp.Y
|
||||
}
|
||||
ps = append(ps, blockPoints...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range ps {
|
||||
buf.Set(v.X, v.Y, NewCell(v.Ch, v.Fg, v.Bg))
|
||||
}
|
||||
buf.Sync()
|
||||
return buf
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gizak/termui"
|
||||
"github.com/gizak/termui/debug"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// run as client
|
||||
if len(os.Args) > 1 {
|
||||
fmt.Print(debug.ConnectAndListen())
|
||||
return
|
||||
}
|
||||
|
||||
// run as server
|
||||
go func() { panic(debug.ListenAndServe()) }()
|
||||
|
||||
if err := termui.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termui.Close()
|
||||
|
||||
//termui.UseTheme("helloworld")
|
||||
b := termui.NewBlock()
|
||||
b.Width = 20
|
||||
b.Height = 20
|
||||
b.Float = termui.AlignCenter
|
||||
b.BorderLabel = "[HELLO](fg-red,bg-white) [WORLD](fg-blue,bg-green)"
|
||||
|
||||
termui.Render(b)
|
||||
|
||||
termui.Handle("/sys", func(e termui.Event) {
|
||||
k, ok := e.Data.(termui.EvtKbd)
|
||||
debug.Logf("->%v\n", e)
|
||||
if ok && k.KeyStr == "q" {
|
||||
termui.StopLoop()
|
||||
}
|
||||
})
|
||||
|
||||
termui.Handle(("/usr"), func(e termui.Event) {
|
||||
debug.Logf("->%v\n", e)
|
||||
})
|
||||
|
||||
termui.Handle("/timer/1s", func(e termui.Event) {
|
||||
t := e.Data.(termui.EvtTimer)
|
||||
termui.SendCustomEvt("/usr/t", t.Count)
|
||||
|
||||
if t.Count%2 == 0 {
|
||||
b.BorderLabel = "[HELLO](fg-red,bg-green) [WORLD](fg-blue,bg-white)"
|
||||
} else {
|
||||
b.BorderLabel = "[HELLO](fg-blue,bg-white) [WORLD](fg-red,bg-green)"
|
||||
}
|
||||
|
||||
termui.Render(b)
|
||||
|
||||
})
|
||||
|
||||
termui.Loop()
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
Usage: app [-bdsuikqs] BOOL1 [STR1] INT3... COMMAND [arg...]
|
||||
|
||||
App Desc
|
||||
|
||||
Arguments:
|
||||
BOOL1 Bool Argument 1 (env $BOOL1)
|
||||
BOOL2 Bool Argument 2 (default true)
|
||||
BOOL3 Bool Argument 3 (env $BOOL3)
|
||||
STR1 String Argument 1 (env $STR1)
|
||||
STR2 String Argument 2 (env $STR2) (default "a value")
|
||||
STR3 String Argument 3 (env $STR3)
|
||||
INT1 Int Argument 1 (env $INT1) (default 0)
|
||||
INT2 Int Argument 2 (env $INT2) (default 1)
|
||||
INT3 Int Argument 3 (env $INT3)
|
||||
STRS1 Strings Argument 1 (env $STRS1)
|
||||
STRS2 (env $STRS2) (default ["value1", "value2"])
|
||||
STRS3 Strings Argument 3 (env $STRS3)
|
||||
INTS1 Ints Argument 1 (env $INTS1)
|
||||
INTS2 Ints Argument 2 (env $INTS2) (default [1, 2, 3])
|
||||
INTS3 Ints Argument 3 (env $INTS3)
|
||||
|
||||
Options:
|
||||
-b, --bool1 Bool Option 1 (env $BOOL1)
|
||||
--bool2 Bool Option 2 (default true)
|
||||
-d Bool Option 3 (env $BOOL3)
|
||||
-s, --str1 String Option 1 (env $STR1)
|
||||
--str2 String Option 2 (default "a value")
|
||||
-u String Option 3 (env $STR3)
|
||||
-i, --int1 (env $INT1, $ALIAS_INT1) (default 0)
|
||||
--int2 Int Option 2 (env $INT2) (default 1)
|
||||
-k Int Option 3 (env $INT3)
|
||||
-x, --strs1 Strings Option 1 (env $STRS1)
|
||||
--strs2 Strings Option 2 (env $STRS2) (default ["value1", "value2"])
|
||||
-z Strings Option 3 (env $STRS3)
|
||||
-q, --ints1 Ints Option 1 (env $INTS1)
|
||||
--ints2 Ints Option 2 (env $INTS2) (default [1, 2, 3])
|
||||
-s Ints Option 3 (env $INTS3)
|
||||
|
||||
Commands:
|
||||
command1 command1 description
|
||||
command2 command2 description
|
||||
command3 command3 description
|
||||
|
||||
Run 'app COMMAND --help' for more information on a command.
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
Usage: app [-o] ARG
|
||||
|
||||
Longer App Desc
|
||||
|
||||
Arguments:
|
||||
ARG Argument
|
||||
|
||||
Options:
|
||||
-o, --opt Option
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2015 Marc-Antoine Ruel. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// panicparse: analyzes stack dump of Go processes and simplifies it.
|
||||
//
|
||||
// It is mostly useful on servers will large number of identical goroutines,
|
||||
// making the crash dump harder to read than strictly necesary.
|
||||
//
|
||||
// Colors:
|
||||
// - Magenta: first goroutine to be listed.
|
||||
// - Yellow: main package.
|
||||
// - Green: standard library.
|
||||
// - Red: other packages.
|
||||
//
|
||||
// Bright colors are used for exported symbols.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/maruel/panicparse/internal"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := internal.Main(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2016 Marc-Antoine Ruel. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.6
|
||||
|
||||
package internal
|
||||
|
||||
const (
|
||||
showGOTRACEBACKBanner = true
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright 2016 Marc-Antoine Ruel. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.1
|
||||
// +build !go1.6
|
||||
|
||||
package internal
|
||||
|
||||
const (
|
||||
showGOTRACEBACKBanner = false
|
||||
)
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright 2015 Marc-Antoine Ruel. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package internal implements panicparse
|
||||
//
|
||||
// It is mostly useful on servers will large number of identical goroutines,
|
||||
// making the crash dump harder to read than strictly necesary.
|
||||
//
|
||||
// Colors:
|
||||
// - Magenta: first goroutine to be listed.
|
||||
// - Yellow: main package.
|
||||
// - Green: standard library.
|
||||
// - Red: other packages.
|
||||
//
|
||||
// Bright colors are used for exported symbols.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/maruel/panicparse/stack"
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/mgutz/ansi"
|
||||
)
|
||||
|
||||
// resetFG is similar to ansi.Reset except that it doesn't reset the
|
||||
// background color, only the foreground color and the style.
|
||||
//
|
||||
// That much for the "ansi" abstraction layer...
|
||||
const resetFG = ansi.DefaultFG + "\033[m"
|
||||
|
||||
// defaultPalette is the default recommended palette.
|
||||
var defaultPalette = stack.Palette{
|
||||
EOLReset: resetFG,
|
||||
RoutineFirst: ansi.ColorCode("magenta+b"),
|
||||
CreatedBy: ansi.LightBlack,
|
||||
Package: ansi.ColorCode("default+b"),
|
||||
SourceFile: resetFG,
|
||||
FunctionStdLib: ansi.Green,
|
||||
FunctionStdLibExported: ansi.ColorCode("green+b"),
|
||||
FunctionMain: ansi.ColorCode("yellow+b"),
|
||||
FunctionOther: ansi.Red,
|
||||
FunctionOtherExported: ansi.ColorCode("red+b"),
|
||||
Arguments: resetFG,
|
||||
}
|
||||
|
||||
// process copies stdin to stdout and processes any "panic: " line found.
|
||||
func process(in io.Reader, out io.Writer, p *stack.Palette, s stack.Similarity, fullPath, parse bool) error {
|
||||
goroutines, err := stack.ParseDump(in, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(goroutines) == 1 && showBanner() {
|
||||
_, _ = io.WriteString(out, "\nTo see all goroutines, visit https://github.com/maruel/panicparse#GOTRACEBACK\n\n")
|
||||
}
|
||||
if parse {
|
||||
stack.Augment(goroutines)
|
||||
}
|
||||
buckets := stack.SortBuckets(stack.Bucketize(goroutines, s))
|
||||
srcLen, pkgLen := stack.CalcLengths(buckets, fullPath)
|
||||
for _, bucket := range buckets {
|
||||
_, _ = io.WriteString(out, p.BucketHeader(&bucket, fullPath, len(buckets) > 1))
|
||||
_, _ = io.WriteString(out, p.StackLines(&bucket.Signature, srcLen, pkgLen, fullPath))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func showBanner() bool {
|
||||
if !showGOTRACEBACKBanner {
|
||||
return false
|
||||
}
|
||||
gtb := os.Getenv("GOTRACEBACK")
|
||||
return gtb == "" || gtb == "single"
|
||||
}
|
||||
|
||||
// Main is implemented here so both 'pp' and 'panicparse' executables can be
|
||||
// compiled. This is to work around the Perl Package manager 'pp' that is
|
||||
// preinstalled on some OSes.
|
||||
func Main() error {
|
||||
signals := make(chan os.Signal)
|
||||
go func() {
|
||||
for {
|
||||
<-signals
|
||||
}
|
||||
}()
|
||||
signal.Notify(signals, os.Interrupt, syscall.SIGQUIT)
|
||||
aggressive := flag.Bool("aggressive", false, "Aggressive deduplication including non pointers")
|
||||
fullPath := flag.Bool("full-path", false, "Print full sources path")
|
||||
noColor := flag.Bool("no-color", !isatty.IsTerminal(os.Stdout.Fd()) || os.Getenv("TERM") == "dumb", "Disable coloring")
|
||||
forceColor := flag.Bool("force-color", false, "Forcibly enable coloring when with stdout is redirected")
|
||||
parse := flag.Bool("parse", true, "Parses source files to deduct types; use -parse=false to work around bugs in source parser")
|
||||
verboseFlag := flag.Bool("v", false, "Enables verbose logging output")
|
||||
flag.Parse()
|
||||
|
||||
log.SetFlags(log.Lmicroseconds)
|
||||
if !*verboseFlag {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
}
|
||||
|
||||
s := stack.AnyPointer
|
||||
if *aggressive {
|
||||
s = stack.AnyValue
|
||||
}
|
||||
|
||||
var out io.Writer
|
||||
p := &defaultPalette
|
||||
if *noColor && !*forceColor {
|
||||
p = &stack.Palette{}
|
||||
out = os.Stdout
|
||||
} else {
|
||||
out = colorable.NewColorableStdout()
|
||||
}
|
||||
|
||||
var in *os.File
|
||||
switch flag.NArg() {
|
||||
case 0:
|
||||
in = os.Stdin
|
||||
case 1:
|
||||
var err error
|
||||
name := flag.Arg(0)
|
||||
if in, err = os.Open(name); err != nil {
|
||||
return fmt.Errorf("did you mean to specify a valid stack dump file name? %s", err)
|
||||
}
|
||||
defer in.Close()
|
||||
default:
|
||||
return errors.New("pipe from stdin or specify a single file")
|
||||
}
|
||||
return process(in, out, p, s, *fullPath, *parse)
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
// Copyright 2015 Marc-Antoine Ruel. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/maruel/panicparse/stack"
|
||||
"github.com/maruel/ut"
|
||||
)
|
||||
|
||||
var data = []string{
|
||||
"panic: runtime error: index out of range",
|
||||
"",
|
||||
"goroutine 11 [running, 5 minutes, locked to thread]:",
|
||||
"github.com/luci/luci-go/client/archiver.(*archiver).PushFile(0xc208032410, 0xc20968a3c0, 0x5b, 0xc20988c280, 0x7d, 0x0, 0x0)",
|
||||
" /gopath/path/to/archiver.go:325 +0x2c4",
|
||||
"github.com/luci/luci-go/client/isolate.archive(0x7fbdab7a5218, 0xc208032410, 0xc20803b0b0, 0x22, 0xc208046370, 0xc20804666a, 0x17, 0x0, 0x0, 0x0, ...)",
|
||||
" /gopath/path/to/isolate.go:148 +0x12d2",
|
||||
"github.com/luci/luci-go/client/isolate.Archive(0x7fbdab7a5218, 0xc208032410, 0xc20803b0b0, 0x22, 0xc208046370, 0x0, 0x0)",
|
||||
" /gopath/path/to/isolate.go:102 +0xc9",
|
||||
"main.func·004(0x7fffc3b8f13a, 0x2c)",
|
||||
" /gopath/path/to/batch_archive.go:166 +0x7cd",
|
||||
"created by main.(*batchArchiveRun).main",
|
||||
" /gopath/path/to/batch_archive.go:167 +0x42c",
|
||||
"",
|
||||
"goroutine 1 [running]:",
|
||||
"gopkg.in/yaml%2ev2.handleErr(0xc208033b20)",
|
||||
" /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
|
||||
"reflect.Value.assignTo(0x570860, 0xc20803f3e0, 0x15)",
|
||||
" c:/go/src/reflect/value.go:2125 +0x368",
|
||||
"main.main()",
|
||||
" /gopath/src/github.com/maruel/pre-commit-go/main.go:428 +0x27",
|
||||
"",
|
||||
"goroutine 2 [running, 1 minutes]:",
|
||||
"gopkg.in/yaml%2ev2.handleErr(0xc208033b20)",
|
||||
" /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
|
||||
"reflect.Value.assignTo(0x570860, 0xc20803f3e0, 0x15)",
|
||||
" c:/go/src/reflect/value.go:2125 +0x368",
|
||||
"main.main()",
|
||||
" /gopath/src/github.com/maruel/pre-commit-go/main.go:428 +0x27",
|
||||
"",
|
||||
}
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
err := process(bytes.NewBufferString(strings.Join(data, "\n")), out, &defaultPalette, stack.AnyPointer, false, false)
|
||||
ut.AssertEqual(t, nil, err)
|
||||
expected := []string{
|
||||
"panic: runtime error: index out of range",
|
||||
"",
|
||||
"\x1b[1;35m1: running [5 minutes] [locked]\x1b[90m [Created by main.(*batchArchiveRun).main @ batch_archive.go:167]\x1b[39m\x1b[m",
|
||||
" \x1b[1;39marchiver \x1b[39m\x1b[marchiver.go:325 \x1b[1;31m(*archiver).PushFile\x1b[39m\x1b[m(#1, 0xc20968a3c0, 0x5b, 0xc20988c280, 0x7d, 0, 0)\x1b[39m\x1b[m",
|
||||
" \x1b[1;39misolate \x1b[39m\x1b[misolate.go:148 \x1b[31marchive\x1b[39m\x1b[m(#4, #1, #2, 0x22, #3, 0xc20804666a, 0x17, 0, 0, 0, ...)\x1b[39m\x1b[m",
|
||||
" \x1b[1;39misolate \x1b[39m\x1b[misolate.go:102 \x1b[1;31mArchive\x1b[39m\x1b[m(#4, #1, #2, 0x22, #3, 0, 0)\x1b[39m\x1b[m",
|
||||
" \x1b[1;39mmain \x1b[39m\x1b[mbatch_archive.go:166 \x1b[1;33mfunc·004\x1b[39m\x1b[m(0x7fffc3b8f13a, 0x2c)\x1b[39m\x1b[m",
|
||||
"2: running [0~1 minutes]\x1b[39m\x1b[m",
|
||||
" \x1b[1;39myaml.v2 \x1b[39m\x1b[myaml.go:153 \x1b[31mhandleErr\x1b[39m\x1b[m(#5)\x1b[39m\x1b[m",
|
||||
" \x1b[1;39mreflect \x1b[39m\x1b[mvalue.go:2125 \x1b[32mValue.assignTo\x1b[39m\x1b[m(0x570860, #6, 0x15)\x1b[39m\x1b[m",
|
||||
" \x1b[1;39mmain \x1b[39m\x1b[mmain.go:428 \x1b[1;33mmain\x1b[39m\x1b[m()\x1b[39m\x1b[m",
|
||||
"",
|
||||
}
|
||||
actual := strings.Split(out.String(), "\n")
|
||||
for i := 0; i < len(actual) && i < len(expected); i++ {
|
||||
ut.AssertEqualIndex(t, i, expected[i], actual[i])
|
||||
}
|
||||
ut.AssertEqual(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestProcessFullPath(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
err := process(bytes.NewBufferString(strings.Join(data, "\n")), out, &defaultPalette, stack.AnyValue, true, false)
|
||||
ut.AssertEqual(t, nil, err)
|
||||
expected := []string{
|
||||
"panic: runtime error: index out of range",
|
||||
"",
|
||||
"\x1b[1;35m1: running [5 minutes] [locked]\x1b[90m [Created by main.(*batchArchiveRun).main @ /gopath/path/to/batch_archive.go:167]\x1b[39m\x1b[m",
|
||||
" \x1b[1;39marchiver \x1b[39m\x1b[m/gopath/path/to/archiver.go:325 \x1b[1;31m(*archiver).PushFile\x1b[39m\x1b[m(#1, 0xc20968a3c0, 0x5b, 0xc20988c280, 0x7d, 0, 0)\x1b[39m\x1b[m",
|
||||
" \x1b[1;39misolate \x1b[39m\x1b[m/gopath/path/to/isolate.go:148 \x1b[31marchive\x1b[39m\x1b[m(#4, #1, #2, 0x22, #3, 0xc20804666a, 0x17, 0, 0, 0, ...)\x1b[39m\x1b[m",
|
||||
" \x1b[1;39misolate \x1b[39m\x1b[m/gopath/path/to/isolate.go:102 \x1b[1;31mArchive\x1b[39m\x1b[m(#4, #1, #2, 0x22, #3, 0, 0)\x1b[39m\x1b[m",
|
||||
" \x1b[1;39mmain \x1b[39m\x1b[m/gopath/path/to/batch_archive.go:166 \x1b[1;33mfunc·004\x1b[39m\x1b[m(0x7fffc3b8f13a, 0x2c)\x1b[39m\x1b[m",
|
||||
"2: running [0~1 minutes]\x1b[39m\x1b[m",
|
||||
" \x1b[1;39myaml.v2 \x1b[39m\x1b[m/gopath/src/gopkg.in/yaml.v2/yaml.go:153 \x1b[31mhandleErr\x1b[39m\x1b[m(#5)\x1b[39m\x1b[m",
|
||||
" \x1b[1;39mreflect \x1b[39m\x1b[mc:/go/src/reflect/value.go:2125 \x1b[32mValue.assignTo\x1b[39m\x1b[m(0x570860, #6, 0x15)\x1b[39m\x1b[m",
|
||||
" \x1b[1;39mmain \x1b[39m\x1b[m/gopath/src/github.com/maruel/pre-commit-go/main.go:428 \x1b[1;33mmain\x1b[39m\x1b[m()\x1b[39m\x1b[m",
|
||||
"",
|
||||
}
|
||||
actual := strings.Split(out.String(), "\n")
|
||||
for i := 0; i < len(actual) && i < len(expected); i++ {
|
||||
ut.AssertEqualIndex(t, i, expected[i], actual[i])
|
||||
}
|
||||
ut.AssertEqual(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestProcessNoColor(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
err := process(bytes.NewBufferString(strings.Join(data, "\n")), out, &stack.Palette{}, stack.AnyPointer, false, false)
|
||||
ut.AssertEqual(t, nil, err)
|
||||
expected := []string{
|
||||
"panic: runtime error: index out of range",
|
||||
"",
|
||||
"1: running [5 minutes] [locked] [Created by main.(*batchArchiveRun).main @ batch_archive.go:167]",
|
||||
" archiver archiver.go:325 (*archiver).PushFile(#1, 0xc20968a3c0, 0x5b, 0xc20988c280, 0x7d, 0, 0)",
|
||||
" isolate isolate.go:148 archive(#4, #1, #2, 0x22, #3, 0xc20804666a, 0x17, 0, 0, 0, ...)",
|
||||
" isolate isolate.go:102 Archive(#4, #1, #2, 0x22, #3, 0, 0)",
|
||||
" main batch_archive.go:166 func·004(0x7fffc3b8f13a, 0x2c)",
|
||||
"2: running [0~1 minutes]",
|
||||
" yaml.v2 yaml.go:153 handleErr(#5)",
|
||||
" reflect value.go:2125 Value.assignTo(0x570860, #6, 0x15)",
|
||||
" main main.go:428 main()",
|
||||
"",
|
||||
}
|
||||
actual := strings.Split(out.String(), "\n")
|
||||
for i := 0; i < len(actual) && i < len(expected); i++ {
|
||||
ut.AssertEqualIndex(t, i, expected[i], actual[i])
|
||||
}
|
||||
ut.AssertEqual(t, expected, actual)
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2017 Marc-Antoine Ruel. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Panic crashes in various ways.
|
||||
//
|
||||
// It is a tool to help test panicparse.
|
||||
package main
|
||||
|
||||
// To install, run:
|
||||
// go install github.com/maruel/panicparse/internal/panic
|
||||
// panic -help
|
||||
//
|
||||
// You can also run directly:
|
||||
// go run ./internal/panic/main.go str |& pp
|
||||
//
|
||||
// To add a new panic stack signature, add it to types type below, keeping the
|
||||
// list ordered by name. If you need utility functions, add it in the section
|
||||
// below. That's it!
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Utility functions.
|
||||
|
||||
func panicint(i int) {
|
||||
panic(i)
|
||||
}
|
||||
|
||||
func panicstr(a string) {
|
||||
panic(a)
|
||||
}
|
||||
|
||||
func panicslicestr(a []string) {
|
||||
panic(a)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// types is all the supported types of panics.
|
||||
//
|
||||
// Keep the list sorted.
|
||||
var types = map[string]struct {
|
||||
desc string
|
||||
f func()
|
||||
}{
|
||||
"goroutine_1": {
|
||||
"panic in one goroutine",
|
||||
func() {
|
||||
go func() {
|
||||
panicint(42)
|
||||
}()
|
||||
time.Sleep(time.Minute)
|
||||
},
|
||||
},
|
||||
|
||||
"goroutine_100": {
|
||||
"start 100 goroutines before panicking",
|
||||
func() {
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
wg.Done()
|
||||
time.Sleep(time.Minute)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
panicint(42)
|
||||
},
|
||||
},
|
||||
|
||||
"int": {
|
||||
"panic(42)",
|
||||
func() {
|
||||
panicint(42)
|
||||
},
|
||||
},
|
||||
|
||||
"simple": {
|
||||
// This is not used for real, here for documentation.
|
||||
"skip the map for a shorter stack trace",
|
||||
func() {},
|
||||
},
|
||||
|
||||
"slice_str": {
|
||||
"panic([]string{\"allo\"}) with cap=2",
|
||||
func() {
|
||||
a := make([]string, 1, 2)
|
||||
a[0] = "allo"
|
||||
panicslicestr(a)
|
||||
},
|
||||
},
|
||||
|
||||
"str": {
|
||||
"panic(\"allo\")",
|
||||
func() {
|
||||
panicstr("allo")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
func main() {
|
||||
fmt.Printf("GOTRACEBACK=%s\n", os.Getenv("GOTRACEBACK"))
|
||||
if len(os.Args) == 2 {
|
||||
n := os.Args[1]
|
||||
if n == "simple" {
|
||||
// Since the map lookup creates another call stack entry, add a one-off
|
||||
// "simple" to test the very minimal case.
|
||||
panic("simple")
|
||||
}
|
||||
if f, ok := types[n]; ok {
|
||||
f.f()
|
||||
}
|
||||
}
|
||||
usage()
|
||||
}
|
||||
|
||||
func usage() {
|
||||
t := `usage: panic <way>
|
||||
|
||||
This tool is meant to be used with panicparse to test different parsing
|
||||
scenarios and ensure output on different version of the Go toolchain can be
|
||||
successfully parsed.
|
||||
|
||||
Set GOTRACEBACK before running this tool to see how it affects the panic output.
|
||||
|
||||
Select the way to panic:
|
||||
`
|
||||
io.WriteString(os.Stderr, t)
|
||||
names := make([]string, 0, len(types))
|
||||
m := 0
|
||||
for n := range types {
|
||||
names = append(names, n)
|
||||
if i := len(n); i > m {
|
||||
m = i
|
||||
}
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, n := range names {
|
||||
fmt.Fprintf(os.Stderr, "- %-*s %s\n", m, n, types[n].desc)
|
||||
}
|
||||
os.Exit(2)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
)
|
||||
|
||||
func main() {
|
||||
stdOut := bufio.NewWriter(colorable.NewColorableStdout())
|
||||
|
||||
fmt.Fprint(stdOut, "\x1B[3GMove to 3rd Column\n")
|
||||
fmt.Fprint(stdOut, "\x1B[1;2HMove to 2nd Column on 1st Line\n")
|
||||
stdOut.Flush()
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
|
||||
logrus.SetOutput(colorable.NewColorableStdout())
|
||||
|
||||
logrus.Info("succeeded")
|
||||
logrus.Warn("not correct")
|
||||
logrus.Error("something error")
|
||||
logrus.Fatal("panic")
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
. "github.com/mattn/go-colorable"
|
||||
)
|
||||
|
||||
func main() {
|
||||
out := NewColorableStdout()
|
||||
fmt.Fprint(out, "\x1B]0;TITLE Changed\007(See title and hit any key)")
|
||||
var c [1]byte
|
||||
os.Stdin.Read(c[:])
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand"
|
||||
|
||||
sqlite "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// Computes x^y
|
||||
func pow(x, y int64) int64 {
|
||||
return int64(math.Pow(float64(x), float64(y)))
|
||||
}
|
||||
|
||||
// Computes the bitwise exclusive-or of all its arguments
|
||||
func xor(xs ...int64) int64 {
|
||||
var ret int64
|
||||
for _, x := range xs {
|
||||
ret ^= x
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Returns a random number. It's actually deterministic here because
|
||||
// we don't seed the RNG, but it's an example of a non-pure function
|
||||
// from SQLite's POV.
|
||||
func getrand() int64 {
|
||||
return rand.Int63()
|
||||
}
|
||||
|
||||
// Computes the standard deviation of a GROUPed BY set of values
|
||||
type stddev struct {
|
||||
xs []int64
|
||||
// Running average calculation
|
||||
sum int64
|
||||
n int64
|
||||
}
|
||||
|
||||
func newStddev() *stddev { return &stddev{} }
|
||||
|
||||
func (s *stddev) Step(x int64) {
|
||||
s.xs = append(s.xs, x)
|
||||
s.sum += x
|
||||
s.n++
|
||||
}
|
||||
|
||||
func (s *stddev) Done() float64 {
|
||||
mean := float64(s.sum) / float64(s.n)
|
||||
var sqDiff []float64
|
||||
for _, x := range s.xs {
|
||||
sqDiff = append(sqDiff, math.Pow(float64(x)-mean, 2))
|
||||
}
|
||||
var dev float64
|
||||
for _, x := range sqDiff {
|
||||
dev += x
|
||||
}
|
||||
dev /= float64(len(sqDiff))
|
||||
return math.Sqrt(dev)
|
||||
}
|
||||
|
||||
func main() {
|
||||
sql.Register("sqlite3_custom", &sqlite.SQLiteDriver{
|
||||
ConnectHook: func(conn *sqlite.SQLiteConn) error {
|
||||
if err := conn.RegisterFunc("pow", pow, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := conn.RegisterFunc("xor", xor, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := conn.RegisterFunc("rand", getrand, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := conn.RegisterAggregator("stddev", newStddev, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
db, err := sql.Open("sqlite3_custom", ":memory:")
|
||||
if err != nil {
|
||||
log.Fatal("Failed to open database:", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
var i int64
|
||||
err = db.QueryRow("SELECT pow(2,3)").Scan(&i)
|
||||
if err != nil {
|
||||
log.Fatal("POW query error:", err)
|
||||
}
|
||||
fmt.Println("pow(2,3) =", i) // 8
|
||||
|
||||
err = db.QueryRow("SELECT xor(1,2,3,4,5,6)").Scan(&i)
|
||||
if err != nil {
|
||||
log.Fatal("XOR query error:", err)
|
||||
}
|
||||
fmt.Println("xor(1,2,3,4,5) =", i) // 7
|
||||
|
||||
err = db.QueryRow("SELECT rand()").Scan(&i)
|
||||
if err != nil {
|
||||
log.Fatal("RAND query error:", err)
|
||||
}
|
||||
fmt.Println("rand() =", i) // pseudorandom
|
||||
|
||||
_, err = db.Exec("create table foo (department integer, profits integer)")
|
||||
if err != nil {
|
||||
log.Fatal("Failed to create table:", err)
|
||||
}
|
||||
_, err = db.Exec("insert into foo values (1, 10), (1, 20), (1, 45), (2, 42), (2, 115)")
|
||||
if err != nil {
|
||||
log.Fatal("Failed to insert records:", err)
|
||||
}
|
||||
|
||||
rows, err := db.Query("select department, stddev(profits) from foo group by department")
|
||||
if err != nil {
|
||||
log.Fatal("STDDEV query error:", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var dept int64
|
||||
var dev float64
|
||||
if err := rows.Scan(&dept, &dev); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("dept=%d stddev=%f\n", dept, dev)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sqlite3conn := []*sqlite3.SQLiteConn{}
|
||||
sql.Register("sqlite3_with_hook_example",
|
||||
&sqlite3.SQLiteDriver{
|
||||
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
||||
sqlite3conn = append(sqlite3conn, conn)
|
||||
conn.RegisterUpdateHook(func(op int, db string, table string, rowid int64) {
|
||||
switch op {
|
||||
case sqlite3.SQLITE_INSERT:
|
||||
log.Println("Notified of insert on db", db, "table", table, "rowid", rowid)
|
||||
}
|
||||
})
|
||||
return nil
|
||||
},
|
||||
})
|
||||
os.Remove("./foo.db")
|
||||
os.Remove("./bar.db")
|
||||
|
||||
srcDb, err := sql.Open("sqlite3_with_hook_example", "./foo.db")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer srcDb.Close()
|
||||
srcDb.Ping()
|
||||
|
||||
_, err = srcDb.Exec("create table foo(id int, value text)")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = srcDb.Exec("insert into foo values(1, 'foo')")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = srcDb.Exec("insert into foo values(2, 'bar')")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = srcDb.Query("select * from foo")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
destDb, err := sql.Open("sqlite3_with_hook_example", "./bar.db")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer destDb.Close()
|
||||
destDb.Ping()
|
||||
|
||||
bk, err := sqlite3conn[1].Backup("main", sqlite3conn[0], "main")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = bk.Step(-1)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = destDb.Query("select * from foo")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = destDb.Exec("insert into foo values(3, 'bar')")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
bk.Finish()
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func createBulkInsertQuery(n int, start int) (query string, args []interface{}) {
|
||||
values := make([]string, n)
|
||||
args = make([]interface{}, n*2)
|
||||
pos := 0
|
||||
for i := 0; i < n; i++ {
|
||||
values[i] = "(?, ?)"
|
||||
args[pos] = start + i
|
||||
args[pos+1] = fmt.Sprintf("こんにちわ世界%03d", i)
|
||||
pos += 2
|
||||
}
|
||||
query = fmt.Sprintf(
|
||||
"insert into foo(id, name) values %s",
|
||||
strings.Join(values, ", "),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func bukInsert(db *sql.DB, query string, args []interface{}) (err error) {
|
||||
stmt, err := db.Prepare(query)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = stmt.Exec(args...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
var sqlite3conn *sqlite3.SQLiteConn
|
||||
sql.Register("sqlite3_with_limit", &sqlite3.SQLiteDriver{
|
||||
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
||||
sqlite3conn = conn
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
os.Remove("./foo.db")
|
||||
db, err := sql.Open("sqlite3_with_limit", "./foo.db")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
sqlStmt := `
|
||||
create table foo (id integer not null primary key, name text);
|
||||
delete from foo;
|
||||
`
|
||||
_, err = db.Exec(sqlStmt)
|
||||
if err != nil {
|
||||
log.Printf("%q: %s\n", err, sqlStmt)
|
||||
return
|
||||
}
|
||||
|
||||
if sqlite3conn == nil {
|
||||
log.Fatal("not set sqlite3 connection")
|
||||
}
|
||||
|
||||
limitVariableNumber := sqlite3conn.GetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER)
|
||||
log.Printf("default SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber)
|
||||
|
||||
num := 400
|
||||
query, args := createBulkInsertQuery(num, 0)
|
||||
err = bukInsert(db, query, args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
smallLimitVariableNumber := 100
|
||||
sqlite3conn.SetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER, smallLimitVariableNumber)
|
||||
|
||||
limitVariableNumber = sqlite3conn.GetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER)
|
||||
log.Printf("updated SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber)
|
||||
|
||||
query, args = createBulkInsertQuery(num, num)
|
||||
err = bukInsert(db, query, args)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
log.Printf("expect failed since SQLITE_LIMIT_VARIABLE_NUMBER is too small: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
bigLimitVariableNumber := 999999
|
||||
sqlite3conn.SetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER, bigLimitVariableNumber)
|
||||
limitVariableNumber = sqlite3conn.GetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER)
|
||||
log.Printf("set SQLITE_LIMIT_VARIABLE_NUMBER: %d", bigLimitVariableNumber)
|
||||
log.Printf("updated SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber)
|
||||
|
||||
query, args = createBulkInsertQuery(500, num+num)
|
||||
err = bukInsert(db, query, args)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("no error if SQLITE_LIMIT_VARIABLE_NUMBER > 999")
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
ifeq ($(OS),Windows_NT)
|
||||
EXE=extension.exe
|
||||
EXT=sqlite3_mod_regexp.dll
|
||||
RM=cmd /c del
|
||||
LDFLAG=
|
||||
else
|
||||
EXE=extension
|
||||
EXT=sqlite3_mod_regexp.so
|
||||
RM=rm
|
||||
LDFLAG=-fPIC
|
||||
endif
|
||||
|
||||
all : $(EXE) $(EXT)
|
||||
|
||||
$(EXE) : extension.go
|
||||
go build $<
|
||||
|
||||
$(EXT) : sqlite3_mod_regexp.c
|
||||
gcc $(LDFLAG) -shared -o $@ $< -lsqlite3 -lpcre
|
||||
|
||||
clean :
|
||||
@-$(RM) $(EXE) $(EXT)
|
43
vendor/github.com/mattn/go-sqlite3/_example/mod_regexp/extension.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/mattn/go-sqlite3"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sql.Register("sqlite3_with_extensions",
|
||||
&sqlite3.SQLiteDriver{
|
||||
Extensions: []string{
|
||||
"sqlite3_mod_regexp",
|
||||
},
|
||||
})
|
||||
|
||||
db, err := sql.Open("sqlite3_with_extensions", ":memory:")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Force db to make a new connection in pool
|
||||
// by putting the original in a transaction
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer tx.Commit()
|
||||
|
||||
// New connection works (hopefully!)
|
||||
rows, err := db.Query("select 'hello world' where 'hello world' regexp '^hello.*d$'")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var helloworld string
|
||||
rows.Scan(&helloworld)
|
||||
fmt.Println(helloworld)
|
||||
}
|
||||
}
|
31
vendor/github.com/mattn/go-sqlite3/_example/mod_regexp/sqlite3_mod_regexp.c
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include <pcre.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sqlite3ext.h>
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
if (argc >= 2) {
|
||||
const char *target = (const char *)sqlite3_value_text(argv[1]);
|
||||
const char *pattern = (const char *)sqlite3_value_text(argv[0]);
|
||||
const char* errstr = NULL;
|
||||
int erroff = 0;
|
||||
int vec[500];
|
||||
int n, rc;
|
||||
pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL);
|
||||
rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500);
|
||||
if (rc <= 0) {
|
||||
sqlite3_result_error(context, errstr, 0);
|
||||
return;
|
||||
}
|
||||
sqlite3_result_int(context, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) {
|
||||
SQLITE_EXTENSION_INIT2(api);
|
||||
return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, (void*)db, regexp_func, NULL, NULL);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
ifeq ($(OS),Windows_NT)
|
||||
EXE=extension.exe
|
||||
EXT=sqlite3_mod_vtable.dll
|
||||
RM=cmd /c del
|
||||
LIBCURL=-lcurldll
|
||||
LDFLAG=
|
||||
else
|
||||
EXE=extension
|
||||
EXT=sqlite3_mod_vtable.so
|
||||
RM=rm
|
||||
LDFLAG=-fPIC
|
||||
LIBCURL=-lcurl
|
||||
endif
|
||||
|
||||
all : $(EXE) $(EXT)
|
||||
|
||||
$(EXE) : extension.go
|
||||
go build $<
|
||||
|
||||
$(EXT) : sqlite3_mod_vtable.cc
|
||||
g++ $(LDFLAG) -shared -o $@ $< -lsqlite3 $(LIBCURL)
|
||||
|
||||
clean :
|
||||
@-$(RM) $(EXE) $(EXT)
|
37
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/extension.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sql.Register("sqlite3_with_extensions",
|
||||
&sqlite3.SQLiteDriver{
|
||||
Extensions: []string{
|
||||
"sqlite3_mod_vtable",
|
||||
},
|
||||
})
|
||||
|
||||
db, err := sql.Open("sqlite3_with_extensions", ":memory:")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
db.Exec("create virtual table repo using github(id, full_name, description, html_url)")
|
||||
|
||||
rows, err := db.Query("select id, full_name, description, html_url from repo")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var id, fullName, description, htmlURL string
|
||||
rows.Scan(&id, &fullName, &description, &htmlURL)
|
||||
fmt.Printf("%s: %s\n\t%s\n\t%s\n\n", id, fullName, description, htmlURL)
|
||||
}
|
||||
}
|
1040
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/picojson.h
generated
vendored
Normal file
238
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/sqlite3_mod_vtable.cc
generated
vendored
Normal file
|
@ -0,0 +1,238 @@
|
|||
#include <string>
|
||||
#include <sstream>
|
||||
#include <sqlite3-binding.h>
|
||||
#include <sqlite3ext.h>
|
||||
#include <curl/curl.h>
|
||||
#include "picojson.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# define EXPORT __declspec(dllexport)
|
||||
#else
|
||||
# define EXPORT
|
||||
#endif
|
||||
|
||||
SQLITE_EXTENSION_INIT1;
|
||||
|
||||
typedef struct {
|
||||
char* data; // response data from server
|
||||
size_t size; // response size of data
|
||||
} MEMFILE;
|
||||
|
||||
MEMFILE*
|
||||
memfopen() {
|
||||
MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE));
|
||||
if (mf) {
|
||||
mf->data = NULL;
|
||||
mf->size = 0;
|
||||
}
|
||||
return mf;
|
||||
}
|
||||
|
||||
void
|
||||
memfclose(MEMFILE* mf) {
|
||||
if (mf->data) free(mf->data);
|
||||
free(mf);
|
||||
}
|
||||
|
||||
size_t
|
||||
memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) {
|
||||
MEMFILE* mf = (MEMFILE*) stream;
|
||||
int block = size * nmemb;
|
||||
if (!mf) return block; // through
|
||||
if (!mf->data)
|
||||
mf->data = (char*) malloc(block);
|
||||
else
|
||||
mf->data = (char*) realloc(mf->data, mf->size + block);
|
||||
if (mf->data) {
|
||||
memcpy(mf->data + mf->size, ptr, block);
|
||||
mf->size += block;
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
char*
|
||||
memfstrdup(MEMFILE* mf) {
|
||||
char* buf;
|
||||
if (mf->size == 0) return NULL;
|
||||
buf = (char*) malloc(mf->size + 1);
|
||||
memcpy(buf, mf->data, mf->size);
|
||||
buf[mf->size] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
my_connect(sqlite3 *db, void *pAux, int argc, const char * const *argv, sqlite3_vtab **ppVTab, char **c) {
|
||||
std::stringstream ss;
|
||||
ss << "CREATE TABLE " << argv[0]
|
||||
<< "(id int, full_name text, description text, html_url text)";
|
||||
int rc = sqlite3_declare_vtab(db, ss.str().c_str());
|
||||
*ppVTab = (sqlite3_vtab *) sqlite3_malloc(sizeof(sqlite3_vtab));
|
||||
memset(*ppVTab, 0, sizeof(sqlite3_vtab));
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
my_create(sqlite3 *db, void *pAux, int argc, const char * const * argv, sqlite3_vtab **ppVTab, char **c) {
|
||||
return my_connect(db, pAux, argc, argv, ppVTab, c);
|
||||
}
|
||||
|
||||
static int my_disconnect(sqlite3_vtab *pVTab) {
|
||||
sqlite3_free(pVTab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
my_destroy(sqlite3_vtab *pVTab) {
|
||||
sqlite3_free(pVTab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
sqlite3_vtab_cursor base;
|
||||
int index;
|
||||
picojson::value* rows;
|
||||
} cursor;
|
||||
|
||||
static int
|
||||
my_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
|
||||
MEMFILE* mf;
|
||||
CURL* curl;
|
||||
char* json;
|
||||
CURLcode res = CURLE_OK;
|
||||
char error[CURL_ERROR_SIZE] = {0};
|
||||
char* cert_file = getenv("SSL_CERT_FILE");
|
||||
|
||||
mf = memfopen();
|
||||
curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.29.0");
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/repositories");
|
||||
if (cert_file)
|
||||
curl_easy_setopt(curl, CURLOPT_CAINFO, cert_file);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
|
||||
res = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
if (res != CURLE_OK) {
|
||||
std::cerr << error << std::endl;
|
||||
return SQLITE_FAIL;
|
||||
}
|
||||
|
||||
picojson::value* v = new picojson::value;
|
||||
std::string err;
|
||||
picojson::parse(*v, mf->data, mf->data + mf->size, &err);
|
||||
memfclose(mf);
|
||||
|
||||
if (!err.empty()) {
|
||||
delete v;
|
||||
std::cerr << err << std::endl;
|
||||
return SQLITE_FAIL;
|
||||
}
|
||||
|
||||
cursor *c = (cursor *)sqlite3_malloc(sizeof(cursor));
|
||||
c->rows = v;
|
||||
c->index = 0;
|
||||
*ppCursor = &c->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
my_close(cursor *c) {
|
||||
delete c->rows;
|
||||
sqlite3_free(c);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
my_filter(cursor *c, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) {
|
||||
c->index = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
my_next(cursor *c) {
|
||||
c->index++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
my_eof(cursor *c) {
|
||||
return c->index >= c->rows->get<picojson::array>().size() ? 1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
my_column(cursor *c, sqlite3_context *ctxt, int i) {
|
||||
picojson::value v = c->rows->get<picojson::array>()[c->index];
|
||||
picojson::object row = v.get<picojson::object>();
|
||||
const char* p = NULL;
|
||||
switch (i) {
|
||||
case 0:
|
||||
p = row["id"].to_str().c_str();
|
||||
break;
|
||||
case 1:
|
||||
p = row["full_name"].to_str().c_str();
|
||||
break;
|
||||
case 2:
|
||||
p = row["description"].to_str().c_str();
|
||||
break;
|
||||
case 3:
|
||||
p = row["html_url"].to_str().c_str();
|
||||
break;
|
||||
}
|
||||
sqlite3_result_text(ctxt, strdup(p), strlen(p), free);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
my_rowid(cursor *c, sqlite3_int64 *pRowid) {
|
||||
*pRowid = c->index;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
my_bestindex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static const sqlite3_module module = {
|
||||
0,
|
||||
my_create,
|
||||
my_connect,
|
||||
my_bestindex,
|
||||
my_disconnect,
|
||||
my_destroy,
|
||||
my_open,
|
||||
(int (*)(sqlite3_vtab_cursor *)) my_close,
|
||||
(int (*)(sqlite3_vtab_cursor *, int, char const *, int, sqlite3_value **)) my_filter,
|
||||
(int (*)(sqlite3_vtab_cursor *)) my_next,
|
||||
(int (*)(sqlite3_vtab_cursor *)) my_eof,
|
||||
(int (*)(sqlite3_vtab_cursor *, sqlite3_context *, int)) my_column,
|
||||
(int (*)(sqlite3_vtab_cursor *, sqlite3_int64 *)) my_rowid,
|
||||
NULL, // my_update
|
||||
NULL, // my_begin
|
||||
NULL, // my_sync
|
||||
NULL, // my_commit
|
||||
NULL, // my_rollback
|
||||
NULL, // my_findfunction
|
||||
NULL, // my_rename
|
||||
};
|
||||
|
||||
static void
|
||||
destructor(void *arg) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
EXPORT int
|
||||
sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) {
|
||||
SQLITE_EXTENSION_INIT2(api);
|
||||
sqlite3_create_module_v2(db, "github", &module, NULL, destructor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
os.Remove("./foo.db")
|
||||
|
||||
db, err := sql.Open("sqlite3", "./foo.db")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
sqlStmt := `
|
||||
create table foo (id integer not null primary key, name text);
|
||||
delete from foo;
|
||||
`
|
||||
_, err = db.Exec(sqlStmt)
|
||||
if err != nil {
|
||||
log.Printf("%q: %s\n", err, sqlStmt)
|
||||
return
|
||||
}
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
stmt, err := tx.Prepare("insert into foo(id, name) values(?, ?)")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
for i := 0; i < 100; i++ {
|
||||
_, err = stmt.Exec(i, fmt.Sprintf("こんにちわ世界%03d", i))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
|
||||
rows, err := db.Query("select id, name from foo")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var name string
|
||||
err = rows.Scan(&id, &name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(id, name)
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
stmt, err = db.Prepare("select name from foo where id = ?")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
var name string
|
||||
err = stmt.QueryRow("3").Scan(&name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(name)
|
||||
|
||||
_, err = db.Exec("delete from foo")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = db.Exec("insert into foo(id, name) values(1, 'foo'), (2, 'bar'), (3, 'baz')")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rows, err = db.Query("select id, name from foo")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var name string
|
||||
err = rows.Scan(&id, &name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(id, name)
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
sqlite3 "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func traceCallback(info sqlite3.TraceInfo) int {
|
||||
// Not very readable but may be useful; uncomment next line in case of doubt:
|
||||
//fmt.Printf("Trace: %#v\n", info)
|
||||
|
||||
var dbErrText string
|
||||
if info.DBError.Code != 0 || info.DBError.ExtendedCode != 0 {
|
||||
dbErrText = fmt.Sprintf("; DB error: %#v", info.DBError)
|
||||
} else {
|
||||
dbErrText = "."
|
||||
}
|
||||
|
||||
// Show the Statement-or-Trigger text in curly braces ('{', '}')
|
||||
// since from the *paired* ASCII characters they are
|
||||
// the least used in SQL syntax, therefore better visual delimiters.
|
||||
// Maybe show 'ExpandedSQL' the same way as 'StmtOrTrigger'.
|
||||
//
|
||||
// A known use of curly braces (outside strings) is
|
||||
// for ODBC escape sequences. Not likely to appear here.
|
||||
//
|
||||
// Template languages, etc. don't matter, we should see their *result*
|
||||
// at *this* level.
|
||||
// Strange curly braces in SQL code that reached the database driver
|
||||
// suggest that there is a bug in the application.
|
||||
// The braces are likely to be either template syntax or
|
||||
// a programming language's string interpolation syntax.
|
||||
|
||||
var expandedText string
|
||||
if info.ExpandedSQL != "" {
|
||||
if info.ExpandedSQL == info.StmtOrTrigger {
|
||||
expandedText = " = exp"
|
||||
} else {
|
||||
expandedText = fmt.Sprintf(" expanded {%q}", info.ExpandedSQL)
|
||||
}
|
||||
} else {
|
||||
expandedText = ""
|
||||
}
|
||||
|
||||
// SQLite docs as of September 6, 2016: Tracing and Profiling Functions
|
||||
// https://www.sqlite.org/c3ref/profile.html
|
||||
//
|
||||
// The profile callback time is in units of nanoseconds, however
|
||||
// the current implementation is only capable of millisecond resolution
|
||||
// so the six least significant digits in the time are meaningless.
|
||||
// Future versions of SQLite might provide greater resolution on the profiler callback.
|
||||
|
||||
var runTimeText string
|
||||
if info.RunTimeNanosec == 0 {
|
||||
if info.EventCode == sqlite3.TraceProfile {
|
||||
//runTimeText = "; no time" // seems confusing
|
||||
runTimeText = "; time 0" // no measurement unit
|
||||
} else {
|
||||
//runTimeText = "; no time" // seems useless and confusing
|
||||
}
|
||||
} else {
|
||||
const nanosPerMillisec = 1000000
|
||||
if info.RunTimeNanosec%nanosPerMillisec == 0 {
|
||||
runTimeText = fmt.Sprintf("; time %d ms", info.RunTimeNanosec/nanosPerMillisec)
|
||||
} else {
|
||||
// unexpected: better than millisecond resolution
|
||||
runTimeText = fmt.Sprintf("; time %d ns!!!", info.RunTimeNanosec)
|
||||
}
|
||||
}
|
||||
|
||||
var modeText string
|
||||
if info.AutoCommit {
|
||||
modeText = "-AC-"
|
||||
} else {
|
||||
modeText = "+Tx+"
|
||||
}
|
||||
|
||||
fmt.Printf("Trace: ev %d %s conn 0x%x, stmt 0x%x {%q}%s%s%s\n",
|
||||
info.EventCode, modeText, info.ConnHandle, info.StmtHandle,
|
||||
info.StmtOrTrigger, expandedText,
|
||||
runTimeText,
|
||||
dbErrText)
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
eventMask := sqlite3.TraceStmt | sqlite3.TraceProfile | sqlite3.TraceRow | sqlite3.TraceClose
|
||||
|
||||
sql.Register("sqlite3_tracing",
|
||||
&sqlite3.SQLiteDriver{
|
||||
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
||||
err := conn.SetTrace(&sqlite3.TraceConfig{
|
||||
Callback: traceCallback,
|
||||
EventMask: uint(eventMask),
|
||||
WantExpandedSQL: true,
|
||||
})
|
||||
return err
|
||||
},
|
||||
})
|
||||
|
||||
os.Exit(dbMain())
|
||||
}
|
||||
|
||||
// Harder to do DB work in main().
|
||||
// It's better with a separate function because
|
||||
// 'defer' and 'os.Exit' don't go well together.
|
||||
//
|
||||
// DO NOT use 'log.Fatal...' below: remember that it's equivalent to
|
||||
// Print() followed by a call to os.Exit(1) --- and
|
||||
// we want to avoid Exit() so 'defer' can do cleanup.
|
||||
// Use 'log.Panic...' instead.
|
||||
|
||||
func dbMain() int {
|
||||
db, err := sql.Open("sqlite3_tracing", ":memory:")
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to open database: %#+v\n", err)
|
||||
return 1
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
dbSetup(db)
|
||||
|
||||
dbDoInsert(db)
|
||||
dbDoInsertPrepared(db)
|
||||
dbDoSelect(db)
|
||||
dbDoSelectPrepared(db)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// 'DDL' stands for "Data Definition Language":
|
||||
|
||||
// Note: "INTEGER PRIMARY KEY NOT NULL AUTOINCREMENT" causes the error
|
||||
// 'near "AUTOINCREMENT": syntax error'; without "NOT NULL" it works.
|
||||
const tableDDL = `CREATE TABLE t1 (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
note VARCHAR NOT NULL
|
||||
)`
|
||||
|
||||
// 'DML' stands for "Data Manipulation Language":
|
||||
|
||||
const insertDML = "INSERT INTO t1 (note) VALUES (?)"
|
||||
const selectDML = "SELECT id, note FROM t1 WHERE note LIKE ?"
|
||||
|
||||
const textPrefix = "bla-1234567890-"
|
||||
const noteTextPattern = "%Prep%"
|
||||
|
||||
const nGenRows = 4 // Number of Rows to Generate (for *each* approach tested)
|
||||
|
||||
func dbSetup(db *sql.DB) {
|
||||
var err error
|
||||
|
||||
_, err = db.Exec("DROP TABLE IF EXISTS t1")
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
_, err = db.Exec(tableDDL)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func dbDoInsert(db *sql.DB) {
|
||||
const Descr = "DB-Exec"
|
||||
for i := 0; i < nGenRows; i++ {
|
||||
result, err := db.Exec(insertDML, textPrefix+Descr)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
resultDoCheck(result, Descr, i)
|
||||
}
|
||||
}
|
||||
|
||||
func dbDoInsertPrepared(db *sql.DB) {
|
||||
const Descr = "DB-Prepare"
|
||||
|
||||
stmt, err := db.Prepare(insertDML)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
for i := 0; i < nGenRows; i++ {
|
||||
result, err := stmt.Exec(textPrefix + Descr)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
resultDoCheck(result, Descr, i)
|
||||
}
|
||||
}
|
||||
|
||||
func resultDoCheck(result sql.Result, callerDescr string, callIndex int) {
|
||||
lastID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
nAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
log.Printf("Exec result for %s (%d): ID = %d, affected = %d\n", callerDescr, callIndex, lastID, nAffected)
|
||||
}
|
||||
|
||||
func dbDoSelect(db *sql.DB) {
|
||||
const Descr = "DB-Query"
|
||||
|
||||
rows, err := db.Query(selectDML, noteTextPattern)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
rowsDoFetch(rows, Descr)
|
||||
}
|
||||
|
||||
func dbDoSelectPrepared(db *sql.DB) {
|
||||
const Descr = "DB-Prepare"
|
||||
|
||||
stmt, err := db.Prepare(selectDML)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query(noteTextPattern)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
rowsDoFetch(rows, Descr)
|
||||
}
|
||||
|
||||
func rowsDoFetch(rows *sql.Rows, callerDescr string) {
|
||||
var nRows int
|
||||
var id int64
|
||||
var note string
|
||||
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&id, ¬e)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
log.Printf("Row for %s (%d): id=%d, note=%q\n",
|
||||
callerDescr, nRows, id, note)
|
||||
nRows++
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
log.Printf("Total %d rows for %s.\n", nRows, callerDescr)
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sql.Register("sqlite3_with_extensions", &sqlite3.SQLiteDriver{
|
||||
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
||||
return conn.CreateModule("github", &githubModule{})
|
||||
},
|
||||
})
|
||||
db, err := sql.Open("sqlite3_with_extensions", ":memory:")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec("create virtual table repo using github(id, full_name, description, html_url)")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rows, err := db.Query("select id, full_name, description, html_url from repo")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var id, fullName, description, htmlURL string
|
||||
rows.Scan(&id, &fullName, &description, &htmlURL)
|
||||
fmt.Printf("%s: %s\n\t%s\n\t%s\n\n", id, fullName, description, htmlURL)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type githubRepo struct {
|
||||
ID int `json:"id"`
|
||||
FullName string `json:"full_name"`
|
||||
Description string `json:"description"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
}
|
||||
|
||||
type githubModule struct {
|
||||
}
|
||||
|
||||
func (m *githubModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
|
||||
err := c.DeclareVTab(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id INT,
|
||||
full_name TEXT,
|
||||
description TEXT,
|
||||
html_url TEXT
|
||||
)`, args[0]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ghRepoTable{}, nil
|
||||
}
|
||||
|
||||
func (m *githubModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
|
||||
return m.Create(c, args)
|
||||
}
|
||||
|
||||
func (m *githubModule) DestroyModule() {}
|
||||
|
||||
type ghRepoTable struct {
|
||||
repos []githubRepo
|
||||
}
|
||||
|
||||
func (v *ghRepoTable) Open() (sqlite3.VTabCursor, error) {
|
||||
resp, err := http.Get("https://api.github.com/repositories")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var repos []githubRepo
|
||||
if err := json.Unmarshal(body, &repos); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ghRepoCursor{0, repos}, nil
|
||||
}
|
||||
|
||||
func (v *ghRepoTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
|
||||
return &sqlite3.IndexResult{}, nil
|
||||
}
|
||||
|
||||
func (v *ghRepoTable) Disconnect() error { return nil }
|
||||
func (v *ghRepoTable) Destroy() error { return nil }
|
||||
|
||||
type ghRepoCursor struct {
|
||||
index int
|
||||
repos []githubRepo
|
||||
}
|
||||
|
||||
func (vc *ghRepoCursor) Column(c *sqlite3.SQLiteContext, col int) error {
|
||||
switch col {
|
||||
case 0:
|
||||
c.ResultInt(vc.repos[vc.index].ID)
|
||||
case 1:
|
||||
c.ResultText(vc.repos[vc.index].FullName)
|
||||
case 2:
|
||||
c.ResultText(vc.repos[vc.index].Description)
|
||||
case 3:
|
||||
c.ResultText(vc.repos[vc.index].HTMLURL)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vc *ghRepoCursor) Filter(idxNum int, idxStr string, vals []interface{}) error {
|
||||
vc.index = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vc *ghRepoCursor) Next() error {
|
||||
vc.index++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vc *ghRepoCursor) EOF() bool {
|
||||
return vc.index >= len(vc.repos)
|
||||
}
|
||||
|
||||
func (vc *ghRepoCursor) Rowid() (int64, error) {
|
||||
return int64(vc.index), nil
|
||||
}
|
||||
|
||||
func (vc *ghRepoCursor) Close() error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
func main() {
|
||||
site := "https://www.sqlite.org/download.html"
|
||||
fmt.Printf("scraping %v\n", site)
|
||||
doc, err := goquery.NewDocument(site)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var url string
|
||||
doc.Find("a").Each(func(_ int, s *goquery.Selection) {
|
||||
if url == "" && strings.HasPrefix(s.Text(), "sqlite-amalgamation-") {
|
||||
url = "https://www.sqlite.org/2017/" + s.Text()
|
||||
}
|
||||
})
|
||||
if url == "" {
|
||||
return
|
||||
}
|
||||
fmt.Printf("downloading %v\n", url)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
resp.Body.Close()
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("extracting %v\n", path.Base(url))
|
||||
r, err := zip.NewReader(bytes.NewReader(b), resp.ContentLength)
|
||||
if err != nil {
|
||||
resp.Body.Close()
|
||||
log.Fatal(err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
for _, zf := range r.File {
|
||||
var f *os.File
|
||||
switch path.Base(zf.Name) {
|
||||
case "sqlite3.c":
|
||||
f, err = os.Create("sqlite3-binding.c")
|
||||
case "sqlite3.h":
|
||||
f, err = os.Create("sqlite3-binding.h")
|
||||
case "sqlite3ext.h":
|
||||
f, err = os.Create("sqlite3ext.h")
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
zr, err := zf.Open()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = io.WriteString(f, "#ifndef USE_LIBSQLITE3\n")
|
||||
if err != nil {
|
||||
zr.Close()
|
||||
f.Close()
|
||||
log.Fatal(err)
|
||||
}
|
||||
scanner := bufio.NewScanner(zr)
|
||||
for scanner.Scan() {
|
||||
text := scanner.Text()
|
||||
if text == `#include "sqlite3.h"` {
|
||||
text = `#include "sqlite3-binding.h"`
|
||||
}
|
||||
_, err = fmt.Fprintln(f, text)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
err = scanner.Err()
|
||||
if err != nil {
|
||||
zr.Close()
|
||||
f.Close()
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = io.WriteString(f, "#else // USE_LIBSQLITE3\n // If users really want to link against the system sqlite3 we\n// need to make this file a noop.\n #endif")
|
||||
if err != nil {
|
||||
zr.Close()
|
||||
f.Close()
|
||||
log.Fatal(err)
|
||||
}
|
||||
zr.Close()
|
||||
f.Close()
|
||||
fmt.Printf("extracted %v\n", filepath.Base(f.Name()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,300 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/nsf/termbox-go"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func tbprint(x, y int, fg, bg termbox.Attribute, msg string) {
|
||||
for _, c := range msg {
|
||||
termbox.SetCell(x, y, c, fg, bg)
|
||||
x += runewidth.RuneWidth(c)
|
||||
}
|
||||
}
|
||||
|
||||
func fill(x, y, w, h int, cell termbox.Cell) {
|
||||
for ly := 0; ly < h; ly++ {
|
||||
for lx := 0; lx < w; lx++ {
|
||||
termbox.SetCell(x+lx, y+ly, cell.Ch, cell.Fg, cell.Bg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func rune_advance_len(r rune, pos int) int {
|
||||
if r == '\t' {
|
||||
return tabstop_length - pos%tabstop_length
|
||||
}
|
||||
return runewidth.RuneWidth(r)
|
||||
}
|
||||
|
||||
func voffset_coffset(text []byte, boffset int) (voffset, coffset int) {
|
||||
text = text[:boffset]
|
||||
for len(text) > 0 {
|
||||
r, size := utf8.DecodeRune(text)
|
||||
text = text[size:]
|
||||
coffset += 1
|
||||
voffset += rune_advance_len(r, voffset)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func byte_slice_grow(s []byte, desired_cap int) []byte {
|
||||
if cap(s) < desired_cap {
|
||||
ns := make([]byte, len(s), desired_cap)
|
||||
copy(ns, s)
|
||||
return ns
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func byte_slice_remove(text []byte, from, to int) []byte {
|
||||
size := to - from
|
||||
copy(text[from:], text[to:])
|
||||
text = text[:len(text)-size]
|
||||
return text
|
||||
}
|
||||
|
||||
func byte_slice_insert(text []byte, offset int, what []byte) []byte {
|
||||
n := len(text) + len(what)
|
||||
text = byte_slice_grow(text, n)
|
||||
text = text[:n]
|
||||
copy(text[offset+len(what):], text[offset:])
|
||||
copy(text[offset:], what)
|
||||
return text
|
||||
}
|
||||
|
||||
const preferred_horizontal_threshold = 5
|
||||
const tabstop_length = 8
|
||||
|
||||
type EditBox struct {
|
||||
text []byte
|
||||
line_voffset int
|
||||
cursor_boffset int // cursor offset in bytes
|
||||
cursor_voffset int // visual cursor offset in termbox cells
|
||||
cursor_coffset int // cursor offset in unicode code points
|
||||
}
|
||||
|
||||
// Draws the EditBox in the given location, 'h' is not used at the moment
|
||||
func (eb *EditBox) Draw(x, y, w, h int) {
|
||||
eb.AdjustVOffset(w)
|
||||
|
||||
const coldef = termbox.ColorDefault
|
||||
fill(x, y, w, h, termbox.Cell{Ch: ' '})
|
||||
|
||||
t := eb.text
|
||||
lx := 0
|
||||
tabstop := 0
|
||||
for {
|
||||
rx := lx - eb.line_voffset
|
||||
if len(t) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if lx == tabstop {
|
||||
tabstop += tabstop_length
|
||||
}
|
||||
|
||||
if rx >= w {
|
||||
termbox.SetCell(x+w-1, y, '→',
|
||||
coldef, coldef)
|
||||
break
|
||||
}
|
||||
|
||||
r, size := utf8.DecodeRune(t)
|
||||
if r == '\t' {
|
||||
for ; lx < tabstop; lx++ {
|
||||
rx = lx - eb.line_voffset
|
||||
if rx >= w {
|
||||
goto next
|
||||
}
|
||||
|
||||
if rx >= 0 {
|
||||
termbox.SetCell(x+rx, y, ' ', coldef, coldef)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if rx >= 0 {
|
||||
termbox.SetCell(x+rx, y, r, coldef, coldef)
|
||||
}
|
||||
lx += runewidth.RuneWidth(r)
|
||||
}
|
||||
next:
|
||||
t = t[size:]
|
||||
}
|
||||
|
||||
if eb.line_voffset != 0 {
|
||||
termbox.SetCell(x, y, '←', coldef, coldef)
|
||||
}
|
||||
}
|
||||
|
||||
// Adjusts line visual offset to a proper value depending on width
|
||||
func (eb *EditBox) AdjustVOffset(width int) {
|
||||
ht := preferred_horizontal_threshold
|
||||
max_h_threshold := (width - 1) / 2
|
||||
if ht > max_h_threshold {
|
||||
ht = max_h_threshold
|
||||
}
|
||||
|
||||
threshold := width - 1
|
||||
if eb.line_voffset != 0 {
|
||||
threshold = width - ht
|
||||
}
|
||||
if eb.cursor_voffset-eb.line_voffset >= threshold {
|
||||
eb.line_voffset = eb.cursor_voffset + (ht - width + 1)
|
||||
}
|
||||
|
||||
if eb.line_voffset != 0 && eb.cursor_voffset-eb.line_voffset < ht {
|
||||
eb.line_voffset = eb.cursor_voffset - ht
|
||||
if eb.line_voffset < 0 {
|
||||
eb.line_voffset = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (eb *EditBox) MoveCursorTo(boffset int) {
|
||||
eb.cursor_boffset = boffset
|
||||
eb.cursor_voffset, eb.cursor_coffset = voffset_coffset(eb.text, boffset)
|
||||
}
|
||||
|
||||
func (eb *EditBox) RuneUnderCursor() (rune, int) {
|
||||
return utf8.DecodeRune(eb.text[eb.cursor_boffset:])
|
||||
}
|
||||
|
||||
func (eb *EditBox) RuneBeforeCursor() (rune, int) {
|
||||
return utf8.DecodeLastRune(eb.text[:eb.cursor_boffset])
|
||||
}
|
||||
|
||||
func (eb *EditBox) MoveCursorOneRuneBackward() {
|
||||
if eb.cursor_boffset == 0 {
|
||||
return
|
||||
}
|
||||
_, size := eb.RuneBeforeCursor()
|
||||
eb.MoveCursorTo(eb.cursor_boffset - size)
|
||||
}
|
||||
|
||||
func (eb *EditBox) MoveCursorOneRuneForward() {
|
||||
if eb.cursor_boffset == len(eb.text) {
|
||||
return
|
||||
}
|
||||
_, size := eb.RuneUnderCursor()
|
||||
eb.MoveCursorTo(eb.cursor_boffset + size)
|
||||
}
|
||||
|
||||
func (eb *EditBox) MoveCursorToBeginningOfTheLine() {
|
||||
eb.MoveCursorTo(0)
|
||||
}
|
||||
|
||||
func (eb *EditBox) MoveCursorToEndOfTheLine() {
|
||||
eb.MoveCursorTo(len(eb.text))
|
||||
}
|
||||
|
||||
func (eb *EditBox) DeleteRuneBackward() {
|
||||
if eb.cursor_boffset == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
eb.MoveCursorOneRuneBackward()
|
||||
_, size := eb.RuneUnderCursor()
|
||||
eb.text = byte_slice_remove(eb.text, eb.cursor_boffset, eb.cursor_boffset+size)
|
||||
}
|
||||
|
||||
func (eb *EditBox) DeleteRuneForward() {
|
||||
if eb.cursor_boffset == len(eb.text) {
|
||||
return
|
||||
}
|
||||
_, size := eb.RuneUnderCursor()
|
||||
eb.text = byte_slice_remove(eb.text, eb.cursor_boffset, eb.cursor_boffset+size)
|
||||
}
|
||||
|
||||
func (eb *EditBox) DeleteTheRestOfTheLine() {
|
||||
eb.text = eb.text[:eb.cursor_boffset]
|
||||
}
|
||||
|
||||
func (eb *EditBox) InsertRune(r rune) {
|
||||
var buf [utf8.UTFMax]byte
|
||||
n := utf8.EncodeRune(buf[:], r)
|
||||
eb.text = byte_slice_insert(eb.text, eb.cursor_boffset, buf[:n])
|
||||
eb.MoveCursorOneRuneForward()
|
||||
}
|
||||
|
||||
// Please, keep in mind that cursor depends on the value of line_voffset, which
|
||||
// is being set on Draw() call, so.. call this method after Draw() one.
|
||||
func (eb *EditBox) CursorX() int {
|
||||
return eb.cursor_voffset - eb.line_voffset
|
||||
}
|
||||
|
||||
var edit_box EditBox
|
||||
|
||||
const edit_box_width = 30
|
||||
|
||||
func redraw_all() {
|
||||
const coldef = termbox.ColorDefault
|
||||
termbox.Clear(coldef, coldef)
|
||||
w, h := termbox.Size()
|
||||
|
||||
midy := h / 2
|
||||
midx := (w - edit_box_width) / 2
|
||||
|
||||
// unicode box drawing chars around the edit box
|
||||
termbox.SetCell(midx-1, midy, '│', coldef, coldef)
|
||||
termbox.SetCell(midx+edit_box_width, midy, '│', coldef, coldef)
|
||||
termbox.SetCell(midx-1, midy-1, '┌', coldef, coldef)
|
||||
termbox.SetCell(midx-1, midy+1, '└', coldef, coldef)
|
||||
termbox.SetCell(midx+edit_box_width, midy-1, '┐', coldef, coldef)
|
||||
termbox.SetCell(midx+edit_box_width, midy+1, '┘', coldef, coldef)
|
||||
fill(midx, midy-1, edit_box_width, 1, termbox.Cell{Ch: '─'})
|
||||
fill(midx, midy+1, edit_box_width, 1, termbox.Cell{Ch: '─'})
|
||||
|
||||
edit_box.Draw(midx, midy, edit_box_width, 1)
|
||||
termbox.SetCursor(midx+edit_box.CursorX(), midy)
|
||||
|
||||
tbprint(midx+6, midy+3, coldef, coldef, "Press ESC to quit")
|
||||
termbox.Flush()
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := termbox.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termbox.Close()
|
||||
termbox.SetInputMode(termbox.InputEsc)
|
||||
|
||||
redraw_all()
|
||||
mainloop:
|
||||
for {
|
||||
switch ev := termbox.PollEvent(); ev.Type {
|
||||
case termbox.EventKey:
|
||||
switch ev.Key {
|
||||
case termbox.KeyEsc:
|
||||
break mainloop
|
||||
case termbox.KeyArrowLeft, termbox.KeyCtrlB:
|
||||
edit_box.MoveCursorOneRuneBackward()
|
||||
case termbox.KeyArrowRight, termbox.KeyCtrlF:
|
||||
edit_box.MoveCursorOneRuneForward()
|
||||
case termbox.KeyBackspace, termbox.KeyBackspace2:
|
||||
edit_box.DeleteRuneBackward()
|
||||
case termbox.KeyDelete, termbox.KeyCtrlD:
|
||||
edit_box.DeleteRuneForward()
|
||||
case termbox.KeyTab:
|
||||
edit_box.InsertRune('\t')
|
||||
case termbox.KeySpace:
|
||||
edit_box.InsertRune(' ')
|
||||
case termbox.KeyCtrlK:
|
||||
edit_box.DeleteTheRestOfTheLine()
|
||||
case termbox.KeyHome, termbox.KeyCtrlA:
|
||||
edit_box.MoveCursorToBeginningOfTheLine()
|
||||
case termbox.KeyEnd, termbox.KeyCtrlE:
|
||||
edit_box.MoveCursorToEndOfTheLine()
|
||||
default:
|
||||
if ev.Ch != 0 {
|
||||
edit_box.InsertRune(ev.Ch)
|
||||
}
|
||||
}
|
||||
case termbox.EventError:
|
||||
panic(ev.Err)
|
||||
}
|
||||
redraw_all()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nsf/termbox-go"
|
||||
"time"
|
||||
)
|
||||
|
||||
func tbPrint(x, y int, fg, bg termbox.Attribute, msg string) {
|
||||
for _, c := range msg {
|
||||
termbox.SetCell(x, y, c, fg, bg)
|
||||
x++
|
||||
}
|
||||
}
|
||||
|
||||
func draw(i int) {
|
||||
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
defer termbox.Flush()
|
||||
|
||||
w, h := termbox.Size()
|
||||
s := fmt.Sprintf("count = %d", i)
|
||||
|
||||
tbPrint((w/2)-(len(s)/2), h/2, termbox.ColorRed, termbox.ColorDefault, s)
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := termbox.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
termbox.SetInputMode(termbox.InputEsc)
|
||||
|
||||
go func() {
|
||||
time.Sleep(5 * time.Second)
|
||||
termbox.Interrupt()
|
||||
|
||||
// This should never run - the Interrupt(), above, should cause the event
|
||||
// loop below to exit, which then exits the process. If something goes
|
||||
// wrong, this panic will trigger and show what happened.
|
||||
time.Sleep(1 * time.Second)
|
||||
panic("this should never run")
|
||||
}()
|
||||
|
||||
var count int
|
||||
|
||||
draw(count)
|
||||
mainloop:
|
||||
for {
|
||||
switch ev := termbox.PollEvent(); ev.Type {
|
||||
case termbox.EventKey:
|
||||
if ev.Ch == '+' {
|
||||
count++
|
||||
} else if ev.Ch == '-' {
|
||||
count--
|
||||
}
|
||||
|
||||
case termbox.EventError:
|
||||
panic(ev.Err)
|
||||
|
||||
case termbox.EventInterrupt:
|
||||
break mainloop
|
||||
}
|
||||
|
||||
draw(count)
|
||||
}
|
||||
termbox.Close()
|
||||
|
||||
fmt.Println("Finished")
|
||||
}
|
|
@ -0,0 +1,722 @@
|
|||
package main
|
||||
|
||||
import "github.com/nsf/termbox-go"
|
||||
import "fmt"
|
||||
|
||||
type key struct {
|
||||
x int
|
||||
y int
|
||||
ch rune
|
||||
}
|
||||
|
||||
var K_ESC = []key{{1, 1, 'E'}, {2, 1, 'S'}, {3, 1, 'C'}}
|
||||
var K_F1 = []key{{6, 1, 'F'}, {7, 1, '1'}}
|
||||
var K_F2 = []key{{9, 1, 'F'}, {10, 1, '2'}}
|
||||
var K_F3 = []key{{12, 1, 'F'}, {13, 1, '3'}}
|
||||
var K_F4 = []key{{15, 1, 'F'}, {16, 1, '4'}}
|
||||
var K_F5 = []key{{19, 1, 'F'}, {20, 1, '5'}}
|
||||
var K_F6 = []key{{22, 1, 'F'}, {23, 1, '6'}}
|
||||
var K_F7 = []key{{25, 1, 'F'}, {26, 1, '7'}}
|
||||
var K_F8 = []key{{28, 1, 'F'}, {29, 1, '8'}}
|
||||
var K_F9 = []key{{33, 1, 'F'}, {34, 1, '9'}}
|
||||
var K_F10 = []key{{36, 1, 'F'}, {37, 1, '1'}, {38, 1, '0'}}
|
||||
var K_F11 = []key{{40, 1, 'F'}, {41, 1, '1'}, {42, 1, '1'}}
|
||||
var K_F12 = []key{{44, 1, 'F'}, {45, 1, '1'}, {46, 1, '2'}}
|
||||
var K_PRN = []key{{50, 1, 'P'}, {51, 1, 'R'}, {52, 1, 'N'}}
|
||||
var K_SCR = []key{{54, 1, 'S'}, {55, 1, 'C'}, {56, 1, 'R'}}
|
||||
var K_BRK = []key{{58, 1, 'B'}, {59, 1, 'R'}, {60, 1, 'K'}}
|
||||
var K_LED1 = []key{{66, 1, '-'}}
|
||||
var K_LED2 = []key{{70, 1, '-'}}
|
||||
var K_LED3 = []key{{74, 1, '-'}}
|
||||
var K_TILDE = []key{{1, 4, '`'}}
|
||||
var K_TILDE_SHIFT = []key{{1, 4, '~'}}
|
||||
var K_1 = []key{{4, 4, '1'}}
|
||||
var K_1_SHIFT = []key{{4, 4, '!'}}
|
||||
var K_2 = []key{{7, 4, '2'}}
|
||||
var K_2_SHIFT = []key{{7, 4, '@'}}
|
||||
var K_3 = []key{{10, 4, '3'}}
|
||||
var K_3_SHIFT = []key{{10, 4, '#'}}
|
||||
var K_4 = []key{{13, 4, '4'}}
|
||||
var K_4_SHIFT = []key{{13, 4, '$'}}
|
||||
var K_5 = []key{{16, 4, '5'}}
|
||||
var K_5_SHIFT = []key{{16, 4, '%'}}
|
||||
var K_6 = []key{{19, 4, '6'}}
|
||||
var K_6_SHIFT = []key{{19, 4, '^'}}
|
||||
var K_7 = []key{{22, 4, '7'}}
|
||||
var K_7_SHIFT = []key{{22, 4, '&'}}
|
||||
var K_8 = []key{{25, 4, '8'}}
|
||||
var K_8_SHIFT = []key{{25, 4, '*'}}
|
||||
var K_9 = []key{{28, 4, '9'}}
|
||||
var K_9_SHIFT = []key{{28, 4, '('}}
|
||||
var K_0 = []key{{31, 4, '0'}}
|
||||
var K_0_SHIFT = []key{{31, 4, ')'}}
|
||||
var K_MINUS = []key{{34, 4, '-'}}
|
||||
var K_MINUS_SHIFT = []key{{34, 4, '_'}}
|
||||
var K_EQUALS = []key{{37, 4, '='}}
|
||||
var K_EQUALS_SHIFT = []key{{37, 4, '+'}}
|
||||
var K_BACKSLASH = []key{{40, 4, '\\'}}
|
||||
var K_BACKSLASH_SHIFT = []key{{40, 4, '|'}}
|
||||
var K_BACKSPACE = []key{{44, 4, 0x2190}, {45, 4, 0x2500}, {46, 4, 0x2500}}
|
||||
var K_INS = []key{{50, 4, 'I'}, {51, 4, 'N'}, {52, 4, 'S'}}
|
||||
var K_HOM = []key{{54, 4, 'H'}, {55, 4, 'O'}, {56, 4, 'M'}}
|
||||
var K_PGU = []key{{58, 4, 'P'}, {59, 4, 'G'}, {60, 4, 'U'}}
|
||||
var K_K_NUMLOCK = []key{{65, 4, 'N'}}
|
||||
var K_K_SLASH = []key{{68, 4, '/'}}
|
||||
var K_K_STAR = []key{{71, 4, '*'}}
|
||||
var K_K_MINUS = []key{{74, 4, '-'}}
|
||||
var K_TAB = []key{{1, 6, 'T'}, {2, 6, 'A'}, {3, 6, 'B'}}
|
||||
var K_q = []key{{6, 6, 'q'}}
|
||||
var K_Q = []key{{6, 6, 'Q'}}
|
||||
var K_w = []key{{9, 6, 'w'}}
|
||||
var K_W = []key{{9, 6, 'W'}}
|
||||
var K_e = []key{{12, 6, 'e'}}
|
||||
var K_E = []key{{12, 6, 'E'}}
|
||||
var K_r = []key{{15, 6, 'r'}}
|
||||
var K_R = []key{{15, 6, 'R'}}
|
||||
var K_t = []key{{18, 6, 't'}}
|
||||
var K_T = []key{{18, 6, 'T'}}
|
||||
var K_y = []key{{21, 6, 'y'}}
|
||||
var K_Y = []key{{21, 6, 'Y'}}
|
||||
var K_u = []key{{24, 6, 'u'}}
|
||||
var K_U = []key{{24, 6, 'U'}}
|
||||
var K_i = []key{{27, 6, 'i'}}
|
||||
var K_I = []key{{27, 6, 'I'}}
|
||||
var K_o = []key{{30, 6, 'o'}}
|
||||
var K_O = []key{{30, 6, 'O'}}
|
||||
var K_p = []key{{33, 6, 'p'}}
|
||||
var K_P = []key{{33, 6, 'P'}}
|
||||
var K_LSQB = []key{{36, 6, '['}}
|
||||
var K_LCUB = []key{{36, 6, '{'}}
|
||||
var K_RSQB = []key{{39, 6, ']'}}
|
||||
var K_RCUB = []key{{39, 6, '}'}}
|
||||
var K_ENTER = []key{
|
||||
{43, 6, 0x2591}, {44, 6, 0x2591}, {45, 6, 0x2591}, {46, 6, 0x2591},
|
||||
{43, 7, 0x2591}, {44, 7, 0x2591}, {45, 7, 0x21B5}, {46, 7, 0x2591},
|
||||
{41, 8, 0x2591}, {42, 8, 0x2591}, {43, 8, 0x2591}, {44, 8, 0x2591},
|
||||
{45, 8, 0x2591}, {46, 8, 0x2591},
|
||||
}
|
||||
var K_DEL = []key{{50, 6, 'D'}, {51, 6, 'E'}, {52, 6, 'L'}}
|
||||
var K_END = []key{{54, 6, 'E'}, {55, 6, 'N'}, {56, 6, 'D'}}
|
||||
var K_PGD = []key{{58, 6, 'P'}, {59, 6, 'G'}, {60, 6, 'D'}}
|
||||
var K_K_7 = []key{{65, 6, '7'}}
|
||||
var K_K_8 = []key{{68, 6, '8'}}
|
||||
var K_K_9 = []key{{71, 6, '9'}}
|
||||
var K_K_PLUS = []key{{74, 6, ' '}, {74, 7, '+'}, {74, 8, ' '}}
|
||||
var K_CAPS = []key{{1, 8, 'C'}, {2, 8, 'A'}, {3, 8, 'P'}, {4, 8, 'S'}}
|
||||
var K_a = []key{{7, 8, 'a'}}
|
||||
var K_A = []key{{7, 8, 'A'}}
|
||||
var K_s = []key{{10, 8, 's'}}
|
||||
var K_S = []key{{10, 8, 'S'}}
|
||||
var K_d = []key{{13, 8, 'd'}}
|
||||
var K_D = []key{{13, 8, 'D'}}
|
||||
var K_f = []key{{16, 8, 'f'}}
|
||||
var K_F = []key{{16, 8, 'F'}}
|
||||
var K_g = []key{{19, 8, 'g'}}
|
||||
var K_G = []key{{19, 8, 'G'}}
|
||||
var K_h = []key{{22, 8, 'h'}}
|
||||
var K_H = []key{{22, 8, 'H'}}
|
||||
var K_j = []key{{25, 8, 'j'}}
|
||||
var K_J = []key{{25, 8, 'J'}}
|
||||
var K_k = []key{{28, 8, 'k'}}
|
||||
var K_K = []key{{28, 8, 'K'}}
|
||||
var K_l = []key{{31, 8, 'l'}}
|
||||
var K_L = []key{{31, 8, 'L'}}
|
||||
var K_SEMICOLON = []key{{34, 8, ';'}}
|
||||
var K_PARENTHESIS = []key{{34, 8, ':'}}
|
||||
var K_QUOTE = []key{{37, 8, '\''}}
|
||||
var K_DOUBLEQUOTE = []key{{37, 8, '"'}}
|
||||
var K_K_4 = []key{{65, 8, '4'}}
|
||||
var K_K_5 = []key{{68, 8, '5'}}
|
||||
var K_K_6 = []key{{71, 8, '6'}}
|
||||
var K_LSHIFT = []key{{1, 10, 'S'}, {2, 10, 'H'}, {3, 10, 'I'}, {4, 10, 'F'}, {5, 10, 'T'}}
|
||||
var K_z = []key{{9, 10, 'z'}}
|
||||
var K_Z = []key{{9, 10, 'Z'}}
|
||||
var K_x = []key{{12, 10, 'x'}}
|
||||
var K_X = []key{{12, 10, 'X'}}
|
||||
var K_c = []key{{15, 10, 'c'}}
|
||||
var K_C = []key{{15, 10, 'C'}}
|
||||
var K_v = []key{{18, 10, 'v'}}
|
||||
var K_V = []key{{18, 10, 'V'}}
|
||||
var K_b = []key{{21, 10, 'b'}}
|
||||
var K_B = []key{{21, 10, 'B'}}
|
||||
var K_n = []key{{24, 10, 'n'}}
|
||||
var K_N = []key{{24, 10, 'N'}}
|
||||
var K_m = []key{{27, 10, 'm'}}
|
||||
var K_M = []key{{27, 10, 'M'}}
|
||||
var K_COMMA = []key{{30, 10, ','}}
|
||||
var K_LANB = []key{{30, 10, '<'}}
|
||||
var K_PERIOD = []key{{33, 10, '.'}}
|
||||
var K_RANB = []key{{33, 10, '>'}}
|
||||
var K_SLASH = []key{{36, 10, '/'}}
|
||||
var K_QUESTION = []key{{36, 10, '?'}}
|
||||
var K_RSHIFT = []key{{42, 10, 'S'}, {43, 10, 'H'}, {44, 10, 'I'}, {45, 10, 'F'}, {46, 10, 'T'}}
|
||||
var K_ARROW_UP = []key{{54, 10, '('}, {55, 10, 0x2191}, {56, 10, ')'}}
|
||||
var K_K_1 = []key{{65, 10, '1'}}
|
||||
var K_K_2 = []key{{68, 10, '2'}}
|
||||
var K_K_3 = []key{{71, 10, '3'}}
|
||||
var K_K_ENTER = []key{{74, 10, 0x2591}, {74, 11, 0x2591}, {74, 12, 0x2591}}
|
||||
var K_LCTRL = []key{{1, 12, 'C'}, {2, 12, 'T'}, {3, 12, 'R'}, {4, 12, 'L'}}
|
||||
var K_LWIN = []key{{6, 12, 'W'}, {7, 12, 'I'}, {8, 12, 'N'}}
|
||||
var K_LALT = []key{{10, 12, 'A'}, {11, 12, 'L'}, {12, 12, 'T'}}
|
||||
var K_SPACE = []key{
|
||||
{14, 12, ' '}, {15, 12, ' '}, {16, 12, ' '}, {17, 12, ' '}, {18, 12, ' '},
|
||||
{19, 12, 'S'}, {20, 12, 'P'}, {21, 12, 'A'}, {22, 12, 'C'}, {23, 12, 'E'},
|
||||
{24, 12, ' '}, {25, 12, ' '}, {26, 12, ' '}, {27, 12, ' '}, {28, 12, ' '},
|
||||
}
|
||||
var K_RALT = []key{{30, 12, 'A'}, {31, 12, 'L'}, {32, 12, 'T'}}
|
||||
var K_RWIN = []key{{34, 12, 'W'}, {35, 12, 'I'}, {36, 12, 'N'}}
|
||||
var K_RPROP = []key{{38, 12, 'P'}, {39, 12, 'R'}, {40, 12, 'O'}, {41, 12, 'P'}}
|
||||
var K_RCTRL = []key{{43, 12, 'C'}, {44, 12, 'T'}, {45, 12, 'R'}, {46, 12, 'L'}}
|
||||
var K_ARROW_LEFT = []key{{50, 12, '('}, {51, 12, 0x2190}, {52, 12, ')'}}
|
||||
var K_ARROW_DOWN = []key{{54, 12, '('}, {55, 12, 0x2193}, {56, 12, ')'}}
|
||||
var K_ARROW_RIGHT = []key{{58, 12, '('}, {59, 12, 0x2192}, {60, 12, ')'}}
|
||||
var K_K_0 = []key{{65, 12, ' '}, {66, 12, '0'}, {67, 12, ' '}, {68, 12, ' '}}
|
||||
var K_K_PERIOD = []key{{71, 12, '.'}}
|
||||
|
||||
type combo struct {
|
||||
keys [][]key
|
||||
}
|
||||
|
||||
var combos = []combo{
|
||||
{[][]key{K_TILDE, K_2, K_SPACE, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_A, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_B, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_C, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_D, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_E, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_F, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_G, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_H, K_BACKSPACE, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_I, K_TAB, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_J, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_K, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_L, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_M, K_ENTER, K_K_ENTER, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_N, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_O, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_P, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_Q, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_R, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_S, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_T, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_U, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_V, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_W, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_X, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_Y, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_Z, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_LSQB, K_ESC, K_3, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_4, K_BACKSLASH, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_RSQB, K_5, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_6, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_7, K_SLASH, K_MINUS_SHIFT, K_LCTRL, K_RCTRL}},
|
||||
{[][]key{K_SPACE}},
|
||||
{[][]key{K_1_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_DOUBLEQUOTE, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_3_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_4_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_5_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_7_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_QUOTE}},
|
||||
{[][]key{K_9_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_0_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_8_SHIFT, K_K_STAR, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_EQUALS_SHIFT, K_K_PLUS, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_COMMA}},
|
||||
{[][]key{K_MINUS, K_K_MINUS}},
|
||||
{[][]key{K_PERIOD, K_K_PERIOD}},
|
||||
{[][]key{K_SLASH, K_K_SLASH}},
|
||||
{[][]key{K_0, K_K_0}},
|
||||
{[][]key{K_1, K_K_1}},
|
||||
{[][]key{K_2, K_K_2}},
|
||||
{[][]key{K_3, K_K_3}},
|
||||
{[][]key{K_4, K_K_4}},
|
||||
{[][]key{K_5, K_K_5}},
|
||||
{[][]key{K_6, K_K_6}},
|
||||
{[][]key{K_7, K_K_7}},
|
||||
{[][]key{K_8, K_K_8}},
|
||||
{[][]key{K_9, K_K_9}},
|
||||
{[][]key{K_PARENTHESIS, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_SEMICOLON}},
|
||||
{[][]key{K_LANB, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_EQUALS}},
|
||||
{[][]key{K_RANB, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_QUESTION, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_2_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_A, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_B, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_C, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_D, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_E, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_F, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_G, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_H, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_I, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_J, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_K, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_L, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_M, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_N, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_O, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_P, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_Q, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_R, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_S, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_T, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_U, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_V, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_W, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_X, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_Y, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_Z, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_LSQB}},
|
||||
{[][]key{K_BACKSLASH}},
|
||||
{[][]key{K_RSQB}},
|
||||
{[][]key{K_6_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_MINUS_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_TILDE}},
|
||||
{[][]key{K_a}},
|
||||
{[][]key{K_b}},
|
||||
{[][]key{K_c}},
|
||||
{[][]key{K_d}},
|
||||
{[][]key{K_e}},
|
||||
{[][]key{K_f}},
|
||||
{[][]key{K_g}},
|
||||
{[][]key{K_h}},
|
||||
{[][]key{K_i}},
|
||||
{[][]key{K_j}},
|
||||
{[][]key{K_k}},
|
||||
{[][]key{K_l}},
|
||||
{[][]key{K_m}},
|
||||
{[][]key{K_n}},
|
||||
{[][]key{K_o}},
|
||||
{[][]key{K_p}},
|
||||
{[][]key{K_q}},
|
||||
{[][]key{K_r}},
|
||||
{[][]key{K_s}},
|
||||
{[][]key{K_t}},
|
||||
{[][]key{K_u}},
|
||||
{[][]key{K_v}},
|
||||
{[][]key{K_w}},
|
||||
{[][]key{K_x}},
|
||||
{[][]key{K_y}},
|
||||
{[][]key{K_z}},
|
||||
{[][]key{K_LCUB, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_BACKSLASH_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_RCUB, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_TILDE_SHIFT, K_LSHIFT, K_RSHIFT}},
|
||||
{[][]key{K_8, K_BACKSPACE, K_LCTRL, K_RCTRL}},
|
||||
}
|
||||
|
||||
var func_combos = []combo{
|
||||
{[][]key{K_F1}},
|
||||
{[][]key{K_F2}},
|
||||
{[][]key{K_F3}},
|
||||
{[][]key{K_F4}},
|
||||
{[][]key{K_F5}},
|
||||
{[][]key{K_F6}},
|
||||
{[][]key{K_F7}},
|
||||
{[][]key{K_F8}},
|
||||
{[][]key{K_F9}},
|
||||
{[][]key{K_F10}},
|
||||
{[][]key{K_F11}},
|
||||
{[][]key{K_F12}},
|
||||
{[][]key{K_INS}},
|
||||
{[][]key{K_DEL}},
|
||||
{[][]key{K_HOM}},
|
||||
{[][]key{K_END}},
|
||||
{[][]key{K_PGU}},
|
||||
{[][]key{K_PGD}},
|
||||
{[][]key{K_ARROW_UP}},
|
||||
{[][]key{K_ARROW_DOWN}},
|
||||
{[][]key{K_ARROW_LEFT}},
|
||||
{[][]key{K_ARROW_RIGHT}},
|
||||
}
|
||||
|
||||
func print_tb(x, y int, fg, bg termbox.Attribute, msg string) {
|
||||
for _, c := range msg {
|
||||
termbox.SetCell(x, y, c, fg, bg)
|
||||
x++
|
||||
}
|
||||
}
|
||||
|
||||
func printf_tb(x, y int, fg, bg termbox.Attribute, format string, args ...interface{}) {
|
||||
s := fmt.Sprintf(format, args...)
|
||||
print_tb(x, y, fg, bg, s)
|
||||
}
|
||||
|
||||
func draw_key(k []key, fg, bg termbox.Attribute) {
|
||||
for _, k := range k {
|
||||
termbox.SetCell(k.x+2, k.y+4, k.ch, fg, bg)
|
||||
}
|
||||
}
|
||||
|
||||
func draw_keyboard() {
|
||||
termbox.SetCell(0, 0, 0x250C, termbox.ColorWhite, termbox.ColorBlack)
|
||||
termbox.SetCell(79, 0, 0x2510, termbox.ColorWhite, termbox.ColorBlack)
|
||||
termbox.SetCell(0, 23, 0x2514, termbox.ColorWhite, termbox.ColorBlack)
|
||||
termbox.SetCell(79, 23, 0x2518, termbox.ColorWhite, termbox.ColorBlack)
|
||||
|
||||
for i := 1; i < 79; i++ {
|
||||
termbox.SetCell(i, 0, 0x2500, termbox.ColorWhite, termbox.ColorBlack)
|
||||
termbox.SetCell(i, 23, 0x2500, termbox.ColorWhite, termbox.ColorBlack)
|
||||
termbox.SetCell(i, 17, 0x2500, termbox.ColorWhite, termbox.ColorBlack)
|
||||
termbox.SetCell(i, 4, 0x2500, termbox.ColorWhite, termbox.ColorBlack)
|
||||
}
|
||||
for i := 1; i < 23; i++ {
|
||||
termbox.SetCell(0, i, 0x2502, termbox.ColorWhite, termbox.ColorBlack)
|
||||
termbox.SetCell(79, i, 0x2502, termbox.ColorWhite, termbox.ColorBlack)
|
||||
}
|
||||
termbox.SetCell(0, 17, 0x251C, termbox.ColorWhite, termbox.ColorBlack)
|
||||
termbox.SetCell(79, 17, 0x2524, termbox.ColorWhite, termbox.ColorBlack)
|
||||
termbox.SetCell(0, 4, 0x251C, termbox.ColorWhite, termbox.ColorBlack)
|
||||
termbox.SetCell(79, 4, 0x2524, termbox.ColorWhite, termbox.ColorBlack)
|
||||
for i := 5; i < 17; i++ {
|
||||
termbox.SetCell(1, i, 0x2588, termbox.ColorYellow, termbox.ColorYellow)
|
||||
termbox.SetCell(78, i, 0x2588, termbox.ColorYellow, termbox.ColorYellow)
|
||||
}
|
||||
|
||||
draw_key(K_ESC, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F1, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F2, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F3, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F4, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F5, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F6, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F7, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F8, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F9, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F10, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F11, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_F12, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_PRN, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_SCR, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_BRK, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_LED1, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_LED2, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_LED3, termbox.ColorWhite, termbox.ColorBlue)
|
||||
|
||||
draw_key(K_TILDE, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_1, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_2, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_3, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_4, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_5, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_6, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_7, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_8, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_9, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_0, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_MINUS, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_EQUALS, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_BACKSLASH, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_BACKSPACE, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_INS, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_HOM, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_PGU, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_NUMLOCK, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_SLASH, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_STAR, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_MINUS, termbox.ColorWhite, termbox.ColorBlue)
|
||||
|
||||
draw_key(K_TAB, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_q, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_w, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_e, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_r, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_t, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_y, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_u, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_i, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_o, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_p, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_LSQB, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_RSQB, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_ENTER, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_DEL, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_END, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_PGD, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_7, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_8, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_9, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_PLUS, termbox.ColorWhite, termbox.ColorBlue)
|
||||
|
||||
draw_key(K_CAPS, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_a, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_s, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_d, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_f, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_g, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_h, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_j, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_k, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_l, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_SEMICOLON, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_QUOTE, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_4, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_5, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_6, termbox.ColorWhite, termbox.ColorBlue)
|
||||
|
||||
draw_key(K_LSHIFT, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_z, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_x, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_c, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_v, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_b, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_n, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_m, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_COMMA, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_PERIOD, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_SLASH, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_RSHIFT, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_ARROW_UP, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_1, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_2, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_3, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_ENTER, termbox.ColorWhite, termbox.ColorBlue)
|
||||
|
||||
draw_key(K_LCTRL, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_LWIN, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_LALT, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_SPACE, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_RCTRL, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_RPROP, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_RWIN, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_RALT, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_ARROW_LEFT, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_ARROW_DOWN, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_ARROW_RIGHT, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_0, termbox.ColorWhite, termbox.ColorBlue)
|
||||
draw_key(K_K_PERIOD, termbox.ColorWhite, termbox.ColorBlue)
|
||||
|
||||
printf_tb(33, 1, termbox.ColorMagenta|termbox.AttrBold, termbox.ColorBlack, "Keyboard demo!")
|
||||
printf_tb(21, 2, termbox.ColorMagenta, termbox.ColorBlack, "(press CTRL+X and then CTRL+Q to exit)")
|
||||
printf_tb(15, 3, termbox.ColorMagenta, termbox.ColorBlack, "(press CTRL+X and then CTRL+C to change input mode)")
|
||||
|
||||
inputmode := termbox.SetInputMode(termbox.InputCurrent)
|
||||
inputmode_str := ""
|
||||
switch {
|
||||
case inputmode&termbox.InputEsc != 0:
|
||||
inputmode_str = "termbox.InputEsc"
|
||||
case inputmode&termbox.InputAlt != 0:
|
||||
inputmode_str = "termbox.InputAlt"
|
||||
}
|
||||
|
||||
if inputmode&termbox.InputMouse != 0 {
|
||||
inputmode_str += " | termbox.InputMouse"
|
||||
}
|
||||
printf_tb(3, 18, termbox.ColorWhite, termbox.ColorBlack, "Input mode: %s", inputmode_str)
|
||||
}
|
||||
|
||||
var fcmap = []string{
|
||||
"CTRL+2, CTRL+~",
|
||||
"CTRL+A",
|
||||
"CTRL+B",
|
||||
"CTRL+C",
|
||||
"CTRL+D",
|
||||
"CTRL+E",
|
||||
"CTRL+F",
|
||||
"CTRL+G",
|
||||
"CTRL+H, BACKSPACE",
|
||||
"CTRL+I, TAB",
|
||||
"CTRL+J",
|
||||
"CTRL+K",
|
||||
"CTRL+L",
|
||||
"CTRL+M, ENTER",
|
||||
"CTRL+N",
|
||||
"CTRL+O",
|
||||
"CTRL+P",
|
||||
"CTRL+Q",
|
||||
"CTRL+R",
|
||||
"CTRL+S",
|
||||
"CTRL+T",
|
||||
"CTRL+U",
|
||||
"CTRL+V",
|
||||
"CTRL+W",
|
||||
"CTRL+X",
|
||||
"CTRL+Y",
|
||||
"CTRL+Z",
|
||||
"CTRL+3, ESC, CTRL+[",
|
||||
"CTRL+4, CTRL+\\",
|
||||
"CTRL+5, CTRL+]",
|
||||
"CTRL+6",
|
||||
"CTRL+7, CTRL+/, CTRL+_",
|
||||
"SPACE",
|
||||
}
|
||||
|
||||
var fkmap = []string{
|
||||
"F1",
|
||||
"F2",
|
||||
"F3",
|
||||
"F4",
|
||||
"F5",
|
||||
"F6",
|
||||
"F7",
|
||||
"F8",
|
||||
"F9",
|
||||
"F10",
|
||||
"F11",
|
||||
"F12",
|
||||
"INSERT",
|
||||
"DELETE",
|
||||
"HOME",
|
||||
"END",
|
||||
"PGUP",
|
||||
"PGDN",
|
||||
"ARROW UP",
|
||||
"ARROW DOWN",
|
||||
"ARROW LEFT",
|
||||
"ARROW RIGHT",
|
||||
}
|
||||
|
||||
func funckeymap(k termbox.Key) string {
|
||||
if k == termbox.KeyCtrl8 {
|
||||
return "CTRL+8, BACKSPACE 2" /* 0x7F */
|
||||
} else if k >= termbox.KeyArrowRight && k <= 0xFFFF {
|
||||
return fkmap[0xFFFF-k]
|
||||
} else if k <= termbox.KeySpace {
|
||||
return fcmap[k]
|
||||
}
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
func pretty_print_press(ev *termbox.Event) {
|
||||
printf_tb(3, 19, termbox.ColorWhite, termbox.ColorBlack, "Key: ")
|
||||
printf_tb(8, 19, termbox.ColorYellow, termbox.ColorBlack, "decimal: %d", ev.Key)
|
||||
printf_tb(8, 20, termbox.ColorGreen, termbox.ColorBlack, "hex: 0x%X", ev.Key)
|
||||
printf_tb(8, 21, termbox.ColorCyan, termbox.ColorBlack, "octal: 0%o", ev.Key)
|
||||
printf_tb(8, 22, termbox.ColorRed, termbox.ColorBlack, "string: %s", funckeymap(ev.Key))
|
||||
|
||||
printf_tb(54, 19, termbox.ColorWhite, termbox.ColorBlack, "Char: ")
|
||||
printf_tb(60, 19, termbox.ColorYellow, termbox.ColorBlack, "decimal: %d", ev.Ch)
|
||||
printf_tb(60, 20, termbox.ColorGreen, termbox.ColorBlack, "hex: 0x%X", ev.Ch)
|
||||
printf_tb(60, 21, termbox.ColorCyan, termbox.ColorBlack, "octal: 0%o", ev.Ch)
|
||||
printf_tb(60, 22, termbox.ColorRed, termbox.ColorBlack, "string: %s", string(ev.Ch))
|
||||
|
||||
modifier := "none"
|
||||
if ev.Mod != 0 {
|
||||
modifier = "termbox.ModAlt"
|
||||
}
|
||||
printf_tb(54, 18, termbox.ColorWhite, termbox.ColorBlack, "Modifier: %s", modifier)
|
||||
}
|
||||
|
||||
func pretty_print_resize(ev *termbox.Event) {
|
||||
printf_tb(3, 19, termbox.ColorWhite, termbox.ColorBlack, "Resize event: %d x %d", ev.Width, ev.Height)
|
||||
}
|
||||
|
||||
var counter = 0
|
||||
|
||||
func pretty_print_mouse(ev *termbox.Event) {
|
||||
printf_tb(3, 19, termbox.ColorWhite, termbox.ColorBlack, "Mouse event: %d x %d", ev.MouseX, ev.MouseY)
|
||||
button := ""
|
||||
switch ev.Key {
|
||||
case termbox.MouseLeft:
|
||||
button = "MouseLeft: %d"
|
||||
case termbox.MouseMiddle:
|
||||
button = "MouseMiddle: %d"
|
||||
case termbox.MouseRight:
|
||||
button = "MouseRight: %d"
|
||||
case termbox.MouseWheelUp:
|
||||
button = "MouseWheelUp: %d"
|
||||
case termbox.MouseWheelDown:
|
||||
button = "MouseWheelDown: %d"
|
||||
case termbox.MouseRelease:
|
||||
button = "MouseRelease: %d"
|
||||
}
|
||||
if ev.Mod&termbox.ModMotion != 0 {
|
||||
button += "*"
|
||||
}
|
||||
counter++
|
||||
printf_tb(43, 19, termbox.ColorWhite, termbox.ColorBlack, "Key: ")
|
||||
printf_tb(48, 19, termbox.ColorYellow, termbox.ColorBlack, button, counter)
|
||||
}
|
||||
|
||||
func dispatch_press(ev *termbox.Event) {
|
||||
if ev.Mod&termbox.ModAlt != 0 {
|
||||
draw_key(K_LALT, termbox.ColorWhite, termbox.ColorRed)
|
||||
draw_key(K_RALT, termbox.ColorWhite, termbox.ColorRed)
|
||||
}
|
||||
|
||||
var k *combo
|
||||
if ev.Key >= termbox.KeyArrowRight {
|
||||
k = &func_combos[0xFFFF-ev.Key]
|
||||
} else if ev.Ch < 128 {
|
||||
if ev.Ch == 0 && ev.Key < 128 {
|
||||
k = &combos[ev.Key]
|
||||
} else {
|
||||
k = &combos[ev.Ch]
|
||||
}
|
||||
}
|
||||
if k == nil {
|
||||
return
|
||||
}
|
||||
|
||||
keys := k.keys
|
||||
for _, k := range keys {
|
||||
draw_key(k, termbox.ColorWhite, termbox.ColorRed)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := termbox.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termbox.Close()
|
||||
|
||||
termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
|
||||
|
||||
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
draw_keyboard()
|
||||
termbox.Flush()
|
||||
inputmode := 0
|
||||
ctrlxpressed := false
|
||||
loop:
|
||||
for {
|
||||
switch ev := termbox.PollEvent(); ev.Type {
|
||||
case termbox.EventKey:
|
||||
if ev.Key == termbox.KeyCtrlS && ctrlxpressed {
|
||||
termbox.Sync()
|
||||
}
|
||||
if ev.Key == termbox.KeyCtrlQ && ctrlxpressed {
|
||||
break loop
|
||||
}
|
||||
if ev.Key == termbox.KeyCtrlC && ctrlxpressed {
|
||||
chmap := []termbox.InputMode{
|
||||
termbox.InputEsc | termbox.InputMouse,
|
||||
termbox.InputAlt | termbox.InputMouse,
|
||||
termbox.InputEsc,
|
||||
termbox.InputAlt,
|
||||
}
|
||||
inputmode++
|
||||
if inputmode >= len(chmap) {
|
||||
inputmode = 0
|
||||
}
|
||||
termbox.SetInputMode(chmap[inputmode])
|
||||
}
|
||||
if ev.Key == termbox.KeyCtrlX {
|
||||
ctrlxpressed = true
|
||||
} else {
|
||||
ctrlxpressed = false
|
||||
}
|
||||
|
||||
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
draw_keyboard()
|
||||
dispatch_press(&ev)
|
||||
pretty_print_press(&ev)
|
||||
termbox.Flush()
|
||||
case termbox.EventResize:
|
||||
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
draw_keyboard()
|
||||
pretty_print_resize(&ev)
|
||||
termbox.Flush()
|
||||
case termbox.EventMouse:
|
||||
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
draw_keyboard()
|
||||
pretty_print_mouse(&ev)
|
||||
termbox.Flush()
|
||||
case termbox.EventError:
|
||||
panic(ev.Err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
package main
|
||||
|
||||
import "github.com/mattn/go-runewidth"
|
||||
import "github.com/nsf/termbox-go"
|
||||
|
||||
const chars = "nnnnnnnnnbbbbbbbbbuuuuuuuuuBBBBBBBBB"
|
||||
|
||||
var output_mode = termbox.OutputNormal
|
||||
|
||||
func next_char(current int) int {
|
||||
current++
|
||||
if current >= len(chars) {
|
||||
return 0
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
func print_combinations_table(sx, sy int, attrs []termbox.Attribute) {
|
||||
var bg termbox.Attribute
|
||||
current_char := 0
|
||||
y := sy
|
||||
|
||||
all_attrs := []termbox.Attribute{
|
||||
0,
|
||||
termbox.AttrBold,
|
||||
termbox.AttrUnderline,
|
||||
termbox.AttrBold | termbox.AttrUnderline,
|
||||
}
|
||||
|
||||
draw_line := func() {
|
||||
x := sx
|
||||
for _, a := range all_attrs {
|
||||
for c := termbox.ColorDefault; c <= termbox.ColorWhite; c++ {
|
||||
fg := a | c
|
||||
termbox.SetCell(x, y, rune(chars[current_char]), fg, bg)
|
||||
current_char = next_char(current_char)
|
||||
x++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, a := range attrs {
|
||||
for c := termbox.ColorDefault; c <= termbox.ColorWhite; c++ {
|
||||
bg = a | c
|
||||
draw_line()
|
||||
y++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func print_wide(x, y int, s string) {
|
||||
red := false
|
||||
for _, r := range s {
|
||||
c := termbox.ColorDefault
|
||||
if red {
|
||||
c = termbox.ColorRed
|
||||
}
|
||||
termbox.SetCell(x, y, r, termbox.ColorDefault, c)
|
||||
w := runewidth.RuneWidth(r)
|
||||
if w == 0 || (w == 2 && runewidth.IsAmbiguousWidth(r)) {
|
||||
w = 1
|
||||
}
|
||||
x += w
|
||||
|
||||
red = !red
|
||||
}
|
||||
}
|
||||
|
||||
const hello_world = "こんにちは世界"
|
||||
|
||||
func draw_all() {
|
||||
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
|
||||
switch output_mode {
|
||||
|
||||
case termbox.OutputNormal:
|
||||
print_combinations_table(1, 1, []termbox.Attribute{
|
||||
0,
|
||||
termbox.AttrBold,
|
||||
})
|
||||
print_combinations_table(2+len(chars), 1, []termbox.Attribute{
|
||||
termbox.AttrReverse,
|
||||
})
|
||||
print_wide(2+len(chars), 11, hello_world)
|
||||
|
||||
case termbox.OutputGrayscale:
|
||||
for y := 0; y < 26; y++ {
|
||||
for x := 0; x < 26; x++ {
|
||||
termbox.SetCell(x, y, 'n',
|
||||
termbox.Attribute(x+1),
|
||||
termbox.Attribute(y+1))
|
||||
termbox.SetCell(x+27, y, 'b',
|
||||
termbox.Attribute(x+1)|termbox.AttrBold,
|
||||
termbox.Attribute(26-y))
|
||||
termbox.SetCell(x+54, y, 'u',
|
||||
termbox.Attribute(x+1)|termbox.AttrUnderline,
|
||||
termbox.Attribute(y+1))
|
||||
}
|
||||
termbox.SetCell(82, y, 'd',
|
||||
termbox.Attribute(y+1),
|
||||
termbox.ColorDefault)
|
||||
termbox.SetCell(83, y, 'd',
|
||||
termbox.ColorDefault,
|
||||
termbox.Attribute(26-y))
|
||||
}
|
||||
|
||||
case termbox.Output216:
|
||||
for r := 0; r < 6; r++ {
|
||||
for g := 0; g < 6; g++ {
|
||||
for b := 0; b < 6; b++ {
|
||||
y := r
|
||||
x := g + 6*b
|
||||
c1 := termbox.Attribute(1 + r*36 + g*6 + b)
|
||||
bg := termbox.Attribute(1 + g*36 + b*6 + r)
|
||||
c2 := termbox.Attribute(1 + b*36 + r*6 + g)
|
||||
bc1 := c1 | termbox.AttrBold
|
||||
uc1 := c1 | termbox.AttrUnderline
|
||||
bc2 := c2 | termbox.AttrBold
|
||||
uc2 := c2 | termbox.AttrUnderline
|
||||
termbox.SetCell(x, y, 'n', c1, bg)
|
||||
termbox.SetCell(x, y+6, 'b', bc1, bg)
|
||||
termbox.SetCell(x, y+12, 'u', uc1, bg)
|
||||
termbox.SetCell(x, y+18, 'B', bc1|uc1, bg)
|
||||
termbox.SetCell(x+37, y, 'n', c2, bg)
|
||||
termbox.SetCell(x+37, y+6, 'b', bc2, bg)
|
||||
termbox.SetCell(x+37, y+12, 'u', uc2, bg)
|
||||
termbox.SetCell(x+37, y+18, 'B', bc2|uc2, bg)
|
||||
}
|
||||
c1 := termbox.Attribute(1 + g*6 + r*36)
|
||||
c2 := termbox.Attribute(6 + g*6 + r*36)
|
||||
termbox.SetCell(74+g, r, 'd', c1, termbox.ColorDefault)
|
||||
termbox.SetCell(74+g, r+6, 'd', c2, termbox.ColorDefault)
|
||||
termbox.SetCell(74+g, r+12, 'd', termbox.ColorDefault, c1)
|
||||
termbox.SetCell(74+g, r+18, 'd', termbox.ColorDefault, c2)
|
||||
}
|
||||
}
|
||||
|
||||
case termbox.Output256:
|
||||
for y := 0; y < 4; y++ {
|
||||
for x := 0; x < 8; x++ {
|
||||
for z := 0; z < 8; z++ {
|
||||
bg := termbox.Attribute(1 + y*64 + x*8 + z)
|
||||
c1 := termbox.Attribute(256 - y*64 - x*8 - z)
|
||||
c2 := termbox.Attribute(1 + y*64 + z*8 + x)
|
||||
c3 := termbox.Attribute(256 - y*64 - z*8 - x)
|
||||
c4 := termbox.Attribute(1 + y*64 + x*4 + z*4)
|
||||
bold := c2 | termbox.AttrBold
|
||||
under := c3 | termbox.AttrUnderline
|
||||
both := c1 | termbox.AttrBold | termbox.AttrUnderline
|
||||
termbox.SetCell(z+8*x, y, ' ', 0, bg)
|
||||
termbox.SetCell(z+8*x, y+5, 'n', c4, bg)
|
||||
termbox.SetCell(z+8*x, y+10, 'b', bold, bg)
|
||||
termbox.SetCell(z+8*x, y+15, 'u', under, bg)
|
||||
termbox.SetCell(z+8*x, y+20, 'B', both, bg)
|
||||
}
|
||||
}
|
||||
}
|
||||
for x := 0; x < 12; x++ {
|
||||
for y := 0; y < 2; y++ {
|
||||
c1 := termbox.Attribute(233 + y*12 + x)
|
||||
termbox.SetCell(66+x, y, 'd', c1, termbox.ColorDefault)
|
||||
termbox.SetCell(66+x, 2+y, 'd', termbox.ColorDefault, c1)
|
||||
}
|
||||
}
|
||||
for x := 0; x < 6; x++ {
|
||||
for y := 0; y < 6; y++ {
|
||||
c1 := termbox.Attribute(17 + x*6 + y*36)
|
||||
c2 := termbox.Attribute(17 + 5 + x*6 + y*36)
|
||||
termbox.SetCell(66+x, 6+y, 'd', c1, termbox.ColorDefault)
|
||||
termbox.SetCell(66+x, 12+y, 'd', c2, termbox.ColorDefault)
|
||||
termbox.SetCell(72+x, 6+y, 'd', termbox.ColorDefault, c1)
|
||||
termbox.SetCell(72+x, 12+y, 'd', termbox.ColorDefault, c2)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
termbox.Flush()
|
||||
}
|
||||
|
||||
var available_modes = []termbox.OutputMode{
|
||||
termbox.OutputNormal,
|
||||
termbox.OutputGrayscale,
|
||||
termbox.Output216,
|
||||
termbox.Output256,
|
||||
}
|
||||
|
||||
var output_mode_index = 0
|
||||
|
||||
func switch_output_mode(direction int) {
|
||||
output_mode_index += direction
|
||||
if output_mode_index < 0 {
|
||||
output_mode_index = len(available_modes) - 1
|
||||
} else if output_mode_index >= len(available_modes) {
|
||||
output_mode_index = 0
|
||||
}
|
||||
output_mode = termbox.SetOutputMode(available_modes[output_mode_index])
|
||||
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
termbox.Sync()
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := termbox.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termbox.Close()
|
||||
|
||||
draw_all()
|
||||
loop:
|
||||
for {
|
||||
switch ev := termbox.PollEvent(); ev.Type {
|
||||
case termbox.EventKey:
|
||||
switch ev.Key {
|
||||
case termbox.KeyEsc:
|
||||
break loop
|
||||
case termbox.KeyArrowUp, termbox.KeyArrowRight:
|
||||
switch_output_mode(1)
|
||||
draw_all()
|
||||
case termbox.KeyArrowDown, termbox.KeyArrowLeft:
|
||||
switch_output_mode(-1)
|
||||
draw_all()
|
||||
}
|
||||
case termbox.EventResize:
|
||||
draw_all()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
var curCol = 0
|
||||
var curRune = 0
|
||||
var backbuf []termbox.Cell
|
||||
var bbw, bbh int
|
||||
|
||||
var runes = []rune{' ', '░', '▒', '▓', '█'}
|
||||
var colors = []termbox.Attribute{
|
||||
termbox.ColorBlack,
|
||||
termbox.ColorRed,
|
||||
termbox.ColorGreen,
|
||||
termbox.ColorYellow,
|
||||
termbox.ColorBlue,
|
||||
termbox.ColorMagenta,
|
||||
termbox.ColorCyan,
|
||||
termbox.ColorWhite,
|
||||
}
|
||||
|
||||
type attrFunc func(int) (rune, termbox.Attribute, termbox.Attribute)
|
||||
|
||||
func updateAndDrawButtons(current *int, x, y int, mx, my int, n int, attrf attrFunc) {
|
||||
lx, ly := x, y
|
||||
for i := 0; i < n; i++ {
|
||||
if lx <= mx && mx <= lx+3 && ly <= my && my <= ly+1 {
|
||||
*current = i
|
||||
}
|
||||
r, fg, bg := attrf(i)
|
||||
termbox.SetCell(lx+0, ly+0, r, fg, bg)
|
||||
termbox.SetCell(lx+1, ly+0, r, fg, bg)
|
||||
termbox.SetCell(lx+2, ly+0, r, fg, bg)
|
||||
termbox.SetCell(lx+3, ly+0, r, fg, bg)
|
||||
termbox.SetCell(lx+0, ly+1, r, fg, bg)
|
||||
termbox.SetCell(lx+1, ly+1, r, fg, bg)
|
||||
termbox.SetCell(lx+2, ly+1, r, fg, bg)
|
||||
termbox.SetCell(lx+3, ly+1, r, fg, bg)
|
||||
lx += 4
|
||||
}
|
||||
lx, ly = x, y
|
||||
for i := 0; i < n; i++ {
|
||||
if *current == i {
|
||||
fg := termbox.ColorRed | termbox.AttrBold
|
||||
bg := termbox.ColorDefault
|
||||
termbox.SetCell(lx+0, ly+2, '^', fg, bg)
|
||||
termbox.SetCell(lx+1, ly+2, '^', fg, bg)
|
||||
termbox.SetCell(lx+2, ly+2, '^', fg, bg)
|
||||
termbox.SetCell(lx+3, ly+2, '^', fg, bg)
|
||||
}
|
||||
lx += 4
|
||||
}
|
||||
}
|
||||
|
||||
func update_and_redraw_all(mx, my int) {
|
||||
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
if mx != -1 && my != -1 {
|
||||
backbuf[bbw*my+mx] = termbox.Cell{Ch: runes[curRune], Fg: colors[curCol]}
|
||||
}
|
||||
copy(termbox.CellBuffer(), backbuf)
|
||||
_, h := termbox.Size()
|
||||
updateAndDrawButtons(&curRune, 0, 0, mx, my, len(runes), func(i int) (rune, termbox.Attribute, termbox.Attribute) {
|
||||
return runes[i], termbox.ColorDefault, termbox.ColorDefault
|
||||
})
|
||||
updateAndDrawButtons(&curCol, 0, h-3, mx, my, len(colors), func(i int) (rune, termbox.Attribute, termbox.Attribute) {
|
||||
return ' ', termbox.ColorDefault, colors[i]
|
||||
})
|
||||
termbox.Flush()
|
||||
}
|
||||
|
||||
func reallocBackBuffer(w, h int) {
|
||||
bbw, bbh = w, h
|
||||
backbuf = make([]termbox.Cell, w*h)
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := termbox.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termbox.Close()
|
||||
termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
|
||||
reallocBackBuffer(termbox.Size())
|
||||
update_and_redraw_all(-1, -1)
|
||||
|
||||
mainloop:
|
||||
for {
|
||||
mx, my := -1, -1
|
||||
switch ev := termbox.PollEvent(); ev.Type {
|
||||
case termbox.EventKey:
|
||||
if ev.Key == termbox.KeyEsc {
|
||||
break mainloop
|
||||
}
|
||||
case termbox.EventMouse:
|
||||
if ev.Key == termbox.MouseLeft {
|
||||
mx, my = ev.MouseX, ev.MouseY
|
||||
}
|
||||
case termbox.EventResize:
|
||||
reallocBackBuffer(ev.Width, ev.Height)
|
||||
}
|
||||
update_and_redraw_all(mx, my)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package main
|
||||
|
||||
import "github.com/nsf/termbox-go"
|
||||
import "math/rand"
|
||||
import "time"
|
||||
|
||||
func draw() {
|
||||
w, h := termbox.Size()
|
||||
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
for y := 0; y < h; y++ {
|
||||
for x := 0; x < w; x++ {
|
||||
termbox.SetCell(x, y, ' ', termbox.ColorDefault,
|
||||
termbox.Attribute(rand.Int()%8)+1)
|
||||
}
|
||||
}
|
||||
termbox.Flush()
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := termbox.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termbox.Close()
|
||||
|
||||
event_queue := make(chan termbox.Event)
|
||||
go func() {
|
||||
for {
|
||||
event_queue <- termbox.PollEvent()
|
||||
}
|
||||
}()
|
||||
|
||||
draw()
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case ev := <-event_queue:
|
||||
if ev.Type == termbox.EventKey && ev.Key == termbox.KeyEsc {
|
||||
break loop
|
||||
}
|
||||
default:
|
||||
draw()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nsf/termbox-go"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func tbprint(x, y int, fg, bg termbox.Attribute, msg string) {
|
||||
for _, c := range msg {
|
||||
termbox.SetCell(x, y, c, fg, bg)
|
||||
x++
|
||||
}
|
||||
}
|
||||
|
||||
var current string
|
||||
var curev termbox.Event
|
||||
|
||||
func mouse_button_str(k termbox.Key) string {
|
||||
switch k {
|
||||
case termbox.MouseLeft:
|
||||
return "MouseLeft"
|
||||
case termbox.MouseMiddle:
|
||||
return "MouseMiddle"
|
||||
case termbox.MouseRight:
|
||||
return "MouseRight"
|
||||
case termbox.MouseRelease:
|
||||
return "MouseRelease"
|
||||
case termbox.MouseWheelUp:
|
||||
return "MouseWheelUp"
|
||||
case termbox.MouseWheelDown:
|
||||
return "MouseWheelDown"
|
||||
}
|
||||
return "Key"
|
||||
}
|
||||
|
||||
func mod_str(m termbox.Modifier) string {
|
||||
var out []string
|
||||
if m&termbox.ModAlt != 0 {
|
||||
out = append(out, "ModAlt")
|
||||
}
|
||||
if m&termbox.ModMotion != 0 {
|
||||
out = append(out, "ModMotion")
|
||||
}
|
||||
return strings.Join(out, " | ")
|
||||
}
|
||||
|
||||
func redraw_all() {
|
||||
const coldef = termbox.ColorDefault
|
||||
termbox.Clear(coldef, coldef)
|
||||
tbprint(0, 0, termbox.ColorMagenta, coldef, "Press 'q' to quit")
|
||||
tbprint(0, 1, coldef, coldef, current)
|
||||
switch curev.Type {
|
||||
case termbox.EventKey:
|
||||
tbprint(0, 2, coldef, coldef,
|
||||
fmt.Sprintf("EventKey: k: %d, c: %c, mod: %s", curev.Key, curev.Ch, mod_str(curev.Mod)))
|
||||
case termbox.EventMouse:
|
||||
tbprint(0, 2, coldef, coldef,
|
||||
fmt.Sprintf("EventMouse: x: %d, y: %d, b: %s, mod: %s",
|
||||
curev.MouseX, curev.MouseY, mouse_button_str(curev.Key), mod_str(curev.Mod)))
|
||||
case termbox.EventNone:
|
||||
tbprint(0, 2, coldef, coldef, "EventNone")
|
||||
}
|
||||
tbprint(0, 3, coldef, coldef, fmt.Sprintf("%d", curev.N))
|
||||
termbox.Flush()
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := termbox.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termbox.Close()
|
||||
termbox.SetInputMode(termbox.InputAlt | termbox.InputMouse)
|
||||
redraw_all()
|
||||
|
||||
data := make([]byte, 0, 64)
|
||||
mainloop:
|
||||
for {
|
||||
if cap(data)-len(data) < 32 {
|
||||
newdata := make([]byte, len(data), len(data)+32)
|
||||
copy(newdata, data)
|
||||
data = newdata
|
||||
}
|
||||
beg := len(data)
|
||||
d := data[beg : beg+32]
|
||||
switch ev := termbox.PollRawEvent(d); ev.Type {
|
||||
case termbox.EventRaw:
|
||||
data = data[:beg+ev.N]
|
||||
current = fmt.Sprintf("%q", data)
|
||||
if current == `"q"` {
|
||||
break mainloop
|
||||
}
|
||||
|
||||
for {
|
||||
ev := termbox.ParseEvent(data)
|
||||
if ev.N == 0 {
|
||||
break
|
||||
}
|
||||
curev = ev
|
||||
copy(data, data[curev.N:])
|
||||
data = data[:len(data)-curev.N]
|
||||
}
|
||||
case termbox.EventError:
|
||||
panic(ev.Err)
|
||||
}
|
||||
redraw_all()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·use(SB),NOSPLIT,$0
|
||||
RET
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for 386, Plan 9
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-32
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-44
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-36
|
||||
JMP syscall·seek(SB)
|
||||
|
||||
TEXT ·exit(SB),NOSPLIT,$4-4
|
||||
JMP syscall·exit(SB)
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for amd64, Plan 9
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-64
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-88
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-56
|
||||
JMP syscall·seek(SB)
|
||||
|
||||
TEXT ·exit(SB),NOSPLIT,$8-8
|
||||
JMP syscall·exit(SB)
|
|
@ -0,0 +1,70 @@
|
|||
package plan9
|
||||
|
||||
// Plan 9 Constants
|
||||
|
||||
// Open modes
|
||||
const (
|
||||
O_RDONLY = 0
|
||||
O_WRONLY = 1
|
||||
O_RDWR = 2
|
||||
O_TRUNC = 16
|
||||
O_CLOEXEC = 32
|
||||
O_EXCL = 0x1000
|
||||
)
|
||||
|
||||
// Rfork flags
|
||||
const (
|
||||
RFNAMEG = 1 << 0
|
||||
RFENVG = 1 << 1
|
||||
RFFDG = 1 << 2
|
||||
RFNOTEG = 1 << 3
|
||||
RFPROC = 1 << 4
|
||||
RFMEM = 1 << 5
|
||||
RFNOWAIT = 1 << 6
|
||||
RFCNAMEG = 1 << 10
|
||||
RFCENVG = 1 << 11
|
||||
RFCFDG = 1 << 12
|
||||
RFREND = 1 << 13
|
||||
RFNOMNT = 1 << 14
|
||||
)
|
||||
|
||||
// Qid.Type bits
|
||||
const (
|
||||
QTDIR = 0x80
|
||||
QTAPPEND = 0x40
|
||||
QTEXCL = 0x20
|
||||
QTMOUNT = 0x10
|
||||
QTAUTH = 0x08
|
||||
QTTMP = 0x04
|
||||
QTFILE = 0x00
|
||||
)
|
||||
|
||||
// Dir.Mode bits
|
||||
const (
|
||||
DMDIR = 0x80000000
|
||||
DMAPPEND = 0x40000000
|
||||
DMEXCL = 0x20000000
|
||||
DMMOUNT = 0x10000000
|
||||
DMAUTH = 0x08000000
|
||||
DMTMP = 0x04000000
|
||||
DMREAD = 0x4
|
||||
DMWRITE = 0x2
|
||||
DMEXEC = 0x1
|
||||
)
|
||||
|
||||
const (
|
||||
STATMAX = 65535
|
||||
ERRMAX = 128
|
||||
STATFIXLEN = 49
|
||||
)
|
||||
|
||||
// Mount and bind flags
|
||||
const (
|
||||
MREPL = 0x0000
|
||||
MBEFORE = 0x0001
|
||||
MAFTER = 0x0002
|
||||
MORDER = 0x0003
|
||||
MCREATE = 0x0004
|
||||
MCACHE = 0x0010
|
||||
MMASK = 0x0017
|
||||
)
|
|
@ -0,0 +1,212 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Plan 9 directory marshalling. See intro(5).
|
||||
|
||||
package plan9
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrShortStat = errors.New("stat buffer too short")
|
||||
ErrBadStat = errors.New("malformed stat buffer")
|
||||
ErrBadName = errors.New("bad character in file name")
|
||||
)
|
||||
|
||||
// A Qid represents a 9P server's unique identification for a file.
|
||||
type Qid struct {
|
||||
Path uint64 // the file server's unique identification for the file
|
||||
Vers uint32 // version number for given Path
|
||||
Type uint8 // the type of the file (plan9.QTDIR for example)
|
||||
}
|
||||
|
||||
// A Dir contains the metadata for a file.
|
||||
type Dir struct {
|
||||
// system-modified data
|
||||
Type uint16 // server type
|
||||
Dev uint32 // server subtype
|
||||
|
||||
// file data
|
||||
Qid Qid // unique id from server
|
||||
Mode uint32 // permissions
|
||||
Atime uint32 // last read time
|
||||
Mtime uint32 // last write time
|
||||
Length int64 // file length
|
||||
Name string // last element of path
|
||||
Uid string // owner name
|
||||
Gid string // group name
|
||||
Muid string // last modifier name
|
||||
}
|
||||
|
||||
var nullDir = Dir{
|
||||
Type: ^uint16(0),
|
||||
Dev: ^uint32(0),
|
||||
Qid: Qid{
|
||||
Path: ^uint64(0),
|
||||
Vers: ^uint32(0),
|
||||
Type: ^uint8(0),
|
||||
},
|
||||
Mode: ^uint32(0),
|
||||
Atime: ^uint32(0),
|
||||
Mtime: ^uint32(0),
|
||||
Length: ^int64(0),
|
||||
}
|
||||
|
||||
// Null assigns special "don't touch" values to members of d to
|
||||
// avoid modifying them during plan9.Wstat.
|
||||
func (d *Dir) Null() { *d = nullDir }
|
||||
|
||||
// Marshal encodes a 9P stat message corresponding to d into b
|
||||
//
|
||||
// If there isn't enough space in b for a stat message, ErrShortStat is returned.
|
||||
func (d *Dir) Marshal(b []byte) (n int, err error) {
|
||||
n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
|
||||
if n > len(b) {
|
||||
return n, ErrShortStat
|
||||
}
|
||||
|
||||
for _, c := range d.Name {
|
||||
if c == '/' {
|
||||
return n, ErrBadName
|
||||
}
|
||||
}
|
||||
|
||||
b = pbit16(b, uint16(n)-2)
|
||||
b = pbit16(b, d.Type)
|
||||
b = pbit32(b, d.Dev)
|
||||
b = pbit8(b, d.Qid.Type)
|
||||
b = pbit32(b, d.Qid.Vers)
|
||||
b = pbit64(b, d.Qid.Path)
|
||||
b = pbit32(b, d.Mode)
|
||||
b = pbit32(b, d.Atime)
|
||||
b = pbit32(b, d.Mtime)
|
||||
b = pbit64(b, uint64(d.Length))
|
||||
b = pstring(b, d.Name)
|
||||
b = pstring(b, d.Uid)
|
||||
b = pstring(b, d.Gid)
|
||||
b = pstring(b, d.Muid)
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
|
||||
//
|
||||
// If b is too small to hold a valid stat message, ErrShortStat is returned.
|
||||
//
|
||||
// If the stat message itself is invalid, ErrBadStat is returned.
|
||||
func UnmarshalDir(b []byte) (*Dir, error) {
|
||||
if len(b) < STATFIXLEN {
|
||||
return nil, ErrShortStat
|
||||
}
|
||||
size, buf := gbit16(b)
|
||||
if len(b) != int(size)+2 {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
b = buf
|
||||
|
||||
var d Dir
|
||||
d.Type, b = gbit16(b)
|
||||
d.Dev, b = gbit32(b)
|
||||
d.Qid.Type, b = gbit8(b)
|
||||
d.Qid.Vers, b = gbit32(b)
|
||||
d.Qid.Path, b = gbit64(b)
|
||||
d.Mode, b = gbit32(b)
|
||||
d.Atime, b = gbit32(b)
|
||||
d.Mtime, b = gbit32(b)
|
||||
|
||||
n, b := gbit64(b)
|
||||
d.Length = int64(n)
|
||||
|
||||
var ok bool
|
||||
if d.Name, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
if d.Uid, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
if d.Gid, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
if d.Muid, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
|
||||
func pbit8(b []byte, v uint8) []byte {
|
||||
b[0] = byte(v)
|
||||
return b[1:]
|
||||
}
|
||||
|
||||
// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit16(b []byte, v uint16) []byte {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
return b[2:]
|
||||
}
|
||||
|
||||
// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit32(b []byte, v uint32) []byte {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
return b[4:]
|
||||
}
|
||||
|
||||
// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit64(b []byte, v uint64) []byte {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
b[4] = byte(v >> 32)
|
||||
b[5] = byte(v >> 40)
|
||||
b[6] = byte(v >> 48)
|
||||
b[7] = byte(v >> 56)
|
||||
return b[8:]
|
||||
}
|
||||
|
||||
// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
|
||||
// returning the remaining slice of b..
|
||||
func pstring(b []byte, s string) []byte {
|
||||
b = pbit16(b, uint16(len(s)))
|
||||
n := copy(b, s)
|
||||
return b[n:]
|
||||
}
|
||||
|
||||
// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
|
||||
func gbit8(b []byte) (uint8, []byte) {
|
||||
return uint8(b[0]), b[1:]
|
||||
}
|
||||
|
||||
// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit16(b []byte) (uint16, []byte) {
|
||||
return uint16(b[0]) | uint16(b[1])<<8, b[2:]
|
||||
}
|
||||
|
||||
// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit32(b []byte) (uint32, []byte) {
|
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
|
||||
}
|
||||
|
||||
// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit64(b []byte) (uint64, []byte) {
|
||||
lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24
|
||||
return uint64(lo) | uint64(hi)<<32, b[8:]
|
||||
}
|
||||
|
||||
// gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
|
||||
// It returns the string with the remaining slice of b and a boolean. If the length is
|
||||
// greater than the number of bytes in b, the boolean will be false.
|
||||
func gstring(b []byte) (string, []byte, bool) {
|
||||
n, b := gbit16(b)
|
||||
if int(n) > len(b) {
|
||||
return "", b, false
|
||||
}
|
||||
return string(b[:n]), b[n:], true
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Plan 9 environment variables.
|
||||
|
||||
package plan9
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func Getenv(key string) (value string, found bool) {
|
||||
return syscall.Getenv(key)
|
||||
}
|
||||
|
||||
func Setenv(key, value string) error {
|
||||
return syscall.Setenv(key, value)
|
||||
}
|
||||
|
||||
func Clearenv() {
|
||||
syscall.Clearenv()
|
||||
}
|
||||
|
||||
func Environ() []string {
|
||||
return syscall.Environ()
|
||||
}
|
||||
|
||||
func Unsetenv(key string) error {
|
||||
return syscall.Unsetenv(key)
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package plan9
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Constants
|
||||
const (
|
||||
// Invented values to support what package os expects.
|
||||
O_CREAT = 0x02000
|
||||
O_APPEND = 0x00400
|
||||
O_NOCTTY = 0x00000
|
||||
O_NONBLOCK = 0x00000
|
||||
O_SYNC = 0x00000
|
||||
O_ASYNC = 0x00000
|
||||
|
||||
S_IFMT = 0x1f000
|
||||
S_IFIFO = 0x1000
|
||||
S_IFCHR = 0x2000
|
||||
S_IFDIR = 0x4000
|
||||
S_IFBLK = 0x6000
|
||||
S_IFREG = 0x8000
|
||||
S_IFLNK = 0xa000
|
||||
S_IFSOCK = 0xc000
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
EINVAL = syscall.NewError("bad arg in system call")
|
||||
ENOTDIR = syscall.NewError("not a directory")
|
||||
EISDIR = syscall.NewError("file is a directory")
|
||||
ENOENT = syscall.NewError("file does not exist")
|
||||
EEXIST = syscall.NewError("file already exists")
|
||||
EMFILE = syscall.NewError("no free file descriptors")
|
||||
EIO = syscall.NewError("i/o error")
|
||||
ENAMETOOLONG = syscall.NewError("file name too long")
|
||||
EINTR = syscall.NewError("interrupted")
|
||||
EPERM = syscall.NewError("permission denied")
|
||||
EBUSY = syscall.NewError("no free devices")
|
||||
ETIMEDOUT = syscall.NewError("connection timed out")
|
||||
EPLAN9 = syscall.NewError("not supported by plan 9")
|
||||
|
||||
// The following errors do not correspond to any
|
||||
// Plan 9 system messages. Invented to support
|
||||
// what package os and others expect.
|
||||
EACCES = syscall.NewError("access permission denied")
|
||||
EAFNOSUPPORT = syscall.NewError("address family not supported by protocol")
|
||||
)
|
|
@ -0,0 +1,138 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# The plan9 package provides access to the raw system call
|
||||
# interface of the underlying operating system. Porting Go to
|
||||
# a new architecture/operating system combination requires
|
||||
# some manual effort, though there are tools that automate
|
||||
# much of the process. The auto-generated files have names
|
||||
# beginning with z.
|
||||
#
|
||||
# This script runs or (given -n) prints suggested commands to generate z files
|
||||
# for the current system. Running those commands is not automatic.
|
||||
# This script is documentation more than anything else.
|
||||
#
|
||||
# * asm_${GOOS}_${GOARCH}.s
|
||||
#
|
||||
# This hand-written assembly file implements system call dispatch.
|
||||
# There are three entry points:
|
||||
#
|
||||
# func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr);
|
||||
# func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
|
||||
# func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr);
|
||||
#
|
||||
# The first and second are the standard ones; they differ only in
|
||||
# how many arguments can be passed to the kernel.
|
||||
# The third is for low-level use by the ForkExec wrapper;
|
||||
# unlike the first two, it does not call into the scheduler to
|
||||
# let it know that a system call is running.
|
||||
#
|
||||
# * syscall_${GOOS}.go
|
||||
#
|
||||
# This hand-written Go file implements system calls that need
|
||||
# special handling and lists "//sys" comments giving prototypes
|
||||
# for ones that can be auto-generated. Mksyscall reads those
|
||||
# comments to generate the stubs.
|
||||
#
|
||||
# * syscall_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Same as syscall_${GOOS}.go except that it contains code specific
|
||||
# to ${GOOS} on one particular architecture.
|
||||
#
|
||||
# * types_${GOOS}.c
|
||||
#
|
||||
# This hand-written C file includes standard C headers and then
|
||||
# creates typedef or enum names beginning with a dollar sign
|
||||
# (use of $ in variable names is a gcc extension). The hardest
|
||||
# part about preparing this file is figuring out which headers to
|
||||
# include and which symbols need to be #defined to get the
|
||||
# actual data structures that pass through to the kernel system calls.
|
||||
# Some C libraries present alternate versions for binary compatibility
|
||||
# and translate them on the way in and out of system calls, but
|
||||
# there is almost always a #define that can get the real ones.
|
||||
# See types_darwin.c and types_linux.c for examples.
|
||||
#
|
||||
# * zerror_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# This machine-generated file defines the system's error numbers,
|
||||
# error strings, and signal numbers. The generator is "mkerrors.sh".
|
||||
# Usually no arguments are needed, but mkerrors.sh will pass its
|
||||
# arguments on to godefs.
|
||||
#
|
||||
# * zsyscall_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Generated by mksyscall.pl; see syscall_${GOOS}.go above.
|
||||
#
|
||||
# * zsysnum_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Generated by mksysnum_${GOOS}.
|
||||
#
|
||||
# * ztypes_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Generated by godefs; see types_${GOOS}.c above.
|
||||
|
||||
GOOSARCH="${GOOS}_${GOARCH}"
|
||||
|
||||
# defaults
|
||||
mksyscall="./mksyscall.pl"
|
||||
mkerrors="./mkerrors.sh"
|
||||
zerrors="zerrors_$GOOSARCH.go"
|
||||
mksysctl=""
|
||||
zsysctl="zsysctl_$GOOSARCH.go"
|
||||
mksysnum=
|
||||
mktypes=
|
||||
run="sh"
|
||||
|
||||
case "$1" in
|
||||
-syscalls)
|
||||
for i in zsyscall*go
|
||||
do
|
||||
sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i
|
||||
rm _$i
|
||||
done
|
||||
exit 0
|
||||
;;
|
||||
-n)
|
||||
run="cat"
|
||||
shift
|
||||
esac
|
||||
|
||||
case "$#" in
|
||||
0)
|
||||
;;
|
||||
*)
|
||||
echo 'usage: mkall.sh [-n]' 1>&2
|
||||
exit 2
|
||||
esac
|
||||
|
||||
case "$GOOSARCH" in
|
||||
_* | *_ | _)
|
||||
echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
plan9_386)
|
||||
mkerrors=
|
||||
mksyscall="./mksyscall.pl -l32 -plan9"
|
||||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
|
||||
mktypes="XXX"
|
||||
;;
|
||||
*)
|
||||
echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
(
|
||||
if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi
|
||||
case "$GOOS" in
|
||||
plan9)
|
||||
syscall_goos="syscall_$GOOS.go"
|
||||
if [ -n "$mksyscall" ]; then echo "$mksyscall $syscall_goos syscall_$GOOSARCH.go |gofmt >zsyscall_$GOOSARCH.go"; fi
|
||||
;;
|
||||
esac
|
||||
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
|
||||
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
|
||||
if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go |gofmt >ztypes_$GOOSARCH.go"; fi
|
||||
) | $run
|
|
@ -0,0 +1,246 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# Generate Go code listing errors and other #defined constant
|
||||
# values (ENAMETOOLONG etc.), by asking the preprocessor
|
||||
# about the definitions.
|
||||
|
||||
unset LANG
|
||||
export LC_ALL=C
|
||||
export LC_CTYPE=C
|
||||
|
||||
CC=${CC:-gcc}
|
||||
|
||||
uname=$(uname)
|
||||
|
||||
includes='
|
||||
#include <sys/types.h>
|
||||
#include <sys/file.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <errno.h>
|
||||
#include <sys/signal.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
'
|
||||
|
||||
ccflags="$@"
|
||||
|
||||
# Write go tool cgo -godefs input.
|
||||
(
|
||||
echo package plan9
|
||||
echo
|
||||
echo '/*'
|
||||
indirect="includes_$(uname)"
|
||||
echo "${!indirect} $includes"
|
||||
echo '*/'
|
||||
echo 'import "C"'
|
||||
echo
|
||||
echo 'const ('
|
||||
|
||||
# The gcc command line prints all the #defines
|
||||
# it encounters while processing the input
|
||||
echo "${!indirect} $includes" | $CC -x c - -E -dM $ccflags |
|
||||
awk '
|
||||
$1 != "#define" || $2 ~ /\(/ || $3 == "" {next}
|
||||
|
||||
$2 ~ /^E([ABCD]X|[BIS]P|[SD]I|S|FL)$/ {next} # 386 registers
|
||||
$2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next}
|
||||
$2 ~ /^(SCM_SRCRT)$/ {next}
|
||||
$2 ~ /^(MAP_FAILED)$/ {next}
|
||||
|
||||
$2 !~ /^ETH_/ &&
|
||||
$2 !~ /^EPROC_/ &&
|
||||
$2 !~ /^EQUIV_/ &&
|
||||
$2 !~ /^EXPR_/ &&
|
||||
$2 ~ /^E[A-Z0-9_]+$/ ||
|
||||
$2 ~ /^B[0-9_]+$/ ||
|
||||
$2 ~ /^V[A-Z0-9]+$/ ||
|
||||
$2 ~ /^CS[A-Z0-9]/ ||
|
||||
$2 ~ /^I(SIG|CANON|CRNL|EXTEN|MAXBEL|STRIP|UTF8)$/ ||
|
||||
$2 ~ /^IGN/ ||
|
||||
$2 ~ /^IX(ON|ANY|OFF)$/ ||
|
||||
$2 ~ /^IN(LCR|PCK)$/ ||
|
||||
$2 ~ /(^FLU?SH)|(FLU?SH$)/ ||
|
||||
$2 ~ /^C(LOCAL|READ)$/ ||
|
||||
$2 == "BRKINT" ||
|
||||
$2 == "HUPCL" ||
|
||||
$2 == "PENDIN" ||
|
||||
$2 == "TOSTOP" ||
|
||||
$2 ~ /^PAR/ ||
|
||||
$2 ~ /^SIG[^_]/ ||
|
||||
$2 ~ /^O[CNPFP][A-Z]+[^_][A-Z]+$/ ||
|
||||
$2 ~ /^IN_/ ||
|
||||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
|
||||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
|
||||
$2 == "ICMPV6_FILTER" ||
|
||||
$2 == "SOMAXCONN" ||
|
||||
$2 == "NAME_MAX" ||
|
||||
$2 == "IFNAMSIZ" ||
|
||||
$2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ ||
|
||||
$2 ~ /^SYSCTL_VERS/ ||
|
||||
$2 ~ /^(MS|MNT)_/ ||
|
||||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
|
||||
$2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ ||
|
||||
$2 ~ /^LINUX_REBOOT_CMD_/ ||
|
||||
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
|
||||
$2 !~ "NLA_TYPE_MASK" &&
|
||||
$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ ||
|
||||
$2 ~ /^SIOC/ ||
|
||||
$2 ~ /^TIOC/ ||
|
||||
$2 !~ "RTF_BITS" &&
|
||||
$2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ ||
|
||||
$2 ~ /^BIOC/ ||
|
||||
$2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ ||
|
||||
$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ ||
|
||||
$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
|
||||
$2 ~ /^CLONE_[A-Z_]+/ ||
|
||||
$2 !~ /^(BPF_TIMEVAL)$/ &&
|
||||
$2 ~ /^(BPF|DLT)_/ ||
|
||||
$2 !~ "WMESGLEN" &&
|
||||
$2 ~ /^W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", $2, $2)}
|
||||
$2 ~ /^__WCOREFLAG$/ {next}
|
||||
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
|
||||
|
||||
{next}
|
||||
' | sort
|
||||
|
||||
echo ')'
|
||||
) >_const.go
|
||||
|
||||
# Pull out the error names for later.
|
||||
errors=$(
|
||||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print $2 }' |
|
||||
sort
|
||||
)
|
||||
|
||||
# Pull out the signal names for later.
|
||||
signals=$(
|
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' |
|
||||
egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
|
||||
sort
|
||||
)
|
||||
|
||||
# Again, writing regexps to a file.
|
||||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print "^\t" $2 "[ \t]*=" }' |
|
||||
sort >_error.grep
|
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' |
|
||||
egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
|
||||
sort >_signal.grep
|
||||
|
||||
echo '// mkerrors.sh' "$@"
|
||||
echo '// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT'
|
||||
echo
|
||||
go tool cgo -godefs -- "$@" _const.go >_error.out
|
||||
cat _error.out | grep -vf _error.grep | grep -vf _signal.grep
|
||||
echo
|
||||
echo '// Errors'
|
||||
echo 'const ('
|
||||
cat _error.out | grep -f _error.grep | sed 's/=\(.*\)/= Errno(\1)/'
|
||||
echo ')'
|
||||
|
||||
echo
|
||||
echo '// Signals'
|
||||
echo 'const ('
|
||||
cat _error.out | grep -f _signal.grep | sed 's/=\(.*\)/= Signal(\1)/'
|
||||
echo ')'
|
||||
|
||||
# Run C program to print error and syscall strings.
|
||||
(
|
||||
echo -E "
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
||||
enum { A = 'A', Z = 'Z', a = 'a', z = 'z' }; // avoid need for single quotes below
|
||||
|
||||
int errors[] = {
|
||||
"
|
||||
for i in $errors
|
||||
do
|
||||
echo -E ' '$i,
|
||||
done
|
||||
|
||||
echo -E "
|
||||
};
|
||||
|
||||
int signals[] = {
|
||||
"
|
||||
for i in $signals
|
||||
do
|
||||
echo -E ' '$i,
|
||||
done
|
||||
|
||||
# Use -E because on some systems bash builtin interprets \n itself.
|
||||
echo -E '
|
||||
};
|
||||
|
||||
static int
|
||||
intcmp(const void *a, const void *b)
|
||||
{
|
||||
return *(int*)a - *(int*)b;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
int i, j, e;
|
||||
char buf[1024], *p;
|
||||
|
||||
printf("\n\n// Error table\n");
|
||||
printf("var errors = [...]string {\n");
|
||||
qsort(errors, nelem(errors), sizeof errors[0], intcmp);
|
||||
for(i=0; i<nelem(errors); i++) {
|
||||
e = errors[i];
|
||||
if(i > 0 && errors[i-1] == e)
|
||||
continue;
|
||||
strcpy(buf, strerror(e));
|
||||
// lowercase first letter: Bad -> bad, but STREAM -> STREAM.
|
||||
if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
|
||||
buf[0] += a - A;
|
||||
printf("\t%d: \"%s\",\n", e, buf);
|
||||
}
|
||||
printf("}\n\n");
|
||||
|
||||
printf("\n\n// Signal table\n");
|
||||
printf("var signals = [...]string {\n");
|
||||
qsort(signals, nelem(signals), sizeof signals[0], intcmp);
|
||||
for(i=0; i<nelem(signals); i++) {
|
||||
e = signals[i];
|
||||
if(i > 0 && signals[i-1] == e)
|
||||
continue;
|
||||
strcpy(buf, strsignal(e));
|
||||
// lowercase first letter: Bad -> bad, but STREAM -> STREAM.
|
||||
if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
|
||||
buf[0] += a - A;
|
||||
// cut trailing : number.
|
||||
p = strrchr(buf, ":"[0]);
|
||||
if(p)
|
||||
*p = '\0';
|
||||
printf("\t%d: \"%s\",\n", e, buf);
|
||||
}
|
||||
printf("}\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
'
|
||||
) >_errors.c
|
||||
|
||||
$CC $ccflags -o _errors _errors.c && $GORUN ./_errors && rm -f _errors.c _errors _const.go _error.grep _signal.grep _error.out
|
|
@ -0,0 +1,319 @@
|
|||
#!/usr/bin/env perl
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# This program reads a file containing function prototypes
|
||||
# (like syscall_plan9.go) and generates system call bodies.
|
||||
# The prototypes are marked by lines beginning with "//sys"
|
||||
# and read like func declarations if //sys is replaced by func, but:
|
||||
# * The parameter lists must give a name for each argument.
|
||||
# This includes return parameters.
|
||||
# * The parameter lists must give a type for each argument:
|
||||
# the (x, y, z int) shorthand is not allowed.
|
||||
# * If the return parameter is an error number, it must be named errno.
|
||||
|
||||
# A line beginning with //sysnb is like //sys, except that the
|
||||
# goroutine will not be suspended during the execution of the system
|
||||
# call. This must only be used for system calls which can never
|
||||
# block, as otherwise the system call could cause all goroutines to
|
||||
# hang.
|
||||
|
||||
use strict;
|
||||
|
||||
my $cmdline = "mksyscall.pl " . join(' ', @ARGV);
|
||||
my $errors = 0;
|
||||
my $_32bit = "";
|
||||
my $plan9 = 0;
|
||||
my $openbsd = 0;
|
||||
my $netbsd = 0;
|
||||
my $dragonfly = 0;
|
||||
my $nacl = 0;
|
||||
my $arm = 0; # 64-bit value should use (even, odd)-pair
|
||||
|
||||
if($ARGV[0] eq "-b32") {
|
||||
$_32bit = "big-endian";
|
||||
shift;
|
||||
} elsif($ARGV[0] eq "-l32") {
|
||||
$_32bit = "little-endian";
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-plan9") {
|
||||
$plan9 = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-openbsd") {
|
||||
$openbsd = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-netbsd") {
|
||||
$netbsd = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-dragonfly") {
|
||||
$dragonfly = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-nacl") {
|
||||
$nacl = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-arm") {
|
||||
$arm = 1;
|
||||
shift;
|
||||
}
|
||||
|
||||
if($ARGV[0] =~ /^-/) {
|
||||
print STDERR "usage: mksyscall.pl [-b32 | -l32] [file ...]\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub parseparamlist($) {
|
||||
my ($list) = @_;
|
||||
$list =~ s/^\s*//;
|
||||
$list =~ s/\s*$//;
|
||||
if($list eq "") {
|
||||
return ();
|
||||
}
|
||||
return split(/\s*,\s*/, $list);
|
||||
}
|
||||
|
||||
sub parseparam($) {
|
||||
my ($p) = @_;
|
||||
if($p !~ /^(\S*) (\S*)$/) {
|
||||
print STDERR "$ARGV:$.: malformed parameter: $p\n";
|
||||
$errors = 1;
|
||||
return ("xx", "int");
|
||||
}
|
||||
return ($1, $2);
|
||||
}
|
||||
|
||||
my $text = "";
|
||||
while(<>) {
|
||||
chomp;
|
||||
s/\s+/ /g;
|
||||
s/^\s+//;
|
||||
s/\s+$//;
|
||||
my $nonblock = /^\/\/sysnb /;
|
||||
next if !/^\/\/sys / && !$nonblock;
|
||||
|
||||
# Line must be of the form
|
||||
# func Open(path string, mode int, perm int) (fd int, errno error)
|
||||
# Split into name, in params, out params.
|
||||
if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$/) {
|
||||
print STDERR "$ARGV:$.: malformed //sys declaration\n";
|
||||
$errors = 1;
|
||||
next;
|
||||
}
|
||||
my ($func, $in, $out, $sysname) = ($2, $3, $4, $5);
|
||||
|
||||
# Split argument lists on comma.
|
||||
my @in = parseparamlist($in);
|
||||
my @out = parseparamlist($out);
|
||||
|
||||
# Try in vain to keep people from editing this file.
|
||||
# The theory is that they jump into the middle of the file
|
||||
# without reading the header.
|
||||
$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
|
||||
|
||||
# Go function header.
|
||||
my $out_decl = @out ? sprintf(" (%s)", join(', ', @out)) : "";
|
||||
$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out_decl;
|
||||
|
||||
# Check if err return available
|
||||
my $errvar = "";
|
||||
foreach my $p (@out) {
|
||||
my ($name, $type) = parseparam($p);
|
||||
if($type eq "error") {
|
||||
$errvar = $name;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
# Prepare arguments to Syscall.
|
||||
my @args = ();
|
||||
my @uses = ();
|
||||
my $n = 0;
|
||||
foreach my $p (@in) {
|
||||
my ($name, $type) = parseparam($p);
|
||||
if($type =~ /^\*/) {
|
||||
push @args, "uintptr(unsafe.Pointer($name))";
|
||||
} elsif($type eq "string" && $errvar ne "") {
|
||||
$text .= "\tvar _p$n *byte\n";
|
||||
$text .= "\t_p$n, $errvar = BytePtrFromString($name)\n";
|
||||
$text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
|
||||
push @args, "uintptr(unsafe.Pointer(_p$n))";
|
||||
push @uses, "use(unsafe.Pointer(_p$n))";
|
||||
$n++;
|
||||
} elsif($type eq "string") {
|
||||
print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
|
||||
$text .= "\tvar _p$n *byte\n";
|
||||
$text .= "\t_p$n, _ = BytePtrFromString($name)\n";
|
||||
push @args, "uintptr(unsafe.Pointer(_p$n))";
|
||||
push @uses, "use(unsafe.Pointer(_p$n))";
|
||||
$n++;
|
||||
} elsif($type =~ /^\[\](.*)/) {
|
||||
# Convert slice into pointer, length.
|
||||
# Have to be careful not to take address of &a[0] if len == 0:
|
||||
# pass dummy pointer in that case.
|
||||
# Used to pass nil, but some OSes or simulators reject write(fd, nil, 0).
|
||||
$text .= "\tvar _p$n unsafe.Pointer\n";
|
||||
$text .= "\tif len($name) > 0 {\n\t\t_p$n = unsafe.Pointer(\&${name}[0])\n\t}";
|
||||
$text .= " else {\n\t\t_p$n = unsafe.Pointer(&_zero)\n\t}";
|
||||
$text .= "\n";
|
||||
push @args, "uintptr(_p$n)", "uintptr(len($name))";
|
||||
$n++;
|
||||
} elsif($type eq "int64" && ($openbsd || $netbsd)) {
|
||||
push @args, "0";
|
||||
if($_32bit eq "big-endian") {
|
||||
push @args, "uintptr($name>>32)", "uintptr($name)";
|
||||
} elsif($_32bit eq "little-endian") {
|
||||
push @args, "uintptr($name)", "uintptr($name>>32)";
|
||||
} else {
|
||||
push @args, "uintptr($name)";
|
||||
}
|
||||
} elsif($type eq "int64" && $dragonfly) {
|
||||
if ($func !~ /^extp(read|write)/i) {
|
||||
push @args, "0";
|
||||
}
|
||||
if($_32bit eq "big-endian") {
|
||||
push @args, "uintptr($name>>32)", "uintptr($name)";
|
||||
} elsif($_32bit eq "little-endian") {
|
||||
push @args, "uintptr($name)", "uintptr($name>>32)";
|
||||
} else {
|
||||
push @args, "uintptr($name)";
|
||||
}
|
||||
} elsif($type eq "int64" && $_32bit ne "") {
|
||||
if(@args % 2 && $arm) {
|
||||
# arm abi specifies 64-bit argument uses
|
||||
# (even, odd) pair
|
||||
push @args, "0"
|
||||
}
|
||||
if($_32bit eq "big-endian") {
|
||||
push @args, "uintptr($name>>32)", "uintptr($name)";
|
||||
} else {
|
||||
push @args, "uintptr($name)", "uintptr($name>>32)";
|
||||
}
|
||||
} else {
|
||||
push @args, "uintptr($name)";
|
||||
}
|
||||
}
|
||||
|
||||
# Determine which form to use; pad args with zeros.
|
||||
my $asm = "Syscall";
|
||||
if ($nonblock) {
|
||||
$asm = "RawSyscall";
|
||||
}
|
||||
if(@args <= 3) {
|
||||
while(@args < 3) {
|
||||
push @args, "0";
|
||||
}
|
||||
} elsif(@args <= 6) {
|
||||
$asm .= "6";
|
||||
while(@args < 6) {
|
||||
push @args, "0";
|
||||
}
|
||||
} elsif(@args <= 9) {
|
||||
$asm .= "9";
|
||||
while(@args < 9) {
|
||||
push @args, "0";
|
||||
}
|
||||
} else {
|
||||
print STDERR "$ARGV:$.: too many arguments to system call\n";
|
||||
}
|
||||
|
||||
# System call number.
|
||||
if($sysname eq "") {
|
||||
$sysname = "SYS_$func";
|
||||
$sysname =~ s/([a-z])([A-Z])/${1}_$2/g; # turn FooBar into Foo_Bar
|
||||
$sysname =~ y/a-z/A-Z/;
|
||||
if($nacl) {
|
||||
$sysname =~ y/A-Z/a-z/;
|
||||
}
|
||||
}
|
||||
|
||||
# Actual call.
|
||||
my $args = join(', ', @args);
|
||||
my $call = "$asm($sysname, $args)";
|
||||
|
||||
# Assign return values.
|
||||
my $body = "";
|
||||
my @ret = ("_", "_", "_");
|
||||
my $do_errno = 0;
|
||||
for(my $i=0; $i<@out; $i++) {
|
||||
my $p = $out[$i];
|
||||
my ($name, $type) = parseparam($p);
|
||||
my $reg = "";
|
||||
if($name eq "err" && !$plan9) {
|
||||
$reg = "e1";
|
||||
$ret[2] = $reg;
|
||||
$do_errno = 1;
|
||||
} elsif($name eq "err" && $plan9) {
|
||||
$ret[0] = "r0";
|
||||
$ret[2] = "e1";
|
||||
next;
|
||||
} else {
|
||||
$reg = sprintf("r%d", $i);
|
||||
$ret[$i] = $reg;
|
||||
}
|
||||
if($type eq "bool") {
|
||||
$reg = "$reg != 0";
|
||||
}
|
||||
if($type eq "int64" && $_32bit ne "") {
|
||||
# 64-bit number in r1:r0 or r0:r1.
|
||||
if($i+2 > @out) {
|
||||
print STDERR "$ARGV:$.: not enough registers for int64 return\n";
|
||||
}
|
||||
if($_32bit eq "big-endian") {
|
||||
$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
|
||||
} else {
|
||||
$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
|
||||
}
|
||||
$ret[$i] = sprintf("r%d", $i);
|
||||
$ret[$i+1] = sprintf("r%d", $i+1);
|
||||
}
|
||||
if($reg ne "e1" || $plan9) {
|
||||
$body .= "\t$name = $type($reg)\n";
|
||||
}
|
||||
}
|
||||
if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
|
||||
$text .= "\t$call\n";
|
||||
} else {
|
||||
$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
|
||||
}
|
||||
foreach my $use (@uses) {
|
||||
$text .= "\t$use\n";
|
||||
}
|
||||
$text .= $body;
|
||||
|
||||
if ($plan9 && $ret[2] eq "e1") {
|
||||
$text .= "\tif int32(r0) == -1 {\n";
|
||||
$text .= "\t\terr = e1\n";
|
||||
$text .= "\t}\n";
|
||||
} elsif ($do_errno) {
|
||||
$text .= "\tif e1 != 0 {\n";
|
||||
$text .= "\t\terr = e1\n";
|
||||
$text .= "\t}\n";
|
||||
}
|
||||
$text .= "\treturn\n";
|
||||
$text .= "}\n\n";
|
||||
}
|
||||
|
||||
chomp $text;
|
||||
chomp $text;
|
||||
|
||||
if($errors) {
|
||||
exit 1;
|
||||
}
|
||||
|
||||
print <<EOF;
|
||||
// $cmdline
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package plan9
|
||||
|
||||
import "unsafe"
|
||||
|
||||
$text
|
||||
EOF
|
||||
exit 0;
|
|
@ -0,0 +1,23 @@
|
|||
#!/bin/sh
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
COMMAND="mksysnum_plan9.sh $@"
|
||||
|
||||
cat <<EOF
|
||||
// $COMMAND
|
||||
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
|
||||
|
||||
package plan9
|
||||
|
||||
const(
|
||||
EOF
|
||||
|
||||
SP='[ ]' # space or tab
|
||||
sed "s/^#define${SP}\\([A-Z0-9_][A-Z0-9_]*\\)${SP}${SP}*\\([0-9][0-9]*\\)/SYS_\\1=\\2/g" \
|
||||
< $1 | grep -v SYS__
|
||||
|
||||
cat <<EOF
|
||||
)
|
||||
EOF
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
package plan9
|
||||
|
||||
import "syscall"
|
||||
|
||||
func fixwd() {
|
||||
syscall.Fixwd()
|
||||
}
|
||||
|
||||
func Getwd() (wd string, err error) {
|
||||
return syscall.Getwd()
|
||||
}
|
||||
|
||||
func Chdir(path string) error {
|
||||
return syscall.Chdir(path)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.5
|
||||
|
||||
package plan9
|
||||
|
||||
func fixwd() {
|
||||
}
|
||||
|
||||
func Getwd() (wd string, err error) {
|
||||
fd, err := open(".", O_RDONLY)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer Close(fd)
|
||||
return Fd2path(fd)
|
||||
}
|
||||
|
||||
func Chdir(path string) error {
|
||||
return chdir(path)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build plan9,race
|
||||
|
||||
package plan9
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const raceenabled = true
|
||||
|
||||
func raceAcquire(addr unsafe.Pointer) {
|
||||
runtime.RaceAcquire(addr)
|
||||
}
|
||||
|
||||
func raceReleaseMerge(addr unsafe.Pointer) {
|
||||
runtime.RaceReleaseMerge(addr)
|
||||
}
|
||||
|
||||
func raceReadRange(addr unsafe.Pointer, len int) {
|
||||
runtime.RaceReadRange(addr, len)
|
||||
}
|
||||
|
||||
func raceWriteRange(addr unsafe.Pointer, len int) {
|
||||
runtime.RaceWriteRange(addr, len)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build plan9,!race
|
||||
|
||||
package plan9
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const raceenabled = false
|
||||
|
||||
func raceAcquire(addr unsafe.Pointer) {
|
||||
}
|
||||
|
||||
func raceReleaseMerge(addr unsafe.Pointer) {
|
||||
}
|
||||
|
||||
func raceReadRange(addr unsafe.Pointer, len int) {
|
||||
}
|
||||
|
||||
func raceWriteRange(addr unsafe.Pointer, len int) {
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build plan9
|
||||
|
||||
package plan9
|
||||
|
||||
func itoa(val int) string { // do it here rather than with fmt to avoid dependency
|
||||
if val < 0 {
|
||||
return "-" + itoa(-val)
|
||||
}
|
||||
var buf [32]byte // big enough for int64
|
||||
i := len(buf) - 1
|
||||
for val >= 10 {
|
||||
buf[i] = byte(val%10 + '0')
|
||||
i--
|
||||
val /= 10
|
||||
}
|
||||
buf[i] = byte(val + '0')
|
||||
return string(buf[i:])
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build plan9
|
||||
|
||||
// Package plan9 contains an interface to the low-level operating system
|
||||
// primitives. OS details vary depending on the underlying system, and
|
||||
// by default, godoc will display the OS-specific documentation for the current
|
||||
// system. If you want godoc to display documentation for another
|
||||
// system, set $GOOS and $GOARCH to the desired system. For example, if
|
||||
// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
|
||||
// to freebsd and $GOARCH to arm.
|
||||
// The primary use of this package is inside other packages that provide a more
|
||||
// portable interface to the system, such as "os", "time" and "net". Use
|
||||
// those packages rather than this one if you can.
|
||||
// For details of the functions and data types in this package consult
|
||||
// the manuals for the appropriate operating system.
|
||||
// These calls return err == nil to indicate success; otherwise
|
||||
// err represents an operating system error describing the failure and
|
||||
// holds a value of type syscall.ErrorString.
|
||||
package plan9 // import "golang.org/x/sys/plan9"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// ByteSliceFromString returns a NUL-terminated slice of bytes
|
||||
// containing the text of s. If s contains a NUL byte at any
|
||||
// location, it returns (nil, EINVAL).
|
||||
func ByteSliceFromString(s string) ([]byte, error) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == 0 {
|
||||
return nil, EINVAL
|
||||
}
|
||||
}
|
||||
a := make([]byte, len(s)+1)
|
||||
copy(a, s)
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// BytePtrFromString returns a pointer to a NUL-terminated array of
|
||||
// bytes containing the text of s. If s contains a NUL byte at any
|
||||
// location, it returns (nil, EINVAL).
|
||||
func BytePtrFromString(s string) (*byte, error) {
|
||||
a, err := ByteSliceFromString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &a[0], nil
|
||||
}
|
||||
|
||||
// Single-word zero for use when we need a valid pointer to 0 bytes.
|
||||
// See mksyscall.pl.
|
||||
var _zero uintptr
|
||||
|
||||
func (ts *Timespec) Unix() (sec int64, nsec int64) {
|
||||
return int64(ts.Sec), int64(ts.Nsec)
|
||||
}
|
||||
|
||||
func (tv *Timeval) Unix() (sec int64, nsec int64) {
|
||||
return int64(tv.Sec), int64(tv.Usec) * 1000
|
||||
}
|
||||
|
||||
func (ts *Timespec) Nano() int64 {
|
||||
return int64(ts.Sec)*1e9 + int64(ts.Nsec)
|
||||
}
|
||||
|
||||
func (tv *Timeval) Nano() int64 {
|
||||
return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
|
||||
}
|
||||
|
||||
// use is a no-op, but the compiler cannot see that it is.
|
||||
// Calling use(p) ensures that p is kept live until that point.
|
||||
//go:noescape
|
||||
func use(p unsafe.Pointer)
|
|
@ -0,0 +1,349 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Plan 9 system calls.
|
||||
// This file is compiled as ordinary Go code,
|
||||
// but it is also input to mksyscall,
|
||||
// which parses the //sys lines and generates system call stubs.
|
||||
// Note that sometimes we use a lowercase //sys name and
|
||||
// wrap it in our own nicer implementation.
|
||||
|
||||
package plan9
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// A Note is a string describing a process note.
|
||||
// It implements the os.Signal interface.
|
||||
type Note string
|
||||
|
||||
func (n Note) Signal() {}
|
||||
|
||||
func (n Note) String() string {
|
||||
return string(n)
|
||||
}
|
||||
|
||||
var (
|
||||
Stdin = 0
|
||||
Stdout = 1
|
||||
Stderr = 2
|
||||
)
|
||||
|
||||
// For testing: clients can set this flag to force
|
||||
// creation of IPv6 sockets to return EAFNOSUPPORT.
|
||||
var SocketDisableIPv6 bool
|
||||
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.ErrorString)
|
||||
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.ErrorString)
|
||||
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
|
||||
func atoi(b []byte) (n uint) {
|
||||
n = 0
|
||||
for i := 0; i < len(b); i++ {
|
||||
n = n*10 + uint(b[i]-'0')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cstring(s []byte) string {
|
||||
for i := range s {
|
||||
if s[i] == 0 {
|
||||
return string(s[0:i])
|
||||
}
|
||||
}
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func errstr() string {
|
||||
var buf [ERRMAX]byte
|
||||
|
||||
RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
|
||||
|
||||
buf[len(buf)-1] = 0
|
||||
return cstring(buf[:])
|
||||
}
|
||||
|
||||
// Implemented in assembly to import from runtime.
|
||||
func exit(code int)
|
||||
|
||||
func Exit(code int) { exit(code) }
|
||||
|
||||
func readnum(path string) (uint, error) {
|
||||
var b [12]byte
|
||||
|
||||
fd, e := Open(path, O_RDONLY)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
defer Close(fd)
|
||||
|
||||
n, e := Pread(fd, b[:], 0)
|
||||
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
|
||||
m := 0
|
||||
for ; m < n && b[m] == ' '; m++ {
|
||||
}
|
||||
|
||||
return atoi(b[m : n-1]), nil
|
||||
}
|
||||
|
||||
func Getpid() (pid int) {
|
||||
n, _ := readnum("#c/pid")
|
||||
return int(n)
|
||||
}
|
||||
|
||||
func Getppid() (ppid int) {
|
||||
n, _ := readnum("#c/ppid")
|
||||
return int(n)
|
||||
}
|
||||
|
||||
func Read(fd int, p []byte) (n int, err error) {
|
||||
return Pread(fd, p, -1)
|
||||
}
|
||||
|
||||
func Write(fd int, p []byte) (n int, err error) {
|
||||
return Pwrite(fd, p, -1)
|
||||
}
|
||||
|
||||
var ioSync int64
|
||||
|
||||
//sys fd2path(fd int, buf []byte) (err error)
|
||||
func Fd2path(fd int) (path string, err error) {
|
||||
var buf [512]byte
|
||||
|
||||
e := fd2path(fd, buf[:])
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
return cstring(buf[:]), nil
|
||||
}
|
||||
|
||||
//sys pipe(p *[2]int32) (err error)
|
||||
func Pipe(p []int) (err error) {
|
||||
if len(p) != 2 {
|
||||
return syscall.ErrorString("bad arg in system call")
|
||||
}
|
||||
var pp [2]int32
|
||||
err = pipe(&pp)
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
return
|
||||
}
|
||||
|
||||
// Underlying system call writes to newoffset via pointer.
|
||||
// Implemented in assembly to avoid allocation.
|
||||
func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
|
||||
|
||||
func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
|
||||
newoffset, e := seek(0, fd, offset, whence)
|
||||
|
||||
if newoffset == -1 {
|
||||
err = syscall.ErrorString(e)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Mkdir(path string, mode uint32) (err error) {
|
||||
fd, err := Create(path, O_RDONLY, DMDIR|mode)
|
||||
|
||||
if fd != -1 {
|
||||
Close(fd)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type Waitmsg struct {
|
||||
Pid int
|
||||
Time [3]uint32
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (w Waitmsg) Exited() bool { return true }
|
||||
func (w Waitmsg) Signaled() bool { return false }
|
||||
|
||||
func (w Waitmsg) ExitStatus() int {
|
||||
if len(w.Msg) == 0 {
|
||||
// a normal exit returns no message
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
//sys await(s []byte) (n int, err error)
|
||||
func Await(w *Waitmsg) (err error) {
|
||||
var buf [512]byte
|
||||
var f [5][]byte
|
||||
|
||||
n, err := await(buf[:])
|
||||
|
||||
if err != nil || w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
nf := 0
|
||||
p := 0
|
||||
for i := 0; i < n && nf < len(f)-1; i++ {
|
||||
if buf[i] == ' ' {
|
||||
f[nf] = buf[p:i]
|
||||
p = i + 1
|
||||
nf++
|
||||
}
|
||||
}
|
||||
f[nf] = buf[p:]
|
||||
nf++
|
||||
|
||||
if nf != len(f) {
|
||||
return syscall.ErrorString("invalid wait message")
|
||||
}
|
||||
w.Pid = int(atoi(f[0]))
|
||||
w.Time[0] = uint32(atoi(f[1]))
|
||||
w.Time[1] = uint32(atoi(f[2]))
|
||||
w.Time[2] = uint32(atoi(f[3]))
|
||||
w.Msg = cstring(f[4])
|
||||
if w.Msg == "''" {
|
||||
// await() returns '' for no error
|
||||
w.Msg = ""
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Unmount(name, old string) (err error) {
|
||||
fixwd()
|
||||
oldp, err := BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldptr := uintptr(unsafe.Pointer(oldp))
|
||||
|
||||
var r0 uintptr
|
||||
var e syscall.ErrorString
|
||||
|
||||
// bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted.
|
||||
if name == "" {
|
||||
r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldptr, 0)
|
||||
} else {
|
||||
namep, err := BytePtrFromString(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(namep)), oldptr, 0)
|
||||
}
|
||||
|
||||
if int32(r0) == -1 {
|
||||
err = e
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Fchdir(fd int) (err error) {
|
||||
path, err := Fd2path(fd)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return Chdir(path)
|
||||
}
|
||||
|
||||
type Timespec struct {
|
||||
Sec int32
|
||||
Nsec int32
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int32
|
||||
Usec int32
|
||||
}
|
||||
|
||||
func NsecToTimeval(nsec int64) (tv Timeval) {
|
||||
nsec += 999 // round up to microsecond
|
||||
tv.Usec = int32(nsec % 1e9 / 1e3)
|
||||
tv.Sec = int32(nsec / 1e9)
|
||||
return
|
||||
}
|
||||
|
||||
func nsec() int64 {
|
||||
var scratch int64
|
||||
|
||||
r0, _, _ := Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0)
|
||||
// TODO(aram): remove hack after I fix _nsec in the pc64 kernel.
|
||||
if r0 == 0 {
|
||||
return scratch
|
||||
}
|
||||
return int64(r0)
|
||||
}
|
||||
|
||||
func Gettimeofday(tv *Timeval) error {
|
||||
nsec := nsec()
|
||||
*tv = NsecToTimeval(nsec)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Getpagesize() int { return 0x1000 }
|
||||
|
||||
func Getegid() (egid int) { return -1 }
|
||||
func Geteuid() (euid int) { return -1 }
|
||||
func Getgid() (gid int) { return -1 }
|
||||
func Getuid() (uid int) { return -1 }
|
||||
|
||||
func Getgroups() (gids []int, err error) {
|
||||
return make([]int, 0), nil
|
||||
}
|
||||
|
||||
//sys open(path string, mode int) (fd int, err error)
|
||||
func Open(path string, mode int) (fd int, err error) {
|
||||
fixwd()
|
||||
return open(path, mode)
|
||||
}
|
||||
|
||||
//sys create(path string, mode int, perm uint32) (fd int, err error)
|
||||
func Create(path string, mode int, perm uint32) (fd int, err error) {
|
||||
fixwd()
|
||||
return create(path, mode, perm)
|
||||
}
|
||||
|
||||
//sys remove(path string) (err error)
|
||||
func Remove(path string) error {
|
||||
fixwd()
|
||||
return remove(path)
|
||||
}
|
||||
|
||||
//sys stat(path string, edir []byte) (n int, err error)
|
||||
func Stat(path string, edir []byte) (n int, err error) {
|
||||
fixwd()
|
||||
return stat(path, edir)
|
||||
}
|
||||
|
||||
//sys bind(name string, old string, flag int) (err error)
|
||||
func Bind(name string, old string, flag int) (err error) {
|
||||
fixwd()
|
||||
return bind(name, old, flag)
|
||||
}
|
||||
|
||||
//sys mount(fd int, afd int, old string, flag int, aname string) (err error)
|
||||
func Mount(fd int, afd int, old string, flag int, aname string) (err error) {
|
||||
fixwd()
|
||||
return mount(fd, afd, old, flag, aname)
|
||||
}
|
||||
|
||||
//sys wstat(path string, edir []byte) (err error)
|
||||
func Wstat(path string, edir []byte) (err error) {
|
||||
fixwd()
|
||||
return wstat(path, edir)
|
||||
}
|
||||
|
||||
//sys chdir(path string) (err error)
|
||||
//sys Dup(oldfd int, newfd int) (fd int, err error)
|
||||
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
|
||||
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
|
||||
//sys Close(fd int) (err error)
|
||||
//sys Fstat(fd int, edir []byte) (n int, err error)
|
||||
//sys Fwstat(fd int, edir []byte) (err error)
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build plan9
|
||||
|
||||
package plan9_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/plan9"
|
||||
)
|
||||
|
||||
func testSetGetenv(t *testing.T, key, value string) {
|
||||
err := plan9.Setenv(key, value)
|
||||
if err != nil {
|
||||
t.Fatalf("Setenv failed to set %q: %v", value, err)
|
||||
}
|
||||
newvalue, found := plan9.Getenv(key)
|
||||
if !found {
|
||||
t.Fatalf("Getenv failed to find %v variable (want value %q)", key, value)
|
||||
}
|
||||
if newvalue != value {
|
||||
t.Fatalf("Getenv(%v) = %q; want %q", key, newvalue, value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnv(t *testing.T) {
|
||||
testSetGetenv(t, "TESTENV", "AVALUE")
|
||||
// make sure TESTENV gets set to "", not deleted
|
||||
testSetGetenv(t, "TESTENV", "")
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
// mksyscall.pl -l32 -plan9 syscall_plan9.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package plan9
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fd2path(fd int, buf []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(buf) > 0 {
|
||||
_p0 = unsafe.Pointer(&buf[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pipe(p *[2]int32) (err error) {
|
||||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func await(s []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(s) > 0 {
|
||||
_p0 = unsafe.Pointer(&s[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func open(path string, mode int) (fd int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
|
||||
use(unsafe.Pointer(_p0))
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func create(path string, mode int, perm uint32) (fd int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
|
||||
use(unsafe.Pointer(_p0))
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func remove(path string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
|
||||
use(unsafe.Pointer(_p0))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func stat(path string, edir []byte) (n int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p1 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
|
||||
use(unsafe.Pointer(_p0))
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func bind(name string, old string, flag int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
|
||||
use(unsafe.Pointer(_p0))
|
||||
use(unsafe.Pointer(_p1))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func mount(fd int, afd int, old string, flag int, aname string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = BytePtrFromString(aname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
|
||||
use(unsafe.Pointer(_p0))
|
||||
use(unsafe.Pointer(_p1))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func wstat(path string, edir []byte) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p1 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
|
||||
use(unsafe.Pointer(_p0))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func chdir(path string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
|
||||
use(unsafe.Pointer(_p0))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Dup(oldfd int, newfd int) (fd int, err error) {
|
||||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pread(fd int, p []byte, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
_p0 = unsafe.Pointer(&p[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
_p0 = unsafe.Pointer(&p[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Close(fd int) (err error) {
|
||||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fstat(fd int, edir []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p0 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fwstat(fd int, edir []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p0 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|