My Orgconfig

My Org config   orgconfig

My personal orgconfig

:properties: :export_file_name: _index

This is my live config, written as an Org file and integrated with my site with ox-hugo. Pretty much everything is subject to drastic change. I'll try not to remove any orgconfig files, but no promises. I can't even promise the same blocks will be in the same file next time you visit.

Oh! Use :noexport: tags on subtrees and :exports none on source blocks for sensitive or redundant material.

[orgconfig]: /tags/orgconfig

Diego Zamboni's book Literate Configuration inspired me to get on an orgconfig project. I copied and pasted my dotfiles into source blocks — still need to finish that. After that, I gradually refactor logically connected chunks into their own blocks.

Well. Logical to me, at least. It's fun to see how other people's brains work, yes?

General Config Settings   shell

:properties: :export_file_name: config :export_description: Everything starts here :export_hugo_weight: 1

This section contains the bulk of my personal config. Settings get spun out into their own sections if I think they make this one unwieldy.

Taskwarrior

Maybe someday org will be my main task manager. Until then, I need to be sure Taskwarrior works.

# Files
data.location=~/.task

<<task/contexts>>
<<task/set-verbosity>>
<<task/adjust-urgency>>
<<task/top-report>>
<<task/sync>>
<<task/points-for-tasks>>
<<task/dark-violets-256-theme>>
~/.taskrc
Contexts
context.blog=-work -pay -finances -personal
context.bucket=+idea
context.focused=(priority:H or priority:M) -idea -shelved -finances urgency > 5.0
context.work=+work -idea -personal
context.offwork=-work
Set verbosity

Use verbose=off to cut out any extra messaging.

verbose=header,footnote,label,new-id,affected,edit,special,project,filter,unwait
Adjust urgency

I should blog about this.

urgency.user.tag.work.coefficient=2.0
urgency.user.tag.idea.coefficient=0.5
Top tasks report

task top for a lightweight overview of what I consider my most important tasks.

# Almost-minimal view of tasks I want to work on most
report.top.columns=id,priority,project,tags,description.count
report.top.description='Minimal details of tasks'
report.top.filter=status:pending (priority:H or priority:M)
report.top.labels=ID,Pri,Project,Tags,Description
report.top.sort=priority-/,project-,description+
Points for tasks

A custom UDA. I wonder if I can validate this to require fibonacci values?

uda.points.type=numeric
uda.points.label=Points

# Display pending tasks that have been pointed
report.pointed.description='Open tasks that have point estimates'
report.pointed.columns=id,points,priority,due,description
report.pointed.sort=urgency-
report.pointed.filter=status:pending points > 0

# Display pending tasks that have *not* been pointed
report.unpointed.description='Open tasks that have point estimates'
report.unpointed.columns=id,project,tags,priority,due,description
report.unpointed.sort=urgency-
report.unpointed.filter=status:pending -idea points:
Sync settings

I use Freecinc as my task server. Don't need to export this block. Instructions are on the site.

Themes

I copied these from the standard Taskwarrior shared files, because standard Taskwarrior shared files can be in different locations. Felt cute. Might delete later.

I decided not to export the source blocks for theme configuration. They're handy for me to keep with my config, but add too much noise for others. You can find these themes with your Taskwarrior installation — /usr/share/task on Ubuntu.

task-counts to show task counts   utility

Give me a quick summary of important tasks in Tmux and shell contexts.

  • tasks I'm working on this second (+ACTIVE)

  • tasks I want done soon (priority:H)

  • tasks I want to get to soon (priority:M)

  • tasks I need to triage (priority:)

  # I have started doing these tasks
  a_tasks=$(task rc.verbose:nothing status:pending +ACTIVE count)

  # I am — or want to be — doing these now
  h_tasks=$(task rc.verbose:nothing status:pending priority:H count)

  # I want to do these soon
  m_tasks=$(task rc.verbose:nothing status:pending priority:M count)

  # I haven't looked at these yet
  n_tasks=$(task rc.verbose:nothing status:pending priority: count)

  echo "A:${a_tasks} H:${h_tasks} M:${m_tasks} ?:${n_tasks}"

And what does that look like? As of 2020-06-03, it looks like this:

A:0 H:2 M:29 ?:0

Tmux

set -g default-terminal "tmux-256color"
set-option -sa terminal-overrides ',xterm-256color:RGB'

# See /opt/local/share/doc/tmux-pasteboard/Usage.md
#if-shell 'test "$(uname -s)" = Darwin' 'set-option -g default-command "exec reattach-to-user-namespace -l zsh"'
set -s escape-time 1
#set -g mouse on

#
# display
#

# via https://github.com/gpakosz/.tmux/blob/master/.tmux.conf
set -g base-index 1          # window numbering
setw -g pane-base-index 1    # pane numbering
set -g renumber-windows on   # renumber when a window closed?
setw -g automatic-rename off # rename window to reflect current program?
set -g set-titles on         # set terminal title

set -g monitor-activity on
set -g visual-activity on

set-window-option -g xterm-keys on
set-window-option -g mode-keys vi

bind c new-window -c "#{pane_current_path}"
bind r source-file ~/.tmux.conf \; display "Reloaded!"

bind \\ split-window -h -c "#{pane_current_path}"
unbind %

bind - split-window -v -c "#{pane_current_path}"
unbind '"'

bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
bind H resize-pane -L 5
bind J resize-pane -D 5
bind K resize-pane -U 5
bind L resize-pane -R 5

#
# status bar
#
set -g status on
set -g status-interval 10    # redraw status line after n seconds
set-window-option -g status-position top

# widgets
# See also: https://github.com/samoshkin/tmux-config/blob/master/tmux/tmux.conf
wg_date="#[$color_secondary]%Y-%m-%d %H:%M%z#[default]"
set -g status-right "#{prefix-highlight} #(task-counts) $wg_date"

<<tmux-colors>>
Tmux colors

Solarized everywhere! Solarize all the things!

  #
  # Solarized colors
  #  via https://github.com/seebi/tmux-colors-solarized/blob/master/tmuxcolors-256.conf
  #

  # default statusbar colors
  set-option -g status-style "fg=colour136 bg=colour235"

  # default window title colors
  set-option -g window-style "fg=colour244 bg=default dim"

  # active window title colors
  set-option -g window-status-current-style "fg=colour166 bright"

  # pane border
  set-option -g pane-border-style fg=colour235
  set-option -g pane-active-border-style fg=colour240

  # message text
  set-option -g message-style "bg=colour166 fg=colour235"

  # pane number display
  set-option -g display-panes-active-colour colour33
  set-option -g display-panes-colour colour166

  # clock
  set-option -g clock-mode-colour colour64

  # bell
  set-option -g window-status-bell-style "fg=colour235,bg=colour160"

Kitty

kitty is a nice lightweight terminal emulator that you can extend with Python.

The Ubuntu / Pop!_os version of kitty is a little dated, so I did my own binary installation.

curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin
mkdir -p ~/.terminfo/x
cp ~/.local/kitty.app/share/terminfo/x/xterm-kitty ~/.terminfo/x/
allow_remote_control yes

font_family Fira Code
bold_font   auto
italic_font auto
font_size   12.0
shell       /usr/bin/zsh --login
<<kitty/set-theme>>
~/.config/kitty/kitty.conf
Themes

The kitty-themes repo includes many plethoras of color schemes.

Grab it.

git clone --depth 1 git@github.com:dexpota/kitty-themes.git ~/.config/kitty/kitty-themes

Then include an interesting theme in ~/.config/kitty/kitty.conf.

include     kitty-themes/themes/Solarized_Dark.conf
Shell integration

Add integration for Zsh.

First, make sure kitty itself is in my path.

addpath "$HOME/.local/kitty.app/bin"

Next, enable autocompletion.

autoload -Uz compinit
compinit

# Completion for kitty
kitty + complete setup zsh | source /dev/stdin

Misc

Ack

I'm using ripgrep quite a bit lately, but muscle memory and several scripts still expect ack.

# I never want to search pyc files
--ignore-dir=__pycache__
perltidy
  # Reset to just use https://metacpan.org/source/SRI/Mojolicious-8.50/.perltidyrc
  --backup-and-modify-in-place
  -pbp     # Start with Perl Best Practices
  -w       # Show all warnings
  -iob     # Ignore old breakpoints
  -l=80    # 80 characters per line
  -mbl=2   # No more than 2 blank lines
  -i=2     # Indentation is 2 columns
  -ci=2    # Continuation indentation is 2 columns
  -vt=0    # Less vertical tightness
  -pt=2    # High parenthesis tightness
  -bt=2    # High brace tightness
  -sbt=2   # High square bracket tightness
  -wn      # Weld nested containers
  -isbc    # Don't indent comments without leading space
  -nst     # Don't output to STDOUT

nvim

I bounce between Emacs, (neo)vim, and other editors constantly. I probably always will. Anyways, here's what my Neovim settings look like. At least until I find a plugin to tangle VimWiki files or something.

This uses vim-plug for plugins.

curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
" Plugins
call plug#begin()
Plug 'dhruvasagar/vim-table-mode'
Plug 'tbabej/taskwiki'
Plug 'vimwiki/vimwiki'
Plug 'liuchengxu/vim-which-key'
Plug 'tpope/vim-markdown'
call plug#end()

"
" More or less universal behaviors
"

filetype on
filetype plugin on
filetype indent on
set shiftwidth=2
set expandtab
set smarttab
set autoindent
set textwidth=100
set encoding=utf-8
set conceallevel=0
set timeoutlen=500
set cursorline

let g:mapleader=" "
let g:maplocalleader=','

" set hlsearch
nmap <silent> <BS> :nohlsearch<CR>

" Where should in-editor python evaluation happen?
let g:python3_host_prog = "~/.pyenv/versions/neovim/bin/python3"

"
" Plugin configuration
"

" vim-which-key
nnoremap <silent> <leader> :<c-u>WhichKey '<Space>'<CR>
nnoremap <silent> <localleader:<c-u>WhichKey ','<CR>

" Vimwiki configuration
let g:vimwiki_conceallevel=2

" Show me characters for inline formatting such as `code` and *bold*
let g:vimwiki_conceal_onechar_markers=0
let g:vimwiki_folding='expr' " fold sections and code blocks
let g:vimwiki_auto_header=1 " automatically generate level 1 header for new wiki pages

" Should we create temporary wikis not already in the wiki list? (no we should not)
let g:vimwiki_global_ext=0

let main_wiki={}
let main_wiki.path='$HOME/vimwiki'
let main_wiki.name='Main'
let main_wiki.auto_toc=1
let main_wiki.links_space_char='-'
let main_wiki.diary_caption_level=1

let g:vimwiki_list=[main_wiki]

"
" Behaviors for specific filetypes
"

" set conventional Python spacing
autocmd FileType python setlocal shiftwidth=4

" disable automatic comment extension in new lines.
autocmd FileType * setlocal formatoptions-=cro

"
" Local tweaks
"

" Open help in a new tab
augroup HelpInTabs
	autocmd!
	autocmd BufEnter *.txt call HelpInNewTab()
augroup END

function! HelpInNewTab()
  if &buftype == 'help'
    execute "normal \<C-W>T"
  endif
endfunction

Alacritty

Kitty's nice and all, but let's try another terminal emulator. Alacritty promises speed and a reasonable amount of configurability.

font:
  normal:
    family: IBM Plex Mono
    style: Regular

  bold:
    family: IBM Plex Mono
    style: Bold

  italic:
    family: IBM Plex Mono
    style: Italic

  bold_italic:
    family: IBM Plex Mono
    style: Bold Italic

  size: 12

window:
  dimensions:
    columns: 120
    lines: 40
  padding:
    x: 12
    y: 12
  decorations: full

Zsh config   shell

:properties: :export_file_name: zsh :export_description: Get my shell behaving :export_hugo_weight: 2

Zsh is my main shell. It's sufficiently compatible with bash that I have yet to do anything interesting with it.

zshenv

Loaded for all sessions.

<<zsh/functions>>
<<zsh/base-variables>>

<<zsh/env-local-before>>

<<zsh/initialize-homebrew>>
<<zsh/initialize-dev-managers>>
<<zsh/load-aliases>>
<<zsh/configure-bat>>
<<zsh/configure-nnn>>
<<emacs/doom-shell-adjustments>>
<<kitty/setup-zshenv>>

<<zsh/env-local-after>>
Support functions

Mostly path manipulation functions, at least until I find the inevitable zsh plugin.

  # Remove $2 from path with name $1, e.g.
  #   remove_from_path PATH ~/bin
  #   remove_from_path PYTHONPATH ~/lib/python2.7/site-packages
  function remove_from_path() {
    local a
    local p
    local s
    local r
    eval "p=\$$1"  # get value of specified path
    a=( ${(s/:/)p} )  # turn it into an array
    # return if $2 isn't in path
    if [[ ${a[(i)${2}]} -gt ${#a} ]] && return
    # rebuild path from elements not matching $2
    for s in $a; do
      if [[ ! $s == $2 ]]; then
        [[ -z "$r" ]] && r=$s || r="$r:$s"
      fi
    done
    eval $1="$r"
  }

  # Add path to start of named path, removing any occurences
  # already in it, e.g.
  #   prepend_path PATH ~/bin
  #   prepend_path PYTHONPATH ~/my-py-stuff
  function prepend_path() {
    # Exit if directory doesn't exit
    [[ ! -d "$2" ]] && return
    local p
    remove_from_path "$1" "$2"
    eval "p=\$$1"
    eval export $1="$2:$p"
  }

  # As above, but add to end of path
  function append_path() {
    # Exit if directory doesn't exit
    [[ ! -d "$2" ]] && return
    local p
    remove_from_path "$1" "$2"
    eval "p=\$$1"
    eval export $1="$p:$2"
  }

  function addpath() {
    prepend_path PATH $1
  }

file_info () {
    # file_info: function to display file information

    if [[ -e $1 ]]; then
        echo -e "\nFile Type:"
        file $1
        echo -e "\nFile Status:"
        gstat $1
    else
        echo "$FUNCNAME: usage $FUNCNAME file" >&2
        return 1
    fi
}
Base environment variables
<<zsh/set-base-path>>
<<zsh/define-editor>>
<<zsh/clicolor>>
<<zsh/add-home-bin>>
Set base path

Been burned so many times — not being able to find a thing after I installed it — that I started setting $PATH myself a while back.

PATH="/usr/bin:/bin:/usr/sbin:/sbin"
PATH="/usr/local/bin:/usr/local/sbin:$PATH"
Define editor

edoom is the shortcut script to invoke Emacs with Doom configuration in the terminal.

export EDITOR="edoom"
clicolor

I like pretty colors.

export CLICOLOR=1

ANSI_COLORS_ALIASES='\
    base00=bright_yellow, on_base00=on_bright_yellow,\
    base01=bright_green,  on_base01=on_bright_green, \
    base02=black,         on_base02=on_black,        \
    base03=bright_black,  on_base03=on_bright_black, \
    base0=bright_blue,    on_base0=on_bright_blue,   \
    base1=bright_cyan,    on_base1=on_bright_cyan,   \
    base2=white,          on_base2=on_white,         \
    base3=bright_white,   on_base3=on_bright_white,  \
    orange=bright_red,    on_orange=on_bright_red,   \
    violet=bright_magenta,on_violet=on_bright_magenta'
Add home bin

Set $PATH so it includes my home bin if it exists

if [ -d "$HOME/bin" ] ; then
    addpath "$HOME/bin"
fi
Early local shell initialization

For stuff specific to a particular work environment. For defining variables and functions that may be needed by later processes.

if [ -f "$HOME/.zshenv_local_before" ]; then
    source "$HOME/.zshenv_local_before"
fi
Homebrew

Homebrew works well enough on macOS and Linux that it's become my default package manager. Of course, Arch has AUR, so there's not as much need there.

  if [ -d "/home/linuxbrew" ] ; then
      # For Homebrew on Linux
      # Output to `/home/linuxbrew/.linuxbrew/bin/brew shellenv`
      eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
  fi

  if which brew &> /dev/null; then
      export BREW_PREFIX=`brew --prefix`
      if [ -f "$BREW_PREFIX/etc/bash_completion" ]; then
      . "$BREW_PREFIX/etc/bash_completion.d/git-completion.bash"
      . "$BREW_PREFIX/etc/bash_completion.d/git-prompt.sh"
      . "$BREW_PREFIX/etc/bash_completion"
      fi

      BREW_PYTHON="$BREW_PREFIX/opt/python@3.8"
      if [ -f "$BREW_PYTHON" ]; then
          addpath "$BREW_PYTHON/bin"
          export LDFLAGS="-L$BREW_PYTHON/lib"
          export CPPFLAGS="-I$BREW_PYTHON/include"
          export PKG_CONFIG_PATH="$BREW_PYTHON/lib/pkgconfig"
      fi

      export RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl@1.1)"
  fi
Programming environments and language managers

plenv, pyenv, and friends. I have issues.

<<zsh/enable-go-dev>>
<<zsh/enable-pyenv>>
<<zsh/enable-plenv>>
<<zsh/enable-rakubrew>>
<<zsh/enable-deno-dev>>
Enable go dev

I don't need a go version manager, but a little environment and path manipulation is nice when it's needed.

  export GOPATH="$HOME/go"
  if [ -d "/usr/local/go" ]; then
      addpath "/usr/local/go/bin"
  fi

  if [ -d "$GOPATH" ]; then
      addpath "$GOPATH/bin"
  fi
Enable pyenv

Pyenv is all sorts of headache really, but it's the easiest way to get a fresh python.

I usually end up installing a few pyenv-specific packages before it all starts to make sense.

$ brew install pyenv pyenv-virtualenv pyenv-which-ext

Anyways here's the shell initialization code.

  if which pyenv > /dev/null; then eval "$(pyenv init -)"; fi
Enable plenv

Plenv for managing Perl installations.

export PLENV_HOME="$HOME/.plenv"
if [ -d "$PLENV_HOME" ] ; then addpath "$PLENV_HOME/bin"; fi
if which plenv > /dev/null; then eval "$(plenv init -)"; fi
Enable rakubrew

Rakubrew manages installed versions of Raku. I generally find its versions fresher than what apt offers and its ability to hand me a functioning build more consistent than Homebrew.

  export RAKUBREW_HOME="$HOME/.rakubrew"
  if [ -d "$RAKUBREW_HOME" ]; then addpath "$RAKUBREW_HOME/bin"; fi
  if which rakubrew > /dev/null; then eval "$(rakubrew init Zsh)"; fi

  if [ -d "$HOME/.perl6/bin" ] ; then
      addpath "$HOME/.perl6/bin"
  fi
Enable deno dev

Deno sure looks interesting.

  export DENO_INSTALL="$HOME/.deno"
  if [ -d "$DENO_INSTALL" ] ; then addpath "$DENO_INSTALL/bin"; fi
Load aliases

Should aliases get loaded for all sessions, or only the interactive ones? No idea yet. I'll put it here for now.

  source "$HOME/.aliases"
Configure nnn

nnn is a terminal-based file browser that I forgot all about until I was cleaning up this config.

export NNN_FALLBACK_OPENER=xdg-open
Configure bat

bat is my preferred terminal file viewer. Syntax highlighting, line numbers, git info in the gutter.

if which bat &> /dev/null; then
   # TODO: Set up dotfiles/config/bat/config
   export BAT_THEME="TwoDark"
   export PAGER='bat'
   export MANPAGER="sh -c 'col -bx | bat -l man -p'"
fi
Late local shell initialization

Again, environment-specific stuff. Assumes everything else has been defined. A good spot for starting services.

if [ -f "$HOME/.zshenv_local_after" ]; then
    source "$HOME/.zshenv_local_after"
fi
zshrc

Loaded for interactive sessions.

<<zsh/set-umask>>
<<zsh/initialize-antigen>>
<<zsh/configure-history>>
<<zsh/start-keychain>>
<<zsh/configure-broot>>
<<zsh/wsl-display>>
<<kitty/setup-zshrc>>
Set umask
umask 002
Antigen bundles

Whole lot of debate out there on the right way to extend the shell. I arbitrarily chose Antigen, but still lean on Oh My Zsh. I don't know enough Zsh to really care about the right approach to plugins.

  source ~/.dotfiles/zsh/antigen.zsh

  antigen use oh-my-zsh

  antigen bundle git
  antigen bundle rbenv
  antigen bundle taskwarrior
  antigen bundle tmux

  # Bundles found in the wild.
  # https://github.com/lukechilds/zsh-nvm
  export NVM_COMPLETION=true
  antigen bundle lukechilds/zsh-nvm

  # aesthetic bundles and theme
  antigen bundle zsh-users/zsh-syntax-highlighting
  antigen theme gozilla

  antigen apply

  # The antigen plugin seems to miss PATH_add?
  eval "$(direnv hook zsh)"
WSL/specific stuff

I like to have an Xorg layer for my WSL experience.

  if grep -qEi "(Microsoft|WSL)" /proc/version &> /dev/null; then
      export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0
      export LIBGL_ALWAYS_INDIRECT=1
  fi
Configure history

Manage history. See http://zsh.sourceforge.net/Guide/zshguide02.html#l17

HISTFILE=~/.zsh_history
SAVEHIST=1000
HISTSIZE=1000
setopt share_history
Configure broot

broot is another console-based file explorer. And like nnn, I don't spend nearly enough time figuring out how to use it.

BROOT_LAUNCHER="$HOME/.config/broot/launcher/bash/br"

if [ -f "$BROOT_LAUNCHER" ]; then
    source "$BROOT_LAUNCHER"
fi
Start keychain

Keychain manages SSH & GPG keys for shell sessions, so you only need to enter your managed keys once per session. Handy in both awesomewm and WSL.

if which keychain > /dev/null; then eval `keychain --eval --agents ssh id_rsa randomgeek_rsa`; fi
Aliases

Originally just .bash_aliases, but I figure I can use the same file for Zsh. Right? God I hope so.

  alias ackpy='ack --type=python'
  alias bbd='brew bundle dump --force --describe --global'
  alias be='bundle exec'
  alias dnuke='docker kill $(docker ps -q);docker system prune --all --volumes -f'
  alias e='emacs -nw'
  alias kexp='mplayer http://live-aacplus-64.kexp.org/kexp64.aac'
  alias ls='ls -hF'
  alias ll='ls -lhF'
  alias l='ls -lAhF'
  alias pr='poetry run'
  alias pri='poetry run invoke'
  alias ta='task add'
  alias tad='task add due:eod'
  alias taw='task add due:eow'
  alias tam='task add due:eom'
  alias tn='task-note'
  alias tickets='task +ticket'
  alias unflicker='xrandr --output DisplayPort-2 --mode 2560x1440 --rate 59.95'
  alias work='task +work'
  alias ymd='date +"%Y%m%d"'

Emacs config   emacs

:properties: :export_description: Set up chemacs and Doom Emacs :export_file_name: emacs :export_hugo_weight: 5

This is the config of a mostly-Vim user trying to use Emacs.

The main noteworthy bits are my fondness for Org and perverse determination to get Emacs functioning equally well on Linux, macOS, and Windows 10.

Chemacs

For me, learning an environment includes trying out other, more functional versions of that environment. Chemacs lets me play with assorted Emacs distributions without destroying the config I've worked so hard to barely understand.

Now I can check out Doom Emacs!

Probably want to put ~/.emacs-doom/bin in my path or set some aliases.

emacs --with-profile doom

See StarterKits for other distributions to try out.

  (("default" . ((user-emacs-directory . "~/.emacs-bmw")))
   ("doom" . ((user-emacs-directory . "~/.emacs-doom"))))

Right now I'm mainly exploring Doom Emacs, so that's my default profile now.

doom

Doom Emacs

The default Emacs bindings work in Doom — the ones I know, at least. So yay! The little bit of muscle memory I have for tangling Org files still works!

Modules

init.example.el shows the full list of modules, including what's activated by default.

Run doom sync after modifying!

Yes, I know. Sounds like a great thing to automate in the tangle process. I'll get there.

(doom! :completion company ivy
:ui doom doom-dashboard doom-quit hl-todo modeline ophints (popup +defaults) tabs neotree vc-gutter vi-tilde-fringe workspaces
:editor (evil +everywhere) file-templates fold snippets
:emacs dired electric undo vc
:checkers syntax
:tools (eval +overlay) lookup magit
:lang crystal data emacs-lisp json javascript lua markdown (org +hugo +journal +roam) python raku rst sh web yaml
:config (default +bindings +smartparens))

Next some basic configurations.

Doom shell adjustments

Adds a few Doom-specific utilities to my path

doom

manage and maintain the Doom Emacs configuration

org-capture

Popup an org capture menu

org-tangle

Tangle files from the command line

addpath "$HOME/.emacs-doom/bin"
edoom to run Emacs Doom in a terminal   utility

Emacs Doom is close enough to Vim for muscle memory and starts fast enough that I won't get impatient waiting. Starting it up without a graphical frame is useful for quick edits in the middle of a tmux session, or my many scripts that fire off $EDITOR.

emacs -nw --with-profile=doom "$@"

Pre-Doom Emacs   emacs

:properties: :export_description: was my default, now aiming for "just comfortable enough to get work done" :export_file_name: emacs-bmw :export_hugo_weight: 7

Leans heavily on use-package, perhaps to excess. Anywhere I can configure something via the use-package macro, I do.

Over in Windows, Emacs doesn't assume I want UTF-8. So first things first I'm going to spell that out.

  (prefer-coding-system 'utf-8)
  <<prefer-utf8>>
  <<emacs-preface>>
  <<emacs-variables>>
  <<config-quick-edit>>

  (add-to-list 'load-path
               (file-name-as-directory
                (concat bmw/org-config-dir "local-lisp")))

  ;;
  ;; My own special mode hooks
  ;;

  ;; base mode for coding
  (add-hook 'prog-mode-hook (lambda ()
                              (setq-local display-line-numbers t)))
  (add-hook 'cperl-mode-hook (lambda ()
                              (setq-local display-line-numbers t)))
  (use-package generic-x)

  <<use-color>>
  <<use-face-remap>>
  <<use-files>>
  <<use-goto-addr>>

  (use-package scroll-bar
    :init
    (scroll-bar-mode -1))

  ;; Fine-tune basic editing commands
  (use-package simple
    :init
    ;; Invoke M-x without Alt
    (global-set-key "\C-x\C-m" 'execute-extended-command)
    (column-number-mode)
    :hook
    ((prog-mode) . turn-on-auto-fill))

  (use-package tool-bar
    :init
    (tool-bar-mode -1))

  <<use-exec-path-from-shell>>
  <<use-whitespace>>
  <<use-adoc-mode>>
  <<use-deft>>
  <<use-elscreen>>
  <<use-gnuplot>>
  <<use-lua-mode>>
  <<use-org-bullets>>
  <<use-org-mode>>
  <<use-markdown-mode>>
  <<use-writegood-mode>>
  <<use-yaml-mode>>
  <<use-crystal-mode>>
  <<emacs-perl-settings>>

  <<load-theme>>
  (setq custom-file "~/.emacs-bmw/.emacs-custom.el")
  (load custom-file)

Set up package repositories and make sure use-package is in force.

Components

Preface

  ;;
  ;; package manager setup
  ;;
  ;; Package not installing?
  ;;  Try 'M-x package-refresh-contents'

  (require 'package)

  ;; via https://mastodon.social/@genehack/103737652356761968
  (defvar bmw/packages-refreshed nil)

  (defadvice package-install (before refresh activate)
    "Call package-refresh-contents` before the first package-install."
    (unless (eq bmw/packages-refreshed t)
      (progn
        (package-refresh-contents)
        (setq bmw/packages-refreshed nil))))

  (add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/"))
  (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))

  (setq package-enable-at-startup nil)
  (package-initialize)

  (unless (package-installed-p 'use-package)
    (package-install 'use-package))

  (eval-when-compile
    (require 'use-package))

Variables

Personal custom variables and settings I haven't assigned to a package yet.

  ;; Arbitrarily chosen, but assigned to a variable for consistency.
  (setq bmw/line-length 100)

  ;; Remove the distractions I don't care for
  (setq-default visible-bell t)
  (setq-default fill-column bmw/line-length)
  (setq-default left-margin-width 2)
  (setq-default right-margin-width 2)
  (setq-default indent-tabs-mode nil)  ; I prefer spaces
  (setq-default tab-width 2)
  (setq-default vc-follow-symlinks nil)
  (setq x-underline-at-descent-line t)
  (setq inhibit-startup-message t
        inhibit-startup-echo-area-message t)

  <<windows-font-cache-workaround>>
  <<emacs-hugo-workaround>>
  <<set-bmw-org-variables>>
Hugo file lock workaround

Keep Hugo from complaining about the presence of Emacs file locks.

  ;; Hugo gets confused by Emacs lockfiles
  (setq-default create-lockfiles nil)

This isn't ideal — file locks are a good thing. OTOH I'm pretty much the only person on my system, so I have nobody but myself to blame if a missing file lock breaks something.

But without it?

  $ hugo
  Building sites … Built in 48 ms
  Error: Error building site: process: readAndProcessContent: walk: Readdir: decorate: lstat /home/random/Sites/rgb-hugo/content/post/2020/04/from-dotfiles-to-org-file/random@geekery.3137437:1588601341: no such file or directory

Still an open issue as of 2020-05-09, using Hugo version:

  Hugo Static Site Generator v0.69.2/extended linux/amd64 BuildDate: unknown
Inhibit compacting font caches on windows

Had a bad problem with performance on a Windows laptop — but one that didn't show up on my Windows desktop PC, oddly. Eventually narrowed it down to org-bullets, and then further to a known font cache issue that comes up when using org-bullets on Windows.

  (if (eq system-type 'windows-nt)
      (setq inhibit-compacting-font-caches t))

Fonts

Using VcXsrv as an X server on Windows, and it seems to be working okay. I'm still working on the finer details of variable-pitch-mode and Emacs fonts in general.

Current font choices:

Sitting in front of Pop!_OS at this split second, so here's the apt instructions.

sudo apt install fonts-ibm-plex

fc-list is the font-listing command I keep forgetting, not xls-fonts

Maybe someday automate stuff like font installation with Ansible or even just a shell script? Maybe, but that kinda depends on me knowing what distro or operating system I'm sitting in front of from one week to the next.

  (set-face-attribute 'default nil
                      :family "IBM Plex Mono"
                      :height 130
                      :weight 'normal
                      :width 'normal)

  (set-face-attribute 'fixed-pitch nil
                      :family "IBM Plex Mono"
                      :height 130
                      :weight 'normal
                      :width 'normal)

  (set-face-attribute 'variable-pitch nil
                      :family "IBM Plex Serif"
                      :height 160
                      :weight 'normal
                      :width 'normal)

  (use-package face-remap
    :custom-face
    (font-lock-keyword-face ((t (:inherit fixed-pitch :background nil))))
    (line-number ((t (:inherit fixed-pitch))))

    (whitespace-indentation ((t (:inherit error))))
    (whitespace-space ((t (:inherit shadow))))
    (whitespace-tab ((t (:inherit font-lock-comment-face)))))
Resources

Org mode

Org mode is why I use Emacs. So yeah this section's gonna be a little big.

Org-specific packages
Org bullets
  (use-package org-bullets
    :ensure t
    :custom
    (org-bullets-bullet-list '("◉" "○"))
    :hook (org-mode . org-bullets-mode))
Org variables for file organization

Because I move stuff around a lot, it's less work in the long run to define my org files by building up from this week's folder layout. In the end I explicitly specify which individual files belong in the agenda. This keeps me from "organizing" my tasks into a thousand different subtopic files. Only six.

So far.

Filename expansion works without fuss on Linux and Windows, so no need to be clever.

Well, you might want to define HOME for yourself in Windows, or Emacs guesses based on what Windows version you're running.

  (setq bmw/org-dir
        (expand-file-name "~/Dropbox/org/"))

  (setq bmw/org-agenda-dir (file-name-as-directory (concat bmw/org-dir "agendas")))
  (setq bmw/org-journal-dir (file-name-as-directory (concat bmw/org-dir "journals")))
  (setq bmw/org-templates-dir (file-name-as-directory (concat bmw/org-dir "templates")))
  (setq bmw/org-config-dir (file-name-as-directory (concat bmw/org-dir "config")))
  (setq bmw/org-site-dir (file-name-as-directory "~/Sites/rgb-hugo/org"))

  (setq
   bmw/org-main (concat bmw/org-agenda-dir "main.org")
   bmw/org-beorg-inbox (concat bmw/org-dir "inbox.org")
   bmw/org-journal (concat bmw/org-journal-dir "journal.org")
   bmw/org-money (concat bmw/org-dir "money.org")
   bmw/org-config-main (concat bmw/org-config-dir "emacs.org")
   bmw/org-ox-site (concat bmw/org-site-dir "site.org"))

  (setq bmw/org-agenda-files
        (list bmw/org-main
              bmw/org-journal
              bmw/org-beorg-inbox
              bmw/org-money
              bmw/org-config-main
              bmw/org-ox-site))
Capture templates
  (org-capture-templates
   (quote (("t" "Task")
           ("ts" "Site" entry
            (file+olp bmw/org-main "Tasks" "Site")
            (file "templates/generic-task.txt") :clock-in t :clock-resume t)
           ("tg" "General" entry
            (file+olp bmw/org-main "Tasks" "General")
            (file "templates/generic-task.txt") :clock-in t :clock-resume t)
           ("tn" "Now" entry
            (file+olp bmw/org-main "Tasks" "General")
            "* NOW %? \nDEADLINE: %t")
           ("ti" "Idea" entry
            (file+olp bmw/org-main "Tasks" "Ideas")
            (file "templates/idea.txt"))

           ("j" "Journal Entry" entry
            (file+datetree bmw/org-journal "Journal")
            (file "templates/journal.txt"))

           ("m" "Money")
           ("me" "Money Expense" entry
            (file+datetree bmw/org-money "Transactions")
            (file "templates/money-expense.txt"))
           ("mi" "Money Income" entry
            (file+datetree bmw/org-money "Transactions")
            (file "templates/money-income.txt")))))

I use org for config, note-taking, and to a lesser extent site management. That currently means I have org files scattered throughout my system. Some go to the cloud, some to version control. I need org-agenda to be grabby.

apply solution via Reddit. I tried putting it in :custom instead — without setq — but Emacs complained about org-agenda-file-regexp being undefined.

Main org section
  <<use-olivetti>>

  (use-package org
    :ensure org-plus-contrib
    :defer t
    :custom
    (org-agenda-files bmw/org-agenda-files)
    (org-babel-python-command "python3")
    (org-catch-invisible-edits 'show-and-error)
    (org-clock-persist t)
    (org-confirm-babel-evaluate nil)
    (org-display-inline-images t)
    (org-edit-src-content-indentation 2)
    (org-directory bmw/org-dir)
    (org-ellipsis "⬎")
    (org-enforce-todo-dependencies t)
    (org-fontify-quote-and-verse-blocks t)
    (org-log-into-drawer t)
    (org-log-done 'time)
    (org-log-redeadline 'time)
    (org-outline-path-complete-in-steps nil)
    (org-redisplay-inline-images t)
    (org-refile-targets '((org-agenda-files :maxlevel . 6)))
    (org-refile-use-outline-path 'file)
    (org-log-reschedule 'time)
    (org-src-fontify-natively t)
    (org-src-tab-acts-natively t)
    (org-startup-indented t)
    (org-startup-with-inline-images "inlineimages")

    <<define-org-capture-templates>>

    ;; just in case it's not set in the org file
    (org-todo-keywords
     '((type "TASK(t)" "NOW(n!)" "|" "DONE(d!)")      ; the main flow
       (type "MAYBE(m) WAIT(w)" "|" "CANCEL(c@)")))  ; common branches

    :init
    (global-set-key (kbd "C-c l") 'org-store-link)
    (global-set-key (kbd "C-c c") 'org-capture)
    (global-set-key (kbd "C-c a") 'org-agenda)
    (advice-add 'org-archive-default-command :after #'org-save-all-org-buffers)

    :config
    (turn-off-auto-fill)
    (setq-local display-line-numbers nil)
    (setq-local org-fontify-whole-heading-line t)
    (setq-local org-fontify-done-headline t)

    <<load-babel-eval-languages>>
    (org-clock-persistence-insinuate))
Custom faces
  (org-table ((t (:inherit fixed-pitch))))
  (org-block ((t (:inherit fixed-pitch))))
  (org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))))
  (org-level-1 ((t (:background nil :weight bold :family "IBM Plex Serif" :height 1.5))))
  (org-level-2 ((t (:background nil :weight bold :family "IBM Plex Serif" :height 1.4))))
  (org-level-3 ((t (:background nil :weight bold :family "IBM Plex Serif" :height 1.3))))
  (org-level-4 ((t (:background nil :weight bold :family "IBM Plex Serif" :height 1.2))))
  (org-level-5 ((t (:background nil :weight bold :family "IBM Plex Serif" :height 1.1))))
  (org-level-6 ((t (:background nil :weight bold :family "IBM Plex Serif" :height 160))))
Babel language support
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((dot , t)
     (gnuplot . t)
     (shell . t)
     (ruby . t)
     (R . t)
     (python . t)
     (sql . t)
     (sqlite . t)
     (perl . t)))

Perl

Initially just extracting interesting bits from genehack's perl.el.

  <<use-perltidy>>
  <<use-cperl-mode>>
  (use-package cperl-mode
    :ensure t
    :defer t
    :custom
    <<cperl-custom-settings>>
    :interpreter "perl"
    :mode "\\.\\(cgi\\|psgi\\|t\\)\\'")
  (defalias 'perl-mode 'cperl-mode)
Customized variables

Using :custom for anything that I could have set via Emacs Customization. #+name:cperl-custom-settings

  ;; automatic indent after semicolon insertion
  (cperl-autoindent-on-semi nil)

  ;; automatically newline before braces, and after braces, colons, and semi-colons
  (cperl-auto-newline nil)

  ;; "not overwrite C-h f"?
  (cperl-clobber-lisp-bindings t)

  ;; extra indent for substatements that start with close parens
  (cperl-close-paren-offset -2)

  ;; extra indent for lines not starting new statements
  (cperl-continued-statement-offset 2)

  ;; keywords are electric in cperl-mode
  (cperl-electric-keywords nil)

  ;; { after $ should be preceded by a space(?)
  (cperl-electric-lbrace-space nil)

  ;; linefeed should be hairy in cperl (otherwise `C-c <return>')
  (cperl-electric-linefeed t)

  ;; parens should be electric in cperl-mode
  (cperl-electric-parens nil)

  ;; use font-lock-mode in cperl buffers
  (cperl-font-lock t)

  ;; perform additional highlighting on variables (currently just scalars)
  (cperl-highlight-variables-indiscriminately t)

  ;; indentation of cperl statements relative to containing block (default 2)
  (cperl-indent-level 2)

  ;; non-block (), {}, and [] (without trailing `,') are indented as blocks
  (cperl-indent-parens-as-block t)

  ;; amount of space to insert between `}' and `else' or `elsif'
  (cperl-indent-region-fix-constructs 1)

  ;; skip prompts on C-h f
  (cperl-info-on-command-no-prompt t)

  ;; "result of evaluation of this expression highlights trailing whitespace"
  (cperl-invalid-face nil)

  ;; show lazy help after given idle time
  (cperl-lazy-help-time 5)

  ;; always reindent the current line on <tab>
  (cperl-tab-always-indent t)
Formatting

Couldn't find a "perltidy" package. I downloaded my own copy of perltidy.el. Might make more sense to check it out from the wiki git repo.

  (require 'tramp)
  (require 'perltidy)

  (if (eq system-type 'windows-nt)
      (setq-default perltidy-program "perltidy.bat"))

  (add-hook 'cperl-mode-hook (lambda ()
                               (local-set-key (kbd "C-c t") 'perltidy-dwim)))

Packages

Color handling
  (use-package color)

  (defun bmw/color-dim (steps)
    (apply 'color-rgb-to-hex
           (car (color-gradient
                 (color-name-to-rgb (face-attribute 'default :background))
                 (color-name-to-rgb (face-attribute 'default :foreground))
                 steps))))
Exec path

Need to get Emacs starting happy under windows-nt before I get back to borrowing from the system environment.

  ;; ensure environment in Emacs is environment in shell
  (if (not (eq system-type 'windows-nt))
      (use-package exec-path-from-shell
        :ensure t
        :config
        (setq exec-path-from-shell-check-startup-files nil
              exec-path-from-shell-variables
              '("PLENV_ROOT"
                "PATH" "MAN_PATH"
                "RAKUBREW_HOME"))
        (exec-path-from-shell-initialize)))
Asciidoctor
  (use-package adoc-mode
    :ensure t
    :mode "\\.adoc")
Crystal programming
  ;; Development with the Crystal language
  (use-package crystal-mode
    :ensure t
    :mode ("\\.cr$" . crystal-mode)
    :interpreter ("crystal" . crystal-mode)
    :config
    (autoload 'crystal-mode "crystal-mode" "Major mode for Crystal files" t))

  (use-package ob-crystal
    :ensure t)
Deft for notes

https://jblevins.org/projects/deft/

  (use-package deft
    :ensure t
    :init
    (setq deft-extensions '("md" "org" "txt"))
    :config
    (setq deft-markdown-mode-title-level 1)
    (setq deft-use-filename-as-title t)
    (setq deft-file-naming-rules
          '((noslash . "-")
            (nospace . "-")
            (case-fn . downcase)))
    (setq deft-directory "~/Dropbox/org/notes"))
Elscreen for multiplexing (tabs)
  ;; elscreen for sessions
  (use-package elscreen
    :ensure t
    :init
    (elscreen-start))
Files

Keeping backups somewhere besides the current folder to reduce Hugo's confusion. It helps, but see also Hugo file lock workaround.

  ;; locks and autosaves all over the place just annoy me
  ;; TODO: set up proper autosave
  (use-package files
    :custom
    (auto-save-default nil)
    (auto-save-file-name-transforms `((".*" ,(concat user-emacs-directory "autosaves"))))
    (backup-directory-alist `(("." . ,(concat user-emacs-directory "backups")))))
gnuplot
  (use-package gnuplot
    :ensure t)
Goto address
  (use-package goto-addr
    :ensure t
    :hook
    ((text-mode prog-mode) . goto-address-mode))
Lua
  (use-package lua-mode
    :ensure t
    :mode ("\\.lua\\'" "\\.luac\\'")
    :config (setq lua-indent-level 2))
Markdown
  ;; markdown-mode
  (use-package markdown-mode
    :ensure t
    :mode (("README\\.md\\'" . gfm-mode)
           ("\\.md\\'" . markdown-mode)
           ("\\.markdown\\'" . markdown-mode))
    :init (setq markdown-command "mmark"))
Mixed Pitch Mode

Really curious to see how mixed-pitch-mode compares to poet, and if I can mix it with solarized.

  (use-package mixed-pitch
    :ensure t
    :custom
    (mixed-pitch-set-height t)

    :hook
    (text-mode . mixed-pitch-mode))
Olivetti

for bigger margins

  (use-package olivetti
    :ensure t
    :custom
    (olivetti-body-width bmw/line-length)
    :hook (text-mode . olivetti-mode))
Pyenv

Why oh why did I pick a version manager for every language I use? Seemed like such a great idea when everything was tmux. Oh well. Most of this pyenv stuff I grabbed from Setup Emacs for Python development using Pyenv by Rakan Al-Hneiti.

  (defun pyenv-activate-current-project ()
    "Automatically activates pyenv version if .python-version file exists."
    (interactive)
    (let ((python-version-directory (locate-dominating-file (buffer-file-name) ".python-version")))
      (if python-version-directory
          (let* ((pyenv-version-path (f-expand ".python-version" python-version-directory))
                 (pyenv-current-version (s-trim (f-read-text pyenv-version-path 'utf-8))))
            (pyenv-mode-set pyenv-current-version)
            (message (concat "Setting virtualenv to " pyenv-current-version))))))

  (defvar pyenv-current-version nil nil)

  (defun pyenv-init()
    "Initialize pyenv's current version to the global one."
    (let ((global-pyenv (replace-regexp-in-string "\n" "" (shell-command-to-string "pyenv global"))))
      (message (concat "Setting pyenv version to " global-pyenv))
      (pyenv-mode-set global-pyenv)
      (setq pyenv-current-version global-pyenv)))

  (add-hook 'after-init-hook 'pyenv-init)

  (use-package pyenv-mode
    :ensure t
    :init
    (add-to-list 'exec-path "~/.pyenv/shims")
    (setenv "WORKON_HOME" "~/.pyenv/versions")
    :config
    (pyenv-mode)
    :bind
    ("C-x p e" . pyenv-activate-current-project))
MAYBE Pyenv and windows?

I'm getting a bit uncertain about pyenv in the first place, and with me trying to using Emacs in windows-nt I'm really getting uncertain. Maybe just explore Elpy for a bit — see if there's anything in there about venv.

Markup faces (for adoc)

I fiddled with these so adoc-mode would be more aesthetically appealing to me.

  (use-package markup-faces
    :config
    (set-face-attribute 'markup-title-0-face nil :height 1.0)
    (set-face-attribute 'markup-title-1-face nil :height 1.0)
    (set-face-attribute 'markup-title-2-face nil :height 1.0)
    (set-face-attribute 'markup-title-3-face nil :height 1.0)
    (set-face-attribute 'markup-title-4-face nil :height 1.0)
    (set-face-attribute 'markup-title-5-face nil :height 1.0)
    (set-face-attribute 'markup-secondary-text-face nil :height 1.0)
    (set-face-attribute 'markup-meta-face nil
                        :height 1.0
                        :foreground (face-attribute 'font-lock-comment-face :foreground))
    (set-face-attribute 'markup-meta-hide-face nil
                        :height 1.0
                        :foreground (face-attribute 'font-lock-comment-face :foreground))
    (set-face-attribute 'markup-list-face nil
                        :background (face-attribute 'font-lock-builtin-face :background)
                        :foreground (face-attribute 'font-lock-builtin-face :foreground))
    (set-face-attribute 'markup-table-face nil
                        :background (face-attribute 'font-lock-builtin-face :background)
                        :foreground (face-attribute 'font-lock-builtin-face :foreground))
    (set-face-attribute 'markup-verbatim-face nil
                        :background (face-attribute 'font-lock-string-face :background)
                        :foreground (face-attribute 'font-lock-string-face :foreground)))

I've experimented with my own settings, but so far poet is better than what I came up with.

Whitespace
  (defun bmw/theme-whitespace ()
    "Apply my own face-attribute changes after loading a custom theme"
    (set-face-attribute 'whitespace-space nil
                        :background (face-attribute 'font-lock-comment-face :background)
                        :foreground (bmw/color-dim 3)))
  <<whitespace-face-attributes>>

  (use-package whitespace
    :ensure t
    :preface
    (defun bmw/whitespace-mode ()
      (unless (eq major-mode 'org-mode)
        (progn
          (whitespace-mode)
          (bmw/theme-whitespace))))
    :custom
    (whitespace-action '(auto-cleanup))
    (whitespace-line-column bmw/line-length)
    (whitespace-style
     '(face lines trailing empty tabs spaces indentation space-mark tab-mark))
    :hook
    ((prog-mode text-mode) . bmw/whitespace-mode))
Writegood mode
  ;; writegood-mode
  (use-package writegood-mode
    :ensure t
    :config
    (global-set-key (kbd "C-c g") 'writegood-mode)
    (add-hook 'text-mode-hook 'writegood-mode))
Yaml Mode
  (use-package yaml-mode
    :ensure t)
Web Mode

Need more than stock HTML for Hugo templates.

  (use-package web-mode
    :ensure t
    :mode (("\\.html?\\'" . web-mode)))

Themes

I bounce between themes a lot.

Startup theme selector

Make some non-standard themes available, then load whatever today's favorite theme is.

  (defadvice load-theme (before clear-previous-themes activate)
    "Clear existing theme settings instead of layering them"
    (mapc #'disable-theme custom-enabled-themes))

  <<use-mixed-pitch>>
  <<use-color-theme-modern>>
More color themes

With mixed-pitch-mode and friends taking care of font sizing, most theme handling is done with replace-colorthemes, which is in MELPA as "color-theme-modern."

  (use-package color-theme-modern
    :ensure t
    :config
    (load-theme 'classic t t)
    (enable-theme 'classic))

Random bits of functionality

Quick-edit config
  (global-set-key (kbd "<f5>")
                  (lambda ()
                    (interactive)
                    (find-file bmw/org-config-main)
                    (message "Opened: %s" (buffer-name))))
Timestamps

Generate ISO-style timestamps with UTC offset, mainly for usage in front matter for site content.

  (defun bmw/timestamp ()
    (concat
     (format-time-string "%Y-%m-%d %T")
     ((lambda (x) (concat (substring x 0 3) ":" (substring x 3 5)))
      (format-time-string "%z"))))

Awesomewm   awesomewm

:properties: :export_file_name: awesomewm :export_description: It's as nice as you make it; mine has a ways to go :export_hugo_weight: 10

Pop Shell is nice, but it ain't awesome.

My starting rc.lua wasn't much different from the stock default.rc.lua. To break it up for tangling I just went with the sections already defined in default.rc.lua's comment blocks.

Trying out awesome-wm-widgets, which in turn needs the Arc Icon Theme.

git clone https://github.com/horst3180/arc-icon-theme --depth 1 && cd arc-icon-theme
./autogen.sh --prefix=/usr
sudo make install

git clone git@github.com:streetturtle/awesome-wm-widgets.git ~/.config/awesome/awesome-wm-widgets

Main rc.lua file

Expected executables:

  <<awesome-quit>>
  <<awesome/load-libraries>>

  <<handle-errors>>
  <<define-variables>>
  <<create-menu>>

  -- Keyboard map indicator and switcher
  mykeyboardlayout = awful.widget.keyboardlayout()

  <<create-wibar>>
  <<create-mouse-bindings>>
  <<create-key-bindings>>
  <<define-rules>>
  <<define-signals>>

  -- {{{ Autorun
    awful.spawn.with_shell("~/.config/awesome/autorun.sh")
  -- }}}
Custom Quit

Override awesome.quit when we're using GNOME

  _awesome_quit = awesome.quit

  awesome.quit = function()
    if os.getenv("DESKTOP_SESSION") == "awesome-gnome" then
      os.execute("/usr/bin/gnome-session-quit")
      -- os.execute("pkill -9 gnome-session")
    else
      _awesome_quit()
    end
  end
Awesome Libraries

Standard

-- If LuaRocks is installed, make sure that packages installed through it are
-- found (e.g. lgi). If LuaRocks is not installed, do nothing.
pcall(require, "luarocks.loader")

-- Standard awesome library
local gears = require("gears")
local awful = require("awful")
require("awful.autofocus")
-- Widget and layout library
local wibox = require("wibox")
-- Theme handling library
local beautiful = require("beautiful")
-- Notification library
local naughty = require("naughty")
local menubar = require("menubar")
local hotkeys_popup = require("awful.hotkeys_popup")
-- Enable hotkeys help widget for VIM and other apps
-- when client with a matching name is opened:
require("awful.hotkeys_popup.keys")

local has_fdo, freedesktop = pcall(require, "freedesktop")
<<awesome/load-awesome-widgets>>
local xrandr = require("recipes.xrandr")
load awesome widgets

Require widgets being used from Awesome WM Widgets.

local cpu_widget = require("awesome-wm-widgets.cpu-widget.cpu-widget")
local volume_widget = require("awesome-wm-widgets.volume-widget.volume")
local calendar_widget = require("awesome-wm-widgets.calendar-widget.calendar")
Error handling
  -- {{{ Error handling
  -- Check if awesome encountered an error during startup and fell back to
  -- another config (This code will only ever execute for the fallback config)
  if awesome.startup_errors then
    naughty.notify({ preset = naughty.config.presets.critical,
                     title = "Oops, there were errors during startup!",
                     text = awesome.startup_errors })
  end

  -- Handle runtime errors after startup
  do
    local in_error = false
    awesome.connect_signal("debug::error", function (err)
                             -- Make sure we don't go into an endless error loop
                             if in_error then return end
                             in_error = true

                             naughty.notify({ preset = naughty.config.presets.critical,
                                              title = "Oops, an error happened!",
                                              text = tostring(err) })
                             in_error = false
    end)
  end
  -- }}}
Variable definitions
<<awesome/define-theme>>
<<awesome/define-tools>>
<<awesome/define-modkey>>
<<awesome/define-layouts>>
Define theme

Themes define colours, icons, font and wallpapers.

local xresources = require("beautiful.xresources")
local dpi = xresources.apply_dpi
beautiful.init(gears.filesystem.get_themes_dir() .. "zenburn/theme.lua")

theme = beautiful.get()
theme.menu_height = dpi(25)
theme.menu_width = dpi(200)
-- theme.font = "Droid Sans 10"
-- theme.title_font = "Droid Sans 12"
theme.useless_gap = 10

theme.widget_main_color = "#74aeab"
theme.widget_red = "#e53935"
theme.widget_yelow = "#c0ca33"
theme.widget_green = "#43a047"
theme.widget_black = "#000000"
theme.widget_transparent = "#00000000"
Define modkey

Usually, Mod4 is the key with a logo between Control and Alt.

modkey = "Mod4"
Define tools

Preferred terminal, editor, and means for editing config.

terminal = "/home/random/.local/kitty.app/bin/kitty"
editor = os.getenv("EDITOR") or "vim"
editor_cmd = terminal .. " -e " .. editor
Define layouts

Table of layouts to cover with awful.layout.inc, order matters.

awful.layout.layouts = {
    awful.layout.suit.floating,
    awful.layout.suit.tile,
    awful.layout.suit.tile.left,
    awful.layout.suit.tile.bottom,
    awful.layout.suit.tile.top,
    awful.layout.suit.fair,
    awful.layout.suit.fair.horizontal,
    awful.layout.suit.spiral,
    awful.layout.suit.spiral.dwindle,
    awful.layout.suit.max,
    awful.layout.suit.max.fullscreen,
    awful.layout.suit.magnifier,
    awful.layout.suit.corner.nw,
    -- awful.layout.suit.corner.ne,
    -- awful.layout.suit.corner.sw,
    -- awful.layout.suit.corner.se,
}
Menu
  -- {{{ Menu
  -- Create a launcher widget and a main menu
  myawesomemenu = {
    { "hotkeys", function() hotkeys_popup.show_help(nil, awful.screen.focused()) end },
    { "manual", terminal .. " -e man awesome" },
    { "edit config", editor_cmd .. " " .. awesome.conffile },
    { "restart", awesome.restart },
    { "quit", function() awesome.quit() end },
  }

  local menu_awesome = { "awesome", myawesomemenu, beautiful.awesome_icon }
  local menu_terminal = { "open terminal", terminal }

  if has_fdo then
    mymainmenu = freedesktop.menu.build({
        before = { menu_awesome },
        after = { menu_terminal }
    })
  else
    mymainmenu = awful.menu({
        items = {
          menu_awesome,
          menu_terminal,
        }
    })
  end

  mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
                                       menu = mymainmenu })

  -- Menubar configuration
  menubar.utils.terminal = terminal -- Set the terminal for applications that require it
Wibar
<<awesome/create-clockcalendar>>

-- Create a wibox for each screen and add it
<<awesome/create-taglist-buttons>>
<<awesome/create-tasklist-buttons>>
<<awesome/set-wallpaper>>
awful.screen.connect_for_each_screen(function(s)
    set_wallpaper(s)
    -- Each screen has its own tag table.
    awful.tag({ "1", "2", "3", "4" }, s, awful.layout.layouts[2])

    -- Create a promptbox for each screen
    s.mypromptbox = awful.widget.prompt()
    -- Create an imagebox widget which will contain an icon indicating which layout we're using.
    -- We need one layoutbox per screen.
    s.mylayoutbox = awful.widget.layoutbox(s)
    s.mylayoutbox:buttons(
      gears.table.join(
        awful.button({ }, 1, function () awful.layout.inc( 1) end),
        awful.button({ }, 3, function () awful.layout.inc(-1) end),
        awful.button({ }, 4, function () awful.layout.inc( 1) end),
        awful.button({ }, 5, function () awful.layout.inc(-1) end)))
    -- Create a taglist widget
    s.mytaglist = awful.widget.taglist {
      screen  = s,
      filter  = awful.widget.taglist.filter.all,
      buttons = taglist_buttons,
      style = {
        shape_border_width = 1,
        shape_border_color = '#777777',
        shape = gears.shape.powerline,
      },
      widget_template = {
        {
          {
            {
              {
                id     = 'icon_role',
                widget = wibox.widget.imagebox,
              },
              margins = 2,
              widget  = wibox.container.margin,
            },
            {
              id     = 'text_role',
              widget = wibox.widget.textbox,
            },
            layout = wibox.layout.fixed.horizontal,
          },
          left  = 10,
          right = 10,
          widget = wibox.container.margin
        },
        id     = 'background_role',
        widget = wibox.container.background,
      },
    }

    -- Create a tasklist widget
    s.mytasklist = awful.widget.tasklist {
      screen  = s,
      filter  = awful.widget.tasklist.filter.currenttags,
      buttons = tasklist_buttons,
      style = {
        shape_border_width = 1,
        shape_border_color = '#777777',
        shape = gears.shape.powerline,
      },
      layout = {
        spacing = 5,
        layout = wibox.layout.fixed.horizontal
      },
      widget_template = {
        {
          {
            {
              {
                id     = 'icon_role',
                widget = wibox.widget.imagebox,
              },
              margins = 2,
              widget  = wibox.container.margin,
            },
            {
              id     = 'text_role',
              widget = wibox.widget.textbox,
            },
            layout = wibox.layout.fixed.horizontal,
          },
          left  = 10,
          right = 10,
          widget = wibox.container.margin
        },
        id     = 'background_role',
        widget = wibox.container.background,
      },
    }

    <<awesome/screen-wibox>>
end)
Create clock/calendar

A text clock that opens a pretty calendar when clicked.

mytextclock = wibox.widget.textclock()
local cw = calendar_widget({placement = 'top_right'})
mytextclock:connect_signal(
  "button::press",
  function(_, _, _, button)
    if button == 1 then cw.toggle() end
  end
)
Create taglist buttons
local taglist_buttons = gears.table.join(
  awful.button({ }, 1, function(t) t:view_only() end),
  awful.button({ modkey }, 1, function(t)
      if client.focus then
        client.focus:move_to_tag(t)
      end
  end),
  awful.button({ }, 3, awful.tag.viewtoggle),
  awful.button({ modkey }, 3, function(t)
      if client.focus then
        client.focus:toggle_tag(t)
      end
  end),
  awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
  awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)
)
Create tasklist buttons
local tasklist_buttons = gears.table.join(
  awful.button({ }, 1, function (c)
      if c == client.focus then
        c.minimized = true
      else
        c:emit_signal(
          "request::activate",
          "tasklist",
          {raise = true}
        )
      end
  end),
  awful.button({ }, 3, function()
      awful.menu.client_list({ theme = { width = 250 } })
  end),
  awful.button({ }, 4, function ()
      awful.client.focus.byidx(1)
  end),
  awful.button({ }, 5, function ()
      awful.client.focus.byidx(-1)
end))
Set wallpaper
local function set_wallpaper(s)
  -- Wallpaper
  if beautiful.wallpaper then
    local wallpaper = beautiful.wallpaper
    -- If wallpaper is a function, call it with the screen
    if type(wallpaper) == "function" then
      wallpaper = wallpaper(s)
    end
    gears.wallpaper.maximized(wallpaper, s, true)
  end
end

-- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
screen.connect_signal("property::geometry", set_wallpaper)
Create screen wibox
-- Create the wibox
s.mywibox = awful.wibar({ position = "top", screen = s })

-- Add widgets to the wibox
s.mywibox:setup {
  layout = wibox.layout.align.horizontal,
  { -- Left widgets
    layout = wibox.layout.fixed.horizontal,
    mylauncher,
    s.mytaglist,
    s.mypromptbox,
  },
  s.mytasklist, -- Middle widget
  { -- Right widgets
    layout = wibox.layout.fixed.horizontal,
    spacing = 10,
    wibox.widget.systray(),
    mykeyboardlayout,
    cpu_widget(),
    volume_widget({display_notification = true}),
    mytextclock,
    s.mylayoutbox,
  },
}
Mouse bindings
  -- {{{ Mouse bindings
  root.buttons(gears.table.join(
                 awful.button({ }, 3, function () mymainmenu:toggle() end),
                 awful.button({ }, 4, awful.tag.viewnext),
                 awful.button({ }, 5, awful.tag.viewprev)
  ))
  -- }}}
Key bindings
  globalkeys = gears.table.join(
    awful.key({ modkey,           }, "s",      hotkeys_popup.show_help,
      {description="show help", group="awesome"}),
    awful.key({ modkey,           }, "Left",   awful.tag.viewprev,
      {description = "view previous", group = "tag"}),
    awful.key({ modkey,           }, "Right",  awful.tag.viewnext,
      {description = "view next", group = "tag"}),
    awful.key({ modkey,           }, "Escape", awful.tag.history.restore,
      {description = "go back", group = "tag"}),

    awful.key({ modkey,           }, "j",
      function ()
        awful.client.focus.byidx( 1)
      end,
      {description = "focus next by index", group = "client"}
    ),
    awful.key({ modkey,           }, "k",
      function ()
        awful.client.focus.byidx(-1)
      end,
      {description = "focus previous by index", group = "client"}
    ),
    awful.key({ modkey,           }, "w", function () mymainmenu:show() end,
      {description = "show main menu", group = "awesome"}),

    -- Layout manipulation
    awful.key({ modkey, "Shift"   }, "j", function () awful.client.swap.byidx(  1)    end,
      {description = "swap with next client by index", group = "client"}),
    awful.key({ modkey, "Shift"   }, "k", function () awful.client.swap.byidx( -1)    end,
      {description = "swap with previous client by index", group = "client"}),
    awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end,
      {description = "focus the next screen", group = "screen"}),
    awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end,
      {description = "focus the previous screen", group = "screen"}),
    awful.key({ modkey,           }, "u", awful.client.urgent.jumpto,
      {description = "jump to urgent client", group = "client"}),
    awful.key({ modkey,           }, "Tab",
      function ()
        awful.client.focus.history.previous()
        if client.focus then
          client.focus:raise()
        end
      end,
      {description = "go back", group = "client"}),



    -- Standard program
    awful.key({ modkey,           }, "Return", function () awful.spawn(terminal) end,
      {description = "open a terminal", group = "launcher"}),
    awful.key({ modkey, "Control" }, "r", awesome.restart,
      {description = "reload awesome", group = "awesome"}),
    awful.key({ modkey, "Shift"   }, "q", awesome.quit,
      {description = "quit awesome", group = "awesome"}),

    awful.key({ modkey,           }, "l",     function () awful.tag.incmwfact( 0.05)          end,
      {description = "increase master width factor", group = "layout"}),
    awful.key({ modkey,           }, "h",     function () awful.tag.incmwfact(-0.05)          end,
      {description = "decrease master width factor", group = "layout"}),
    awful.key({ modkey, "Shift"   }, "h",     function () awful.tag.incnmaster( 1, nil, true) end,
      {description = "increase the number of master clients", group = "layout"}),
    awful.key({ modkey, "Shift"   }, "l",     function () awful.tag.incnmaster(-1, nil, true) end,
      {description = "decrease the number of master clients", group = "layout"}),
    awful.key({ modkey, "Control" }, "h",     function () awful.tag.incncol( 1, nil, true)    end,
      {description = "increase the number of columns", group = "layout"}),
    awful.key({ modkey, "Control" }, "l",     function () awful.tag.incncol(-1, nil, true)    end,
      {description = "decrease the number of columns", group = "layout"}),
    awful.key({ modkey,           }, "space", function () awful.layout.inc( 1)                end,
      {description = "select next", group = "layout"}),
    awful.key({ modkey, "Shift"   }, "space", function () awful.layout.inc(-1)                end,
      {description = "select previous", group = "layout"}),

    awful.key({ modkey, "Control" }, "n",
      function ()
        local c = awful.client.restore()
        -- Focus restored client
        if c then
          c:emit_signal(
            "request::activate", "key.unminimize", {raise = true}
          )
        end
      end,
      {description = "restore minimized", group = "client"}),

    -- Prompt
    awful.key({ modkey },            "r",     function () awful.screen.focused().mypromptbox:run() end,
      {description = "run prompt", group = "launcher"}),

    -- Menubar
    awful.key({ modkey }, "p", function() menubar.show() end,
      {description = "show the menubar", group = "launcher"})
  )

  clientkeys = gears.table.join(
    -- move windows with Mod4+Shift+arrow keys
    awful.key({ modkey, "Shift"   }, "Down",
      function (c) c:relative_move(  0,  20,   0,   0) end,
      {description = "move down", group = "client"}),
    awful.key({ modkey, "Shift"   }, "Up",
      function (c) c:relative_move(  0, -20,   0,   0) end,
      {description = "move up", group = "client"}),
    awful.key({ modkey, "Shift"   }, "Left",
      function (c) c:relative_move(-20,   0,   0,   0) end,
      {description = "move left", group = "client"}),
    awful.key({ modkey, "Shift"   }, "Right",
      function (c) c:relative_move( 20,   0,   0,   0) end,
      {description = "move right", group = "client"}),

    awful.key({ modkey,           }, "f",
      function (c)
        c.fullscreen = not c.fullscreen
        c:raise()
      end,
      {description = "toggle fullscreen", group = "client"}),
    awful.key({ modkey, "Shift"   }, "c",      function (c) c:kill()                         end,
      {description = "close", group = "client"}),
    awful.key({ modkey, "Control" }, "space",  awful.client.floating.toggle                     ,
      {description = "toggle floating", group = "client"}),
    awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
      {description = "move to master", group = "client"}),
    awful.key({ modkey,           }, "o",      function (c) c:move_to_screen()               end,
      {description = "move to screen", group = "client"}),
    awful.key({ modkey,           }, "t",      function (c) c.ontop = not c.ontop            end,
      {description = "toggle keep on top", group = "client"}),
    awful.key({ modkey,           }, "n",
      function (c)
        -- The client currently has the input focus, so it cannot be
        -- minimized, since minimized clients can't have the focus.
        c.minimized = true
      end ,
      {description = "minimize", group = "client"}),
    awful.key({ modkey,           }, "m",
      function (c)
        c.maximized = not c.maximized
        c:raise()
      end ,
      {description = "(un)maximize", group = "client"}),
    awful.key({ modkey, "Control" }, "m",
      function (c)
        c.maximized_vertical = not c.maximized_vertical
        c:raise()
      end ,
      {description = "(un)maximize vertically", group = "client"}),
    awful.key({ modkey, "Shift"   }, "m",
      function (c)
        c.maximized_horizontal = not c.maximized_horizontal
        c:raise()
      end ,
      {description = "(un)maximize horizontally", group = "client"})
  )

  -- Bind all key numbers to tags.
  -- Be careful: we use keycodes to make it work on any keyboard layout.
  -- This should map on the top row of your keyboard, usually 1 to 9.
  for i = 1, 4 do
    globalkeys = gears.table.join(
      globalkeys,
      -- View tag only.
      awful.key({ modkey }, i,
        function ()
          local screen = awful.screen.focused()
          local tag = screen.tags[i]
          if tag then
            tag:view_only()
          end
        end,
        {description = "view tag #"..i, group = "tag"}),
      -- Toggle tag display.
      awful.key({ modkey, "Control" }, i,
        function ()
          local screen = awful.screen.focused()
          local tag = screen.tags[i]
          if tag then
            awful.tag.viewtoggle(tag)
          end
        end,
        {description = "toggle tag #" .. i, group = "tag"}),
      -- Move client to tag.
      awful.key({ modkey, "Shift" }, i,
        function ()
          if client.focus then
            local tag = client.focus.screen.tags[i]
            if tag then
              client.focus:move_to_tag(tag)
            end
          end
        end,
        {description = "move focused client to tag #"..i, group = "tag"}),
      -- Toggle tag on focused client.
      awful.key({ modkey, "Control", "Shift" }, i,
        function ()
          if client.focus then
            local tag = client.focus.screen.tags[i]
            if tag then
              client.focus:toggle_tag(tag)
            end
          end
        end,
        {description = "toggle focused client on tag #" .. i, group = "tag"})
    )
  end

<<awesome/enable-xrandr-switch>>
<<awesome/set-media-keys>>

  clientbuttons = gears.table.join(
    awful.button({ }, 1, function (c)
        c:emit_signal("request::activate", "mouse_click", {raise = true})
    end),
    awful.button({ modkey }, 1, function (c)
        c:emit_signal("request::activate", "mouse_click", {raise = true})
        awful.mouse.client.move(c)
    end),
    awful.button({ modkey }, 3, function (c)
        c:emit_signal("request::activate", "mouse_click", {raise = true})
        awful.mouse.client.resize(c)
    end)
  )

  -- Set keys
  root.keys(globalkeys)
Lua execute prompt
  awful.key({ modkey }, "x",
    function ()
      awful.prompt.run {
        prompt       = "Run Lua code: ",
        textbox      = awful.screen.focused().mypromptbox.widget,
        exe_callback = awful.util.eval,
        history_path = awful.util.get_cache_dir() .. "/history_eval"
      }
    end,
    {description = "lua execute prompt", group = "awesome"})
Rules
  -- {{{ Rules
  -- Rules to apply to new clients (through the "manage" signal).
  awful.rules.rules = {
    -- All clients will match this rule.
    { rule = { },
      properties = { border_width = beautiful.border_width,
                     border_color = beautiful.border_normal,
                     focus = awful.client.focus.filter,
                     raise = true,
                     keys = clientkeys,
                     buttons = clientbuttons,
                     screen = awful.screen.preferred,
                     placement = awful.placement.no_overlap+awful.placement.no_offscreen
      }
    },

    -- Floating clients.
    { rule_any = {
        instance = {
          "DTA",  -- Firefox addon DownThemAll.
          "copyq",  -- Includes session name in class.
          "pinentry",
        },
        class = {
          "Arandr",
          "Blueman-manager",
          "Gpick",
          "Kruler",
          "MessageWin",  -- kalarm.
          "Sxiv",
          "Tor Browser", -- Needs a fixed window size to avoid fingerprinting by screen size.
          "Wpa_gui",
          "veromix",
          "xtightvncviewer"},

        -- Note that the name property shown in xprop might be set slightly after creation of the client
        -- and the name shown there might not match defined rules here.
        name = {
          "Event Tester",  -- xev.
        },
        role = {
          "AlarmWindow",  -- Thunderbird's calendar.
          "ConfigManager",  -- Thunderbird's about:config.
          "pop-up",       -- e.g. Google Chrome's (detached) Developer Tools.
        }
    }, properties = { floating = true }},

    -- Add titlebars to normal clients and dialogs
    { rule_any = {type = { "normal", "dialog" }
                 }, properties = { titlebars_enabled = true }
    },

    -- Set Firefox to always map on the tag named "2" on screen 1.
    -- { rule = { class = "Firefox" },
    --   properties = { screen = 1, tag = "2" } },
  }
  -- }}}
Signals
  -- {{{ Signals
  -- Signal function to execute when a new client appears.
  client.connect_signal("manage", function (c)
                          -- Set the windows at the slave,
                          -- i.e. put it at the end of others instead of setting it master.
                          -- if not awesome.startup then awful.client.setslave(c) end

                          if awesome.startup
                            and not c.size_hints.user_position
                          and not c.size_hints.program_position then
                            -- Prevent clients from being unreachable after screen count changes.
                            awful.placement.no_offscreen(c)
                          end
  end)

  -- Add a titlebar if titlebars_enabled is set to true in the rules.
  client.connect_signal("request::titlebars", function(c)
                          -- buttons for the titlebar
                          local buttons = gears.table.join(
                            awful.button({ }, 1, function()
                                c:emit_signal("request::activate", "titlebar", {raise = true})
                                awful.mouse.client.move(c)
                            end),
                            awful.button({ }, 3, function()
                                c:emit_signal("request::activate", "titlebar", {raise = true})
                                awful.mouse.client.resize(c)
                            end)
                          )

                          awful.titlebar(c) : setup {
                            { -- Left
                              awful.titlebar.widget.iconwidget(c),
                              buttons = buttons,
                              layout  = wibox.layout.fixed.horizontal
                            },
                            { -- Middle
                              { -- Title
                                align  = "center",
                                widget = awful.titlebar.widget.titlewidget(c)
                              },
                              buttons = buttons,
                              layout  = wibox.layout.flex.horizontal
                            },
                            { -- Right
                              awful.titlebar.widget.floatingbutton (c),
                              awful.titlebar.widget.maximizedbutton(c),
                              awful.titlebar.widget.stickybutton   (c),
                              awful.titlebar.widget.ontopbutton    (c),
                              awful.titlebar.widget.closebutton    (c),
                              layout = wibox.layout.fixed.horizontal()
                            },
                            layout = wibox.layout.align.horizontal
                                                    }
  end)

  -- Enable sloppy focus, so that focus follows mouse.
  client.connect_signal("mouse::enter", function(c)
                          c:emit_signal("request::activate", "mouse_enter", {raise = false})
  end)

  client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
  client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
  -- }}}
Volume and media control with keys

Let's see if the Volumearc Widget works for me.

Check releases to find an installable version of playerctl for whatever distribution I'm using this week.

globalkeys = gears.table.join(
  globalkeys,
  awful.key({}, "XF86AudioPlay", function() awful.util.spawn("playerctl play-pause") end,
    {description = "toggle player", group = "media"}),
  awful.key({}, "XF86AudioRaiseVolume",
    volume_widget.raise,
    {description = "raise volume", group = "media"}),
  awful.key({}, "XF86AudioLowerVolume",
    volume_widget.lower,
    {description = "lower volume", group = "media"}),
  awful.key({}, "XF86AudioMute",
    volume_widget.toggle,
    {description = "toggle mute", group = "media"}
  ),
  awful.key({}, "XF86AudioNext", function() awful.util.spawn("playerctl next")       end,
    {description = "play next", group = "media"}),
  awful.key({}, "XF86AudioPrev", function() awful.util.spawn("playerctl previous")   end,
    {description = "play previous", group = "media"})
)
xrandr

I grabbed xrandr.lua from this recipe, to help with my multi-monitor display.

Banging on Super+F11 cycles xrandr through each of the available arrangements. Leaving it alone will save the currently loaded arrangement.

globalkeys = gears.table.join(
  globalkeys,
  awful.key({ modkey }, "F11", function() xrandr.xrandr() end)
)

autorun

  function run {
    if ! pgrep -f $1 ;
    then
      $@&
    fi
  }

  #run dropbox
  run setxkbmap -option ctrl:nocaps -option compose:ralt
  run feh --bg-scale ~/OneDrive/Pictures/Backgrounds/IMB_0809.jpg

  if [ -f "~/.Xmodmap" ]; then
      xmodmap ~/.Xmodmap
  fi

  #run xcape -e 'Control_L=Escape'
Got a comment? A question? More of a comment than a question? Talk to me about this config!

Back to the config section