mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-31 01:08:14 +00:00
feat(oled): misc-fixed 6x9 font on SSD1306 too, for GAT562 30S solo
The misc-fixed font (full Latin/Greek/Cyrillic) had only ever reached SH1106Display and the e-ink driver. GAT562 30S -- like 24 other variants -- uses SSD1306Display, which stayed on the built-in 5x7 font, so every keyboard alphabet beyond ASCII and every accented contact name was transliterated (Łódź -> Lodz) or drawn as a filled block. Extract the renderer into MiscFixedRenderer.h rather than copying it: Adafruit_SH110X and Adafruit_SSD1306 both derive from Adafruit_GFX and glyph drawing is pure pixel plotting, so one implementation serves both drivers (-79 lines from SH1106Display.cpp, same flash usage as before). The header is .cpp-only by contract -- the font tables are static const, so including it from a driver header would land a copy in every translation unit. On SSD1306 the path is opt-in behind OLED_MISC_FIXED_FONT, set only in the two GAT562 30S solo envs: the font costs ~14 KB of flash and the other 24 variants' repeater/companion builds have no keyboard to type those alphabets on. Without the flag that driver is unchanged, verified by building GAT562_30S_Mesh_Kit_repeater. Drive-by consistency fix from the extraction: a newline in print() now advances the cursor by yAdvance * text_size instead of a single yAdvance, which was half a row short at setTextSize(2). The e-ink driver always scaled it. No effect at size 1, which is everywhere the UI prints '\n'. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
- **Bot Trigger fields accept multiple phrases.** Pack several trigger words into one Trigger field, comma-separated (`hi,hello there,yo`) — matching any one of them fires the reply, same as before for a single phrase.
|
||||
- **Remote Bot Actions gain `!gpio1`..`!gpio4`** (Wio Tracker L1 only — 4 otherwise-unused pins). Each pin is independently set to Off/Input/Output (GPIO1/GPIO2 also offer Analog) from a new **Tools › GPIO** screen; `!gpio1 on`/`!gpio1 off` drives an Output pin remotely, a bare `!gpio1` reports the current mode and reading (including a millivolt value in Analog mode). Gated by the same per-target Actions toggle as `!buzz`/`!gps`/`!advert`.
|
||||
- **Optional CardKB support (Wio Tracker L1, Grove I2C) — full keyboard-only navigation.** Plug an M5Stack CardKB into the Grove connector and it's auto-detected at boot — no setting to flip. Typing goes straight into the message/name field instead of navigating the on-screen keyboard grid, always as plain Latin text regardless of the Settings › Keyboard language/type — arrows and Esc work as expected everywhere else too (including inside the placeholder and accent popups). Enter acts like the physical centre button (advances the on-screen grid selection) rather than submitting, so **Fn+Enter** sends the message/confirms the field from anywhere, and **Fn+`<letter>`** opens that letter's accent popup directly (no arrow-hunting needed — handy for Polish/Czech/etc. diacritics — and works no matter what language/keyboard type is configured, since CardKB always types Latin either way). **Tab** is the Hold-Enter equivalent everywhere, including message reply/navigate and the Bot/Admin/Repeater menus — same single shortcut whether or not the on-screen keyboard is showing.
|
||||
- **GAT562 30S solo builds now use the same OLED font as the Wio Tracker L1.** The misc-fixed 6x9 font (full Latin, Greek and Cyrillic) had only ever reached the SH1106 and e-ink drivers, so on this board every keyboard alphabet beyond ASCII, and every accented contact name, was still being transliterated (`Łódź` → `Lodz`) or drawn as a filled block. Solo builds only — the font costs ~14 KB of flash and the repeater/companion builds have no keyboard to type those alphabets on; they keep the built-in font and are byte-for-byte unchanged.
|
||||
- **Settings › Keyboard gets an "Ext. KB" row** (boards that support CardKB, above). Switching it to **Compact** hides the on-screen letter grid, special-row icons, and status line entirely — a CardKB typist never looks at any of it, and none of it (script/page, T9-vs-ABC, caps) is actually actionable from CardKB anyway — replacing them with just a reminder of the Tab/Fn+letter shortcuts; the accent and placeholder popups still show normally. With the grid hidden, arrows and Tab take on more useful direct meanings instead of driving an invisible grid selection: **arrows** move the text cursor immediately (no more entering cursor mode first), **plain Enter** submits the message/field (same as Fn+Enter — there's no grid cell to commit), and **plain Tab** opens the placeholder picker directly. Compact is designed to need no joystick/physical button at all, and reclaims the freed space for extra message-preview lines. Off (**Full**) by default.
|
||||
|
||||
### Fixes
|
||||
|
||||
103
src/helpers/ui/MiscFixedRenderer.h
Normal file
103
src/helpers/ui/MiscFixedRenderer.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <Adafruit_GFX.h>
|
||||
#include "DisplayDriver.h"
|
||||
#include "MiscFixedFont.h"
|
||||
#include "LemonIcons.h"
|
||||
|
||||
// Shared misc-fixed 6x9 text renderer for the monochrome OLED drivers.
|
||||
//
|
||||
// Both Adafruit_SH110X and Adafruit_SSD1306 derive from Adafruit_GFX and the
|
||||
// font path is pure pixel plotting, so SH1106Display and SSD1306Display render
|
||||
// off this one implementation instead of two copies of it. Everything here
|
||||
// bypasses GFX's own print(): GFX walks bytes and would treat each byte of a
|
||||
// multi-byte UTF-8 sequence as its own CP437 glyph, and it can't reach the
|
||||
// custom UI icons either.
|
||||
//
|
||||
// Include only from a .cpp — the font tables are static const arrays, so a
|
||||
// header pulling this in would land a copy of them in every translation unit.
|
||||
//
|
||||
// `y` is the TOP of the text row in every function below, matching the UI's
|
||||
// coordinate convention (GFX fonts would use the baseline).
|
||||
|
||||
// Pixel advance of one codepoint at text size sz. Unmapped codepoints get the
|
||||
// font's own 6px cell, same as the substitution box drawn for them.
|
||||
static inline uint8_t miscFixedXAdvance(uint32_t cp, int sz) {
|
||||
uint8_t xa;
|
||||
if (cp < MiscFixed.first || cp > MiscFixed.last) xa = 6;
|
||||
else xa = pgm_read_byte(&MiscFixedGlyphs[cp - MiscFixed.first].xAdvance);
|
||||
return xa * sz;
|
||||
}
|
||||
|
||||
// Draw one codepoint at (x, y); returns the x to continue from.
|
||||
static inline int16_t miscFixedDrawGlyph(Adafruit_GFX& gfx, int16_t x, int16_t y,
|
||||
uint32_t cp, int sz, uint16_t color) {
|
||||
for (uint8_t i = 0; i < lemonIconCount; i++) {
|
||||
if (pgm_read_dword(&lemonIconCPs[i]) == cp) {
|
||||
const GFXglyph* g = &lemonIconGlyphs[i];
|
||||
uint8_t w = pgm_read_byte(&g->width), h = pgm_read_byte(&g->height);
|
||||
int8_t xo = (int8_t)pgm_read_byte(&g->xOffset), yo = (int8_t)pgm_read_byte(&g->yOffset);
|
||||
uint8_t xa = pgm_read_byte(&g->xAdvance);
|
||||
uint16_t bo = pgm_read_word(&g->bitmapOffset);
|
||||
uint8_t bits = 0, bit = 0;
|
||||
for (uint8_t row = 0; row < h; row++)
|
||||
for (uint8_t col = 0; col < w; col++) {
|
||||
if (!bit) { bits = pgm_read_byte(&lemonIconBitmaps[bo++]); bit = 0x80; }
|
||||
if (bits & bit) {
|
||||
// The UI icons keep a +6 baseline; the font glyphs below use +7
|
||||
// (misc-fixed's ascent), so the icons sit 1px higher in their cell.
|
||||
if (sz == 1) gfx.drawPixel(x + xo + col, y + 6 + yo + row, color);
|
||||
else gfx.fillRect(x + xo*sz + col*sz, y + 6*sz + yo*sz + row*sz, sz, sz, color);
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
return x + xa * sz;
|
||||
}
|
||||
}
|
||||
|
||||
if (cp < MiscFixed.first || cp > MiscFixed.last) {
|
||||
// Substitution box for anything the font doesn't cover. Drawn at plain `y`
|
||||
// (not y - 7*sz): `y` is already the ascent top, so the box stays inside
|
||||
// its own row instead of bleeding into the one above.
|
||||
if (cp >= 0x20) gfx.fillRect(x + sz, y, 4*sz, 6*sz, color);
|
||||
return x + 6 * sz;
|
||||
}
|
||||
|
||||
const GFXglyph* g = &MiscFixedGlyphs[cp - MiscFixed.first];
|
||||
uint8_t w = pgm_read_byte(&g->width), h = pgm_read_byte(&g->height);
|
||||
int8_t xo = (int8_t)pgm_read_byte(&g->xOffset), yo = (int8_t)pgm_read_byte(&g->yOffset);
|
||||
uint8_t xa = pgm_read_byte(&g->xAdvance);
|
||||
uint16_t bo = pgm_read_word(&g->bitmapOffset);
|
||||
uint8_t bits = 0, bit = 0;
|
||||
for (uint8_t row = 0; row < h; row++)
|
||||
for (uint8_t col = 0; col < w; col++) {
|
||||
if (!bit) { bits = pgm_read_byte(&MiscFixedBitmaps[bo++]); bit = 0x80; }
|
||||
if (bits & bit) {
|
||||
if (sz == 1) gfx.drawPixel(x + xo + col, y + 7 + yo + row, color);
|
||||
else gfx.fillRect(x + xo*sz + col*sz, y + 7*sz + yo*sz + row*sz, sz, sz, color);
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
return x + xa * sz;
|
||||
}
|
||||
|
||||
// Draw a UTF-8 string from the driver's current cursor, honouring '\n', and
|
||||
// leave the cursor where the text ended (same contract as GFX's print()).
|
||||
static inline void miscFixedPrint(Adafruit_GFX& gfx, const char* str, int sz, uint16_t color) {
|
||||
int16_t cx = gfx.getCursorX();
|
||||
int16_t cy = gfx.getCursorY();
|
||||
const uint8_t* p = (const uint8_t*)str;
|
||||
while (*p) {
|
||||
uint32_t cp = DisplayDriver::decodeCodepoint(p);
|
||||
if (cp == '\n') { cy += MiscFixed.yAdvance * sz; cx = 0; }
|
||||
else { cx = miscFixedDrawGlyph(gfx, cx, cy, cp, sz, color); }
|
||||
}
|
||||
gfx.setCursor(cx, cy);
|
||||
}
|
||||
|
||||
static inline uint16_t miscFixedTextWidth(const char* str, int sz) {
|
||||
uint16_t width = 0;
|
||||
const uint8_t* p = (const uint8_t*)str;
|
||||
while (*p) width += miscFixedXAdvance(DisplayDriver::decodeCodepoint(p), sz);
|
||||
return width;
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "SH1106Display.h"
|
||||
#include <Adafruit_GrayOLED.h>
|
||||
#include "Adafruit_SH110X.h"
|
||||
#include "MiscFixedFont.h"
|
||||
#include "LemonIcons.h"
|
||||
#include "MiscFixedRenderer.h"
|
||||
|
||||
bool SH1106Display::i2c_probe(TwoWire &wire, uint8_t addr)
|
||||
{
|
||||
@@ -67,64 +66,8 @@ void SH1106Display::setCursor(int x, int y)
|
||||
display.setCursor(x, y);
|
||||
}
|
||||
|
||||
int16_t SH1106Display::drawGlyph(int16_t x, int16_t y, uint32_t cp) {
|
||||
int sz = _text_sz;
|
||||
for (uint8_t i = 0; i < lemonIconCount; i++) {
|
||||
if (pgm_read_dword(&lemonIconCPs[i]) == cp) {
|
||||
const GFXglyph* g = &lemonIconGlyphs[i];
|
||||
uint8_t w = pgm_read_byte(&g->width), h = pgm_read_byte(&g->height);
|
||||
int8_t xo = (int8_t)pgm_read_byte(&g->xOffset), yo = (int8_t)pgm_read_byte(&g->yOffset);
|
||||
uint8_t xa = pgm_read_byte(&g->xAdvance);
|
||||
uint16_t bo = pgm_read_word(&g->bitmapOffset);
|
||||
uint8_t bits = 0, bit = 0;
|
||||
for (uint8_t row = 0; row < h; row++)
|
||||
for (uint8_t col = 0; col < w; col++) {
|
||||
if (!bit) { bits = pgm_read_byte(&lemonIconBitmaps[bo++]); bit = 0x80; }
|
||||
if (bits & bit) {
|
||||
if (sz == 1) display.drawPixel(x + xo + col, y + 6 + yo + row, _color);
|
||||
else display.fillRect(x + xo*sz + col*sz, y + 6*sz + yo*sz + row*sz, sz, sz, _color);
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
return x + xa * sz;
|
||||
}
|
||||
}
|
||||
|
||||
// Font glyphs come from misc-fixed 6x9 (full Latin/Greek/Cyrillic, ascent 7) —
|
||||
// baseline +7. The custom UI icons above keep +6, so they sit 1px higher.
|
||||
if (cp < MiscFixed.first || cp > MiscFixed.last) {
|
||||
// y here is the top of the current text row, i.e. already baseline minus
|
||||
// the font's 7px ascent (see real-glyph rendering just below: y + 7 + yo +
|
||||
// row) -- unlike GxEPDDisplay's drawGlyph, where y IS the baseline and
|
||||
// the box sits at y - 7*sc for the same reason. Drawing this box at plain
|
||||
// `y` (not y - 7*sz) lands it in the same ascent-top position, within its
|
||||
// own row instead of bleeding into the row above.
|
||||
if (cp >= 0x20) display.fillRect(x + sz, y, 4*sz, 6*sz, _color);
|
||||
return x + 6 * sz;
|
||||
}
|
||||
const GFXglyph* g = &MiscFixedGlyphs[cp - MiscFixed.first];
|
||||
uint8_t w = pgm_read_byte(&g->width), h = pgm_read_byte(&g->height);
|
||||
int8_t xo = (int8_t)pgm_read_byte(&g->xOffset), yo = (int8_t)pgm_read_byte(&g->yOffset);
|
||||
uint8_t xa = pgm_read_byte(&g->xAdvance);
|
||||
uint16_t bo = pgm_read_word(&g->bitmapOffset);
|
||||
uint8_t bits = 0, bit = 0;
|
||||
for (uint8_t row = 0; row < h; row++)
|
||||
for (uint8_t col = 0; col < w; col++) {
|
||||
if (!bit) { bits = pgm_read_byte(&MiscFixedBitmaps[bo++]); bit = 0x80; }
|
||||
if (bits & bit) {
|
||||
if (sz == 1) display.drawPixel(x + xo + col, y + 7 + yo + row, _color);
|
||||
else display.fillRect(x + xo*sz + col*sz, y + 7*sz + yo*sz + row*sz, sz, sz, _color);
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
return x + xa * sz;
|
||||
}
|
||||
|
||||
uint8_t SH1106Display::glyphXAdvance(uint32_t cp) {
|
||||
uint8_t xa;
|
||||
if (cp < MiscFixed.first || cp > MiscFixed.last) xa = 6;
|
||||
else xa = pgm_read_byte(&MiscFixedGlyphs[cp - MiscFixed.first].xAdvance);
|
||||
return xa * _text_sz;
|
||||
return miscFixedXAdvance(cp, _text_sz);
|
||||
}
|
||||
|
||||
void SH1106Display::translateUTF8ToBlocks(char* dest, const char* src, size_t dest_size) {
|
||||
@@ -141,16 +84,7 @@ void SH1106Display::translateUTF8ToBlocks(char* dest, const char* src, size_t de
|
||||
void SH1106Display::print(const char *str)
|
||||
{
|
||||
if (!_single_font) { display.print(str); return; }
|
||||
|
||||
int16_t cx = display.getCursorX();
|
||||
int16_t cy = display.getCursorY();
|
||||
const uint8_t* p = (const uint8_t*)str;
|
||||
while (*p) {
|
||||
uint32_t cp = decodeCodepoint(p);
|
||||
if (cp == '\n') { cy += MiscFixed.yAdvance; cx = 0; }
|
||||
else { cx = drawGlyph(cx, cy, cp); }
|
||||
}
|
||||
display.setCursor(cx, cy);
|
||||
miscFixedPrint(display, str, _text_sz, _color);
|
||||
}
|
||||
|
||||
void SH1106Display::fillRect(int x, int y, int w, int h)
|
||||
@@ -170,12 +104,7 @@ void SH1106Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h)
|
||||
|
||||
uint16_t SH1106Display::getTextWidth(const char *str)
|
||||
{
|
||||
if (_single_font) {
|
||||
uint16_t width = 0;
|
||||
const uint8_t* p = (const uint8_t*)str;
|
||||
while (*p) width += glyphXAdvance(decodeCodepoint(p));
|
||||
return width;
|
||||
}
|
||||
if (_single_font) return miscFixedTextWidth(str, _text_sz);
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
|
||||
|
||||
@@ -31,7 +31,8 @@ class SH1106Display : public DisplayDriver
|
||||
bool _force_redraw = true;
|
||||
|
||||
bool i2c_probe(TwoWire &wire, uint8_t addr);
|
||||
int16_t drawGlyph(int16_t x, int16_t y, uint32_t cp);
|
||||
// Thin wrapper over the shared misc-fixed renderer (MiscFixedRenderer.h), kept
|
||||
// out of this header so the font tables land in one translation unit only.
|
||||
uint8_t glyphXAdvance(uint32_t cp);
|
||||
|
||||
public:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "SSD1306Display.h"
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
#include "MiscFixedRenderer.h"
|
||||
#endif
|
||||
|
||||
bool SSD1306Display::i2c_probe(TwoWire& wire, uint8_t addr) {
|
||||
wire.beginTransmission(addr);
|
||||
@@ -54,6 +57,7 @@ void SSD1306Display::startFrame(Color bkg) {
|
||||
|
||||
void SSD1306Display::setTextSize(int sz) {
|
||||
_text_sz = sz;
|
||||
_vw_dirty = true;
|
||||
display.setTextSize(sz);
|
||||
}
|
||||
|
||||
@@ -66,8 +70,18 @@ void SSD1306Display::setCursor(int x, int y) {
|
||||
display.setCursor(x, y);
|
||||
}
|
||||
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
uint8_t SSD1306Display::glyphXAdvance(uint32_t cp) {
|
||||
return miscFixedXAdvance(cp, _text_sz);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SSD1306Display::print(const char* str) {
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
miscFixedPrint(display, str, _text_sz, _color);
|
||||
#else
|
||||
display.print(str);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SSD1306Display::fillRect(int x, int y, int w, int h) {
|
||||
@@ -83,10 +97,14 @@ void SSD1306Display::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
|
||||
}
|
||||
|
||||
uint16_t SSD1306Display::getTextWidth(const char* str) {
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
return miscFixedTextWidth(str, _text_sz);
|
||||
#else
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
|
||||
return w;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SSD1306Display::endFrame() {
|
||||
|
||||
@@ -23,6 +23,11 @@ class SSD1306Display : public DisplayDriver {
|
||||
RefCountedDigitalPin* _peripher_power;
|
||||
|
||||
bool i2c_probe(TwoWire& wire, uint8_t addr);
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
// Thin wrapper over the shared misc-fixed renderer (MiscFixedRenderer.h), kept
|
||||
// out of this header so the font tables land in one translation unit only.
|
||||
uint8_t glyphXAdvance(uint32_t cp);
|
||||
#endif
|
||||
public:
|
||||
SSD1306Display(RefCountedDigitalPin* peripher_power=NULL) : DisplayDriver(128, 64),
|
||||
display(128, 64, &Wire, PIN_OLED_RESET),
|
||||
@@ -33,10 +38,29 @@ public:
|
||||
bool begin();
|
||||
|
||||
int getCharWidth() const override { return 6 * _text_sz; }
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
// Same misc-fixed 6x9 font the SH1106 and e-ink drivers use: full
|
||||
// Latin/Greek/Cyrillic instead of the built-in font's ASCII-with-blocks, so
|
||||
// the keyboard's alphabets and accented names render as themselves. Opt-in
|
||||
// per variant (the font costs ~14 KB of flash) — see the boards that set
|
||||
// OLED_MISC_FIXED_FONT in their platformio.ini.
|
||||
int getLineHeight() const override { return 9 * _text_sz; } // 6x9 box height
|
||||
// The font's own glyph table is ink-tight, unlike the built-in font (below).
|
||||
int textWidthTrailingGap() const override { return 0; }
|
||||
bool isSingleFont() const override { return true; }
|
||||
void setSingleFont(bool) override { } // single-font: ignore toggles
|
||||
void translateUTF8ToBlocks(char* dest, const char* src, size_t dest_size) override {
|
||||
// No transliteration needed — print() renders UTF-8 directly.
|
||||
strncpy(dest, src, dest_size - 1);
|
||||
dest[dest_size - 1] = '\0';
|
||||
}
|
||||
uint16_t getCodepointWidth(uint32_t cp) override { return glyphXAdvance(cp); }
|
||||
#else
|
||||
int getLineHeight() const override { return 8 * _text_sz; }
|
||||
// Classic Adafruit_GFX built-in font pads every measured string by one
|
||||
// trailing advance column (see DisplayDriver::textWidthTrailingGap()).
|
||||
int textWidthTrailingGap() const override { return 1; }
|
||||
#endif
|
||||
|
||||
bool isOn() override { return _isOn; }
|
||||
void turnOn() override;
|
||||
|
||||
@@ -135,6 +135,10 @@ build_flags =
|
||||
-D UI_HAS_JOYSTICK_UPDOWN=1
|
||||
-D ADVERT_NAME='"@@MAC"'
|
||||
-D ENV_INCLUDE_GPS=1
|
||||
; misc-fixed 6x9 OLED font (full Latin/Greek/Cyrillic), same as the Wio L1
|
||||
; builds. Solo only: it costs ~14 KB of flash and the repeater/companion
|
||||
; builds have no keyboard to type those alphabets on.
|
||||
-D OLED_MISC_FIXED_FONT=1
|
||||
-Os
|
||||
; QSPIFLASH disabled: QSPI IO0/IO2 pins (P0.30/P0.28) conflict with joystick LEFT/UP
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
@@ -173,6 +177,10 @@ build_flags =
|
||||
-D UI_HAS_JOYSTICK_UPDOWN=1
|
||||
-D ADVERT_NAME='"@@MAC"'
|
||||
-D ENV_INCLUDE_GPS=1
|
||||
; misc-fixed 6x9 OLED font (full Latin/Greek/Cyrillic), same as the Wio L1
|
||||
; builds. Solo only: it costs ~14 KB of flash and the repeater/companion
|
||||
; builds have no keyboard to type those alphabets on.
|
||||
-D OLED_MISC_FIXED_FONT=1
|
||||
-D DUAL_SERIAL=1
|
||||
-D ENABLE_SCREENSHOT
|
||||
-Os
|
||||
|
||||
Reference in New Issue
Block a user