diff --git a/kernel/HTMLRenderer.cpp b/kernel/EventHandler.cpp similarity index 100% rename from kernel/HTMLRenderer.cpp rename to kernel/EventHandler.cpp diff --git a/kernel/EventHandler.h b/kernel/EventHandler.h new file mode 100644 index 0000000..2c01c76 --- /dev/null +++ b/kernel/EventHandler.h @@ -0,0 +1,18 @@ +#pragma once + +using MouseEventHandler = void (*)(MouseEvent&); + +struct MouseEvent { +public: + Sheet *sheet; + Task *task; + + MouseEvent(Sheet *sht, Task *t); +}; + +struct TaskEvent { +public: + Task *task; + + TaskEvent(Task *t); +}; \ No newline at end of file diff --git a/kernel/GUI/GUITask.h b/kernel/GUI/GUITask.h new file mode 100644 index 0000000..b4ebb43 --- /dev/null +++ b/kernel/GUI/GUITask.h @@ -0,0 +1,26 @@ +#pragma once + +class Screen { +private: + static union VRAM { + unsigned short *p16; + unsigned char (*p24)[3]; + unsigned int *p32; + } vram; + Container container; + + Screen() : container() { + // BootInfo から + + // 描画 + } + void refresh() { + // container の buf を vram に書き写し + } + +public: + Screen &getInstance() { + static Screen instance = Screen(); + return instance; + } +}; diff --git a/kernel/GUI/LLGraphics.h b/kernel/GUI/LLGraphics.h new file mode 100644 index 0000000..1e529fa --- /dev/null +++ b/kernel/GUI/LLGraphics.h @@ -0,0 +1,14 @@ +#pragma once + +namespace LLG { + drawLine + drawRect + drawCircle + fillRect + fillCircle + gradLine + gradRect + gradCircle + drawString + drawCharacter +}; diff --git a/kernel/GUI/ModalWindow.cpp b/kernel/GUI/ModalWindow.cpp new file mode 100644 index 0000000..64446b9 --- /dev/null +++ b/kernel/GUI/ModalWindow.cpp @@ -0,0 +1,23 @@ +#include "../headers.h" + +ModalWindow::ModalWindow(const string &msg) : sheet(new Sheet(Size(480, 300))), message(msg) { + // background and message + sheet->fillRect(sheet->frame, 0xffffff); + sheet->drawString(msg, Point((480 - msg.length() * 8) / 2, ((300 - 48 - 10) - 16) / 2), 0); + + // button + Sheet *button = new Sheet(Size(64, 48), true); + button->fillRect(button->frame, 0xaaaaaa); + button->drawString("OK", Point((button->frame.size.width - 16) / 2, (button->frame.size.height - 16) / 2), 0); + button->borderRadius(true, true, true, true); + button->moveTo(Point((sheet->frame.size.width - button->frame.size.width) / 2, sheet->frame.size.height - button->frame.size.height - 10)); + sheet->appendChild(button, true); + button->onClick = [](const Point &pos, Sheet &sheet) { + delete sheet.parent; + }; + + // show this window + sheet->moveTo(Point((SheetCtl::resolution.width - 480) / 2, (SheetCtl::resolution.height - 300) / 2)); + SheetCtl::back->appendChild(sheet); + sheet->upDown(1); +} diff --git a/kernel/GUI/ModalWindow.h b/kernel/GUI/ModalWindow.h new file mode 100644 index 0000000..49eb490 --- /dev/null +++ b/kernel/GUI/ModalWindow.h @@ -0,0 +1,11 @@ +#pragma once +#include + +class ModalWindow { +private: + unique_ptr sheet; + string message; + +public: + ModalWindow(const string &msg); +}; diff --git a/kernel/GUI/Tab.cpp b/kernel/GUI/Tab.cpp new file mode 100644 index 0000000..5441f25 --- /dev/null +++ b/kernel/GUI/Tab.cpp @@ -0,0 +1,105 @@ +#include "../headers.h" + +Tab::Tab(const string &tabName) : index(SheetCtl::numOfTab++), tabBar(new Sheet(Size(150 - 2, 22), true)), sheet(new Sheet(Size(SheetCtl::resolution.width - 150, SheetCtl::resolution.height), false)), name(tabName) { + // タブ一覧に登録 + SheetCtl::tabs[index] = this; + + // 基本デザイン + sheet->fillRect(sheet->frame, 0xffffff); + sheet->drawRect(sheet->frame, 0); + sheet->moveTo(Point(150, 0)); + SheetCtl::back->appendChild(sheet); + + // タブバー初期化の続き + tabBar->tab = this; + tabBar->onClick = [](const Point &pos, Sheet &sht) { + if (2 <= pos.x && pos.x <= 2 + 22) { + // 閉じる + delete sht.tab; + } else if (sht.tab->index != SheetCtl::activeTab && 2 <= pos.x) { + // アクティブ化 + sht.tab->active(); + } + }; + Rectangle newTabRange(2, 35 + 23 * index, 150 - 2, 22); + tabBar->fillRect(tabBar->frame, kActiveTabColor); + tabBar->borderRadius(true, false, true, false); + tabBar->drawLine(Line(7, 7, 15, 15), kActiveTextColor); + tabBar->drawLine(Line(7, 15, 15, 7), kActiveTextColor); + tabBar->drawString(string(tabName, 0, 15), Point(4 + 22, 4), kActiveTextColor); + tabBar->moveTo(Point(2, 35 + 23 * index)); + SheetCtl::back->appendChild(tabBar); + tabBar->upDown(1); + + // タブ切り替え + active(); +} + +Tab::Tab(const string &tabName, void (*mainLoop)(Tab *)) : Tab(tabName) { + int args[] = { (int)this }; + _task = new Task(name, 3, 2, mainLoop, args); +} +Tab::Tab(const string &tabName, int queueSize, void (*mainLoop)(Tab *)) : Tab(tabName) { + int args[] = { (int)this }; + _task = new Task(name, 3, 2, queueSize, mainLoop, args); +} + +Tab::~Tab() { + // アクティブタブの調整 + if (SheetCtl::activeTab == index) { + if (SheetCtl::numOfTab > 1) { + // アクティブタブでかつ他のタブがあれば,他のタブへ切り替える + if (SheetCtl::numOfTab - 1 == index) { + SheetCtl::tabs[index - 1]->active(); + } else { + // 次のタブを選ばせる + SheetCtl::tabs[(SheetCtl::activeTab + 1) % SheetCtl::numOfTab]->active(); + } + } else { + // 最後の1つのタブなら,SheetCtl::activeTab を -1 へ + SheetCtl::activeTab = -1; + } + } + if (SheetCtl::activeTab > index) { + --SheetCtl::activeTab; + } + + // タブを閉じる + delete tabBar; + + // タブをずらす + for (int i = index; i < SheetCtl::numOfTab - 1; ++i) { + SheetCtl::tabs[i] = SheetCtl::tabs[i + 1]; + --SheetCtl::tabs[i]->index; + SheetCtl::tabs[i]->tabBar->moveTo(Point(SheetCtl::tabs[i]->tabBar->frame.offset.x, SheetCtl::tabs[i]->tabBar->frame.offset.y - 23)); + } + + // タブの個数を減らす + --SheetCtl::numOfTab; + + // その他の解放 + delete sheet; + if (_task) delete _task; +} + +void Tab::active() { + if (SheetCtl::activeTab == index) return; + + Rectangle tabRect(0, 0, tabBar->frame.size.width, tabBar->frame.size.height); + + // 新しくアクティブになるタブ + tabBar->changeColor(tabRect, kPassiveTabColor, kActiveTabColor); + tabBar->changeColor(tabRect, kPassiveTextColor, kActiveTextColor); + tabBar->refresh(tabRect); + sheet->upDown(1); + + // アクティブだったタブ + if (SheetCtl::activeTab >= 0) { + SheetCtl::tabs[SheetCtl::activeTab]->tabBar->changeColor(tabRect, kActiveTabColor, kPassiveTabColor); + SheetCtl::tabs[SheetCtl::activeTab]->tabBar->changeColor(tabRect, kActiveTextColor, kPassiveTextColor); + SheetCtl::tabs[SheetCtl::activeTab]->tabBar->refresh(tabRect); + SheetCtl::tabs[SheetCtl::activeTab]->sheet->upDown(-1); + } + + SheetCtl::activeTab = index; +} diff --git a/kernel/GUI/Tab.h b/kernel/GUI/Tab.h new file mode 100644 index 0000000..5803532 --- /dev/null +++ b/kernel/GUI/Tab.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +class Tab { +private: + Task *_task = nullptr; + int index; + Sheet *tabBar; + + Tab(const string &tabName); + +public: + Sheet *sheet; + string name; + Task *const &task = _task; + + Tab(const string &tabName, void (*mainLoop)(Tab *)); + Tab(const string &tabName, int queueSize, void (*mainLoop)(Tab *)); + ~Tab(); + void active(); +}; diff --git a/kernel/GUI/Widget.h b/kernel/GUI/Widget.h new file mode 100644 index 0000000..c7cb114 --- /dev/null +++ b/kernel/GUI/Widget.h @@ -0,0 +1,9 @@ +#pragma once + +class Widget { + +}; + +class Container : public Widget { + +}; diff --git a/kernel/GUI/graphic.cpp b/kernel/GUI/graphic.cpp new file mode 100644 index 0000000..c727928 --- /dev/null +++ b/kernel/GUI/graphic.cpp @@ -0,0 +1,1225 @@ +#include +#include +#include +#include +#include "../headers.h" +#include "HTMLTokenizer.h" +#include "HTMLTreeConstructor.h" + +Sheet::Sheet(const Size &size, bool _nonRect) : +_frame(size), +nonRect(_nonRect), +buf(new unsigned int[size.getArea()]) {} + +Sheet::~Sheet() { + // 子シートを delete + while (!children.empty()) { + delete _children.front(); + _children.pop_front(); + } + + // 自分を非表示 + if (_parent && find(_parent->children.begin(), _parent->children.end(), this) != _parent->children.end()) + upDown(-1); + + // onClosed 実行 + if (onClosed) onClosed(); + + // buf 解放 + delete[] buf; +} + +// シートの高さを変更 +void Sheet::upDown(int zIndex) { + // 非表示にする + if (zIndex < 0) { + _parent->_children.remove(this); + SheetCtl::refreshMap(frame/*, 0*/); + SheetCtl::refreshSub(frame); + return; + } + + // 現在の index + auto old = find(_parent->children.begin(), _parent->children.end(), this); + + // 指定された index + auto it = _parent->children.begin(); + for (; it != _parent->children.end() && zIndex > 0; ++it, --zIndex) {} + + if (old != _parent->children.end()) { + // 現在表示されており,変更を求められている + _parent->_children.erase(it); + if (it != _parent->children.end()) { // 表示 + _parent->_children.insert(it, this); + SheetCtl::refreshMap(frame/*, z + 1*/); + SheetCtl::refreshSub(frame); + } else { // 非表示にする + SheetCtl::refreshMap(frame);//, 0); + SheetCtl::refreshSub(frame); + } + } else { + // 現在非表示 + _parent->_children.insert(it, this); + SheetCtl::refreshMap(frame);//, z); + SheetCtl::refreshSub(frame); + } +} + +// シートのリフレッシュ +void Sheet::refresh(Rectangle range) const { + if (this == SheetCtl::back || (_parent && find(_parent->children.begin(), _parent->children.end(), this) != _parent->children.end())) { // 非表示シートはリフレッシュしない + // 画面上でのオフセット計算 + for (auto p = this; p != nullptr; p = p->_parent) { + range.slide(p->frame.offset); + } + SheetCtl::refreshMap(range);//, zIndex); + SheetCtl::refreshSub(range); + } +} + +// シートを移動 +void Sheet::moveTo(const Point &pos) { + Rectangle oldFrame(frame); + _frame.offset = pos; + if (_parent && find(_parent->children.begin(), _parent->children.end(), this) != _parent->children.end()) { // 非表示シートはリフレッシュしない + SheetCtl::refreshMap(oldFrame);//, 0); + SheetCtl::refreshMap(frame);//, zIndex); + SheetCtl::refreshSub(oldFrame); + SheetCtl::refreshSub(frame); + } +} + +// 子シートを追加 +void Sheet::appendChild(Sheet *child, bool show) { + child->_parent = this; + if (show) { + _children.push_front(child); + child->refresh(Rectangle(0, 0, child->frame.size.width, child->frame.size.height)); + } +} + +// 単色直線を描画 +void Sheet::drawLine(const Line &line, unsigned int color) { + int x, y, dx, dy, len; + + // 縦横の直線高速化 + if (line.start.y == line.end.y) { + for (x = line.start.x; x <= line.end.x; ++x) { + buf[line.start.y * frame.size.width + x] = color; + } + return; + } else if (line.start.x == line.end.x) { + for (y = line.start.y; y <= line.end.y; ++y) { + buf[y * frame.size.width + line.start.x] = color; + } + return; + } + + dx = line.end.x - line.start.x > 0 ? line.end.x - line.start.x : line.start.x - line.end.x; + dy = line.end.y - line.start.y > 0 ? line.end.y - line.start.y : line.start.y - line.end.y; + x = line.start.x << 10; + y = line.start.y << 10; + if (dx >= dy) { + len = dx + 1; + dx = line.end.x - line.start.x < 0 ? -1024 : 1024; + dy = line.end.y - line.start.y >= 0 ? ((line.end.y - line.start.y + 1) << 10) / len + : ((line.end.y - line.start.y - 1) << 10) / len; + } else { + len = dy + 1; + dy = line.end.y - line.start.y < 0 ? -1024 : 1024; + dx = line.end.x - line.start.x >= 0 ? ((line.end.x - line.start.x + 1) << 10) / len + : ((line.end.x - line.start.x - 1) << 10) / len; + } + + for (int i = 0; i < len; ++i) { + buf[(y >> 10) * frame.size.width + (x >> 10)] = color; + x += dx; + y += dy; + } +} + +// グラデーション直線を描画 +void Sheet::gradLine(const Line &line, unsigned int col0, unsigned int col1, GradientDirection direction) { + int x, y, dx, dy, len; + + dx = line.end.x - line.start.x > 0 ? line.end.x - line.start.x : line.start.x - line.end.x; + dy = line.end.y - line.start.y > 0 ? line.end.y - line.start.y : line.start.y - line.end.y; + x = line.start.x << 10; + y = line.start.y << 10; + if (dx >= dy) { + len = dx + 1; + dx = line.end.x - line.start.x < 0 ? -1024 : 1024; + dy = line.end.y - line.start.y >= 0 ? ((line.end.y - line.start.y + 1) << 10) / len + : ((line.end.y - line.start.y - 1) << 10) / len; + } else { + len = dy + 1; + dy = line.end.y - line.start.y < 0 ? -1024 : 1024; + dx = line.end.x - line.start.x >= 0 ? ((line.end.x - line.start.x + 1) << 10) / len + : ((line.end.x - line.start.x - 1) << 10) / len; + } + + if (direction == GradientDirection::LeftToRight) { // 横 + if (line.start.x == line.end.x) { // 高速 + for (int i = 0; i < len; ++i) { + buf[(y >> 10) * frame.size.width + (x >> 10)] = col0; + x += dx; + y += dy; + } + } else { + for (int i = 0; i < len; ++i) { + buf[(y >> 10) * frame.size.width + (x >> 10)] = GetGrad(line.start.x, line.end.x, x >> 10, col0, col1); + x += dx; + y += dy; + } + } + } else if (direction == GradientDirection::TopToBottom) { // 縦 + if (line.start.y == line.end.y) { // 高速 + for (int i = 0; i < len; ++i) { + buf[(y >> 10) * frame.size.width + (x >> 10)] = col0; + x += dx; + y += dy; + } + } else { + for (int i = 0; i < len; ++i) { + buf[(y >> 10) * frame.size.width + (x >> 10)] = GetGrad(line.start.y, line.end.y, y >> 10, col0, col1); + x += dx; + y += dy; + } + } + } +} + +// 枠のみ長方形を描画 +void Sheet::drawRect(const Rectangle &rect, unsigned int color) { + int endy = rect.getEndPoint().y - 1; + for (int x = 0; x < rect.size.width; ++x) { + // 上の辺を描画 + buf[rect.offset.y * frame.size.width + x + rect.offset.x] = color; + // 下の辺を描画 + buf[endy * frame.size.width + x + rect.offset.x] = color; + } + int endx = rect.getEndPoint().x - 1; + for (int y = 1; y < rect.size.height - 1; ++y) { + // 左の辺を描画 + buf[(y + rect.offset.y) * frame.size.width + rect.offset.x] = color; + // 右の辺を描画 + buf[(y + rect.offset.y) * frame.size.width + endx] = color; + } +} + +// 塗りつぶし長方形を描画 +void Sheet::fillRect(const Rectangle &rect, unsigned int color) { + for (int y = 0; y < rect.size.height; ++y) { + int by = y + rect.offset.y; + for (int x = 0; x < rect.size.width; ++x) { + buf[by * frame.size.width + x + rect.offset.x] = color; + } + } +} + +// グラデーション長方形を描画 +void Sheet::gradRect(const Rectangle &rect, unsigned int col0, unsigned int col1, GradientDirection direction) { + if (direction == GradientDirection::LeftToRight) { // 横 + for (int x = 0; x < rect.size.width; ++x) { + unsigned int gradColor = GetGrad(0, rect.size.width - 1, x, col0, col1); + for (int y = 0; y < rect.size.height; ++y) { + buf[(y + rect.offset.y) * frame.size.width + x + rect.offset.x] = gradColor; + } + } + } else if (direction == GradientDirection::TopToBottom) { //縦 + for (int y = 0; y < rect.size.height; ++y) { + unsigned int gradColor = GetGrad(0, rect.size.height - 1, y, col0, col1); + for (int x = 0; x < rect.size.width; ++x) { + buf[(y + rect.offset.y) * frame.size.width + x + rect.offset.x] = gradColor; + } + } + } +} + +// 枠のみ円を描画 +void Sheet::drawCircle(const Circle &cir, unsigned int color) { + int x = cir.radius; + int y = 0; + int F = -2 * cir.radius + 3; + /*if (直径 % 2 == 1) { + while (x >= y) { + buf[(yo + y) * frame.size.width + xo + x] = c; + buf[(yo + y) * frame.size.width + xo - x] = c; + buf[(yo - y) * frame.size.width + xo + x] = c; + buf[(yo - y) * frame.size.width + xo - x] = c; + buf[(yo + x) * frame.size.width + xo + y] = c; + buf[(yo + x) * frame.size.width + xo - y] = c; + buf[(yo - x) * frame.size.width + xo + y] = c; + buf[(yo - x) * frame.size.width + xo - y] = c; + if (F >= 0) { + --x; + F -= 4 * x; + } + ++y; + F += 4 * y + 2; + } + } else {*/ + while (x >= y) { + buf[(cir.center.y + y) * frame.size.width + cir.center.x + x - 1] = color; + buf[(cir.center.y + y) * frame.size.width + cir.center.x - x] = color; + buf[(cir.center.y - y) * frame.size.width + cir.center.x + x - 1] = color; + buf[(cir.center.y - y) * frame.size.width + cir.center.x - x] = color; + buf[(cir.center.y + x - 1) * frame.size.width + cir.center.x + y] = color; + buf[(cir.center.y + x - 1) * frame.size.width + cir.center.x - y] = color; + buf[(cir.center.y - x) * frame.size.width + cir.center.x + y] = color; + buf[(cir.center.y - x) * frame.size.width + cir.center.x - y] = color; + if (F >= 0) { + --x; + F -= 4 * x; + } + ++y; + F += 4 * y + 2; + } + //} +} + +// 塗りつぶし円を描画 +void Sheet::fillCircle(const Circle &cir, unsigned int color) { + int x = cir.radius; + int y = 0; + int F = -2 * cir.radius + 3; + /*if (d % 2 == 1) { + while (x >= y) { + for (int xx = xo - x; xx < xo + x; ++xx) { + sht->buf[(yo + y) * sht->frame.size.width + xx] = c; + sht->buf[(yo - y) * sht->frame.size.width + xx] = c; + } + for (int xx = xo - y; xx < xo + y; ++xx) { + sht->buf[(yo + x) * sht->frame.size.width + xx] = c; + sht->buf[(yo - x) * sht->frame.size.width + xx] = c; + } + if (F >= 0) { + --x; + F -= 4 * x; + } + ++y; + F += 4 * y + 2; + } + } else {*/ + while (x >= y) { + for (int xx = cir.center.x - x; xx < cir.center.x + x; ++xx) { + buf[(cir.center.y + y) * frame.size.width + xx] = color; + buf[(cir.center.y - y) * frame.size.width + xx] = color; + } + for (int xx = cir.center.x - y; xx < cir.center.x + y; ++xx) { + buf[(cir.center.y + x - 1) * frame.size.width + xx] = color; + buf[(cir.center.y - x) * frame.size.width + xx] = color; + } + if (F >= 0) { + --x; + F -= 4 * x; + } + ++y; + F += 4 * y + 2; + } + //} +} + +// TopToBottom グラデーションで塗りつぶされた円を描画 +void Sheet::gradCircle(const Circle &cir, unsigned int col0, unsigned int col1) { + int x = cir.radius; + int y = 0; + int F = -2 * cir.radius + 3; + /*if (d % 2 == 1) { + while (x >= y) { + for (int xx = xo - x; xx < xo + x; ++xx) { + sht->buf[(yo + y) * sht->frame.size.width + xx] = GetGrad(y0, y0 + d, yo + y, c0, c1); + sht->buf[(yo - y) * sht->frame.size.width + xx] = GetGrad(y0, y0 + d, yo - y, c0, c1); + } + for (int xx = xo - y; xx < xo + y; ++xx) { + sht->buf[(yo + x) * sht->frame.size.width + xx] = GetGrad(y0, y0 + d, yo + x, c0, c1); + sht->buf[(yo - x) * sht->frame.size.width + xx] = GetGrad(y0, y0 + d, yo - x, c0, c1); + } + if (F >= 0) { + --x; + F -= 4 * x; + } + ++y; + F += 4 * y + 2; + } + } else {*/ + while (x >= y) { + for (int xx = cir.center.x - x; xx < cir.center.x + x; ++xx) { + buf[(cir.center.y + y) * frame.size.width + xx] = GetGrad(-cir.radius, cir.radius, y, col0, col1); + buf[(cir.center.y - y) * frame.size.width + xx] = GetGrad(-cir.radius, cir.radius, -y, col0, col1); + } + for (int xx = cir.center.x - y; xx < cir.center.x + y; ++xx) { + buf[(cir.center.y + x - 1) * frame.size.width + xx] = GetGrad(-cir.radius, cir.radius, x - 1, col0, col1); + buf[(cir.center.y - x) * frame.size.width + xx] = GetGrad(-cir.radius, cir.radius, -y, col0, col1); + } + if (F >= 0) { + --x; + F -= 4 * x; + } + ++y; + F += 4 * y + 2; + } + //} +} + +// 単色文字を描画 +void Sheet::drawChar(unsigned char *font, const Point &pos, unsigned int color) { + for (int i = 0; i < 16; ++i) { + unsigned int *p = buf + (pos.y + i) * frame.size.width + pos.x; + unsigned char d = font[i]; + if (d & 0x80) { p[0] = color; } + if (d & 0x40) { p[1] = color; } + if (d & 0x20) { p[2] = color; } + if (d & 0x10) { p[3] = color; } + if (d & 0x08) { p[4] = color; } + if (d & 0x04) { p[5] = color; } + if (d & 0x02) { p[6] = color; } + if (d & 0x01) { p[7] = color; } + } +} + +// 単色文字列を描画 +void Sheet::drawString(const string &str, Point pos, unsigned int color, Encoding encode) { + using uchar = unsigned char; + unsigned char *fontdat = SheetCtl::font->read(); + unsigned char *font; + int k, t; + unsigned short langbyte1 = 0; + unsigned int u8code; + for (string::const_iterator s = str.begin(); s != str.end(); ++s) { + if (!langbyte1) { + if (encode == Encoding::SJIS && ((0x81 <= (uchar)*s && (uchar)*s <= 0x9f) || (0xe0 <= (uchar)*s && (uchar)*s <= 0xfc))) { // Shift_JIS + langbyte1 = (uchar)*s; + } else if (encode == Encoding::UTF8) { // UTF-8 + string::const_iterator next = s; + ++next; + if (((0xe2 <= (uchar)*s && (uchar)*s <= 0xef) || (0xc2 <= (uchar)*s && (uchar)*s <= 0xd1)) + && 0x80 <= (uchar)*next && (uchar)*next <= 0xbf) { + langbyte1 = (((uchar)*s << 8) | (uchar)*next); + ++s; + ++next; + } else { + drawChar(fontdat + (uchar)*s * 16, pos, color); + } + if (langbyte1 == 0xefbd) { // 。~ソ + ++s; + drawChar(fontdat + (uchar)*s * 16, pos, color); + langbyte1 = 0; + } else if (langbyte1 == 0xefbe) { // タ~゚ + ++s; + drawChar(fontdat + ((uchar)*s + 0x40) * 16, pos, color); + langbyte1 = 0; + } else if (langbyte1 == 0xe280 && (uchar)*next == 0xbe) { // 波ダッシュ(~) + ++s; + drawChar(fontdat + 0x7e * 16, pos, color); + langbyte1 = 0; + } else if (langbyte1 == 0xc2a5) { // 円マーク(\) + drawChar(fontdat + 0x5c * 16, pos, color); + langbyte1 = 0; + } + } else if (encode == Encoding::EUCJP && 0x81 <= (uchar)*s && (uchar)*s <= 0xfe) { // EUC-JP + langbyte1 = (uchar)*s; + } else { // 半角1バイト文字 + drawChar(fontdat + (uchar)*s * 16, pos, color); + } + } else { + if (encode == Encoding::SJIS) { + k = (0x81 <= langbyte1 && langbyte1 <= 0x9f) ? (langbyte1 - 0x81) * 2 + : (langbyte1 - 0xe0) * 2 + 62; + if (0x40 <= (uchar)*s && (uchar)*s <= 0x7e) { + t = (uchar)*s - 0x40; + } else if (0x80 <= (uchar)*s && (uchar)*s <= 0x9e) { + t = (uchar)*s - 0x80 + 63; + } else { + t = (uchar)*s - 0x9f; + ++k; + } + font = fontdat + 256 * 16 + (k * 94 + t) * 32; + } else if (encode == Encoding::UTF8) { + if (langbyte1 >> 12 != 0xc && langbyte1 >> 12 != 0xd) { + u8code = ((langbyte1 << 8) | (uchar)*s); + } else { + u8code = langbyte1; + --s; + } + font = fontdat + 256 * 16 + Utf8ToKT(u8code) * 32; + } else/* if (encode == Encoding::EUCJP)*/ { + font = fontdat + 256 * 16 + ((langbyte1 - 0xa1) * 94 + (uchar)*s - 0xa1) * 32; + } + langbyte1 = 0; + drawChar(font, Point(pos.x - 8, pos.y), color); + drawChar(font + 16, pos, color); + } + pos.x += 8; + } +} + +void Sheet::borderRadius(bool ltop, bool rtop, bool lbottom, bool rbottom) { + int x = frame.size.width, y = frame.size.height; + // 左上 + if (ltop) { + drawLine(Line(0, 0, 2, 0), kTransColor); // □□□ + drawLine(Line(0, 1, 1, 1), kTransColor); // □□■ + drawLine(Line(0, 2, 0, 2), kTransColor); // □■■ + } + // 右上 + if (rtop) { + drawLine(Line(x - 3, 0, x - 1, 0), kTransColor); // □□□ + drawLine(Line(x - 2, 1, x - 1, 1), kTransColor); // ■□□ + drawLine(Line(x - 1, 2, x - 1, 2), kTransColor); // ■■□ + } + // 左下 + if (lbottom) { + drawLine(Line(0, y - 3, 0, y - 3), kTransColor); // □■■ + drawLine(Line(0, y - 2, 1, y - 2), kTransColor); // □□■ + drawLine(Line(0, y - 1, 2, y - 1), kTransColor); // □□□ + } + // 右下 + if (rbottom) { + drawLine(Line(x - 1, y - 3, x - 1, y - 3), kTransColor); // ■■□ + drawLine(Line(x - 2, y - 2, x - 1, y - 2), kTransColor); // ■□□ + drawLine(Line(x - 3, y - 1, x - 1, y - 1), kTransColor); // □□□ + } +} + +// 画像を描画 +void Sheet::drawPicture(const char *fileName, const Point &pos, long transColor, int ratio) { + int info[4]; + unsigned int color; + int i; + DLL_STRPICENV env; + File imagefile(fileName); + + if (imagefile.open()) { + unsigned char *filebuf = imagefile.read(); + unsigned int fsize = imagefile.size; + + if (!_info_JPEG(&env, info, fsize, filebuf) && !_info_BMP(&env, info, fsize, filebuf)) { + return; + } + + unique_ptr picbuf(new RGB[info[2] * info[3]]); + if (picbuf) { + if (info[0] == 1) { + i = _decode0_BMP(&env, fsize, filebuf, 4, (unsigned char*)picbuf.get(), 0); + } else { + i = _decode0_JPEG(&env, fsize, filebuf, 4, (unsigned char*)picbuf.get(), 0); + } + if (!i && info[2] <= SheetCtl::resolution.width && info[3] <= SheetCtl::resolution.height) { + for (int yy = 0; yy < info[3]; ++yy) { + for (int xx = 0; xx < info[2]; ++xx) { + color = Rgb(picbuf[yy * info[2] + xx].r, picbuf[yy * info[2] + xx].g, picbuf[yy * info[2] + xx].b); + if ((long)color != transColor && buf[(yy + pos.y) * ratio * frame.size.width + (xx + pos.x) * ratio] != color) { + buf[(yy + pos.y) * ratio * frame.size.width + (xx + pos.x) * ratio] = color; + } + } + } + } + } + } +} + +// 指定色を変更 +void Sheet::changeColor(const Rectangle &range, unsigned int col0, unsigned int col1) { + for (int y = 0; y < range.size.height; ++y) { + for (int x = 0; x < range.size.width; ++x) { + if (buf[(y + range.offset.y) * frame.size.width + x + range.offset.x] == col0) { + buf[(y + range.offset.y) * frame.size.width + x + range.offset.x] = col1; + } + } + } +} + +//int SheetCtl::_top = -1; +//const int &SheetCtl::top = _top; +int SheetCtl::caretPosition = 2; +unsigned int SheetCtl::caretColor = 0; +Timer *SheetCtl::caretTimer; +string *SheetCtl::tboxString; +SheetCtl::VRAM SheetCtl::vram; +const Sheet **SheetCtl::map; +Size SheetCtl::_resolution(0, 0); +const Size &SheetCtl::resolution = _resolution; +TaskQueue *SheetCtl::queue; +Sheet *SheetCtl::back; +Sheet *SheetCtl::contextMenu; +Tab *SheetCtl::tabs[kMaxTabs]; +int SheetCtl::numOfTab = 0; +int SheetCtl::activeTab = -1; +Sheet *SheetCtl::sheets[kMaxSheets]; +int SheetCtl::color; +const int &SheetCtl::colorDepth = color; +File *SheetCtl::font; +Point SheetCtl::mouseCursorPos(-1, 0); +Sheet *SheetCtl::mouseCursorSheet; +const char *SheetCtl::mouseCursor[] = { + "*****OOOOOO*****", + "***OO@@@@@@OO***", + "**O@@@GCCG@@@O**", + "*O@@JUUUUUUJ@@O*", + "*O@JUUUUUUUUJ@O*", + "O@@UUUUUUUUUU@@O", + "O@GUUUUUUUUUUG@O", + "O@CUUUUUUUUUUC@O", + "O@CUUUUUUUUUUC@O", + "O@GUUUUUUUUUUG@O", + "O@@UUUUUUUUUU@@O", + "*O@JUUUUUUUUJ@O*", + "*O@@JUUUUUUJ@@O*", + "**O@@@GCCG@@@O**", + "***OO@@@@@@OO***", + "*****OOOOOO*****" +}; + +void rPrintNode(shared_ptr &pnode, Sheet &sht, int &i, int x0) { + sht.drawString(pnode->getData(), Point(1 + x0, 17 + i++ * 16), 0); + for (auto &&node : pnode->children) { + rPrintNode(node, sht, i, x0 + 8); + } +} + +// シートコントロールを初期化 +void SheetCtl::init() { + /* データメンバ初期化 */ + BootInfo *binfo = (BootInfo *)ADDRESS_BOOTINFO; + vram.p16 = reinterpret_cast(binfo->vram); + _resolution = Size(binfo->scrnx, binfo->scrny); + color = binfo->vmode; + + // デバッグ用 + initEmuVGA(1366, 768, 32); + _resolution = Size(1366, 768); + color = 32; + vram.p16 = reinterpret_cast(0xe0000000); + + map = new const Sheet *[resolution.getArea()]; + tboxString = new string(); + + /* フォント読み込み */ + font = new File("japanese.fnt"); + font->open(); + + /* サイドバー */ + back = new Sheet(resolution, false); + back->onClick = &onClickBack; + // 背景色 + back->fillRect(Rectangle(0, 0, 150, back->frame.size.height), kBackgroundColor); + back->gradRect(Rectangle(150, 0, back->frame.size.width - 150, back->frame.size.height / 2), 0x0d2c51, 0x68a3c3, GradientDirection::TopToBottom); + back->gradRect(Rectangle(150, back->frame.size.height / 2, back->frame.size.width - 150, back->frame.size.height / 2), 0x68a3c3, 0xffab5b, GradientDirection::TopToBottom); + // 戻る・進むボタン枠 + back->drawPicture("b_f.bmp", Point(4, 4), 0xff00ff); + // 更新ボタン枠 + back->drawPicture("btn_r.bmp", Point(59, 4), 0xff00ff); + // キーマップスイッチ + back->drawRect(Rectangle(42, back->frame.size.height - 20 - 46, 66, 22), 0xffffff); + back->fillRect(Rectangle(43, back->frame.size.height - 20 - 45, 32, 20), 0xffffff); + back->drawString("JP", Point(50, back->frame.size.height - 20 - 43), 0); + back->drawString("US", Point(50 + 32, back->frame.size.height - 20 - 43), 0xfffffe); + // 検索窓 + back->fillRect(Rectangle(2, back->frame.size.height - 20 - 22, 150 - 2 - 2, 22), 0xffffff); + // refresh + back->refresh(back->frame); + + // マウスポインタ描画 + mouseCursorPos = Point(-1, 0); + mouseCursorSheet = new Sheet(Size(16, 16), true); + for (int y = 0; y < 16; ++y) { + for (int x = 0; x < 16; ++x) { + switch (mouseCursor[x][y]) { + case 'O': + mouseCursorSheet->buf[y * 16 + x] = Rgb(255, 255, 255, 100); + break; + case '@': + mouseCursorSheet->buf[y * 16 + x] = Rgb(12, 69, 255, 100); + break; + case 'G': + mouseCursorSheet->buf[y * 16 + x] = Rgb(27, 81, 255, 100); + break; + case 'J': + mouseCursorSheet->buf[y * 16 + x] = Rgb(58, 104, 255, 100); + break; + case 'C': + mouseCursorSheet->buf[y * 16 + x] = Rgb(73, 116, 255, 100); + break; + case 'U': + mouseCursorSheet->buf[y * 16 + x] = Rgb(0, 182, 200, 100); + break; + default: + mouseCursorSheet->buf[y * 16 + x] = kTransColor; + break; + } + } + } + mouseCursorSheet->moveTo(Point(resolution.width / 2, resolution.height / 2)); + back->appendChild(mouseCursorSheet, true); + + /* 右クリックメニュー */ + contextMenu = new Sheet(Size(150, 150), true); + contextMenu->fillRect(contextMenu->frame, kTransColor); + //contextMenu->gradCircle(Circle(Point(75, 75), 75), Rgb(200, 230, 255, 50), Rgb(100, 150, 255)); + contextMenu->fillCircle(Circle(Point(75, 75), 75), 0x19e0e0e0); + //contextMenu->drawCircle(Circle(Point(75, 75), 75), Rgb(0, 0, 255, 50)); + contextMenu->fillCircle(Circle(Point(75, 75), 35), kTransColor); + //contextMenu->drawCircle(Circle(Point(75, 75), 35), Rgb(0, 0, 255, 50)); + contextMenu->drawPicture("copy.bmp", Point(contextMenu->frame.size.width / 2 - 16, 3), 0xff00ff); + contextMenu->drawPicture("source.bmp", Point(contextMenu->frame.size.width / 2 + 38, contextMenu->frame.size.height / 2 - 16), 0xff00ff); + contextMenu->drawPicture("search.bmp", Point(contextMenu->frame.size.width / 2 - 16, contextMenu->frame.size.height - 32 - 3), 0xff00ff); + contextMenu->drawPicture("refresh.bmp", Point(contextMenu->frame.size.width / 2 - 38 - 32, contextMenu->frame.size.height / 2 - 16), 0xff00ff); + back->appendChild(contextMenu); + + refreshSub(Rectangle(resolution)); + + // GUI タスクを起動 + Task *guiTask = new Task("GUI Task", 1, 2, 256, &guiTaskMain); + queue = guiTask->queue; +} + +void SheetCtl::guiTaskMain() { + Task &task = *TaskSwitcher::getNowTask(); + + // キャレットの表示とタイマー設定 + back->drawLine( + Line( + caretPosition + 2, + back->frame.size.height - 20 - 22 + 2, + caretPosition + 2, + back->frame.size.height - 20 - 22 + 2 + 18 + ), caretColor); + back->refresh(Rectangle(caretPosition + 2, back->frame.size.height - 20 - 22 + 2, 1, 18)); + caretColor = 0xffffff; + caretTimer = new Timer(queue, 0x80); + caretTimer->set(50); + + for (;;) { + Cli(); + if (task.queue->isempty()) { + task.sleep(); + Sti(); + } else { + int data = task.queue->pop(); + Sti(); + if (data < 0x80) { + // from Keyboard Driver + switch (data) { + case 0x08: // BS + if (caretPosition > 2) { + caretPosition -= 8; + Rectangle clearRange(caretPosition + 2, back->frame.size.height - 20 - 22 + 2, 9, 18); + back->fillRect(clearRange, 0xffffff); + caretColor = 0; + back->drawLine(Line(clearRange.offset, clearRange.offset + Point(0, 18)), caretColor); + back->refresh(clearRange); + caretTimer->cancel(); + caretTimer->set(50); + tboxString->erase(tboxString->length() - 1, tboxString->length()); + } + break; + + case 0x09: // TAB + if (KeyboardController::alt) { + // タブ切り替え + int newIndex = (activeTab + 1) % numOfTab; + tabs[newIndex]->active(); + } + break; + + case 0x0a: { // LF + if (tboxString->length() == 0) break; + + string url = *tboxString; + + if (url.compare(0, 6, "about:") == 0) { + // about URI scheme + if (url.compare(6, 5, "blank") == 0) { + // about:blank + new Tab("about:blank", [](Tab *) { + TaskSwitcher::getNowTask()->sleep(); + }); + } else if (url.compare(6, 7, "sysinfo") == 0) { + // about:sysinfo + new Tab("system info", 128, &SysinfoMain); + } else if (url.compare("about:") == 0) { + // about: + new Tab("About", [](Tab *tab) { + tab->sheet->drawString("Cloumo", Point(1, 1), 0); + tab->sheet->drawString("今日の一言: 早くウェブアプリ動かしたい.", Point(1, 1 + 16), 0); + tab->sheet->refresh(Rectangle(Point(0, 0), tab->sheet->frame.size)); + + TaskSwitcher::getNowTask()->sleep(); + }); + } else { + // invalid URIs + new Tab(url, [](Tab *tab) { + tab->sheet->drawString(tab->name + " is invalid", Point(1, 1), 0); + tab->sheet->refresh(Rectangle(Point(0, 0), tab->sheet->frame.size)); + + TaskSwitcher::getNowTask()->sleep(); + }); + } + } else { + // file URI scheme + if (url.compare(0, 8, "file:///") != 0) { + // "file:///" を追加 + url.insert(0, "file:///"); + } + new Tab(url, [](Tab *tab) { + string url = tab->name; + Sheet &sht = *tab->sheet; + url.erase(0, 8); // "file:///" の削除 + unique_ptr htmlFile(new File(url)); + if (htmlFile->open()) { + // ソースの取得 + string source(reinterpret_cast(htmlFile->read().get()), htmlFile->size); + + // トークン化 + HTML::Tokenizer tokenizer; + Queue> &tokens = tokenizer.tokenize(source); + + // ツリー構築 + HTML::TreeConstructor constructor; + HTML::Document &document = constructor.construct(tokens); + + // レンダリング + sht.drawString("パース結果", Point(1, 1), 0); + int i = 0; + for (auto &&node : document.children) { + rPrintNode(node, sht, i, 0); + } + sht.refresh(Rectangle(Point(0, 0), sht.frame.size)); + } else { + // Not found + sht.drawString("File not found", Point(1, 1), 0); + sht.drawString("Can't find the file at 'file:///" + url + "'", Point(1, 1 + 16), 0); + sht.refresh(Rectangle(Point(0, 0), sht.frame.size)); + } + + TaskSwitcher::getNowTask()->sleep(); + }); + } + + *tboxString = ""; + caretPosition = 2; + Rectangle clearRange(2, back->frame.size.height - 20 - 22, 150 - 2 - 2, 22); + back->fillRect(clearRange, 0xffffff); + back->refresh(clearRange); + break; + } + + default: { + char s[2]; + s[0] = static_cast(data); + s[1] = 0; + back->drawLine( + Line( + caretPosition + 2, + back->frame.size.height - 20 - 22 + 2, + caretPosition + 2, + back->frame.size.height - 20 - 22 + 2 + 18 + ), 0xffffff); + back->drawString(s, Point(caretPosition + 2, back->frame.size.height - 20 - 22 + 3), 0); + caretPosition += 8; + caretColor = 0; + back->drawLine( + Line( + caretPosition + 2, + back->frame.size.height - 20 - 22 + 2, + caretPosition + 2, + back->frame.size.height - 20 - 22 + 2 + 18 + ), caretColor); + back->refresh(Rectangle(caretPosition - 8 + 2, back->frame.size.height - 20 - 22 + 2, 9, 18)); + caretTimer->cancel(); + caretTimer->set(50); + *tboxString += s[0]; + break; + } + } + } else if (data == 0x80) { + // キャレットカーソル用タイマーのタイムアウト + back->drawLine( + Line( + caretPosition + 2, + back->frame.size.height - 20 - 22 + 2, + caretPosition + 2, + back->frame.size.height - 20 - 22 + 2 + 18 + ), caretColor); + back->refresh(Rectangle(caretPosition + 2, back->frame.size.height - 20 - 22 + 2, 1, 18)); + caretColor ^= 0xffffff; + caretTimer->set(50); + } else if (data < 260) { + // from Mouse Driver + switch (data) { + case 256: // move + mouseCursorSheet->moveTo(mouseCursorPos + Point(-8, -8)); + break; + + case 257: { // left click + // Close the context menu + if (find(back->children.begin(), back->children.end(), contextMenu) != back->children.end()) { + contextMenu->upDown(-1); + } + + // 各シートの onClick イベントを発動 + Stack sheetStack(256); + sheetStack.push(back); + bool finished = false; + while (!sheetStack.isempty() && !finished) { + Sheet &sht = *sheetStack.pop(); + + // マウスカーソル自身だったら skip + if (&sht == mouseCursorSheet) continue; + + if (sht.children.empty()) { // リーフ + // offset 足す + Point offset; + for (auto p = sht._parent; p != nullptr; p = p->_parent) { + offset += p->frame.offset; + } + if (Rectangle(sht.frame).slide(offset).contains(mouseCursorPos)) { + // sht 自身に onClick があって,マウスポインタ直下にあれば実行 + if (sht.onClick) sht.onClick(mouseCursorPos, sht); + finished = true; + } else { + // sht が最後の子なら親も onClick + for (auto p = &sht; p->_parent != nullptr && p->_parent->children.back() == p; p = p->_parent) { + // offset 足す + Point offset; + for (auto q = p->_parent->_parent; q != nullptr; q = q->_parent) { + offset += q->frame.offset; + } + if (Rectangle(p->_parent->frame).slide(offset).contains(mouseCursorPos)) { + if (p->_parent->onClick) p->_parent->onClick(mouseCursorPos, *p->_parent); + finished = true; + } + } + } + } else { // 中間ノード + // 子をスタックに高さが低い順にプッシュ + for (auto it = --sht.children.end(); it != sht.children.begin(); --it) { + sheetStack.push(*it); + } + sheetStack.push(sht.children.front()); + } + } + if (back->onClick && back->frame.contains(mouseCursorPos)) { + back->onClick(mouseCursorPos, *back); + } + break; + } + + case 258: // right click + if (find(back->children.begin(), back->children.end(), contextMenu) == back->children.end()) { + // Open the context menu + contextMenu->moveTo(Point(mouseCursorPos.x - contextMenu->frame.size.width / 2, mouseCursorPos.y - contextMenu->frame.size.height / 2)); + contextMenu->upDown(1); + } + break; + + case 259: // scroll up + break; + + case 260: // scroll down + break; + } + } + } + } +} + +// 再初期化 +void SheetCtl::reInit() { + // タブ全部閉じる + for (auto &&tab : tabs) { + delete tab; + } + + // back の確保し直し + delete back; + back = new Sheet(resolution, false); + back->onClick = &onClickBack; + // 背景色 + back->fillRect(Rectangle(0, 0, 150, back->frame.size.height), kBackgroundColor); + back->gradRect(Rectangle(150, 0, back->frame.size.width - 150, back->frame.size.height / 2), 0x0d2c51, 0x68a3c3, GradientDirection::TopToBottom); + back->gradRect(Rectangle(150, back->frame.size.height / 2, back->frame.size.width - 150, back->frame.size.height / 2), 0x68a3c3, 0xffab5b, GradientDirection::TopToBottom); + // 戻る・進むボタン枠 + back->drawPicture("b_f.bmp", Point(4, 4), 0xff00ff); + // 更新ボタン枠 + back->drawPicture("btn_r.bmp", Point(59, 4), 0xff00ff); + // キーマップスイッチ + back->drawRect(Rectangle(42, back->frame.size.height - 20 - 46, 66, 22), 0xffffff); + back->fillRect(Rectangle(43, back->frame.size.height - 20 - 45, 32, 20), 0xffffff); + back->drawString("JP", Point(50, back->frame.size.height - 20 - 43), 0); + back->drawString("US", Point(50 + 32, back->frame.size.height - 20 - 43), 0xfffffe); + // 検索窓 + back->fillRect(Rectangle(2, back->frame.size.height - 20 - 22, 150 - 2 - 2, 22), 0xffffff); + // 表示設定 + back->refresh(back->frame); + //back->upDown(0); +} + +void SheetCtl::onClickBack(const Point &pos, Sheet &sht) { + if (43 <= pos.x && pos.x <= 74 && sht.frame.size.height - 20 - 45 <= pos.y && pos.y <= sht.frame.size.height - 45) { + // JP 選択 + KeyboardController::switchToJIS(); + sht.changeColor(Rectangle(43, sht.frame.size.height - 20 - 45, 32, 20), 0xfffffe, 0); + sht.changeColor(Rectangle(43, sht.frame.size.height - 20 - 45, 32, 20), kBackgroundColor, 0xffffff); + sht.changeColor(Rectangle(74, sht.frame.size.height - 20 - 45, 33, 20), 0, 0xfffffe); + sht.changeColor(Rectangle(74, sht.frame.size.height - 20 - 45, 33, 20), 0xffffff, kBackgroundColor); + sht.refresh(Rectangle(43, sht.frame.size.height - 20 - 45, 64, 20)); + } else if (75 <= pos.x && pos.x <= 107 && sht.frame.size.height - 20 - 45 <= pos.y && pos.y <= sht.frame.size.height - 45) { + // US 選択 + KeyboardController::switchToUS(); + sht.changeColor(Rectangle(74, sht.frame.size.height - 20 - 45, 33, 20), 0xfffffe, 0); + sht.changeColor(Rectangle(74, sht.frame.size.height - 20 - 45, 33, 20), kBackgroundColor, 0xffffff); + sht.changeColor(Rectangle(43, sht.frame.size.height - 20 - 45, 32, 20), 0, 0xfffffe); + sht.changeColor(Rectangle(43, sht.frame.size.height - 20 - 45, 32, 20), 0xffffff, kBackgroundColor); + sht.refresh(Rectangle(43, sht.frame.size.height - 20 - 45, 64, 20)); + } +} + +// 指定範囲の変更をmapに適用 +void SheetCtl::refreshMap(const Rectangle &range) { + int bx0, by0, bx1, by1;//, sid4; + int vx0 = max(0, range.offset.x), vy0 = max(0, range.offset.y); + int vx1 = min(resolution.width, range.getEndPoint().x), vy1 = min(resolution.height, range.getEndPoint().y); + Stack sheetStack(256); + sheetStack.push(back); + + while (!sheetStack.isempty()) { + const Sheet &sht = *sheetStack.pop(); + + // 先祖の分の offset を足す + Point offset = sht.frame.offset; + for (auto p = sht._parent; p != nullptr; p = p->_parent) { + offset += p->frame.offset; + } + /* vx0~vy1を使って、bx0~by1を逆算する */ + bx0 = max(0, vx0 - offset.x); + by0 = max(0, vy0 - offset.y); + bx1 = min(sht.frame.size.width, vx1 - offset.x); + by1 = min(sht.frame.size.height, vy1 - offset.y); + + if (!sht.nonRect) { + //if (!(sht.frame.offset.x & 3) && !(bx0 & 3) && !(bx1 & 3)) { + /* 透明色なし専用の高速版(4バイト型) */ + /*bx1 = (bx1 - bx0) / 4; + sid4 = sid | sid << 8 | sid << 16 | sid << 24; + for (int by = by0; by < by1; ++by) { + for (int bx = 0; bx < bx1; ++bx) { + ((int*) &map[(sht.frame.offset.y + by) * resolution.width + sht.frame.offset.x + bx0])[bx] = sid4; + } + } + } else {*/ + /* 透明色なし専用の高速版(1バイト型) */ + for (int by = by0; by < by1; ++by) { + for (int bx = bx0; bx < bx1; ++bx) { + if (&sht == back || sht._parent->frame.contains(Point(bx, by) + offset)) + map[(offset.y + by) * resolution.width + offset.x + bx] = &sht; + } + } + //} + } else { + /* 透明色ありの一般版(1バイト型) */ + for (int by = by0; by < by1; ++by) { + for (int bx = bx0; bx < bx1; ++bx) { + if ((unsigned char)(sht.buf[by * sht.frame.size.width + bx] >> 24) != 0xff + && sht._parent->frame.contains(Point(bx, by) + offset)) { + map[(offset.y + by) * resolution.width + offset.x + bx] = &sht; + } + } + } + } + + // 子をスタックにプッシュ + for (auto &&child : sht.children) { + sheetStack.push(child); + } + } +} + +// 指定範囲の変更をvramに適用 +void SheetCtl::refreshSub(const Rectangle &range) { + int bx0, by0, bx1, by1; + unsigned int rgb; + + int vx0 = max(0, range.offset.x), vy0 = max(0, range.offset.y); + int vx1 = min(resolution.width, range.getEndPoint().x), vy1 = min(resolution.height, range.getEndPoint().y); + unique_ptr backrgb(new unsigned int[(vx1 - vx0) * (vy1 - vy0)]); + Stack sheetStack(256); + sheetStack.push(back); + + while (!sheetStack.isempty()) { + const Sheet &sht = *sheetStack.pop(); + + // 先祖の分の offset を足す + Point offset = sht.frame.offset; + for (auto p = sht._parent; p != nullptr; p = p->_parent) { + offset += p->frame.offset; + } + /* vx0~vy1を使って、bx0~by1を逆算する */ + bx0 = max(0, vx0 - offset.x); + by0 = max(0, vy0 - offset.y); + bx1 = min(sht.frame.size.width, vx1 - offset.x); + by1 = min(sht.frame.size.height, vy1 - offset.y); + + if (color == 32) { + for (int by = by0; by < by1; ++by) { + for (int bx = bx0; bx < bx1; ++bx) { + rgb = sht.buf[by * sht.frame.size.width + bx]; + if (map[(offset.y + by) * resolution.width + offset.x + bx] == &sht) { + vram.p32[((offset.y + by) * resolution.width + (offset.x + bx))] + = &sht == back + ? rgb + : MixRgb(rgb, backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)]); + } else if ((unsigned char)(rgb >> 24) != 0xff) { + backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)] + = &sht == back + ? rgb + : MixRgb(rgb, backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)]); + } + } + } + } else if (color == 24) { + for (int by = by0; by < by1; ++by) { + for (int bx = bx0; bx < bx1; ++bx) { + rgb = sht.buf[by * sht.frame.size.width + bx]; + if (map[(offset.y + by) * resolution.width + offset.x + bx] == &sht) { + if (&sht != back) { + rgb = MixRgb(rgb, backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)]); + } + vram.p24[(offset.y + by) * resolution.width + (offset.x + bx)][0] = (unsigned char)rgb; + vram.p24[(offset.y + by) * resolution.width + (offset.x + bx)][1] = (unsigned char)(rgb >> 8); + vram.p24[(offset.y + by) * resolution.width + (offset.x + bx)][2] = (unsigned char)(rgb >> 16); + } else if ((unsigned char)(rgb >> 24) != 0xff) { + backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)] + = &sht == back + ? rgb + : MixRgb(rgb, backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)]); + } + } + } + } else if (color == 16) { + for (int by = by0; by < by1; ++by) { + for (int bx = bx0; bx < bx1; ++bx) { + rgb = sht.buf[by * sht.frame.size.width + bx]; + if (map[(offset.y + by) * resolution.width + offset.x + bx] == &sht) { + vram.p16[(offset.y + by) * resolution.width + (offset.x + bx)] + = &sht == back + ? ((((unsigned char) (rgb >> 16) << 8) & 0xf800) + | (((unsigned char) (rgb >> 8) << 3) & 0x07e0) + | ((unsigned char) rgb >> 3)) + : ((((((unsigned char) (backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)] >> 16) - (unsigned char) (rgb >> 16)) * (unsigned char) (rgb >> 24) / 255 + (unsigned char) (rgb >> 16)) << 8) & 0xf800) + | (((((unsigned char) (backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)] >> 8) - (unsigned char) (rgb >> 8)) * (unsigned char) (rgb >> 24) / 255 + (unsigned char) (rgb >> 8)) << 3) & 0x07e0) + | (((unsigned char) backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)] - (unsigned char) rgb) * (unsigned char) (rgb >> 24) / 255 + (unsigned char) rgb) >> 3); + } else if ((unsigned char) (rgb >> 24) != 0xff) { + backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)] + = &sht == back + ? rgb + : MixRgb(rgb, backrgb[(offset.y + by - vy0) * (vx1 - vx0) + (offset.x + bx - vx0)]); + } + } + } + } + + // 子をスタックにプッシュ + for (auto &&child : sht.children) { + sheetStack.push(child); + } + } +} + +void SheetCtl::blueScreen(const char *str) { + using uchar = unsigned char; + + // 塗りつぶし + for (int i = 0; i < resolution.width * resolution.height; ++i) { + if (color == 32) { + vram.p32[i] = 0x0000ff; + } else if (color == 24) { + vram.p24[i][0] = 0xff; + vram.p24[i][1] = 0; + vram.p24[i][2] = 0; + } else if (color == 16) { + vram.p16[i] = 0xff >> 3; + } + } + + // 文字表示 + auto drawChar = [](unsigned char *font, const Point &pos, unsigned int fcolor) { + for (int i = 0; i < 16; ++i) { + unsigned char d = font[i]; + + if (color == 32) { + unsigned int *p = &vram.p32[(pos.y + i) * resolution.width + pos.x]; + if (d & 0x80) { p[0] = fcolor; } + if (d & 0x40) { p[1] = fcolor; } + if (d & 0x20) { p[2] = fcolor; } + if (d & 0x10) { p[3] = fcolor; } + if (d & 0x08) { p[4] = fcolor; } + if (d & 0x04) { p[5] = fcolor; } + if (d & 0x02) { p[6] = fcolor; } + if (d & 0x01) { p[7] = fcolor; } + } else if (color == 24) { + unsigned char (*p)[3] = &vram.p24[(pos.y + i) * resolution.width + pos.x]; + if (d & 0x80) { + p[0][0] = (unsigned char)(fcolor); + p[0][1] = (unsigned char)(fcolor >> 8); + p[0][2] = (unsigned char)(fcolor >> 16); + } + if (d & 0x40) { + p[1][0] = (unsigned char)(fcolor); + p[1][1] = (unsigned char)(fcolor >> 8); + p[1][2] = (unsigned char)(fcolor >> 16); + } + if (d & 0x20) { + p[2][0] = (unsigned char)(fcolor); + p[2][1] = (unsigned char)(fcolor >> 8); + p[2][2] = (unsigned char)(fcolor >> 16); + } + if (d & 0x10) { + p[3][0] = (unsigned char)(fcolor); + p[3][1] = (unsigned char)(fcolor >> 8); + p[3][2] = (unsigned char)(fcolor >> 16); + } + if (d & 0x08) { + p[4][0] = (unsigned char)(fcolor); + p[4][1] = (unsigned char)(fcolor >> 8); + p[4][2] = (unsigned char)(fcolor >> 16); + } + if (d & 0x04) { + p[5][0] = (unsigned char)(fcolor); + p[5][1] = (unsigned char)(fcolor >> 8); + p[5][2] = (unsigned char)(fcolor >> 16); + } + if (d & 0x02) { + p[6][0] = (unsigned char)(fcolor); + p[6][1] = (unsigned char)(fcolor >> 8); + p[6][2] = (unsigned char)(fcolor >> 16); + } + if (d & 0x01) { + p[7][0] = (unsigned char)(fcolor); + p[7][1] = (unsigned char)(fcolor >> 8); + p[7][2] = (unsigned char)(fcolor >> 16); + } + } else if (color == 16) { + unsigned short *p = &vram.p16[(pos.y + i) * resolution.width + pos.x]; + unsigned short fcolor16 = (((unsigned char)(fcolor >> 16) << 8) & 0xf800) | (((unsigned char)(fcolor >> 8) << 3) & 0x07e0) | ((unsigned char)(fcolor >> 3)); + if (d & 0x80) { p[0] = fcolor16; } + if (d & 0x40) { p[1] = fcolor16; } + if (d & 0x20) { p[2] = fcolor16; } + if (d & 0x10) { p[3] = fcolor16; } + if (d & 0x08) { p[4] = fcolor16; } + if (d & 0x04) { p[5] = fcolor16; } + if (d & 0x02) { p[6] = fcolor16; } + if (d & 0x01) { p[7] = fcolor16; } + } + } + }; + Point pos(0, 0); + unsigned char *fontdat = SheetCtl::font->read(); + uchar *s = (uchar *)str; + + for (int i = 0; s[i]; ++i) { + drawChar(fontdat + s[i] * 16, pos, 0xffffff); + pos.x += 8; + } +} diff --git a/kernel/GUI/graphic.h b/kernel/GUI/graphic.h new file mode 100644 index 0000000..dcaed56 --- /dev/null +++ b/kernel/GUI/graphic.h @@ -0,0 +1,217 @@ +/* + * シート + */ + +#pragma once + +#include +#include + +const int kMaxSheets = 256; +const int kMaxTabs = 100; + +enum class GradientDirection { LeftToRight, TopToBottom }; +enum class Encoding { SJIS, UTF8, EUCJP }; + +struct Point { + int x = 0, y = 0; + + Point() = default; + Point(int x_, int y_) : x(x_), y(y_) {} + Point(const Point &p) : x(p.x), y(p.y) {} + Point operator +(const Point &p) const { + return Point(x + p.x, y + p.y); + } + Point operator -(const Point &p) const { + return Point(x - p.x, y - p.y); + } + Point operator /(int n) const { + return Point(x / n, y / n); + } + Point &operator +=(const Point &p) { + x += p.x; + y += p.y; + return *this; + } + Point &operator -=(const Point &p) { + x -= p.x; + y -= p.y; + return *this; + } +}; + +struct Size { + int width, height; + + Size(int w, int h) : width(w), height(h) {} + Size operator /(int n) const { + return Size(width / n, height / n); + } + inline int getArea() const { + return width * height; + } +}; + +struct Line { + Point start, end; + + Line(const Point &o, const Point &s) : start(o), end(s) {} + Line(int x0, int y0, int x1, int y1) : start(x0, y0), end(x1, y1) {} +}; + +struct Rectangle { + Point offset; + Size size; + + Rectangle(const Point &o, const Size &s) : offset(o), size(s) {} + Rectangle(const Size &s) : offset(0, 0), size(s) {} + Rectangle(int x, int y, int width, int height) : offset(x, y), size(width, height) {} + Rectangle(int width, int height) : offset(0, 0), size(width, height) {} + bool contains(const Point &p) const { + return offset.x <= p.x && p.x <= offset.x + size.width + && offset.y <= p.y && p.y <= offset.y + size.height; + } + Point getEndPoint() const { + return Point(offset.x + size.width, offset.y + size.height); + } + inline int getArea() const { + return size.width * size.height; + } + Rectangle &slide(const Point &p) { + offset += p; + return *this; + } +}; + +struct Circle { + Point center; + int radius; + + Circle(const Point &p, int rad) : center(p), radius(rad) {} + explicit Circle(int rad) : Circle(Point(0, 0), rad) {} +}; + +class Sheet { +private: + Rectangle _frame = Rectangle(0, 0); + //int _zIndex = -1; + bool nonRect; + List _children; + Sheet *_parent = nullptr; + +public: + unsigned int *buf; + const List &children = _children; + Sheet *const &parent = _parent; + const Rectangle &frame = _frame; + //const int &zIndex = _zIndex; + Tab *tab = nullptr; + void (*onClick)(const Point &pos, Sheet &sht) = nullptr; + void (*onClosed)() = nullptr; + + friend class SheetCtl; + Sheet(const Size &size, bool _nonRect = false); + virtual ~Sheet(); + void upDown(int zIndex); + void refresh(Rectangle range) const; + void moveTo(const Point &pos); + void appendChild(Sheet *child, bool show = false); + + // 描画関数 + void drawLine(const Line &line, unsigned int color); + void gradLine(const Line &line, unsigned int col0, unsigned int col1, GradientDirection direction); + void drawRect(const Rectangle &rect, unsigned int color); + void fillRect(const Rectangle &rect, unsigned int color); + void gradRect(const Rectangle &rect, unsigned int col0, unsigned int col1, GradientDirection direction); + void drawCircle(const Circle &cir, unsigned int color); + void fillCircle(const Circle &cir, unsigned int color); + void gradCircle(const Circle &cir, unsigned int col0, unsigned int col1); + void drawChar(unsigned char *font, const Point &pos, unsigned int color); + void drawString(const string &str, Point pos, unsigned int color, Encoding encode = Encoding::UTF8); + void borderRadius(bool ltop, bool rtop, bool lbottom, bool rbottom); + void drawPicture(const char *fileName, const Point &pos, long transColor = -1, int ratio = 1); + void changeColor(const Rectangle &range, unsigned int col0, unsigned int col1); +}; + +class Task; +class File; +class Tab; + +class SheetCtl { +private: + static union VRAM { + unsigned short *p16; + unsigned char (*p24)[3]; + unsigned int *p32; + } vram; + static const Sheet **map; + static File *font; + static Sheet *sheets[]; + static Size _resolution; + static int color; + static int _top; + + // for GUI Task + static Tab *tabs[]; + static int numOfTab; + static int activeTab; + static const char *mouseCursor[]; + static Sheet *mouseCursorSheet; + static Sheet *contextMenu; + + // for Text Boxes + static int caretPosition; + static unsigned int caretColor; + static Timer *caretTimer; + static string *tboxString; + + static void guiTaskMain(); + static void onClickBack(const Point &pos, Sheet &sht); + static void refreshMap(const Rectangle &range); + static void refreshSub(const Rectangle &range); + +public: + static Sheet *back; + + //static const int ⊤ + static const Size &resolution; + static const int &colorDepth; + + // for GUI Task + static TaskQueue *queue; + static Point mouseCursorPos; + + friend class Sheet; + friend class Tab; + static void init(); + static void reInit(); + static void blueScreen(const char *str); +}; + +// 赤緑青をあわせてunsigned intで出力 +constexpr unsigned int Rgb(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 0) { + return ((a << 24) | (r << 16) | (g << 8) | b); +} + +// 透明化処理 +constexpr unsigned int MixRgb(unsigned int rgb1, unsigned int rgb2) { + return (((unsigned char) (rgb1 >> 24) << 24) + | (((unsigned char) (rgb2 >> 16) - (unsigned char) (rgb1 >> 16)) * (unsigned char) (rgb1 >> 24) / 255 + (unsigned char) (rgb1 >> 16)) << 16 + | (((unsigned char) (rgb2 >> 8) - (unsigned char) (rgb1 >> 8)) * (unsigned char) (rgb1 >> 24) / 255 + (unsigned char) (rgb1 >> 8)) << 8 + | (((unsigned char) rgb2 - (unsigned char) rgb1) * (unsigned char) (rgb1 >> 24) / 255 + (unsigned char) rgb1)); +} + +// グラデーション色を出力 +constexpr unsigned int GetGrad(int p0, int p1, int p, unsigned int c0, unsigned int c1) { + return (((unsigned char) (c0 >> 24) << 24) + | (((unsigned char) (c0 >> 16) + ((unsigned char) (c1 >> 16) - (unsigned char) (c0 >> 16)) * (p - p0) / (p1 - p0)) << 16) + | (((unsigned char) (c0 >> 8) + ((unsigned char) (c1 >> 8) - (unsigned char) (c0 >> 8)) * (p - p0) / (p1 - p0)) << 8) + | ((unsigned char) c0 + ((unsigned char) c1 - (unsigned char) c0) * (p - p0) / (p1 - p0))); +} + +const int kTransColor = 0xff000000; +const auto kBackgroundColor = 0x263238;//Rgb(0, 84, 255); +const auto kActiveTabColor = 0xffffff; +const auto kActiveTextColor = 0; +const auto kPassiveTabColor = 0x96a2a8;//Rgb(127, 169, 255); +const auto kPassiveTextColor = 0x13191c;//Rgb(0, 42, 127); diff --git a/kernel/HTML/HTMLNode.cpp b/kernel/HTML/HTMLNode.cpp new file mode 100644 index 0000000..c9c20a2 --- /dev/null +++ b/kernel/HTML/HTMLNode.cpp @@ -0,0 +1,12 @@ +#include "HTMLNode.h" + +using namespace HTML; + +// Node +const shared_ptr &Node::appendChild(const shared_ptr &node) { + children.push_back(node); + return node; +} + +// Element +Element::Element(const string &name) : _tagName(name) {} diff --git a/kernel/HTML/HTMLNode.h b/kernel/HTML/HTMLNode.h new file mode 100644 index 0000000..fe36db4 --- /dev/null +++ b/kernel/HTML/HTMLNode.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include +#include + +namespace HTML { + class Node { + public: + List> children; + + virtual ~Node() {} + virtual string getData() { + return ""; + } + const shared_ptr &appendChild(const shared_ptr &node); + }; + + class Element : public Node { + private: + string _tagName; + string id; + string className; + + public: + //const string &tagName = _tagName; + + //Element() {} + Element(const string &name); + string getData() { + return "<" + _tagName + ">"; + } + }; + + class TextNode : public Node { + public: + string wholeText; + + TextNode(string str) : wholeText(str) {} + string getData() { + return wholeText; + } + }; + + class DocumentType : public Node { + public: + string name; + string publicId = ""; + string systemId = ""; + + explicit DocumentType(const string &n) : name(n) {} + DocumentType(const string &n, const string &p, const string &s) : name(n), publicId(p), systemId(s) {} + string getData() { + return ""; + } + }; + + class Comment : public Node { + public: + explicit Comment(string data = ""); + + string getData() { + return ""; + } + }; + + class Document : public Node { + public: + //DocumentType doctype; + //Element documentElement; + + string getData() { + return "Document ノード"; + } + }; +} diff --git a/kernel/HTML/HTMLRenderer.cpp b/kernel/HTML/HTMLRenderer.cpp new file mode 100644 index 0000000..e69de29 diff --git a/kernel/HTML/HTMLRenderer.h b/kernel/HTML/HTMLRenderer.h new file mode 100644 index 0000000..8799797 --- /dev/null +++ b/kernel/HTML/HTMLRenderer.h @@ -0,0 +1,7 @@ +#pragma once + +namespace HTML { + class Renderer { + + }; +} diff --git a/kernel/HTML/HTMLToken.cpp b/kernel/HTML/HTMLToken.cpp new file mode 100644 index 0000000..8d5c80e --- /dev/null +++ b/kernel/HTML/HTMLToken.cpp @@ -0,0 +1,27 @@ +#include "HTMLToken.h" + +using namespace HTML; + +Token::Attribute::Attribute(string _name, string _value) : name(_name), value(_value) {} + +Token::Token(Type tokenType) : type(tokenType) {} + +void Token::setSelfClosingFlag() { + selfClosingFlag = true; +} + +bool Token::isSelfClosing() { + return selfClosingFlag; +} + +void Token::appendAttribute(char c) { + attributes.push_back(shared_ptr(new Attribute(string(1, c), ""))); +} + +void Token::appendAttributeName(char c) { + attributes.back()->name += c; +} + +void Token::appendAttributeValue(char c) { + attributes.back()->value += c; +} diff --git a/kernel/HTML/HTMLToken.h b/kernel/HTML/HTMLToken.h new file mode 100644 index 0000000..812f108 --- /dev/null +++ b/kernel/HTML/HTMLToken.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +namespace HTML { + class Token { + private: + // for StartTag and EndTag + struct Attribute { + string name; + string value; + + Attribute(string _name, string _value); + }; + bool selfClosingFlag = false; + List> attributes; + + public: + // for all types + enum class Type { + Character, StartTag, EndTag, DOCTYPE, Comment, EndOfFile + }; + const Type type; + string data; + + // for all types + explicit Token(Type tokenType); + + // for StartTag and EndTag + void setSelfClosingFlag(); + bool isSelfClosing(); + void appendAttribute(char c); + void appendAttributeName(char c); + void appendAttributeValue(char c); + }; +} diff --git a/kernel/HTML/HTMLTokenizer.cpp b/kernel/HTML/HTMLTokenizer.cpp new file mode 100644 index 0000000..61d47d1 --- /dev/null +++ b/kernel/HTML/HTMLTokenizer.cpp @@ -0,0 +1,1275 @@ +#include "HTMLTokenizer.h" + +using namespace HTML; + +enum class Tokenizer::State { + Data, + CharacterReferenceInData, + RCDATA, + CharacterReferenceInRCDATA, + RAWTEXT, + ScriptData, + PLAINTEXT, + TagOpen, + EndTagOpen, + TagName, + RCDATALessThanSign, + RCDATAEndTagOpen, + RCDATAEndTagName, + RAWTEXTLessThanSign, + RAWTEXTEndTagOpen, + RAWTEXTEndTagName, + ScriptDataLessThanSign, + ScriptDataEndTagOpen, + ScriptDataEndTagName, + ScriptDataEscapeStart, + ScriptDataEscapeStartDash, + ScriptDataEscaped, + ScriptDataEscapedDash, + ScriptDataEscapedDashDash, + ScriptDataEscapedLessThanSign, + ScriptDataEscapedEndTagOpen, + ScriptDataEscapedEndTagName, + ScriptDataDoubleEscapeStart, + ScriptDataDoubleEscaped, + ScriptDataDoubleEscapedDash, + ScriptDataDoubleEscapedDashDash, + ScriptDataDoubleEscapedLessThanSign, + ScriptDataDoubleEscapeEnd, + BeforeAttributeName, + AttributeName, + AfterAttributeName, + BeforeAttributeValue, + AttributeValueDoubleQuoted, + AttributeValueSingleQuoted, + AttributeValueUnquoted, + CharacterReferenceInAttributeValue, + AfterAttributeValueQuoted, + SelfClosingStartTag, + BogusComment, + MarkupDeclarationOpen, + CommentStart, + CommentStartDash, + Comment, + CommentEndDash, + CommentEnd, + CommentEndBang, + DOCTYPE, + BeforeDOCTYPEName, + DOCTYPEName, + AfterDOCTYPEName, + AfterDOCTYPEPublicKeyword, + BeforeDOCTYPEPublicIdentifier, + DOCTYPEPublicIdentifierDoubleQuoted, + DOCTYPEPublicIdentifierSingleQuoted, + AfterDOCTYPEPublicIdentifier, + BetweenDOCTYPEPublicAndSystemIdentifiers, + AfterDOCTYPESystemKeyword, + BeforeDOCTYPESystemIdentifier, + DOCTYPESystemIdentifierDoubleQuoted, + DOCTYPESystemIdentifierSingleQuoted, + AfterDOCTYPESystemIdentifier, + BogusDOCTYPE, + CDATASection +}; + +Tokenizer::Tokenizer() : tokens(128) {} + +Queue> &Tokenizer::tokenize(const string &inputStream) { + State state = State::Data; // Data state + unique_ptr token; + + auto it = inputStream.begin(); + int i = 0; + bool endFlag = false; + while (!endFlag) { + switch (state) { + case State::Data: // Data state + if (it == inputStream.end()) { + // EOF + // Emit the end-of-file token. + emitEOFToken(); + endFlag = true; + continue; + } + switch (*it) { + case '&': + // Switch to the character reference in data state. + state = State::CharacterReferenceInData; + break; + + case '<': + // Switch to the tag open state. + state = State::TagOpen; + break; + + default: + // Emit the current input character as a character token. + emitCharacterToken(*it); + break; + } + break; + + case State::CharacterReferenceInData: // Character reference in data state + break; + + case State::RCDATA: // RCDATA state + break; + + case State::CharacterReferenceInRCDATA: // Character reference in RCDATA state + break; + + case State::RAWTEXT: // RAWTEXT state + break; + + case State::ScriptData: // Script data state + break; + + case State::PLAINTEXT: // PLAINTEXT state + break; + + case State::TagOpen: // Tag open state + switch (*it) { + case '!': + // Switch to the markup declaration open state. + state = State::MarkupDeclarationOpen; + break; + + case '/': + // Switch to the end tag open state. + state = State::EndTagOpen; + break; + + case '?': + // Parse Error. + parseError(); + // Switch to the bogus comment state. + state = State::BogusComment; + break; + + default: + // ASCII letter + if (('A' <= *it && *it <= 'Z') || ('a' <= *it && *it <= 'z')) { + // Create a new start tag token. + token.reset(new Token(Token::Type::StartTag)); + state = State::TagName; + continue; + } + + // Emit a U+003C LESS-THAN SIGN character token and reconsume the current input character in the data state. + parseError(); + emitCharacterToken('<'); + state = State::Data; + continue; + } + break; + + case State::EndTagOpen: // End tag open state + if (it == inputStream.end()) { + // Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F SOLIDUS character token. Reconsume the EOF character in the data state. + parseError(); + emitCharacterToken('<'); + emitCharacterToken('/'); + state = State::Data; + continue; + } else if (*it == '>') { + parseError(); + state = State::Data; + } else { + if (('A' <= *it && *it <= 'Z') || ('a' <= *it && *it <= 'z')) { + // Create a new end tag token, set its tag name to the current input character, then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.) + token.reset(new Token(Token::Type::EndTag)); + token->data = ('A' <= *it && *it <= 'Z') ? *it + 0x20 : *it; + state = State::TagName; + break; + } else { + parseError(); + state = State::BogusComment; + } + } + break; + + case State::TagName: // Tag name state + if (it == inputStream.end()) { + // EOF + parseError(); + state = State::Data; + continue; + } + switch (*it) { + case 0x0009: // Tab + case 0x000a: // LF + case 0x000c: // FF + case ' ': + // Switch to the before attribute name state. + state = State::BeforeAttributeName; + break; + + case '/': + // Switch to the self-closing start tag state. + state = State::SelfClosingStartTag; + break; + + case '>': + // Emit the current tag token. + emitToken(token); + state = State::Data; + break; + + case 0: // NULL + // Parse error. + parseError(); + // Append a U+FFFD REPLACEMENT CHARACTER character to the current tag token's tag name. + token->data += "\ufffd"; + emitToken(token); + break; + + default: + // Append the current input character to the current tag token's tag name. + token->data += ('A' <= *it && *it <= 'Z') ? *it + 0x20 : *it; + break; + } + break; + + case State::BeforeAttributeName: // Before attribute name state + if (it == inputStream.end()) { + // EOF + parseError(); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + // Ignore + break; + + case '/': + // Switch to the self-closing start tag state. + state = State::SelfClosingStartTag; + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + case '"': + case '\'': + case '<': + case '=': + parseError(); + default: + // Start a new attribute in the current tag token. + // Set that attribute's name to the current input character, and its value to the empty string. + token->appendAttribute(('A' <= *it && *it <= 'Z') ? *it + 0x20 : *it); + // Switch to the attribute name state. + state = State::AttributeName; + break; + } + break; + + case State::AttributeName: + if (it == inputStream.end()) { + // EOF + parseError(); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + state = State::AfterAttributeName; + break; + + case '/': + state = State::SelfClosingStartTag; + break; + + case '=': + state = State::BeforeAttributeValue; + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + case '"': + case '\'': + case '<': + parseError(); + default: + token->appendAttributeName(('A' <= *it && *it <= 'Z') ? *it + 0x20 : *it); + break; + } + break; + + case State::AfterAttributeName: + if (it == inputStream.end()) { + // EOF + parseError(); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + // ignore + break; + + case '/': + state = State::SelfClosingStartTag; + break; + + case '=': + state = State::BeforeAttributeValue; + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + case '"': + case '\'': + case '<': + parseError(); + default: + // Start a new attribute in the current tag token. + // Set that attribute's name to the current input character, and its value to the empty string. + token->appendAttribute(('A' <= *it && *it <= 'Z') ? *it + 0x20 : *it); + // Switch to the attribute name state. + state = State::AttributeName; + break; + } + break; + + case State::BeforeAttributeValue: + if (it == inputStream.end()) { + // EOF + parseError(); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + // ignore + break; + + case '"': + state = State::AttributeValueDoubleQuoted; + break; + + case '&': + state = State::AttributeValueUnquoted; + continue; + + case '\'': + state = State::AttributeValueSingleQuoted; + break; + + case '>': + parseError(); + state = State::Data; + emitToken(token); + break; + + case '<': + case '=': + case '`': + parseError(); + default: + state = State::AttributeValueUnquoted; + continue; + } + break; + + case State::AttributeValueDoubleQuoted: + if (it == inputStream.end()) { + // EOF + parseError(); + state = State::Data; + continue; + } + switch (*it) { + case '"': + state = State::AfterAttributeValueQuoted; + break; + + case '&': + state = State::CharacterReferenceInAttributeValue; + // with the additional allowed character being U+0022 QUOTATION MARK ("). + break; + + default: + token->appendAttributeValue(*it); + break; + } + break; + + case State::AttributeValueSingleQuoted: + if (it == inputStream.end()) { + // EOF + parseError(); + state = State::Data; + continue; + } + switch (*it) { + case '\'': + state = State::AfterAttributeValueQuoted; + break; + + case '&': + state = State::CharacterReferenceInAttributeValue; + // with the additional allowed character being U+0027 APOSTROPHE ('). + break; + + default: + token->appendAttributeValue(*it); + break; + } + break; + + case State::AttributeValueUnquoted: + if (it == inputStream.end()) { + // EOF + parseError(); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + state = State::BeforeAttributeName; + break; + + case '&': + state = State::CharacterReferenceInAttributeValue; + // with the additional allowed character being U+003E GREATER-THAN SIGN (>). + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + case '"': + case '\'': + case '<': + case '=': + case '`': + parseError(); + default: + token->appendAttributeValue(*it); + break; + } + break; + + case State::CharacterReferenceInAttributeValue: + break; + + case State::AfterAttributeValueQuoted: + if (it == inputStream.end()) { + // EOF + parseError(); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + state = State::BeforeAttributeName; + break; + + case '/': + state = State::SelfClosingStartTag; + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + default: + parseError(); + state = State::BeforeAttributeName; + continue; + } + break; + + case State::SelfClosingStartTag: // Self-closing start tag state + if (it == inputStream.end()) { // EOF + parseError(); + state = State::Data; + continue; + } else if (*it == '>') { + // Set the self-closing flag of the current tag token. Switch to the data state. Emit the current tag token. + token->setSelfClosingFlag(); + state = State::Data; + emitToken(token); + } else { + parseError(); + state = State::BeforeAttributeName; + continue; + } + break; + + case State::MarkupDeclarationOpen: + if (inputStream.compare(i, 2, "--") == 0) { + // create a comment token whose data is the empty string, and switch to the comment start state. + token.reset(new Token(Token::Type::Comment)); + i += 2; + state = State::CommentStart; + continue; + } else if (inputStream.comparei(i, 7, "DOCTYPE") == 0) { + i += 7; + state = State::DOCTYPE; + continue; + } + // Otherwise, if the insertion mode is "in foreign content" and the current node is not an element in the HTML namespace and the next seven characters are an case-sensitive match for the string "[CDATA[" (the five uppercase letters "CDATA" with a U+005B LEFT SQUARE BRACKET character before and after), then consume those characters and switch to the CDATA section state. + // Otherwise, this is a parse error. Switch to the bogus comment state. The next character that is consumed, if any, is the first character that will be in the comment. + else { + parseError(); + state = State::BogusComment; + continue; + } + break; + + case State::CommentStart: + if (it == inputStream.end()) { + // EOF + parseError(); + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case '-': + state = State::CommentStartDash; + break; + + case '>': + parseError(); + state = State::Data; + emitToken(token); + break; + + default: + state = State::Comment; + continue; + } + break; + + case State::CommentStartDash: + if (it == inputStream.end()) { + // EOF + parseError(); + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case '-': + state = State::CommentEnd; + break; + + case '>': + parseError(); + state = State::Data; + emitToken(token); + break; + + default: + token->data += '-'; + state = State::Comment; + continue; + } + break; + + case State::Comment: + if (it == inputStream.end()) { // EOF + parseError(); + emitToken(token); + state = State::Data; + continue; + } else if (*it == '-') { + state = State::CommentEndDash; + } else { + token->data += *it; + } + break; + + case State::CommentEndDash: + if (it == inputStream.end()) { // EOF + parseError(); + emitToken(token); + state = State::Data; + continue; + } else if (*it == '-') { + state = State::CommentEnd; + } else { + token->data += '-'; + state = State::Comment; + continue; + } + break; + + case State::CommentEnd: + if (it == inputStream.end()) { + // EOF + parseError(); + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case '>': + state = State::Data; + emitToken(token); + break; + + case '!': + parseError(); + state = State::CommentEndBang; + break; + + case '-': + parseError(); + token->data += '-'; + break; + + default: + parseError(); + state = State::Comment; + continue; + } + break; + + case State::CommentEndBang: + if (it == inputStream.end()) { + // EOF + parseError(); + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case '-': + token->data += "-!"; + state = State::CommentEndDash; + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + default: + token->data += "-!"; + state = State::Comment; + continue; + } + break; + + case State::DOCTYPE: + if (it == inputStream.end()) { + // EOF + // Parse error. Create a new DOCTYPE token. Set its force-quirks flag to on. Emit the token. Reconsume the EOF character in the data state. + parseError(); + token.reset(new Token(Token::Type::DOCTYPE)); + // force-quirks flag to on + state = State::Data; + continue; + } + switch (*it) { + case '\t': + case 0x0a: + case 0x0c: + case ' ': + state = State::BeforeDOCTYPEName; + break; + + default: + parseError(); + state = State::BeforeDOCTYPEName; + continue; + } + break; + + case State::BeforeDOCTYPEName: + if (it == inputStream.end()) { + // EOF + parseError(); + state = State::Data; + token.reset(new Token(Token::Type::DOCTYPE)); + // set forse-quirks flag to on + emitToken(token); + continue; + } + switch (*it) { + case '\t': + case 0x0a: + case 0x0c: + case ' ': + // ignore + break; + + case 0: // NULL + // Parse error. Set the token's name to a U+FFFD REPLACEMENT CHARACTER character. Switch to the DOCTYPE name state. + parseError(); + token->data += "\ufffd"; + state = State::DOCTYPEName; + continue; + + case '>': + state = State::Data; + break; + + default: + // create a new DOCTYPE token + token.reset(new Token(Token::Type::DOCTYPE)); + token->data += ('A' <= *it && *it <= 'Z') ? *it + 0x20 : *it; + state = State::DOCTYPEName; + break; + } + break; + + case State::DOCTYPEName: + if (it == inputStream.end()) { + // EOF + // Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. + // set force-quirks flag to on. + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case '\t': + case 0x0a: + case 0x0c: + case ' ': + state = State::AfterDOCTYPEName; + break; + + case '>': + state = State::Data; + // emit the current DOCTYPE token + emitToken(token); + break; + + default: + token->data += ('A' <= *it && *it <= 'Z') ? *it + 0x20 : *it; + break; + } + break; + + case State::AfterDOCTYPEName: + if (it == inputStream.end()) { + // EOF + parseError(); + // set force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case '\t': + case 0x0a: + case 0x0c: + case ' ': + // ignore + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + default: + if (inputStream.comparei(i, 6, "public") == 0) { + // consume those characters and switch to the after DOCTYPE public keyword state. + i += 6; + state = State::AfterDOCTYPEPublicKeyword; + continue; + } else if (inputStream.comparei(i, 6, "system") == 0) { + // consume those characters and switch to the after DOCTYPE system keyword state. + i += 6; + state = State::AfterDOCTYPESystemKeyword; + continue; + } else { + parseError(); + // force-quirks flag to on + state = State::BogusDOCTYPE; + } + break; + } + break; + + case State::AfterDOCTYPEPublicKeyword: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + state = State::BeforeDOCTYPEPublicIdentifier; + break; + + case '"': + parseError(); + // Set the DOCTYPE token's public identifier to the empty string (not missing), + // then switch to the DOCTYPE public identifier (double-quoted) state. + state = State::DOCTYPEPublicIdentifierDoubleQuoted; + break; + + case '\'': + parseError(); + // Set the DOCTYPE token's public identifier to the empty string (not missing), + // then switch to the DOCTYPE public identifier (single-quoted) state. + state = State::DOCTYPEPublicIdentifierSingleQuoted; + break; + + case '>': + parseError(); + // force-quirks flag to on + state = State::Data; + emitToken(token); + break; + + default: + parseError(); + // force-quirks flag to on + state = State::BogusDOCTYPE; + break; + } + break; + + case State::BeforeDOCTYPEPublicIdentifier: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + // ignore + break; + + case '"': + parseError(); + // Set the DOCTYPE token's public identifier to the empty string (not missing), + // then switch to the DOCTYPE public identifier (double-quoted) state. + state = State::DOCTYPEPublicIdentifierDoubleQuoted; + break; + + case '\'': + parseError(); + // Set the DOCTYPE token's public identifier to the empty string (not missing), + // then switch to the DOCTYPE public identifier (single-quoted) state. + state = State::DOCTYPEPublicIdentifierSingleQuoted; + break; + + case '>': + parseError(); + // force-quirks flag to on + state = State::Data; + emitToken(token); + break; + + default: + parseError(); + // force-quirks flag to on + state = State::BogusDOCTYPE; + break; + } + break; + + case State::DOCTYPEPublicIdentifierDoubleQuoted: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case '"': + state = State::AfterDOCTYPEPublicIdentifier; + break; + + case '>': + parseError(); + // force-quirks flag to on + state = State::Data; + emitToken(token); + break; + + default: + // Append the current input character to the current DOCTYPE token's public identifier. + break; + } + break; + + case State::DOCTYPEPublicIdentifierSingleQuoted: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case '\'': + state = State::AfterDOCTYPEPublicIdentifier; + break; + + case '>': + parseError(); + // force-quirks flag to on + state = State::Data; + emitToken(token); + break; + + default: + // Append the current input character to the current DOCTYPE token's public identifier. + break; + } + break; + + case State::AfterDOCTYPEPublicIdentifier: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + state = State::BetweenDOCTYPEPublicAndSystemIdentifiers; + break; + + case '"': + parseError(); + // Set the DOCTYPE token's system identifier to the empty string (not missing), + // then switch to the DOCTYPE system identifier (double-quoted) state. + state = State::DOCTYPESystemIdentifierDoubleQuoted; + break; + + case '\'': + parseError(); + // Set the DOCTYPE token's system identifier to the empty string (not missing), + // then switch to the DOCTYPE system identifier (single-quoted) state. + state = State::DOCTYPESystemIdentifierSingleQuoted; + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + default: + parseError(); + // force-quirks flag to on + state = State::BogusDOCTYPE; + break; + } + break; + + case State::BetweenDOCTYPEPublicAndSystemIdentifiers: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + // ignore + break; + + case '"': + parseError(); + // Set the DOCTYPE token's system identifier to the empty string (not missing), + // then switch to the DOCTYPE system identifier (double-quoted) state. + state = State::DOCTYPESystemIdentifierDoubleQuoted; + break; + + case '\'': + parseError(); + // Set the DOCTYPE token's system identifier to the empty string (not missing), + // then switch to the DOCTYPE system identifier (single-quoted) state. + state = State::DOCTYPESystemIdentifierSingleQuoted; + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + default: + parseError(); + // force-quirks flag to on + state = State::BogusDOCTYPE; + break; + } + break; + + case State::AfterDOCTYPESystemKeyword: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + state = State::BeforeDOCTYPESystemIdentifier; + break; + + case '"': + parseError(); + // Set the DOCTYPE token's system identifier to the empty string (not missing), + // then switch to the DOCTYPE system identifier (double-quoted) state. + state = State::DOCTYPESystemIdentifierDoubleQuoted; + break; + + case '\'': + parseError(); + // Set the DOCTYPE token's system identifier to the empty string (not missing), + // then switch to the DOCTYPE system identifier (single-quoted) state. + state = State::DOCTYPESystemIdentifierSingleQuoted; + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + default: + parseError(); + // force-quirks flag to on + state = State::BogusDOCTYPE; + break; + } + break; + + case State::BeforeDOCTYPESystemIdentifier: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + // ignore + break; + + case '"': + parseError(); + // Set the DOCTYPE token's system identifier to the empty string (not missing), + // then switch to the DOCTYPE system identifier (double-quoted) state. + state = State::DOCTYPESystemIdentifierDoubleQuoted; + break; + + case '\'': + parseError(); + // Set the DOCTYPE token's system identifier to the empty string (not missing), + // then switch to the DOCTYPE system identifier (single-quoted) state. + state = State::DOCTYPESystemIdentifierSingleQuoted; + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + default: + parseError(); + // force-quirks flag to on + state = State::BogusDOCTYPE; + break; + } + break; + + case State::DOCTYPESystemIdentifierDoubleQuoted: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case '"': + state = State::AfterDOCTYPESystemIdentifier; + break; + + case '>': + parseError(); + // force-quirks flag to on + state = State::Data; + emitToken(token); + break; + + default: + // Append the current input character to the current DOCTYPE token's system identifier. + break; + } + break; + + case State::DOCTYPESystemIdentifierSingleQuoted: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case '\'': + state = State::AfterDOCTYPESystemIdentifier; + break; + + case '>': + parseError(); + // force-quirks flag to on + state = State::Data; + emitToken(token); + break; + + default: + // Append the current input character to the current DOCTYPE token's system identifier. + break; + } + break; + + case State::AfterDOCTYPESystemIdentifier: + if (it == inputStream.end()) { + // EOF + parseError(); + // force-quirks flag to on + emitToken(token); + state = State::Data; + continue; + } + switch (*it) { + case 0x09: + case 0x0a: + case 0x0c: + case ' ': + // ignore + break; + + case '>': + state = State::Data; + emitToken(token); + break; + + default: + parseError(); + state = State::BogusDOCTYPE; + // This does not set the DOCTYPE token's force-quirks flag to on. + break; + } + break; + + case State::BogusDOCTYPE: + if (it == inputStream.end()) { // EOF + emitToken(token); + state = State::Data; + continue; + } else if (*it == '>') { + state = State::Data; + emitToken(token); + } else { + // ignore + } + break; + + default: + break; + } + + // TODO: 本来はこれがなくても無限ループは起こらないはずなので,解決したら外す + if (it == inputStream.end()) { + // EOF + endFlag = true; + continue; + } + + ++it; + ++i; + } + + return tokens; +} + +void Tokenizer::emitCharacterToken(char c) { + shared_ptr token(new Token(Token::Type::Character)); + token->data += c; + tokens.push(token); +} + +void Tokenizer::emitEOFToken() { + tokens.push(shared_ptr(new Token(Token::Type::EndOfFile))); +} + +void Tokenizer::emitToken(unique_ptr &token) { + // 現在のままではよくない. + // std::move 相当のものを実装したら修正するべき + tokens.push(shared_ptr(token.release())); +} + +void Tokenizer::parseError() {} diff --git a/kernel/HTML/HTMLTokenizer.h b/kernel/HTML/HTMLTokenizer.h new file mode 100644 index 0000000..0ea70aa --- /dev/null +++ b/kernel/HTML/HTMLTokenizer.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include "HTMLToken.h" + +namespace HTML { + class Tokenizer { + private: + enum class State; + Queue> tokens; + + void emitCharacterToken(char c); + void emitEOFToken(); + void emitToken(unique_ptr &token); + void parseError(); + + public: + Tokenizer(); + Queue> &tokenize(const string &inputStream); + }; +} + diff --git a/kernel/HTML/HTMLTreeBuilder.cpp b/kernel/HTML/HTMLTreeBuilder.cpp new file mode 100644 index 0000000..12e06bb --- /dev/null +++ b/kernel/HTML/HTMLTreeBuilder.cpp @@ -0,0 +1,696 @@ +#include +#include "HTMLTreeBuilder.h" + +using namespace HTML; + +enum class TreeBuilder::Mode { + Initial, + BeforeHtml, + BeforeHead, + InHead, + InHeadNoscript, + AfterHead, + InBody, + Text, + InTable, + InTableText, + InCaption, + InColumnGroup, + InTableBody, + InRow, + InCell, + InSelect, + InSelectInTable, + InTemplate, + AfterBody, + InFrameset, + AfterFrameset, + AfterAfterBody, + AfterAfterFrameseet +}; + +Document &TreeBuilder::construct(Queue> &tokens) { + Mode mode = Mode::Initial; + shared_ptr token; + Stack> openTags(256); // stack of open elements + bool scripting = false; // scripting flag + + if (tokens.isempty()) return document; + token = tokens.pop(); + + // token 取り出し + do { + switch (mode) { + case Mode::Initial: + switch (token->type) { + case Token::Type::Character: + // ignore + break; + + case Token::Type::Comment: + // Append a Comment node to the Document object with the data attribute set to the data given in the comment token. + break; + + case Token::Type::DOCTYPE: + /* parseError の条件あり */ + + document.appendChild(shared_ptr(new DocumentType(token->data))); + // publicId and systemId も + + mode = Mode::BeforeHtml; + break; + + default: + // If the document is not an iframe srcdoc document, then this is a parse error; set the Document to quirks mode. + + // In any case, switch the insertion mode to "before html", then reprocess the current token. + mode = Mode::BeforeHtml; + continue; + } + break; + + case Mode::BeforeHtml: { + auto actAsAnythingElse = [&] { + // Create an html element. Append it to the Document object. Put this element in the stack of open elements. + shared_ptr elem(new Element(token->data)); + document.appendChild(elem); + openTags.push(elem); + + // If the Document is being loaded as part of navigation of a browsing context, then: run the application cache selection algorithm with no manifest, passing it the Document object. + + // Switch the insertion mode to "before head", then reprocess the current token. + mode = Mode::BeforeHead; + }; + switch (token->type) { + case Token::Type::DOCTYPE: + parseError(); + // ignore + break; + + case Token::Type::Comment: + // Append a Comment node to the Document object with the data attribute set to the data given in the comment token. + break; + + case Token::Type::Character: + // ignore + break; + + case Token::Type::StartTag: + if (token->data == "html") { + // Create an element for the token in the HTML namespace. + shared_ptr elem(new Element(token->data)); + // Append it to the Document object. + document.appendChild(elem); + // Put this element in the stack of open elements. + openTags.push(elem); + + // If the Document is being loaded as part of navigation of a browsing context, + // then: if the newly created element has a manifest attribute whose value is not the empty string, + // then resolve the value of that attribute to an absolute URL, relative to the newly created element, + // and if that is successful, run the application cache selection algorithm + // with the resulting absolute URL with any component removed; + // otherwise, if there is no such attribute, or its value is the empty string, + // or resolving its value fails, run the application cache selection algorithm with no manifest. + // The algorithm must be passed the Document object. + + mode = Mode::BeforeHead; + } else { + actAsAnythingElse(); + continue; + } + break; + + case Token::Type::EndTag: + if (token->data == "head" || token->data == "body" || token->data == "html" || token->data == "br") { + actAsAnythingElse(); + continue; + } else { + parseError(); + // ignore + } + break; + + default: + actAsAnythingElse(); + continue; + } + break; + } + + case Mode::BeforeHead: { + auto actAsAnythingElse = [&] { + // Act as if a start tag token with the tag name "head" and no attributes had been seen, then reprocess the current token. + // Insert an HTML element for the token. + + // Set the head element pointer to the newly created head element. + + mode = Mode::InHead; + }; + switch (token->type) { + case Token::Type::Character: + if (token->data == "\t" || token->data == "\n" || token->data == "\f" || token->data == "\r" || token->data == " ") { + // ignore + } else { + actAsAnythingElse(); + continue; + } + break; + + case Token::Type::Comment: + // Append a Comment node to the Document object with the data attribute set to the data given in the comment token. + break; + + case Token::Type::DOCTYPE: + parseError(); + // ignore + break; + + case Token::Type::StartTag: + if (token->data == "html") { + mode = Mode::InBody; + continue; + } else if (token->data == "head") { + // Insert an HTML element for the token. + shared_ptr elem(new Element(token->data)); + openTags.top()->appendChild(elem); + openTags.push(elem); + + // Set the head element pointer to the newly created head element. + + mode = Mode::InHead; + } + break; + + case Token::Type::EndTag: + if (token->data == "head" || token->data == "body" || token->data == "html" || token->data == "br") { + actAsAnythingElse(); + continue; + } else { + parseError(); + // ignore + } + break; + + default: + actAsAnythingElse(); + continue; + } + break; + } + + case Mode::InHead: { + auto actAsAnythingElse = [&] { + openTags.pop(); + mode = Mode::AfterHead; + }; + switch (token->type) { + case Token::Type::Character: + break; + + case Token::Type::Comment: + break; + + case Token::Type::DOCTYPE: + break; + + case Token::Type::StartTag: + if (token->data == "html") { + + } else if (token->data == "base" || token->data == "basefont" || token->data == "bgsound" || token->data == "link") { + + } else if (token->data == "meta") { + + } else if (token->data == "title") { + // Follow the generic RCDATA element parsing algorithm. + } else if ((token->data == "noscript" && scripting) || token->data == "noframes" || token->data == "style") { + + } else if (token->data == "noscript") { // && !scripting + + } else if (token->data == "script") { + + } else if (token->data == "template") { + + } else if (token->data == "head") { + parseError(); + // ignore + } + break; + + case Token::Type::EndTag: + if (token->data == "head") { + openTags.pop(); + mode = Mode::AfterHead; + } else if (token->data == "body" || token->data == "html" || token->data == "br") { + actAsAnythingElse(); + continue; + } else if (token->data == "template") { + + } else { + parseError(); + // ignore + } + break; + + default: + actAsAnythingElse(); + continue; + } + break; + } + + case Mode::InHeadNoscript: + break; + + case Mode::AfterHead: + switch (token->type) { + case Token::Type::Character: + break; + + case Token::Type::Comment: + break; + + case Token::Type::DOCTYPE: + break; + + case Token::Type::StartTag: + if (token->data == "html") { + + } else if (token->data == "body") { + openTags.push(openTags.top()->appendChild(shared_ptr(new Element(token->data)))); + + // Set the frameset-ok flag to "not ok". + + mode = Mode::InBody; + } else if (token->data == "frameset") { + + } else if (token->data == "base" + || token->data == "basefont" + || token->data == "bgsound" + || token->data == "link" + || token->data == "meta" + || token->data == "noframes" + || token->data == "script" + || token->data == "style" + || token->data == "template" + || token->data == "title") { + parseError(); + + + } else if (token->data == "head") { + parseError(); + // ignore + } + break; + + case Token::Type::EndTag: + if (token->data == "template") { + + } else if (token->data == "body" || token->data == "html" || token->data == "br") { + + } else { + parseError(); + // ignore + } + break; + + default: + break; + } + break; + + case Mode::InBody: + switch (token->type) { + case Token::Type::Character: + /*if (token->data == 0) { + parseError(); + // ignore + } else */ + if (token->data == "\t" || token->data == "\n" || token->data == "\f" || token->data == "\r" || token->data == " ") { + // Reconstruct the active formatting elements, if any. + + // Insert the token's character. + } else { + // Reconstruct the active formatting elements, if any. + + // Insert the token's character. + + // Set the frameset-ok flag to "not ok". + } + break; + + case Token::Type::Comment: + break; + + case Token::Type::DOCTYPE: + parseError(); + // ignore + break; + + case Token::Type::StartTag: + if (token->data == "html") { + + } else if (token->data == "base" + || token->data == "basefont" + || token->data == "bgsound" + || token->data == "link" + || token->data == "meta" + || token->data == "noframes" + || token->data == "script" + || token->data == "style" + || token->data == "template" + || token->data == "title") { + + } else if (token->data == "body") { + + } else if (token->data == "frameset") { + + } else if (token->data == "address" + || token->data == "article" + || token->data == "aside" + || token->data == "blockquote" + || token->data == "center" + || token->data == "details" + || token->data == "dialog" + || token->data == "dir" + || token->data == "div" + || token->data == "dl" + || token->data == "fieldset" + || token->data == "figcaption" + || token->data == "figure" + || token->data == "footer" + || token->data == "header" + || token->data == "hgroup" + || token->data == "main" + || token->data == "menu" + || token->data == "nav" + || token->data == "ol" + || token->data == "p" + || token->data == "section" + || token->data == "summary" + || token->data == "ul") { + // If the stack of open elements does not have an element in scope that is an HTML element with + // the same tag name as that of the token, then this is a parse error; ignore the token. + + // Otherwise, run these steps: + // 1. Generate implied end tags. + // 2. If the current node is not an HTML element with the same tag name as that of the token, then this is a parse error. + // 3. Pop elements from the stack of open elements until an HTML element with the same tag name as the token has been popped from the stack. + } else if (token->data == "h1" + || token->data == "h2" + || token->data == "h3" + || token->data == "h4" + || token->data == "h5" + || token->data == "h6") { + openTags.push(openTags.top()->appendChild(shared_ptr(new Element(token->data)))); + // If the stack of open elements does not have an element in scope that is an HTML element and + // whose tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then this is a parse error; ignore the token. + + // Otherwise, run these steps: + // 1. Generate implied end tags. + // 2. If the current node is not an HTML element with the same tag name as that of the token, then this is a parse error. + // 3. Pop elements from the stack of open elements until an HTML element whose tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6" has been popped from the stack. + } else if (token->data == "pre" || token->data == "listing") { + + } else if (token->data == "form") { + + } else if (token->data == "li") { + + } else if (token->data == "dd" || token->data == "dt") { + + } else if (token->data == "plaintext") { + + } else if (token->data == "button") { + + } else if (token->data == "a") { + + } else if (token->data == "b" + || token->data == "big" + || token->data == "code" + || token->data == "em" + || token->data == "font" + || token->data == "i" + || token->data == "s" + || token->data == "small" + || token->data == "strike" + || token->data == "strong" + || token->data == "tt" + || token->data == "u") { + + } else if (token->data == "nobr") { + + } else if (token->data == "applet" || token->data == "marquee" || token->data == "object") { + + } else if (token->data == "table") { + + } else if (token->data == "area" + || token->data == "br" + || token->data == "embed" + || token->data == "img" + || token->data == "keygen" + || token->data == "wbr") { + + } else if (token->data == "input") { + + } else if (token->data == "menuitem" + || token->data == "param" + || token->data == "source" + || token->data == "track") { + + } else if (token->data == "hr") { + + } else if (token->data == "image") { + + } else if (token->data == "isindex") { + + } else if (token->data == "textarea") { + + } else if (token->data == "xmp") { + + } else if (token->data == "iframe") { + + } else if (token->data == "noembed" || (token->data == "noscript" && scripting)) { + + } else if (token->data == "select") { + + } else if (token->data == "optgroup" || token->data == "option") { + + } else if (token->data == "rp" || token->data == "rt") { + + } else if (token->data == "math") { + + } else if (token->data == "svg") { + + } else if (token->data == "caption" + || token->data == "col" + || token->data == "colgroup" + || token->data == "frame" + || token->data == "head" + || token->data == "tbody" + || token->data == "td" + || token->data == "tfoot" + || token->data == "th" + || token->data == "thead" + || token->data == "tr") { + + } else { + + } + break; + + case Token::Type::EndTag: + if (token->data == "template") { + // Process the token using the rules for the "in head" insertion mode. + } else if (token->data == "body") { + // If the stack of open elements does not have a body element in scope, this is a parse error; ignore the token. + + // Otherwise, if there is a node in the stack of open elements that is not either + // a dd element, a dt element, an li element, an optgroup element, an option element, + // a p element, an rp element, an rt element, a tbody element, a td element, a tfoot element, + // a th element, a thead element, a tr element, the body element, or the html element, + // then this is a parse error. + + // Switch the insertion mode to "after body". + mode = Mode::AfterBody; + } else if (token->data == "html") { + + } else if (token->data == "address" + || token->data == "article" + || token->data == "aside" + || token->data == "blockquote" + || token->data == "button" + || token->data == "center" + || token->data == "details" + || token->data == "dialog" + || token->data == "dir" + || token->data == "div" + || token->data == "dl" + || token->data == "fieldset" + || token->data == "figcaption" + || token->data == "figure" + || token->data == "footer" + || token->data == "header" + || token->data == "hgroup" + || token->data == "main" + || token->data == "menu" + || token->data == "nav" + || token->data == "ol" + || token->data == "pre" + || token->data == "section" + || token->data == "summary" + || token->data == "ul") { + + } else if (token->data == "form") { + + } else if (token->data == "p") { + + } else if (token->data == "li") { + + } else if (token->data == "dd" || token->data == "dt") { + + } else if (token->data == "h1" + || token->data == "h2" + || token->data == "h3" + || token->data == "h4" + || token->data == "h5" + || token->data == "h6") { + + } else if (token->data == "sarcasm") { + + } else if (token->data == "a" + || token->data == "b" + || token->data == "big" + || token->data == "code" + || token->data == "em" + || token->data == "font" + || token->data == "i" + || token->data == "nobr" + || token->data == "s" + || token->data == "small" + || token->data == "strike" + || token->data == "strong" + || token->data == "tt" + || token->data == "u") { + + } else if (token->data == "applet" || token->data == "marquee" || token->data == "object") { + + } else if (token->data == "br") { + + } else { + + } + break; + + case Token::Type::EndOfFile: + break; + } + break; + + case Mode::Text: + break; + + case Mode::InTable: + break; + + case Mode::InTableText: + break; + + case Mode::InCaption: + break; + + case Mode::InColumnGroup: + break; + + case Mode::InTableBody: + break; + + case Mode::InRow: + break; + + case Mode::InCell: + break; + + case Mode::InSelect: + break; + + case Mode::InSelectInTable: + break; + + case Mode::InTemplate: + break; + + case Mode::AfterBody: + switch (token->type) { + case Token::Type::Character: + break; + + case Token::Type::Comment: + break; + + case Token::Type::DOCTYPE: + break; + + case Token::Type::StartTag: + break; + + case Token::Type::EndTag: + if (token->data == "html") { + // If the parser was originally created as part of the HTML fragment parsing algorithm, this is a parse error; + // ignore the token. (fragment case) + + mode = Mode::AfterAfterBody; + } + break; + + case Token::Type::EndOfFile: + // Stop parsing. + break; + + default: + break; + } + break; + + case Mode::InFrameset: + break; + + case Mode::AfterFrameset: + break; + + case Mode::AfterAfterBody: + switch (token->type) { + case Token::Type::Comment: + break; + + case Token::Type::DOCTYPE: + break; + + case Token::Type::Character: + break; + + case Token::Type::StartTag: + break; + + case Token::Type::EndOfFile: + // Stop parsing. + break; + + default: + parseError(); + mode = Mode::InBody; + continue; + } + break; + + case Mode::AfterAfterFrameseet: + break; + } + + token = tokens.pop(); + } while (!tokens.isempty()); + + return document; +} + +void TreeBuilder::parseError() { + +} diff --git a/kernel/HTML/HTMLTreeBuilder.h b/kernel/HTML/HTMLTreeBuilder.h new file mode 100644 index 0000000..6fb5d75 --- /dev/null +++ b/kernel/HTML/HTMLTreeBuilder.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include "HTMLToken.h" +#include "HTMLNode.h" + +namespace HTML { + class TreeBuilder { + private: + enum class Mode; + Document document; + + public: + TreeBuilder() {} + Document &construct(Queue> &tokens); + void parseError(); + }; +} diff --git a/kernel/HTML/browser.cpp b/kernel/HTML/browser.cpp new file mode 100644 index 0000000..955c699 --- /dev/null +++ b/kernel/HTML/browser.cpp @@ -0,0 +1,364 @@ +#include "../headers.h" +#include + +Browser::Browser(const char *url) { + // ファイル読み込み + File htmlFile(url); + if (htmlFile.open()) { + source.reset(htmlFile.read()); + size = htmlFile.size; + + // 内部シート作成 + sheet.reset(new Sheet(Size(SheetCtl::scrnx_ - SheetCtl::back_->bxsize - 1, SheetCtl::scrny_ * 3), false)); + sheet->fillRect(Rectangle(sheet->bxsize - 1, sheet->bysize - 1), 0xffffff); + + // マウスに登録 + Mouse::browserTask = TaskSwitcher::getNowTask(); + } +} + +// HTMLファイルを表示 +void Browser::Render() { + int x = 15, y = 15, /*titlex = 50, */j; + Encoding encode = Encoding::SJIS; + char s[11]; + bool uline = false, bold = false, title = false, line = false, pre = false, list = false; + unsigned int fcolor = 0; + //char text[240 * 1024]; + //int tcount = 0; + + for (int i = 0; i < size; ++i) { + if (source[i] == '<') { // タグ + ++i; + // 要素名を小文字でsに代入 + for (j = 0; source[i] != ' ' && source[i] != '>' && j < 10 && i < size; ++i, ++j) { + if ('A' <= source[i] && source[i] <= 'Z') source[i] += 0x20; + s[j] = source[i]; + } + s[j] = 0; + + if (!strcmp(s, "meta")) { + if (source[i] == ' ') ++i; + if (!strncmpi((char*)source + i, "charset=", 8)) { + i += 8; + if (source[i] == '"') ++i; + if (!strncmpi((char*)source + i, "shift_jis", 9)) { + encode = Encoding::SJIS; + } else if (!strncmpi((char*)source + i, "utf-8", 5)) { + encode = Encoding::UTF8; + } else if (!strncmpi((char*)source + i, "euc-jp", 6)) { + encode = Encoding::EUCJP; + } + } else if (!strncmpi((char*)source + i, "http-equiv=\"content-type\" ", 26)) { + i += 26; + if (!strncmpi((char*)source + i, "content=\"", 9)) { + i += 9; + while (source[i] != '"' && source[i] != '>' && i + 8 < size) { + if (!strncmp((char*)source + i, "charset=", 8)) { + i += 8; + if (!strncmpi((char*)source + i, "shift_jis", 9)) { + encode = Encoding::SJIS; + break; + } else if (!strncmpi((char*)source + i, "utf-8", 5)) { + encode = Encoding::UTF8; + break; + } else if (!strncmpi((char*)source + i, "euc-jp", 6)) { + encode = Encoding::EUCJP; + break; + } + } + ++i; + } + } + } + } else if (!strcmp(s, "title")) { + title = true; + } else if (!strcmp(s, "/title")) { + title = false; + } else if (!strcmp(s, "br")) { + y += 18; + x = 15; + } else if (!strcmp(s, "hr")) { + if (x > 15) { + y += 18; + x = 15; + } + if (y < sheet->bysize - 1 - 16 - 4) { + SheetCtl::drawLine(sheet, Rgb(0, 0, 0), x, y + 8, sheet->bxsize - 1 - 4, y + 8); + } + y += 18; + } else if (!strcmp(s, "p")) { + y += 18; + x = 15; + } else if (!strcmp(s, "/p") && x > 15) { + y += 18; + x = 15; + } else if (!strcmp(s, "h1")) { + if (x > 15) { + y += 18; + x = 15; + } + y += 18; + x = 15; + bold = true; + } else if (!strcmp(s, "/h1")) { + y += 32; + x = 15; + bold = false; + } else if (!strcmp(s, "h2")) { + if (x > 15) { + y += 18; + x = 15; + } + y += 18; + x = 15; + bold = true; + } else if (!strcmp(s, "/h2")) { + y += 32; + x = 15; + bold = false; + } else if (!strcmp(s, "h3")) { + if (x > 15) { + y += 18; + x = 15; + } + y += 18; + x = 15; + bold = true; + } else if (!strcmp(s, "/h3")) { + y += 32; + x = 15; + bold = false; + } else if (!strcmp(s, "h4")) { + if (x > 15) { + y += 18; + x = 15; + } + y += 18; + x = 15; + bold = true; + } else if (!strcmp(s, "/h4")) { + y += 32; + x = 15; + bold = false; + } else if (!strcmp(s, "h5")) { + if (x > 15) { + y += 18; + x = 15; + } + y += 18; + x = 15; + bold = true; + } else if (!strcmp(s, "/h5")) { + y += 32; + x = 15; + bold = false; + } else if (!strcmp(s, "h6")) { + if (x > 15) { + y += 18; + x = 15; + } + y += 18; + x = 15; + bold = true; + } else if (!strcmp(s, "/h6")) { + y += 32; + x = 15; + bold = false; + } else if (!strcmp(s, "u")) { + uline = true; + } else if (!strcmp(s, "/u")) { + uline = false; + } else if (!strcmp(s, "b")) { + bold = true; + } else if (!strcmp(s, "/b")) { + bold = false; + } else if (!strcmp(s, "strong")) { + bold = true; + } else if (!strcmp(s, "/strong")) { + bold = false; + } else if (!strcmp(s, "div") && x > 15) { + y += 18; + x = 15; + } else if (!strcmp(s, "/div") && x > 15) { + y += 18; + x = 15; + } else if (!strcmp(s, "a")) { + ++i; + if (!memcmp(source + i, "href=\"", 6) || !memcmp(source + i, "HREF=\"", 6)) { + uline = true; + fcolor = Rgb(0, 0, 255); + } + } else if (!strcmp(s, "/a")) { + uline = false; + fcolor = Rgb(0, 0, 0); + } else if (!strcmp(s, "button")) { + for (; source[i] != '>' && i < size; ++i) {} + ++i; + for (; source[i] != '>' && i < size; ++i) {} + continue; + } else if (!strcmp(s, "s")) { + line = true; + } else if (!strcmp(s, "/s")) { + line = false; + } else if (!strcmp(s, "pre")) { + pre = true; + } else if (!strcmp(s, "/pre")) { + pre = false; + } else if (!strcmp(s, "ul")) { + list = true; + } else if (!strcmp(s, "/ul")) { + list = false; + } else if (!strcmp(s, "li") && list) { + x = 15 + 8; + y += 18; + //SheetCtl::fillenn(sheet, 0, x + 4, y + 4, x + 12, y + 12); + x += 16; + } else if (!strcmp(s, "/li")) { + } else if (!strcmp(s, "img")) { + SheetCtl::drawRect(sheet, 0, x, y, x + 40, y + 16); + x = 15; + y += 18; + } + for (; source[i] != '>' && i < size; ++i) {} + continue; + } else if (source[i] == '&') { // 特殊記号 + ++i; + if (!strncmpi((char*)source + i, "lt", 2)) { // 不等号より小 + SheetCtl::drawString(sheet, x, y, fcolor, "<"); + x += 8; + i += 2; + } else if (!strncmpi((char*)source + i, "gt", 2)) { // 不等号より大 + SheetCtl::drawString(sheet, x, y, fcolor, ">"); + x += 8; + i += 2; + } else if (!strncmpi((char*)source + i, "amp", 3)) { // アンパサンド + SheetCtl::drawString(sheet, x, y, fcolor, "&"); + x += 8; + i += 3; + } else if (!strncmpi((char*)source + i, "copy", 4)) { // 著作権記号 + SheetCtl::drawString(sheet, x + 4, y, fcolor, "C"); + //SheetCtl::drawenn(sheet, fcolor, x, y, x + 16, y + 16); + x += 16; + i += 4; + } else if (!strncmpi((char*)source + i, "yen", 3)) { // 円記号 + SheetCtl::drawString(sheet, x, y, fcolor, "\\"); + x += 8; + i += 3; + } else if (!strncmpi((char*)source + i, "reg", 3)) { // 登録商標記号 + SheetCtl::drawString(sheet, x + 4, y, fcolor, "R"); + //SheetCtl::drawenn(sheet, fcolor, x, y, x + 16, y + 16); + x += 16; + i += 3; + } else if (!strncmpi((char*)source + i, "quot", 4)) { // 引用符 + SheetCtl::drawString(sheet, x, y, fcolor, "\""); + x += 8; + i += 4; + } else { + --i; + continue; + } + if (source[i] == ';') ++i; + continue; + } else if (source[i] == 0x0a) { + if (pre) { + x = 15; + y += 18; + } else if (x > 15) { + for (; source[i] == 0x0a || source[i] == 0x0d || source[i] == ' ' && i < size; ++i) {} + x += 8; + --i; + } + continue; + } else if (source[i] == '\t' || (source[i] == ' ' && !title && !pre && x == 15) || source[i] == 0x0d) { + continue; + } + + // ウィンドウからx座標がはみ出ていたら無視 + if (x >= sheet->bxsize - 8 - 15) continue; + // ウィンドウからy座標がはみ出ていたら終了 + if (y >= sheet->bysize - 16 - 15) break; + + if (0xe2 <= source[i] && source[i] <= 0xef && encode == Encoding::UTF8) { // UTF-8 3バイト全角文字 + s[0] = source[i]; + s[1] = source[i + 1]; + s[2] = source[i + 2]; + s[3] = 0; + if (title) { + //SheetCtl::drawString(sheet, titlex - sheet.x0, 5 - sheet.y0, Rgb(0, 0, 0), s); + //titlex += 16; + } else { + SheetCtl::drawString(sheet, x, y, fcolor, s); + //text[tcount .. tcount + 2] = s[0 .. 2]; + //tcount += 3; + x += 16; + } + i += 2; + if (bold) SheetCtl::drawString(sheet, x - 15, y, fcolor, s); + if (uline) SheetCtl::drawLine(sheet, fcolor, x - 16, y + 15, x, y + 15); + if (line) SheetCtl::drawLine(sheet, fcolor, x - 16, y + 7, x, y + 7); + } else if ((0xc2 <= source[i] && source[i] <= 0xd1 && encode == Encoding::UTF8) + || ((0x81 <= source[i] && source[i] <= 0x9f) || (0xe0 <= source[i] && source[i] <= 0xfc) && encode == Encoding::SJIS) + || (0x81 <= source[i] && source[i] <= 0xfe && encode == Encoding::EUCJP)) { // 2バイト全角文字 + s[0] = source[i]; + s[1] = source[i + 1]; + s[2] = 0; + if (title) { + //SheetCtl::drawString(sheet, titlex - sheet.x0, 5 - sheet.y0, Rgb(0, 0, 0), s, encode); + //titlex += 16; + } else { + SheetCtl::drawString(sheet, x, y, fcolor, s, encode); + //text[tcount .. tcount + 1] = s[0 .. 1]; + //tcount += 2; + x += 16; + } + ++i; + if (bold) SheetCtl::drawString(sheet, x - 15, y, fcolor, s, encode); + if (uline) SheetCtl::drawLine(sheet, fcolor, x - 16, y + 15, x, y + 15); + if (line) SheetCtl::drawLine(sheet, fcolor, x - 16, y + 7, x, y + 7); + } else { // 半角文字 + s[0] = source[i]; + s[1] = 0; + if (title) { + //SheetCtl::drawString(sheet, titlex - sheet.x0, 5 - sheet.y0, Rgb(0, 0, 0), s); + //titlex += 8; + } else { + SheetCtl::drawString(sheet, x, y, fcolor, s); + //text[tcount] = s[0]; + //++tcount; + x += 8; + } + if (bold) SheetCtl::drawString(sheet, x - 7, y, fcolor, s); + if (uline) SheetCtl::drawLine(sheet, fcolor, x - 8, y + 15, x, y + 15); + if (line) SheetCtl::drawLine(sheet, fcolor, x - 8, y + 7, x, y + 7); + } + } + + //text[tcount] = 0; + //SheetCtl::drawString(SheetCtl::back, 0, 0, Rgb(255, 255, 255), text); + + // sheet の内容の一部を window_[0] に表示 + dy = 0; + Mapping(); +} + +void Browser::Scroll(int data) { + if (data > 0) { + dy += 16; + if (dy > sheet->bysize - SheetCtl::window_[0]->bysize) dy = sheet->bysize - SheetCtl::window_[0]->bysize; + Mapping(); + } else if (data < 0) { + dy -= 16; + if (dy < 0) dy = 0; + Mapping(); + } +} + +void Browser::Mapping() { + for (int y = 0; y < SheetCtl::window_[0]->bysize - 2; ++y) { + for (int x = 0; x < SheetCtl::window_[0]->bxsize - 2; ++x) { + if (SheetCtl::window_[0]->buf[(y + 1) * SheetCtl::window_[0]->bxsize + x + 1] != sheet->buf[(y + dy) * sheet->bxsize + x]) + SheetCtl::window_[0]->buf[(y + 1) * SheetCtl::window_[0]->bxsize + x + 1] = sheet->buf[(y + dy) * sheet->bxsize + x]; + } + } +} diff --git a/kernel/HTML/browser.h b/kernel/HTML/browser.h new file mode 100644 index 0000000..4226476 --- /dev/null +++ b/kernel/HTML/browser.h @@ -0,0 +1,21 @@ +/* + * ブラウザー + */ + +#pragma once + +#include + +class Browser { +private: + shared_ptr source; + unique_ptr sheet; + unsigned int size; + int dy; + +public: + Browser(const char *url); + void Render(); + void Scroll(int data); + void Mapping(); +};