-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor library API and provide more helpers. Support Go 1.9.
* Support for Go 1.9 * Refactor and rewrite most of API surface * Merged SDK Dockerfile into repository * Provide helpers for networking, certs and wrapper for CLI apps * Add README
- Loading branch information
Showing
15 changed files
with
568 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
FROM ubuntu:xenial | ||
|
||
RUN apt-get update && \ | ||
apt-get install -y xz-utils build-essential libc6-i386 wget nano && \ | ||
apt-get clean | ||
|
||
# download Pocketbook SDK | ||
RUN wget https://storage.googleapis.com/dennwc-public/pbsdk-linux-1.1.0.deb -qO /tmp/pbsdk-linux.deb && \ | ||
dpkg -i /tmp/pbsdk-linux.deb && \ | ||
rm /tmp/pbsdk-linux.deb | ||
|
||
ADD ./patches/* /tmp/ | ||
|
||
# download specified Go binary release that will act as a bootstrap compiler for Go toolchain | ||
# download sources for that release and apply the patch | ||
# build a new toolchain and remove an old one | ||
RUN wget https://dl.google.com/go/go1.9.4.linux-amd64.tar.gz -qO /tmp/go.tar.gz && \ | ||
tar -xf /tmp/go.tar.gz && \ | ||
rm /tmp/go.tar.gz && \ | ||
wget https://dl.google.com/go/go1.9.4.src.tar.gz -qO /tmp/go.tar.gz && \ | ||
mkdir -p /gosrc && tar -xf /tmp/go.tar.gz -C /gosrc && \ | ||
rm /tmp/go.tar.gz && \ | ||
patch /gosrc/go/src/cmd/go/internal/work/build.go < /tmp/go-pb.patch && \ | ||
patch /gosrc/go/src/net/dnsconfig_unix.go < /tmp/dns-pb.patch && \ | ||
cd /gosrc/go/src && GOROOT_BOOTSTRAP=/go ./make.bash && \ | ||
rm -r /go && mv /gosrc/go /go && rm -r /gosrc | ||
|
||
WORKDIR /app | ||
VOLUME /app | ||
|
||
ADD build.sh / | ||
ENTRYPOINT ["/build.sh"] | ||
|
||
ADD ./* /gopath/src/github.com/dennwc/inkview/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,63 @@ | ||
Go SDK for Pocketbook based on libinkview. | ||
# Go SDK for Pocketbook | ||
|
||
To build your app or examples, run: | ||
Unofficial Go SDK for Pocketbook based on libinkview. | ||
|
||
Supports graphical user interfaces and CLI apps. | ||
|
||
## Build a CLI app | ||
|
||
Standard Go compiler should be able to cross-compile the binary | ||
for the device (no need for SDK): | ||
|
||
``` | ||
GOOS=linux GOARCH=arm GOARM=5 go build main.go | ||
``` | ||
|
||
Note that some additional workarounds are necessary if you want to access | ||
a network from your app. In this case you may still need SDK. | ||
|
||
Although this binary will run on the device, you will need a third-party | ||
application to actually see an output of you program (like | ||
[pbterm](http://users.physik.fu-berlin.de/~jtt/PB/)). | ||
|
||
The second option is to wrap the program into `RunCLI` - it will | ||
emulate terminal output and write it to device display. | ||
|
||
## Build an app with UI | ||
|
||
To build your app or any example, run (requires Docker): | ||
|
||
```bash | ||
cd ./go_app_path/ | ||
cd ./examples/devinfo/ | ||
docker run --rm -v $PWD:/app dennwc/pocketbook-go-sdk main.go | ||
``` | ||
``` | ||
|
||
You may also need to mount GOPATH to container to build your app: | ||
|
||
``` | ||
docker run --rm -v $PWD:/app -v $GOPATH:/gopath dennwc/pocketbook-go-sdk main.go | ||
``` | ||
|
||
To run an binary, copy it into `applications/app-name.app` folder | ||
on the device and it should appear in the applications list. | ||
|
||
## Notes on networking | ||
|
||
By default, device will try to shutdown network interface to save battery, | ||
thus you will need to call SDK functions to keep device online (see `KeepNetwork`). | ||
|
||
Also note that establishing TLS will require Go to read system | ||
certificate pool that might take up to 30 sec on some devices and will | ||
lead to TLS handshake timeouts. You will need to call `InitCerts` first | ||
to fix the problem. | ||
|
||
IPv6 is not enabled on some devices, thus a patch to Go DNS lib is required | ||
to skip lookup on IPv6 address (SDK already includes the patch). | ||
Similar problems may arise when trying to dial IPv6 directly. | ||
|
||
## Notes on workdir | ||
|
||
Application will have a working directory set to FS root, and not to | ||
a parent directory. | ||
To use relative paths properly change local dir to a binary's parent | ||
directory: `os.Chdir(filepath.Dir(os.Args[0]))`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package ink | ||
|
||
type App interface { | ||
// Init is called when application is started. | ||
Init() error | ||
// Close is called before exiting an application. | ||
Close() error | ||
|
||
// Draw is called each time an application view should be updated. | ||
// Can be queued by Repaint. | ||
Draw() | ||
|
||
//// Show is called when application becomes active. | ||
//// Delivered on application start and when switching from another app. | ||
//Show() bool | ||
//// Hide is called when application becomes inactive (switching to another app). | ||
//Hide() bool | ||
|
||
// Key is called on each key-related event. | ||
Key(e KeyEvent) bool | ||
// Pointer is called on each pointer-related event. | ||
Pointer(e PointerEvent) bool | ||
// Touch is called on each touch-related event. | ||
Touch(e TouchEvent) bool | ||
// Orientation is called each time an orientation of device changes. | ||
Orientation(o Orientation) bool | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#!/usr/bin/env bash | ||
export GOROOT=/go | ||
export GOPATH=/gopath | ||
export PATH="$GOROOT/bin:$PATH" | ||
cd /app | ||
CC=arm-none-linux-gnueabi-gcc GOOS=linux GOARCH=arm GOARM=5 CGO_ENABLED=1 go build "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package ink | ||
|
||
import ( | ||
"crypto/x509" | ||
"encoding/asn1" | ||
) | ||
|
||
// InitCerts will read system certificates pool. | ||
// | ||
// This pool is usually populated by the first call to tls.Dial or similar, | ||
// but this operation might take up to 30 sec on some devices, leading to handshake timeout. | ||
// | ||
// Calling this function before dialing will fix the problem. | ||
func InitCerts() error { | ||
// hand-crafted fake cert that will force system pool to be populated | ||
// but will fail with an error directly after this | ||
cert := x509.Certificate{ | ||
Raw: []byte{0}, | ||
UnhandledCriticalExtensions: []asn1.ObjectIdentifier{nil}, | ||
} | ||
_, err := cert.Verify(x509.VerifyOptions{}) | ||
if _, ok := err.(x509.SystemRootsError); ok { | ||
return err | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
package ink | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"sync" | ||
"sync/atomic" | ||
"time" | ||
) | ||
|
||
var DefaultFontHeight = 14 | ||
|
||
type RunFunc func(ctx context.Context, w io.Writer) error | ||
|
||
func newLogWriter(log *Log, update func()) *logWriter { | ||
return &logWriter{log: log, update: update} | ||
} | ||
|
||
type logWriter struct { | ||
log *Log | ||
update func() | ||
} | ||
|
||
func (w *logWriter) Write(p []byte) (int, error) { | ||
defer w.update() | ||
return w.log.Write(p) | ||
} | ||
func (w *logWriter) draw() { | ||
w.log.Draw() | ||
} | ||
func (w *logWriter) close() { | ||
w.log.Close() | ||
} | ||
|
||
func newCliApp(fnc RunFunc, c RunConfig) *cliApp { | ||
return &cliApp{cli: fnc, conf: c} | ||
} | ||
|
||
type cliApp struct { | ||
redraws int32 // atomic | ||
|
||
conf RunConfig | ||
cli RunFunc | ||
|
||
wg sync.WaitGroup | ||
err error | ||
stop func() | ||
|
||
log *logWriter | ||
|
||
stopNet func() | ||
|
||
rmu sync.Mutex | ||
running bool | ||
} | ||
|
||
func (app *cliApp) setRunning(v bool) { | ||
app.rmu.Lock() | ||
app.running = v | ||
app.rmu.Unlock() | ||
} | ||
|
||
func (app *cliApp) isRunning() bool { | ||
app.rmu.Lock() | ||
v := app.running | ||
app.rmu.Unlock() | ||
return v | ||
} | ||
|
||
func (app *cliApp) redraw() { | ||
// allow only one repaint in queue | ||
if atomic.CompareAndSwapInt32(&app.redraws, 0, 1) { | ||
Repaint() | ||
} | ||
} | ||
func (app *cliApp) draw() { | ||
ClearScreen() | ||
app.log.draw() | ||
FullUpdate() | ||
atomic.StoreInt32(&app.redraws, 0) | ||
} | ||
|
||
func (app *cliApp) println(args ...interface{}) { | ||
fmt.Fprintln(app.log, args...) | ||
} | ||
|
||
func (app *cliApp) Init() error { | ||
ClearScreen() | ||
l := NewLog(Pad(Screen(), 10), DefaultFontHeight) | ||
app.log = newLogWriter(l, app.redraw) | ||
|
||
if app.conf.Certs { | ||
now := time.Now() | ||
app.println("reading certs...") | ||
app.draw() | ||
if err := InitCerts(); err != nil { | ||
app.println("error reading certs:", err) | ||
} else { | ||
app.println("loaded certs in", time.Since(now)) | ||
} | ||
app.draw() | ||
} | ||
|
||
if app.conf.Network { | ||
var err error | ||
app.stopNet, err = KeepNetwork() | ||
if err != nil { | ||
app.println("cannot connect to the network:", err) | ||
} else { | ||
app.println("network connected") | ||
} | ||
app.draw() | ||
} | ||
|
||
app.setRunning(true) | ||
|
||
ctx, cancel := context.WithCancel(context.Background()) | ||
app.stop = cancel | ||
app.wg.Add(1) | ||
go func() { | ||
defer app.wg.Done() | ||
err := app.cli(ctx, app.log) | ||
if app.stopNet != nil { | ||
app.stopNet() | ||
} | ||
app.err = err | ||
if err != nil { | ||
app.println("error:", err) | ||
} | ||
app.println("<press any key to exit>") | ||
app.redraw() | ||
}() | ||
return nil | ||
} | ||
|
||
func (app *cliApp) stopCli() error { | ||
if !app.isRunning() { | ||
return app.err | ||
} | ||
app.stop() | ||
app.wg.Wait() | ||
app.setRunning(false) | ||
return app.err | ||
} | ||
|
||
func (app *cliApp) Close() error { | ||
err := app.stopCli() | ||
app.log.close() | ||
if app.stopNet != nil { | ||
app.stopNet() | ||
} | ||
return err | ||
} | ||
|
||
func (app *cliApp) Draw() { | ||
app.draw() | ||
} | ||
|
||
func (*cliApp) Show() bool { | ||
return false | ||
} | ||
|
||
func (*cliApp) Hide() bool { | ||
return false | ||
} | ||
|
||
func (app *cliApp) Key(e KeyEvent) bool { | ||
if app.isRunning() || (e.Key == KeyPrev && e.State == KeyStateDown) { | ||
Exit() | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
func (app *cliApp) Pointer(e PointerEvent) bool { | ||
if app.isRunning() { | ||
Exit() | ||
return true | ||
} | ||
if e.State == PointerDown { | ||
app.redraw() | ||
} | ||
return true | ||
} | ||
|
||
func (*cliApp) Touch(e TouchEvent) bool { | ||
return false | ||
} | ||
|
||
func (*cliApp) Orientation(o Orientation) bool { | ||
return false | ||
} | ||
|
||
type RunConfig struct { | ||
Certs bool // initialize certificate pool | ||
Network bool // keep networking enabled while app is running | ||
} | ||
|
||
// RunCLI starts a command-line application that can write to device display. | ||
// Context will be cancelled when application is closed. | ||
// Provided callback can use any SDK functions. | ||
func RunCLI(fnc RunFunc, c *RunConfig) error { | ||
if c == nil { | ||
c = &RunConfig{} | ||
} | ||
return Run(newCliApp(fnc, *c)) | ||
} |
Oops, something went wrong.