Notcurses
高度なグラフィックスとマルチメディアサポートを備えた強力なTUIライブラリ。24ビットRGBカラー、画像・動画の表示、レイヤー機能などを提供。
GitHub概要
dankamongmen/notcurses
blingful character graphics/TUI library. definitely not curses.
スター4,009
ウォッチ33
フォーク127
作成日:2019年11月17日
言語:C
ライセンス:Other
トピックス
cclincursesterminalterminal-emulators
スター履歴
データ取得日時: 2025/7/25 11:08
Notcurses
Notcursesは、モダンなターミナル機能を活用した強力なTUIライブラリです、24ビットRGBカラー、グラフィックス、動画再生、レイヤー機能など、従来のNCursesを大幅に拡張した機能を提供します。Cで書かれたAPIに加え、C++ラッパーも提供されています。
特徴
グラフィックス・マルチメディア
- 24ビットRGB: True Color対応
- 画像表示: PNG、JPEGなどの表示
- 動画再生: ビデオファイルの再生
- ブレイル文字: 高解像度グラフィックス
高度なレンダリング
- プレーン: レイヤー化された描画面
- Zオーダー: 深度管理
- アルファブレンディング: 透明度サポート
- セルスタイル: 細かい制御
パフォーマンス
- 最適化: 高速レンダリング
- 非同期入力: ノンブロッキングI/O
- マルチスレッド: スレッドセーフ
- 差分更新: 効率的な画面更新
ポータビリティ
- Linux/Unix: 完全サポート
- Windows: Windows Terminal対応
- macOS: ネイティブサポート
- terminfo: ターミナル自動検出
基本的な使用方法
インストール
# Ubuntu/Debian
sudo apt-get install libnotcurses-dev
# macOS
brew install notcurses
# ソースからビルド
mkdir build && cd build
cmake ..
make -j
sudo make install
# コンパイル
g++ -o myapp myapp.cpp -lnotcurses-core -lnotcurses
Hello World (C++)
#include <notcurses/notcurses.h>
#include <iostream>
int main() {
// オプション設定
notcurses_options opts = {};
opts.flags = NCOPTION_SUPPRESS_BANNERS;
// Notcursesの初期化
notcurses* nc = notcurses_core_init(&opts, nullptr);
if (!nc) {
return 1;
}
// 標準プレーンを取得
ncplane* stdplane = notcurses_stdplane(nc);
// テキストを表示
ncplane_putstr_yx(stdplane, 2, 2, "Hello, Notcurses!");
// RGBカラーでテキストを表示
ncplane_set_fg_rgb8(stdplane, 255, 100, 100);
ncplane_putstr_yx(stdplane, 4, 2, "RGB Colored Text");
// レンダリング
notcurses_render(nc);
// キー入力待ち
notcurses_getc_blocking(nc, nullptr);
// クリーンアップ
notcurses_stop(nc);
return 0;
}
プレーンとグラフィックス
#include <notcurses/notcurses.h>
#include <cmath>
int main() {
notcurses_options opts = {};
notcurses* nc = notcurses_core_init(&opts, nullptr);
ncplane* stdplane = notcurses_stdplane(nc);
// プレーンのサイズを取得
unsigned y, x;
ncplane_dim_yx(stdplane, &y, &x);
// 新しいプレーンを作成
ncplane_options nopts = {};
nopts.y = 5;
nopts.x = 10;
nopts.rows = 20;
nopts.cols = 40;
ncplane* plane = ncplane_create(stdplane, &nopts);
// ボーダーを描画
uint64_t channels = 0;
ncchannel_set_rgb8(&channels, 0, 255, 0);
ncplane_perimeter(plane, &ncplane_box_chars, 0, channels,
NCBOXMASK_TOP | NCBOXMASK_RIGHT |
NCBOXMASK_BOTTOM | NCBOXMASK_LEFT);
// グラデーション描画
for (int i = 0; i < 18; i++) {
for (int j = 0; j < 38; j++) {
uint32_t r = (i * 255) / 18;
uint32_t g = (j * 255) / 38;
uint32_t b = 128;
ncplane_set_fg_rgb8(plane, r, g, b);
ncplane_set_bg_rgb8(plane, 0, 0, 0);
ncplane_putchar_yx(plane, i + 1, j + 1, '█');
}
}
notcurses_render(nc);
notcurses_getc_blocking(nc, nullptr);
ncplane_destroy(plane);
notcurses_stop(nc);
return 0;
}
画像表示
#include <notcurses/notcurses.h>
#include <notcurses/direct.h>
void display_image(notcurses* nc, const char* filename) {
ncplane* stdplane = notcurses_stdplane(nc);
// ビジュアルを作成
ncvisual_options vopts = {};
vopts.n = stdplane;
vopts.y = 2;
vopts.x = 2;
vopts.scaling = NCSCALE_STRETCH;
vopts.blitter = NCBLIT_PIXEL; // ピクセルブリッター
// 画像を読み込み
ncvisual* ncv = ncvisual_from_file(filename);
if (!ncv) {
return;
}
// 画像をレンダリング
ncplane* ncp = ncvisual_blit(nc, ncv, &vopts);
notcurses_render(nc);
// クリーンアップ
ncvisual_destroy(ncv);
}
int main() {
notcurses_options opts = {};
opts.flags = NCOPTION_NO_ALTERNATE_SCREEN;
notcurses* nc = notcurses_core_init(&opts, nullptr);
// 画像を表示
display_image(nc, "image.png");
notcurses_getc_blocking(nc, nullptr);
notcurses_stop(nc);
return 0;
}
アニメーション
#include <notcurses/notcurses.h>
#include <thread>
#include <chrono>
class Animation {
private:
notcurses* nc;
ncplane* plane;
int frame;
public:
Animation(notcurses* nc) : nc(nc), frame(0) {
ncplane_options nopts = {};
nopts.y = 5;
nopts.x = 10;
nopts.rows = 10;
nopts.cols = 30;
plane = ncplane_create(notcurses_stdplane(nc), &nopts);
}
~Animation() {
ncplane_destroy(plane);
}
void update() {
ncplane_erase(plane);
// フレームに基づいてアニメーション
int offset = frame % 20;
// 移動するボール
ncplane_set_fg_rgb8(plane, 255, 255, 0);
ncplane_putchar_yx(plane, 5, offset, '●');
// トレイル効果
for (int i = 1; i < 5 && offset - i >= 0; i++) {
int alpha = 255 - (i * 50);
ncplane_set_fg_rgb8(plane, alpha, alpha, 0);
ncplane_putchar_yx(plane, 5, offset - i, '·');
}
frame++;
}
void render() {
notcurses_render(nc);
}
};
int main() {
notcurses_options opts = {};
notcurses* nc = notcurses_core_init(&opts, nullptr);
Animation anim(nc);
// アニメーションループ
for (int i = 0; i < 100; i++) {
anim.update();
anim.render();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
notcurses_stop(nc);
return 0;
}
ウィジェットと入力
#include <notcurses/notcurses.h>
#include <string>
class InputField {
private:
ncplane* plane;
std::string text;
size_t cursor_pos;
public:
InputField(ncplane* parent, int y, int x, int width)
: cursor_pos(0) {
ncplane_options nopts = {};
nopts.y = y;
nopts.x = x;
nopts.rows = 3;
nopts.cols = width;
plane = ncplane_create(parent, &nopts);
}
~InputField() {
ncplane_destroy(plane);
}
void draw() {
ncplane_erase(plane);
// ボーダー描画
uint64_t channels = 0;
ncchannel_set_rgb8(&channels, 100, 100, 255);
ncplane_perimeter(plane, &ncplane_box_chars, 0, channels,
NCBOXMASK_TOP | NCBOXMASK_RIGHT |
NCBOXMASK_BOTTOM | NCBOXMASK_LEFT);
// テキスト表示
ncplane_putstr_yx(plane, 1, 1, text.c_str());
// カーソル
if (cursor_pos <= text.length()) {
ncplane_set_bg_rgb8(plane, 255, 255, 255);
ncplane_set_fg_rgb8(plane, 0, 0, 0);
char ch = (cursor_pos < text.length()) ? text[cursor_pos] : ' ';
ncplane_putchar_yx(plane, 1, 1 + cursor_pos, ch);
}
}
void handle_input(const ncinput* ni) {
if (ni->id == 'a' && ni->id <= 'z') {
// 文字入力
text.insert(cursor_pos, 1, ni->id);
cursor_pos++;
} else if (ni->id == NCKEY_BACKSPACE) {
// バックスペース
if (cursor_pos > 0) {
text.erase(cursor_pos - 1, 1);
cursor_pos--;
}
} else if (ni->id == NCKEY_LEFT) {
// 左移動
if (cursor_pos > 0) {
cursor_pos--;
}
} else if (ni->id == NCKEY_RIGHT) {
// 右移動
if (cursor_pos < text.length()) {
cursor_pos++;
}
}
}
const std::string& get_text() const {
return text;
}
};
int main() {
notcurses_options opts = {};
notcurses* nc = notcurses_core_init(&opts, nullptr);
ncplane* stdplane = notcurses_stdplane(nc);
ncplane_putstr_yx(stdplane, 2, 2, "Input Field Demo (ESC to exit)");
InputField input(stdplane, 5, 5, 30);
ncinput ni;
while (true) {
input.draw();
notcurses_render(nc);
notcurses_getc_blocking(nc, &ni);
if (ni.id == NCKEY_ESC) {
break;
}
input.handle_input(&ni);
}
notcurses_stop(nc);
printf("入力: %s\n", input.get_text().c_str());
return 0;
}
高度な機能
マルチメディア再生
// 動画ファイルの再生
ncvisual* ncv = ncvisual_from_file("video.mp4");
// ループでフレームを表示
while (ncvisual_decode(ncv) == 0) {
ncvisual_blit(nc, ncv, &vopts);
notcurses_render(nc);
}
レイヤーとアルファブレンディング
// 透明度を持つプレーン
ncplane_set_fg_alpha(plane, NCALPHA_BLEND);
ncplane_set_bg_alpha(plane, NCALPHA_TRANSPARENT);
パフォーマンス統計
// 統計情報を取得
ncstats stats;
notcurses_stats_alloc(nc, &stats);
// レンダリング後
notcurses_stats(nc, &stats);
printf("Renders: %ju, Failed renders: %ju\n",
stats.renders, stats.failed_renders);
エコシステム
関連ツール
- ncls: lsコマンドの拡張版
- ncplayer: メディアプレイヤー
- ncneofetch: システム情報表示
- notcurses-demo: 機能デモ
言語バインディング
- Python: notcurses-python
- Rust: notcurses-rs
- Go: notcurses-go
- Zig: notcurses-zig
利点
- 最新機能: True Color、マルチメディア対応
- 高パフォーマンス: 最適化されたレンダリング
- 豊富なAPI: 幅広い機能セット
- アクティブ開発: 継続的な改善
- クロスプラットフォーム: 良好な互換性
制約事項
- 新しい: NCursesと比べて歴史が浅い
- 複雑性: APIが大規模で学習コストが高い
- 依存関係: マルチメディア機能には追加ライブラリが必要
- ターミナル要件: 最新機能にはモダンなターミナルが必要
他のライブラリとの比較
項目 | Notcurses | ncurses | FTXUI |
---|---|---|---|
レベル | 中-低 | 低 | 高 |
機能 | 非常に豊富 | 基本的 | 豊富 |
マルチメディア | 完全サポート | なし | なし |
True Color | ネイティブ | 限定的 | サポート |
学習コスト | 高 | 高 | 中 |
まとめ
Notcursesは、モダンなターミナルの機能を最大限に活用したTUIライブラリです。True Color、マルチメディア、レイヤー機能など、従来のNCursesでは不可能だった機能を提供します。学習コストは高いですが、リッチなターミナルアプリケーションを開発したい開発者にとっては、非常に強力な選択肢となるでしょう。