読者です 読者をやめる 読者になる 読者になる

プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

WindowsでもEmacsをDaemonのように使いたい

 Windowsで何のエディタ使っていますか?私はなぜか結局Emacsです。


 ところで、WindowsのEmacsはdaemonフラグが有効ではありません。
 server使えば似たようなことできるからいいといえばいいのですが、サーバー用のウィンドウが邪魔くさいです。
 最小化してもタスクバーに残るしね。HideItとか使ってみたことあるけど、自分でウィンドウ隠すという作業が面倒くさい。あと、タスクバーにいなくなっても、タスクトレイに存在するのが希に気になる。タスクトレイ実行ツールも試したけど、駄目だった。仮想デスクトップ作って見えないように押し込んだりとかいろいろ試したけど、あぁ、Daemonがほしい。


 というわけで、WindowsでもEmacsをDaemon化したいなーとかずっと思ってたので、ふと何を思ったのか似たような動作になるようにPowerShellでやってみました。

 方法としては

  1. emacs.exeを実行
  2. しばらく待機してから
  3. ウィンドウを非表示にする

ってだけ。まぁ、要するに今までHideItとかタスクトレイ実行ツールや仮想デスクトップに頼ってたのを自分で書いただけですね。とりあえず、自動でウィンドウが見えなくなるので、なんかDeamonっぽいです。
 ※serverの起動は.emacsに書くなりしてください。

コマンド 引数 説明
Emacs-Daemon [ bin-directory [ wait-time [work-directory]] デーモンっぽく起動してみる。
bin-directory:emacs.exeがあるディレクトリ。デフォルトは空文字(Pathに登録されている場合にだけうまく動く)
wait-time:待機ミリ秒
work-directory:作業ディレクトリ
Emacs-Kill Emacsの画面全部閉じる。Emacs開いてなくて、Power Shellだけ開いてるときに使うといいんじゃないかな

 コードは下にはっつけとくので、使ってみたいという希有な人は、適宜profile.ps1にでも貼り付けるなり、スクリプトにするなりしてください 

powershell -Command emacs-daemon

 とかを適当にWindows起動後に実行するようにすれば勝手に動いてくれるからよりデーモンライクだね。ちろちろ画面が出るのがうざいけど。



 ちなみに、「しばらく待機」ってのがやっかいで、早すぎるとウィンドウがとれなくて非表示に失敗するし、かといって待ちすぎるのもあれだし。
 とりあえず、私の環境では2秒じゃだめだけど、5秒だと安定して大丈夫っぽいから5秒待ってる。
 Process.WaitForInputIdleとかでできるかなぁとか思ってた時期が私にもありましたけど、全然無理だった。

 だれかうまい方法ご存じないですかね。誰かWindowsのEmacsでもDaemon化してくれませんかね。


PowerShellのコード

 まぁ、PowerShellというか、ほとんどC#だけど。

$CSCODE=@'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace EmacsDaemon
{
    public class WindowControl
    {
        [DllImport("user32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
        [DllImport("user32.dll")]
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, ref uint lpdwProcessId);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool EnumWindows([MarshalAs(UnmanagedType.FunctionPtr)] EnumWindowsProc lpEnumFunc, IntPtr lParam);

        public static bool KillWindow(IntPtr hWnd) { return SendMessage(hWnd, 0x0112, 0xf060, 0); }
        public static bool HideWindow(IntPtr hWnd) { return ShowWindow(hWnd, 0); }

        class CloseProcess
        {
            Process process;
            public CloseProcess(Process p) { process = p; }
            public bool method(IntPtr hWnd, IntPtr lParam)
            {
                uint p = 0;
                GetWindowThreadProcessId(hWnd, ref p);
                if (p == process.Id)
                {
                    KillWindow(hWnd);
                }
                return true;
            }
        }
        public static void CloseAllWindow(Process process)
        {
            CloseProcess cp = new CloseProcess(process);
            EnumWindowsProc ewp = new EnumWindowsProc(cp.method);
            IntPtr i = new IntPtr();
            EnumWindows(ewp, i);
        }
    }
}
'@
Add-Type -Language CSharp -TypeDefinition $CSCODE

function Emacs-Daemon(){
  param($dir="",$wait=-1,$workDir=$env:HOME);
  $emacsexe="emacs.exe";
  if($dir -ne ""){
    $emacsexe = $dir +"\emacs.exe";
  }
  if($workDir -eq ""){
    $workDir = ".";
  }
  if($wait -eq -1){
    $wait = 5000;
  }
  $eproc=Start-Process $emacsexe -WindowStyle Hidden -PassThru  -WorkingDirectory $workDir
  [Threading.Thread]::Sleep($wait);#誰かもっといい方法教えて
  [EmacsDaemon.WindowControl]::HideWindow($eproc.MainWindowHandle);
}
function Emacs-Kill(){
  $eprocess = Get-Process "emacs";
  foreach($pr in $eprocess){
    [EmacsDaemon.WindowControl]::CloseAllWindow($pr);
  }
}

 てか、はてな記法ってPowerShellに対応していないんだね…。