xmonadとxmobarの設定というのを書いていたのですが、いろいろ改善点を直しているうちに、追記では収まらなくなっていたので、新たにエントリーを書くことにしました。
xmonadを含むタイリングウィンドウマネージャの魅力については、他の人も多く語っているので、少しググってみると、その魅力に惹かれる人はいるんじゃないかなと思います。ということで,今回はxmonadとはなんぞや、といった話はあまりしません。
また、当人はプログラミング言語のhaskellに関する知識はなく、公式ドキュメントやメーリングリストの情報から設定ファイルを書いているのであしからず。
スクリーンショット
xmonadの設定
GitHubで公開しています。最新の設定はこちらを参照してください。
-------------------------------------------------------------------------------
-- __ ____ __ _ --
-- \ \/ / \/ | ___ _ __ __ _ __| | --
-- \ /| |\/| |/ _ \| '_ \ / _` |/ _` | --
-- / \| | | | (_) | | | | (_| | (_| | --
-- /_/\_\_| |_|\___/|_| |_|\__,_|\__,_| --
-- --
-------------------------------------------------------------------------------
-- written by Shotaro Fujimoto (https://github.com/ssh0) --
-------------------------------------------------------------------------------
-- Import modules {{{
-------------------------------------------------------------------------------
import qualified Data.Map as M
import Control.Monad (liftM2) -- myManageHookShift
import Data.Monoid
import System.IO -- for xmobar
import XMonad
import qualified XMonad.StackSet as W -- myManageHookShift
import XMonad.Actions.CopyWindow
import XMonad.Actions.CycleWS
import qualified XMonad.Actions.FlexibleResize as Flex -- flexible resize
import XMonad.Actions.FloatKeys
import XMonad.Actions.UpdatePointer
import XMonad.Actions.WindowGo
import XMonad.Hooks.DynamicLog -- for xmobar
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.ManageDocks -- avoid xmobar area
import XMonad.Hooks.ManageHelpers
import XMonad.Layout
import XMonad.Layout.DragPane -- see only two window
import XMonad.Layout.Gaps
import XMonad.Layout.NoBorders -- In Full mode, border is no use
import XMonad.Layout.PerWorkspace -- Configure layouts on a per-workspace
import XMonad.Layout.ResizableTile -- Resizable Horizontal border
import XMonad.Layout.Simplest
import XMonad.Layout.SimplestFloat
import XMonad.Layout.Spacing -- this makes smart space around windows
import XMonad.Layout.ToggleLayouts -- Full window at any time
import XMonad.Layout.TwoPane
import XMonad.Prompt
import XMonad.Prompt.Window -- pops up a prompt with window names
import XMonad.Util.EZConfig -- removeKeys, additionalKeys
import XMonad.Util.Run
import XMonad.Util.Run(spawnPipe) -- spawnPipe, hPutStrLn
import XMonad.Util.SpawnOnce
import Graphics.X11.ExtraTypes.XF86
--------------------------------------------------------------------------- }}}
-- local variables {{{
-------------------------------------------------------------------------------
myWorkspaces = ["1", "2", "3", "4", "5"]
modm = mod4Mask
-- Color Setting
colorBlue = "#868bae"
colorGreen = "#00d700"
colorRed = "#ff005f"
colorGray = "#666666"
colorWhite = "#bdbdbd"
colorNormalbg = "#1c1c1c"
colorfg = "#a8b6b8"
-- Border width
borderwidth = 4
-- Border color
mynormalBorderColor = "#262626"
myfocusedBorderColor = "#ededed"
-- Float window control width
moveWD = borderwidth
resizeWD = 2*borderwidth
-- gapwidth
gapwidth = 4
gwU = 11
gwD = 11
gwL = 56
gwR = 55
--------------------------------------------------------------------------- }}}
-- main {{{
-------------------------------------------------------------------------------
main :: IO ()
main = do
wsbar <- spawnPipe myWsBar
xmonad $ ewmh defaultConfig
{ borderWidth = borderwidth
, terminal = "urxvtc"
, focusFollowsMouse = True
, normalBorderColor = mynormalBorderColor
, focusedBorderColor = myfocusedBorderColor
, startupHook = myStartupHook
, manageHook = myManageHookShift <+>
myManageHookFloat <+>
manageDocks
, layoutHook = avoidStruts $ ( toggleLayouts (noBorders Full)
$ onWorkspace "3" simplestFloat
$ onWorkspace "5" (
spacing 14
$ gaps [(U, 2),(D, 2),(L, 5),(R, 5)]
$ ResizableTall 0 (1/42) (1/2) [])
$ myLayout
)
-- xmobar setting
, logHook = myLogHook wsbar
>> updatePointer (0.5, 0.5) (0, 0)
, handleEventHook = fullscreenEventHook
, workspaces = myWorkspaces
, modMask = modm
, mouseBindings = newMouse
}
-------------------------------------------------------------------- }}}
-- Define keys to remove {{{
------------------------------------------------------------------------
`removeKeysP`
[
-- Unused gmrun binding
"M-S-p",
-- Unused close window binding
"M-S-c",
"M-S-<Return>"
]
-------------------------------------------------------------------- }}}
-- Keymap: window operations {{{
------------------------------------------------------------------------
`additionalKeysP`
[
-- Shrink / Expand the focused window
("M-," , sendMessage Shrink)
, ("M-." , sendMessage Expand)
, ("M-z" , sendMessage MirrorShrink)
, ("M-a" , sendMessage MirrorExpand)
-- Close the focused window
, ("M-c" , kill1)
-- Toggle layout (Fullscreen mode)
, ("M-f" , sendMessage ToggleLayout)
, ("M-S-f" , withFocused (keysMoveWindow (-borderwidth,-borderwidth)))
-- Move the focused window
, ("M-C-<R>", withFocused (keysMoveWindow (moveWD, 0)))
, ("M-C-<L>", withFocused (keysMoveWindow (-moveWD, 0)))
, ("M-C-<U>", withFocused (keysMoveWindow (0, -moveWD)))
, ("M-C-<D>", withFocused (keysMoveWindow (0, moveWD)))
-- Resize the focused window
, ("M-s" , withFocused (keysResizeWindow (-resizeWD, resizeWD) (0.5, 0.5)))
, ("M-i" , withFocused (keysResizeWindow (resizeWD, resizeWD) (0.5, 0.5)))
-- Increase / Decrese the number of master pane
, ("M-S-;" , sendMessage $ IncMasterN 1)
, ("M--" , sendMessage $ IncMasterN (-1))
-- Go to the next / previous workspace
, ("M-<R>" , nextWS )
, ("M-<L>" , prevWS )
, ("M-l" , nextWS )
, ("M-h" , prevWS )
-- Shift the focused window to the next / previous workspace
, ("M-S-<R>", shiftToNext)
, ("M-S-<L>", shiftToPrev)
, ("M-S-l" , shiftToNext)
, ("M-S-h" , shiftToPrev)
-- CopyWindow
, ("M-v" , windows copyToAll)
, ("M-S-v" , killAllOtherCopies)
-- Move the focus down / up
, ("M-<D>" , windows W.focusDown)
, ("M-<U>" , windows W.focusUp)
, ("M-j" , windows W.focusDown)
, ("M-k" , windows W.focusUp)
-- Swap the focused window down / up
, ("M-S-j" , windows W.swapDown)
, ("M-S-k" , windows W.swapUp)
-- Shift the focused window to the master window
, ("M-S-m" , windows W.shiftMaster)
-- Search a window and focus into the window
, ("M-g" , windowPromptGoto myXPConfig)
-- Search a window and bring to the current workspace
, ("M-b" , windowPromptBring myXPConfig)
-- Move the focus to next screen (multi screen)
, ("M-<Tab>", nextScreen)
]
-------------------------------------------------------------------- }}}
-- Keymap: Manage workspace {{{
------------------------------------------------------------------------
-- mod-[1..9] Switch to workspace N
-- mod-shift-[1..9] Move window to workspace N
-- mod-control-[1..9] Copy window to workspace N
`additionalKeys`
[ ((m .|. modm, k), windows $ f i)
| (i, k) <- zip myWorkspaces [xK_1 ..]
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask), (copy, controlMask)]
]
-------------------------------------------------------------------- }}}
-- Keymap: custom commands {{{
------------------------------------------------------------------------
`additionalKeysP`
[
-- Lock screen
("M1-C-l", spawn "xscreensaver-command -lock")
-- Toggle compton (compsite manager)
, ("M1-C-t", spawn "bash toggle_compton.sh")
-- Launch terminal
, ("M-<Return>", spawn "urxvtc")
-- Launch terminal with a float window
, ("M-S-<Return>", spawn "urxvt_float.sh")
-- Insert a transparent panel
, ("M-S-t", spawn "python $HOME/Workspace/python/transparent.py")
-- Launch file manager
, ("M-e", spawn "thunar")
-- Launch web browser
, ("M-w", spawn "luakit")
-- Launch dmenu for launching applicatiton
, ("M-p", spawn "exe=`dmenu_run -l 10 -fn 'Migu 1M:size=20'` && exec $exe")
-- Lauch websearch application (See https://github.com/ssh0/web_search)
, ("M1-C-f", spawn "websearch")
-- Play / Pause media keys
, ("<XF86AudioPlay>" , spawn "mpc toggle")
, ("<XF86HomePage>" , spawn "mpc toggle")
, ("S-<F6>" , spawn "mpc toggle")
, ("S-<XF86AudioPlay>", spawn "streamradio pause")
, ("S-<XF86HomePage>" , spawn "streamradio pause")
-- Volume setting media keys
, ("<XF86AudioRaiseVolume>", spawn "sound_volume_change_wrapper.sh +")
, ("<XF86AudioLowerVolume>", spawn "sound_volume_change_wrapper.sh -")
, ("<XF86AudioMute>" , spawn "sound_volume_change_wrapper.sh m")
-- Brightness Keys
, ("<XF86MonBrightnessUp>" , spawn "xbacklight + 5 -time 100 -steps 1")
, ("<XF86MonBrightnessDown>", spawn "xbacklight - 5 -time 100 -steps 1")
-- Take a screenshot (whole window)
, ("<Print>", spawn "screenshot.sh")
-- Take a screenshot (selected area)
, ("S-<Print>", spawn "screenshot_select.sh")
-- Launch ipython qtconsole
, ("<XF86Calculator>", spawn "ipython qtconsole --matplotlib=inline")
-- Toggle touchpad
, ("C-<Escape>", spawn "touchpad_toggle.sh")
-- Toggle trackpoint (Lenovo PC)
, ("M1-<Escape>", spawn "trackpoint_toggle.sh")
]
--------------------------------------------------------------------------- }}}
-- myLayout: Handle Window behaveior {{{
-------------------------------------------------------------------------------
myLayout = spacing gapwidth $ gaps [(U, gwU),(D, gwD),(L, gwL),(R, gwR)]
$ (ResizableTall 1 (1/201) (116/201) [])
||| (TwoPane (1/201) (116/201))
||| Simplest
-- -- For 10x20 character size
-- myLayout = spacing gapwidth $ gaps [(U, gwU),(D, gwD),(L, gwL),(R, gwR)]
-- $ (ResizableTall 1 (1/181) (96/181) [])
-- ||| (TwoPane (1/181) (96/181))
-- ||| Simplest
--------------------------------------------------------------------------- }}}
-- myStartupHook: Start up applications {{{
-------------------------------------------------------------------------------
myStartupHook = do
spawnOnce "gnome-settings-daemon"
spawnOnce "nm-applet"
spawnOnce "xscreensaver -no-splash"
spawnOnce "$HOME/.dropbox-dist/dropboxd"
-- spawnOnce "bash $HOME/.fehbg"
-- spawnOnce "compton -b --config $HOME/.config/compton/compton.conf"
--------------------------------------------------------------------------- }}}
-- myManageHookShift: some window must created there {{{
-------------------------------------------------------------------------------
myManageHookShift = composeAll
-- if you want to know className, type "$ xprop|grep CLASS" on shell
[ className =? "Gimp" --> mydoShift "3"
, stringProperty "WM_NAME" =? "Figure 1" --> doShift "5"
]
where mydoShift = doF . liftM2 (.) W.greedyView W.shift
--------------------------------------------------------------------------- }}}
-- myManageHookFloat: new window will created in Float mode {{{
-------------------------------------------------------------------------------
myManageHookFloat = composeAll
[ className =? "Gimp" --> doFloat
, className =? "Tk" --> doFloat
, className =? "mplayer2" --> doCenterFloat
, className =? "mpv" --> doCenterFloat
, className =? "feh" --> doCenterFloat
, className =? "Display.im6" --> doCenterFloat
, className =? "Shutter" --> doCenterFloat
, className =? "Thunar" --> doCenterFloat
, className =? "Nautilus" --> doCenterFloat
, className =? "Plugin-container" --> doCenterFloat
, className =? "Screenkey" --> (doRectFloat $ W.RationalRect 0.7 0.9 0.3 0.1)
, className =? "Websearch" --> doCenterFloat
, className =? "XClock" --> doSideFloat NE
, title =? "Speedbar" --> doCenterFloat
, title =? "urxvt_float" --> doSideFloat SC
, isFullscreen --> doFullFloat
, isDialog --> doCenterFloat
, stringProperty "WM_NAME" =? "LINE" --> (doRectFloat $ W.RationalRect 0.60 0.1 0.39 0.82)
, stringProperty "WM_NAME" =? "Google Keep" --> (doRectFloat $ W.RationalRect 0.3 0.1 0.4 0.82)
, stringProperty "WM_NAME" =? "tmptex.pdf - 1/1 (96 dpi)" --> (doRectFloat $ W.RationalRect 0.29 0.25 0.42 0.5)
, stringProperty "WM_NAME" =? "Figure 1" --> doFloat
]
--------------------------------------------------------------------------- }}}
-- myLogHook: loghock settings {{{
-------------------------------------------------------------------------------
myLogHook h = dynamicLogWithPP $ wsPP { ppOutput = hPutStrLn h }
--------------------------------------------------------------------------- }}}
-- myWsBar: xmobar setting {{{
-------------------------------------------------------------------------------
myWsBar = "xmobar $HOME/.xmonad/xmobarrc"
wsPP = xmobarPP { ppOrder = \(ws:l:t:_) -> [ws,t]
, ppCurrent = xmobarColor colorRed colorNormalbg . \s -> "●"
, ppUrgent = xmobarColor colorGray colorNormalbg . \s -> "●"
, ppVisible = xmobarColor colorRed colorNormalbg . \s -> "⦿"
, ppHidden = xmobarColor colorGray colorNormalbg . \s -> "●"
, ppHiddenNoWindows = xmobarColor colorGray colorNormalbg . \s -> "○"
, ppTitle = xmobarColor colorRed colorNormalbg
, ppOutput = putStrLn
, ppWsSep = " "
, ppSep = " "
}
--------------------------------------------------------------------------- }}}
-- myXPConfig: XPConfig {{{
myXPConfig = defaultXPConfig
{ font = "xft:Migu 1M:size=20:antialias=true"
, fgColor = colorfg
, bgColor = colorNormalbg
, borderColor = colorNormalbg
, height = 35
, promptBorderWidth = 0
, autoComplete = Just 100000
, bgHLight = colorNormalbg
, fgHLight = colorRed
, position = Bottom
}
--------------------------------------------------------------------------- }}}
-- newMouse: Right click is used for resizing window {{{
-------------------------------------------------------------------------------
myMouse x = [ ((modm, button3), (\w -> focus w >> Flex.mouseResizeWindow w)) ]
newMouse x = M.union (mouseBindings defaultConfig x) (M.fromList (myMouse x))
--------------------------------------------------------------------------- }}}
-- vim: ft=haskell
設定しているキーバインドのうち,便利そうなものを抜粋すると,
-
Super + Shift + Return
- 端末urxvtをフロートモードで起動(urxvt_float.sh
) - 音量アップボタン(0x1008ff13) - 音量を上げてunity環境と同じ音でフィードバック
- 音量ダウンボタン(0x1008ff11) - 音量を下げてunity環境と同じ音でフィードバック
- ミュートボタン(0x1008ff12) - 音量をミュート
- 輝度アップボタン(0x1008FF02) - xbacklightを使って画面の明るさを上げる
- 輝度ダウンボタン(0x1008FF03) - xbacklightを使って画面の明るさを下げる
-
Super + f
- 全画面表示(ただしステータスバーは残す)と切り替え -
Super + .
- マスター領域の横幅を大きくする -
Super + ,
- マスター領域の横幅を小さくする -
Super + a
- 領域の縦幅を小さくする -
Super + z
- 領域の縦幅を大きくする -
Super + g
- ウィンドウ名で検索、フォーカスを移す -
Super + b
- ウィンドウ名で検索、現在のワークスペースに持ってくる -
Super + Tab
- 次の(物理)モニターにフォーカス -
Super + Control + 1, 2, ..
- ウィンドウをn番目のワークスペースにコピー
設定しておいた方がいいもの
Libreofficeを起動するとフォーカスがループする問題
でも指摘したように,いくつかのアプリケーションLibreOffice等でダイアログを立ち上げるとフォーカスが勝手に遷移してCPU使用率が上がり,PCが使用不能になる問題がありました。
これを解決するためにはXMonad.Hooks.EwmhDesktops
をインポートして,main = xmonad $ defaultConfig
のようになっていた行を
main = xmonad $ ewmh defaultConfig
としてやればいいです。
デメリットは特に無いので,マストで設定すべきでしょう。
デュアルスクリーンを使用する場合
普段使用する際にはスクリーンを2つ使っていて,セカンドスクリーン(1200x1600)は常にワークスペース5を割り当てており,
この解像度で適切に表示されるようにスペーシングなどをカスタマイズ(後述)してあります。
どのモニタで映されているか検出してレイアウトを変えるのは骨が折れたので,このような解決策をとっています。
, layoutHook = onWorkspace "5" (
-- ワークスペース5用のレイアウトをここに書く
)
$ myLayout
)
レイアウトへのこだわり
xmonadでこだわるところといえば,レイアウトでしょう。
というのも,ウィンドウが自動で配置されるのが特徴のタイル型ウィンドウマネージャで,その幅や高さをしょっちゅう変えていたのでは本末転倒感があります。
僕の場合これだ! と思ったレイアウトでしばらく過ごして,リサイズする場面が多いと思ったらレイアウトを調整する...みたいなことをやって,いまの配置に落ち着いており,リサイズ系のキーバインドは使わなくても良いようになってきています。
どんな気持ちで今のレイアウトになったのか,少しまとめておきます。
通常状態(ウィンドウ数1) :
ウィンドウを1つ表示している状態では,以下のようであって欲しいです。
- ターミナルをおいた時, 右端,左端に隙間を作らない
- アスペクト比の16:9動画が,端の黒い部分なく映る
これを実現するためにはターミナル上のフォントサイズや,urxvtのinternalBorder,ボーダー幅,Gaps,Spacingをうまく設定してやる必要があります。
xwininfo
でウィンドウサイズを取得したり,tput cols
やtput lines
をターミナルで実行することで文字数を確認したりして,
1つのウィンドウの幅と高さを決定します。あとはボーダー幅やGapを計算して設定するといい感じにできるのではと思います。
レイアウトの変更はSuper + q
でxmonadの再起動をしても反映されないことがあるので,一旦ログインしなおして設定していました。
通常状態(ウィンドウ数2以上) :
ウィンドウ数が二つ以上のとき,ResizableTallを使っていると,左側にマスター領域,右側にサブ領域と配置されます。
ここで,ターミナルのウィンドウ幅が変わってしまい,端ができて残念な気持ちになりますが,諦めることはありません。
(Spacing+ボーダー幅,internalBorder) ✕ 2
したものが端末上のフォント幅の整数倍となっていれば良いのです。
また,ResizableTallの設定でマスター領域の分割幅とリサイズ時の刻みを指定できますが,ここの分母を1ウィンドウでの端末の文字数+(Spacing+ボーダー幅,internalBorder) ✕ 4 ÷ フォント幅
として分子を適当に選んでやれば,端末で右端が出ることはありません。
ウィンドウ最大化状態 :
ウィンドウの最大化をしたい時もあるかと思います。ただ,ステータスバーは隠したくありませんでした。
このときavoidStruts $ ( toggleLayouts (noBorders Full)
としていたように,avoidStruts
を入れてやることで
Super + f
でウィンドウの最大化を行った時もステータスバーが表示されます。noBorders
をつけて,最大化時はボーダーが表示されないようにします。
さて,ここでまた気になる点が。
端末を最大化した時に,やはり右端と下端に隙間ができてしまうのです。
これはxmobarの高さとurxvtのinternalBorderを調整することで解決できます。
まとめると
以上のようなことを考え,自分の場合1920x1080のメインモニタと1200x1600のサブモニタに対し,フォントサイズ9x19の場合にうまく設定したのが現在の設定となっています。
もし同じ解像度で同じフォントサイズであればまるっと使えると思うので,試してみてください。
イケてると思うxmonadの隙間について【自分のPCで使用しているソフトウェア,設定の紹介 #3】 - service ssh0 start
実際に動かしてみた動画
YouTubeに実際に動かしてみた様子をあげたので、よろしければご覧ください。(設定などはかなり古いです)
xmobarの設定
~/.xmonad/xmobarrcの中身は以下のようになっています。
フォントは,以前はTakaoPGothicを使っていましたが現在Migu 1Mを使っています。
-- vim: ft=haskell
Config { font = "xft:Migu 1M:size=13"
, bgColor = "#1c1c1c"
, fgColor = "#404040"
, position = TopSize C 100 33
-- , position = Static { xpos = 37 , ypos = 0, width = 1845, height = 18 }
, lowerOnStart = False
, overrideRedirect = False
, border = BottomBM 0
, borderColor = "#4e4e4e"
, commands = [ Run Network "eth0" [ "-t" , "⇩<rx> ⇧<tx> "
, "--Low" , "40"
, "--High" , "200"
, "-m" , "4"
, "--normal" , "#b0bdbd"
, "--high" , "#a5d6a7"
] 10
, Run MultiCpu [ "-t" , "■ <total0>.<total1>.<total2>.<total3>.<total4>.<total5>.<total6>.<total7> "
, "--Low" , "40"
, "--High" , "85"
, "-m" , "2"
, "--normal" , "#b0bdbd"
, "--high" , "#ef9a9a"
] 10
, Run Memory [ "-t" , "□ <usedratio>% "
, "--Low" , "40"
, "--High" , "90"
, "-m" , "2"
, "--normal" , "#b0bdbd"
, "--high" , "#ef9a9a"
] 10
, Run BatteryP ["CMB1"]
[ "-t" , "⌇ <acstatus> "
, "--Low" , "20"
, "--High" , "80"
, "--low" , "#ef9a9a"
, "--normal" , "#b0bdbd"
, "--"
, "-o" , "<left>% (<timeleft>)"
, "-O" , "Charging <left>%"
, "-i" , "<left>%"
] 50
, Run Date "%a %m/%d %H:%M" "date" 10
, Run StdinReader
]
, sepChar = "%"
, alignSep = "}{"
, template = " %StdinReader% }{ %multicpu%%memory%%eth0%%battery%<fc=#ece391>%date%</fc> "
}
xmonad.hs
側で,以下のように各ワークスペースの状態にあわせてワークスペースラベルの表示を切り替えるように設定しておくと,
現在のワークスペースがどこか(●
色付き),ウィンドウはどこにあるか(●
灰色),他のモニタはどのワークスペースを写しているか(⦿
)がひと目で分かるようになるので,おすすめです。
wsPP = xmobarPP { ppOrder = \(ws:l:t:_) -> [ws,t]
, ppCurrent = xmobarColor colorRed colorNormalbg . \s -> "●"
, ppUrgent = xmobarColor colorGray colorNormalbg . \s -> "●"
, ppVisible = xmobarColor colorRed colorNormalbg . \s -> "⦿"
, ppHidden = xmobarColor colorGray colorNormalbg . \s -> "●"
, ppHiddenNoWindows = xmobarColor colorGray colorNormalbg . \s -> "○"
, ppTitle = xmobarColor colorRed colorNormalbg
, ppOutput = putStrLn
, ppWsSep = " "
, ppSep = " "
}
まとめ
タイル型ウィンドウマネージャを使うメリットは,作業領域を固定(=視線を固定)しつつも,少ない操作で目的のウィンドウにフォーカスできることだと思っています。
その中でxmonadを使うメリットは,その設定が細かくできるという点にあります。
毎日使う机(デスクトップ)だからこそ,ここにこだわっても良いんじゃないかと個人的には思います。
xmonadはいいぞ。