# 通知設計書 62-SignalTextChanged

## 概要

本ドキュメントは、Windows Terminal における SignalTextChanged 通知の設計仕様を記載する。この通知は、ターミナルのテキストバッファの内容が変更されたことを UI Automation (UIA) クライアントに通知するためのイベントである。

### 本通知の処理概要

SignalTextChanged は、ターミナルコントロールのテキストバッファに新しい出力が追加されたり、テキスト内容が変更された際に、スクリーンリーダーなどの支援技術にその変更を伝達する UIA 通知である。

**業務上の目的・背景**：視覚障害を持つユーザーがスクリーンリーダー（NVDA、JAWS、ナレーターなど）を使用してターミナルを操作する場合、画面上のテキスト変更をリアルタイムに把握する必要がある。コマンド実行結果やプログラム出力がターミナルに表示された際、この通知により UIA クライアントがテキストバッファの変更を検知し、変更内容をユーザーに音声等で伝達できるようになる。Microsoft の UIA Text Pattern 仕様に準拠するために必須の通知である。

**通知の送信タイミング**：レンダラーの描画サイクル（Paint cycle）において、テキストバッファの変更が検出された場合に送信される。具体的には、`UiaEngine::Invalidate()`、`UiaEngine::InvalidateAll()`、または `UiaEngine::NotifyNewText()` が呼ばれて `_textBufferChanged` フラグが true に設定された後、`UiaEngine::Present()` メソッドの実行時に `IUiaEventDispatcher::SignalTextChanged()` が呼び出される。

**通知の受信者**：UIA クライアント（スクリーンリーダー、自動テストフレームワークなど）が受信者である。XAML ベースの Windows Terminal では `TermControlAutomationPeer` が、WPF/HwndTerminal ベースでは `HwndTerminalAutomationPeer` が UIA イベントを発火し、OS の UIA インフラストラクチャを通じてクライアントに配信される。

**通知内容の概要**：`AutomationEvents::TextPatternOnTextChanged`（UIA_Text_TextChangedEventId）イベントが発火される。テキストの具体的な変更内容は本イベント自体には含まれない。UIA クライアントは必要に応じて `ITextProvider` の各メソッドを呼び出してバッファ内容を再取得する。なお、別途 `NotifyNewOutput` イベントにより新規出力テキストがスクリーンリーダーに直接通知される（本設計書の範囲外）。

**期待されるアクション**：UIA クライアント（スクリーンリーダー等）は、このイベントを受信した後、テキストバッファの内容が変更されたことを認識し、必要に応じて `GetVisibleRanges()` や `DocumentRange()` を通じてテキスト内容を再取得する。

## 通知種別

アプリ内通知（UI Automation イベント）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（`Dispatcher.RunAsync` による UIスレッドへのディスパッチ） |
| 優先度 | Normal（`CoreDispatcherPriority::Normal`） |
| リトライ | 無し |

### 送信先決定ロジック

送信先は OS の UIA インフラストラクチャにより自動的に決定される。UIA イベントをリッスンしているすべてのクライアント（スクリーンリーダー、自動テストツール等）に対してブロードキャストされる。TermControl が UI ツリーから削除されている場合（`_termControl` が null の場合）はイベントの発火をスキップする。

## 通知テンプレート

### UIA イベント情報

| 項目 | 内容 |
|-----|------|
| イベント種別 | `AutomationEvents::TextPatternOnTextChanged` |
| UIA イベントID | `UIA_Text_TextChangedEventId` |
| 形式 | UIA イベント通知（データペイロードなし） |

### 本文テンプレート

```
(テンプレートなし - UIA イベントはペイロードを持たない純粋なシグナルイベント)
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| （なし） | 本通知はペイロードを持たないイベント通知のため、テンプレート変数は存在しない | - | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| レンダラー内部イベント | `Invalidate()` 呼び出し | `_isEnabled == true` かつ `_textBufferChanged == true` | テキストバッファの特定領域が変更された際に `_textBufferChanged` フラグを設定 |
| レンダラー内部イベント | `InvalidateAll()` 呼び出し | `_isEnabled == true` かつ `_textBufferChanged == true` | テキストバッファ全体の再描画が必要な際に `_textBufferChanged` フラグを設定 |
| レンダラー内部イベント | `NotifyNewText()` 呼び出し | `_isEnabled == true` かつ新テキストが空でない | 新しいテキストが出力された際に `_textBufferChanged` フラグを設定し、テキストをバッファリング |
| 描画サイクル完了 | `UiaEngine::Present()` 実行 | `_isEnabled == true` かつ `_isPainting == true` | 描画サイクル終了時にフラグを確認してイベントをディスパッチする |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| `_isEnabled == false` | `UiaEngine::Disable()` が呼ばれ、エンジンが無効化されている場合、イベントは発火されない |
| `_textBufferChanged == false` | テキストバッファに変更がない場合、イベントは発火されない |
| TermControl が UI ツリーから削除済み | `_termControl` の weak_ref が null を返す場合、`RaiseAutomationEvent` は呼び出されない |
| Dispatcher が null | XAML Dispatcher が利用できない場合、イベントはスキップされる |
| `NotifyNewText()` で `_isEnabled == false` | 無効状態では新テキストのバッファリング自体がスキップされる（GH#16217対応） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[テキストバッファ変更の検出] --> B{変更の種類}
    B -->|領域変更| C[Invalidate呼び出し]
    B -->|全体再描画| D[InvalidateAll呼び出し]
    B -->|新テキスト出力| E[NotifyNewText呼び出し]
    C --> F[_textBufferChanged = true]
    D --> F
    E --> G{_isEnabled?}
    G -->|true| H[テキストをバッファに追加]
    G -->|false| I[S_FALSE - スキップ]
    H --> F
    F --> J[StartPaint - 描画サイクル開始]
    J -->|somethingToDo == true| K[EndPaint - _newOutput を _queuedOutput にスワップ]
    J -->|somethingToDo == false| L[S_FALSE - スキップ]
    K --> M[Present実行]
    M --> N{_textBufferChanged?}
    N -->|true| O[dispatcher->SignalTextChanged]
    N -->|false| P[スキップ]
    O --> Q[InteractivityAutomationPeer::SignalTextChanged]
    Q --> R[TextChanged.raise - イベント発火]
    R --> S[TermControlAutomationPeer::SignalTextChanged]
    S --> T[UiaTracing::Signal::TextChanged - トレース記録]
    T --> U[Dispatcher.RunAsync - UIスレッドへディスパッチ]
    U --> V{_termControl有効?}
    V -->|Yes| W[RaiseAutomationEvent - TextPatternOnTextChanged]
    V -->|No| X[処理終了]
    W --> Y[_textBufferChanged = false - フラグリセット]
    Y --> Z[終了]
    I --> Z
    L --> Z
    P --> Z
    X --> Z
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（UIA通知はデータベースを使用しない）

### 更新テーブル一覧

該当なし（UIA通知はデータベースを使用しない）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| Dispatcher取得失敗 | XAML UIスレッドの Dispatcher が null | イベント発火をスキップ（早期リターン） |
| TermControl参照失敗 | weak_ref からの TermControl 取得失敗 | イベント発火をスキップ |
| RaiseAutomationEvent例外 | UIA イベント発火時の例外 | CATCH_LOG() でログ記録後、処理を継続 |
| UiaRaiseAutomationEvent失敗（HWND版） | Win32 UIA API 呼び出し失敗 | `LOG_IF_FAILED` でログ記録 |
| NotifyNewText例外 | 新テキストバッファリング中の例外 | CATCH_LOG_RETURN_HR(E_FAIL) でログ記録しE_FAILを返す |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（レンダラーの描画サイクルに依存） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし。アプリケーション実行中は常時送信可能。ただし、`UiaEngine` が `Disable()` されている場合はイベントが抑止される。複数の TermControl が存在する場合、フォーカスを持つコントロールのみが `Enable()` 状態となり、他は `Disable()` される。

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

- 本通知はテキスト変更という事実のみを通知し、変更されたテキスト内容は含まない。そのため、通知自体に個人情報や機密情報は含まれない。
- UIA クライアントが `ITextProvider` のメソッドを通じてテキスト内容を取得する際は、OS の UIA セキュリティモデル（UIPI: User Interface Privilege Isolation）に従い、適切な権限を持つプロセスのみがアクセス可能。

## 備考

- `HwndTerminalAutomationPeer`（WPF/HWND版）では、`UiaRaiseAutomationEvent()` Win32 API を直接呼び出してイベントを発火する。XAML 版とは異なり、Dispatcher を介さず同期的に処理される。
- `UiaTracing::Signal::TextChanged()` によりETW（Event Tracing for Windows）にトレースログが記録される（WINEVENT_LEVEL_VERBOSE レベル）。
- `Invalidate()`、`InvalidateAll()`、`NotifyNewText()` の3つのメソッドがすべて `_textBufferChanged` フラグを設定する。`NotifyNewText()` はさらに新テキストを `_newOutput` バッファに追加し、`EndPaint()` 時に `_queuedOutput` にスワップされる（ダブルバッファリング）。
- `NotifyNewText()` の新テキストバッファリングは、`_isEnabled == false` の場合にスキップされる（GH#16217 の対応）。無効状態でバッファリングし続けると、再度有効化された際に古いデータが通知される可能性があるための措置。
- `Disable()` 呼び出し時には `_newOutput` もクリアされる。

---

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

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

### 推奨読解順序

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

IUiaEventDispatcher インターフェースが定義するメソッドシグネチャを理解することが出発点となる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | IUiaEventDispatcher.h | `src/types/IUiaEventDispatcher.h` | 25行目: `SignalTextChanged()` の純粋仮想関数宣言。引数なし・戻り値なしのインターフェース |

**読解のコツ**: このインターフェースには `SignalTextChanged()` と `NotifyNewOutput()` の2つのテキスト関連メソッドがある。`SignalTextChanged` は「テキストが変わった」というシグナル、`NotifyNewOutput` は「新しい出力テキスト」の内容を伝達する。本設計書は前者を対象とする。

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

UiaEngine がレンダラーとしてテキストバッファ変更を検出し、イベントをディスパッチする流れを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | UiaRenderer.hpp | `src/renderer/uia/UiaRenderer.hpp` | 70行目: `_textBufferChanged` フラグの定義。72-73行目: `_newOutput` と `_queuedOutput` のダブルバッファ |
| 2-2 | UiaRenderer.cpp | `src/renderer/uia/UiaRenderer.cpp` | 65-69行目: `Invalidate()` で `_textBufferChanged = true`。141-145行目: `InvalidateAll()` で同様。147-162行目: `NotifyNewText()` でテキストバッファリングとフラグ設定。232-239行目: `Present()` 内で `_dispatcher->SignalTextChanged()` 呼び出し |

**主要処理フロー**:
1. **65-69行目**: `Invalidate()` - テキストバッファ変更フラグの設定
2. **141-145行目**: `InvalidateAll()` - 全体再描画時のフラグ設定
3. **147-162行目**: `NotifyNewText()` - 新テキストのバッファリングとフラグ設定
4. **170行目**: `StartPaint()` - `_textBufferChanged` を含む `somethingToDo` チェック
5. **190-202行目**: `EndPaint()` - `_newOutput` を `_queuedOutput` にスワップ
6. **219行目**: `Present()` - フラグ確認とディスパッチャ呼び出し
7. **232-239行目**: `SignalTextChanged()` の呼び出し
8. **263行目**: `_textBufferChanged = false` でフラグリセット

#### Step 3: InteractivityAutomationPeer の中継処理を理解する

InteractivityAutomationPeer が UiaEngine からのシグナルを受け取り、XAML イベントとして中継する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | InteractivityAutomationPeer.h | `src/cascadia/TerminalControl/InteractivityAutomationPeer.h` | 50行目: `SignalTextChanged()` のオーバーライド宣言。74行目: `TextChanged` イベントの定義 |
| 3-2 | InteractivityAutomationPeer.cpp | `src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp` | 80-83行目: `SignalTextChanged()` の実装 - `TextChanged.raise()` でイベントを発火 |

**主要処理フロー**:
- **82行目**: `TextChanged.raise(*this, nullptr)` - til::typed_event を使ってイベントを発火

#### Step 4: TermControlAutomationPeer の UIA イベント発火を理解する

TermControlAutomationPeer が最終的に XAML の AutomationEvent を発火する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TermControlAutomationPeer.h | `src/cascadia/TerminalControl/TermControlAutomationPeer.h` | 68行目: `SignalTextChanged()` のオーバーライド宣言 |
| 4-2 | TermControlAutomationPeer.cpp | `src/cascadia/TerminalControl/TermControlAutomationPeer.cpp` | 82行目: コンストラクタでの `TextChanged` イベントハンドラ登録。158-176行目: `SignalTextChanged()` の実装 |

**主要処理フロー**:
- **82行目**: `_contentAutomationPeer.TextChanged([this](auto&&, auto&&) { SignalTextChanged(); })` - イベントハンドラ登録
- **160行目**: `UiaTracing::Signal::TextChanged()` - ETWトレース記録
- **166行目**: `dispatcher.RunAsync(...)` - UIスレッドへの非同期ディスパッチ
- **172行目**: `RaiseAutomationEvent(AutomationEvents::TextPatternOnTextChanged)` - UIA イベント発火

#### Step 5: HwndTerminalAutomationPeer の代替実装を理解する

WPF/HWND ベースの代替実装パスを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | HwndTerminalAutomationPeer.hpp | `src/cascadia/TerminalControl/HwndTerminalAutomationPeer.hpp` | 36行目: `SignalTextChanged()` のオーバーライド宣言 |
| 5-2 | HwndTerminalAutomationPeer.cpp | `src/cascadia/TerminalControl/HwndTerminalAutomationPeer.cpp` | 116-120行目: Win32 UIA API を直接呼び出す実装 |

**主要処理フロー**:
- **118行目**: `UiaTracing::Signal::TextChanged()` - ETWトレース
- **119行目**: `UiaRaiseAutomationEvent(this, UIA_Text_TextChangedEventId)` - Win32 UIA API直接呼び出し

#### Step 6: トレーシング処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | UiaTracing.h | `src/types/UiaTracing.h` | 87行目: `Signal::TextChanged()` の宣言 |
| 6-2 | UiaTracing.cpp | `src/types/UiaTracing.cpp` | 733-744行目: ETW `TraceLoggingWrite` による `"Signal::TextChanged"` トレース出力 |

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

```
Renderer (描画サイクル)
    │
    ├─ UiaEngine::Invalidate()                    [UiaRenderer.cpp:65]
    │      └─ _textBufferChanged = true
    │
    ├─ UiaEngine::InvalidateAll()                 [UiaRenderer.cpp:141]
    │      └─ _textBufferChanged = true
    │
    ├─ UiaEngine::NotifyNewText()                 [UiaRenderer.cpp:147]
    │      ├─ _newOutput.append(newText)
    │      └─ _textBufferChanged = true
    │
    ├─ UiaEngine::StartPaint()                    [UiaRenderer.cpp:170]
    │      └─ somethingToDo チェック
    │
    ├─ UiaEngine::EndPaint()                      [UiaRenderer.cpp:190]
    │      └─ std::swap(_queuedOutput, _newOutput)
    │
    └─ UiaEngine::Present()                       [UiaRenderer.cpp:219]
           │
           └─ _dispatcher->SignalTextChanged()
                  │
                  ├─[XAML版] InteractivityAutomationPeer::SignalTextChanged()
                  │      └─ TextChanged.raise()
                  │             └─ TermControlAutomationPeer::SignalTextChanged()
                  │                    ├─ UiaTracing::Signal::TextChanged()
                  │                    └─ Dispatcher.RunAsync()
                  │                           └─ RaiseAutomationEvent(TextPatternOnTextChanged)
                  │
                  └─[HWND版] HwndTerminalAutomationPeer::SignalTextChanged()
                         ├─ UiaTracing::Signal::TextChanged()
                         └─ UiaRaiseAutomationEvent(UIA_Text_TextChangedEventId)
```

### データフロー図

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

テキストバッファ変更   ───▶  UiaEngine::Invalidate()            ───▶  _textBufferChanged = true
  (til::rect)                    │
                                 ▼
全体再描画要求        ───▶  UiaEngine::InvalidateAll()          ───▶  _textBufferChanged = true
                                 │
                                 ▼
新テキスト出力        ───▶  UiaEngine::NotifyNewText()          ───▶  _newOutput にバッファリング
  (wstring_view)                 │                                    + _textBufferChanged = true
                                 ▼
                           UiaEngine::EndPaint()                ───▶  _newOutput → _queuedOutput スワップ
                                 │
                                 ▼
                           UiaEngine::Present()                 ───▶  IUiaEventDispatcher
                                 │                                    ::SignalTextChanged()
                                 ▼
                           AutomationPeer                       ───▶  UIA イベント
                           (XAML or HWND)                              (TextPatternOnTextChanged)
                                 │
                                 ▼
                           OS UIA Infrastructure                ───▶  スクリーンリーダー等の
                                                                       UIA クライアント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| IUiaEventDispatcher.h | `src/types/IUiaEventDispatcher.h` | ソース（ヘッダー） | UIA イベントディスパッチャのインターフェース定義 |
| UiaRenderer.hpp | `src/renderer/uia/UiaRenderer.hpp` | ソース（ヘッダー） | UIA レンダラーエンジンのクラス定義 |
| UiaRenderer.cpp | `src/renderer/uia/UiaRenderer.cpp` | ソース | UIA レンダラーエンジンの実装（Invalidate, InvalidateAll, NotifyNewText, Present） |
| InteractivityAutomationPeer.h | `src/cascadia/TerminalControl/InteractivityAutomationPeer.h` | ソース（ヘッダー） | Interactivity 層の AutomationPeer 定義 |
| InteractivityAutomationPeer.cpp | `src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp` | ソース | Interactivity 層の SignalTextChanged 実装 |
| TermControlAutomationPeer.h | `src/cascadia/TerminalControl/TermControlAutomationPeer.h` | ソース（ヘッダー） | TermControl の AutomationPeer 定義 |
| TermControlAutomationPeer.cpp | `src/cascadia/TerminalControl/TermControlAutomationPeer.cpp` | ソース | XAML UIA イベント発火の実装 |
| HwndTerminalAutomationPeer.hpp | `src/cascadia/TerminalControl/HwndTerminalAutomationPeer.hpp` | ソース（ヘッダー） | HWND 版 AutomationPeer 定義 |
| HwndTerminalAutomationPeer.cpp | `src/cascadia/TerminalControl/HwndTerminalAutomationPeer.cpp` | ソース | HWND 版の Win32 UIA イベント発火実装 |
| UiaTracing.h | `src/types/UiaTracing.h` | ソース（ヘッダー） | UIA トレーシングクラス定義 |
| UiaTracing.cpp | `src/types/UiaTracing.cpp` | ソース | ETW トレースログ出力の実装 |
