# 機能設計書 102-読み取り専用モード

## 概要

本ドキュメントは、Ghosttyターミナルエミュレータにおける「読み取り専用モード」機能(toggle_readonly)の設計を記載する。この機能は、ターミナルサーフェスへの入力を一時的に無効化し、誤操作によるコマンド実行を防止する保護機能を提供する。

### 本機能の処理概要

読み取り専用モードは、ターミナルサーフェスの入力をブロックしながらも、閲覧・選択・スクロールといった非破壊的な操作は許可する機能である。

**業務上の目的・背景**：本番サーバーへのSSH接続時や、重要なログを監視している際に、誤ってキー入力やマウスクリックでコマンドを実行してしまうリスクがある。読み取り専用モードは、そのような事故を防止しつつ、画面内容の確認・コピー・検索といった閲覧操作は継続して行えるようにする安全機構である。また、終了確認が常に有効化されるため、読み取り専用モード中の誤終了も防止できる。

**機能の利用シーン**：
- 本番環境への接続時に誤入力を防ぎたい場合
- ログ監視中に内容を確認しながら誤操作を防ぎたい場合
- デモやプレゼンテーション中に画面を見せながら操作を防ぎたい場合
- 子供や他者にターミナル画面を見せる際に変更を防ぎたい場合

**主要な処理内容**：
1. toggle_readonlyアクションによるモードの切り替え
2. Surface.readonly状態フラグの反転
3. PTYへの書き込みメッセージのフィルタリング
4. GUIオーバーレイによるモード状態の視覚的表示
5. 終了確認の強制有効化

**関連システム・外部連携**：
- macOS: NotificationCenterによるUI状態更新
- GTK: GObjectプロパティ通知によるUI状態更新
- 右クリックコンテキストメニューからのトグル操作

**権限による制御**：
- 読み取り専用モードでもコピー、選択、スクロール、キーバインドアクション(ターミナルレベル)は動作する
- PTYへの書き込み(キー入力、マウスイベント送信)のみがブロックされる

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | メインターミナルウィンドウ | 主画面 | 読み取り専用モードのトグル操作と状態表示 |

## 機能種別

状態管理 / セキュリティ機能

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| なし | - | - | toggle_readonlyはパラメータ不要 | - |

### 入力データソース

- **キーバインドアクション**: toggle_readonlyアクションのトリガー
- **コンテキストメニュー**: 右クリックメニューからの「Terminal Read-only」選択

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| readonly | bool | サーフェスの読み取り専用状態(true: 有効、false: 無効) |
| readonly_action | .on/.off | GUIへ通知するアクション値 |

### 出力先

| 対象 | 出力内容 |
|------|---------|
| Surface.readonly | bool値の状態更新 |
| rt_app.performAction | .readonly(.on/.off)通知をGUIへ |
| UI | オーバーレイインジケーター表示/非表示 |

## 処理フロー

### 処理シーケンス

```
1. アクショントリガー
   └─ toggle_readonlyアクションが発火(キーバインドまたはメニュー)

2. 状態の反転
   └─ self.readonly = !self.readonly

3. GUIへの通知
   └─ rt_app.performAction(.readonly, .on/.off)

4. GUI更新
   ├─ [macOS] NotificationCenter通知 → readonlyプロパティ更新
   └─ [GTK] GObjectプロパティ通知 → Revealerアニメーション

5. 入力フィルタリング(継続的に作用)
   └─ queueIo関数でwrite系メッセージをブロック

6. 終了確認強制
   └─ needsConfirmQuit()がtrueを返す
```

### フローチャート

```mermaid
flowchart TD
    A[toggle_readonly発火] --> B[readonly反転]
    B --> C[rt_app.performAction呼び出し]
    C --> D{プラットフォーム}
    D -->|macOS| E[NotificationCenter通知]
    D -->|GTK| F[GObject notify]
    E --> G[UIオーバーレイ更新]
    F --> G
    G --> H[完了]

    subgraph 継続的動作
    I[queueIo呼び出し] --> J{readonly?}
    J -->|Yes| K{write系?}
    K -->|Yes| L[メッセージ破棄]
    K -->|No| M[通常処理]
    J -->|No| M
    end

    subgraph 終了確認
    N[needsConfirmQuit] --> O{readonly?}
    O -->|Yes| P[true返却]
    O -->|No| Q[通常判定]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 書き込みブロック | readonlyモード中はPTYへの書き込みをブロック | readonly == true |
| BR-002 | 終了確認強制 | readonlyモード中は常に終了確認が有効 | readonly == true |
| BR-003 | 選択許可 | readonlyモード中もテキスト選択は可能 | readonly == true |
| BR-004 | スクロール許可 | readonlyモード中もネイティブスクロールは可能 | readonly == true |
| BR-005 | コピー許可 | readonlyモード中もクリップボードへのコピーは可能 | readonly == true |
| BR-006 | トグル動作 | 呼び出しごとに状態が反転 | 常時 |

### 計算ロジック

なし

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

### 操作別データベース影響一覧

該当なし(データベースは使用しない)

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | なし | この機能ではエラーは発生しない | - |

### リトライ仕様

該当なし

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

- readonly状態の変更はアトミック(boolフラグの反転のみ)
- GUI通知の失敗は状態変更に影響しない(best-effort)

## パフォーマンス要件

- 状態変更は即時反映(O(1)操作)
- queueIoのチェックはメッセージ送信ごとに行われるがオーバーヘッドは最小限

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

- 読み取り専用モードは誤操作防止が目的であり、悪意あるユーザーからの保護ではない
- プログラムからのアクセスや設定変更により解除可能
- 終了確認の強制により、セッション切断による作業損失を防止

## 備考

- readonlyフラグはSurface構造体のインスタンスフィールドとして保持
- macOSでは右クリックメニューの「Terminal Read-only」項目にチェックマークで状態表示
- GTKではRevealerウィジェットによるクロスフェードアニメーションで状態表示

---

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

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

### 推奨読解順序

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

まず、読み取り専用モードの状態管理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Surface.zig | `src/Surface.zig` | readonlyフィールドの定義(152-156行目) |
| 1-2 | Binding.zig | `src/input/Binding.zig` | toggle_readonlyアクションの定義(611-619行目) |

**読解のコツ**: Surface構造体のreadonly: boolフィールドがモードの状態を保持する。デフォルトはfalse(通常モード)。

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

処理の起点となるアクションハンドラを特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Surface.zig | `src/Surface.zig` | performBindingAction関数内のtoggle_readonlyケース(5675-5683行目) |

**主要処理フロー**:
1. **5675行目**: .toggle_readonlyケースの開始
2. **5676行目**: self.readonly = !self.readonlyで状態反転
3. **5677-5681行目**: rt_app.performAction(.readonly, .on/.off)でGUIに通知

#### Step 3: 入力フィルタリングを理解する

読み取り専用モードが入力をブロックする仕組みを理解。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Surface.zig | `src/Surface.zig` | queueIo関数(840-857行目) |

**主要処理フロー**:
- **845行目**: if (self.readonly)で状態チェック
- **846-850行目**: write_small/write_stable/write_allocメッセージは早期リターンで破棄
- **852行目**: その他のメッセージは通常処理

#### Step 4: 終了確認の強制を理解する

読み取り専用モードでの終了確認動作を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Surface.zig | `src/Surface.zig` | needsConfirmQuit関数(917-926行目) |

**主要処理フロー**:
- **921行目**: if (self.readonly) return trueで常に確認を要求

#### Step 5: GUI表示(macOS)を理解する

macOSでのUI更新処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | GhosttyPackage.swift | `macos/Sources/Ghostty/GhosttyPackage.swift` | 通知名の定義(364-365行目) |
| 5-2 | SurfaceView_AppKit.swift | `macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift` | readonly状態の受信と表示(749-752行目, 1477-1479行目) |

**主要処理フロー**:
- **749-752行目**: ghosttyDidChangeReadonly通知を受信しreadonly変数を更新
- **1477-1479行目**: コンテキストメニューに「Terminal Read-only」項目、readonly状態でチェックマーク表示

#### Step 6: GUI表示(GTK)を理解する

GTKでのUI更新処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | surface.zig | `src/apprt/gtk/class/surface.zig` | readonlyプロパティ定義(404-421行目) |
| 6-2 | surface.zig | `src/apprt/gtk/class/surface.zig` | getReadonly/setReadonly関数(1131-1143行目) |
| 6-3 | surface.blp | `src/apprt/gtk/ui/1.2/surface.blp` | Revealerによるオーバーレイ(79-110行目) |
| 6-4 | style.css | `src/apprt/gtk/css/style.css` | readonly_overlayスタイル(137-146行目) |

**主要処理フロー**:
- **81行目**: reveal-child: bind template.readonlyでプロパティをバインド
- **89-109行目**: readonly_overlayボックスに"Read-only"ラベルとアイコン表示
- **137-146行目**: CSSでオーバーレイのスタイリング(色、パディング、角丸)

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

```
Surface.performBindingAction
    │
    └─ .toggle_readonly
           │
           ├─ self.readonly = !self.readonly
           │
           └─ rt_app.performAction(.readonly, .on/.off)
                  │
                  ├─ [macOS] Ghostty.App
                  │      └─ NotificationCenter.post(.ghosttyDidChangeReadonly)
                  │             └─ SurfaceView.ghosttyDidChangeReadonly
                  │                    └─ readonly = value (UI更新)
                  │
                  └─ [GTK] application.setReadonly
                         └─ surface.setReadonly
                                └─ notifyByPspec (Revealer更新)
```

### データフロー図

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

toggle_readonly   ───▶ Surface.readonly反転     ───▶ boolフラグ更新
アクション                     │
                               │
                               └───▶ performAction(.readonly) ───▶ UI通知
                                                                    │
                                                                    ├─▶ オーバーレイ表示
                                                                    └─▶ メニュー状態更新

[継続的フィルタ]

キー入力/マウス  ───▶ queueIo  ───▶ readonly? ───▶ write破棄/通過
                                         │
終了要求        ───▶ needsConfirmQuit ───▶ 強制true/通常判定
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Surface.zig | `src/Surface.zig` | ソース | readonlyフィールド、toggle_readonly処理、queueIo、needsConfirmQuit |
| Binding.zig | `src/input/Binding.zig` | ソース | toggle_readonlyアクション定義 |
| application.zig | `src/apprt/gtk/class/application.zig` | ソース | GTK版setReadonly処理 |
| surface.zig | `src/apprt/gtk/class/surface.zig` | ソース | GTK版readonlyプロパティとgetter/setter |
| surface.blp | `src/apprt/gtk/ui/1.2/surface.blp` | UI定義 | GTK版readonly_overlayのRevealer定義 |
| style.css | `src/apprt/gtk/css/style.css` | スタイル | GTK版readonly_overlayのCSS |
| SurfaceView_AppKit.swift | `macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift` | ソース | macOS版readonly状態管理とUI |
| GhosttyPackage.swift | `macos/Sources/Ghostty/GhosttyPackage.swift` | ソース | macOS版通知名定義 |
| Ghostty.App.swift | `macos/Sources/Ghostty/Ghostty.App.swift` | ソース | macOS版通知送信処理 |
