40 constexpr std::string_view
defaultFont =
"skins/VeraMono.ttf.gz";
42 OSDConsoleRenderer::OSDConsoleRenderer(
44 unsigned screenW_,
unsigned screenH_,
46 :
Layer(COVER_NONE, Z_CONSOLE)
48 , display(reactor.getDisplay())
50 , consoleSetting(console.getConsoleSetting())
54 , consolePlacementSetting(
55 reactor.getCommandController(),
"consoleplacement",
56 "position of the console within the emulator",
62 {
"topleft", CP_TOPLEFT},
64 {
"topright", CP_TOPRIGHT},
66 {
"center", CP_CENTER},
68 {
"bottomleft", CP_BOTTOMLEFT},
69 {
"bottom", CP_BOTTOM},
70 {
"bottomright", CP_BOTTOMRIGHT}})
71 , fontSizeSetting(reactor.getCommandController(),
72 "consolefontsize",
"Size of the console font", 12, 8, 32)
73 , fontSetting(reactor.getCommandController(),
75 , consoleColumnsSetting(reactor.getCommandController(),
76 "consolecolumns",
"number of columns in the console",
77 initFontAndGetColumns(), 32, 999)
78 , consoleRowsSetting(reactor.getCommandController(),
79 "consolerows",
"number of rows in the console",
81 , backgroundSetting(reactor.getCommandController(),
82 "consolebackground",
"console background file",
83 "skins/ConsoleBackgroundGrey.png")
88 bgPos = bgSize =
ivec2();
90 lastBlinkTime = Timer::getTime();
91 lastCursorX = lastCursorY = 0;
95 setCoverage(COVER_PARTIAL);
100 backgroundSetting.setChecker([
this](TclObject& value) {
101 loadBackground(value.getString());
105 consoleSetting.attach(*
this);
106 fontSetting.attach(*
this);
107 fontSizeSetting.attach(*
this);
108 setActive(consoleSetting.getBoolean());
111 int OSDConsoleRenderer::initFontAndGetColumns()
114 fontSetting.setChecker([
this](TclObject& value) {
115 loadFont(value.getString());
118 loadFont(fontSetting.getString());
119 }
catch (MSXException&) {
122 reactor.getCliComm().printWarning(
123 "Loading selected font (", fontSetting.getString(),
124 ") failed. Reverting to default font (",
defaultFont,
").");
128 throw FatalError(
"Couldn't load default console font.\n"
129 "Please check your installation.");
133 return (((screenW -
CHAR_BORDER) / font.getWidth()) * 30) / 32;
135 int OSDConsoleRenderer::getRows()
138 return ((screenH / font.getHeight()) * 6) / 15;
140 OSDConsoleRenderer::~OSDConsoleRenderer()
142 fontSizeSetting.detach(*
this);
143 fontSetting.detach(*
this);
144 consoleSetting.detach(*
this);
148 void OSDConsoleRenderer::adjustColRow()
150 unsigned consoleColumns = std::min<unsigned>(
151 consoleColumnsSetting.getInt(),
153 unsigned consoleRows = std::min<unsigned>(
154 consoleRowsSetting.getInt(),
155 screenH / font.getHeight());
156 console.setColumns(consoleColumns);
157 console.setRows(consoleRows);
160 void OSDConsoleRenderer::update(
const Setting&
setting) noexcept
162 if (&
setting == &consoleSetting) {
163 setActive(consoleSetting.getBoolean());
164 }
else if (&
setting ==
one_of(&fontSetting, &fontSizeSetting)) {
165 loadFont(fontSetting.getString());
171 void OSDConsoleRenderer::setActive(
bool active_)
173 if (active == active_)
return;
176 display.repaintDelayed(40000);
178 activeTime = Timer::getTime();
181 byte OSDConsoleRenderer::getVisibility()
const
183 const uint64_t FADE_IN_DURATION = 100000;
184 const uint64_t FADE_OUT_DURATION = 150000;
186 auto now = Timer::getTime();
187 auto dur = now - activeTime;
189 if (dur > FADE_IN_DURATION) {
192 display.repaintDelayed(40000);
193 return byte((dur * 255) / FADE_IN_DURATION);
196 if (dur > FADE_OUT_DURATION) {
199 display.repaintDelayed(40000);
200 return byte(255 - ((dur * 255) / FADE_OUT_DURATION));
205 bool OSDConsoleRenderer::updateConsoleRect()
210 font.getHeight() * console.getRows());
214 switch (consolePlacementSetting.getEnum()) {
223 pos[0] = (screenW -
size[0]);
229 pos[0] = (screenW -
size[0]) / 2;
232 switch (consolePlacementSetting.getEnum()) {
241 pos[1] = (screenH -
size[1]) / 2;
247 pos[1] = (screenH -
size[1]);
251 bool result = (pos != bgPos) || (
size != bgSize);
257 void OSDConsoleRenderer::loadFont(std::string_view value)
260 auto newFont = TTFFont(
filename, fontSizeSetting.getInt());
261 if (!newFont.isFixedWidth()) {
262 throw MSXException(value,
" is not a monospaced font");
264 font = std::move(newFont);
268 void OSDConsoleRenderer::loadBackground(std::string_view value)
271 backgroundImage.reset();
274 auto* output = display.getOutputSurface();
276 backgroundImage.reset();
281 backgroundImage = std::make_unique<SDLImage>(*output,
filename, bgSize);
285 backgroundImage = std::make_unique<GLImage>(*output,
filename, bgSize);
290 void OSDConsoleRenderer::drawText(OutputSurface& output, std::string_view text,
291 int cx,
int cy,
byte alpha, uint32_t rgb)
293 auto xy = getTextPos(cx, cy);
294 auto [inCache,
image, width] = getFromCache(text, rgb);
296 std::string textStr(text);
298 uint32_t rgb2 = openGL ? 0xffffff : rgb;
300 width = font.getSize(textStr)[0];
301 surf = font.render(textStr,
305 }
catch (MSXException&
e) {
306 static bool alreadyPrinted =
false;
307 if (!alreadyPrinted) {
308 alreadyPrinted =
true;
309 reactor.getCliComm().printWarning(
310 "Invalid console text (invalid UTF-8): ",
315 std::unique_ptr<BaseImage> image2;
318 }
else if (!openGL) {
319 image2 = std::make_unique<SDLImage>(output, std::move(surf));
323 image2 = std::make_unique<GLImage>(output, std::move(surf));
326 image = image2.get();
327 insertInCache(std::move(textStr), rgb, std::move(image2), width);
331 byte r = (rgb >> 16) & 0xff;
332 byte g = (rgb >> 8) & 0xff;
333 byte b = (rgb >> 0) & 0xff;
334 image->draw(output, xy, r,
g, b, alpha);
336 image->draw(output, xy, alpha);
341 std::tuple<bool, BaseImage*, unsigned> OSDConsoleRenderer::getFromCache(
342 std::string_view text, uint32_t rgb)
350 if ((it->text == text) && (openGL || (it->rgb == rgb))) {
357 for (it =
begin(textCache); it !=
end(textCache); ++it) {
358 if (it->text != text)
continue;
359 if (!openGL && (it->rgb != rgb))
continue;
360 found: BaseImage*
image = it->image.get();
361 unsigned width = it->width;
363 if (it !=
begin(textCache)) {
366 textCache.splice(
begin(textCache), textCache, it);
368 return {
true,
image, width};
370 return {
false,
nullptr, 0};
373 void OSDConsoleRenderer::insertInCache(
374 std::string text, uint32_t rgb, std::unique_ptr<BaseImage>
image,
377 constexpr
unsigned MAX_TEXT_CACHE_SIZE = 250;
378 if (textCache.size() == MAX_TEXT_CACHE_SIZE) {
380 if (
auto it = std::prev(
std::end(textCache)); it == cacheHint) {
381 cacheHint =
begin(textCache);
383 textCache.pop_back();
385 textCache.emplace_front(std::move(text), rgb, std::move(
image), width);
388 void OSDConsoleRenderer::clearCache()
392 textCache.emplace_back(std::string{}, 0,
nullptr, 0);
393 cacheHint =
begin(textCache);
396 gl::ivec2 OSDConsoleRenderer::getTextPos(
int cursorX,
int cursorY)
const
399 bgSize[1] - (font.getHeight() * (cursorY + 1)) - 1);
402 void OSDConsoleRenderer::paint(OutputSurface& output)
404 byte visibility = getVisibility();
405 if (!visibility)
return;
407 if (updateConsoleRect()) {
409 loadBackground(backgroundSetting.getString());
410 }
catch (MSXException&
e) {
411 reactor.getCliComm().printWarning(
e.getMessage());
416 if (!backgroundImage) {
420 backgroundImage = std::make_unique<SDLImage>(
425 backgroundImage = std::make_unique<GLImage>(
429 }
catch (MSXException&) {
433 if (backgroundImage) {
434 backgroundImage->draw(output, bgPos, visibility);
437 drawConsoleText(output, visibility);
440 auto now = Timer::getTime();
441 if (lastBlinkTime < now) {
446 auto [cursorX, cursorY] = console.getCursorPosition();
447 if ((
unsigned(cursorX) != lastCursorX) || (
unsigned(cursorY) != lastCursorY)) {
450 lastCursorX = cursorX;
451 lastCursorY = cursorY;
453 if (blink && (console.getScrollBack() == 0)) {
454 drawText(output,
"_", cursorX, cursorY, visibility, 0xffffff);
458 void OSDConsoleRenderer::drawConsoleText(OutputSurface& output,
byte visibility)
460 const auto rows = console.getRows();
461 const auto columns = console.getColumns();
462 const auto scrollBack = console.getScrollBack();
463 const auto& lines = console.getLines();
466 auto [cursorY_, subLine, lineIdx] = [&] {
468 size_t target = rows + scrollBack;
469 for (
auto idx :
xrange(lines.size())) {
470 count +=
std::max(
size_t(1), (lines[idx].numChars() + columns - 1) / columns);
471 if (
count >= target) {
472 return std::tuple(
int(rows - 1),
int(
count - target), idx);
475 int y = int(
count - 1 - scrollBack);
476 return std::tuple(y, 0, lines.size() - 1);
478 int cursorY = cursorY_;
481 std::string_view text = lines[lineIdx].str();
482 auto it =
begin(text);
483 auto endIt =
end(text);
485 std::string_view::size_type idx = it -
begin(text);
486 unsigned chunkIdx = 1;
487 const auto& chunks0 = lines[lineIdx].getChunks();
488 while ((chunkIdx < chunks0.size()) && (chunks0[chunkIdx].pos <= idx)) {
494 auto remainingColumns = columns;
495 const auto& chunks = lines[lineIdx].getChunks();
496 if (!chunks.empty()) {
498 while (remainingColumns && (it < endIt)) {
499 auto startColumn = remainingColumns;
501 auto nextColorIt = (chunkIdx == chunks.size())
503 :
begin(text) + chunks[chunkIdx].pos;
504 auto maxIt =
std::min(endIt, nextColorIt);
505 while (remainingColumns && (
e < maxIt)) {
510 std::string_view subText(&*it,
e - it);
511 auto rgb = chunks[chunkIdx - 1].rgb;
512 auto cursorX = columns - startColumn;
513 drawText(output, subText, cursorX, cursorY, visibility, rgb);
518 if (
e == nextColorIt) ++chunkIdx;
522 if (--cursorY < 0)
break;
526 text = lines[lineIdx].str();
Wrapper around a SDL_Surface.
std::string resolve(std::string_view filename) const
Interface for display layers.
Contains the main loop of openMSX.
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
This file implemented 3 utility functions:
const FileContext & systemFileContext()
uint8_t byte
8 bit unsigned integer
constexpr int CONSOLE_ALPHA
How transparent is the console? (0=invisible, 255=opaque) Note that when using a background image on ...
constexpr std::string_view defaultFont
constexpr uint64_t BLINK_RATE
constexpr const char *const filename
constexpr int CHAR_BORDER
void advance(octet_iterator &it, distance_type n)
size_t size(std::string_view utf8)
uint32_t next(octet_iterator &it)
constexpr auto xrange(T e)
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)