# 通知設計書 22-Layout

## 概要

本ドキュメントは、Windows Terminalのコンソールホスト（conhost）におけるLayoutアクセシビリティ通知の設計を記述する。コンソールのレイアウト（スクロールバー位置やウィンドウサイズ）が変更された際に、MSAA（Microsoft Active Accessibility）経由でスクリーンリーダー等の支援技術にレイアウト変更を通知する仕組みである。

### 本通知の処理概要

Layout通知は、コンソールのレイアウトが変更された際に、EVENT_CONSOLE_LAYOUTイベントを支援技術に送信するアクセシビリティ通知である。

**業務上の目的・背景**：視覚障がいのあるユーザーがスクリーンリーダーを使用してコンソールを操作する際、レイアウト変更（ウィンドウサイズ変更、スクロールバー状態変更等）を把握する必要がある。EVENT_CONSOLE_LAYOUTイベントのWindows公式ドキュメントの説明は「The console layout has changed.」とだけ記載されており、具体的な意味は曖昧であるが、ソースコードのコメントではスクロールバー位置やウィンドウサイズ変更時に発行すべきとされている。

**通知の送信タイミング**：スクロールバー状態の更新時（FetchScrollBarState）およびスクリーンバッファのリサイズ完了時に送信される。

**通知の受信者**：WinEventフック（SetWinEventHookでEVENT_CONSOLE_LAYOUTを購読しているプロセス）を通じて、スクリーンリーダー等の支援技術が受信する。

**通知内容の概要**：レイアウトが変更されたことを示すイベントのみ。追加パラメータは含まれない（idObject=0、idChild=0）。

**期待されるアクション**：支援技術はレイアウト変更を認識し、コンソールの表示範囲を再取得して、ユーザーに適切なフィードバックを提供する。

## 通知種別

アクセシビリティ通知（MSAA WinEvent: EVENT_CONSOLE_LAYOUT）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（デバウンスタイマーによるバッチ処理）、ただしdelay=0の場合は同期 |
| 優先度 | 低 |
| リトライ | 無（NotifyWinEvent APIに委譲） |

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

WinEventフック機構を使用する。`IsWinEventHookInstalled(EVENT_CONSOLE_LAYOUT)`がtrueの場合のみ通知を発行する。受信先はSetWinEventHookでEVENT_CONSOLE_LAYOUTを購読している全プロセスとなる。

## 通知テンプレート

### WinEventパラメータ

| 項目 | 内容 |
|-----|------|
| イベントID | EVENT_CONSOLE_LAYOUT |
| HWND | コンソールウィンドウハンドル（_hwnd） |
| idObject (LONG) | 0 |
| idChild (LONG) | 0 |

### 本文テンプレート

```
NotifyWinEvent(EVENT_CONSOLE_LAYOUT, _hwnd, 0, 0)
```

### 添付ファイル

該当なし

## テンプレート変数

該当なし（パラメータを伴わないイベント通知）

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 内部処理 | SCREEN_INFORMATION::FetchScrollBarState | MSAAが有効 かつ EVENT_CONSOLE_LAYOUTフック登録済み | スクロールバー状態更新時 |
| 内部処理 | SCREEN_INFORMATION::ResizeScreenBuffer | MSAAが有効 かつ EVENT_CONSOLE_LAYOUTフック登録済み かつ IsActiveScreenBuffer() | スクリーンバッファのリサイズ完了時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| MSAAが無効 | _msaaEnabled が false の場合 |
| WinEventフック未登録 | IsWinEventHookInstalled(EVENT_CONSOLE_LAYOUT) が false の場合 |
| 非アクティブバッファ | ResizeScreenBuffer時にIsActiveScreenBuffer()がfalseの場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[レイアウト変更発生] --> B{発生元}
    B -->|スクロールバー更新| C[FetchScrollBarState]
    B -->|バッファリサイズ| D[ResizeScreenBuffer]
    C --> E[AccessibilityNotifier::Layout呼び出し]
    D --> F{アクティブバッファ?}
    F -->|Yes| E
    F -->|No| G[スキップ]
    E --> H{MSAAが有効 かつ WinEventフック登録済み?}
    H -->|Yes| I[排他ロック取得]
    H -->|No| G
    I --> J[State.eventConsoleLayoutPrimed = true]
    J --> K[_timerSet呼び出し]
    K --> L[デバウンス後にNotifyWinEvent発行]
    L --> M[終了]
    G --> M
```

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

### 参照テーブル一覧

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| NotifyWinEvent失敗 | OSレベルの障害 | エラーは黙殺される |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| デバウンス遅延 | msaaDelay設定値 |
| デバウンスウィンドウ | 遅延の1/5 |

### 配信時間帯

制限なし

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

- パラメータなしのイベント通知のため、情報漏洩リスクはない
- WinEvent機構のセキュリティモデルに準拠

## 備考

- EVENT_CONSOLE_LAYOUTの仕様はWindowsの公式ドキュメントでも「The console layout has changed.」としか記載されておらず、ソースコードのコメント（行291-293）でも「absolutely unclear what that even means」と述べられている
- FetchScrollBarState内での呼び出しはCM_UPDATE_SCROLL_BARSメッセージ処理の一部として実行される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AccessibilityNotifier.h | `src/host/AccessibilityNotifier.h` | State構造体のeventConsoleLayoutPrimedフィールド（行62） |

**読解のコツ**: Layout通知はState構造体内のboolフラグ1つだけで管理される最もシンプルなイベントである。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | screenInfo.cpp | `src/host/screenInfo.cpp` | FetchScrollBarState内でのLayout()呼び出し（行585） |
| 2-2 | screenInfo.cpp | `src/host/screenInfo.cpp` | ResizeScreenBuffer内でのLayout()呼び出し（行1399） |

**主要処理フロー**:
1. **行585**: スクロールバー状態更新時に `accessibilityNotifier.Layout()` が呼び出される
2. **行1395-1399**: リサイズ成功かつアクティブバッファの場合に `an.Layout()` が呼び出される

#### Step 3: Layout関数の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AccessibilityNotifier.cpp | `src/host/AccessibilityNotifier.cpp` | Layout関数（行294-304）。条件チェック後にPrimedフラグ設定 |

**主要処理フロー**:
- **行296**: `_msaaEnabled && IsWinEventHookInstalled(EVENT_CONSOLE_LAYOUT)` の条件チェック
- **行300**: `_state.eventConsoleLayoutPrimed = true` 設定
- **行302**: `_timerSet()` 呼び出し

#### Step 4: イベント発行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AccessibilityNotifier.cpp | `src/host/AccessibilityNotifier.cpp` | _emitEvents関数内のLayout発行（行538-542） |

**主要処理フロー**:
- **行540**: `cc->NotifyWinEvent(EVENT_CONSOLE_LAYOUT, _hwnd, 0, 0)` で発行

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

```
SCREEN_INFORMATION::FetchScrollBarState (screenInfo.cpp:585)
    |
    +-- AccessibilityNotifier::Layout()
           |
           +-- _timerSet() -> _emitEvents()
                  |
                  +-- NotifyWinEvent(EVENT_CONSOLE_LAYOUT, _hwnd, 0, 0)

SCREEN_INFORMATION::ResizeScreenBuffer (screenInfo.cpp:1399)
    |
    +-- AccessibilityNotifier::Layout()
           |
           +-- (同上)
```

### データフロー図

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

スクロールバー更新     AccessibilityNotifier           WinEvent
or バッファリサイズ --> ::Layout()                 --> EVENT_CONSOLE_LAYOUT
                       State.layoutPrimed=true         (パラメータなし)
                       デバウンスタイマー                   |
                                                         v
                                                   支援技術（NVDA等）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AccessibilityNotifier.h | `src/host/AccessibilityNotifier.h` | ソース（ヘッダ） | AccessibilityNotifierクラス定義 |
| AccessibilityNotifier.cpp | `src/host/AccessibilityNotifier.cpp` | ソース（実装） | Layout、_emitEvents実装 |
| screenInfo.cpp | `src/host/screenInfo.cpp` | ソース（実装） | FetchScrollBarState、ResizeScreenBufferからの呼び出し元 |
| globals.h | `src/host/globals.h` | ソース（ヘッダ） | グローバルaccessibilityNotifierインスタンス |
