# 機能設計書 73-TTYモード設定

## 概要

本ドキュメントは、libuv が提供する TTY モード設定機能 `uv_tty_set_mode()` の設計仕様を記述する。この関数は、TTY ハンドルの入力モードを変更し、端末のエコー、行編集、シグナル処理などの動作を制御する。

### 本機能の処理概要

`uv_tty_set_mode()` は、TTY ハンドルの入力モードを NORMAL / RAW / IO の3モード（RAW_VT は Unix 上では RAW に統合）間で切り替える。初回のモード変更時には現在の termios 設定を保存し、後で `uv_tty_reset_mode()` によるリストアを可能にする。

**業務上の目的・背景**：端末アプリケーションでは、用途に応じて端末の動作モードを切り替える必要がある。REPL ではノーマルモードで行編集を有効にしつつ、VIM のようなエディタではキー入力を即座に取得する RAW モードが必要となる。libuv は POSIX termios API をラップし、クロスプラットフォームで一貫した TTY モード制御を提供する。

**機能の利用シーン**：REPL でのインタラクティブ入力、テキストエディタでの RAW モード操作、バイナリプロトコル通信での IO モード、パスワード入力時のエコー無効化など。

**主要な処理内容**：
1. RAW_VT モードの場合は RAW モードに正規化する（Unix では区別なし）
2. 現在のモードと要求モードが同一の場合は処理をスキップする
3. 初回のノーマルからの変更時に `tcgetattr()` で現在の termios 設定を保存する
4. グローバルな `orig_termios` / `orig_termios_fd` にも保存する（`uv_tty_reset_mode()` 用）
5. 要求されたモードに応じて termios 設定を変更する
6. `tcsetattr(TCSADRAIN)` で変更を適用する（出力バッファのドレイン後）

**関連システム・外部連携**：POSIX termios API（`tcgetattr()`、`tcsetattr()`）、`cfmakeraw()`。

**権限による制御**：TTY ハンドルが関連付けられたファイルディスクリプタへのアクセス権が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 20 | TTY | 主機能 | uv_tty_set_mode()によるTTYモード設定のAPI定義 |

## 機能種別

設定変更処理 - TTY 端末の動作モード切り替え

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| tty | uv_tty_t* | Yes | モードを変更する TTY ハンドル | 初期化済みの TTY ハンドルであること |
| mode | uv_tty_mode_t | Yes | 設定するモード | UV_TTY_MODE_NORMAL(0) / UV_TTY_MODE_RAW(1) / UV_TTY_MODE_IO(2) / UV_TTY_MODE_RAW_VT(3) |

### 入力データソース

API 呼び出し元から直接パラメータとして渡される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | int | 成功時 0、失敗時 UV エラーコード |
| tty->mode | int | 設定後のモード値 |

### 出力先

TTY ハンドルの mode フィールドが更新される。OS レベルでは termios 設定が変更される。

## 処理フロー

### 処理シーケンス

```
1. RAW_VT モードの正規化
   └─ uv__is_raw_tty_mode(mode) が true なら mode = UV_TTY_MODE_RAW に変更
2. 現在のモードと比較
   └─ tty->mode == mode なら return 0（変更不要）
3. 初回ノーマル→非ノーマル変更時の termios 保存
   a. tcgetattr(fd, &tty->orig_termios) で現在の設定を取得
   b. atomic spinlock を取得
   c. orig_termios_fd == -1 なら orig_termios / orig_termios_fd をグローバルに保存
   d. spinlock を解放
4. モードに応じた termios 設定変更
   a. NORMAL: 保存した orig_termios をそのまま使用
   b. RAW: BRKINT,ICRNL,INPCK,ISTRIP,IXON off / ONLCR on / CS8 / ECHO,ICANON,IEXTEN,ISIG off / VMIN=1,VTIME=0
   c. IO: cfmakeraw() による完全バイナリモード
5. uv__tcsetattr(fd, TCSADRAIN, &tmp) で適用
   └─ 成功時に tty->mode = mode を設定
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{RAW_VT mode?}
    B -->|Yes| C[mode = UV_TTY_MODE_RAW]
    B -->|No| D{tty->mode == mode?}
    C --> D
    D -->|Yes| E[return 0]
    D -->|No| F{NORMAL→非NORMALへの初回変更?}
    F -->|Yes| G[tcgetattr で termios 保存]
    G --> H[spinlock 取得]
    H --> I[グローバル orig_termios 保存]
    I --> J[spinlock 解放]
    F -->|No| K{mode の種類}
    J --> K
    K -->|NORMAL| L[orig_termios をそのまま使用]
    K -->|RAW| M[RAW モード設定を適用]
    K -->|IO| N[cfmakeraw で完全バイナリモード]
    L --> O[tcsetattr TCSADRAIN]
    M --> O
    N --> O
    O --> P{成功?}
    P -->|Yes| Q[tty->mode = mode, return 0]
    P -->|No| R[return エラーコード]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-73-01 | RAW_VT統合 | Unix では UV_TTY_MODE_RAW_VT は UV_TTY_MODE_RAW と同等に扱われる | mode == UV_TTY_MODE_RAW_VT |
| BR-73-02 | 同一モードスキップ | 現在のモードと要求モードが同じ場合は処理をスキップ | tty->mode == mode |
| BR-73-03 | termios保存 | 初めてノーマルモードから変更する際に元の termios 設定を保存する | NORMAL → 非NORMAL への初回変更 |
| BR-73-04 | TCSADRAIN適用 | モード変更は出力バッファのドレイン後に適用される | 常時 |
| BR-73-05 | スピンロック保護 | グローバル orig_termios の更新は atomic spinlock で保護される | orig_termios_fd の更新時 |

### 計算ロジック

RAW モードの termios 設定:
- `c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON)`
- `c_oflag |= (ONLCR)`
- `c_cflag |= (CS8)`
- `c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG)`
- `c_cc[VMIN] = 1, c_cc[VTIME] = 0`

## データベース操作仕様

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UV_EBADF | システムエラー | tcgetattr() が失敗した（無効な fd） | TTY ハンドルが正しく初期化されているか確認する |
| UV_ENOTTY | システムエラー | fd が TTY デバイスを指していない | uv_tty_init() での初期化を確認する |
| UV_EIO | システムエラー | tcsetattr() が I/O エラーで失敗した | 端末デバイスの状態を確認する |

### リトライ仕様

`tcgetattr()` および `uv__tcsetattr()` 内の `tcsetattr()` は EINTR で中断された場合に自動リトライする（do-while ループ）。

## トランザクション仕様

該当なし。ただし、`tcsetattr()` 失敗時は `tty->mode` は更新されない（変更前の状態を維持）。

## パフォーマンス要件

termios 操作は軽量な ioctl ベースの処理であり、特別なパフォーマンス要件はない。TCSADRAIN 指定により出力バッファのフラッシュ待ちが発生する場合がある。

## セキュリティ考慮事項

- atomic spinlock によりマルチスレッドでの同時アクセスを保護する
- RAW モードではシグナル（Ctrl+C 等）が無効化されるため、アプリケーション側でのシグナル処理が必要

## 備考

- `uv__tty_make_raw()` は Solaris / z/OS では `cfmakeraw()` が利用できないため手動で termios 設定を行う
- C++ での呼び出し時は `static_cast<uv_tty_mode_t>(mode)` による型変換オーバーロードが提供される（`include/uv.h` L849-851）
- グローバル変数 `orig_termios_fd` / `orig_termios` / `termios_spinlock` はプロセス全体で共有される

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | **L812-824**: uv_tty_mode_t 列挙型 - NORMAL(0), RAW(1), IO(2), RAW_VT(3) |
| 1-2 | tty.c | `src/unix/tty.c` | **L65-67**: グローバル変数 orig_termios_fd, orig_termios, termios_spinlock |

**読解のコツ**: `_Atomic int termios_spinlock` は C11 のアトミック型を使用したスピンロック。`atomic_compare_exchange_strong` と `atomic_store` で CAS パターンを実現している。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | tty.c | `src/unix/tty.c` | **L281-342**: uv_tty_set_mode() の全実装 |

**主要処理フロー**:
1. **287-289行目**: RAW_VT を RAW に正規化
2. **292-293行目**: 同一モードなら早期リターン
3. **296-315行目**: 初回変更時の termios 保存（CAS スピンロック使用）
4. **317-334行目**: モード別の termios 設定（switch 文）
5. **337行目**: `uv__tcsetattr(fd, TCSADRAIN, &tmp)` で適用
6. **338-339行目**: 成功時に `tty->mode = mode` を設定

#### Step 3: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | tty.c | `src/unix/tty.c` | **L69-80**: uv__tcsetattr() - EINTR リトライ付きの tcsetattr ラッパー |
| 3-2 | tty.c | `src/unix/tty.c` | **L244-279**: uv__tty_make_raw() - IO モード用の RAW 設定（Solaris/z/OS 対応あり） |

### プログラム呼び出し階層図

```
uv_tty_set_mode(tty, mode)
    |
    +-- uv__is_raw_tty_mode(mode)
    |
    +-- tcgetattr(fd, &tty->orig_termios)  [初回保存]
    |
    +-- atomic_compare_exchange_strong()    [spinlock]
    |
    +-- [mode別処理]
    |   +-- NORMAL: orig_termios をそのまま
    |   +-- RAW: 手動フラグ設定
    |   +-- IO: uv__tty_make_raw()
    |       +-- cfmakeraw()  [Linux/macOS等]
    |       +-- 手動設定     [Solaris/z/OS]
    |
    +-- uv__tcsetattr(fd, TCSADRAIN, &tmp)
        +-- tcsetattr()  [EINTRリトライ付き]
```

### データフロー図

```
[入力]                [処理]                              [出力]

tty ハンドル -------> termios設定取得(初回のみ) ----------> orig_termios保存
mode (列挙値) ------> モード別termios設定構成 -----------> 新termios構造体
                      tcsetattr(TCSADRAIN)で適用 -------> OS端末設定変更
                                                   -----> tty->mode更新
                                                   -----> return 0 or エラー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダー | uv_tty_mode_t定義、uv_tty_set_mode()宣言 |
| tty.c (Unix) | `src/unix/tty.c` | ソース | Unix版実装（L281-342）、ヘルパー関数 |
| tty.c (Win) | `src/win/tty.c` | ソース | Windows版実装 |
