aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/logview.go
diff options
context:
space:
mode:
Diffstat (limited to 'ui/logview.go')
-rw-r--r--ui/logview.go128
1 files changed, 128 insertions, 0 deletions
diff --git a/ui/logview.go b/ui/logview.go
new file mode 100644
index 00000000..e7a8a237
--- /dev/null
+++ b/ui/logview.go
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package ui
+
+import (
+ "errors"
+ "fmt"
+ "github.com/lxn/walk"
+ "github.com/lxn/win"
+ "golang.zx2c4.com/wireguard/windows/ringlogger"
+ "strings"
+ "syscall"
+ "time"
+ "unsafe"
+)
+
+type LogView struct {
+ walk.WidgetBase
+ logChan chan string
+}
+
+const (
+ TEM_APPENDTEXT = win.WM_USER + 6
+)
+
+func NewLogView(parent walk.Container, rl *ringlogger.Ringlogger) (*LogView, error) {
+ lc := make(chan string, 1024)
+ lv := &LogView{logChan: lc}
+
+ if err := walk.InitWidget(
+ lv,
+ parent,
+ "EDIT",
+ win.WS_TABSTOP|win.WS_VISIBLE|win.WS_VSCROLL|win.ES_MULTILINE|win.ES_WANTRETURN,
+ win.WS_EX_CLIENTEDGE); err != nil {
+ return nil, err
+ }
+ lv.setReadOnly(true)
+ lv.SendMessage(win.EM_SETLIMITTEXT, 4294967295, 0)
+
+ go func() {
+ var lines []ringlogger.FollowLine
+ cursor := ringlogger.CursorAll
+ for {
+ if lv.IsDisposed() {
+ return
+ }
+ lines, cursor = rl.FollowFromCursor(cursor)
+ sb := strings.Builder{}
+ for _, line := range lines {
+ sb.WriteString(fmt.Sprintf("%s: %s\r\n", line.Stamp.Format("2006-01-02 15:04:05.000000"), strings.ReplaceAll(line.Line, "\n", "\r\n")))
+ }
+ newLines := sb.String()
+ if len(newLines) > 0 {
+ lv.AppendText(newLines)
+ }
+ time.Sleep(300 * time.Millisecond)
+ }
+ }()
+ return lv, nil
+}
+
+func (*LogView) LayoutFlags() walk.LayoutFlags {
+ return walk.ShrinkableHorz | walk.ShrinkableVert | walk.GrowableHorz | walk.GrowableVert | walk.GreedyHorz | walk.GreedyVert
+}
+
+func (*LogView) MinSizeHint() walk.Size {
+ return walk.Size{20, 12}
+}
+
+func (*LogView) SizeHint() walk.Size {
+ return walk.Size{100, 100}
+}
+
+func (lv *LogView) setTextSelection(start, end int) {
+ lv.SendMessage(win.EM_SETSEL, uintptr(start), uintptr(end))
+}
+
+func (lv *LogView) textLength() int {
+ return int(lv.SendMessage(0x000E, uintptr(0), uintptr(0)))
+}
+
+func (lv *LogView) AppendText(value string) {
+ textLength := lv.textLength()
+ lv.setTextSelection(textLength, textLength)
+ lv.SendMessage(win.EM_REPLACESEL, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(value))))
+}
+
+func (lv *LogView) setReadOnly(readOnly bool) error {
+ if 0 == lv.SendMessage(win.EM_SETREADONLY, uintptr(win.BoolToBOOL(readOnly)), 0) {
+ return errors.New("fail to call EM_SETREADONLY")
+ }
+
+ return nil
+}
+
+func (lv *LogView) PostAppendText(value string) {
+ lv.logChan <- value
+ win.PostMessage(lv.Handle(), TEM_APPENDTEXT, 0, 0)
+}
+
+func (lv *LogView) Write(p []byte) (int, error) {
+ lv.PostAppendText(string(p) + "\r\n")
+ return len(p), nil
+}
+
+func (lv *LogView) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
+ switch msg {
+ case win.WM_GETDLGCODE:
+ if wParam == win.VK_RETURN {
+ return win.DLGC_WANTALLKEYS
+ }
+
+ return win.DLGC_HASSETSEL | win.DLGC_WANTARROWS | win.DLGC_WANTCHARS
+ case TEM_APPENDTEXT:
+ select {
+ case value := <-lv.logChan:
+ lv.AppendText(value)
+ default:
+ return 0
+ }
+ }
+
+ return lv.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
+}