-
Notifications
You must be signed in to change notification settings - Fork 0
/
Display.scala
267 lines (214 loc) · 6.58 KB
/
Display.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
// Display.scala
// Copyright (c) 2015 J. M. Spivey
/** A view that keeps a terminal up to date with respect to a text model. */
class Display(terminal: Display.Hardware) {
/** The text model that is shown on the display. */
private var ed: EdBuffer = _
/** Register the text model to be shown */
def show(ed: EdBuffer) { this.ed = ed }
/** Number of lines in the main display */
private val LINES = Display.HEIGHT - 1
/** The top line of the text model to be shown on the main display */
private var origin = 0
/** Top line from the previous time the display was refreshed */
private var oldOrigin = 0
/** Current editing position in the text model for main display */
private var row = 0
private var col = 0
/** mark's position*/
private var markRow = 0
private var markCol = 0
/** Line for showing the minibuffer */
private val MINIROW = Display.HEIGHT - 1
/** The minibuffer being displayed, or null if none */
private var minibuf: MiniBuffer = _
/** A message to show in the minbuffer or null if none */
private var message: String = _
// Delegates for keyboard methods
/** If >= 0, a keystroke that has been pushed back
* to be read again later. */
private var pbkey = -1
/** Get a keystroke */
def getKey: Int = {
if (pbkey >= 0) {
val key = pbkey; pbkey = -1; key
} else {
terminal.getKey
}
}
/** Push back a keystroke to be read again later. */
def pushBack(key: Int) { assert(pbkey == -1); pbkey = key }
/** Flush type-ahead */
def flush() { terminal.flush(); pbkey = -1 }
/** Just beep */
def beep() { terminal.beep() }
// These routines rewrite parts of the display, but leave the cursor
// where they please
/** Scratch buffer for use by rewrite */
private val line = new Text(Display.WIDTH)
/** Rewrite the entire screen */
private def rewrite() {
terminal.clear()
for (r <- 0 until Math.min(LINES, ed.numLines - origin)) {
ed.fetchLine(origin + r, line)
if (line.length > 0) {
terminal.gotoRC(r, 0)
terminal.write(line)
}
}
}
/** Rewrite just the line containing the cursor */
private def rewriteLine() {
terminal.gotoRC(row - origin, 0)
terminal.clearLine()
ed.fetchLine(row, line)
terminal.write(line)
}
/** Rewrite just the minibuffer line */
private def rewriteMinibuf() {
terminal.gotoRC(MINIROW, 0)
terminal.clearLine()
if (minibuf != null) {
terminal.setRevVideo(true)
terminal.write(minibuf.prompt)
terminal.write(':')
terminal.setRevVideo(false)
terminal.write(' ')
minibuf.getText(line)
terminal.write(line)
} else {
val mflag = if (ed.isModified) "*" else ""
terminal.setRevVideo(true)
terminal.write("--- EWOKS: %s%s ---".format(ed.filename, mflag))
terminal.setRevVideo(false)
if (message != null) {
terminal.write(' ')
terminal.write(message)
}
}
}
/** Move the cursor to the correct place */
private def moveCursor(flag: Boolean = true) {
if (minibuf != null && minibuf.pos >= 0 && flag)
terminal.gotoRC(MINIROW, minibuf.prompt.length + minibuf.pos + 2)
else
terminal.gotoRC(row - origin, Math.min(col, Display.WIDTH - 1))
}
private def moveMark(): Unit = {
terminal.markRC(markRow - origin, Math.min(markCol, Display.WIDTH - 1))
}
/** Update the display */
def refresh(damage: Int, r: Int, c: Int, mr: Int = -1, mc: Int = -1) {
var dmg = damage
row = r; col = c
markRow = mr; markCol = mc
checkScroll()
if (origin != oldOrigin)
dmg = EdBuffer.REWRITE
dmg match {
case EdBuffer.REWRITE => rewrite()
case EdBuffer.REWRITE_LINE => rewriteLine()
case EdBuffer.CLEAN => ()
case _ => throw new Error("Display.refresh")
}
rewriteMinibuf()
moveCursor()
moveMark()
oldOrigin = origin
}
/** Update the minibuffer line */
def refreshMinibuf(flag: Boolean = true) {
rewriteMinibuf()
moveCursor(flag)
}
/** Post or (with null) remove a minibuffer */
def setMiniBuf(minibuf: MiniBuffer) {
this.minibuf = minibuf
refreshMinibuf()
}
/** Post or (with null) remove a message. */
def setMessage(message: String) {
this.message = message
refreshMinibuf()
}
/* These routines implement the scrolling policy. External calls of
* chooseOrigin and scroll may set an origin that does not obey
* the scrolling policy; this will be corrected by checkScroll next
* time the display is refreshed. */
/** Check that the origin obeys the rules, and move it if not */
private def checkScroll() {
if (row < origin || row >= origin + LINES)
chooseOrigin()
/* Ensure that the origin is within the buffer, if possible by
* half a screen at the end */
origin = Math.max(Math.min(origin, ed.numLines - LINES / 2), 0)
}
/** Choose display origin to centre the cursor */
def chooseOrigin() {
// This is used for Redraw
origin = row - LINES / 2
}
/** Suggest scrolling by a specified amount */
def scroll(n: Int) {
// This is used by PageUp and PageDown
origin += n
}
}
object Display {
/** Dimensions of the display (fixed for now) */
val HEIGHT = 24
val WIDTH = 80
/** Codes returned by the keyboard */
val UP = 513
val DOWN = 514
val RIGHT = 515
val LEFT = 516
val HOME = 517
val END = 518
val PAGEUP = 519
val PAGEDOWN = 520
val INS = 521
val DEL = 522
val F1 = 523
val F2 = 524
val F3 = 525
val F4 = 526
val F5 = 527
val F6 = 528
val F7 = 529
val F8 = 530
val F9 = 531
val F10 = 532
val F11 = 533
val F12 = 534
val CTRLHOME = 535
val CTRLEND = 536
val TAB = 537
val RETURN = 538
val UNDEFINED: Int = -1
def ctrl(ch: Int): Int = ch ^ 0x40
val printable: Range = 32 until 127
/** Interface for display hardware */
trait Hardware {
/** Wait for for a keystroke and return it. */
def getKey: Int
/** Discard any pending input */
def flush(): Unit
/** Clear the whole screen */
def clear(): Unit
/** Clear from the cursor to the end of the line */
def clearLine(): Unit
/** Move the cursor to a specified row and column */
def gotoRC(row: Int, col: Int): Unit
/** mark the marker*/
def markRC(row: Int, col: Int): Unit
/** Display a string at the cursor */
def write(s: CharSequence): Unit
/** Display a character at the cursor */
def write(ch: Char): Unit
/** Set reverse video mode for future writes */
def setRevVideo(rev: Boolean): Unit
/** Ring the bell */
def beep(): Unit
}
}