Xmonad on a MacBook Pro with Fedora 19

For years I have dual booted my Macbook with Fedora. Things have always almost worked, but never quite as smoothly as they do under OSX. Typically the problems are with the trackpad, wireless drivers and heat control – The fans on my MBP didn’t want to kick on at the point I’d like them to and Fedora just runs hotter than OSX does for the same workflow.

With F19 things are somewhat better, the following xorg config file dropped into /etc/X11/xorg.conf.d/ sets up the trackpad fairly well.

Section "InputClass"
       Identifier "tap-by-default"
       MatchIsTouchpad "on"
       Option "TapButton1" "1"
       Option "TapButton2" "3"

Section "InputClass"
       Identifier "force clickpad"
       MatchProduct "device name substring"
       Option "ClickPad" "on"
       # Enable bottom right half as right button
       Option "SoftButtonAreas" "50% 0 82% 0 0 0 0 0"
       Option "ZAxisMapping" "5 4"

But the fans still didn’t want to kick on, and in Xmonad I wanted to be able to adjust backlighting.

The dual boot install with Fedora 19 works pretty well, I shrank the OSX disk using the Disk Utility (filesystem operations are always safer when using native tools), and then booted from a USB install of Fedora 19 Beta or maybe slightly newer – the final release isn’t for a few more weeks but things are looking pretty stable.

It has been a few weeks since I did the install so I am not sure if I used autopart or custom. But I ended up with a LUKS encrypted xfs filesystem that boots when you hold down the option key. At the time the installer had a bug in the kickstart output so it didn’t write the exact partitioning information. That has since been fixed.

I went with a lxde install with Xmonad. You can do this in Anaconda by selecting a basic desktop on the left side of software selection and LXDE and Xmonad on the right side. Yes, we removed the individual package selection option, but you can still select major groups for the install. I install LXDE so that on those occasions where the nmcli Network Manager cmdline tool won’t cooperate I can get to the GUI network settings. Other than that, I spend all my time in Xmonad.

One of Xmonad’s features is the ability to completely customize the system behavior using Haskell. For example I have the media keys setup to use mpc and wanted to also control the fans, disply and keyboard brightness. After a bit of searching I came across this blog post using a bash script. I wanted something a bit more friendly, and I wanted to display the system temperature and fan speeds in Xmobar. So I wrote mac_ctrl to do this. It allows you to controls the fans, the display and keyboard backlight, and read the temperature sensors. It is written in python and structured so that new machines can be added easily by subclassing the existing classes and updating a map.

Xmonad setup

My Xmonad keyboard mapping now look like this:

    -- Media commands (use xev to display keysyms)
    , ((0                 , 0x1008ff03), spawn "sudo /home/bcl/bin/mac_ctrl --set-display 5%-")
    , ((0                 , 0x1008ff02), spawn "sudo /home/bcl/bin/mac_ctrl --set-display 5%+")
    , ((modm              , 0x1008ff03), spawn "sudo /home/bcl/bin/mac_ctrl --set-fans 500-")
    , ((modm              , 0x1008ff02), spawn "sudo /home/bcl/bin/mac_ctrl --set-fans 500+")
    , ((0                 , 0x1008ff06), spawn "sudo /home/bcl/bin/mac_ctrl --set-kbd 10%-")
    , ((0                 , 0x1008ff05), spawn "sudo /home/bcl/bin/mac_ctrl --set-kbd 10%+")
    , ((0                 , 0x1008ff14), spawn "mpc toggle")
    , ((0                 , 0x1008ff17), spawn "mpc next")
    , ((0                 , 0x1008ff16), spawn "mpc prev")
    , ((0                 , 0x1008ff13), spawn "amixer -q set Master 5%+ unmute")
    , ((0                 , 0x1008ff11), spawn "amixer -q set Master 5%- unmute")
    , ((0                 , 0x1008ff12), spawn "amixer -q set Master toggle")
    , ((0                 , 0x1008fff2), spawn "eject /dev/sr0")

In addition to the obvious mappings for brightness and media meta+brightness will adjust the fans by 500rpm.

Displaying values in Xmobar is easy, my .xmobarrc looks like this:

Config { font = "xft:Mono Bold:size=10:antialias=true"
       , bgColor = "black"
       , fgColor = "grey"
       , position = Top
       , lowerOnStart = True
       , commands = [ Run Network "wlp3s0" ["-t","<rx>/<tx>","-L","10","-H","100","--normal","green","--high","red"] 10
                    , Run MultiCpu ["-t","cpu <autototal>","-p","2","-L","3","-H","75","--normal","green","--high","red"] 10
                    , Run Memory ["-t","Mem <usedratio>%"] 10
                    , Run Swap ["-t","<usedratio>"] 10
                    , Run Date "%a %b %_d %Y %H:%M:%S" "date" 10
                    , Run BatteryP ["BAT0"]
                      ["-t", "<acstatus><watts> (<left>%)",
                       "-L", "10", "-H", "80", "-p", "3",
                       "--", "-O", "<fc=green>On</fc> - ", "-o", "",
                       "-L", "-15", "-H", "-5",
                       "-l", "red", "-m", "blue", "-h", "green" ]
                    , Run Com "mac_ctrl" ["--get-max-temp"] "maxtemp" 100
                    , Run Com "mac_ctrl" ["--get-fans"] "fanspeed" 100
                    , Run StdinReader
       , sepChar = "%"
       , alignSep = "}{"
       , template = "%StdinReader% }{ %maxtemp% C | %fanspeed% rpm | %wlp3s0% | %multicpu% | %memory% %swap% | %battery% | <fc=#ee9a00>%date%</fc>"

mac_ctrl setup

It seems that every release of Mac hardware is slightly different from the previous, mac_ctrl needs to know the paths to the various pieces of hardware so you will probably need to customize these the first time you run mac_ctrl. These are set in the classes for each device.

 class DisplayBrightnessMB31(DisplayBrightness):
     """ Display brightness """
     # brightness has the value
     # max_brightness has the max
     # Set this too low and you won't see anything so limit it to 5% of max

When adding a new device you should find the correct base path, create a subclass and set the new path. Give it a name that indicates which hardware it is for and make a new entry in the sys_map dict.

 # Mapping of /sys/class/dmi/id/product_name to dict of classes
 sys_map = { "MacBookPro9,1" : { "temperature"   : Temperature,
                                 "fans"          : Fans,
                                 "keyboard"      : KeyboardBrightness,
                                 "display"       : DisplayBrightness
             "MacBookAir3,2" : { "temperature"   : Temperature,
                                 "fans"          : Fans,
                                 "keyboard"      : DummyKeyboard,
                                 "display"       : DisplayBrightnessMBA32
             "MacBook3,1" :    { "temperature"   : Temperature,
                                 "fans"          : Fans,
                                 "keyboard"      : DummyKeyboard,
                                 "display"       : DisplayBrightnessMB31

If a piece of hardware isn’t supported set it to a dummy class that just adds the cmdline args and empty methods. See the DummyKeyboard for an example.

If you add new hardware please submit a pull request to the github project