スクリーンセーバーの邪魔をするスクリプトをHTAでささっと作る

# はじめに
スクリーンセイバーが邪魔だ。会社のルールでスクリーンセイバーによるスクリーンロックが義務付けられているのだが設定時間が５分と短く，ビデオ会議など相手の話を聞いているうちにロック状態になってしまうことが多々ある。

ということで，スクリーンセイバーをうまく騙してスクリーンロックを避けることはできないか？というのが今回のお題である。

# 基本方針
- 機能のオン／オフを簡単に切り替えられるようにする。
- 機能のオン／オフ状態が一目でわかること。
- 邪魔にならないよう大きさは必要最小限とする。

Screen Saver の邪魔をするということで Screen Saver Jammer と名付けた。お手軽にユーザーインターフェースを作れる HTA（HTML Application）で作成することにする。

# 画面デザイン
機能のオン／オフを制御するトグルスイッチと終了ボタンのみとする。一応，ショートカットキーも働くようにした。Alt-X で終了，Alt-S で開始／終了を切り替える。余計なスペース（改行コード含む）が入らないようタグの間をコメント `<!-- NOBR -->` で繋げている。

```html
<body><!-- NOBR
--><form name="Form" id="Canvas"><!-- NOBR
--><button name="StartStop" accesskey="S" onclick="javascript:start_stop(); return false;">開始(<u>S</u>)</button><!-- NOBR
--><button name="Exit" accesskey="X" onclick="javascript:exit(); return false;">終了(<u>X</u>)</button><!-- NOBR
--></form><!-- NOBR
--></body>
```

# ウィンドウのリサイズ
初期化時にウィンドウサイズを計測し，Form の大きさを超過する分だけウィンドウを小さくしている。つまり，ウィンドウを Form の大きさに合わせている。
※ややしこしいが Form の ID を Canvas としている。
```JavaScript:初期化処理
//------------------------------------------------------------------------------
// 初期化
//------------------------------------------------------------------------------
function initial() {
  var dx = document.body.clientWidth  - Canvas.offsetWidth  - Canvas.offsetLeft * 2;
  var dy = document.body.clientHeight - Canvas.offsetHeight - Canvas.offsetTop  * 2;
  window.resizeBy( -dx, -dy );
}
```

# スクリーンセイバーの妨害方法

１分ごとにシフトキーを押す周期イベントを発生させるようにした。

```JavaScript:初期化処理
//------------------------------------------------------------------------------
// グローバル変数の宣言
//------------------------------------------------------------------------------
var objShell;
var bStart = false;
//------------------------------------------------------------------------------
// 初期化
//------------------------------------------------------------------------------
function initial() {
  //----------------------------------------------------------------------------
  // シェルオブジェクトの作成
  //----------------------------------------------------------------------------
  objShell = new ActiveXObject( "WScript.Shell" );
  //----------------------------------------------------------------------------
  // タイマの起動
  //----------------------------------------------------------------------------
  setInterval( dummy, 60 * 1000 );
}
```

```JavaScript:１分ごとに呼び出される周期処理
//------------------------------------------------------------------------------
// ダミー入力
//------------------------------------------------------------------------------
function dummy() {
  if( bStart ) objShell.sendKeys( "+" );
}
```

# 機能のオン／オフ方法
機能のオン／オフ状態に応じてボタンの表示を切り替えるようにした。

```JavaScript:機能のオン／オフ切替処理
//------------------------------------------------------------------------------
// 開始／停止　※ボタンのテキストをトグルに切り替える
//------------------------------------------------------------------------------
function start_stop() {
  if( bStart ) {
    bStart = false;
    Form.StartStop.value = "開始(<u>S</u>)";
    Form.StartStop.style.borderStyle = "outset";
  } else {
    bStart = true;
    Form.StartStop.value = "停止(<u>S</u>)";
    Form.StartStop.style.borderStyle = "inset";
  }
}
```

# 実装コード
実装コードを以下に示す。

```html:ScreenSavarJammer.HTA
<html>
<head>
<meta http-equiv="MSThemeCompatible" content="yes" />
<meta http-equiv="content-type" content="text/html; charset=Shift_JIS" />
<title>SSJ</title>
<HTA:APPLICATION ID="ScreenSaverJammer"
  APPLICATIONNAME="ScreenSaverJammer"
  BORDER="thin"
  BORDERSTYLE="normal"
  CAPTION="yes"
  ICON="ScreenSaverJammer.ICO"
  INNERBORDER="no"
  MAXIMIZEBUTTON="no"
  MINIMIZEBUTTON="no"
  SHOWINTASKBAR="yes"
  SINGLEINSTANCE="yes"
  SYSMENU="yes"
  VERSION="1.0"
  WINDOWSTATE="normal"
  SCROLL="no"
  SCROLLFLAT="no"
  SELECTION="yes"
  CONTEXTMENU="yes"
  NAVIGABLE="yes"
/>
<style type="text/css">
body {
  background-color: threedface;
  margin: 0px;
  padding: 0px;
}
form {
  display: inline;
}
body, button {
  font-family: BIZ UDゴシック;
  font-size: 10.5pt;
}
</style>
<script languate="javascript">
//------------------------------------------------------------------------------
// グローバル変数の宣言
//------------------------------------------------------------------------------
var objShell;
var bStart = false;
//------------------------------------------------------------------------------
// 初期化
//------------------------------------------------------------------------------
function initial() {
  //----------------------------------------------------------------------------
  // ウィンドウサイズの調整
  //----------------------------------------------------------------------------
  var dx = document.body.clientWidth  - Canvas.offsetWidth  - Canvas.offsetLeft * 2;
  var dy = document.body.clientHeight - Canvas.offsetHeight - Canvas.offsetTop  * 2;
  window.resizeBy( -dx, -dy );
  //----------------------------------------------------------------------------
  // シェルオブジェクトの作成
  //----------------------------------------------------------------------------
  objShell = new ActiveXObject( "WScript.Shell" );
  //----------------------------------------------------------------------------
  // タイマの起動
  //----------------------------------------------------------------------------
  setInterval( dummy, 60 * 1000 );
}
//------------------------------------------------------------------------------
// 開始／停止　※ボタンのテキストをトグルに切り替える
//------------------------------------------------------------------------------
function start_stop() {
  if( bStart ) {
    bStart = false;
    Form.StartStop.value = "開始(<u>S</u>)";
    Form.StartStop.style.borderStyle = "outset";
  } else {
    bStart = true;
    Form.StartStop.value = "停止(<u>S</u>)";
    Form.StartStop.style.borderStyle = "inset";
  }
}
//------------------------------------------------------------------------------
// 終了
//------------------------------------------------------------------------------
function exit() {
  window.close();
}
//------------------------------------------------------------------------------
// ダミー入力
//------------------------------------------------------------------------------
function dummy() {
  if( bStart ) objShell.sendKeys( "+" );
}
window.onload = initial;
</script>
</head>
<body><!-- NOBR
--><form name="Form" id="Canvas"><!-- NOBR
--><button name="StartStop" accesskey="S" onclick="javascript:start_stop(); return false;">開始(<u>S</u>)</button><!-- NOBR
--><button name="Exit" accesskey="X" onclick="javascript:exit(); return false;">終了(<u>X</u>)</button><!-- NOBR
--></form><!-- NOBR
--></span><!-- NOBR
</body>
</html>
```

# アイコン画像

本スクリプトで使用しているアイコン ScreenSaverJammer.ICO は Windows の shell32.dll から抽出したものなので公開できない。なので各自用意して欲しい。※ただしアイコンがなくても動く。


```plantuml
@startuml
<style>
root {
  margin: 0;
  padding: 0;
}
footer {
  fontcolor: black;
  fontstyle: normal;
  fontsize: 16;
}
</style>
title <img https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490655/39360dd0-699a-49bf-6b6b-4e9a2fe2b6ef.png>
footer 図１　shell32.dll のアイコン一覧
@enduml
```

# 実行画面
こんな感じ。

```plantuml
@startuml
<style>
root {
  margin: 0;
  padding: 0;
}
footer {
  fontcolor: black;
  fontstyle: normal;
  fontsize: 16;
}
</style>
title <img https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490655/35e2020d-cc58-a837-f9af-efcf1e9b6eb4.png>
footer 図２　停止状態
@enduml
```

```plantuml
@startuml
<style>
root {
  margin: 0;
  padding: 0;
}
footer {
  fontcolor: black;
  fontstyle: normal;
  fontsize: 16;
}
</style>
title <img https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490655/d4cc528a-0406-3a6a-39b2-6b6f9372500b.png>
footer 図３　稼働状態
@enduml
```

# 注意事項

- １分間に１度シフトキーを押すので，そのときにたまたま文字を入力していると文字化けしてしまう可能性がある。
- テキスト入力を行っているアプリケーションにフォーカスがあるときは，スクリーンセイバーの妨害機能がうまく働かない。可能であれば，この Screen Saver Jammer にフォーカスを移しておくほうが良い。