当時は 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 でアクセスガイド使いますから、出番はないです。