container screen [
num-rows:num
num-columns:num
cursor-row:num
cursor-column:num
data:&:@:screen-cell
]
container screen-cell [
contents:char
color:num
]
def new-fake-screen w:num, h:num -> result:&:screen [
local-scope
load-ingredients
result <- new screen:type
bufsize:num <- multiply w, h
data:&:@:screen-cell <- new screen-cell:type, bufsize
*result <- merge h/num-rows, w/num-columns, 0/cursor-row, 0/cursor-column, data
result <- clear-screen result
]
def clear-screen screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
{
break-if screen
clear-display
return
}
buf:&:@:screen-cell <- get *screen, data:offset
max:num <- length *buf
i:num <- copy 0
{
done?:bool <- greater-or-equal i, max
break-if done?
curr:screen-cell <- merge 0/empty, 7/white
*buf <- put-index *buf, i, curr
i <- add i, 1
loop
}
*screen <- put *screen, cursor-row:offset, 0
*screen <- put *screen, cursor-column:offset, 0
]
def sync-screen screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
return-if screen
sync-display
]
def fake-screen-is-empty? screen:&:screen -> result:bool [
local-scope
load-ingredients
return-unless screen, 1/true
buf:&:@:screen-cell <- get *screen, data:offset
i:num <- copy 0
len:num <- length *buf
{
done?:bool <- greater-or-equal i, len
break-if done?
curr:screen-cell <- index *buf, i
curr-contents:char <- get curr, contents:offset
i <- add i, 1
loop-unless curr-contents
return 0/false
}
return 1/true
]
def print screen:&:screen, c:char -> screen:&:screen [
local-scope
load-ingredients
color:num, color-found?:bool <- next-ingredient
{
break-if color-found?
color <- copy 7/white
}
bg-color:num, bg-color-found?:bool <- next-ingredient
{
break-if bg-color-found?
bg-color <- copy 0/black
}
c2:num <- character-to-code c
trace 90, [print-character], c2
{
break-if screen
print-character-to-display c, color, bg-color
return
}
width:num <- get *screen, num-columns:offset
height:num <- get *screen, num-rows:offset
row:num <- get *screen, cursor-row:offset
row <- round row
legal?:bool <- greater-or-equal row, 0
return-unless legal?
legal? <- lesser-than row, height
return-unless legal?
column:num <- get *screen, cursor-column:offset
column <- round column
legal? <- greater-or-equal column, 0
return-unless legal?
legal? <- lesser-than column, width
return-unless legal?
{
newline?:bool <- equal c, 10/newline
break-unless newline?
{
bottom:num <- subtract height, 1
at-bottom?:bool <- greater-or-equal row, bottom
break-if at-bottom?
column <- copy 0
*screen <- put *screen, cursor-column:offset, column
row <- add row, 1
*screen <- put *screen, cursor-row:offset, row
}
return
}
index:num <- multiply row, width
index <- add index, column
buf:&:@:screen-cell <- get *screen, data:offset
len:num <- length *buf
{
backspace?:bool <- equal c, 8
break-unless backspace?
{
at-left?:bool <- lesser-or-equal column, 0
break-if at-left?
column <- subtract column, 1
*screen <- put *screen, cursor-column:offset, column
index <- subtract index, 1
cursor:screen-cell <- merge 32/space, 7/white
*buf <- put-index *buf, index, cursor
}
return
}
cursor:screen-cell <- merge c, color
*buf <- put-index *buf, index, cursor
{
right:num <- subtract width, 1
at-right?:bool <- greater-or-equal column, right
break-if at-right?
column <- add column, 1
*screen <- put *screen, cursor-column:offset, column
}
]
scenario print-character-at-top-left [
local-scope
fake-screen:&:screen <- new-fake-screen 3/width, 2/height
run [
a:char <- copy 97/a
fake-screen <- print fake-screen, a:char
cell:&:@:screen-cell <- get *fake-screen, data:offset
1:@:screen-cell/raw <- copy *cell
]
memory-should-contain [
1 <- 6
2 <- 97
3 <- 7
4 <- 0
]
]
scenario print-character-at-fractional-coordinate [
local-scope
fake-screen:&:screen <- new-fake-screen 3/width, 2/height
a:char <- copy 97/a
run [
move-cursor fake-screen, 0.5, 0
fake-screen <- print fake-screen, a:char
cell:&:@:screen-cell <- get *fake-screen, data:offset
1:@:screen-cell/raw <- copy *cell
]
memory-should-contain [
1 <- 6
2 <- 97
3 <- 7
4 <- 0
]
]
scenario print-character-in-color [
local-scope
fake-screen:&:screen <- new-fake-screen 3/width, 2/height
run [
a:char <- copy 97/a
fake-screen <- print fake-screen, a:char, 1/red
cell:&:@:screen-cell <- get *fake-screen, data:offset
1:@:screen-cell/raw <- copy *cell
]
memory-should-contain [
1 <- 6
2 <- 97
3 <- 1
4 <- 0
]
]
scenario print-backspace-character [
local-scope
fake-screen:&:screen <- new-fake-screen 3/width, 2/height
a:char <- copy 97/a
fake-screen <- print fake-screen, a
run [
backspace:char <- copy 8/backspace
fake-screen <- print fake-screen, backspace
10:num/raw <- get *fake-screen, cursor-column:offset
cell:&:@:screen-cell <- get *fake-screen, data:offset
11:@:screen-cell/raw <- copy *cell
]
memory-should-contain [
10 <- 0
11 <- 6
12 <- 32
13 <- 7
14 <- 0
]
]
scenario print-extra-backspace-character [
local-scope
fake-screen:&:screen <- new-fake-screen 3/width, 2/height
a:char <- copy 97/a
fake-screen <- print fake-screen, a
run [
backspace:char <- copy 8/backspace
fake-screen <- print fake-screen, backspace
fake-screen <- print fake-screen, backspace
1:num/raw <- get *fake-screen, cursor-column:offset
cell:&:@:screen-cell <- get *fake-screen, data:offset
3:@:screen-cell/raw <- copy *cell
]
memory-should-contain [
1 <- 0
3 <- 6
4 <- 32
5 <- 7
6 <- 0
]
]
scenario print-character-at-right-margin [
local-scope
fake-screen:&:screen <- new-fake-screen 2/width, 2/height
a:char <- copy 97/a
fake-screen <- print fake-screen, a
b:char <- copy 98/b
fake-screen <- print fake-screen, b
run [
c:char <- copy 99/c
fake-screen <- print fake-screen, c
10:num/raw <- get *fake-screen, cursor-column:offset
cell:&:@:screen-cell <- get *fake-screen, data:offset
11:@:screen-cell/raw <- copy *cell
]
memory-should-contain [
10 <- 1
11 <- 4
12 <- 97
13 <- 7
14 <- 99
15 <- 7
16 <- 0
]
]
scenario print-newline-character [
local-scope
fake-screen:&:screen <- new-fake-screen 3/width, 2/height
a:char <- copy 97/a
fake-screen <- print fake-screen, a
run [
newline:char <- copy 10/newline
fake-screen <- print fake-screen, newline
10:num/raw <- get *fake-screen, cursor-row:offset
11:num/raw <- get *fake-screen, cursor-column:offset
cell:&:@:screen-cell <- get *fake-screen, data:offset
12:@:screen-cell/raw <- copy *cell
]
memory-should-contain [
10 <- 1
11 <- 0
12 <- 6
13 <- 97
14 <- 7
15 <- 0
]
]
scenario print-newline-at-bottom-line [
local-scope
fake-screen:&:screen <- new-fake-screen 3/width, 2/height
newline:char <- copy 10/newline
fake-screen <- print fake-screen, newline
fake-screen <- print fake-screen, newline
run [
fake-screen <- print fake-screen, newline
10:num/raw <- get *fake-screen, cursor-row:offset
11:num/raw <- get *fake-screen, cursor-column:offset
]
memory-should-contain [
10 <- 1
11 <- 0
]
]
scenario print-character-at-bottom-right [
local-scope
fake-screen:&:screen <- new-fake-screen 2/width, 2/height
newline:char <- copy 10/newline
fake-screen <- print fake-screen, newline
a:char <- copy 97/a
fake-screen <- print fake-screen, a
b:char <- copy 98/b
fake-screen <- print fake-screen, b
c:char <- copy 99/c
fake-screen <- print fake-screen, c
fake-screen <- print fake-screen, newline
run [
d:char <- copy 100/d
fake-screen <- print fake-screen, d
10:num/raw <- get *fake-screen, cursor-row:offset
11:num/raw <- get *fake-screen, cursor-column:offset
cell:&:@:screen-cell <- get *fake-screen, data:offset
20:@:screen-cell/raw <- copy *cell
]
memory-should-contain [
10 <- 1
11 <- 1
20 <- 4
21 <- 0
22 <- 7
23 <- 0
24 <- 7
25 <- 97
26 <- 7
27 <- 100
28 <- 7
29 <- 0
]
]
def clear-line screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
space:char <- copy 0/nul
{
break-if screen
clear-line-on-display
return
}
width:num <- get *screen, num-columns:offset
column:num <- get *screen, cursor-column:offset
original-column:num <- copy column
{
right:num <- subtract width, 1
done?:bool <- greater-or-equal column, right
break-if done?
print screen, space
column <- add column, 1
loop
}
*screen <- put *screen, cursor-column:offset, original-column
]
def clear-line-until screen:&:screen, right:num/inclusive -> screen:&:screen [
local-scope
load-ingredients
_, column:num <- cursor-position screen
space:char <- copy 32/space
bg-color:num, bg-color-found?:bool <- next-ingredient
{
break-if bg-color-found?
bg-color <- copy 0/black
}
{
done?:bool <- greater-than column, right
break-if done?
screen <- print screen, space, 7/white, bg-color
column <- add column, 1
loop
}
]
def cursor-position screen:&:screen -> row:num, column:num [
local-scope
load-ingredients
{
break-if screen
row, column <- cursor-position-on-display
return
}
row:num <- get *screen, cursor-row:offset
column:num <- get *screen, cursor-column:offset
]
def move-cursor screen:&:screen, new-row:num, new-column:num -> screen:&:screen [
local-scope
load-ingredients
{
break-if screen
move-cursor-on-display new-row, new-column
return
}
*screen <- put *screen, cursor-row:offset, new-row
*screen <- put *screen, cursor-column:offset, new-column
]
scenario clear-line-erases-printed-characters [
local-scope
fake-screen:&:screen <- new-fake-screen 3/width, 2/height
a:char <- copy 97/a
fake-screen <- print fake-screen, a
fake-screen <- move-cursor fake-screen, 0/row, 0/column
run [
fake-screen <- clear-line fake-screen
cell:&:@:screen-cell <- get *fake-screen, data:offset
10:@:screen-cell/raw <- copy *cell
]
memory-should-contain [
10 <- 6
11 <- 0
12 <- 7
13 <- 0
14 <- 7
15 <- 0
16 <- 7
17 <- 0
18 <- 7
19 <- 0
20 <- 7
21 <- 0
22 <- 7
]
]
def cursor-down screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
{
break-if screen
move-cursor-down-on-display
return
}
height:num <- get *screen, num-rows:offset
row:num <- get *screen, cursor-row:offset
max:num <- subtract height, 1
at-bottom?:bool <- greater-or-equal row, max
return-if at-bottom?
row <- add row, 1
*screen <- put *screen, cursor-row:offset, row
]
def cursor-up screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
{
break-if screen
move-cursor-up-on-display
return
}
row:num <- get *screen, cursor-row:offset
at-top?:bool <- lesser-or-equal row, 0
return-if at-top?
row <- subtract row, 1
*screen <- put *screen, cursor-row:offset, row
]
def cursor-right screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
{
break-if screen
move-cursor-right-on-display
return
}
width:num <- get *screen, num-columns:offset
column:num <- get *screen, cursor-column:offset
max:num <- subtract width, 1
at-bottom?:bool <- greater-or-equal column, max
return-if at-bottom?
column <- add column, 1
*screen <- put *screen, cursor-column:offset, column
]
def cursor-left screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
{
break-if screen
move-cursor-left-on-display
return
}
column:num <- get *screen, cursor-column:offset
at-top?:bool <- lesser-or-equal column, 0
return-if at-top?
column <- subtract column, 1
*screen <- put *screen, cursor-column:offset, column
]
def cursor-to-start-of-line screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
row:num <- cursor-position screen
column:num <- copy 0
screen <- move-cursor screen, row, column
]
def cursor-to-next-line screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
screen <- cursor-down screen
screen <- cursor-to-start-of-line screen
]
def move-cursor-to-column screen:&:screen, column:num -> screen:&:screen [
local-scope
load-ingredients
row:num, _ <- cursor-position screen
move-cursor screen, row, column
]
def screen-width screen:&:screen -> width:num [
local-scope
load-ingredients
{
break-unless screen
width <- get *screen, num-columns:offset
return
}
width <- display-width
]
def screen-height screen:&:screen -> height:num [
local-scope
load-ingredients
{
break-unless screen
height <- get *screen, num-rows:offset
return
}
height <- display-height
]
def hide-cursor screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
reply-if screen
hide-cursor-on-display
]
def show-cursor screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
reply-if screen
show-cursor-on-display
]
def hide-screen screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
reply-if screen
hide-display
]
def show-screen screen:&:screen -> screen:&:screen [
local-scope
load-ingredients
reply-if screen
show-display
]
def print screen:&:screen, s:text -> screen:&:screen [
local-scope
load-ingredients
color:num, color-found?:bool <- next-ingredient
{
break-if color-found?
color <- copy 7/white
}
bg-color:num, bg-color-found?:bool <- next-ingredient
{
break-if bg-color-found?
bg-color <- copy 0/black
}
len:num <- length *s
i:num <- copy 0
{
done?:bool <- greater-or-equal i, len
break-if done?
c:char <- index *s, i
print screen, c, color, bg-color
i <- add i, 1
loop
}
]
scenario print-text-stops-at-right-margin [
local-scope
fake-screen:&:screen <- new-fake-screen 3/width, 2/height
run [
fake-screen <- print fake-screen, [abcd]
cell:&:@:screen-cell <- get *fake-screen, data:offset
10:@:screen-cell/raw <- copy *cell
]
memory-should-contain [
10 <- 6
11 <- 97
12 <- 7
13 <- 98
14 <- 7
15 <- 100
16 <- 7
17 <- 0
]
]
def print-integer screen:&:screen, n:num -> screen:&:screen [
local-scope
load-ingredients
color:num, color-found?:bool <- next-ingredient
{
break-if color-found?
color <- copy 7/white
}
bg-color:num, bg-color-found?:bool <- next-ingredient
{
break-if bg-color-found?
bg-color <- copy 0/black
}
s:text <- to-text n
screen <- print screen, s, color, bg-color
]
def print screen:&:screen, n:num -> screen:&:screen [
local-scope
load-ingredients
color:num, color-found?:bool <- next-ingredient
{
break-if color-found?
color <- copy 7/white
}
bg-color:num, bg-color-found?:bool <- next-ingredient
{
break-if bg-color-found?
bg-color <- copy 0/black
}
screen <- print-integer screen, n, color, bg-color
]
def print screen:&:screen, n:&:_elem -> screen:&:screen [
local-scope
load-ingredients
color:num, color-found?:bool <- next-ingredient
{
break-if color-found?
color <- copy 7/white
}
bg-color:num, bg-color-found?:bool <- next-ingredient
{
break-if bg-color-found?
bg-color <- copy 0/black
}
n2:num <- copy n
screen <- print-integer screen, n2, color, bg-color
]