XMonadでScratchpadをやる

なんとまぁ更新のないことでしょう。最近はもうtwitterばっかりです。それでもそんなにつぶやいているわけでもないので、どれだけアウトプットが少ないのかと。

巷ではタイル型WM熱が高まっているようで、特にawesomeの人気が高いように見受けられます。他の有名なものとしては、タイトルにもあるxmonad、そしてratpoison、stumpwmといったところでしょうか。もちろんこれ以外にも色々あり、これらの後発も作成されているようです。参考(日本タイル型ウィンドウマネージャ推進委員会
ちなみに私はHaskellで作られたXmonadを利用しています。XMonadがどんなWMかは、↑のリンクを辿るなりしていただくとして、ここでは語りません。

awesomeの特徴として聞き齧っているものは以下の通りです(当人はawesome未経験です)

  • dwmベース
  • デフォルトでステータスバーがある
  • Xineramaサポート
  • タグによる分類
  • 透明化のサポート
  • 汎用スクリプト言語Luaによるダイナミックな拡張
  • ...などなど(wikipedia参考)

上記に加え、ちょっと探しただけでも日本語の情報がすぐに見つかるため、設定の共有などもしやすそうです。
また、Luaを利用しているため、動的に設定を変更することが可能です。私はXMonadを利用しているのですが、XMonadは設定ファイルがHaskellかつ、修正したら再起動が必要なため、ちょっと気になる部分があっても修正がおっくうになりがちです。
・・・それとHaskell自体が難しいというのもありますが、それはそれとして。


私はratpoison → stumpwm → xmonad と辿ってきたため、awesomeがどんなものかはほとんど知りませんでしたが、こちらの方が、新たにタイル型WMの世界に飛び込まれたようで、awesomeの機能(というか拡張)について紹介されていました。

この中で、「ターミナルをポップアップ」というものがあり、これが非常に気になりました。
xmonadも、フローティングウィンドウという、タイルから独立して動かすことができるウィンドウをサポートしていますが、これの表示をトグルしたいなー、とか思っていました。

awesomeで出来るのなら、xmonadで作成されていないはずがない!と思い調べてみると、あっさりと見付かりました。惜しいのは日本語情報がほとんど無いということでしょうか・・・。

xmonadでのポップアップ設定例

xmonadでフロートウィンドウのポップアップを実現するには、xmonad-contrib(xmonadの拡張をまとめたもの?です)に入っている、「XMonad.Util.NamedScratchpad」というモジュールを利用します。

以下は、例として、私の利用しているxmonad.hsです。

import Control.Monad
import XMonad
import XMonad.Config.Gnome
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.ManageHelpers (doCenterFloat)
import XMonad.Util.Run (spawnPipe)
import XMonad.Util.EZConfig
import XMonad.Util.WorkspaceCompare
import System.IO
import Data.List -- isPrefixOf,isSuffixOf,isInfixOfを使用可能に
import Control.Monad (liftM2)
import qualified XMonad.StackSet as W
import XMonad.Actions.CycleWS
import XMonad.Layout.MultiToggle
import XMonad.Layout.MultiToggle.Instances
import XMonad.Config.Desktop (desktopLayoutModifiers)
import XMonad.Layout.Named
import XMonad.ManageHook
import XMonad.Util.NamedScratchpad

-- ウィンドウ作成時のデフォルトワークスペースを指定
myManageHookShift = composeAll
    [ className =? "Firefox"                --> viewShift "1"
    , className =? "emacs"                   --> viewShift "2"
    , className =? "mlterm"         --> viewShift "2"
    , className =? "emacs-special"         --> viewShift "3"
    , className =? "chromium" --> viewShift "1"
    ]
    where viewShift = doF . liftM2 (.) W.view W.shift
-- ウィンドウ作成時のフローティングを指定
myManageHookFloat = composeAll
    [ 
    -- className =? "Smplayer"              --> doCenterFloat
    ]
    where role = stringProperty "WM_WINDOW_ROLE"


-- ウィンドウ作成時に、ウィンドウの選択から無視されるウィンドウ
myManageHookIgnore = composeAll
    [ className =? "trayer"              --> doIgnore
    ]

myWorkspaces = ["1","2","3","4","5","6","7","8","9"]

-- ここからポップアップさせるウィンドウの設定
-- scratchpad settings
scratchpads = [
  -- mltermでhtopを実行して、それをデフォルトのフローティングウィンドウで表示させます。
  -- ここではタイトル一致( title =? )を利用しています。
  -- run htop in xterm, find it by title, use default floating window placement
  NS "htop" "mlterm -T 'htop' -e htop" (title =? "htop") defaultFloating ,

  -- smplayerをフローティングウィンドウで管理します。smplayerは、クラス一致を
  -- 利用しています。
  -- customFloatingで、表示する基準をディスプレイの高さの1/6、幅の1/6として、
  -- フローティングウィンドウ自体の幅と高さを、それぞれディスプレイの幅/高さの2/3にしています。
  -- run smplayer, find it by class name, place it in the floating window
  -- 1/6 of screen width from the left, 1/6 of screen height
  -- from the top, 2/3 of screen width by 2/3 of screen height
  NS "smplayer" "smplayer" (className =? "Smplayer")
  (customFloating $ W.RationalRect (1/6) (1/6) (2/3) (2/3))

  ] where role = stringProperty "WM_WINDOW_ROLE"

-- ここまでがポップアップさせるウィンドウの設定

myKeys =
    [
    -- execute dmenu
      ("M-@", spawn "dmenu_run -fn \"-*-Fixed-Bold-R-Normal-*-16-*-*-*-*-*-*-*\""),
    -- CycleWS setup
      ("M-n", moveTo Next NonEmptyWS)
    , ("M-p", moveTo Prev NonEmptyWS)
    -- カレントウィンドウを最初の未使用ワークスペースへ移動
    , ("M-S-n", do t <- findWorkspace getSortByIndex Next EmptyWS 1
                   (windows . W.shift) t
                   (windows . W.greedyView) t)
    , ("M-S-p", shiftTo Prev EmptyWS)
    -- フルスクリーンをトグル(MultiToggle)
    , ("M-f", sendMessage $ Toggle FULL)
    -- 画面リセット(スクリーン1でワークスペース2を開き、スクリーン0でワークスペース1を開く)
    , ("M-S-r", do
        screenWorkspace 1 >>= flip whenJust (windows.W.view)
        (windows . W.greedyView) "2"
        screenWorkspace 0 >>= flip whenJust (windows.W.view)
        (windows . W.greedyView) "1")
    -- デフォルトキーバインドの退避
    , ("M-r", refresh)
    , ("M-S-,", sendMessage Shrink)
    , ("M-S-.", sendMessage Expand)
    ]
    ++ -- (S-)M-h/lでスクリーン切り替え
    [((m ++ "M-" ++ [key]), screenWorkspace sc >>= flip whenJust (windows . f))
        | (key, sc) <- zip "hl" [0..]
        , (f, m) <- [(W.view, ""), (W.shift, "S-")]]
    ++
    -- ポップアップ用のキー設定
    -- toggling scratchpad
    [
      -- ポップアップさせるウィンドウの設定でつけた名称のポップアップの表示をトグルします。
      -- 起動されていなかったら起動されます。
      ("M-S-C-t", namedScratchpadAction scratchpads "htop")
    , ("M-S-C-p", namedScratchpadAction scratchpads "smplayer")
    ]

tall = Tall 1 (3/100) (1/2)

main = do
    xmproc <- spawnPipe "/home/derui/bin/xmobar /home/derui/.xmobarrc"
    xmonad $ defaultConfig
        { workspaces = myWorkspaces
        , manageHook = myManageHookShift
                       <+> myManageHookIgnore
                       <+> manageHook defaultConfig
                       -- manageHokに、Scratchpad用のHookを追加します。
                       <+> namedScratchpadManageHook scratchpads
        , layoutHook = mkToggle1 FULL $ desktopLayoutModifiers (named "V" tall ||| (named "H" $ Mirror tall))
        , logHook = dynamicLogWithPP $ xmobarPP
                        { ppOutput = hPutStrLn xmproc
                        , ppTitle = xmobarColor "green" "" . shorten 50
                        }
        , modMask = mod4Mask     -- Rebind Mod to the Windows key
        , borderWidth = 3  -- フォーカスボーダーのサイズを3ピクセルに。
        } `additionalKeysP` myKeys

この設定では、M-S-C-tでhtopを起動したmltermが、M-S-C-pでsmplayerがポップアップします。
ポップアップされるウィンドウは、「NSP」という専用のワークスペースに移動されます。このワークスペースについては気にしなくてよいようです。

上の設定で利用している部分は、namedScratchpadのドキュメントに書いてあった設定を少しだけ弄ったものです。
まぁawesomeで同様のコードを実現するために必要なコード量を見ると、今回必要だったコード量より大分少ないようです。が、XMonadxmonad-contribにどんな拡張が登録されているか把握していないため、もしかしたらもっと効率的な設定のしかたとかがあるのかもしれません。というか多分あるでしょう。