Friday, June 26, 2020

Draw Boxes, etc, on Debian GNU/Linux Terminal Window With Go Lang & tcell

     A cleaner version of this document is here.
Draw Boxes, etc, on Debian GNU/Linux
Terminal Window With
Go Lang
&
tcell

Have a functioning Debian system.

Install go

# apt install go

Make sure go works:

Create a workspace directory and cd into it; something like:

$ mkdir ~/PROGRAMMING/GO/HELLOWORLD
$ cd ~/PROGRAMMING/GO/HELLOWORLD

Create a test “helloworld.go” file with the following:

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hi")
}

Compile it:

$ go build helloworld.go

Be aware that if you copy and paste from this document into a text editor, the quote marks might paste as “smartquotes”, causing your program to generate errors during compilation. In such a case, just manually delete and retype the quote marks.

Run it, to test:

$ ./helloworld

Do a basic app to draw a character on the “graphical” screen

Create a new workspace for our box-drawing program: and cd into it:

     $ mkdir ~/PROGRAMMING/GO/BOXES
    $ cd ~/PROGRAMMING/GO/BOXES

Create a new program file, “boxes.go”, and put the following code into it:

package main
/*
 This code draws a single character (or 'rune', or 'glyph') at a single
 location on a terminal window in Debian GNU/Linux.
*/

import (
     "fmt"
"os"
"time"
     "github.com/gdamore/tcell"
) // end of import

func main() {
// Create a "canvas" (a window, frame) on which to draw
// ‘s’ is the name of the screen/canvas on which we’ll draw
// ‘e’ is some error information the NewScreen() method insists on generating
// (NewScreen() returns both values, so we have to deal with both)
s, e := tcell.NewScreen()

// Deal with the ‘e’ value by checking for some error situations; exit if need be
// (Requires "os" package in "import" section at top.)
if e != nil {
        fmt.Fprintf(os.Stderr, "%v\n", e)
        os.Exit(1)
}
if e = s.Init(); e != nil {
         fmt.Fprintf(os.Stderr, "%v\n", e)
         os.Exit(1)
     }

// Set the 's' canvas’s default bg & fg colors from tcell’s list of colors
s.SetStyle(tcell.StyleDefault.
    Foreground(tcell.ColorWhite).
    Background(tcell.ColorBlack))

// Clear the "canvas"
s.Clear()

// Create a “glyph” using the “rune” (character) we want to draw
glyph := rune('W')

// Create and set the coordinates for where we'll draw our character ('glyph', 'rune')
x := 10
y := 20

// Create and set variables for foreground and background colors
fg := tcell.ColorYellow
bg := tcell.ColorBlue

// We need to send an entire “clump” of tcell info to the 
// SetCell() command below, so we’ll create a copy of the
// default tcell “clump” named ‘st’, and then modify the
// bg & fg colors to what we set above, for just this copy,
// which will be what we draw the cell with.
st := tcell.StyleDefault
st = st.Background(bg)
st = st.Foreground(fg)

// Place the cell at the desired location, with our copy of the tcell “clump”,
// and the glyph we want to draw.  All the cells of the ‘s’ screen/canvas
// except this one will get the default settings, such as the black background.
s.SetCell(x, y, st, glyph)

// Nothing has actually been drawn on screen yet; so unveil the "canvas"
s.Show()

// Pause X seconds before resetting "graphics" screen to normal
// time.Second is the unit (a second); time.Duration is how many units
// (Requires “time” package in the “import” section at top.)
secondsToSleep := time.Duration(3) * time.Second
time.Sleep(secondsToSleep)

//Reset screen to normal
s.Fini()
} // end of main()

Compile it:

$ go build boxes.go

If you get an error like ‘cannot find package "github.com/gdamore/tcell"’,

    $ go get github.com/gdamore/tcell

Run it, to test:

$ ./boxes

You should see the terminal window turn black, and then a yellow “W” printed on a blue background at 10 characters in from the left of the terminal window and 20 lines down from the top of the terminal window.

Feel free to tinker with the settings in this program and recompile/re-run until you feel comfortable with what the program is doing.

Move the Actual Drawing Part into a function

Modify your program so it looks like the one below. Note that the bulk of the program listing has been replaced here with an ellipses (“...”), but you won’t do that in your program listing. I’m only doing so here to make this document less cumbersome. Note also that functions do not have to be below the main() function; they can be above if that’s your preference.

package main
/*
 This code draws a single character (or 'rune', or 'glyph') at a single
 location on a terminal window in Debian GNU/Linux.
*/

import (
...
) // end of import

func main() {
...
} // end of main()

func drawChar(s tcell.Screen, x, y int, fg, bg tcell.Color, glyph rune) int {
    // We need to send an entire “clump” of tcell info to the
    // SetCell() command below, so we’ll create a copy of the
    // default tcell “clump” named ‘st’, and then modify the
    // bg & fg colors to what we set above, for just this copy,
    // which will be what we draw the cell with.
    st := tcell.StyleDefault
    st = st.Background(bg)
    st = st.Foreground(fg)

// Place the cell at the desired location, with our copy of the tcell “clump”,
// and the glyph we want to draw. All the cells of the 's' screen/canvas
// except this one will get the default settings, such as the black background.
s.SetCell(x, y, st, glyph)
return 0
} // end of drawChar()

You’ll notice that the four lines (one line of code, and three lines of comment) were copied from the main() func into the drawChar() func.
Now, of those four lines in main(), leave the three comment lines for now, and replace just the one line of:

s.SetCell(x, y, st, glyph)

with the single line of:

drawChar(s tcell.Screen,, x, y, fg, bg, glyph)

Note also that a new line of “return 0” was added to the bottom of the drawChar() function. I think it’s a good practice to provide a return value from functions. The last “int” of the top line of the function specifies that the return value will be an integer; since we’re assuming everything will work without error, we’re returning a return value of zero, which is usually interpreted to mean “no errors occurred while running this function”. We’re not actually doing anything with this return value, but we could do something like:
   
retVal := drawChar(s tcell.Screen, x, y, fg, bg, glyph)
    If retVatl != 0 (print some error information to the screen or to a log file, etc)

If you compile and run this new code, you should see the same result on the screen.

It may seem like you’ve added some unneeded complexity to your program (which I’ll explain about a bit in a bit), but you’ve just made it easier to draw new characters on the screen. For example, just before the existing lines that say:

// Nothing has actually been drawn on screen yet; so unveil the "canvas"
s.Show()

add these lines:

x = 30
y = 10
fg = tcell.ColorRed
bg = tcell.ColorGreen
glyph = rune(‘?’)
drawChar(s, x, y, fg, bg, glyph)

Now compile and run your program. In addition to the “W” you saw earlier, you should now see a red “Y” on a green background 30 columns in and 10 rows down.

Now for Some Explanation

The function drawChar() expects some information from the main program (aka, the main() function). These pieces of information are sent to the function by the line that calls the function:

drawChar(s, x, y, fg, bg, glyph)

And the function’s top line defines what it expects as incoming information to the function:

func drawChar(s tcell.Screen, x, y int, fg, bg tcell.Color, glyph rune) int {

The number and types of arguments (or parameters) sent to the function must match the number and types of arguments (parameters) expected by the function.

In this case, I used the same names for the outgoing information as for the incoming information, but it’s important to know that (in this case, at least) the incoming information is only a copy of the outgoing information; the variables have the same names, and a copy of the same values, but they are completely different variables in the function than in the main program. Just as Paul Simon the musician and Paul the Apostle from the Bible have the same name “Paul”, they are completely different people, and the “fg” variable in the main program and which is being sent to the function is a completely different variable than the “fg” variable used in the function, even though ti has the same name and an identical copy of the variable’s value.

If we wanted to do so, we could have used different names in the function, like this:

func drawChar(screenOnWhichToDraw tcell.Screen, x_coordinate, y_coordinate int, foreground_color, background_color tcell.Color, charToDisplay rune) int {

The types of the arguments specify what type of information is in the variable.

For example, the ‘s’ (or ‘screenOnWhichToDraw’) variable holds a “clump” of data that is predefined in the tcell package, named “Screen” (thus, it’s referenced as “tcell.Screen”).

The ‘x’ and ‘y’ variables are integers (referenced as “int”), which means they’re just normal “counting numbers”, not fractions or weird imaginary numbers or having decimal points, etc, just -2 or 6 or 248, etc.

The ‘fg’ and ‘bg’ variable are also a pre-defined type from the tcell package, named “Color” (referenced as “tcell.Color”).

And the “glyph” variable is a type named “rune”. A “rune” is a special case of 32-bit integer used with Unicode characters. Just think of them as any character you might find on a computer keyboard, even in far-off places that use weird symbols instead of English alphabetic characters. For example, the letter “W” is a rune; so is ‘w’, and so are ‘╔’ and ‘&’, although “under the hood” the computer really sees them as 32-bit integers. If you remember your ASCII studies from your high-school Computer Literacy Class, ASCII chars are runes.

So the main program sets up a “screen” with a default white lettering on a black background, and sends a copy of that to the drawChar() function as ‘s’ (or ‘screenOnWhichToDraw’). It sends a copy of the X and Y coordinates, and a copy of the foreground and background colors to use for drawing the one cell that will change in the function, and a copy of the rune that we’ll draw in that one cell.

When the function gets all these copies, it creates a new copy of the “clump” of information containing all the style defaults provided by the tcell package, named “st”, and then modifies two of those styles, the foreground and background colors, and then sends that “clump” of information, along with the x and y coordinates and the glyph rune, to the screen’s “SetCell” function, which sets one cell on the screen according to all that information.

Now, Let’s Draw a Box

Let’s create a brand-new function. Leave the old drawChar() function in place; even if we decided to remove the calls for that function from the main program, and never use that function again, it doesn’t hurt us to have it in our program listing file just sitting there doing nothing.

After the last line of the drawChar() function, create this code:

...
} // end of drawChar()


func drawBox(s tcell.Screen, leftEdge, topEdge, width, height int, fg, bg tcell.Color) int {
    // Calculate bottom-right corner of box
    rightEdge := leftEdge + width
    bottomEdge := topEdge + height
    glyph := ' '        // Create a rune variable

    glyph = ‘╔’
    drawChar(s, leftEdge, topEdge, fg, bg, glyph)

glyph = ‘╗’
    drawChar(s, rightEdge, topEdge, fg, bg, glyph)

glyph = ‘╚’
    drawChar(s, leftEdge, bottomEdge, fg, bg, glyph)

glyph = ‘╝’
    drawChar(s, rightEdge, bottomEdge, fg, bg, glyph)

    s.Show()

    return 0
}

Depending on your operating system, there are different ways of entering in these characters. On my Debian GNU/Linux box, I enter such a rune by pressing Ctrl-Shift-U, which will create an underlined ‘u’, then typing in the Unicode number for the character I want. I find the easiest way to find these codes is to web-search for “Unicode character set box-drawing” and using the first Wikipedia hit I get.

To create ‘╚’, I press Ctrl-Shift-U, then 255a, then ENTER.

Next, create the drawbox() calling line (and it’s comment, “Draw a box”, and a blank line above that, leaving the rest of the contextual lines around it alone):

    ...
glyph = rune('?')
drawChar(s, x, y, fg, bg, glyph)

// Draw a box
drawBox(s, x, y, 20, 4, tcell.ColorWhite, tcell.ColorBlack)

// Nothing has actually been drawn on screen yet; so unveil the "canvas"
s.Show()
    ...

When you compile and run this, you should have the four corners of a box.

Note that since we left the starting x & w the same as when we drew the ‘?’, the top-left rune of the box overwrote that ‘?’.

Also notice that I used “magic numbers” in this line; six months from now, when you go to look at this code, you won’t remember what the “20” and “4” represent, or what the colors color. It’s a bad habit to use “magic numbers”, so let’s replace those with more meaningful identifiers:

    ...
glyph = rune('?')
drawChar(s, x, y, fg, bg, glyph)
boxWidth := 20
boxHeight := 4
boxForeGroundColor := tcell.ColorWhite
boxBackGroundColor := tcell.ColorBlack
drawBox(s, x, y, boxWidth, boxHeight, boxForeGroundColor, boxBackGroundColor)

// Nothing has actually been drawn on screen yet; so unveil the "canvas"
s.Show()
   

Now we need to finish drawing the perimeter lines of the box.

If we try to draw the perimeter line after we’ve drawn the corners, we’ll have to make exceptions for the corners, and that can get messy. So instead, let’s draw the entire box, then re-draw the corners with the correct runes. To start, let’s draw the first and last lines with ‘═’ (Ctrl-Shift-U, 2550, Enter), from leftEdge to rightEdge, then the left and right edges, and then finally the four corners, like so:

func drawBox(s tcell.Screen, leftEdge, topEdge, width, height int, fg, bg tcell.Color) int {
    // Calculate bottom-right corner of box
    rightEdge := leftEdge + width
    bottomEdge := topEdge + height
    glyph := ' '        // Create a rune variable

    // Draw the top and bottom lines of box
    glyph = '═'
    for x := leftEdge; x <= rightEdge; x++ {
        drawChar(s, x, topEdge, fg, bg, glyph)
        drawChar(s, x, bottomEdge, fg, bg, glyph)
    }
    
    // Draw the sides of box
    glyph = '║'
    for y := topEdge + 1; y <= bottomEdge; y++ {
        drawChar(s, leftEdge, y, fg, bg, glyph)   
        drawChar(s, rightEdge, y, fg, bg, glyph)   
    }
    
    // Draw the four corners of box
    glyph = '╔'
    drawChar(s, leftEdge, topEdge, fg, bg, glyph)
    glyph = '╗'
    drawChar(s, rightEdge, topEdge, fg, bg, glyph)
    glyph = '╚'
    drawChar(s, leftEdge, bottomEdge, fg, bg, glyph)
    glyph = '╝'
    drawChar(s, rightEdge, bottomEdge, fg, bg, glyph)

    // Display the screen
    s.Show()
    
    return 0
}

Now you can draw more boxes just by adding more drawBox() lines in main(), like so:

...
// Draw a box
boxWidth := 20
boxHeight := 4
boxForeGroundColor := tcell.ColorWhite
boxBackGroundColor := tcell.ColorBlack
drawBox(s, x, y, boxWidth, boxHeight, boxForeGroundColor, boxBackGroundColor)

// Draw box #2
x = 40
y = 8
boxWidth = 10
boxHeight = 6
boxForeGroundColor = tcell.ColorYellow
boxBackGroundColor = tcell.ColorBlack
drawBox(s, x, y, boxWidth, boxHeight, boxForeGroundColor, boxBackGroundColor)

// Draw box #3
x = 3
y = 2
boxWidth = 5
boxHeight = 14
boxForeGroundColor = tcell.ColorRed
boxBackGroundColor = tcell.ColorYellow
drawBox(s, x, y, boxWidth, boxHeight, boxForeGroundColor, boxBackGroundColor)

We can even add a fill option. Add a boolean (true/false) variable, “fill”, to the drawBox() header line, like this:

func drawBox(s tcell.Screen, leftEdge, topEdge, width, height int, fg, bg tcell.Color, fill bool) int {

and change the function itself to this:

    ...
glyph := ' '        // Create a rune variable
    
    // If fill, fill in the entire box
    if fill {
    for y := topEdge; y <= bottomEdge; y++ {
                for x := leftEdge; x <= rightEdge; x++ {
                        drawChar(s, x, y, fg, bg, glyph)
                }
            }
    }

    // Draw the top and bottom lines of box
     ...

Then add a fill variable to the calling lines, like so:

fill := true    // or ‘false’
drawBox(s, x, y, boxWidth, boxHeight, boxForeGroundColor, boxBackGroundColor, fill)

(Remember the colon only on the first, declaration, of a new variable; otherwise leave the colon off.)

Monday, June 22, 2020

Born of Water and Spirit

There are lots of interpretations about what Jesus meant by "born of water and spirit" in John 3.

Y'all do know that Jesus was a Bible expert, and used it as the basis of most of his teaching, right?

So where does his Bible talk about being born anew in water and spirit?

Below is the one born of water only:

Ezek 16: 4 As for your birth, in the day you were born your navel was not cut. You weren’t washed in water to cleanse you. You weren’t salted at all, nor wrapped in blankets at all. 5 No eye pitied you, to do any of these things to you, to have compassion on you; but you were cast out in the open field, because you were abhorred in the day that you were born.
6 “‘“When I passed by you, and saw you wallowing in your blood, I said to you, ‘Though you are in your blood, live!’ Yes, I said to you, ‘Though you are in your blood, live!’ 7 I caused you to multiply as that which grows in the field, and you increased and grew great, and you attained to excellent ornament. Your breasts were formed, and your hair grew; yet you were naked and bare.
8 “‘“Now when I passed by you, and looked at you, behold, your time was the time of love; and I spread my skirt over you, and covered your nakedness. Yes, I swore to you, and entered into a covenant with you,” says the Lord Yahweh, “and you became mine.
9 “‘“Then I washed you with water. Yes, I thoroughly washed away your blood from you, and I anointed you with oil. 10 I clothed you also with embroidered work, and put sealskin sandals on you. I dressed you with fine linen and covered you with silk. 11 I decked you with ornaments, put bracelets on your hands, and put a chain on your neck. 12 I put a ring on your nose, and earrings in your ears, and a beautiful crown on your head. 13 Thus you were decked with gold and silver. Your clothing was of fine linen, silk, and embroidered work. You ate fine flour, honey, and oil. You were exceedingly beautiful, and you prospered to royal estate. 14 Your renown went out among the nations for your beauty; for it was perfect, through my majesty which I had put on you,” says the Lord Yahweh.
15 “‘“But you trusted in your beauty, and played the prostitute because of your renown, and poured out your prostitution on everyone who passed by.

Below is the one born of water and spirit

Ezek 36: 25 I will sprinkle clean water on you, and you will be clean. I will cleanse you from all your filthiness, and from all your idols. 26 I will also give you a new heart, and I will put a new spirit within you. I will take away the stony heart out of your flesh, and I will give you a heart of flesh. 27 I will put my Spirit within you, and cause you to walk in my statutes. You will keep my ordinances and do them. 28 You will dwell in the land that I gave to your fathers. You will be my people, and I will be your God. 29 I will save you from all your uncleanness.

Saturday, June 20, 2020

Using the 'tcell' graphics-in-a-terminal Library in a GoLang Program

package main
/*
 This code draws a single character (or 'rune', or 'glyph') at a single
 location on a terminal window in Debian GNU/Linux.
*/

import (
 "fmt"
 "os"
 "time"
 "github.com/gdamore/tcell"
)

func main() {
 // Create a "canvas" (a window, frame) on which to draw
 s, e := tcell.NewScreen()

 // Check for some error situations; exit if need be
 if e != nil {
  fmt.Fprintf(os.Stderr, "%v\n", e)
  os.Exit(1)
 }
 if e = s.Init(); e != nil {
  fmt.Fprintf(os.Stderr, "%v\n", e)
  os.Exit(1)
 }

 // Set foreground and background colors of "canvas"
 s.SetStyle(tcell.StyleDefault.
  Foreground(tcell.ColorWhite).
  Background(tcell.ColorBlack))

 // Clear the "canvas"
 s.Clear()

 // Set foreground and background colors of box
 fg := tcell.ColorYellow
 bg := tcell.ColorBlue

 // Set the coordinates for where we'll draw our character ('glyph', 'rune')
 x := 10
 y := 20

 st := tcell.StyleDefault
 st = st.Background(bg)
 st = st.Foreground(fg)

 glyph := rune('W')

 s.SetCell(x, y, st, glyph)

 // Display the prepared "canvas"
 s.Show()

 // Pause X seconds before resetting "graphics" screen to normal
 // Number of time units times length of time units
 duration := time.Duration(3) * time.Second
 time.Sleep(duration)

 //Reset screen to normal
 s.Fini()
}

Sunday, May 31, 2020

Some Things I Don't Like About the Rust Language

1. The print! macro requires a flush of the output device to show any text.

This is just stupid. The newbie programmer to Rust will use print! and expect to see output. If the high priests of Rust programming want to keep their purity of not flushing output automatically, then create a new print! macro that does flush the output; name it something like printout!. (This retains backward compatibility with previous programs using print!.)

2. Functions return the last expression implicitly. Again, that will confuse the newbie Rust programmer, as well as any non-Rustacean who is just looking over the code. It's wrong, because it requires esoteric knowledge of the language. A returned value should require an explicit return ; statement.

3. The --release flag during compilation handles integer overflow differently than a normal developmental compilation, producing completely different results.

What?! Again, to avoid unexpected errors, the developer must know esoteric knowledge about Rust. Bad, bad, bad.

4. Strings are way more complicated than they should be.

====

NOTE: These viewpoints are from a non-programmer, who has no right to pontificate on how a programming language should be. Nevertheless, these viewpoints are correct.


Sunday, May 17, 2020

Ye Are Saved by Grace, Not By Making a Passing Score on the Final Exam

There are those who, perhaps unintentionally, and perhaps unknowingly, and perhaps while denying it, advocate a works-based salvation.

They say things like, "The only people going to heaven are those who 'do the right things/in the right ways'."

Yes, Jesus expects (demands) obedience. But it's an obedience from the heart that saves, not a perfect-performance obedience.

WEB Rom 6:[17 ]But thanks be to God, that, whereas you were bondservants of sin, you became obedient from the heart to that form of teaching whereunto you were delivered.

God looks at one's heart, not at one's test-scores.

The fundamental problem with the Pharisees was not that they invented commands (this was a *symptom* of the problem), or that they taught those man-made commandments as doctrine (also a symptom), but that their heart was far from God.

God accepted Abel's sacrifice as a testimony that God accepted Abel (Heb 11:4), because Abel was righteous, that is, he had the right heart. Cain, judging by his actions, his attitude, his whining, and by how his children turned out, did not have a righteous heart.

The problem, and the solution, is the heart, not the external rule-keeping.

Strive to keep "the law", yes; that's what Jesus wants you to do. But don't rely on how well you keep the law. If you seek to be justified by your keeping of the law, you have fallen from grace; you are alienated from Christ (Gal 5:4). Trust in his righteousness, given to you as a gift; don't trust in your own righteousness, as did the Israelites, and who failed to attain the righteousness of God (Rom 10:1-4). Lean not on your own understanding; you won't get it right. There is none righteous, no, not one.

If you think you're saved by your perfect following of the Bible, you better get it 100% right (Rom 10:4-5); anything less means you've broken the entire law (Jam 2:10).