Sanskar's blog


My no mouse setup!





Sections:




Introduction:



Hi again! I wanted to share my no mouse setup with you guys! Recently, my mouse broke down at home and I no longer have a functional mouse that I can use at my home setup. So, I just continued on coding and playing around my computer withtout a mouse and realized that I don’t really need a mouse anymore. Although, doing frontend stuff still requires a mouse but I am not doing that a lot at home. So, I just wanted to share how I acheived that! Before I start with anything, you can find my dotfiles here and you can find a clip of my setup below:

Another motivation for this blog post is that I do quite enjoy tinkering with my setup trying to squeeze out any optimization possible to make my workflow better. Additionally with that, I like my setup to be aesthetic because that helps me be more productive. I learnt and am still learning a LOT whenever I edit my configs and try to make my workflow better. That is how I found out about git worktree. I also made a personal project out of that because I found it to be super useful.


I also love programming a lot so, if there is something that will help me program faster and learn faster. Why not? Also, most of these tools may not exactly be helping me towards not using a mouse. Some of them are just here to look nice!


Okay, then. Stopping my personal monologue here and actually getting into my config, I have a few key things in my setup:

  • Xmonad - My tiling window manager.
  • Alacritty - My preferred terminal emulator.
  • Polybar - The cool bar at the top of my setup!
  • Tmux - Terminal multiplexer like screen.
  • Starship - For my terminal prompt.
  • Fish - My preferred shell.
  • Picom - My display compositer.
  • Vimium - This is a plugin for my browser (chrome), so I don’t need to go to my mouse to click on things.
  • Neovim - My code editor.

Prequisites:

  • Have Nerdfonts installed!

Great! Let’s get started!


Xmonad:



I started off using Xmonad instead of any other window manager (wm) like i3 or bspwm because there is this guy on youtube who has a channel named distrotube and I saw him use Xmonad as his primary wm at the time. And, it looked really cool! Although, I realized later, after tinkering with configs, that you can make the same setup with almost any other wm as well. But, by then, I already had a working xmonad config which I didn’t want to change anymore. And, it was very minimalistic which was good enough for me. But, regardless, it was a fun experience. Here is my Xmonad config:

import XMonad
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.ManageDocks
import XMonad.Layout.Magnifier
import XMonad.Layout.Spacing
import XMonad.Layout.ThreeColumns
import XMonad.Util.EZConfig
import XMonad.Util.Run
import XMonad.Util.SpawnOnce
import XMonad.Actions.SpawnOn
import XMonad.Util.Ungrab
import XMonad.Hooks.ManageHelpers
import XMonad.StackSet
import XMonad.Layout.ThreeColumns

fColor = "#d73a49"

nColor = "#586069"

myLayout = avoidStruts (tiled ||| Full)
  where
    tiled = Tall nmaster delta ratio
    nmaster = 1
    ratio = 1 / 2
    delta = 3 / 100

myStartupHook = do
    spawn "~/.config/polybar/launch.sh"
    spawn "~/.fehbg &"
    spawnOnce "xrandr -s 3440x1440 -r 120"
    spawnOnce "picom -b --config ~/.config/picom/picom.conf"

main = do
  xmonad $
    docks $
      ewmh
        def
          { normalBorderColor = nColor,
            borderWidth = 3,
            focusedBorderColor = fColor,
            layoutHook = spacingWithEdge 10 myLayout,
            manageHook = manageSpawn,
            terminal = "alacritty",
            startupHook = myStartupHook
          }
        `additionalKeys` [ ((mod1Mask, xK_b), spawn "google-chrome-stable"),
                           ((mod1Mask, xK_s), spawn "flameshot gui"),
                           ((mod1Mask, xK_x), spawn "xsecurelock"),
                           ((mod1Mask, xK_p), spawn "rofi -show run"),
                           ((mod1Mask, xK_r), spawn "ffmpeg -f x11grab -framerate 60 -video_size 3440x1440 -i $DISPLAY -threads 8 ~/screenrecords/`date +'%d-%m-%y-%T'`.mp4"),
                           ((mod1Mask .|. shiftMask, xK_r), spawn "killall ffmpeg")
        ]

It is pretty lightweight. And, also, please note that I have no idea how to read and write haskell code properly. I barely got this working as the syntax for haskell was so confusing.


I have a couple of colors setup for active windows vs inactive windows denoted by fColor and nColor. I am running some startup scripts, so things initialize correctly when I login. First, launch polybar, set my background image to the last one I set using feh, change my display resolution using xrandr and finally start picom.


In terms of layout, I am pretty minimal as well. Most of the time, I only have one terminal open as I use tmux often. Just two layout, tiled and Full. Tiled looks like this:

tiled layout

Where as Full is just this:

Full

Lastly, the dodgy code block at the end glues everything together. Here, I just tell xmonad to start with my config. Basically saying I wanted this as my border colors, my border width is this and this is what I want to run when I startup. Additionally, I set up some keybinds for starting up chrome, application launcher, lock screen, screenshotting and screen-recording. With that, I have a nice minimalistic setup for my window manager. Let’s move on to my terminal emulator.


Alacritty:



font:
  normal:
    family: FiraMono Nerd Font
    style: Regular

  bold:
    family: FiraMono Nerd Font
    style: Bold

  italic:
    family: FiraMono Nerd Font
    style: Italic

  bold_italic:
    family: FiraMono Nerd Font
    style: Bold Italic
  size: 14

colors:
  # Default colors
  primary:
    background: "0x0a0c10"
    foreground: "0xf0f3f6"

  cursor:
    text: "0x0a0c10"
    cursor: "0xf0f3f6"

  # Normal colors
  normal:
    black: "0x7a828e"
    red: "0xff9492"
    green: "0x26cd4d"
    yellow: "0xf0b72f"
    blue: "0x71b7ff"
    magenta: "0xcb9eff"
    cyan: "0x39c5cf"
    white: "0xd9dee3"

  # Bright colors
  bright:
    black: "0x9ea7b3"
    red: "0xffb1af"
    green: "0x4ae168"
    yellow: "0xf7c843"
    blue: "0x91cbff"
    magenta: "0xcb9eff"
    cyan: "0x39c5cf"
    white: "0xd9dee3"

env:
  TERM: xterm-256color

shell:
  program: fish

window:
  dynamic_padding: true
  padding:
    x: 0
    y: 0

  decorations: None

I used to use kitty for the longest time but I recently switched to Alacritty because my work friends use it. I realized how I didn’t use a lot of the kitty features like its tabs, image viewer, etc. So, when I switched to barebones and minimalistic alacritty, I really liked it and also the config file was in yaml which I find better than other configuration files. It still looks the same as my old kitty setup:

alacritty


Polybar:



Polybar is my status/navigation bar for my setup. While I mostly never need to use my status bar, it’s just there to show me things I care about like cpu usage, memory usage, whether I am connected to the wifi or not, volume, etc.


Here is my config for that:

[bar/mybar]
modules-right = wireless-network backlight filesystem cpu memory pulseaudio date
modules-left = ewmh xwindow
font-0 = "Noto Sans:size=12;4"
font-1 = "Noto Color Emoji: size=12:scale=11;2"
font-2 = "Noto Sans:weight:bold:size=12;4"
background = ${colors.background}
forground = ${colors.foreground}
height = 45
separator = %{F#f6c177}•%{F-}
separator-padding = 2
line-size = 3
padding = 5
border-width = 2
border-radius = 10
width = 98.75%
; width + (2 * offset-x) = 100
; meaning width + offset-x should equal to 100% of the width
offset-x = 0.625%

[colors]
background = #010409
foreground = #b87fff
currentWorkspace =  #ff6a69
hasWindowsWorkspace = #409eff
notUsedWorkspace = #6e6a86

[module/date]
type=internal/date
date=%Y-%m-%d%
time= %H:%M
format = <label>
label = %{u#ffffff}%{+u}%{T3}%date% %time% %{T-}%{u-}
label-font = 4
interval = 1.0

[module/ewmh]
type = internal/xworkspaces
format = <label-state>
label-active-foreground = ${colors.currentWorkspace}
label-active-padding = 1
label-active-underline = ${colors.currentWorkspace}
label-empty-foreground = ${colors.notUsedWorkspace}
label-occupied-foreground = ${colors.hasWindowsWorkspace}
label-active = %{T3}[%name%]%{T-}

[module/xwindow]
type = internal/xwindow
format = <label>
label-maxlen= 20
format-foreground = ${colors.foreground}
format-underline = ${colors.foreground}
label = %{T3}%title%%{T-}
label-empty = Empty

[module/pulseaudio]
type = internal/pulseaudio
format-volume = %{u#FFAC81}%{+u}%{F#FFAC81}%{T3}sound:%{T-}%{F-} (<label-volume>)%{u-}

[module/memory]
type = internal/memory
interval = 5
label = %{u#DBF9B8}%{+u}%{F#DBF9B8}%{T3}mem:%{T-}%{F-} (%percentage_used%%)%{u-}

[module/cpu]
type = internal/cpu
interval = 3
label = %{u#F5A65B}%{+u}%{F#F5A65B}%{T3}cpu:%{T-}%{F-} (%percentage%%)%{u-}

[module/filesystem]
type = internal/fs
mount-0 = /
interval = 120
label-mounted = %{u#2B9EB3}%{+u}%{F#2B9EB3}%{T3}hdd:%{T-}%{F-} (%percentage_used%%)%{u-}

[module/wireless-network]
type = internal/network
interface = wlan0
interface-type = wireless
interval = 1
format-connected = <label-connected>
format-disconnected = <label-disconnected>
label-connected = %{u#44AF69}%{+u}%{F#44AF69}%{T3}%essid%:%{T-}%{F-} (%downspeed%)%{u-}
label-disconnected = %{F#F8333C}wifi:%{F-} none

Most of it is just, formatting and making it look pretty. Polybar parses this config and just replaces %{whatever}% using things it has access to. And, just displays it in a nice format:

polybar

I used to use xmobar for the longest time. For I think about a year, I used nothing but xmobar. I remember seeing polybar but never really transistioned onto it. As soon as I saw an extremely cool setup on reddit in r/unixporn, I knew I had to change my setup to use polybar. And, I haven’t looked back since then!


Tmux:



Tmux is what I use so that I don’t need to spawn multiple terminals. Sometimes that might be desirable but most of the time, I don’t have more than 2 or 3 terminal running. And, I just using tmux to manage windows and splits inside the terminal. Here the config for that:

unbind C-b
unbind Up
unbind Down
unbind Left
unbind Right

set-option -g prefix C-a
set -g status-style bg=default
set-window-option -g mode-keys vi

bind h select-pane -L
bind j select-pane -D
bind l select-pane -R
bind k select-pane -U

bind H resize-pane -L 20
bind J resize-pane -D 20
bind K resize-pane -U 20
bind L resize-pane -R 20

bind Enter copy-mode # enter copy mode

bind -T copy-mode-vi v send -X begin-selection
bind -T copy-mode-vi C-v send -X rectangle-toggle
bind -T copy-mode-vi y send -X copy-selection-and-cancel
bind -T copy-mode-vi Escape send -X cancel
bind -T copy-mode-vi H send -X start-of-line
bind -T copy-mode-vi L send -X end-of-line
set-option -g default-shell /usr/bin/fish
set -g default-terminal "tmux-256color"
set -ag terminal-overrides ",xterm-256color:RGB"

I have replaced my prefix to be C-a instead of C-b as I find that more comfortable. I don’t want to use arrow keys as it slows down my workflow so I set up tmux binds to use binds similar to vim for switching in between panes. Next, I setup some binds to shrink/grow panes. Setting them to be the same bindings as vim which makes it super nice. Next, just setting up copying in a tmux so, that I am able to copy text without needing to touch my mouse. I use this a lot whenever I need to ssh as well. And, it is pretty easy to install using apt on a remote machine, if it isn’t already pre-installed, and just scping your tmux.conf. Now, we don’t every need to worry about our ssh connection dying and losing progress! We can re-ssh in and just attach our tmux session! Lastly, the color schemes looks a bit off in alacritty so I need to update the $TERM environment variable.


I started using tmux because I saw ThePrimeagen use it. And, I was just at awe whenever he spawned a new session to run a single command and then returned back to the previous shell. Seeing that made me realize, I want that in my setup. While I can send my current process to the background using C-z and use fg to then return back to do the same thing, I feel like there is a lot of friction there. So, incorporating tmux into my workflow helps a lot!


Starship:



As you might already seen through my setup in the introduction, I really like pretty things. Starship is a tool used to make my terminal prompt prettier! No need to use the ugly default prompt. You can use your own nice fun prompt! Currently, mine looks like this:

starship

I didn’t need to change anything in my starship config as I just used a preset which looked pretty nice! The preset I used was bracketed-segments on starship.


Fish:



For my shell, I use fish. The main reason being the sweet autocompletion, nice clean syntax highlighting and better scripting syntax out of the box. No more fiddling with path doing stuff like export PATH=$PATH:~/bin cause now you have fish_add_path which is much easier to use. It also be configured with vim bindings as with other shells which I have done for my config.


The shell itself is very nice but one downside is that it’s not POSIX compliant meaning your POSIX script that runs in all the other POSIX compliant shells might not work in fish (such as nvm). That might also be a deal breaker for someone who has a lot of shell scripts for their workflow as they will either need to be ported over or executed using another shell. For me, it’s not a huge problem, as I don’t use shell scripts a lot. Hence, the reason why I kept on sticking with it.


Here’s my fish config:

if status --is-interactive
  # Path variables for unix
  switch (uname)
    case Linux
      fish_add_path /home/(whoami)/.yarn/bin
      fish_add_path /usr/local/go/bin
      fish_add_path /home/(whoami)/go/bin

    case Darwin
      # Update path for fish
      # Bins are installed using brew for mac os configs
      fish_add_path /opt/homebrew/bin
  end

  # Start zoxide and starship
  zoxide init fish | source
  starship init fish | source

  # Setup fish bindings
  fish_vi_key_bindings
  bind -M insert jj 'set fish_bind_mode default; commandline -f backward-char force-repaint'
  bind -M insert \cn accept-autosuggestion
  bind -M default \cn accept-autosuggestion
  set fish_greeting
  bind -M default A 'set fish_bind_mode insert; commandline -f end-of-buffer'

  set -Ux PYENV_ROOT $HOME/.pyenv
  set -U fish_user_paths $PYENV_ROOT/bin $fish_user_paths
  pyenv init - | source

  pyenv virtualenv-init - | source
end

Before anything, I make sure that the session I am spawning is an interactive session, if it isn’t then the things in my config.fish don’t need to be sourced. It helps squeeze out little bit more performance (Eg. when running a shell script).


I have both Mac and Linux devices so based on the device, I have tried to add correct things to the path. I think for now this works but in the future would be better to use a shell scripts to add paths once universally rather than sourcing it everytime in config.fish. This will also remove any bloat in the startup sourcing.


After that, I initialize some CLI tools. I use a CLI tool call zoxide which helps me jump from directory to directory fast. So, I need to initialize it here along with starship, the CLI prompt we talked about earlier. Lastly, just setting up vim bindings + my personal bindings like jj in the terminal. In the end, I don’t think I will be moving away from fish anytime soon unless I need to as for me it is sufficient gives me the features I like and is friendlier to use. Also, recently a friend told me about fish_config which is actually so mind-blowing. Although all my themeing is handled by alacritty and all my prompts are done through startship, but if you were using fish for it then you could easily do it here because it opens up a nice user-friendly web application for handling such stuff:

fish_config


Picom:



picom is my choice of display compositor. I have a pretty light weight setup for this as well:

backend = "glx";
corner-radius =  12;
round-borders = 1;
rounded-corners-exclude=["class_g = 'Rofi'"];

Just saying what engine I want to start picom with, along with the rounded-corners I have in my setup. Lastly, I am asking it to not include rounded corners for my application launcher rofi which I very rarely use.


Vimium:



This is a heavy hitting tool that I use everyday. This is a browser plugin only available in chrome but there are alternatives for other browsers. So, basically what this plugin does is it gives me access to some of my vim bindings in my browser. So such as opening links, opening new tabs, searching for currently open tabs, save an image, searching within the page, etc. can all be done without taking your hands off the keyboard. It helps me a lot in my workflow as I don’t need to touch my mouse to click a link. I don’t have any config for this, so I just use what comes out of the box. Here is a quick demo on how I use it:


Neovim:



Neovim is my choice of code editor, is my lengthiest config and it keeps on ever changing. So, my latest neovim config, will always be in my github as I push everytime I make a change to it. But, we can checkout what I have currently. Starting off, I have my init.lua file:

require("plugins");
require("set");
require("remap");

vim.loader.enable();

-- [[ Highlight on yank ]]
-- See `:help vim.highlight.on_yank()`
local highlight_group = vim.api.nvim_create_augroup('YankHighlight', { clear = true })
vim.api.nvim_create_autocmd('TextYankPost', {
  callback = function()
    vim.highlight.on_yank({
      higroup = "IncSearch",
      timeout = "40"
    })
  end,
  group = highlight_group,
  pattern = '*',
})

There is a couple of things going on here. Firstly, we require 3 different modules which is plugins, set and remap. We will get into each of them after this. After that, we call vim.loader.enable() which is for speeding up the start up time which previously used be done through using impatient.nvim. After that, I set up highlight on yank which gives me this cool visual everytime I yank/copy and I know what I copied as well:

My set.lua files have global vim configurations as you can see below:

-- Set highlight on search
vim.o.hlsearch = false
vim.o.wrap = false

vim.o.swapfile = false

vim.o.relativenumber = true;

-- Make line numbers default
vim.wo.number = true

-- Enable mouse mode
vim.o.mouse = nil

-- Enable break indent
vim.o.breakindent = true

-- Save undo history
vim.o.undofile = true

-- Case insensitive searching UNLESS /C or capital in search
vim.o.ignorecase = true
vim.o.smartcase = true

-- Decrease update time
vim.o.updatetime = 250
vim.wo.signcolumn = 'yes'

-- Set colorscheme
vim.o.termguicolors = true

-- Set completeopt to have a better completion experience
vim.o.completeopt = 'menuone,noselect'

vim.o.scrolloff = 8;

vim.g.mapleader = ' '
vim.g.maplocalleader = ' '
vim.g.netrw_banner = 0
vim.o.tabstop = 2
vim.o.softtabstop = 2
vim.o.shiftwidth = 2
vim.o.expandtab = true

Most of this was copied over from kickstart.nvim but I have added some of my own stuff like setting wrap as false, not using swapfiles and removing the netrw banner. I also disable mouse usage in nvim. Additionally, I updated scrolloff to make sure I have 8 lines above/below when navigating the file.


Next file is remap.lua:

-- Keymaps for better default experience
-- See `:help vim.keymap.set()`
vim.keymap.set({ 'n', 'v' }, '<Space>', '<Nop>', { silent = true })
vim.keymap.set('i', 'jj', '<Esc>', { silent = true })
vim.keymap.set('n', '<leader>pv', vim.cmd.Ex)
vim.keymap.set("n", "<C-d>", "<C-d>zz")
vim.keymap.set("n", "<C-u>", "<C-u>zz")
vim.keymap.set("n", "n", "nzzzv")
vim.keymap.set("n", "N", "Nzzzv")
vim.keymap.set({"n", "v"}, "<leader>y", [["+y]])
vim.keymap.set("n", "<leader>Y", [["+Y]])
vim.keymap.set("n", "H", "^")
vim.keymap.set("n", "L", "$")

-- Remap for dealing with word wrap
vim.keymap.set('n', 'k', "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true })
vim.keymap.set('n', 'j', "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true })

-- Diagnostic keymaps
vim.keymap.set('n', '<leader>p', vim.diagnostic.goto_prev)
vim.keymap.set('n', '<leader>n', vim.diagnostic.goto_next)
vim.keymap.set('n', '<leader>e', vim.diagnostic.open_float)
vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist)

I use jj for going back to normal mode from insert mode, so I set that binding up first. Next, I setup LeaderKey and pv as a binding to launch netrw. Ctrl-d and Ctrl-u are remapped to center text on using page down and up. Same for n and N as both are remapped to center text in the screen when jumping to next occurence. Next, I have some keybinds to deal with navigating when I have word wrap on. Lastly, I have some bindings for seeing where are LSP errors in my code and also viewing what these errors are.


Lastly, my plugins.lua file:

local is_bootstrap = os.getenv("is_bootstrap")

require('packer').startup(function(use)
  -- Package manager
  use 'wbthomason/packer.nvim'

  use { -- LSP Configuration & Plugins
    'neovim/nvim-lspconfig',
    requires = {
      -- Automatically install LSPs to stdpath for neovim
      'williamboman/mason.nvim',
      'williamboman/mason-lspconfig.nvim',

      -- Useful status updates for LSP
      {'j-hui/fidget.nvim'},

      -- Additional lua configuration, makes nvim stuff amazing
      'folke/neodev.nvim',
    },
  }

  use { -- Autocompletion
    'hrsh7th/nvim-cmp',
    requires = { 'hrsh7th/cmp-nvim-lsp', 'L3MON4D3/LuaSnip', 'saadparwaiz1/cmp_luasnip' },
  }

  use { -- Highlight, edit, and navigate code
    'nvim-treesitter/nvim-treesitter',
    run = function()
      pcall(require('nvim-treesitter.install').update { with_sync = true })
    end,
  }

  use 'mbbill/undotree'

  use 'nvim-lua/plenary.nvim'
  use 'ThePrimeagen/harpoon'

  use({
    'projekt0n/github-nvim-theme',
    config = function()
      require('github-theme').setup({});
      vim.cmd.colorscheme "github_dark_high_contrast"
    end
  })

  -- Git related plugins
  use 'tpope/vim-fugitive'
  use 'tpope/vim-rhubarb'
  use 'lewis6991/gitsigns.nvim'

  use 'nvim-lualine/lualine.nvim'           -- Fancier statusline
  use 'lukas-reineke/indent-blankline.nvim' -- Add indentation guides even on blank lines
  use 'numToStr/Comment.nvim'               -- "gc" to comment visual regions/lines
  use 'tpope/vim-sleuth'                    -- Detect tabstop and shiftwidth automatically
  use 'folke/zen-mode.nvim'

  -- Fuzzy Finder (files, lsp, etc)
  use { 'nvim-telescope/telescope.nvim', branch = '0.1.x', requires = { 'nvim-lua/plenary.nvim' } }

  -- Fuzzy Finder Algorithm which requires local dependencies to be built. Only load if `make` is available
  use { 'nvim-telescope/telescope-fzf-native.nvim', run = 'make', cond = vim.fn.executable 'make' == 1 }

  use {
    "windwp/nvim-autopairs",
    config = function() require("nvim-autopairs").setup {} end
  }
end)

-- If bootstrap don't load plugins
if is_bootstrap then
  return
end

This file has all the plugins I use. To start, I check whether we are in bootstrap mode or not. I bootstrap new machines using ansible, so in those cases, I pass in an environment variable called is_bootstrap. If it is true, then instead of loading plugin configs the startup sourcing will stop and return.


Besides that, going into each plugin and its config can take a while so, you can have a look yourself in my /nvim/after/plugin folder.


Other CLI tools that I use:



  • zoxide - smater cd
  • direnv - do something on entering a folder
  • exa - better ls
  • ripgrep - faster grep
  • bottom - process management tool

Completing mundane things:



  • Change resolution? Just use xrandr.
  • Need to connect to a wifi, just use nmcli or iwd.
  • Need to watch a video? Use mpv.
  • Need to record a video? Use ffmpeg.
  • Need to screenshot something? Use scrot or flameshot.
  • Need to increase your laptop’s volume? Use amixer.
  • Need to a bluetooth device? Use bluetoothd.

Anything you can do through a GUI or need to use a mouse to do, can be done via a termnial. So, just search it up!