当時は Java 6 の時代で、Windows での使用しか想定していませんでしたが、今回は Java 14 ベースの Mac 版にリライトしてみました。Windows でも動くんじゃないかと思いますが、未確認です。
ダウンロードとインストール
Mac 用には jpackage で JRE ごとパッケージ化してみました。開くとマウントされますので、Applications にドラッグ&ドロップです。
ダウンロード (for Mac)その他のプラットフォームでは JRE 14 をインストールの上、下記 jar を使ってください。
ダウンロード (Java 14 用実行可能 jar)
使用方法
babyproof.jar をダブルクリックすると起動します。起動すると、キーボードとマウスがほぼ操作不可状態になります。Windows の Alt+TAB や Mac の Command+TAB は効きますので、アプリケーションをBabyproof から他に切り替えれば、操作可能になります。もう一度 Babyproof を選択すれば、再度操作不可状態になります。
操作不能となっている状態でキーボードから「babyproof」と入力すれば、Babyproof は終了します。
制限事項
アプリケーションまで届かないイベントは防げないので、Windows キーなどには反応してしまいます。Mac だと、画面上部のメニューバーがガードされておらず、この領域にはマウスが反応します。実現方法
Swing で透明なフレームを全画面表示にすることで、画面表示を妨げることなく、キーボード、マウスのイベントを全部 Babyproof に拾わせるという作戦になっています。ソースコード
import java.awt.Color; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.Timer; public class Babyproof extends JFrame { private static final long serialVersionUID = 1L; public static void main(final String args[]) { new Babyproof().setVisible(true); } private static final String PASSWORD = "babyproof"; private static final char PASSWORD_HEAD = PASSWORD.charAt(0); private final StringBuilder input = new StringBuilder(PASSWORD.length()); public Babyproof() { super("Babyproof"); setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE); setExtendedState(JFrame.MAXIMIZED_BOTH); setUndecorated(true); setBackground(new Color(0x00000000, true)); final MessageLabel label = new MessageLabel(); add(label); final Timer timer = new Timer(20, label); timer.start(); addKeyListener(new KeyListener() { public void keyPressed(final KeyEvent e) { } public void keyReleased(final KeyEvent e) { } public void keyTyped(final KeyEvent e) { final char c = e.getKeyChar(); input.append(c); if (input.length() >= PASSWORD.length() && input.toString().startsWith(PASSWORD)) { System.exit(0); } if (!PASSWORD.startsWith(input.toString())) { input.setLength(0); input.append(c); if (c != PASSWORD_HEAD) { label.restart(); } } } }); } private static final class MessageLabel extends JLabel implements ActionListener { private static final long serialVersionUID = 1L; private MessageLabel() { super("Type `" + PASSWORD + "' to exit."); setFont(getFont().deriveFont(Font.BOLD, 32.0f)); setForeground(new Color(0x00000000, true)); setBackground(new Color(0x00000000, true)); setHorizontalAlignment(JLabel.LEFT); setVerticalAlignment(JLabel.TOP); } private int mode = 0; private int alpha = 0; public void restart() { if (mode == 0) { this.mode = 1; } } public void actionPerformed(final ActionEvent e) { switch (mode) { case 1: alpha += 4; if (alpha >= 0x100) { mode = -1; } else { setForeground(new Color(0xff, 0xff, 0xff, alpha)); } break; case -1: alpha -= 4; if (alpha > 0) { setForeground(new Color(0xff, 0xff, 0xff, alpha)); } else { setForeground(new Color(0xff, 0xff, 0xff, 0)); mode = 0; } break; default: } } } }
見所1 babyproof と入力すると終了するロジック
使用方法で述べた通り、「babyproof」と入力すると終了するようになっていますが、それが42〜53行目のロジックです。Babyproof が有効なとき、キー入力のたびにこの keyTyped() が呼ばれます。ここで、今まで入力された文字列が「babyproof」に合致するかどうかを調べ、合致したら終了させています。- 入力された文字を一旦 StringBuilder に積む
- 積まれた文字列が「babyproof」の長さ以上になったら、前方から「babyproof」に合致するかどうか調べ、合致したら終了
- 入力された文字が「babyproof」に前方一致しなかったら、積んである文字列を一旦破棄し、今入力された1文字だけ積み直す
最後に1文字積み直しているのは、例えば「babyproobabyproof」と入力したときにちゃんと終了するためです。「babyproob」のところで合致しないと判定されて破棄しますが、この「b」を積み直さないと残りの「abyproof」だけでは合致しないからです。
見所2 パスワードが合致しないときに「Type `babyproof' to exit.」と浮かびあがらせるロジック
実はコードの半分はこの処理です……。
「babyproof」以外のキー入力を行うと、画面左上にメッセージが浮かびあがるようになっています。赤ちゃんはきっと読めないだろうとタカをくくってパスワードを画面に表示しています。
まず、20ms ごとに駆動するタイマーをセットしています (33〜34行目)。
メッセージを表示させる要求をタイマーに伝えると (51行目→73〜77行目)、メッセージ文字の透過度を徐々に下げて (不透明にして) から、また上げる (透明にする) という処理が動くようになっています (80〜99行目)。一巡したらまた要求があるまで待ちます。透明度を変えている途中で新しい要求があった場合は無視します。
ちょっとしたツールですが、うちの子が小さいときはけっこう重宝しました。
でも最近は iPad でアクセスガイド使いますから、出番はないです。