blechmusikの日記

キー・カスタマイズ・ソフトウェア "DvorakJ" の覚え書きをはじめとして様々なことを書いています。

IME有効化のためのウィンドウ操作プログラムを導入する

はじめに

 前回の記事で説明したとおり、Windows 10Emacs 25.0.94.2 (IMEパッチ適用) を無事導入することができた。

 誠に残念なことだが、喜ぶのは時期尚早であって、私の環境では何度試しても、起動直後に日本語入力を行うことができなかった。いったい何が原因でこのような症状が発生するのか、未だに分からない。
 私と同様にこの問題に直面し、なんとか打開策を見いだそうとする人がいることには気づいていた。スレッド gnupackフォーラム:windows 8での日本語入力について - gnupack - OSDNによると、対処方法は三種類あるようだ。

  • マウスでウィンドウを移動する
  • マウスでウィンドウのサイズを変更する
  • Windowsキー2回押し

 これまで私は一番目の「移動」によって、問題を解消しようとしてきた。だが、上記のスレッドでも指摘されたように挙動が不安定なところがあった。
 一見すると手軽に対応できるのは三番目の「キー2回押し」だろうが、 Classic Shell - 窓の杜ライブラリ を導入しているためか、私の環境では改善の効果が見られなかった。
 そこでこの記事では、二番目の「マウスによるウィンドウのサイズ変更」による対処方法を選択する。
 なお、余談であるが、マウスを利用せずに(外部プログラムからの操作によって)ウィンドウのサイズを変更しても、IME が有効にならなかったことを明記しておく。どうやら、マウス操作自体に IME 有効化の鍵が何かありそうだ。

emacs lisp の設定(外部プログラムの起動)

 init.el で、Emacs 起動時に外部プログラム(後述)を利用して、ウィンドウのサイズを素早く変更するよう設定する。外部プログラムの名称が resize-the-emacs-window-to-enable-ime.exe で、~/ にそれを置くならば、 path-of-program-resizing-the-emacs-window 変数の値としてそれを設定する。

(setq path-of-program-resizing-the-emacs-window
      "~/resize-the-emacs-window-to-enable-ime.exe")
  
(defun resize-the-emacs-window (path &rest args)
  (with-temp-buffer
    (apply 'start-process
           (append (list "my-process" nil path)
                   (loop for x in args
                         collect (if (numberp x)
                                     (number-to-string x)
                                   x))))))

;; GitHub - rejeep/f.el: Modern API for working with files and directories in Emacs
;; https://github.com/rejeep/f.el
(require 'f)
(when (f-exists? (expand-file-name path-of-program-resizing-the-emacs-window))
  (add-hook 'emacs-startup-hook 
            '(lambda ()
               (resize-the-emacs-window
                path-of-program-resizing-the-emacs-window
                "--sleep_time" 100
                "--button" "right"
                )))
  )

マウスを使いウィンドウのサイズを変更するプログラム

 マウスの入力操作をブロックした後、Emacs の左上にマウスを自動的に移し、ウィンドウのサイズをほんの少しだけ変更してから、もとの位置にマウスを戻すプログラムを作成した。後述の内容を AutoHotkey スクリプトとして作成し、実行バイナリ化すればよい。

 プログラムに渡すことができるオプションとその初期設定の値を表にまとめる。上記の設定例で示したように、引数の前にはハイフン二つを挿入し、オプションの後に空白を入れてから、設定する値を記述する。オプションはどのような順番で記述してもかまわない。

オプション 初期設定値
fromX -1
fromY 0
toX 0
toY 0
sleep_time 10
block_time 10
speed 0
button "left"

 from_ と to_ はEmacs のウィンドウの左上を基準としてどこにマウスを動かすかを指定する。これは変更しなくてもよいだろう。
 sleep_time はプログラムの処理過程毎に入れる休みの長さ(単位はミリ秒)である。
 block_time はマウスの入力操作をブロックした直後に入れる休みの長さ(単位はミリ秒)である。この長さが短すぎると、後続の処理が適切に行われない。この値は長めにする方がよいだろう。
 speed はマウスの移動速度である。数字が小さいほどすばやく移動する。
 button はウィンドウのサイズを変更するために、クリックするマウスのボタンを設定する。マウスの右と左のボタンを入れ替えているならば、 "right" を指定すること。

 もちろんコマンドプロンプトからこのプログラムを動かし Emacs のウィンドウのサイズを変更できるので、思い通りの挙動が得られるよう調整してみてほしい。
 

 上記の emacs lisp の例では --sleep_time の値(初期設定は10ミリ秒)を 100 (ミリ秒)に設定するということだ。from_ と to_ の値を変更しなければ、ごくわずかにウィンドウのサイズを変更することになる。また、マウスの右ボタンを使ってクリックするよう設定している。

;; -*-mode: ahk; coding:utf-8-with-signature-dos ; tab-width : 4 -*-"
/*
-----マウスをクリックしてEmacsのウィンドウを移動し、IMEを有効にするスクリプト---

Emacs起動中にこのプログラムを起動すると、
マウスのクリックによりウィンドウサイズを変更し、IMEを有効にする。
Emacsが起動していなければエラーメッセージを表示し、終了する。

msys2版Emacsで使うことを想定している。
gnupackのEmacsでも使うことができると思われる。
AutoHotKey_Lw / Ahk2Exe_Lで動作。

[1] コンパイルして適当な場所に置く

[2] Emacs 起動時にこのプログラムを起動する
IMEが有効にならない場合は sleep_time の設定値を大きくすること。
設定例を示す。

(setq path-of-program-resizing-the-emacs-window
      "~/resize-the-emacs-window-to-enable-ime.exe")
  
(defun resize-the-emacs-window (path &rest args)
  (with-temp-buffer
    (apply 'start-process
           (append (list "my-process" nil path)
                   (loop for x in args
                         collect (if (numberp x)
                                     (number-to-string x)
                                   x))))))

;; https://github.com/rejeep/f.el
(require 'f)
(when (f-exists? (expand-file-name path-of-program-resizing-the-emacs-window))
  (add-hook 'emacs-startup-hook 
            '(lambda ()
               (resize-the-emacs-window
                path-of-program-resizing-the-emacs-window
                "--sleep_time" 50
                "--button" "right"
                )))
  )

----------------------------------------------------------------
*/

#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%
#SingleInstance force

DetectHiddenWindows, On

CoordMode, Mouse, Client

;; =============================================================
;; メイン実行部

initalizeValues()
setValues(getArgv())

activateEmacs(getEmacsWindowHandle())
dragEmacsWindow(getMousePos())

ExitApp

; ========================================
initalizeValues()
{
    global
    
    fromX:= -1
    fromY:= 0
    toX:= 0
    toY:= 0
    sleep_time:= 10
    block_time:= 10
    speed:= 0
    button:= "left"
}


getArgv()
{
    global
    _Argv := Object()
    
    Loop, %0%
    {
        _Argv.push(%A_Index%)
    }
   
    return _Argv
}



setValues(_options)
{
    global
    
    for index, value in _options {
        if ("--fromX" == value) {
            fromX := _options[index + 1]
        }
        if ("--fromY" == value) {
            fromY := _options[index + 1]
        }
        if ("--toX" == value) {
            toX := _options[index + 1]
        }
        if ("--toY" == value) {
            toY := _options[index + 1]
        }
        if ("--sleep_time" == value) {
            sleep_time := _options[index + 1]
        }
        if ("--speed" == value) {
            speed := _options[index + 1]
        }
        if ("--button" == value) {
            button := _options[index + 1]
        }
    }
}


getEmacsWindowHandle()
{
    Try
    {
        whnd := WinExist("ahk_class Emacs")
        if not (whnd)
        {
             Throw "起動中の Emacs が見つかりません"
        }
    }
    Catch e
    {
        MsgBox, プログラム検索のエラー`n%e%`nプログラムを終了します
        ExitApp
    }

    return whnd
}

activateEmacs(whnd)
{
    WinActivate, ahk_id %whnd%
}


getMousePos()
{
    MouseGetPos, mouseX, mouseY
    return Object("mouseX", mouseX
                , "mouseY", mouseY)
}


dragEmacsWindow(_mousePos)
{
    global

    _mouseX := _mousePos["mouseX"]
    _mouseY := _mousePos["mouseY"]

    BlockInput, MouseMove

    sleep,%sleep_time%
    MouseClick, %Button%, %fromX%, %fromY%, , %speed%, D

    sleep,%sleep_time%
    MouseMove, %toX%, %toY%, %speed%

    sleep,%sleep_time%
    MouseClick, %Button%, %fromX%, %fromY%, , %speed%, U

    sleep,%sleep_time%
    MouseMove, %_mouseX%, %_mouseY%, %speed%
    
    BlockInput, MouseMoveOff
}

次に取り組む課題

 今回の設定によって、 Emacs で日本語入力を起動当初からきちんと入力できるようになった。
 最後の課題は emacsclient に似た機能の実装だ。こちらも AutoHotkey スクリプトを利用して環境を整えることになる。