27 using std::string_view;
45 OSDConsoleRenderer::OSDConsoleRenderer(
47 unsigned screenW_,
unsigned screenH_,
49 :
Layer(COVER_NONE, Z_CONSOLE)
51 , display(reactor.getDisplay())
53 , consoleSetting(console.getConsoleSetting())
57 , consolePlacementSetting(
58 reactor.getCommandController(),
"consoleplacement",
59 "position of the console within the emulator",
65 {
"topleft", CP_TOPLEFT},
67 {
"topright", CP_TOPRIGHT},
69 {
"center", CP_CENTER},
71 {
"bottomleft", CP_BOTTOMLEFT},
72 {
"bottom", CP_BOTTOM},
73 {
"bottomright", CP_BOTTOMRIGHT}})
74 , fontSizeSetting(reactor.getCommandController(),
75 "consolefontsize",
"Size of the console font", 12, 8, 32)
76 , fontSetting(reactor.getCommandController(),
78 , consoleColumnsSetting(reactor.getCommandController(),
79 "consolecolumns",
"number of columns in the console",
80 initFontAndGetColumns(), 32, 999)
81 , consoleRowsSetting(reactor.getCommandController(),
82 "consolerows",
"number of rows in the console",
84 , backgroundSetting(reactor.getCommandController(),
85 "consolebackground",
"console background file",
86 "skins/ConsoleBackgroundGrey.png")
91 bgPos = bgSize =
ivec2();
93 lastBlinkTime = Timer::getTime();
94 lastCursorX = lastCursorY = 0;
98 setCoverage(COVER_PARTIAL);
103 backgroundSetting.setChecker([
this](TclObject& value) {
104 loadBackground(value.getString());
108 consoleSetting.attach(*
this);
109 fontSetting.attach(*
this);
110 fontSizeSetting.attach(*
this);
111 setActive(consoleSetting.getBoolean());
114 int OSDConsoleRenderer::initFontAndGetColumns()
117 fontSetting.setChecker([
this](TclObject& value) {
118 loadFont(
string(value.getString()));
121 loadFont(fontSetting.getString());
122 }
catch (MSXException&) {
125 reactor.getCliComm().printWarning(
126 "Loading selected font (", fontSetting.getString(),
127 ") failed. Reverting to default font (",
defaultFont,
").");
131 throw FatalError(
"Couldn't load default console font.\n"
132 "Please check your installation.");
136 return (((screenW -
CHAR_BORDER) / font.getWidth()) * 30) / 32;
138 int OSDConsoleRenderer::getRows()
141 return ((screenH / font.getHeight()) * 6) / 15;
143 OSDConsoleRenderer::~OSDConsoleRenderer()
145 fontSizeSetting.detach(*
this);
146 fontSetting.detach(*
this);
147 consoleSetting.detach(*
this);
151 void OSDConsoleRenderer::adjustColRow()
153 unsigned consoleColumns = std::min<unsigned>(
154 consoleColumnsSetting.getInt(),
156 unsigned consoleRows = std::min<unsigned>(
157 consoleRowsSetting.getInt(),
158 screenH / font.getHeight());
159 console.setColumns(consoleColumns);
160 console.setRows(consoleRows);
163 void OSDConsoleRenderer::update(
const Setting& setting) noexcept
165 if (&setting == &consoleSetting) {
166 setActive(consoleSetting.getBoolean());
167 }
else if (&setting ==
one_of(&fontSetting, &fontSizeSetting)) {
168 loadFont(fontSetting.getString());
174 void OSDConsoleRenderer::setActive(
bool active_)
176 if (active == active_)
return;
179 display.repaintDelayed(40000);
181 activeTime = Timer::getTime();
184 byte OSDConsoleRenderer::getVisibility()
const
186 const uint64_t FADE_IN_DURATION = 100000;
187 const uint64_t FADE_OUT_DURATION = 150000;
189 auto now = Timer::getTime();
190 auto dur = now - activeTime;
192 if (dur > FADE_IN_DURATION) {
195 display.repaintDelayed(40000);
196 return byte((dur * 255) / FADE_IN_DURATION);
199 if (dur > FADE_OUT_DURATION) {
202 display.repaintDelayed(40000);
203 return byte(255 - ((dur * 255) / FADE_OUT_DURATION));
208 bool OSDConsoleRenderer::updateConsoleRect()
213 font.getHeight() * console.getRows());
217 switch (consolePlacementSetting.getEnum()) {
226 pos[0] = (screenW -
size[0]);
232 pos[0] = (screenW -
size[0]) / 2;
235 switch (consolePlacementSetting.getEnum()) {
244 pos[1] = (screenH -
size[1]) / 2;
250 pos[1] = (screenH -
size[1]);
254 bool result = (pos != bgPos) || (
size != bgSize);
260 void OSDConsoleRenderer::loadFont(string_view value)
263 auto newFont = TTFFont(
filename, fontSizeSetting.getInt());
264 if (!newFont.isFixedWidth()) {
265 throw MSXException(value,
" is not a monospaced font");
267 font = std::move(newFont);
271 void OSDConsoleRenderer::loadBackground(string_view value)
274 backgroundImage.reset();
277 auto* output = display.getOutputSurface();
279 backgroundImage.reset();
284 backgroundImage = std::make_unique<SDLImage>(*output,
filename, bgSize);
288 backgroundImage = std::make_unique<GLImage>(*output,
filename, bgSize);
293 void OSDConsoleRenderer::drawText(OutputSurface& output, string_view text,
294 int cx,
int cy,
byte alpha, uint32_t rgb)
296 auto xy = getTextPos(cx, cy);
297 auto [inCache, image, width] = getFromCache(text, rgb);
299 string textStr(text);
301 uint32_t rgb2 = openGL ? 0xffffff : rgb;
303 width = font.getSize(textStr)[0];
304 surf = font.render(textStr,
308 }
catch (MSXException& e) {
309 static bool alreadyPrinted =
false;
310 if (!alreadyPrinted) {
311 alreadyPrinted =
true;
312 reactor.getCliComm().printWarning(
313 "Invalid console text (invalid UTF-8): ",
318 std::unique_ptr<BaseImage> image2;
321 }
else if (!openGL) {
322 image2 = std::make_unique<SDLImage>(output, std::move(surf));
326 image2 = std::make_unique<GLImage>(output, std::move(surf));
329 image = image2.get();
330 insertInCache(std::move(textStr), rgb, std::move(image2), width);
334 byte r = (rgb >> 16) & 0xff;
335 byte g = (rgb >> 8) & 0xff;
336 byte b = (rgb >> 0) & 0xff;
337 image->draw(output, xy, r,
g, b, alpha);
339 image->draw(output, xy, alpha);
344 std::tuple<bool, BaseImage*, unsigned> OSDConsoleRenderer::getFromCache(
345 string_view text, uint32_t rgb)
353 if ((it->text == text) && (openGL || (it->rgb == rgb))) {
360 for (it =
begin(textCache); it !=
end(textCache); ++it) {
361 if (it->text != text)
continue;
362 if (!openGL && (it->rgb != rgb))
continue;
363 found: BaseImage* image = it->image.get();
364 unsigned width = it->width;
366 if (it !=
begin(textCache)) {
369 textCache.splice(
begin(textCache), textCache, it);
371 return {
true, image, width};
373 return {
false,
nullptr, 0};
376 void OSDConsoleRenderer::insertInCache(
377 string text, uint32_t rgb, std::unique_ptr<BaseImage> image,
380 constexpr
unsigned MAX_TEXT_CACHE_SIZE = 250;
381 if (textCache.size() == MAX_TEXT_CACHE_SIZE) {
383 if (
auto it = std::prev(
std::end(textCache)); it == cacheHint) {
384 cacheHint =
begin(textCache);
386 textCache.pop_back();
388 textCache.emplace_front(std::move(text), rgb, std::move(image), width);
391 void OSDConsoleRenderer::clearCache()
395 textCache.emplace_back(
string{}, 0,
nullptr, 0);
396 cacheHint =
begin(textCache);
399 gl::ivec2 OSDConsoleRenderer::getTextPos(
int cursorX,
int cursorY)
const
402 bgSize[1] - (font.getHeight() * (cursorY + 1)) - 1);
405 void OSDConsoleRenderer::paint(OutputSurface& output)
407 byte visibility = getVisibility();
408 if (!visibility)
return;
410 if (updateConsoleRect()) {
412 loadBackground(backgroundSetting.getString());
413 }
catch (MSXException& e) {
414 reactor.getCliComm().printWarning(e.getMessage());
419 if (!backgroundImage) {
423 backgroundImage = std::make_unique<SDLImage>(
428 backgroundImage = std::make_unique<GLImage>(
432 }
catch (MSXException&) {
436 if (backgroundImage) {
437 backgroundImage->draw(output, bgPos, visibility);
440 drawConsoleText(output, visibility);
443 auto now = Timer::getTime();
444 if (lastBlinkTime < now) {
449 auto [cursorX, cursorY] = console.getCursorPosition();
450 if ((
unsigned(cursorX) != lastCursorX) || (
unsigned(cursorY) != lastCursorY)) {
453 lastCursorX = cursorX;
454 lastCursorY = cursorY;
456 if (blink && (console.getScrollBack() == 0)) {
457 drawText(output,
"_", cursorX, cursorY, visibility, 0xffffff);
461 void OSDConsoleRenderer::drawConsoleText(OutputSurface& output,
byte visibility)
463 const auto rows = console.getRows();
464 const auto columns = console.getColumns();
465 const auto scrollBack = console.getScrollBack();
466 const auto& lines = console.getLines();
469 auto [cursorY_, subLine, lineIdx] = [&] {
471 size_t target = rows + scrollBack;
472 for (
auto idx :
xrange(lines.size())) {
473 count +=
std::max(
size_t(1), (lines[idx].numChars() + columns - 1) / columns);
474 if (
count >= target) {
475 return std::tuple(
int(rows - 1),
int(
count - target), idx);
478 int y = int(
count - 1 - scrollBack);
479 return std::tuple(y, 0, lines.size() - 1);
481 int cursorY = cursorY_;
484 std::string_view text = lines[lineIdx].str();
485 auto it =
begin(text);
486 auto endIt =
end(text);
488 std::string_view::size_type idx = it -
begin(text);
489 unsigned chunkIdx = 1;
490 const auto& chunks0 = lines[lineIdx].getChunks();
491 while ((chunkIdx < chunks0.size()) && (chunks0[chunkIdx].second <= idx)) {
497 auto remainingColumns = columns;
498 const auto& chunks = lines[lineIdx].getChunks();
499 if (!chunks.empty()) {
501 while (remainingColumns && (it < endIt)) {
502 auto startColumn = remainingColumns;
504 auto nextColorIt = (chunkIdx == chunks.size())
506 :
begin(text) + chunks[chunkIdx].second;
507 auto maxIt =
std::min(endIt, nextColorIt);
508 while (remainingColumns && (e < maxIt)) {
513 std::string_view subText(&*it, e - it);
514 auto rgb = chunks[chunkIdx - 1].first;
515 auto cursorX = columns - startColumn;
516 drawText(output, subText, cursorX, cursorY, visibility, rgb);
521 if (e == nextColorIt) ++chunkIdx;
525 if (--cursorY < 0)
break;
529 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 uint64_t BLINK_RATE
constexpr const char *const filename
constexpr string_view defaultFont
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)