├── Makefile ├── README.asciidoc ├── compton.conf ├── dot-config-i3-config ├── dot-config-i3status-config ├── i3-apps ├── i3-display ├── i3-keyboard ├── i3-make ├── i3-mode ├── i3-mouse └── i3-wrapper /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile 3 | # 4 | # Requires : shellcheck 5 | # 6 | 7 | # 8 | # This Makefile is best invoked via a custom script called 'i3-make' 9 | # that assigns values to these custom variables where necessary: 10 | # 11 | 12 | # Local git repo directory. 13 | I3FILES=$(shell readlink -f `pwd`) 14 | 15 | # The i3 config file to be installed. 16 | I3CFG=$(HOME)/.config/i3/config 17 | 18 | # A running i3wm references the file it used at startup, so it is 19 | # prudent to keep old and new in sync after relocating the file. 20 | I3ALTCFG=$(HOME)/.i3/config 21 | 22 | # The i3status config file to be installed. 23 | I3STATUSCFG=$(HOME)/.config/i3status/config 24 | 25 | # The compton config file to be installed. 26 | COMPTONCFG=$(HOME)/.config/compton.conf 27 | 28 | # Directory where i3 helper scripts will be installed. 29 | # Ensure that this directory is on your ${PATH}. 30 | I3BIN=$(HOME)/bin 31 | 32 | # Helpfiles. 33 | # 34 | # 1, Create one line from file for sed. 35 | # 2. Replace line continuations with a token. 36 | # 3. Replace ordinary line end with linefeed. 37 | # 4A. Grep for bindsyms inside modes, 38 | # 4B. Grep for mode "headers", 39 | # 4C. Grep for numpad bindsyms in default mode. 40 | # 5. Restore the line continuations marked with tokens. 41 | # 42 | I3BINDINGS=/tmp/i3/bindings 43 | 44 | # Install permissions for: directories, executables, configurations. 45 | DIRMODE=755 46 | EXEMODE=755 47 | CFGMODE=644 48 | 49 | # Audio feedback. Substitute 'true' for silence. 50 | BINGBONG="play -q /usr/share/sounds/freedesktop/stereo/complete.oga" 51 | 52 | # Nothing is installed unless this is set to "true". 53 | # Maintainers: Use 'make CONFIGURED=true'. 54 | CONFIGURED=false 55 | 56 | # 57 | # Do not customise anything below here. 58 | # 59 | 60 | all: .configured .custom .checks .install .restart .reload .done 61 | 62 | help: 63 | @echo "Please read the Makefile." 64 | @echo I3FILES=$(I3FILES) 65 | @echo I3CFG=$(I3CFG) 66 | @echo I3ALTCFG=$(I3ALTCFG) 67 | @echo I3STATUSCFG=$(I3STATUSCFG) 68 | @echo COMPTONCFG=$(COMPTONCFG) 69 | @echo I3BIN=$(I3BIN) 70 | @echo I3BINDINGS=$(I3BINDINGS) 71 | @echo modes: $(DIRMODE) $(EXEMODE) $(CFGMODE) 72 | @echo audio: `basename $(BINGBONG)` 73 | @echo CONFIGURED=$(CONFIGURED) 74 | 75 | .configured: 76 | @$(CONFIGURED) 77 | 78 | .install: \ 79 | $(I3CFG) \ 80 | $(I3ALTCFG) \ 81 | $(I3STATUSCFG) \ 82 | $(COMPTONCFG) \ 83 | $(I3BIN)/i3-apps \ 84 | $(I3BIN)/i3-display \ 85 | $(I3BIN)/i3-keyboard \ 86 | $(I3BIN)/i3-mode \ 87 | $(I3BIN)/i3-mouse \ 88 | $(I3BIN)/i3-wrapper \ 89 | $(I3BINDINGS) 90 | 91 | .checks: \ 92 | $(I3FILES)/.i3-apps.log \ 93 | $(I3FILES)/.i3-display.log \ 94 | $(I3FILES)/.i3-keyboard.log \ 95 | $(I3FILES)/.i3-mode.log \ 96 | $(I3FILES)/.i3-mouse.log \ 97 | $(I3FILES)/.i3-wrapper.log 98 | 99 | .custom: \ 100 | $(I3FILES)/.i3-custom.save 101 | 102 | $(I3CFG): $(I3FILES)/dot-config-i3-config 103 | @install -m $(DIRMODE) -d `dirname $(I3CFG)` 104 | @install -m $(CFGMODE) -T $(I3FILES)/dot-config-i3-config $(I3CFG) 105 | @touch $(I3FILES)/RELOAD 106 | 107 | $(I3ALTCFG): $(I3FILES)/dot-config-i3-config 108 | @install -m $(DIRMODE) -d `dirname $(I3ALTCFG)` 109 | @install -m $(CFGMODE) -T $(I3FILES)/dot-config-i3-config $(I3ALTCFG) 110 | 111 | $(I3STATUSCFG): $(I3FILES)/dot-config-i3status-config 112 | @install -m $(DIRMODE) -d `dirname $(I3STATUSCFG)` 113 | @install -m $(CFGMODE) -T $(I3FILES)/dot-config-i3status-config $(I3STATUSCFG) 114 | @touch $(I3FILES)/RESTART 115 | 116 | $(COMPTONCFG): $(I3FILES)/compton.conf 117 | @install -m $(DIRMODE) -d `dirname $(COMPTONCFG)` 118 | @install -m $(CFGMODE) -T $(I3FILES)/compton.conf $(COMPTONCFG) 119 | 120 | $(I3BIN)/i3-apps: $(I3FILES)/i3-apps 121 | @install -m $(DIRMODE) -d $(I3BIN) 122 | @install -m $(EXEMODE) $(I3FILES)/i3-apps $(I3BIN) 123 | 124 | $(I3BIN)/i3-display: $(I3FILES)/i3-display 125 | @install -m $(DIRMODE) -d $(I3BIN) 126 | @install -m $(EXEMODE) $(I3FILES)/i3-display $(I3BIN) 127 | 128 | $(I3BIN)/i3-keyboard: $(I3FILES)/i3-keyboard 129 | @install -m $(DIRMODE) -d $(I3BIN) 130 | @install -m $(EXEMODE) $(I3FILES)/i3-keyboard $(I3BIN) 131 | 132 | $(I3BIN)/i3-mode: $(I3FILES)/i3-mode 133 | @install -m $(DIRMODE) -d $(I3BIN) 134 | @install -m $(EXEMODE) $(I3FILES)/i3-mode $(I3BIN) 135 | 136 | $(I3BIN)/i3-mouse: $(I3FILES)/i3-mouse 137 | @install -m $(DIRMODE) -d $(I3BIN) 138 | @install -m $(EXEMODE) $(I3FILES)/i3-mouse $(I3BIN) 139 | 140 | $(I3BIN)/i3-wrapper: $(I3FILES)/i3-wrapper 141 | @install -m $(DIRMODE) -d $(I3BIN) 142 | @install -m $(EXEMODE) $(I3FILES)/i3-wrapper $(I3BIN) 143 | 144 | $(I3BINDINGS): $(I3FILES)/dot-config-i3-config $(I3FILES)/Makefile 145 | @mkdir -p `dirname $(I3BINDINGS)` 146 | @cat $(I3FILES)/dot-config-i3-config | awk 1 ORS='@' | \ 147 | sed 's/\\@/%%%/g' | sed 's/@/\n/g' | \ 148 | egrep '([^.]bindsym|^mode|bindsym KP|^bindsym Mod)' | \ 149 | sed 's/%%%/\\\n/g' > $(I3BINDINGS) 150 | 151 | # 152 | # Checks. 153 | # 154 | 155 | $(I3FILES)/.i3-apps.log: $(I3FILES)/i3-apps 156 | @rm -f $@ 157 | @shellcheck $(I3FILES)/i3-apps > $<.log 158 | @mv $<.log $@ 159 | 160 | $(I3FILES)/.i3-display.log: $(I3FILES)/i3-display 161 | @rm -f $@ 162 | @shellcheck $(I3FILES)/i3-display > $<.log 163 | @mv $<.log $@ 164 | 165 | $(I3FILES)/.i3-keyboard.log: $(I3FILES)/i3-keyboard 166 | @rm -f $@ 167 | @shellcheck $(I3FILES)/i3-keyboard > $<.log 168 | @mv $<.log $@ 169 | 170 | $(I3FILES)/.i3-mode.log: $(I3FILES)/i3-mode 171 | @rm -f $@ 172 | @shellcheck $(I3FILES)/i3-mode > $<.log 173 | @mv $<.log $@ 174 | 175 | $(I3FILES)/.i3-mouse.log: $(I3FILES)/i3-mouse 176 | @rm -f $@ 177 | @shellcheck $(I3FILES)/i3-mouse > $<.log 178 | @mv $<.log $@ 179 | 180 | $(I3FILES)/.i3-wrapper.log: $(I3FILES)/i3-wrapper 181 | @rm -f $@ 182 | @shellcheck $(I3FILES)/i3-wrapper > $<.log 183 | @mv $<.log $@ 184 | 185 | # 186 | # Customisations. 187 | # 188 | 189 | $(I3FILES)/.i3-custom.save: $(I3FILES)/i3-wrapper 190 | @[ `git diff $(I3FILES)/i3-wrapper | wc -l` -ne 0 ] && \ 191 | awk '/i3-custom-opening-tag/,/i3-custom-closing-tag/' \ 192 | $(I3FILES)/i3-wrapper > "$@" || touch "$@" 193 | 194 | # The WM restarts and clears the flag after the flag has been set by 195 | # changes that require a restart. 196 | .restart: 197 | @test -e $(I3FILES)/RESTART && \ 198 | i3-msg unmark && \ 199 | test `i3-msg restart 2>&1 | grep [E]RROR | wc -l` -eq 0 && \ 200 | rm -f $(I3FILES)/RESTART || \ 201 | test ! -e $(I3FILES)/RESTART 202 | 203 | # The WM reloads its configuration and clears the flag after the flag 204 | # has been set by configuration file changes. 205 | .reload: 206 | @test -e $(I3FILES)/RELOAD && \ 207 | test `i3-msg reload 2>&1 | grep [E]RROR | wc -l` -eq 0 && \ 208 | rm -f $(I3FILES)/RELOAD || \ 209 | test ! -e $(I3FILES)/RELOAD 210 | 211 | # I like audio feedback to confirm success. 212 | .done: 213 | @eval $(BINGBONG) 214 | 215 | # 216 | # Done. 217 | # 218 | -------------------------------------------------------------------------------- /README.asciidoc: -------------------------------------------------------------------------------- 1 | 2 | = Why I use the i3 window manager 3 | 4 | == Background 5 | 6 | This article is the result of a passing remark made on reddit, 7 | a popular news aggregation and discussion website. 8 | Topics are discussed in dedicated areas called subreddits. 9 | The subreddit /r/i3wm discusses the i3 window manager. 10 | 11 | .remark 12 | [quote, /u/EllaTheCat /r/i3wm, I3WM and XFCE4 - need reassurance] 13 | ____ 14 | i3 is great for Parkinson's because it's scriptable and mouse-neutral. It's keeping me able to use a computer. 15 | ____ 16 | .reply 17 | [quote, /u/airblader /r/i3wm, I3WM and XFCE4 - need reassurance] 18 | ____ 19 | If you ever find inspiration and time to maybe write some kind of article on how you use i3 with your disability, I would be quite interested in reading that. 20 | ____ 21 | 22 | This is the article. Although third-person language is preferred by academics and professionals, 23 | I believe the appropriate writing style here is first-person. 24 | 25 | == What is i3? 26 | 27 | Please take a minute or two to read the link:http://www.i3wm.org[i3 homepage at www.i3wm.org], 28 | especially the stated goals and values. Here's my TL;DR summary: 29 | 30 | [quote,www.i3wm.org] 31 | ____ 32 | i3 is a tiling window manager, completely written from scratch. 33 | i3 is primarily targeted at advanced users and developers. 34 | ____ 35 | 36 | == What is Parkinson's Disease? 37 | 38 | The Google Dictionary defines Parkinson's Disease (PD) as 39 | 40 | [quote,Google] 41 | ____ 42 | a progressive disease of the nervous system marked by tremor, muscular rigidity, and slow, imprecise movement, 43 | chiefly affecting middle-aged and elderly people. 44 | It is associated with degeneration of the basal ganglia of the brain and 45 | a deficiency of the neurotransmitter dopamine. 46 | ____ 47 | 48 | == What are my symptoms? 49 | 50 | The two following instructions attempt to give the reader some idea of how PD affects me, 51 | but others' symptoms may differ: 52 | 53 | * Rest your dominant hand palm down on a table. 54 | Raise your palm slightly such that only your fingertips touch the table. 55 | Now use your thumb to tap the table two or three times a second, as hard and as quickly as you possibly can. 56 | Now imagine your thumb doing that, sometimes at full power, sometimes just twitching. 57 | 58 | * Try typing as if your fingers on your dominant hand were held together by a not-too-tight rubber band, 59 | this simulates the lack of fine motor control. 60 | 61 | In summary, my thumb and fingers on the right hand cannot press keys reliably, 62 | mistakes occur in every third? fifth? word on average, and working around this is where i3 helps. 63 | 64 | == How does i3 help? 65 | 66 | === Context 67 | 68 | My desktop enviroment is XFCE-based, with i3 replacing the XFCE window manager. 69 | 70 | i3 is used alongside a collection of bash scripts and the i3-msg program. 71 | I have since reaiised that using the i3-ipc library is the better approach. 72 | 73 | What matters to me at this stage is being able to use my PC, 74 | my emphasis is on automation, ergonomics, and discovering what works for me. 75 | Please view my current implementation as a fast prototype. 76 | 77 | === Human Interface Devices 78 | 79 | Using a mouse implies being able to position the cursor accurately, 80 | which becomes increasingly difficult when you have PD. 81 | 82 | Using a keyboard offers the user these choices: 83 | in hardware, a light or heavy action, a linear or clicky feel; 84 | in software, the keyboard layout, accessibility features such as sticky keys, 85 | and one that I particularly like, using modifier keys standalone via 'xcape'. 86 | 87 | * Some decades ago I successfully adapted to using the mouse with my left hand, 88 | in order to place the QWERTY section of the keyboard in the middle of my desk, 89 | and thereby acheve a balanced posture. 90 | Unfortunately the temptation to use the mouse with my good hand 91 | is becoming an obstacle to my making better use of the keyboard. 92 | 93 | i3 presents the keyboard as the primary human interface device, 94 | making it a better choice for people with PD. 95 | 96 | === Windowing Operations 97 | 98 | i3 is a tiling window manager. 99 | 100 | I don't need the mouse or fine motor control to manipulate windows, 101 | because tiling automatically arranges windows without overlapping them. 102 | Key bindings let me refine the arrangement manually. 103 | The IPC socket lets me automate window management operations. 104 | 105 | === Key bindings 106 | 107 | The default i3 keybindings combine two or three keys 108 | to invoke the basic window management commands. 109 | I can't co-ordinate two hands to use some of them, 110 | but I can use the left hand or right hand on its own to press some combinations, 111 | e.g. Control+Tab, Windows+Tab, Control+Menu, Control+Delete. 112 | 113 | Although I was born right-handed, I am effectively left-handed due to PD. 114 | I have gained a better understanding of how people born left-handed feel 115 | in a right-handed world. 116 | 117 | Consequently, I design my keyboard layout such that any special keys 118 | are available on the left and on the right. If necessary I willl use 'xcape' 119 | and 'xmodmap' to ensure this prior to defining my i3 key bindings. 120 | 121 | * The left-side Windows key alone behaves like the Menu key. 122 | * The Alt and AltGr keys alone behave like the Escape key. 123 | 124 | I use the term "key combination" to describe a keybinding such as "Control+Alt+Delete". 125 | The Control and Alt keys are modifiers, they do nothing until the Delete key is pressed. 126 | 127 | I use the term "key sequence" to describe the typing of several keys 128 | (or key combinations) in succession. 129 | For example the key sequence "Control-x Control-c" to quit Emacs. 130 | 131 | === Primary Modes 132 | 133 | i3 modes reduce the need to co-ordinate the hands. 134 | 135 | The Menu key cycles through three primary modes. 136 | . Pressing Menu while in default mode enters the first non-default mode. 137 | . Pressing Menu a second time enters the second non-default mode. 138 | . Pressing Menu a third time returns to the default mode. 139 | 140 | In the first non-default mode, there are three inverted T cursor controls for focus, move, resize; 141 | the three "cursor keypads" are the four letters : WASD, TFGH, IJKL, respectively. 142 | 143 | In the second non-default mode there are three sets of 3 keys on the left, QWE,ASD,ZXC. 144 | These move focus to output, move container to output, move workspace to output. 145 | The key triplets correspond to monitors on the left, in the middle and on the right. 146 | 147 | Entering a non-default mode from any other mode resets all i3 keybindings. 148 | Re-entering default mode restores the i3 keybindings for default mode. 149 | 150 | When defining a non-default mode, it is good practice, albeit tedious, 151 | to bind otherwise unbound keys to a 'nop', 152 | otherwise the key "falls through" to the application in the current window. 153 | 154 | === Secondary Modes 155 | 156 | Pressing the Tab or Return key enters a secondary mode from either of the non-default modes. 157 | This secondary mode exists to solve a problem associated with PD; 158 | namely that it is just too easy to accidentally close a window and lose work. 159 | 160 | This secondary mode provides the key sequence "Menu, Tab, Control+q" 161 | to invoke the i3 "kill" command, which closes the currently focused window. 162 | This key sequence is fairly easy to type intentionally but difficult to type by mistake. 163 | 164 | i3 "Command Criteria" 165 | define the subset of all windows affected by a command. 166 | Applying this to the aforementioned 'kill' command 167 | prevents the accidental closure of a program such as Google Chrome. 168 | 169 | === Mode Indicator 170 | 171 | The 'xrandr' utility is used to dim the screen when not in default mode, 172 | to act as a reminder and a warning. 173 | 174 | == My User Interface (Is Weird) 175 | 176 | A dedicated key brings up a custom dialog box, 177 | into which the user types exactly two characters. 178 | After a short timeout has expired the dialog then closes 179 | without the user having to press Enter. 180 | 181 | === Launch or Focus. 182 | 183 | Entering two lowercase letters into the custom dialog, 184 | let's say 'gc' for Google Chrome, launches the associated program, 185 | unless the program is already running, in which case the program's window will get focus. 186 | 187 | With exception of terminals, launching a program creates a workspace for that program. 188 | The workspace is given a name that matches the two lowercase letters that launched the program. 189 | This works well with web browsers, spreadsheets, presentations, videos, 190 | indeed most of the applications I use, except terminals and Emacs frames. 191 | 192 | === Terminals 193 | 194 | In my workflow, approximately one third of windows are terminal windows, 195 | and terminals need to be grouped together. 196 | 197 | If the terminal is to be opened in the current workspace, 198 | two dedicated two-letter commands are available. 199 | Either will split the currently focused window to accommodate another terminal, 200 | one performs a horizontal split, the other performs a vertical split. 201 | 202 | Entering two lowercase letters that aren't explicitly assigned will open a terminal. 203 | This terminal will be assigned to an automatically managed workspace whose name matches the two letters. 204 | Multiple terminals can be added to such an existing workspace, by entering the same two letters. 205 | 206 | === Marks 207 | 208 | Every terminal is automatically assigned a two digit visible mark. 209 | Emacs "frames" (windows) are also assigned a two digit visible mark. 210 | Entering two digits in the custom dialog switches focus to the terminal or Emacs frame with that mark. 211 | 212 | === Emacs frames 213 | 214 | I try to avoid viewing more than one document at a time inside an Emacs frame. 215 | My preference is to split such an Emacs frame into separate Emacs frames managed by i3. 216 | I have changed Emacs bindings 'C-x 2' and 'C-x 3' to call the function normally bound to 'C-x 5 2'. 217 | 218 | Opening a new Emacs frame with 'C-x 5 2' assigns a mark as described earlier, 219 | then prompts for which workspace the Emacs frame should be moved to. 220 | This facilitates use cases where the screen has an Emacs frame in one half, 221 | and e.g. a browser or some terminals in the other half. 222 | 223 | === Swapping 224 | 225 | i3 has a command to swap the current window with another window identified by a mark. 226 | I've recently realised that swapping automatically marked windows can be easier than 227 | using the cursor keys and the split keys to get the window arrangement I need. 228 | 229 | 230 | 231 | == Links 232 | 233 | . https://www.reddit.com/r/i3wm/comments/830qdi/i3wm_and_xfce4_need_reassurance/ 234 | . https://penandthepad.com/write-third-person-english-writing-7833317.html 235 | . https://i3wm.org/ 236 | -------------------------------------------------------------------------------- /compton.conf: -------------------------------------------------------------------------------- 1 | # 2 | # http://askubuntu.com/questions/751149/screen-tearing-when-using-i3-none-when-using-unity 3 | # 4 | # compton --config ~/Programs/dotfiles/compton.conf -b 5 | # 6 | 7 | backend = "glx"; 8 | vsync = "opengl-swc"; 9 | 10 | glx-copy-from-front = true; 11 | glx-swap-method = 2; 12 | xrender-sync = true; 13 | xrender-sync-fence = true; 14 | 15 | # Transparency settings for i3. 16 | opacity-rule = [ 17 | "0:_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'" 18 | ]; 19 | 20 | # 21 | # Done. 22 | # 23 | -------------------------------------------------------------------------------- /dot-config-i3-config: -------------------------------------------------------------------------------- 1 | # -*- Mode: Shell-script -*- 2 | # 3 | 4 | # i3 config file (v4.15.1 on Xubuntu 16.04 LTS) 5 | # 6 | # Please see http://i3wm.org/docs/userguide.html for a complete reference! 7 | 8 | # 9 | # VARIABLES 10 | # 11 | 12 | # Helper scripts. Embedding the path explicitly is belt and braces. 13 | set $i3apps exec --no-startup-id ~/local/bin/i3-apps 14 | set $i3display exec --no-startup-id ~/local/bin/i3-display 15 | set $i3keyboard exec --no-startup-id ~/local/bin/i3-keyboard 16 | set $i3make exec --no-startup-id ~/local/bin/i3-make 17 | set $i3mode exec --no-startup-id ~/local/bin/i3-mode 18 | set $i3mouse exec --no-startup-id ~/local/bin/i3-mouse 19 | set $i3wrapper exec --no-startup-id ~/local/bin/i3-wrapper 20 | # Variables cannot reference other variables. 21 | set $default exec --no-startup-id ~/local/bin/i3-mode default 22 | set $dropdown exec --no-startup-id ~/local/bin/i3-wrapper dd 23 | 24 | # These hooks are currently unused but preserved. 25 | set $defaultnop nop 26 | set $systemnop nop 27 | set $layoutnop nop 28 | set $switchnop nop 29 | set $extrasnop nop 30 | 31 | # Use 'xrandr' with no arguments to list monitor names and geometry. 32 | # The values need not be mutually exclusive, but must be assigned. 33 | set $lmon HDMI2 34 | set $cmon HDMI1 35 | set $rmon VGA1 36 | 37 | # Type 'pacmd list sinks' to list audio outputs. 38 | set $firstaudiosink alsa_output.pci-0000_00_1b.0.analog-stereo 39 | set $secondaudiosink bluez_sink.00_01_01_00_12_CB 40 | set $fifthaudiosink alsa_output.pci-0000_00_03.0.hdmi-stereo 41 | # Type 'pacmd list sources' to list audio inputs. 42 | set $firstaudiosource alsa_input.pci-0000_00_1b.0.analog-stereo 43 | set $secondaudiosource alsa_input.usb-Alcor_Micro__Corp._TeckNet-02.analog-mono 44 | set $thirdaudiosource alsa_input.usb-C-Media_Electronics_Inc._USB_Audio_Device-00-Device.analog-mono 45 | 46 | # The font used in i3-input prompt+command boxes. 47 | # Forward declaration because used in various bindings. 48 | set $i3inputfont "pango:DejaVu Sans 12" 49 | 50 | # 51 | # BINDINGS 1 (reserved, recommended) 52 | # 53 | 54 | # Mod4 is the Windows key. Mod1 is the Alt key. 55 | # Menu can be generated by a modifier key using 'xcape'. 56 | 57 | # The standard entry point to my modes: Layout, Switch, Tab. 58 | # Menu is deliberate do not change. 59 | # I have used 'xcape' to bind Super_L to Menu for the left hand. 60 | bindsym Menu $i3mode Layout 61 | 62 | # Run i3-make. 63 | # 64 | # This installs the configuration into its runtime locations, 65 | # and as required, reloads this config or restarts i3. 66 | bindsym Control+Tab $i3make 67 | 68 | # The lines to the right of the ### comments illustrate the body of an 69 | # example i3-make script. 70 | # 71 | ### I3FILES=$(readlink -f ~/Programs/i3files) 72 | ### cd ${I3FILES} 73 | ### make \ 74 | ### CONFIGURED=true I3BIN="${HOME}/local/bin" \ 75 | ### -f ${I3FILES}/Makefile -C ${I3FILES} "$@" 76 | 77 | # Command prompt. 78 | bindsym Mod4+Tab $i3wrapper -p 79 | 80 | # Scratchpad terminal. 81 | bindsym Control+Delete $dropdown 82 | 83 | # System mode is for emergency use only. 84 | # Control-Alt-Delete is deliberate do not change. 85 | bindsym Control+Mod1+Delete $i3mode System 86 | 87 | # This lets us move and resize without having to position the mouse 88 | # with precision on window edges. 89 | floating_modifier Mod4 90 | 91 | # 92 | # BINDINGS 2 Numpad "immediate". 93 | # 94 | 95 | # Mute the default preferred microphone with the large key. 96 | # Subject to change: 97 | # - source 2 TeckNet microphone. 98 | bindsym KP_Insert \ 99 | exec pactl set-source-mute $secondaudiosource on # mute 100 | bindsym KP_Delete \ 101 | exec pactl set-source-mute $secondaudiosource off # unmute 102 | 103 | # Volume controls for inputs (sources) and outputs (sinks). 104 | # Subject to change: 105 | # - sink 1 3.5mm rear line out, 106 | # - sink 2 Bluetooth (headphones), 107 | bindsym KP_Divide \ 108 | exec pactl set-sink-volume $secondaudiosink -10% 109 | bindsym KP_Multiply \ 110 | exec pactl set-sink-volume $secondaudiosink +10% 111 | bindsym KP_Subtract \ 112 | exec pactl set-sink-volume $firstaudiosink 100% 113 | bindsym KP_Add \ 114 | exec pactl set-sink-volume $firstaudiosink +10% 115 | bindsym KP_Enter \ 116 | exec pactl set-sink-volume $firstaudiosink -10% 117 | 118 | # 119 | # MODES 120 | # 121 | 122 | # System mode. 123 | # Bind Control+ keys only. 124 | # It must be possible to have limited use of 'vi' from within this mode. 125 | mode "System" { 126 | 127 | # Get back to default mode. 128 | bindsym Control+q $default 129 | bindsym Control+d mode "default" 130 | 131 | # The minimum required to get to a terminal. 132 | bindsym Control+w restart 133 | bindsym Control+r reload 134 | bindsym Control+t exec --no-startup-id xfce4-terminal -T xfce4-terminal & 135 | bindsym Control+p exec --no-startup-id xfce4-panel --restart & 136 | 137 | # Restart file (g) and focus (f) watchers. 138 | bindsym Control+f $i3wrapper -f 139 | bindsym Control+g $i3wrapper -g 140 | 141 | # Untried but here just in case. 142 | bindsym Control+b shmlog $((64*1024*1024)) 143 | bindsym Control+n shmlog toggle 144 | bindsym Control+m debuglog toggle 145 | } 146 | 147 | # Layout mode. 148 | mode "Layout" { 149 | 150 | # Toggle between workspaces. 151 | bindsym q $default, workspace back_and_forth 152 | 153 | # Change focus. 154 | bindsym w focus up 155 | bindsym a focus left 156 | bindsym s focus down 157 | bindsym d focus right 158 | 159 | # Move focused window. 160 | bindsym t move up 161 | bindsym f move left 162 | bindsym g move down 163 | bindsym h move right 164 | 165 | # Increase or decrease the height or width symmetrically. 166 | bindsym i resize grow up 6 px or 1 ppt, resize grow down 6 px or 1 ppt 167 | bindsym j resize shrink left 6 px or 1 ppt, resize shrink right 6 px or 1 ppt 168 | bindsym k resize shrink down 6 px or 1 ppt, resize shrink up 6 px or 1 ppt 169 | bindsym l resize grow right 6 px or 1 ppt, resize grow left 6 px or 1 ppt 170 | 171 | bindsym e $layoutnop 172 | bindsym r $layoutnop 173 | 174 | bindsym y $layoutnop 175 | bindsym u $layoutnop 176 | 177 | # Focus the child container. 178 | bindsym o focus child 179 | # Focus the parent container. 180 | bindsym p focus parent 181 | 182 | # Standard terminal, preceded by a split. 183 | bindsym bracketleft $layoutnop 184 | bindsym bracketright $layoutnop 185 | 186 | # Toggle between being a tiled|floating window. 187 | # Toggle focus between tiled|floating windows. 188 | bindsym semicolon floating toggle 189 | bindsym apostrophe focus mode_toggle 190 | 191 | # Command prompt. 192 | bindsym backslash $default, $i3wrapper -p 193 | 194 | # Layouts. 195 | bindsym z layout default 196 | bindsym c layout tabbed 197 | 198 | # Split vertically next time. 199 | bindsym v split vertical 200 | # Split horizontally next time. 201 | bindsym b split horizontal 202 | 203 | # Swap (eXchange) containers. 204 | bindsym x \ 205 | $default, exec i3-input -f $i3inputfont \ 206 | -P '(swap container with mark): ' -F 'swap container with mark "%s"' 207 | 208 | # reName a workspace. 209 | bindsym n \ 210 | $default, exec i3-input -f $i3inputfont \ 211 | -P '(rename this workspace to): ' \ 212 | -F 'rename workspace to "%s"' 213 | 214 | # Set or clear the window Mark. 215 | # Avoid marks that match command aliases. 216 | # System alias: [A-Z][A-Z] User alias; [a-z][a-z]. 217 | # Marks are not limited to length two (here), but by i3input. 218 | bindsym m \ 219 | $default, exec i3-input -f $i3inputfont \ 220 | -P '(toggle mark): ' -F 'mark --toggle "%s"' 221 | 222 | # Switch to (named) workspace. 223 | bindsym comma \ 224 | $default, exec i3-input -f $i3inputfont \ 225 | -P '(switch focus to workspace): ' \ 226 | -F 'workspace "%s"' 227 | # Move to (named) workspace. 228 | bindsym period \ 229 | $default, exec i3-input -f $i3inputfont \ 230 | -P '(move container to workspace): ' \ 231 | -F 'move container to workspace "%s"' 232 | 233 | # DMenu. 234 | bindsym slash $default, $i3wrapper -d 235 | 236 | # Tab mode. 237 | bindsym Tab mode "Tab" 238 | bindsym Return mode "Tab" 239 | 240 | # Go to Switch mode. 241 | bindsym Menu mode "Switch" 242 | # Go back to normal. 243 | bindsym space $default 244 | } 245 | 246 | # Switch mode. 247 | # Entered from Layout mode. 248 | mode "Switch" { 249 | 250 | # Move workspace to a specific monitor. 251 | bindsym q $default, move workspace to output $lmon 252 | bindsym w $default, move workspace to output $cmon 253 | bindsym e $default, move workspace to output $rmon 254 | 255 | # Displays ON. This causes monitors to switch back to DVI. 256 | bindsym r $default, exec xrandr --output $lmon --auto --left-of $cmon & 257 | bindsym t $default, exec xrandr --output $cmon --auto & 258 | bindsym y $default, exec xrandr --output $rmon --auto --right-of $cmon & 259 | 260 | # Switch to workspace with urgency hints. 261 | bindsym u $default, [urgent=latest] focus 262 | 263 | # Displays OFF. This allows monitors to switch to HDMI. 264 | bindsym i $default, exec xrandr --output $lmon --off & 265 | bindsym o $default, exec xrandr --output $cmon --off & 266 | bindsym p $default, pexec xrandr --output $rmon --off & 267 | 268 | bindsym bracketleft $switchnop 269 | bindsym bracketright $switchnop 270 | 271 | # Move container to a specific monitor. 272 | bindsym a $default, move container to output $lmon 273 | bindsym s $default, move container to output $cmon 274 | bindsym d $default, move container to output $rmon 275 | 276 | # Enter fullscreen mode for the focused container. 277 | bindsym f $default, fullscreen toggle 278 | bindsym g $default, fullscreen toggle 279 | 280 | bindsym h $switchnop 281 | bindsym j $switchnop 282 | bindsym k $switchnop 283 | bindsym l $switchnop 284 | 285 | bindsym semicolon $switchnop 286 | bindsym apostrophe $switchnop 287 | 288 | # Command prompt. 289 | bindsym backslash $default, $i3wrapper -p 290 | 291 | # Switch focus to a specific monitor. 292 | bindsym z $default, focus output $lmon 293 | bindsym x $default, focus output $cmon 294 | bindsym c $default, focus output $rmon 295 | 296 | # Toggle an adjacent horizontal pair to Vertical. Left has focus, Upper gets focus. 297 | bindsym v focus right, split v, focus left, move right, move up ; 298 | # Toggle an adjacent vertical pair to Horizontal. Upper has focus, Left gets focus. 299 | bindsym b focus down, split h, focus up, move down, move left; 300 | 301 | bindsym n $switchnop 302 | bindsym m $switchnop 303 | 304 | bindsym comma $switchnop 305 | bindsym period $switchnop 306 | bindsym slash $switchnop 307 | 308 | # Tab mode. 309 | bindsym Tab mode "Tab" 310 | bindsym Return mode "Tab" 311 | 312 | # Go to default mode. 313 | # Going directly to Layout mode doesn't work here ... ??? 314 | bindsym Menu $default 315 | 316 | # Go back to normal. 317 | bindsym space $default 318 | } 319 | 320 | # Tab Mode. 321 | # Entered from switch or layout mode using Tab (or Return). 322 | # Originally created for the 'kill' binding only. Keep it sparse. 323 | mode "Tab" { 324 | 325 | # Kill window. using the wrapper's 'kill' option to prevent 326 | # killing critical windows by mistake. 327 | bindsym Control+q $default, \ 328 | $i3wrapper -k \ 329 | emacs 330 | 331 | # Keyboard & Mouse. Display & Desktop. 332 | # Bindings are vaguely mnemonic. 333 | 334 | bindsym q $extrasnop 335 | bindsym w $default, $i3display wake 336 | bindsym e $default, $i3mouse enable 337 | bindsym r $extrasnop 338 | bindsym t $extrasnop 339 | bindsym y $extrasnop 340 | bindsym u $extrasnop 341 | bindsym i $default, $i3apps start bgapps 342 | bindsym o $default, $i3apps stop fgapps bgapps 343 | bindsym p $extrasnop 344 | 345 | bindsym a $extrasnop 346 | bindsym s $default, $i3display sleep 347 | bindsym d $default, $i3mouse disable 348 | bindsym f $extrasnop 349 | bindsym g $extrasnop 350 | bindsym h $extrasnop 351 | bindsym j $extrasnop 352 | bindsym k $default, $i3keyboard 353 | bindsym l $extrasnop 354 | 355 | bindsym z $extrasnop 356 | bindsym x $extrasnop 357 | bindsym c $extrasnop 358 | bindsym v $extrasnop 359 | bindsym b $extrasnop 360 | bindsym m $extrasnop 361 | bindsym n $default, exec --no-startup-id nitrogen --restore 362 | 363 | bindsym comma $extrasnop 364 | bindsym period $extrasnop 365 | bindsym slash $extrasnop 366 | 367 | bindsym Tab $default 368 | bindsym Return $default 369 | 370 | # Go back to normal. 371 | bindsym space $default 372 | } 373 | 374 | # 375 | # SETTINGS 376 | # 377 | 378 | # Support toggling between workspaces. 379 | workspace_auto_back_and_forth yes 380 | 381 | # Assign workspaces to outputs. 382 | workspace "wm" output $lmon 383 | workspace "tv" output $lmon 384 | workspace "mp" output $lmon 385 | workspace "fp" output $lmon 386 | 387 | # Assign applications to workspaces. 388 | assign [instance="xfce4-terminal" title="watcher$"] wm 389 | assign [instance="xfce4-terminal" title="tvheadend"] tv 390 | assign [instance="gl" class="mpv"] mp 391 | assign [class="ffplay"] fp 392 | 393 | # Windows that look'n'feel better when floating. 394 | for_window [instance="dtvkit"] floating enable; 395 | for_window [instance="xfce4-notifyd$"] floating enable; 396 | for_window [instance="yad"] floating enable; 397 | for_window [title="Bluetooth Devices"] floating enable; 398 | for_window [title="Do Not Panic"] floating enable; 399 | for_window [title="File Operation Progress"] floating enable; 400 | for_window [title="KeyClick"] floating enable; 401 | for_window [title="Scratchpad"] floating enable; 402 | for_window [title="Unlock Keyring"] floating enable; 403 | 404 | # Mark with a two digit number and change workspace. 405 | for_window [instance="emacs"] $i3wrapper i3em; 406 | for_window [class="Google-chrome"] $i3wrapper i3gc; 407 | 408 | # VirtualBox windows assume full screen dimensions. 409 | for_window [title="VirtualBox Manager"] layout tabbed; 410 | 411 | # Colours for windows. 412 | # Defaults changed to green title bar and border when focused. 413 | # class | border | background | text | indicator | child_border 414 | client.focused #4c7899 #185522 #ffffff #2e9ef4 #185522 415 | client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a 416 | client.unfocused #333333 #222222 #888888 #292d2e #222222 417 | client.urgent #2f343a #900000 #ffffff #900000 #900000 418 | client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c 419 | 420 | # Font for window titles. The font for the bar is set explicitly. 421 | font pango:DejaVu Sans 8 422 | 423 | # Start i3bar to display a workspace bar (plus the system information i3status 424 | # finds out, if available) 425 | bar { 426 | i3bar_command /usr/bin/i3bar 427 | status_command /usr/bin/i3status --config ~/.config/i3status/config 428 | tray_output $cmon 429 | font pango:DejaVu Sans 10 430 | bindsym button2 $default, [urgent=latest] focus 431 | bindsym button3 workspace back_and_forth 432 | } 433 | 434 | # Mouse behaviour wrt windows. 435 | focus_follows_mouse no 436 | 437 | # Additional Menu and Escape keys. US layout on UK PC105. 'keycode 438 | # 51' is mapped to Return to mimic the geometry of the US keyboard. 439 | # Stuck modiiers are cleared. 440 | $i3keyboard 441 | 442 | # Put the "i3-nagbar" on the primary monitor. 443 | exec --no-startup-id xrandr --output $cmon --primary & 444 | 445 | # Compton. As per manpage requires (~/.config/compton.conf). 446 | # Desktop background (for empty workspaces). 447 | # https://faq.i3wm.org/question/6/how-can-i-set-a-desktop-background-image-in-i3/ 448 | # The 10 second delay between starting compton and applying the 449 | # desktop background avoids screen corruption on my machine. 450 | exec --no-startup-id compton -b & 451 | exec --no-startup-id sleep 10s && nitrogen --restore & 452 | 453 | # Applications. 454 | # The 10 second delay ensures that the dialog appears after the 455 | # desktop background changes. 456 | exec --no-startup-id sleep 10s && ~/local/bin/i3-apps start bgapps 457 | 458 | # 459 | # Done 460 | # 461 | -------------------------------------------------------------------------------- /dot-config-i3status-config: -------------------------------------------------------------------------------- 1 | # i3status configuration file. 2 | # see "man i3status" for documentation. 3 | 4 | # It is important that this file is edited as UTF-8. 5 | # The following line should contain a sharp s: 6 | # ß 7 | # If the above line is not correctly displayed, fix your editor first! 8 | 9 | general { 10 | colors = true 11 | interval = 2 12 | } 13 | 14 | order += "disk /" 15 | order += "disk /mnt/94bd806f-4aa9-4514-b2fc-9688182fa571" 16 | ### order += "wireless _first_" 17 | order += "ethernet _first_" 18 | ### order += "battery all" 19 | order += "cpu_temperature 0" 20 | order += "load" 21 | order += "tztime local" 22 | 23 | wireless _first_ { 24 | format_up = "W: (%quality at %essid) %ip" 25 | format_down = "W: down" 26 | } 27 | 28 | ethernet _first_ { 29 | # if you use %speed, i3status requires root privileges 30 | format_up = "E: %ip (%speed)" 31 | format_down = "E: down" 32 | } 33 | 34 | battery all { 35 | format = "%status %percentage %remaining" 36 | } 37 | 38 | cpu_temperature 0 { 39 | format = "T: %degrees °C" 40 | path = "/sys/devices/platform/coretemp.0/hwmon/hwmon1/temp1_input" 41 | } 42 | 43 | tztime local { 44 | format = "%Y-%m-%d %H:%M:%S" 45 | } 46 | 47 | load { 48 | format = "%1min" 49 | } 50 | 51 | disk "/" { 52 | format = "%avail" 53 | } 54 | 55 | disk "/mnt/94bd806f-4aa9-4514-b2fc-9688182fa571" { 56 | format = "%avail" 57 | } 58 | 59 | # 60 | # Done. 61 | # 62 | -------------------------------------------------------------------------------- /i3-apps: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # A script to start|stop various programs. 4 | # 5 | 6 | verb=start 7 | for argument in "${@}" 8 | do 9 | case "${argument}" in 10 | (wrapper) 11 | i3-msg '[title="file watcher"] kill' 12 | sleep 0.1 13 | i3-msg '[title="focus watcher"] kill' 14 | sleep 0.1 15 | if [ "_${verb}" != "_stop" ]; then 16 | path=$(dirname "$0") 17 | (eval "${path}/i3-wrapper -g") & 18 | sleep 0.1 19 | (eval "${path}/i3-wrapper -f") & 20 | fi 21 | ;; 22 | 23 | # This listens for playlists sent from my phone in response to 24 | # voice requests, and plays them via my desktop speakers. 25 | (music) 26 | pkill -f "playlist.m3u" # KISS. 27 | if [ "_${verb}" != "_stop" ]; then 28 | playlist="/dev/shm/${USER}/inotify/music" # The directory not the file. 29 | mkdir -p "${playlist}" 30 | playlist="${playlist}/playlist.m3u" 31 | if [ ! -e "${playlist}" ]; then 32 | touch "${playlist}" # The file must exist else the watcher terminates. 33 | fi 34 | playlist="--watch-files ${playlist} --on-modify-command \"mpv ${playlist}\"" 35 | (eval "inotify-hookable ${playlist}") & 36 | fi 37 | ;; 38 | 39 | # Dropbox needs to be launched with dbus. 40 | (dropbox) 41 | dropbox stop 42 | if [ "_${verb}" != "_stop" ]; then 43 | dbus-launch dropbox start -i 44 | fi 45 | ;; 46 | 47 | # SpiderOakONE. 48 | (spideroak) 49 | if [ "$(pgrep -c SpiderOakONE)" -ne 0 ]; then 50 | if [ "_${verb}" != "_start" ]; then 51 | (SpiderOakONE --shutdown > /dev/null 2>&1) & 52 | fi 53 | fi 54 | if [ "$(pgrep -c SpiderOakONE)" -eq 0 ]; then 55 | if [ "_${verb}" != "_stop" ]; then 56 | (SpiderOakONE) & 57 | fi 58 | fi 59 | ;; 60 | 61 | # Synergy. 62 | (synergy) 63 | if [ "$(pgrep -c synergy)" -ne 0 ]; then 64 | if [ "_${verb}" != "_start" ]; then 65 | pkill -f synergy 66 | fi 67 | fi 68 | if [ "$(pgrep -c synergy)" -eq 0 ]; then 69 | if [ "_${verb}" != "_stop" ]; then 70 | synergy & 71 | fi 72 | fi 73 | ;; 74 | 75 | # TV Headend behaves better without authentication. 76 | (tvheadend) 77 | if [ "$(pgrep -c tvheadend)" -ne 0 ]; then 78 | if [ "_${verb}" != "_start" ]; then 79 | pkill tvheadend 80 | fi 81 | fi 82 | if [ "$(pgrep -c tvheadend)" -eq 0 ]; then 83 | if [ "_${verb}" != "_stop" ]; then 84 | (xfce4-terminal -T 'tvheadend' -x tvheadend --noacl) & 85 | fi 86 | fi 87 | ;; 88 | 89 | # Start or Stop the "background" programs. Press OK to confirm. 90 | (bgapps) 91 | yad --title="${verb} bgapps" --timeout=300 --timeout-indicator=top 92 | if [ "$?" -eq 0 ]; then 93 | eval "$0 ${verb} wrapper tvheadend synergy music dropbox spideroak" 94 | fi 95 | ;; 96 | 97 | # Stop the important "foreground" programs. Press OK to confirm. 98 | (fgapps) 99 | for app in emacs firefox chrome thunderbird virtualbox 100 | do 101 | if [ "$(pgrep -c -f ${app})" -ne 0 ]; then 102 | yad --title="stop ${app}" --timeout=300 --timeout-indicator=top 103 | if [ "$?" -eq 0 ]; then 104 | case "${app}" in 105 | (firefox) 106 | # Firefox treats 'kill'/'pkill' as a crash. 107 | i3-msg '[class="Firefox"] kill' 108 | ;; 109 | (virtualbox) 110 | # Running VMs must be shutdown manually. 111 | if [ "$(VBoxManage list runningvms | wc -l)" -ne 0 ]; then 112 | # Allow plenty of time for user intervention. 113 | yad --title="halt ${app}" --timeout=7200 114 | # Allow two minutes for shutdown to complete. 115 | sleep 120s 116 | # Abandon system shutdown if any VMs are still running. 117 | if [ "$(VBoxManage list runningvms | wc -l)" -ne 0 ]; then 118 | exit 0 119 | fi 120 | fi 121 | ;; 122 | (*) 123 | pkill -f ${app} 124 | ;; 125 | esac 126 | fi 127 | fi 128 | done 129 | ;; 130 | 131 | # A verb argument affects all subsequent object arguments up 132 | # until either the next verb argument or end-of-list. The 133 | # verb variable is initialised such that the first argument 134 | # can safely be an object. 135 | (start|stop) 136 | verb=${argument} 137 | ;; 138 | esac 139 | done 140 | 141 | # 142 | # Done. 143 | # 144 | -------------------------------------------------------------------------------- /i3-display: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Display off/on. 4 | # 5 | # Requires i3-mouse for setmouse, xdotool. 6 | # 7 | 8 | setdisplay () 9 | { 10 | case "$1" in 11 | # Sleep the display using the keyboard or console, not the 12 | # mouse. 13 | (sleep|off) 14 | xdotool mousemove --clearmodifiers 2560 8 15 | setmouse disable 16 | xset s blank 17 | xset dpms 0 0 0 18 | xset dpms force off 19 | ;; 20 | # Wake the display using the keyboard or console, not the 21 | # mouse. 22 | (wake|on) 23 | setmouse enable 24 | xdotool mousemove --clearmodifiers 2048 128 25 | setmouse fast 26 | xset s off 27 | xset -dpms 28 | ;; 29 | (*) 30 | ;; 31 | esac 32 | } 33 | -------------------------------------------------------------------------------- /i3-keyboard: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # i3-keyboard 4 | # 5 | 6 | # 7 | # Bind standalone modifier keys. 8 | # 9 | case ${XDG_CURRENT_DESKTOP} in 10 | (XFCE) 11 | killall -q ibus-x11 # mumble 12 | killall -q xcape 13 | k1='Super_L=Menu' 14 | k2='Alt_L=Escape' 15 | k3='ISO_Level3_Shift=Escape' 16 | # The timeout is a compromise. ymmv. 17 | xcape -t 667 -e "$k1;$k2;$k3" 18 | ;; 19 | esac 20 | 21 | # 22 | # Set the keyboard layout. 23 | # 24 | # To override XFCE settings, you must TICK this tickbox: 25 | # "Settings_Manager|Keyboard|Layout|Use_system_defaults" 26 | # to avoid randomly reverting this: 27 | # 28 | case ${XDG_CURRENT_DESKTOP} in 29 | (XFCE) 30 | setxkbmap -model pc105 -layout us -variant altgr-intl option ctrl:nocaps 31 | xmodmap -e 'keycode 51 = Return' 32 | xmodmap -e 'pointer = default' 33 | ;; 34 | esac 35 | 36 | # 37 | # Restore the keyboard in i3wm. 38 | # 39 | case ${XDG_CURRENT_DESKTOP} in 40 | (XFCE) 41 | i3-msg mode default 42 | xdotool key --clearmodifiers Return 43 | ;; 44 | esac 45 | 46 | # 47 | # Done. 48 | # 49 | -------------------------------------------------------------------------------- /i3-make: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # i3-make 4 | # 5 | 6 | I3FILES=$(readlink -f ~/Programs/i3files) 7 | cd ${I3FILES} 8 | make \ 9 | CONFIGURED=true I3BIN="${HOME}/local/bin" \ 10 | -f ${I3FILES}/Makefile -C ${I3FILES} "$@" 11 | 12 | # 13 | # Done. 14 | # 15 | -------------------------------------------------------------------------------- /i3-mode: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # i3-mode 4 | # 5 | # Make the screen go dim when inside a non-default mode. 6 | # 7 | 8 | outputs=$(xrandr | grep ' connect' | awk '{print $1}') 9 | 10 | case "$1" in 11 | (default) 12 | i3-msg "mode \"$1\"" 13 | for output in ${outputs} ; do 14 | xrandr --output "${output}" --brightness 1.0 15 | done 16 | # Kill any sleeping instance of this script. 17 | pkill i3-mode 18 | ;; 19 | ([A-Z][a-z]*) 20 | i3-msg "mode \"$1\"" 21 | # Allow this instance to be killed before it dims the display. 22 | sleep 0.7 23 | for output in ${outputs} ; do 24 | xrandr --output "${output}" --brightness 0.5 25 | done 26 | ;; 27 | esac 28 | exit 0 29 | 30 | # 31 | # Done. 32 | # 33 | -------------------------------------------------------------------------------- /i3-mouse: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # i3-mouse 4 | # 5 | # Requires : xinput 6 | # 7 | 8 | # 9 | # Use this to tame the mouse. 10 | # 11 | setmouse () 12 | { 13 | id=$(xinput list | grep Mouse | cut -d= -f 2 | cut -f 1) 14 | cd=$(xinput --list-props "${id}" | grep "Constant Deceleration" | cut -d: -f 2 | awk '{print $1}') 15 | 16 | case "$1" in 17 | (disable) 18 | xinput --set-prop "${id}" "Device Enabled" "0" 19 | ;; 20 | (enable) 21 | xinput --set-prop "${id}" "Device Enabled" "1" 22 | ;; 23 | (fast) 24 | xset m 2 0 25 | xinput --set-prop "${id}" "Device Accel Constant Deceleration" 1 26 | ;; 27 | (slow) 28 | xset m 1 0 29 | xinput --set-prop "${id}" "Device Accel Constant Deceleration" 8 30 | ;; 31 | (*) 32 | if [ "${cd}" = "1.000000" ] ; then 33 | setmouse slow 34 | else 35 | setmouse fast 36 | fi 37 | ;; 38 | esac 39 | } 40 | -------------------------------------------------------------------------------- /i3-wrapper: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # i3-wrapper 4 | # 5 | # - Command aliases. 6 | # - Contains at least one bashism: https://wiki.ubuntu.com/DashAsBinSh 7 | # 8 | 9 | # 10 | # The wrapper Interface is a frequently modified file in shared memory. 11 | # 12 | dir=/dev/shm/${USER}/i3 13 | file=command 14 | 15 | # 16 | # Label the (L)eft (C)entre (R)ight (MON)itors. 17 | # My three monitors were arranged thus: [HDMI2] [HDMI1] [VGA1]. 18 | # Use 'xrandr' with no arguments to list monitor names and geometry. 19 | # 20 | lmon=HDMI2 21 | cmon=HDMI1 22 | rmon=VGA1 23 | 24 | # 25 | # Focus Watcher. 26 | # 27 | dofocus='emacs' 28 | infocus='true' 29 | exfocus='true' 30 | # 31 | timestamp=/dev/shm/${USER}/i3/timestamp 32 | 33 | # 34 | # Focus or launch a program. 35 | # 36 | # EITHER switch focus to the running program matching on $1=$2, OR 37 | # launch $5 with arguments ${@:6} on workspace $3 and output $4. 38 | # 39 | focus () 40 | { 41 | # Attempt to match the running program and obtain its window id. 42 | case "$1" in 43 | (instance) 44 | W=$(xdotool search --classname "$2" | head -1) ;; 45 | (class) 46 | W=$(xdotool search --class "$2" | head -1) ;; 47 | (title) 48 | W=$(xdotool search --name "$2" | head -1) ;; 49 | (*) 50 | W='' ;; 51 | esac 52 | 53 | if [ -z "$W" ]; then 54 | # Launch the program because no matching window id was found. 55 | if [ $# -ge 5 ]; then 56 | if [ "$4" == 'none' ]; then 57 | # Prefer to let i3 and dot-i3-config assign the 58 | # workspace and output? Take this branch. 59 | i3-msg "exec ${*:5}" 60 | else 61 | # {$3,$4} should be valid because they are constants. 62 | case "$4" in 63 | (${lmon}|${cmon}|${rmon}) 64 | cws=$(i3-msg -t get_workspaces | jq '.[] | .name,.focused' | grep true -B1 | grep -v true) 65 | if [ "$3" != 'none' ] && [ "${cws}" != "\"$3"\" ]; then 66 | cmd="workspace $3; exec ${*:5};" 67 | else 68 | cmd="exec ${*:5}" 69 | fi 70 | i3-msg "$cmd" 71 | sleep 1 72 | cmd="move workspace to output $4" 73 | i3-msg "$cmd" 74 | ;; 75 | esac 76 | fi 77 | fi 78 | else 79 | # Focus the workspace. 80 | # Escape any whitespace to suit 'i3-msg'. 81 | cmd="[$1=${2// /\\x20}] focus" 82 | cws=$(i3-msg -t get_workspaces | jq '.[] | .name,.focused' | grep true -B1 | grep -v true) 83 | if [ "$3" != 'none' ] && [ "${cws}" != "\"$3"\" ]; then 84 | cmd="workspace empty; workspace back_and_forth; ${cmd}" 85 | fi 86 | i3-msg "${cmd}" 87 | fi 88 | } 89 | 90 | # 91 | # Scratchpad (drop down) terminal. 92 | # 93 | i3dd() 94 | { 95 | # Only one key binding is required, the first keypress performs 96 | # initialisation and hides the terminal again. 97 | if [ "_$(xdotool search --classname "Scratchpad" | head -1)" = "_" ]; then 98 | xfce4-terminal -T Scratchpad & 99 | sleep 1 100 | # Set the instance to identify the scratchpad. 101 | xdotool getwindowfocus set_window --classname "Scratchpad" 102 | i3-msg "[instance=\"Scratchpad\"] resize set 80 ppt 80 ppt" 103 | i3-msg "[instance=\"Scratchpad\"] move absolute position 2112 24" 104 | i3-msg "[instance=\"Scratchpad\"] move scratchpad" 105 | else 106 | i3-msg "focus output ${cmon}" 107 | i3-msg "[instance=\"Scratchpad\"] scratchpad show" 108 | fi 109 | } 110 | 111 | # 112 | # Mark Emacs frames in the same way as terminals. 113 | # 114 | i3em() 115 | { 116 | id=$((10#$(date +%N) % 100)) 117 | id=$(printf "%02d" "${id}") 118 | sleep 1 119 | windowid=$(printf "0x%x" "$(xdotool getwindowfocus)") 120 | i3-msg "[id=\"${windowid}\"] mark --toggle \"${id}\"" 121 | # Move to (named) workspace. 122 | i3inputfont="pango:DejaVu Sans 12" 123 | i3-input -f "${i3inputfont}" \ 124 | -P '(move Emacs frame to workspace): ' \ 125 | -F 'move container to workspace "%s"' 126 | i3-msg "[con_mark=\"${id}\"] focus" 127 | } 128 | 129 | # 130 | # Mark Google Chrome windows in the same way as terminals. 131 | # 132 | i3gc() 133 | { 134 | id=$((10#$(date +%N) % 100)) 135 | id=$(printf "%02d" "${id}") 136 | sleep 1 137 | windowid=$(printf "0x%x" "$(xdotool getwindowfocus)") 138 | i3-msg "[id=\"${windowid}\"] mark --toggle \"${id}\"" 139 | # Move to (named) workspace. 140 | i3inputfont="pango:DejaVu Sans 12" 141 | i3-input -f "${i3inputfont}" \ 142 | -P '(move Google Chrome to workspace): ' \ 143 | -F 'move container to workspace "%s"' 144 | i3-msg "[title=\"Google Chrome\" con_mark=\"${id}\"] focus" 145 | } 146 | 147 | # 148 | # Add a terminal to the current workspace. (split h, side-by-side) 149 | # 150 | i3tt() 151 | { 152 | i3-msg "split h" 153 | i3lowerlower 'tt' 154 | } 155 | 156 | # 157 | # Add a terminal to the current workspace. (split v, stacked) 158 | # 159 | i3yy() 160 | { 161 | i3-msg "split v" 162 | i3lowerlower 'tt' # sic, 'tt'. 163 | } 164 | 165 | # 166 | # Add a terminal to a workspace matching the command alias. 167 | # 168 | i3lowerlower() 169 | { 170 | # Terminals are launched with two digits inside the title and 171 | # a matching mark. Collisions are indeed possible with this 172 | # but in practice it seems not. Pad with a leading zero. 173 | id=$((10#$(date +%N) % 100)) 174 | id=$(printf "%02d" "${id}") 175 | # i3wm suffers from gnome|xfce4-terminals that don't redraw 176 | # properly when shown again after being hidden. 177 | title="xfce4-terminal-${id}" 178 | xfce4-terminal -T "${title}" & 179 | # Allow time for the window to appear. 180 | sleep 1 181 | i3-msg "[title=\"${title}\"] mark \"${id}\"" 182 | if [ "$1" != 'tt' ]; then 183 | # Terminals invoked with two letters other than 'tt' are put 184 | # on a dedicated workspace, the 'tt' ones on the current one. 185 | i3-msg "move container to workspace $1, workspace $1" 186 | fi 187 | } 188 | 189 | # 190 | # Focus a window that has the matching mark. 191 | # 192 | i3mark() 193 | { 194 | # A mark can be anything alphanumeric that isn't a command alias, 195 | # e.g 22 AA a2 2a A2 2A but excluding 'aa' when pre-empted. 196 | cmd="[con_mark=\"$1\"] focus" 197 | i3-msg "${cmd}" 198 | } 199 | 200 | # 201 | # Common programs with examples of how to integrate them. 202 | # 203 | i3programs () 204 | { 205 | case "$1" in 206 | (:*) echo "${@/:/}" | xclip ;; 207 | 208 | (em|emacs) 209 | focus instance emacs 'em' ${cmon} emacs -rv 210 | ;; # ok 211 | (gc|google-chrome) 212 | focus class "Google-chrome" 'gc' 'none' google-chrome 213 | ;; # ok 214 | (ff|firefox) 215 | focus instance Navigator 'ff' ${cmon} firefox 216 | ;; 217 | (tb|thunderbird) 218 | focus class Thunderbird 'tb' ${lmon} thunderbird 219 | ;; # ok 220 | (ex|explorer) 221 | focus title 'File Manager' 'ex' ${lmon} exo-open --launch FileManager "${HOME}/Downloads" 222 | ;; # ok 223 | 224 | (sy|synaptic) 225 | focus instance synaptic 'sy' ${lmon} synaptic-pkexec 226 | ;; # ok 227 | (vb|virtualbox) 228 | focus title 'Oracle VM VirtualBox' 'vb' ${cmon} virtualbox 229 | ;; # ok 230 | (ws|wireshark) 231 | focus instance wireshark 'ws' ${cmon} wireshark 232 | ;; # n/a 233 | 234 | (ww|alt-word) 235 | focus title "LibreOffice Writer" 'ww' 'none' libreoffice --writer 236 | ;; 237 | (pp|alt-powerpoint) 238 | focus title "LibreOffice Impress" 'pp' 'none' libreoffice --impress 239 | ;; 240 | (ss|alt-excel) 241 | focus title "LibreOffice Calc" 'ss' 'none' libreoffice --calc 242 | ;; 243 | (sg|screengrab) 244 | focus none none 'none' xfce4-screenshooter -d 2 245 | ;; 246 | (im|images) 247 | focus instance ristretto 'im' ${cmon} ristretto 248 | ;; 249 | 250 | (mu|music) 251 | focus instance clementine 'mu' ${cmon} clementine 252 | ;; 253 | (fp) 254 | focus class ffplay 'fp' 255 | ;; 256 | (mp) 257 | focus class mpv 'mp' 258 | ;; 259 | (hb|handbrake) 260 | focus title HandBrake 'hb' ${cmon} handbrake 261 | ;; 262 | 263 | (dd) 264 | i3dd # The dropdown terminal. 265 | ;; 266 | (tt) 267 | i3tt # Terminal in the current workspace. Split is left-right. 268 | ;; 269 | (yy) 270 | i3yy # Terminal in the current workspace. Split is top-bottom. 271 | ;; 272 | ([a-z][a-z]) 273 | if [ "_$(which i3custom)" = "_" ]; then 274 | i3lowerlower "$1" # Terminals in other workspaces. 275 | else 276 | # An optional script that adds commands. It should succeed 277 | # when $1 is matched, and fail otherwise. 278 | i3custom "$1" 279 | if [ $? -ne 0 ]; then 280 | i3lowerlower "$1" 281 | fi 282 | fi 283 | ;; 284 | (i3em) 285 | i3em 286 | ;; 287 | (i3gc) 288 | i3gc 289 | ;; 290 | ([a-zA-Z0-9][a-zA-Z0-9]) # This cannot match all lowercase here. 291 | i3mark "$1" # Mark Terminals. 292 | ;; 293 | (*) 294 | # Do nothing. Do not be tempted to eval/exec arbitrary strings! 295 | ;; 296 | esac 297 | } 298 | 299 | # 300 | # Entry point for invoking a command alias or visiting a mark supplied 301 | # either directly via command line or indirectly via the file watcher. 302 | # 303 | start () 304 | { 305 | i3programs "$@" 306 | } 307 | 308 | # 309 | # The watcher calls the wrapper when the file held in shared memory 310 | # changes. The wrapper obtains the command alias or mark from the 311 | # file, and visits it, invoking as necessary. 312 | # 313 | dashs () 314 | { 315 | # The file typically consists of a two character string for the 316 | # first argument but as a last resort calling i3-msg supports 317 | # commands with argument lists. 318 | cmd=$(cat "${dir}/${file}") 319 | start "${cmd}" 320 | } 321 | 322 | # 323 | # Create a file in shared memory, monitor it for changes, and when it 324 | # changes, call this script with the file contents passed as argument. 325 | # 326 | dashg () 327 | { 328 | # Destroy any running instance of the file watcher. 329 | pid=$(pgrep -af 'inotify-hookable' | grep 'i3-wrapper -s' | awk '{print $1}') 330 | if ! test -z "${pid}"; then 331 | kill -HUP "${pid}" 332 | fi 333 | sleep 1 334 | # Create the file and populate with a no-op. 335 | mkdir -p "${dir}" 336 | echo "--" | tee "${dir}/${file}" 337 | # Create a singleton instance of the file watcher. 338 | # Create it inside a terminal. 339 | exe=$(basename "$0") 340 | xfce4-terminal -T "${exe}: file watcher" -x \ 341 | inotify-hookable \ 342 | --watch-files "${dir}/${file}" \ 343 | --on-modify-command "$0 -s" & 344 | } 345 | 346 | # 347 | # The prompt dialog writes the command alias or the mark to the file 348 | # held in shared memory. 349 | # 350 | dashp () 351 | { 352 | t=5 353 | ( 354 | # Assume one or two characters input and apply the 355 | # Return key 1 second before the dialog goes away. 356 | sleep "$((t - 1))" 357 | W=$(xdotool search --class Yad | head -1); 358 | if [ ! -z "$W" ]; then 359 | xdotool windowactivate "$W" 360 | xdotool key Return 361 | fi 362 | ) & 363 | cmd=$(yad --width=64 --center --no-buttons \ 364 | --title='i3 command alias or mark to visit' \ 365 | --entry --entry-label='(two characters)': \ 366 | --timeout=${t} --timeout-indicator=bottom) 367 | # Enforce the two character limit here, the limit only applies 368 | # to the prompt dialog. 369 | echo "${cmd}" | cut -c 1-2 | tee "${dir}/${file}" 370 | } 371 | 372 | # 373 | # Do one action when the name of the focused window matches the name 374 | # of the window argument, do the other action when they don't match. 375 | # 376 | dasha () 377 | { 378 | # 379 | # $1 = window label (substring) to be matched against. 380 | # $2 = command to run when the window in focus matches. 381 | # $3 = command to run otherwise. 382 | # 383 | focus=$(xdotool getwindowfocus) 384 | for id in $(xdotool search "${dofocus}") ; do 385 | if [ "${focus}" = "${id}" ] ; then 386 | # Indicator light ON (Dell keyboard). 387 | xset led 3 388 | # Success. The window has focus. Execute this command: 389 | (eval "${infocus}") & 390 | return 0 391 | fi 392 | done 393 | # Indicator light OFF (Dell keyboard). 394 | xset -led 3 395 | # Failure. The window has lost focus. Execute this command: 396 | (eval "${exfocus}") & 397 | return 1 398 | } 399 | 400 | # 401 | # Watch the window stack for focus events. 402 | # 403 | dashn () 404 | { 405 | # The search emits the names of the window that have lost and 406 | # gained focus plus lines containing hexadecimal ids. Considering 407 | # that the lines in a group are clustered in time, the beginning 408 | # of a group is marked by the larger time difference between 409 | # lines 410 | while true ; do 411 | xdotool search . \ 412 | behave %@ focus getwindowname | while read line 413 | do 414 | old=$(cat "${timestamp}") 415 | new=$(date +"%s%N") 416 | echo "${new}" > "${timestamp}" 417 | delta=$(( new - old )) 418 | # Threshold is 100 ms, the trailing 100 triplets are defensive 419 | # programming. 420 | [ ${delta} -ge 100100100 ] && "$0" -a && \ 421 | echo "${delta::-6} ms [$line]" 422 | count=$(echo "$line" | grep -c 'Unexpected event:') 423 | [ "${count}" -gt 0 ] && break 424 | done 425 | sleep 3 426 | done 427 | } 428 | 429 | # 430 | # Watch the window stack for focus events. 431 | # 432 | dashf () 433 | { 434 | # Destroy any running instance of the focus watcher. 435 | pid=$(pgrep -af 'xdotool search' | grep 'focus getwindowname' | awk '{print $1}') 436 | if ! test -z "${pid}"; then 437 | kill -HUP "${pid}" 438 | fi 439 | tsdir=$(basename "${timestamp}") 440 | mkdir -p "${tsdir}" 441 | # Create a singleton instance of the focus watcher. 442 | # Create it inside a terminal. 443 | exe=$(basename "$0") 444 | xfce4-terminal -T "${exe}: focus watcher" -x "$0" -n & 445 | } 446 | 447 | # 448 | # Kill the focused window unless its name matches the name of this 449 | # script or a name on the argument list. 450 | # 451 | dashk () 452 | { 453 | name=$(xdotool getwindowfocus getwindowname) 454 | # Terminals running this script are immune. 455 | for save in $(basename "$0") "$@"; do 456 | if test "${name#*${save}}" != "${name}"; then 457 | return 458 | fi 459 | done 460 | # Windows with all upper case marks are immune. 461 | mark=$(i3-msg -t get_tree | sed s/\{/\\n/g | \ 462 | grep '"focused":true' | sed s/\,/\\n/g | grep mark | cut -c11-12) 463 | case "${mark}" in 464 | ([A-Z][A-Z]) ;; 465 | (*) i3-msg kill ;; 466 | esac 467 | } 468 | 469 | # 470 | # The i3 dynamic menu. 471 | # 472 | dashd () 473 | { 474 | # Parse this script to make a dmenu on-the-fly. 475 | # It's fast and we don't need to use 'less' so not Makefile. 476 | 477 | # [1] find all case expressions in lowercase. 478 | # [2] (mumble about dashes), 479 | # [3] reject double-dash case expressions, 480 | # [4] cut upto and including the first bar, 481 | # [5] reject anything that stlll has a bar, 482 | # [6] sort and assemble the dmenu, 483 | # [7] wait for user to choose or cancell, 484 | # [8] pass any choice to 'start' function. 485 | 486 | choice=$(grep '[(][a-z][a-z][|]' "$0" | sed 's/--[a-z]*//g' | \ 487 | sed 's/[\x20]*//g' | cut -c 5- | sed 's/)//g' | grep -v '|' | \ 488 | sort -u | \ 489 | dmenu -fn '-misc-fixed-bold-r-normal--14-*-*-*-*-*-*-*') 490 | [ ! -z "${choice}" ] && start "${choice}" 491 | } 492 | 493 | # 494 | # Start here. 495 | # 496 | case ${XDG_CURRENT_DESKTOP} in 497 | (XFCE) 498 | case "$1" in 499 | # These dash options are internal messages sent between i3 500 | # config and i3 scripts. 501 | (-a) dasha ;; 502 | (-d) dashd ;; 503 | (-f) dashf ;; 504 | (-g) dashg ;; 505 | (-k) dashk "${@:2}" ;; 506 | (-n) dashn ;; 507 | (-p) dashp ;; 508 | (-s) dashs ;; 509 | (-*) ;; 510 | # Inject a command or mark directly, bypasssing the usual 511 | # interface. 512 | (*) 513 | start "$@" 514 | ;; 515 | esac 516 | exit 0 517 | ;; 518 | (*) 519 | exit 2 520 | ;; 521 | esac 522 | 523 | # 524 | # Done. 525 | # 526 | --------------------------------------------------------------------------------