Delphiでマルチディスプレイを使いこなす！


## 皆さん、PCディスプレイは何を使ってますか。

完全在宅勤務に移行し、全く外出しなくなったことで使い所のなくなった小遣いと、テレワークの補助金で4kディスプレイを導入しました。

PCが3画面出力に対応していることもあり、調子に乗って4kトリプルディスプレイ環境を作ったのですが、マウスカーソルの移動が非常に大変で困ったので、自分なりに解消するツールをDelphiで作成したので紹介します。


こんな感じ![IMG_20201113_180428 (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101615/287a3709-5363-4b1f-7bc7-b2a288c60871.jpeg)




## 構成
- PC：Windows10
- マウス：Logicool MX Anywhere 2S（最近3が出ましたね、欲しい！）
- ディスプレイ：縦、横、縦という並び、全て27インチの4K、スケーリングは125%。真ん中がメインディスプレイ。
![2020-11-13_17h16_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101615/2e682bd0-cc48-51eb-7fb9-6d33bf56fdcc.png)


縦横の解像度は、スケーリングが125%なので、左から[X,Y]=[1728,3104]、[3104,1728]、[1728,3104]となっています。（150%にしたら負けだと思ってる）
この状態だと、橋から端まで移動するのに14cmくらいマウスを動かさなければいけません。
これだけで在宅勤務でなまった体が筋肉痛になりそうです。

そこで、マウスのショートカットボタンを使ってジャンプさせることを思いつきました。


## プログラム
VCLアプリケーションのプロジェクトソースを書き換えます。コンソールアプリでも構いません。

- 左のディスプレイに移動するためのプログラム

```pascal:ToLeft.dpr
program ToLeft;
// {$R *.res}
uses
  Vcl.Forms, Winapi.Windows;
begin
  Application.Initialize;
  SetCursorPos(-864,1600);
end.
```

- 真ん中のディスプレイに移動するためのプログラム

```pascal:ToMain.dpr
program ToMain;
// {$R *.res}
uses
  Vcl.Forms, Winapi.Windows;
begin
  Application.Initialize;
  SetCursorPos(1552,1080);
end.
```

- 右のディスプレイに移動するためのプログラム

```pascal:ToRight.dpr
program ToRight;
// {$R *.res}
uses
  Vcl.Forms, Winapi.Windows;
begin
  Application.Initialize;
  SetCursorPos(3936,1600);
end.
```


## 設定の割当
これらのアプリケーションを、Logicool Optionsというアプリケーションでジェスチャーボタンボタンに割り当てます。（特別なボタンなので、Delphiでマウスイベントを受け取ることはできなそうでした）

1. 起動してマウスを選択
![2020-11-06_11h21_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101615/3c237b45-a1fe-9fc7-5dcc-6166a7777915.png)

1. マウスのジェスチャーボタンを選択
![2020-11-06_11h21_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101615/c9d237b4-d93c-dbb1-00d0-6944599048aa.png)

1. ジェスチャーボタンの動作を「カスタム」に変更し、「カスタマイズ」をクリック
![2020-11-06_11h21_37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101615/88971227-193b-4c0d-e059-d3a922c82ff2.png)

1. ○をクリックして、アプリケーションの起動を選択、ToMain.exeを指定する
![2020-11-13_16h49_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101615/e6557193-060e-604b-d93b-df9a34a1907a.png)

1. ←をクリックして、アプリケーションの起動を選択、ToLeft.exeを指定する
![2020-11-06_11h22_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101615/b18a42f4-ed18-b522-e8b8-8837af6fbacd.png)

1. →をクリックして、アプリケーションの起動を選択、ToRight.exeを指定する 
![2020-11-06_11h22_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101615/6c8be3a2-15b9-d5b2-def7-d871ede90912.png)


これで、
・ジェスチャーボタンを押したら真ん中のディスプレイに移動する
・ジェスチャーボタンを押しながらマウスを左に移動させたら左のディスプレイに移動する
・ジェスチャーボタンを押しながらマウスを右に移動させたら右のディスプレイに移動する
という構成ができました。


## 注意
- `{$R *.res}`の指定があると、100%の解像度の位置（[2160,3840]、[3840,2160]、[2160,3840]）で指定ができます。
- `{$R *.res}`の指定がない場合は、スケーリング後（この場合は125%）の解像度（[1728,3104]、[3104,1728]、[1728,3104]）で指定が必要です。
- 位置はメインディスプレイの左上が[0,0]です。そのため、左のディスプレイの左上はX,Y共にマイナス座標になっています。右のディスプレイはYがマイナス座標です。


## もっと
上記のコードだと、一瞬アプリケーションウィンドウが表示され、気になってしまいます。
これを回避するためには、以下のようにVCLアプリケーションとして作成し、`SetCursorPos(xxx)`の記述をフォームのOnCreateイベントに、`Application.ShowMainForm:=False;`をプロジェクトソースに記述します。


- フォームのOnCreateでカーソル位置を指定し、移動後にPostMessageでアプリケーションを強制的に終了させる

```pascal:uToMain.pas
procedure TfMain.FormCreate(Sender: TObject);
begin
  SetCursorPos(1940,1350);
  PostMessage(handle,WM_CLOSE,0,0);
end;
```

- プロジェクトソースに`Application.ShowMainForm:=False;`の記述を追加する
 
```pascal:ToMain.dpr
program ToMain;

uses
  Vcl.Forms,
  uToMain in 'uToMain.pas' {fMain};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.ShowMainForm:=False; // ここ
  Application.CreateForm(TfMain, fMain);
  Application.Run;
end.
```

これで起動の度にアプリケーションが表示されなくなり、気持ちよくマウスを移動できるようになりました。

後は、iniファイルを読み込んで移動先の位置を自由に変更できるような形にすると、みんなhappyになれそう（やるとは言ってない）