スクリーン セーバーの起動をラズパイピコで阻止したい!

スクリーン セーバーの起動をラズパイピコで阻止したい! マイコン
スクリーン セーバーの起動をラズパイピコで阻止したい!

動機

自宅でリモート ワークをしているとき、少し席を外したりするとスクリーン セーバーが起動してしまうため、その都度パスワードを入力してロック画面を解除しなければなりません。これが地味に面倒です。

自宅で作業しているときは、外部の人間に PC の内容を見られるといった可能性は極めて低いため、スクリーン セーバーを起動させないようにさせたいものですが、組織のセキュリティ ポリシー設定により、スクリーン セーバーを無効化することはできません。

そこで、Raspberry Pi Pico を利用して、スクリーン セーバーの起動を阻止できないかと考えました。

仕組み

Raspberry Pi Pico を HID デバイスとして動作させ (Windows からは HID マウスと認識されます)、Raspberry Pi Pico から一定間隔でマウス メッセージを送信し続けることにより、PC からはあたかもマウス操作が行われているように見せかけます。

これにより、PC がアイドル状態にならないため、結果としてスクリーン セーバーの起動を阻止することができます。

ファームウェアの作成

プログラムは、Raspberry Pi Pico サンプル コードdev_hid_composite サンプルを改造して作成します。dev_hid_composite サンプルは様々な HID デバイスをデモするように作られていますが、今回はマウスとして機能させるため、その他のデバイス用のコードは削除していきます。

以下で、サンプルの改造個所を示していきます。

enum 値の追加

サンプル コードの main.c に相当するファイル (私のプログラムでは、PicoSleepBlocker.c) に以下の enum を追加します。これは、マウスのカーソル移動のメッセージを表しています。

C
// マウス メッセージ
enum
{
  MOUSE_MOVE_RIGHT = 1,
  MOUSE_MOVE_LEFT,
};

関数のプロトタイプ宣言の追加

自作関数のプロトタイプ宣言を追加します。関数の本体は次項で説明します。

C
uint GetMessage(void);

マウス メッセージを取得する関数の追加

送信すべきマウス メッセージを取得する関数を作成します。この関数は、hid_task から定期的に呼び出されます。

関数内で、呼び出された回数を count 変数でカウントしており、100 回の倍数の時だけ MOUSE_MOVE_RIGHT または MOUSE_MOVE_LEFT メッセージを返し、それ以外の時は 0 (無効値) を返します。hid_task は 10 ms 間隔で実行されるため、結果として 1000 ms ごとにマウス メッセージが 1 回送信されることになります。

また、1000 ms ごとに返されるマウス メッセージは、MOUSE_MOVE_RIGHT、MOUSE_MOVE_LEFT へ交互に切り替わります。これにより、マウス カーソルが左右に動く動作をエミュレートします。

C
uint GetMessage(void)
{
    static uint msg = MOUSE_MOVE_RIGHT;
    static uint count = 0;

    // 1000 ms ごとに、交互にメッセージを切り替えます (10 ms × 100 回 = 1000 ms)。
    if (++count >= 100)
    {
        count = 0;

        switch (msg)
        {
        case MOUSE_MOVE_LEFT:
            msg = MOUSE_MOVE_RIGHT;
            break;
        default:
            msg = MOUSE_MOVE_LEFT;
            break;
        }
    }
    else
    {
        return 0;
    }

    return msg;
}

send_hid_report 関数の改造

サンプル コードでは様々なデバイスを扱っていますが、今回はマウスとして動作させるため、switch ステートメントの REPORT_ID_MOUSE 以外の case ブロックを削除します。

そして、REPORT_ID_MOUSE ブロックで、左右方向 (X 方向) にカーソルを移動させるメッセージが送られるように書き換えます。ここで、カーソル移動量は delta 定数に 2 と定義しています。これにより、マウス カーソルが左右に 2 ピクセル移動するようになります。また、マウス ボタンやホイール動作についてはすべて 0 を指定しています。

C
static void send_hid_report(uint8_t report_id, uint32_t btn)
{
  // skip if hid is not ready yet
  if ( !tud_hid_ready() ) return;

  switch(report_id)
  {
    // マウス
    case REPORT_ID_MOUSE:
    {
        int8_t const delta = 2; // カーソルの移動量

        switch (btn)
        {
        case MOUSE_MOVE_RIGHT:
            // ボタン クリックなし、右移動、スクロールなし、パンなし
            tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, 0, 0, 0);
            break;

        case MOUSE_MOVE_LEFT:
            // ボタン クリックなし、左移動、スクロールなし、パンなし
            tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, -delta, 0, 0, 0);
            break;

        default:
            break;
        }
    }
    break;

    default:
      break;
  }
}

hid_task の改造

サンプル コードで board_button_read 関数を呼び出している箇所を、自作した GetMessage 関数に置き換えます。さらに、send_hid_report 関数呼び出しの第一引数を REPORT_ID_MOUSE に書き換えます。

C
void hid_task(void)
{
  // Poll every 10ms
  const uint32_t interval_ms = 10;
  static uint32_t start_ms = 0;

  if ( board_millis() - start_ms < interval_ms) return; // not enough time
  start_ms += interval_ms;

  // 送信するマウス メッセージを取得します。
  uint32_t const msg = GetMessage();

  // Remote wakeup
  if ( tud_suspended() && msg )
  {
    // Wake up host if we are in suspend mode
    // and REMOTE_WAKEUP feature is enabled by host
    tud_remote_wakeup();
  }else
  {
    // Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
    send_hid_report(REPORT_ID_MOUSE, msg);
  }
}

tud_hid_report_complete_cb 関数の改造

今回はマウス メッセージしか処理しないため、この関数の処理は空にします。

C
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len)
{
  (void) instance;
  (void) len;
  (void) report;

  // 何も処理しません。
}

usb_descriptors.c の書き換え

サンプル コードのままでもプログラムの動作には特に影響ないのですが、string_desc_arr の 1 番 (Manufacturer) と 2 番 (Product) を一応書き換えました。

C
char const *string_desc_arr[] =
{
  (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
  "Kentaro",                     // 1: Manufacturer
  "Pseudo Mouse Device",         // 2: Product
  NULL,                          // 3: Serials will use unique ID if possible
};

使用方法

ビルドしたファームウェアを書き込んだ Raspberry Pi Pico を、USB ケーブルで PC に接続するだけです。既存のマウスなどは接続したままで構いません。

マウスを操作していないときも、マウス カーソルが 1 秒間隔で左右に動くようになります (マウスと競合するせいか、カーソルが左右に動かない時もあるのですが、試した範囲ではこの場合でもスクリーン セーバーの起動を阻止できているようです)。

マウス カーソルの移動量は 2 ピクセルであるため、たいていの操作では邪魔になることはありません。ただし、図形の描画など、繊細な操作を要する場合は、PC から取り外したほうが良いかもしれません。

ソース コード

ソース コード一式は、GitHub にて公開しています。

感想

サンプル コードの簡単な改造だけで、オリジナル HID デバイスを作成できるのは、Raspberry Pi Pico の魅力だなと改めて感じました。