Xmonad with two monitors - xmonad

how can I setup XMonad to work with two monitors? I have a laptop and when at my desk I plug a second monitor on the HDMI port.
With the monitor plugged and activated, XMonad thinks I have a single very wide screen. I3 works good; I have different workspaces for different monitors.
import System.IO
import XMonad
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.ManageDocks
import XMonad.Util.EZConfig (additionalKeys)
import XMonad.Util.Run (spawnPipe)
import qualified Data.Map as M
main =
xmonad =<<
xmobar
defaultConfig
{ terminal = "alacritty"
, manageHook = manageDocks <+> manageHook defaultConfig
, layoutHook = avoidStruts $ layoutHook defaultConfig
, handleEventHook = mconcat [docksEventHook, handleEventHook defaultConfig]
, borderWidth = 2
, modMask = mod4Mask
, keys = mykeys
}
mykeys :: XConfig Layout -> M.Map (KeyMask, KeySym) (X ())
mykeys c = (myKeys c) `M.union` (XMonad.keys defaultConfig c)
where
myKeys (XConfig {modMask = modm}) = myKeyBindings modm
myKeyBindings modm = M.fromList $ []
This is my xmonad.hs
My xorg.conf is:
➜ X11 cat xorg.conf
Section "ServerLayout"
Identifier "layout"
Screen 0 "nvidia"
Inactive "intel"
EndSection
Section "Device"
Identifier "intel"
Driver "modesetting"
BusID "PCI:0#0:2:0"
Option "AccelMethod" "None"
EndSection
Section "Screen"
Identifier "intel"
Device "intel"
EndSection
Section "Device"
Identifier "nvidia"
Driver "nvidia"
BusID "PCI:1#0:0:0"
Option "ConstrainCursor" "off"
EndSection
Section "Screen"
Identifier "nvidia"
Device "nvidia"
Option "AllowEmptyInitialConfiguration" "on"
Option "IgnoreDisplayDevices" "CRT"
EndSection

I run XMonad as my display manager on a multi-monitor setup, so I can help.
#1
XMonad is not what controls how the monitors are laid out / oriented; that is the job of X itself. You can play around with xrandr to get them laid out how you want. Just make sure both monitors can be detected, and then run this from the command line:
xrandr
That will give you all of your monitor info, like resolution and where it is plugged in.
(https://www.x.org/releases/X11R7.5/doc/man/man1/xrandr.1.html)
This is what I run in my .xinitrc before XMonad launches to configure my monitors:
xrandr --dpi 192
xrandr --output HDMI-0 --mode 1920x1080 --pos 0x0 --scale 1.5x1.5
xrandr --output HDMI-1 --mode 3840x2160 --pos 3840x0
Just make sure you switch out the output name, resolution, and position with how yours are laid out, and only configure the DPI / scale if the monitors have different resolutions.
#2
As far as configuring XMonad to have additional functionality regarding the two-monitor setup, you may want to check out XMonad.Actions.OnScreen from xmonad-contrib.
(https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Actions-OnScreen.html)
Personally I use focusOnMouseMove from XMonad.Actions.UpdateFocus too, to switch the focus monitor when I move the cursor to the other monitor.
(https://hackage.haskell.org/package/xmonad-contrib-0.17.1/docs/XMonad-Actions-UpdateFocus.html#v:focusOnMouseMove)

You can have it set up like this on your .profile.
xrandr --output name_of_primary_display --mode ResxRes --left/right/top/bottom-of secondary-display --auto &

Related

Migrating XMobar to the new 0.17 standard

Just to make it clear, my XMobar uses UnsafeStdinReader and SpawnPipe to send information about workspaces right now. Here are the relevant portions of the configuration:
main = do
xmprocleft <- spawnPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc0.hs"
xmonad $ docks $ ewmhFullscreen $ ewmh $ def
{ manageHook = myManageHook <+> manageDocks
, modMask = myModMask
, terminal = myTerminal
, startupHook = myStartupHook
, layoutHook = showWName' myShowWNameTheme $ myLayoutHook
, workspaces = myWorkspaces
, borderWidth = myBorderWidth
, normalBorderColor = myNormColor
, focusedBorderColor = myFocusColor
, logHook = dynamicLogWithPP $ xmobarPP
{ ppOutput = \x -> hPutStrLn xmprocleft x
, ppCurrent = xmobarColor "#f8f16a" "" . wrap "<fn=1>" "</fn>" -- Workspace that I am viewing now
, ppVisible = xmobarColor "#98be65" "" . wrap "<fn=1>" "</fn>" . clickable -- Workspace that is open on any monitor other than this one
, ppHidden = xmobarColor "#2ac3de" "" . wrap "<fn=1>" "</fn>" . clickable -- Hidden workspaces that have any open software in it but not open on any monitors
, ppHiddenNoWindows = xmobarColor "#c0caf5" "" . wrap "<fn=1>" "</fn>" . clickable -- Workspaces with no open softwares and not open on any monitors
, ppTitle = xmobarColor "#c0caf5" "" . shorten 60 -- Title of active window
, ppSep = "<fc=#444b6a> | </fc>" -- Separator character
, ppUrgent = xmobarColor "#EBCB8B" "" . wrap "!<fn=1>" "</fn>!" -- Urgent workspace
, ppExtras = [windowCount] -- # of windows current workspace
-- name of workspaces, current layout, current title of open software, number of open windows in current workspace
, ppOrder = \(ws:_:_:_) -> [ws] -- stopped showing the current layout, number of open programs in current workspace
}
} `additionalKeysP` myKeys
What I am trying to achieve
According to the XMonad Wiki, SpawnPipe is deprecated for the newer use of XMonadLog to send data to XMobar. I am trying to use Dynamic status bar using dynamicEasySBs according to XMonad.Hooks.StatusBar.PP and XMonad.Hooks.StatusBar.
I did make necessary changes on the XMobar config too. But, the configuration is a bit confusing for me. Has anyone yet made a working config using this new format?
While reading the new Tutorial I manage to Update my setup, this way.
This is how you want to write your xmobarrc
-- appearance
font = "xft:Fira Code:size=11:bold:antialias=true"
, bgColor = "#272727"
, fgColor = "#073642"
, position = Top
, border = BottomB
, borderColor = "#646464"
, textOffset = 11
-- layout
, sepChar = "%" -- delineator between plugin names and straight text
, alignSep = "}{" -- separator between left-right alignment
, template = " %XMonadLog% | %coretemp% | %memory% | %dynnetwork% }{%StdinReader% | %dropbox% | %RJTT% | %date% || %kbd% "
-- general behavior
, lowerOnStart = True -- send to bottom of window stack on start
, hideOnStart = False -- start with window unmapped (hidden)
, allDesktops = True -- show on all desktops
, overrideRedirect = True -- set the Override Redirect flag (Xlib)
, pickBroadest = False -- choose widest display (multi-monitor)
, persistent = True -- enable/disable hiding (True = disabled)
- plugins
-- Numbers can be automatically colored according to their value. xmobar
-- decides color based on a three-tier/two-cutoff system, controlled by
-- command options:
-- --Low sets the low cutoff
-- --High sets the high cutoff
--
-- --low sets the color below --Low cutoff
-- --normal sets the color between --Low and --High cutoffs
-- --High sets the color above --High cutoff
--
-- The --template option controls how the plugin is displayed. Text
-- color can be set by enclosing in <fc></fc> tags. For more details
-- see http://projects.haskell.org/xmobar/#system-monitor-plugins.
, commands =
-- weather monitor
[ Run Weather "RJTT" [ "--template", "<skyCondition> | <fc=#4682B4><tempC></fc>°C | <fc=#4682B4><rh></fc>% | <fc=#4682B4><pressure></fc>hPa"
] 36000
-- network activity monitor (dynamic interface resolution)
, Run DynNetwork [ "--template" , "<dev>: <tx>kB/s|<rx>kB/s"
, "--Low" , "1000" -- units: kB/s
, "--High" , "5000" -- units: kB/s
, "-m" , "4"
, "--low" , "darkgreen"
, "--normal" , "darkorange"
, "--high" , "darkred"
] 10
-- cpu activity monitor
, Run MultiCpu [ "--template" , "Cpu: <total0> <total1> <total2> <total3> <total4> <total5> <total6> <total7>%"
, "--Low" , "50" -- units: %
, "--High" , "85" -- units: %
, "-p" , "3"
, "--low" , "darkgreen"
, "--normal" , "darkorange"
, "--high" , "darkred"
] 10
-- cpu core temperature monitor
, Run CoreTemp [ "--template" , "Temp: <core0> <core1> <core2> <core3>°C"
, "--Low" , "70" -- units: °C
, "--High" , "80" -- units: °C
, "--low" , "darkgreen"
, "--normal" , "darkorange"
, "--high" , "darkred"
] 50
-- memory usage monitor
, Run Memory [ "--template" ,"Mem: <usedratio>%"
, "--Low" , "20" -- units: %
, "--High" , "90" -- units: %
, "--low" , "darkgreen"
, "--normal" , "darkorange"
, "--high" , "darkred"
] 10
-- battery monitor
, Run Battery [ "--template" , "Batt: <left>% - <timeleft>"
, "--Low" , "10" -- units: %
, "--High" , "80" -- units: %
, "--low" , "darkred"
, "--normal" , "darkorange"
, "--high" , "darkgreen"
, "--" -- battery specific options
-- discharging status
, "-o" , "<left>% (<timeleft>)"
-- AC "on" status
, "-O" , "<fc=#dAA520>Charging</fc>"
-- charged status
, "-i" , "<fc=#006000>Charged</fc>"
] 50
-- time and date indicator
-- (%F = y-m-d date, %a = day of week, %T = h:m:s time)
, Run Date "<fc=#ABABAB>%F (%a) %T</fc>" "date" 10
-- Xmonad Xmobar Constructor
, Run XMonadLog
]
And this should be your xmonad.hs
main :: IO ()
main = xmonad
. ewmhFullscreen
. ewmh
. withEasySB (statusBarProp "xmobar" (pure def)) defToggleStrutsKey
$ myConfig
According with this documentation about dinamicLog
DynamicLog API is frozen and users are encouraged to migrate to these modern replacements.
That is then XMonad.Hooks.StatusBar
This module provides a composable interface for (re)starting these status bars and logging to them, either using pipes or X properties. There's also XMonad.Hooks.StatusBar.PP which provides an abstraction and some utilities for customization what is logged to a status bar. Together, these are a modern replacement for XMonad.Hooks.DynamicLog, which is now just a compatibility wrapper.

define an xmonad keymap to set doFullFloat on current window

I want to do something like this from the standard maps:
, ((modMask, xK_t ), withFocused $ windows . W.sink)
But the opposite ie a bit like:
, ((modMask, xK_t ), withFocused $ windows . W.doFullFloat)
I get some way with:
, ((modMask, xK_t ), withFocused $ float)
But that doesnt maximise it - it would to be
, ((modMask, xK_t ), do
withFocused $ float
[SOMETHING TO MAXIMISE WINDOW]
)
Any suggestions?
Thanks
The float function from XMonad.StackSet takes a window and a rectangle.
With import qualified XMonad.StackSet as W :
((modm, xK_f), withFocused $ windows . (flip W.float $ W.RationalRect 0 0 1 1)),
Slightly orthogonal answer but this answer does what I actually want - just not how I had planned to it: Using MultiToggle
This is the magic module: MultiToggle
And I did it by modifying my layouts to inject
id . smartBorders . mkToggle (NOBORDERS ?? FULL ?? EOT)
before the list of layouts and then added a keymap
, ((modMask, xK_w ), sendMessage $ LMT.Toggle FULL)
If anyone can give an answer to my original question/method, I'll vote it as the correct answer! Thanks.
Another alternative seems to be provided Layout.Maximise

How to use Dzen instead of Xmobar in this unusual setup

I am a Haskell newbie and I currently don't have time to really learn Haskell so I'm asking for help from the experts that have it already figured out. :)
This is my current xmonad.hs file: https://github.com/Greduan/dotfiles/blob/dd45d287fade73a3191ad313ec37231a8c802942/xmonad/xmonad.hs
How can I add/configure keybindings (see the myKeys variable) and how can I change from Xmobar to a basic (no config yet) Dzen setup.
It's a setup that doesn't seem to be used in any config I've found and every time eI try to convert it to the other format it doesn't work all that well.
The other format is the main = do etc. etc. etc. BTW.
And also, how can I just convert from this format to the one that's more commonly used.
About "converting" :
As xmonad.hs is just haskell source code that is compiled into "your" xmonad, there isn't really a "format" and there are many ways to write the same thing.
If you mean how to convert main from using =<< operator to do notation :
main = xmonad =<< statusBar myBar myPP statusbarToggleKey myConfig
Here you are using two functions :
the statusBar function that takes four arguments and returns IO (XConfig (ModifiedLayout AvoidStruts l). So basically, using what you passed to it statusBar creates the corresponding XConfig and returns it wrapped in the IO monad.
the xmonad function taking an XConfig and returning IO()
The =<< combines the two : takes the XConfig returned by statusBar out of the IO monad and pass it to xmonad.
The equivalent in do notation is :
main = do
config <- statusBar myBar myPP statusbarToggleKey myConfig
xmonad config
But once you understand what the monads operators do, they can look more elegant than do notation.
Using dzen :
If you want to keep using the statusBar function, you just need to change the arguments you are passing to it.
-- the command line to launch the status bar
myBar = "dzen2 -y -1" --that's for dzen at the bottom of the screen
-- the PP
myPP = defaultPP
Key bindings :
You can see the type of keys in XConfig definition. It's a function taking an XConfig and returning a map.
Here is an example of a "pretty" way to write it, using the fromList function from Data.Map :
import qualified Data.Map as M
myKeys conf#(XConfig {modMask = modm}) = M.fromList $
[
((modm, xK_c), kill),
((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
]
And then if you want to use the keys defined in defaultConfig in addition to yours, you can use <+> :
myConfig = defaultConfig
{
...
, keys = myKeys <+> keys defaultConfig
}
As for the key bindings, use additionalKeys (from the module XMonad.Util.EZConfig).
Here are some key bindings I use (maybe you need some more imports to make everything work):
defaultConfig
{
-- stuff
} `additionalKeys`
[ ((0, xK_Print), spawn "scrot")
, ((mod1Mask, xK_Print), spawn "scrot -m -d 1")
, ((mod1Mask .|. shiftMask, xK_t), spawn "killall trayer && trayer --edge top --align right --SetDockType true --SetPartialStrut true --expand true --transparent true --width 5 --alpha 255 --tint 0x191970 --height 17")
, ((mod1Mask, xK_p), spawn "dmenu_run")
, ((mod1Mask, xK_b ), sendMessage ToggleStruts)
, ((mod1Mask, xK_m ), focusUrgent)
, ((mod1Mask, xK_n ), D.dzen "Hi, mom!" (seconds 4))
, ((mod1Mask, xK_f ), goToSelected defaultGSConfig)
, ((mod4Mask, xK_l ), spawn "cmus-remote -n ") --next song
, ((mod4Mask, xK_h ), spawn "cmus-remote -r") --previous song
, ((mod4Mask, xK_s ), spawn "cmus-remote -s") --stop
, ((mod4Mask, xK_p ), spawn "cmus-remote -p") --play
, ((mod4Mask, xK_Right ), spawn "cmus-remote -k +5") --forward 5 sec
, ((mod4Mask, xK_Left ), spawn "cmus-remote -k -5") --rewind 5 sec
, ((mod4Mask, xK_KP_Subtract ), spawn "amixer -q sset PCM 2dB-") --quieter
, ((mod4Mask, xK_KP_Add ), spawn "amixer -q sset PCM 2dB+") --louder
, ((mod1Mask .|. shiftMask, xK_udiaeresis), removeWorkspace)
, ((mod1Mask .|. shiftMask, xK_numbersign), selectWorkspace defaultXPConfig)
]
The D.dzen comes from import qualified XMonad.Util.Dzen as D. I don't use dzen as status bar but maybe looking into this module might give you some hints.
edit: here is a dzen config: And1's_xmonad.hs. Taken from this site with many examples: Config_archive.
edit2: I just played around a little with the new statusBar function which is apparently quite new and came up with a working example.
edit3: removed the logHook as it isn't needed with statusBar. main now looks like this:
main = do
xmonad =<< statusBar "dzen2" myPP toggleStrutsKey
defaultConfig { --stuff
}
The keys setting didn't work for me and I had to stick to additionalKeys (don't forget the braces then):
main = do
xmonad =<< statusBar "dzen2" myPP toggleStrutsKey
(defaultConfig { --stuff
} `additionalKeys`
[ -- key bindings
])
Once I tidied my xmonad.hs I can also provide the whole file..

XMonad: start program floating based on window title

I use this truly excellent Firefox add-on: https://github.com/docwhat/itsalltext/
Hit ctrl-e to edit any textarea in Vim.
However vim starts up tiled. My browser workspaces are single-window tabbed, so a fullscreen editor is overkill. Here's what I tried to make it start as a floating window:
-- This works, but matches any vim instance, not just itsalltext instances:
-- , className =? "Gvim" --> doFloat
-- This does nothing:
, fmap (isInfixOf "itsalltext") title --> doFloat
xprop shows this for the window that is created:
WM_CLASS(STRING) = "gvim", "Gvim"
WM_ICON_NAME(STRING) = "mail.google.com.3c1b1v2w21.txt (~/.mozilla/firefox/3waevusx.default/itsalltext) - GVIM1"
_NET_WM_ICON_NAME(UTF8_STRING) = "mail.google.com.3c1b1v2w21.txt (~/.mozilla/firefox/3waevusx.default/itsalltext) - GVIM1"
WM_NAME(STRING) = "mail.google.com.3c1b1v2w21.txt (~/.mozilla/firefox/3waevusx.default/itsalltext) - GVIM1"
_NET_WM_NAME(UTF8_STRING) = "mail.google.com.3c1b1v2w21.txt (~/.mozilla/firefox/3waevusx.default/itsalltext) - GVIM1"
Bonus question: doFloat works, but I'd really like to specify how to float. For scratchpads, this works:
customFloating $ W.RationalRect (1/6) (1/6) (2/3) (2/3)
But I couldn't figure out how to use customFloating for a window that's not a scratchpad.

XMonad startup on different workspaces

I want to startup some applications in different workspaces(it is important) on xmonad start. So, I wrote following startupHook:
startupApps :: [String]
startupApps = ["konsole", "emacs", "firefox", "gvim", "konsole"]
startupSpawn :: X ()
startupSpawn = zipWithM_ id (map (spawnOn . show) [1..]) startupApps
But, it spawns all apps in first workspace. It seems to be part of more general problem -- if I start application, it get workspace not when it actually started, but when it loaded.
So, if I start firefox on WS1, then switch to WS2, firefox will spawn on WS2.
Still, what can I do about my intention?
You can use the manageHook to tell xmonad to move certain applications to certain desktops.
myManageHook = composeAll . concat $ [
[ className =? "Firefox" --> doF (shiftToWs 2) ]
, [ className =? "gvim" --> doF (shiftToWs 3) ]
-- and so on
]
The classNames might vary, though.

Resources