# 通知設計書 20-ScrollBuffer

## 概要

バッファスクロール操作をスクリーンリーダー等のアクセシビリティクライアントに通知するMSAAイベントの設計書である。EVENT_CONSOLE_UPDATE_SCROLLイベントを発火する。

### 本通知の処理概要

ScrollBufferは、ターミナルのテキストバッファがスクロールされた際に、MSAAのEVENT_CONSOLE_UPDATE_SCROLLイベントを通じてアクセシビリティクライアントに通知するイベントである。バッファスクロール（テキスト出力による自動スクロール）に特化しており、ビューポートスクロール（ScrollViewport）とは区別される。

**業務上の目的・背景**：スクリーンリーダーはバッファ座標をキャッシュしてコンソール内容を追跡している。バッファがスクロールされると、以前キャッシュされた座標が無効になるため、スクリーンリーダーにスクロールデルタを通知してキャッシュの座標を調整させる必要がある。EVENT_CONSOLE_UPDATE_SCROLLはこの目的のために設計されたWindowsの標準アクセシビリティイベントである。

**通知の送信タイミング**：テキストバッファがスクロールした際（例：新しい行が出力されてバッファ末尾に達した場合の自動スクロール）にトリガーされる。デバウンス処理により、複数のスクロールイベントがバッチ処理される。

**通知の受信者**：MSAA経由で、システムに登録されたアクセシビリティクライアント。

**通知内容の概要**：スクロールのデルタ値（X方向は通常0、Y方向はスクロールした行数）がEVENT_CONSOLE_UPDATE_SCROLLのパラメータとして含まれる。

**期待されるアクション**：スクリーンリーダーがキャッシュされたバッファ座標をスクロールデルタ分だけ調整し、正しい位置のテキストを参照し続ける。

## 通知種別

アクセシビリティ通知（MSAA EVENT_CONSOLE_UPDATE_SCROLL）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（タイマーベースのデバウンス後、バックグラウンドスレッドで発火） |
| 優先度 | 中 |
| リトライ | 無し |

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

システムに登録された全てのMSAAクライアントに対してブロードキャストされる。`NotifyWinEvent`APIを通じてOSレベルで配信される。

## 通知テンプレート

### イベント情報

| 項目 | 内容 |
|-----|------|
| MSAA Event ID | EVENT_CONSOLE_UPDATE_SCROLL |
| MSAA idObject | dx（X方向デルタ） |
| MSAA idChild | dy（Y方向デルタ） |

### 本文テンプレート

```
（テキストメッセージは含まない。構造化されたイベントデータのみ。）
```

### 添付ファイル

該当なし。

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| delta | スクロールデルタ（行数） | til::CoordType | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| システム内部 | バッファスクロール | MSAAが有効かつEVENT_CONSOLE_UPDATE_SCROLLフックが設置済み | テキスト出力による自動スクロール |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| MSAAが無効 | `_msaaEnabled == false`の場合 |
| フックが未設置 | `IsWinEventHookInstalled(EVENT_CONSOLE_UPDATE_SCROLL) == false`の場合 |
| タイマーデバウンス期間中 | デルタが蓄積され、タイマー発火時にまとめて処理される |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[バッファスクロール発生] --> B[AccessibilityNotifier::ScrollBuffer]
    B --> C{MSAA有効 & フック設置済み?}
    C -->|No| D[スキップ]
    C -->|Yes| E[排他ロック取得]
    E --> F[デルタをeventConsoleUpdateScrollDeltaYに加算]
    F --> G{カーソル位置がプライム済み?}
    G -->|Yes| H[カーソルY座標をデルタ分調整]
    G -->|No| I{領域がプライム済み?}
    H --> I
    I -->|Yes| J[領域Y座標をデルタ分調整]
    I -->|No| K[_timerSet呼び出し]
    J --> K
    K --> L[タイマーまたは即座にイベント発火]
    L --> M[NotifyWinEvent EVENT_CONSOLE_UPDATE_SCROLL]
```

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

### 参照テーブル一覧

該当なし。

### 更新テーブル一覧

該当なし。

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| NotifyWinEvent失敗 | システムエラー | エラーは無視 |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| デバウンス | タイマーベースのデバウンス。複数のスクロールデルタが加算されてバッチ処理される |

### 配信時間帯

制限なし。

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

スクロールデルタ値のみが通知されるため、テキスト内容の漏洩リスクはない。

## 備考

- EVENT_CONSOLE_UPDATE_SCROLLはバッファスクロールとビューポートスクロールの両方を同じイベントとして扱う設計上の問題がある（コメントに「making the information carried by the event completely useless」との記載あり、AccessibilityNotifier.cpp 行253-254）。
- ScrollBufferが呼ばれた際、既にプライムされたCursorChangedの座標（行261-263）やRegionChangedの座標（行265-269）のY座標もデルタ分調整される。これにより、スクロール後のイベント発火で正しい座標が使用される。
- デルタは加算方式で蓄積される（行258: `_state.eventConsoleUpdateScrollDeltaY += delta`）。タイマー発火までに複数回のスクロールがあった場合、合計デルタが1回のイベントで通知される。
- ScrollViewportメソッドも同じフラグ（eventConsoleUpdateScrollPrimed）を使用するが、ScrollViewportはX/Y両方のデルタを持つ点が異なる（行277-289）。
- conhost v2（Windows 10以降）ではScrollConsoleScreenBuffer呼び出しごとにイベントが発火されていたが、現在の実装ではバッチ処理により最適化されている。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AccessibilityNotifier.h | `src/host/AccessibilityNotifier.h` | 行56-59: State構造体のeventConsoleUpdateScroll関連フィールド（DeltaX, DeltaY, Primed） |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AccessibilityNotifier.cpp | `src/host/AccessibilityNotifier.cpp` | 行244-274: `ScrollBuffer`メソッド。デルタの蓄積、関連座標の調整、_timerSet呼び出し |

**主要処理フロー**:
1. **行248**: MSAAの有効性とフックの設置状況を確認
2. **行258**: `_state.eventConsoleUpdateScrollDeltaY += delta`でデルタを蓄積
3. **行259**: `_state.eventConsoleUpdateScrollPrimed = true`でフラグを設定
4. **行261-263**: カーソル位置のY座標をデルタ分調整
5. **行265-269**: 領域座標のY座標をデルタ分調整
6. **行272**: `_timerSet()`呼び出し

#### Step 3: イベント発火処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AccessibilityNotifier.cpp | `src/host/AccessibilityNotifier.cpp` | 行526-536: `_emitEvents`内のscroll処理。`castSaturated`でSHORT型に変換後、NotifyWinEventで発火 |

**主要処理フロー**:
- **行528-529**: `castSaturated<LONG>`でデルタ値をLONG型にキャスト
- **行531**: `NotifyWinEvent(EVENT_CONSOLE_UPDATE_SCROLL, _hwnd, dx, dy)`で発火

#### Step 4: ScrollViewportとの比較

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AccessibilityNotifier.cpp | `src/host/AccessibilityNotifier.cpp` | 行277-289: `ScrollViewport`メソッド。X/Y両方向のデルタを持つ点がScrollBufferと異なる |

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

```
呼び出し元（テキスト出力処理等）
    |
    +-- AccessibilityNotifier::ScrollBuffer(delta) (AccessibilityNotifier.cpp:246)
            |
            +-- MSAA有効 & フック確認
            +-- 排他ロック取得
            +-- _state.eventConsoleUpdateScrollDeltaY += delta
            +-- _state.eventConsoleUpdateScrollPrimed = true
            +-- [プライム済み] カーソルY座標調整
            +-- [プライム済み] 領域Y座標調整
            +-- _timerSet()
                    |
                    +-- _emitEvents()
                            |
                            +-- castSaturated(DeltaX, DeltaY)
                            +-- NotifyWinEvent(EVENT_CONSOLE_UPDATE_SCROLL, hwnd, dx, dy)
```

### データフロー図

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

バッファスクロール    AccessibilityNotifier
(delta: 行数)  ----> ScrollBuffer()
        |                |
        v           デルタ蓄積                           [MSAA]
    til::CoordType   (加算方式)                          EVENT_CONSOLE_UPDATE_SCROLL
                         |                               (dx=0, dy=合計デルタ)
                    関連座標調整
                    (Cursor, Region)
                         |
                    _timerSet() -> _emitEvents()
                         |
                    NotifyWinEvent()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AccessibilityNotifier.h | `src/host/AccessibilityNotifier.h` | ヘッダー | ScrollBufferメソッド宣言・State定義 |
| AccessibilityNotifier.cpp | `src/host/AccessibilityNotifier.cpp` | ソース | ScrollBuffer・ScrollViewport・_emitEvents実装 |
