/trunk/client/Game2/HighConsole.cpp – HoverRace

root/trunk/client/Game2/HighConsole.cpp @ 910

Revision 910, 8.4 KB (checked in by zoogie, 6 months ago)

Client::Console now extends Script::Env.
We now only use one scripting state, with SysConsole and HighConsole running inside sandboxed environments.

  • Property svn:eol-style set to native
Line 
1
2// HighConsole.cpp
3// On-screen debug console.
4//
5// Copyright (c) 2009 Michael Imamura.
6//
7// Licensed under GrokkSoft HoverRace SourceCode License v1.0(the "License");
8// you may not use this file except in compliance with the License.
9//
10// A copy of the license should have been attached to the package from which
11// you have taken this file. If you can not find the license you can not use
12// this file.
13//
14//
15// The author makes no representations about the suitability of
16// this software for any purpose.  It is provided "as is" "AS IS",
17// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
18// implied.
19//
20// See the License for the specific language governing permissions
21// and limitations under the License.
22
23#include "StdAfx.h"
24
25#include <deque>
26
27#include <boost/thread/locks.hpp>
28
29#include "../../engine/Script/Core.h"
30#include "../../engine/Util/Config.h"
31#include "../../engine/VideoServices/2DViewPort.h"
32#include "../../engine/VideoServices/StaticText.h"
33#include "../../engine/VideoServices/VideoBuffer.h"
34
35#include "HighConsole.h"
36
37using namespace HoverRace;
38using HoverRace::Util::Config;
39using HoverRace::Util::OS;
40using HoverRace::VideoServices::Font;
41using HoverRace::VideoServices::StaticText;
42
43#ifdef max
44#   undef max
45#endif
46
47namespace {
48    static const std::string COMMAND_PROMPT(">> ");
49    static const std::string CONTINUE_PROMPT(":> ");
50}
51
52namespace HoverRace {
53namespace Client {
54
55class HighConsole::LogLines
56{
57    public:
58        LogLines();
59        ~LogLines();
60
61    public:
62        int GetHeight() const { return height; }
63
64    public:
65        void Add(const std::string &s, const Font &font, MR_UInt8 color);
66        void Clear();
67
68        void Render(MR_2DViewPort *vp, int x, int y);
69
70    private:
71        typedef std::deque<StaticText*> lines_t;
72        lines_t lines;
73        int height;
74
75        static const int MAX_LINES = 10;
76};
77
78HighConsole::HighConsole(Script::Core *scripting) :
79    SUPER(scripting), visible(false), cursorOn(true), cursorTick(0)
80{
81    vp = new MR_2DViewPort();
82
83    logFont = new Font("Courier New", 16);
84
85    commandPrompt = new StaticText(COMMAND_PROMPT, *logFont, 0x0a, StaticText::EFFECT_SHADOW);
86    continuePrompt = new StaticText(CONTINUE_PROMPT, *logFont, 0x0a, StaticText::EFFECT_SHADOW);
87    cursor = new StaticText("_", *logFont, 0x0e, StaticText::EFFECT_SHADOW);
88
89    charWidth = commandPrompt->GetWidth() / COMMAND_PROMPT.length();
90    consoleWidth = 800;  // Will be corrected on first rendered frame.
91
92    submitBuffer.reserve(1024);
93    historyBuffer.reserve(1024);
94    commandLine.reserve(1024);
95    commandLineDisplay = new StaticText("", *logFont, 0x0a, StaticText::EFFECT_SHADOW);
96
97    logLines = new LogLines();
98
99    // Introductory text for the console log.
100    Config *cfg = cfg->GetInstance();
101    logLines->Add(PACKAGE_NAME " version " + cfg->GetVersion(),
102        Font("Arial", 20, true), 0x0a);
103
104    Script::Core *env = GetScripting();
105    std::string intro = env->GetVersionString();
106    intro += " :: Console active.";
107    logLines->Add(intro, *logFont, 0x0e);
108}
109
110HighConsole::~HighConsole()
111{
112    delete logLines;
113
114    delete cursor;
115    delete continuePrompt;
116    delete commandPrompt;
117    delete commandLineDisplay;
118
119    delete logFont;
120
121    delete vp;
122}
123
124void HighConsole::Advance(Util::OS::timestamp_t tick)
125{
126    // Cursor visibility is based on the last character typed
127    // (so that the cursor stays visible while typing).
128    cursorOn = (OS::TimeDiff(tick, cursorTick) % 1000) < 500;
129
130    if (submitBuffer.empty()) return;
131
132    std::string history;
133    std::string chunk;
134    {
135        // See OnChar() for why we're careful about this.
136        boost::lock_guard<boost::mutex> lock(submitBufferMutex);
137        history.swap(historyBuffer);
138        chunk.swap(submitBuffer);
139    }
140   
141    // Add the history lines to the log.
142    // We assume that each line in the log terminates with "\n".
143    std::string line;
144    line.reserve(1024);
145    for (std::string::iterator iter = history.begin();
146        iter != history.end(); ++iter)
147    {
148        if (*iter == '\n') {
149            logLines->Add(line, *logFont, 0x0a);
150            line.clear();
151        }
152        else {
153            line += *iter;
154        }
155    }
156
157    SubmitChunk(chunk);
158}
159
160void HighConsole::Clear()
161{
162    logLines->Clear();
163}
164
165/**
166 * Add a log entry, pre-processing the string for display purposes.
167 * @param s The log string.  We assume that the trailing "\n" has been stripped.
168 * @param color The color.
169 */
170void HighConsole::AddLogEntry(const std::string &s, MR_UInt8 color)
171{
172    std::string buf;
173    buf.reserve(1024);
174    int i = 0;
175    for (std::string::const_iterator iter = s.begin(); iter != s.end();
176        ++iter, ++i)
177    {
178        char c = *iter;
179        switch (c) {
180            case '\t':
181                buf.resize(buf.length() + (8 - (buf.length() % 8)), ' ');
182                break;
183
184            case '\n':
185                logLines->Add(buf, *logFont, color);
186                buf.clear();
187                break;
188
189            default:
190                if (c >= 32 && c < 127) {
191                    // Line wrap if necessary.
192                    if ((buf.length() + 1) * charWidth > consoleWidth) {
193                        logLines->Add(buf, *logFont, color);
194                        buf.clear();
195                    }
196                    buf += c;
197                }
198        }
199    }
200    logLines->Add(buf, *logFont, color);
201}
202
203void HighConsole::LogInfo(const std::string &s)
204{
205    AddLogEntry(s, 0x0a);
206}
207
208void HighConsole::LogError(const std::string &s)
209{
210    AddLogEntry(s, 0x23);
211}
212
213/**
214 * Handle a character keypress.
215 * Assume that the keypress is always consumed.
216 * @param c The character.
217 */
218void HighConsole::OnChar(char c)
219{
220    cursorTick = OS::Time();
221
222    switch (c) {
223        case 8:   // Backspace.
224        case 127: // DEL.
225            if (commandLine.length() > 0) {
226                commandLine.resize(commandLine.length() - 1);
227                //TODO: Update wrapped version of line.
228            }
229            break;
230        case 13:  // CR.
231            commandLine += '\n';
232            {
233                // We assume that OnChar() will probably be called on one
234                // thread while Advance() is called on another.
235                boost::lock_guard<boost::mutex> lock(submitBufferMutex);
236                historyBuffer +=
237                    (GetInputState() == ISTATE_COMMAND) ?
238                    COMMAND_PROMPT :
239                    CONTINUE_PROMPT;
240                historyBuffer += commandLine;
241                submitBuffer += commandLine;
242            }
243            commandLine.clear();
244            break;
245        default:
246            if (c >= 32 && c <= 126) {
247                commandLine += c;
248                //TODO: Update wrapped version of line.
249            }
250    }
251}
252
253/**
254 * Renders the console.
255 * @param dest The destination viewport (may not be @c NULL).
256 */
257void HighConsole::Render(MR_VideoBuffer *dest)
258{
259    if (!visible) return;
260
261    vp->Setup(dest, 0, 0, dest->GetXRes(), dest->GetYRes());
262
263    const int viewHeight = vp->GetYRes();
264    const int viewWidth = vp->GetXRes();
265
266    consoleWidth = viewWidth - (PADDING_LEFT * 2);
267
268    // Prepare the command-line.
269    // Select prompt based on state.
270    const StaticText *prompt =
271        (GetInputState() == ISTATE_COMMAND) ?
272        commandPrompt :
273        continuePrompt;
274    commandLineDisplay->SetText(commandLine);
275
276    // Calculate the total height of the console (for background).
277    int commandLineHeight =
278        std::max(prompt->GetHeight(),
279            std::max(commandLineDisplay->GetHeight(), cursor->GetHeight()));
280    int totalHeight =
281        PADDING_TOP +
282        logLines->GetHeight() +
283        commandLineHeight +
284        PADDING_BOTTOM;
285
286    // Draw the background.
287    for (int ly = viewHeight - totalHeight - 1;
288        ly < viewHeight - commandLineHeight - PADDING_BOTTOM; ++ly)
289    {
290        vp->DrawHorizontalLine(ly, 0, viewWidth - 1, 0x18);
291    }
292    for (int i = 0, ly = viewHeight - commandLineHeight - PADDING_BOTTOM;
293        ly < viewHeight; ++i, ++ly)
294    {
295        vp->DrawHorizontalLine(ly, 0, viewWidth - 1, 0x1a - ((i >= 5) ? 5 : i));
296    }
297
298    int x = 0;
299    int y = viewHeight - totalHeight + PADDING_TOP;
300
301    // Render the log lines.
302    logLines->Render(vp, PADDING_LEFT, y);
303    y += logLines->GetHeight() + (PADDING_BOTTOM / 2);
304
305    // Render the command line.
306    x = PADDING_LEFT;
307    prompt->Blt(x, y, vp);
308    x += prompt->GetWidth();
309    commandLineDisplay->Blt(x, y, vp);
310    if (cursorOn) {
311        x += commandLineDisplay->GetWidth();
312        cursor->Blt(x, y, vp);
313    }
314}
315
316HighConsole::LogLines::LogLines() :
317    lines(), height(0)
318{
319}
320
321HighConsole::LogLines::~LogLines()
322{
323    Clear();
324}
325
326void HighConsole::LogLines::Add(const std::string &s, const Font &font, MR_UInt8 color)
327{
328    // Remove the top line if we're full.
329    if (lines.size() == MAX_LINES) {
330        StaticText *line = lines.front();
331        height -= line->GetHeight();
332        delete line;
333        lines.pop_front();
334    }
335
336    // Add the new line.
337    StaticText *line = new StaticText(s, font, color);
338    height += line->GetHeight();
339    lines.push_back(line);
340}
341
342void HighConsole::LogLines::Clear()
343{
344    for (lines_t::iterator iter = lines.begin(); iter != lines.end(); ++iter)
345    {
346        delete *iter;
347    }
348    lines.clear();
349    height = 0;
350}
351
352void HighConsole::LogLines::Render(MR_2DViewPort *vp, int x, int y)
353{
354    for (lines_t::iterator iter = lines.begin(); iter != lines.end(); ++iter) {
355        const StaticText *line = *iter;
356        line->Blt(x, y, vp);
357        y += line->GetHeight();
358    }
359}
360
361}  // namespace Client
362}  // namespace HoverRace
Note: See TracBrowser for help on using the browser.