接続されているモニター情報を取得する

availableMonitors() などで全モニターの名前・解像度・位置・倍率・作業領域を取得する。自ウィンドウがある画面の解像度(物理・論理)の求め方、別モニターへの移動、Rust 版も示す。

ウィンドウ 対象: Tauri 2.x 更新日: 読了目安: 約8分 win-022
目次
  1. 前提条件
  2. 1. フロントエンドから実装する (TypeScript)
  3. Monitor オブジェクトの中身
  4. 画面の解像度を知りたいとき
  5. 特定モニターにウィンドウを移動する
  6. 2. バックエンドから実装する (Rust)
  7. 動作確認
  8. よくあるエラーと対処法
  9. OS ごとの違いと注意点
  10. 関連レシピ

「前回と同じディスプレイにウィンドウを出す」「サブモニターにプレゼン用ウィンドウを置く」「モニターごとの DPI に合わせて座標を補正する」には、接続中の各モニターの位置・解像度・スケール倍率が必要です。@tauri-apps/api/window の availableMonitors() で全モニター、primaryMonitor() でメイン、currentMonitor() で自ウィンドウが乗っているモニター、monitorFromPoint(x, y) で座標からモニターを引けます。Rust 側にも同名のメソッドがあります。

前提条件

プラグインは不要です。4 つの関数の権限(core:window:allow-available-monitors / allow-primary-monitor / allow-current-monitor / allow-monitor-from-point)はすべて core:window:default に含まれるので、core:default があれば追加なしで動きます。ウィンドウを移動する場合だけ core:window:allow-set-position を追加します。

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "default",
  "description": "Capability for the main window",
  "windows": ["main"],
  "permissions": ["core:default", "core:window:allow-set-position"]
}

1. フロントエンドから実装する (TypeScript)

Monitor オブジェクトの中身

プロパティ型意味
namestring(取れなければ null)OS の識別名。Windows は \\.\DISPLAY1 のような内部名
sizePhysicalSize解像度(物理ピクセル)
positionPhysicalPosition仮想デスクトップ上の左上座標。プライマリが (0, 0)、左に置いたモニターは負の x
scaleFactornumber拡大率。150% なら 1.5、Retina は 2
workArea{ position, size }タスクバー / Dock を除いた作業領域(物理ピクセル)

size は物理ピクセルです。CSS や LogicalSize と比べるときは scaleFactor で割ります。4K (3840x2160) を 150% で使うと論理サイズは 2560x1440 です。

import {
  availableMonitors, primaryMonitor, currentMonitor, monitorFromPoint, cursorPosition,
} from '@tauri-apps/api/window';

export async function dumpMonitors() {
  const monitors = await availableMonitors();
  const primary = await primaryMonitor();
  monitors.forEach((m, i) => {
    const lw = Math.round(m.size.width / m.scaleFactor);
    const lh = Math.round(m.size.height / m.scaleFactor);
    console.log(
      `#${i} ${m.name ?? '(no name)'}${m.name === primary?.name ? ' [primary]' : ''}`,
      `${m.size.width}x${m.size.height} @ (${m.position.x}, ${m.position.y})`,
      `scale ${m.scaleFactor} -> logical ${lw}x${lh}`,
      `workArea ${m.workArea.size.width}x${m.workArea.size.height}`,
    );
  });
  console.log('current:', (await currentMonitor())?.name);

  // マウスカーソルがあるモニター(座標は物理ピクセル)
  const cur = await cursorPosition();
  console.log('under cursor:', (await monitorFromPoint(cur.x, cur.y))?.name);
}

画面の解像度を知りたいとき

「画面解像度」として多くの場合に欲しいのは、自分のウィンドウが表示されているモニターの値です。currentMonitor() の size がそれにあたり、物理ピクセルなので 4K モニターなら拡大率に関係なく 3840x2160 です。CSS や LogicalSize と同じ単位で比べるなら size.toLogical(scaleFactor) で論理ピクセルに直します。currentMonitor() はモニターを特定できないと null を返すので、primaryMonitor() に切り替えます。

import { currentMonitor, primaryMonitor } from '@tauri-apps/api/window';

// 自ウィンドウがあるモニターの解像度(物理・論理)を返す
export async function getScreenResolution() {
  const m = (await currentMonitor()) ?? (await primaryMonitor());
  if (!m) return null; // モニターを特定できなかった
  const logical = m.size.toLogical(m.scaleFactor);
  return {
    physical: { width: m.size.width, height: m.size.height },
    logical: { width: Math.round(logical.width), height: Math.round(logical.height) },
    scaleFactor: m.scaleFactor,
  };
}

拡大率 150% の 4K モニターなら physical は 3840x2160、logical は 2560x1440 です。ウィンドウを別のモニターへ動かすと値が変わるので、使う直前に取り直します。ウィンドウの大きさを画面に合わせる目的なら、タスクバーなどを除いた workArea を基準にします(ウィンドウのサイズを変更する)。Rust では、コマンドの引数に tauri::WebviewWindow を受け取って current_monitor()(None なら primary_monitor())を呼べば同じ値が取れます。

特定モニターにウィンドウを移動する

モニターの position を基準に配置を物理ピクセルで計算し、setPosition() に渡します。次の例は 2 台目のモニター中央に自ウィンドウを置きます。

import { availableMonitors, getCurrentWindow, PhysicalPosition } from '@tauri-apps/api/window';

export async function moveToMonitor(index: number) {
  const target = (await availableMonitors())[index];
  if (!target) throw new Error(`monitor #${index} not found`);
  const win = getCurrentWindow();
  const size = await win.outerSize(); // 物理ピクセル
  await win.setPosition(new PhysicalPosition(
    target.position.x + Math.floor((target.size.width - size.width) / 2),
    target.position.y + Math.floor((target.size.height - size.height) / 2),
  ));
}

モニター間でスケール倍率が違うと移動後に物理サイズが変わるため、厳密に中央にしたいなら移動後に center() を呼びます。

2. バックエンドから実装する (Rust)

Window / WebviewWindow / AppHandle のいずれからも呼べます。Monitor のメソッドは name() -> Option<&String>、size() -> &PhysicalSize<u32>、position() -> &PhysicalPosition<i32>、scale_factor() -> f64、work_area() -> &PhysicalRect<i32, u32> です。

use tauri::Manager;

#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
struct MonitorInfo {
    name: Option<String>,
    width: u32,
    height: u32,
    x: i32,
    y: i32,
    scale_factor: f64,
    is_primary: bool,
}

#[tauri::command]
fn list_monitors(app: tauri::AppHandle) -> Result<Vec<MonitorInfo>, String> {
    let primary = app.primary_monitor().map_err(|e| e.to_string())?
        .and_then(|m| m.name().cloned());
    let monitors = app.available_monitors().map_err(|e| e.to_string())?;
    Ok(monitors.iter().map(|m| MonitorInfo {
        name: m.name().cloned(),
        width: m.size().width,
        height: m.size().height,
        x: m.position().x,
        y: m.position().y,
        scale_factor: m.scale_factor(),
        is_primary: m.name().cloned() == primary,
    }).collect())
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![list_monitors])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
import { invoke } from '@tauri-apps/api/core';
const list = await invoke<{ name: string | null; width: number; isPrimary: boolean }[]>('list_monitors');

動作確認

npm run tauri dev で dumpMonitors() を実行すると、Windows のデュアルモニター(右に 4K を 150% で配置)では次のようになります。

#0 \\.\DISPLAY1 [primary] 1920x1080 @ (0, 0) scale 1 -> logical 1920x1080 workArea 1920x1032
#1 \\.\DISPLAY2 3840x2160 @ (1920, -540) scale 1.5 -> logical 2560x1440 workArea 3840x2160
current: \\.\DISPLAY1

moveToMonitor(1) を呼ぶとウィンドウが右のモニター中央へ移動し、currentMonitor() が DISPLAY2 になります。

よくあるエラーと対処法

  • primaryMonitor() / currentMonitor() が null: リモートデスクトップや VM、モニター切替直後などで OS が特定できないケースです。availableMonitors()[0] へフォールバックしてください。
  • monitorFromPoint が常に null: 引数は物理ピクセルです。event.screenX などの論理値は scaleFactor を掛けてから渡します。
  • window.set_position not allowed. Permissions associated with this command: core:window:allow-set-position という趣旨のエラー: 移動には別権限が要ります。前提条件の JSON を確認してください。
  • name で同一判定したら別モニターが同名だった: Linux の一部環境では name が null や重複になります。position と size の組で識別する方が確実です。

OS ごとの違いと注意点

  • Windows: name は \\.\DISPLAY1 形式で、設定アプリの「ディスプレイ 1/2」の番号と一致しないことがあります。モニターごとに DPI が異なるので scaleFactor は必ずモニター単位で見ます。
  • macOS: Retina で scaleFactor は 2。「ディスプレイの配置」でメインを変えると primaryMonitor() も変わります。
  • Linux: X11 では name が HDMI-1 のような出力名になることが多く、Wayland では position が (0, 0) 固定になるコンポジターがあります。
  • モニターの接続・切断を通知するイベントは Tauri にありません。必要なら数秒おきに availableMonitors() を呼んで台数の変化を検知します。

関連レシピ

参考リンク(公式ドキュメント)

Web Ninja

この記事を書いた人

Web Ninja ウェブエンジニア (Web Engineer)

会社員ネットワークエンジニアから独立してかれこれ 25 年以上 Web エンジニアとして活動中。普段は JavaScript と Node.js を自在に操り、時には C++ や Perl といった古流の技も嗜みます。近年は Tauri × Rust という新たな武器を手に、デスクトップアプリ開発の最前線を駆け抜けています。「作りたい」を「作れる」に変えるための、実践的な「技」をお届けします。

お問い合わせ: tauri.ninja@gmail.com

内容の誤り・動かないコードを報告する