Compare commits

...

10 Commits

Author SHA1 Message Date
9b544ce55d . 2026-05-07 21:36:22 +09:00
aceec99522 . 2026-05-07 21:35:39 +09:00
fe5a09edfb Merge branch 'master' of 192.168.10.104:suwako/Hexagon 2026-05-07 21:33:09 +09:00
ffa88142e8 macOS対応 2026-05-07 21:30:59 +09:00
1fff6cf5be ごめん・・・ 2026-05-04 22:48:56 +09:00
a143b44f59 ちんこ 2026-05-03 20:50:56 +09:00
c68adc95fb . 2026-05-03 20:34:18 +09:00
a8eae150d4 Windowsでウィンドウサイズの修正 2026-05-03 20:15:47 +09:00
6415199b8c ファイルなしでも開ける様に+:oコマンドの追加 2026-05-03 20:08:03 +09:00
b510b2830f Windowsでマウスクリックが動いている 2026-05-03 19:44:22 +09:00
8 changed files with 220 additions and 43 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.svn
hexagon
hexagon.exe
hexagon-*

View File

@@ -1,6 +1,11 @@
## 1.3.0 (????年??月??日)
* macOS対応
## 1.2.0 (2026年05月03日)
* Debianでコンパイル出来る様に
* マウス対応の追加
* ファイルなしでも開ける様に
* `:o`コマンドの追加
## 1.1.0 (2025年12月28日)
* PgUp・PgDownで使ってページを動ける様に

View File

@@ -35,11 +35,17 @@ PREFIX = /usr
PREFIX = /boot/home/config/non-packaged
.endif
CFLAGS = -I/usr/include -I/usr/local/include -I/usr/pkg/include\
-I/usr/pkg/include/ncurses -I/boot/system/develop/headers\
-L/usr/lib -L/usr/local/lib -L/usr/pkg/lib -L/boot/system/develop/lib
CFLAGS = -I/usr/include -I/usr/local/include -L/usr/lib -L/usr/local/lib
.if ${UNAME_S} == "NetBSD"
.if ${OS} == "netbsd"
CFLAGS += -I/usr/pkg/include -I/usr/pkg/include/ncurses -L/usr/pkg/lib
.elif ${OS} == "macos"
CFLAGS += -I/opt/homebrew/opt/ncurses/include -L/opt/homebrew/opt/ncurses/lib
.elif ${OS} == "haiku"
CFLAGS += -I/boot/system/develop/headers -L/boot/system/develop/lib
.endif
.if ${OS} == "netbsd"
LDFLAGS = -lncurses
.else
LDFLAGS = -lncursesw
@@ -52,6 +58,10 @@ all:
${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -std=c++17 -static ${LDFLAGS}
strip ${NAME}
mac:
${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -std=c++17 ${LDFLAGS}
strip ${NAME}
debian:
${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -std=c++17 -static ${LDFLAGS} -ltinfo
strip ${NAME}

View File

@@ -8,12 +8,24 @@ make
doas make install
```
### Linux/macOS
### Linux
```sh
bmake
sudo bmake install
```
#### Debianの場合
```sh
bmake debian
sudo bmake install
```
### macOS
```sh
bmake mac
sudo bmake install
```
### Haiku
```sh
bmake

10
main.cc
View File

@@ -39,13 +39,13 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "src/hexeditor.hh"
int main(int argc, char *argv[]) {
if (argc != 2) {
std::cerr << "usage: hexagon <filename>\n";
return 1;
}
std:: string filename;
if (argc > 1) filename = argv[1];
else filename = "";
try {
HexEditor editor(argv[1]);
HexEditor editor(filename);
editor.run();
} catch (const std::exception &e) {
std::cerr << "エラー: " << e.what() << "\n";

View File

@@ -1,5 +1,5 @@
#!/bin/sh
g++ -O3 -o hexagon.exe -I/mingw64/include -L/mingw64/lib main.cc src/*.cc\
g++ -O3 -DNDEBUG -o hexagon.exe -I/mingw64/include -L/mingw64/lib main.cc src/*.cc\
-std=c++17 -static -lpdcurses -lgdi32 -lcomdlg32 -lwinmm -luser32 -lwinpthread\
-lstdc++ -mwindows
-lstdc++ -mconsole -s

View File

@@ -80,7 +80,7 @@ const std::vector<HexEditor::FileSignature> HexEditor::signatures = {
HexEditor::HexEditor(const std::string &filename)
: curPos(0), dpOffset(0), bpr(16),
statusMode(Status_Normal), modified(false),
running(true),
running(true), hasFile(!filename.empty()),
lastSearchDir(Direction_Forward),
headerLen(0), headerType("") {
// SIGINT (CTRL + C)を無効に
@@ -94,6 +94,40 @@ HexEditor::HexEditor(const std::string &filename)
// ncurses
initscr();
#ifdef _WIN32
HWND console = GetConsoleWindow();
if (console) {
LONG style = GetWindowLong(console, GWL_STYLE);
style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
SetWindowLong(console, GWL_STYLE, style);
const int MIN_WIDTH = 100;
const int MIN_HEIGHT = 30;
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hOut, &csbi);
RECT rect;
GetWindowRect(console, &rect);
int cw = csbi.srWindow.Right - csbi.srWindow.Left + 1;
int ch = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
MINMAXINFO mmi = {};
mmi.ptMinTrackSize.x = MIN_WIDTH * 8;
mmi.ptMinTrackSize.y = MIN_HEIGHT * 16;
COORD minBuf = { MIN_WIDTH, MIN_HEIGHT };
SetConsoleScreenBufferSize(hOut, minBuf);
SMALL_RECT rectWin = { 0, 0, MIN_WIDTH - 1, MIN_HEIGHT - 1 };
SetConsoleWindowInfo(hOut, TRUE, &rectWin);
}
resize_term(40, 120);
#endif
getmaxyx(stdscr, rows, cols);
clearok(stdscr, TRUE);
cbreak();
noecho();
@@ -102,7 +136,13 @@ HexEditor::HexEditor(const std::string &filename)
start_color();
// マウス対応
#ifdef _WIN32
mousemask(ALL_MOUSE_EVENTS, nullptr);
mouse_set(ALL_MOUSE_EVENTS);
request_mouse_pos();
#else
mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, nullptr);
#endif
isMouse = true;
init_pair(1, COLOR_BLACK, COLOR_WHITE); // カーソル
@@ -117,39 +157,46 @@ HexEditor::HexEditor(const std::string &filename)
refresh();
// ファイルをバッファーに読み込む
std::ifstream file(filename, std::ios::binary);
if (!file) {
endwin();
throw std::runtime_error("ファイルを開くに失敗");
}
if (hasFile) {
std::ifstream file(filename, std::ios::binary);
if (!file) {
endwin();
throw std::runtime_error("ファイルを開くに失敗");
}
buf.assign((std::istreambuf_iterator<char>(file)), {});
file.close();
buf.assign((std::istreambuf_iterator<char>(file)), {});
file.close();
if (buf.empty()) {
endwin();
throw std::runtime_error("ファイルが空です");
}
if (buf.empty()) {
endwin();
throw std::runtime_error("ファイルが空です");
}
fname = filename;
fname = filename;
hasFile = true;
// ファイルヘッダー
for (const auto &sig : signatures) {
if (buf.size() >= sig.signature.size()) {
bool match = true;
for (size_t i = 0; i < sig.signature.size(); ++i) {
if (buf[i] != sig.signature[i]) {
match = false;
// ファイルヘッダー
for (const auto &sig : signatures) {
if (buf.size() >= sig.signature.size()) {
bool match = true;
for (size_t i = 0; i < sig.signature.size(); ++i) {
if (buf[i] != sig.signature[i]) {
match = false;
break;
}
}
if (match) {
headerLen = sig.signature.size();
headerType = sig.type;
break;
}
}
if (match) {
headerLen = sig.signature.size();
headerType = sig.type;
break;
}
}
} else {
fname = "[No File]";
buf.clear();
hasFile = false;
}
// ウィンドウを作成する
@@ -510,9 +557,13 @@ void HexEditor::handleCommand() {
} else if (statusText == "noh") {
lastSearch = "";
isCommand = true;
} else if (statusText.rfind("o ", 0) == 0 || statusText.rfind("open ", 0) == 0) {
std::string path = statusText.substr(statusText.find(' ') + 1);
handleOpen(path);
isCommand = true;
} else {
statusMode = Status_Error;
statusText.clear();
statusText = "不正なコマンド。";
}
if (isCommand) {
@@ -532,6 +583,67 @@ void HexEditor::handleQuit(bool force) {
}
}
void HexEditor::handleOpen(const std::string &path) {
if (path.empty()) {
statusMode = Status_Error;
statusText = "利用方法: :o <filename>";
render();
return;
}
std::ifstream file(path, std::ios::binary);
if (!file) {
statusMode = Status_Error;
statusText = "ファイルを開くに失敗: " + path;
render();
return;
}
std::vector<uint8_t> newbuf((std::istreambuf_iterator<char>(file)), {});
file.close();
if (newbuf.empty()) {
statusMode = Status_Error;
statusText = "ファイルは空です。";
render();
return;
}
buf = std::move(newbuf);
fname = path;
hasFile = true;
curPos = 0;
dpOffset = 0;
modified = false;
undoStack.clear();
redoStack.clear();
lastSearch.clear();
lastHexSearch.clear();
headerLen = 0;
headerType = "";
for (const auto &sig : signatures) {
if (buf.size() >= sig.signature.size()) {
bool match = true;
for (size_t i = 0; i < sig.signature.size(); ++i) {
if (buf[i] != sig.signature[i]) {
match = false;
break;
}
}
if (match) {
headerLen = sig.signature.size();
headerType = sig.type;
break;
}
}
}
statusMode = Status_Normal;
statusText = path + " を開きました。";
render();
}
void HexEditor::handleSave() {
std::ofstream file(fname, std::ios::binary);
if (file) {
@@ -764,17 +876,24 @@ void HexEditor::handleReplace() {
void HexEditor::handleMouse() {
#ifdef _WIN32
if (getmouse() == 0) return;
request_mouse_pos();
int mx = Mouse_status.x;
int my = Mouse_status.y;
if (!(Mouse_status.button[0] & BUTTON1_PRESSED)
|| (Mouse_status.changes & 0x01)) return;
bool click = false;
for (int i = 0; i < 3; ++i) {
if ((Mouse_status.button[i] & BUTTON1_PRESSED)
|| (Mouse_status.button[i] & BUTTON1_CLICKED)) {
click = true;
break;
}
}
if (!click) return;
#else
MEVENT event;
if (getmouse(&event) != OK) return;
if (!(event.bstate & BUTTON1_PRESSED)) return;
if (!(event.bstate & (BUTTON1_PRESSED | BUTTON1_CLICKED))) return;
int mx = event.x;
int my = event.y;
@@ -849,6 +968,34 @@ void HexEditor::input() {
if (statusMode != Status_Normal && statusMode != Status_Error) continue;
ch = getch();
if (ch == KEY_RESIZE) {
getmaxyx(stdscr, rows, cols);
delwin(hexPanel);
delwin(asciiPanel);
bpr = std::min<size_t>(16, (cols / 2 - 10) / 4);
if (bpr < 4) bpr = 4;
hexPanel = newwin(rows - 3, cols / 2, 1, 0);
asciiPanel = newwin(rows - 3, cols / 2, 1, cols / 2);
scrollok(hexPanel, TRUE);
scrollok(asciiPanel, TRUE);
wattrset(hexPanel, COLOR_PAIR(8));
wattrset(asciiPanel, COLOR_PAIR(8));
render();
continue;
}
if (!hasFile && ch != ':' && ch != 'Z') {
statusMode = Status_Error;
statusText = "ファイルを読み込んでいません。「:o <file>」を御利用下さい。";
render();
continue;
}
if ((ch == 'j' || ch == KEY_DOWN) && curPos + bpr < buf.size()) {
curPos += bpr; // 下
if (statusMode == Status_Error) statusMode = Status_Normal;

View File

@@ -79,6 +79,7 @@ class HexEditor {
bool running;
std::string lastSearch;
bool isMouse = false;
bool hasFile = false;
WINDOW *hexPanel;
WINDOW *asciiPanel;
@@ -102,6 +103,7 @@ class HexEditor {
void topbar();
void statusbar();
void handleCommand();
void handleOpen(const std::string &path);
void handleSave();
void handleQuit(bool force);
void handleSearch();