├── .gitignore ├── .gitmodules ├── LISENCE ├── README.md ├── data └── main.lua ├── lib ├── chrscreen │ ├── chrscreen.cpp │ └── chrscreen.h ├── dicttool │ ├── dicttool.cpp │ └── dicttool.h ├── editor │ ├── editor.cpp │ └── editor.h ├── fep │ ├── fep.cpp │ └── fep.h ├── luaexec │ ├── luaexec.cpp │ └── luaexec.h ├── luashell │ ├── luashell.cpp │ └── luashell.h ├── shell │ ├── shell.cpp │ └── shell.h ├── task │ ├── task.cpp │ └── task.h └── util │ ├── util.cpp │ └── util.h ├── platformio.ini ├── src └── main.ino └── test └── test_native ├── test_chrscreen.h ├── test_editor.cpp └── test_util.h /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/lua"] 2 | path = lib/lua 3 | url = https://github.com/lua/lua.git 4 | -------------------------------------------------------------------------------- /LISENCE: -------------------------------------------------------------------------------- 1 | Copyright 2021 inajob( http://twitter.com/ina_ani ) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32 hid keyboard editor 2 | 3 | M5StackにUSB Host Shieldを接続し、キーボードをつなげた状態で動作するエディタです。 4 | 5 | エディタのコア部分をlibに分離してあるので、例えばCardKBなどに対応させるのは簡単だと思います。 6 | 7 | 8 | 絶賛作業中です。 9 | 10 | ローマ字入力により日本語が入力できたりします。 11 | 12 | ## Memo 13 | 14 | ### USB Host Shieldとの接続 15 | 16 | - https://okiraku-camera.tokyo/blog/?p=8333 17 | - USB Host Shieldに改造が必要です。 18 | - 改造が面倒な場合は、[M5Stack用MAX3421E搭載 USBモジュール](https://amzn.to/3D4Y7tX)を使うと良いです。 19 | 20 | 21 | ### Setup dictionary 22 | 23 | http://openlab.ring.gr.jp/skk/dic-ja.html から辞書をダウンロードしてください。 24 | 25 | 辞書が大きいと、かな漢字変換が非常に遅くなるので「SKK-JISYO.S」がおススメです。 26 | 27 | ファイルの中身をUTF-8、改行コードLFに変換したうえで、`data/SKK-JISYO.S.txt`に保存してください。 28 | 29 | 下記コマンドラインで辞書をSPIFFSに格納してください。 30 | 31 | ``` 32 | $ platformio run -e esp32 --target uploadfs 33 | ``` 34 | 35 | ### Build and upload 36 | 37 | ``` 38 | $ platformio run -e esp32 --target upload 39 | ``` 40 | 41 | ### Test 42 | 43 | ``` 44 | $ platformio test -e native 45 | ``` 46 | -------------------------------------------------------------------------------- /data/main.lua: -------------------------------------------------------------------------------- 1 | putstring("hello from lua!") 2 | putstring("hello from lua!") 3 | 4 | fileName = "/test.txt" 5 | history = {} 6 | lines = {""} 7 | topRow = 1; 8 | row = 1 9 | col = 1 10 | statusLine = "" 11 | yank = "" 12 | 13 | -- below line is `mode` 14 | -- below line 2 is `henkan` 15 | lastRow = getmaxline() - 3 16 | 17 | globalKey = 0 18 | globalCode = "" 19 | fontH = 12 20 | fontW = 6 21 | 22 | function draw() 23 | setcursor(0, 0) 24 | -- redraw all lines 25 | cursorY = 0 26 | cursorX = 0 27 | posY = 0 28 | posX = 0 29 | for i, line in pairs(lines) do 30 | if i < topRow then 31 | goto continue 32 | end 33 | if i - topRow > lastRow then 34 | goto continue 35 | end 36 | setcursor(posX, posY) 37 | -- clear line 38 | setcolor(0,0,0, 0,0,0); 39 | fillrect(0, posY, 320, fontH) 40 | setcolor(255,255,255, 0,0,0); 41 | -- draw string 42 | count = 0; 43 | hit = false 44 | for p, cc in utf8.codes(line) do 45 | c = utf8.char(cc) 46 | if i == row and count +1 == col then 47 | setcolor(0,0,0, 0,255,0); 48 | hit = true 49 | putstring(c) 50 | setcolor(255,255,255, 0,0,0); 51 | cursorX = posX 52 | cursorY = posY 53 | else 54 | putstring(c) 55 | end 56 | posX = posX + gettextwidth(c) 57 | if posX > 320 - fontW*2 then -- fold 58 | posX = 0 59 | posY = posY + fontH 60 | setcursor(posX, posY) 61 | -- clear line 62 | setcolor(0,0,0, 0,0,0); 63 | fillrect(0, posY, 320, fontH) 64 | setcolor(255,255,255, 0,0,0); 65 | end 66 | count = count + 1 67 | end 68 | if i == row and not(hit) then 69 | setcolor(0,0,0, 0,255,0); 70 | putstring("*") 71 | setcolor(255,255,255, 0,0,0); 72 | cursorX = posX 73 | cursorY = posY 74 | end 75 | posY = posY + fontH 76 | posX = 0; 77 | ::continue:: 78 | end 79 | setcolor(0,0,0, 0,0,0); 80 | fillrect(0, posY, 320, fontH) 81 | setcolor(255,255,255, 0,0,0); 82 | 83 | -- draw status line for heap 84 | setcolor(0,0,255, 0,0,0); 85 | fillrect(0, (lastRow - 1)*fontH, 320, fontH) 86 | setcolor(255,255,255, 0,0,255); 87 | setcursor(0, (lastRow-1)*fontH) 88 | putstring("heap: " .. getfreeheap()) 89 | putstring(" " .. statusLine) 90 | -- draw status line for char 91 | fillrect(0, (lastRow)*fontH, 320, fontH) 92 | setcolor(0,0,0, 0,0,255); 93 | setcursor(0, (lastRow)*fontH) 94 | putstring("key: ") 95 | putstring(globalKey) 96 | putstring(",code of c: ") 97 | if globalCode then 98 | putstring("" .. globalCode) 99 | else 100 | putstring("nil") 101 | end 102 | putstring(",row " .. row) 103 | putstring(",cow " .. col) 104 | putstring(",topRow " .. topRow) 105 | putstring(",fileName " .. fileName) 106 | 107 | -- setcursor for fep 108 | setcursor(cursorX, cursorY) 109 | end 110 | 111 | function getCharAt(s, i) 112 | count = 0 113 | for p, c in utf8.codes(s) do 114 | if count == i then 115 | return utf8.char(c) 116 | end 117 | count = count + 1 118 | end 119 | return "" 120 | end 121 | 122 | function insertChar(s, c, i) 123 | newLine = "" 124 | count = 0 125 | hit = false 126 | for p, lc in utf8.codes(s) do 127 | if count == i - 1 then 128 | newLine = newLine .. c 129 | hit = true 130 | end 131 | newLine = newLine .. utf8.char(lc) 132 | count = count + 1 133 | end 134 | if not(hit) then 135 | newLine = newLine .. c 136 | end 137 | return newLine 138 | end 139 | 140 | function removeChar(s, i) 141 | newLine = "" 142 | count = 1 143 | for p, lc in utf8.codes(s) do 144 | if count ~= i then 145 | newLine = newLine .. utf8.char(lc) 146 | end 147 | count = count + 1 148 | end 149 | return newLine 150 | end 151 | 152 | function saveFile(fn) 153 | savefile(fn, table.concat(lines, "\n")) 154 | end 155 | function loadFile(fn) 156 | if exists(fn) then 157 | body = readfile(fn) 158 | lines = {} 159 | line = "" 160 | for p, lc in utf8.codes(body) do 161 | c = utf8.char(lc) 162 | if c == "\n" then 163 | lines[#lines + 1] = line 164 | line = "" 165 | goto continue 166 | end 167 | line = line .. c 168 | ::continue:: 169 | end 170 | lines[#lines + 1] = line 171 | statusLine = "load " .. fileName 172 | else 173 | lines = {""} 174 | col = 1 175 | row = 1 176 | statusLine = "new " .. fileName 177 | end 178 | topRow = 1 179 | col = 1 180 | row = 1 181 | setcolor(0,0,0, 0,0,0) 182 | fillrect(0, 0, 320, 240) 183 | 184 | end 185 | 186 | function keydown(key, c, ctrl) 187 | code = string.byte(c) 188 | globalKey = key 189 | globalCode = code 190 | if ctrl then 191 | if c == "s" then 192 | saveFile(fileName) 193 | statusLine = "saved " .. fileName 194 | elseif c == "c" then 195 | statusLine = "check" .. exists(fileName) 196 | elseif c == "x" then 197 | exit() 198 | statusLine = "exit" 199 | elseif c == "l" then 200 | loadFile(fileName) 201 | elseif c == "k" then 202 | yank = lines[row] 203 | table.remove(lines, row) 204 | elseif c == "u" then 205 | table.insert(lines, row, yank) 206 | elseif c == "g" then 207 | -- seek wiki name 208 | nextFile = "" 209 | hit = false 210 | found = false 211 | count = 0 212 | for p, cc in utf8.codes(lines[row]) do 213 | c = utf8.char(cc) 214 | if hit then 215 | if count == col then 216 | found = true 217 | end 218 | if c == "[" then 219 | found = false 220 | break 221 | elseif c == "]" then 222 | hit = false 223 | if found then 224 | break 225 | else 226 | nextFile = "" 227 | end 228 | else 229 | nextFile = nextFile .. c 230 | end 231 | else 232 | if c == "[" then 233 | hit = true 234 | end 235 | end 236 | count = count + 1 237 | end 238 | if found then 239 | statusLine = "next file is: " .. nextFile 240 | debug("next file is: " .. nextFile) 241 | table.insert(history, fileName) 242 | fileName = "/" .. nextFile .. ".txt" 243 | loadFile(fileName) 244 | end 245 | elseif c == "b" then 246 | if #history > 0 then 247 | fileName = history[#history] 248 | table.remove(history) 249 | loadFile(fileName) 250 | end 251 | end 252 | draw() 253 | return 254 | end 255 | if code == 13 then 256 | newLine = "" 257 | newLineNext = "" 258 | count = 1 259 | for p, lc in utf8.codes(lines[row]) do 260 | if count < col then 261 | newLine = newLine .. utf8.char(lc) 262 | else 263 | newLineNext = newLineNext .. utf8.char(lc) 264 | end 265 | count = count + 1 266 | end 267 | lines[row] = newLine 268 | table.insert(lines, row + 1, "") 269 | row = row + 1 270 | lines[row] = newLineNext 271 | if row - topRow > lastRow - 2 then 272 | topRow = topRow + 1 273 | end 274 | col = 1 275 | elseif key == 41 then -- ESC 276 | collectgarbage() 277 | elseif key == 82 then -- Up 278 | if row > 1 then 279 | row = row - 1 280 | if col >= utf8.len(lines[row]) then 281 | col = utf8.len(lines[row]) + 1 282 | end 283 | end 284 | if row < topRow then 285 | topRow = topRow - 1 286 | end 287 | elseif key == 81 then -- Down 288 | if row < #lines then 289 | row = row + 1 290 | if col >= utf8.len(lines[row]) then 291 | col = utf8.len(lines[row]) + 1 292 | end 293 | end 294 | if row - topRow > lastRow - 4 then 295 | topRow = topRow + 1 296 | end 297 | 298 | elseif key == 80 then -- Left 299 | if col == 1 then 300 | if row > 1 then 301 | row = row - 1 302 | col = utf8.len(lines[row]) + 1 303 | end 304 | else 305 | col = col - 1 306 | end 307 | elseif key == 79 then -- Right 308 | if col == utf8.len(lines[row]) + 1 and row < #lines then 309 | col = 1 310 | row = row + 1 311 | else 312 | col = col + 1 313 | end 314 | elseif key == 42 then -- BS 315 | if col == 1 then 316 | if row > 1 then 317 | -- merge line 318 | nowLine = lines[row] 319 | table.remove(lines, row) 320 | row = row - 1 321 | col = utf8.len(lines[row]) + 1 322 | lines[row] = lines[row] .. nowLine 323 | end 324 | else 325 | col = col - 1 326 | lines[row] = removeChar(lines[row], col) 327 | end 328 | else 329 | if code and 32 <= code and code <= 126 then 330 | lines[row] = insertChar(lines[row], c, col) 331 | col = col + 1 332 | end 333 | end 334 | draw() 335 | end 336 | 337 | function onChar(c) 338 | lines[row] = insertChar(lines[row], c, col) 339 | col = col + 1 340 | draw() 341 | end 342 | 343 | draw() 344 | -------------------------------------------------------------------------------- /lib/chrscreen/chrscreen.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // to use utf16CharToUtf8 4 | #include 5 | 6 | // w means the number of half characters 7 | void ChrScreen::init(int w, int h){ 8 | vector> ::iterator line; 9 | screenWidth = w; 10 | cursorCol = 0; 11 | cursorColPos = 0; 12 | cursorLine = 0; 13 | 14 | // create character buffer 15 | for(int i = 0; i < h/(fontPointH * fontScale); i ++){ 16 | line = lines.begin(); 17 | lines.insert(line, vector(w/(fontPointW * fontScale))); 18 | } 19 | } 20 | #ifdef ESP32 21 | void ChrScreen::draw(LGFX lgfx){ 22 | vector>::iterator itr; 23 | vector::iterator itr2; 24 | lgfx.setTextSize(fontScale, fontScale); 25 | int x = 0; 26 | int y = 0; 27 | 28 | // draw character screen 29 | int lineNo = 0; 30 | int colNo = 0; 31 | for(itr = lines.begin(); itr != lines.end(); itr ++){ 32 | colNo = 0; 33 | for(itr2 = itr->begin(); itr2 != itr->end(); itr2 ++){ 34 | char utf8[4]; 35 | if(itr2->w == 0){ // null character 36 | if(itr2->dirty){ 37 | lgfx.fillRect(x, y, fontPointW * fontScale, fontPointH * fontScale, itr2->bg); 38 | itr2->dirty = false; 39 | } 40 | x += fontPointW * fontScale; 41 | }else{ // not null character 42 | utf16CharToUtf8(itr2->w, utf8); // TODO: move from editor to util 43 | 44 | if(itr2->dirty){ 45 | if(lineNo == cursorLine && colNo == cursorCol){ 46 | // draw cursor 47 | lgfx.drawRect(x, y, fontPointW * fontScale, fontPointH * fontScale, itr2->fg); 48 | } 49 | // draw char 50 | lgfx.drawRect(x, y, fontPointW * fontScale, fontPointH * fontScale, itr2->bg); 51 | lgfx.setTextColor(itr2->fg, itr2->bg); 52 | lgfx.drawString(utf8, x, y); 53 | itr2->dirty = false; 54 | } 55 | 56 | if(isAscii(itr2->w)){ // TODO: move from editor to util 57 | x += fontPointW * fontScale; 58 | }else{ 59 | x += fontPointW * 2 * fontScale; 60 | } 61 | if(x > screenWidth){ // not support fold 62 | break; 63 | } 64 | } 65 | colNo ++; 66 | } 67 | x = 0; 68 | y += fontPointH * fontScale; 69 | lineNo ++; 70 | } 71 | } 72 | #endif 73 | 74 | void ChrScreen::setCursor(int col, int line){ 75 | cursorCol = col; 76 | cursorLine = line; 77 | // TODO: cursorColPos 78 | } 79 | 80 | void ChrScreen::putString(wchar_t* wp, int16_t fg, int16_t bg){ 81 | while(*wp != 0){ 82 | putChar(*wp, fg, bg); 83 | wp ++; 84 | } 85 | } 86 | 87 | void ChrScreen::putString(char* cp, int16_t fg, int16_t bg){ 88 | while(*cp != 0){ 89 | putChar(*cp, fg, bg); 90 | cp ++; 91 | } 92 | } 93 | 94 | void ChrScreen::putChar(wchar_t w, int16_t fg, int16_t bg){ 95 | Chr c = {w, (uint16_t)fg, (uint16_t)bg, true}; 96 | //if(cursorColPos + getCharSize(w) > screenWidth){ // folding 97 | // cursorColPos = 0; 98 | // cursorCol = 0; 99 | // cursorLine ++; 100 | //} 101 | if(cursorLine >= lines.size() || cursorCol >= lines.at(cursorLine).size()){ 102 | return; 103 | } 104 | 105 | lines.at(cursorLine).at(cursorCol) = c; 106 | cursorCol ++; 107 | cursorColPos += getCharSize(w); 108 | } 109 | void ChrScreen::back(){ 110 | if(cursorCol > 0){ 111 | cursorCol --; 112 | } 113 | } 114 | void ChrScreen::nextLine(){ 115 | cursorCol = 0; 116 | cursorColPos = 0; 117 | cursorLine ++; 118 | if(cursorLine >= lines.size()){ 119 | cursorLine = 0; 120 | } 121 | } 122 | void ChrScreen::clearLine(int line, int16_t fg, int16_t bg){ 123 | Chr c = {0, (uint16_t)fg, (uint16_t)bg, true}; 124 | vector::iterator itr2; 125 | for(itr2 = lines.at(line).begin(); itr2 != lines.at(line).end(); itr2++){ 126 | *itr2 = c; 127 | } 128 | } 129 | void ChrScreen::resetLine(int line){ 130 | Chr c = {0, 0, 0, false}; // not dirty! 131 | vector::iterator itr2; 132 | for(itr2 = lines.at(line).begin(); itr2 != lines.at(line).end(); itr2++){ 133 | *itr2 = c; 134 | } 135 | } 136 | 137 | wchar_t ChrScreen::getChar(int col, int line){ 138 | return lines.at(line).at(col).w; 139 | } 140 | int ChrScreen::getCharSize(wchar_t w){ 141 | if(isAscii(w)){ 142 | return fontPointW * fontScale; 143 | }else{ 144 | return fontPointW * 2 * fontScale; 145 | } 146 | } 147 | int ChrScreen::getMaxLine(){ 148 | return lines.size(); 149 | } 150 | int ChrScreen::getMaxColumn(){ 151 | return screenWidth / fontPointW; 152 | } 153 | -------------------------------------------------------------------------------- /lib/chrscreen/chrscreen.h: -------------------------------------------------------------------------------- 1 | using namespace std; 2 | #include 3 | #include 4 | 5 | #ifdef ESP32 6 | #include 7 | #define LGFX_M5STACK 8 | #include 9 | #endif 10 | 11 | #ifndef CHRSCREEN_H 12 | #define CHRSCREEN_H 13 | 14 | typedef struct { 15 | wchar_t w; 16 | uint16_t fg; 17 | uint16_t bg; 18 | bool dirty; 19 | } Chr; 20 | 21 | class ChrScreen { 22 | const int fontScale = 1; 23 | const int fontPointH = 12; 24 | const int fontPointW = 6; 25 | int screenWidth = 320; 26 | 27 | public: 28 | vector> lines; 29 | int cursorCol = 0; // wchar_t positoin 30 | int cursorColPos = 0; // size position 31 | int cursorLine = 0; 32 | void init(int w, int h); 33 | #ifdef ESP32 34 | void draw(LGFX lgfx); 35 | #endif 36 | void setCursor(int col, int line); 37 | void putString(wchar_t* w, int16_t fg, int16_t bg); 38 | void putString(char* w, int16_t fg, int16_t bg); 39 | void putChar(wchar_t w, int16_t fg, int16_t bg); 40 | void back(); 41 | void nextLine(); 42 | void clearLine(int line, int16_t fg, int16_t bg); 43 | void resetLine(int line); 44 | wchar_t getChar(int col, int line); 45 | int getCharSize(wchar_t w); 46 | int getMaxLine(); 47 | int getMaxColumn(); 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /lib/dicttool/dicttool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void search(char* target, vector* results, char* path){ 4 | #ifdef ESP32 5 | File fp; 6 | #else 7 | FILE* fp; 8 | #endif 9 | const size_t line_size = 1024; 10 | char line[1024]; 11 | char* linePtr = line; // TODO: more smart way? 12 | char* targetPtr = target; 13 | #ifdef ESP32 14 | fp = SPIFFS.open(path, "r"); 15 | #else 16 | fp = fopen(path, "r"); 17 | #endif 18 | int i = 0; 19 | // TODO: check fp is null; 20 | #ifdef ESP32 21 | while(fp.available()){ 22 | String lineStr = fp.readStringUntil('\n'); 23 | char* line = (char*)lineStr.c_str(); 24 | #else 25 | while(fgets(line, line_size, fp) != NULL) { 26 | #endif 27 | i++; 28 | linePtr = line; 29 | if(*linePtr == ';'){ // skip comment 30 | continue; 31 | } 32 | targetPtr = target; 33 | bool hit = false; 34 | while(*linePtr != 0){ 35 | if(*linePtr == ' '){ // end of okuri-ari 36 | if(*targetPtr == 0){ 37 | hit = true; 38 | } 39 | break; 40 | } 41 | if(*linePtr != *targetPtr){ 42 | break; 43 | } 44 | linePtr ++; 45 | targetPtr ++; 46 | } 47 | if(hit == false)continue; 48 | 49 | // here is hit 50 | linePtr ++; // skip ` ` 51 | linePtr ++; // skip first `/` 52 | 53 | while(*linePtr != 0 && *linePtr != '\r' && *linePtr != '\n'){ 54 | string result; 55 | 56 | while(*linePtr != '/'){ 57 | result += *linePtr; 58 | linePtr ++; 59 | } 60 | result += '\0'; 61 | results->push_back(result); 62 | linePtr ++; // skip `/` 63 | } 64 | break; 65 | } 66 | #ifdef ESP32 67 | fp.close(); 68 | #else 69 | fclose(fp); 70 | #endif 71 | } 72 | -------------------------------------------------------------------------------- /lib/dicttool/dicttool.h: -------------------------------------------------------------------------------- 1 | using namespace std; 2 | #include 3 | #include 4 | #ifdef ESP32 5 | #include 6 | #include 7 | #endif 8 | #include // for native test 9 | #include // for native test 10 | #include // for native test 11 | 12 | #ifndef DICTTOOL_H 13 | #define DICTTOOL_H 14 | 15 | void search(char* target, vector* results, char* path); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /lib/editor/editor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Editor::init(){ 4 | lines.clear(); 5 | line = lines.begin(); // lines 6 | line = lines.insert(line, vector()); 7 | colItr = line->begin(); // line 8 | } 9 | 10 | void Editor::onkeydown(char key, char c, bool ctrl){ 11 | switch(key){ 12 | case 0x2A: // BS 13 | backSpace(); 14 | break; 15 | case 0x50: // <- 16 | left(); 17 | break; 18 | case 0x4F: // -> 19 | right(); 20 | break; 21 | case 0x52: // ^ 22 | up(); 23 | break; 24 | case 0x51: // v 25 | down(); 26 | break; 27 | default: 28 | if(c == '\r'){ 29 | enter(); 30 | }else{ 31 | onChar(c); 32 | } 33 | } 34 | } 35 | 36 | void Editor::backSpace(){ 37 | if(colItr != line->begin()){ 38 | colItr --; 39 | colItr = line->erase(colItr); 40 | }else{ 41 | if(line == lines.begin())return; 42 | // marge lines 43 | vector ::iterator beginItr = colItr; 44 | vector ::iterator endItr = line->end(); 45 | vector> ::iterator prevLine = line; 46 | 47 | line --; 48 | int pos = line->end() - line->begin(); 49 | copy(beginItr, endItr, back_inserter(*line)); 50 | line = lines.erase(prevLine); 51 | line --; 52 | colItr = line->begin() + pos; 53 | //lines.shrink_to_fit(); 54 | } 55 | } 56 | 57 | void Editor::left(){ 58 | if(colItr != line->begin()){ 59 | colItr --; 60 | } 61 | } 62 | 63 | void Editor::right(){ 64 | if(colItr != line->end()){ 65 | colItr ++; 66 | } 67 | } 68 | 69 | void Editor::up(){ 70 | if(line != lines.begin()){ 71 | int pos = colItr - line->begin(); 72 | 73 | line --; 74 | colItr = line->begin(); 75 | for(;pos != 0 && colItr != line->end(); pos--){ 76 | colItr ++; 77 | } 78 | } 79 | } 80 | 81 | void Editor::down(){ 82 | if(line + 1 != lines.end()){ 83 | int pos = colItr - line->begin(); 84 | 85 | line ++; 86 | colItr = line->begin(); 87 | for(;pos != 0 && colItr != line->end(); pos--){ 88 | colItr ++; 89 | } 90 | } 91 | } 92 | 93 | void Editor::enter(){ 94 | vector newLine; 95 | 96 | copy(colItr, line->end(), back_inserter(newLine)); 97 | while(colItr != line->end()){ 98 | colItr = line->erase(colItr); 99 | } 100 | 101 | line ++; 102 | line = lines.insert(line, newLine); 103 | 104 | colItr = line->begin(); 105 | } 106 | 107 | void Editor::onChar(wchar_t c){ 108 | colItr = line->insert(colItr, c); 109 | colItr ++; 110 | } 111 | 112 | void Editor::load(){ 113 | #ifdef ESP32 114 | init(); 115 | File fp; 116 | fp = SPIFFS.open(filename , "r"); 117 | while(fp.available()){ 118 | char c1 = fp.read(); 119 | char c2 = fp.read(); 120 | printf("load %x,%x\n", c1, c2); 121 | wchar_t w = (c1 << 8) | (c2); 122 | if(c1 == 0 && c2 == '\n'){ 123 | enter(); 124 | }else{ 125 | onChar(w); 126 | } 127 | } 128 | fp.close(); 129 | #endif 130 | } 131 | void Editor::save(){ 132 | #ifdef ESP32 133 | vector>::iterator itr; 134 | vector::iterator itr2; 135 | 136 | File fp; 137 | fp = SPIFFS.open(filename , "w"); 138 | for(itr = lines.begin(); itr != lines.end(); itr ++){ 139 | for(itr2 = itr->begin(); itr2 != itr->end(); itr2 ++){ 140 | char c1 = (*itr2 >> 8); 141 | char c2 = *itr2 & 0xff; 142 | printf("save %x,%x\n", c1, c2); 143 | fp.write(0xff & c1); // save as UTF16 144 | fp.write(0xff & c2); // save as UTF16 145 | } 146 | fp.write(0x00); 147 | fp.write('\n'); 148 | } 149 | fp.close(); 150 | #endif 151 | } 152 | 153 | wchar_t KanjiEditor::table[][5] = { 154 | {L'あ',L'い',L'う',L'え',L'お'}, 155 | {L'か',L'き',L'く',L'け',L'こ'}, 156 | {L'さ',L'し',L'す',L'せ',L'そ'}, 157 | {L'た',L'ち',L'つ',L'て',L'と'}, 158 | {L'な',L'に',L'ぬ',L'ね',L'の'}, 159 | {L'は',L'ひ',L'ふ',L'へ',L'ほ'}, 160 | {L'ま',L'み',L'む',L'め',L'も'}, 161 | {L'や',L'い',L'ゆ',L'え',L'よ'}, 162 | {L'ら',L'り',L'る',L'れ',L'ろ'}, 163 | {L'わ',L'い',L'う',L'え',L'を'}, 164 | {L'が',L'ぎ',L'ぐ',L'げ',L'ご'}, 165 | {L'ざ',L'じ',L'ず',L'ぜ',L'ぞ'}, 166 | {L'だ',L'ぢ',L'づ',L'で',L'ど'}, 167 | {L'ば',L'び',L'ぶ',L'べ',L'ぼ'}, 168 | {L'ぱ',L'ぴ',L'ぷ',L'ぺ',L'ぽ'}, 169 | {L'ゃ',L'ぃ',L'ゅ',L'ぇ',L'ょ'}, 170 | {L'っ',L'ん',L'-',L'-',L'-'}, // 16 171 | {L'ぁ',L'ぃ',L'ぅ',L'ぇ',L'ぉ'} 172 | }; 173 | wchar_t KanjiEditor::kata_table[][5] = { 174 | {L'ア',L'イ',L'ウ',L'エ',L'オ'}, 175 | {L'カ',L'キ',L'ク',L'ケ',L'コ'}, 176 | {L'サ',L'シ',L'ス',L'セ',L'ソ'}, 177 | {L'タ',L'チ',L'ツ',L'テ',L'ト'}, 178 | {L'ナ',L'ニ',L'ヌ',L'ネ',L'ノ'}, 179 | {L'ハ',L'ヒ',L'フ',L'ヘ',L'ホ'}, 180 | {L'マ',L'ミ',L'ム',L'メ',L'モ'}, 181 | {L'ヤ',L'イ',L'ユ',L'エ',L'ヨ'}, 182 | {L'ラ',L'リ',L'ル',L'レ',L'ロ'}, 183 | {L'ワ',L'イ',L'ウ',L'エ',L'ヲ'}, 184 | {L'ガ',L'ギ',L'グ',L'ゲ',L'ゴ'}, 185 | {L'ザ',L'ジ',L'ズ',L'ゼ',L'ゾ'}, 186 | {L'ダ',L'ヂ',L'ヅ',L'デ',L'ド'}, 187 | {L'バ',L'ビ',L'ブ',L'ベ',L'ボ'}, 188 | {L'パ',L'ピ',L'プ',L'ペ',L'ポ'}, 189 | {L'ャ',L'ィ',L'ュ',L'ェ',L'ョ'}, 190 | {L'ッ',L'ン',L'-',L'-',L'-'}, 191 | {L'ァ',L'ィ',L'ゥ',L'ェ',L'ォ'} 192 | }; 193 | 194 | bool isAscii(wchar_t utf16){ 195 | return utf16 >= 0 && utf16 <= 0x7e; 196 | } 197 | 198 | uint8_t utf16CharToUtf8(wchar_t utf16, char* utf8){ 199 | uint8_t len = 0; 200 | if (utf16 < 128) { 201 | utf8[0] = char(utf16); 202 | utf8[1] = 0; 203 | utf8[2] = 0; 204 | len = 1; 205 | } else if (utf16 < 2048) { 206 | utf8[0] = 0xC0 | char(utf16 >> 6); 207 | utf8[1] = 0x80 | (char(utf16) & 0x3F); 208 | utf8[2] = 0; 209 | len = 2; 210 | } else if (utf16 < 65536) { 211 | utf8[0] = 0xE0 | char(utf16 >> 12); 212 | utf8[1] = 0x80 | (char(utf16 >> 6) & 0x3F); 213 | utf8[2] = 0x80 | (char(utf16) & 0x3F); 214 | utf8[3] = 0; 215 | len = 3; 216 | } 217 | return len; 218 | } 219 | 220 | int GetUtf8ByteCount(char c) { 221 | if (0 <= uint8_t(c) && uint8_t(c) < 0x80) { 222 | return 1; 223 | } 224 | if (0xC2 <= uint8_t(c) && uint8_t(c) < 0xE0) { 225 | return 2; 226 | } 227 | if (0xE0 <= uint8_t(c) && uint8_t(c) < 0xF0) { 228 | return 3; 229 | } 230 | if (0xF0 <= uint8_t(c) && uint8_t(c) < 0xF8) { 231 | return 4; // not support yet 232 | } 233 | return 0; 234 | } 235 | 236 | size_t utf8CharToUtf16(char* utf8, wchar_t* utf16){ 237 | int numBytes = GetUtf8ByteCount(utf8[0]); 238 | if (numBytes == 0) { 239 | return 0; 240 | } 241 | switch (numBytes) { 242 | case 1: 243 | *utf16 = wchar_t(uint8_t(utf8[0])); 244 | break; 245 | case 2: 246 | if ((uint8_t(utf8[0]) & 0x1E) == 0) { 247 | return 0; 248 | } 249 | 250 | *utf16 = wchar_t(utf8[0] & 0x1F) << 6; 251 | *utf16 |= wchar_t(utf8[1] & 0x3F); 252 | break; 253 | case 3: 254 | if ((uint8_t(utf8[0]) & 0x0F) == 0 && 255 | (uint8_t(utf8[1]) & 0x20) == 0) { 256 | return 0; 257 | } 258 | 259 | *utf16 = wchar_t(utf8[0] & 0x0F) << 12; 260 | *utf16 |= wchar_t(utf8[1] & 0x3F) << 6; 261 | *utf16 |= wchar_t(utf8[2] & 0x3F); 262 | break; 263 | case 4: 264 | // not implement 265 | break; 266 | default: 267 | return 0; 268 | } 269 | 270 | return numBytes; 271 | } 272 | 273 | 274 | void KanjiEditor::init(){ 275 | Editor::init(); 276 | dictPath = "/SKK-JISYO.S.txt"; 277 | } 278 | void KanjiEditor::onkeydown(char key, char c, bool ctrl){ 279 | switch(key){ 280 | case 0x2A: // BS 281 | backSpace(); 282 | break; 283 | case 0x50: // <- 284 | left(); 285 | break; 286 | case 0x4F: // -> 287 | right(); 288 | break; 289 | case 0x52: // ^ 290 | up(); 291 | break; 292 | case 0x51: // v 293 | down(); 294 | break; 295 | default: 296 | if(c == '\r'){ 297 | enter(); 298 | }else if(c == 'x' && ctrl){ 299 | terminate(); 300 | filename[0] = 0; // todo: implement clearFileName 301 | }else{ 302 | onCharRoma(c,ctrl); 303 | } 304 | } 305 | } 306 | 307 | void KanjiEditor::backSpace(){ 308 | if(kanjiMode == KanjiMode::ROME || kanjiMode == KanjiMode::KATA || kanjiMode == KanjiMode::DIRECT){ 309 | if(shiin2 != 0){ 310 | shiin2 = 0; 311 | return; 312 | } 313 | if(shiin1 != 0){ 314 | shiin1 = 0; 315 | return; 316 | } 317 | Editor::backSpace(); 318 | }else if(kanjiMode == KanjiMode::KANJI){ 319 | if(rawInputsItr != rawInputs.begin()){ 320 | rawInputsItr --; 321 | rawInputsItr = rawInputs.erase(rawInputsItr); 322 | if(rawInputsItr == rawInputs.begin()){ 323 | kanjiMode = KanjiMode::ROME; 324 | } 325 | }else{ 326 | // cancel KANJI MODE 327 | kanjiMode = KanjiMode::ROME; 328 | } 329 | } 330 | } 331 | void KanjiEditor::right(){ 332 | if(kanjiMode == KanjiMode::HENKAN){ 333 | nextKanji(); 334 | }else{ 335 | Editor::right(); 336 | } 337 | } 338 | void KanjiEditor::left(){ 339 | if(kanjiMode == KanjiMode::HENKAN){ 340 | prevKanji(); 341 | }else{ 342 | Editor::left(); 343 | } 344 | } 345 | void KanjiEditor::up(){ 346 | Editor::up(); 347 | } 348 | void KanjiEditor::down(){ 349 | Editor::down(); 350 | } 351 | void KanjiEditor::enter(){ 352 | if(kanjiMode == KanjiMode::HENKAN){ 353 | kanjiDecide(); 354 | }else{ 355 | Editor::enter(); 356 | } 357 | } 358 | void KanjiEditor::onChar(wchar_t c){ 359 | if(kanjiMode == KanjiMode::ROME || kanjiMode == KanjiMode::KATA || kanjiMode == KanjiMode::DIRECT){ 360 | Editor::onChar(c); 361 | }else if(kanjiMode == KanjiMode::KANJI){ 362 | rawInputsItr = rawInputs.insert(rawInputsItr, c); 363 | rawInputsItr ++; 364 | } 365 | } 366 | 367 | wchar_t KanjiEditor::getKana(int r, int c){ 368 | if(kanjiMode == KanjiMode::KATA){ 369 | return kata_table[r][c]; 370 | } 371 | return table[r][c]; 372 | } 373 | void KanjiEditor::onCharRoma(uint8_t c, bool ctrl){ 374 | if(ctrl){ 375 | if(c == 'j'){ 376 | kanjiMode = KanjiMode::ROME; 377 | } 378 | if(c == 's'){ 379 | save(); 380 | } 381 | if(c == 'l'){ 382 | load(); 383 | } 384 | return; 385 | } 386 | if(kanjiMode == KanjiMode::DIRECT){ 387 | onChar(c); 388 | return; 389 | } 390 | if(kanjiMode == KanjiMode::HENKAN){ 391 | if(c != ' '){ 392 | kanjiDecide(); 393 | } 394 | }else if(kanjiMode == KanjiMode::KANJI){ 395 | if(c == ' '){ 396 | kanjiHenkan(); 397 | return; 398 | } 399 | } 400 | 401 | if(isalpha(c) && c == toupper(c)){ // uppar case 402 | if(kanjiMode == KanjiMode::ROME || kanjiMode == KanjiMode::KATA){ // start kanji mode 403 | setStartKanjiMode(); 404 | c = tolower(c); 405 | }else if(kanjiMode == KanjiMode::KANJI){ // last 1 char and henkan 406 | c = tolower(c); 407 | onChar(c); 408 | kanjiHenkan(); 409 | return; 410 | }else if(kanjiMode == KanjiMode::HENKAN){ // decide and next kanji 411 | kanjiDecide(); 412 | setStartKanjiMode(); 413 | c = tolower(c); 414 | } 415 | } 416 | 417 | switch(c){ 418 | case ' ': 419 | if(kanjiMode == KanjiMode::HENKAN){ 420 | nextKanji(); 421 | return; 422 | } 423 | onChar(c); 424 | break; 425 | case 'a': 426 | case 'i': 427 | case 'u': 428 | case 'e': 429 | case 'o': 430 | onBoin(c); 431 | break; 432 | case 'k': 433 | case 's': 434 | case 't': 435 | case 'n': 436 | case 'h': 437 | case 'm': 438 | case 'y': 439 | case 'r': 440 | case 'w': 441 | case 'g': 442 | case 'z': 443 | case 'd': 444 | case 'b': 445 | case 'p': 446 | case 'x': 447 | if(shiin1 == c){ 448 | if(shiin1 == 'n'){ 449 | onChar(getKana(16, 1)); // ん 450 | shiin1 = 0; 451 | shiin2 = 0; 452 | }else{ 453 | onChar(getKana(16, 0)); // っ 454 | } 455 | }else if(c == 'y' && shiin1 != 0){ 456 | shiin2 = 'y'; 457 | }else{ 458 | if(shiin1 == 'n'){ 459 | onChar(getKana(16, 1)); // ん 460 | } 461 | shiin1 = c; 462 | } 463 | break; 464 | case 'l': 465 | kanjiMode = KanjiMode::DIRECT; 466 | break; 467 | case 'q': 468 | kanjiMode = KanjiMode::KATA; 469 | break; 470 | default: onChar(c); 471 | } 472 | } 473 | 474 | void KanjiEditor::onBoin(uint8_t c){ 475 | uint8_t b = 0; 476 | uint8_t pb = 0; 477 | uint8_t s = 0; 478 | switch(c){ 479 | case 'a': b = 0; break; 480 | case 'i': b = 1; break; 481 | case 'u': b = 2; break; 482 | case 'e': b = 3; break; 483 | case 'o': b = 4; break; 484 | } 485 | switch(shiin1){ 486 | case 0: s = 0; break; 487 | case 'k': s = 1; break; 488 | case 's': s = 2; break; 489 | case 't': s = 3; break; 490 | case 'n': s = 4; break; 491 | case 'h': s = 5; break; 492 | case 'm': s = 6; break; 493 | case 'y': s = 7; break; 494 | case 'r': s = 8; break; 495 | case 'w': s = 9; break; 496 | case 'g': s = 10; break; 497 | case 'z': s = 11; break; 498 | case 'd': s = 12; break; 499 | case 'b': s = 13; break; 500 | case 'p': s = 14; break; 501 | case 'x': s = 17; break; 502 | 503 | } 504 | shiin1 = 0; 505 | switch(shiin2){ 506 | case 'y': 507 | pb = b; 508 | b = 1; 509 | onChar(getKana(s, b)); 510 | onChar(getKana(15, pb)); 511 | break; 512 | case 0: 513 | onChar(getKana(s, b)); 514 | } 515 | shiin2 = 0; 516 | } 517 | 518 | void KanjiEditor::setStartKanjiMode(){ 519 | kanjiMode = KanjiMode::KANJI; 520 | rawInputs.clear(); 521 | rawInputsItr = rawInputs.begin(); 522 | } 523 | 524 | void KanjiEditor::kanjiHenkan(){ 525 | kanjiMode = KanjiMode::HENKAN; 526 | char target[256]; // TODO: check length 527 | uint16_t pos = 0; 528 | for(vector::iterator itr = rawInputs.begin(); itr != rawInputs.end(); itr ++){ 529 | uint8_t len = utf16CharToUtf8(*itr, &target[pos]); 530 | pos += len; 531 | } 532 | target[pos] = 0; // null terminate 533 | 534 | search(target, &kanjiList, (char*)dictPath.c_str()); 535 | kanjiListItr = kanjiList.begin(); 536 | if(kanjiList.empty()){ 537 | kanjiDecide(); 538 | } 539 | } 540 | void KanjiEditor::kanjiDecide(){ 541 | kanjiMode = KanjiMode::ROME; 542 | if(!kanjiList.empty()){ 543 | const char* p = kanjiListItr->c_str(); 544 | while(*p != 0){ 545 | wchar_t utf16; 546 | size_t n = utf8CharToUtf16((char*)p, &utf16); 547 | onChar(utf16); 548 | p += n; 549 | } 550 | kanjiList.clear(); 551 | // copy tail alpha at rawInputs to line 552 | vector::iterator itr; 553 | for(itr = rawInputs.begin(); itr != rawInputs.end(); itr ++){ 554 | if(*itr < 128 && isalpha(char(*itr))){ 555 | printf("add char %c\n", char(*itr)); 556 | onCharRoma(char(*itr), false); 557 | } 558 | } 559 | }else{ 560 | // copy rawInputs to line 561 | vector::iterator itr; 562 | for(itr = rawInputs.begin(); itr != rawInputs.end(); itr ++){ 563 | if(*itr < 128 && isalpha(char(*itr))){ 564 | onCharRoma(char(*itr), false); 565 | }else{ 566 | onChar(*itr); 567 | } 568 | } 569 | } 570 | rawInputs.clear(); 571 | rawInputsItr = rawInputs.begin(); 572 | } 573 | 574 | void KanjiEditor::nextKanji(){ 575 | kanjiListItr ++; 576 | if(kanjiListItr == kanjiList.end()){ 577 | kanjiListItr = kanjiList.begin(); 578 | } 579 | } 580 | 581 | void KanjiEditor::prevKanji(){ 582 | if(kanjiListItr != kanjiList.begin()){ 583 | kanjiListItr --; 584 | }else{ 585 | kanjiListItr = kanjiList.end() - 1; 586 | } 587 | } 588 | 589 | void KanjiEditor::draw(){ 590 | // draw decided characters 591 | vector>::iterator itr; 592 | vector::iterator itr2; 593 | int x = 0, y = 0; 594 | int cursorX = 0, cursorY = 0; 595 | for(itr = lines.begin(); itr != lines.end(); itr ++){ 596 | chrScreen->setCursor(0, y); 597 | chrScreen->clearLine(y, TFT_WHITE, TFT_BLUE); 598 | chrScreen->putChar((wchar_t)('0' + y/10), TFT_WHITE, TFT_BLACK); 599 | chrScreen->putChar((wchar_t)('0' + y%10), TFT_WHITE, TFT_BLACK); 600 | chrScreen->putChar(L' ', TFT_WHITE, TFT_BLACK); 601 | x += 3; 602 | 603 | for(itr2 = itr->begin(); itr2 != itr->end(); itr2 ++){ 604 | if(itr2 == colItr){ // cursor 605 | chrScreen->putChar(*itr2, TFT_BLACK, TFT_WHITE); 606 | cursorX = x; 607 | cursorY = y; 608 | }else{ 609 | chrScreen->putChar(*itr2, TFT_WHITE, TFT_BLACK); 610 | } 611 | x ++; 612 | } 613 | if(line == itr && itr->end() == colItr){ // cursor 614 | chrScreen->putChar(0, TFT_BLACK, TFT_WHITE); 615 | cursorX = x; 616 | cursorY = y; 617 | } 618 | x = 0; 619 | y ++; 620 | if(y == chrScreen->getMaxLine() - 2){ 621 | break; 622 | } 623 | } 624 | chrScreen->clearLine(y, TFT_WHITE, TFT_BLACK); 625 | 626 | // draw un-decided characters 627 | x = cursorX; 628 | y = cursorY; 629 | chrScreen->setCursor(cursorX, cursorY); 630 | 631 | bool hasRawInputs = false; 632 | for(itr2 = rawInputs.begin(); itr2 != rawInputs.end(); itr2 ++){ 633 | char utf8[4]; 634 | chrScreen->putChar(*itr2, TFT_BLACK, TFT_WHITE); 635 | x ++; 636 | hasRawInputs = true; 637 | } 638 | if(!hasRawInputs){ 639 | if(shiin1 != 0){ 640 | chrScreen->putChar((wchar_t)shiin1, TFT_BLACK, TFT_WHITE); 641 | x ++; 642 | } 643 | if(shiin2 != 0){ 644 | chrScreen->putChar((wchar_t)shiin2, TFT_BLACK, TFT_WHITE); 645 | x ++; 646 | } 647 | } 648 | 649 | // mode line 650 | chrScreen->setCursor(0, chrScreen->getMaxLine() - 1); 651 | chrScreen->clearLine(chrScreen->getMaxLine() - 1, TFT_WHITE, TFT_BLACK); 652 | switch(kanjiMode){ 653 | case KanjiMode::DIRECT: chrScreen->putString(L"[A]", TFT_BLACK, TFT_WHITE); break; 654 | case KanjiMode::KATA: chrScreen->putString(L"[ア]", TFT_BLACK, TFT_WHITE); break; 655 | case KanjiMode::ROME: chrScreen->putString(L"[あ]", TFT_BLACK, TFT_WHITE); break; 656 | case KanjiMode::KANJI: chrScreen->putString(L"[漢]", TFT_BLACK, TFT_WHITE); break; 657 | case KanjiMode::HENKAN: break; 658 | } 659 | 660 | chrScreen->setCursor(5, chrScreen->getMaxLine() - 1); 661 | chrScreen->putString(filename, TFT_BLACK, TFT_WHITE); 662 | 663 | x = 0; 664 | y = chrScreen->getMaxLine() - 2; 665 | chrScreen->setCursor(x, y); 666 | chrScreen->clearLine(y, TFT_WHITE, TFT_BLACK); 667 | if(kanjiMode == KanjiMode::HENKAN){ 668 | for(vector:: iterator kanji = kanjiList.begin(); kanji != kanjiList.end(); kanji ++){ 669 | int16_t fg = TFT_WHITE; 670 | int16_t bg = TFT_BLACK; 671 | if(kanji == kanjiListItr){ 672 | fg = TFT_BLACK; 673 | bg = TFT_WHITE; 674 | } 675 | // kanji is utf8 676 | const char* k = kanji->c_str(); 677 | wchar_t w; 678 | while(*k != 0){ 679 | size_t n = utf8CharToUtf16((char*)k, &w); 680 | chrScreen->putChar(w, fg, bg); 681 | x ++; 682 | k += n; 683 | } 684 | chrScreen->putChar(L' ', fg, bg); 685 | x ++; 686 | // TODO: overflow x 687 | } 688 | } 689 | 690 | } 691 | -------------------------------------------------------------------------------- /lib/editor/editor.h: -------------------------------------------------------------------------------- 1 | using namespace std; 2 | #include 3 | #include 4 | #include // for native test 5 | #include // for native test 6 | 7 | #ifdef ESP32 8 | #include 9 | #define LGFX_M5STACK 10 | #include 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #ifndef EDITOR_H 18 | #define EDITOR_H 19 | 20 | enum class KanjiMode { 21 | DIRECT = 0, 22 | KATA, 23 | ROME, 24 | KANJI, 25 | HENKAN 26 | }; 27 | 28 | bool isAscii(wchar_t utf16); 29 | uint8_t utf16CharToUtf8(wchar_t utf16, char* utf8); 30 | size_t utf8CharToUtf16(char* utf8, wchar_t* utf16); 31 | // TODO: GetUtf8ByteCount 32 | 33 | class Editor : public Task{ 34 | public: 35 | vector> lines; 36 | vector> ::iterator line; 37 | vector ::iterator colItr; 38 | char filename[256]; 39 | 40 | virtual void init(); 41 | virtual void onkeydown(char key, char c, bool ctrl); 42 | virtual void backSpace(); 43 | virtual void right(); 44 | virtual void left(); 45 | virtual void up(); 46 | virtual void down(); 47 | virtual void enter(); 48 | virtual void onChar(wchar_t c); 49 | void load(); 50 | void save(); 51 | }; 52 | 53 | class KanjiEditor: public Editor{ 54 | public: 55 | KanjiMode kanjiMode = KanjiMode::ROME; 56 | uint8_t shiin1 = 0; 57 | uint8_t shiin2 = 0; 58 | const int16_t TFT_WHITE = 0xffff; 59 | const int16_t TFT_BLACK = 0x0000; 60 | 61 | string dictPath; 62 | vector kanjiList; 63 | vector ::iterator kanjiListItr; 64 | vector rawInputs; 65 | vector ::iterator rawInputsItr; 66 | 67 | // rome to hira table 68 | static wchar_t table[][5]; 69 | static wchar_t kata_table[][5]; 70 | 71 | void init(); 72 | void onkeydown(char key, char c, bool ctrl); 73 | void backSpace(); 74 | void right(); 75 | void left(); 76 | void up(); 77 | void down(); 78 | void enter(); 79 | void onChar(wchar_t c); 80 | wchar_t getKana(int r, int c); 81 | void onCharRoma(uint8_t c, bool ctrl = false); 82 | void onBoin(uint8_t c); 83 | void setStartKanjiMode(); 84 | void kanjiHenkan(); 85 | void kanjiDecide(); 86 | void nextKanji(); 87 | void prevKanji(); 88 | void draw(); 89 | }; 90 | #endif 91 | -------------------------------------------------------------------------------- /lib/fep/fep.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | wchar_t KanjiFep::table[][5] = { 4 | {L'あ',L'い',L'う',L'え',L'お'}, 5 | {L'か',L'き',L'く',L'け',L'こ'}, 6 | {L'さ',L'し',L'す',L'せ',L'そ'}, 7 | {L'た',L'ち',L'つ',L'て',L'と'}, 8 | {L'な',L'に',L'ぬ',L'ね',L'の'}, 9 | {L'は',L'ひ',L'ふ',L'へ',L'ほ'}, 10 | {L'ま',L'み',L'む',L'め',L'も'}, 11 | {L'や',L'い',L'ゆ',L'え',L'よ'}, 12 | {L'ら',L'り',L'る',L'れ',L'ろ'}, 13 | {L'わ',L'い',L'う',L'え',L'を'}, 14 | {L'が',L'ぎ',L'ぐ',L'げ',L'ご'}, 15 | {L'ざ',L'じ',L'ず',L'ぜ',L'ぞ'}, 16 | {L'だ',L'ぢ',L'づ',L'で',L'ど'}, 17 | {L'ば',L'び',L'ぶ',L'べ',L'ぼ'}, 18 | {L'ぱ',L'ぴ',L'ぷ',L'ぺ',L'ぽ'}, 19 | {L'ゃ',L'ぃ',L'ゅ',L'ぇ',L'ょ'}, 20 | {L'っ',L'ん',L'-',L'-',L'-'}, // 16 21 | {L'ぁ',L'ぃ',L'ぅ',L'ぇ',L'ぉ'} 22 | }; 23 | wchar_t KanjiFep::kata_table[][5] = { 24 | {L'ア',L'イ',L'ウ',L'エ',L'オ'}, 25 | {L'カ',L'キ',L'ク',L'ケ',L'コ'}, 26 | {L'サ',L'シ',L'ス',L'セ',L'ソ'}, 27 | {L'タ',L'チ',L'ツ',L'テ',L'ト'}, 28 | {L'ナ',L'ニ',L'ヌ',L'ネ',L'ノ'}, 29 | {L'ハ',L'ヒ',L'フ',L'ヘ',L'ホ'}, 30 | {L'マ',L'ミ',L'ム',L'メ',L'モ'}, 31 | {L'ヤ',L'イ',L'ユ',L'エ',L'ヨ'}, 32 | {L'ラ',L'リ',L'ル',L'レ',L'ロ'}, 33 | {L'ワ',L'イ',L'ウ',L'エ',L'ヲ'}, 34 | {L'ガ',L'ギ',L'グ',L'ゲ',L'ゴ'}, 35 | {L'ザ',L'ジ',L'ズ',L'ゼ',L'ゾ'}, 36 | {L'ダ',L'ヂ',L'ヅ',L'デ',L'ド'}, 37 | {L'バ',L'ビ',L'ブ',L'ベ',L'ボ'}, 38 | {L'パ',L'ピ',L'プ',L'ペ',L'ポ'}, 39 | {L'ャ',L'ィ',L'ュ',L'ェ',L'ョ'}, 40 | {L'ッ',L'ン',L'-',L'-',L'-'}, 41 | {L'ァ',L'ィ',L'ゥ',L'ェ',L'ォ'} 42 | }; 43 | 44 | bool isAscii(wchar_t utf16){ 45 | return utf16 >= 0 && utf16 <= 0x7e; 46 | } 47 | 48 | uint8_t utf16CharToUtf8(wchar_t utf16, char* utf8){ 49 | uint8_t len = 0; 50 | if (utf16 < 128) { 51 | utf8[0] = char(utf16); 52 | utf8[1] = 0; 53 | utf8[2] = 0; 54 | len = 1; 55 | } else if (utf16 < 2048) { 56 | utf8[0] = 0xC0 | char(utf16 >> 6); 57 | utf8[1] = 0x80 | (char(utf16) & 0x3F); 58 | utf8[2] = 0; 59 | len = 2; 60 | } else if (utf16 < 65536) { 61 | utf8[0] = 0xE0 | char(utf16 >> 12); 62 | utf8[1] = 0x80 | (char(utf16 >> 6) & 0x3F); 63 | utf8[2] = 0x80 | (char(utf16) & 0x3F); 64 | utf8[3] = 0; 65 | len = 3; 66 | } 67 | return len; 68 | } 69 | 70 | int GetUtf8ByteCount(char c) { 71 | if (0 <= uint8_t(c) && uint8_t(c) < 0x80) { 72 | return 1; 73 | } 74 | if (0xC2 <= uint8_t(c) && uint8_t(c) < 0xE0) { 75 | return 2; 76 | } 77 | if (0xE0 <= uint8_t(c) && uint8_t(c) < 0xF0) { 78 | return 3; 79 | } 80 | if (0xF0 <= uint8_t(c) && uint8_t(c) < 0xF8) { 81 | return 4; // not support yet 82 | } 83 | return 0; 84 | } 85 | 86 | size_t utf8CharToUtf16(char* utf8, wchar_t* utf16){ 87 | int numBytes = GetUtf8ByteCount(utf8[0]); 88 | if (numBytes == 0) { 89 | return 0; 90 | } 91 | switch (numBytes) { 92 | case 1: 93 | *utf16 = wchar_t(uint8_t(utf8[0])); 94 | break; 95 | case 2: 96 | if ((uint8_t(utf8[0]) & 0x1E) == 0) { 97 | return 0; 98 | } 99 | 100 | *utf16 = wchar_t(utf8[0] & 0x1F) << 6; 101 | *utf16 |= wchar_t(utf8[1] & 0x3F); 102 | break; 103 | case 3: 104 | if ((uint8_t(utf8[0]) & 0x0F) == 0 && 105 | (uint8_t(utf8[1]) & 0x20) == 0) { 106 | return 0; 107 | } 108 | 109 | *utf16 = wchar_t(utf8[0] & 0x0F) << 12; 110 | *utf16 |= wchar_t(utf8[1] & 0x3F) << 6; 111 | *utf16 |= wchar_t(utf8[2] & 0x3F); 112 | break; 113 | case 4: 114 | // not implement 115 | break; 116 | default: 117 | return 0; 118 | } 119 | 120 | return numBytes; 121 | } 122 | 123 | 124 | void KanjiFep::init(){ 125 | line.clear(); 126 | colItr = line.begin(); 127 | dictPath = "/SKK-JISYO.S.txt"; 128 | } 129 | 130 | void KanjiFep::setParentTask(Task* t){ 131 | parentTask = t; 132 | } 133 | 134 | bool KanjiFep::onkeydown(char key, char c, bool ctrl){ 135 | switch(key){ 136 | case 0x2A: // BS 137 | return backSpace(); 138 | break; 139 | case 0x50: // <- 140 | return left(); 141 | break; 142 | case 0x4F: // -> 143 | return right(); 144 | break; 145 | case 0x52: // ^ 146 | return up(); 147 | break; 148 | case 0x51: // v 149 | return down(); 150 | break; 151 | default: 152 | if(c == '\r'){ 153 | return enter(); 154 | } 155 | return onCharRoma(c,ctrl); 156 | } 157 | } 158 | 159 | bool KanjiFep::backSpace(){ 160 | if(kanjiMode == KanjiMode::ROME || kanjiMode == KanjiMode::KATA || kanjiMode == KanjiMode::DIRECT){ 161 | if(shiin2 != 0){ 162 | shiin2 = 0; 163 | return false; 164 | } 165 | if(shiin1 != 0){ 166 | shiin1 = 0; 167 | return false; 168 | } 169 | return true; 170 | }else if(kanjiMode == KanjiMode::KANJI){ 171 | if(rawInputsItr != rawInputs.begin()){ 172 | rawInputsItr --; 173 | rawInputsItr = rawInputs.erase(rawInputsItr); 174 | if(rawInputsItr == rawInputs.begin()){ 175 | kanjiMode = KanjiMode::ROME; 176 | } 177 | }else{ 178 | // cancel KANJI MODE 179 | kanjiMode = KanjiMode::ROME; 180 | } 181 | isBackspace = true; 182 | return false; 183 | } 184 | // unreach here? 185 | return true; 186 | } 187 | bool KanjiFep::right(){ 188 | if(kanjiMode == KanjiMode::HENKAN){ 189 | nextKanji(); 190 | return false; 191 | } 192 | return true; 193 | } 194 | bool KanjiFep::left(){ 195 | if(kanjiMode == KanjiMode::HENKAN){ 196 | prevKanji(); 197 | return false; 198 | } 199 | return true; 200 | } 201 | bool KanjiFep::up(){ 202 | return true; 203 | } 204 | bool KanjiFep::down(){ 205 | return true; 206 | } 207 | bool KanjiFep::enter(){ 208 | if(kanjiMode == KanjiMode::HENKAN){ 209 | kanjiDecide(); 210 | return false; 211 | } 212 | return true; 213 | } 214 | bool KanjiFep::onChar(wchar_t c){ 215 | if(kanjiMode == KanjiMode::DIRECT){ 216 | return true; 217 | }else if(kanjiMode == KanjiMode::ROME || kanjiMode == KanjiMode::KATA){ 218 | // send parentTask 219 | parentTask->onChar(c); 220 | return false; 221 | }else if(kanjiMode == KanjiMode::KANJI){ 222 | rawInputsItr = rawInputs.insert(rawInputsItr, c); 223 | rawInputsItr ++; 224 | return false; 225 | } 226 | // unreach here 227 | return true; 228 | } 229 | 230 | wchar_t KanjiFep::getKana(int r, int c){ 231 | if(kanjiMode == KanjiMode::KATA){ 232 | return kata_table[r][c]; 233 | } 234 | return table[r][c]; 235 | } 236 | bool KanjiFep::onCharRoma(uint8_t c, bool ctrl){ 237 | if(ctrl){ 238 | if(c == 'j'){ 239 | kanjiMode = KanjiMode::ROME; 240 | return false; 241 | } 242 | return true; 243 | } 244 | if(kanjiMode == KanjiMode::DIRECT){ 245 | return onChar(c); 246 | } 247 | if(kanjiMode == KanjiMode::HENKAN){ 248 | if(c != ' '){ 249 | kanjiDecide(); 250 | } 251 | }else if(kanjiMode == KanjiMode::KANJI){ 252 | if(c == ' '){ 253 | kanjiHenkan(); 254 | return false; 255 | } 256 | } 257 | 258 | if(isalpha(c) && c == toupper(c)){ // uppar case 259 | if(kanjiMode == KanjiMode::ROME || kanjiMode == KanjiMode::KATA){ // start kanji mode 260 | setStartKanjiMode(); 261 | c = tolower(c); 262 | }else if(kanjiMode == KanjiMode::KANJI){ // last 1 char and henkan 263 | c = tolower(c); 264 | onChar(c); 265 | kanjiHenkan(); 266 | return false; 267 | }else if(kanjiMode == KanjiMode::HENKAN){ // decide and next kanji 268 | kanjiDecide(); 269 | setStartKanjiMode(); 270 | c = tolower(c); 271 | } 272 | } 273 | 274 | switch(c){ 275 | case ' ': 276 | if(kanjiMode == KanjiMode::HENKAN){ 277 | nextKanji(); 278 | return false; 279 | } 280 | return onChar(c); 281 | break; 282 | case 'a': 283 | case 'i': 284 | case 'u': 285 | case 'e': 286 | case 'o': 287 | onBoin(c); 288 | return false; 289 | break; 290 | case 'k': 291 | case 's': 292 | case 't': 293 | case 'n': 294 | case 'h': 295 | case 'm': 296 | case 'y': 297 | case 'r': 298 | case 'w': 299 | case 'g': 300 | case 'z': 301 | case 'd': 302 | case 'b': 303 | case 'p': 304 | case 'x': 305 | if(shiin1 == c){ 306 | if(shiin1 == 'n'){ 307 | onChar(getKana(16, 1)); // ん 308 | shiin1 = 0; 309 | shiin2 = 0; 310 | }else{ 311 | return onChar(getKana(16, 0)); // っ 312 | } 313 | }else if(c == 'y' && shiin1 != 0){ 314 | shiin2 = 'y'; 315 | }else{ 316 | if(shiin1 == 'n'){ 317 | return onChar(getKana(16, 1)); // ん 318 | } 319 | shiin1 = c; 320 | } 321 | return false; 322 | break; 323 | case '-': 324 | return onChar(L'ー'); 325 | break; 326 | case '.': 327 | return onChar(L'。'); 328 | break; 329 | case ',': 330 | return onChar(L'、'); 331 | break; 332 | case 'l': 333 | kanjiMode = KanjiMode::DIRECT; 334 | return false; 335 | break; 336 | case 'q': 337 | kanjiMode = KanjiMode::KATA; 338 | return false; 339 | break; 340 | default: return onChar(c); 341 | } 342 | } 343 | 344 | void KanjiFep::onBoin(uint8_t c){ 345 | uint8_t b = 0; 346 | uint8_t pb = 0; 347 | uint8_t s = 0; 348 | switch(c){ 349 | case 'a': b = 0; break; 350 | case 'i': b = 1; break; 351 | case 'u': b = 2; break; 352 | case 'e': b = 3; break; 353 | case 'o': b = 4; break; 354 | } 355 | switch(shiin1){ 356 | case 0: s = 0; break; 357 | case 'k': s = 1; break; 358 | case 's': s = 2; break; 359 | case 't': s = 3; break; 360 | case 'n': s = 4; break; 361 | case 'h': s = 5; break; 362 | case 'm': s = 6; break; 363 | case 'y': s = 7; break; 364 | case 'r': s = 8; break; 365 | case 'w': s = 9; break; 366 | case 'g': s = 10; break; 367 | case 'z': s = 11; break; 368 | case 'd': s = 12; break; 369 | case 'b': s = 13; break; 370 | case 'p': s = 14; break; 371 | case 'x': s = 17; break; 372 | 373 | } 374 | shiin1 = 0; 375 | switch(shiin2){ 376 | case 'y': 377 | pb = b; 378 | b = 1; 379 | onChar(getKana(s, b)); 380 | onChar(getKana(15, pb)); 381 | break; 382 | case 0: 383 | onChar(getKana(s, b)); 384 | } 385 | shiin2 = 0; 386 | } 387 | 388 | void KanjiFep::setStartKanjiMode(){ 389 | kanjiMode = KanjiMode::KANJI; 390 | rawInputs.clear(); 391 | rawInputsItr = rawInputs.begin(); 392 | } 393 | 394 | void KanjiFep::kanjiHenkan(){ 395 | kanjiMode = KanjiMode::HENKAN; 396 | char target[256]; // TODO: check length 397 | uint16_t pos = 0; 398 | for(vector::iterator itr = rawInputs.begin(); itr != rawInputs.end(); itr ++){ 399 | uint8_t len = utf16CharToUtf8(*itr, &target[pos]); 400 | pos += len; 401 | } 402 | target[pos] = 0; // null terminate 403 | 404 | search(target, &kanjiList, (char*)dictPath.c_str()); 405 | kanjiListItr = kanjiList.begin(); 406 | if(kanjiList.empty()){ 407 | kanjiDecide(); 408 | } 409 | } 410 | void KanjiFep::kanjiDecide(){ 411 | kanjiMode = KanjiMode::ROME; 412 | if(!kanjiList.empty()){ 413 | const char* p = kanjiListItr->c_str(); 414 | while(*p != 0){ 415 | wchar_t utf16; 416 | size_t n = utf8CharToUtf16((char*)p, &utf16); 417 | onChar(utf16); 418 | p += n; 419 | } 420 | kanjiList.clear(); 421 | // copy tail alpha at rawInputs to line 422 | vector::iterator itr; 423 | for(itr = rawInputs.begin(); itr != rawInputs.end(); itr ++){ 424 | if(*itr < 128 && isalpha(char(*itr))){ 425 | printf("add char %c\n", char(*itr)); 426 | onCharRoma(char(*itr), false); 427 | } 428 | } 429 | }else{ 430 | // copy rawInputs to line 431 | vector::iterator itr; 432 | for(itr = rawInputs.begin(); itr != rawInputs.end(); itr ++){ 433 | if(*itr < 128 && isalpha(char(*itr))){ 434 | onCharRoma(char(*itr), false); 435 | }else{ 436 | onChar(*itr); 437 | } 438 | } 439 | } 440 | rawInputs.clear(); 441 | rawInputsItr = rawInputs.begin(); 442 | } 443 | 444 | void KanjiFep::nextKanji(){ 445 | kanjiListItr ++; 446 | if(kanjiListItr == kanjiList.end()){ 447 | kanjiListItr = kanjiList.begin(); 448 | } 449 | } 450 | 451 | void KanjiFep::prevKanji(){ 452 | if(kanjiListItr != kanjiList.begin()){ 453 | kanjiListItr --; 454 | }else{ 455 | kanjiListItr = kanjiList.end() - 1; 456 | } 457 | } 458 | 459 | void KanjiFep::draw(){ 460 | // draw decided characters 461 | vector>::iterator itr; 462 | vector::iterator itr2; 463 | int x = 0, y = 0; 464 | 465 | // draw un-decided characters 466 | 467 | int preCol = chrScreen->cursorCol; 468 | int preLine = chrScreen->cursorLine; 469 | 470 | bool hasRawInputs = false; 471 | for(itr2 = rawInputs.begin(); itr2 != rawInputs.end(); itr2 ++){ 472 | char utf8[4]; 473 | chrScreen->putChar(*itr2, TFT_BLACK, TFT_WHITE); 474 | x ++; 475 | hasRawInputs = true; 476 | } 477 | if(!hasRawInputs){ 478 | if(shiin1 != 0){ 479 | chrScreen->putChar((wchar_t)shiin1, TFT_BLACK, TFT_WHITE); 480 | x ++; 481 | } 482 | if(shiin2 != 0){ 483 | chrScreen->putChar((wchar_t)shiin2, TFT_BLACK, TFT_WHITE); 484 | x ++; 485 | } 486 | } 487 | if(isBackspace){ // remove backspaced char 488 | chrScreen->putChar(L' ', TFT_BLACK, TFT_WHITE); 489 | isBackspace = false; 490 | } 491 | 492 | // mode line 493 | chrScreen->setCursor(0, chrScreen->getMaxLine() - 1); 494 | chrScreen->clearLine(chrScreen->getMaxLine() - 1, TFT_WHITE, TFT_BLACK); 495 | switch(kanjiMode){ 496 | case KanjiMode::DIRECT: chrScreen->putString((wchar_t*)L"[A]", TFT_BLACK, TFT_WHITE); break; 497 | case KanjiMode::KATA: chrScreen->putString((wchar_t*)L"[ア]", TFT_BLACK, TFT_WHITE); break; 498 | case KanjiMode::ROME: chrScreen->putString((wchar_t*)L"[あ]", TFT_BLACK, TFT_WHITE); break; 499 | case KanjiMode::KANJI: chrScreen->putString((wchar_t*)L"[漢]", TFT_BLACK, TFT_WHITE); break; 500 | case KanjiMode::HENKAN: break; 501 | } 502 | 503 | chrScreen->setCursor(5, chrScreen->getMaxLine() - 1); 504 | //chrScreen->putString(filename, TFT_BLACK, TFT_WHITE); 505 | 506 | x = 0; 507 | y = chrScreen->getMaxLine() - 2; 508 | chrScreen->setCursor(x, y); 509 | chrScreen->clearLine(y, TFT_WHITE, TFT_BLACK); 510 | if(kanjiMode == KanjiMode::HENKAN){ 511 | for(vector:: iterator kanji = kanjiList.begin(); kanji != kanjiList.end(); kanji ++){ 512 | int16_t fg = TFT_WHITE; 513 | int16_t bg = TFT_BLACK; 514 | if(kanji == kanjiListItr){ 515 | fg = TFT_BLACK; 516 | bg = TFT_WHITE; 517 | } 518 | // kanji is utf8 519 | const char* k = kanji->c_str(); 520 | wchar_t w; 521 | while(*k != 0){ 522 | size_t n = utf8CharToUtf16((char*)k, &w); 523 | chrScreen->putChar(w, fg, bg); 524 | x ++; 525 | k += n; 526 | } 527 | chrScreen->putChar(L' ', fg, bg); 528 | x ++; 529 | // TODO: overflow x 530 | } 531 | } 532 | 533 | chrScreen->setCursor(preCol, preLine); 534 | } 535 | -------------------------------------------------------------------------------- /lib/fep/fep.h: -------------------------------------------------------------------------------- 1 | using namespace std; 2 | #include 3 | #include 4 | #include // for native test 5 | #include // for native test 6 | 7 | #ifdef ESP32 8 | #include 9 | #define LGFX_M5STACK 10 | #include 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #ifndef EDITOR_H 18 | #define EDITOR_H 19 | 20 | enum class KanjiMode { 21 | DIRECT = 0, 22 | KATA, 23 | ROME, 24 | KANJI, 25 | HENKAN 26 | }; 27 | 28 | bool isAscii(wchar_t utf16); 29 | uint8_t utf16CharToUtf8(wchar_t utf16, char* utf8); 30 | size_t utf8CharToUtf16(char* utf8, wchar_t* utf16); 31 | // TODO: GetUtf8ByteCount 32 | 33 | class KanjiFep: public Task{ 34 | public: 35 | vector line; 36 | vector ::iterator colItr; 37 | Task* parentTask; 38 | 39 | KanjiMode kanjiMode = KanjiMode::ROME; 40 | uint8_t shiin1 = 0; 41 | uint8_t shiin2 = 0; 42 | bool isBackspace = false; 43 | const int16_t TFT_WHITE = 0xffff; 44 | const int16_t TFT_BLACK = 0x0000; 45 | 46 | string dictPath; 47 | vector kanjiList; 48 | vector ::iterator kanjiListItr; 49 | vector rawInputs; 50 | vector ::iterator rawInputsItr; 51 | 52 | // rome to hira table 53 | static wchar_t table[][5]; 54 | static wchar_t kata_table[][5]; 55 | 56 | void init(); 57 | void setParentTask(Task* t); 58 | bool onkeydown(char key, char c, bool ctrl); 59 | bool backSpace(); 60 | bool right(); 61 | bool left(); 62 | bool up(); 63 | bool down(); 64 | bool enter(); 65 | bool onChar(wchar_t c); 66 | wchar_t getKana(int r, int c); 67 | bool onCharRoma(uint8_t c, bool ctrl = false); 68 | void onBoin(uint8_t c); 69 | void setStartKanjiMode(); 70 | void kanjiHenkan(); 71 | void kanjiDecide(); 72 | void nextKanji(); 73 | void prevKanji(); 74 | void draw(); 75 | }; 76 | #endif 77 | -------------------------------------------------------------------------------- /lib/luaexec/luaexec.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | // to use utf16CharToUtf8 5 | 6 | #include 7 | 8 | 9 | #include 10 | 11 | extern "C" { 12 | int gprint(char* s){ 13 | Serial.println(s); 14 | return 0; 15 | } 16 | const char *getF(lua_State *L, void *ud, size_t *size){ 17 | struct LoadF *lf = (struct LoadF *)ud; 18 | (void)L; /* not used */ 19 | char* ret = NULL; 20 | 21 | if(!lf->f.available()){ 22 | *size = 0; 23 | return NULL; 24 | } 25 | 26 | lf->f.readStringUntil('\n').toCharArray(lf->buf, MAX_CHAR); 27 | ret = lf->buf; 28 | int len = strnlen(ret, MAX_CHAR); 29 | ret[len] = '\n'; // todo n, n+1 > MAX_CHAR ? 30 | ret[len + 1] = 0; 31 | 32 | *size = len + 1; 33 | //Serial.print(""); 34 | //Serial.println(ret); 35 | //Serial.println(*size); 36 | return ret; 37 | } 38 | } 39 | 40 | void LuaEngine::init(ChrScreen* cs){ 41 | chrScreen = cs; 42 | isTerminate = false; 43 | 44 | L = luaL_newstate(); 45 | luaL_openlibs(L); 46 | 47 | lua_pushlightuserdata(L, this); 48 | lua_pushcclosure(L, l_getFiles, 1); 49 | lua_setglobal(L, "getfiles"); 50 | 51 | lua_pushlightuserdata(L, this); 52 | lua_pushcclosure(L, l_exists, 1); 53 | lua_setglobal(L, "exists"); 54 | 55 | lua_pushlightuserdata(L, this); 56 | lua_pushcclosure(L, l_saveFile, 1); 57 | lua_setglobal(L, "savefile"); 58 | 59 | lua_pushlightuserdata(L, this); 60 | lua_pushcclosure(L, l_readFile, 1); 61 | lua_setglobal(L, "readfile"); 62 | 63 | lua_pushlightuserdata(L, this); 64 | lua_pushcclosure(L, l_putString, 1); 65 | lua_setglobal(L, "putstring"); 66 | 67 | lua_pushlightuserdata(L, this); 68 | lua_pushcclosure(L, l_clear, 1); 69 | lua_setglobal(L, "clear"); 70 | 71 | lua_pushlightuserdata(L, this); 72 | lua_pushcclosure(L, l_fillRect, 1); 73 | lua_setglobal(L, "fillrect"); 74 | 75 | lua_pushlightuserdata(L, this); 76 | lua_pushcclosure(L, l_setCursor, 1); 77 | lua_setglobal(L, "setcursor"); 78 | 79 | lua_pushlightuserdata(L, this); 80 | lua_pushcclosure(L, l_getFreeHeap, 1); 81 | lua_setglobal(L, "getfreeheap"); 82 | 83 | lua_pushlightuserdata(L, this); 84 | lua_pushcclosure(L, l_getTextWidth, 1); 85 | lua_setglobal(L, "gettextwidth"); 86 | 87 | lua_pushlightuserdata(L, this); 88 | lua_pushcclosure(L, l_getMaxLine, 1); 89 | lua_setglobal(L, "getmaxline"); 90 | 91 | lua_pushlightuserdata(L, this); 92 | lua_pushcclosure(L, l_getScreenWidth, 1); 93 | lua_setglobal(L, "getscreenwidth"); 94 | 95 | lua_pushlightuserdata(L, this); 96 | lua_pushcclosure(L, l_setColor, 1); 97 | lua_setglobal(L, "setcolor"); 98 | 99 | lua_pushlightuserdata(L, this); 100 | lua_pushcclosure(L, l_debug, 1); 101 | lua_setglobal(L, "debug"); 102 | 103 | lua_pushlightuserdata(L, this); 104 | lua_pushcclosure(L, l_exit, 1); 105 | lua_setglobal(L, "exit"); 106 | 107 | File fp; 108 | if(isSD){ 109 | fp = SD.open(fileName, FILE_READ); 110 | }else{ 111 | fp = SPIFFS.open(fileName, FILE_READ); 112 | } 113 | struct LoadF lf; 114 | lf.f = fp; 115 | char cFileName[32]; 116 | bool runError = false; 117 | fileName.toCharArray(cFileName, 32); 118 | if(lua_load(L, getF, &lf, cFileName, NULL)){ 119 | Serial.printf("error? %s\n", lua_tostring(L, -1)); 120 | runError = true; 121 | errorString = lua_tostring(L, -1); 122 | isTerminate = true; 123 | return; 124 | } 125 | fp.close(); 126 | 127 | if(runError == false){ 128 | if(lua_pcall(L, 0, 0,0)){ 129 | Serial.printf("init error? %s\n", lua_tostring(L, -1)); 130 | runError = true; 131 | errorString = lua_tostring(L, -1); 132 | isTerminate = true; 133 | return; 134 | } 135 | } 136 | } 137 | void LuaEngine::eval(char* utf8LuaString){ 138 | int result = luaL_loadstring(L, utf8LuaString); 139 | if(result != LUA_OK){ 140 | Serial.println("lua error"); 141 | Serial.printf("error? %s\n", lua_tostring(L, -1)); 142 | char* err = (char*)lua_tostring(L, -1); 143 | lgfx->setCursor(0,0); 144 | lgfx->print((char*)lua_tostring(L, -1)); 145 | 146 | }else{ 147 | Serial.println("lua ok"); 148 | 149 | if(lua_pcall(L,0,0,0) != LUA_OK) { 150 | Serial.println("lua error"); 151 | Serial.println(lua_tostring(L, -1)); 152 | //lua_close(L); 153 | //exit(EXIT_FAILURE); 154 | } 155 | } 156 | } 157 | void LuaEngine::keydown(char key, char c, bool ctrl){ 158 | lua_getglobal(L, "keydown"); 159 | lua_pushnumber(L, key); 160 | char s[2]; 161 | s[0] = c; 162 | s[1] = 0; 163 | lua_pushstring(L, s); 164 | lua_pushboolean(L, ctrl); 165 | if(lua_pcall(L, 3, 0, 0)){ 166 | Serial.printf("run error? %s\n", lua_tostring(L, -1)); 167 | lgfx->setTextColor(TFT_WHITE, TFT_RED); 168 | lgfx->setCursor(0,0); 169 | lgfx->print((char*)lua_tostring(L, -1)); 170 | runError = true; 171 | errorString = lua_tostring(L, -1); 172 | isTerminate = true; 173 | return; 174 | } 175 | } 176 | void LuaEngine::onChar(char* utf8char){ 177 | lua_getglobal(L, "onChar"); 178 | lua_pushstring(L, utf8char); 179 | if(lua_pcall(L, 1, 0, 0)){ 180 | Serial.printf("run error? %s\n", lua_tostring(L, -1)); 181 | lgfx->setTextColor(TFT_WHITE, TFT_RED); 182 | lgfx->setCursor(0,0); 183 | lgfx->print((char*)lua_tostring(L, -1)); 184 | runError = true; 185 | errorString = lua_tostring(L, -1); 186 | isTerminate = true; 187 | return; 188 | } 189 | } 190 | 191 | int LuaEngine::l_getFiles(lua_State* L){ 192 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 193 | const char* path = lua_tostring(L, 1); 194 | 195 | lua_newtable(L); 196 | int i = 1; 197 | File dir = SPIFFS.open(path); 198 | File f = dir.openNextFile(); 199 | while(f){ 200 | lua_pushnumber(L, i); 201 | lua_pushstring(L, (char*)f.name()); 202 | lua_settable(L, -3); 203 | f = dir.openNextFile(); 204 | i ++; 205 | } 206 | return 1; 207 | } 208 | 209 | int LuaEngine::l_exists(lua_State* L){ 210 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 211 | const char* fileName = lua_tostring(L, 1); 212 | 213 | bool exists = SD.exists(fileName); 214 | lua_pushboolean(L, exists); 215 | 216 | return 1; 217 | } 218 | 219 | int LuaEngine::l_saveFile(lua_State* L){ 220 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 221 | const char* fileName = lua_tostring(L, 1); 222 | const char* body = lua_tostring(L, 2); 223 | 224 | File fp = SD.open(fileName, FILE_WRITE); 225 | fp.print(body); 226 | fp.close(); 227 | 228 | return 0; 229 | } 230 | 231 | int LuaEngine::l_readFile(lua_State* L){ 232 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 233 | const char* fileName = lua_tostring(L, 1); 234 | char buf[1024]; 235 | 236 | File fp = SD.open(fileName, FILE_READ); 237 | lua_pushstring(L, ""); 238 | while(fp.available()){ 239 | int c = fp.readBytes((char*)buf, 1024); 240 | Serial.println(c); 241 | if(c == 0){ 242 | break; 243 | } 244 | buf[c] = 0; // null terminate 245 | lua_pushstring(L, buf); 246 | lua_concat(L, 2); 247 | Serial.println(buf); 248 | } 249 | fp.close(); 250 | 251 | return 1; 252 | } 253 | 254 | int LuaEngine::l_putString(lua_State* L){ 255 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 256 | const char* text = lua_tostring(L, 1); 257 | self->lgfx->print(text); 258 | return 0; 259 | } 260 | 261 | int LuaEngine::l_clear(lua_State* L){ 262 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 263 | 264 | self->lgfx->fillScreen(self->bgColor); 265 | return 0; 266 | } 267 | int LuaEngine::l_fillRect(lua_State* L){ 268 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 269 | const int x = lua_tointeger(L, 1); 270 | const int y = lua_tointeger(L, 2); 271 | const int w = lua_tointeger(L, 3); 272 | const int h = lua_tointeger(L, 4); 273 | 274 | self->lgfx->fillRect(x, y, w, h, self->fgColor); 275 | return 0; 276 | } 277 | int LuaEngine::l_setCursor(lua_State* L){ 278 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 279 | const int col = lua_tointeger(L, 1); 280 | const int line = lua_tointeger(L, 2); 281 | 282 | self->lgfx->setCursor(col, line); 283 | self->chrScreen->resetLine(line/12); // TODO: ad-hoc 284 | self->chrScreen->setCursor(col/6, line/12); // TODO: magic number 285 | return 0; 286 | } 287 | int LuaEngine::l_getFreeHeap(lua_State* L){ 288 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 289 | 290 | lua_pushinteger(L, (lua_Integer)ESP.getFreeHeap()); 291 | return 1; 292 | } 293 | int LuaEngine::l_getTextWidth(lua_State* L){ 294 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 295 | const char* text = lua_tostring(L, 1); 296 | 297 | int width = self->lgfx->textWidth(text); // TODO: implement 298 | lua_pushinteger(L, (lua_Integer)width); 299 | return 1; 300 | } 301 | int LuaEngine::l_getMaxLine(lua_State* L){ 302 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 303 | 304 | int line = 20; // TODO: implement 305 | lua_pushinteger(L, (lua_Integer)line); 306 | return 1; 307 | } 308 | int LuaEngine::l_getScreenWidth(lua_State* L){ 309 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 310 | 311 | int width = 320; // TODO 312 | lua_pushinteger(L, (lua_Integer)width); 313 | return 1; 314 | } 315 | 316 | int LuaEngine::l_setColor(lua_State* L){ 317 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 318 | int r,g,b; 319 | r = lua_tointeger(L, 1); 320 | g = lua_tointeger(L, 2); 321 | b = lua_tointeger(L, 3); 322 | int r2,g2,b2; 323 | r2 = lua_tointeger(L, 4); 324 | g2 = lua_tointeger(L, 5); 325 | b2 = lua_tointeger(L, 6); 326 | 327 | self->fgColor = rgb24to16(r, g, b); 328 | self->bgColor = rgb24to16(r2, g2, b2); 329 | self->lgfx->setTextColor(self->fgColor, self->bgColor); 330 | return 0; 331 | } 332 | int LuaEngine::l_debug(lua_State* L){ 333 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 334 | const char* text = lua_tostring(L, 1); 335 | Serial.println(text); 336 | return 0; 337 | } 338 | 339 | int LuaEngine::l_exit(lua_State* L){ 340 | LuaEngine* self = (LuaEngine*)lua_touserdata(L, lua_upvalueindex(1)); 341 | self->isTerminate = true; 342 | return 0; 343 | } 344 | void LuaEngine::exit(){ 345 | Serial.println("CALL close!"); 346 | Serial.println(runError); 347 | Serial.println(errorString); 348 | lua_close(L); 349 | } 350 | 351 | 352 | -------------------------------------------------------------------------------- /lib/luaexec/luaexec.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef LUA_EXEC_H 5 | #define LUA_EXEC_H 6 | 7 | extern "C" { 8 | #include "lua.h" 9 | 10 | #include "lualib.h" 11 | #include "lauxlib.h" 12 | } 13 | 14 | #define MAX_CHAR 256 15 | 16 | struct LoadF{ 17 | File f; 18 | char buf[MAX_CHAR]; 19 | }; 20 | 21 | inline uint16_t rgb24to16(uint8_t r, uint8_t g, uint8_t b) { 22 | uint16_t tmp = ((r>>3) << 11) | ((g>>2) << 5) | (b>>3); 23 | return tmp; //(tmp >> 8) | (tmp << 8); 24 | } 25 | 26 | class LuaEngine{ 27 | public: 28 | ChrScreen *chrScreen; 29 | lua_State* L; 30 | const int16_t TFT_WHITE = 0xffff; 31 | const int16_t TFT_BLACK = 0x0000; 32 | const int16_t TFT_RED = 0xF800; 33 | int16_t fgColor = 0xffff; 34 | int16_t bgColor = 0x0000; 35 | LGFX *lgfx; 36 | bool isSD = false; 37 | String fileName = "/main.lua"; 38 | String errorString; 39 | bool runError; 40 | 41 | bool isTerminate = false; 42 | void init(ChrScreen* cs); 43 | void eval(char* utf8LuaString); 44 | void keydown(char key, char c, bool ctrl); 45 | void onChar(char* utf8char); 46 | void exit(); 47 | static int l_getFiles(lua_State* L); 48 | static int l_exists(lua_State* L); 49 | static int l_saveFile(lua_State* L); 50 | static int l_readFile(lua_State* L); 51 | static int l_putString(lua_State* L); 52 | static int l_clear(lua_State* L); 53 | static int l_fillRect(lua_State* L); 54 | static int l_setCursor(lua_State* L); 55 | static int l_getFreeHeap(lua_State* L); 56 | static int l_getTextWidth(lua_State* L); 57 | static int l_getMaxLine(lua_State* L); 58 | static int l_getScreenWidth(lua_State* L); 59 | static int l_setColor(lua_State* L); 60 | static int l_debug(lua_State* L); 61 | static int l_exit(lua_State* L); 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /lib/luashell/luashell.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // to use utf16CharToUtf8 4 | #include 5 | #include 6 | 7 | // Define CS pin for the SD card module 8 | #define SD_MISO 19 9 | #define SD_MOSI 23 10 | #define SD_SCLK 18 11 | #define SD_CS 4 12 | SPIClass sdSPI(VSPI); 13 | 14 | void LuaShell::init(){ 15 | rawInputsItr = rawInputs.begin(); 16 | for(int i = 0; i < chrScreen->getMaxLine(); i ++){ 17 | chrScreen->clearLine(i, TFT_WHITE, TFT_BLACK); 18 | } 19 | isTerminate = false; 20 | lua.init(chrScreen); 21 | sdSPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS); 22 | if(!SD.begin(SD_CS, sdSPI)){ 23 | Serial.println("SD init failed"); 24 | } 25 | } 26 | bool LuaShell::onkeydown(char key, char c, bool ctrl){ 27 | if(fep){ 28 | bool through = fep->onkeydown(key, c, ctrl); 29 | if(!through){ 30 | return false; 31 | } 32 | } 33 | 34 | lua.keydown(key, c, ctrl); 35 | 36 | isTerminate = lua.isTerminate; 37 | if(isTerminate == 1){ 38 | lua.exit(); 39 | } 40 | return true; 41 | } 42 | bool LuaShell::onChar(wchar_t c){ 43 | char utf8char[4]; 44 | int count = 0; 45 | uint8_t len = utf16CharToUtf8(c, utf8char); 46 | 47 | lua.onChar(utf8char); 48 | isTerminate = lua.isTerminate; 49 | if(isTerminate){ 50 | lua.exit(); 51 | } 52 | return true; 53 | } 54 | -------------------------------------------------------------------------------- /lib/luashell/luashell.h: -------------------------------------------------------------------------------- 1 | using namespace std; 2 | #include 3 | #include 4 | #include // for native test 5 | #include // for native test 6 | 7 | #ifdef ESP32 8 | #include 9 | #define LGFX_M5STACK 10 | #include 11 | #endif 12 | 13 | #include 14 | //#include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifndef LUA_SHELL_H 20 | #define LUA_SHELL_H 21 | 22 | class LuaShell: public Task{ 23 | public: 24 | int x = 0; 25 | int y = 0; 26 | lua_State* L; 27 | const int16_t TFT_WHITE = 0xffff; 28 | const int16_t TFT_BLACK = 0x0000; 29 | 30 | LuaEngine lua; 31 | vector rawInputs; 32 | vector ::iterator rawInputsItr; 33 | //KanjiEditor* editor; 34 | 35 | virtual void init(); 36 | virtual bool onkeydown(char key, char c, bool ctrl); 37 | virtual bool onChar(wchar_t c); 38 | }; 39 | #endif 40 | 41 | 42 | -------------------------------------------------------------------------------- /lib/shell/shell.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Shell::init(){ 4 | rawInputsItr = rawInputs.begin(); 5 | for(int i = 0; i < chrScreen->getMaxLine(); i ++){ 6 | chrScreen->clearLine(i, TFT_WHITE, TFT_BLACK); 7 | } 8 | chrScreen->setCursor(0, 0); 9 | chrScreen->putChar(L'>', TFT_WHITE, TFT_BLACK); 10 | chrScreen->putChar(L' ', TFT_WHITE, TFT_BLACK); 11 | } 12 | bool Shell::onkeydown(char key, char c, bool ctrl){ 13 | if(fep){ 14 | bool through = fep->onkeydown(key, c, ctrl); 15 | if(!through){ 16 | return false; 17 | } 18 | } 19 | switch(key){ 20 | case 0x2A: // BS 21 | backSpace(); 22 | break; 23 | case 0x50: // <- 24 | left(); 25 | break; 26 | case 0x4F: // -> 27 | right(); 28 | break; 29 | case 0x52: // ^ 30 | up(); 31 | break; 32 | case 0x51: // v 33 | down(); 34 | break; 35 | default: 36 | if(c == '\r'){ 37 | enter(); 38 | }else{ 39 | onChar(c); 40 | } 41 | } 42 | return true; 43 | } 44 | void Shell::backSpace(){ 45 | if(rawInputs.size() == 0){ 46 | return; 47 | } 48 | rawInputs.pop_back(); 49 | chrScreen->back(); 50 | chrScreen->putChar(0, TFT_WHITE, TFT_BLACK); 51 | chrScreen->putChar(0, TFT_WHITE, TFT_BLACK); 52 | chrScreen->back(); 53 | chrScreen->back(); 54 | } 55 | void Shell::right(){ 56 | } 57 | void Shell::left(){ 58 | } 59 | void Shell::up(){ 60 | } 61 | void Shell::down(){ 62 | } 63 | void Shell::enter(){ 64 | nextLine(); 65 | int i = 0; 66 | wchar_t cmd[256]; 67 | char debug[256]; 68 | if(rawInputs.size() == 0){ 69 | chrScreen->putChar(L'>', TFT_WHITE, TFT_BLACK); 70 | chrScreen->putChar(L' ', TFT_WHITE, TFT_BLACK); 71 | 72 | return; 73 | } 74 | for(vector::iterator itr = rawInputs.begin(); itr != rawInputs.end(); itr ++){ 75 | //chrScreen->putChar(*itr, TFT_WHITE, TFT_BLACK); 76 | cmd[i] = *itr; 77 | i ++; 78 | } 79 | cmd[i] = 0; 80 | vector> args = split(rawInputs); 81 | to_wchar(args.at(0), cmd, 256); 82 | 83 | if(wcsncmp(cmd, L"ls", 256) == 0){ 84 | #ifdef ESP32 85 | File root = SD.open("/"); 86 | File f = root.openNextFile(); 87 | while(f){ 88 | chrScreen->putString((char*)f.name(), TFT_WHITE, TFT_BLACK); 89 | f = root.openNextFile(); 90 | nextLine(); 91 | } 92 | #endif 93 | }else if(wcsncmp(cmd, L"clear", 256) == 0){ 94 | for(int i = 0; i < chrScreen->getMaxLine(); i ++){ 95 | chrScreen->clearLine(i, TFT_WHITE, TFT_BLACK); 96 | } 97 | x = 0; 98 | y = 0; 99 | chrScreen->setCursor(0, 0); 100 | }else if(wcsncmp(cmd, L"lua", 256) == 0){ 101 | #ifdef ESP32 102 | if(args.size() > 1){ 103 | char filename[256]; 104 | to_char(args.at(1), filename, 256); 105 | 106 | luaShell->lua.isSD = true; 107 | luaShell->lua.fileName = filename; 108 | }else{ 109 | luaShell->lua.isSD = false; 110 | luaShell->lua.fileName = "/main.lua"; 111 | } 112 | 113 | setNextTask(luaShell); 114 | for(int i = 0; i < chrScreen->getMaxLine(); i ++){ 115 | chrScreen->clearLine(i, TFT_WHITE, TFT_BLACK); 116 | } 117 | rawInputs.clear(); 118 | luaShell->init(); 119 | return; 120 | #endif 121 | }/*else if(wcsncmp(cmd, L"edit", 256) == 0){ 122 | if(args.size() > 1){ 123 | to_char(args.at(1), editor->filename, 256); // todo: implement setter 124 | editor->load(); 125 | } 126 | setNextTask(editor); 127 | for(int i = 0; i < chrScreen->getMaxLine(); i ++){ 128 | chrScreen->clearLine(i, TFT_WHITE, TFT_BLACK); 129 | } 130 | rawInputs.clear(); 131 | return; 132 | }*/else if(wcsncmp(cmd, L"rm", 256) == 0){ 133 | if(args.size() > 1){ 134 | char filename[256]; 135 | to_char(args.at(1), filename, 256); 136 | #ifdef ESP32 137 | SD.remove(filename); 138 | #endif 139 | sprintf(debug, "%s removed", filename); 140 | chrScreen->putString(debug, TFT_WHITE, TFT_BLACK); 141 | nextLine(); 142 | } 143 | }else{ 144 | chrScreen->putString((char*)L"unknown command", TFT_WHITE, TFT_BLACK); 145 | nextLine(); 146 | } 147 | 148 | x = 0; 149 | rawInputs.clear(); 150 | rawInputsItr = rawInputs.begin(); 151 | 152 | chrScreen->putChar(L'>', TFT_WHITE, TFT_BLACK); 153 | chrScreen->putChar(L' ', TFT_WHITE, TFT_BLACK); 154 | } 155 | bool Shell::onChar(wchar_t c){ 156 | chrScreen->putChar(c, TFT_WHITE, TFT_BLACK); 157 | rawInputs.push_back(c); 158 | return true; 159 | } 160 | void Shell::nextLine(){ 161 | chrScreen->nextLine(); 162 | } 163 | -------------------------------------------------------------------------------- /lib/shell/shell.h: -------------------------------------------------------------------------------- 1 | using namespace std; 2 | #include 3 | #include 4 | #include // for native test 5 | #include // for native test 6 | 7 | #ifdef ESP32 8 | #include 9 | #define LGFX_M5STACK 10 | #include 11 | #endif 12 | 13 | #include 14 | //#include 15 | #ifdef ESP32 16 | #include 17 | #endif 18 | #include 19 | #include 20 | 21 | #ifndef SHELL_H 22 | #define SHELL_H 23 | class Shell: public Task{ 24 | public: 25 | int x = 0; 26 | int y = 0; 27 | const int16_t TFT_WHITE = 0xffff; 28 | const int16_t TFT_BLACK = 0x0000; 29 | 30 | vector rawInputs; 31 | vector ::iterator rawInputsItr; 32 | //KanjiEditor* editor; 33 | #ifdef ESP32 34 | LuaShell* luaShell; 35 | #endif 36 | 37 | virtual void init(); 38 | virtual bool onkeydown(char key, char c, bool ctrl); 39 | virtual void backSpace(); 40 | virtual void right(); 41 | virtual void left(); 42 | virtual void up(); 43 | virtual void down(); 44 | virtual void enter(); 45 | virtual bool onChar(wchar_t c); 46 | virtual void nextLine(); 47 | }; 48 | #endif 49 | 50 | 51 | -------------------------------------------------------------------------------- /lib/task/task.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | void Task::init(){} 3 | void Task::setChrScreen(ChrScreen* pChrScreen){ 4 | chrScreen = pChrScreen; 5 | } 6 | void Task::setFep(Task* pFep){ 7 | fep = pFep; 8 | } 9 | 10 | bool Task::onkeydown(char key, char c, bool ctrl){return true;} 11 | bool Task::onChar(wchar_t c){} 12 | void Task::draw(){} 13 | void Task::setNextTask(Task* t){ 14 | nextTask = t; 15 | } 16 | void Task::terminate(){ 17 | isTerminate = true; 18 | } 19 | -------------------------------------------------------------------------------- /lib/task/task.h: -------------------------------------------------------------------------------- 1 | using namespace std; 2 | #include 3 | #include 4 | #include // for native test 5 | #include // for native test 6 | 7 | #ifdef ESP32 8 | #include 9 | #define LGFX_M5STACK 10 | #include 11 | #endif 12 | 13 | #include 14 | 15 | #ifndef TASK_H 16 | #define TASK_H 17 | class Task{ 18 | public: 19 | ChrScreen* chrScreen; 20 | Task* fep = NULL; 21 | Task* nextTask = NULL; 22 | bool isTerminate = false; 23 | virtual void init(); 24 | virtual void setChrScreen(ChrScreen* chrScreen); 25 | virtual void setFep(Task* fep); 26 | virtual bool onkeydown(char key, char c, bool ctrl); 27 | virtual bool onChar(wchar_t c); 28 | virtual void draw(); 29 | virtual void setNextTask(Task* t); 30 | virtual void terminate(); 31 | }; 32 | #endif 33 | -------------------------------------------------------------------------------- /lib/util/util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void to_wchar(vector line, wchar_t* w, int n){ 4 | for(vector::iterator itr = line.begin(); itr != line.end(); itr ++){ 5 | *w = *itr; 6 | w ++; 7 | n --; 8 | if(n == 1)break; 9 | } 10 | *w = 0; 11 | } 12 | void to_char(vector line, char* c, int n){ 13 | for(vector::iterator itr = line.begin(); itr != line.end(); itr ++){ 14 | *c = (char)*itr; 15 | c ++; 16 | n --; 17 | if(n == 1)break; 18 | } 19 | *c = 0; 20 | } 21 | 22 | vector> split(vector line){ 23 | vector> ret; 24 | vector now; 25 | 26 | bool inSpace = false; 27 | for(vector::iterator itr = line.begin(); itr != line.end(); itr ++){ 28 | if(*itr == L' '){ 29 | if(inSpace){ // not first space 30 | // skip 31 | }else{ // first space 32 | if(now.size() > 0){ 33 | ret.push_back(now); 34 | } 35 | now.clear(); 36 | } 37 | inSpace = true; 38 | }else{ 39 | now.push_back(*itr); 40 | } 41 | } 42 | if(now.size() > 0){ 43 | ret.push_back(now); 44 | } 45 | return ret; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /lib/util/util.h: -------------------------------------------------------------------------------- 1 | using namespace std; 2 | #include 3 | 4 | #ifndef UTIL_H 5 | #define UTIL_H 6 | 7 | void to_wchar(vector line, wchar_t* w, int n); 8 | void to_char(vector line, char* c, int n); 9 | vector> split(vector line); 10 | #endif 11 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | [env:esp32] 2 | platform = espressif32 3 | board = esp32dev 4 | framework = arduino 5 | upload_speed = 921600 6 | lib_deps = M5Stack 7 | USB-Host-Shield-20 8 | lovyan03/LovyanGFX @ 0.4.18 9 | tanakamasayuki/efont Unicode Font Data@1.0.4 10 | board_build.f_cpu = 2400000000L 11 | board_build.f_flash = 80000000L 12 | 13 | test_ignore = native 14 | build_flags = 15 | -DLUA_32BITS=1 16 | -D"lua_writestring(s,l)=gprint(s)" 17 | -D"lua_writestringerror(s,l)=gprint(s)" 18 | 19 | [env:native] 20 | platform = native 21 | 22 | lib_ldf_mode = chain+ 23 | build_flags = 24 | -DLUA_32BITS=1 25 | -D"lua_writestring(s,l)=gprint(s)" 26 | -D"lua_writestringerror(s,l)=gprint(s)" 27 | 28 | -------------------------------------------------------------------------------- /src/main.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Satisfy the IDE, which needs to see the include statment in the ino too. 5 | #ifdef dobogusinclude 6 | #include 7 | #endif 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | //#include 14 | //#include 15 | 16 | #define LGFX_M5STACK 17 | #include 18 | 19 | #include 20 | #include 21 | //#include 22 | #include 23 | #include 24 | 25 | 26 | LGFX lcd; 27 | const int fontSize = 1; 28 | const int fontPointH = 12; 29 | const int fontPointW = 6; 30 | Task* app; 31 | //KanjiEditor editor; 32 | LuaShell luaShell; 33 | Shell shell; 34 | ChrScreen chrScreen; 35 | KanjiFep fep; 36 | 37 | void draw(){ 38 | app->draw(); 39 | fep.draw(); 40 | chrScreen.draw(lcd); 41 | } 42 | 43 | class KbdRptParser : public KeyboardReportParser 44 | { 45 | void PrintKey(uint8_t mod, uint8_t key); 46 | 47 | protected: 48 | void OnControlKeysChanged(uint8_t before, uint8_t after); 49 | 50 | void OnKeyDown (uint8_t mod, uint8_t key); 51 | void OnKeyUp (uint8_t mod, uint8_t key); 52 | void OnKeyPressed(uint8_t c); 53 | }; 54 | 55 | void KbdRptParser::PrintKey(uint8_t m, uint8_t key) 56 | { 57 | MODIFIERKEYS mod; 58 | *((uint8_t*)&mod) = m; 59 | Serial.print((mod.bmLeftCtrl == 1) ? "C" : " "); 60 | Serial.print((mod.bmLeftShift == 1) ? "S" : " "); 61 | Serial.print((mod.bmLeftAlt == 1) ? "A" : " "); 62 | Serial.print((mod.bmLeftGUI == 1) ? "G" : " "); 63 | 64 | Serial.print(" >"); 65 | PrintHex(key, 0x80); 66 | Serial.print("< "); 67 | 68 | Serial.print((mod.bmRightCtrl == 1) ? "C" : " "); 69 | Serial.print((mod.bmRightShift == 1) ? "S" : " "); 70 | Serial.print((mod.bmRightAlt == 1) ? "A" : " "); 71 | Serial.println((mod.bmRightGUI == 1) ? "G" : " "); 72 | }; 73 | 74 | void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) 75 | { 76 | Serial.print("DN "); 77 | PrintKey(mod, key); 78 | uint8_t c = OemToAscii(mod, key); 79 | uint8_t ctrl = (mod & 0x11); 80 | app->onkeydown(key, c, ctrl); 81 | 82 | if(app->nextTask != NULL){ 83 | Serial.println("start nextTask"); 84 | Task* p = app; 85 | app = app->nextTask; 86 | fep.setParentTask(app); 87 | p->nextTask = NULL; 88 | } 89 | if(app->isTerminate){ // TODO: shell is special app like `init` 90 | Serial.println("terminated"); 91 | app->isTerminate = false; // for next execute 92 | //shell.init(); // clear terminal 93 | app = &shell; 94 | fep.setParentTask(app); 95 | } 96 | draw(); 97 | } 98 | 99 | void KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) { 100 | 101 | MODIFIERKEYS beforeMod; 102 | *((uint8_t*)&beforeMod) = before; 103 | 104 | MODIFIERKEYS afterMod; 105 | *((uint8_t*)&afterMod) = after; 106 | 107 | if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl) { 108 | Serial.println("LeftCtrl changed"); 109 | } 110 | if (beforeMod.bmLeftShift != afterMod.bmLeftShift) { 111 | Serial.println("LeftShift changed"); 112 | } 113 | if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt) { 114 | Serial.println("LeftAlt changed"); 115 | } 116 | if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI) { 117 | Serial.println("LeftGUI changed"); 118 | } 119 | 120 | if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl) { 121 | Serial.println("RightCtrl changed"); 122 | } 123 | if (beforeMod.bmRightShift != afterMod.bmRightShift) { 124 | Serial.println("RightShift changed"); 125 | } 126 | if (beforeMod.bmRightAlt != afterMod.bmRightAlt) { 127 | Serial.println("RightAlt changed"); 128 | } 129 | if (beforeMod.bmRightGUI != afterMod.bmRightGUI) { 130 | Serial.println("RightGUI changed"); 131 | } 132 | 133 | } 134 | 135 | void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key) 136 | { 137 | Serial.print("UP "); 138 | PrintKey(mod, key); 139 | } 140 | 141 | void KbdRptParser::OnKeyPressed(uint8_t c) 142 | { 143 | /* no use */ 144 | Serial.print("ASCII: "); 145 | Serial.println((char)c); 146 | //M5.Lcd.print((char)c); 147 | 148 | draw(); 149 | }; 150 | 151 | USB Usb; 152 | //USBHub Hub(&Usb); 153 | HIDBoot HidKeyboard(&Usb); 154 | 155 | KbdRptParser Prs; 156 | 157 | void setup() 158 | { 159 | Serial.begin( 115200 ); 160 | //M5.begin(); 161 | SPIFFS.begin(); 162 | lcd.init(); 163 | lcd.setTextSize(fontSize, fontSize); 164 | lcd.setTextColor(0xFFFFFFU); 165 | //lcd.setFont(&fonts::efont); 166 | lcd.setFont(&fonts::efontJA_12); 167 | //M5.Lcd.setTextSize(2); 168 | #if !defined(__MIPSEL__) 169 | while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection 170 | #endif 171 | Serial.println("Start"); 172 | 173 | if (Usb.Init() == -1) 174 | Serial.println("OSC did not start."); 175 | 176 | delay( 200 ); 177 | 178 | HidKeyboard.SetReportParser(0, &Prs); 179 | lcd.println("Start"); 180 | 181 | chrScreen.init(320, 240); 182 | 183 | fep.setChrScreen(&chrScreen); 184 | fep.init(); 185 | 186 | //editor.setChrScreen(&chrScreen); 187 | //editor.setFep(&fep); 188 | //editor.init(); 189 | 190 | luaShell.setChrScreen(&chrScreen); 191 | luaShell.setFep(&fep); 192 | luaShell.lua.lgfx = &lcd; // TODO: setter 193 | //luaShell.init(); 194 | //shell.editor = &editor; 195 | 196 | shell.setChrScreen(&chrScreen); 197 | shell.setFep(&fep); 198 | shell.luaShell = &luaShell; 199 | shell.init(); 200 | 201 | app = &shell; 202 | fep.setParentTask(app); 203 | 204 | lcd.clear(BLACK); 205 | draw(); 206 | } 207 | 208 | void loop() 209 | { 210 | Usb.Task(); 211 | } 212 | -------------------------------------------------------------------------------- /test/test_native/test_chrscreen.h: -------------------------------------------------------------------------------- 1 | #include 2 | //#include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | ChrScreen chrScreen; 9 | 10 | void chrscreen_function_true(void){ 11 | TEST_ASSERT_TRUE(true); 12 | } 13 | 14 | void chrscreen_simple(void){ 15 | chrScreen.init(10*6, 40*12); 16 | TEST_ASSERT_EQUAL_INT32_MESSAGE(40, chrScreen.lines.size(), "lines size"); 17 | TEST_ASSERT_EQUAL_INT32_MESSAGE(10, chrScreen.lines.begin()->size(), "line size"); 18 | TEST_ASSERT_EQUAL_INT32_MESSAGE(10, chrScreen.getMaxColumn(), "column size"); 19 | 20 | chrScreen.setCursor(0, 0); 21 | chrScreen.putChar(L'a', 0, 0); 22 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'a', chrScreen.getChar(0, 0), "put/get char"); 23 | 24 | chrScreen.putChar(L'あ', 0, 0); 25 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'あ', chrScreen.getChar(1, 0), "put/get wide char"); 26 | 27 | chrScreen.setCursor(0, 0); 28 | chrScreen.putString((wchar_t*)L"test", 0, 0); 29 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L't', chrScreen.getChar(0, 0), "putString"); 30 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'e', chrScreen.getChar(1, 0), "putString"); 31 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L's', chrScreen.getChar(2, 0), "putString"); 32 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L't', chrScreen.getChar(3, 0), "putString"); 33 | } 34 | 35 | void chrscreen_test(){ 36 | RUN_TEST(chrscreen_function_true); 37 | RUN_TEST(chrscreen_simple); 38 | } 39 | -------------------------------------------------------------------------------- /test/test_native/test_editor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | //#include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "test_chrscreen.h" 9 | #include "test_util.h" 10 | 11 | using namespace std; 12 | //KanjiEditor editor; 13 | KanjiFep fep; 14 | Shell shell; 15 | 16 | void test_function_true(void){ 17 | TEST_ASSERT_TRUE(true); 18 | } 19 | /* 20 | int getMaxLines(){ 21 | return editor.lines.size(); 22 | } 23 | */ 24 | int getColumnNo(){ 25 | return fep.colItr - fep.line.begin(); 26 | } 27 | /* 28 | int getLineNo(){ 29 | return editor.line - editor.lines.begin(); 30 | } 31 | */ 32 | 33 | void test_fep_init(void){ 34 | chrScreen.init(320, 240); 35 | shell.setChrScreen(&chrScreen); 36 | fep.setChrScreen(&chrScreen); 37 | fep.setParentTask(&shell); 38 | shell.init(); 39 | fep.init(); 40 | //fep.setParentTask(&shell); 41 | // int col is 0 42 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, getColumnNo(), "column 0 in initial"); 43 | } 44 | 45 | /* 46 | void test_editor_move_cursor(void){ 47 | editor.backSpace(); 48 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, getLineNo(), "check line after bs"); 49 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, getColumnNo(), "check column after bs"); 50 | 51 | editor.onChar('a'); 52 | editor.onChar('b'); 53 | editor.onChar('c'); 54 | // cursor pos 55 | TEST_ASSERT_EQUAL_INT32_MESSAGE(3, getColumnNo(), "type 3 keys"); 56 | editor.enter(); 57 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, getLineNo(), "enter"); 58 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, getColumnNo(), "enter"); 59 | 60 | editor.onChar('1'); 61 | editor.onChar('2'); 62 | editor.onChar('3'); 63 | editor.onChar('4'); 64 | editor.up(); 65 | TEST_ASSERT_EQUAL_INT32_MESSAGE(3, getColumnNo(), "type 4 keys in next line and up"); 66 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, getLineNo(), "check line after up"); 67 | TEST_ASSERT_EQUAL_INT32_MESSAGE(3, getColumnNo(), "check column after up"); 68 | editor.down(); 69 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, getLineNo(), "check line after down"); 70 | TEST_ASSERT_EQUAL_INT32_MESSAGE(3, getColumnNo(), "check column after down"); 71 | 72 | editor.right(); 73 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, getLineNo(), "check line after right"); 74 | TEST_ASSERT_EQUAL_INT32_MESSAGE(4, getColumnNo(), "check column after right"); 75 | 76 | editor.right(); 77 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, getLineNo(), "check line after over right"); 78 | TEST_ASSERT_EQUAL_INT32_MESSAGE(4, getColumnNo(), "check column after over right"); 79 | 80 | editor.backSpace(); 81 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, getLineNo(), "check line after back-space"); 82 | TEST_ASSERT_EQUAL_INT32_MESSAGE(3, getColumnNo(), "check cloumn after back-space"); 83 | 84 | editor.right(); 85 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, getLineNo(), "check line after back-space and right"); 86 | TEST_ASSERT_EQUAL_INT32_MESSAGE(3, getColumnNo(), "check line after back-space and right"); 87 | 88 | editor.left(); 89 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, getLineNo(), "check line after left"); 90 | TEST_ASSERT_EQUAL_INT32_MESSAGE(2,getColumnNo(), "check line after left"); 91 | 92 | editor.left(); 93 | editor.left(); 94 | editor.left(); 95 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, getLineNo(), "check line after over left"); 96 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, getColumnNo(), "check line after over left"); 97 | 98 | editor.onChar('0'); 99 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, getLineNo(), "check line after insert char"); 100 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1,getColumnNo(), "check line after insert char"); 101 | 102 | editor.left(); 103 | editor.backSpace(); 104 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, getLineNo(), "check line after merge line"); 105 | TEST_ASSERT_EQUAL_INT32_MESSAGE(3, getColumnNo(), "check column after merge line"); 106 | 107 | string stdString(editor.lines.at(0).begin(),editor.lines.at(0).end()); 108 | const char *cstr = stdString.c_str(); 109 | TEST_ASSERT_EQUAL_STRING_MESSAGE("abc0123", cstr, "check line0"); 110 | } 111 | */ 112 | 113 | void test_fep_rome_conversion(void){ 114 | fep.init(); 115 | 116 | fep.onCharRoma('a', false); 117 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'あ', shell.rawInputs.at(0), "rome a"); 118 | fep.backSpace(); 119 | 120 | fep.onCharRoma('k'); 121 | fep.onCharRoma('a'); 122 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'か', shell.rawInputs.at(0), "rome ka"); 123 | 124 | fep.backSpace(); 125 | fep.onCharRoma('s'); 126 | fep.onCharRoma('s'); 127 | fep.onCharRoma('a'); 128 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'っ', shell.rawInputs.at(0), "rome ssa"); 129 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'さ', shell.rawInputs.at(1), "rome ssa"); 130 | 131 | fep.backSpace(); 132 | fep.backSpace(); 133 | 134 | fep.onCharRoma('n'); 135 | fep.onCharRoma('n'); 136 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'ん', shell.rawInputs.at(0), "rome nn"); 137 | 138 | fep.backSpace(); 139 | 140 | fep.onCharRoma('y'); 141 | fep.onCharRoma('a'); 142 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'や', shell.rawInputs.at(0), "rome ya"); 143 | 144 | fep.backSpace(); 145 | 146 | 147 | fep.onCharRoma('n'); 148 | fep.onCharRoma('y'); 149 | fep.onCharRoma('a'); 150 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'に', shell.rawInputs.at(0), "rome nya1"); 151 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'ゃ', shell.rawInputs.at(1), "rome nya2"); 152 | } 153 | 154 | void test_dicttool_search(){ 155 | vector results; 156 | search((char*)"わるs", &results,(char*)"data/SKK-JISYO.S.txt"); 157 | //printf("result: %s", results); 158 | TEST_ASSERT_EQUAL_STRING_MESSAGE("悪", results.at(0).c_str(), "check result"); 159 | 160 | results.clear(); 161 | search((char*)"れき", &results,(char*)"data/SKK-JISYO.S.txt"); 162 | //printf("result: %x\n", results.at(0).c_str()[0]); 163 | TEST_ASSERT_EQUAL_STRING_MESSAGE("歴", results.at(0).c_str(), "check result2"); 164 | TEST_ASSERT_EQUAL_STRING_MESSAGE("暦", results.at(1).c_str(), "check result2-2"); 165 | 166 | results.clear(); 167 | search((char*)"わん", &results,(char*)"data/SKK-JISYO.S.txt"); 168 | //printf("result: %s", results); 169 | TEST_ASSERT_EQUAL_STRING_MESSAGE("腕", results.at(0).c_str(), "check result3"); 170 | TEST_ASSERT_EQUAL_STRING_MESSAGE("碗", results.at(1).c_str(), "check result3-2"); 171 | TEST_ASSERT_EQUAL_STRING_MESSAGE("湾", results.at(2).c_str(), "check result3-3"); 172 | TEST_ASSERT_EQUAL_STRING_MESSAGE("椀", results.at(3).c_str(), "check result3-4"); 173 | 174 | } 175 | 176 | void test_henkan(){ 177 | fep.init(); 178 | fep.dictPath = "data/SKK-JISYO.S.txt"; 179 | 180 | fep.kanjiMode = KanjiMode::KANJI; 181 | fep.onCharRoma('w'); 182 | fep.onCharRoma('a'); 183 | fep.onCharRoma('n'); 184 | fep.onCharRoma('n'); 185 | fep.onCharRoma(' '); 186 | 187 | TEST_ASSERT_EQUAL_INT32_MESSAGE(4, fep.kanjiList.size(), "check kanjiList size"); 188 | 189 | TEST_ASSERT_EQUAL_STRING_MESSAGE("腕",fep.kanjiList.at(0).c_str(), "check result"); 190 | 191 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, fep.kanjiListItr - fep.kanjiList.begin(), "check kanjiList position"); 192 | fep.nextKanji(); 193 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, fep.kanjiListItr - fep.kanjiList.begin(), "check kanjiList position after next"); 194 | fep.prevKanji(); 195 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, fep.kanjiListItr - fep.kanjiList.begin(), "check kanjiList position after prev"); 196 | fep.prevKanji(); 197 | TEST_ASSERT_EQUAL_INT32_MESSAGE(3, fep.kanjiListItr - fep.kanjiList.begin(), "check kanjiList position after first prev"); 198 | fep.nextKanji(); 199 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, fep.kanjiListItr - fep.kanjiList.begin(), "check kanjiList position last next"); 200 | fep.enter(); 201 | } 202 | 203 | void test_henkan2(){ 204 | fep.init(); 205 | fep.dictPath = "data/SKK-JISYO.S.txt"; 206 | 207 | fep.onCharRoma('K'); 208 | fep.onCharRoma('a'); 209 | fep.onCharRoma('n'); 210 | fep.onCharRoma('n'); 211 | fep.onCharRoma('g'); 212 | fep.onCharRoma('a'); 213 | fep.onCharRoma('E'); 214 | 215 | TEST_ASSERT_EQUAL_INT32_MESSAGE(1, fep.kanjiList.size(), "check kanjiList size"); 216 | 217 | TEST_ASSERT_EQUAL_STRING_MESSAGE("考", fep.kanjiList.at(0).c_str(), "check result"); 218 | 219 | fep.enter(); 220 | TEST_ASSERT_EQUAL_INT32_MESSAGE(2, shell.rawInputs.size(), "check line[0]"); 221 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'考', shell.rawInputs.at(0), "rome kanga"); 222 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'え', shell.rawInputs.at(1), "rome e"); 223 | } 224 | 225 | void test_henkan_bs(){ 226 | fep.init(); 227 | fep.kanjiList.clear(); 228 | fep.kanjiListItr = fep.kanjiList.begin(); 229 | 230 | fep.rawInputs.clear(); 231 | fep.rawInputsItr = fep.rawInputs.begin(); 232 | 233 | fep.dictPath = "data/SKK-JISYO.S.txt"; 234 | 235 | 236 | fep.kanjiMode = KanjiMode::KANJI; 237 | fep.onCharRoma('w'); 238 | fep.onCharRoma('a'); 239 | fep.onCharRoma('n'); 240 | fep.onCharRoma('n'); 241 | 242 | TEST_ASSERT_EQUAL_INT32_MESSAGE(2, fep.rawInputs.size(), "check rawInputs"); 243 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'わ', fep.rawInputs.at(0), "check rawInputs"); 244 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'ん', fep.rawInputs.at(1), "check rawInputs"); 245 | fep.backSpace(); 246 | fep.backSpace(); 247 | 248 | TEST_ASSERT_EQUAL_INT32_MESSAGE(KanjiMode::ROME, fep.kanjiMode, "back to rome mode"); 249 | } 250 | 251 | void test_henkan_ng(){ 252 | fep.init(); 253 | fep.kanjiList.clear(); 254 | fep.kanjiListItr = fep.kanjiList.begin(); 255 | 256 | fep.rawInputs.clear(); 257 | fep.rawInputsItr = fep.rawInputs.begin(); 258 | 259 | fep.dictPath = "data/SKK-JISYO.S.txt"; 260 | 261 | 262 | fep.kanjiMode = KanjiMode::ROME; 263 | fep.onCharRoma('K'); 264 | TEST_ASSERT_EQUAL_INT32_MESSAGE(KanjiMode::KANJI, fep.kanjiMode, "go to kanji mode"); 265 | fep.onCharRoma('a'); 266 | fep.onCharRoma('k'); 267 | fep.onCharRoma('u'); 268 | fep.onCharRoma('K'); 269 | 270 | TEST_ASSERT_EQUAL_INT32_MESSAGE('k', fep.shiin1, "check shiin1"); 271 | TEST_ASSERT_EQUAL_INT32_MESSAGE(0, fep.rawInputs.size(), "check rawInputs"); 272 | 273 | TEST_ASSERT_EQUAL_INT32_MESSAGE(2, shell.rawInputs.size(), "check line[0]"); 274 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'か', shell.rawInputs.at(0), "rome ka"); 275 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'く', shell.rawInputs.at(1), "rome ku"); 276 | TEST_ASSERT_EQUAL_INT32_MESSAGE(KanjiMode::ROME, fep.kanjiMode, "back to rome mode"); 277 | } 278 | 279 | void test_kata(){ 280 | fep.init(); 281 | 282 | fep.onCharRoma('q'); 283 | fep.onCharRoma('a'); 284 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'ア', shell.rawInputs.at(0), "rome a"); 285 | fep.backSpace(); 286 | 287 | fep.onCharRoma('k'); 288 | fep.onCharRoma('a'); 289 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'カ', shell.rawInputs.at(0), "rome ka"); 290 | } 291 | void test_direct(){ 292 | fep.init(); 293 | fep.kanjiMode = KanjiMode::ROME; 294 | 295 | fep.onCharRoma('l'); 296 | fep.onChar('a'); 297 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'a', shell.rawInputs.at(0), "direct a"); 298 | } 299 | 300 | void setUp(void) { 301 | // set stuff up here 302 | } 303 | 304 | void tearDown(void) { 305 | // clean stuff up here 306 | } 307 | 308 | int main(int argc, char **argv){ 309 | RUN_TEST(test_function_true); 310 | RUN_TEST(test_fep_init); 311 | //RUN_TEST(test_editor_move_cursor); 312 | RUN_TEST(test_fep_rome_conversion); 313 | RUN_TEST(test_kata); 314 | RUN_TEST(test_direct); 315 | 316 | RUN_TEST(test_dicttool_search); 317 | RUN_TEST(test_henkan); 318 | RUN_TEST(test_henkan2); 319 | RUN_TEST(test_henkan_ng); 320 | RUN_TEST(test_henkan_bs); 321 | chrscreen_test(); 322 | util_test(); 323 | } 324 | -------------------------------------------------------------------------------- /test/test_native/test_util.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | void util_function_true(void){ 9 | TEST_ASSERT_TRUE(true); 10 | } 11 | 12 | void util_simple(void){ 13 | vector> result; 14 | vector target; 15 | target.push_back(L't'); 16 | target.push_back(L'e'); 17 | target.push_back(L's'); 18 | target.push_back(L't'); 19 | target.push_back(L' '); 20 | target.push_back(L'f'); 21 | target.push_back(L'o'); 22 | target.push_back(L'o'); 23 | result = split(target); 24 | TEST_ASSERT_EQUAL_INT32_MESSAGE(2, result.size(), "split size"); 25 | vector arg1 = result.at(0); 26 | vector arg2 = result.at(1); 27 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L't', arg1.at(0), "split 0,0"); 28 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'e', arg1.at(1), "split 0,1"); 29 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L's', arg1.at(2), "split 0,2"); 30 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L't', arg1.at(3), "split 0,3"); 31 | 32 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'f', arg2.at(0), "split 1,0"); 33 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'o', arg2.at(1), "split 1,1"); 34 | TEST_ASSERT_EQUAL_INT16_MESSAGE(L'o', arg2.at(2), "split 1,2"); 35 | 36 | } 37 | 38 | void util_test(){ 39 | RUN_TEST(util_function_true); 40 | RUN_TEST(util_simple); 41 | } 42 | --------------------------------------------------------------------------------