From 8ab758ac9a7d1f4844a6f060e5912827b4f1992e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 16 Feb 2021 21:18:56 +0100 Subject: [PATCH] login-app: Use a pure-Go tty interface instead of curses --- Makefile | 5 +- pkg/login-app/Dockerfile | 5 +- pkg/login-app/cmd/dialog-checklogin.go | 79 ++++++++ pkg/login-app/cmd/dialog-errmsg.go | 85 +++++++++ pkg/login-app/cmd/dialog-login.go | 124 ++++++++++++ pkg/login-app/cmd/dialog-reboot.go | 50 +++++ pkg/login-app/cmd/main.go | 69 +++---- pkg/login-app/cmd/windows.go | 249 ------------------------- 8 files changed, 372 insertions(+), 294 deletions(-) create mode 100644 pkg/login-app/cmd/dialog-checklogin.go create mode 100644 pkg/login-app/cmd/dialog-errmsg.go create mode 100644 pkg/login-app/cmd/dialog-login.go create mode 100644 pkg/login-app/cmd/dialog-reboot.go delete mode 100644 pkg/login-app/cmd/windows.go diff --git a/Makefile b/Makefile index f300294..b38df16 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ tuto1: token-validator/token-validator server.iso -login-app: pkg/login-app/build.yml pkg/login-app/Dockerfile pkg/login-app/cmd/login.go pkg/login-app/cmd/main.go pkg/login-app/cmd/windows.go +pkg/login-app: pkg/login-app/cmd/login.go pkg/login-app/cmd/dialog-checklogin.go pkg/login-app/cmd/cmd pkg/login-app/cmd/dialog-login.go pkg/login-app/cmd/login-app pkg/login-app/cmd/dialog-errmsg.go pkg/login-app/cmd/main.go pkg/login-app/cmd/dialog-reboot.go pkg/login-app/cmd/debug.log pkg/login-app/build.yml pkg/login-app/Dockerfile linuxkit pkg build -org nemunaire pkg/login-app/ #linuxkit pkg push -org nemunaire --sign=false pkg/login-app/ + touch pkg/login-app -login-initrd.img: login.yml login-app +login-initrd.img: login.yml pkg/login-app linuxkit build -docker $< token-validator/token-validator: token-validator/*.go diff --git a/pkg/login-app/Dockerfile b/pkg/login-app/Dockerfile index fef3694..71f3767 100644 --- a/pkg/login-app/Dockerfile +++ b/pkg/login-app/Dockerfile @@ -3,14 +3,14 @@ FROM golang:alpine as gobuild ENV GOOS linux ENV GOARCH amd64 -RUN apk add --no-cache git pkgconfig ncurses-dev ncurses-static gcc libc-dev +RUN apk add --no-cache git gcc WORKDIR /go/src/login-app ADD cmd ./ RUN go get -d -v -RUN go build -v -tags netgo -ldflags '-w -extldflags "-static -lpanelw -lncursesw"' +RUN go build -v -tags netgo FROM alpine @@ -19,7 +19,6 @@ MAINTAINER Pierre-Olivier Mercier EXPOSE 8081 COPY --from=gobuild /go/src/login-app/login-app /bin/login-app -COPY --from=gobuild /etc/terminfo/l/linux /etc/terminfo/l/linux COPY --from=gobuild /usr/share/udhcpc/default.script /usr/share/udhcpc/default.script ENTRYPOINT ["/bin/login-app"] diff --git a/pkg/login-app/cmd/dialog-checklogin.go b/pkg/login-app/cmd/dialog-checklogin.go new file mode 100644 index 0000000..82c79fb --- /dev/null +++ b/pkg/login-app/cmd/dialog-checklogin.go @@ -0,0 +1,79 @@ +package main + +import ( + "math" + "time" + + ui "github.com/VladimirMarkelov/clui" +) + +type CheckDialog struct { + View *ui.Window + + result int + beforeClose func(ui.Event) + onClose func() +} + +func CreateCheckDialog(title, username, password string) *CheckDialog { + dlg := new(CheckDialog) + + sWidth, sHeight := ui.ScreenSize() + wWidth, wHeight := 40, 5 + dlg.View = ui.AddWindow(sWidth/2-wWidth/2, sHeight/2-wHeight/2, wWidth, wHeight, title) + ui.WindowManager().BeginUpdate() + defer ui.WindowManager().EndUpdate() + + dlg.View.SetModal(true) + dlg.View.SetSizable(false) + dlg.View.SetTitleButtons(ui.ButtonDefault) + dlg.View.SetPack(ui.Vertical) + + textfrm := ui.CreateFrame(dlg.View, 1, 1, ui.BorderNone, ui.Fixed) + textfrm.SetPaddings(1, 1) + textfrm.SetPack(ui.Vertical) + textfrm.SetGaps(2, 1) + ui.CreateLabel(textfrm, ui.AutoSize, ui.AutoSize, " Please wait", ui.Fixed) + + progress := ui.CreateProgressBar(textfrm, ui.AutoSize, ui.AutoSize, 1) + progress.SetLimits(0, 100) + + ui.CreateLabel(textfrm, ui.AutoSize, ui.AutoSize, "Connecting to login server...", ui.Fixed) + + dlg.View.OnKeyDown(func(ev ui.Event, data interface{}) bool { + if ev.Key == 0 && ev.Msg == "login-complete" { + if dlg.beforeClose != nil { + dlg.beforeClose(ev) + } + + ui.WindowManager().DestroyWindow(dlg.View) + ui.WindowManager().BeginUpdate() + + closeFunc := dlg.onClose + ui.WindowManager().EndUpdate() + if closeFunc != nil { + closeFunc() + } + return true + } + return false + }, nil) + + go func() { + if ok, err := checkLogin(username, password); ok { + ui.PutEvent(ui.Event{Type: ui.EventKey, Msg: "login-complete"}) + } else { + ui.PutEvent(ui.Event{Type: ui.EventKey, Msg: "login-complete", Err: err}) + } + }() + + go func() { + for i := 0; i < 422; i += 1 { + progress.SetValue(int(math.Floor(math.Log(float64(i)*8)*16 - 30))) + time.Sleep(64 * time.Millisecond) + ui.PutEvent(ui.Event{Type: ui.EventRedraw}) + } + }() + + return dlg +} diff --git a/pkg/login-app/cmd/dialog-errmsg.go b/pkg/login-app/cmd/dialog-errmsg.go new file mode 100644 index 0000000..9ed0ac8 --- /dev/null +++ b/pkg/login-app/cmd/dialog-errmsg.go @@ -0,0 +1,85 @@ +package main + +import ( + ui "github.com/VladimirMarkelov/clui" + term "github.com/nsf/termbox-go" +) + +type ErrMsgDialog struct { + View *ui.Window + + result int + beforeClose func() + onClose func() +} + +func CreateErrMsgDialog(title string, err error) *ErrMsgDialog { + dlg := new(ErrMsgDialog) + + sWidth, sHeight := ui.ScreenSize() + wWidth, wHeight := 60, 10 + dlg.View = ui.AddWindow(sWidth/2-wWidth/2, sHeight/2-wHeight/2, wWidth, wHeight, title) + ui.WindowManager().BeginUpdate() + defer ui.WindowManager().EndUpdate() + + dlg.View.SetModal(true) + dlg.View.SetSizable(false) + dlg.View.SetTitleButtons(ui.ButtonDefault) + dlg.View.SetPack(ui.Vertical) + + textfrm := ui.CreateFrame(dlg.View, 1, 1, ui.BorderNone, ui.Fixed) + textfrm.SetPaddings(1, 1) + textfrm.SetPack(ui.Vertical) + textfrm.SetGaps(2, 1) + + lbl := ui.CreateLabel(textfrm, ui.AutoSize, ui.AutoSize, "An error occurs:", ui.Fixed) + lbl.SetTextColor(ui.ColorRedBold) + + tv := ui.CreateTextView(textfrm, ui.AutoSize, 4, ui.Fixed) + tv.SetWordWrap(true) + tv.SetText([]string{err.Error()}) + tv.SetTextColor(ui.ColorWhite) + tv.SetBackColor(ui.ColorBlack) + + ui.CreateLabel(textfrm, ui.AutoSize, ui.AutoSize, "Please try again.", ui.Fixed) + + btnOk := ui.CreateButton(textfrm, 20, 4, "Ok", 1) + + btnOk.OnClick(func(ev ui.Event) { + if dlg.beforeClose != nil { + dlg.beforeClose() + } + + ui.WindowManager().DestroyWindow(dlg.View) + ui.WindowManager().BeginUpdate() + + closeFunc := dlg.onClose + ui.WindowManager().EndUpdate() + if closeFunc != nil { + closeFunc() + } + }) + + dlg.View.OnKeyDown(func(ev ui.Event, data interface{}) bool { + if ev.Key == term.KeyEnter { + if dlg.beforeClose != nil { + dlg.beforeClose() + } + + ui.WindowManager().DestroyWindow(dlg.View) + ui.WindowManager().BeginUpdate() + + closeFunc := dlg.onClose + ui.WindowManager().EndUpdate() + if closeFunc != nil { + closeFunc() + } + return true + } + return false + }, nil) + + ui.ActivateControl(dlg.View, btnOk) + + return dlg +} diff --git a/pkg/login-app/cmd/dialog-login.go b/pkg/login-app/cmd/dialog-login.go new file mode 100644 index 0000000..a9b4c88 --- /dev/null +++ b/pkg/login-app/cmd/dialog-login.go @@ -0,0 +1,124 @@ +package main + +import ( + ui "github.com/VladimirMarkelov/clui" + term "github.com/nsf/termbox-go" +) + +const ( + LoginOk = iota + LoginCanceled + LoginInvalid +) + +type LoginDialog struct { + View *ui.Window + Username string + Password string + Action int + + result int + beforeClose func() + onClose func() + onCheck func(string, string) bool +} + +func CreateLoginDialog(title string) *LoginDialog { + dlg := new(LoginDialog) + + sWidth, sHeight := ui.ScreenSize() + wWidth, wHeight := 40, 15 + dlg.View = ui.AddWindow(sWidth/2-wWidth/2, sHeight/2-wHeight/2, wWidth, wHeight, title) + ui.WindowManager().BeginUpdate() + defer ui.WindowManager().EndUpdate() + + dlg.View.SetModal(true) + dlg.View.SetSizable(false) + dlg.View.SetTitleButtons(ui.ButtonDefault) + dlg.View.SetPack(ui.Vertical) + + userfrm := ui.CreateFrame(dlg.View, 1, 1, ui.BorderNone, ui.Fixed) + userfrm.SetPaddings(1, 1) + userfrm.SetPack(ui.Horizontal) + userfrm.SetGaps(1, 0) + ui.CreateLabel(userfrm, ui.AutoSize, ui.AutoSize, " Login", ui.Fixed) + edUser := ui.CreateEditField(userfrm, 20, "", 1) + + passfrm := ui.CreateFrame(dlg.View, 1, 1, ui.BorderNone, 1) + passfrm.SetPaddings(1, 1) + passfrm.SetPack(ui.Horizontal) + passfrm.SetGaps(1, 0) + ui.CreateLabel(passfrm, ui.AutoSize, ui.AutoSize, "Password", ui.Fixed) + edPass := ui.CreateEditField(passfrm, 20, "", 1) + edPass.SetPasswordMode(true) + + filler := ui.CreateFrame(dlg.View, 1, 1, ui.BorderNone, 1) + filler.SetPack(ui.Horizontal) + lbRes := ui.CreateLabel(filler, ui.AutoSize, ui.AutoSize, "", 1) + + blist := ui.CreateFrame(dlg.View, 1, 1, ui.BorderNone, ui.Fixed) + blist.SetPack(ui.Horizontal) + blist.SetPaddings(1, 1) + btnOk := ui.CreateButton(blist, 20, 4, "Authenticate me", 1) + + btnOk.OnClick(func(ev ui.Event) { + if dlg.onCheck != nil && !dlg.onCheck(edUser.Title(), edPass.Title()) { + lbRes.SetTitle("Invalid username or password") + dlg.Action = LoginInvalid + return + } + + dlg.Action = LoginOk + if dlg.onCheck == nil { + dlg.Username = edUser.Title() + dlg.Password = edPass.Title() + } + + if dlg.beforeClose != nil { + dlg.beforeClose() + } + ui.WindowManager().DestroyWindow(dlg.View) + ui.WindowManager().BeginUpdate() + + closeFunc := dlg.onClose + ui.WindowManager().EndUpdate() + if closeFunc != nil { + closeFunc() + } + }) + + dlg.View.OnKeyDown(func(ev ui.Event, data interface{}) bool { + if ev.Key == term.KeyEnter { + if edUser.Title() == "" { + ui.ActivateControl(dlg.View, edUser) + } else if edPass.Title() == "" { + ui.ActivateControl(dlg.View, edPass) + } else { + if dlg.beforeClose != nil { + dlg.beforeClose() + } + ui.WindowManager().DestroyWindow(dlg.View) + ui.WindowManager().BeginUpdate() + + closeFunc := dlg.onClose + ui.WindowManager().EndUpdate() + if closeFunc != nil { + closeFunc() + } + } + return true + } + return false + }, nil) + + edUser.OnChange(func(ev ui.Event) { + lbRes.SetTitle("") + }) + edPass.OnChange(func(ev ui.Event) { + lbRes.SetTitle("") + }) + + ui.ActivateControl(dlg.View, edUser) + + return dlg +} diff --git a/pkg/login-app/cmd/dialog-reboot.go b/pkg/login-app/cmd/dialog-reboot.go new file mode 100644 index 0000000..97e3974 --- /dev/null +++ b/pkg/login-app/cmd/dialog-reboot.go @@ -0,0 +1,50 @@ +package main + +import ( + "time" + + ui "github.com/VladimirMarkelov/clui" +) + +type RebootDialog struct { + View *ui.Window +} + +func CreateRebootDialog(title, login string) *RebootDialog { + dlg := new(RebootDialog) + + sWidth, sHeight := ui.ScreenSize() + wWidth, wHeight := 50, 10 + dlg.View = ui.AddWindow(sWidth/2-wWidth/2, sHeight/2-wHeight/2, wWidth, wHeight, title) + ui.WindowManager().BeginUpdate() + defer ui.WindowManager().EndUpdate() + + dlg.View.SetModal(true) + dlg.View.SetSizable(false) + dlg.View.SetTitleButtons(ui.ButtonDefault) + dlg.View.SetPack(ui.Vertical) + + textfrm := ui.CreateFrame(dlg.View, 1, 1, ui.BorderNone, ui.Fixed) + textfrm.SetPaddings(1, 1) + textfrm.SetPack(ui.Vertical) + textfrm.SetGaps(2, 1) + ui.CreateLabel(textfrm, ui.AutoSize, ui.AutoSize, "You are now successfully logged in as:", ui.Fixed) + ui.CreateLabel(textfrm, ui.AutoSize, ui.AutoSize, login, ui.Fixed) + + ui.CreateLabel(textfrm, ui.AutoSize, ui.AutoSize, "Your computer will automatically", ui.Fixed) + ui.CreateLabel(textfrm, ui.AutoSize, ui.AutoSize, "reboot in a few seconds...", ui.Fixed) + + progress := ui.CreateProgressBar(textfrm, ui.AutoSize, ui.AutoSize, 1) + progress.SetLimits(0, 100) + + go func() { + for i := 0; i < 100; i += 1 { + progress.SetValue(i) + ui.PutEvent(ui.Event{Type: ui.EventRedraw}) + time.Sleep(64 * time.Millisecond) + } + ui.WindowManager().DestroyWindow(dlg.View) + }() + + return dlg +} diff --git a/pkg/login-app/cmd/main.go b/pkg/login-app/cmd/main.go index fb679e5..50560f3 100644 --- a/pkg/login-app/cmd/main.go +++ b/pkg/login-app/cmd/main.go @@ -1,58 +1,47 @@ package main import ( - gc "github.com/rthornton128/goncurses" + "os" + + ui "github.com/VladimirMarkelov/clui" ) const URLLogin = "https://auth.adlin.nemunai.re/login" -func goLogin(stdscr *gc.Window, in chan gc.Key) (string, string, bool) { - username, password := login(stdscr, in) +var logged = false - validator := make(chan bool) - validator_err := make(chan error) - go func(username, password string, progress chan bool, err chan error) { - st, errm := checkLogin(username, password) - progress <- st - err <- errm - }(username, password, validator, validator_err) +func askLogin() (lgd *LoginDialog) { + lgd = CreateLoginDialog(" SRS AdLin - Login ") - if connection(stdscr, in, validator, validator_err) { - e := <-validator_err - return username, e.Error(), true - } else { - return goLogin(stdscr, in) + lgd.beforeClose = func() { + // Display next dialoag + ckd := CreateCheckDialog(" SRS AdLin - Login ", lgd.Username, lgd.Password) + + ckd.beforeClose = func(ev ui.Event) { + if ev.Err == nil { + logged = true + CreateRebootDialog(" SRS AdLin - Login", lgd.Username) + } else { + errd := CreateErrMsgDialog(" SRS AdLin - Login ", ev.Err) + errd.beforeClose = func() { + askLogin() + } + } + } } + + return } func main() { - stdscr, _ := gc.Init() - defer gc.End() + ui.InitLibrary() + defer ui.DeinitLibrary() - // Set main properties - gc.Cursor(0) - gc.StartColor() - gc.Raw(true) - gc.Echo(false) - stdscr.Keypad(true) + askLogin() - // Define colors - gc.InitPair(1, gc.C_WHITE, gc.C_BLUE) - gc.InitPair(2, gc.C_GREEN, gc.C_BLACK) - gc.InitPair(3, gc.C_CYAN, gc.C_BLACK) - gc.InitPair(4, gc.C_RED, gc.C_BLACK) - gc.InitPair(5, gc.C_BLACK, gc.C_WHITE) + ui.MainLoop() - // Register pressed key through channel - in := make(chan gc.Key) - go func(w *gc.Window, ch chan<- gc.Key) { - for { - ch <- w.GetChar() - } - }(stdscr, in) - - // Run! - if username, ip, ok := goLogin(stdscr, in); ok { - okreboot(stdscr, username, ip) + if !logged { + os.Exit(1) } } diff --git a/pkg/login-app/cmd/windows.go b/pkg/login-app/cmd/windows.go deleted file mode 100644 index af00fca..0000000 --- a/pkg/login-app/cmd/windows.go +++ /dev/null @@ -1,249 +0,0 @@ -package main - -import ( - "strings" - "time" - - gc "github.com/rthornton128/goncurses" -) - -func login(stdscr *gc.Window, in chan gc.Key) (username, password string) { - gc.Cursor(1) - stdscr.Clear() - - // Initialize the fields and Set field options - fields := make([]*gc.Field, 3) - fields[0], _ = gc.NewField(1, 28, 2, 12, 0, 0) - defer fields[0].Free() - fields[0].SetBackground(gc.ColorPair(5) | gc.A_UNDERLINE) - fields[0].SetOptionsOff(gc.FO_AUTOSKIP) - - fields[1], _ = gc.NewField(1, 28, 4, 12, 0, 0) - defer fields[1].Free() - fields[1].SetBackground(gc.ColorPair(5) | gc.A_UNDERLINE) - fields[1].SetOptionsOff(gc.FO_PUBLIC) - fields[1].SetOptionsOff(gc.FO_AUTOSKIP) - - fields[2], _ = gc.NewField(1, 11, 7, 18, 0, 0) - defer fields[2].Free() - fields[2].SetBuffer("< Connect >") - fields[2].SetBackground(gc.ColorPair(1)) - fields[2].SetOptionsOff(gc.FO_EDIT) - fields[2].SetOptionsOff(gc.FO_AUTOSKIP) - - // Create the form and post it - form, _ := gc.NewForm(fields) - defer form.Free() - - // Create the window to be associated with the form - rows, cols := stdscr.MaxYX() - height, width := 14, 44 - y, x := (rows-height)/2, (cols-width)/2 - - mainwin, _ := gc.NewWindow(height, width, y, x) - defer mainwin.Delete() - defer mainwin.Erase() - mainwin.Keypad(true) - - // Set main window and sub window - form.SetWindow(mainwin) - form.SetSub(mainwin.Derived(8, 42, 3, 1)) - - // Print a border around the main window and print a title - mainwin.Box(0, 0) - - y, x = mainwin.MaxYX() - title := "SRS ADLIN - Login" - mainwin.ColorOn(2) - mainwin.MovePrint(1, (x/2)-(len(title)/2), title) - mainwin.ColorOff(1) - mainwin.MoveAddChar(2, 0, gc.ACS_LTEE) - mainwin.HLine(2, 1, gc.ACS_HLINE, x-2) - mainwin.MoveAddChar(2, x-1, gc.ACS_RTEE) - stdscr.Refresh() - - form.Post() - defer form.UnPost() - - mainwin.MovePrint(5, 3, "Login:") - mainwin.MovePrint(7, 3, "Password:") - - form.Driver(gc.REQ_FIRST_FIELD) - - stdscr.Refresh() - mainwin.Refresh() - -login: - for { - select { - case ch := <-in: - switch ch { - case gc.KEY_DOWN, gc.KEY_TAB: - form.Driver(gc.REQ_NEXT_FIELD) - form.Driver(gc.REQ_END_LINE) - case gc.KEY_UP: - form.Driver(gc.REQ_PREV_FIELD) - form.Driver(gc.REQ_END_LINE) - case gc.KEY_BACKSPACE: - form.Driver(gc.REQ_CLR_FIELD) - case gc.KEY_RETURN: - form.Driver(gc.REQ_NEXT_FIELD) - form.Driver(gc.REQ_END_LINE) - if len(strings.TrimSpace(fields[0].Buffer())) > 0 && len(strings.TrimSpace(fields[1].Buffer())) > 0 { - break login - } - default: - form.Driver(ch) - } - } - mainwin.Refresh() - } - - username = strings.TrimSpace(fields[0].Buffer()) - password = strings.TrimSpace(fields[1].Buffer()) - - return -} - -func connection(stdscr *gc.Window, in chan gc.Key, validator chan bool, validator_err chan error) (canContinue bool) { - gc.Cursor(0) - stdscr.Clear() - - // Create the window to be associated with the form - rows, cols := stdscr.MaxYX() - height, width := 10, 60 - y, x := (rows-height)/2, (cols-width)/2 - - mainwin, _ := gc.NewWindow(height, width, y, x) - defer mainwin.Delete() - mainwin.Keypad(true) - - // Print a border around the main window and print a title - mainwin.Box(0, 0) - - y, x = mainwin.MaxYX() - title := "SRS ADLIN - Login" - mainwin.ColorOn(2) - mainwin.MovePrint(1, (x/2)-(len(title)/2), title) - mainwin.ColorOff(2) - mainwin.MoveAddChar(2, 0, gc.ACS_LTEE) - mainwin.HLine(2, 1, gc.ACS_HLINE, x-2) - mainwin.MoveAddChar(2, x-1, gc.ACS_RTEE) - stdscr.Refresh() - - mainwin.MovePrint(4, 4, " Please wait...") - mainwin.ColorOn(3) - mainwin.MovePrint(6, 2, "Connecting to login server...") - mainwin.ColorOff(3) - - stdscr.Refresh() - mainwin.Refresh() - - ticker := time.NewTicker(time.Millisecond * 150) - - rotPos := 0 -loginloop: - for { - select { - case ch := <-in: - if gc.Char(ch) == gc.Char('r') { - break loginloop - } - case st := <-validator: - if st { - canContinue = true - break loginloop - } else { - e := <-validator_err - - mainwin.ColorOn(4) - mainwin.MovePrint(4, 2, e.Error()) - mainwin.ColorOff(4) - ticker.Stop() - - mainwin.MovePrint(7, 2, " Press any key to retry ") - mainwin.Refresh() - <-in - - break loginloop - } - case <-ticker.C: - switch rotPos += 1; rotPos { - case 0, 4: - mainwin.MovePrint(4, 4, "|") - case 1, 5: - mainwin.MovePrint(4, 4, "/") - case 2, 6: - mainwin.MovePrint(4, 4, "-") - case 3, 7: - mainwin.MovePrint(4, 4, "\\") - default: - mainwin.MovePrint(4, 4, "|") - rotPos = 0 - } - } - mainwin.Refresh() - } - ticker.Stop() - - return -} - -func okreboot(stdscr *gc.Window, login string, ip string) { - gc.Cursor(0) - stdscr.Clear() - - // Create the window to be associated with the form - rows, cols := stdscr.MaxYX() - height, width := 14, 42 - y, x := (rows-height)/2, (cols-width)/2 - - mainwin, _ := gc.NewWindow(height, width, y, x) - defer mainwin.Delete() - mainwin.Keypad(true) - - // Print a border around the main window and print a title - mainwin.Box(0, 0) - - y, x = mainwin.MaxYX() - title := "SRS ADLIN" - mainwin.ColorOn(2) - mainwin.MovePrint(1, (x/2)-(len(title)/2), title) - mainwin.ColorOff(2) - mainwin.MoveAddChar(2, 0, gc.ACS_LTEE) - mainwin.HLine(2, 1, gc.ACS_HLINE, x-2) - mainwin.MoveAddChar(2, x-1, gc.ACS_RTEE) - stdscr.Refresh() - - mainwin.ColorOn(2) - mainwin.MovePrint(4, 2, "You are now successfully logged in as: ") - mainwin.ColorOff(2) - mainwin.ColorOn(3) - mainwin.MovePrint(5, (x/2)-(len(login)/2), login) - mainwin.ColorOff(3) - - mainwin.MovePrint(8, 2, "Your computer will automatically") - mainwin.MovePrint(9, 2, "reboot in a few seconds...") - - mainwin.MovePrint(11, 2, "|------------------------------------|") - - stdscr.Refresh() - mainwin.Refresh() - - ticker := time.NewTicker(time.Millisecond * 98) - - pos := 0 -rebootloop: - for { - select { - case <-ticker.C: - mainwin.MovePrint(11, 3+pos, "*") - pos += 1 - if pos > 36 { - break rebootloop - } - } - mainwin.Refresh() - } - ticker.Stop() -}