My Org config
My personal orgconfig
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 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.
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?
Emacs
Emacs
I frequently experiment with GNU Emacs configuration. Different starters like Doom or Centaur. Sometimes even one of my own experiments.
Chemacs for the Emacs profiles
Chemacs helps me juggle all those configurations. Since I am now up to four configs — five, since I started collecting these notes — this may or may not be a good thing.
Grabbing installation instructions from the Chemacs README
:
[ -f ~/.emacs ] && mv ~/.emacs ~/.emacs.bak
[ -d ~/.emacs.d ] && mv ~/.emacs.d ~/.emacs.default.d
git clone https://github.com/plexus/chemacs2.git ~/.emacs.d
I add the configuration of the week to ~/.emacs-profiles.el
.
(("better" . ((user-emacs-directory . "~/.emacs.better.d")))
("bmw" . ((user-emacs-directory . "~/.emacs.bmw.d")))
("centaur" . ((user-emacs-directory . "~/.emacs.centaur.d")))
("doom" . ((user-emacs-directory . "~/.emacs.doom.d")))
("prelude" . ((user-emacs-directory . "~/.emacs.prelude.d"))))
Now I can launch a specific profile by name:
emacs --with-profile better
If I fire up “Emacs” from the desktop, it’ll use the profile named in ~/.emacs-profile
.
prelude
“Better Defaults” for guests or a safe fallback
I need a stable profile for when pairing. Also handy when my experiments break something. That’s where Phil Hagelberg’s better-defaults comes in.
Clone the repo and load it, and you’re basically done. It’s Emacs, but just a little better.
(add-to-list 'load-path "~/.emacs.better.d/elisp")
(require 'better-defaults)
Prelude — for a solid Emacs foundation
The Prelude experience is somewhere in between better-defaults and Doom. Thinks more about features than Better, but not as intensely customized as Doom. You can still get useful information from the default Emacs documentation. Oh, and Prelude has very nice online documentation.
This config is pretty bare right now. That’s because it’s my latest “Emacs bankruptcy” config. I only wanted needed enough to tangle and publish these pages.
Not to worry. It’ll soon be twisted beyond recognition.
Specify prelude modules to load
This is one of those big files where you comment out the lines you don’t need. I won’t drop the whole file here. Mostly enabled stuff for my favorite Web and server development languages.
Set up additional personalizations
Prelude looks in ~/.emacs.prelude.d/personal/
on my machine —
probably ~/.emac.d/personal/
on yours —
for personalization changes.
You can throw as many files as you want in there.
For now, I’ll just throw all these blocks into one file.
If that doesn’t work?
Well okay.
I’ll fix it after I put out the fires.
<<prelude+lua>>
<<prelude+ox-hugo>>
Lua
I mainly need lua-mode to configure AwesomeWM. But someday I’ll dig deeper into Lua.
(prelude-require-package 'lua-mode)
(autoload 'lua-mode "lua-mode" "Lua editing mode." t)
(add-to-list 'auto-mode-alist '("\\.lua$" . lua-mode))
(add-to-list 'interpreter-mode-alist '("lua" . lua-mode))
Ox-Hugo
I need ox-hugo to publish these settings through Hugo as anything but one huge HTML file.
(prelude-require-package 'ox-hugo)
(with-eval-after-load 'ox
(require 'ox-hugo))
Doom — for a highly tweaked Vim-ish experience
Introduction
This is the config of a mostly-Vim user trying to use Emacs.
I might be getting the hang of Doom Emacs and its not-quite-Emacs-not-quite-Vim quirks.
I have no idea how well it will work.
config
See the Doom config example for extremely helpful inline comments, which I have impatiently stripped from my own config.
However, I know I’ll need this bit:
Here are some additional functions/macros that could help you configure Doom:
load!
for loading external *.el files relative to this oneuse-package!
for configuring packagesafter!
for running code after a package has loadedadd-load-path!
for adding directories to theload-path
, relative to this file. Emacs searches theload-path
when you load packages withrequire
oruse-package
.map!
for binding new keysTo get information about any of these functions/macros, move the cursor over the highlighted symbol at press
K
(non-evil users must pressC-c c k
). This will open documentation for it, including demos of how they are used.You can also try
gd
(orC-c c d
) to jump to their definition and see how they are implemented.
For themes I’m just going through emacs-doom-themes until I find a few that stick.
;;; -*- lexical-binding: t; -*-
<<doom/define-global-variables>>
(setq doom-theme 'doom-vibrant)
(setq display-line-numbers-type t)
<<doom/configure-projectile>>
<<doom/configure-org-mode>>
<<doom/configure-doom-dashboard>>
Personal Variables
Some are preferences, some are handy ways to define my environment.
(setq user-full-name "Brian Wisti" user-mail-address "brianwisti@pobox.com") <<define-private-variables>>
And some are for work or personal details that we don’t need to be showing the public.
Org mode
Honestly, Org mode is mostly what I use Emacs for.
(setq org-directory "~/org/" org-startup-indented nil) <<doom/configure-org-roam>>
Projectile
Projectile provides one approach to project management in Emacs.
(after! projectile (dolist (project bmw/projects) (projectile-add-known-project project)))
Doom Dashboard
(setq +doom-dashboard-menu-sections '(("Reload last session" :icon (all-the-icons-octicon "history" :face 'doom-dashboard-menu-title) :when (cond ((require 'persp-mode nil t) (file-exists-p (expand-file-name persp-auto-save-fname persp-save-dir))) ((require 'desktop nil t) (file-exists-p (desktop-full-file-name)))) :face (:inherit (doom-dashboard-menu-title bold)) :action doom/quickload-session) ("Open org-roam Daily" :icon (all-the-icons-octicon "squirrel" :face 'doom-dashboard-menu-title) :when (fboundp 'org-roam-dailies-find-today) :action org-roam-dailies-today) ("Open org-agenda" :icon (all-the-icons-octicon "calendar" :face 'doom-dashboard-menu-title) :when (fboundp 'org-agenda) :action org-agenda) ("Recently opened files" :icon (all-the-icons-octicon "file-text" :face 'doom-dashboard-menu-title) :action recentf-open-files) ("Open project" :icon (all-the-icons-octicon "briefcase" :face 'doom-dashboard-menu-title) :action projectile-switch-project) ("Jump to bookmark" :icon (all-the-icons-octicon "bookmark" :face 'doom-dashboard-menu-title) :action bookmark-jump) ("Open private configuration" :icon (all-the-icons-octicon "tools" :face 'doom-dashboard-menu-title) :when (file-directory-p doom-private-dir) :action doom/open-private-config) ("Open documentation" :icon (all-the-icons-octicon "book" :face 'doom-dashboard-menu-title) :action doom/help)))
init.el
The best use of init.el
is just uncommenting entries from the extensive Doom init file for bundled packages you want enabled, and adding bundle options where relevant.
Again, grabbing a useful tip from the original
Move your cursor over a module’s name (or its flags) and press ‘K’ (or ‘C-c c k’ for non-vim users) to view its documentation. This works on flags as well (those symbols that start with a plus).
Alternatively, press ‘gd’ (or ‘C-c c d’) on a module to browse its directory (for easy access to its source code).
packages.el
Nothing here yet, but this is where I would install packages beyond the plethora of options bundled with Doom.
And when I do hit that point, I may want to look at the Doom packages example.
My early attempts
I don’t know Emacs. I don’t know Lisp. And yet I created a more or less functioning config. Well, last I checked it was functioning.
My version of “.emacs
bankruptcy” is falling back on this collection of learned habits and accumulated preferences.
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-coding-system 'utf-8)
;;
;; 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))
;; 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)
(if (eq system-type 'windows-nt)
(setq inhibit-compacting-font-caches t))
;; Hugo gets confused by Emacs lockfiles
(setq-default create-lockfiles nil)
(setq bmw/org-dir
(expand-file-name "~/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-oxn-site (concat bmw/org-site-dir "content.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))
(global-set-key (kbd "<f5>")
(lambda ()
(interactive)
(find-file bmw/org-config-main)
(message "Opened: %s" (buffer-name))))
(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-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))))
(set-face-attribute 'default nil
:family "IBM Plex Mono"
:height 120
:weight 'normal
:width 'normal)
(set-face-attribute 'fixed-pitch nil
:family "IBM Plex Mono"
:height 120
:weight 'normal
:width 'normal)
(set-face-attribute 'variable-pitch nil
:family "IBM Plex Sans"
:height 120
: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)))))
;; 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")))))
(use-package goto-addr
:ensure t
:hook
((text-mode prog-mode) . goto-address-mode))
(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))
;; 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)))
(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)))
(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))
(use-package adoc-mode
:ensure t
:mode "\\.adoc")
(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 sessions
(use-package elscreen
:ensure t
:init
(elscreen-start))
(use-package gnuplot
:ensure t)
(use-package lua-mode
:ensure t
:mode ("\\.lua\\'" "\\.luac\\'")
:config (setq lua-indent-level 2))
(use-package org-bullets
:ensure t
:custom
(org-bullets-bullet-list '("◉" "○"))
:hook (org-mode . org-bullets-mode))
(use-package olivetti
:ensure t
:custom
(olivetti-body-width bmw/line-length)
:hook (text-mode . olivetti-mode))
(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")
(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")))))
;; 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)
(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)))
(org-clock-persistence-insinuate))
;; markdown-mode
(use-package markdown-mode
:ensure t
:mode (("README\\.md\\'" . gfm-mode)
("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode))
:init (setq markdown-command "mmark"))
;; 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))
(use-package yaml-mode
:ensure t)
;; 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)
(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)))
(use-package cperl-mode
:ensure t
:defer t
:custom
;; 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)
:interpreter "perl"
:mode "\\.\\(cgi\\|psgi\\|t\\)\\'")
(defalias 'perl-mode 'cperl-mode)
(require 'digraphs)
(defadvice load-theme (before clear-previous-themes activate)
"Clear existing theme settings instead of layering them"
(mapc #'disable-theme custom-enabled-themes))
(use-package color-theme-modern
:ensure t
:config
(load-theme 'wombat t t)
(enable-theme 'wombat))
(setq custom-file "~/.bmw.emacs.d/.emacs-custom.el")
(load custom-file)
Set up package repositories and make sure use-package
is in force.
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)
(if (eq system-type 'windows-nt)
(setq inhibit-compacting-font-caches t))
;; Hugo gets confused by Emacs lockfiles
(setq-default create-lockfiles nil)
(setq bmw/org-dir
(expand-file-name "~/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-oxn-site (concat bmw/org-site-dir "content.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))
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 120
:weight 'normal
:width 'normal)
(set-face-attribute 'fixed-pitch nil
:family "IBM Plex Mono"
:height 120
:weight 'normal
:width 'normal)
(set-face-attribute 'variable-pitch nil
:family "IBM Plex Sans"
:height 120
: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 "~/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-oxn-site (concat bmw/org-site-dir "content.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-package olivetti
:ensure t
:custom
(olivetti-body-width bmw/line-length)
:hook (text-mode . olivetti-mode))
(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")
(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")))))
;; 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)
(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)))
(org-clock-persistence-insinuate))
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.
(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)))
(use-package cperl-mode
:ensure t
:defer t
:custom
;; 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)
:interpreter "perl"
:mode "\\.\\(cgi\\|psgi\\|t\\)\\'")
(defalias 'perl-mode 'cperl-mode)
(use-package cperl-mode
:ensure t
:defer t
:custom
;; 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)
: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.
;; 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"))
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 inwindows-nt
I’m really getting uncertain. Maybe just explore Elpy for a bit — see if there’s anything in there aboutvenv
.
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)))
(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)))
(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-package color-theme-modern
:ensure t
:config
(load-theme 'wombat t t)
(enable-theme 'wombat))
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 'wombat t t)
(enable-theme 'wombat))
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))))
Digraphs
Like Vim digraphs, but for Emacs. I like those. C-c d
calls digraph-map
, which gets Emacs listening for the next sequence of characters.
Via Github user ibarland.
That’s a big source block. Just go to the gist.
I know for sure I’ll be revisiting this until I get it just so for my own tastes. With that in mind, here are some resources for future me.
Resources
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"))))
Awesome WM
Introduction
Every once in a while I get bored of those smooth-running, everything basically works desktops, and I pull out awesomewm. It basically works too, but not after I’m through with it.
My starting rc.lua
wasn’t much different from the stock default.rc.lua
. I started with the themes that ship with the Ubuntu setup one gets with apt-get install awesomewm
. You can find those in /usr/share/awesome/themes
.
That chafed pretty quick, though. Awesome WM Copycats is a large theme collection for Awesome.
git clone --recursive https://github.com/lcpz/awesome-copycats.git \
mv -bv awesome-copycats/* ~/.config/awesome && rm -rf awesome-copycats
Course, it also needs a different rc.lua
to drive it. So I scrapped my old config and used rc.lua.template
from Awesome WM Copycats as my new starter.
Main rc.lua
file
<<awesome-required-libraries>>
<<awesome-error-handling>>
<<awesome-autostart-windowless-processes>>
<<awesome-variable-definitions>>
<<awesome-menu>>
<<awesome-screen>>
<<awesome-mouse-bindings>>
<<awesome-key-bindings>>
<<awesome-rules>>
<<awesome-signals>>
Tangle fragments
Required libraries
- awesome API index
- a reference for all the core awesome libraries
gears
awful
beautiful
naughty
menubar
- Awesome-Freedesktop
- Freedesktop menu and desktop icon support for Awesome WM 4.x
- Lain
- layouts, widgets, and utilities for Awesome WM 4.x
local awesome, client, mouse, screen, tag = awesome, client, mouse, screen, tag
local ipairs, string, os, table, tostring, tonumber, type = ipairs, string, os, table, tostring, tonumber, type
local gears = require("gears")
local awful = require("awful")
require("awful.autofocus")
local wibox = require("wibox")
local beautiful = require("beautiful")
local naughty = require("naughty")
local lain = require("lain")
--local menubar = require("menubar")
local freedesktop = require("freedesktop")
local hotkeys_popup = require("awful.hotkeys_popup").widget
require("awful.hotkeys_popup.keys")
local my_table = awful.util.table or gears.table -- 4.{0,1} compatibility
local dpi = require("beautiful.xresources").apply_dpi
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)
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
Autostart windowless processes
In order to make these work I installed rxvt-unicode
, which provides the urxvtd
daemon.
sudo apt install rxvt-unicode
-- This function will run once every time Awesome is started
local function run_once(cmd_arr)
for _, cmd in ipairs(cmd_arr) do
awful.spawn.with_shell(string.format("pgrep -u $USER -fx '%s' > /dev/null || (%s)", cmd, cmd))
end
end
run_once({ "urxvtd", "unclutter -root" }) -- entries must be separated by commas
-- This function implements the XDG autostart specification
--[[
awful.spawn.with_shell(
'if (xrdb -query | grep -q "^awesome\\.started:\\s*true$"); then exit; fi;' ..
'xrdb -merge <<< "awesome.started:true";' ..
-- list each of your autostart commands, followed by ; inside single quotes, followed by ..
'dex --environment Awesome --autostart --search-paths --"$XDG_CONFIG_DIRS/autostart:$XDG_CONFIG_HOME/autostart"'
-- see https://github.com/jceb/dex
)
--]]
Variable definitions
I found slock
in suckless-tools
.
local themes = {
"blackburn", -- 1
"copland", -- 2
"dremora", -- 3
"holo", -- 4
"multicolor", -- 5
"powerarrow", -- 6
"powerarrow-dark", -- 7
"rainbow", -- 8
"steamburn", -- 9
"vertex", -- 10
}
local chosen_theme = themes[5]
local modkey = "Mod4"
local altkey = "Mod1"
local shiftkey = "Shift"
local controlkey = "Control"
local terminal = "gnome-terminal"
-- vi-like client focus
-- https://github.com/lcpz/awesome-copycats/issues/275
local vi_focus = false
-- cycle through all previous client or just the first
-- https://github.com/lcpz/awesome-copycats/issues/274
local cycle_prev = true
local editor = os.getenv("EDITOR") or "vim"
local gui_editor = os.getenv("GUI_EDITOR") or "gvim"
local browser = os.getenv("BROWSER") or "firefox"
local scrlocker = "slock"
awful.util.terminal = terminal
awful.util.tagnames = { "1", "2", "3", "4", "5" }
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.magnifier,
awful.layout.suit.corner.sw,
awful.layout.suit.corner.se,
lain.layout.cascade,
lain.layout.cascade.tile,
lain.layout.centerwork,
lain.layout.termfair,
lain.layout.termfair.center,
}
awful.util.taglist_buttons = my_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)
)
awful.util.tasklist_buttons = my_table.join(
awful.button({ }, 1, function (c)
if c == client.focus then
c.minimized = true
else
--c:emit_signal("request::activate", "tasklist", {raise = true})<Paste>
-- Without this, the following
-- :isvisible() makes no sense
c.minimized = false
if not c:isvisible() and c.first_tag then
c.first_tag:view_only()
end
-- This will also un-minimize
-- the client, if needed
client.focus = c
c:raise()
end
end),
awful.button({ }, 2, function (c) c:kill() end),
awful.button({ }, 3, function ()
local instance = nil
return function ()
if instance and instance.wibox.visible then
instance:hide()
instance = nil
else
instance = awful.menu.clients({theme = {width = dpi(250)}})
end
end
end),
awful.button({ }, 4, function () awful.client.focus.byidx(1) end),
awful.button({ }, 5, function () awful.client.focus.byidx(-1) end)
)
lain.layout.termfair.nmaster = 3
lain.layout.termfair.ncol = 1
lain.layout.termfair.center.nmaster = 3
lain.layout.termfair.center.ncol = 1
lain.layout.cascade.tile.offset_x = dpi(2)
lain.layout.cascade.tile.offset_y = dpi(32)
lain.layout.cascade.tile.extra_padding = dpi(5)
lain.layout.cascade.tile.nmaster = 5
lain.layout.cascade.tile.ncol = 2
beautiful.init(
string.format("%s/.config/awesome/themes/%s/theme.lua", os.getenv("HOME"), chosen_theme)
)
Menu
Better change that “edit config” menu option so it’s launching Emacs with this file. Sure, at some point I should be launching emacsclient
, but one step at a time.
local myawesomemenu = {
{ "hotkeys", function() return false, hotkeys_popup.show_help end },
{ "manual", terminal .. " -e man awesome" },
{ "edit config", "emacs ~/org/config/awesomewm.org" },
{ "restart", awesome.restart },
{ "quit", function() awesome.quit() end }
}
awful.util.mymainmenu = freedesktop.menu.build({
icon_size = beautiful.menu_height or dpi(16),
before = {
{ "Awesome", myawesomemenu, beautiful.awesome_icon },
-- other triads can be put here
},
after = {
{ "Open terminal", terminal },
-- other triads can be put here
}
})
Screen
-- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
screen.connect_signal(
"property::geometry",
function(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)
-- No borders when rearranging only 1 non-floating or maximized client
screen.connect_signal(
"arrange",
function (s)
local only_one = #s.tiled_clients == 1
for _, c in pairs(s.clients) do
if only_one and not c.floating or c.maximized then
c.border_width = 0
else
c.border_width = beautiful.border_width
end
end
end)
-- Create a wibox for each screen and add it
awful.screen.connect_for_each_screen(function(s) beautiful.at_screen_connect(s) end)
Mouse bindings
root.buttons(
my_table.join(
awful.button({ }, 3, function () awful.util.mymainmenu:toggle() end),
awful.button({ }, 4, awful.tag.viewnext),
awful.button({ }, 5, awful.tag.viewprev)
))
Key bindings
This chunk is big. It’s really really big. I broke it down into smaller chunks and subchunks. That doesn’t make it smaller, but it does make things easier to chew.
Global keys
Key bindings in effect at all times. I pick and choose from the available awesome-keys-global-*
chunks.
globalkeys = my_table.join(
<<awesome-keys-global-screenshot>>
<<awesome-keys-global-lock>>
<<awesome-keys-global-hotkeys>>
<<awesome-keys-global-tags-browse-all>>
<<awesome-keys-global-tags-browse-nonempty>>
<<awesome-keys-global-focus-byindex>>
<<awesome-keys-global-layout>>
<<awesome-keys-global-wibox>>
<<awesome-keys-global-tagging>>
<<awesome-keys-global-standard>>
<<awesome-keys-global-dropdown>>
<<awesome-keys-global-widgets>>
<<awesome-keys-global-volume-alsa>>
<<awesome-keys-global-media-mpd>>
<<awesome-keys-global-clipboard>>
<<awesome-keys-global-user>>
<<awesome-keys-global-prompt>>
)
Take a screenshot
Building on the example
screenshot
script at https://github.com/lcpz/dots/blob/master/bin/screenshot.The
screenshot
script asks scrot to take a screenshot of the current root window, saving it to a timestamped file. Guaranteed to be more screenshot than I need 98% of the time, but it will do until I get the hang of things.timestamp="$(date +%Y-%m-%dT%H-%M-%S)" screenshot_file="screenshot-$timestamp.png" screenshot_dir="$HOME/Pictures" [ -d $screenshot_dir ] || exit 1 scrot $screenshot_dir/$screenshot_file
awful.key({ modkey }, "Print", function() os.execute("screenshot") end, {description = "take a screenshot", group = "hotkeys"}),
X screen locker
awful.key({ modkey, controlkey }, "l", function () os.execute(scrlocker) end, {description = "lock screen", group = "hotkeys"}),
Hotkeys popup
awful.key({ modkey, }, "s", hotkeys_popup.show_help, {description = "show help", group="awesome"}),
Tag browsing
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"}),
Non-empty tag browsing
awful.key({ modkey, shiftkey }, "Left", function () lain.util.tag_view_nonempty(-1) end, {description = "view previous nonempty", group = "tag"}), awful.key({ modkey, shiftkey }, "Right", function () lain.util.tag_view_nonempty(1) end, {description = "view previous nonempty", group = "tag"}),
Default client focus
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"} ),
By direction client focus
awful.key({ modkey }, "j", function() awful.client.focus.global_bydirection("down") if client.focus then client.focus:raise() end end, {description = "focus down", group = "client"}), awful.key({ modkey }, "k", function() awful.client.focus.global_bydirection("up") if client.focus then client.focus:raise() end end, {description = "focus up", group = "client"}), awful.key({ modkey }, "h", function() awful.client.focus.global_bydirection("left") if client.focus then client.focus:raise() end end, {description = "focus left", group = "client"}), awful.key({ modkey }, "l", function() awful.client.focus.global_bydirection("right") if client.focus then client.focus:raise() end end, {description = "focus right", group = "client"}),
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 () if cycle_prev then awful.client.focus.history.previous() else awful.client.focus.byidx(-1) end if client.focus then client.focus:raise() end end, {description = "cycle with previous/go back", group = "client"}), awful.key({ modkey, "Shift" }, "Tab", function () if cycle_prev then awful.client.focus.byidx(1) if client.focus then client.focus:raise() end end end, {description = "go forth", group = "client"}),
Show / Hide Wibox
awful.key({ modkey }, "b", function () for s in screen do s.mywibox.visible = not s.mywibox.visible if s.mybottomwibox then s.mybottomwibox.visible = not s.mybottomwibox.visible end end end, {description = "toggle wibox", group = "awesome"}),
Dynamic tagging
awful.key({ modkey, "Shift" }, "n", function () lain.util.add_tag() end, {description = "add new tag", group = "tag"}), awful.key({ modkey, "Shift" }, "r", function () lain.util.rename_tag() end, {description = "rename tag", group = "tag"}), awful.key({ modkey, "Shift" }, "Left", function () lain.util.move_tag(-1) end, {description = "move tag to the left", group = "tag"}), awful.key({ modkey, "Shift" }, "Right", function () lain.util.move_tag(1) end, {description = "move tag to the right", group = "tag"}), awful.key({ modkey, "Shift" }, "d", function () lain.util.delete_tag() end, {description = "delete tag", group = "tag"}),
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({ altkey, "Shift" }, "l", function () awful.tag.incmwfact( 0.05) end, {description = "increase master width factor", group = "layout"}), awful.key({ altkey, "Shift" }, "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 client.focus = c c:raise() end end, {description = "restore minimized", group = "client"}), awful.key({ modkey, }, "w", function () awful.util.mymainmenu:show() end, {description = "show main menu", group = "awesome"}),
Dropdown application
awful.key({ modkey, }, "z", function () awful.screen.focused().quake:toggle() end, {description = "dropdown application", group = "launcher"}),
Widgets popups
awful.key({ altkey, }, "c", function () if beautiful.cal then beautiful.cal.show(7) end end, {description = "show calendar", group = "widgets"}), awful.key({ altkey, }, "h", function () if beautiful.fs then beautiful.fs.show(7) end end, {description = "show filesystem", group = "widgets"}), awful.key({ altkey, }, "w", function () if beautiful.weather then beautiful.weather.show(7) end end, {description = "show weather", group = "widgets"}),
Brightness
I might find this more useful if any of my monitors had a backlight property.
awful.key({ }, "XF86MonBrightnessUp", function () os.execute("xbacklight -inc 10") end, {description = "+10%", group = "hotkeys"}), awful.key({ }, "XF86MonBrightnessDown", function () os.execute("xbacklight -dec 10") end, {description = "-10%", group = "hotkeys"}),
ALSA volume control
awful.key({ altkey }, "Up", function () os.execute(string.format("amixer -q set %s 1%%+", beautiful.volume.channel)) beautiful.volume.update() end, {description = "volume up", group = "hotkeys"}), awful.key({ altkey }, "Down", function () os.execute(string.format("amixer -q set %s 1%%-", beautiful.volume.channel)) beautiful.volume.update() end, {description = "volume down", group = "hotkeys"}), awful.key({ altkey }, "m", function () os.execute( string.format( "amixer -q set %s toggle", beautiful.volume.togglechannel or beautiful.volume.channel )) beautiful.volume.update() end, {description = "toggle mute", group = "hotkeys"}), awful.key({ altkey, "Control" }, "m", function () os.execute(string.format("amixer -q set %s 100%%", beautiful.volume.channel)) beautiful.volume.update() end, {description = "volume 100%", group = "hotkeys"}), awful.key({ altkey, "Control" }, "0", function () os.execute(string.format("amixer -q set %s 0%%", beautiful.volume.channel)) beautiful.volume.update() end, {description = "volume 0%", group = "hotkeys"}),
MPD control
awful.key({ altkey, "Control" }, "Up", function () os.execute("mpc toggle") beautiful.mpd.update() end, {description = "mpc toggle", group = "widgets"}), awful.key({ altkey, "Control" }, "Down", function () os.execute("mpc stop") beautiful.mpd.update() end, {description = "mpc stop", group = "widgets"}), awful.key({ altkey, "Control" }, "Left", function () os.execute("mpc prev") beautiful.mpd.update() end, {description = "mpc prev", group = "widgets"}), awful.key({ altkey, "Control" }, "Right", function () os.execute("mpc next") beautiful.mpd.update() end, {description = "mpc next", group = "widgets"}), awful.key({ altkey }, "0", function () local common = { text = "MPD widget ", position = "top_middle", timeout = 2 } if beautiful.mpd.timer.started then beautiful.mpd.timer:stop() common.text = common.text .. lain.util.markup.bold("OFF") else beautiful.mpd.timer:start() common.text = common.text .. lain.util.markup.bold("ON") end naughty.notify(common) end, {description = "mpc on/off", group = "widgets"}),
Clipboard
Transferring terminal clipboard to and from the GTK clipboard.
awful.key({ modkey }, "c", function () awful.spawn.with_shell("xsel | xsel -i -b") end, {description = "copy terminal to gtk", group = "hotkeys"}), awful.key({ modkey }, "v", function () awful.spawn.with_shell("xsel -b | xsel") end, {description = "copy gtk to terminal", group = "hotkeys"}),
User programs
awful.key({ modkey }, "q", function () awful.spawn(browser) end, {description = "run browser", group = "launcher"}), awful.key({ modkey }, "a", function () awful.spawn(gui_editor) end, {description = "run gui editor", group = "launcher"}),
Prompt
awful.key({ modkey }, "r", function () awful.screen.focused().mypromptbox:run() end, {description = "run prompt", group = "launcher"}), 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"})
Default
Disabled in
rc.lua.template
. I uncommented the code bits but haven’t included it in my menu definition.-- Menubar awful.key({ modkey }, "p", function() menubar.show() end, {description = "show the menubar", group = "launcher"}) -- dmenu awful.key({ modkey }, "x", function () os.execute( string.format( "dmenu_run -i -fn 'Monospace' -nb '%s' -nf '%s' -sb '%s' -sf '%s'", beautiful.bg_normal, beautiful.fg_normal, beautiful.bg_focus, beautiful.fg_focus ) ) end, {description = "show dmenu", group = "launcher"}) -- alternatively use rofi, a dmenu-like application with more features -- check https://github.com/DaveDavenport/rofi for more details -- rofi awful.key({ modkey }, "x", function () os.execute( string.format("rofi -show %s -theme %s", 'run', 'dmenu') ) end, {description = "show rofi", group = "launcher"}),
Client keys
Keys for managing the client — whatever has focus right now.
clientkeys = my_table.join(
awful.key({ altkey, "Shift" }, "m", lain.util.magnify_client,
{description = "magnify client", 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 = "maximize", group = "client"})
)
Tag keys
Key bindings to manage your tags. That’s what AwesomeWM calls its workspaces.
Bind all key numbers to tags. Be careful: we use keycodes to make it works on any keyboard layout. This should map on the top row of your keyboard, usually 1 to 9.
for i = 1, 9 do
-- Hack to only show tags 1 and 9 in the shortcut window (mod+s)
local descr_view, descr_toggle, descr_move, descr_toggle_focus
if i == 1 or i == 9 then
descr_view = {description = "view tag #", group = "tag"}
descr_toggle = {description = "toggle tag #", group = "tag"}
descr_move = {description = "move focused client to tag #", group = "tag"}
descr_toggle_focus = {description = "toggle focused client on tag #", group = "tag"}
end
globalkeys = my_table.join(
globalkeys,
-- View tag only.
awful.key({ modkey }, "#" .. i + 9,
function ()
local screen = awful.screen.focused()
local tag = screen.tags[i]
if tag then
tag:view_only()
end
end,
descr_view),
-- Toggle tag display.
awful.key({ modkey, "Control" }, "#" .. i + 9,
function ()
local screen = awful.screen.focused()
local tag = screen.tags[i]
if tag then
awful.tag.viewtoggle(tag)
end
end,
descr_toggle),
-- Move client to tag.
awful.key({ modkey, "Shift" }, "#" .. i + 9,
function ()
if client.focus then
local tag = client.focus.screen.tags[i]
if tag then
client.focus:move_to_tag(tag)
end
end
end,
descr_move),
-- Toggle tag on focused client.
awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
function ()
if client.focus then
local tag = client.focus.screen.tags[i]
if tag then
client.focus:toggle_tag(tag)
end
end
end,
descr_toggle_focus)
)
end
Client buttons
Use the keyboard to send mouse clicks.
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)
)
Putting it all together
<<awesome-keys-global>>
<<awesome-keys-client>>
<<awesome-keys-tags>>
<<awesome-keys-clientbuttons>>
-- Set keys
root.keys(globalkeys)
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,
size_hints_honor = false
}
},
-- Titlebars
{ rule_any = { type = { "dialog", "normal" } },
properties = { titlebars_enabled = true } },
{ rule = { class = "Emacs" },
properties = { screen = 2, tag = awful.util.tagnames[1] } },
-- Set Firefox to always map on the first tag on screen 1.
{ rule = { class = "Firefox" },
properties = { screen = 1, tag = awful.util.tagnames[1] } },
{ rule = { class = "Gimp", role = "gimp-image-window" },
properties = { maximized = true } },
}
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)
-- Custom
if beautiful.titlebar_fun then
beautiful.titlebar_fun(c)
return
end
-- Default
-- buttons for the titlebar
local buttons = my_table.join(
awful.button({ }, 1, function()
c:emit_signal("request::activate", "titlebar", {raise = true})
awful.mouse.client.move(c)
end),
awful.button({ }, 2, function() c:kill() end),
awful.button({ }, 3, function()
c:emit_signal("request::activate", "titlebar", {raise = true})
awful.mouse.client.resize(c)
end)
)
awful.titlebar(c, {size = dpi(16)}) : 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 = vi_focus})
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)
-- possible workaround for tag preservation when switching back to default screen:
-- https://github.com/lcpz/awesome-copycats/issues/251
autorun
#!/usr/bin/env bash
# stricter bash
# see http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
IFS=$'\n\t'
function run {
if ! pgrep -f $1 ;
then
$@&
fi
}
run dropbox
#run setxkbmap -option compose:ralt
run xrandr --output DisplayPort-1 --mode 2560x1440 --rate 59.95
if [ -f "~/.Xmodmap" ]; then
xmodmap ~/.Xmodmap
fi
#run xcape -e 'Control_L=Escape'
Resources
- Awesomewm API documentation
- Lain: Layouts, widgets and utilities for Awesome WM 4.x
- AwesomeWM posts by Epsi