aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go-src/csi/csi.go90
-rw-r--r--go-src/utils/utils.go14
-rw-r--r--go-src/windows.go92
-rw-r--r--go-src/windowsHelpers.go70
4 files changed, 173 insertions, 93 deletions
diff --git a/go-src/csi/csi.go b/go-src/csi/csi.go
new file mode 100644
index 0000000..100db59
--- /dev/null
+++ b/go-src/csi/csi.go
@@ -0,0 +1,90 @@
+package csi
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "runtime"
+)
+
+const (
+ ansiCUUf = "\033[%vA" // Cursor (move) Up, formatted version (expects to be given a number)
+ ansiCUDf = "\033[%vB" // Cursor (move) Down, formated version (expects to be given a number)
+ ansiCUFf = "\033[%vC" // Cursor (move) Forward (to the right), formatted version (expects to be given a number)
+
+ ansiCUU = "\033[A" // Cursor (move) Up
+ ansiCUD = "\033[B" // Cursor (move) Down
+ ansiCUF = "\033[C" // Cursor (move) Forward (to the right)
+ ansiCNL = "\033[E" // Cursor (move) Next Line
+ ansiED = "\033[2J" // Erase in Display (clears entire screen)
+ ansiEL = "\033[K" // Erase in Line (clears from cursor to end of line)
+
+ ansiSCP = "\033[s" // Save Current Cursor Position
+ ansiRCP = "\033[u" // Restore Saved Cursor Position
+)
+
+func MoveCursorUpN(times int) {
+ fmt.Printf(ansiCUUf, times)
+}
+
+func MoveCursorDownN(times int) {
+ fmt.Printf(ansiCUDf, times)
+}
+
+func MoveCursorRightN(times int) {
+ fmt.Printf(ansiCUFf, times)
+}
+
+func MoveCursorUp() {
+ fmt.Print(ansiCUU)
+}
+
+func MoveCursorDown() {
+ fmt.Print(ansiCUD)
+}
+
+func MoveCursorRight() {
+ fmt.Printf(ansiCUF)
+}
+
+func MoveCursorToBeginningOfLine() {
+ fmt.Print(ansiCNL)
+}
+
+func ClearLine() {
+ fmt.Print(ansiEL)
+}
+
+var blockClearing = false
+
+func ClearScreen() {
+ if blockClearing {
+ blockClearing = false
+ return
+ }
+
+ if runtime.GOOS == "windows" {
+ cmd := exec.Command("cmd", "/c", "cls")
+ cmd.Stdout = os.Stdout
+ cmd.Run()
+ } else {
+ fmt.Println(ansiED)
+ }
+}
+
+// The next time that ClearScreen is called, it won't clear the screen
+func BlockClearScreenNextTime() {
+ blockClearing = true
+}
+
+func EraseToEndOfLine() {
+ fmt.Print(ansiEL)
+}
+
+func SaveCursorPosition() {
+ fmt.Print(ansiSCP)
+}
+
+func RestoreCursorPosition() {
+ fmt.Print(ansiRCP)
+}
diff --git a/go-src/utils/utils.go b/go-src/utils/utils.go
index 8d0c1c7..5676744 100644
--- a/go-src/utils/utils.go
+++ b/go-src/utils/utils.go
@@ -2,8 +2,6 @@ package utils
import (
"os"
-
- ctfcmath "gitlab.com/Syndamia/ctfc/go-src/ctfcMath"
)
// Repeats a rune given amount of times and returns the result as a string
@@ -35,15 +33,3 @@ func AppendToFile(path string, value string) {
allChatsFile.WriteString(value)
allChatsFile.Close()
}
-
-/* Pagination */
-
-const PageSize = 15
-
-func TotalPages(messageAmount int) int {
- return ctfcmath.CeilDivInt(messageAmount, PageSize)
-}
-
-func Paginate(page int, messages ...string) []string {
- return messages[ctfcmath.MaxInt(len(messages)-PageSize*page, 0) : len(messages)-PageSize*(page-1)]
-}
diff --git a/go-src/windows.go b/go-src/windows.go
index 584058a..b3af04e 100644
--- a/go-src/windows.go
+++ b/go-src/windows.go
@@ -4,14 +4,14 @@ import (
"strconv"
"strings"
+ "gitlab.com/Syndamia/ctfc/go-src/csi"
"gitlab.com/Syndamia/ctfc/go-src/ui"
- "gitlab.com/Syndamia/ctfc/go-src/utils"
)
type window func(...string)
func StartupWindow(...string) {
- clearScreen()
+ csi.ClearScreen()
ui.NormalBox(true, "Login", "Register")
input := ui.InputField("[L/R]")
@@ -26,15 +26,19 @@ func StartupWindow(...string) {
}
func loginWindow(values ...string) {
- inputs := formWindow("Login", StartupWindow, []formInput{
- {"Username", " or [B for Back to start page]", nil},
- {"Password", "", nil},
- }, values...,
+ // Takes form user input
+ inputs := formWindow("Login", StartupWindow,
+ []formInput{
+ {"Username", " or [B for Back to start page]", nil},
+ {"Password", "", nil},
+ },
+ values...,
)
if len(inputs) == 0 {
return
}
+ // If login is successful, go to chats window, else show error
if logInUser(inputs[0], inputs[1]) {
defer chatsWindow()
} else {
@@ -43,11 +47,13 @@ func loginWindow(values ...string) {
}
func registerWindow(values ...string) {
- inputs := formWindow("Register", StartupWindow, []formInput{
- {"Username", " [A-z, 0-9, *, ., _, -; at least 1 letter] or [B for Back to start page]", stringValidUsername},
- {"Password", " [5-40 characters; at least 1 number]", stringValidPassword},
- {"Name", " [2-60 characters]", stringValidName},
- }, values...,
+ inputs := formWindow("Register", StartupWindow,
+ []formInput{
+ {"Username", " [A-z, 0-9, *, ., _, -; at least 1 letter] or [B for Back to start page]", stringValidUsername},
+ {"Password", " [5-40 characters; at least 1 number]", stringValidPassword},
+ {"Name", " [2-60 characters]", stringValidName},
+ },
+ values...,
)
if len(inputs) == 0 {
return
@@ -63,7 +69,7 @@ func registerWindow(values ...string) {
}
func chatsWindow(values ...string) {
- clearScreen()
+ csi.ClearScreen()
ui.NormalBox(true, "Direct Messages", "Account", "Logout")
allChats := getAllChats()
@@ -83,7 +89,7 @@ func chatsWindow(values ...string) {
inputAction{"H", chatsWindow, []string{"ShowHelp"}},
)
if !handled {
- // if is number
+ // If user input is number, navigate to chat of given number, else show error
if chatI, err := strconv.Atoi(input); chatI >= 0 && chatI <= len(allChats) && err == nil {
defer chatWindow(strings.Split(allChats[chatI-1], " : ")[0])
} else {
@@ -93,10 +99,12 @@ func chatsWindow(values ...string) {
}
func createChatWindow(values ...string) {
- inputs := formWindow("Creating a new chat", chatsWindow, []formInput{
- {"Name", " [2-20 characters; A-z, 0-9, spaces, _, -] or [B for Back to chats page]", stringValidChatName},
- {"Description", " [0-30 characters]", stringValidChatDesc},
- }, values...,
+ inputs := formWindow("Creating a new chat", chatsWindow,
+ []formInput{
+ {"Name", " [2-20 characters; A-z, 0-9, spaces, _, -] or [B for Back to chats page]", stringValidChatName},
+ {"Description", " [0-30 characters]", stringValidChatDesc},
+ },
+ values...,
)
if len(inputs) == 0 {
return
@@ -111,8 +119,10 @@ func createChatWindow(values ...string) {
}
func chatWindow(values ...string) {
- clearScreen()
+ csi.ClearScreen()
+ // We determine page number by length of a string
+ // This method should be faster than having to cast to int all the time
if len(values) == 1 {
values = append(values, ".")
}
@@ -122,7 +132,9 @@ func chatWindow(values ...string) {
ui.TextField(currChat.Name + " : " + currChat.Description)
ui.TextField("Brought to you by " + currChat.Owner.Name + " (" + currChat.Owner.Username + ")")
- for i := 0; i <= 16; i++ {
+ // One empty line to separate the "Brought to you by" line
+ // with the chat messages, and then empty line for each chat message
+ for i := 0; i <= pageSize+1; i++ {
ui.EmptyLine()
}
@@ -139,25 +151,28 @@ func chatWindow(values ...string) {
if !handled {
switch input {
case ">":
- if len(values[1]) < utils.TotalPages(len(currChat.Messages)) {
+ // If possible, increment to the next page (adds a dot to the end of the string)
+ if len(values[1]) < totalPages(len(currChat.Messages)) {
values[1] += "."
}
case "<":
+ // If possible, decrement to the previous page (removes a dot from the string)
if len(values[1]) > 1 {
values[1] = values[1][:len(values[1])-1]
}
- default:
- values[1] = "."
+ default: // Send a message
+ values[1] = "." // Make sure to be at the first page, when sending a message
+ // Since "C" is a command, to just enter the letter C you'll need to input "\C"
currChat.addMessage(strings.TrimPrefix(input, "\\"), loggedInUser.Username)
}
defer chatWindow(values...)
}
- lastLine = -1
+ lastLine = -1 // Practically stops execution of the paginated subwindow routine
}
func logoutWindow(...string) {
- clearScreen()
+ csi.ClearScreen()
ui.NormalBox(true, "Are you sure you want to Log out of "+loggedInUser.Username+"?")
ui.EmptyLine()
@@ -172,6 +187,15 @@ func logoutWindow(...string) {
}
}
+func showError(message string, callback window, callbackData ...string) {
+ csi.ClearScreen()
+
+ ui.ErrorBox(message)
+ csi.BlockClearScreenNextTime()
+
+ defer callback(callbackData...)
+}
+
/* Form */
type formInput struct {
@@ -182,23 +206,29 @@ type formInput struct {
func formWindow(boxTitle string, backWindow window, formInputs []formInput, currentValues ...string) (userInputs []string) {
userInputs = currentValues
+ // Go through all inputs
for i, v := range formInputs[len(currentValues):] {
- clearScreen()
+ csi.ClearScreen()
ui.NormalBox(true, boxTitle)
+ // Show filled input fields
for j, va := range userInputs {
ui.InputFieldFilled(formInputs[j].name, va)
}
+ // Wait for input on current input field
input := ui.InputField(v.name + v.specification)
+ // Go to the "backWindow", if we're on the first input, we are given a back window, and the letter B is given
if i == 0 && backWindow != nil && strings.ToLower(input) == "b" {
defer backWindow()
return
}
+ // If we're validating input data, try to validate
if v.validationFunc != nil {
+ // If input data isn't valid, show an error and the same screen
if !v.validationFunc(input) {
formCallback := func(...string) {
defer formWindow(boxTitle, backWindow, formInputs, userInputs...)
@@ -208,6 +238,7 @@ func formWindow(boxTitle string, backWindow window, formInputs []formInput, curr
}
}
+ // Add current input as a filled input
userInputs = append(userInputs, input)
}
return
@@ -238,14 +269,3 @@ func handleInputActions(input string, handleNav bool, ia ...inputAction) bool {
}
return false
}
-
-/* Errors and Clear */
-
-func showError(message string, callback window, callbackData ...string) {
- clearScreen()
-
- ui.ErrorBox(message)
- clearNext = false
-
- defer callback(callbackData...)
-}
diff --git a/go-src/windowsHelpers.go b/go-src/windowsHelpers.go
index 13cb3c1..909a673 100644
--- a/go-src/windowsHelpers.go
+++ b/go-src/windowsHelpers.go
@@ -1,70 +1,54 @@
package ctfc
import (
- "fmt"
- "os"
- "os/exec"
- "runtime"
"time"
+ "gitlab.com/Syndamia/ctfc/go-src/csi"
+ ctfcmath "gitlab.com/Syndamia/ctfc/go-src/ctfcMath"
"gitlab.com/Syndamia/ctfc/go-src/ui"
- "gitlab.com/Syndamia/ctfc/go-src/utils"
)
+/* Pagination */
+
+const pageSize = 15
+
+func totalPages(messageAmount int) int {
+ return ctfcmath.CeilDivInt(messageAmount, pageSize)
+}
+
+func paginate(page int, messages ...string) []string {
+ return messages[ctfcmath.MaxInt(len(messages)-pageSize*page, 0) : len(messages)-pageSize*(page-1)]
+}
+
+// Must be run in a routine ( go routinePaginatedSubwindow(...) )
+// There must be empty lines for the subwindow
func routinePaginatedSubwindow(messages *[]string, updateMessages func(*[]string), lastLine *int, page int) {
for *lastLine > -1 {
+ // Update messages, if we've already shown the last message
if *lastLine == len(*messages)-1 {
updateMessages(messages)
continue
}
*lastLine = len(*messages) - 1
- fmt.Print("\033[s")
+ csi.SaveCursorPosition()
- moveCursorUp(utils.PageSize + 2)
+ csi.MoveCursorUpN(pageSize + 2) // +2 because of the input and pagination lines
+ csi.MoveCursorToBeginningOfLine()
- pageMessages := utils.Paginate(page, *messages...)
- for i := 0; i < utils.PageSize-len(pageMessages); i++ {
- ui.EmptyLine()
+ pageMessages := paginate(page, *messages...)
+ // Leaves empty lines at the top, if there aren't enough messages to fill a full page
+ for i := 0; i < pageSize-len(pageMessages); i++ {
+ csi.MoveCursorDown()
}
for _, v := range pageMessages {
- fmt.Print("\033[K")
+ csi.ClearLine()
ui.TextField(v)
}
- ui.PageField(page, utils.TotalPages(len(*messages)))
+ ui.PageField(page, totalPages(len(*messages)))
- fmt.Print("\033[u")
+ csi.RestoreCursorPosition()
time.Sleep(500 * time.Millisecond)
}
}
-
-var clearNext = true
-
-func clearScreen() {
- if !clearNext {
- clearNext = true
- return
- }
-
- if runtime.GOOS == "windows" {
- cmd := exec.Command("cmd", "/c", "cls")
- cmd.Stdout = os.Stdout
- cmd.Run()
- } else {
- fmt.Println("\033[2J")
- }
-}
-
-func moveCursorUp(times int) {
- fmt.Printf("\033[%vA", times)
- fmt.Print("\033[E")
-}
-
-func moveCursorDown(times int) {
- fmt.Printf("\033[%vB", times)
-}
-
-func moveCursorRight(times int) {
- fmt.Printf("\033[%vC", times)
-}